Node-Red Flow Samples/Sharing

Anyone seeing issues with their alexa routines (applestrudel) within the last hour ~9pm EST?

Alexa appears to be down

That's what I thought but just wanted to make sure it was not something I did.

Split an array of values into individual arrays

I was looking for an easier way to split an array (without having to loop through the arrays and split them in a function node) and discovered the $spread JSONATA function. Worked brilliantly but for some reason, it would not work for a date field returned from a SQL query on a mySQL database. The folks over on the Node-RED community suggested some other solutions, so thought I would share them here in case it helps someone.

[{"id":"337250983f7905d8","type":"inject","z":"9dd3cdef.2f2f38","name":"","props":[{"p":"payload"},{"p":"props","v":"[\"totalHomeuse\",\"gridToHome\",\"solarToHome\",\"batteryToHome\",\"totalSolar\",\"solarToGrid\",\"solarToBattery\",\"day\"]","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":267.6666564941406,"y":147.99989318847656,"wires":[["da751414a159fccd"]]},{"id":"da751414a159fccd","type":"function","z":"9dd3cdef.2f2f38","name":"generate dummy data (ignore me)","func":"\nconst templateRow = {\n    totalHomeuse: 67.74,\n    gridToHome: 34.62,\n    solarToHome: 21.11,\n    batteryToHome: 12.01,\n    totalSolar: 34.88,\n    solarToGrid: 0.05,\n    solarToBattery: 13.72,\n    day: \"2022-01-17T06:00:00.0002\"\n}\n\nconst data = []\nfor (let index = 0; index < 10; index++) {\n    const element = RED.util.cloneMessage(templateRow);\n    element.totalHomeuse = randomFloat(60,70)\n    element.gridToHome = randomFloat(30,40)\n    element.solarToHome = randomFloat(20,30)\n    element.batteryToHome = randomFloat(10,20)\n    element.totalSolar = randomFloat(30,40)\n    element.solarToGrid = randomFloat(0.01,0.99)\n    element.solarToBattery = randomFloat(11,14)\n    element.day = new Date(Date.now() + ((index+1)*1000*60)).toISOString()\n    data.push(element)\n}\nmsg.payload = data;\n\n\nreturn msg;\n\n\n/**\n* @param {number} min\n* @param {number} max\n*/\nfunction randomFloat(min, max) {\n    return Math.random() * (max - min) + min;\n};","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":505.6666564941406,"y":147.99989318847656,"wires":[["942662580d53e493","e3e67cb9207e5e23","2f1ca195.87b276","ff10a763.0f6b3","a39977a.9ef3d08"]]},{"id":"942662580d53e493","type":"debug","z":"9dd3cdef.2f2f38","name":"Dummy data","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":546.6666564941406,"y":57.99989318847656,"wires":[]},{"id":"e3e67cb9207e5e23","type":"function","z":"9dd3cdef.2f2f38","name":"toArrays - function","func":"\n//Props can be dynamically sent in via the msg object if required (to make it generic)\n/*const props = [\n    \"totalHomeuse\",\n    \"gridToHome\",\n    \"solarToHome\",\n    \"batteryToHome\",\n    \"totalSolar\",\n    \"solarToGrid\",\n    \"solarToBattery\",\n    \"day\"\n];\n*/\nconst props = msg.props\n\nmsg.result = toArrays(msg.payload, props)\nreturn msg;\n\n\n\n\n\n/**\n * Convert object properties in `data` to array of values\n * @param {any[]} data - Data to scan\n * @param {string[]} propNames - property names to collect\n*/\nfunction toArrays(data, propNames) {\n    const result = {};\n    propNames.forEach(p => {\n        result[p] = [];\n        data.forEach(e => {\n            result[p].push(e[p]);\n        })\n    })\n    return result;\n}\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":825.6665954589844,"y":221.99987030029297,"wires":[["66081cdb3d263701"]]},{"id":"2f1ca195.87b276","type":"change","z":"9dd3cdef.2f2f38","name":"Split selected values - JSONATA spread","rules":[{"t":"set","p":"totalHomeuse","pt":"msg","to":"$spread(payload.totalHomeuse)","tot":"jsonata"},{"t":"set","p":"dateTime","pt":"msg","to":"$spread(payload.day)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":895.3333435058594,"y":72.99999237060547,"wires":[["e8504ec0.fc0998"]]},{"id":"ff10a763.0f6b3","type":"change","z":"9dd3cdef.2f2f38","name":"Split selected values - JSONATA keys","rules":[{"t":"set","p":"totalHomeuse","pt":"msg","to":"$spread(payload.totalHomeuse)","tot":"jsonata"},{"t":"set","p":"dateTime","pt":"msg","to":"$$payload.day","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":892.6665954589844,"y":147.66663360595703,"wires":[["9e3e4f84.4bbc2"]]},{"id":"a39977a.9ef3d08","type":"change","z":"9dd3cdef.2f2f38","name":"Split all - JSONATA keys","rules":[{"t":"set","p":"payload","pt":"msg","to":"$keys($$.payload).${$:$lookup($$.payload,$)}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":845.6665954589844,"y":298.6666488647461,"wires":[["4ac93c40.a17184"]]},{"id":"66081cdb3d263701","type":"debug","z":"9dd3cdef.2f2f38","name":"Split all - function","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"result","targetType":"msg","statusVal":"payload","statusType":"auto","x":1033.6666564941406,"y":219.99987030029297,"wires":[]},{"id":"e8504ec0.fc0998","type":"debug","z":"9dd3cdef.2f2f38","name":"After selected - spread","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1198.3333435058594,"y":72.99999237060547,"wires":[]},{"id":"9e3e4f84.4bbc2","type":"debug","z":"9dd3cdef.2f2f38","name":"After selected - keys","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1192.6665954589844,"y":147.66661834716797,"wires":[]},{"id":"4ac93c40.a17184","type":"debug","z":"9dd3cdef.2f2f38","name":"Split all - keys","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1079.6665954589844,"y":298.6666488647461,"wires":[]}]

I don't fully understand the Javascript in the function node nor do I understand the JSONATA used to navigate the structure in the split all solution. The other two are much easier to grasp!

1 Like

JSONATA rocks!!

The date issue sounds weird but is good to know - I'm sure you tried reformatting the incoming field to something else?

It was weird - I'm not sure if it is just the mySQL node or it will have the same issue with other databases. The strange thing is that if I copy the payload and send it via the inject node, it works perfectly. The $$.payload.[..name..] worked for me, so I stopped looking at why $spread was not working. I did create a Github issue for this but the person maintaining that node said this would be unlikely to be fixed since other folks may be relying on the data type of a TIMESTAMP columns.

For anyone interested, node-red 2.2.0 was released a few days ago:

It has some nice enhancements to the editor. I've only been running it for a few hours, and haven't noticed any issues with sequences that use @fblackburn's Hubitat nodes.

4 Likes

@aaiyar, @kuzenkohome - so I realized that my screencap was an older version of the the toggle subflow.. My newer one looks like this... and also I agree with your assessment about having 2 outputs! Sorry for the confusion..

This advanced toggle subflow allows you to define your output via JSON. You can either specify a "command" (see "off") or a set of "commands" defined as an array of objects under "_actions"... For the input you specify which property to check and what the 2 states are - on/off, open/close etc..

In this example turning a light "on" (msg.payload.value = "on") would set level and temp. You can also invert the process as well - which is the default, because, well, you know it's a "toggle" function.. :wink:

edit: I'm going to tweak the output parameter a little bit - don't like my current format.

1 Like

So here is the updated Advanced Toggle.. simplified the output - now there are 2 outputs corresponding to the binary events like "on/off" - "output1"/ "output2"

you can define either a single object that will populate the specified property usually "msg.command" + "msg.attributes" (possibly others) or an array of such objects (edit: which will generate a separate message for each element in the array). Note "msg." is implied and not used.

Advanced Toggle V1.21

[{"id":"049dd7eab1551ac1","type":"subflow","name":"AdvancedToggle","info":"","category":"","in":[{"x":60,"y":60,"wires":[{"id":"c11d7711d06a4d51"}]}],"out":[{"x":640,"y":80,"wires":[{"id":"c11d7711d06a4d51","port":0}]}],"env":[{"name":"INPUT","type":"json","value":"{\"payload.value\":\"on/off\"}"},{"name":"OUTPUT1","type":"json","value":"[{\"command\":\"setLevel\",\"arguments\":\"100\"},{\"command\":\"setTemperature\",\"arguments\":\"2800\"}]"},{"name":"OUTPUT2","type":"json","value":"{ \"command\": \"off\" }"},{"name":"INVERT","type":"bool","value":"true"}],"meta":{},"color":"#FDF0C2","icon":"node-red-contrib-sun-position/inputTypeRandomNumber.svg","status":{"x":640,"y":200,"wires":[{"id":"c11d7711d06a4d51","port":0}]}},{"id":"c11d7711d06a4d51","type":"function","z":"049dd7eab1551ac1","name":"Based on Input and \"invert\" convert to Output","func":"\nvar input = env.get(\"INPUT\");\n//var output = env.get(\"OUTPUT\");\nvar output1 = env.get(\"OUTPUT1\");\nvar output2 = env.get(\"OUTPUT2\");\n\nvar output = [];\n\n\nvar invert = env.get(\"INVERT\");\n\n\nvar retMsg;\nvar inputProp = Object.keys(input)[0];\nvar inputTypes = input[inputProp].split(\"/\");\nvar inputVal = getVal(msg,inputProp);\nvar valIndex = inputTypes.findIndex(type => type === inputVal.toString());\nvar val;\nif (valIndex != -1) {\n    //retMsg = msg;\n    var newIndex = (invert ? (valIndex == 0 ? 1 : 0) : valIndex );\n    \n    \n    if (newIndex == 0) {\n        if (Array.isArray(output1)){\n            output = output1\n        } else {\n            output.push(output1);\n        }\n    } else {\n        if (Array.isArray(output2)){\n            output = output1\n        } else {\n            output.push(output2);\n        }\n    }\n\n    var outputActions = output;\n\n    for (var i=0;i<outputActions.length;i++) {\n        retMsg = {};\n        oAction = outputActions[i];\n        keys = Object.keys(oAction);\n        for (var j=0;j<keys.length;j++) {\n            val = oAction[keys[j]];\n            setVal(retMsg,keys[j],val);\n        }\n        //node.warn(retMsg);\n        node.send(retMsg);\n    }\n        \n    node.done();\n\n}\n\n//return retMsg;\nreturn;\n\nfunction getVal(obj,propName) {\n\n    var retVal;\n    var props = propName.split(\".\");\n    var name = props[0];\n    \n    if (props.length > 1) {\n        if (obj.hasOwnProperty(name)) {\n            props.shift();\n            var newProps = props.join(\".\");\n            retVal = getVal(obj[name],newProps)\n        }\n    } else {\n        retVal = obj[name];\n    }\n    \n    return retVal;\n\n}\n\n\nfunction setVal(obj,propName,val) {\n    var props = propName.split(\".\");\n    var name = props[0];\n\n    if (props.length > 1) {\n        if (!obj.hasOwnProperty(name)) {\n            obj[name] = {};\n        }\n        \n        props.shift();\n        var newProps = props.join(\".\");\n        setVal(obj[name],newProps,val)\n    } else {\n        obj[name] = val;\n    }\n    \n    return;\n\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":140,"wires":[[]]},{"id":"8718e43bfa9eb921","type":"inject","z":"38a24d1b8c9ddb0f","name":"on","props":[{"p":"payload.value","v":"on","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1510,"y":100,"wires":[["aef00f486d7ace86"]]},{"id":"88b4e89f761f8078","type":"debug","z":"38a24d1b8c9ddb0f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1950,"y":120,"wires":[]},{"id":"c1d79180d6369b6c","type":"inject","z":"38a24d1b8c9ddb0f","name":"off","props":[{"p":"payload.value","v":"off","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1510,"y":160,"wires":[["aef00f486d7ace86"]]},{"id":"aef00f486d7ace86","type":"subflow:049dd7eab1551ac1","z":"38a24d1b8c9ddb0f","name":"","env":[{"name":"OUTPUT","value":"{\"output1\":[{\"command\":\"setLevel\",\"arguments\":\"100\"},{\"command\":\"setTemperature\",\"arguments\":\"2800\"}],\"output2\":{\"command\":\"off\"}}","type":"json"}],"x":1740,"y":120,"wires":[["88b4e89f761f8078"]]}]

NOTE: of course had small bug with "invert" not inverting, now fixed.

1 Like

Okay another update, last one - pinky swear... :raised_hand_with_fingers_splayed:

This adds multiple inputs, basic status and maybe a bug fix or two... also note my previous screen cap mentions "setTemperature" which is wrong wrong wrong - use "setColorTemperature" and yes you can setLevel by passing additional parameters to setColorTemperature but where is the fun in that?

Advanced Toggle V1.3

[{"id":"049dd7eab1551ac1","type":"subflow","name":"AdvancedToggle","info":"","category":"","in":[{"x":60,"y":60,"wires":[{"id":"c11d7711d06a4d51"}]}],"out":[{"x":640,"y":80,"wires":[{"id":"c11d7711d06a4d51","port":0}]}],"env":[{"name":"INPUT","type":"json","value":"{\"payload.value\":\"on/off\"}"},{"name":"OUTPUT1","type":"json","value":"[{\"command\":\"setLevel\",\"arguments\":\"100\"},{\"command\":\"setTemperature\",\"arguments\":\"2800\"}]"},{"name":"OUTPUT2","type":"json","value":"{ \"command\": \"off\" }"},{"name":"INVERT","type":"bool","value":"true"}],"meta":{},"color":"#FDF0C2","icon":"node-red-contrib-sun-position/inputTypeRandomNumber.svg","status":{"x":640,"y":200,"wires":[{"id":"c11d7711d06a4d51","port":1}]}},{"id":"c11d7711d06a4d51","type":"function","z":"049dd7eab1551ac1","name":"Based on Input and \"invert\" convert to Output","func":"\nvar inputParm = env.get(\"INPUT\");\n//var output = env.get(\"OUTPUT\");\nvar output1 = env.get(\"OUTPUT1\");\nvar output2 = env.get(\"OUTPUT2\");\n\nvar input = [];\nvar invert = env.get(\"INVERT\");\nvar retMsg;\n\nif (Array.isArray(inputParm)){\n    input = inputParm;\n} else {\n    input.push(inputParm);\n}\n\n/*\nvar inputProp = Object.keys(input)[0];\nvar inputTypes = input[inputProp].split(\"/\");\nvar inputVal = getVal(msg,inputProp);\nvar valIndex = inputTypes.findIndex(type => type === inputVal.toString());\n\nvar val;\n\nif (valIndex != -1) {\n*/\n\nfor (var k=0;k<input.length;k++) {\n\n    var inputProp = Object.keys(input[k])[0];\n    var inputTypes = input[k][inputProp].split(\"/\");\n    var inputVal = getVal(msg,inputProp).toString();\n    var valIndex = inputTypes.indexOf(inputVal);\n\n    var val;\n    var output = [];\n    \n\n    if (valIndex != -1) {\n        var newIndex = (invert ? (valIndex == 0 ? 1 : 0) : valIndex );\n        \n        \n        if (newIndex == 0) {\n            if (Array.isArray(output1)){\n                output = output1\n            } else {\n                output.push(output1);\n            }\n        } else {\n            if (Array.isArray(output2)){\n                output = output2\n            } else {\n                output.push(output2);\n            }\n        }\n\n        for (var i=0;i<output.length;i++) {\n            retMsg = {\"topic\":msg.topic,\"payload\":\"\"};\n            var oAction = output[i];\n            var actionVal = Object.keys(oAction);\n            var keys =[];\n\n            if(Array.isArray(actionVal)) {\n                keys = actionVal;\n            } else {\n                keys.push(actionVal);\n            }\n            //node.warn(\"keys: \" + keys.length);\n            for (var j=0;j<keys.length;j++) {\n                val = oAction[keys[j]];\n                setVal(retMsg,keys[j],val);\n                node.send([undefined,{\"payload\":inputTypes[valIndex] + \" => \" + keys[j] + \"/\" + val}]);\n                node.done();\n            }\n            node.send([retMsg,undefined]);\n            node.done();\n        }\n            \n    }\n\n}\n\n//return retMsg;\nreturn;\n\nfunction getVal(obj,propName) {\n\n    var retVal;\n    var props = propName.split(\".\");\n    var name = props[0];\n    \n    if (props.length > 1) {\n        if (obj.hasOwnProperty(name)) {\n            props.shift();\n            var newProps = props.join(\".\");\n            retVal = getVal(obj[name],newProps)\n        }\n    } else {\n        retVal = obj[name];\n    }\n    \n    return retVal;\n\n}\n\n\nfunction setVal(obj,propName,val) {\n    var props = propName.split(\".\");\n    var name = props[0];\n\n    if (props.length > 1) {\n        if (!obj.hasOwnProperty(name)) {\n            obj[name] = {};\n        }\n        \n        props.shift();\n        var newProps = props.join(\".\");\n        setVal(obj[name],newProps,val)\n    } else {\n        obj[name] = val;\n    }\n    \n    return;\n\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":140,"wires":[[],[]]},{"id":"cf50f7f2ae7a2cb6","type":"subflow:049dd7eab1551ac1","z":"e6807da71ee5477b","name":"","env":[{"name":"INPUT","value":"[{\"payload.value\":\"1/2\"},{\"payload.value\":\"3/4\"}]","type":"json"},{"name":"OUTPUT1","value":"[{\"command\":\"setLevel\",\"arguments\":\"100\"},{\"command\":\"setColorTemperature\",\"arguments\":\"2900\"}]","type":"json"},{"name":"INVERT","value":"false","type":"bool"}],"x":1490,"y":160,"wires":[["688dccfb3250f01f"]]},{"id":"8c6e50277564290c","type":"inject","z":"e6807da71ee5477b","name":"payload.value = 1","props":[{"p":"payload.value","v":"1","vt":"num"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1180,"y":80,"wires":[["cf50f7f2ae7a2cb6"]]},{"id":"fd881a58b0a25f3f","type":"inject","z":"e6807da71ee5477b","name":"payload.value = 2","props":[{"p":"payload.value","v":"2","vt":"num"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1180,"y":120,"wires":[["cf50f7f2ae7a2cb6"]]},{"id":"a2b804cb8750dc53","type":"inject","z":"e6807da71ee5477b","name":"payload.value = 3","props":[{"p":"payload.value","v":"3","vt":"num"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1180,"y":200,"wires":[["cf50f7f2ae7a2cb6"]]},{"id":"09b0f48ac933414e","type":"inject","z":"e6807da71ee5477b","name":"payload.value = 4","props":[{"p":"payload.value","v":"4","vt":"num"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1180,"y":240,"wires":[["cf50f7f2ae7a2cb6"]]},{"id":"688dccfb3250f01f","type":"debug","z":"e6807da71ee5477b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1670,"y":160,"wires":[]}]

3 Likes

So this is an update NOT related to the "Advanced Toggle"... Whilst adding AT to my sequences discovered a bug in my Button Tap subflow which can mess up the "held" action.. fixed and all is well.. Below is an example. For this I have button 3 "held" set to 5 taps.

Button Taps Update of unknown revision..

[{"id":"d0e305e1.7ad558","type":"subflow","name":"Button Taps","info":"","category":"","in":[{"x":50,"y":70,"wires":[{"id":"dceb31cb.fb78f"}]}],"out":[{"x":1230,"y":60,"wires":[{"id":"959b5260.c1c1c","port":0}]},{"x":1230,"y":120,"wires":[{"id":"959b5260.c1c1c","port":1}]},{"x":1220,"y":180,"wires":[{"id":"959b5260.c1c1c","port":2}]},{"x":1230,"y":240,"wires":[{"id":"959b5260.c1c1c","port":3}]}],"env":[{"name":"TIME_LIMIT_MS","type":"num","value":"450"},{"name":"HELD_TAPS","type":"num","value":"0"}],"meta":{"module":"adsavia-button-taps","version":"1.0","license":"GPL-3.0"},"color":"#E2D96E","inputLabels":["Button Press"],"outputLabels":["Single Tap","Double Tap","Triple Tap","Quad+ Tap"],"icon":"node-red-dashboard/ui_button.png","status":{"x":1180,"y":320,"wires":[{"id":"b4ba35d5.020018","port":0}]}},{"id":"959b5260.c1c1c","type":"switch","z":"d0e305e1.7ad558","name":"Single Tap \\n Double Tap \\n Triple Tap \\n Quad+ Tap","property":"count","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"2","vt":"num"},{"t":"eq","v":"3","vt":"str"},{"t":"gte","v":"4","vt":"str"}],"checkall":"true","repair":false,"outputs":4,"x":950,"y":140,"wires":[[],[],[],[]]},{"id":"b4ba35d5.020018","type":"function","z":"d0e305e1.7ad558","name":"set status payload","func":"\nvar payload = {\n\"fill\": \"green\",\n\"shape\":\"dot\",\n\"text\": (flow.get(\"held\")!==undefined?\"Held/\":\"\") + ( msg.count == 1 ? \"Single\" : (msg.count == 2 ? \"Double\": (msg.count == 3 ? \"Triple\": (msg.count == 4 ? \"Quadruple\": msg.count.toString() )))) + \" Tap\"\n};\n\nflow.set(\"held\",undefined);\n\nmsg.payload = payload;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":970,"y":240,"wires":[[]]},{"id":"dbeab83f.200be8","type":"function","z":"d0e305e1.7ad558","name":"Gnerate \"Held\" Taps","func":"var iterate = env.get(\"HELD_TAPS\");\nfor (var i = 0; i < iterate; i++){\n    node.send(msg);\n    node.done();\n}\n\nflow.set(\"held\",true);\n\nreturn;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":200,"wires":[["4e584c74.8f1724"]]},{"id":"dceb31cb.fb78f","type":"switch","z":"d0e305e1.7ad558","name":"","property":"payload.name","propertyType":"msg","rules":[{"t":"eq","v":"pushed","vt":"str"},{"t":"eq","v":"doubleTapped","vt":"str"},{"t":"eq","v":"held","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":190,"y":80,"wires":[["4e584c74.8f1724"],["b6af3007.c57de","3eafdc18.33b6e4"],["dbeab83f.200be8"]]},{"id":"b6af3007.c57de","type":"delay","z":"d0e305e1.7ad558","name":"","pauseType":"delay","timeout":"100","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":390,"y":160,"wires":[["4e584c74.8f1724"]]},{"id":"3eafdc18.33b6e4","type":"delay","z":"d0e305e1.7ad558","name":"","pauseType":"delay","timeout":"50","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":390,"y":120,"wires":[["4e584c74.8f1724"]]},{"id":"4b49418ef2d1a440","type":"change","z":"d0e305e1.7ad558","name":"","rules":[{"t":"set","p":"count","pt":"msg","to":"$flowContext(\"held\") ? $env(\"HELD_TAPS\") : count","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":140,"wires":[["b4ba35d5.020018","959b5260.c1c1c"]]},{"id":"4e584c74.8f1724","type":"timed-counter","z":"d0e305e1.7ad558","name":"","timelimit":"${TIME_LIMIT_MS}","timeunit":1,"withhold":true,"fixedtimeout":true,"pertopic":false,"x":640,"y":80,"wires":[["4b49418ef2d1a440"]]},{"id":"320f82a042c8d0ac","type":"inject","z":"e6807da71ee5477b","name":"pushed: Button 1","props":[{"p":"payload.value","v":"1","vt":"num"},{"p":"payload.name","v":"pushed","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1320,"y":80,"wires":[["68997bcf.fda874"]]},{"id":"68997bcf.fda874","type":"subflow:d0e305e1.7ad558","z":"e6807da71ee5477b","name":"","env":[{"name":"TIME_LIMIT_MS","value":"600","type":"num"},{"name":"HELD_TAPS","value":"5","type":"num"}],"x":1790,"y":240,"wires":[["cea029c1cf0e527b"],["cea029c1cf0e527b"],["cea029c1cf0e527b"],["cea029c1cf0e527b"]]},{"id":"4272e3f528e50cef","type":"inject","z":"e6807da71ee5477b","name":"doubleTapped: Button 2","props":[{"p":"payload.value","v":"2","vt":"num"},{"p":"payload.name","v":"doubleTapped","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1340,"y":140,"wires":[["68997bcf.fda874"]]},{"id":"a4e3de48fc1a4958","type":"inject","z":"e6807da71ee5477b","name":"pushed: Button 1","props":[{"p":"payload.value","v":"1","vt":"num"},{"p":"payload.name","v":"pushed","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1320,"y":400,"wires":[["387831eff3200d23","2fa651dff3acf9bb","ec54009e600c3a89"]]},{"id":"c46c398899cb4555","type":"inject","z":"e6807da71ee5477b","name":"held: Button 3","props":[{"p":"payload.value","v":"3","vt":"num"},{"p":"payload.name","v":"held","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1310,"y":200,"wires":[["68997bcf.fda874"]]},{"id":"387831eff3200d23","type":"delay","z":"e6807da71ee5477b","name":"","pauseType":"delay","timeout":"100","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1570,"y":360,"wires":[["68997bcf.fda874"]]},{"id":"2fa651dff3acf9bb","type":"delay","z":"e6807da71ee5477b","name":"","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1570,"y":400,"wires":[["68997bcf.fda874"]]},{"id":"ec54009e600c3a89","type":"delay","z":"e6807da71ee5477b","name":"","pauseType":"delay","timeout":"300","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1570,"y":440,"wires":[["68997bcf.fda874"]]},{"id":"cea029c1cf0e527b","type":"debug","z":"e6807da71ee5477b","name":"Number Of Taps","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"count & \" Tap / Button \" & payload.value","targetType":"jsonata","statusVal":"","statusType":"auto","x":2020,"y":240,"wires":[]},{"id":"8e7008e187d2ae7c","type":"inject","z":"e6807da71ee5477b","name":"pushed: Button 1","props":[{"p":"payload.value","v":"1","vt":"num"},{"p":"payload.name","v":"pushed","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1320,"y":280,"wires":[["a59bb37df1ed0c6b","713799729224c346"]]},{"id":"a59bb37df1ed0c6b","type":"delay","z":"e6807da71ee5477b","name":"","pauseType":"delay","timeout":"100","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1570,"y":260,"wires":[["68997bcf.fda874"]]},{"id":"141b87b130e692ba","type":"inject","z":"e6807da71ee5477b","name":"pushed: Button 1","props":[{"p":"payload.value","v":"1","vt":"num"},{"p":"payload.name","v":"pushed","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1320,"y":540,"wires":[["bb369ec8a67d202e","1d86df1979f825bb","5e7ee91208d956b5","648c5b29defdf580"]]},{"id":"bb369ec8a67d202e","type":"delay","z":"e6807da71ee5477b","name":"","pauseType":"delay","timeout":"100","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1570,"y":500,"wires":[["68997bcf.fda874"]]},{"id":"1d86df1979f825bb","type":"delay","z":"e6807da71ee5477b","name":"","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1570,"y":540,"wires":[["68997bcf.fda874"]]},{"id":"5e7ee91208d956b5","type":"delay","z":"e6807da71ee5477b","name":"","pauseType":"delay","timeout":"300","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1570,"y":580,"wires":[["68997bcf.fda874"]]},{"id":"648c5b29defdf580","type":"delay","z":"e6807da71ee5477b","name":"","pauseType":"delay","timeout":"400","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1570,"y":620,"wires":[["68997bcf.fda874"]]},{"id":"713799729224c346","type":"delay","z":"e6807da71ee5477b","name":"","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1570,"y":300,"wires":[["68997bcf.fda874"]]}]

Note: You need to install the node-red-contrib-timed-counter node for this to work.

... and here is an in-production example for thems that are curious how I'm using..

2 Likes

Archiving System-Wide Sensor Data for long term trend analysis:

Here's something I've been enjoying. I slurp up all events from Hubitat and dump them into a metrics table in Postgres. Then a Graphana instance can render dashboards which allow me to scroll back through time, build helpful visualizations, etc.

I used a link-out/in pair between the even receiver and the postgres node because I also manually dispatch events from elsewhere through the same postgres node.

3 Likes

That looks great, thanks for posting! I am always interested in how this kind of data can be used effectively..

1 Like

We moved last yea, and the home is not at all efficient. Lots of insulation and other upgrades coming, and I would like to be able to prove that it works. The first step in that is visualizing what needs work and collecting data about it!

I haven’t yet found a good source for weather data, so I don’t have reference temperature for outside temperatures during the winter.

It did just occur to me that I can dump my spreadsheet of information about utility bills into this metrics DB too…

1 Like

Question for anyone using Tasmota based switches with NR & Hubitat. Have some switches using the driver Marcus wrote. But since it is no longer supported I want to try and integrate them through NR.

I am testing a flow but have encountered a race condition where the switch will cycle on/off rapidly for 10-15 seconds then stop and function correctly. I suspect it has to due with timing between the HE status node and the command node. I can't duplicate it but the race happened twice in testing.

Anyone have any insite or a better flow. The flow allows control and maintains status from a HE virtual switch and locally from the switch itself.

Can you split them into 2 sequences maybe? First 3 nodes for the first + Last three nodes for the second. Providing the "wifiplug99" node sends on/off events of course...

I can split them I just don't see what that would accomplish. The wifiplug99 node does send events when the switch changes state either physically or digitally.

I went down the same path, but then decided I didnt need the tasmota switches on Hubitat. All of my logic is now in Node Red. Sorry, I cant be of much help.

1 Like

Well my thought is if it's a timing thing with the "wifiplug99" then splitting them might smooth things out..

@erktrek I'll give it a try, now if I can get them to act up to determine if it fixes the issue.

@mike I'm kind of thinking that in the back of my mind as I'm moving my logic to NR also

1 Like