Node-Red

I know, right?!

So I didn't find any documentation, but here's how I'm doing it in my .NET Core code. Basically whenever the websocket sends an event, I deserialize it and raise it as a System.Event in my code, so then I can look at all my automations and see if any of them subscribe to events from the device that sent it.

1 Like

Guess we are meant to be smart enough to figure it out ... looks like you are making progress.

Thanks for sharing!

1 Like

I have way too much code in node-red! In fact I have just bought an HE in order to remove all but the most essential rubbish. Some of the things that are easy in RuleEngine are messy in node-red, though you do find yourself repeating "patterns".

Stuff I really really want to remove from node-red

  • the Google Home "integration" implementation which took me a long time to get stable.
  • The simpler monitoring (e.g. is washing machine on, motion lighting),
  • all the weird home built device integration stuff (eg 6-LED Information panels which is 150 nodes on its own)

And other automation stuff I never got around to.

Here's a rough copy of a sketch that integrates Hubitat, MQTT and Home Assistant. It's not very well documented, but you should be able to work with it if you know Node-Red.

The Config node at the top needs to be tweaked for your installation of the Maker API App (Hubitat IP, Maker API App ID and Access Token.) You'll also need to set your MQTT server in the two MQTT nodes and may need to change the url on the websocket listener.

From there, it's just a matter of setting up the Maker API App with the devices you want to control and setting up Home Assistant for your MQTT broker and enabling auto discovery. You can run everything using HASSIO on a raspberry pi or VM and use the MQTT and Node-Red Addons.

[{"id":"5d1597b5.fb5dc8","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"9fcc6f08.06431","type":"http in","z":"5d1597b5.fb5dc8","name":"","url":"/mqttweb/push/:platform","method":"post","upload":false,"swaggerDoc":"","x":240,"y":300,"wires":[["8275f94b.3e5878","366e87e9.bdd828"]]},{"id":"8275f94b.3e5878","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":500,"y":200,"wires":[]},{"id":"8b72eb2e.0b7448","type":"http response","z":"5d1597b5.fb5dc8","name":"","statusCode":"200","headers":{},"x":840,"y":300,"wires":[]},{"id":"49662e81.790a5","type":"mqtt out","z":"5d1597b5.fb5dc8","name":"","topic":"","qos":"","retain":"","broker":"d637a738.68b148","x":830,"y":500,"wires":[]},{"id":"366e87e9.bdd828","type":"change","z":"5d1597b5.fb5dc8","name":"Prepare MQTT Message","rules":[{"t":"set","p":"topic","pt":"msg","to":"$join([$string($flowContext(\"config.mqtt_prefix\")),$string(payload.platform),$string(payload.device),$string(payload.attribute),$string($flowContext(\"config.mqtt_state_suffix\"))], '/')","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"payload.state","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":380,"wires":[["49662e81.790a5","3d6a1859.8cd618"]]},{"id":"c01c257e.694b08","type":"catch","z":"5d1597b5.fb5dc8","name":"","scope":["9fcc6f08.06431","49662e81.790a5","366e87e9.bdd828"],"x":830,"y":360,"wires":[["f9a5aa1f.b54158","2f103c0d.489b34"]]},{"id":"f9a5aa1f.b54158","type":"http response","z":"5d1597b5.fb5dc8","name":"","statusCode":"500","headers":{},"x":980,"y":340,"wires":[]},{"id":"32de4617.9cccda","type":"comment","z":"5d1597b5.fb5dc8","name":"New event push from web","info":"Section that makes an endpoint to push new MQTT messages to from the web. \n\n\nExpects the format:\n```\n{ \"platform\": \"hubitat\",\n \"device\": \"MYDEVICE01\",\n \"attribute\": \"motion\",\n \"state\": \"active\"\n}\n```","x":190,"y":140,"wires":[]},{"id":"7ea6096c.3c5b98","type":"http in","z":"5d1597b5.fb5dc8","name":"","url":"/mqttweb/get","method":"get","upload":false,"swaggerDoc":"","x":230,"y":760,"wires":[["aa177cd2.30b0e"]]},{"id":"ba72a90.b2e0758","type":"mqtt in","z":"5d1597b5.fb5dc8","name":"","topic":"ha/+/+/+/cmd","qos":"2","broker":"d637a738.68b148","x":230,"y":900,"wires":[["cc6123c9.0c96a"]]},{"id":"23aa66ae.33552a","type":"comment","z":"5d1597b5.fb5dc8","name":"Retrieve Commands from MQTT","info":"","x":250,"y":820,"wires":[]},{"id":"cc6123c9.0c96a","type":"switch","z":"5d1597b5.fb5dc8","name":"","property":"$split(topic,\"/\")[1]","propertyType":"jsonata","rules":[{"t":"eq","v":"smartthings","vt":"str"},{"t":"eq","v":"hubitat","vt":"str"},{"t":"eq","v":"homeassistant","vt":"str"},{"t":"eq","v":"blueiris","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":5,"x":410,"y":900,"wires":[[],["fc823e26.9596e"],[],[],[]],"outputLabels":["smartthings","hubitat","homeassistant","","other"]},{"id":"fc823e26.9596e","type":"link out","z":"5d1597b5.fb5dc8","name":"MQTT to Hubitat Output","links":["14a9f701.a16fd9"],"x":675,"y":880,"wires":[]},{"id":"a71ddd30.56933","type":"link in","z":"5d1597b5.fb5dc8","name":"MQTT Output","links":["8947dadf.ddbf98","7d9f56e0.67f6b8","ba55732a.ddd17"],"x":575,"y":500,"wires":[["9fbd7e85.f1c4a","49662e81.790a5"]]},{"id":"1a76da84.b7b1f5","type":"config","z":"5d1597b5.fb5dc8","name":"","properties":[{"p":"config","pt":"flow","to":"{}","tot":"json"},{"p":"config.mqtt_prefix","pt":"flow","to":"ha","tot":"str"},{"p":"config.mqtt_state_suffix","pt":"flow","to":"state","tot":"str"},{"p":"config.mqtt_cmd_suffix","pt":"flow","to":"cmd","tot":"str"},{"p":"hubitat_ip","pt":"flow","to":"hassio","tot":"str"},{"p":"hubitat_maker_token","pt":"flow","to":"1234zxc","tot":"str"},{"p":"hubitat_maker_app_id","pt":"flow","to":"134","tot":"str"}],"active":true,"x":130,"y":80,"wires":[]},{"id":"8c9496c.3b57968","type":"http in","z":"5d1597b5.fb5dc8","name":"","url":"/mqttweb/discovery/:platform","method":"post","upload":false,"swaggerDoc":"","x":220,"y":600,"wires":[["8d77ff45.84c07","758251ce.f9c3b","b778284d.3df338"]]},{"id":"7ffcdadb.0683f4","type":"switch","z":"5d1597b5.fb5dc8","name":"","property":"req.params.platform","propertyType":"msg","rules":[{"t":"eq","v":"smartthings","vt":"str"},{"t":"eq","v":"hubitat","vt":"str"},{"t":"eq","v":"homeassistant","vt":"str"},{"t":"eq","v":"blueiris","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":5,"x":610,"y":660,"wires":[[],["25cd93f3.a1f65c"],[],["b4cf4686.7162b8"],[]],"outputLabels":["smartthings","hubitat","homeassistant","","other"]},{"id":"50022e4f.1f0ac","type":"link out","z":"5d1597b5.fb5dc8","name":"Hubitat Discovery to Home Assistant","links":["4f6d4959.d2fb78"],"x":1155,"y":680,"wires":[]},{"id":"8d77ff45.84c07","type":"change","z":"5d1597b5.fb5dc8","name":"","rules":[{"t":"set","p":"config","pt":"msg","to":"config","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":680,"wires":[["7ffcdadb.0683f4"]]},{"id":"9fbd7e85.f1c4a","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":670,"y":540,"wires":[]},{"id":"68615af5.ef4384","type":"link in","z":"5d1597b5.fb5dc8","name":"Web Bridge Discovery Input","links":["639f2b68.9ce224","94fd5f0e.ae487","791fa86b.6321b8"],"x":235,"y":660,"wires":[["8d77ff45.84c07"]]},{"id":"b4cf4686.7162b8","type":"change","z":"5d1597b5.fb5dc8","name":"","rules":[{"t":"set","p":"config.devicePrefix","pt":"msg","to":"BI","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":860,"y":700,"wires":[["50022e4f.1f0ac"]]},{"id":"25cd93f3.a1f65c","type":"change","z":"5d1597b5.fb5dc8","name":"","rules":[{"t":"set","p":"config.devicePrefix","pt":"msg","to":"HE","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":840,"y":620,"wires":[["50022e4f.1f0ac"]]},{"id":"b778284d.3df338","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":710,"y":780,"wires":[]},{"id":"758251ce.f9c3b","type":"http response","z":"5d1597b5.fb5dc8","name":"","statusCode":"200","headers":{},"x":520,"y":580,"wires":[]},{"id":"aa177cd2.30b0e","type":"http response","z":"5d1597b5.fb5dc8","name":"","statusCode":"200","headers":{},"x":500,"y":760,"wires":[]},{"id":"54ed5b6.b1dfba4","type":"comment","z":"5d1597b5.fb5dc8","name":"Device Discovery","info":"","x":160,"y":560,"wires":[]},{"id":"1441e577.1bf58b","type":"link in","z":"5d1597b5.fb5dc8","name":"MQQT Event Push Input","links":["bab56929.0c4f78"],"x":175,"y":420,"wires":[["366e87e9.bdd828"]]},{"id":"3d6a1859.8cd618","type":"switch","z":"5d1597b5.fb5dc8","name":"Check for request","property":"res","propertyType":"msg","rules":[{"t":"nnull"}],"checkall":"true","repair":false,"outputs":1,"x":630,"y":300,"wires":[["8b72eb2e.0b7448"]]},{"id":"2f103c0d.489b34","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":1050,"y":400,"wires":[]},{"id":"ed020679.19a898","type":"websocket in","z":"5d1597b5.fb5dc8","name":"Hubitat Event Stream","server":"","client":"fe9107d9.d7c468","x":220,"y":1200,"wires":[["eb8a63f0.474ce"]]},{"id":"eb8a63f0.474ce","type":"json","z":"5d1597b5.fb5dc8","name":"","property":"payload","action":"obj","pretty":false,"x":410,"y":1200,"wires":[["e8da958a.d03778","dddff7e3.196968"]]},{"id":"c60c6981.421278","type":"change","z":"5d1597b5.fb5dc8","name":"convert event to our format","rules":[{"t":"set","p":"event","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"payload.device","pt":"msg","to":"event.deviceId","tot":"msg"},{"t":"set","p":"payload.attribute","pt":"msg","to":"event.name","tot":"msg"},{"t":"set","p":"payload.state","pt":"msg","to":"event.value","tot":"msg"},{"t":"set","p":"payload.platform","pt":"msg","to":"hubitat","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":1200,"wires":[["bab56929.0c4f78"]]},{"id":"e8da958a.d03778","type":"switch","z":"5d1597b5.fb5dc8","name":"","property":"payload.source","propertyType":"msg","rules":[{"t":"eq","v":"DEVICE","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":550,"y":1200,"wires":[["c60c6981.421278"]]},{"id":"bab56929.0c4f78","type":"link out","z":"5d1597b5.fb5dc8","name":"Hubitat Event Stream Post Conversion","links":["1441e577.1bf58b"],"x":895,"y":1200,"wires":[]},{"id":"c7d09d2b.b6c08","type":"http request","z":"5d1597b5.fb5dc8","name":"","method":"GET","ret":"txt","url":"","tls":"","x":370,"y":1360,"wires":[["59dc9284.00075c"]]},{"id":"8ba133ed.fe51a","type":"change","z":"5d1597b5.fb5dc8","name":"","rules":[{"t":"set","p":"req.params.platform","pt":"msg","to":"hubitat","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":1360,"wires":[["791fa86b.6321b8"]]},{"id":"791fa86b.6321b8","type":"link out","z":"5d1597b5.fb5dc8","name":"Hubitat Discovery Flow Out","links":["68615af5.ef4384"],"x":915,"y":1360,"wires":[]},{"id":"36d6ff89.99639","type":"inject","z":"5d1597b5.fb5dc8","name":"","topic":"","payload":"","payloadType":"date","repeat":"300","crontab":"","once":true,"onceDelay":0.1,"x":110,"y":1380,"wires":[["913ea46a.015ab8"]]},{"id":"2ea690c3.b11b","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":1520,"wires":[]},{"id":"59dc9284.00075c","type":"json","z":"5d1597b5.fb5dc8","name":"","property":"payload","action":"obj","pretty":false,"x":510,"y":1360,"wires":[["8ba133ed.fe51a","2ea690c3.b11b"]]},{"id":"6e0f0033.7c64b","type":"comment","z":"5d1597b5.fb5dc8","name":"Websocket Event to MQTT","info":"May need to change your websocket definition if your hub has a different id or you don't have a dns server","x":230,"y":1140,"wires":[]},{"id":"fafdb19a.6174","type":"comment","z":"5d1597b5.fb5dc8","name":"Discover via Maker API","info":"","x":200,"y":1320,"wires":[]},{"id":"14a9f701.a16fd9","type":"link in","z":"5d1597b5.fb5dc8","name":"MQTT to Hubitat Input","links":["93acba0.f32c648","fc823e26.9596e"],"x":275,"y":1620,"wires":[["28088d0a.d10772"]]},{"id":"e49fd615.b32f68","type":"comment","z":"5d1597b5.fb5dc8","name":"Execute MQTT Commands","info":"","x":250,"y":1560,"wires":[]},{"id":"486b8571.734cfc","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":1010,"y":1560,"wires":[]},{"id":"28088d0a.d10772","type":"change","z":"5d1597b5.fb5dc8","name":"","rules":[{"t":"set","p":"deviceID","pt":"msg","to":"$split(topic, '/')[2]","tot":"jsonata"},{"t":"set","p":"attribute","pt":"msg","to":"$split(topic, '/')[3]","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":1640,"wires":[["c4dc540f.354f98"]]},{"id":"eba9ad17.a980f","type":"change","z":"5d1597b5.fb5dc8","name":"","rules":[{"t":"set","p":"url","pt":"msg","to":"$join(\t [\t \"http:/\",\t $flowContext(\"hubitat_ip\"),\t \"apps/api\",\t $flowContext(\"hubitat_maker_app_id\"),\t \"devices\",\t deviceID,\t command\t \t ],\t '/'\t\t) & (\t $exists(parameter) ? (\"/\" & parameter) : \"\"\t) & \"?access_token=\" & $flowContext(\"hubitat_maker_token\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":850,"y":1680,"wires":[["486b8571.734cfc","24ecc018.0d704"]]},{"id":"c4dc540f.354f98","type":"function","z":"5d1597b5.fb5dc8","name":"Set command","func":"switch(msg.attribute) {\n case 'switch':\n msg.command = msg.payload;\n break;\n case 'level':\n msg.command = \"setLevel\";\n msg.parameter = msg.payload;\n break;\n default: \n msg.command = msg.payload;\n break;\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":630,"y":1660,"wires":[["eba9ad17.a980f"]]},{"id":"24ecc018.0d704","type":"http request","z":"5d1597b5.fb5dc8","name":"","method":"GET","ret":"txt","url":"","tls":"","x":1240,"y":1600,"wires":[["4a8bc556.2bb6ec"]]},{"id":"4a8bc556.2bb6ec","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1430,"y":1580,"wires":[]},{"id":"dddff7e3.196968","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":677.5,"y":1259,"wires":[]},{"id":"913ea46a.015ab8","type":"change","z":"5d1597b5.fb5dc8","name":"","rules":[{"t":"set","p":"url","pt":"msg","to":"$join(\t [\t \"http:/\",\t $flowContext(\"hubitat_ip\"),\t \"apps/api\",\t $flowContext(\"hubitat_maker_app_id\"),\t \"devices/all\"\t \t ],\t '/'\t\t) & (\t $exists(parameter) ? (\"/\" & parameter) : \"\"\t) & \"?access_token=\" & $flowContext(\"hubitat_maker_token\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":1420,"wires":[["c7d09d2b.b6c08","7625a9c1.bae908"]]},{"id":"f051d67c.9c3cd8","type":"comment","z":"5d1597b5.fb5dc8","name":"Hubitat Integration","info":"Handle listening to event stream, pulling devices to manage from the Maker API and processing commands from MQTT into Maker API calls","x":130,"y":1100,"wires":[]},{"id":"660429ee.773a88","type":"comment","z":"5d1597b5.fb5dc8","name":"Home Assistant Integration","info":"Parse device information and turn it into pushes to MQTT to support Home Assistant's native discovery. Should automatically create devices and entities in Home Assistant for whatever passes through as long as it matches the format. \n\nYou should not need to change the home assistant import config but it's there in case you want to add additional transforms, etc. ","x":130,"y":1740,"wires":[]},{"id":"4f6d4959.d2fb78","type":"link in","z":"5d1597b5.fb5dc8","name":"Discovery Input","links":["5eba5b74.300294","50022e4f.1f0ac"],"x":175,"y":2000,"wires":[["d915d6c0.eb08b8","f60ca12b.94306"]]},{"id":"ce2feda2.5d87a","type":"split","z":"5d1597b5.fb5dc8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":430,"y":2020,"wires":[["87aeec0e.5061d","badc58bb.c0a658"]]},{"id":"87aeec0e.5061d","type":"change","z":"5d1597b5.fb5dc8","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"device","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":2060,"wires":[["3abdda7.9322726"]]},{"id":"2bc078c5.56fa98","type":"config","z":"5d1597b5.fb5dc8","name":"Home Assistant Import Config","properties":[{"p":"config.blacklist","pt":"flow","to":"DeviceWatch-DeviceStatus,checkInterval,indicatorStatus,dataType,values,lockCodes","tot":"str"},{"p":"config.bLookup","pt":"flow","to":"{\"water\":{\"device_class\":\"moisture\",\"payload_on\":\"wet\",\"payload_off\":\"dry\"},\"smoke\":{\"device_class\":\"smoke\",\"payload_on\":\"detected\",\"payload_off\":\"clear\"},\"motion\":{\"device_class\":\"motion\",\"payload_on\":\"active\",\"payload_off\":\"inactive\"},\"contact\":{\"device_class\":\"opening\",\"payload_on\":\"open\",\"payload_off\":\"closed\"},\"sound\":{\"device_class\":\"sound\",\"payload_on\":\"detected\",\"payload_off\":\"not detected\"},\"tamper\":{\"device_class\":\"vibration\",\"payload_on\":\"detected\",\"payload_off\":\"clear\"},\"carbonMonoxide\":{\"device_class\":\"safety\",\"payload_on\":\"detected\",\"payload_off\":\"clear\"},\"shock\":{\"device_class\":\"safety\",\"payload_on\":\"detected\",\"payload_off\":\"clear\"},\"vibration\":{\"device_class\":\"vibration\",\"payload_on\":\"detected\",\"payload_off\":\"clear\"},\"acceleration\":{\"device_class\":\"moving\",\"payload_on\":\"detected\",\"payload_off\":\"clear\"}}","tot":"json"},{"p":"config.sLookup","pt":"flow","to":"{\"temperature\":{\"unit_of_measurement\":\"°F\"},\"battery\":{\"unit_of_measurement\":\"% B\",\"icon\":\"mdi:battery\"},\"power\":{\"unit_of_measurement\":\"W\"},\"voltage\":{\"unit_of_measurement\":\"V\"},\"humidity\":{\"unit_of_measurement\":\"% H\",\"icon\":\"mdi:water-percent\"},\"energy\":{\"unit_of_measurement\":\"kWh\"},\"illuminance\":{\"unit_of_measurement\":\"lux\",\"icon\":\"mdi:brightness-5\"},\"powerLow\":{\"unit_of_measurement\":\"W\"},\"powerHigh\":{\"unit_of_measurement\":\"W\"},\"current\":{\"unit_of_measurement\":\"A\"},\"currentLow\":{\"unit_of_measurement\":\"A\"},\"currentHigh\":{\"unit_of_measurement\":\"A\"},\"amperage\":{\"unit_of_measurement\":\"A\"}}","tot":"json"},{"p":"config.sDefaults","pt":"flow","to":"{\"force_update\":true}","tot":"json"},{"p":"config.customize","pt":"flow","to":"{}","tot":"json"},{"p":"config.ha_prefix","pt":"flow","to":"homeassistant","tot":"str"},{"p":"config.device_name_prefix","pt":"flow","to":"HE","tot":"str"}],"active":true,"x":190,"y":1840,"wires":[]},{"id":"342ae531.54249a","type":"split","z":"5d1597b5.fb5dc8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"attributeName","x":570,"y":2360,"wires":[["e4ca37f6.5c81b8"]]},{"id":"e4ca37f6.5c81b8","type":"function","z":"5d1597b5.fb5dc8","name":"create home assistant mqtt discovery config message","func":"var config = flow.get('config');\nvar platform = '';\n\n\n//var node_id = msg.device.id + '_' + msg.attributeName;\nvar node_id = msg.req.params.platform + '-' + msg.device.id;\n//var ob_id = msg.device.id + ((['level','switch'].indexOf(msg.attributeName) == -1)? `_${msg.attributeName}` : '');\n//var ob_id = node_id + '_' + msg.attributeName;\nvar ob_id = msg.attributeName;\nvar friendly_name = msg.device.label + ((['level','switch'].indexOf(msg.attributeName) == -1)? ` ${msg.attributeName}` : '');\n\nvar c = {};\n\n\n// Setup base config object with friendly name\nvar e = {\n //\"name\": msg.config.devicePrefix + ' ' + friendly_name,\n \"unique_id\": node_id + '-' + ob_id,\n \"device\": msg.device.config,\n \"name\": friendly_name\n};\n\n//set topic prefix for actual device communication vs the config topic\nvar topicPrefix = [msg.config.mqtt_prefix, msg.req.params.platform, msg.device.id].join('/');\n\n// Add specific settings for the light platform\nif (msg.attributeName == 'level'){\n e.brightness_state_topic= [topicPrefix,'level',msg.config.mqtt_state_suffix].join('/');\n e.brightness_command_topic= [topicPrefix,'level',msg.config.mqtt_cmd_suffix].join('/');\n e.brightness_scale= 99;\n platform = 'light';\n} \n\n// Add specific settings for both light and the switch platform\nif (['level','switch'].indexOf(msg.attributeName) > -1){\n e.command_topic= [topicPrefix,'switch',msg.config.mqtt_cmd_suffix].join('/');\n e.payload_on =\"on\";\n e.payload_off= \"off\";\n e.state_topic = [topicPrefix,'switch',msg.config.mqtt_state_suffix].join('/');\n if (msg.attributeName == 'switch')\n platform = 'switch';\n}\nelse {\n // We're setting up a sensor attribute\n e.state_topic = [topicPrefix,msg.attributeName,msg.config.mqtt_state_suffix].join('/');\n \n // Add specific settings if it matches our binary sensor list\n if(msg.attributeName.match(config.bReg)){\n var binary_lookup = flow.get('config.bLookup');\n Object.assign(e,binary_lookup[msg.attributeName]);\n platform = 'binary_sensor';\n }\n \n // Or Add specific sensors if we match our known sensor type list\n else if(msg.attributeName.match(config.sReg)){\n var sensor_lookup = flow.get('config.sLookup');\n e = Object.assign({},config.sDefaults,sensor_lookup[msg.attributeName],e);\n platform = 'sensor'\n }\n // Or Treat it as a generic sensor\n else {\n platform = 'sensor';\n e = Object.assign({},config.sDefaults,e);\n }\n}\n\n\nvar topic =\n [\n config.ha_prefix,\n platform,\n node_id,\n ob_id,\n 'config'\n ].join('/');\n\n\n\n/*var payload = {\n \"topic\": topic,\n \"content\": e,\n //\"entity\": platform + '.' + ob_id\n \"entity\": platform + '.' + friendly_name.replace(/\\s+/g, '_').replace(/[^A-z0-9\\s]/g,'').toLowerCase()\n};\n*/\n\nmsg.topic = topic;\nmsg.payload = e;\n\nreturn msg;","outputs":1,"noerr":0,"x":760,"y":2460,"wires":[["e9c3174b.4c0378"]]},{"id":"3abdda7.9322726","type":"change","z":"5d1597b5.fb5dc8","name":"Clear Device Split and Prepare to Process Attrs","rules":[{"t":"set","p":"payload","pt":"msg","to":"device.attributes","tot":"msg"},{"t":"delete","p":"parts","pt":"msg"},{"t":"delete","p":"device.attributes","pt":"msg"},{"t":"set","p":"device.config","pt":"msg","to":"{\t\"name\": device.label,\t\"identifiers\": [$join([req.params.platform, device.id], '-')]\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":2120,"wires":[["1d0be0e7.eb2aff","eb6ad0c8.f81a7"]]},{"id":"badc58bb.c0a658","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":670,"y":1980,"wires":[]},{"id":"d915d6c0.eb08b8","type":"function","z":"5d1597b5.fb5dc8","name":"Prepare regex","func":"\nvar config = flow.get('config');\n\nconfig.sReg = '^(' + Object.keys(config.sLookup).join('|') + ')$';\nconfig.bReg = '^(' + Object.keys(config.bLookup).join('|') + ')$';\n\n\n//node.log(`binary lookup is: ${JSON.stringify(config.bLookup)}`);\n//node.log(`sensor lookup is: ${JSON.stringify(config.sLookup)} `);\n\n\n\nflow.set('config', config);\n/*\nfunction filterSwitch(t, cur, ind, arr) {\n //var lightFound = arr.some(checkLight);\n //var lightFound = arr.some(x => x.name === 'level');\n var re= new RegExp('(^|,)'+ cur.name+'(?:,|$)');\n\tif ( (cur.name == \"switch\" && arr.some(x => x.name === 'level')) || (processed.includes(cur.name)) || (flow.get('config.blacklist').match(re)) ){\n \t//node.log(`found switch and checklight is true. t= ${JSON.stringify(t)}`);\n \treturn t;\n }\n else {\n processed.push(cur.name);\n return t.concat(cur);\n }\n}\n\n\n\nvar processed = [];\nmsg.payload = msg.device.attributes.reduce(filterSwitch, []); \n\nnode.log('attributes are: '+ JSON.stringify(msg.payload));\n*/\nreturn msg;","outputs":1,"noerr":0,"x":240,"y":2120,"wires":[["ce2feda2.5d87a"]]},{"id":"f60ca12b.94306","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":410,"y":1920,"wires":[]},{"id":"ba55732a.ddd17","type":"link out","z":"5d1597b5.fb5dc8","name":"","links":["a71ddd30.56933"],"x":1135,"y":2280,"wires":[]},{"id":"e9c3174b.4c0378","type":"json","z":"5d1597b5.fb5dc8","name":"","property":"payload","action":"str","pretty":false,"x":790,"y":2380,"wires":[["ba55732a.ddd17","9210b506.66eec8"]]},{"id":"ec73bb7f.df7a18","type":"change","z":"5d1597b5.fb5dc8","name":"Pass current state to MQTT","rules":[{"t":"set","p":"topic","pt":"msg","to":"$join([config.mqtt_prefix, req.params.platform, device.id,attributeName,config.mqtt_state_suffix],'/')","tot":"jsonata"},{"t":"set","p":"retain","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":820,"y":2280,"wires":[["ba55732a.ddd17"]]},{"id":"fc9f79f2.630ec8","type":"comment","z":"5d1597b5.fb5dc8","name":"Discovery Processing","info":"Process discovery notifications from the bridge and create new devices","x":100,"y":1960,"wires":[]},{"id":"9210b506.66eec8","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":1090,"y":2360,"wires":[]},{"id":"1d0be0e7.eb2aff","type":"function","z":"5d1597b5.fb5dc8","name":"Dedupe lights and switches and remove blacklisted attributes","func":"\nvar blacklist = flow.get('config.blacklist');\n\nfunction filterSwitch(t, cur, ind, arr) {\n var re= new RegExp('(^|,)'+ cur+'(?:,|$)');\n\tif ( (cur == \"switch\" && arr.some(x => x === 'level')) || blacklist.match(re) ){\n \t//node.log(`found switch and checklight is true. t= ${JSON.stringify(t)}`);\n \tdelete msg.payload[cur];\n \treturn t;\n }\n else {\n return t.concat(cur);\n }\n}\n\n\nObject.keys(msg.payload).reduce(filterSwitch, []); \n\n//node.log('attributes are: '+ JSON.stringify(msg.payload));\n\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":2280,"wires":[["342ae531.54249a"]]},{"id":"eb6ad0c8.f81a7","type":"split","z":"5d1597b5.fb5dc8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"attributeName","x":630,"y":2220,"wires":[["ec73bb7f.df7a18"]]},{"id":"3330818b.7064ce","type":"comment","z":"5d1597b5.fb5dc8","name":"Create Home Assistant Discovery MQTT Entries","info":"","x":240,"y":2200,"wires":[]},{"id":"49ebdba.8462f24","type":"comment","z":"5d1597b5.fb5dc8","name":"Pass current states of attributes if we have them","info":"","x":960,"y":2200,"wires":[]},{"id":"7625a9c1.bae908","type":"debug","z":"5d1597b5.fb5dc8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":440,"y":1480,"wires":[]},{"id":"bf1e703c.9276e","type":"comment","z":"5d1597b5.fb5dc8","name":"","info":"","x":120,"y":40,"wires":[]},{"id":"d637a738.68b148","type":"mqtt-broker","z":"","name":"hassio","broker":"hassio","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"fe9107d9.d7c468","type":"websocket-client","z":"","path":"ws://hubitat/eventsocket","tls":"","wholemsg":"false"}]
3 Likes

Hey thanks @chris, I have tried this and it's working pretty well. I previously created a similar nodeRED flow converting the 'homie3' MQTT discovery protocol into HomeAssistant's discovery protocol (homie3 is a vendor agnostic alternative to the HA MQTT discovery protocol).
This was really to link the Athom Homey home controller into HA, there is an app that does the reverse too i.e. HA devices into Homey.

Very interested in this Chris as I would like the same. I am not sure it will be too easy via hub link - even though that's ideally where it should be done to keep things tidy. Such a shame to have to create yet another Hubitat link app that does almost the same things .. via virtual devices.

keep me posted... Kevin

Thank you very much for sharing Chris! I've only been running it for a few hours now but already like the results. I had quite a few issues with the MQTT bridge Hubitat-HomeAssistant a few weeks back which made me try local HA zha and z-wave. I upgraded to the lastest Hassio and it messed up my zigbee buttons (which barely worked to begin with). So I decided to put the hubitat (Nortek) USB zigbee stick back in the hubitat and try to make them talk to each other again. And here we are.... Thanks again!

@chris
This is brilliant
I wish to keep my NodeRed installation on my HA and use for interface and then hubitat to slowly replace the WINK2.... but if I can run both alongside eachother with the HA controlling both = heaven.

HI @chris
I imported the NodeRed flow into my HA installation and I see the config node being set as unknown.
"This node is a type unknown to your installation of Node-RED."
What am I missing for a pallet?

Ok I tried installing the following:

That should be the right one. I use that node to make it easier to share things, but setting flow variables in one place right at startup without having to edit any files.

1 Like

Does this go from:
Hubitat Maker API -> Node-Red -> MQTT Broker -> Home Assistant?
Trying to follow this.
Is it bidirectional?

1 Like

@chris does this work with the Mosquitto broker as well on HA?
also echoing @keithcroshaw here is it bidirectional?

Thanks @chris this is working perfectly. Probably a bit late but yes communication is bi-directional. In addition to what chris mentioned you should also make sure that your base topic matches the mqtt discovery prefix in homeassistant. That caught me out because I was already using devices that publish to 'homeassistant' rather than 'ha'. Changed that and everything is working great. Communication is very fast.

are there an video guides that anyone has seen for hubitat/nodered?

Now this is for Home Assistant.
But I can't imagine it being much different for however you've got HE integrated with Node-Red.

Edit: Also there's a nice custom component for bringing Hubitat devices into Home Assistant, which is how I run everything lately.

1 Like

To do what, exactly, with Hubitat in node-red?

There is the Hubitat node set (another thread in here on that topic), there is manual maker API integration, there is manual event socket integration...

It is a big topic with a lot of permutations.

1 Like

I was looking for a visual way to build rules

I've used Node Red on Home Assistant on a Pi. It's absolutely superb in terms of visualising the logic flow and cut/copy/paste of nodes and flows. But I did find the message passing concept of it a it hard to understand and get to grips with.

If there was a Hubitat app that worked like Node Red visually and in terms of editing but with all the command options of Rule Machine that would be the perfect solution I reckon. I know that can sort of be achieved by using Node Red on another machine and linking devices via MQTT or such, but I tried that and found it got way too complicated and its another machine you need to backup/sort out a ups for etc etc.

2 Likes

I much prefer doing my logic in node-red over RM. Although RM is super impressive as an app - I certainly couldn't make a better one.

Sometimes I do it in node blocks, but just as often I define the input/output in node blocks and then do the logic in a function node w/JavaScript.

I just prefer it that way as it makes editing SUPER fast and I can copy/paste at will.

I use NodeRED a lot it has really great plugins and it's also really well integrated with MQTT. I like the visual flow approach and perhaps RM might be able to leverage 'Blockly' or similar.

However with a lot of flows in NodeRed , and inparticular with a lot of nodes in one displayed flow it can quickly get messy and confusing. I try and use as many segmented flow tabs as possible..

This for example is just one tab/flow handling my boiler 7 segment LED display decoding to numeric (decimal)

2 Likes

Yikes!

1 Like