Logic error - can i get some input please?

Guys,

I have a flow (below) - it accepts a string in from MQTT, runs it through a change node and outputs to the dashboard to tell me the state of incoming values.

What it does not do is to telll me when state transitions occur and what the transition was - it is at this stage purely an informational display on the dashboard.

Last week i made a request for some help with the next stage of development to enable me to identify and capture the transitions so they can be recorded and later analyzed - so i can say at (say) 1:30PM Solar On, 3:30PM Solar Off etc

Based on the responses from all the senior guys to my cry for help - i chose to follow a path that Andrei suggested in this thread - Advice please on best way to attack this

I have been slowly building that up and believe that i have that almost ready to implement.

However before i get to that i have an issue with initialisation of my data after a restart - here is the flow

[{"id":"32abd967.0a4c56","type":"mqtt in","z":"a78930fe.ed232","name":"Picaxe Pins Output Status","topic":"PicaxeOutputStatus","qos":"2","broker":"389dd264.7716de","x":150,"y":1540,"wires":[["f828a955.f5f338","86972491.f793a8","2f3e3321.e5e1bc"]]},{"id":"f828a955.f5f338","type":"debug","z":"a78930fe.ed232","name":"PicaxeStringDebug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":430,"y":1520,"wires":[]},{"id":"d1b4a842.c505c8","type":"ui_text","z":"a78930fe.ed232","group":"b7f64022.ddf0d","order":1,"width":0,"height":0,"name":"Status Update","label":"System Status","format":"{{msg.payload}}","layout":"col-center","x":740,"y":1560,"wires":[]},{"id":"86972491.f793a8","type":"change","z":"a78930fe.ed232","name":"Parse Picaxe Outputs and Report","rules":[{"t":"change","p":"payload","pt":"msg","from":"128.00","fromt":"str","to":"OutPut Status: Overheat CP Off","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"144.00","fromt":"str","to":"Circ Pump on - no heat","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"160.00","fromt":"str","to":"Overheat ! CP Off - Solar On","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"176.00","fromt":"str","to":"Solar On - CP On","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"192.00","fromt":"str","to":"CP off - Boiler on","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"208.00","fromt":"str","to":"Boiler on - CP on","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":1560,"wires":[["d1b4a842.c505c8"]]},{"id":"bd9d6b84.f540a8","type":"function","z":"a78930fe.ed232","name":"TestFunction","func":"//This function will only get output on the change of\n//Pins value from Picaxe/Arduino\n\n//We need to capture the current state from the flow variable\n//Then update that with the new values\n\n//Then we need to perform calculations to work out what\n//has changed with the transition\nvar p = flow.get('PreviousPicaxePinState') || 0;\n\n//Read Current Value of Variable If Blank then set to 0\nvar c = flow.get('CurrentPicaxePinState') || 0;\n\n//Save the Previous value so it can be used later\nflow.set('PreviousPicaxePinState', c);\n\n//Assign incoming value to variable\nvar n = msg.payload;\n\n//Update the Flow varaible\nflow.set('CurrentPicaxePinState', n);\n\nvar n = (c.slice(0, 3) + (p.slice(0, 3)));\nmsg.payload = n;\nreturn msg;","outputs":2,"noerr":0,"x":410,"y":1640,"wires":[["3072377d.fda008","7af13880.aa14e8"],[]]},{"id":"3072377d.fda008","type":"debug","z":"a78930fe.ed232","name":"TestFunctionVariable","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":760,"y":1640,"wires":[]},{"id":"2f3e3321.e5e1bc","type":"rbe","z":"a78930fe.ed232","name":"TestForPinStateChange","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":450,"y":1600,"wires":[["8e7a7b75.f17478","bd9d6b84.f540a8"]]},{"id":"8e7a7b75.f17478","type":"debug","z":"a78930fe.ed232","name":"RBETestDebug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":740,"y":1600,"wires":[]},{"id":"7af13880.aa14e8","type":"link out","z":"a78930fe.ed232","name":"Out to Status and Action","links":["28448ec9.40c042"],"x":555,"y":1680,"wires":[]},{"id":"389dd264.7716de","type":"mqtt-broker","z":"a78930fe.ed232","broker":"localhost","port":"1883","clientid":"Node-Red","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"b7f64022.ddf0d","type":"ui_group","z":"","name":"Control Status","tab":"c9d53e06.a95c8","order":2,"disp":true,"width":"6"},{"id":"c9d53e06.a95c8","type":"ui_tab","z":"","name":"CURTIN house main page","icon":"dashboard","order":1}]

Lets say that the MQTT incoming data is 176 - i do a reload of NR and the first data (for example) it gets in is 128 my variable that i want to pass along to Andrei's function should be 176128 but what happens is that the first value is initialised as the first incoming data and the 2nd run through gives me the same so i end up with 176176 - which is not a valid action i.e. nothing has changed - at the moment whilst i am testing this i have written some dummy values in Andreis function for each of these cases (128128, 144144 etc etc) - but i am looking for a better way to handle it.

Would i be better off writing the value each time i take a reading into a text file that could then be read back for the first run ? I am running this on a PC/VM so do not have any write issues with media etc.

Any input greatly appreciated

(First time pasting a flow - hopefully got that one right)

regards

Craig

You should use presistable context storage. Documented here https://nodered.org/docs/user-guide/context
And there is many topics and examples about this.

And then you probably need to do something like the flow bellow. Investigate it deeply and let's see.

[{"id":"a8d284bc.f736a8","type":"inject","z":"81c5c63b.6b8288","name":"","topic":"","payload":"176","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":370,"y":400,"wires":[["178c7467.7db38c"]]},{"id":"831e79fc.497688","type":"inject","z":"81c5c63b.6b8288","name":"","topic":"","payload":"192","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":370,"y":440,"wires":[["178c7467.7db38c"]]},{"id":"c1263d06.c4dbb","type":"inject","z":"81c5c63b.6b8288","name":"","topic":"","payload":"208","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":370,"y":480,"wires":[["178c7467.7db38c"]]},{"id":"9f98d42a.a96b58","type":"inject","z":"81c5c63b.6b8288","name":"","topic":"","payload":"240","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":370,"y":520,"wires":[["178c7467.7db38c"]]},{"id":"178c7467.7db38c","type":"change","z":"81c5c63b.6b8288","name":"parse","rules":[{"t":"set","p":"command","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"time","pt":"msg","to":"$split($split($now(), \"T\")[1],\".\")[0]\t","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"( $bits := $formatNumber($number($formatBase(payload, 2)), '00000000').$split(''); $pins := ['Power','Pump','Boiler','Solar','Valve1','Valve2','Valve3','Valve4']; $zip($pins, $bits) { $[0]: $number($[1]) } ) ","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":530,"y":460,"wires":[["d1fbfafd.c90218"]]},{"id":"d1fbfafd.c90218","type":"function","z":"81c5c63b.6b8288","name":"store changes","func":"var lastcommand = global.get(\"lastcommand\");\nlastcommand.lastupdate = msg.time;\nlastcommand.value = msg.command;\nglobal.set(\"lastcommand\",lastcommand);\n\nfor(var item in msg.payload){\n var globalitem = global.get(item);\n if(globalitem.value != msg.payload[item]){\n globalitem.value = msg.payload[item];\n globalitem.lastupdate = msg.time;\n global.set(item,globalitem);\n }\n \n}\n\n\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":460,"wires":[["9081f200.4a9b7","36c5ebbe.3293c4","e971b277.9c2b2","3c7658c6.640d78","f176ecf0.56154","2c97a3d1.c74fcc","955da864.f0fc18","bf10843d.541258","bb74d27a.ece01"]]},{"id":"b42cec17.c0a69","type":"change","z":"81c5c63b.6b8288","name":"create context","rules":[{"t":"set","p":"Power","pt":"global","to":"{\"lastupdate\":0,\"value\":0}","tot":"json"},{"t":"set","p":"Pump","pt":"global","to":"{\"lastupdate\":0,\"value\":0}","tot":"json"},{"t":"set","p":"Boiler","pt":"global","to":"{\"lastupdate\":0,\"value\":0}","tot":"json"},{"t":"set","p":"Solar","pt":"global","to":"{\"lastupdate\":0,\"value\":0}","tot":"json"},{"t":"set","p":"Valve1","pt":"global","to":"{\"lastupdate\":0,\"value\":0}","tot":"json"},{"t":"set","p":"Valve2","pt":"global","to":"{\"lastupdate\":0,\"value\":0}","tot":"json"},{"t":"set","p":"Valve3","pt":"global","to":"{\"lastupdate\":0,\"value\":0}","tot":"json"},{"t":"set","p":"Valve4","pt":"global","to":"{\"lastupdate\":0,\"value\":0}","tot":"json"},{"t":"set","p":"lastcommand","pt":"global","to":"{\"lastupdate\":0,\"value\":0}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":140,"wires":[[]]},{"id":"8cb92c40.311bc","type":"inject","z":"81c5c63b.6b8288","name":"init","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":370,"y":140,"wires":[["b42cec17.c0a69"]]},{"id":"9081f200.4a9b7","type":"template","z":"81c5c63b.6b8288","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Power : {{global.Power.value}} | {{global.Power.lastupdate}}","output":"str","x":900,"y":340,"wires":[["42201104.7ac43"]]},{"id":"42201104.7ac43","type":"ui_text","z":"81c5c63b.6b8288","group":"b261ec5b.39f62","order":2,"width":0,"height":0,"name":"","label":"","format":"{{msg.payload}}","layout":"row-left","x":1070,"y":340,"wires":[]},{"id":"36c5ebbe.3293c4","type":"template","z":"81c5c63b.6b8288","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Pump : {{global.Pump.value}} | {{global.Pump.lastupdate}}","output":"str","x":900,"y":380,"wires":[["7de6675c.bb3058"]]},{"id":"7de6675c.bb3058","type":"ui_text","z":"81c5c63b.6b8288","group":"b261ec5b.39f62","order":3,"width":0,"height":0,"name":"","label":"","format":"{{msg.payload}}","layout":"row-left","x":1070,"y":380,"wires":[]},{"id":"e971b277.9c2b2","type":"template","z":"81c5c63b.6b8288","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Boiler : {{global.Boiler.value}} | {{global.Boiler.lastupdate}}","output":"str","x":900,"y":420,"wires":[["8905aafb.00fea8"]]},{"id":"8905aafb.00fea8","type":"ui_text","z":"81c5c63b.6b8288","group":"b261ec5b.39f62","order":4,"width":0,"height":0,"name":"","label":"","format":"{{msg.payload}}","layout":"row-left","x":1070,"y":420,"wires":[]},{"id":"3c7658c6.640d78","type":"template","z":"81c5c63b.6b8288","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Solar : {{global.Solar.value}} | {{global.Solar.lastupdate}}","output":"str","x":900,"y":460,"wires":[["9615fa3d.2cf5d8"]]},{"id":"9615fa3d.2cf5d8","type":"ui_text","z":"81c5c63b.6b8288","group":"b261ec5b.39f62","order":5,"width":0,"height":0,"name":"","label":"","format":"{{msg.payload}}","layout":"row-left","x":1070,"y":460,"wires":[]},{"id":"f176ecf0.56154","type":"template","z":"81c5c63b.6b8288","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Valve1 : {{global.Valve1.value}} | {{global.Valve1.lastupdate}}","output":"str","x":900,"y":500,"wires":[["390acfc9.27fc8"]]},{"id":"390acfc9.27fc8","type":"ui_text","z":"81c5c63b.6b8288","group":"b261ec5b.39f62","order":6,"width":0,"height":0,"name":"","label":"","format":"{{msg.payload}}","layout":"row-left","x":1070,"y":500,"wires":[]},{"id":"2c97a3d1.c74fcc","type":"template","z":"81c5c63b.6b8288","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Valve2 : {{global.Valve2.value}} | {{global.Valve2.lastupdate}}","output":"str","x":900,"y":540,"wires":[["d0bf318b.3b08f"]]},{"id":"d0bf318b.3b08f","type":"ui_text","z":"81c5c63b.6b8288","group":"b261ec5b.39f62","order":7,"width":0,"height":0,"name":"","label":"","format":"{{msg.payload}}","layout":"row-left","x":1070,"y":540,"wires":[]},{"id":"955da864.f0fc18","type":"template","z":"81c5c63b.6b8288","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Valve3 : {{global.Valve3.value}} | {{global.Valve3.lastupdate}}","output":"str","x":900,"y":580,"wires":[["2e51816a.61e0de"]]},{"id":"2e51816a.61e0de","type":"ui_text","z":"81c5c63b.6b8288","group":"b261ec5b.39f62","order":8,"width":0,"height":0,"name":"","label":"","format":"{{msg.payload}}","layout":"row-left","x":1070,"y":580,"wires":[]},{"id":"bf10843d.541258","type":"template","z":"81c5c63b.6b8288","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Valve4 : {{global.Valve4.value}} | {{global.Valve4.lastupdate}}","output":"str","x":900,"y":620,"wires":[["90ac77f3.1a10b8"]]},{"id":"90ac77f3.1a10b8","type":"ui_text","z":"81c5c63b.6b8288","group":"b261ec5b.39f62","order":9,"width":0,"height":0,"name":"","label":"","format":"{{msg.payload}}","layout":"row-left","x":1070,"y":620,"wires":[]},{"id":"81d4dff1.9c9f9","type":"ui_text","z":"81c5c63b.6b8288","group":"b261ec5b.39f62","order":1,"width":0,"height":0,"name":"","label":"","format":"{{msg.payload}}","layout":"row-left","x":1070,"y":260,"wires":[]},{"id":"bb74d27a.ece01","type":"template","z":"81c5c63b.6b8288","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Last command : {{global.lastcommand.value}} | {{global.lastcommand.lastupdate}}","output":"str","x":900,"y":260,"wires":[["81d4dff1.9c9f9"]]},{"id":"b261ec5b.39f62","type":"ui_group","z":"","name":"Default","tab":"b5e9809.d91cc8","disp":true,"width":"6","collapse":false},{"id":"b5e9809.d91cc8","type":"ui_tab","z":"","name":"Default","icon":"dashboard","order":1}]

Hi Craig,

Since version 0.19 it is possible to store the context in the filesystem. Using this functionality your data will persist across system restarts. I just tested your flow and it seems to be working as you expect and want.

In order to enable this functionality you need to make a small change to the settings.js file from node-RED. These are the lines I have added.

contextStorage: {
        default: {
            module: "localfilesystem"
        },
        memoryOnly: {
            module: "memory"
        }
     }

You will see that Node-RED wil create a folder named "context" and the persistable data for each flow will be stored in a separate json file.

{
    "PreviousPicaxePinState": "128.00",
    "CurrentPicaxePinState": "144.00"
}

Here is the link with directions on how to enable it: Working with context : Node-RED

I forgot to show you what happened after i tested your flow and then restarted the computer (I had to for another reason) and then triggered the flow again:

r-001

After restarting I input the value 128 and got:

r-002

Debug panel:

r-003

There is a small thing to be verified in the code. It is not breaking the code but it deserves a look.

In the very first two iterations, the variables p and c will be assigned to the number zero. Afterward, you will try to concatenate them as strings and use the string method slice. Because in the initial iterations those are numbers a warning error is thrown. Perhaps you should verify if it is possible to replace these lines ?

var p = flow.get('PreviousPicaxePinState') || 0;

var c = flow.get('CurrentPicaxePinState') || 0;

By these ones:

var p = flow.get('PreviousPicaxePinState') || "000";

var c = flow.get('CurrentPicaxePinState') || "000";

Hey Guys,

Once again thanks for the quick and succinct replies !

I will implement your suggestions Andrei and report back if that lets me remove the kludges i hat put into the code !! Thanks also for the bug pickup on the incorrect cast - i did not know i could do it as astring for the default value if not assigned - that was next on my list to chase down !

I will also study the other flow from @hotNipi and see what i can learn from that.

Once again this forum is the most helpful i have ever been involved with

regards

Craig

Andrei,

OK i have tried to add the context storage option to my node-red and it will not have a bar of it - keeps on hanging on a restart - see screen shots below

I have tried to add it right to the end of the current file (after the last Ellipse) and that produced the error, i have then tried to add it into the middle of the settings area - same result - and then finally i added it right at the start of the file and same result.

I have referred to the doc page you supplied and it looks the same

I am running Node-Red 19.1 i am initiating a restart with the command

systemctl restart nodered

The first screenshot is with my standard settings.js

Here is when i add it to end of the file (you can see the last ellipse above the block)

I am using Notepad ++ on windows - i have tried scp-ing the file to my desktop and editing it there and have also tried to edit it in place using winscp

And here is the screenshot with a restart

I get the same error if i move it above the ellipse - or have it at the start of the file

Not sure what is going on here ?

Craig

I have even tried taking the default from the examples doc as such

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

and i get the same error no matter where i insert it into the file.

Craig

Craig, note that it is mandatory to insert a comma before the contextStorage key to separe it from the previous property (editorTheme)

 editorTheme: {
        projects: {
            enabled: true
        }
    } ,
    contextStorage: {
        default: {
            module: "localfilesystem"
        },
        memoryOnly: {
            module: "memory"
        }
     }
}

Aah - almost obvious !!!

Will try that one thanks !!

Craig

Yep that did it thanks Sir !!

I wonder where you could put something like that in a FAQ so people know ? I guess you only get bitten once !

Craig

Good to know. I am pretty sure that this functionality will be very useful for your purpose.

Andrei,

One last one to put this to bed - how do i switch between the two types of storage (memory) and Filesystem - obviously as you say now the default is filesystem and i can see the files being created - there must be a programmatic way to switch to memory if i do not want the filesystem overhead for some variables ??

On a side note - code you gave me previously to create the memory def table (which is where all this feeds into) - will that still be stored in memory or will that now also default to filesystem ?

regards

Craig

Answered the 2nd part myself - there is a directory and file created for the def that contains all of the defined message types

regards

Craig

When using a node like the change node you can select the storage option with the icon on the right side.

r-01

When using JavaScript statement inside a function node then you follow the explanation given in this link (multiple context stores):

https://nodered.org/docs/writing-functions#storing-data

Brilliant thanks !

Craig