Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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'))
@uturnr
Copy link

uturnr commented Apr 18, 2021

For me the 404s went away after I made sure I had at least the following in configuration.yaml:

alexa:
  smart_home:

And then I restarted home assistant. If you navigate directly to this URL in your browser: https://<YOUR-HA-URL:YOUR-PORT>/api/alexa/smart_home you should see 405: Method Not Allowed rather than 404: Not Found

edit: That being said I have not been able to successfully discover devices with Alexa/HA (although testing the Lambda code is successful)

@zoltan4929
Copy link

zoltan4929 commented May 20, 2021

In my case, the two programs cannot be paired. When logging in to HomeAssistant, print: invalid client id or redirect uri

@sampathkumarspace
Copy link

sampathkumarspace commented May 27, 2021

Everything is working , even i linked my dev skills account still my devices is not listing in "Alexa Discovery Devices" but i am getting response in code test

Test Event Name
Discovery

Response
{
"event": {
"header": {
"payloadVersion": "3",
"namespace": "Alexa.Discovery",
"name": "Discover.Response",
"messageId": "673f6b16-44f1-4e56-aa09-e6b1fb192213"
},
"payload": {
"endpoints": [
{
"additionalAttributes": {
"customIdentifier": "-switch.relay",
"model": "switch",
"softwareVersion": "2021.5.5",
"manufacturer": "Home Assistant"
},
"manufacturerName": "Home Assistant",
"description": "switch.relay via Home Assistant",
"capabilities": [
{
"interface": "Alexa.PowerController",
"version": "3",
"type": "AlexaInterface",
"properties": {
"retrievable": true,
"supported": [
{
"name": "powerState"
}
],
"proactivelyReported": true
}
},
{
"interface": "Alexa.EndpointHealth",
"version": "3",
"type": "AlexaInterface",
"properties": {
"retrievable": true,
"supported": [
{
"name": "connectivity"
}
],
"proactivelyReported": true
}
},
{
"interface": "Alexa",
"version": "3",
"type": "AlexaInterface"
}
],
"cookie": {},
"endpointId": "switch#relay",
"friendlyName": "Relay",
"displayCategories": [
"SWITCH"
]
},
{
"additionalAttributes": {
"customIdentifier": "-switch.relay_2",
"model": "switch",
"softwareVersion": "2021.5.5",
"manufacturer": "Home Assistant"
},
"manufacturerName": "Home Assistant",
"description": "switch.relay_2 via Home Assistant",
"capabilities": [
{
"interface": "Alexa.PowerController",
"version": "3",
"type": "AlexaInterface",
"properties": {
"retrievable": true,
"supported": [
{
"name": "powerState"
}
],
"proactivelyReported": true
}
},
{
"interface": "Alexa.EndpointHealth",
"version": "3",
"type": "AlexaInterface",
"properties": {
"retrievable": true,
"supported": [
{
"name": "connectivity"
}
],
"proactivelyReported": true
}
},
{
"interface": "Alexa",
"version": "3",
"type": "AlexaInterface"
}
],
"cookie": {},
"endpointId": "switch#relay_2",
"friendlyName": "Relay",
"displayCategories": [
"SWITCH"
]
}
]
}
}
}

@blueal
Copy link

blueal commented May 29, 2021

For me the 404s went away after I made sure I had at least the following in configuration.yaml:

alexa:
  smart_home:

And then I restarted home assistant. If you navigate directly to this URL in your browser: https://<YOUR-HA-URL:YOUR-PORT>/api/alexa/smart_home you should see 405: Method Not Allowed rather than 404: Not Found

edit: That being said I have not been able to successfully discover devices with Alexa/HA (although testing the Lambda code is successful)

Thanks so much for the help! It turns out I didn't update the config file properly, and I had a trailing "/" in the BASE_URL.

However, the Alexa app is failing to discover any new devices despite them being discovered in the lambda function.

Edit: Figured it out. The AWS Regions for the Lambda function are VERY sensitive. The US West Oregon region may support English, but it's only Canadian English. All English (US) users need to use us-east-1 (N. Virginia).

@JorgeAnzola
Copy link

JorgeAnzola commented Jul 16, 2021

For me the problem was also a trailing slash at the end

@orrious
Copy link

orrious commented Jul 21, 2021

Is there a way to debug Account Linking? When I "Enable to Use" from Alexa App -> Skills it redirects to my Home Assistant authentication portal. I successfully authenticate to Home Assistant and then receive "Unable to link "MySkill Name" with Alexa. Test Discovery messages work. I have been using the haaska lambda code for over 2 years and trying to migrate to this integration. Btw, I'm not just replacing the lambda code in the haaska skill. I have created a new skill for this with its own lambda instance and configured it exactly as instructions provided in https://www.home-assistant.io/integrations/alexa.smart_home

@Pandafuchs
Copy link

Pandafuchs commented Sep 1, 2021

Is there any way to make this script compatible with an IPV6 address? A lot of people do not get their own IPV4 adress anymore nowadays. If I just try an IPV6 adress behind a hostname I get:

Failed to establish a new connection: [Errno -5] No address associated with hostname'))",

@albertoxamin
Copy link

albertoxamin commented Oct 20, 2021

@orrious have you managed to fix account linking?

@jjmerri
Copy link

jjmerri commented Nov 3, 2021

Can you update this to add base_url = base_url.strip("/") after you assert base_url is not None? It will prevent people in the future from getting errors when they accidentally add a trailing / to their HA URL.

My forked gist has the change if you want to take a look.

@matt2005
Copy link
Author

matt2005 commented Nov 4, 2021

@jjmerri added suggested line

@Kieffer87
Copy link

Kieffer87 commented Dec 16, 2021

@orrious or @albertoxamin Curious if you fixed the account linking issue? I'm able to execute my lambda and discover all my devices but I can't get my account to properly link.

@albertoxamin
Copy link

albertoxamin commented Dec 16, 2021

@Kieffer87 no but I managed to use https://github.com/mike-grant/haaska instead

@orrious
Copy link

orrious commented Dec 16, 2021

No, I'm still using haasks. Discord user thefunkygibbon DMed me and said his HA was mapped externally to port 8213. He said he remapped it to port 443 and the account linking worked. I have not had time to look back into it, however it's potentially viable as my HA is externally running on 8123 as well.

If you're in the middle of testing, change the external port to 443 and let us know what happens

@Kieffer87
Copy link

Kieffer87 commented Dec 16, 2021

I'm currently running on port 443, though HA sits behind NGINX so I'm sure that adds some complexity to it. I'll play around and keep you all updated if I get it working.

EDIT:
I can see the authorize call in the NGINX logs, but nothing after that... According to https://developers.home-assistant.io/docs/auth_api/ the next call should be to /auth/token which I don't see. I'll have to figure out where to turn next to view logs, doesn't look like the Alexa developer portal exposes anything like that.

@williamokano-dh
Copy link

williamokano-dh commented Dec 19, 2021

Yeah, I'm not able to login as well, I'm getting invalid client id or redirect uri

@edit: Found my issue. Somehow, I defined my client id as layla.amazon.com since I'm based on Europe, but when I login, the redirect URI is set to pitangui.amazon.com. Didn't try to understand why, just changed the client id to pitangui and the login worked.

I'm struggling with device discovery now 😂

@crookedview
Copy link

crookedview commented Dec 23, 2021

After changing my Home Assistant server port to 443 instead of 8123, I'm now also experiencing the
We were unable to link Home Assistant at this time
problem after disabling the Alexa skill and trying to re-link accounts.

I'm able to hit the Home Assistant login page when clicking the 'Enable' button, but after entering credentials I get the 'unable to link' error. I don't see any errors with the alexa component logging level set to debug.

Edit: I just ended up having to re-create the Alexa skill, despite updating URLS in the console.

@dpinkard
Copy link

dpinkard commented Jan 6, 2022

I believe it's just the processing of 'Alexa.Authorization' 'AcceptGrant' but haven't found an implementation that makes the Skill's callback happy.

@matt2005
Copy link
Author

matt2005 commented Jan 6, 2022

As per Alexa account linking requirements

Protocol – It must be accessible over HTTPS on port 443.

@r3pek
Copy link

r3pek commented Jan 7, 2022

Hi there @matt2005 !
Thanks for your lambda code. One question: Is this compatible with using "Custom" integration for alexa skills?
I use it for the "Smart Home" integration and it works like a charm (finds devices and all), but now, I'm trying to integrate intent_scripts and for that I need the "Custom" integration, but when it calls the lambda code, I get this on the logs:

[ERROR] AssertionError: Malformatted request - missing directive
Traceback (most recent call last):
  File "/var/task/lambda_function.py", line 37, in lambda_handler
    assert directive is not None, 'Malformatted request - missing directive'

Maybe this requests don't work then same way?

@Kieffer87
Copy link

Kieffer87 commented Jan 7, 2022

@dpinkard I'm not so sure that's the case (at least for me). After I login to HA, I can see the code and state being sent back to the Amazon redirect URL (https://pitangui.amazon.com/api/skill/link/M88T1IQQREUL9), however the next call should be to get a token, which I never see happening. I see no invocation attempts of my lambda, no logs in HA nor any tokens created in HA.

I did remove NGINX from the mix just to confirm that wasn't the issue. The last step for troubleshooting will be to remove Cloudflare tunnel and test with HA directly forward on my firewall.

@LinkXX92
Copy link

LinkXX92 commented Jan 13, 2022

IMG_20220109_152325
Hello,

When I make a lambda test. Appear this error. Anybody have solutions?

Thanks

{
  "event": {
    "payload": {
      "type": "INTERNAL_ERROR",
      "message": "404: Not Found"
    }
  }
}

@Kieffer87
Copy link

Kieffer87 commented Jan 13, 2022

@LinkXX92 check your base_url and that you've enabled the api endpoint in the config. A 404 message means the endpoint/url you requested doesn't exist.

@stomko11
Copy link

stomko11 commented Feb 14, 2022

Getting the same as @LinkXX92
However in my case it might be related to home-assistant/core#65917

@Quixote1
Copy link

Quixote1 commented Feb 28, 2022

I'm seeing some strange behavior after what I thought was a successful install.
I can see all of my Home Assistant devices in the Alexa Android app and I even created a binary sensor that reflects the state of an input boolean toggle which I can see changing states real-time in the app when I manually switch it in Lovelace (with a slight delay, of course). However, I cannot trigger a routine based on that binary sensor even though it is seen in the app as a door and presents itself as a trigger option for routines.
Am I missing something obvious here? Thanks in advance!

@backslashV
Copy link

backslashV commented Mar 5, 2022

Is it possible to query an Amazon skill and get the response in HA?

@Quixote1
Copy link

Quixote1 commented Mar 5, 2022

From my understanding, the only way to use any skills with HA aside from TTS is to use routines triggered by binary sensors with the type door, window, garage door, and opening. I haven't had any success using this method, but others claim they have.
I'm not really adept at this sort of thing and I only followed a tutorial on Youtube, so I'm sure there is someone here with a better undertsanding that can give you a better answer.

@DennisGaida
Copy link

DennisGaida commented Mar 9, 2022

To actually understand what is happening, I am missing debug output for the response, I added the following line just before the last line:

_logger.debug('Response: %s', response.data.decode("utf-8"))

What is missing from the documentation is that when you delete the LONG_LIVED_ACCESS_TOKEN you can't test anymore via AWS Lambda test feature because it relies on that token.

@matt2005
Copy link
Author

matt2005 commented May 25, 2022

@DennisGaida Thanks, I've added the debug line to the code.

@rmarcoccio
Copy link

rmarcoccio commented Jun 16, 2022

After changing my Home Assistant server port to 443 instead of 8123, I'm now also experiencing the We were unable to link Home Assistant at this time problem after disabling the Alexa skill and trying to re-link accounts.

I'm able to hit the Home Assistant login page when clicking the 'Enable' button, but after entering credentials I get the 'unable to link' error. I don't see any errors with the alexa component logging level set to debug.

Edit: I just ended up having to re-create the Alexa skill, despite updating URLS in the console.

I'm experiencing the same. I just tried several ways and I noticed either via direct to HA or via LWA and in both cases the skill is enabled ONLY if the "Send Alexa messaging" in the Permissions tab is NEVER enabled. Whenever you enable that the linking of the skills FAILS. I guess has to be something regarding the OAuth 2.0 protocol I googled a lot but I m not familiar with this kind of programming and debugging and I m still jammed.

I m using HA Core on docker and ssl support via Caddy and noip.com (for DDDNS) . I m not sure if Caddy support to OAuth2 could be the key .........anyone has any idea ?

PLEASE HELP

Otherwise I've to switch to activate routines via Alex Media Player...............

EDIT: The same behaviour either with original haaska script or matt2005/lambda_function.py . That's something before the use of the lambda functions strictly depending by account linking process............is my understanding

@GamingMaker
Copy link

GamingMaker commented Aug 8, 2022

Hi Guys, i have a little Problem.
I have a DS-Lite Connection at home so i'm using AAAA Records to remote-Access my Home-Assistant via IPV6.
But unfortunately this Code doesn't work for me for some reasons... I can normally access my HomeAssistant and i even get a "405" when entering https://YOUR-HA-URL:YOUR-PORT/api/alexa/smart_home , wherever i am. But why can't the Lambda function conntect to my HA... error message as follows:
error msg

I hope someone can help me... don't know what to do.. or is the Lambda Function just unable to do ipv6 connections?
Thanks in advance!

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