Dialogue Vocal Bidirectionnel Alexa ↔ Home Assistant

:studio_microphone: Dialogue Vocal Bidirectionnel Alexa ↔ Home Assistant

Créez des interactions vocales naturelles et dynamiques avec votre maison intelligente


:clipboard: Table des matières


Introduction

Ce tutoriel vous permet de créer un système où Home Assistant déclenche automatiquement une question Alexa, qui attend votre réponse vocale « oui » ou « non » pour exécuter une action.

:bullseye: Résultat final

:white_check_mark: Home Assistant détecte un événement (lumière allumée, température, heure, etc.)
:white_check_mark: Alexa pose automatiquement une question : « Voulez-vous allumer le chauffage ? »
:white_check_mark: Vous répondez simplement « oui » ou « non »
:white_check_mark: L’action correspondante s’exécute dans Home Assistant

:light_bulb: La puissance du système dynamique

Contrairement aux tutoriels classiques, ce skill est entièrement paramétrable depuis Home Assistant :

  • :counterclockwise_arrows_button: Changez la question sans toucher au code Alexa

  • :counterclockwise_arrows_button: Modifiez les actions associées en quelques clics

  • :counterclockwise_arrows_button: Créez autant de scénarios que vous voulez

Aucune modification du code Alexa nécessaire après l’installation initiale !


Prérequis

Avant de commencer, assurez-vous d’avoir :

  • :white_check_mark: Home Assistant installé et fonctionnel

  • :white_check_mark: Nabu Casa Cloud actif (abonnement requis)

  • :white_check_mark: Alexa configurée et connectée à Home Assistant

  • :white_check_mark: Alexa Media Player installé dans Home Assistant via HACS

  • :white_check_mark: Un appareil Alexa (Echo, Echo Dot, Freebox Delta/Pop, etc.)

  • :white_check_mark: Un compte Amazon (pour créer le compte Developer)


Partie 1 : Configuration Home Assistant

1.1 - Récupérer l’URL Nabu Casa

:round_pushpin: Navigation :
ParamètresHome Assistant Cloud

:memo: Action :

  1. Copiez votre URL Nabu Casa (format : xxxxx.ui.nabu.casa)

  2. Sauvegardez-la dans un fichier texte - vous en aurez besoin plus tard

Exemple d’URL :

abcd1234efgh5678ijkl.ui.nabu.casa

:warning: Important : Notez bien l’URL SANS https:// devant


1.2 - Créer le Token d’Accès

Le token permet au skill Alexa de communiquer avec Home Assistant de manière sécurisée.

:round_pushpin: Navigation :
Cliquez sur votre profil (en bas à gauche) →

Pui en Haut Onglet Sécurité

:memo: Action :

  1. Scrollez jusqu’à la section « Jetons d’accès de longue durée »

  2. Cliquez sur Créer un jeton

  3. Nom du jeton : Alexa Skill Dynamique

  4. Cliquez sur OK

  1. :warning: ATTENTION : Une popup s’affiche avec le token

  2. COPIEZ IMMÉDIATEMENT LE TOKEN (commence par eyJ...)

  3. Collez-le dans votre fichier texte (vous ne pourrez plus le revoir !)

:locked: Sécurité : Ne partagez JAMAIS ce token publiquement ! C’est la clé d’accès à votre Home Assistant.

Votre fichier texte devrait maintenant contenir :

URL: abcd1234efgh5678ijkl.ui.nabu.casa
Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...


1.3 - Créer les Helpers

Les helpers sont des variables qui stockent :

  • Un interrupteur pour déclencher le skill Alexa

  • La question qu’Alexa doit poser

  • Le script à exécuter si vous répondez OUI

  • Le script à exécuter si vous répondez NON

:round_pushpin: Navigation :
ParamètresAppareils et services → Onglet Entrées


Helper 0 : Alexa - Lancer Skill (Interrupteur)

Ce helper est un interrupteur (boolean) qui sera activé par vos automatisations pour déclencher le skill Alexa.

:memo: Action :

  1. Cliquez sur + CRÉER UNE ENTRÉE

  2. Sélectionnez Interrupteur

  3. Remplissez le formulaire :

Champ « Nom » :

Alexa - Lancer Skill

Icône (optionnel) :

mdi:microphone

:light_bulb: Note : L’icône est optionnelle. Vous pouvez laisser ce champ vide ou utiliser mdi:microphone, mdi:robot-outline ou mdi:speaker.

  1. Cliquez sur CRÉER

:light_bulb: Important : Ce helper sera utilisé par vos automatisations Home Assistant pour déclencher automatiquement le skill Alexa. Il sera aussi exposé à Alexa (nous verrons ça plus tard) pour que la routine Alexa puisse le détecter.

Si vous avez plusieurs appareils Alexa dans votre maison et que vous voulez choisir quel appareil pose la question pour chaque automatisation, vous devez créer un helper par appareil.


Helper 1 : Alexa - Question

:memo: Action :

  1. Cliquez sur + CRÉER UNE ENTRÉE

  2. Sélectionnez Texte

  3. Remplissez le formulaire :

Champ « Nom » :

Alexa - Question

Champ « Longueur maximale » :

255

Mode d’affichage : Texte (par défaut)

  1. Cliquez sur CRÉER

:warning: Important : Le helper est créé mais sans valeur. On va la remplir juste après !

  1. Dans la liste des entrées, cliquez sur Alexa - Question (le helper que vous venez de créer)

  2. Une page de détail s’ouvre. Dans le champ (empty value), saisissez :

Voulez-vous allumer la lumière de la véranda ?

  1. Appuyez sur Entrée pour valider


Helper 2 : Alexa - Script OUI

:memo: Action :

  1. Cliquez sur + CRÉER UNE ENTRÉE

  2. Sélectionnez Texte

  3. Remplissez le formulaire :

Champ « Nom » :

Alexa - Script OUI

Champ « Longueur maximale » :

100

  1. Cliquez sur CRÉER

  2. Dans la liste des entrées, cliquez sur Alexa - Script OUI

  3. Dans le champ (empty value), saisissez :

script.alexa_reponse_oui

  1. Appuyez sur Entrée pour valider

Helper 3 : Alexa - Script NON

:memo: Action :

  1. Cliquez sur + CRÉER UNE ENTRÉE

  2. Sélectionnez Texte

  3. Remplissez le formulaire :

Champ « Nom » :

Alexa - Script NON

Champ « Longueur maximale » :

100

  1. Cliquez sur CRÉER

  2. Dans la liste des entrées, cliquez sur Alexa - Script NON

  3. Dans le champ (empty value), saisissez :

script.alexa_reponse_non

  1. Appuyez sur Entrée pour valider

Vérification

:memo: Action :
Dans la liste des Entrées, vous devez maintenant voir :

  • :white_check_mark: Alexa - Lancer Skill → Interrupteur (off par défaut)

  • :white_check_mark: Alexa - QuestionVoulez-vous allumer la lumière de la véranda ?

  • :white_check_mark: Alexa - Script OUIscript.alexa_reponse_oui

  • :white_check_mark: Alexa - Script NONscript.alexa_reponse_non

[CAPTURE 9 : Liste des 4 helpers créés]


:white_check_mark: Checkpoint : Partie 1 Terminée

Ce que vous avez maintenant :

:white_check_mark: URL Nabu Casa sauvegardée
:white_check_mark: Token d’accès créé et sauvegardé
:white_check_mark: 4 Helpers créés et configurés dans Home Assistant :

  • 1 interrupteur (Lancer Skill)

  • 3 textes (Question, Script OUI, Script NON)

Fichier texte contenant :

URL: votre_url.ui.nabu.casa
Token: eyJ...



Auteur : Daniel
Date : Février 2026
Forum : Home Assistant Community France (HACF)
Licence : Partagé librement avec la communauté :heart:

1 « J'aime »

:studio_microphone: Dialogue Vocal Bidirectionnel Alexa ↔ Home Assistant

Partie 2 : Création du Skill Alexa


:clipboard: Table des matières - Partie 2


2.1 - Créer un compte Amazon Developer

Le compte Amazon Developer (gratuit) permet de créer et gérer vos skills Alexa personnalisés.

:round_pushpin: URL :

:memo: Action :

  1. Allez sur https://developer.amazon.com

  2. Cliquez sur « Sign In » (en haut à droite)

  3. Important : Utilisez le même compte Amazon que celui utilisé pour votre Alexa

  4. Si vous n’avez pas encore de compte Developer, cliquez sur « Create your Amazon Developer account »

  5. Acceptez les conditions d’utilisation

[CAPTURE 11 : Page de connexion / création de compte]

:light_bulb: Astuce : En utilisant le même compte Amazon que votre Alexa, le skill sera automatiquement disponible sur vos appareils sans configuration supplémentaire.


2.2 - Créer le Skill

:round_pushpin: Navigation :

https://developer.amazon.com/alexa/console/ask

:memo: Action :

  1. Une fois connecté, vous arrivez sur la Console Alexa Developer

  2. Cliquez sur le bouton bleu « Create Skill » (en haut à droite)


Page 1 : Name, Locale

  1. Remplissez le formulaire :

Skill name :

Contrôle Maison

:light_bulb: Note : Vous pouvez choisir un autre nom si vous préférez (ex: "Ma Maison ", « Assistant Maison », etc.)

Primary locale :

French (FR)

  1. Cliquez sur le bouton bleu « Next » (en haut à droite)

Page 2 : Experience, Model, Hosting service

  1. Section 1 - Choose a type of experience

Sélectionnez : Other (tout en bas de la liste)

  1. Section 2 - Choose a model

La carte Custom devrait se sélectionner automatiquement (encadrée en bleu)

  1. Section 3 - Hosting services

Cliquez sur la carte : Alexa-hosted (Node.js) (celle du milieu)

  1. Hosting region

Dans le menu déroulant en bas, sélectionnez : EU (Ireland)

  1. Sync Locales (toggle en haut) : Laissez-le sur OFF (position gauche)

:light_bulb: Note : Ce toggle sert à synchroniser plusieurs langues. Comme on crée le skill uniquement en français, on n’en a pas besoin.

  1. Cliquez sur « Next » (en haut à droite)

Page 3 : Templates

  1. Sélectionnez le template : Start from Scratch (première carte à gauche)

  2. Cliquez sur « Next » (en haut à droite)


Page 4 : Review

  1. Vérifiez que tout est correct :
  • :white_check_mark: Skill name : Contrôle Maison

  • :white_check_mark: Primary locale : French (FR)

  • :white_check_mark: Type : Other

  • :white_check_mark: Model : Custom

  • :white_check_mark: Hosting : Alexa-hosted (Node.JS)

  • :white_check_mark: Region : EU (Ireland)

  • :white_check_mark: Template : Start from Scratch

  1. Cliquez sur le bouton bleu « Create Skill » (en haut à droite)

  2. Attendez 30-60 secondes que le skill soit créé

  3. Une fois créé, vous arrivez sur le Dashboard du skill

:white_check_mark: Votre skill est créé !


2.3 - Configurer le nom d’invocation

Le nom d’invocation est la phrase que vous direz pour activer le skill : « Alexa, ouvre ma maison »

:round_pushpin: Navigation :
Dans le menu de gauche : Invocations → Skill Invocation Name

:memo: Action :

  1. Dans le champ « Skill Invocation Name », remplacez le texte par défaut par :
ma maison

  1. Cliquez sur « Save Model » (en haut de la page)

  2. Attendez le message « Model saved successfully »

:light_bulb: Astuce : Vous pouvez utiliser d’autres noms d’invocation comme « contrôle maison », « assistant maison », etc. Évitez les noms trop courts (1 mot) ou trop longs (plus de 3 mots).


2.4 - Créer les Intents vocaux

Les intents sont les intentions vocales que le skill reconnaîtra. Nous allons créer 2 intents :

  • OuiIntent : détecte quand vous dites « oui »

  • NonIntent : détecte quand vous dites « non »


2.4.1 - Créer OuiIntent

:round_pushpin: Navigation :
Menu de gauche : Interaction Model → Intents

:memo: Action :

  1. Cliquez sur « + Add Intent » (bouton en haut)
  2. « Create custom intent » devrait déjà être sélectionné

  1. Dans le champ « Enter name for intent », tapez :
OuiIntent

:warning: Important : Respectez bien les majuscules ! OuiIntent (pas ouiintent ou Ouiintent)

  1. Cliquez sur « Create custom intent »

  2. Vous arrivez sur la page de configuration de l’intent

  3. Dans la section « Sample Utterances » (exemples de phrases), ajoutez les phrases suivantes une par une :

Tapez chaque phrase et appuyez sur le bouton + ou sur Entrée après chaque phrase :

oui

(Entrée)

oui s'il vous plaît

(Entrée)

oui merci

(Entrée)

d'accord

(Entrée)

ok

(Entrée)

ouais

(Entrée)

bien sûr

(Entrée)

  1. Cliquez sur « Save Model » (en haut)

2.4.2 - Créer NonIntent

:memo: Action :

  1. Cliquez sur « + Add Intent » (en haut)

  2. « Create custom intent » devrait déjà être sélectionné

  3. Nom de l’intent :

NonIntent

  1. Cliquez sur « Create custom intent »

  2. Ajoutez les Sample Utterances suivantes (une par une) :

non

non merci

pas maintenant

négatif

jamais

ça va

  1. Cliquez sur « Save Model »

2.5 - Build du modèle

Maintenant que les intents sont créés, il faut compiler le modèle pour que le skill puisse reconnaître vos phrases.

:memo: Action :

  1. Cliquez sur le bouton bleu « Build Skill » (en haut à droite de la page)

  2. Une popup s’affiche avec un message de confirmation

  3. Cliquez sur « Build » dans la popup

  4. Un message apparaît : « Build in progress… »

  5. Attendez 20-30 secondes

  6. Un message apparaît : « Build Successful » :white_check_mark:

:light_bulb: Astuce : Si vous voyez des warnings (avertissements) en jaune, ce n’est pas grave. Seules les erreurs en rouge bloquent le build.


2.5.5 - Créer les scripts de réponse

Avant de passer au code, créons les scripts que le skill va appeler quand vous répondrez « oui » ou « non ».

Pour l’instant, on va créer des scripts simples qui allument/éteignent une lumière.

:warning: Important : Ces scripts doivent exister AVANT de tester le skill, sinon vous aurez des erreurs !

:round_pushpin: Navigation :
ParamètresAutomatisations et scènes → Onglet Scripts

:memo: Action :

Script 1 : alexa_reponse_oui

  1. Cliquez sur « + CRÉER UN SCRIPT » (en bas à droite)
  2. Cliquez sur les 3 points (⋮) en haut à droite
  3. Sélectionnez « Renommer »
  4. Nom du script :
Alexa Reponse OUI
  1. ID du script (en bas) :
alexa_reponse_oui
  1. Appuyez sur « OK »

  2. Cliquez sur « AJOUTER UNE ACTION »

  3. Sélectionnez « Appeler un service »

  4. Service :

light.turn_on
  1. Cible → Choisir une entité

  2. Sélectionnez une lumière de test (par exemple : light.l_veranda)

  3. Cliquez sur « ENREGISTRER » (en haut à droite)


Script 2 : alexa_reponse_non

  1. Répétez les étapes pour créer un second script :

Nom :

Alexa Reponse NON

ID :

alexa_reponse_non

Action :

  • Service : light.turn_off
  • Cible : La même lumière que pour le script OUI

:white_check_mark: Les 2 scripts sont créés !

:light_bulb: Pourquoi maintenant ? Ces scripts seront appelés par le skill Alexa quand vous répondrez « oui » ou « non ». Il faut qu’ils existent avant de tester le skill pour éviter les erreurs !

2.6 - Ajouter le code JavaScript

C’est ici que la magie opère ! Le code permet au skill de :

  • Lire la question depuis Home Assistant

  • Appeler les scripts selon votre réponse (oui/non)

:round_pushpin: Navigation :
Cliquez sur l’onglet « Code » (en haut de la page)

:memo: Action :

Vous devriez voir directement l’éditeur de code avec index.js dans la liste de gauche.

:light_bulb: Note : Comme vous avez choisi « Alexa-hosted (Node.js) » lors de la création, l’éditeur de code est directement disponible. Aucune conversion nécessaire !



2.6.1 - Remplacer par le code dynamique

:memo: Action :

  1. Dans l’éditeur, SÉLECTIONNEZ TOUT le code (Ctrl+A ou Cmd+A)

  2. SUPPRIMEZ TOUT (Delete)

  3. COPIEZ le code suivant et COLLEZ-LE dans l’éditeur :

/* *
 * Skill Alexa Dynamique - Tout paramétrable depuis Home Assistant
 * Par Daniel - Février 2026
 * Version 2.0 - Support entités multiples (scripts, lights, switches, covers, etc.)
 * */
const Alexa = require('ask-sdk-core');

// ===== CONFIGURATION (à modifier) =====
const HA_CONFIG = {
    token: 'VOTRE TOKEN',
    hostname: 'URL NABUCASA'
};

// ===== FONCTION POUR LIRE UN ÉTAT HA =====
async function getHAState(entityId) {
    const https = require('https');
    
    return new Promise((resolve, reject) => {
        const options = {
            hostname: HA_CONFIG.hostname,
            path: `/api/states/${entityId}`,
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${HA_CONFIG.token}`,
                'Content-Type': 'application/json'
            }
        };
        
        https.get(options, (res) => {
            let data = '';
            res.on('data', (chunk) => { data += chunk; });
            res.on('end', () => {
                try {
                    const state = JSON.parse(data);
                    resolve(state.state);
                } catch (error) {
                    console.log('Erreur parse JSON:', error);
                    reject(error);
                }
            });
            res.on('error', (e) => {
                console.log('Erreur lecture état:', e);
                reject(e);
            });
        });
    });
}

// ===== FONCTION POUR APPELER UNE ENTITÉ HA (scripts, lights, switches, covers, etc.) =====
async function callHAEntity(entityId, action) {
    const https = require('https');
    
    // Détecter le domaine de l'entité
    const domain = entityId.split('.')[0];
    
    // Déterminer le service à appeler
    let servicePath;
    if (domain === 'script') {
        servicePath = '/api/services/script/turn_on';
    } else if (domain === 'light') {
        servicePath = `/api/services/light/${action}`;
    } else if (domain === 'switch') {
        servicePath = `/api/services/switch/${action}`;
    } else if (domain === 'cover') {
        if (action === 'turn_on') {
            servicePath = '/api/services/cover/open_cover';
        } else if (action === 'turn_off') {
            servicePath = '/api/services/cover/close_cover';
        }
    } else if (domain === 'climate') {
        if (action === 'turn_on') {
            servicePath = '/api/services/climate/turn_on';
        } else if (action === 'turn_off') {
            servicePath = '/api/services/climate/turn_off';
        }
    } else if (domain === 'fan') {
        servicePath = `/api/services/fan/${action}`;
    } else if (domain === 'media_player') {
        if (action === 'turn_on') {
            servicePath = '/api/services/media_player/turn_on';
        } else if (action === 'turn_off') {
            servicePath = '/api/services/media_player/turn_off';
        }
    } else {
        // Par défaut, essayer homeassistant.turn_on/off
        servicePath = `/api/services/homeassistant/${action}`;
    }
    
    const postData = JSON.stringify({
        entity_id: entityId
    });
    
    return new Promise((resolve, reject) => {
        const options = {
            hostname: HA_CONFIG.hostname,
            path: servicePath,
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${HA_CONFIG.token}`,
                'Content-Type': 'application/json',
                'Content-Length': postData.length
            }
        };
        
        const req = https.request(options, (res) => {
            console.log(`Entité ${entityId} - Action ${action} - Statut:`, res.statusCode);
            resolve();
        });
        
        req.on('error', (e) => {
            console.log('Erreur appel entité:', e);
            reject(e);
        });
        
        req.write(postData);
        req.end();
    });
}

// ===== LAUNCH REQUEST (Pose la question) =====
const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    async handle(handlerInput) {
        try {
            // Lire la question depuis HA
            const question = await getHAState('input_text.alexa_question');
            console.log('Question lue depuis HA:', question);
            
            return handlerInput.responseBuilder
                .speak(question)
                .reprompt('Dites oui ou non')
                .getResponse();
        } catch (error) {
            console.log('Erreur LaunchRequest:', error);
            return handlerInput.responseBuilder
                .speak('Désolé, je ne peux pas récupérer la question depuis Home Assistant.')
                .getResponse();
        }
    }
};

// ===== OUI INTENT (Réponse OUI) =====
const OuiIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'OuiIntent';
    },
    async handle(handlerInput) {
        let speakOutput = 'D\'accord, c\'est fait';
        
        try {
            // Lire le message personnalisé
            const messageOui = await getHAState('input_text.alexa_message_oui');
            speakOutput = messageOui || speakOutput;
            
            // Lire l'entité à appeler depuis HA
            const entityOui = await getHAState('input_text.alexa_script_oui');
            console.log('Entité OUI à appeler:', entityOui);
            
            // Appeler l'entité avec l'action turn_on
            await callHAEntity(entityOui, 'turn_on');
            
        } catch (error) {
            console.log('Erreur OuiIntent:', error);
        }
        
        return handlerInput.responseBuilder
            .speak(speakOutput)
            .getResponse();
    }
};

// ===== NON INTENT (Réponse NON) =====
const NonIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'NonIntent';
    },
    async handle(handlerInput) {
        let speakOutput = 'Très bien, je n\'ai rien fait';
        
        try {
            // Lire le message personnalisé
            const messageNon = await getHAState('input_text.alexa_message_non');
            speakOutput = messageNon || speakOutput;
            
            // Lire l'entité à appeler depuis HA
            const entityNon = await getHAState('input_text.alexa_script_non');
            console.log('Entité NON à appeler:', entityNon);
            
            // Appeler l'entité avec l'action turn_off
            await callHAEntity(entityNon, 'turn_off');
            
        } catch (error) {
            console.log('Erreur NonIntent:', error);
        }
        
        return handlerInput.responseBuilder
            .speak(speakOutput)
            .getResponse();
    }
};

// ===== AUTRES HANDLERS =====
const HelpIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
            .speak('Vous pouvez répondre oui ou non.')
            .reprompt('Dites oui ou non')
            .getResponse();
    }
};

const CancelAndStopIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
                || Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
            .speak('Au revoir!')
            .getResponse();
    }
};

const FallbackIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.FallbackIntent';
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
            .speak('Désolé, je ne comprends pas. Réessayez.')
            .reprompt('Dites oui ou non')
            .getResponse();
    }
};

const SessionEndedRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
    },
    handle(handlerInput) {
        console.log(`Session ended: ${JSON.stringify(handlerInput.requestEnvelope)}`);
        return handlerInput.responseBuilder.getResponse();
    }
};

const ErrorHandler = {
    canHandle() {
        return true;
    },
    handle(handlerInput, error) {
        console.log(`Error: ${JSON.stringify(error)}`);
        return handlerInput.responseBuilder
            .speak('Désolé, problème technique. Réessayez.')
            .reprompt('Dites oui ou non')
            .getResponse();
    }
};

// ===== EXPORT =====
exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        HelpIntentHandler,
        OuiIntentHandler,
        NonIntentHandler,
        CancelAndStopIntentHandler,
        FallbackIntentHandler,
        SessionEndedRequestHandler)
    .addErrorHandlers(ErrorHandler)
    .withCustomUserAgent('home-assistant-skill-dynamic/v2.0')
    .lambda();


2.6.2 - Personnaliser le code avec vos informations

:warning: ÉTAPE CRUCIALE !

Il faut maintenant remplacer les valeurs temporaires par vos vraies informations (Token et URL Nabu Casa).

:memo: Action :

  1. Reprenez votre fichier texte où vous avez noté :

    • Votre URL Nabu Casa

    • Votre token

  2. Dans l’éditeur, cherchez la ligne 9 :

    token: 'VOTRE_TOKEN_ICI',

  1. Remplacez VOTRE_TOKEN_ICI par votre vrai token (celui qui commence par eyJ...)

Exemple :

    token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI1NTljM...',

  1. Cherchez la ligne 10 :
    hostname: 'VOTRE_URL_NABU_CASA_ICI'

  1. Remplacez VOTRE_URL_NABU_CASA_ICI par votre URL Nabu Casa (:warning: SANS https://)

Exemple :

    hostname: 'abcd1234efgh5678.ui.nabu.casa'

:locked: Sécurité : Ne partagez JAMAIS cette capture avec le token visible !


2.7 - Déployer le skill

Maintenant que le code est configuré, il faut le déployer pour qu’il soit actif.

:memo: Action :

  1. Cliquez sur le bouton « Save » (en haut de l’éditeur)

[CAPTURE 40 : Bouton « Save » en haut]

  1. Attendez le message « Saved successfully »

  2. Cliquez sur le bouton « Deploy » (juste à côté de Save)

  3. Attendez 30-60 secondes que le déploiement se termine

  4. Un message apparaît : « Deployment successful » :white_check_mark:


2.8 - Tester dans le simulateur

Avant de tester vocalement, on vérifie que tout fonctionne dans le simulateur.

:round_pushpin: Navigation :
Cliquez sur l’onglet « Test » (en haut de la page)

:memo: Action :

  1. En haut à gauche, changez « Off » en « Development »

  1. Dans le champ texte en bas (qui dit « Type or click… »), tapez :
ouvre ma maison

  1. Appuyez sur Entrée

  2. Résultat attendu : Alexa répond avec la question stockée dans votre helper HA :

"Voulez-vous allumer la lumière de la véranda ?"

  1. Dans le champ texte, tapez maintenant :
oui

  1. Appuyez sur Entrée

  2. Résultat attendu :

    • Alexa répond : « D’accord, c’est fait »

    • Dans Home Assistant, le script script.alexa_reponse_oui devrait s’exécuter

    • Si ce script allume une lumière, elle devrait s’allumer !


:white_check_mark: Checkpoint : Partie 2 Terminée

Ce que vous avez maintenant :

:white_check_mark: Compte Amazon Developer créé
:white_check_mark: Skill Alexa « Contrôle Maison » créé (nouveau workflow en 4 pages)
:white_check_mark: Nom d’invocation configuré : « ma maison »
:white_check_mark: 2 Intents créés et configurés : OuiIntent, NonIntent
:white_check_mark: Modèle compilé (Build successful)
:white_check_mark: Code JavaScript dynamique déployé
:white_check_mark: Skill testé avec succès dans le simulateur

Le skill lit maintenant dynamiquement :

  • :white_check_mark: La question depuis input_text.alexa_question

  • :white_check_mark: Le script OUI depuis input_text.alexa_script_oui

  • :white_check_mark: Le script NON depuis input_text.alexa_script_non


:bullseye: Prochaine étape

Partie 3 - Configuration finale et automatisation (à venir)

Dans la prochaine partie, nous verrons comment :

  • Exposer le helper alexa_lancer_skill à Alexa

  • Créer la routine Alexa pour le déclenchement automatique

  • Créer vos premières automatisations Home Assistant

  • Tester le workflow complet de bout en bout


Auteur : Daniel
Date : Février 2026
Forum : Home Assistant Community France (HACF)
Licence : Partagé librement avec la communauté :heart:

1 « J'aime »

:studio_microphone: Dialogue Vocal Bidirectionnel Alexa ↔ Home Assistant

Partie 3 : Configuration finale et test complet


:clipboard: Table des matières - Partie 3


Prérequis

Avant de commencer cette partie, vous devez avoir terminé :

:white_check_mark: PARTIE 1 : Helpers créés dans Home Assistant
:white_check_mark: PARTIE 2 : Skill Alexa créé et testé dans le simulateur


3.1 - Exposer le helper à Alexa

Pour que la routine Alexa puisse détecter l’activation de input_boolean.alexa_lancer_skill, il faut d’abord exposer ce helper à Alexa.

:round_pushpin: Navigation :
ParamètresAppareils et servicesEntrées

:memo: Action :

  1. Dans la liste des entrées, cliquez sur Alexa - Lancer Skill

  2. Cliquez sur la roue dentée :gear: (en haut à droite)

  3. Scrollez jusqu’à la section « Assistant vocaux » (au milieu de la page)

  4. Activez le toggle « Pour Exposer »

  5. Juste en dessous, activez le toggle « Alexa »

  6. Fermez la fenêtre (le helper est maintenant exposé à Alexa)

:light_bulb: Note : La synchronisation avec Alexa se fait automatiquement. L’entité sera visible dans l’app Alexa dans les 1-2 minutes suivantes.

:white_check_mark: Le helper est maintenant visible par Alexa !


3.2 - Créer la routine Alexa

La routine Alexa est le pont qui relie tout :

  • Elle détecte quand alexa_lancer_skill s’active

  • Elle lance automatiquement votre skill

:round_pushpin: Où :
Application Alexa (sur smartphone ou tablette)

:memo: Action :

Étape 1 : Ouvrir les routines

  1. Ouvrez l’application Alexa sur votre smartphone

  2. Appuyez sur « Routines » (directement accessible dans le menu)

  3. Appuyez sur le bouton « + » (en haut à droite) pour créer une nouvelle routine


Étape 2 : Configurer le déclencheur

  1. Appuyez sur « Ajouter un événement » (ou « Lorsque cela se produit »)

  2. Sélectionnez « Maison connectée » (ou « Smart Home »)

  3. Dans le champ de recherche, tapez :

lancer skill

ou

alexa lancer

  1. Sélectionnez « Alexa - Lancer Skill »

  2. Appuyez sur « Suivant »

  3. Sélectionnez « Ouvrir » (ou « S’allume » / « Turns on »)

  4. Appuyez sur « Suivant »


Étape 3 : Configurer l’action

  1. Appuyez sur « Ajouter une action + »

  2. Sélectionnez « Personnalisé » (ou « Custom » / « Skills »)

  3. Dans le champ, tapez (ou laissez le texte qui s’affiche) :

Lancer Skill ma maison

:light_bulb: Note : Le texte peut s’afficher automatiquement. C’est la commande vocale pour lancer votre skill.

  1. Appuyez sur « Suivant »

Étape 4 : Finaliser la routine

  1. Nom de la routine : Tapez un nom descriptif
Skill Alexa Auto

  1. Sélectionner l’appareil de sortie : Appuyez sur cette option

  2. Choisissez votre appareil Alexa (Echo, Freebox, etc.)

  3. La routine s’enregistre automatiquement

:white_check_mark: La routine est créée !

:light_bulb: Astuce : Vous pouvez créer plusieurs routines avec différents appareils Alexa si vous voulez que la question soit posée sur différents appareils selon le contexte.

IL faudra créer autant de routine que d’appareils Alexa ou vous voulez interagir .


3.3 - Créer les scripts de réponse

Les scripts script.alexa_reponse_oui et script.alexa_reponse_non sont appelés par le skill quand vous répondez « oui » ou « non ».

Pour l’instant, on va créer des scripts simples qui allument/éteignent une lumière.

:round_pushpin: Navigation :
ParamètresAutomatisations et scènes → Onglet Scripts

:memo: Action :

Script 1 : alexa_reponse_oui

  1. Cliquez sur « + CRÉER UN SCRIPT » (en bas à droite)

  2. Cliquez sur les 3 points (⋮) en haut à droite

  3. Sélectionnez « Renommer »

  4. Nom du script :

Alexa Reponse OUI

  1. ID du script (en bas) :
alexa_reponse_oui

  1. Appuyez sur « OK »

  2. Cliquez sur « AJOUTER UNE ACTION »

  3. Sélectionnez « Appeler un service »

  4. Service :

light.turn_on

  1. Cible → Choisir une entité

  2. Sélectionnez une lumière de test (par exemple : light.l_veranda)

  3. Cliquez sur « ENREGISTRER » (en haut à droite)


Script 2 : alexa_reponse_non

  1. Répétez les étapes pour créer un second script :

Nom :

Alexa Reponse NON

ID :

alexa_reponse_non

Action :

  • Service : light.turn_off

  • Cible : La même lumière que pour le script OUI

[CAPTURE 76 : Script NON enregistré]

:white_check_mark: Les 2 scripts sont créés !


3.4 - Créer votre première automatisation

Maintenant, on crée une automatisation Home Assistant qui va déclencher tout le système.

Exemple : Quand la lumière de la salle de bain s’allume, Alexa demande si on veut allumer le chauffage.

:round_pushpin: Navigation :
ParamètresAutomatisations et scènes → Onglet Automatisations

:memo: Action :

  1. Cliquez sur « + CRÉER UNE AUTOMATISATION »

  2. Sélectionnez « Créer une nouvelle automatisation »

  3. Nom de l’automatisation :

Alexa - Chauffage SDB


Déclencheur

  1. Dans la section « Lorsque », cliquez sur « AJOUTER UN DÉCLENCHEUR »

  2. Type : « État »

  3. Entité : Sélectionnez une lumière (exemple : light.salle_de_bain)

  4. Vers : on


Actions

  1. Dans la section « Faire », cliquez sur « AJOUTER UNE ACTION »

  2. Action 1 - Définir la question

Type : « Appeler un service »

Service :

input_text.set_value

Cible : input_text.alexa_question

Valeur :

Voulez-vous allumer le chauffage de la salle de bain ?


  1. Cliquez sur « AJOUTER UNE ACTION » (pour ajouter une 2ème action)

  2. Action 2 - Définir le script OUI

Service :

input_text.set_value

Cible : input_text.alexa_script_oui

Valeur :

script.chauffage_sdb_on

:light_bulb: Note : Remplacez par le nom de votre script de chauffage (ou créez-le)


  1. Action 3 - Définir le script NON

Service :

input_text.set_value

Cible : input_text.alexa_script_non

Valeur :

script.chauffage_sdb_off


  1. Action 4 - Activer le skill

Service :

input_boolean.turn_on

Cible : input_boolean.alexa_lancer_skill


  1. Action 5 - Attendre et désactiver (important !)

Cliquez sur « AJOUTER UNE ACTION »

Type : « Délai »

Durée :

00:00:05

(5 secondes)


  1. Action 6 - Désactiver le skill

Service :

input_boolean.turn_off

Cible : input_boolean.alexa_lancer_skill

:warning: Important : Cette action évite que la routine se déclenche en boucle !


  1. Cliquez sur « ENREGISTRER »

:white_check_mark: Votre première automatisation est créée !


3.5 - Test complet du workflow

Maintenant, testons TOUT le système de bout en bout !

Test 1 : Activation manuelle du helper

:memo: Action :

  1. Allez dans Outils de développementÉtats

  2. Cherchez : input_boolean.alexa_lancer_skill

  3. Cliquez dessus

  4. Cliquez sur « ACTIVER »

Résultat attendu :

  • :white_check_mark: La routine Alexa se déclenche

  • :white_check_mark: Alexa lance le skill

  • :white_check_mark: Alexa pose la question : « Voulez-vous allumer le chauffage de la salle de bain ? »

  1. Répondez « OUI » à voix haute

Résultat attendu :

  • :white_check_mark: Alexa répond : « D’accord, c’est fait »

  • :white_check_mark: Le script script.chauffage_sdb_on s’exécute

  • :white_check_mark: Le chauffage s’allume (ou la lumière de test)


Test 2 : Déclenchement automatique

:memo: Action :

  1. Allumez la lumière de la salle de bain (celle configurée dans l’automatisation)

Résultat attendu :

  • :white_check_mark: L’automatisation se déclenche

  • :white_check_mark: Les helpers sont mis à jour automatiquement

  • :white_check_mark: alexa_lancer_skill s’active

  • :white_check_mark: La routine Alexa détecte l’activation

  • :white_check_mark: Alexa lance le skill

  • :white_check_mark: Alexa pose la question

  1. Répondez « NON »

Résultat attendu :

  • :white_check_mark: Alexa répond : « Très bien, je n’ai rien fait »

  • :white_check_mark: Le script script.chauffage_sdb_off s’exécute


:white_check_mark: Checkpoint : Partie 3 Terminée

Ce que vous avez maintenant :

:white_check_mark: Helper alexa_lancer_skill exposé à Alexa
:white_check_mark: Routine Alexa créée et fonctionnelle
:white_check_mark: Scripts de réponse OUI/NON créés
:white_check_mark: Automatisation exemple créée
:white_check_mark: Système testé de bout en bout

Le workflow complet fonctionne :

  1. :house: Home Assistant détecte un événement (lumière allumée)

  2. :wrench: Automatisation met à jour les helpers et active alexa_lancer_skill

  3. :mobile_phone: Routine Alexa détecte l’activation et lance le skill

  4. :studio_microphone: Skill Alexa lit la question depuis HA et la pose

  5. :bust_in_silhouette: Vous répondez « oui » ou « non »

  6. :high_voltage: Script HA s’exécute selon votre réponse




Auteur : Daniel
Date : Février 2026
Forum : Home Assistant Community France (HACF)
Licence : Partagé librement avec la communauté :heart:

et Pour Finir , un exemple d’automatisation :

alias: Alexa - Fermeture volets 22h
description: À 22h, Alexa demande si on veut fermer les volets
triggers:
  - at: "22:15:00"
    trigger: time
conditions: []
actions:
  - target:
      entity_id: input_text.alexa_question
    data:
      value: Voulez-vous fermer les volets ?
    action: input_text.set_value
  - target:
      entity_id: input_text.alexa_script_oui
    data:
      value: script.fermeture_tous_les_volets
    action: input_text.set_value
  - target:
      entity_id: input_text.alexa_script_non
    data:
      value: script.ne_rien_faire
    action: input_text.set_value
  - target:
      entity_id: input_boolean.alexa_lancer_skill
    data: {}
    action: input_boolean.turn_on
  - delay:
      hours: 0
      minutes: 0
      seconds: 5
      milliseconds: 0
  - target:
      entity_id: input_boolean.alexa_lancer_skill
    data: {}
    action: input_boolean.turn_off
mode: single

blueprint:
  name: Alexa - Dialogue Vocal Bidirectionnel
  description: |
    Déclenche une question vocale Alexa avec réponse OUI/NON.
    
    Fonctionnalités :
    - Déclencheur personnalisable (heure, capteur, lumière, etc.)
    - Conditions optionnelles (température, heure, état, etc.)
    - Choix de l'appareil Alexa qui pose la question
    - Question personnalisée
    - Messages de confirmation personnalisés
    - Actions OUI/NON au choix (scripts, lumières, switches, volets, etc.)
    
    Créé par Daniel - Février 2026
    Version 2.1 - Aide conditions intelligentes + Script "ne rien faire"
    
  domain: automation
  
  input:
    trigger_config:
      name: Déclencheur
      description: Quand déclencher la question Alexa
      selector:
        trigger: {}
    
    condition_config:
      name: Conditions (FORTEMENT RECOMMANDÉ)
      description: |
        ⚠️ IMPORTANT : Ajoutez des conditions pour éviter les questions inutiles !
        
        💡 Exemples intelligents :
        
        🪟 Volets - Si action OUI ferme les volets :
           → Ajouter condition : État "cover.volets" = "open"
           (Alexa ne demandera que si volets ouverts)
        
        💡 Lumière - Si action OUI allume la lumière :
           → Ajouter condition : État "light.cuisine" = "off"
           (Alexa ne demandera que si lumière éteinte)
        
        🌡️ Chauffage - Si action OUI allume le chauffage :
           → Ajouter condition : Numérique "sensor.temperature" < 19
           (Alexa ne demandera que si température basse)
        
        ⏰ Heures - Pour toutes les automatisations :
           → Ajouter condition : Heure après 07:00 ET avant 22:00
           (Alexa ne dérangera pas la nuit)
        
        🏠 Présence - Pour toutes les automatisations :
           → Ajouter condition : Quelqu'un à la maison
           (Alexa ne parlera pas si maison vide)
      default: []
      selector:
        condition: {}
    
    alexa_device:
      name: Appareil Alexa
      description: Quel appareil Alexa va poser la question
      selector:
        entity:
          domain: input_boolean
    
    question_text:
      name: Question à poser
      description: La question qu'Alexa va poser (ex "Voulez-vous fermer les volets ?")
      selector:
        text:
          multiline: false
    
    action_oui:
      name: Action si réponse OUI
      description: Entité à activer (script, lumière, switch, volet, etc.)
      selector:
        entity:
          filter:
            - domain: script
            - domain: light
            - domain: switch
            - domain: cover
            - domain: climate
            - domain: fan
            - domain: media_player
    
    action_non:
      name: Action si réponse NON
      description: |
        Entité à désactiver (script, lumière, switch, volet, etc.)
        
        💡 ASTUCE - Pour "ne rien faire" :
        Créez un script nommé "Alexa Ne Rien Faire" :
        1. Scripts → Créer un script
        2. Nom : "Alexa Ne Rien Faire"
        3. Action : "Délai" avec durée 0h 0m 0s
        4. Enregistrer
        
        Puis sélectionnez "script.alexa_ne_rien_faire" ici.
      selector:
        entity:
          filter:
            - domain: script
            - domain: light
            - domain: switch
            - domain: cover
            - domain: climate
            - domain: fan
            - domain: media_player
    
    message_oui:
      name: Message après OUI
      description: Ce qu'Alexa dit après avoir exécuté l'action OUI
      default: "D'accord, c'est fait"
      selector:
        text:
          multiline: false
    
    message_non:
      name: Message après NON
      description: Ce qu'Alexa dit après avoir exécuté l'action NON
      default: "Très bien, je n'ai rien fait"
      selector:
        text:
          multiline: false

trigger: !input trigger_config

condition: !input condition_config

action:
  # 1. Définir la question
  - service: input_text.set_value
    target:
      entity_id: input_text.alexa_question
    data:
      value: !input question_text
  
  # 2. Définir l'entité OUI
  - service: input_text.set_value
    target:
      entity_id: input_text.alexa_script_oui
    data:
      value: !input action_oui
  
  # 3. Définir l'entité NON
  - service: input_text.set_value
    target:
      entity_id: input_text.alexa_script_non
    data:
      value: !input action_non
  
  # 4. Définir le message OUI
  - service: input_text.set_value
    target:
      entity_id: input_text.alexa_message_oui
    data:
      value: !input message_oui
  
  # 5. Définir le message NON
  - service: input_text.set_value
    target:
      entity_id: input_text.alexa_message_non
    data:
      value: !input message_non
  
  # 6. Activer le skill sur l'appareil choisi
  - service: input_boolean.turn_on
    target:
      entity_id: !input alexa_device
    data: {}
  
  # 7. Attendre 5 secondes
  - delay:
      seconds: 5
  
  # 8. Désactiver le skill
  - service: input_boolean.turn_off
    target:
      entity_id: !input alexa_device
    data: {}

mode: single
1 « J'aime »

Nouveau Code V2.1 MAJ ,

/* *
 * Skill Alexa Dynamique - Tout paramétrable depuis Home Assistant
 * Par Daniel - Février 2026
 * Version 2.0 - Support entités multiples (scripts, lights, switches, covers, etc.)
 * */
const Alexa = require('ask-sdk-core');

// ===== CONFIGURATION (à modifier) =====
const HA_CONFIG = {
    token: 'xxxxxxxxxx',
    hostname: 'yyyyyyyyy'
};

// ===== FONCTION POUR LIRE UN ÉTAT HA =====
async function getHAState(entityId) {
    const https = require('https');
    
    return new Promise((resolve, reject) => {
        const options = {
            hostname: HA_CONFIG.hostname,
            path: `/api/states/${entityId}`,
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${HA_CONFIG.token}`,
                'Content-Type': 'application/json'
            }
        };
        
        https.get(options, (res) => {
            let data = '';
            res.on('data', (chunk) => { data += chunk; });
            res.on('end', () => {
                try {
                    const state = JSON.parse(data);
                    resolve(state.state);
                } catch (error) {
                    console.log('Erreur parse JSON:', error);
                    reject(error);
                }
            });
            res.on('error', (e) => {
                console.log('Erreur lecture état:', e);
                reject(e);
            });
        });
    });
}

// ===== FONCTION POUR APPELER UNE ENTITÉ HA (scripts, lights, switches, covers, etc.) =====
async function callHAEntity(entityId, action) {
    const https = require('https');
    
    // Détecter le domaine de l'entité
    const domain = entityId.split('.')[0];
    
    // Déterminer le service à appeler
    let servicePath;
    if (domain === 'script') {
        servicePath = '/api/services/script/turn_on';
    } else if (domain === 'light') {
        servicePath = `/api/services/light/${action}`;
    } else if (domain === 'switch') {
        servicePath = `/api/services/switch/${action}`;
    } else if (domain === 'cover') {
        if (action === 'turn_on') {
            servicePath = '/api/services/cover/close_cover';
        } else if (action === 'turn_off') {
            servicePath = '/api/services/cover/open_cover';
        }
    } else if (domain === 'climate') {
        if (action === 'turn_on') {
            servicePath = '/api/services/climate/turn_on';
        } else if (action === 'turn_off') {
            servicePath = '/api/services/climate/turn_off';
        }
    } else if (domain === 'fan') {
        servicePath = `/api/services/fan/${action}`;
    } else if (domain === 'media_player') {
        if (action === 'turn_on') {
            servicePath = '/api/services/media_player/turn_on';
        } else if (action === 'turn_off') {
            servicePath = '/api/services/media_player/turn_off';
        }
    } else {
        // Par défaut, essayer homeassistant.turn_on/off
        servicePath = `/api/services/homeassistant/${action}`;
    }
    
    const postData = JSON.stringify({
        entity_id: entityId
    });
    
    return new Promise((resolve, reject) => {
        const options = {
            hostname: HA_CONFIG.hostname,
            path: servicePath,
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${HA_CONFIG.token}`,
                'Content-Type': 'application/json',
                'Content-Length': postData.length
            }
        };
        
        const req = https.request(options, (res) => {
            console.log(`Entité ${entityId} - Action ${action} - Statut:`, res.statusCode);
            resolve();
        });
        
        req.on('error', (e) => {
            console.log('Erreur appel entité:', e);
            reject(e);
        });
        
        req.write(postData);
        req.end();
    });
}

// ===== LAUNCH REQUEST (Pose la question) =====
const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    async handle(handlerInput) {
        try {
            // Lire la question depuis HA
            const question = await getHAState('input_text.alexa_question');
            console.log('Question lue depuis HA:', question);
            
            return handlerInput.responseBuilder
                .speak(question)
                .reprompt('Dites oui ou non')
                .getResponse();
        } catch (error) {
            console.log('Erreur LaunchRequest:', error);
            return handlerInput.responseBuilder
                .speak('Désolé, je ne peux pas récupérer la question depuis Home Assistant.')
                .getResponse();
        }
    }
};

// ===== OUI INTENT (Réponse OUI) =====
const OuiIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'OuiIntent';
    },
    async handle(handlerInput) {
        let speakOutput = 'D\'accord, c\'est fait';
        
        try {
            // Lire le message personnalisé
            const messageOui = await getHAState('input_text.alexa_message_oui');
            speakOutput = messageOui || speakOutput;
            
            // Lire l'entité à appeler depuis HA
            const entityOui = await getHAState('input_text.alexa_script_oui');
            console.log('Entité OUI à appeler:', entityOui);
            
            // Appeler l'entité avec l'action turn_on
            await callHAEntity(entityOui, 'turn_on');
            
        } catch (error) {
            console.log('Erreur OuiIntent:', error);
        }
        
        return handlerInput.responseBuilder
            .speak(speakOutput)
            .getResponse();
    }
};

// ===== NON INTENT (Réponse NON) =====
const NonIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'NonIntent';
    },
    async handle(handlerInput) {
        let speakOutput = 'Très bien, je n\'ai rien fait';
        
        try {
            // Lire le message personnalisé
            const messageNon = await getHAState('input_text.alexa_message_non');
            speakOutput = messageNon || speakOutput;
            
            // Lire l'entité à appeler depuis HA
            const entityNon = await getHAState('input_text.alexa_script_non');
            console.log('Entité NON à appeler:', entityNon);
            
            // Appeler l'entité avec l'action turn_off
            await callHAEntity(entityNon, 'turn_off');
            
        } catch (error) {
            console.log('Erreur NonIntent:', error);
        }
        
        return handlerInput.responseBuilder
            .speak(speakOutput)
            .getResponse();
    }
};

// ===== AUTRES HANDLERS =====
const HelpIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
            .speak('Vous pouvez répondre oui ou non.')
            .reprompt('Dites oui ou non')
            .getResponse();
    }
};

const CancelAndStopIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
                || Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
            .speak('Au revoir!')
            .getResponse();
    }
};

const FallbackIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.FallbackIntent';
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
            .speak('Désolé, je ne comprends pas. Réessayez.')
            .reprompt('Dites oui ou non')
            .getResponse();
    }
};

const SessionEndedRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
    },
    handle(handlerInput) {
        console.log(`Session ended: ${JSON.stringify(handlerInput.requestEnvelope)}`);
        return handlerInput.responseBuilder.getResponse();
    }
};

const ErrorHandler = {
    canHandle() {
        return true;
    },
    handle(handlerInput, error) {
        console.log(`Error: ${JSON.stringify(error)}`);
        return handlerInput.responseBuilder
            .speak('Désolé, problème technique. Réessayez.')
            .reprompt('Dites oui ou non')
            .getResponse();
    }
};

// ===== EXPORT =====
exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        HelpIntentHandler,
        OuiIntentHandler,
        NonIntentHandler,
        CancelAndStopIntentHandler,
        FallbackIntentHandler,
        SessionEndedRequestHandler)
    .addErrorHandlers(ErrorHandler)
    .withCustomUserAgent('home-assistant-skill-dynamic/v2.0')
    .lambda();
1 « J'aime »

Bonjour,

Merci pour le tutoriel ! Les possibilités ont l’air vraiment sympa.

Je rencontre un problème sur le déclencheur de la routine Alexa. Tous les tests unitaires ont fonctionné (console developer Alexa, scripts et automatisations HA, activation routine manuellement). Faut-il obligatoirement avoir Nabu Casa ? Car j’expose via une autre skill avec fonction lambda. J’ai essayé de mettre un switch au lieu d’un input boolean mais rien à faire, la routine ne se déclenche pas lors de l’activation de l’interrupteur « Alexa - Lancer skill »

Merci pour le tuto détaillé !

Direct dans ma todo list… Je suis dans le cas sans nabu casa avec une fonction Lambda, je verrai bien si j’arrive à activer le truc de mon coté.

Tu as essayé en manipulant le booléen « à la main » dans HA?

Avec Alexa media Player tu peux aussi directement lancer une routine normalement, tu n’est pas obligé de passer par un booléen pour ça: wiki - routines

A tester si on ne parviens pas à lancer la routine va un booléen.

Oui j’avais essayé de le manipuler à la main, sans succès

Je ne connaissais pas mais c’est merveilleux. Je viens de tester et ça fonctionne parfaitement.

Merci :grinning_face:

1 « J'aime »

Je ne sais pas si cela marche sans le lien nabu.

Bonjour.

Bon jusqu’à la partie de test depuis la console Alexa dev, pas de soucis pour allumer/eteindre une lampe en passant par un accès externe Tailscale plutot qu’un abo Nabu Casa.

J’ai tenté une automatisation de façon à ce que Alexa demande si on veut voir le JT à l’heure des infos (auquel cas elle mets M6) en se basant sur ce tuto et avec une petite pointe d’IA pour rediger le tout en plus. C’est un echec.

Je vais refaire une autre automatisation de test genre allumer une ampoule pour voir. Si le test sur la console dev et que ça foire ensuite pour l’automatisation, c’est que le probleme est devant l’ecran lol

Comme dit 2 messages plus haut, le problème est peut être dans la routine Alexa, essaie de la lancer directement avec Alexa Media Player

1 « J'aime »

Si tu sais exposer des entités à Alexa directement, et lancer une routine, il n’y a pas de raison d’avoir besoin du lien Nabu.

Apparament @Hbo18 a réussi.

Un chose à regarder: comment lancer la routine Alexa:

  • via une detection de changement de boléen comme tu le fais
  • directement via une commande Alexa Media Player (on peut alors se passer du helper lancer-skill dans ce cas)

Si j’ai bien compris, dans mon yalm je vire l’action input_boolean.turn_on et je colle une action où mediaplayer lance la routine, c’est ça ?

Oui c’est ça.

Ca lancera directement ta routine Alexa sans avoir besoin d’un changement d’état du booléen.

Ca ne marchera que si c’est cette detection de changement d’état qui coince dans la routine…

1 « J'aime »

J’ai pas mal de routine Alexa au comportement parfois un peu aléatoire… Cette astuce permet d’assurer le lancement par HA sans s’appuyer sur Alexa…

Ca ne marche pas si:

  • la detection doit être faite par Alexa (commande vocale, detection de son ou autre)
  • le souci est ailleurs…

Bon petit test apèrs changement. L’echo pose bien la question. Je vais pas plus perturber ma femme qui regarde une serie en ce moment pour voir si la tv va bien switcher sur M6. On va faire un test live à l’heure des infos. Merci bcp.

1 « J'aime »

:bullseye: Résumé - Système Alexa Dialogue Bidirectionnel

:white_check_mark: Entités actionnables

Le système peut contrôler les types d’entités suivants :

  • :flashlight: Lumières (light.*) - Allumer/Éteindre
  • :electric_plug: Switches (switch.*) - Activer/Désactiver
  • :window: Volets (cover.*) - Ouvrir/Fermer
  • :thermometer: Chauffage/Climatisation (climate.*) - Allumer/Éteindre
  • :dashing_away: Ventilateurs (fan.*) - Allumer/Éteindre
  • :television: Media Players (media_player.*) - Allumer/Éteindre
  • :scroll: Scripts (script.*) - Exécuter n’importe quelle action complexe

:warning: Limites d’utilisation

Comportement FIXE :

  • Réponse OUI → Toujours ACTIVER l’entité (turn_on / open_cover)
  • Réponse NON → Toujours DÉSACTIVER l’entité (turn_off / close_cover)

Vous NE POUVEZ PAS :

  • Inverser les actions (ex: OUI=éteindre, NON=allumer)
  • Faire du toggle (basculer l’état)
  • Exécuter des actions complexes directement sans script

Solutions de contournement :

  1. Créer un script pour les actions complexes ou inversées
  2. Reformuler la question pour que la logique corresponde au comportement
    • :cross_mark: Mauvais : « Voulez-vous éteindre la lumière ? » → (OUI allumerait !)
    • :white_check_mark: Bon : « La lumière est allumée, voulez-vous la laisser ? » → (NON l’éteindrait)

:scroll: Script « Ne Rien Faire »

Pour utiliser l’option « ne rien faire » dans vos automatisations, vous devez créer un script vide.

Création via l’interface :

  1. ParamètresAutomatisations et scènes → Onglet Scripts
  2. Cliquez sur « + CRÉER UN SCRIPT »
  3. Menu (3 points) → « Renommer »
  4. Nom : Alexa Ne Rien Faire
  5. ID : alexa_ne_rien_faire
  6. Cliquez sur « AJOUTER UNE ACTION »
  7. Sélectionnez « Délai »
  8. Définissez la durée :
    • Heures : 0
    • Minutes : 0
    • Secondes : 0
  9. Cliquez sur « ENREGISTRER »

Création via YAML :

script:
  alexa_ne_rien_faire:
    alias: "Alexa Ne Rien Faire"
    icon: mdi:cancel
    sequence:
      - delay:
          hours: 0
          minutes: 0
          seconds: 0
Utilisation :
Sélectionnez script.alexa_ne_rien_faire comme Action si réponse NON dans vos automatisations Blueprint.
💡 Exemples d'utilisation
Exemple 1 - Fermeture des volets
Configuration :
Question : "Voulez-vous fermer les volets ?"
Action OUI : cover.volets_salon (ferme les volets)
Action NON : script.alexa_ne_rien_faire (ne fait rien)
Résultat :
Vous répondez "OUI" → Les volets se ferment
Vous répondez "NON" → Rien ne se passe
Exemple 2 - Activation du chauffage
Configuration :
Question : "La température est basse, allumer le chauffage ?"
Action OUI : climate.chauffage_sdb (allume le chauffage)
Action NON : script.alexa_ne_rien_faire (ne fait rien)
Résultat :
Vous répondez "OUI" → Le chauffage s'allume
Vous répondez "NON" → Le chauffage reste éteint
Exemple 3 - Contrôle d'une lumière
Configuration :
Question : "Voulez-vous allumer la lumière de la cuisine ?"
Action OUI : light.cuisine (allume la lumière)
Action NON : light.cuisine (éteint la lumière)
Résultat :
Vous répondez "OUI" → La lumière s'allume
Vous répondez "NON" → La lumière s'éteint (si elle était allumée)
Créé par Daniel - Février 2026

Version V2.1

Correction Pour les Volets inversion

Ancien Code

} else if (domain === 'cover') {
    if (action === 'turn_on') {
        servicePath = '/api/services/cover/open_cover';
    } else if (action === 'turn_off') {
        servicePath = '/api/services/cover/close_cover';
    }
}

Nouveau Code

} else if (domain === 'cover') {
    if (action === 'turn_on') {
        servicePath = '/api/services/cover/close_cover';  // ← OUI = FERMER
    } else if (action === 'turn_off') {
        servicePath = '/api/services/cover/open_cover';   // ← NON = OUVRIR
    }
}