Timer with UI feedback

Hi,

BigTimer is really great and featured. However, it can't be programmed for timeout from outside, or I didn't find the way. Other time-related nodes (delay, trigger, etc.) can't transmit specific message by time offset on the input.

So I'm trying to implement countdown timer with existing nodes.

[{"id":"44e84237.2ae1cc","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"2e64fd78.68e542","type":"debug","z":"44e84237.2ae1cc","name":"light_F","active":true,"tosidebar":true,"console":true,"tostatus":false,"complete":"true","x":740,"y":160,"wires":[]},{"id":"c5e5e86d.921848","type":"ui_slider","z":"44e84237.2ae1cc","name":"light_timeout","label":"Off in {{ value }} min","group":"e1a7cd3b.e5b6","order":2,"width":0,"height":0,"passthru":true,"topic":"/shm/garden/light/timeout","min":0,"max":"90","step":1,"x":530,"y":220,"wires":[["a6ace8e7.f931a8"]]},{"id":"e91c5ed6.359de","type":"ui_switch","z":"44e84237.2ae1cc","name":"control_light","label":"Garden Light","group":"e1a7cd3b.e5b6","order":3,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"shm/garden/light/state","style":"","onvalue":"on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"off","offvalueType":"str","officon":"","offcolor":"","x":110,"y":140,"wires":[["2c4963d.80b119c"]]},{"id":"2c4963d.80b119c","type":"function","z":"44e84237.2ae1cc","name":"parse_timeout","func":"if (msg.payload === 'on') {\n    // Set delay, start countdown, on load\n    var delay = flow.get_light_timeout\n    m1 = {payload: delay};\n    m2 = {payload: 1};\n    return m1, m2, m1;\n} else if ( msg.payload === 'off')\n{   // off load, reset flow.get_light_timeout\n    flow.set('get_light_timeout', delay);\n    return null, {payload: 0}, delay;\n} else if ( msg.payload > 0) {\n    // decrement countdown\n    m1 = {payload: msg.payload--};\n    return m1, null, m1;\n}","outputs":3,"noerr":0,"x":340,"y":180,"wires":[["387e5754.580be8"],["2e64fd78.68e542"],["c5e5e86d.921848"]]},{"id":"a6ace8e7.f931a8","type":"function","z":"44e84237.2ae1cc","name":"UI_to_flow_context","func":"flow.set(\"get_light_timeout\", msg.payload);\nreturn null;","outputs":1,"noerr":0,"x":730,"y":220,"wires":[[]]},{"id":"739761e1.8e26b","type":"inject","z":"44e84237.2ae1cc","name":"","topic":"","payload":"10","payloadType":"num","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":130,"y":220,"wires":[["c5e5e86d.921848"]]},{"id":"387e5754.580be8","type":"trigger","z":"44e84237.2ae1cc","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"1","extend":false,"units":"min","reset":"","bytopic":"all","name":"","x":330,"y":120,"wires":[["2c4963d.80b119c"]]},{"id":"e1a7cd3b.e5b6","type":"ui_group","z":"","name":"Garden: Light","tab":"531ef1f0.9a426","order":3,"disp":true,"width":"6","collapse":false},{"id":"531ef1f0.9a426","type":"ui_tab","z":"","name":"MyDashboard","icon":"home"}]

The problem is:
When turning the switch On, the exceptions thrown:

"TypeError: Cannot read property 'req' of undefined"

This is similar to the reported problem.
However, if I connect the trigger's input to other output then it works OK.
So I think the bug is in my function parse_timeout.

Please, help to find the bug. Or other method of time count down with ability of UI feedback is welcome.

The function parse_timeout has 3 outputs:

  1. For own functional state (like recursion);
  2. For control (0 or 1);
  3. For UI feedback.

Hi,
Just for fun I’ve modified your function:

/* your function
if (msg.payload === 'on') {
    // Set delay, start countdown, on load
    var delay = flow.get_light_timeout
    m1 = {payload: delay};
    m2 = {payload: 1};
    return m1, m2, m1;
} else if ( msg.payload === 'off')
{   // off load, reset flow.get_light_timeout
    flow.set('get_light_timeout', delay);
    return null, {payload: 0}, delay;
} else if ( msg.payload > 0) {
    // decrement countdown
    m1 = {payload: msg.payload--};
    return m1, null, m1;
}
*/

// try this
var m1 = {};
var m2 = {};
var m3 = {};

var delay = flow.get('get_light_timeout');

if (msg.payload === 'on') {
    // Set delay, start countdown, on load
    m1.payload = delay;
    m2.payload = 1;
    return [m1, m2, m1];
} else if ( msg.payload === 'off') {   // off load, reset flow.get_light_timeout
    flow.set('get_light_timeout', delay);
    m2.payload = 0;
    m3.payload = delay;
    return [null, m2, m3];
} else if ( msg.payload > 0) {
    // decrement countdown
    m1.payload = parseInt(msg.payload)-1;
    return [m1, null, m1];
}

but there are better solutions.

@cflurin
Thank you for your hints. Number of errors are found in my function:

  1. flow.variable name didn’t work. flow.get(‘variable_name’) works.
  2. var is required on object declaration.
  3. Syntax error in return: lack of brackets.
  4. variable-- doesn’t work, explicit parsing required.

Beside that, functional bug found and fixed.
Finally, all we have end-user defined timer with feedback to UI.

 [{"id":"44e84237.2ae1cc","type":"tab","label":"Flow 1","disabled":true,"info":""},{"id":"1d73e543.0cecfb","type":"debug","z":"44e84237.2ae1cc","name":"light_F","active":true,"tosidebar":true,"console":true,"tostatus":false,"complete":"true","x":740,"y":240,"wires":[]},{"id":"edfc392f.1686d8","type":"ui_slider","z":"44e84237.2ae1cc","name":"light_timeout","label":"Off in {{ value }} min","group":"e1a7cd3b.e5b6","order":2,"width":0,"height":0,"passthru":true,"topic":"/shm/garden/light/timeout","min":0,"max":"90","step":1,"x":530,"y":300,"wires":[["db11335d.0fbf6"]]},{"id":"f57e83f0.b2c5c","type":"ui_switch","z":"44e84237.2ae1cc","name":"control_light","label":"Garden Light","group":"e1a7cd3b.e5b6","order":3,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"shm/garden/light/state","style":"","onvalue":"on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"off","offvalueType":"str","officon":"","offcolor":"","x":110,"y":160,"wires":[["7b619538.159dec"]]},{"id":"7b619538.159dec","type":"function","z":"44e84237.2ae1cc","name":"parse_timeout","func":"var delay = flow.get('get_light_timeout');\n\nif ('on' == msg.payload) {\n    // Set delay, start countdown, on load\n    var m1 = {payload: delay};\n    return [m1, {payload: 1}, m1];  \n} else if ('off' == msg.payload || 0 == msg.payload) {\n    // off load, reset flow.get_light_timeout\n    flow.set('get_light_timeout', delay);\n    return [null, {payload: 0}, delay];\n} else if (msg.payload > 0) {\n    // decrement countdown\n    var m1 = {payload: parseInt(msg.payload)-1};\n    return [m1, null, m1];\n}\n","outputs":3,"noerr":0,"x":340,"y":260,"wires":[["34fb8e5f.7da442"],["1d73e543.0cecfb"],["edfc392f.1686d8"]]},{"id":"db11335d.0fbf6","type":"function","z":"44e84237.2ae1cc","name":"UI_to_flow_context","func":"flow.set(\"get_light_timeout\", msg.payload);\nreturn null;","outputs":1,"noerr":0,"x":730,"y":300,"wires":[[]]},{"id":"407c54ca.8d14ec","type":"inject","z":"44e84237.2ae1cc","name":"","topic":"","payload":"10","payloadType":"num","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":130,"y":300,"wires":[["edfc392f.1686d8"]]},{"id":"34fb8e5f.7da442","type":"trigger","z":"44e84237.2ae1cc","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"1","extend":false,"units":"min","reset":"","bytopic":"all","name":"","x":330,"y":200,"wires":[["7b619538.159dec"]]},{"id":"e1a7cd3b.e5b6","type":"ui_group","z":"","name":"Garden: Light","tab":"531ef1f0.9a426","order":3,"disp":true,"width":"6","collapse":false},{"id":"531ef1f0.9a426","type":"ui_tab","z":"","name":"MyDashboard","icon":"home"}]

Update: there is another bug in this flow. Working on it…

Almost there:

[
    {
        "id": "44e84237.2ae1cc",
        "type": "tab",
        "label": "Flow 1",
        "disabled": true,
        "info": ""
    },
    {
        "id": "e7d8df53.29ff2",
        "type": "debug",
        "z": "44e84237.2ae1cc",
        "name": "light_F",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": false,
        "complete": "true",
        "x": 680,
        "y": 240,
        "wires": []
    },
    {
        "id": "d86a599.12d44a8",
        "type": "ui_switch",
        "z": "44e84237.2ae1cc",
        "name": "enable_light",
        "label": "Enable Auto Light",
        "group": "e1a7cd3b.e5b6",
        "order": 1,
        "width": "6",
        "height": "4",
        "passthru": true,
        "decouple": "false",
        "topic": "shm/garden/light/enable",
        "style": "",
        "onvalue": "auto",
        "onvalueType": "str",
        "onicon": "",
        "oncolor": "",
        "offvalue": "stop",
        "offvalueType": "str",
        "officon": "",
        "offcolor": "",
        "x": 130,
        "y": 140,
        "wires": [
            [
                "370a7823.aa6a48"
            ]
        ]
    },
    {
        "id": "370a7823.aa6a48",
        "type": "bigtimer",
        "z": "44e84237.2ae1cc",
        "outtopic": "shm/garden/light/state",
        "outpayload1": "on",
        "outpayload2": "off",
        "name": "GardenLightTimer",
        "lat": "32",
        "lon": "34",
        "starttime": 5001,
        "endtime": "1365",
        "startoff": "-15",
        "endoff": "0",
        "offs": 0,
        "outtext1": "1",
        "outtext2": "0",
        "timeout": 1440,
        "sun": true,
        "mon": true,
        "tue": true,
        "wed": true,
        "thu": true,
        "fri": true,
        "sat": true,
        "jan": true,
        "feb": true,
        "mar": true,
        "apr": true,
        "may": true,
        "jun": true,
        "jul": true,
        "aug": true,
        "sep": true,
        "oct": true,
        "nov": true,
        "dec": true,
        "day1": 0,
        "month1": 0,
        "day2": 0,
        "month2": 0,
        "day3": 0,
        "month3": 0,
        "day4": 0,
        "month4": 0,
        "day5": 0,
        "month5": 0,
        "d1": 0,
        "w1": 0,
        "d2": 0,
        "w2": 0,
        "d3": 0,
        "w3": 0,
        "d4": 0,
        "w4": 0,
        "d5": 0,
        "w5": 0,
        "suspend": false,
        "random": false,
        "repeat": false,
        "atstart": true,
        "odd": false,
        "even": false,
        "x": 370,
        "y": 160,
        "wires": [
            [],
            [],
            [
                "b7fd2b90.f31e08"
            ]
        ]
    },
    {
        "id": "46a9b6b.5954648",
        "type": "ui_slider",
        "z": "44e84237.2ae1cc",
        "name": "light_timeout",
        "label": "Off in {{ value }} min",
        "group": "e1a7cd3b.e5b6",
        "order": 2,
        "width": "0",
        "height": "0",
        "passthru": true,
        "topic": "/shm/garden/light/timeout",
        "min": 0,
        "max": "90",
        "step": 1,
        "x": 550,
        "y": 280,
        "wires": [
            [
                "fec0831f.1f4ae"
            ]
        ]
    },
    {
        "id": "5a8ae6d5.823038",
        "type": "ui_switch",
        "z": "44e84237.2ae1cc",
        "name": "control_light",
        "label": "Garden Light",
        "group": "e1a7cd3b.e5b6",
        "order": 3,
        "width": "5",
        "height": "1",
        "passthru": true,
        "decouple": "false",
        "topic": "shm/garden/light/state",
        "style": "",
        "onvalue": "on",
        "onvalueType": "str",
        "onicon": "",
        "oncolor": "",
        "offvalue": "off",
        "offvalueType": "str",
        "officon": "",
        "offcolor": "",
        "x": 130,
        "y": 180,
        "wires": [
            [
                "370a7823.aa6a48",
                "46a283dc.976ddc"
            ]
        ]
    },
    {
        "id": "46a283dc.976ddc",
        "type": "function",
        "z": "44e84237.2ae1cc",
        "name": "parse_timeout",
        "func": "// Outputs:\n// Counter_to_display, GPIO control via BigTimer, Counter_to_process\n\nvar delay = flow.get('get_light_timeout') || 10;\n\n// Block loop of messages when off\nvar prev = flow.get('prev_light_msg') || null;\nif ('off' == msg.payload && !prev) return [null, null, null];\nif ('off' == msg.payload && 'off' == prev.payload) return [null, null, null];\n\n// Keep msg for next round\nflow.set('prev_light_msg', msg);\nif ('on' == msg.payload) {\n    // Set delay, start countdown, on load\n    var m1 = {payload: delay};\n    // var m2 = {payload: 1};\n    return [m1, null, m1];  \n} else if (('off' == msg.payload) || (0 == msg.payload)) {\n    var m1 = {payload: 0};\n    if (0 == delay) { // request never off\n        return [m1, null, m1];\n    } else {\n        // off load, reset clear counter\n        return [{payload: ' '}, {payload: 'off'}, null];\n    }\n} else if (msg.payload > 0) {\n    // decrement countdown\n    var m1 = {payload: parseInt(msg.payload)-1};\n    return [m1, null, m1];\n}",
        "outputs": 3,
        "noerr": 0,
        "x": 160,
        "y": 240,
        "wires": [
            [
                "dae4d3d8.57793"
            ],
            [
                "e7d8df53.29ff2",
                "5a8ae6d5.823038"
            ],
            [
                "ddfb7cf4.4c837"
            ]
        ]
    },
    {
        "id": "b7fd2b90.f31e08",
        "type": "debug",
        "z": "44e84237.2ae1cc",
        "name": "light_T",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": false,
        "complete": "true",
        "x": 680,
        "y": 180,
        "wires": []
    },
    {
        "id": "fec0831f.1f4ae",
        "type": "function",
        "z": "44e84237.2ae1cc",
        "name": "UI_to_flow_context",
        "func": "flow.set(\"get_light_timeout\", msg.payload);\nreturn null;",
        "outputs": 1,
        "noerr": 0,
        "x": 750,
        "y": 280,
        "wires": [
            []
        ]
    },
    {
        "id": "496bebfb.99aba4",
        "type": "inject",
        "z": "44e84237.2ae1cc",
        "name": "",
        "topic": "",
        "payload": "10",
        "payloadType": "num",
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "x": 390,
        "y": 280,
        "wires": [
            [
                "46a9b6b.5954648"
            ]
        ]
    },
    {
        "id": "e0f44b28.9ba5e8",
        "type": "debug",
        "z": "44e84237.2ae1cc",
        "name": "light_M",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": false,
        "complete": "true",
        "x": 680,
        "y": 120,
        "wires": []
    },
    {
        "id": "ddfb7cf4.4c837",
        "type": "trigger",
        "z": "44e84237.2ae1cc",
        "op1": "",
        "op2": "",
        "op1type": "nul",
        "op2type": "pay",
        "duration": "1",
        "extend": false,
        "units": "min",
        "reset": "",
        "bytopic": "all",
        "name": "",
        "x": 190,
        "y": 300,
        "wires": [
            [
                "46a283dc.976ddc"
            ]
        ]
    },
    {
        "id": "dae4d3d8.57793",
        "type": "ui_text",
        "z": "44e84237.2ae1cc",
        "group": "e1a7cd3b.e5b6",
        "order": 0,
        "width": "1",
        "height": "1",
        "name": "counter",
        "label": "",
        "format": "{{msg.payload}}",
        "layout": "row-right",
        "x": 380,
        "y": 220,
        "wires": []
    },
    {
        "id": "e1a7cd3b.e5b6",
        "type": "ui_group",
        "z": "",
        "name": "Garden: Light",
        "tab": "531ef1f0.9a426",
        "order": 3,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "531ef1f0.9a426",
        "type": "ui_tab",
        "z": "",
        "name": "MyDashboard",
        "icon": "home"
    }
]

Features:

  • programmable automated timer (BigTimer) which can be:
  • Enabled or disabled from UI;
  • Or overridden with switch;
  • Or enable load for timeout defined in UI with countdown feedback.
  • Plus logging.

The only flaw so far: when the automated timer works/engaged there is no UI feedback/indication.

Easy to add:

  • MQTT posting.
  • Local IO control.

In the post above I noticed a bug, fixed it and tested few days.
Here is the fixed flow. It has the same features as above, with command execution node.

[
    {
        "id": "3ce33470.31935c",
        "type": "ui_switch",
        "z": "669e4b2e.c077d4",
        "name": "",
        "label": "Water Trees",
        "group": "e6e9e9c5.46a0c8",
        "order": 6,
        "width": "5",
        "height": "1",
        "passthru": false,
        "decouple": "true",
        "topic": "shm/garden/water_tree/state",
        "style": "",
        "onvalue": "on",
        "onvalueType": "str",
        "onicon": "nature",
        "oncolor": "green",
        "offvalue": "off",
        "offvalueType": "str",
        "officon": "nature",
        "offcolor": "grey",
        "x": 120,
        "y": 1020,
        "wires": [
            [
                "d6f61713.adc4b8"
            ]
        ]
    },
    {
        "id": "339a41b2.52fd0e",
        "type": "bigtimer",
        "z": "669e4b2e.c077d4",
        "outtopic": "shm/garden/water_tree/state",
        "outpayload1": "on",
        "outpayload2": "off",
        "name": "TreeTimer",
        "lat": "32",
        "lon": "34",
        "starttime": 5001,
        "endtime": "10090",
        "startoff": 0,
        "endoff": 0,
        "offs": 0,
        "outtext1": "on",
        "outtext2": "off",
        "timeout": 1440,
        "sun": true,
        "mon": false,
        "tue": false,
        "wed": false,
        "thu": true,
        "fri": false,
        "sat": false,
        "jan": false,
        "feb": false,
        "mar": false,
        "apr": false,
        "may": true,
        "jun": true,
        "jul": true,
        "aug": true,
        "sep": true,
        "oct": true,
        "nov": false,
        "dec": false,
        "day1": 0,
        "month1": 0,
        "day2": 0,
        "month2": 0,
        "day3": 0,
        "month3": 0,
        "day4": 0,
        "month4": 0,
        "day5": 0,
        "month5": 0,
        "d1": 0,
        "w1": 0,
        "d2": 0,
        "w2": 0,
        "d3": 0,
        "w3": 0,
        "d4": 0,
        "w4": 0,
        "d5": 0,
        "w5": 0,
        "suspend": false,
        "random": false,
        "repeat": false,
        "atstart": true,
        "odd": false,
        "even": false,
        "x": 380,
        "y": 980,
        "wires": [
            [],
            [],
            [
                "3ce33470.31935c",
                "a138f48e.2bbb08",
                "c413f53e.29ef58",
                "b7472e0c.efbdf"
            ]
        ]
    },
    {
        "id": "d6f61713.adc4b8",
        "type": "function",
        "z": "669e4b2e.c077d4",
        "name": "parse_timeout",
        "func": "// Outputs:\n// Counter_to_display, GPIO control via BigTimer, Counter_to_process\n\ntry{\n    var delay = flow.get('get_water_timeout');\n} catch (er) {\n    var delay = 10;\n}\n\n// Block loop of messages when off\nvar prev = flow.get('prev_tree_msg') || {payload: 'ignore'};\nnode.status({fill:\"green\",shape:\"ring\",text: delay+','+prev.payload+','+msg.payload});\nif ('off' == msg.payload && 'off' == prev.payload) return [null, null, null];\nnode.status({fill:\"green\",shape:\"dot\",text: delay+','+prev.payload+','+msg.payload});\n\n// Keep msg for next round\nflow.set('prev_tree_msg', msg);\nif ('on' == msg.payload) {\n    // Set delay, start countdown, on load\n    var m1 = {payload: delay};\n    return [m1, msg, m1];  \n} else if (('off' == msg.payload) || (msg.payload <= 1)) {\n    var m1 = {payload: 0};\n    if ((msg.payload <= 1) && (0 == delay)) { // request never off\n        return [m1, null, m1];\n    } else {\n        // off load, reset clear counter\n        return [{payload: ' '}, {payload: 'off'}, null];\n    }\n} else if (msg.payload > 1) {\n    // decrement countdown\n    var m1 = {payload: parseInt(msg.payload)-1};\n    return [m1, null, m1];\n}\n",
        "outputs": 3,
        "noerr": 0,
        "x": 170,
        "y": 1080,
        "wires": [
            [
                "d25b362a.630378",
                "b7472e0c.efbdf"
            ],
            [
                "3ce33470.31935c",
                "339a41b2.52fd0e",
                "b7472e0c.efbdf"
            ],
            [
                "a1168f85.09676",
                "b7472e0c.efbdf"
            ]
        ]
    },
    {
        "id": "a1168f85.09676",
        "type": "trigger",
        "z": "669e4b2e.c077d4",
        "op1": "",
        "op2": "",
        "op1type": "nul",
        "op2type": "pay",
        "duration": "1",
        "extend": false,
        "units": "min",
        "reset": "",
        "bytopic": "all",
        "name": "",
        "x": 190,
        "y": 1140,
        "wires": [
            [
                "d6f61713.adc4b8"
            ]
        ]
    },
    {
        "id": "d25b362a.630378",
        "type": "ui_text",
        "z": "669e4b2e.c077d4",
        "group": "e6e9e9c5.46a0c8",
        "order": 7,
        "width": "1",
        "height": "1",
        "name": "counter",
        "label": "",
        "format": "{{msg.payload}}",
        "layout": "row-right",
        "x": 390,
        "y": 1060,
        "wires": []
    },
    {
        "id": "b7472e0c.efbdf",
        "type": "debug",
        "z": "669e4b2e.c077d4",
        "name": "cmd_debug",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": false,
        "complete": "payload",
        "x": 620,
        "y": 1080,
        "wires": []
    },
    {
        "id": "a138f48e.2bbb08",
        "type": "mqtt out",
        "z": "669e4b2e.c077d4",
        "name": "",
        "topic": "",
        "qos": "",
        "retain": "",
        "broker": "2bd2afca.e962e",
        "x": 810,
        "y": 1020,
        "wires": []
    },
    {
        "id": "fcd2d087.c8a49",
        "type": "debug",
        "z": "669e4b2e.c077d4",
        "name": "watering",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": false,
        "complete": "payload",
        "x": 770,
        "y": 960,
        "wires": []
    },
    {
        "id": "c413f53e.29ef58",
        "type": "exec",
        "z": "669e4b2e.c077d4",
        "command": "actuators/valve.py -i 2 ",
        "addpay": true,
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "oldrc": false,
        "name": "",
        "x": 560,
        "y": 920,
        "wires": [
            [
                "fcd2d087.c8a49"
            ],
            [],
            []
        ]
    },
    {
        "id": "aff2deab.9a23a",
        "type": "inject",
        "z": "669e4b2e.c077d4",
        "name": "",
        "topic": "",
        "payload": "10",
        "payloadType": "num",
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "x": 400,
        "y": 1280,
        "wires": [
            [
                "7998052f.fd021c"
            ]
        ]
    },
    {
        "id": "7998052f.fd021c",
        "type": "ui_slider",
        "z": "669e4b2e.c077d4",
        "name": "timeout",
        "label": "Stop in {{value}} min",
        "group": "e6e9e9c5.46a0c8",
        "order": 3,
        "width": 0,
        "height": 0,
        "passthru": true,
        "topic": "/shm/garden/timeout",
        "min": 0,
        "max": "90",
        "step": 1,
        "x": 550,
        "y": 1280,
        "wires": [
            [
                "7cd4b83b.e9d328"
            ]
        ]
    },
    {
        "id": "7cd4b83b.e9d328",
        "type": "function",
        "z": "669e4b2e.c077d4",
        "name": "UI_to_flow_context",
        "func": "flow.set(\"get_water_timeout\", msg.payload);\nreturn null;",
        "outputs": 1,
        "noerr": 0,
        "x": 740,
        "y": 1280,
        "wires": [
            []
        ]
    },
    {
        "id": "e6e9e9c5.46a0c8",
        "type": "ui_group",
        "z": "",
        "name": "Garden: Watering",
        "tab": "531ef1f0.9a426",
        "order": 3,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "2bd2afca.e962e",
        "type": "mqtt-broker",
        "z": "",
        "name": "",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "60",
        "cleansession": true,
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": ""
    },
    {
        "id": "531ef1f0.9a426",
        "type": "ui_tab",
        "z": "",
        "name": "MyDashboard",
        "icon": "home"
    }
]

The flow works OK with the schedule on BigTimer.
And manual override works too with side-triggered timer… but only in days when the BigTimer should work.

I noticed when BigTimer shows status “No action today”, then manual override “on” and “off” in the input are ignored on output. However, again, the same inputs work well on “action days”.

Does anybody encounter this behavior?
Any idea for the root cause? …for solution?

Tried to debug it - see the cmd_debug node. The messages from the parse_timeout function arrive well, as designed in every day, active and inactive.

Thank you.

Maybe address this to Peter Scargill on his blog where he talks extensively about BigTimer ?

Craig

Thank you, Craig.
Peter replied that’s normal behavior of BigTimer.
So, as solution, I just over-passed the BigTimer node, connecting parse_timeout straight to switch control. All works now, every day :slight_smile:

Code here, maybe helpful for someone.

[
    {
        "id": "6491080e.1afc78",
        "type": "ui_switch",
        "z": "669e4b2e.c077d4",
        "name": "enable_water",
        "label": "Enable Watering",
        "group": "e6e9e9c5.46a0c8",
        "order": 1,
        "width": 0,
        "height": 0,
        "passthru": true,
        "decouple": "false",
        "topic": "shm/garden/water/enable",
        "style": "",
        "onvalue": "on",
        "onvalueType": "str",
        "onicon": "",
        "oncolor": "",
        "offvalue": "off",
        "offvalueType": "str",
        "officon": "",
        "offcolor": "",
        "x": 120,
        "y": 720,
        "wires": [
            [
                "82219b7f.5ef6e8"
            ]
        ]
    },
    {
        "id": "82219b7f.5ef6e8",
        "type": "change",
        "z": "669e4b2e.c077d4",
        "name": "switch2timer",
        "rules": [
            {
                "t": "change",
                "p": "payload",
                "pt": "msg",
                "from": "off",
                "fromt": "str",
                "to": "stop",
                "tot": "str"
            },
            {
                "t": "change",
                "p": "payload",
                "pt": "msg",
                "from": "on",
                "fromt": "str",
                "to": "auto",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 150,
        "y": 760,
        "wires": [
            [
                "45189448.b43a1c",
                "339a41b2.52fd0e",
                "a6e8ed21.e3a26"
            ]
        ]
    },
    {
        "id": "9752622f.768e6",
        "type": "inject",
        "z": "669e4b2e.c077d4",
        "name": "water_sch_on",
        "topic": "shm/garden/water/enable",
        "payload": "on",
        "payloadType": "str",
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "x": 100,
        "y": 680,
        "wires": [
            [
                "6491080e.1afc78"
            ]
        ]
    },
    {
        "id": "b8860b8c.1579e8",
        "type": "inject",
        "z": "669e4b2e.c077d4",
        "name": "init_off",
        "topic": "",
        "payload": "off",
        "payloadType": "str",
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "0.05",
        "x": 370,
        "y": 920,
        "wires": [
            [
                "c413f53e.29ef58",
                "c5ae1192.32ff4",
                "b52a0805.d13df8"
            ]
        ]
    },
    {
        "id": "c413f53e.29ef58",
        "type": "exec",
        "z": "669e4b2e.c077d4",
        "command": "actuators/valve.py -i 2 ",
        "addpay": true,
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "oldrc": false,
        "name": "",
        "x": 560,
        "y": 920,
        "wires": [
            [
                "fcd2d087.c8a49"
            ],
            [],
            []
        ]
    },
    {
        "id": "fcd2d087.c8a49",
        "type": "debug",
        "z": "669e4b2e.c077d4",
        "name": "watering",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": false,
        "complete": "payload",
        "x": 770,
        "y": 960,
        "wires": []
    },
    {
        "id": "a138f48e.2bbb08",
        "type": "mqtt out",
        "z": "669e4b2e.c077d4",
        "name": "",
        "topic": "",
        "qos": "",
        "retain": "",
        "broker": "2bd2afca.e962e",
        "x": 810,
        "y": 1020,
        "wires": []
    },
    {
        "id": "339a41b2.52fd0e",
        "type": "bigtimer",
        "z": "669e4b2e.c077d4",
        "outtopic": "shm/garden/water_tree/state",
        "outpayload1": "on",
        "outpayload2": "off",
        "name": "TreeTimer",
        "lat": "32",
        "lon": "34",
        "starttime": 5001,
        "endtime": "10090",
        "startoff": 0,
        "endoff": 0,
        "offs": 0,
        "outtext1": "on",
        "outtext2": "off",
        "timeout": 1440,
        "sun": false,
        "mon": false,
        "tue": true,
        "wed": false,
        "thu": false,
        "fri": false,
        "sat": true,
        "jan": false,
        "feb": false,
        "mar": false,
        "apr": false,
        "may": true,
        "jun": true,
        "jul": true,
        "aug": true,
        "sep": true,
        "oct": true,
        "nov": false,
        "dec": false,
        "day1": 0,
        "month1": 0,
        "day2": 0,
        "month2": 0,
        "day3": 0,
        "month3": 0,
        "day4": 0,
        "month4": 0,
        "day5": 0,
        "month5": 0,
        "d1": 0,
        "w1": 0,
        "d2": 0,
        "w2": 0,
        "d3": 0,
        "w3": 0,
        "d4": 0,
        "w4": 0,
        "d5": 0,
        "w5": 0,
        "suspend": false,
        "random": false,
        "repeat": false,
        "atstart": true,
        "odd": false,
        "even": false,
        "x": 380,
        "y": 980,
        "wires": [
            [],
            [],
            [
                "3ce33470.31935c",
                "a138f48e.2bbb08",
                "c413f53e.29ef58"
            ]
        ]
    },
    {
        "id": "3ce33470.31935c",
        "type": "ui_switch",
        "z": "669e4b2e.c077d4",
        "name": "",
        "label": "Water Trees",
        "group": "e6e9e9c5.46a0c8",
        "order": 6,
        "width": "5",
        "height": "1",
        "passthru": false,
        "decouple": "true",
        "topic": "shm/garden/water_tree/state",
        "style": "",
        "onvalue": "on",
        "onvalueType": "str",
        "onicon": "nature",
        "oncolor": "green",
        "offvalue": "off",
        "offvalueType": "str",
        "officon": "nature",
        "offcolor": "grey",
        "x": 120,
        "y": 1020,
        "wires": [
            [
                "d6f61713.adc4b8"
            ]
        ]
    },
    {
        "id": "d6f61713.adc4b8",
        "type": "function",
        "z": "669e4b2e.c077d4",
        "name": "parse_timeout",
        "func": "// Outputs:\n// Counter_to_display, GPIO control via BigTimer, Counter_to_process\n\ntry{\n    var delay = flow.get('get_water_timeout');\n} catch (er) {\n    var delay = 10;\n}\n\n// Block loop of messages when off\nvar prev = flow.get('prev_tree_msg') || {payload: 'ignore'};\nnode.status({fill:\"green\",shape:\"ring\",text: delay+','+prev.payload+','+msg.payload});\nif ('off' == msg.payload && 'off' == prev.payload) return [null, null, null];\nnode.status({fill:\"green\",shape:\"dot\",text: delay+','+prev.payload+','+msg.payload});\n\n// Keep msg for next round\nflow.set('prev_tree_msg', msg);\nif ('on' == msg.payload) {\n    // Set delay, start countdown, on load\n    var m1 = {payload: delay};\n    return [m1, msg, m1];  \n} else if (('off' == msg.payload) || (msg.payload <= 1)) {\n    var m1 = {payload: 0};\n    if ((msg.payload <= 1) && (0 == delay)) { // request never off\n        return [m1, null, m1];\n    } else {\n        // off load, reset clear counter\n        return [{payload: ' '}, {payload: 'off'}, null];\n    }\n} else if (msg.payload > 1) {\n    // decrement countdown\n    var m1 = {payload: parseInt(msg.payload)-1};\n    return [m1, null, m1];\n}\n",
        "outputs": 3,
        "noerr": 0,
        "x": 170,
        "y": 1080,
        "wires": [
            [
                "d25b362a.630378"
            ],
            [
                "3ce33470.31935c",
                "c413f53e.29ef58"
            ],
            [
                "a1168f85.09676"
            ]
        ]
    },
    {
        "id": "d25b362a.630378",
        "type": "ui_text",
        "z": "669e4b2e.c077d4",
        "group": "e6e9e9c5.46a0c8",
        "order": 7,
        "width": "1",
        "height": "1",
        "name": "counter",
        "label": "",
        "format": "{{msg.payload}}",
        "layout": "row-right",
        "x": 390,
        "y": 1060,
        "wires": []
    },
    {
        "id": "a1168f85.09676",
        "type": "trigger",
        "z": "669e4b2e.c077d4",
        "op1": "",
        "op2": "",
        "op1type": "nul",
        "op2type": "pay",
        "duration": "1",
        "extend": false,
        "units": "min",
        "reset": "",
        "bytopic": "all",
        "name": "",
        "x": 190,
        "y": 1140,
        "wires": [
            [
                "d6f61713.adc4b8"
            ]
        ]
    },
    {
        "id": "7cd4b83b.e9d328",
        "type": "function",
        "z": "669e4b2e.c077d4",
        "name": "UI_to_flow_context",
        "func": "flow.set(\"get_water_timeout\", msg.payload);\nreturn null;",
        "outputs": 1,
        "noerr": 0,
        "x": 740,
        "y": 1280,
        "wires": [
            []
        ]
    },
    {
        "id": "7998052f.fd021c",
        "type": "ui_slider",
        "z": "669e4b2e.c077d4",
        "name": "timeout",
        "label": "Stop in {{value}} min",
        "group": "e6e9e9c5.46a0c8",
        "order": 3,
        "width": 0,
        "height": 0,
        "passthru": true,
        "topic": "/shm/garden/timeout",
        "min": 0,
        "max": "90",
        "step": 1,
        "x": 550,
        "y": 1280,
        "wires": [
            [
                "7cd4b83b.e9d328"
            ]
        ]
    },
    {
        "id": "aff2deab.9a23a",
        "type": "inject",
        "z": "669e4b2e.c077d4",
        "name": "",
        "topic": "",
        "payload": "10",
        "payloadType": "num",
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "x": 400,
        "y": 1280,
        "wires": [
            [
                "7998052f.fd021c"
            ]
        ]
    },
    {
        "id": "e6e9e9c5.46a0c8",
        "type": "ui_group",
        "z": "",
        "name": "Garden: Watering",
        "tab": "531ef1f0.9a426",
        "order": 3,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "2bd2afca.e962e",
        "type": "mqtt-broker",
        "z": "",
        "name": "",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "60",
        "cleansession": true,
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": ""
    },
    {
        "id": "531ef1f0.9a426",
        "type": "ui_tab",
        "z": "",
        "name": "MyDashboard",
        "icon": "home"
    }
] 

The valve/switch control script is here.

An alternative solution was:
Every time the UI control is taken, the parse_timeout should send 2 messages to the BigTimer: Manual + On or Off + Auto respectively.
This can be done with node.send() command (async messaging).
Advantage of this method versus current solution: MQTT receives manual on/off commands too for registration.
Disadvantage: more code, longer run time.

Hi guys,

Finally I overhauled the timer with UI feedback.

  • BigTimer was replaced with Schedex (thanks to @davidcgu) ;
  • Added adjusments;
  • Added status save/restore with persist nodes (Thanks to @Paul-Reed);
  • Fixed bugs.

The flow example is posted in Flows. With pictures and essential usage.
Thank you everyone!

3 Likes

Thanks a lot for sharing, this is the best anybody can do for other people

1 Like