Some thoughts on Node Red variables

In learning how to use Node-Red I’ve changed my coding countless times as I’ve found better ways to do things.

Please note that in 2017, the way I use global variables here is out of date. It still works but for example context.global.fred=1 is now written as global.set("fred",1). Read this later article on using globals and context variables.

I’m currently working on my thermostat code using a Nextion touch-sensitive display, Raspberry Pi2 as controller and ESP8266s for relay and temperature/humidity sensing. This was a slow-running project until my old Arduino-based heating controller packed in yesterday without warning and so now the fate of the household heating is in the hands of an on-going project.

So the general idea is easy enough – lots of settings for weekdays and weekends, read sensor, control relay. That’s pretty much it in a nutshell except that on my first attempt I didn’t take into account power cuts – and when the power was restored to the Pi, hardcoded defaults took over instead of the settings I’d put in using the display.

So there’s a need for lots of variables and a means to store them permanently. This is what I’ve come up with and the logic behind it:

In order to pass variables around in Node-Red, you need to use “context.global” variables as these are the only ones that will work from page to page (i.e. from powering up node-red to powering down across all pages of your project). This can make for messy variable names for those of us who like to keep things short because we’re lazy or developing typists finger.

context.global.stat_inside_temperature

etc..

I’m a C person and so a struct is ideal for me – keeps everything together.  The nearest I get to that in JavaScript is an object and that’s fine.

A global object containing everything to do with my stat.

   var stat = {
display_temperature : 22,
display_humidity : 45,
heating : 0,
display_external_temperature : 14,
display_external_humidity : 54,
display_status : "Normal",
display_target_temperature : 20,
display_enabled : 0,
display_last_page : "home",
display_last_colour : 0,
pass : "",
display_dim : 60,
peak : 21,
night : 16,
frost : 12,
hold : 0,
weekon : 480,
weekoff : 720,
weon : 480,
weoff : 720,
week2on : 780,
week2off : 1380,
we2on : 780,
we2off : 1380,
temperature_manual_override : 0,
update : 0
};

That stat var of course is only valid while the relevant function is in use and is invisible outside of that particular page hence…

context.global.stat=stat;

So now we have a nice but somewhat longwinded way to access the variables..

context.global.stat.display_temperature;

(I know I should probably use displayTemperature but there’s no global replace in the Node-Red editor (hint)).

Because JavaScript objects are passed by reference – you can use a reference to this in other pages which makes things a little more readable.

var stat=context.global.stat

Hence

stat.display_temperature;

That’s more like it and you can write to that new variable and it will update the original as they are one and the same.

The obvious solution for storing variables permanently, is a database, but I’m trying to minimise that to keep writes down for the time when I run all of this and much more on an SD (currently using a little 2.5” drive with the Pi2).

The file node lets you read and write text files and so the next challenge was how to get that object into a text file – easy:

msg.payload=JSON.stringify(context.global.stat);

That JSON string can be saved directly to disk and the inverse is true of course…

context.global.stat=JSON.parse(msg.payload);

The good part of that is that the string saved to disk is readable and if need be directly writeable.

Thankfully when the file node reads a file if the file is not there, it returns “undefined” so in my function I check for undefined and if so – create a new one with defaults in it. I added a timeout variable in there and whenever I update any item in the object I also set a counter to 10.  That counter is decremented every second and when it reaches zero, the object is updated on disk. That means I can do all sorts of adjusting of controls on the Nextion display and only when I’m done will there be a single block write to the disk. The READ is only done once when Node-Red powers up.

Seems like a reasonable solution and it’s nice and warm in here.

In an earlier blog you’ll see my Nextion display solution which is quite pretty ( created my own imagery because built-in Nextion options are currently not that good) – here just for reference is the current Node-Red block controlling the display and the stat itself including disk reading and writing.

tmp7866

Facebooktwittergoogle_pluspinterestlinkedin

11 thoughts on “Some thoughts on Node Red variables

  1. Hi Pete,

    What's in the function "get vars into global or define". Its the define bit i'm looking for.

    I've tried to create a var and save it. That works. What i'm stuck on is adding a new element to the var and update the file saving the existing data.

    Any pointers would be helpful.

    Regards

    Paul

    1. First thing that I should point out is that my code is now deprocated - that is it still works but it's not the latest way to do it.

      Where I might use context.global.fred=5;

      this should now be:

      global.set("fred",5);

      For now this is academic but it is the stated goal of the Node-Red people to give non-vol options to variables - not there yet but "coming soon".

      For now, as you request, my "get variables into global or define" - here's the contents...

      if (msg.payload===undefined)
      {
      var stat = {
      max_humidity : 100,
      display_temperature : 22,
      desired : 22,
      display_humidity : 45,
      heating : 0,
      display_external_temperature : 14,
      display_external_humidity : 54,
      display_status : "Normal",
      display_enabled : 0,
      display_last_page : "home",
      display_last_colour : 0,
      pass : "",
      display_dim : 60,

      frost : 14,
      hold : 0,

      p1 : 180,
      p2 : 480,
      p3 : 600,
      p4 : 1080,
      p5 : 1380,

      pw1 : 180,
      pw2 : 480,
      pw3 : 600,
      pw4 : 1080,
      pw5 : 1380,

      t1 : 16,
      t2 : 21,
      t3 : 19,
      t4 : 21,
      t5 : 18,

      tw1 : 16,
      tw2 : 21,
      tw3 : 19,
      tw4 : 21,
      tw5 : 18,

      temperature_offset : 0,
      last_state : 99,
      current_state : 0,
      update : 0,
      daynow: 1
      };
      context.global.thermostat=stat;
      msg.payload=JSON.stringify(stat);
      node.send([msg,null]);
      }
      else
      {
      context.global.thermostat=JSON.parse(msg.payload);
      }

      msg.payload=""; node.send([null,msg]); // get any crap out of the buffer
      msg.payload="page home"; node.send([null,msg]); // Send initial page
      msg.payload="home.target.txt=\""+context.global.thermostat.display_target_temperature+"\""; node.send([null,msg]); // Send initial state
      msg.payload="home.temperature.txt=\""+context.global.thermostat.display_temperature+"\""; node.send([null,msg]); // Send initial state

      // Some thermostat controls here
      context.global.thermostat.laststate=-1; // detecting change of timezone
      context.global.thermostat.temperature_offset=0;

  2. Thank you Pete, this article assisted me to understand how to work with persisted variables. I am adding my node-red / esp8266 solution the ability to control my 4-legged sprinkling system. I want to have an an 'auto' mode, where the node-red will start the programme to pump water for leg 1, starting in the morning, based on a stored variable in the persisted file. After completion of leg one, it must rest for x minutes (45min) to give the borehole time to build up water again, and then switch on leg 2. This 45min - also from the global variables. etc. Your detailed explanations helped me to get the variables going, to store them, retrieve them, etc.
    But my request is around the scheduling, which one then start, based on the retrieved variable. I had a look at your big timer, the SCHEDEX one, and the others, none of them can start based on a incoming variable, containing the start time, (or delay time prior to next leg switched on). Do you have any guidance for me how to solve this in maybe a function, or somethings else you bumped into? (tx for your great work - you are an inspiration.)

    1. Hi there

      I've yet to figure out how to override the settings in the HTML permanently - sure you could just write code to do that and store in a database but then you have to be able to handle multiple instances etc... I'll get my head around that some day and update BIGTIMER.

  3. Hi Pete,
    very nice blog. Thank you for sharing that useful article.

    Actual i start to learn how to use Node-Red for my Home Automation 🙂

    Currently i attempt to store global context variable across Node-Red restarts. Therefore i already save as Json String into File. Now i want to reload this file automated once at Node-Red startup. I have no glue how.. did you have a hint / tip what i could use as 'Trigger' ?

    Thanks in advance
    Marcus

  4. Hi Pete! Thanks for sharing this great post! Really interesting.
    Since you clearly are a programmer, do you recommend any other language or framework for reading/controlling/actuating stuff similar to node-red?
    Any tips on alternatives not necessarily visual?

    Thanks in advance

    1. If you use Node-Red you are actually programming in node-js unless you just connect boxes together. Other than that I use C and C# - I've used many other languages - and I think these two have the others beaten hands down - of course - I don't expect users of other languages to agree with that. Since the 80's I've used just about everything out there at least once. And no - I don't recommend Lua.

Leave a Reply

Your email address will not be published. Required fields are marked *