Skip to content

Instantly share code, notes, and snippets.

@matt2005
Forked from awarecan/lambda_function.py
Last active April 17, 2024 15:37
Show Gist options
  • Star 84 You must be signed in to star a gist
  • Fork 48 You must be signed in to fork a gist
  • Save matt2005/744b5ef548cc13d88d0569eea65f5e5b to your computer and use it in GitHub Desktop.
Save matt2005/744b5ef548cc13d88d0569eea65f5e5b to your computer and use it in GitHub Desktop.
Alexa Smart Home Skill Adapter for Home Assistant
"""
Copyright 2019 Jason Hu <awaregit at gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import os
import json
import logging
import urllib3
_debug = bool(os.environ.get('DEBUG'))
_logger = logging.getLogger('HomeAssistant-SmartHome')
_logger.setLevel(logging.DEBUG if _debug else logging.INFO)
def lambda_handler(event, context):
"""Handle incoming Alexa directive."""
_logger.debug('Event: %s', event)
base_url = os.environ.get('BASE_URL')
assert base_url is not None, 'Please set BASE_URL environment variable'
base_url = base_url.strip("/")
directive = event.get('directive')
assert directive is not None, 'Malformatted request - missing directive'
assert directive.get('header', {}).get('payloadVersion') == '3', \
'Only support payloadVersion == 3'
scope = directive.get('endpoint', {}).get('scope')
if scope is None:
# token is in grantee for Linking directive
scope = directive.get('payload', {}).get('grantee')
if scope is None:
# token is in payload for Discovery directive
scope = directive.get('payload', {}).get('scope')
assert scope is not None, 'Malformatted request - missing endpoint.scope'
assert scope.get('type') == 'BearerToken', 'Only support BearerToken'
token = scope.get('token')
if token is None and _debug:
token = os.environ.get('LONG_LIVED_ACCESS_TOKEN') # only for debug purpose
verify_ssl = not bool(os.environ.get('NOT_VERIFY_SSL'))
http = urllib3.PoolManager(
cert_reqs='CERT_REQUIRED' if verify_ssl else 'CERT_NONE',
timeout=urllib3.Timeout(connect=2.0, read=10.0)
)
response = http.request(
'POST',
'{}/api/alexa/smart_home'.format(base_url),
headers={
'Authorization': 'Bearer {}'.format(token),
'Content-Type': 'application/json',
},
body=json.dumps(event).encode('utf-8'),
)
if response.status >= 400:
return {
'event': {
'payload': {
'type': 'INVALID_AUTHORIZATION_CREDENTIAL'
if response.status in (401, 403) else 'INTERNAL_ERROR',
'message': response.data.decode("utf-8"),
}
}
}
_logger.debug('Response: %s', response.data.decode("utf-8"))
return json.loads(response.data.decode('utf-8'))
@Baz-ace
Copy link

Baz-ace commented Feb 14, 2023

help
I followed Lewis's video which was fantastic, I got as far as testing the link to my Home Assistant instance with worked showing my equipment. Unfortunately my SD card crashed and its took me 11days to resurrect my Home Assistant instance, anyway I tried to recover the AWS, Alexa and Home Assistant links. Anyway I get all the way through until I test the setup and then I get an error relating to config START RequestId: 5e47a47a-1306-4dbc-9656-7672bc89a22b Version: $LATEST
[ERROR] AssertionError: Only support payloadVersion == 3
Traceback (most recent call last):
  File "/var/task/lambda_function.py", line 36, in lambda_handler
    'Only support payloadVersion == 3'
END RequestId: 5e47a47a-1306-4dbc-9656-7672bc89a22b
REPORT RequestId: 5e47a47a-1306-4dbc-9656-7672bc89a22b Duration: 20.64 ms Billed Duration: 21 ms Memory Size: 128 MB Max Memory Used: 38 MB Init Duration: 145.78 ms
I am completely lost as to what to do, I tried deleting all from AWS Function and Role redoing from scratch, this made no difference I got the same error.
Bazza

@hs6666
Copy link

hs6666 commented Mar 5, 2023

hmmm .. when adding default endpoint in alexa developer console getting following, any ideas please? ..... Please make sure that "Alexa Smart Home" is selected for the event source type, for provided arn [Invalid value] : arn:aws:lambda:eu-west-1:xxxxxxxxxxxxxx:function:HomeAssistant

thanks

@Baz-ace
Copy link

Baz-ace commented Mar 5, 2023

Make sure your using the region Ireland, there’s only a few of the available options work.

@hs6666
Copy link

hs6666 commented Mar 6, 2023

Hi, I think I've chosen the right regions, I've attached the screenshots from my aws and developer consoles.... thanks

Capture

Capture2

@Baz-ace
Copy link

Baz-ace commented Mar 6, 2023

Sorry I’m not the right person to help you, I’m also completely stuck, the region is one area I went astray on!

@cannock607
Copy link

I'm also stuck on this same point. None of the suggested regions appear to have the "smart home" skill. Everything else works and the lambda code can interact with home assistant when I run a test. I just can't get the Alexa skill to integrate with the lambda code. Has anybody figured this out?

@Baz-ace
Copy link

Baz-ace commented Mar 20, 2023

I'm also stuck on this same point. None of the suggested regions appear to have the "smart home" skill. Everything else works and the lambda code can interact with home assistant when I run a test. I just can't get the Alexa skill to integrate with the lambda code. Has anybody figured this out?

Make sure you use the Ireland region!

@cannock607
Copy link

I'm also stuck on this same point. None of the suggested regions appear to have the "smart home" skill. Everything else works and the lambda code can interact with home assistant when I run a test. I just can't get the Alexa skill to integrate with the lambda code. Has anybody figured this out?

Make sure you use the Ireland region!

Thanks for your reply. I have tried all suggested regions. I've attached a screenshot of the Ireland region as an example. The only available trigger is the "Alexa" trigger. Neither the "Alexa Smart Home" or "Alexa Skill Kit" options appear. It is like this for all suggested regions.
Screenshot 2023-03-20 at 8 06 58 AM

@nonkronk
Copy link

nonkronk commented Apr 1, 2023

For those who are using Cloudflare's free tier behind your IP and are having difficulty getting it to work, try disabling Bot Fight Mode.
I know it's not ideal, but it's the only way I could get it to work for my setup. Maybe anyone have any suggestions?

image

@Crayzme
Copy link

Crayzme commented Apr 1, 2023

What would it cost to get one of you to remote build me a virtualbox for this? I can’t use HA cloud

@cannock607
Copy link

I'm also stuck on this same point. None of the suggested regions appear to have the "smart home" skill. Everything else works and the lambda code can interact with home assistant when I run a test. I just can't get the Alexa skill to integrate with the lambda code. Has anybody figured this out?

Make sure you use the Ireland region!

Thanks for your reply. I have tried all suggested regions. I've attached a screenshot of the Ireland region as an example. The only available trigger is the "Alexa" trigger. Neither the "Alexa Smart Home" or "Alexa Skill Kit" options appear. It is like this for all suggested regions. Screenshot 2023-03-20 at 8 06 58 AM

I don't know why it was not doing this when I first posted, but selecting the Alexa iot skill in a supported region now gives me additional options. After selecting the skill, I can select between Alexa Smart Home or Alexa Skill Kit. Selecting Alexa Smart Home seems to do the trick.

@quietsnow
Copy link

I'm at a loss. This has worked - and not worked - and worked - etc - according to the results of the Lambda tests. I have fiddled with so many things and it seems completely random when it works and not. I rarely get two successful tests in a row (but I did a couple times). When it doesn't work, I typically get "Task timed out after 3.01 seconds". When it does work, I've been able to see the devices/entities I expose in the configuration.yaml file in the logging details, so I know it's communicating w/ my HA. Yet, no matter the situation, Alexa never discovers these exposed entities.

I'm using CloudFlare's Free Tunnel and have allowed WAF (Web Application Firewall) rules that allow unfettered communication between about a dozen Amazon ASNs as well as any traffic originating from HA.

@ben-kenney
Copy link

ben-kenney commented Jun 2, 2023

I've also setup my HA using cloudflare. This integration was working very well for me for a while but has recently stopped working, complaining about an SSL error.

The lambda test result now gives me this:

"errorMessage": "HTTPSConnectionPool(host='MY.HA.URL', port=443): Max retries exceeded with url: /api/alexa/smart_home (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1091)')))",

@ballakers
Copy link

For those who are using Cloudflare's free tier behind your IP and are having difficulty getting it to work, try disabling Bot Fight Mode. I know it's not ideal, but it's the only way I could get it to work for my setup. Maybe anyone have any suggestions?

image

I found I had to do this a few months back when I changed ISP providers and I was messing with CloudFlare settings at the same time. Think this could be many peoples problem but maybe that setting is off as default and I turned it on myself!?

@ElVit
Copy link

ElVit commented Nov 17, 2023

For those who are using Cloudflare's free tier behind your IP and are having difficulty getting it to work, try disabling Bot Fight Mode. I know it's not ideal, but it's the only way I could get it to work for my setup. Maybe anyone have any suggestions?
image

I found I had to do this a few months back when I changed ISP providers and I was messing with CloudFlare settings at the same time. Think this could be many peoples problem but maybe that setting is off as default and I turned it on myself!?

Thanks for the hint. Same problem here.
But instead of "Bot Fight Mode" I had enabled a "WAF" (Web Application Firewall).
I had blocked all connections instead of Germany.
But since the Alexa requests are coming from Ireland they were always blocked.
Now after adding Irland to my white list it’s working again 😃.

@enspiro
Copy link

enspiro commented Nov 20, 2023

i am trying to add a user agent in the post request. For that i have added this below.
Unfortunately it is not working as expected.

User_Agent = "python-urllib3/1.26.9"

http = urllib3.PoolManager(
    cert_reqs='CERT_REQUIRED' if verify_ssl else 'CERT_NONE',
    timeout=urllib3.Timeout(connect=2.0, read=10.0)
)

response = http.request(
    'POST', 
    '{}/api/alexa/smart_home'.format(base_url),
    headers={
        'Authorization': 'Bearer {}'.format(token),
        'Content-Type': 'application/json',
        'User-Agent': User_Agent
    },
    body=json.dumps(event).encode('utf-8'),
)

@stalsma
Copy link

stalsma commented Nov 21, 2023

I've been fighting this for what seems like two days now. Lots of good comments here, that pointed me in the right direction. Figured I would document what worked for me, in hopes that it helps someone save some time.

Specifically, I'm running a Docker instance on Synology, and my only public IP is IP6. Amazon servers will only resolve IP4, so I need a IP4->IP6 proxy. This is pretty simple (festeip.de, MyOnlinePortal.net) both work, but they provide a port that is NOT 443 for the proxy address. Everything worked until account linking, which always failed after logging in to HASS. As noted above, the connections for Alexa account linking have to be made over SSL AND over port 443.

I finally got the Alexa account to link by:

  1. setup a cloudflare tunnel (gives me the public IP4 addr) that
  2. tunnels to a CNAME entry maintained by DDNS (Synology's built-in service supports IP6)
  3. CNAME IP resolves to my NAS IP directly (router forwards port 443 is to NAS)
  4. A reverse proxy (Synology Login Portal) translates the incoming address (https://my-cloudflare-domain:443) to http://localhost:8123.

@trilu2000
Copy link

Many thanks for the script, it is working for me since a while now without any issues.
Would it be possible to use the share for Assist information and also the alias settings from there in the future?

@rohrbrecher
Copy link

So I had similar issues like so many people here and also in some forums around the web. My internet provider at home only assigns a public IPv6 to me and so everything I tried using reverse proxy and other services like fest-ip.net didn't work.
I found a user in a german forum (heimnetz.de) who helped me to analyse the problem for over 3 days and we ultimately found a solution which works pretty nicely for me and which might also be helpful for others.

To solve the need for a public IPv4 I rented the cheapest vServer I found, which I got from Ionos and which costs 1€/month.
This server has both a public IPv4 (to communicate with AWS) as well as a publich IPv6 (to communicate with me).
I installed a tool called 6tunnel, which is super easy to configure: 6tunnel 443 public.IPv6.of.my.reverse.proxy 443
This tool now takes all the requests sent to it on port 443 and forwards them to my reverse proxy on the same port.
My reverse proxy takes the request and forwards it to my HomeAssistant at port 8123, so I didn't even have to change that.

And it works! I know that this is a similar solution like proposed by stalsma before me but with this vServer I even have the flexibility to host my reverse proxy there, if I'd want to.

@dlangamer
Copy link

after following the instructions to a letter, my issue was in cloudflare (its always dns isnt it?). i had "bot fight mode" (Security->Bots) turned on. and it was blocking any attempt at connecting the app, as well as the lambda test. as a work around under WAF -> Tools, i added AS16509 (make sure to hit enter after you type it in) as a ip access rule to allow access. seems to work, but be kinda hacky.

Man, you killed the riddle!

I had the same problem and was having trouble for over 2 weeks. It was Bot Fight and WAF. All I had to do was add the rule in waf tools releasing the Amazon ASN and everything went well. Thank you very much. Everything working perfectly. For those who don't know how to identify the ASN that is trying to connect to the Amazon instance, just go to the CloudFlare panel > Security > Events and check the connection attempts to your instance.

God bless you! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment