Node-Red Flow Samples/Sharing

Here's my take on a laundry monitor. I used a $14 Sonoff Dual R3 v2 flashed with Tasmota, which is very easy with this device. I don't use it to actively control power to the appliances, just monitor power consumption so I know when the laundry is done. Tasmota allows reporting intervals to be set, so I have mine at 10 seconds. Note this does not go through HE in anyway. Sonoff/Tasmota --> MQTT --> Node Red. I then get a Alexa announcement and a Alexa notification using the Alexa NotifyMe skill and associated node. I have to thank @erktrek for bringing it to my attention.

I have to plug the NotifyMe skill from a purely user standpoint. While Alexa TTS is great, it's real-time, If you miss the announcement, oh well. NotifyMe (free) sends Alexa notifications that are read when you ask for them, "Alexa read my notifications". And the notifications are queued if there's more than one.

[{"id":"7db15e49bb5571aa","type":"Tasmota Sensor","z":"8201e7b6e9033a68","broker":"f02b0b101445e38b","device":"laundrypwr","name":"","outputs":2,"uidisabler":false,"fullTopic":"","cmndPrefix":"","statPrefix":"","telePrefix":"","qos":1,"retain":false,"rules":["ENERGY.Power[0]","ENERGY.Power[1]"],"outputTopic":"","x":121,"y":459,"wires":[["948017c9dab9cd99"],["0adfb80bd990b8e9"]]},{"id":"948017c9dab9cd99","type":"power-monitor","z":"8201e7b6e9033a68","name":"Washer","startthreshold":"2","stopthreshold":"2","startafter":1,"stopafter":1,"x":315,"y":442,"wires":[["ca126105c0d276aa"]]},{"id":"0adfb80bd990b8e9","type":"power-monitor","z":"8201e7b6e9033a68","name":"Dryer","startthreshold":"0","stopthreshold":"0","startafter":1,"stopafter":1,"x":304,"y":493,"wires":[["ca126105c0d276aa"]]},{"id":"ca126105c0d276aa","type":"switch","z":"8201e7b6e9033a68","name":"stop","property":"payload.event","propertyType":"msg","rules":[{"t":"eq","v":"stop","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":492,"y":459,"wires":[["ea699a2b3e0075be"]]},{"id":"ea699a2b3e0075be","type":"change","z":"8201e7b6e9033a68","name":"","rules":[{"t":"change","p":"payload.name","pt":"msg","from":"Dryer","fromt":"str","to":"The dryer is finished","tot":"str"},{"t":"change","p":"payload.name","pt":"msg","from":"Washer","fromt":"str","to":"The washer is finished","tot":"str"},{"t":"move","p":"payload.name","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":675,"y":459,"wires":[["b50d35b30858b870","44a32bc1801521b0"]]},{"id":"f02b0b101445e38b","type":"tasmota-mqtt-broker","name":"Tasmota MQTT","broker":"127.0.0.1","port":"1883","clientid":"","usetls":false,"keepalive":"60","cleansession":true,"credentials":{}}]
1 Like

Introducing a (formatted) timestamp into a flow

This is more by way of a tip than a sequence of utility by itself. Because I use Node-RED for logging as well as notifications, there are lots of occasions when I need to introduce a formatted timestamp before writing a log or sending a notification. Here are two easy ways of doing this:

  1. Use node-red-contrib-simpletime in the sequence
    node-red-contrib-simpletime (node) - Node-RED
    This node introduces a series of msg objects (msg.mydate, msg.mytimes etc. etc.) that can be used later in the flow.
  2. Use a change node to introduce the current time where needed as a timestamp (or use the javascript function getTime), and then format it using node-red-contrib-moment
    node-red-contrib-moment (node) - Node-RED
    This node creates a single formatted output message with substantial flexibility in the format. One advantage of this node over the previous node, is that it is possible to run a series of previously saved timestamps through it to generate human readable time sequences.
5 Likes

You could also do this with JSONATA in the change node by using something like the following:

payload.$moment($).tz("America/Chicago").format("YYYY-MMM-DD hh:mm a")

The sample flow is:

[{"id":"0f3424890bfdf267","type":"change","z":"499025fc2609cff1","name":"","rules":[{"t":"set","p":"time","pt":"msg","to":"payload.$moment($).tz(\"America/Chicago\").format(\"YYYY-MMM-DD hh:mm a\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":383,"wires":[["85347fbad2252cbe"]]},{"id":"e8d540d4b5fb5bd4","type":"inject","z":"499025fc2609cff1","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":133,"y":383,"wires":[["0f3424890bfdf267"]]},{"id":"85347fbad2252cbe","type":"debug","z":"499025fc2609cff1","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":458,"y":382,"wires":[]}]

The "format" (I believe) can be any format supported by the moment.js library.

4 Likes

I did not realize "moment" was available in JASONata - that's nifty.

2 Likes

Agreed - I didn't know it was available either. But yes, reduces the need for another node!

3 Likes

Does anyone have a flow that takes Alexa speech to text (On Device Activity node) and sets up a future time based event?

What I want to do is speak to Alexa to turn on a particular device (fixed) at some time (variable) in the next 23:59 hours.

I can't find the post right now, but I believe I saw someone on here with your same request and I think the outcome was that this functionality is already built into alexa, no need for a third party flow.

Edit: Found it

Edit 2: After rereading that thread it sounds like it only works for turning things off after a timer, not on, but I cannot say I have tried it so maybe Amazon added it?

I don't have one, but an inject node at the action time that goes through a virtual switch controlled by Alexa would do it. You then reset the virtual switch after the check.

@cjkeenan I saw that and the takeaway seemed to be to parse the text generated by Alexa.

@napalmcsr I want the inject time to be a variable time parsed from the transcript Alexa generates. I don't want to have to manually change the inject time each day.

Thanks for the options though. I think parsing the text transcript will be the best option. Will post it when finished.

You could use the Delay node, calculate the milliseconds between the current time and event time and pass in the time to wait in msg.delay.

1 Like

That's a great idea however the issue is interpreting the transcribed text that Alexa generates into a numeric value that I can use in an inject node.

I need something like this: "turn the coffee pot on at four thirty nine a.m."

Converted to this: "04:39"

Ouch - that can be a nightmare but I guess you could do it in a complicated function node? How accurate is the transcribed text?

I played around with how to split up the text in a function node based on your example (the current example has "four thirty nine am", but the other cases are just copy paste of the first one and adjust for the spaces) and a sample flow is below:

[{"id":"8590441.9e2cdb8","type":"inject","z":"2140a9d4.13a7fe","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"four thirty nine am","payloadType":"str","x":363.3333435058594,"y":40,"wires":[["d3927e10.0a17"]]},{"id":"d3927e10.0a17","type":"function","z":"2140a9d4.13a7fe","name":"","func":"let timeText = msg.payload\nlet count = 0\nlet position1 = 0\nlet position2 = 0\nlet position3 = 0\nlet position4 = 0\nlet strLength = 0\nlet hour = ''\nlet minutes1 = ''\nlet minutes2 = ''\nlet ampm = ''\n\ncount = timeText.match(/\\s/g||[])\n\nmsg.count = count.length\nstrLength = timeText.length\n\nswitch(count.length){\n    case 3:\n        position1=timeText.indexOf(' ')\n        msg.position1=position1\n        hour = timeText.substring(0,position1)\n        msg.hour = hour\n    \n        position2 = timeText.indexOf(' ',position1+1)\n        minutes1= timeText.substring(position1+1,position2)\n        msg.position2 = position2\n        msg.minutes1 = minutes1\n    \n        position3 = timeText.indexOf(' ',position2+1)\n        minutes2= timeText.substring(position2+1,position3)\n        msg.position3 = position3\n        msg.minutes2 = minutes2\n    \n        amPM = timeText.slice(position3+1,strLength)\n        msg.amPM = amPM\n    break;\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":553.3333129882812,"y":110,"wires":[["4d100b0b.fe1b3c"]]},{"id":"261306c1.8451b2","type":"inject","z":"2140a9d4.13a7fe","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"four thirty am","payloadType":"str","x":356.6666564941406,"y":109.66666412353516,"wires":[["d3927e10.0a17"]]},{"id":"88deeae2.48cfd8","type":"inject","z":"2140a9d4.13a7fe","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"four am","payloadType":"str","x":371.6666564941406,"y":194.6666488647461,"wires":[["d3927e10.0a17"]]},{"id":"4d100b0b.fe1b3c","type":"debug","z":"2140a9d4.13a7fe","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":722.3333435058594,"y":110,"wires":[]}]

You should be able to take this and add a couple of cases to account for some variations in the transcribed text (e.g. four thirty am, four am). Basically the number of spaces determines hour, minutes and am/pm and is used to parse out the text. You can then convert the text to numbers (eg. one = 1, two = 2 etc.)

Hope this helps. Ping me if you need any help.

1 Like

Wow, thanks, that's a great start. I can work with that. This is for my wife as she wakes at varying times for work and I don't feel like editing the flow each time.

Here's five actual speech to text transcriptions using the Alexa node. She was spot on every time. Other than "six oh five", which that's easy enough to change to zero.
turn the coffee pot on at six fifteen a.m.
turn the coffee pot on at four thirty nine a.m.
turn the coffee pot on at four forty five a.m.
turn the coffee pot on at six oh five
turn the coffee pot on at six a.m.

1 Like

Here's my attempt at some AI :smiley: to use Alexa speech to text to interpret the command "Alexa, turn the coffee pot on at; some variable time" within the next 23:59 hours.

This flow will parse the transcribed text of what Alexa hears and turn on the switch at the desired time and then provides a spoken confirmation that the correct time has been set by using the "schedex" node to output it's configured on-time.

@rakeshg gave me some code to split out the various possibilities for spoken time. I've added some filtering to eliminate the possibility that some random phrase could be mistaken for a command to set the time. The text to numbers function was also found online.

[{"id":"b3a78e0918d07187","type":"alexa-remote-event","z":"719a3759c325f79f","name":"","account":"03d5dfd689975b6f","event":"ws-device-activity","x":141,"y":1190,"wires":[["da8e875d86c8e82a"]]},{"id":"da8e875d86c8e82a","type":"match","z":"719a3759c325f79f","name":"","rules":[{"property":"payload.data.intent","propertyType":"msg","type":"eq","value":"TurnOnApplianceIntent","valueType":"str"},{"property":"payload.name","propertyType":"msg","type":"eq","value":"Office","valueType":"str"},{"property":"payload.description.summary","propertyType":"msg","type":"regex","value":"coffee pot|maker","valueType":"str","case":true}],"x":137,"y":1258,"wires":[["f9c3e07ef133d4d3"],[]]},{"id":"f9c3e07ef133d4d3","type":"change","z":"719a3759c325f79f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.description.summary","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":148,"y":1326,"wires":[["b4c12c470b32b8af"]]},{"id":"b4c12c470b32b8af","type":"string","z":"719a3759c325f79f","name":"Only pass time","methods":[{"name":"delLeftMost","params":[{"type":"str","value":" at "}]},{"name":"trimLeft","params":[]},{"name":"trimRight","params":[]},{"name":"stripPunctuation","params":[]},{"name":"toLowerCase","params":[]}],"prop":"payload","propout":"payload","object":"msg","objectout":"msg","x":388,"y":1190,"wires":[["d3927e10.0a17"]]},{"id":"d3927e10.0a17","type":"function","z":"719a3759c325f79f","name":"Parse msg","func":"let timeText = msg.payload\nlet count = 0\nlet position1 = 0\nlet position2 = 0\nlet position3 = 0\nlet position4 = 0\nlet strLength = 0\nlet hour = ''\nlet minutes1 = ''\nlet minutes2 = ''\nlet ampm = ''\n\ncount = timeText.match(/\\s/g||[])\n\nmsg.count = count.length\nstrLength = timeText.length\n\nswitch(count.length){\n        case 3:\n        position1=timeText.indexOf(' ')\n        //msg.position1=position1\n        hour = timeText.substring(0,position1)\n        msg.hour = hour\n    \n        position2 = timeText.indexOf(' ',position1+1)\n        minutes1= timeText.substring(position1+1,position2)\n        //msg.position2 = position2\n        msg.minutes1 = minutes1\n    \n        position3 = timeText.indexOf(' ',position2+1)\n        minutes2= timeText.substring(position2+1,position3)\n        //msg.position3 = position3\n        msg.minutes2 = minutes2\n    \n        amPM = timeText.slice(position3+1,strLength)\n        msg.amPM = amPM\n    break;\n        case 2:\n        position1=timeText.indexOf(' ')\n        //msg.position1=position1\n        hour = timeText.substring(0,position1)\n        msg.hour = hour\n    \n        position2 = timeText.indexOf(' ',position1+1)\n        minutes1= timeText.substring(position1+1,position2)\n        //msg.position2 = position2\n        msg.minutes1 = minutes1\n    \n        amPM = timeText.slice(position2+1,strLength)\n        msg.amPM = amPM\n    break;\n    case 1:\n        position1=timeText.indexOf(' ')\n        //msg.position1=position1\n        hour = timeText.substring(0,position1)\n        msg.hour = hour\n\n        amPM = timeText.slice(position1+1,strLength)\n        msg.amPM = amPM\n    break;\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":576,"y":1190,"wires":[["e44f10d0334213df"]]},{"id":"e44f10d0334213df","type":"switch","z":"719a3759c325f79f","name":"Only if has AM/PM","property":"amPM","propertyType":"msg","rules":[{"t":"regex","v":"am|pm","vt":"str","case":true}],"checkall":"true","repair":false,"outputs":1,"x":437,"y":1241,"wires":[["760d90e4a88545bd"]]},{"id":"760d90e4a88545bd","type":"switch","z":"719a3759c325f79f","name":"word count","property":"count","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"2","vt":"num"},{"t":"eq","v":"3","vt":"num"}],"checkall":"true","repair":false,"outputs":3,"x":457,"y":1309,"wires":[["2d8a3b5a5284ede3"],["ab40443ba768d134"],["9b80181e34165b14"]]},{"id":"2d8a3b5a5284ede3","type":"Input Split","z":"719a3759c325f79f","name":"One word","inputProps":["hour"],"outputs":1,"x":671,"y":1241,"wires":[["042a98914153f06d"]]},{"id":"ab40443ba768d134","type":"Input Split","z":"719a3759c325f79f","name":"Two word","inputProps":["hour","minutes1"],"outputs":2,"x":671,"y":1275,"wires":[["e7e0dce3b0e89bfe"],["3877414c8af8e847"]]},{"id":"9b80181e34165b14","type":"Input Split","z":"719a3759c325f79f","name":"Three word","inputProps":["hour","minutes1","minutes2"],"outputs":3,"x":678,"y":1326,"wires":[["033a1ed64264fb8d"],["cd98a44478476fd4"],["f5ff95deb0703f4f"]]},{"id":"042a98914153f06d","type":"function","z":"719a3759c325f79f","name":"Text to Numbers","func":"var Small = {\n    'oh' : 0,\n    'zero': 0,\n    'one': 1,\n    'two': 2,\n    'three': 3,\n    'four': 4,\n    'five': 5,\n    'six': 6,\n    'seven': 7,\n    'eight': 8,\n    'nine': 9,\n    'ten': 10,\n    'eleven': 11,\n    'twelve': 12,\n    'thirteen': 13,\n    'fourteen': 14,\n    'fifteen': 15,\n    'sixteen': 16,\n    'seventeen': 17,\n    'eighteen': 18,\n    'nineteen': 19,\n    'twenty': 20,\n    'thirty': 30,\n    'forty': 40,\n    'fifty': 50,\n    'sixty': 60,\n    'seventy': 70,\n    'eighty': 80,\n    'ninety': 90,\n    //'am':' am',\n    //'pm':' pm'\n};\n\nvar Magnitude = {\n    'thousand':     1000,\n    'million':      1000000,\n    'billion':      1000000000,\n    'trillion':     1000000000000,\n    'quadrillion':  1000000000000000,\n    'quintillion':  1000000000000000000,\n    'sextillion':   1000000000000000000000,\n    'septillion':   1000000000000000000000000,\n    'octillion':    1000000000000000000000000000,\n    'nonillion':    1000000000000000000000000000000,\n    'decillion':    1000000000000000000000000000000000,\n};\n\nvar a, n, g;\nvar payload=msg.payload; //get payload\n\nfunction text2num(s) {\n    a = s.toString().split(/[\\s-]+/);\n    n = \"\";\n    //n = 0;\n    g = \"\";\n    //g = 0;\n    a.forEach(feach);\n    return n + g;\n}\n\nfunction feach(w) {\n    var x = Small[w];\n    if (x != null) {\n        g = g + x;\n    }\n    else if (w == \"hundred\") {\n        g = g * 100;\n    }\n    else {\n        x = Magnitude[w];\n        if (x != null) {\n            n = n + g * x\n            g = 0;\n        }\n        else { \n            alert(\"Unknown number: \"+w); \n        }\n    }\n}\nmsg.payload=text2num(payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":889,"y":1173,"wires":[["6af3d5ce81b1db31"]]},{"id":"e7e0dce3b0e89bfe","type":"function","z":"719a3759c325f79f","name":"Text to Numbers","func":"var Small = {\n    'oh' : 0,\n    'zero': 0,\n    'one': 1,\n    'two': 2,\n    'three': 3,\n    'four': 4,\n    'five': 5,\n    'six': 6,\n    'seven': 7,\n    'eight': 8,\n    'nine': 9,\n    'ten': 10,\n    'eleven': 11,\n    'twelve': 12,\n    'thirteen': 13,\n    'fourteen': 14,\n    'fifteen': 15,\n    'sixteen': 16,\n    'seventeen': 17,\n    'eighteen': 18,\n    'nineteen': 19,\n    'twenty': 20,\n    'thirty': 30,\n    'forty': 40,\n    'fifty': 50,\n    'sixty': 60,\n    'seventy': 70,\n    'eighty': 80,\n    'ninety': 90,\n    'am':' am',\n    'pm':' pm'\n};\n\nvar Magnitude = {\n    'thousand':     1000,\n    'million':      1000000,\n    'billion':      1000000000,\n    'trillion':     1000000000000,\n    'quadrillion':  1000000000000000,\n    'quintillion':  1000000000000000000,\n    'sextillion':   1000000000000000000000,\n    'septillion':   1000000000000000000000000,\n    'octillion':    1000000000000000000000000000,\n    'nonillion':    1000000000000000000000000000000,\n    'decillion':    1000000000000000000000000000000000,\n};\n\nvar a, n, g;\nvar payload=msg.payload; //get payload\n\nfunction text2num(s) {\n    a = s.toString().split(/[\\s-]+/);\n    n = \"\";\n    //n = 0;\n    g = \"\";\n    //g = 0;\n    a.forEach(feach);\n    return n + g;\n}\n\nfunction feach(w) {\n    var x = Small[w];\n    if (x != null) {\n        g = g + x;\n    }\n    else if (w == \"hundred\") {\n        g = g * 100;\n    }\n    else {\n        x = Magnitude[w];\n        if (x != null) {\n            n = n + g * x\n            g = 0;\n        }\n        else { \n            alert(\"Unknown number: \"+w); \n        }\n    }\n}\nmsg.payload=text2num(payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":889,"y":1224,"wires":[["7c4384669d451c6e"]]},{"id":"3877414c8af8e847","type":"function","z":"719a3759c325f79f","name":"Text to Numbers","func":"var Small = {\n    'oh' : 0,\n    'zero': 0,\n    'one': 1,\n    'two': 2,\n    'three': 3,\n    'four': 4,\n    'five': 5,\n    'six': 6,\n    'seven': 7,\n    'eight': 8,\n    'nine': 9,\n    'ten': 10,\n    'eleven': 11,\n    'twelve': 12,\n    'thirteen': 13,\n    'fourteen': 14,\n    'fifteen': 15,\n    'sixteen': 16,\n    'seventeen': 17,\n    'eighteen': 18,\n    'nineteen': 19,\n    'twenty': 20,\n    'thirty': 30,\n    'forty': 40,\n    'fifty': 50,\n    'sixty': 60,\n    'seventy': 70,\n    'eighty': 80,\n    'ninety': 90,\n    'am':' am',\n    'pm':' pm'\n};\n\nvar Magnitude = {\n    'thousand':     1000,\n    'million':      1000000,\n    'billion':      1000000000,\n    'trillion':     1000000000000,\n    'quadrillion':  1000000000000000,\n    'quintillion':  1000000000000000000,\n    'sextillion':   1000000000000000000000,\n    'septillion':   1000000000000000000000000,\n    'octillion':    1000000000000000000000000000,\n    'nonillion':    1000000000000000000000000000000,\n    'decillion':    1000000000000000000000000000000000,\n};\n\nvar a, n, g;\nvar payload=msg.payload; //get payload\n\nfunction text2num(s) {\n    a = s.toString().split(/[\\s-]+/);\n    n = \"\";\n    //n = 0;\n    g = \"\";\n    //g = 0;\n    a.forEach(feach);\n    return n + g;\n}\n\nfunction feach(w) {\n    var x = Small[w];\n    if (x != null) {\n        g = g + x;\n    }\n    else if (w == \"hundred\") {\n        g = g * 100;\n    }\n    else {\n        x = Magnitude[w];\n        if (x != null) {\n            n = n + g * x\n            g = 0;\n        }\n        else { \n            alert(\"Unknown number: \"+w); \n        }\n    }\n}\nmsg.payload=text2num(payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":889,"y":1258,"wires":[["7c4384669d451c6e"]]},{"id":"033a1ed64264fb8d","type":"function","z":"719a3759c325f79f","name":"Text to Numbers","func":"var Small = {\n    'oh' : 0,\n    'zero': 0,\n    'one': 1,\n    'two': 2,\n    'three': 3,\n    'four': 4,\n    'five': 5,\n    'six': 6,\n    'seven': 7,\n    'eight': 8,\n    'nine': 9,\n    'ten': 10,\n    'eleven': 11,\n    'twelve': 12,\n    'thirteen': 13,\n    'fourteen': 14,\n    'fifteen': 15,\n    'sixteen': 16,\n    'seventeen': 17,\n    'eighteen': 18,\n    'nineteen': 19,\n    'twenty': 20,\n    'thirty': 30,\n    'forty': 40,\n    'fifty': 50,\n    'sixty': 60,\n    'seventy': 70,\n    'eighty': 80,\n    'ninety': 90,\n    'am':' am',\n    'pm':' pm'\n};\n\nvar Magnitude = {\n    'thousand':     1000,\n    'million':      1000000,\n    'billion':      1000000000,\n    'trillion':     1000000000000,\n    'quadrillion':  1000000000000000,\n    'quintillion':  1000000000000000000,\n    'sextillion':   1000000000000000000000,\n    'septillion':   1000000000000000000000000,\n    'octillion':    1000000000000000000000000000,\n    'nonillion':    1000000000000000000000000000000,\n    'decillion':    1000000000000000000000000000000000,\n};\n\nvar a, n, g;\nvar payload=msg.payload; //get payload\n\nfunction text2num(s) {\n    a = s.toString().split(/[\\s-]+/);\n    n = \"\";\n    //n = 0;\n    g = \"\";\n    //g = 0;\n    a.forEach(feach);\n    return n + g;\n}\n\nfunction feach(w) {\n    var x = Small[w];\n    if (x != null) {\n        g = g + x;\n    }\n    else if (w == \"hundred\") {\n        g = g * 100;\n    }\n    else {\n        x = Magnitude[w];\n        if (x != null) {\n            n = n + g * x\n            g = 0;\n        }\n        else { \n            alert(\"Unknown number: \"+w); \n        }\n    }\n}\nmsg.payload=text2num(payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":889,"y":1309,"wires":[["c28a24fe51c76127"]]},{"id":"cd98a44478476fd4","type":"function","z":"719a3759c325f79f","name":"Text to Numbers","func":"var Small = {\n    'oh' : 0,\n    'zero': 0,\n    'one': 1,\n    'two': 2,\n    'three': 3,\n    'four': 4,\n    'five': 5,\n    'six': 6,\n    'seven': 7,\n    'eight': 8,\n    'nine': 9,\n    'ten': 10,\n    'eleven': 11,\n    'twelve': 12,\n    'thirteen': 13,\n    'fourteen': 14,\n    'fifteen': 15,\n    'sixteen': 16,\n    'seventeen': 17,\n    'eighteen': 18,\n    'nineteen': 19,\n    'twenty': 20,\n    'thirty': 30,\n    'forty': 40,\n    'fifty': 50,\n    'sixty': 60,\n    'seventy': 70,\n    'eighty': 80,\n    'ninety': 90,\n    'am':' am',\n    'pm':' pm'\n};\n\nvar Magnitude = {\n    'thousand':     1000,\n    'million':      1000000,\n    'billion':      1000000000,\n    'trillion':     1000000000000,\n    'quadrillion':  1000000000000000,\n    'quintillion':  1000000000000000000,\n    'sextillion':   1000000000000000000000,\n    'septillion':   1000000000000000000000000,\n    'octillion':    1000000000000000000000000000,\n    'nonillion':    1000000000000000000000000000000,\n    'decillion':    1000000000000000000000000000000000,\n};\n\nvar a, n, g;\nvar payload=msg.payload; //get payload\n\nfunction text2num(s) {\n    a = s.toString().split(/[\\s-]+/);\n    n = \"\";\n    //n = 0;\n    g = \"\";\n    //g = 0;\n    a.forEach(feach);\n    return n + g;\n}\n\nfunction feach(w) {\n    var x = Small[w];\n    if (x != null) {\n        g = g + x;\n    }\n    else if (w == \"hundred\") {\n        g = g * 100;\n    }\n    else {\n        x = Magnitude[w];\n        if (x != null) {\n            n = n + g * x\n            g = 0;\n        }\n        else { \n            alert(\"Unknown number: \"+w); \n        }\n    }\n}\nmsg.payload=text2num(payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":889,"y":1343,"wires":[["c28a24fe51c76127"]]},{"id":"f5ff95deb0703f4f","type":"function","z":"719a3759c325f79f","name":"Text to Numbers","func":"var Small = {\n    'oh' : 0,\n    'zero': 0,\n    'one': 1,\n    'two': 2,\n    'three': 3,\n    'four': 4,\n    'five': 5,\n    'six': 6,\n    'seven': 7,\n    'eight': 8,\n    'nine': 9,\n    'ten': 10,\n    'eleven': 11,\n    'twelve': 12,\n    'thirteen': 13,\n    'fourteen': 14,\n    'fifteen': 15,\n    'sixteen': 16,\n    'seventeen': 17,\n    'eighteen': 18,\n    'nineteen': 19,\n    'twenty': 20,\n    'thirty': 30,\n    'forty': 40,\n    'fifty': 50,\n    'sixty': 60,\n    'seventy': 70,\n    'eighty': 80,\n    'ninety': 90,\n    'am':' am',\n    'pm':' pm'\n};\n\nvar Magnitude = {\n    'thousand':     1000,\n    'million':      1000000,\n    'billion':      1000000000,\n    'trillion':     1000000000000,\n    'quadrillion':  1000000000000000,\n    'quintillion':  1000000000000000000,\n    'sextillion':   1000000000000000000000,\n    'septillion':   1000000000000000000000000,\n    'octillion':    1000000000000000000000000000,\n    'nonillion':    1000000000000000000000000000000,\n    'decillion':    1000000000000000000000000000000000,\n};\n\nvar a, n, g;\nvar payload=msg.payload; //get payload\n\nfunction text2num(s) {\n    a = s.toString().split(/[\\s-]+/);\n    n = \"\";\n    //n = 0;\n    g = \"\";\n    //g = 0;\n    a.forEach(feach);\n    return n + g;\n}\n\nfunction feach(w) {\n    var x = Small[w];\n    if (x != null) {\n        g = g + x;\n    }\n    else if (w == \"hundred\") {\n        g = g * 100;\n    }\n    else {\n        x = Magnitude[w];\n        if (x != null) {\n            n = n + g * x\n            g = 0;\n        }\n        else { \n            alert(\"Unknown number: \"+w); \n        }\n    }\n}\nmsg.payload=text2num(payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":889,"y":1377,"wires":[["c28a24fe51c76127"]]},{"id":"6af3d5ce81b1db31","type":"template","z":"719a3759c325f79f","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{payload}}:00 {{amPM}}","output":"str","x":1079,"y":1173,"wires":[["42921e2a01feb911"]]},{"id":"7c4384669d451c6e","type":"join","z":"719a3759c325f79f","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":1070,"y":1241,"wires":[["e71a603c631dfcd0"]]},{"id":"c28a24fe51c76127","type":"join","z":"719a3759c325f79f","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"3","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":1070,"y":1343,"wires":[["8ff84037ac52b37d"]]},{"id":"42921e2a01feb911","type":"function","z":"719a3759c325f79f","name":"convert to 24hr","func":"const convertTime = timeStr => {\n   const [time, modifier] = timeStr.split(' ');\n   let [hours, minutes] = time.split(':');\n   if (hours === '12') {\n      hours = '00';\n   }\n   if (modifier === 'pm') {\n      hours = parseInt(hours, 10) + 12;\n   }\n   return `${hours}:${minutes}`;\n};\nmsg.payload=(convertTime(msg.payload));\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1450,"y":1173,"wires":[["2451bf41bb4aa412"]]},{"id":"e71a603c631dfcd0","type":"function","z":"719a3759c325f79f","name":"Format time string","func":"msg.payload = msg.payload[0] + \":\" + msg.payload[1] + \" \" + msg.amPM;\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1229,"y":1241,"wires":[["42921e2a01feb911"]]},{"id":"8ff84037ac52b37d","type":"function","z":"719a3759c325f79f","name":"Format time string","func":"combinedMinutes = parseFloat(msg.payload[1]) + parseFloat(msg.payload[2]);\nif (combinedMinutes < 10) { combinedMinutes = \"0\" + combinedMinutes;}\nmsg.payload = msg.payload[0] + \":\" + combinedMinutes + \" \" + msg.amPM;\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1229,"y":1343,"wires":[["42921e2a01feb911"]]},{"id":"2451bf41bb4aa412","type":"change","z":"719a3759c325f79f","name":"On/Off Times","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.ontime","tot":"msg"},{"t":"set","p":"payload.offtime","pt":"msg","to":"","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1630,"y":1173,"wires":[["c6c6f9eb2ccb0cff","a9fd416b360fd507"]]},{"id":"c6c6f9eb2ccb0cff","type":"schedex","z":"719a3759c325f79f","name":"","passthroughunhandled":false,"suspended":false,"lat":"41.2414404191641","lon":"-80.78194611847465","ontime":"","ontopic":"","onpayload":"on","onoffset":0,"onrandomoffset":0,"offtime":"","offtopic":"","offpayload":"off","offoffset":0,"offrandomoffset":0,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"sun":true,"x":1444,"y":1309,"wires":[["452fbd38efca09f9"]]},{"id":"a9fd416b360fd507","type":"delay","z":"719a3759c325f79f","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1453,"y":1241,"wires":[["92415ec33bf6ce84"]]},{"id":"92415ec33bf6ce84","type":"change","z":"719a3759c325f79f","name":"Annouce Info","rules":[{"t":"set","p":"payload","pt":"msg","to":"info","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1609,"y":1241,"wires":[["c6c6f9eb2ccb0cff"]]},{"id":"452fbd38efca09f9","type":"switch","z":"719a3759c325f79f","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"","vt":"str"},{"t":"eq","v":"info","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1565,"y":1309,"wires":[["65cf77e34efe93c1"],["ab76cbaea42b2486"]]},{"id":"65cf77e34efe93c1","type":"Tasmota Switch","z":"719a3759c325f79f","broker":"f02b0b101445e38b","device":"teckin1","name":"","outputs":1,"uidisabler":false,"fullTopic":"","cmndPrefix":"","statPrefix":"","telePrefix":"","qos":1,"retain":false,"x":1743,"y":1309,"wires":[[]]},{"id":"ab76cbaea42b2486","type":"moment","z":"719a3759c325f79f","name":"","topic":"","input":"payload.on","inputType":"msg","inTz":"Europe/London","adjAmount":0,"adjType":"days","adjDir":"add","format":"dddd h:mm a","locale":"en-US","output":"payload","outputType":"msg","outTz":"America/New_York","x":1362,"y":1394,"wires":[["238986dd8627c5e2"]]},{"id":"238986dd8627c5e2","type":"template","z":"719a3759c325f79f","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"The coffee maker will turn on {{payload}} !","output":"str","x":1556,"y":1394,"wires":[["8e397e21b858ef69"]]},{"id":"8e397e21b858ef69","type":"alexa-remote-routine","z":"719a3759c325f79f","name":"","account":"03d5dfd689975b6f","routineNode":{"type":"speak","payload":{"type":"regular","text":{"type":"msg","value":"payload"},"devices":["abf53d7765f04fe6bdf3e159e2841cda"]}},"x":1722,"y":1394,"wires":[[]]},{"id":"03d5dfd689975b6f","type":"alexa-remote-account","name":"Mkuzenko-Amazon","authMethod":"proxy","proxyOwnIp":"192.168.0.2","proxyPort":"3456","cookieFile":"/home/mint/Documents/NodeRedData/applestrudel.txt","refreshInterval":"3","alexaServiceHost":"pitangui.amazon.com","amazonPage":"amazon.com","acceptLanguage":"en-US","onKeywordInLanguage":"on","userAgent":"","useWsMqtt":"on","autoInit":"on"},{"id":"f02b0b101445e38b","type":"tasmota-mqtt-broker","name":"Tasmota MQTT","broker":"192.168.0.2","port":"1883","clientid":"","usetls":false,"keepalive":"60","cleansession":true}]
2 Likes

FYI - at least Pico double-taps will be supported by the platform in the next release ...

1 Like

That's cool to know!

I believe my subflow takes doubletap events into account so can continue to trap Pico triple-tap events.. :wink:

1 Like

What is the best practice to store configuration that are needed across multiple flows? I have several flows that do things like reboot/shutdown the HE hubs, download backups, and gather performance metrics and store those in a MariaDB database. In each of these flows I have function nodes that set variables with hub details like IP Address, name, etc. I recently replaced my Asus router with UDM and realized I had a ton of hard coded IP addresses, etc baked within my flows and hoping to come up with a better way to manage this.

Thoughts?

I'm not sure I understand you exactly, but I store settings as global variables because those are available across flows. I also use disk storage for all my global variables and most of my flow variables, so they survive across node-red restarts.

2 Likes

Two things, one you could use global context variables to store common hub details, etc.
https://nodered.org/docs/user-guide/context

Or two, for functions that are repeated (shutdown or startup for example) you could either make a subflow for that and then just have a single place to adjust it trickling down to every instance of that subflow, or you can have only a single instance of that sequence and then use link-in and link-out nodes as necessary to trigger that sequence from another flow.

1 Like

Like @aaiyar said - store configuration values in global variables. I don't use disk storage for context stores (I probably should) since I don't store current state or value (they are fixed values), instead I have a flow that executes on Node-RED startup that re-populates all the variables.

2 Likes