Smarwi – ukázka integrace do HomeKit pomocí Node-RED
Co v HomeKit funguje:
- změna stavu, pokud např. otevřu okno tlačítkem, přenese se informaci do stavu okna v HomeKitu
- detekce obstrukce, nouzové zastavení okna tlačítkem či obstrukce, HomeKit nahlásí, že je okno zablokované
- v případě, že je otvírač okna vypnutý a z HomeKitu přijde příkaz okno otevřít, tak aby v HomeKitu nepsalo, že se stále okno otevírá (dost zpomalovalo HomeKit), tak po 5s timeoutu se okno přepne v HomeKitu zpět do stavu zavřeno
Tak to vypadá konkrétní flow v Node-RED
Poznámka: jelikož není rozjetý vlastní MQTT server, je použit MQTT server Vektiva, což znamená, že veškeré operace s oknem v rámci HomeKit jdou přes internet. Pokud se ovládá okno přes Node-RED v rámci flows, tak se používá HTTP Request na lokální IP adresu okna.
Velký dík patří Vratislavovi Čermákovi, který napsal do Node-RED funkci na „vyzobání“ dat MQTT zprávy pro stavy HomeKit příslušenství typu Okno viz forum Vektiva. Vratislav použil na integraci okna do HomeKitu MQTT-Thing plugin v HomeBridge a vzhledem k tomu, že parsování informací z MQTT probíhá v Node-RED, bylo Vratislavovo flow upraveno tak, že to celé provede Node-RED včetně vytvoření HomeKit zařízení typu Okno.
Samotný Flow:
[{"id":"8cdc8f41.9fa68","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"c7203d7d.0792f","type":"mqtt in","z":"8cdc8f41.9fa68","name":"","topic":"ion/Remote ID/%Device ID/status","qos":"1","datatype":"auto","broker":"fcc3a487.815fc8","x":250,"y":200,"wires":[["2142b4d6.5b038c"]]},{"id":"2142b4d6.5b038c","type":"function","z":"8cdc8f41.9fa68","name":"MQTT to State Conversion","func":"{\nvar plansActive = false;\ntemp = msg.payload.split(\"\\n\");\ntemp.forEach(function(item){\n itemArray = item.split(\":\");\n if (itemArray[0]==\"pos\") { windowPos = itemArray[1]; }\n if (itemArray[0]==\"s\") { windowStatus = itemArray[1]; }\n if (itemArray[0]==\"fix\") { fixed = itemArray[1]; }\n if (itemArray[0]==\"a\") { \n if (itemArray[1]==\"-1\") { plansActive = false; } \n if (itemArray[1]==\"0\") { plansActive = true; }\n }\n});\n\nvar currentPosition = 0;\nvar targetPosition = 0;\nvar positionState = \"stopped-value\";\nvar obstructionDetected = false;\n\n\n// 250 je klidový stav a okno otevreno\nif ((windowStatus==250) && (windowPos==\"o\")) {\n currentPosition = 100;\n targetPosition = 100;\n positionState = \"stopped-value\";\n}\n\n// 250 je klidový stav a okno otevreno\nif ((windowStatus==250) && (windowPos==\"c\")) {\n currentPosition = 0;\n targetPosition = 0;\n positionState = \"stopped-value\";\n}\n\n// 200 odjisteni okna a 210 otevirani\nif ((windowStatus==200 || windowStatus==210)) {\n currentPosition = 10;\n targetPosition = 100;\n positionState = \"increasing-value\";\n}\n\n// 220 zavirani okna a 230 fixing\nif ((windowStatus==220 || windowStatus==230)) {\n currentPosition = 100;\n targetPosition = 0;\n positionState = \"decreasing-value\";\n}\n\n//\nif (fixed==\"0\") { \n obstructionDetected = true;\n}\nif (fixed==\"1\") { \n obstructionDetected = false;\n}\n\n// datum stavu\nvar dateNow = new Date();\nvar dateoptions = { hour12: false };\nvar dateString = dateNow.toLocaleString(\"cs-CZ\", dateoptions);\n\nvar jsonWindowState = {};\njsonWindowState.currentPosition = currentPosition;\njsonWindowState.targetPosition = targetPosition;\njsonWindowState.positionState = positionState;\njsonWindowState.obstructionDetected = obstructionDetected;\njsonWindowState.plansActive = plansActive;\njsonWindowState.datetime = dateString;\n\nmsg.payload = jsonWindowState;\nreturn msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":560,"y":200,"wires":[["b08afc83.34bfa","85074fa1.7f492","574cfcd6.f480b4","c2be620a.f0f58","ed575d74.4c882"]]},{"id":"ad307296.f7df9","type":"switch","z":"8cdc8f41.9fa68","name":"Open / Close","property":"payload.TargetPosition","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"str"},{"t":"gt","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":470,"y":500,"wires":[["27cdf158.57140e"],["94546cbc.8e49a"]],"outputLabels":["close me","open me"]},{"id":"27cdf158.57140e","type":"change","z":"8cdc8f41.9fa68","name":"Close","rules":[{"t":"set","p":"payload","pt":"msg","to":"close","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":500,"wires":[["55ee3be0.a688d4","53926a10.538ca4"]]},{"id":"9dddac2e.a2f78","type":"template","z":"8cdc8f41.9fa68","name":"Target Position to MQTT Conversion","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"open;{{payload}}","output":"str","x":990,"y":580,"wires":[["55ee3be0.a688d4","99d72cb9.db3c"]]},{"id":"55ee3be0.a688d4","type":"mqtt out","z":"8cdc8f41.9fa68","name":"","topic":"ion/Remote ID/%Device ID/cmd","qos":"","retain":"","broker":"fcc3a487.815fc8","x":1290,"y":500,"wires":[]},{"id":"a4161ad8.a7bc28","type":"homekit-service","z":"8cdc8f41.9fa68","isParent":true,"bridge":"c951ee55.2487e","parentService":"","name":"Okno pracovna","serviceName":"Window","topic":"","filter":false,"manufacturer":"Smarwi","model":"Window Opener","serialNo":"SN","firmwareRev":"3.4","hardwareRev":"1.0","softwareRev":"1.0","cameraConfigVideoProcessor":"ffmpeg","cameraConfigSource":"","cameraConfigStillImageSource":"","cameraConfigMaxStreams":2,"cameraConfigMaxWidth":1280,"cameraConfigMaxHeight":720,"cameraConfigMaxFPS":10,"cameraConfigMaxBitrate":300,"cameraConfigVideoCodec":"libx264","cameraConfigAudioCodec":"libfdk_aac","cameraConfigAudio":false,"cameraConfigPacketSize":1316,"cameraConfigVerticalFlip":false,"cameraConfigHorizontalFlip":false,"cameraConfigMapVideo":"0:0","cameraConfigMapAudio":"0:1","cameraConfigVideoFilter":"scale=1280:720","cameraConfigAdditionalCommandLine":"-tune zerolatency","cameraConfigDebug":false,"cameraConfigSnapshotOutput":"disabled","cameraConfigInterfaceName":"","characteristicProperties":"{}","waitForSetupMsg":false,"outputs":2,"x":1380,"y":300,"wires":[["c31ff59d.7646f8"],[]]},{"id":"b08afc83.34bfa","type":"function","z":"8cdc8f41.9fa68","name":"Current Position","func":"msg.payload = msg.payload.currentPosition;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":840,"y":120,"wires":[["e50fb1e3.41ac"]]},{"id":"e50fb1e3.41ac","type":"change","z":"8cdc8f41.9fa68","name":"Current Position to HK","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.CurrentPosition","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1060,"y":120,"wires":[["a4161ad8.a7bc28"]]},{"id":"85074fa1.7f492","type":"function","z":"8cdc8f41.9fa68","name":"Target Position","func":"msg.payload = msg.payload.targetPosition;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":840,"y":180,"wires":[["b96e4bb3.139d58"]]},{"id":"b96e4bb3.139d58","type":"change","z":"8cdc8f41.9fa68","name":"Target Position to HK","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.TargetPosition","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1060,"y":180,"wires":[["a4161ad8.a7bc28"]]},{"id":"574cfcd6.f480b4","type":"function","z":"8cdc8f41.9fa68","name":"Position State","func":"msg.payload = msg.payload.positionState;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":840,"y":240,"wires":[["90ab24fd.831e08"]]},{"id":"90ab24fd.831e08","type":"change","z":"8cdc8f41.9fa68","name":"Position State to HK","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.PositionState","tot":"msg"},{"t":"change","p":"payload.PositionState","pt":"msg","from":"stopped-value","fromt":"str","to":"2","tot":"num"},{"t":"change","p":"payload.PositionState","pt":"msg","from":"increasing-value","fromt":"str","to":"1","tot":"num"},{"t":"change","p":"payload.PositionState","pt":"msg","from":"decreasing-value","fromt":"str","to":"0","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1060,"y":240,"wires":[["a4161ad8.a7bc28","79df422b.9d762c"]]},{"id":"c2be620a.f0f58","type":"function","z":"8cdc8f41.9fa68","name":"Obstructiona Detected","func":"msg.payload = msg.payload.obstructionDetected;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":860,"y":300,"wires":[["96ffad8a.3e84d"]]},{"id":"96ffad8a.3e84d","type":"change","z":"8cdc8f41.9fa68","name":"Obstruction Detected to HK","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.ObstructionDetected","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1120,"y":300,"wires":[["a4161ad8.a7bc28"]]},{"id":"53926a10.538ca4","type":"debug","z":"8cdc8f41.9fa68","name":"Window Close","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":820,"y":460,"wires":[]},{"id":"99d72cb9.db3c","type":"debug","z":"8cdc8f41.9fa68","name":"Window Open","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1280,"y":600,"wires":[]},{"id":"94546cbc.8e49a","type":"change","z":"8cdc8f41.9fa68","name":"Open - Target Position","rules":[{"t":"move","p":"payload.TargetPosition","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":580,"wires":[["9dddac2e.a2f78"]]},{"id":"c31ff59d.7646f8","type":"throttle","z":"8cdc8f41.9fa68","name":"Pass only 1 msg. in 5s","throttleType":"time","timeLimit":"5","timeLimitType":"seconds","countLimit":0,"blockSize":0,"locked":false,"x":260,"y":500,"wires":[["ad307296.f7df9","a69aafb4.a8ccb"]]},{"id":"ed575d74.4c882","type":"debug","z":"8cdc8f41.9fa68","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":570,"y":120,"wires":[]},{"id":"9d2ef567.2f6628","type":"comment","z":"8cdc8f41.9fa68","name":"Window","info":"","x":130,"y":80,"wires":[]},{"id":"a40d29a9.456618","type":"trigger","z":"8cdc8f41.9fa68","name":"Wait 5s for Position State Change","op1":"","op2":"timeout","op1type":"nul","op2type":"str","duration":"5","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":651,"y":381,"wires":[["d8b00bc3.4ee758"]]},{"id":"d8b00bc3.4ee758","type":"function","z":"8cdc8f41.9fa68","name":"Convert Timeout to Target Position = 0","func":"if (msg.payload == \"timeout\" ) {\nmsg.payload = 0;\n} else\n{\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1090,"y":400,"wires":[["51b1245f.e5ccfc"]]},{"id":"a69aafb4.a8ccb","type":"switch","z":"8cdc8f41.9fa68","name":"Send only Output > 0 ","property":"payload.TargetPosition","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":480,"y":460,"wires":[["a40d29a9.456618"]]},{"id":"51b1245f.e5ccfc","type":"change","z":"8cdc8f41.9fa68","name":"Target Position to HK","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.TargetPosition","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1460,"y":380,"wires":[["a4161ad8.a7bc28","e7bd5c7b.e3724"]]},{"id":"8be982f9.f8fba","type":"change","z":"8cdc8f41.9fa68","name":"Change of Position State to Reset Trigger","rules":[{"t":"move","p":"payload.PositionState","pt":"msg","to":"reset","tot":"msg"},{"t":"set","p":"reset","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":380,"wires":[["a40d29a9.456618"]]},{"id":"e7bd5c7b.e3724","type":"debug","z":"8cdc8f41.9fa68","name":"Window Close Timeout","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1400,"y":440,"wires":[]},{"id":"79df422b.9d762c","type":"switch","z":"8cdc8f41.9fa68","name":"Send only Position State == 1 (Window Moves)","property":"payload.PositionState","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":500,"y":320,"wires":[["8be982f9.f8fba"]]},{"id":"47e25bb3.b83334","type":"comment","z":"8cdc8f41.9fa68","name":"Window Opener Unreachable - Set Window to Close in HK (after 5s)","info":"","x":960,"y":360,"wires":[]},{"id":"fcc3a487.815fc8","type":"mqtt-broker","name":"","broker":"broker.vektiva.com","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"c951ee55.2487e","type":"homekit-bridge","bridgeName":"NR to HK","pinCode":"123-45-678","port":"","allowInsecureRequest":false,"manufacturer":"RPi","model":"1.2.0","serialNo":"Default Serial Number","firmwareRev":"1.2.0","hardwareRev":"1.2.0","softwareRev":"1.2.0","customMdnsConfig":false,"mdnsMulticast":true,"mdnsInterface":"","mdnsPort":"","mdnsIp":"","mdnsTtl":"","mdnsLoopback":true,"mdnsReuseAddr":true,"allowMessagePassthrough":true}]
Převzat návod z Facebooku od autora Michal Srna s pomocí příkladu od Vratislav Čermák. Díky
Závěrečná poznámka: Zvažte, zda tento výrobek opravdu chcete a kde ho budete mít. V případě pojistné události vloupáním přes tento mechanismus, by mohly pojišťovny přistupovat k pojistné události ošklivě.
$ s myšlenky na „$ s“
Bohužel, výrobce ukončil výrobu tohoto zařízení ☹️
Od 7.11.2023 zase vyrábí 🙂