Node-RED nodes for hubitat

Thanks, @fblackburn, this has removed the unnerving messages in my debug sidebar.
:+1:

Node-Red with Hub Mesh token, Can one use the token from Hub Mesh with Node-Red instead of using Maker API? If so, any reason to do one or the other?

1 Like

Nope. Need maker API. I would love it if Hubitat open sourced/documented the message format for hub mesh, but I doubt that will happen.

3 Likes

small fix version 1.5.2 related to the dynamic name feature

  • device: set msg.topic to device label when name is empty
  • command: fix name placeholder when no device selected
6 Likes

Lets see if I can figure out how to share this so that someone else can use it. Exporting from node-red feels unpredictable to me.

This is a subflow which does zone activity aggregation. Notably it will take regular contact switches to trigger zone activity as well as the traditional motion sensors. In my case, doors are placed at 90º angles to the room so covering them with a motion sensor is difficult without adding a dedicated motion sensor. The contact event will auto-reset similar to the way a motion sensor does.

The subflow takes configuration values for contact sensor reset and zone activity reset via topic:config messages, which means that the parameters can be reconfigured depending on the time of day... or whatever you want.

The motion aggregation component is in an unsafe function node, and is mostly code from someone else on this forum/thread. I removed a couple features and implemented them as node-red sequences, and I try to use as few third-party nodes as possible.

[{"id":"4c7c8cd1.6a2a1c","type":"subflow","name":"My Zone Activity Aggregator","info":"","category":"","in":[{"x":100,"y":200,"wires":[{"id":"28720ce3.415504"}]}],"out":[{"x":700,"y":520,"wires":[{"id":"f95fc416.12cdb8","port":"0"}]},{"x":1120,"y":620,"wires":[{"id":"f9fb7202.670248","port":0}]}],"env":[{"name":"Zone Name","type":"str","value":"Motion zone"}],"color":"#DDAA99","status":{"x":1060,"y":840,"wires":[{"id":"baa794b2.a49bd","port":0},{"id":"3adf6856.200d98","port":0},{"id":"6214c0ab.73eb08","port":0}]}},{"id":"37082482.1d726c","type":"unsafe-function","z":"4c7c8cd1.6a2a1c","name":"Motion Aggregator","func":"let motionStatus = null;\nlet activeCount = 0;\n\nlet sensors = context.get(\"sensors\") || {};\nlet wasActive = context.get(\"active\") || false;\nlet deviceId\n\nif (typeof msg.payload != \"undefined\" && msg.payload !== null) {\n    deviceId = msg.payload.deviceId;\n}\n\nif (typeof deviceId != \"undefined\") {\n\n    // If we didn't receive an event, the process was triggered by something else (eg. enabling the automation)\n    motionStatus = msg.payload.value\n\n    if (typeof sensors[deviceId] == \"undefined\") {\n        sensors[deviceId] = {}\n    }\n\n    let deviceObject = sensors[deviceId]\n\n    deviceObject.state = motionStatus\n    deviceObject.displayName = msg.topic\n\n    context.set(\"sensors\", sensors)\n}\n\n\nfor (let [deviceId, values] of Object.entries(sensors)) {\n    if ( values.state == 'active' ) {\n        activeCount++\n    }\n}\n\nconst active = activeCount > 0\n\nnewMessage = {\n  topic: env.get(\"Zone Name\"),\n  payload: active ? \"active\" : \"inactive\",\n  count: activeCount\n}\n\n    context.set(\"active\", active)\n    return newMessage\n","outputs":1,"noerr":0,"x":990,"y":160,"wires":[["1135a6a5.8ffeb9"]]},{"id":"baa794b2.a49bd","type":"change","z":"4c7c8cd1.6a2a1c","name":"Active sensor count","rules":[{"t":"set","p":"payload","pt":"msg","to":"count  & \" \" & payload & \" sensors\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":920,"y":880,"wires":[[]]},{"id":"f9fb7202.670248","type":"trigger","z":"4c7c8cd1.6a2a1c","name":"Activity off delay","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"5","extend":true,"overrideDelay":true,"units":"min","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":1000,"y":620,"wires":[[]]},{"id":"f95fc416.12cdb8","type":"switch","z":"4c7c8cd1.6a2a1c","name":"is active?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"active","vt":"str"},{"t":"eq","v":"inactive","vt":"str"}],"checkall":"false","repair":false,"outputs":2,"x":600,"y":600,"wires":[["bf8561c.27047a"],["4c35d83e.70c12"]]},{"id":"d1fef5e9.1faa","type":"switch","z":"4c7c8cd1.6a2a1c","name":"is active?","property":"count","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":720,"y":860,"wires":[["3adf6856.200d98"],["baa794b2.a49bd"]]},{"id":"3adf6856.200d98","type":"change","z":"4c7c8cd1.6a2a1c","name":"Inactive","rules":[{"t":"set","p":"payload","pt":"msg","to":"\"inactive\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":880,"y":840,"wires":[[]]},{"id":"28720ce3.415504","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Separate config messages","property":"topic","propertyType":"msg","rules":[{"t":"else"},{"t":"eq","v":"config","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":280,"y":200,"wires":[["5e909849.a5ede"],["ab2b2200.580fe"]]},{"id":"bf8561c.27047a","type":"change","z":"4c7c8cd1.6a2a1c","name":"Reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":810,"y":580,"wires":[["f9fb7202.670248"]]},{"id":"5e909849.a5ede","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Separate motion from contact","property":"payload.name","propertyType":"msg","rules":[{"t":"eq","v":"motion","vt":"str"},{"t":"eq","v":"contact","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":690,"y":200,"wires":[["37082482.1d726c"],["ee67eca9.7c9548","83dac1a6.f49bb"]]},{"id":"ee67eca9.7c9548","type":"change","z":"4c7c8cd1.6a2a1c","name":"Active-true","rules":[{"t":"set","p":"payload","pt":"msg","to":"active","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":970,"y":220,"wires":[["de4555d9.03c9f8"]]},{"id":"9dec185d.0fa0b","type":"change","z":"4c7c8cd1.6a2a1c","name":"Active-false","rules":[{"t":"set","p":"payload","pt":"msg","to":"inactive","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1350,"y":260,"wires":[["de4555d9.03c9f8"]]},{"id":"979d8215.69ba18","type":"link out","z":"4c7c8cd1.6a2a1c","name":"Switch Event","links":["b4f2e905.4ec488","c551054e.c3663","db637502.ed7b1"],"x":1535,"y":220,"wires":[]},{"id":"1135a6a5.8ffeb9","type":"link out","z":"4c7c8cd1.6a2a1c","name":"Motion Event","links":["b4f2e905.4ec488","c551054e.c3663"],"x":1195,"y":160,"wires":[]},{"id":"c551054e.c3663","type":"link in","z":"4c7c8cd1.6a2a1c","name":"","links":["1135a6a5.8ffeb9","979d8215.69ba18"],"x":295,"y":840,"wires":[["2d4ae563.a0394a"]]},{"id":"b4f2e905.4ec488","type":"link in","z":"4c7c8cd1.6a2a1c","name":"","links":["1135a6a5.8ffeb9","979d8215.69ba18"],"x":295,"y":600,"wires":[["a6ce7a5b.5718f8"]]},{"id":"ab2b2200.580fe","type":"link out","z":"4c7c8cd1.6a2a1c","name":"Config","links":["e1a2c208.c8ace8"],"x":575,"y":260,"wires":[]},{"id":"e1a2c208.c8ace8","type":"link in","z":"4c7c8cd1.6a2a1c","name":"","links":["ab2b2200.580fe"],"x":295,"y":1120,"wires":[["c4696ddd.05a93"]]},{"id":"7878ae9d.d67828","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Config","info":"","x":650,"y":260,"wires":[]},{"id":"cb4ad9ea.ed3e38","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Motion event","info":"","x":1310,"y":160,"wires":[]},{"id":"acfeb2ce.da0e98","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Sensor event","info":"","x":1650,"y":220,"wires":[]},{"id":"c4696ddd.05a93","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Set config parameter","property":"payload.name","propertyType":"msg","rules":[{"t":"eq","v":"sensor_reset_delay","vt":"str"},{"t":"eq","v":"active_reset_delay","vt":"str"},{"t":"eq","v":"zone_active","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":480,"y":1120,"wires":[["3f0f1369.4b070c"],["b127ab0f.e7777"],["49b85432.3fb394"]]},{"id":"3f0f1369.4b070c","type":"change","z":"4c7c8cd1.6a2a1c","name":"","rules":[{"t":"set","p":"sensor_reset_delay","pt":"flow","to":"payload.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":780,"y":1060,"wires":[[]]},{"id":"b127ab0f.e7777","type":"change","z":"4c7c8cd1.6a2a1c","name":"","rules":[{"t":"set","p":"activity_reset_delay","pt":"flow","to":"payload.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":780,"y":1120,"wires":[[]]},{"id":"83dac1a6.f49bb","type":"change","z":"4c7c8cd1.6a2a1c","name":"Sensor reset delay","rules":[{"t":"set","p":"delay","pt":"msg","to":"sensor_reset_delay","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":990,"y":260,"wires":[["b5799027.c31f78","1f831024.572ef8"]]},{"id":"b5799027.c31f78","type":"debug","z":"4c7c8cd1.6a2a1c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1170,"y":320,"wires":[]},{"id":"1f831024.572ef8","type":"trigger","z":"4c7c8cd1.6a2a1c","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"5","extend":true,"overrideDelay":true,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":1180,"y":260,"wires":[["9dec185d.0fa0b"]]},{"id":"4c35d83e.70c12","type":"change","z":"4c7c8cd1.6a2a1c","name":"Activity reset delay","rules":[{"t":"set","p":"delay","pt":"msg","to":"activity_reset_delay","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":620,"wires":[["f9fb7202.670248"]]},{"id":"7a24866e.db567","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Zone Active","info":"","x":810,"y":520,"wires":[]},{"id":"268891b5.f0e4f6","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Zone Inactive","info":"","x":1070,"y":560,"wires":[]},{"id":"de4555d9.03c9f8","type":"rbe","z":"4c7c8cd1.6a2a1c","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":1475,"y":220,"wires":[["979d8215.69ba18"]],"l":false},{"id":"28fb421d.2d7a2e","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Status text","info":"","x":340,"y":780,"wires":[]},{"id":"3dc117c7.63b51","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Config Parameters","info":"","x":370,"y":1060,"wires":[]},{"id":"2d4ae563.a0394a","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Motion zone active","property":"zone_active","propertyType":"flow","rules":[{"t":"else"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":470,"y":840,"wires":[["6214c0ab.73eb08"],["d1fef5e9.1faa"]]},{"id":"6214c0ab.73eb08","type":"change","z":"4c7c8cd1.6a2a1c","name":"Disabled","rules":[{"t":"set","p":"payload","pt":"msg","to":"\"Disabled\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":880,"y":800,"wires":[[]]},{"id":"49b85432.3fb394","type":"change","z":"4c7c8cd1.6a2a1c","name":"","rules":[{"t":"set","p":"zone_active","pt":"flow","to":"payload.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":760,"y":1180,"wires":[[]]},{"id":"a6ce7a5b.5718f8","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Zone enabled?","property":"zone_active","propertyType":"flow","rules":[{"t":"true"}],"checkall":"false","repair":false,"outputs":1,"x":440,"y":600,"wires":[["f95fc416.12cdb8"]]},{"id":"c90679d.51c6188","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Outputs","info":"","x":330,"y":540,"wires":[]}]

I'm not sure if this picture will be helpful or not, but here it is visually:

3 Likes

I suggest you post this in the Node-Red Flow Samples/Sharing topic.

2 Likes

Question for the group...

Would it be useful to anyone other than me to have an option in the command node to "only send command if different than current state"?

The thought is that there are a lot of times I check the current status of a device and don't send an on/off (for example) command if it is already in that state. Why? Processing power on the node-red side is cheap, and any reduction in hubitat hub and zwave/zigbee mesh traffic is a big plus.

Not sure how useful that would be to others, so thought I would ask before dropping it in Francois' suggestion box. :slight_smile:

Example:
Here I check if the light is off before turning on. With the proposed option you could get rid of the circled device node and switch node, and then let the command node send it (or not) depending on the current state of the device.

It isn't especially difficult to put those 2 extra nodes in there, though, so it is not a huge deal either way to me. Just an idea.

Edit: The downside, though, is commands and state values don't always match... So instead of a single check box it might be required for user to manually specify the state value on which to not send the command. Example - presence... Arrived/departed commands versus states of present/not present...

4 Likes

Yes, a million times yes. I have often thought this. It would be great if it could be incorporated into the HE node.

3 Likes

Another idea I had was a new node type called "deviceId" or "Find Id" - or something like that. User specifies string name of device (either in-node or as msg input), and have it output the deviceId for use in downstream hubitat node msg.deviceId inputs.

Main idea is to make it even easier to make subflows to wrap logic together, without having to manually go look up deviceId values.

1 Like

Yes - good idea!

I know it's not all that hard to work around using another device node etc but that seems like a nice way to simplify things..

1 Like

My first thought was...meh, I haven't needed it though I'd probably find a way to use it. Then you pointed out the benefit of reducing mesh traffic and I perked right up. So, even if for no other benefit, I'll say go for it.

1 Like

In reality it isn't a huge issue unless you are sending a ton of commands (initialization, mode changes [bedtime modes], bad logic, etc are times where that is more likely to happen).

But it is easy enough to do in node-red I just do it to reduce mesh commands. Many (really most/almost all) drivers will send the command on the mesh if on/off/etc are triggered regardless of current state - so this technique does indeed remove commands off the mesh.

How valuable that is obviously depends on volume, the device in question, and health of the mesh.

2 Likes

I asked about this before if I am getting this right

2 Likes

If it could simplify flow of the majority, then we can ask ourselves the question :slight_smile:

It's the challenge of this feature. It would needs to have a mapping of "command value" with "device attribute"
I still have difficulty to imagine: how to implement/expose this feature to the user without adding a layer of complexity for him

Yes you're right, it's not the first time this feature is requested. But these nodes have changed over time:

  • Since 1.3.0, devices logic has been moved to the config node instead of device node (to use global/shared cache)
  • Since 1.4.0 we cache all devices available through Maker API.

With these changes, a lot of technical difficulty have been gone and some of the already requested feature can be easier to do or can be thought of differently

5 Likes

What should be the output of this node?
A list of device with the name requested or the first one?
ex: you have two devices with the same name:

  • bedroom outlet
  • bedroom outlet

Then if you request "bedroom outlet" you will have two devices or only the first one?

Do you want a fuzzy match too?
ex: you have three devices:

  • bedroom outlet 1
  • bedroom outlet 2
  • bedroom light

Then if you request "bedroom" you will have three devices or only the first one or none?

In case we only return the first exact match:
Should be more interesting to have an input attribute for device node? (ex: msg.deviceName)

Limitation:

  • HE allow to have the same name on different devices. Behavior needs to be defined for this case
  • using NR cache would imply to restart flows after editing device name in HE (or new devices)

Agreed. It might not be worth doing at all.

After some thought, the 'easy' way to do this would be to make the user manually enter the attribute and value in a string fields.

"Do not send command when device attribute " + attribute + " is " + value + "."

Then user enters

  • attribute - switch, level, etc
  • value - on, off, 99, etc

But to your point, that does add complexity to the command node, for something that is readily done with just 2 nodes externally today.

I didn't think of that. :slight_smile:

Maybe it can be half automated
send-event

6 Likes

That looks cool! You wouldn't even need to test the related attribute for example the command might be to switch "on" but you could test for hue... seems like even more possibilities doing it this way..

1 Like

It exactly what you said that's scare me. I don't want to add this feature to be used as "logic maker". If you want to do this kind of logic, it definitively nothing to be inside the node. But I can see your enthusiasm :slight_smile:

I'll think about it again for the next few days: how to automate/simplify the logic for this feature

2 Likes

This is a feature of the home assistant "current state" node as well as the "events state" node. I use that functionality almost all the time. It basically builds the switch node right into the main node, or it can be used exactly how you are in the example by not specifying the state and then it sends based on any state. It makes the flows so much cleaner looking which is great for my OCD :slight_smile:

1 Like