SONOFF Door sensor to pulse counter

Guys, i stuck here: using NodeRED in Homa Assisstant system (HAOS). I managed to implement zigbee2mqtt gateway so i can see temperature and humidity from on of the sonoff sensors. I have SONOFF door/window sensor as well. Slightly modified - i removed reed switch and soldered different reed switch - the one that matches my domestic gas supply meter. So now, on every 0.1 m3 of gas consumption i am receiving mqtt event. My problem is to convert it to usable data and present daily, weekly, monthly, yearly gas consumption data and trend it. For the moment SONOFF door sensor is giving me object message like:
zigbee2mqtt/gas_meter : msg.payload : Object

object

battery: 100

battery_low: false

contact: false

linkquality: 64

tamper: false

voltage: 3000

The usable bit is "contact" which is changing from true to false everytime gas meter revolves.

How to extract false->true->false change and use it to increment global variable that will hold total gas volume and child variables that contain daily, weekly, etc data? Do we have a nice node that is designed to do similar things? Thank you for any help.

  1. you can address the item by copying its path (see blurb below)
  2. use that path in a switch node - to test it "is true"
  3. increment your global variable.

input > switch node -> increment.


blurb

There’s a great page in the docs (Working with messages : Node-RED) that will explain how to use the debug panel to find the right path to any data item.

Pay particular attention to the part about the buttons that appear under your mouse pointer when you over hover a debug message property in the sidebar.

BX00Cy7yHi

You can use a filter node to only allow change of payload.contact to pass, then convert the boolean to a number and add to global/flow var.
e.g.

[{"id":"1455b1df33f53d9c","type":"rbe","z":"d1395164b4eec73e","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":false,"property":"payload.contact","topi":"topic","x":330,"y":5760,"wires":[["1b12b62c8be69a4a"]]},{"id":"1b12b62c8be69a4a","type":"change","z":"d1395164b4eec73e","name":"","rules":[{"t":"set","p":"temp_count","pt":"flow","to":"$sum([$flowContext(\"temp_count\")]) + $number($$.payload.contact)","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"temp_count","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":5760,"wires":[["1e4ee8196628f73d"]]},{"id":"1e4ee8196628f73d","type":"debug","z":"d1395164b4eec73e","name":"debug 2479","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":530,"y":5820,"wires":[]},{"id":"2dac0a1c3d22ee99","type":"inject","z":"d1395164b4eec73e","name":"true","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"battery\":100,\"battery_low\":false,\"contact\":true,\"linkquality\":64,\"tamper\":false,\"voltage\":3000}","payloadType":"json","x":150,"y":5720,"wires":[["1455b1df33f53d9c"]]},{"id":"da13e457cd5b1f10","type":"inject","z":"d1395164b4eec73e","name":"false","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{     \"battery\": 100,     \"battery_low\": false,     \"contact\": false,     \"linkquality\": 64,     \"tamper\": false,     \"voltage\": 3000 }","payloadType":"json","x":150,"y":5800,"wires":[["1455b1df33f53d9c"]]}]

Thank you for response. I try to understand your notation E1cid:
$sum([$flowContext("temp_count")]) + $number($$.payload.contact)

Bloody hell, what kind of language is it? Where can i read about this language? Why so many $?

1 Like

The language is JSONata JSONata Documentation · JSONata

I will break it down

$flowContext("temp_count") references the flow context named "temp_count"
$sum([.......]) is a trick to deal with a undefined context variable ( basically first run). It will add the array [ ...] creates, as temp_count is undefined first time, $sum() will ignore null and undefined vars and treat them as 0.
$$.payload.contact references msg.payload.contact which is a boolean.
$number($$.payload.contact)converts the boolean to a number, either 0 or 1.

The + adds the context var to the new msg.payload.contact.

Hope that is helpful.

You can also do it in a function node if you wish.

2 Likes

wow, thank you. i'd prefer function node as it looks for me easier to understand/write code in java script. Java Script is new to me as well so after C++ it is taking me time to digest it.

No offence to anyone who uses or prefers JSONata, but I am in the other camp - preferring to do data manipulation in JS.

I find JS is far easier to read, there are an order of magnitude more developers who know JS vs JSONata, JS is almost always faster than JSONata, the JS editor has much richer help and last, but certainly not least, I simply cannot get to grips with JSONata for much more than simple manipulations.

YMMV.

PS: here is how I'd do it based on my suggestion (post #2 in this thread)

hWRmIPc1nt

[{"id":"1455b1df33f53d9c","type":"rbe","z":"3642c7ee286f9c17","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":false,"property":"payload.contact","topi":"topic","x":910,"y":380,"wires":[["2f67febae08fe331"]]},{"id":"1e4ee8196628f73d","type":"debug","z":"3642c7ee286f9c17","name":"complete msg","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1220,"y":440,"wires":[]},{"id":"2dac0a1c3d22ee99","type":"inject","z":"3642c7ee286f9c17","name":"true","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"battery\":100,\"battery_low\":false,\"contact\":true,\"linkquality\":64,\"tamper\":false,\"voltage\":3000}","payloadType":"json","x":750,"y":340,"wires":[["1455b1df33f53d9c"]]},{"id":"da13e457cd5b1f10","type":"inject","z":"3642c7ee286f9c17","name":"false","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{     \"battery\": 100,     \"battery_low\": false,     \"contact\": false,     \"linkquality\": 64,     \"tamper\": false,     \"voltage\": 3000 }","payloadType":"json","x":750,"y":380,"wires":[["1455b1df33f53d9c"]]},{"id":"704f852b3b6204d2","type":"function","z":"3642c7ee286f9c17","name":"resetable counter","func":"let count = context.get('count') || 0\ncount++\n\nif (msg.reset) {\n    count = 0\n    node.status({});\n} else {\n    node.status({fill:\"green\",shape:\"dot\",text:\"count:\" + count});\n}\n\ncontext.set('count', count)\n\nif (!msg.reset) {\n    msg.count = count\n    return msg\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":990,"y":440,"wires":[["1e4ee8196628f73d","7cf195ce14c87f11"]]},{"id":"7cf195ce14c87f11","type":"debug","z":"3642c7ee286f9c17","name":"msg.count","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"count ? count : 0","statusType":"jsonata","x":1210,"y":480,"wires":[]},{"id":"6bbd0c05aaea631d","type":"inject","z":"3642c7ee286f9c17","name":"reset","props":[{"p":"reset","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":750,"y":480,"wires":[["704f852b3b6204d2","7cf195ce14c87f11"]]},{"id":"2f67febae08fe331","type":"switch","z":"3642c7ee286f9c17","name":"payload.contact === true?","property":"payload.contact","propertyType":"msg","rules":[{"t":"true"}],"checkall":"true","repair":false,"outputs":1,"x":1090,"y":380,"wires":[["704f852b3b6204d2"]]}]

Here is my model that I use on my pir sensors so I can see how many times they have been activated.

[{"id":"555a36692e710e71","type":"function","z":"36262c6c92423eb2","name":"pirCount","func":"// On Start message\n\nlet pirCount = context.get('pirCount')\nif(msg.payload === true) {\n    pirCount +=1\n}\nif(msg.payload === 'reset') {\n    pirCount = 0\n}\nnode.status({ fill: \"red\", shape: \"ring\", text: pirCount })\ncontext.set('pirCount', pirCount)\n","outputs":0,"timeout":"","noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is started.\n\nlet pirCount = context.get('pirCount') ?? 0\nnode.status({ fill: \"red\", shape: \"ring\", text: pirCount })\ncontext.set('pirCount', pirCount)\n","finalize":"","libs":[],"x":500,"y":540,"wires":[]},{"id":"9bc660f0e28db2de","type":"inject","z":"36262c6c92423eb2","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":290,"y":500,"wires":[["90ca37ebe7630041"]]},{"id":"1907b7ffbe4cc4d4","type":"inject","z":"36262c6c92423eb2","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":290,"y":540,"wires":[["90ca37ebe7630041"]]},{"id":"d3b34545a1300e61","type":"inject","z":"36262c6c92423eb2","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"reset","payloadType":"str","x":290,"y":580,"wires":[["90ca37ebe7630041"]]},{"id":"90ca37ebe7630041","type":"junction","z":"36262c6c92423eb2","x":380,"y":540,"wires":[["555a36692e710e71"]]}]

Guys, how about creating something in NodeRED, something that will capture SONOFF contact change and based on that update some variable accesible from HA using Utility Meter intergration? Utility Meter - Home Assistant

Then the rest to show statistics is done in Utility Meter integration in HA.
But how to use NR to receive and format data to feed HA integration?