Zigbee2MQTT, NodeRed a Loxone

Zigbee2MQTT, NodeRed a Loxone

Po delší odmlce dnes článek o tom, jak propojit Zigbee2mqtt a Loxone. Jde o volné pokračování článku “Zigbee brána pomocí Raspbery PI“.  Minulý článek jsme skončili tím, že nám z RaspberyPI leze pomocí protokolu MQTT informace o stavech čidel, případně pomocí MQTT jsme schopni zapnout třeba žárovku nebo inteligentní zásuvku. Dnes se pojdmě podívat, jak to nyní propojit s Loxone tak, abychom mohli vše ovládat nativně z Loxone prostředí.

Jelikož Loxone nepodporuje MQTT (a zřejmě ani nikdy podpodorvat nebude), je opět potřeba si pomoci aplikací NodeRED, kterou jsem zde na blogu zmiňoval už spoustukrát (Propojení všeho se vším od Arduina po Loxone).

V návodu začneme směrem od Zigbme2mqtt k Loxone. Budu předpokládat, že máte čidla nainstalované a nampárované a že víte, jaká data z nich lezou (to jde vidět například v Zigbee2MQTT konzoly).

Xiaomi teploměr

Jako první ukážu propojení na teploměru Xiaomi. Jako MQTT kanál se použije dle nastavení například zigbee2mqtt/temperature01. Do kterého se posílají data:

{"temperature":26.05,"linkquality":47,"humidity":32.86,"pressure":927.38,"battery":42,"voltage":2985}

Abychom data přijali do Loxone, je potřeba je z MQTT převést do UDP (což je asi nejjednoduší a nejrychlejší cesta jak je do Loxone předat).

Vytvoříme si proto v NodeRed novou záložku Zigbee, do které jako první přidáme MQTT vstup. Ten nakonfigurujeme na připojení k našemu MQTT serveru a jako “Topic” vyplníme topic dle čidla. V našem případě “zigbee2mqtt/temperature01″

 

Jako další přidáme funkci, ve které napíšeme logiku převodu MQTT na UDP. V případě teploměru nás krom samotné teploty zajímá také tlak a vlhkost.

Abychom nemuseli provádět nějaké složité parsování na straně Loxone, vytvoříme v NodeRED logiku tak, že z jedné vstupní zprávy s těmito údaji vytvoříme tři výstupní UDP pakety, které se postupně pošlou do Loxonu.

var objPayload = JSON.parse(msg.payload);

var temperature = new Object();
temperature.payload = "temperature01/temperature=" + objPayload.temperature;

var humidity = new Object();
humidity.payload = "temperature01/humidity=" + objPayload.humidity;

var preasure = new Object();
preasure.payload = "temperature01/pressure=" + objPayload.pressure;

return [ [temperature,humidity,preasure] ];

Na prvním řádku převedeme Json objekt do Javascrip objektu. Díky tomu do něj můžeme dále jednoduše přistupovat.

Na řádcích 3 – 10 pak postupně vytvoříme tři objekty s výslednými daty. Každému objektu nastavíme payload, což je proměnná, které se pak v dalším bloku (UDP výstup) pošle na zadanou UDP adresu a port.

Abychom si v Loxonu parsování co nejvíce usnadnili, v každém UDP paketu posíláme název dat, rovnítko a hodnotu. Tento název spolu s rovnítkem pak budeme v řetezci hledat a v primitivních parserech Loxonu parsovat.

Jako poslední pak vrátíme pole objektů. Tím NodeREDu říkáme, že nechceme vrátit a zpracovat jeden objekt, ale několik objektů.

Xiaomi Cube

Krom čidla teploty byste mohli chtít parsovat například data z Xiami Cube. Z něj naopak čteme vždy jen jednu hodnotu, ale může jich být více typů. Proto zde přikládám i program na vyparsování typu události, její hodnoty a převod hodnoty do Loxone.

 

var objPayload = JSON.parse(msg.payload);

if ( objPayload.action == "flip90" )
{
   node.error("flip90 "+ objPayload.to_side);
   msg.payload = "flip"+ objPayload.to_side;
}
else if ( objPayload.action == "flip180" )
{
   node.error("flip180 "+ objPayload.side);
   msg.payload = "flip"+ objPayload.side;
}
if ( objPayload.action == "rotate_right" )
{
    node.error("rotate_right " + objPayload.angle);
    msg.payload = "rotate_right"; //+ objPayload.side;
    return msg;
}
else if ( objPayload.action  == "rotate_left" )
{
    node.error("rotate_left " + objPayload.angle);
    msg.payload = "rotate_left"; //+ objPayload.side;
}
else if ( objPayload.action == "slide" )
{
   node.error("slide "+objPayload.side);
   msg.payload = "slide"+ objPayload.side;
}
else if ( objPayload.action == "shake" )
{
   node.error("shake");
   msg.payload = "shake";
}
else if ( objPayload.action == "tap" )
{
   node.error("tap " + objPayload.side);
   msg.payload = "tap"+ objPayload.side;
   return msg;
}
else if ( objPayload.action == "wakeup" )
{
    //do nothing
    return null;
} else
{
    node.error("Unknown " +  objPayload.action);
    return null;
}

node.error(msg.payload);

Kostku jsem nakonec do ostrého provozu nenasadil, protože citlivost ovládání není ideální a úplně mi její používání k srdci nepřirostlo. Proto program není dodělán do finální podoby (nejsou tam například klíč=hodnota příkazy) ale jde spíše u ukázku toho, jak případně logiku řešit.

UDP komunikace

Poslední node který do našeho projektu v NodeRED přidáme je UDP output. V něm nastavíme IP adresu Loxonu a port, na kterém bude Loxone naše data naslouchat.

Tím máme v NodeRED pro teď hotovo a přesuňme se do Loxone Configu, kde přidáme UDP virtuální vstup.

A do něj jednotlivé UDP příkazy. Například parsování teploty nastavíme takto:

Postupně takto vytvoříme všechny UDP příkazy, které z NodeRED posíláme:

A tím máme hotovo. Hodnoty pak použijeme v Loxone configu stejně, jako jakékoli jiné hodnoty z teploměrů a čidel.

A nyní opačný směr

Tím máme vyřešeny všechna čidla a ovladače. To další, co nás ale zajímá, je ovládání zásuvek a osvětlení z Loxone směrem k Zigbee2MQTT.

Začneme zase v NodeRED. Zde nyní jako první vytvoříme UDP vstup, na kterém bude NodeRED naslouchat příkazy, které mu budeme z Loxone posílat.

Jako druhá je opět funkce, tentokrát taková, co převede textový příkaz z Loxone na MQTT příkaz. Abych nemusel psát hromadu malých funkcí, co převede jednotlivé příkazy, vytvořil jsem si univerzální funkci, která umí převést libovolný příkaz z Loxone na MQTT.

Funkce vezme jakýkoli vstupní řetezec ve formátu GROUP/DEVICE/VALUE, rozparsuje jej a převede na MQTT příkaz. Příakaz vytvoří tak, že topic je vždy zigbee2mqtt/ následovaný parametry dle typu zařízení.

node.error("incomming: " + msg.payload);

//parse incoming data to group-device-value
const regex = new RegExp("([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)");
var res = regex.exec(msg.payload);

var nameGroup = res[1];
var nameDevice = res[2];
var value = res[3];

node.error("group=" + nameGroup);
node.error("device=" + nameDevice);
node.error("value=" + value);

//preapre MQTT command
msg.payload = {};
msg.topic = "zigbee2mqtt/";

//create light command
if ( nameGroup == "light" )
{
    msg.topic += nameDevice + "/set";
    
    if ( value === 0 )
      msg.payload.state = "off";
    else 
      //loxone has 0-100, but we need 0-255
      msg.payload.brightness = value*2.55;
}

node.error(msg);
return msg;

V současnosti je připravena logika jen pro světla, kdy topic je zigbee2mqtt/devicename/set a hodnota buď “off”, pokud je nastavena 0, nebo číselná hodnota 0-255 dle nastavení 0-100.

Až bude potřeba přidat například spínanou zásuvku, pridám nový group-name “powersocket” a dle potřeby vygeneruju cílový payload. Je to určitě o dost přehlednější, než mít pro každou žárovku a každou zásuvku zvlášť program.

Třetím blokem v NodeRED diagramu je pak MQTT output node. To dle nastaveného msg.payload vytvoří MQTT topic a odešle ho na zadaný server.

Loxone UDP výstup

Poslední co zbývá je Loxone UDP výstup, který bude posílat data do NodeRED.

Jako první přidáme “Virtuální výstup” do Loxone configu (bacha, zatímco existuje UDP vstup, tak opakem je Virtuální výstup, nikoli UDP výstup…..).

Tento výstup pak nakonfigurujeme jak je ukázáno na obrázku výše. Důležitá je adresa, kde to, že je to UDP určujeme přímým odkazem na zařízení v URI. Tzn v mém případě /dev/udp/192.168.0.222/60001

Příkaz pro nastavení svítivosti Ikea žárovky pak vypadá takto. Je potřeba natavit instrukci pro zapnutí a vypnutí, která je ale v našem případě stejná a až samotná hodnota určuje, zda zapínáme nebo vypínáme.

Na takto vytvoření příkaz pak normálně napojíme blok ovládání osvětlení AQ1, takže v okamžiku, kdy budete měnit osvětlení se budou přes UDP posílat příkazy s aktuální hodnotou.

Tady bohužel pak trochu narážíme na limity MQTT, jelikož při plynulé změně hodnot z 100 na 0 se pošle 100 příkazů, které se musí nejprve převést na UDP, z nějak pak na MQTT, kde ho pak Zigbee2MQTT musí převést na Zigbee a poslat žárovce. Výsledkem je, že při stmívání a rozsvěcení žárovky může dojít ke zpoždění.

Zde by bylo určitě lepší mít možnost poslat přes UDP příkaz rovnou k Zigbee bráně. Bohužel, nic takového jsem nenašel.

A to je vše

A to by mělo být vše pro to, abyste propojili Zigbee a Loxone v obou směrech. Na většinu zařízení je celý koncept naprosto dostatečný a stabilní. Jediná vyjímka jsou ta stmavovaná světla, kdy při plynulém přechodu z maxima na minium se může stlumění světla trochu zpozdit.

PS: Tak, jako je univerzální funkce na ovládání a nastavování MQTT zařízení, šla by napsat i univerzální funkce na příjem hodnot z teploměrů. Předpokládám, že až čidla po době více rozšířím, tak že i tu ještě trochu předělám.

 

Pomohl Vám náš blog? Chcete nás podpořit? I málo udělá radost 😉
Become a patron at Patreon!
0 0 votes
Hodnocení články
Subscribe
Notify of
guest

15 Komentáře
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
radek
5 years ago

Díky moc za popis, přenos analogových hodnot funguje!
S čím ale bojuju je přenos textových hodnot. Např. Xiaomi magnet přenáší v proměnné “contact” stavy TRUE nebo FALSE. A Loxone si s tím neumí poradit. Mám podezření že potíž bude na straně Loxone, který si neumí poradit s textovými řetězci přes UDP.

Petr
Guest
Petr
5 years ago

Díky za článek. Já jsem komunikaci nede-red loxone rozchodil za pomocí node-red-contrib-loxone. Zajímalo by mě, jestli má někdo nějaká pro a proti pro použití UDP, node-red-contrib-loxone nebo i jiná řešení.

Co mi u komunikace mezi různými systémy pořád dělá problém je, jak zajistit ověření provedení příkazu a obnovení stavu po výpadku. Třeba při chybě komunikace se zigbee zásuvkou si loxone může myslet, že je zapnutá, ale ve skutečnosti se příkaz nedoručil. Nebo po výpadku 230 V zůstane relé v zásuvce vypnuté, i když bylo před výpadkem zapnuté.

Petr
Guest
Petr
5 years ago
Reply to  L

Množství komunikace jsem trochu zkoumal a mám dojem, že se posílají jen změny stavu.

Petr
Guest
Petr
5 years ago
Reply to  L

Já to myslel tak, že posílá jen změny stavů sledovaných objektů, alespoň tak to vypadá podle objemu přenášených dat. Sledoval jsem, jestli se sledování dat nějak projeví na zatížení miniserveru, protože mimo zigbee stahuju data pro statistiky a na žádný problém jsem nenarazil.

elpaso
4 years ago
Reply to  Petr

IMHO,
to prenasi pouze to co ma v NR v contribu vybrano. Pouzivam to na jediny vypinac a to skrz Harmony contrib a vypinac “aktivity.

UDP je dobre imho jen na senzory

cokoli ma zpetnou vazbu (svetla apod) by melo jet skrz web socket.

prave si hraju s HUE zarovnou pripojenou do ConBee a zkusim se poprat s websocketem na analog in/out

radek
5 years ago

Ještě jsem začal zkoumat plugin do Loxberry (MQTT gateway), kde mají již nějak připraven přenos do/z Loxberry přes HTTP nebo UDP. Zatím jsem to nezkoušel, ale zaujalo mě tam, že píšou:
I suggest to use HTTP instead of UDP. It is easier to configure in Loxone Config, and creates less CPU stress on the Miniserver. Only if the transferred data require to use special command recognitions, use UDP.

zdroj: https://www.loxwiki.eu/display/LOXBERRY/MQTT+Gateway+-+Troubleshooting+Guide

radek
5 years ago

Uměli by jste mě někdo prosím poradit,jakym způsobem si převedeno v Node-red text na číslo (Např. true -> 1, false -> 0)?

Petr
Guest
Petr
5 years ago
Reply to  radek

Já bych použil z funkcí Change. Tam je jednoduché nastavit, že konkrétní textová hodnota se má převést na číslo.

radek
5 years ago

Nakonec jsem převod textu na číslo zvládnul. Asi ne úplně elegantně, ale funguje to 🙂
Kdyby někdo potřeboval, tady je export z Node-Red:

[{“id”:”42884b62.2ff894″,”type”:”influxdb out”,”z”:”4f0442f3.67b62c”,”influxdb”:””,”name”:”Xiaomi_Magnet”,”measurement”:”Xiaomi_Magnet”,”precision”:””,”retentionPolicy”:””,”x”:1060,”y”:1721,”wires”:[]},{“id”:”5fd75566.312c7c”,”type”:”json”,”z”:”4f0442f3.67b62c”,”name”:””,”property”:”payload”,”action”:””,”pretty”:false,”x”:474,”y”:1735,”wires”:[[“4950d4a8.d6374c”]]},{“id”:”4950d4a8.d6374c”,”type”:”switch”,”z”:”4f0442f3.67b62c”,”name”:””,”property”:”payload.contact”,”propertyType”:”msg”,”rules”:[{“t”:”true”},{“t”:”false”}],”checkall”:”true”,”repair”:false,”outputs”:2,”x”:608,”y”:1733,”wires”:[[“6ced72f5.69035c”],[“5d2d80f1.c33b5”]]},{“id”:”6ced72f5.69035c”,”type”:”change”,”z”:”4f0442f3.67b62c”,”name”:””,”rules”:[{“t”:”set”,”p”:”payload.contact”,”pt”:”msg”,”to”:”1″,”tot”:”num”}],”action”:””,”property”:””,”from”:””,”to”:””,”reg”:false,”x”:822,”y”:1694,”wires”:[[“42884b62.2ff894”]]},{“id”:”5d2d80f1.c33b5″,”type”:”change”,”z”:”4f0442f3.67b62c”,”name”:””,”rules”:[{“t”:”set”,”p”:”payload.contact”,”pt”:”msg”,”to”:”0″,”tot”:”num”}],”action”:””,”property”:””,”from”:””,”to”:””,”reg”:false,”x”:820,”y”:1762,”wires”:[[“42884b62.2ff894”]]},{“id”:”1ea7aa5f.afd9a6″,”type”:”mqtt in”,”z”:”4f0442f3.67b62c”,”name”:”Xiaomi_Magnet”,”topic”:”zigbee2mqtt/0x00158d0002ca3627″,”qos”:”2″,”broker”:”f4ecd1b0.57f34″,”x”:276,”y”:1794,”wires”:[[“5fd75566.312c7c”]]},{“id”:”f4ecd1b0.57f34″,”type”:”mqtt-broker”,”z”:””,”name”:”MQTT server Loxberry”,”broker”:”192.168.1.150″,”port”:”1883″,”clientid”:””,”usetls”:false,”compatmode”:true,”keepalive”:”60″,”cleansession”:true,”birthTopic”:””,”birthQos”:”0″,”birthPayload”:””,”closeTopic”:””,”closeQos”:”0″,”closePayload”:””,”willTopic”:””,”willQos”:”0″,”willPayload”:””}]

elpaso
4 years ago

zdarec,
tak v ramci hledani jsem narazil na toto:

https://gist.github.com/sstroot/a2be61a889a6e6712fa0591ab1a69e35

bohuzel na RPI kde mam ConBee se mi nedari loxone contrib nainstalovat 🙂 a tam kde mi jede nemam USB abych tam pripojit ConBee… cekam az mi pomuze clovek co ten contrib napsal 🙁

D
D
3 years ago

Ahojte.

Tuje kod, ktory pouzivam na parsovanie oppo tlacidiel, aqara zaplavovych senzorov a danalock zamku.
Tlacidla musia zacinat na oppo, zaplavove senzory na leak a zamok na danalock

//node.warn(msg)
if (!String.prototype.startsWith) {
  String.prototype.startsWith = function(searchString, position) {
    position = position || 0;
    return this.indexOf(searchString, position) === position;
  };
}
var objPayload = JSON.parse(msg.payload);
const regex = new RegExp("([a-zA-Z0-9]+)\/([a-zA-Z0-9-]+)");
var res = regex.exec(msg.topic);
var nameDevice = res[2];

if(nameDevice.startsWith("leak")) {
    var action = new Object();
    if(objPayload.water_leak == true)
    { 
        action.payload = nameDevice + "-leak:1";
    } else if(objPayload.water_leak == false){
        action.payload = nameDevice + "-leak:0";
    }
} else if (nameDevice.startsWith("oppo")) {
    var action = new Object();
    action.payload = nameDevice + "-action:" + objPayload.action;
} else if(nameDevice.startsWith("danalock")) {
    var action = new Object();
    if(objPayload.state == "LOCK")
    { 
        action.payload = nameDevice + "-locked:1";
    } else if(objPayload.state == "UNLOCK"){
        action.payload = nameDevice + "-locked:0";
    }
}
if(nameDevice.startsWith("leak") || nameDevice.startsWith("oppo") || nameDevice.startsWith("danalock")) {
    var link = new Object();
    link.payload = nameDevice + "-link:" + objPayload.linkquality;
     
    var batt = new Object();
    batt.payload = nameDevice + "-batt:" + objPayload.battery;
    
//    node.warn(action);
//    node.warn(link);
//    node.warn(batt);
    return [ [action,link,batt] ];
}
15
0
Would love your thoughts, please comment.x
()
x