Day of Year Function To Control a Relay - Charging Device

Hello everybody. I am using NR to control chargers - to prevent overcharging. However, one charger - for toothbrush - cannot be controlled by (for example) a power-reading socket because the power consumed is too small to be recorded. (I have used this solution for washing machine alerts.) I have therefore resorted to a fixed 3 day charging cycle but this cannot be automated with Big Timer because the "BAN" options are only for "even" and "odd" days (ok for a 2 day charging cycle but not any other). So my first idea was to use a "get day of year" function so that the relay is only triggered when the day (counting from 1 January) is divisible by 3.

However, as I am not an IT specialist and have no programming background I have not been able to find Javascript code to do this - cide which can be cut and pasted into a NR function node. I also want to have code which allows me (in NR) to select whether I want 3, 4 or 5 etc. days as the period between charges. If the solution allows simplifies the process of choosing the actual charging period (in hours) that would be very handy. So my questions are:

  1. Is a function node the best way to control a relay in this way?
    2, If so what would the code be - bearing in mind leap year complications.
  2. If not what alternative NR solution could be used?

Have you looked at the other timing/scheduling nodes? There are quite a few.

Tried simpletime last week to get day of ytear but did not work - "mymonth" and others were ok but not the one I wanted. So just checked again and realised that it had been updated last week. It now produces a string containing the number for current doy so now I have a good starting point.

I am new to NR so had assumed that nodes automatically updated if you had installed them. Bad assumption!

also worth checking out https://flows.nodered.org/node/node-red-contrib-cron-plus
as it is very flexible

I like using function nodes and built in nodes as it reduces your dependency on 3rd party nodes which may stop being supported.

This is a modification of a timer I built. It uses a flow variable flow.chargeTime (how long you want to charge the toothbrush for) and an inject node that will start the timer every 72 hours. The stop node can be removed for your purposes, it is used to stop the timer partway through and sends 'chargeDisable'. The function sends a message 'chargeEnable' when started, and 'chargeDisable' when stopped.

The 'Start every 72 hours' inject node could be modified to send a payload every day, and the function modified to increment a count when it receives a message. When this value reaches 3, it would start the timer and reset, otherwise it would do nothing. This would allow you to have better control of when the charger is turned on and off - at the moment the beginning of the 3 day cycle will be dependent on when you deploy the flow.

[
    {
        "id": "72140aba.c30d64",
        "type": "inject",
        "z": "59fcda25.edd454",
        "name": "Start every 72 hours",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "00 22 * * *",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "true",
        "payloadType": "bool",
        "x": 520,
        "y": 120,
        "wires": [
            [
                "6e6703ab.87e13c"
            ]
        ]
    },
    {
        "id": "30b0c638.4abdfa",
        "type": "inject",
        "z": "59fcda25.edd454",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "stop",
        "payloadType": "str",
        "x": 470,
        "y": 180,
        "wires": [
            [
                "6e6703ab.87e13c"
            ]
        ]
    },
    {
        "id": "ff53b61c.471938",
        "type": "inject",
        "z": "59fcda25.edd454",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "5",
        "payloadType": "num",
        "x": 470,
        "y": 60,
        "wires": [
            [
                "4ec48c94.eaf824"
            ]
        ]
    },
    {
        "id": "4ec48c94.eaf824",
        "type": "change",
        "z": "59fcda25.edd454",
        "name": "chargeTime (s)",
        "rules": [
            {
                "t": "set",
                "p": "chargeTime",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 660,
        "y": 60,
        "wires": [
            []
        ]
    },
    {
        "id": "6e6703ab.87e13c",
        "type": "function",
        "z": "59fcda25.edd454",
        "name": "",
        "func": "if(msg.payload !== true && msg.payload !== 'stop') {\n    return;\n}\n\nvar timerEnable;\n\nfunction timer() {\n    context.set(\"timerValue\", (context.get(\"timerValue\") - 1));\n    node.status({fill:\"yellow\",shape:\"dot\",text:\"Charge Time Remaining: \" +context.get(\"timerValue\")}); \n    if (context.get(\"timerValue\") === 0) {\n        msg.payload = \"chargeDisable\"\n        node.status({fill:\"green\",shape:\"dot\",text:\"Charging finished\"}); \n        node.send(msg);\n        clearInterval(context.timer);\n        context.set(\"timerRunning\", false)\n        return;\n   }\n}\n\nif (msg.payload === \"stop\") {\n    timerEnable = false;\n}\nelse {\n    timerEnable = true;\n}\n\nif (context.get(\"timerRunning\") === false && timerEnable === true) { //Start\n    context.set(\"timerValue\", flow.get(\"timeout\"));\n    node.status({fill:\"blue\",shape:\"dot\",text:\"Charge Time Remaining: \" + context.get(\"timerValue\")});\n    context.timer = setInterval(timer,1000);\n    context.set(\"timerRunning\", true);\n    \n    msg.payload = 'chargeEnable';\n    node.send(msg);\n}\n\nelse if (context.get(\"timerRunning\") === true && timerEnable === true) { //Reset\n    context.set(\"timerValue\", flow.get(\"timeout\"));\n    //msg.payload = 'timer reset';\n    node.status({fill:\"blue\",shape:\"ring\",text:\"Charge Time Remaining: \" + context.get(\"timerValue\")});\n    context.set(\"timerRunning\", true);\n}\n\nelse if (timerEnable === false) { //Stop\n    //msg.payload = 'timer stop';\n    clearInterval(context.timer);\n    node.status({fill:\"red\",shape:\"ring\",text:\"Stopped\"});\n    context.set(\"timerRunning\", false);\n    msg.payload = 'chargeDisable';\n    node.send(msg);\n}\n\nreturn;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "context.set(\"timerRunning\", false);",
        "finalize": "",
        "x": 640,
        "y": 180,
        "wires": [
            [
                "3e2c38c1.05ad18"
            ]
        ]
    },
    {
        "id": "3e2c38c1.05ad18",
        "type": "debug",
        "z": "59fcda25.edd454",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 810,
        "y": 180,
        "wires": []
    }
]

You could put this code into a function node, and it will send true if the doy, when divided by 3 returns a whole number, otherwise does nothing;

// When divided by 3, is the result an integer?
if (Number.isInteger((msg.mydoy)/3)){
// If it is, then send the payload as true
node.send({payload:true});
// ...otherwise - do nothing
} else {return;}
[{"id":"22d2c7a5.e93958","type":"inject","z":"c53060.842a0fa","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":140,"y":1290,"wires":[["30069df9.a01282"]]},{"id":"82432f72.e126f","type":"debug","z":"c53060.842a0fa","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":640,"y":1290,"wires":[]},{"id":"5a92ee25.6c136","type":"function","z":"c53060.842a0fa","name":"","func":"// When divided by 3, is the result an integer?\nif (Number.isInteger((msg.mydoy)/3)){\n// If it is, then send the payload as true\nnode.send({payload:true});\n// ...otherwise - do nothing\n} else {return;}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":470,"y":1290,"wires":[["82432f72.e126f"]]},{"id":"30069df9.a01282","type":"simpletime","z":"c53060.842a0fa","name":"","mydate":true,"myymd":true,"myyear":true,"mymonth":true,"mymonthn":true,"mydom":true,"mydoy":true,"myday":true,"myhourpm":true,"myhour":true,"mytime":true,"mytimes":true,"myminute":true,"myminutes":true,"mysecond":true,"mymillis":true,"myepoch":true,"myrawdate":true,"mypm":true,"x":300,"y":1290,"wires":[["5a92ee25.6c136"]]}]

2 Likes

Or
This in a function will return true every 3 days from 1st of January.

msg.days3 = Math.round((new Date().setHours(23) - new Date(new Date().getFullYear(), 0, 1, 0, 0, 0))/1000/60/60/24) % 3 === 0 ? true : false;
return msg;

Thanks for the replies everyone. I shall play around with the various suggestions - part of this process is, of course, just learning some useful approaches and having some more samples of coding that can be cut and pasted into other flows where this is going to be of help. The elegance of coding a function node is, of course, the way to go - but using existing nodes and piecing together a flow that is more aligned to the individual's IT skills (or lack thereor) has a certain educative value for the individual concerned.

1 Like