Como integrar a Alexa no Home Assistant do jeito "raiz" (Skill + Lambda + IAM + Alexa Media Player)

Como integrar a Alexa no Home Assistant do jeito "raiz" (Skill + Lambda + IAM + Alexa Media Player)
Tempo de leitura: 13 min de leitura
Link copiado!

Integrar Alexa no Home Assistant pelo caminho “oficial e completo” é aquele tipo de coisa que funciona muito bem… depois que você apanha o suficiente.

Aqui eu vou deixar o passo a passo inteiro, do zero, com a parte chata (Skill, Lambda, IAM, região certa), a parte crítica (HTTPS/443 e account linking) e a parte divertida (Alexa Media Player pra falar coisas e fazer anúncio nos Echos).

Isso aqui é o caminho “Smart Home Skill” do Home Assistant. Ele é mais trabalhoso, mas é o que dá a experiência de casa inteligente “de verdade”, tipo “Alexa, apaga a luz” sem precisar falar nome de skill. A doc oficial do HA descreve esse fluxo e os requisitos, inclusive a exigência de HTTPS/443 e certificado confiável.


Pré-requisitos que você precisa ter resolvido antes

Seu Home Assistant precisa estar acessível pela internet com HTTPS, usando um certificado válido (Let’s Encrypt vale) e, idealmente, na porta 443. Certificado autoassinado não funciona pra account linking.

Isso significa uma dessas opções (a que você preferir):

Eu não vou entrar fundo nesse pedaço aqui, porque cada infra é um universo, mas guarda essa regra: account linking da Alexa tende a implicar quando foge do “https válido e porta 443”. A própria doc do HA bate muito nessa tecla.


Passo 1. Criar a Smart Home Skill na Alexa Developer Console
  1. Entra na Alexa Developer Console e cria uma skill nova.
  2. Escolhe o tipo “Smart Home” e “Provision your own”.
  3. Confere se está em payload v3 e anota o Skill ID porque você vai usar no Lambda.

Usa a mesma conta Amazon do teu app Alexa e dos teus Echos, senão você cria uma skill linda que nunca vai aparecer onde importa.


Passo 2. Criar a Role no IAM (pra Lambda poder rodar)

No AWS Console IAM, cria uma Role para serviço “Lambda”. Anexa a policy AWSLambdaBasicExecutionRole.

É isso. Não inventa muito aqui.

O Home Assistant descreve exatamente esse caminho, inclusive citando a policy.


Passo 3. Criar a função Lambda (e escolher a região certa)

Aqui tem uma pegadinha que derruba muita gente: a região da Lambda precisa ser compatível com a região/locale da skill, não com onde você mora.

Pra pt-BR, a doc do Home Assistant recomenda US East (N. Virginia). Se você criar a Lambda em outra região, você pode até “concluir” o setup e mesmo assim a skill não funcionar sem erro óbvio.

Então:

  1. No AWS Lambda Console, Create function -> Author from scratch
  2. Runtime: Python 3.x (pega a mais recente disponível)
  3. Execution role: usa a role que você criou no passo anterior
  4. Add trigger: “Alexa Smart Home”
  5. Cola o Skill ID aqui

Passo 4. Código do Lambda (o proxy que joga pra /api/alexa/smart_home)

No fundo esse Lambda só faz uma coisa que é receber o que a Alexa manda e jogar direto no /api/alexa/smart_home do Home Assistant. Não tem muita mágica aqui.

A doc do Home Assistant manda copiar um script Python de um gist específico (ele já vem adaptado pra alguns cenários, inclusive proactive events).

Eu vou deixar aqui uma versão “bonita” e formatada do lambda_function.py (baseada nesse gist) e que funciona bem na prática:

"""
Alexa Smart Home Skill Adapter for Home Assistant
Based on the community gist referenced by Home Assistant documentation.

Env vars:
- BASE_URL (required): https://seu-dominio (sem barra final)
- NOT_VERIFY_SSL (optional): True para ignorar problemas de SSL (nao recomendo)
- DEBUG (optional): True para logs mais verbosos e permitir fallback de token
- LONG_LIVED_ACCESS_TOKEN (optional): usar so para debug/teste
"""
import json
import logging
import os
from typing import Any, Dict

import urllib3

_debug = bool(os.environ.get("DEBUG"))

_logger = logging.getLogger("HomeAssistant-SmartHome")
_logger.setLevel(logging.DEBUG if _debug else logging.INFO)

# evita log gigante do urllib3
logging.getLogger("urllib3").setLevel(logging.INFO)


def _mk_error(error_type: str, message: str) -> Dict[str, Any]:
    return {
        "event": {
            "payload": {
                "type": error_type,
                "message": message,
            }
        }
    }


def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
    _logger.info("Processing Alexa request")

    base_url = os.environ.get("BASE_URL")
    if not base_url:
        _logger.error("BASE_URL environment variable not set")
        return _mk_error("INVALID_REQUEST", "BASE_URL environment variable must be set")

    base_url = base_url.rstrip("/")

    directive = event.get("directive")
    if not directive:
        return _mk_error("INVALID_REQUEST", "Request missing directive")

    payload_version = directive.get("header", {}).get("payloadVersion")
    if payload_version != "3":
        return _mk_error("INVALID_REQUEST", f"Only payloadVersion 3 is supported, got {payload_version}")

    # o token pode estar em lugares diferentes dependendo do tipo de directive
    scope = (
        directive.get("endpoint", {}).get("scope")
        or directive.get("payload", {}).get("grantee")
        or directive.get("payload", {}).get("scope")
    )

    if not scope or scope.get("type") != "BearerToken":
        return _mk_error("INVALID_REQUEST", "Request missing BearerToken scope")

    token = scope.get("token")

    # se estiver debug, permite fallback via LONG_LIVED_ACCESS_TOKEN
    if not token and _debug:
        token = os.environ.get("LONG_LIVED_ACCESS_TOKEN")

    if not token:
        return _mk_error("INVALID_AUTHORIZATION_CREDENTIAL", "Authentication token is required")

    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),
    )

    try:
        resp = http.request(
            "POST",
            f"{base_url}/api/alexa/smart_home",
            headers={
                "Authorization": f"Bearer {token}",
                "Content-Type": "application/json",
            },
            body=json.dumps(event).encode("utf-8"),
        )

        if resp.status >= 400:
            body = resp.data.decode("utf-8")
            _logger.error("Home Assistant returned %s: %s", resp.status, body)

            error_type = "INVALID_AUTHORIZATION_CREDENTIAL" if resp.status in (401, 403) else "INTERNAL_ERROR"
            return _mk_error(error_type, body)

        return json.loads(resp.data.decode("utf-8"))

    except Exception as e:
        _logger.exception("Unexpected error: %s", str(e))
        return _mk_error("INTERNAL_ERROR", "An unexpected error occurred")

Depois de colar esse código, clica em Deploy.

Passo 5. Variáveis de ambiente do Lambda (não pula isso)

Ainda no Lambda, vai em Configuration -> Environment variables e adiciona:

  • BASE_URL = a URL pública do seu HA, sem barra no final
    Exemplo: https://home.seudominio.com.br

E se precisar:

  • DEBUG = True (pra teste e logs)
  • LONG_LIVED_ACCESS_TOKEN = (apenas pra debug/teste, depois remove)
  • NOT_VERIFY_SSL = True (eu só usaria se você está testando algo temporário e sabe o risco)

A doc oficial do Home Assistant descreve essas variáveis e também avisa sobre o risco de deixar token ali dentro.

Passo 6. Configurar o endpoint da Smart Home Skill (ARN do Lambda)

Agora você volta na Alexa Developer Console: Build -> Smart Home -> Endpoint (Smart Home service endpoint)

E cola o ARN da sua função Lambda como Default endpoint.

A doc do HA descreve esse exato passo.

Passo 7. Account Linking (onde a maioria das pessoas tropeça)

Na Alexa Developer Console: Build -> Account Linking

Você vai configurar assim:

  • Authorization URI: https://SEU_HA/auth/authorize
  • Access Token URI: https://SEU_HA/auth/token

E lembra: se teu HA não está acessível “bonitinho” por HTTPS e preferencialmente 443, aqui vira uma fábrica de frustração.

A doc do HA também lista os Client IDs do “Login with Amazon” (pitangui/layla) e comenta que Let’s Encrypt é aceito.

Coisas importantes aqui:

  • Não habilita a opção de linking “from within your application”, porque isso te obriga a redirect URI que não funciona no fluxo do HA
  • Authentication Scheme: “Credentials in request body”
  • Scope: cria “smart_home”
Passo 8. Configuração no Home Assistant (configuration.yaml)

Agora sim, dentro do Home Assistant.

No configuration.yaml, adiciona no mínimo:

alexa:
  smart_home:
    locale: pt-BR

Se você fizer só isso, ele vai tentar expor tudo que for suportado, e isso geralmente é ruim porque a Alexa vira um lixão de entidade.

Então eu recomendo fortemente usar filter e ir incluindo aos poucos:

alexa:
  smart_home:
    locale: pt-BR
    filter:
      include_domains:
        - light
        - switch
        - cover
        - climate
      include_entity_globs:
        - binary_sensor.*_motion
      exclude_entities:
        - switch.alguma_coisa_que_voce_nao_quer

A doc do HA tem exemplos de filter e explica a ordem de aplicação dos includes/excludes.

Reinicia o Home Assistant.

Passo 9. Habilitar a skill no app Alexa e descobrir dispositivos

No app Alexa (ou app móvel): More -> Skills & Games -> Your Skills -> Dev

Habilita sua skill.

Ela vai abrir o login do Home Assistant (account linking) e, se tudo deu certo, a Alexa já começa discovery.

A doc do HA descreve esse fluxo e o “Discover devices” também.

Testando o Lambda antes de tudo (opcional, mas eu gosto)

A doc do HA sugere criar um evento de teste de Discovery no console do Lambda. Se você habilitou DEBUG e colocou LONG_LIVED_ACCESS_TOKEN, esse teste fica fácil.

Exemplo de payload de teste (Discovery) que a doc do HA usa:

{
  "directive": {
    "header": {
      "namespace": "Alexa.Discovery",
      "name": "Discover",
      "payloadVersion": "3",
      "messageId": "1bd5d003-31b9-476f-ad03-71d471922820"
    },
    "payload": {
      "scope": {
        "type": "BearerToken"
      }
    }
  }
}

Ela explica que sem token, o Lambda só vai funcionar se você estiver em DEBUG com LONG_LIVED_ACCESS_TOKEN configurado.


Expor scripts para Alexa (e fazer a Alexa “executar”)

O domínio script é suportado na integração Alexa Smart Home do HA.

Só que tem um detalhe: na Alexa, script geralmente aparece como um “dispositivo” acionável (tipo switch) ou pode virar ação em Rotinas dependendo do app e do tipo do device que a Alexa resolveu criar. Na prática, o que mais funciona sem dor é:

  • expor o script
  • colocar ele como ação numa Rotina (quando o app mostrar)
  • ou criar um input_boolean/switch e usar automação para disparar o script (isso quase sempre aparece bem na Alexa)

Eu uso muito o plano B, porque é previsível.

Criar um script no Home Assistant (exemplo real)

Exemplo de script que fala um status básico da casa. Eu vou manter ele simples, mas já dá pra ver a ideia.

Coloca em scripts.yaml (ou cria pelo UI e depois ajusta se quiser):

status_da_casa:
  alias: "Status da casa"
  sequence:
    - variables:
        lights_on: "{{ states.light | selectattr('state','eq','on') | list | length }}"
    - service: notify.alexa_media_echo_sala
      data:
        message: >
          {% if lights_on == 0 %}
            Todas as luzes estão apagadas.
          {% else %}
            Existem {{ lights_on }} luzes ligadas.
          {% endif %}
        data:
          type: announce

Observações importantes:

  • eu estou usando notify.alexa_media_echo_sala, que vem do Alexa Media Player (a gente instala daqui a pouco)
  • e estou usando announce pra tocar como anúncio
  • Se você tentou usar tts.google_translate_say e deu erro, isso é porque você não tem aquele serviço/config (e, sinceramente, com Alexa Media Player você nem precisa disso)
Garantir que o script aparece na Alexa (via filter)

No configuration.yaml, inclui esse script explicitamente pra não depender de “include_domains”:

alexa:
  smart_home:
    locale: pt-BR
    filter:
      include_entities:
        - script.status_da_casa

Reinicia o HA, e manda a Alexa “Discover devices”.

Plano B que sempre funciona: criar um “botão” (input_boolean) e uma automação

Cria um helper input_boolean:

input_boolean:
  acionar_status_da_casa:
    name: "Acionar Status da Casa"
    icon: mdi:home-analytics

Expõe esse input_boolean pra Alexa:

alexa:
  smart_home:
    locale: pt-BR
    filter:
      include_entities:
        - input_boolean.acionar_status_da_casa

E cria uma automação que, quando ligar, dispara o script e já desliga o boolean de volta:

automation:
  - alias: "Alexa - Rodar Status da Casa"
    trigger:
      - platform: state
        entity_id: input_boolean.acionar_status_da_casa
        to: "on"
    action:
      - service: script.status_da_casa
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.acionar_status_da_casa

Na Alexa, isso aparece como uma chave. Aí você consegue criar Rotina, comando de voz, o que quiser.

É feio? é. Mas é o feio que funciona.

Passo 10. Instalar Alexa Media Player (pra TTS, announce e controle de Echo)

Alexa Smart Home Skill é pra controlar entidades do Home Assistant.
Alexa Media Player é pra controlar os dispositivos Alexa dentro do Home Assistant, e principalmente pra fazer a Alexa falar coisas, tocar anúncio, etc.

Esse componente é um custom component (geralmente via HACS) e usa uma API não oficial, então tem aquela realidade: pode quebrar se a Amazon mudar algo. O próprio repositório deixa esse aviso claro.

A instalação recomendada é via HACS.

Instala via HACS (Easy Mode)
  1. Vai em Settings -> Devices & Services -> Add Integration -> Alexa Media Player
  2. Faz login e configura 2SV com Authenticator App Key (sim, isso é importante).

A wiki do projeto explica o fluxo do 2SV e como pegar o “Enter your key” (o segredo) na página de aprovação da Amazon.

Depois que configurar, você vai ganhar:

  • entidades media_player.echo_*
  • serviços notify.alexa_media_*
  • ações de speak/announce (dependendo da versão)
Exemplos prontos que eu uso muito com Alexa Media Player

Anunciar em um Echo específico

service: notify.alexa_media_echo_quarto_lara
data:
  message: "Lara, vem almoçar"
  data:
    type: announce

Falar como TTS normal (sem ser announce)

service: notify.alexa_media_echo_sala
data:
  message: "Boa noite. Luzes apagadas."
  data:
    type: tts

Anúncio em vários Echos (grupo)

Você pode chamar mais de um notify.* em sequência, ou criar um script “broadcast”:

script:
  anunciar_em_todos:
    alias: "Anunciar em todos os Echos"
    fields:
      msg:
        description: "Mensagem"
        example: "Teste"
    sequence:
      - service: notify.alexa_media_echo_sala
        data:
          message: "{{ msg }}"
          data:
            type: announce
      - service: notify.alexa_media_echo_quarto_lara
        data:
          message: "{{ msg }}"
          data:
            type: announce

Troubleshooting: quando “não aparece nada” na Alexa

Se você mandar “Discover devices” e ela disser que não achou nada, normalmente é um destes:

  • HA não está acessível externamente em HTTPS válido (account linking falha ou fica “meio funcionando”)
  • Lambda na região errada pro locale da skill (pt-BR costuma ser N. Virginia)
  • BASE_URL no Lambda está errado (com barra no final, ou apontando pra URL interna)
  • você esqueceu de reiniciar o HA depois de mexer no configuration.yaml
  • filter está excluindo tudo sem querer (começa expondo 1 entidade e cresce)
  • você habilitou a skill em outra conta Amazon que não é a dos seus Echos (clássico)

Depois que passa pelo setup, fica bom de verdade. Controle nativo via Smart Home Skill pro “controlar coisas”, e Alexa Media Player pro “fazer a casa falar” e pra automações com anúncio.