Please help me with creating a simple generator counter

Could someone help me with creating a simple generator counter, only using the JavaScript function node without using context storage?
Here is the prototype, but I can't use it in my application because it uses an additional trigger node

image

[{"id":"99785fc8.6687a","type":"inject","z":"15cf35e317766a1e","name":"trigger to start","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"0","payloadType":"str","x":1550,"y":1940,"wires":[["9bc2c452.643d38"]]},{"id":"5cbf5a97.a340a4","type":"debug","z":"15cf35e317766a1e","name":"","active":true,"console":false,"complete":false,"x":1910,"y":1940,"wires":[]},{"id":"1ab3bd1e42e54874","type":"inject","z":"15cf35e317766a1e","name":"trigger to stop","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"stop","payloadType":"str","x":1550,"y":2000,"wires":[["28e99bac12b0ed7b"]]},{"id":"2ec4101beac37945","type":"group","z":"15cf35e317766a1e","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["9bc2c452.643d38","28e99bac12b0ed7b"],"x":1634,"y":1901,"w":173,"h":140},{"id":"9bc2c452.643d38","type":"function","z":"15cf35e317766a1e","g":"2ec4101beac37945","name":"counter","func":"msg.payload = parseInt(msg.payload) + 1;\n\n\nreturn msg;","outputs":"1","noerr":0,"initialize":"","finalize":"","libs":[],"x":1721,"y":1942,"wires":[["5cbf5a97.a340a4","28e99bac12b0ed7b"]]},{"id":"28e99bac12b0ed7b","type":"trigger","z":"15cf35e317766a1e","g":"2ec4101beac37945","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"1","extend":false,"overrideDelay":false,"units":"s","reset":"stop","bytopic":"all","topic":"topic","outputs":1,"x":1720,"y":2000,"wires":[["9bc2c452.643d38"]]}]

Could I ask why you are averse to using context storage?

You can use the javascript timer function setInterval() to do that.

This will be used inside the node-red-contrib-hal2 node as a script, but multiple times (similar to template usage)

Contrib nodes can store data within their own context object, and as far as I can recall would be stored in the scope of each node, but I'm sure you will have already considered that option.

Here is my flow:

[{"id":"9ff1d0c1701d7a88","type":"inject","z":"15cf35e317766a1e","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"start","payloadType":"str","x":1150,"y":340,"wires":[["595eb2d2d60ffe40"]]},{"id":"3984225bc1c55359","type":"debug","z":"15cf35e317766a1e","name":"debug 152","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1530,"y":340,"wires":[]},{"id":"595eb2d2d60ffe40","type":"function","z":"15cf35e317766a1e","name":"function 7","func":"var counter = 0;\nvar interval;\nvar running = false;\n\nfunction startCounter() {\n    if (!running) {\n        running = true;\n        interval = setInterval(function () {\n            counter++;\n            if (counter <= 9) {\n                node.send({ payload: counter });\n            } else {\n                stopCounter();\n            }\n        }, 1000);\n    }\n}\n\nfunction stopCounter() {\n    if (running) {\n        clearInterval(interval);\n        running = false;\n    }\n}\n\nif (msg.payload === 'start') {\n    startCounter();\n} else if (msg.payload === 'stop') {\n    stopCounter();\n} else {\n    node.error('Invalid input. Must be \"start\" or \"stop\".');\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1340,"y":340,"wires":[["3984225bc1c55359"]]},{"id":"b22bfaaa6840af24","type":"inject","z":"15cf35e317766a1e","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"error","payloadType":"str","x":1150,"y":420,"wires":[["595eb2d2d60ffe40"]]},{"id":"5b4ee0997e709bcf","type":"inject","z":"15cf35e317766a1e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"stop","payloadType":"str","x":1150,"y":380,"wires":[["595eb2d2d60ffe40"]]}]

One problem stop "button" wont stop the count, and the multi-click of the start "button" creates a multi-count instead of ignoring or starting from zero.

var counter = 0;
var interval;
var running = false;

function startCounter() {
    if (!running) {
        running = true;
        interval = setInterval(function () {
            counter++;
            if (counter <= 9) {
                node.send({ payload: counter });
            } else {
                stopCounter();
            }
        }, 1000);
    }
}

function stopCounter() {
    if (running) {
        clearInterval(interval);
        running = false;
    }
}

if (msg.payload === 'start') {
    startCounter();
} else if (msg.payload === 'stop') {
    stopCounter();
} else {
    node.error('Invalid input. Must be "start" or "stop".');
}

image

Try this.

[
    {
        "id": "98726f178094f33b",
        "type": "tab",
        "label": "Flow 2",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "9ff1d0c1701d7a88",
        "type": "inject",
        "z": "98726f178094f33b",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "start",
        "payloadType": "str",
        "x": 190,
        "y": 160,
        "wires": [
            [
                "595eb2d2d60ffe40"
            ]
        ]
    },
    {
        "id": "3984225bc1c55359",
        "type": "debug",
        "z": "98726f178094f33b",
        "name": "debug 152",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 570,
        "y": 160,
        "wires": []
    },
    {
        "id": "595eb2d2d60ffe40",
        "type": "function",
        "z": "98726f178094f33b",
        "name": "function 7",
        "func": "var counter = 0;\nlet intervalID = context.get('intervalID') ?? undefined\n\n\nfunction startCounter() {\n    if (intervalID != undefined) { clearInterval(intervalID) }\n    intervalID = setInterval(function () {\n        counter++;\n        if (counter <= 9) {\n            node.send({ payload: counter });\n        } else {\n            stopCounter();\n        }\n    }, 1000);\n    \n}\n\n\nfunction stopCounter() {\n    clearInterval(intervalID);\n}\n\n\nif (msg.payload === 'start') {\n    startCounter();\n} else if (msg.payload === 'stop') {\n    stopCounter()\n} else {\n    node.error('Invalid input. Must be \"start\" or \"stop\".');\n}\n\n\ncontext.set('intervalID', intervalID)\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 380,
        "y": 160,
        "wires": [
            [
                "3984225bc1c55359"
            ]
        ]
    },
    {
        "id": "b22bfaaa6840af24",
        "type": "inject",
        "z": "98726f178094f33b",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "error",
        "payloadType": "str",
        "x": 190,
        "y": 240,
        "wires": [
            [
                "595eb2d2d60ffe40"
            ]
        ]
    },
    {
        "id": "5b4ee0997e709bcf",
        "type": "inject",
        "z": "98726f178094f33b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "stop",
        "payloadType": "str",
        "x": 190,
        "y": 200,
        "wires": [
            [
                "595eb2d2d60ffe40"
            ]
        ]
    }
]

PS. You can't do it without using context storage.

I need to use this counting script in the node-red-contrib-hal2 and usage context storage interferes somehow.

Thanks for cleaning up the code.

It's always a good idea to check the node's gitHub page to see your issue matches someone else's. You might want to see if this helps:

It's me who opened this issue here: Home assistant NodeRed addon memory only in Context store · Issue #26 · flic/node-red-contrib-hal2 · GitHub

But the essence of the problem is quite different. And I find out that the problem is not with the node-red-contrib-hal2 node, but with my NodeRed settings.

Does this mean you have solved your issue? If not what do you mean my your NodeRed settings what you have in settings.js?

These are two completely different questions/issues.

So are you going to answer my question?

Solved this by enabling "Saving context data to the file-system" this is disabled in settings.js by default:
https://nodered.org/docs/user-guide/context

Saving context data to the file-system
To enable file-based storage, the following option can be used:

contextStorage: {
default: {
module: "localfilesystem"
}
}

So if this 'solution' is to be used in a contrib node, do you expect all users of the node to edit their settings.js file?

I think this is clear from the Node-red user guide: Working with context : Node-RED
If the user wants to save any node data to localfilesystem

Saving context data to the file-system

To enable file-based storage, the following option can be used:

contextStorage: {
   default: {
       module: "localfilesystem"
   }
}

This sets the default context store to be an instance of the localfilesystem plugin, with all of its default settings. That means:

  • it will store the context data in files under ~/.node-red/context/
  • it caches the values in memory and only writes them out to the file-system every 30 seconds.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.