Guten Morgen,
wollte mal kurz zusammenfassen wie ich meine Rollladen mit Google, NodeRed + KNX steuern kann und was ich dabei gelernt habe.
Vorbemerkung 1: Anlass für Raspi und NodeRed war übrigens die Einbindung der Velux-Fenster via API, dabei bin ich über GoogleHome gestolpert und habe das vorgezogen.
Vorbemerkung 2: Das hier ist nur ein technischer Erfahrungsbericht: ob man Cloud (oder Velux) gut findet oder nicht können die Trolls gerne woanders diskutieren ;-)
(Meine) Voraussetzungen:
- Fertige KNX-Installation, bei mir MDT-Aktoren mit 220V
- KNX-Installation / GAs entsprechend der Vorschläge in der Doku im ETS-Schnellkurs von eibmeier , also einheitliche Untergruppen für die verschiedenen Kommunikationsobjekte: "Setze Absolut", "Status" und letzte Stelle der GA immer gleich fürs gleiche Gerät.
- RaspberryPi mit NodeRed
- Programmierkenntnisse
1) Für die GoogleHomeAssistant Nodes in Node-Red muss man sich bei https://googlehome.hardill.me.uk/ registrieren und kann seine Geräte anlegen. Damit kann man dann schonmal "HelloWorld" mit NodeRed-Dashboard oder Debug spielen.
2) Nachdem ich erst einzelne GAs mit den KNX-Easy-Node angesteuert habe bin ich später zu knx-ultimate (ping TheMax74) übergegangen, weil mir das zuviel Copy-Paste wurde. Ich habe 18 Rollladen und für jede muss man zwei Kommandos abfangen und an zwei Gruppenadressen weiterschicken (inkl. auslesen/umschreiben der Parameter im Payload)
3) Jetzt kommt der erste interessante Teil des Ganzen: Das Mapping zwischen GoogleHome und KNX habe ich den den globalen context von Node-Red ausgelagert:
In settings.js:
Details zu context hier: Working with context : Node-RED
Nach Änderungen an der settings.js muss man node-red evtl. neu starten ...
Das Mapping "googleDevices" zwischen Device-Ids und Device-Names ist leider nötig weil der Gerätename nicht in der msg vorkommt sondern nur die deviceID, deshalb pflege ich die Tabelle händisch: Wann immer ich ein Gerät hinzufüge, findetn man im HTML-Quellcode von Node-RED Google Assistant Bridge (hardill.me.uk) ganz unten das json-objekt für die devices, darin kann man deviceID und Namen finden und in die Tabelle hier übertragen. (Das könnte man auch kürzen auf ein Mapping von deviceId => GAs, aber mir waren hier "sprechende Namen" wichtig sonst wird's unwartbar)
Damit kann man dann im nächsten Schritt die Werte nachschlagen. zB so:
4) Der zweite Schritt ist dann ein Function-Node, der die GoogleHome-Kommandos auf KNX-Gruppenadresse und Befehl übersetzt. Echter Code siehe unten. Pseudocode:
Wenn Google-Kommando = "OnOff" schreibe boolschen Wert von Google an die richtige GruppenAdresse für "StartStop" (mit richtigen Datentyp etc.)
Wenn Google-Kommando = "OpenClose" extrahiere Prozentwert, und schreibe diesen an die an die richtige GruppenAdresse für "setze Absoluten Wert" (mit richtigen Datentyp etc.).
Sonderfälle OpenClose: "100% bzw 0% bei Google ist gerade andersrum als bei KNX. Das führt dazu dass das Kommando "Öffne die Jalousie X" eigentlich schließen würde, deswegen habe ich diese Fälle nochmal extra abgefangen.
5) Verbinden: Alle Google-Devices einzeln an den Function Node, diesen weiter an knx-ultimate (siehe screenshot unten)
Das wars, eigentlich einfach und ein schönes Kochrezept für weitere Mappings.
Grüße, Dostl!
PS:
1) Node-Red-Screenshot:
2021-02-28 (2).png
2) Volltext des verwendeten Function-Nodes:
wollte mal kurz zusammenfassen wie ich meine Rollladen mit Google, NodeRed + KNX steuern kann und was ich dabei gelernt habe.
Vorbemerkung 1: Anlass für Raspi und NodeRed war übrigens die Einbindung der Velux-Fenster via API, dabei bin ich über GoogleHome gestolpert und habe das vorgezogen.
Vorbemerkung 2: Das hier ist nur ein technischer Erfahrungsbericht: ob man Cloud (oder Velux) gut findet oder nicht können die Trolls gerne woanders diskutieren ;-)
(Meine) Voraussetzungen:
- Fertige KNX-Installation, bei mir MDT-Aktoren mit 220V
- KNX-Installation / GAs entsprechend der Vorschläge in der Doku im ETS-Schnellkurs von eibmeier , also einheitliche Untergruppen für die verschiedenen Kommunikationsobjekte: "Setze Absolut", "Status" und letzte Stelle der GA immer gleich fürs gleiche Gerät.
- RaspberryPi mit NodeRed
- Programmierkenntnisse
1) Für die GoogleHomeAssistant Nodes in Node-Red muss man sich bei https://googlehome.hardill.me.uk/ registrieren und kann seine Geräte anlegen. Damit kann man dann schonmal "HelloWorld" mit NodeRed-Dashboard oder Debug spielen.
2) Nachdem ich erst einzelne GAs mit den KNX-Easy-Node angesteuert habe bin ich später zu knx-ultimate (ping TheMax74) übergegangen, weil mir das zuviel Copy-Paste wurde. Ich habe 18 Rollladen und für jede muss man zwei Kommandos abfangen und an zwei Gruppenadressen weiterschicken (inkl. auslesen/umschreiben der Parameter im Payload)
3) Jetzt kommt der erste interessante Teil des Ganzen: Das Mapping zwischen GoogleHome und KNX habe ich den den globalen context von Node-Red ausgelagert:
In settings.js:
Code:
functionGlobalContext: { googleDevices : { "8908":"Büro", "8913":"Fernsehen", "9084":"Gemütlich", ... knxLookup : { //EG "Garten": { "ga_setpos":"2/2/0", "ga_onoff":"2/0/0"}, "Spielplatz": { "ga_setpos":"2/2/1", "ga_onoff":"2/0/1"}, "Terrasse": { "ga_setpos":"2/2/2", "ga_onoff":"2/0/2"}, "Terrassentür": { "ga_setpos":"2/2/3", "ga_onoff":"2/0/3"}, ...
Nach Änderungen an der settings.js muss man node-red evtl. neu starten ...
Das Mapping "googleDevices" zwischen Device-Ids und Device-Names ist leider nötig weil der Gerätename nicht in der msg vorkommt sondern nur die deviceID, deshalb pflege ich die Tabelle händisch: Wann immer ich ein Gerät hinzufüge, findetn man im HTML-Quellcode von Node-RED Google Assistant Bridge (hardill.me.uk) ganz unten das json-objekt für die devices, darin kann man deviceID und Namen finden und in die Tabelle hier übertragen. (Das könnte man auch kürzen auf ein Mapping von deviceId => GAs, aber mir waren hier "sprechende Namen" wichtig sonst wird's unwartbar)
Damit kann man dann im nächsten Schritt die Werte nachschlagen. zB so:
Code:
var googleDeviceId = String(msg.deviceId); var googleDeviceName = global.get('googleDevices')[googleDeviceId]; // get knx-lookup table var lookup = global.get('knxLookup') //set destination groupadress for knx-ultimate msg.destination = lookup[googleDeviceName].ga_setpos;
Wenn Google-Kommando = "OnOff" schreibe boolschen Wert von Google an die richtige GruppenAdresse für "StartStop" (mit richtigen Datentyp etc.)
Wenn Google-Kommando = "OpenClose" extrahiere Prozentwert, und schreibe diesen an die an die richtige GruppenAdresse für "setze Absoluten Wert" (mit richtigen Datentyp etc.).
Sonderfälle OpenClose: "100% bzw 0% bei Google ist gerade andersrum als bei KNX. Das führt dazu dass das Kommando "Öffne die Jalousie X" eigentlich schließen würde, deswegen habe ich diese Fälle nochmal extra abgefangen.
5) Verbinden: Alle Google-Devices einzeln an den Function Node, diesen weiter an knx-ultimate (siehe screenshot unten)
Das wars, eigentlich einfach und ein schönes Kochrezept für weitere Mappings.
Grüße, Dostl!
PS:
1) Node-Red-Screenshot:
2021-02-28 (2).png
2) Volltext des verwendeten Function-Nodes:
Code:
// a lookup table that maps goolgeDeviceIds to groupadresses // keys must match device ids from sources in https://googlehome.hardill.me.uk/user/devices var lookup = global.get('knxLookup') var googleDeviceName = global.get('googleDevices')[String(msg.deviceId)]; var cmd = String(msg.payload.command); switch (cmd) { case "action.devices.commands.OpenClose": // "closing" means for google 0, but the knx blinds need 100 and vice versa, // otherwise move openpercent to payload switch (msg.payload.params.openPercent) { case 0: newPayload=100; break; case 100: newPayload=0; break; default: newPayload=msg.payload.params.openPercent; break; } //set destination ga, datatype, event & payload msg.destination = lookup[googleDeviceName].ga_setpos; msg.dpt = "5.001"; msg.event = "GroupValue_Write"; msg.payload = newPayload; break; case "action.devices.commands.OnOff": //set destination ga, datatype, event & payload msg.destination = lookup[googleDeviceName].ga_onoff; msg.dpt = "1.008"; msg.event = "GroupValue_Write"; msg.payload = msg.payload.params.on; break; } return msg;
Kommentar