mytimeout example

The node mytimeout is a count down timer that I created to turn off devices after a set period of time. Either using default values or JSON passed to the input of the node.

This is the flow I created so my wife can turn on the timer (which will turn on the various devices) and turn off various devices if mytimeout is allowed to timeout. The timer flow communicates with the devices via MQTT topics.

The user interface provided is via the Node-Red Dashboard. The code is organized in 3 sections. The dials, the momentary sleep button and the on/off switches.

The dials set the minutes the timer will run (green for timer length, yellow for warning time remaining to trigger the warning message). Hitting sleep button or timer switch will cause the timer to start and run for the length of time in the dials. It will also cause the timer switch to turn on as well as the 3 devices. They will remain on until the timer counts down to 0 and then they'll turn off. Or if the user turns them off manually.

The commands supported by the node are 'on', 'off', 'stop' and 'cancel'. See the Readme for the node to get further details. Only 'on' and 'off' are demonstrated here.

NOTE: Each device topic and sleep button topic is the same for the subscribe and publish of the device. Normally this is not a good idea as the node passes the subscribed data to the published topic in an endless loop but the mytimeout node blocks any received message it has sent. For the mytimeout node it attempts to block anything that matches what it last published.

[{"id":"da9e69ab.c8ab58","type":"mqtt out","z":"5b55a004.bc7ba","name":"Sleep button","topic":"home/device/sleep","qos":"0","retain":"false","broker":"1413863b.baa24a","x":573,"y":320.2499828338623,"wires":[]},{"id":"69a350bd.de644","type":"comment","z":"5b55a004.bc7ba","name":"Momentary Push Button","info":"","x":160.5,"y":273.75000190734863,"wires":[]},{"id":"df980657.708b38","type":"ui_button","z":"5b55a004.bc7ba","name":"","group":"dcfb116.92399f","order":2,"width":0,"height":0,"passthru":false,"label":"- Sleep -","color":"black","bgcolor":"","icon":"","payload":"on","payloadType":"str","topic":"","x":122,"y":319.2499828338623,"wires":[["c5598cfe.ca1e2"]]},{"id":"a4ffb816.004c28","type":"ui_template","z":"5b55a004.bc7ba","group":"dcfb116.92399f","name":"Timer Dial","order":1,"width":"3","height":"3","format":"<style>\n    .nr-dashboard-template {\n        padding: 0;\n        margin: 0;\n        overflow-x: hidden;\n        overflow-y: hidden;\n}\n</style>\n\n<script src=\"/myjs/jquery.knob.min.js\"></script>\n \n<script>\n    // https://tech.scargill.net/more-dashboard-goodness/\n    (function(scope){\n        scope.$watch('msg', function(data) {\n            scope.msgSend = false;\n            $(\"#jqueryknob\" + scope.$id).val(data.payload);\n            $(\"#jqueryknob\" + scope.$id).change();\n            scope.msgSend = true;\n        });\n        scope.msgSend = true;\n        scope.value = 0;\n        scope.sendData = function(data){scope.send({payload:data})};   \n        scope.init= function(){\n            $(\"#jqueryknob\" + scope.$id).knob({\n                change : function (value) {\n                    //console.log(\"change: \" + value);\n                },\n                release : function (value) { \n                    if(scope.msgSend)scope.sendData(value);\n                },\n                cancel : function () {\n                },\n                draw : function () { }\n            }); \n        }\n        //Wait for angular to assign id to DOM element\n        setTimeout(function(){\n            scope.init();\n        },200)\n    })(scope);\n      \n</script>\n \n<input  id=\"jqueryknob{{$id}}\"\n        style=\"margin-left:auto;margin-right:auto;\"\n        class=\"cDial1\"\n        data-width=\"150\"\n        data-min=1\n        data-max=255\n        data-cursor=false\n        data-thickness=.4\n        data-fgColor=\"chartreuse\"\n        data-linecap=round\n        value=\"{{msg.payload ?  msg.payload : 1}}\"\n>\n<!--\n<input  id=\"jqueryknob{{$id}}\"\n        class=\"cDial1\"\n        data-width=\"98%\"\n        data-height=\"98%\" \n        data-cursor=true\n        data-thickness=.4\n        data-linecap=round\n        value=\"{{msg.payload ?  msg.payload : 0}}\"\n>\n-->","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":327,"y":98.2499828338623,"wires":[["61df5740.ac6518","24b92cd2.e57e74","4303f0df.6b06"]]},{"id":"61df5740.ac6518","type":"mqtt out","z":"5b55a004.bc7ba","name":"switchTimerValue","topic":"home/virtual/switchTimerValue","qos":"0","retain":"true","broker":"4ea8322.ae0dacc","x":594,"y":158.2499828338623,"wires":[]},{"id":"24b92cd2.e57e74","type":"debug","z":"5b55a004.bc7ba","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":577,"y":44.249982833862305,"wires":[]},{"id":"abd58ade.ad9728","type":"comment","z":"5b55a004.bc7ba","name":"Timer Minutes","info":"","x":130.50003814697266,"y":41,"wires":[]},{"id":"5c4f4ad8.214bf4","type":"debug","z":"5b55a004.bc7ba","name":"Sleep-n-Warning","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":593,"y":271.2499828338623,"wires":[]},{"id":"4303f0df.6b06","type":"function","z":"5b55a004.bc7ba","name":"Set Timer Minutes*60","func":"global.set('timerMin', msg.payload * 60); // seconds to minutes\nreturn msg;","outputs":1,"noerr":0,"x":606.0000076293945,"y":99.2500114440918,"wires":[[]]},{"id":"89f14ea4.d4cff","type":"function","z":"5b55a004.bc7ba","name":"Set Warning Minutes","func":"global.set('warningMin', msg.payload * 60 ); // seconds to minutes\nreturn msg;","outputs":1,"noerr":0,"x":602.0000076293945,"y":224.50000762939453,"wires":[[]]},{"id":"c5598cfe.ca1e2","type":"function","z":"5b55a004.bc7ba","name":"Get global Time settings","func":"// Need to change the on to\n// { \"payload\": \"on\", \"timeout\": 690, \"warning\": 100 }\n\nif(msg.payload == \"on\") {\n    msg.payload = { \n        \"payload\": \"on\", \n        \"timeout\":   global.get('timerMin')||600,\n        \"warning\": global.get('warningMin')||150\n    };\n\n    return msg;\n}\n","outputs":1,"noerr":0,"x":332,"y":319.8499584197998,"wires":[["da9e69ab.c8ab58","5c4f4ad8.214bf4"]]},{"id":"a2920b5c.821e98","type":"ui_template","z":"5b55a004.bc7ba","group":"dcfb116.92399f","name":"Warning","order":14,"width":"3","height":"3","format":"<style>\n    .nr-dashboard-template {\n        padding: 0;\n        margin: 0;\n        overflow-x: hidden;\n        overflow-y: hidden;\n}\n</style>\n\n<script src=\"/myjs/jquery.knob.min.js\"></script>\n \n<script>\n    // https://tech.scargill.net/more-dashboard-goodness/\n    (function(scope){\n        scope.$watch('msg', function(data) {\n            scope.msgSend = false;\n            $(\"#jqueryknob\" + scope.$id).val(data.payload);\n            $(\"#jqueryknob\" + scope.$id).change();\n            scope.msgSend = true;\n        });\n        scope.msgSend = true;\n        scope.value = 0;\n        scope.sendData = function(data){scope.send({payload:data})};   \n        scope.init= function(){\n            $(\"#jqueryknob\" + scope.$id).knob({\n                change : function (value) {\n                    //console.log(\"change: \" + value);\n                },\n                release : function (value) { \n                    if(scope.msgSend)scope.sendData(value);\n                },\n                cancel : function () {\n                },\n                draw : function () { }\n            }); \n        }\n        //Wait for angular to assign id to DOM element\n        setTimeout(function(){\n            scope.init();\n        },200)\n    })(scope);\n      \n</script>\n \n<input  id=\"jqueryknob{{$id}}\"\n        style=\"margin-left:auto;margin-right:auto;\"\n        class=\"cDial1\"\n        data-width=\"150\"\n        data-min=1\n        data-max=255\n        data-cursor=true\n        data-thickness=.3\n        data-fgColor=\"yellow\"\n        data-linecap=round\n        value=\"{{msg.payload ?  msg.payload : 1}}\"\n>\n--><div ng-bind-html=\"msg.payload\"></div>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":328.10003662109375,"y":224.8499584197998,"wires":[["5c4f4ad8.214bf4","89f14ea4.d4cff"]]},{"id":"3555d8f5.7fac98","type":"inject","z":"5b55a004.bc7ba","name":"","topic":"","payload":"60","payloadType":"str","repeat":"","crontab":"","once":true,"x":131.77999877929688,"y":97.40998649597168,"wires":[["a4ffb816.004c28"]]},{"id":"718ca646.93fa98","type":"inject","z":"5b55a004.bc7ba","name":"","topic":"","payload":"30","payloadType":"str","repeat":"","crontab":"","once":true,"x":130.77999877929688,"y":223.40999794006348,"wires":[["a2920b5c.821e98"]]},{"id":"9c43bf30.b9a0b","type":"comment","z":"5b55a004.bc7ba","name":"Warning Minutes","info":"","x":138.5,"y":163.7499828338623,"wires":[]},{"id":"58f4e49d.0777cc","type":"ui_switch","z":"5b55a004.bc7ba","name":"","label":"Timer","group":"dcfb116.92399f","order":3,"width":0,"height":0,"passthru":false,"decouple":"false","topic":"","style":"","onvalue":"on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"off","offvalueType":"str","officon":"","offcolor":"","x":365.5,"y":586,"wires":[["a2f4bf13.a2a0f"]]},{"id":"64dbaab0.2ab254","type":"mqtt in","z":"5b55a004.bc7ba","name":"switchTimer","topic":"home/virtual/switchTimer","qos":"0","broker":"4ea8322.ae0dacc","x":128.83334011501734,"y":646.6666870117188,"wires":[["58f4e49d.0777cc","5c0e63ba.8cb08c","aa431539.5207e8","dd123ad8.471418"]]},{"id":"7af8c362.7114fc","type":"mqtt out","z":"5b55a004.bc7ba","name":"switchTimer","topic":"home/virtual/switchTimer","qos":"0","retain":"false","broker":"4ea8322.ae0dacc","x":977.5,"y":641,"wires":[]},{"id":"3a7bc79e.0e5ce8","type":"debug","z":"5b55a004.bc7ba","name":"toMyTimerF","active":false,"tosidebar":true,"console":true,"tostatus":false,"complete":"true","x":775.5,"y":587,"wires":[]},{"id":"612bb4c9.2b3bfc","type":"comment","z":"5b55a004.bc7ba","name":"Timer On/Off","info":"","x":124.5,"y":452,"wires":[]},{"id":"a2f4bf13.a2a0f","type":"function","z":"5b55a004.bc7ba","name":"Get global time settings","func":"// Need to change the on to\n// { \"payload\": \"on\", \"timeout\": 69, \"warning\": 10 }\n\nif(msg.payload == \"on\") {\n    msg.payload = { \n        \"payload\": \"on\", \n        \"timeout\": global.get('timerMin'),\n        \"warning\": global.get('warningMin')\n    };\n} else {\n    msg.payload = {  \"payload\": \"off\" };\n}\nreturn msg;","outputs":1,"noerr":0,"x":551.5,"y":586,"wires":[["3a7bc79e.0e5ce8","5c0e63ba.8cb08c"]]},{"id":"aa431539.5207e8","type":"debug","z":"5b55a004.bc7ba","name":"switchTimerTopic2Timer","active":false,"tosidebar":true,"console":true,"tostatus":false,"complete":"true","x":432.75000381469727,"y":520.7500114440918,"wires":[]},{"id":"5c0e63ba.8cb08c","type":"mytimeout","z":"5b55a004.bc7ba","name":"My Timeout","outtopic":"","outsafe":"on","outwarning":"warning","outunsafe":"off","warning":"5","timer":"30","repeat":false,"again":false,"x":765.5,"y":647,"wires":[["7af8c362.7114fc","640c8790.2c0518"],["6a0ac65.129a638"]]},{"id":"4ce275d7.c58cfc","type":"mqtt in","z":"5b55a004.bc7ba","name":"Sleep button","topic":"home/device/sleep","qos":"0","broker":"1413863b.baa24a","x":128.75000762939453,"y":716.0000133514404,"wires":[["5c0e63ba.8cb08c"]]},{"id":"eab35e82.79bb2","type":"ui_switch","z":"5b55a004.bc7ba","name":"","label":"DeviceA","group":"dcfb116.92399f","order":9,"width":0,"height":0,"passthru":false,"decouple":"true","topic":"","style":"","onvalue":"on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"off","offvalueType":"str","officon":"","offcolor":"","x":443.16665988498266,"y":901,"wires":[["8852eb4d.b26af8"]]},{"id":"ddb00d5c.3c53d","type":"mqtt in","z":"5b55a004.bc7ba","name":"DeviceA topic","topic":"home/test/DeviceA","qos":"1","broker":"4ea8322.ae0dacc","x":123.5,"y":900.6666870117188,"wires":[["eab35e82.79bb2"]]},{"id":"8852eb4d.b26af8","type":"mqtt out","z":"5b55a004.bc7ba","name":"DeviceA topic","topic":"home/test/DeviceA","qos":"0","retain":"true","broker":"4ea8322.ae0dacc","x":801.1666598849827,"y":901,"wires":[]},{"id":"e6aea08a.20a47","type":"ui_switch","z":"5b55a004.bc7ba","name":"","label":"DeviceB","group":"dcfb116.92399f","order":10,"width":0,"height":0,"passthru":false,"decouple":"true","topic":"","style":"","onvalue":"on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"off","offvalueType":"str","officon":"","offcolor":"","x":443.16665988498266,"y":959,"wires":[["3d07c931.2beb06"]]},{"id":"67edca83.b41994","type":"mqtt in","z":"5b55a004.bc7ba","name":"DeviceB topic","topic":"home/test/DeviceB","qos":"1","broker":"4ea8322.ae0dacc","x":124.5,"y":958.6666870117188,"wires":[["e6aea08a.20a47"]]},{"id":"3d07c931.2beb06","type":"mqtt out","z":"5b55a004.bc7ba","name":"DeviceB topic","topic":"home/test/DeviceB","qos":"0","retain":"true","broker":"4ea8322.ae0dacc","x":801.1666598849827,"y":959,"wires":[]},{"id":"c765c7f4.441b48","type":"ui_switch","z":"5b55a004.bc7ba","name":"","label":"DeviceC","group":"dcfb116.92399f","order":4,"width":0,"height":0,"passthru":false,"decouple":"true","topic":"","style":"","onvalue":"on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"off","offvalueType":"str","officon":"","offcolor":"","x":442.8333197699653,"y":1017,"wires":[["780bd651.4cab88"]]},{"id":"14d33b17.d7b005","type":"mqtt in","z":"5b55a004.bc7ba","name":"DeviceC topic","topic":"home/test/DeviceC","qos":"0","broker":"4ea8322.ae0dacc","x":124.16665988498266,"y":1016.6666870117188,"wires":[["c765c7f4.441b48"]]},{"id":"780bd651.4cab88","type":"mqtt out","z":"5b55a004.bc7ba","name":"DeviceC topic","topic":"home/test/DeviceC","qos":"0","retain":"false","broker":"4ea8322.ae0dacc","x":801.8333197699653,"y":1017,"wires":[]},{"id":"cc544d4.f1646b","type":"comment","z":"5b55a004.bc7ba","name":"Device Switches","info":"","x":132.91665988498266,"y":847.5,"wires":[]},{"id":"dd123ad8.471418","type":"function","z":"5b55a004.bc7ba","name":"Devices list","func":"/*\n--------------------------------------------------------------------------------\nOkay a bit confusing but ...\nI've been sending only on 'on'/'off'/'warning' to the first output (I now have\n2).\n\nI want to create a list of device topics to send to. This list would be in a\nspecific order. And this function you take the 'msg.payload' off and build\na msg.list = [ 'nodeA', 'nodeB', 'nodeC' ] and unshift each as they are resent\nthrough a delay node on output 2. This would work down to none where the node\nwould stop sending.\n\nWe'll keep the on off support for now. Later I'll stop the on as the timer is\nmeant to turn off things not turn on things.\n\nTo talk to a specific topic we'll need the following format:\nmsg = {\n    payload:  string | buffer\n              most users prefer simple text payloads, but binary buffers can\n              also be published.\n    topic:    string\n              the MQTT topic to publish to.\n    qos:      number\n              0, fire and forget - 1, at least once - 2, once and once only.\n              Default 0.\n    retain:   boolean\n              set to true to retain the message on the broker.\n              Default false.\n}\n\nmsg = {\n    payload: \"off\",\n    topics: [ 't1', 't2', t3', 't4']\n}\n*/\n\nvar nMsg = {};\n\n//\n// NOTE: this is the device list (devices are listening on these topics)\n//\nvar topics = [\n    'home/test/DeviceA',\n    'home/test/DeviceB',\n    'home/test/DeviceC'\n];\n\nif(msg.topics === undefined) {\n    // Not sure what to do if it's not a string\n    if(false && typeof(msg.payload) == 'string') {\n        nMsg = JSON.parse(msg.payload);\n        // Not sure if I should check to see if it's on or off\n        // return [ nMsg, ];\n    } else {\n        nMsg = msg;\n    }\n\n    // the Payload remains untouched\n    nMsg['topic']  = topics.shift(); // pop a topic off the list\n    nMsg['topics'] = topics;         // save the rest for later\n\n    console.log(\"Out: \" + JSON.stringify(nMsg));\n    return [ nMsg, nMsg ];\n} else if(msg.topics.length) {\n    nMsg = msg;\n\n    nMsg['topic']  = msg.topics.shift(); // pop a topic off the list\n    nMsg['topics'] = msg.topics;         // save the rest for later\n\n    console.log(\"Out: \" + JSON.stringify(nMsg));\n    return [ nMsg, nMsg ];\n}\n","outputs":2,"noerr":0,"x":379.5,"y":403,"wires":[["9801a037.10559","a895ffc0.9eeb7"],["4e3fa0e0.322d5"]]},{"id":"4e3fa0e0.322d5","type":"delay","z":"5b55a004.bc7ba","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":375.5,"y":472,"wires":[["dd123ad8.471418"]]},{"id":"9801a037.10559","type":"debug","z":"5b55a004.bc7ba","name":"Delay loop","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":667.5,"y":396,"wires":[]},{"id":"a895ffc0.9eeb7","type":"mqtt out","z":"5b55a004.bc7ba","name":"MQTT Devices","topic":"","qos":"","retain":"","broker":"4ea8322.ae0dacc","x":681.5,"y":444,"wires":[]},{"id":"640c8790.2c0518","type":"debug","z":"5b55a004.bc7ba","name":"toSwitchTimer","active":false,"tosidebar":true,"console":true,"tostatus":false,"complete":"true","x":999,"y":689.6000061035156,"wires":[]},{"id":"6a0ac65.129a638","type":"debug","z":"5b55a004.bc7ba","name":"ticks","active":false,"tosidebar":true,"console":true,"tostatus":false,"complete":"true","x":970.7000122070312,"y":732.7999877929688,"wires":[]},{"id":"1413863b.baa24a","type":"mqtt-broker","z":"","broker":"192.168.24.2","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"15","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"dcfb116.92399f","type":"ui_group","z":"","name":"Default","tab":"db89d438.d66268","disp":true,"width":"6"},{"id":"4ea8322.ae0dacc","type":"mqtt-broker","z":"","broker":"192.168.24.2","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"15","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"db89d438.d66268","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":8}]

Flow Info

Created 7 years ago
Rating: 3.3333333333333335 3

Owner

Actions

Rate:

Node Types

Core
  • comment (x5)
  • debug (x7)
  • delay (x1)
  • function (x5)
  • inject (x2)
  • mqtt in (x5)
  • mqtt out (x7)
  • mqtt-broker (x2)
Other
  • mytimeout (x1)
  • ui_button (x1)
  • ui_group (x1)
  • ui_switch (x4)
  • ui_tab (x1)
  • ui_template (x2)

Tags

  • mytimeout
  • timer
  • countdown
  • cherry
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option