Node-RED as Alternative Rule Engine

So this one is a bit tricky. I think the actual solution needs to be a new type of node, that is intended specifically for the log function. Or perhaps it needs to be added as another type of payload or msg (forgive my use of the terms). It should be Send Command, Post Update, and Log {type(info, debug, etc)}. Unfortunately using the item and trying to trigger a rule off this to post the logging won’t work. I tried this for something else and you run into issues when stuff fires off too fast. Honestly, it’s not extremely necessary, but I do like to use it as a way to easily identify in my logs when certain rules are running as mentioned for various purposes. Logging files or from the debug inside the NR interface would be of no help as it needs to appear in OpenHAB.log or Events.log like so (output from frontail):

2017-09-26 19:47:46.004 [vent.ItemStateChangedEvent] - master_bathroom_scene changed from 1.0 to 2.0
2017-09-26 19:47:46.030 [ome.event.ItemCommandEvent] - Item 'left_vanity_switch' received command OFF
==> /var/log/openhab2/openhab.log <==
2017-09-26 19:47:46.035 [INFO ] [rthome.model.script.rules.automation] - Master Bathroom - All OFF rule executed (1 Tap DOWN)
==> /var/log/openhab2/events.log <==
2017-09-26 19:47:46.051 [ome.event.ItemCommandEvent] - Item 'right_vanity_switch' received command OFF
2017-09-26 19:47:46.065 [ome.event.ItemCommandEvent] - Item 'master_bathroom_dimmer' received command 0
2017-09-26 19:47:46.082 [vent.ItemStateChangedEvent] - master_bathroom_dimmer changed from 100 to 0
2017-09-26 19:47:46.083 [GroupItemStateChangedEvent] - g_master_bathroom changed from ON to OFF through master_bathroom_dimmer
2017-09-26 19:47:46.088 [vent.ItemStateChangedEvent] - master_bathroom_scene changed from 2.0 to 2.1

Probably helps to provide a sample rule:

rule "Office Dimmer Scene"
when
  Item office_scene changed
then
{
  switch(office_scene.state){
    case 2.1 : {}
    case 1.3 : {
      sendCommand(office_dimmer, 100)
        logInfo("rules.automation", "Office Dimmer set to 100 - (2 Taps UP)")
      postUpdate(office_scene, 2.1)
    }
    case 2.3 : {
      sendCommand(office_dimmer, 1)
        logInfo("rules.automation", "Office Dimmer set to 1 - (2 Taps DOWN)")
      postUpdate(office_scene, 2.1)
    }
  }
}
end

========> Side note - you can see the use of the logInfo part I spoke about in the first section
So what I’m doing is if I see the 2 taps up, I turn the dimmer to 100%, if I see the 2 taps down, I turn the dimmer to 1%. My understanding of rules processing, first case match goes to end so I put the 2.1 as a catch to “expedite” the rules. Though it may just be completely unnecessary.

My goal here though, is to sendCommand to the dimmer, log an info event, and THEN, send a postUpdate to the scene state to reset to 2.1. I’ll spare you the boring explanation of why 2.1, but the point is if I walk into the office and want to use the Dim scene, then later turn the light to 100% using OH instead without touching the switch, then try to use the switch scene (taps down) again - it won’t work if the scene hasn’t been reset. Funny way our switches work - if you change the actual light item (or channel), it won’t affect the scene channel/item.

Should this just be as I’ve designed now?

Ok, I see so if you need to evaluate against WHICH device triggered the rule to fire, then you need to do some JSON manipulation to call it out? Or will this come from the msg.payload? Again, samples probably help but I think you have the right idea here already:

rule "Presence Based Automation"
when
  Item virtual_user1 changed or
  Item virtual_user2 changed or
  Item virtual_user3 changed
then

Ok, I think that does help. I’m still trying to wrap my head around and play around with the translation from one node to the next, and more specifically understanding the outputs. I’m having to put in a bunch of the debug pieces to see the output and start to piece it together. My main reason to want to understand this is that part someone suggested about HomeKit could be a FANTASTIC solution to a few of the items I have that don’t respond as well with the OH items - specifically the Thermostats and some other non-generic items. If you manage to get that working, please do share!! But I don’t have high hopes as I believe I saw you indicate you’re on Android :wink:

I imagine it should look something like this:
42 PM
But I need to figure out the way to translate the msg.payload from OH format to the necessary HomeKit format, and then back again into OH. Once I get that, this will be CLUTCH!

PS - Thanks for all the effort in putting together this little tutorial! This is fantastic!!

Oh and one more side question - organization. Is it advised to create a new “Flow” for each rule? Or can/should I put multiple similar rules into one “Flow”? I use the quotes on the term flow as I really see them as just tabs here, but I know it’s likely more than that as I see Flows have an ID, and I saw in some instances you seem to have included the flows you mentioned in a one liner type of format - presumably an export type capability?

That is most likely not possible, unless they exposed the logInfo to the REST api since that is what the OH nodes are tied to.

I’m assuming you mean that it will inject the log message into OH logs (which I just tested and works fine) but that the message gets logged before the device triggers? If so you could always put a slight delay in front of your log message in Node-RED to make sure it comes out later though if you are only trying to figure out if your rule fired or not I’m not sure why the sequence in your log file is of issue. As far as the need to be in a single file, personally I actually break my OH into separate log files already for mqtt and zwave so I can turn on debug w/o over saturating my main log file. I then added the node-red logging and I just look at time stamps across the various files to figure out timing. A little more work on my part but not that big of a deal.

That looks about right from what I see you trying to do (assuming your items are all defined correctly). I don’t see you changing the msg.payload back to 2.1 before sending to the Office Reset unless you are doing it in the outbound node. I think you are good for not triggering a circular loop when changing the office scene back to 2.1 (which it looks like you trapped in your rule and left off your case switch in Node-RED so should be fine).

To add your logging to this, what I would do is just add another OH node to each change node that sends a message to a virtual logging string and then have a rule that fires off that string to insert the message into your OH log. So it would look something like this (remember I use mqtt not OH nodes now but same idea):

With the appropriate items defined and a rule like this:

rule "Logging"
when
	Item vLogMsg changed
then
    logInfo(vLogTopic.state.toString, vLogMsg.state.toString)
end

You could also attach your “Office Reset Scene” node (with the appropriate payload as per above) after the node that changes the vLogMsg item to get the sequence to follow as well.

Actually all you have to do using the OH Nodes is to use the msg.item in your switch rules to pull the item name.(not the item label) and then if you want to use a simpler name just change the topic or payload based on mapping the msg.item name (let me know if you want an example).

Correct, Android guy here so no need for HomeKit. I found for some of the other items I deal with I can find interfaces either direct to mqtt (hence my switch over), direct through Node-RED or now I’ve figured out how to interface directly into node.js so can get to them if someone wrote an interface there but no one wrote a wrapper for Node-RED yet. Sorry I can’t be of help there.

Personally, I group rules by “Flow” my flows tend to tie to specific physical rooms or groups of flows.

Most of my flows (also not sure why the use that word interchangable between a linked group of nodes and the tabs. Here I mean the groups of nodes) are isolated and I have multiple per tab. For this tutorial and most my responses I mock them up for simplicity to screen shot them. Here’s an example of what one of my tabs looks like if that helps to clarify:

The more I work with Node-RED the more I love it. It’s much more intuitive and better to debug than the native rule engine with its very special language.

I often used the functions changedSince oder historicState, like in this example:

                        if (! PlayerPaused.changedSince(now.minusMinutes(30), "influxdb") {
                            sendCommand(HomeEntertainmentMainSwitch,OFF)
                            sendCommand(BeamerSwitch,OFF)
                            sendCommand(LivingroomRenderer,OFF)
                            logInfo("Entertainment","Player on Pause for more than 30 minutes, powering off")
                    }

Is there a way to copy such a functionality in Node-RED?

Another question is, how can I e.g. count members of a group having a specific state like with these statements in xTend:

                if(gIndoorMotion.members.filter(m | m.changedSince(now.minusMinutes(presence_timeout), "influxdb")).size == 0) {
                    motion_occured = false
                    logInfo("PresenceCheck", "No motion occured during the last " + presence_timeout + " minutes")
            }

If those functions could be integrated in Node-RED I would start migrating all my rules in a moment.
If that is not possible I have the idea of using “proxy” items in Node-RED to set to then afterwards trigger a rule in xTend, use the functions in question and then to set another “proxy” item to a specific value to be handled in Node-RED again. But that would not be very comfortable and my intention is to replace xTend totally.

What I would do is use a flow variable to store your old state and then use a Trigger node to fire your Off command @ 30 minutes (I don’t know what command comes from PlayerPaused to tell you it’s no longer paused so put it in as ??? and I now use mqtt but you can use the OH nodes instead)

[{"id":"a43d6014.e0288","type":"mqtt in","z":"1535d28d.7fd35d","name":"PlayerPaused","topic":"","qos":"2","broker":"35e878ff.e53308","x":426,"y":613,"wires":[["ed81ef67.482e2","d4e93074.de7a3"]]},{"id":"ed81ef67.482e2","type":"change","z":"1535d28d.7fd35d","name":"playerPausedOld","rules":[{"t":"set","p":"playerPausedOld","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":572,"wires":[[]]},{"id":"d4e93074.de7a3","type":"trigger","z":"1535d28d.7fd35d","op1":"","op2":"OFF","op1type":"nul","op2type":"str","duration":"30","extend":false,"units":"min","reset":"?????","name":"30 Minute Off","x":684,"y":625,"wires":[["896ca1c2.2b99","d3541aaf.22a328","9586ea6d.2e41a8"]]},{"id":"896ca1c2.2b99","type":"mqtt out","z":"1535d28d.7fd35d","name":"HomeEntertainmentMainSwitch","topic":"","qos":"","retain":"","broker":"35e878ff.e53308","x":970,"y":567,"wires":[]},{"id":"d3541aaf.22a328","type":"mqtt out","z":"1535d28d.7fd35d","name":"BeamerSwitch","topic":"","qos":"","retain":"","broker":"35e878ff.e53308","x":914,"y":620,"wires":[]},{"id":"9586ea6d.2e41a8","type":"mqtt out","z":"1535d28d.7fd35d","name":"LivingroomRenderer","topic":"","qos":"","retain":"","broker":"35e878ff.e53308","x":934,"y":670,"wires":[]},{"id":"35e878ff.e53308","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"nodered","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}]

.

Use can probably use flow variables (haven’t used groups like you are so hard to suggest an approach) or as you identified use proxy items to carry information between Node-RED and rules. I’ve had to do that for a few rules that can only be captured in OH.

Well the advantage would mainly be you would be able to use other types in your IOS Home application like fans, garage door openers … (there is a fairly long list apple already supports) and as a result ask siri to “turn the fan on” or “open the garage”.

Good news is I managed to make it work, so for those interested have a look at this post
https://community.openhab.org/t/other-homekit-types-through-red-node/34002/3

Glad you were able to get it to work and add to the depth of knowledge on extending Node-RED with OH. I’ll have to take a look to see if it fills any gaps, but since I don’t use Siri not sure it it will make sense for me.

Any way to check switch status without overwriting the payload?
Second “if statement” example does what I want, there is the flow, then it comes to “openhab2 get” node to see if the “override” switch is on or off. But the thing is that the message gets overwrite by the node, and I would like to keep it, while still checking the switch state?
F.eks. I want to send notifications, where the message is in the payload, but I also want to prevent sending it if the openhab item is on/off (using openhab2-get node).
So basically

  1. msg.payload= “Waterleak!” ->
  2. openhab2-get check if “silence notification” is ON ->
  3. send email with msg.payload (from point 1.)

I would really like to avoid keeping track of switch values if possible, as I sometimes just need to go in and change something, and often would variable be outdated.

Any tips about this? I was thinking to save original msg globally before the check, then after the check to read it, but it doesn’t look very elegant (but then again, if it works…)

I can think of a couple of approaches:

  • One would be to store the original payload into a flow variable like you were thinking and then switch back the payload to that variable if it passes your if statement.
  • Another option would be to use an if statement ahead of your check (so you are only checking if msg.payload == “Waterleak!” then if your openhab2-get check returns ==“ON” use a change node to set the payload to Waterleak ahead of your notification. Unless the value of your original payload is going to vary there is no need to use a variable.
  • One last way that gets you to a single node post openhab2-get is to use a delay node where it sends Nothing for the first message, has a short or 0 second delay and sends the “Waterleak” payload unless it gets a reset message of “OFF” (so basically using an if != “OFF” send payload) Kind of cludgy but would only take one node vs. two.

I tried figuring out if there was an easy way to add to the json object to include a msg.payload.notification value but just using a change node to move that the original message payload to that part didn’t work. Might be able to convert to an array object but I haven’t yet played with that enough to fully understand how arrays work using join/splits.

Thanks, it looks like option one is most suitable for my scenario, as I would actually need to store several things beside payload (custom elements like topic, priority and maybe some others).

I am building some “communication” flow/subflow that has a link as input, and the messages can come from various other flows, and then be separated by priority (alert, notification, log…) and have some few other parameters as well. That is why option 2 is less desired, I would like it more contained and centralized.

It would be awesome if “openhab2-get” node could append original message somehow inside the new payload, but i guess temporarily storing old message before and restoring it after contacting openhab will (have to) work just fine :slight_smile:

If someone else needs this scenario, it is one node that stores the original message with a function

flow.set(‘tempOrgMsg’,JSON.stringify(msg));
return msg;

then you check with openhab2-get if switch is on, after that you have a switch that stops the payload if it is OFF (like the example on the beginning)

[{“id”:“9b511355.08ed4”,“type”:“switch”,“z”:“26012d94.6ffd42”,“name”:“Current Plug state”,“property”:“payload.state”,“propertyType”:“msg”,“rules”:[{“t”:“eq”,“v”:“OFF”,“vt”:“str”},{“t”:“eq”,“v”:“ON”,“vt”:“str”}],“checkall”:“true”,“outputs”:2,“x”:770,“y”:540,“wires”:[[“15b0f703.d6ed79”],],“outputLabels”:[“Is OFF”,“Is ON”]}]

and then you read back original message with a function

var msg = JSON.parse(flow.get(‘tempOldMsg’));
return msg;

Works fine with my scenario, your mileage may vary

1 Like

Looks like you found a pretty simple approach. I got thinking later that these nodes might be of use: https://flows.nodered.org/node/norelite

@dakipro just saw this which might be useful
https://www.npmjs.com/package/node-red-contrib-batcher

I saw that one before for some other application (I want to collect and send log-type messages once a day), but it looks I have to use database for that (mysql, because that I know).

Not sure how link you shared applies for checking if a switch is on/off in openhab? If I understand from description, it just collects messages and when buffer is full it sends them as one message. It could perhaps hold messages until a switch is triggered back?
Or were you referring to some other scenario?

Anyway, since I am going to use database for log-type messages, I might as well log all communication and send it with the email. Just for a control, and if I was “offline” with the switch I would get missed communication the next day.

I would like daily report of activity, I like the way home-assistant does that with history overview https://home-assistant.io/components/history/
but that is perhaps separate project from node-red :slight_smile: (one could install home-assistant just for that functionality and monitor mqtt)

I saw it as a potential option for not needing to use variables to hold your message while you check staus. Since this allows you to store the two messages in an array which you can then pull them back out of.

ok, so that would then go in “parallel” on the flow, when message comes in the flow it would at the same time check for openhab, store both messages in this node, and then catch/compare them in another if/else statement? Not sure what would happen if few messages come at the same time, i just started with node-red, still getting used to asynch way of thinking, sorry if it is obvious

Anyway, local flow variable solution is pretty simple and it is just a small amount of data that is stored in it (small json), so I think I will continie with it :slight_smile: might even create two simple custom store-retrieve nodes, to avoid writing 3 lines of codes in function in case i need to use it again :slight_smile:

but it would be awesome if openhab2-get could forward original message within its flow

If my memory serves me right, there was some discussion here:

At one point about trying to create a switch/get node in the openhab2 contrib nodes. I switched from them to mqtt so haven’t been engaged in any discussion. You could suggest it to the maintainer of those nodes.

if anyone needs it, openhab2-get now does forward original message with its flow as msg.payload_in, which significantly simplifies things. You just need to parse/process data once after fetching the item (as now both messages are available).

You need to upgrade openhab2 nodes to the latest version for this (there is a little issue with automatic update if you installed node-red via openhabian script but there is a “fix” https://github.com/openhab/openhabian/issues/213#issuecomment-341232300 )

I’ve created a more advanced version of the input node, to make my NodeRED flows less bloated. I’ve contacted the original creator and asked if he/she would be open for adding it to the plugin.

This is what you have to do when using the current original input node to subscribe to a change in state for a sensor:

So this is what my input node requires to accomplish the same:

And the configuration screen looks like this (to give an idea of what it can do):

My version of the more advanced input node is on github, but it’s not an official plugin so using it requires some manual installation action (not recommended though). If you’d like to see this added tot the openhab2 nodered plugin you can help out here.

2 Likes