Node-RED nodes for hubitat

I won't use the websocket connection. It is unsupported by hubitat, and could change at any time (although it has not in quite some time). I don't need the 20ms it saves versus non-websocket.

EDIT: Oh, and some event data doesn't come across at all when using the websocket (e.g. some lock code info).

2 Likes

Another reason for multicast. Maybe a future release!

I found I also had to de-dup some event data using rbe nodes. FWIW, I stopped using the websocket connection as well.

1 Like

Has anyone developed Node-Red equivalent of zone motion controller? I would like to represent the same structure in node-red because I want to be able to tune the parameters dynamically.

For example I want to be able to change the delay and reset parameters by the time of day.

Does anyone have anything similar i can look at for reference? The closest I can get at the moment is to run a join on the collection of motion sensors. But there is no certainty in that model for how many sensors are active or not.

I’d like to be able to do this without writing a custom node but if that is what needs to happen I’ll get there eventually.

Cheers

Yes, I've taken something from @jason-lane and also @JasonJoel.

It's a subflow that I've worked up (stolen) from their ideas. It records the number of active motions sensors and will not go inactive until all sensors report inactive.

My long term goal is to have it so that I can change the length of delay off (based on time of day), but have not reached that point yet, mainly because the time of day changes in different rooms (late in the evening in the kitchen, early in the hallway etc).

You will need the unsafe function node. Active (lights on) goes out Output 1, inactive (lights off) Output 2.

[{"id":"41a0d0c3.e686a","type":"change","z":"acbe928e.120e6","name":"stop","rules":[{"t":"set","p":"payload","pt":"msg","to":"stop","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":260,"wires":[["b7925a95.e71ca8"]]},{"id":"b7925a95.e71ca8","type":"stoptimer-varidelay","z":"acbe928e.120e6","duration":"time_delay_minutes","durationType":"env","units":"Minute","payloadtype":"num","payloadval":"0","name":"Timer","reporting":"last_minute_seconds","persist":true,"x":970,"y":320,"wires":[["3ad07871.0639e8"],[],[]]},{"id":"3ad07871.0639e8","type":"delay","z":"acbe928e.120e6","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":1210,"y":220,"wires":[["7bf14e2d.695bd"]]},{"id":"7c13f4b6.50f13c","type":"unsafe-function","z":"acbe928e.120e6","name":"All sensors inactive","func":"// Output 1 - Active event\n// Output 2 - Inactive event\n\n// node.warn(\"Processing Motion Event\");\n\nvar motionStatus = null;\nvar activeCount = 0;\n\nvar motionSensors = flow.get(\"motionSensors\");\nvar automationActive = flow.get(\"automationActive\");\n\nif (typeof motionSensors == \"undefined\") {\n    // node.warn(\"flow.motionSensors not yet defined. Creating new.\");\n    var motionSensors = {};\n}\n\nif (typeof msg.payload != \"undefined\" && msg.payload !== null) {\n    var deviceId = msg.payload.deviceId;\n}\n\n\nif (typeof deviceId != \"undefined\") {\n    // Receiving a device ID indicates we have received an event, so process it.\n    // Note: 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 motionSensors[deviceId] == \"undefined\") {\n    \t// First time seeing this devie, so add it to the list now\n        // node.warn(\"Device object doesn't exist\");\n        motionSensors[deviceId] = {};\n    }\n\n    var deviceObject = motionSensors[deviceId];\n\n    deviceObject.state = motionStatus;\n    deviceObject.displayName = msg.topic;\n\n    flow.set(\"motionSensors\", motionSensors);\n    \n    // If this is an 'active' event, then pass the msg on now and finish here\n    if (motionStatus == 'active' ) {\n        // node.warn(\"New active event received.\");\n        return [msg, null];\n    }\n}\n\n\nfor (let [deviceId, values] of Object.entries(motionSensors)) {\n    if ( values.state == 'active' ) {\n        activeCount++;\n    }\n}\n\nif (activeCount > 0) {\n    // If any of the sensors are still active, proceed as an 'active' event\n    // node.warn(\"Still active.\");\n    return [msg, null];\n} else {\n    // If all sensors are now inactive, proceed as an 'inactive' event\n    // node.warn(\"Status: inactive.\");\n    return [null, msg];\n}","outputs":2,"noerr":0,"x":570,"y":300,"wires":[["94445edf.4f075"],["b7925a95.e71ca8"]]},{"id":"94445edf.4f075","type":"switch","z":"acbe928e.120e6","name":"","property":"payload.value","propertyType":"msg","rules":[{"t":"eq","v":"active","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":735,"y":220,"wires":[["41a0d0c3.e686a","3ad07871.0639e8"]],"l":false},{"id":"7bf14e2d.695bd","type":"q-gate","z":"acbe928e.120e6","name":"Gate","controlTopic":"control","defaultState":"open","openCmd":"gate_open","closeCmd":"gate_closed","toggleCmd":"toggle","queueCmd":"queue","defaultCmd":"default","triggerCmd":"trigger","flushCmd":"flush","resetCmd":"reset","peekCmd":"peek","dropCmd":"drop","statusCmd":"status","maxQueueLength":"100","keepNewest":false,"qToggle":false,"persist":true,"x":1370,"y":180,"wires":[["2f19a526.a2874a"]]},{"id":"6ad89231.76929c","type":"switch","z":"acbe928e.120e6","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"gate_closed","vt":"str"},{"t":"eq","v":"gate_open","vt":"str"},{"t":"eq","v":"stop","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":4,"x":280,"y":200,"wires":[["7bf14e2d.695bd","aa8c585e.bae7c8","e549dab9.6467a8"],["7bf14e2d.695bd","e549dab9.6467a8"],["41a0d0c3.e686a"],["e549dab9.6467a8"]]},{"id":"2f19a526.a2874a","type":"switch","z":"acbe928e.120e6","name":"","property":"payload.value","propertyType":"msg","rules":[{"t":"eq","v":"active","vt":"str"},{"t":"eq","v":"inactive","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1510,"y":180,"wires":[[],[]]},{"id":"aa8c585e.bae7c8","type":"link out","z":"acbe928e.120e6","name":"","links":["296a6fee.751ce"],"x":375,"y":140,"wires":[]},{"id":"296a6fee.751ce","type":"link in","z":"acbe928e.120e6","name":"","links":["aa8c585e.bae7c8"],"x":1155,"y":280,"wires":[["d47d10af.6250b"]]},{"id":"d47d10af.6250b","type":"change","z":"acbe928e.120e6","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"Closed","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1215,"y":280,"wires":[["d1228c6d.8cdc7"]],"l":false},{"id":"e549dab9.6467a8","type":"q-gate","z":"acbe928e.120e6","name":"Gate","controlTopic":"control","defaultState":"open","openCmd":"gate_open","closeCmd":"gate_closed","toggleCmd":"toggle","queueCmd":"queue","defaultCmd":"default","triggerCmd":"trigger","flushCmd":"flush","resetCmd":"reset","peekCmd":"peek","dropCmd":"drop","statusCmd":"status","maxQueueLength":"100","keepNewest":false,"qToggle":false,"persist":true,"x":410,"y":300,"wires":[["7c13f4b6.50f13c"]]},{"id":"d1228c6d.8cdc7","type":"delay","z":"acbe928e.120e6","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":1320,"y":280,"wires":[[]]}]

I try and have all the logic about open gates happen before the motion events come in, so that it's quicker. The open gate is sent from outside the subflow, and can be anything(s) you want (button press, lux, presence - just change the message to: topic: control, payload: gate_open and change the relevant line in the switch. Likewise for gate_closed).

There's currently one environment variable (time_delay_minutes), which can be set to whatever you want for each separate deployment (default currently 10 mins).

Improvements welcome.

Just a heads up I recently encountered an issue with the unsafe node - of course I've forgotten what it was - problems accessing global variables or something. Once I switched to the regular function node my code started working again..

I think due to the lack of updates it's getting further functionality-wise from the regular function node so it's something to be aware of.

What's the difference between the normal and unsafe function nodes?

Basically unsafe node does not run in a sandbox so it eliminates overhead. However it is potentially less safe due to having direct access to the system.

Remember Little Bobby Tables!

4 Likes

After looking at it some more I think the issue is/was you cannot use "env.get" - as the functionality has been commented out.

I use environment variables a lot for my subflows.

Hmmm... I just double-checked with a quick test function in my sublfow:

node.warn(env.get('TIMEOUT'));

and it seems to work fine :man_shrugging:t3:

Huh.. then I am misremembering. sorry about the confusion. I may add the unsafe function back in just to see what the error was. If I do will let you all know.

1 Like

Just to expand on what @erktrek said... Because the Unsafe Function node isn't sandboxed, it's especially risky if you copy-n-paste a block of code from somebody on the Internet, and you don't go through it yourself to make sure there's nothing malicious in there.

2 Likes

As @mike mentioned, I do have a subflow which does kinda mimic what Zone Motion Controller does, even though that wasn't really what I'd set out to do. I was more aiming to mimic the normal Motion Lighting app, with the ability to use multiple sensors in one room. My subflow does have some level of customizability, and does allow changing parameters on the fly. I actually haven't worked on it for several weeks now, so it's currently in that state of "This isn't quite polished and finalized, but it's been working fine, so I haven't bothered to get back to it, but the code is kinda ugly, and I'm embarrassed to share it on the Internet." :rofl:

However, if you're interested, I will do a quick polish up and share it with you.

Okay I found the weirdness - it has to do with the async send process - node.send(). Apparently instead of sending both messages it just repeats the last one..

Check out this simple example:

The sequence:

The subflow:

The code & result (unsafe & safe function code are same):

EDIT: It is also interesting to note that in the 4 entries on the debug pane the "node.warn()" is reported differently in safe vs unsafe - "function:(warn)" vs "msg: Object". May be a system vs custom node thing.

Ah... Good to know! I don't think I've ever used the async sending before.

@mike interesting, thank you for sharing! It will take me awhile to digest this, especially because I am not familiar with q gate nor stop timer. The unsafe function mode is a neat idea and should be useful especially if I decide to create a dedicated node for this.

@jason-lane of course I would love to see what you have but I have no expectation of polish! This is the internet after all :joy: if you're willing to share I'm sure I would learn something from it.

1 Like

I have never found a problem I couldn't solve without using the unsafe function node - one way or the other.

But I'm weird. I trust my own code less than you guys do I guess. Lol

1 Like

This thread is so long now finding this info is not easy. I had this installed on my node-red docker and it worked great. I just stood up a new pi 4 running hassio, added node-red and what i've noticed is i don't receive any events from hubitat unless i make node-red deploy again then for instance it will read the mode correctly. Put it back on my node-red docker and it again works great, back to the hassio setup with node-red not working. I can see this in the log when I make node-red refresh [info] [hubitat config:Hubitat] Starting endpoint for /hubitat/webhook

I assume you updated the webhook configuration when you switched to the new container? Make sure it is pointed to the right IP and node-red interface port that is accessible from outside of hassio.

Try to connect to the node-red UI from a computer outside of hass-io to verify you have the IP and port right.

Once you verify it, make sure that is where the Maker API post events are configured to go to.

2 Likes

Yes, everything was switched. When i switch it back it works.

Cannot GET /hubitat/webhook is what I get when I go to http://192.168.2.2:1880/hubitat/webhook (new hassio instance) but it does ask me for a login/pass. Same thing for the http://192.168.2.9:1880/hubitat/webhook (docker instance) but not asking for a user/pass. Got me thinking the user/pass request from the hassio version is getting me?? The whole object here is to have one box (pi 4) running my automation and turn off the dell server sucking power