"Alexa, Licht an" – aber nur in diesem Zimmer!

Eine Lösung für Single-Room-Commands mit Alexa und ioBroker

Javascript-Code erläutert

Jetzt erweitern wir den bisherigen Code. Wir beginnen wie oben:

on('alexa2.0.History.json'/*json*/, function (obj) {
log(obj.state.val);

Der aktuelle Status (state) des übergebenen Objekts (obj) hat den Wert (val). Wir greifen mit obj.state.val darauf zu und erhalten einen String, der das JSON-Objekt darstellt. Wir wandeln den String obj.state.val in ein tatsächliches JSON-Objekt um, damit wir die Eigenschaften direkt ansprechen können.

    var jsonObj = JSON.parse(obj.state.val); 

Jetzt erhalten wir mit jsonObj.name, jsonObj.status und jsonObj.summary die unten benötigten Werte.

Es sollte nur weitergehen, wenn kein Problem aufgetreten ist. Wir fragen den Status ab:

    if (jsonObj.status == "SUCCESS") {

Wie wir im letzten Test auf der vorigen Seite festgestellt haben, kann der Befehl in der "summary" auch ohne Aktivierungswort ("Alexa") auftreten. Bevor wir den gelesenen Befehl auswerten können, muss, falls vorhanden, das Aktivierungswort entfernt werden.

    var wakeWords = ["alexa", "echo", "computer", "amazon"]; 
var wakeWord = commandArray[0];
   if (wakeWords.indexOf(wakeWord) >= 0) {
      commandArray.shift();
   }

Der auszuführende Befehl steht in jsonObj.summary. Damit wir auf die einzelnen Wörter leichter zugreifen können, wandeln wir den String in einen Array um.

        var commandArray = jsonObj.summary.split(" ");

Damit wir etwas haben, das wir ausführen können, muss commandArray mindesten ein Befehlswort enthalten.

        if (commandArray.length >= 1) {

Wir legen einen Verteiler nach dem ersten Befehlswort an. Weitere Fälle können nun leicht hinzugefügt werden.

            switch (commandArray[0]) {
            case "licht":
               //...
               break;
            case "tür":
               //...
               break;
            case "rolllade":
               //...
               break;
            }
}
}
});

Soweit die grundlegende Struktur der Lösung. Im Folgenden findest du ein voll ausgeführtes Beispiel.

Ein komplettes Beispiel

Hier ist eine praktische Umsetzung. Etwa so ist es bei mir im Einsatz.

var wakeWords = ["alexa", "echo", "computer", "amazon"];

on('alexa2.0.History.json'/*json*/, function (json) {
log(json.state.val);

// Parameter json ist eine String; Umwandeln in ein JSON-Objekt
var jsonObj = JSON.parse(json.state.val);

// es geht nur weiter, wenn kein Problem aufgetreten ist
if (jsonObj.status == "SUCCESS") {

//Alexa-Aufruf aus jsonObj.summary in Array umwandeln
var commandArray = jsonObj.summary.split(" ");

// Entfernen des WakeWord, falls vorhanden
var wakeWord = commandArray[0];
if (wakeWords.indexOf(wakeWord) >= 0) {
    commandArray.shift();
}

// Name des Alexa-Geräts, das den Befehl empfangen hat
var deviceName = jsonObj.name.toLowerCase();

//commandArray muss mindesten ein Befehlswort enthalten
if (commandArray.length >= 1) {
log("Echo-Device " + deviceName + ": " + commandArray);

//Verteiler nach dem ersten Befehlswort
switch (commandArray[0]) {
case "licht":
calling_Licht(deviceName, commandArray);
break;
case "rolllade":
case "rollladen":
calling_Rolllade(deviceName, commandArray);
break;
}
}
}
});

/*************** Functions ****************/
function calling_Licht(deviceName, commandArray) {
switch (commandArray[1]) {
case "an":
switch (deviceName) {
case "eins": //Wohnzimmer
setState('hm-rpc.0.XXXXXXXXXX.1.STATE', true);
break;
case "zwei": //Küche
exec('pilight-send -p intertechno -i X -u X -t',
function () {
log("Küchenlampe: an", "info");
});
break;
case "drei": //Arbeitszimmer
log("deconz.0.Light_1.on: " + true, "info");
setState('deconz.0.Light_1.on'/*Light 1 on*/, true);
break;
}
break;
case "aus":
switch (deviceName) {
case "eins": //Wohnzimmer
setState('hm-rpc.0.XXXXXXXXXX.STATE', false);
break;
case "zwei": //Küche
exec('pilight-send -p intertechno -i X -u X -f',
function () {
log("Küchenlampe: aus", "info");
});
break;
case "drei": //Arbeitszimmer
log("deconz.0.Light_1.on: " + false, "info");
setState('deconz.0.Light_1.on'/*Light 1 on*/, false);
break;
}
break;
}
}

var rollladeWohnzimmerRechts = 'hm-rpc.0.XXXXXXXXXX.1.';
var rollladeWohnzimmerLinks = 'hm-rpc.0.XXXXXXXXXX.1.';
var rollladeKueche = 'hm-rpc.0.XXXXXXXXXX.1.';
var rollladeArbeitszimmer = 'hm-rpc.0.XXXXXXXXXX.1.';

function calling_Rolllade(deviceName, commandArray) {
//100 = hochgefahren, 0 = komplett heruntergefahren

switch (commandArray[1]) {
case "stopp":
case "stop": {
switch (deviceName) {
case "eins": //Wohnzimmer
setState(rollladeWohnzimmerRechts + "STOP", true);
setState(rollladeWohnzimmerLinks + "STOP", true);
break;
case "zwei": //Küche
setState(rollladeKueche + "STOP", true);
break;
case "drei": //Arbeitszimmer
setState(rollladeArbeitszimmer + "STOP", true);
break;
}
break;
}
case "hoch": {
switch (deviceName) {
case "eins": //Wohnzimmer
setState(rollladeWohnzimmerRechts + "LEVEL", 100);
setState(rollladeWohnzimmerLinks + "LEVEL", 100);
break;
case "zwei": //Küche
setState(rollladeKueche + "LEVEL", 100);
break;
case "drei": //Arbeitszimmer
setState(rollladeArbeitszimmer + "LEVEL", 100);
break;
}
break;
}
case "runter": {
switch (deviceName) {
case "eins": //Wohnzimmer
setState(rollladeWohnzimmerRechts + "LEVEL", 0);
setState(rollladeWohnzimmerLinks + "LEVEL", 0);
break;
case "zwei": //Küche
setState(rollladeKueche + "LEVEL", 0);
break;
case "drei": //Arbeitszimmer
setState(rollladeArbeitszimmer + "LEVEL", 0);
break;
}
break;
}
}
}

Die über den Verteiler aufgerufenen Funktionen "calling_Licht" und "calling_Rolllade" sind nicht weiter erläutert. Sie sind als Beispiele für die Integration von aktiven Elementen aus verschiedenen Bereichen unter dem Dach von ioBroker zu sehen: Homematic, Zigbee (mit dem ConBee-Gateway auf USB-Stick von dresden elektronik) und der direkte Aufruf über die Commandline (mit Pilight, einem 433Mhz-Funk-Modul für den Raspberry Pi).

Wenn dir dieser Artikel geholfen hat,
deine eigene Lösung zu verwirklichen,
dann lass' es mich wissen.
Schreibe einen kurzen Kommentar.
Vielen Dank!


Autor: Rai

jahrelanger Homematic-Benutzer, öffnet sich mit Hilfe von Raspberry Pi und ioBroker für Neues im Internet of Things

17 Gedanken zu „"Alexa, Licht an" – aber nur in diesem Zimmer!“

  1. Ich musste folgende zeilen austauschen, damit das Skript wieder funktioniert:

    das im Original if (jsonObj.status == "SUCCESS") {

    gegen diese Zeile: if (getState('alexa2.0.History.status').val == "SUCCESS") {

    1. Hallo Holger,

      habe das gleiche Problem. Funktionierte monatelang problemlos und dann mit einem Mal nicht mehr. Habe die Zeile so geändert, wie du es vorgeschlagen hast. Funktioniert bei leider immer noch nicht.

      Hast du noch einen Tipp, woran es liegen könnte.

      Gruß
      Christian

    2. Hallo Holger,
      funktioniert das Skript bei dir ? Ich hab alles eingetragen aber bei mir funktioniert nichts mehr… In der Jscript dokumentation bei Eingang eines Befehls fällt auf, dass die Success rückmeldung anscheinend nicht mehr erfolgt:

      javascript.0 (890) script.js.common.jsCallFromEchoDevice: {"name":"Arbeitszimmer","serialNumber":"XXXXXXXXXXXXXX","summary":"licht an","creationTime":1627878267835,"domainApplicationId":"","domainApplicationName":"","cardContent":"","card":"","answerText":"","utteranceType":"GENERAL","domain":"Routines","intent":"InvokeRoutineIntent"}

      1. Moin Claus,

        bei mir funktioniert es so:

        var wakeWords = ["alexa", "echo", "computer", "amazon"];

        on('alexa2.0.History.json’/*json*/, function (json) {

        // Parameter json ist eine String; Umwandeln in ein JSON-Objekt
        var jsonObj = JSON.parse(json.state.val);
        log(jsonObj.name + " " + jsonObj.summary);
        // es geht nur weiter, wenn kein Problem aufgetreten ist
        // HOP Ausgetauscht 5.4.21…Stukturänderung
        //if (jsonObj.status == "SUCCESS") {
        if (getState('alexa2.0.History.status').val == "SUCCESS") {

        //Alexa-Aufruf aus jsonObj.summary in Array umwandeln
        var commandArray = jsonObj.summary.split(" ");

        // Entfernen des WakeWord, falls vorhanden
        var wakeWord = commandArray[0];
        if (wakeWords.indexOf(wakeWord) >= 0) {
        commandArray.shift();
        }

        // Name des Alexa-Geräts, das den Befehl empfangen hat
        var deviceName = jsonObj.name.toLowerCase();

        //commandArray muss mindesten ein Befehlswort enthalten
        if (commandArray.length >= 1) {
        // log("Echo-Device " + deviceName + ": " + commandArray);

        //Verteiler nach dem ersten Befehlswort
        switch (commandArray[0]) {
        case "licht":
        calling_Licht(deviceName, commandArray);
        break;

        }
        }
        }
        });

        /*************** Functions ****************/
        function calling_Licht(deviceName, commandArray) {
        // log("in der Funktion");
        switch (commandArray[1]) {
        case "an":
        // log("case an");
        switch (deviceName) {
        case "gaming": //Hannes Gaming
        setState('hue.0.All.on', true);
        break;
        case "bad2": //Bad 2. OG
        setState('mqtt.0.p2m.sta.omd.3.out0’/*Bad2*/,true);

        break;
        case "büro": //Arbeitszimmer

        setState('deconz.0.Groups.15.on’/*Büro on*/,true);
        break;
        case "schlafzimmer":
        setState('mqtt.0.p2m.sta.dim.7.out1’/*Schlafen*/,true);

        break;
        case "wohnzimmer":

        setState('mqtt.0.p2m.sta.dim.1.out0’/*Gallerie*/,true);
        break;
        }
        break;

        break;
        case "aus":
        switch (deviceName) {
        case "gaming": //Hannes Gaming
        setState('hue.0.All.on', false);
        break;
        case "bad2":
        setState('mqtt.0.p2m.sta.omd.3.out0’/*Bad2*/,false);

        break;
        case "büro": //Arbeitszimmer
        //log("deconz.0.group.15.on: " + false, "info");
        setState('deconz.0.Groups.15.on’/*Büro on*/,false);
        break;
        case "schlafzimmer":
        setState('mqtt.0.p2m.sta.dim.7.out1’/*Schlafen*/,false);

        break;

        case "wohnzimmer":
        setState('mqtt.0.p2m.sta.dim.1.out0’/*Gallerie*/,false);

        break;
        }
        break;
        }
        }

        1. Hallo Holger,
          funktioniert weiter nicht bei mir… Mein Script lautet:
          var wakeWords = ["alexa", "echo", "computer", "amazon"];
          on('alexa2.0.History.json’/*json*/, function (json) {
          var jsonObj = JSON.parse(json.state.val);
          log(jsonObj.name + " " + jsonObj.summary);
          if (getState('alexa2.0.History.status').val == "SUCCESS") {
          var commandArray = jsonObj.summary.split(" ");
          var wakeWord = commandArray[0];
          if (wakeWords.indexOf(wakeWord) >= 0) {
          commandArray.shift();
          }
          var deviceName = jsonObj.name.toLowerCase();
          if (commandArray.length >= 1) {
          switch (commandArray[0]) {
          case "licht":
          calling_Licht(deviceName, commandArray);
          break;
          }
          }
          }
          });

          /*************** Functions ****************/
          function calling_Licht(deviceName, commandArray) {
          switch (commandArray[1]) {
          case "an":
          switch (deviceName) {
          case "Arbeitszimmer": //Arbeitszimmer
          setState('hm-rpc.0.NEQ0213373.1.STATE', true);
          break;
          case "flur": //flur
          setState('hm-rpc.0.NEQ0213778.1.STATE', true);
          break;
          case "zwei": //Küche
          exec('pilight-send -p intertechno -i X -u X -t',
          function () {
          log("Küchenlampe: an", "info");
          });
          break;
          case "schlafzimmer":
          setState('hm-rpc.0.NEQ1656523.1.STATE', true);
          break;
          case "wohnzimmer":
          setState('hm-rpc.0.NEQ1781419.2.STATE', false);
          break;
          }
          break;

          break;
          case "aus":
          switch (deviceName) {
          case "wohnzimmer": //Hannes Gaming
          setState('hm-rpc.0.NEQ1781419.2.STATE', false);
          break;
          case "schlafzimmer":
          setState('hm-rpc.0.NEQ1656523.1.STATE', false);

          break;
          case "zwei": //Küche
          exec('pilight-send -p intertechno -i X -u X -f',
          function () {
          log("Küchenlampe: aus", "info");
          });
          break;
          case "flur":
          setState('hm-rpc.0.NEQ0213778.1.STATE', false);
          break;

          case "Arbeitszimmer":
          setState('hm-rpc.0.NEQ0213373.1.STATE', false);

          break;
          }
          break;
          }
          }
          function calling_Lichtvorn(deviceName, commandArray) {
          switch (commandArray[1]) {
          case "an":
          switch (deviceName) {
          case "wohnzimmer": //Küche
          setState('hm-rpc.0.NEQ1781419.1.STATE', true);
          break;
          }
          break;

          case "aus":
          switch (deviceName) {
          case "wohnzimmer": //Küche
          setState('hm-rpc.0.NEQ1781419.1.STATE', false);
          break;
          }
          break;
          }
          }
          Ich musste bei Zeile 3 dein 'alexa2.0.History.json’ in 'alexa2.0.History.json' ändern sonst bekam ich einen Fehler…. Leider will weder mein Licht noch irgendwas anderes sich einschalten.
          Wie gesagt ist mir aufgefallen dass ich keine ausführliche Dokumentation wie von RAI beschrieben im Javascript log habe.
          Mein Log sieht extrem mager aus:
          21:55:02.711 info javascript.0 (10727) script.js.common.Befehlssteuerung: Arbeitszimmer licht an
          21:55:03.778 info javascript.0 (10727) script.js.common.Befehlssteuerung: Arbeitszimmer

  2. Hallo Alex,
    hat monatelang bei mir zuper funktioniert, plötzlich nicht mehr…ich nehme an, bei Alexa hat sich etwas geändert!? Hast Du eine Korrekturidee oder bi ich der einzige mit Problemen?

    Danke und Gruß
    Holger

  3. Hi Rai, super Artikel! Danke für die super Anleitung!
    Ist es möglich Alex mehrere Stimmen anzulernen und dann per Skript auszuwerten wer den Befehl gegeben hat. Um z.B. zu umgehen das die Kinder die Heizung auf 30 Grad einstellen oder den TV einschalten, wenn sie es nicht sollen?

  4. Funktioniert alles,

    Man sollte dazu sagen (für Menschen wie mich die von Programmen 0 Plan haben) das:
    Man auf groß und Kleinschreibung achten muss bei dem Namen der Alexa z.b.
    Man die Routine zwingend benötigt bei mir z.b. die Routine Rollladen stopp (stop ging nicht)

    Aber ansonsten echt sehr übersichtlich wann man einmal durchgeblickt hat.

    Sehr gute Arbeit
    Danke

  5. Script läuft, Licht funktioniert schon, man muss dazu sagen ich hab absolut kein plan vom Programmieren und hab die groß und Kleinschreibung nicht beachtet (deswegen wurde am Anfang meine Alexa nicht gefunden) bis auf das was mein Fehler war top. Rolladen Steuerung wird heut Abend ausprobiert.

    Danke für das super script

  6. Hallöchen

    Super Lösung mit viel Hirnschmalz

    Habe 8 Echo´s im Einsatz. Mich interessieren folgende Werte: Letzter Befehl / welches Gerät / erfolgreich ja nein. Diese Werte brauche ich in drei IoBroker Objekten (Alexa_letzer_Befehl (Text)/ Alexa_Gerät (Text) / Alexa_erfolgreich (logisch). Leider bin ich, was Programmierung angeht, eine totale Niete. Kann mir hier jemand der Experten weiterhelfen und mir ggf ein Skript basteln?
    Würde mich riesig freuen

    Liebe Grüße Ulli

  7. Sehr gutes Beispiel, vielen Dank.
    Habe es um das Anlegen von Datenpunkten erweitert, sobald die Routine da ist, werde diese anhand des DeviceName angelegt, hier am Beispiel Wetter:

    Beim zweiten Start wird dann ein timestamp, DevideName und und das erste Commando eingefügt.

    Nutze diese Datenpunkte um die Logik in Node-Red auszuführen, somit wird das JavaScript nicht so unübersichtlich.

    case "wetter":
    createState("javascript.0.Script.Alexa_Device_abhängige_Ausführung." + deviceNameNew + ".Wetter","Neuer Datenpunkt", function () {});
    setState("javascript.0.Script.Alexa_Device_abhängige_Ausführung." + deviceNameNew + ".Wetter", stamp + " " + deviceName + " " + commandArray[0] + " " + commandArray[1]);
    break;

    Weiterhin gefällt mir das mit dem Command Arrayx!
    Damit kann man wunderbar auf das erste Command den Case setzen,
    und dann aufgliedern nach den folgenden Befehlen, echt super deine Vorlage.

    case "spiele":
    if (commandArray[1] = "musik") {
    createState("javascript.0.Script.Alexa_Device_abhängige_Ausführung." + deviceNameNew + ".Spiele_Musik","Neuer Datenpunkt", function () {});
    setState("javascript.0.Script.Alexa_Device_abhängige_Ausführung." + deviceNameNew + ".Spiele_Musik", stamp + " " + deviceName + " " + commandArray[0] + " " + commandArray[1]);
    }
    break;

  8. ne coole Sache. Leider habe ich das Problem, dass ich in meinem ioBroker den State vom Schalter irgendwie nicht per checkbox switchen kann. D.h. ich muss den State im Dialogfenster per Text auf TRUE setzen, so dass die Lampe angeht. Warum auch immer.

    Wenn ich dann sage Alexa: Licht AN, wird die Routinenausführung bestätigt aber das Licht geht nicht an 🙁

    Hier mal das Log:
    avascript.0 (962) script.js.LXSkript.jsCallFromEchoDevice: {"name":"Bad Echo Dot","serialNumber":"xxxxxxsdf","summary":"","creationTime":1585122686348,"status":"SUCCESS","domainApplicationId":"","domainApplicationName":"","cardContent":"","card":"","answerText":""}
    08:51:26.954 info javascript.0 (962) script.js.LXSkript.jsCallFromEchoDevice: Echo-Device bad echo dot:
    08:51:26.978 info javascript.0 (962) script.js.LXSkript.jsCallFromEchoDevice: {"name":"Bad Echo Dot","serialNumber":"xxxxxxx","summary":"licht an","creationTime":1585122686526,"status":"SUCCESS","domainApplicationId":"","domainApplicationName":"","cardContent":"","card":"","answerText":""}
    08:51:26.979 info javascript.0 (962) script.js.LXSkript.jsCallFromEchoDevice: Echo-Device bad echo dot: licht,an
    08:51:27.879 info javascript.0 (962) script.js.LXSkript.jsCallFromEchoDevice: {"name":"Bad Echo Dot","serialNumber":"xxxxx","summary":"sprich mir nach ok befehl","creationTime":1585122686975,"status":"SUCCESS","domainApplicationId":"","domainApplicationName":"","cardContent":"","card":"","answerText":""}
    08:51:27.881 info javascript.0 (962) script.js.LXSkript.jsCallFromEchoDevice: Echo-Device bad echo dot: sprich,mir,nach,ok

    1. Hallo Alex, am Log-File bemerke ich nichts Außergewöhnliches. Vielleicht kannst du mal dein JavaScript zum Licht-An/Ausschalten posten. Gruß Rai!

    1. Hallo Pavel, danke für deine Rückmeldung. Ja, wenn es möglich ist, ziehe ich Lösungen vor, die ohne Daten in der Cloud auskommen. Denk dran, dass, wenn Alexa im Spiel ist, auf jeden Fall die Amazon Cloud im Hintergrund steht. Ganz ohne Cloud geht es also nicht immer. Gruß Rai!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.