Fancy buttons updating

Hi I am working with @scargill 's wonderful "fancy buttons" I have run into some problems. I am trying to make a two state button.

  1. When I click on the buttons on the computer the status is not updated on my phone or the other way around. (note the injects in the flow if I trigger these the status is updated across all devices but the attached LEDs don't change)

  2. The buttons don't have an initial status, you have to click twice to get the formatting to change.

I am not sure if this is not possible because of just changing the background of the button and not really changing the state. Any help would be appreciated. Below is my sample flow.

 [{"id":"60de705.e2c779","type":"tab","label":"Flow 5","disabled":false,"info":""},{"id":"712497b9.67d868","type":"inject","z":"60de705.e2c779","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":100,"wires":[["1a6dbabf.5da795","99342e02.5d917","515f2c37.fce404"]]},{"id":"15c400c6.c4d3df","type":"inject","z":"60de705.e2c779","name":"","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":160,"wires":[["1a6dbabf.5da795","515f2c37.fce404","99342e02.5d917"]]},{"id":"50959269.99d92c","type":"function","z":"60de705.e2c779","name":"","func":"\nif (msg.payload === false ){\n    msg.payload = true;\n}\nelse { msg.payload = false;\n}\nreturn msg;\n","outputs":1,"noerr":0,"x":340.0001220703125,"y":137.33332061767578,"wires":[["1a6dbabf.5da795","db80c73c.1d6848"]]},{"id":"1a6dbabf.5da795","type":"ui_template","z":"60de705.e2c779","group":"1732cfe.a123c3","name":"Push-Walk","order":9,"width":6,"height":2,"format":"<md-button class=\"vibrate filled touched bigfont rounded\" style=\"background-color:#212227\" ng-click=\"send({payload: msg.payload })\">\n\n<svg  width=\"360px\" height=\"100px\" version=\"1.1\" viewBox=\"0 0 800 200\">\n <g id=\"Button_Long\">\n  <rect fill=\"#212227\" width=\"800\" height=\"200\"/>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#23C48E' : '#212227'}\">   \n    <rect width=\"800\" height=\"200\" rx=\"100\" ry=\"100\"/>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#212227' : '#23C48E'}\">\n    <rect fill x=\"10\" y=\"10\" width=\"778\" height=\"180\" rx=\"90\" ry=\"90\"/>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? 'none' : '#212227'}\">\n    <text x=\"400\" y=\"125\"  style=\"text-anchor:middle\" font-weight=\"normal\" font-size=\"75\" font-family=\"Arial\">WALK LIGHTS ON</text>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#23C48E' : 'none'}\">\n    <text x=\"400\" y=\"125\"  style=\"text-anchor:middle\" font-weight=\"normal\" font-size=\"75\" font-family=\"Arial\">WALK LIGHTS OFF</text>\n  </g>\n </g>\n</svg>\n\n\n</md-button>\n","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","x":330,"y":180,"wires":[["50959269.99d92c"]]},{"id":"515f2c37.fce404","type":"ui_template","z":"60de705.e2c779","group":"1732cfe.a123c3","name":"Push-SideWalk","order":6,"width":6,"height":2,"format":"<md-button class=\"vibrate filled touched bigfont rounded\" style=\"background-color:#212227\" ng-click=\"send({payload: msg.payload })\">\n\n<svg  width=\"360px\" height=\"90px\" version=\"1.1\" viewBox=\"0 0 800 200\">\n <g id=\"Button_Long\">\n  <rect fill=\"#212227\" width=\"800\" height=\"200\"/>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#23C48E' : '#212227'}\">   \n    <rect width=\"800\" height=\"200\" rx=\"100\" ry=\"100\"/>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#212227' : '#23C48E'}\">\n    <rect fill x=\"10\" y=\"10\" width=\"778\" height=\"180\" rx=\"90\" ry=\"90\"/>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#23C48E' : '#212227'}\">\n    <text x=\"400\" y=\"125\"  style=\"text-anchor:middle\" font-weight=\"normal\" font-size=\"75\" font-family=\"Arial\">SIDEWALK LIGHTS</text>\n  </g>\n </g>\n</svg>\n\n\n</md-button>\n","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","x":320,"y":320,"wires":[["98741886.86c3e8"]]},{"id":"1723bcb9.0afbb3","type":"ui_led","z":"60de705.e2c779","order":1,"group":"1732cfe.a123c3","width":0,"height":0,"label":"","labelPlacement":"left","labelAlignment":"left","colorForValue":[{"color":"#ff0000","value":"false","valueType":"bool"},{"color":"#008000","value":"true","valueType":"bool"}],"allowColorForValueInMessage":false,"shape":"circle","showGlow":true,"name":"","x":500,"y":380,"wires":[]},{"id":"db80c73c.1d6848","type":"ui_led","z":"60de705.e2c779","order":2,"group":"1732cfe.a123c3","width":0,"height":0,"label":"","labelPlacement":"left","labelAlignment":"left","colorForValue":[{"color":"#ff0000","value":"false","valueType":"bool"},{"color":"#008000","value":"true","valueType":"bool"}],"allowColorForValueInMessage":false,"shape":"circle","showGlow":true,"name":"","x":550,"y":80,"wires":[]},{"id":"424fc71e.976628","type":"function","z":"60de705.e2c779","name":"","func":"\nif (msg.payload === false ){\n    msg.payload = true;\n}\nelse { msg.payload = false;\n}\nreturn msg;\n","outputs":1,"noerr":0,"x":130,"y":400,"wires":[["99342e02.5d917"]]},{"id":"99342e02.5d917","type":"ui_template","z":"60de705.e2c779","group":"1732cfe.a123c3","name":"Push-RND","order":13,"width":4,"height":2,"format":"<md-button class=\"vibrate filled touched bigfont rounded\" style=\"background-color:#212227\" ng-click=\"send({payload: msg.payload })\">\n\n<svg  width=\"90px\" height=\"90px\" version=\"1.1\" viewBox=\"0 0 200 200\">\n <g id=\"Button_Long\">\n  <rect fill=\"#212227\" width=\"200\" height=\"200\"/>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#23c48e' : '#212227'}\">   \n    <rect width=\"200\" height=\"200\" rx=\"100\" ry=\"100\"/>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#212227' : '#23C48E'}\">\n    <rect fill x=\"10\" y=\"10\" width=\"180\" height=\"180\" rx=\"90\" ry=\"90\"/>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? 'none' : '#212227'}\">   \n    <text x=\"100\" y=\"125\"  style=\"text-anchor:middle\" font-weight=\"normal\" font-size=\"75\" font-family=\"Arial\">On</text>\n   </g>\n   <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#23C48e' : 'none'}\">\n    <text x=\"100\" y=\"125\"  style=\"text-anchor:middle\" font-weight=\"normal\" font-size=\"75\" font-family=\"Arial\">Off</text>\n   </g>\n </g>\n</svg>\n\n\n</md-button>\n","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","x":142.9998779296875,"y":489.6666793823242,"wires":[["424fc71e.976628"]]},{"id":"98741886.86c3e8","type":"function","z":"60de705.e2c779","name":"","func":"\nif (msg.payload === false ){\n    msg.payload = true;\n}\nelse { msg.payload = false;\n}\nreturn msg;\n","outputs":1,"noerr":0,"x":330,"y":220,"wires":[["515f2c37.fce404","1723bcb9.0afbb3"]]},{"id":"1732cfe.a123c3","type":"ui_group","z":"","name":"test","tab":"4b43e7b2.411658","order":1,"disp":true,"width":"6","collapse":false},{"id":"4b43e7b2.411658","type":"ui_tab","z":"","name":"Tab 8","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

Not sure about the relationship between computer & phone but this addresses the problem of the initial state & the LED when using a inject node.

[{"id":"712497b9.67d868","type":"inject","z":"60de705.e2c779","name":"Push Walk","props":[{"p":"topic","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"pushWalk.payload","payloadType":"flow","x":130,"y":100,"wires":[["50959269.99d92c"]]},{"id":"50959269.99d92c","type":"function","z":"60de705.e2c779","name":"","func":"msg.payload = !msg.payload;\nflow.set('pushWalk', msg)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":100,"wires":[["1a6dbabf.5da795","db80c73c.1d6848","e71b6f34.37de9"]]},{"id":"db80c73c.1d6848","type":"ui_led","z":"60de705.e2c779","order":2,"group":"1732cfe.a123c3","width":0,"height":0,"label":"","labelPlacement":"left","labelAlignment":"left","colorForValue":[{"color":"#ff0000","value":"false","valueType":"bool"},{"color":"#008000","value":"true","valueType":"bool"}],"allowColorForValueInMessage":false,"shape":"circle","showGlow":true,"name":"LED","x":550,"y":80,"wires":[]},{"id":"1a6dbabf.5da795","type":"ui_template","z":"60de705.e2c779","group":"1732cfe.a123c3","name":"Push-Walk","order":9,"width":6,"height":2,"format":"<md-button class=\"vibrate filled touched bigfont rounded\" style=\"background-color:#212227\" ng-click=\"send({payload: msg.payload })\">\n\n<svg  width=\"360px\" height=\"100px\" version=\"1.1\" viewBox=\"0 0 800 200\">\n <g id=\"Button_Long\">\n  <rect fill=\"#212227\" width=\"800\" height=\"200\"/>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#23C48E' : '#212227'}\">   \n    <rect width=\"800\" height=\"200\" rx=\"100\" ry=\"100\"/>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#212227' : '#23C48E'}\">\n    <rect fill x=\"10\" y=\"10\" width=\"778\" height=\"180\" rx=\"90\" ry=\"90\"/>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? 'none' : '#212227'}\">\n    <text x=\"400\" y=\"125\"  style=\"text-anchor:middle\" font-weight=\"normal\" font-size=\"75\" font-family=\"Arial\">WALK LIGHTS ON</text>\n  </g>\n  <g ng-style=\"{fill: (msg.payload || 0) % 2 === 0 ? '#23C48E' : 'none'}\">\n    <text x=\"400\" y=\"125\"  style=\"text-anchor:middle\" font-weight=\"normal\" font-size=\"75\" font-family=\"Arial\">WALK LIGHTS OFF</text>\n  </g>\n </g>\n</svg>\n\n\n</md-button>\n","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","x":330,"y":180,"wires":[["50959269.99d92c"]]},{"id":"e71b6f34.37de9","type":"debug","z":"60de705.e2c779","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":700,"y":180,"wires":[]},{"id":"1732cfe.a123c3","type":"ui_group","name":"test","tab":"4b43e7b2.411658","order":1,"disp":true,"width":"6","collapse":false},{"id":"4b43e7b2.411658","type":"ui_tab","name":"Tab 8","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
1 Like

So to understand this, you just added an "inject node" that pushes the current payload as soon as the flow is active?

I am still struggling with updating the button across two devices. I tried an MQTT call back but that just fires an endless loop. Unless I am not doing that right....

If the problem is that when you press on one device the other doesn't update (rather than the inject buttons) - then it may be because of the msg.socketid property on the msg coming back from the UI. If that is there when you pass the message on it will then only go back to that socketid (ie the UI that sent it). If you delete that then it will get sent to all UIs. luckily you already have a function perfectly placed to delete msg.socketid in each of your paths.

1 Like

WOW! Thanks!

So for other that run across this in the existing function node.

delete msg.socketid
msg.payload = !msg.payload;
flow.set('pushWalk', msg)
return msg;

Ok that is the solution if the UI's are open...

If I open a UI after a change has been made it doesn't update when I open it. Is there a solution for that?

EDIT:
If I open the flow in the browser of the next device and trigger the "inject" node it updates the button for that device.

Should the "inject" node not trigger every time a new device opens the flow?

Button with states.

The Button
Button is dummy. It has no knowledge about what the world is, but it happily takes in commands to be "red" or "blue" and can show what ever label is told to show.
Only thing it does know is that it must react when you click on it.

The state.
The state is property of something. Mostly boolean (day - night) but why not more (morning - day - evening- night)
The button with states does not own the state. But represents the state. So it can "blue" when state is "night" and "yellow" if state is "day".

State -> Button.
The state must be told to button. Button can't ask for state. Also as the button is dummy, it can't remember the state.

The Event
When day turns to night.
When dashboard connects to server side. (when you change tabs on dashboard)
Those events are triggers which must be feed to the logical part of flow where the state is determined and then sent to the button.

Button click changes the state
That is an event which itself has no connection to all of above but as the result of this event, another thing happened - the state changes. Look at the Event.

In your flow it looks like this

The fancy part is ui_control node. This does most of the hard work for you. It fires an event when (any) dashboard connects. so the state can be determined and sent to the button.

1 Like

Makes sense. I am very new to visual programing. I have been with C++ in Arduinos for a few years.

You are saying if I create a variable that hold the last state that was sent to NR by the Arduino I can make the inject node inject it into each new instance of NR that opens?

The state must be held somewhere so the function before the button can reach that state and make decision what kind of message needs to be sent to the button.
And you don't need the trigger node if you are using the ui_control node like shown.

What are the options to hold the state?
flow context, global context, database, file, mqtt, some equipment you can query ...

I'm trying to wrap my head around your explanation. So in the ui_control node I need to code. I'll give it a hazard here.

///// this code in UI_Control node
msg.payload = context.get('state') || 0;  //takes context 'state' to msg payload
return msg;

///code for function node
delete msg.socketid

var state2 = context.get('state');  //take context 'state' to local var
state2 = !state2;                   //inverts state
msg.payload = state2;
context.set('state',state2)         //saves current state to context 'state'

return msg;

Now I am getting an update every time I open NR but it now inverts the state where as the previous inject didn't toggle the state.

EDIT:
Ok seems like I got it to work, I had the UI_control into the function node. When I put it to the button node I only get a single state.

I am back to having to push the button 2 times after deploying any change. I'll continue to work on that yet..

IDK it must be time to go to bed!!! I thought it was working but now if the state is false when I open a NR instance it changes the button color but not the state. If I hit the but it show the "true" state. The strange thing is if the state is "true" when I come online it works perfect! :frowning:

I'm going to try something which may seems unacceptable at first but really, you are struggling because of you have taken fancy look which is given by code you are not familiar and you are trying to solve the technical stuff on top of it. That is hardest way to solve the problem.

The easy route will be to solve technical part of problem before look and feel.
And the fancy look of the button can be achieved even with using standard button widget. It is just the CSS.

Technical stuff requires one and clear understanding and decision to make. Can the button drive itself or it is completely driven by input with correct state.
If the output of button is feed back to input, such route makes button smart. Just the wires around the button don't take the smartness away from button so it drives it's own state.
And if at same time you attempt to make button to react on states from outside of that loop - which one is the correct state? And where to make such decisions?

So the first advise will be to get rid of fancy buttons and do one working flow with regular button component. Just eliminate good amount of code part which is really out of the main scope.
you can go back to the template based buttons at any time if surrounding functionality works as expected.

As I sated before - the button is dummy. Well, redefining - best buttons are dummy. Dummy buttons don't drive anything. Last thing they should do is to drive themselves. The just behave as told and look as designed and react on click.
For that, the dashboard button widget is ready to play. It does exactly that much.

So take simple route, and make the technical part working with basic dashboard widgets and then if needed, replace base components with more advanced ones if there is need for that.

Explore the example and see if it does technically what you need.
If it does - time to step to fanciness. If not, adjust, ask questions and make it work.

[{"id":"a6a76bfc.d20848","type":"inject","z":"81f889b.d2db878","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":150,"y":140,"wires":[["22f22b25.09fff4"]]},{"id":"209e1028.4c9e4","type":"ui_button","z":"81f889b.d2db878","name":"","group":"93fae10c.09faa","order":11,"width":0,"height":0,"passthru":false,"label":"{{txt}}","tooltip":"","color":"","bgcolor":"{{bgr}}","icon":"","payload":"","payloadType":"date","topic":"topic","topicType":"msg","x":390,"y":240,"wires":[["48d8dddf.a2dc74"]]},{"id":"907a1a43.d157d8","type":"function","z":"81f889b.d2db878","name":"get state ","func":"// here the actual incoming payload is treated as an event.\n// payload content is not inspected\n// the work is done by read the state \n//and decide the outgoing payload\n\n\n// read actual state from global context\nlet state = global.get('state')\nif(state == undefined){\n    //safequard. \n    state = 'orange'\n}\n// I'm cheatng with state names so they are also name of color.\nmsg.bgr = state//backgound color\nmsg.txt = state// button text\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":240,"wires":[["209e1028.4c9e4"]]},{"id":"22f22b25.09fff4","type":"function","z":"81f889b.d2db878","name":"","func":"var state = global.get('state')\nif(state == undefined){\n    state = 'orange'\n    global.set('state',state)\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":140,"wires":[[]]},{"id":"547d5d89.1511e4","type":"comment","z":"81f889b.d2db878","name":"init state in global context if it does not exist","info":"","x":220,"y":100,"wires":[]},{"id":"d05af06a.fcbe4","type":"ui_ui_control","z":"81f889b.d2db878","name":"","events":"all","x":120,"y":300,"wires":[["907a1a43.d157d8"]]},{"id":"48d8dddf.a2dc74","type":"function","z":"81f889b.d2db878","name":"read state and change it ","func":"let state = global.get('state')\nnode.warn('current state is: '+state)\nif(state == undefined){\n    // safequard\n    state = 'orange'\n}\nif(state == 'orange'){\n    state = 'lime'\n}\nelse{\n    state = 'orange'\n}\nnode.warn('state is changed to: '+state)\n\nglobal.set('state',state)\n// state is now changed and stored\n//msg pyload does not matter but if needed, can carry the state\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":570,"y":240,"wires":[["8f7211e3.6e9ed"]]},{"id":"b4d956be.929d68","type":"comment","z":"81f889b.d2db878","name":"let's  pretend that state change takes some time.","info":"","x":780,"y":200,"wires":[]},{"id":"8f7211e3.6e9ed","type":"delay","z":"81f889b.d2db878","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":790,"y":240,"wires":[["c60adc57.f695a"]]},{"id":"c60adc57.f695a","type":"link out","z":"81f889b.d2db878","name":"feedbak from state change","links":["2feb727c.25dc5e"],"x":615,"y":320,"wires":[]},{"id":"2feb727c.25dc5e","type":"link in","z":"81f889b.d2db878","name":"state change to button","links":["c60adc57.f695a"],"x":155,"y":200,"wires":[["907a1a43.d157d8"]]},{"id":"52350363.ad7aac","type":"comment","z":"81f889b.d2db878","name":"This output is an event. the state is changed","info":"","x":750,"y":360,"wires":[]},{"id":"1ef0e776.e65a59","type":"comment","z":"81f889b.d2db878","name":"ui_control node just injects a message when dashboard connects","info":"","x":230,"y":340,"wires":[]},{"id":"93fae10c.09faa","type":"ui_group","name":"Default","tab":"6ff5405c.a8e6","order":1,"disp":true,"width":"6","collapse":false},{"id":"6ff5405c.a8e6","type":"ui_tab","name":"TEST","icon":"dashboard","order":3,"disabled":false,"hidden":false}]

Ok, I'll "put that in my pipe and smoke it". See if I can figure out what' going on.

Thanks for now.

So here is where I got to from what you got me onto.
Button that turns "lime" or "orange" based on the feedback from the esp8266. Also a turns red and says offline if the 2 heartbeats are missed from the esp. As well the esp remembers the state and pushes it to NR when it comes online.

[{"id":"9ac09054.cd7d9","type":"mqtt in","z":"c5102488.371d98","name":"","topic":"My_MQTT_Test/NodeMCU/LED1fs","qos":"1","datatype":"auto","broker":"65918903.34abd8","x":185,"y":164,"wires":[["fcee7c60.b401e"]]},{"id":"c9d5e69e.66ff08","type":"mqtt out","z":"c5102488.371d98","name":"","topic":"My_MQTT_Test/NodeMCU/LEDa","qos":"1","retain":"","broker":"65918903.34abd8","x":685,"y":364,"wires":[]},{"id":"83b5b691.b54828","type":"mqtt in","z":"c5102488.371d98","name":"","topic":"My_MQTT_Test/NodeMCU/LED1a","qos":"1","datatype":"auto","broker":"65918903.34abd8","x":160,"y":340,"wires":[["d93b98aa.7b8cb8"]]},{"id":"9f832a1f.a90528","type":"ui_button","z":"c5102488.371d98","name":"","group":"1732cfe.a123c3","order":11,"width":0,"height":0,"passthru":false,"label":"{{txt}}","tooltip":"","color":"","bgcolor":"{{bgr}}","icon":"","payload":"","payloadType":"date","topic":"topic","x":275,"y":284,"wires":[["c1497510.228028"]]},{"id":"d93b98aa.7b8cb8","type":"function","z":"c5102488.371d98","name":"get state ","func":"// here the actual incoming payload is treated \n//as an event.\n// payload content is not inspected\n// the work is done by read the state \n//and decide the outgoing payload\n\n\n// read actual state from global context\nlet state1 = msg.payload\nif(state1 === 'ON')\n{\n    global.set('state','orange')\n    msg.payload = \"ON\";\n}\nif(state1 === 'OFF')\n{\n    global.set('state','lime')\n    msg.payload = \"OFF\";\n}\nlet state = global.get('state')\n\n// I'm cheatng with state names so they are also name of color.\nmsg.bgr = state//backgound color\nmsg.txt = state// button text\n\n\nreturn msg;","outputs":1,"noerr":0,"x":105,"y":284,"wires":[["9f832a1f.a90528"]]},{"id":"c1497510.228028","type":"function","z":"c5102488.371d98","name":"read state and change it ","func":"let state = global.get('state')\n//node.warn('current state is: '+state)\n//if(state == undefined){\n    // safequard\n   // state = 'orange'\n//}\nif(state == 'orange'){\n    state = 'lime'\n    msg.payload = \"ON\";\n}\nelse{\n    state = 'orange'\n    msg.payload = \"OFF\";\n}\n//node.warn('state is changed to: '+state)\n\nglobal.set('state',state)\n// state is now changed and stored\n//msg pyload does not matter but if needed, can carry the state\nreturn msg;","outputs":1,"noerr":0,"x":475,"y":304,"wires":[["70f824d1.a492cc"]]},{"id":"70f824d1.a492cc","type":"delay","z":"c5102488.371d98","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":675,"y":264,"wires":[["c9d5e69e.66ff08"]]},{"id":"fcee7c60.b401e","type":"function","z":"c5102488.371d98","name":"payload from littleFS","func":"if(msg.payload === \"1\")\n{\n    msg.payload = 'OFF';\n}\nif(msg.payload === \"0\")\n{\n    msg.payload = 'ON';\n}\nreturn msg;","outputs":1,"noerr":0,"x":165,"y":224,"wires":[["70f824d1.a492cc","fab4e05d.834cd","484d98e.6f0f968"]]},{"id":"fab4e05d.834cd","type":"function","z":"c5102488.371d98","name":"force update state","func":"if(msg.payload === 'ON')\n{\n    state = 'lime'\n}\n\nif(msg.payload === 'OFF')    \n{\n    state = 'orange'\n}\n//node.warn('state is changed to: '+state)\nglobal.set('state',state)\n// state is now changed and stored\n//msg pyload does not matter but if needed, can carry the state\nmsg.bgr = state//backgound color\nmsg.txt = state// button text\n\nreturn msg;","outputs":1,"noerr":0,"x":470,"y":220,"wires":[["9f832a1f.a90528"]]},{"id":"30c9c347.902dcc","type":"trigger","z":"c5102488.371d98","op1":"","op2":"1","op1type":"nul","op2type":"str","duration":"1","extend":true,"units":"min","reset":"0","bytopic":"all","name":"","x":435,"y":124,"wires":[["b2fefd44.db99c","ce22bfc8.4f77b"]]},{"id":"b2fefd44.db99c","type":"function","z":"c5102488.371d98","name":"Offline button","func":"if(msg.payload === '1')\n{\nmsg.bgr = 'red'//backgound color\nmsg.txt = 'Offline'// button text\n}\nreturn msg;","outputs":1,"noerr":0,"x":655,"y":124,"wires":[["9f832a1f.a90528"]]},{"id":"11027d0c.5a60c3","type":"mqtt in","z":"c5102488.371d98","name":"","topic":"My_MQTT_Test/NodeMCU/heartbeat","qos":"1","datatype":"auto","broker":"65918903.34abd8","x":175,"y":84,"wires":[["30c9c347.902dcc"]]},{"id":"9291fc0d.53355","type":"remote-access","z":"c5102488.371d98","confignode":"eee4102.90625f","name":"D1","verbose":0,"x":75,"y":24,"wires":[[]]},{"id":"38b298e7.4fc258","type":"file","z":"c5102488.371d98","name":"","filename":"C:\\Users\\david\\Documents\\NR.txt","appendNewline":true,"createDir":true,"overwriteFile":"false","encoding":"none","x":685,"y":64,"wires":[[]]},{"id":"ce22bfc8.4f77b","type":"function","z":"c5102488.371d98","name":"OFF_line log","func":"//node.warn('before: '+repeatflag)\n//global.set('repeatflag','0');           give inital value\nlet repeatflag1 = global.get('repeatflag')\nnode.warn('loaded: '+repeatflag1)\nif(repeatflag1 == '0')\n{\n    repeatflag1 = '1';\n    global.set('repeatflag',repeatflag1);\n    msg.payload = msg.payload = \"D1 OFFLINE \"+ Date();\n    return msg;\n}\n","outputs":1,"noerr":0,"x":430,"y":60,"wires":[["38b298e7.4fc258"]]},{"id":"d5741122.bbe6a","type":"comment","z":"c5102488.371d98","name":"Remote access node IOS app","info":"","x":260,"y":20,"wires":[]},{"id":"484d98e.6f0f968","type":"function","z":"c5102488.371d98","name":"ON_line log","func":"let repeatflag = global.get('repeatflag')\n\nif(repeatflag == '1')\n{\n    repeatflag = '0'\n    global.set('repeatflag',repeatflag);\n    msg.payload = msg.payload = \"D1 ONLINE \"+ Date();\n    return msg;\n}\n","outputs":1,"noerr":0,"x":450,"y":180,"wires":[["38b298e7.4fc258"]]},{"id":"205afb8d.def3d4","type":"comment","z":"c5102488.371d98","name":"seem to need to load an inital value for global variable","info":"","x":280,"y":400,"wires":[]},{"id":"65918903.34abd8","type":"mqtt-broker","z":"","name":"","broker":"192.168.0.97","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"1732cfe.a123c3","type":"ui_group","z":"","name":"test","tab":"4b43e7b2.411658","order":1,"disp":false,"width":"6","collapse":false},{"id":"eee4102.90625f","type":"remote-config","z":"","name":"Node-RED UI","host":"192.168.0.97","protocol":"http","port":"1880","baseurl":"/ui/#!/0?socketid=x24c9TFEwcJ2B_DkAAAC","instancehash":"7jrq6c2a0948m6cr9928gvhyuso383jt6lta2gv4r0wxiwc16sv9o73ptpaxmqbx","server":"nodered03.remote-red.com","region":"us"},{"id":"4b43e7b2.411658","type":"ui_tab","z":"","name":"Tab 8","icon":"dashboard","order":1,"disabled":false,"hidden":true}]

So you are saying the technical part is working as you are expecting?