Home Assistant Alexa โ€” Layer 2: AWS Lambda Custom Skill

๐Ÿ“… April 2026โฑ๏ธ 12 min read๐Ÿท๏ธ Home Assistant ยท Alexa ยท AWS Lambda

This is the complex layer โ€” and the one no guide explains fully. It enables "Alexa, ask house will the battery last tonight?" to be answered by a local AI on your Raspberry Pi. Lambda receives the spoken query, passes it to Home Assistant in under 1 second, and says "Checking with the house" while your local Ollama model works out the answer.

Why this approach?

Lambda is the relay โ€” it does almost nothing itself. It receives the Alexa query, sets an input_text entity in HA via the API, and returns immediately. All the intelligence happens locally on the Pi. Lambda never sees any home data โ€” it only passes a query string through.

Step 1 โ€” Create a HA Long-Lived Access Token

Lambda needs to call the HA API. Create a dedicated token:

  1. HA โ†’ Profile โ†’ Security (click your username bottom-left)
  2. Scroll to Long-Lived Access Tokens โ†’ Create Token
  3. Name: "Alexa Skill"
  4. Copy the token โ€” you can only see it once

Step 2 โ€” Create the AWS Lambda Function

โš ๏ธ Critical: Region must be eu-west-1 (Ireland)
eu-north-1 (Stockholm) silently fails โ€” Lambda is created but Alexa's endpoint verification cannot reach it. You get no error message; queries simply never arrive at your Lambda. This cost hours of debugging.
  1. Go to https://console.aws.amazon.com โ†’ sign in (free account works)
  2. Top-right region dropdown โ†’ select Europe (Ireland) โ€” eu-west-1
  3. Search for Lambda โ†’ click Lambda
  4. Click Create function โ†’ Author from scratch
  5. Fill in:
    • Function name: alexa-house-manager
    • Runtime: Python 3.12
    • Architecture: arm64 (20% cheaper than x86_64)
  6. Click Create function

Configure the function

  1. Configuration โ†’ General configuration โ†’ Edit โ†’ set Timeout to 10 seconds (default 3s is too short)
  2. Configuration โ†’ Environment variables โ†’ Edit โ†’ add:
    • HA_URL = https://XXXXXXXX.ui.nabu.casa (no trailing slash)
    • HA_TOKEN = the token from Step 1
  3. Click the Code tab โ†’ delete all existing code โ†’ paste the Lambda function (see below)
  4. Click Deploy
  5. Copy the Lambda ARN from the top of the page

The Lambda Function (Python 3.12)

The full function is surprisingly short. Lambda's only job is to receive the spoken query and set input_text.alexa_query in HA. It appends a timestamp nonce so repeated identical queries still trigger a state change in HA.

import json, os, time, urllib.request, urllib.error

HA_URL = os.environ.get("HA_URL")
HA_TOKEN = os.environ.get("HA_TOKEN")

CHECKING_MESSAGE = "Checking with the house. I'll announce the result shortly."
ERROR_MESSAGE = "Sorry, I could not reach Home Assistant right now."

def build_response(output_speech, should_end_session=True, reprompt=None):
    response = {"version":"1.0","response":{"outputSpeech":{"type":"PlainText","text":output_speech},"shouldEndSession":should_end_session}}
    if reprompt:
        response["response"]["reprompt"] = {"outputSpeech":{"type":"PlainText","text":reprompt}}
    return response

def fire_query_to_ha(query):
    # Append timestamp nonce โ€” forces state change even for repeated identical queries
    value = query + "|" + str(int(time.time()))
    url = HA_URL + "/api/services/input_text/set_value"
    payload = {"entity_id":"input_text.alexa_query","value":value}
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(url, data=data,
        headers={"Authorization":"Bearer "+HA_TOKEN,"Content-Type":"application/json"}, method="POST")
    try:
        with urllib.request.urlopen(req, timeout=5) as resp:
            return resp.status == 200
    except Exception as e:
        print("fire_query_to_ha failed: " + str(e))
        return False

def handle_query_intent(event):
    slots = event.get("request",{}).get("intent",{}).get("slots",{})
    query = slots.get("query",{}).get("value","").strip()
    if not query:
        return build_response("I did not catch that.", should_end_session=False)
    success = fire_query_to_ha(query)
    return build_response(CHECKING_MESSAGE) if success else build_response(ERROR_MESSAGE)

def lambda_handler(event, context):
    print(json.dumps(event))  # CloudWatch logging for debugging
    request_type = event.get("request",{}).get("type","")
    if request_type == "LaunchRequest":
        return build_response("House manager ready. What would you like to know?", False)
    if request_type == "SessionEndedRequest":
        return {}
    if request_type == "IntentRequest":
        intent_name = event.get("request",{}).get("intent",{}).get("name","")
        if intent_name in ("QueryIntent","AMAZON.FallbackIntent"):
            return handle_query_intent(event)
        if intent_name in ("AMAZON.StopIntent","AMAZON.CancelIntent"):
            return build_response("Goodbye.")
    return build_response("I'm not sure how to handle that.")

Step 3 โ€” Create the Alexa Custom Skill

  1. Go to https://developer.amazon.com/alexa/console/ask
  2. Sign in โ†’ Create Skill
  3. Skill name: House Manager
  4. Experience type: Other โ†’ Custom
  5. Hosting services: Provision your own โ† critical โ€” NOT "Alexa-hosted"
  6. Template: Start from scratch โ†’ Create skill
โš ๏ธ Wrong option shows Amazon's ARN:
If the Endpoint page shows arn:aws:lambda:us-east-1:797176512873:..., you accidentally selected "Alexa-hosted". Delete the skill and recreate with Provision your own.

Configure invocation name

Build tab โ†’ Invocation โ†’ Skill invocation name: house

Users say: "Alexa, ask house..." or "Alexa, open house"

Set the endpoint

Build tab โ†’ Endpoint โ†’ AWS Lambda ARN โ†’ paste your Lambda ARN in Default Region โ†’ Save Endpoints

Add the Alexa trigger to Lambda

Back in AWS Lambda โ†’ + Add trigger โ†’ Alexa Skills Kit โ†’ paste your Skill ID โ†’ Add

The Skill ID is shown in the Alexa Developer Console URL or under Build โ†’ Invocation. Format: amzn1.ask.skill.XXXXXXXX

Load the interaction model

Build tab โ†’ JSON Editor โ†’ paste the full interaction_model.json โ†’ Save Model โ†’ Build Model (30-60 seconds)

Deploy and test

Click Deploy โ†’ Enable the skill in Alexa app โ†’ say "Alexa, ask house what is the battery level"

Echo should respond: "Checking with the house. I'll announce the result shortly."

The spoken answer comes from Layer 3 (Ollama + NR + HA scripts) โ€” set that up next.

Key Debugging Tools

Lambda working? Now add the local AI.

Layer 3: Ollama + Node-RED โ†’