The right logic for this task

So not the sharpest programmer in the world... in fact not programmer. However I do like to make things, and have programmed some stuff before.

However, I am struggling with getting some logic right. I want to:

Make a system that, based on indoor/outdoor humidity, starts a ventilator.
I have so far made an bool variable that reads humidity data from my Netatmo weather station devices and toggles the the on/off variable. However, I'd like two more inputs to enter the script:
A) a dashboard controlled max time that the ventilator can run, regardless of whether it started due to humidity readings or by (B) a manual toggle.
B) a manual on/off toggle.

I have done it previously on an old Arduino with a super crappy code that I can hardly live with. Now I'm redoing it and I thought that maybe you peeps on the internet could find a minute to provide a few hints as to how that is best done...
My flow WIP: (note that I had to remove usr/pw info)

[{"id":"5dd2ed34.6894bc","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"83eb50e3.700aa","type":"inject","z":"5dd2ed34.6894bc","name":"","topic":"5 Minutes Timer","payload":"true","payloadType":"bool","repeat":"300","crontab":"","once":true,"onceDelay":"0.1","x":580,"y":280,"wires":[["5550ac86.74f474"]]},{"id":"5550ac86.74f474","type":"netatmo-dashboard","z":"5dd2ed34.6894bc","creds":"25b2494d.73122e","x":580,"y":340,"wires":[["d9d6c6cc.b17648"]]},{"id":"9ba10667.b50b8","type":"ui_gauge","z":"5dd2ed34.6894bc","name":"","group":"1a5ecff2.92b44","order":1,"width":"3","height":"2","gtype":"gage","title":"T Kælder","label":"°C","format":"{{value}}","min":"0","max":"40","colors":["#0432ff","#00fdff","#ff2600"],"seg1":"5","seg2":"15","x":820,"y":340,"wires":[]},{"id":"7563139b.e691cc","type":"ui_gauge","z":"5dd2ed34.6894bc","name":"","group":"b37cbe7b.05118","order":1,"width":"3","height":"2","gtype":"gage","title":"T Ude","label":"°C","format":"{{value}}","min":"0","max":"40","colors":["#0432ff","#00fdff","#ff2600"],"seg1":"5","seg2":"15","x":810,"y":460,"wires":[]},{"id":"635b2b58.8aec84","type":"ui_gauge","z":"5dd2ed34.6894bc","name":"","group":"b37cbe7b.05118","order":2,"width":"3","height":"2","gtype":"gage","title":"Luftfugtighed Ude","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#00fa00","#fffc00","#ff2600"],"seg1":"50","seg2":"60","x":850,"y":500,"wires":[]},{"id":"d9d6c6cc.b17648","type":"function","z":"5dd2ed34.6894bc","name":"get Netatmo Data","func":"\n\nvar msg1 = {};\nvar msg2 = {};\nvar msg3 = {};\nvar msg4 = {};\nvar msg_AhK = {};\nvar msg_AhU = {};\n\n\nmsg1.payload = Number(msg.payload.compact.modules[0].temperature); //kælder\nmsg1.topic = 'Kælder';\nmsg2.payload = Number(msg.payload.compact.modules[0].Humidity); //Kælder\nmsg2.topic = 'Kælder';\nmsg3.payload = Number(msg.payload.compact.outdoor.temperature);\nmsg3.topic = 'Ude';\nmsg4.payload = Number(msg.payload.compact.outdoor.humidity);\nmsg4.topic = 'Ude';\n//msg_AhK.payload = (6.112 * 2.718281828459^((17.67 * msg1.payload)/(msg1.payload+243.5)) * msg2.payload * 2.1674)/(273.15+msg1.payload);\n//msg_AhK.payload = (6.112*(2.718281828459^((17.67 * msg1.payload)/(msg1.payload+243.5))) * msg2.payload * 2.1674)/(273.15+msg1.payload);\n\nmsg_AhK.payload =  Number(6.112* Math.pow(2.718281828459,((17.67 * msg1.payload)/(msg1.payload+243.5))) * msg2.payload * 2.1674)/(273.15+msg1.payload);\nmsg_AhK.topic = 'Kælder';\nmsg_AhU.payload =  Number(6.112* Math.pow(2.718281828459,((17.67 * msg3.payload)/(msg3.payload+243.5))) * msg4.payload * 2.1674)/(273.15+msg3.payload);\nmsg_AhU.topic = 'Ude';\n\nflow.set(\"AhK\", msg_AhK);\nflow.set(\"AhU\", msg_AhU);\n\nreturn [msg1,msg2,msg_AhK,msg3,msg4,msg_AhU];","outputs":6,"noerr":0,"x":590,"y":420,"wires":[["9ba10667.b50b8"],["735f73a6.cb5c84","f5869f70.a4834"],["892c221.fca67e","eca7aaa7.d4c7e8"],["7563139b.e691cc"],["635b2b58.8aec84","d7586f4.5a02e1"],["7653673f.39f42","eca7aaa7.d4c7e8"]]},{"id":"735f73a6.cb5c84","type":"ui_gauge","z":"5dd2ed34.6894bc","name":"","group":"1a5ecff2.92b44","order":2,"width":"3","height":"2","gtype":"gage","title":"Luftfugtighed Kælder","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#00fa00","#fffc00","#ff2600"],"seg1":"50","seg2":"60","x":860,"y":380,"wires":[]},{"id":"892c221.fca67e","type":"ui_gauge","z":"5dd2ed34.6894bc","name":"","group":"1a5ecff2.92b44","order":3,"width":"0","height":"0","gtype":"compass","title":"Vandindhold Kælder (g/m3)","label":"g/m3","format":"{{value | number:1}}","min":0,"max":"25","colors":["#00b500","#e6e600","#ca3838"],"seg1":"8","seg2":"12","x":880,"y":420,"wires":[]},{"id":"7653673f.39f42","type":"ui_gauge","z":"5dd2ed34.6894bc","name":"","group":"b37cbe7b.05118","order":3,"width":"0","height":"0","gtype":"compass","title":"Vandindhold Ude (g/m3)","label":"g/m3","format":"{{value | number:1}}","min":0,"max":"25","colors":["#00b500","#e6e600","#ca3838"],"seg1":"8","seg2":"12","x":870,"y":540,"wires":[]},{"id":"f361a804.0ea4d","type":"function","z":"5dd2ed34.6894bc","name":"","func":"\nvar msg1 = {};\nvar msg2 = {};\nvar msg3 = {};\nvar msg4 = {};\nvar msg5 = {};\nvar msg6 = {};\nvar msg7 = {};\nmsg7.topic = 'On/Off'\n\nvar AhK = flow.get(\"AhK\");\nvar AhU = flow.get(\"AhU\");\nvar ahTol = flow.get(\"ahTol\");\n\nmsg1.payload = AhK.payload;\nmsg2.payload = AhU.payload;\nmsg4.payload = ahTol.payload;\nmsg5.payload = AhU.payload*(1+(ahTol.payload/100));\nmsg6.payload = AhU.payload-AhK.payload;\n\nif (AhK.payload > (msg5.payload) ) {\nmsg3.payload = true;\nhumOnoff = true;\nmsg7.payload = 20;\n}\nelse {\nmsg3.payload = false;\nhumOnoff =false;\nmsg7.payload = 0;\n\n}\n\n\n\nreturn [msg1, msg2, msg3, msg4, msg5, msg6, msg7];","outputs":7,"noerr":0,"x":502,"y":825,"wires":[["967df6b6.66d01"],["f95cc6b1.beeed"],["9f2359bf.354838"],["4c9de6d7.98bc9"],["d7cbd3ee.50a16"],["4b6580c1.f10c6","8343ee88.6811a"],["c576495c.2a52d8","eca7aaa7.d4c7e8"]]},{"id":"d4f21e42.19fb9","type":"inject","z":"5dd2ed34.6894bc","name":"","topic":"","payload":"true","payloadType":"bool","repeat":"1","crontab":"","once":true,"onceDelay":"0.2","x":362,"y":825,"wires":[["f361a804.0ea4d"]]},{"id":"9f2359bf.354838","type":"ui_text","z":"5dd2ed34.6894bc","group":"44e5433c.f98cac","order":5,"width":0,"height":0,"name":"","label":"Ventilator Status: ","format":"{{msg.payload}}","layout":"row-right","x":742,"y":805,"wires":[]},{"id":"ecbb9f58.459318","type":"ui_slider","z":"5dd2ed34.6894bc","name":"","label":"ahTol slider","tooltip":"{{value}} %","group":"8eae17cc.67f588","order":1,"width":0,"height":0,"passthru":false,"outs":"all","topic":"","min":"5","max":"50","step":"5","x":240,"y":540,"wires":[["aaf9ce50.654c9"]]},{"id":"aaf9ce50.654c9","type":"function","z":"5dd2ed34.6894bc","name":"ahTol","func":"var ahTol = {};\n\n\nahTol.payload = msg.payload;\n\nflow.set(\"ahTol\", ahTol);\n\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":540,"wires":[[]]},{"id":"967df6b6.66d01","type":"ui_text","z":"5dd2ed34.6894bc","group":"44e5433c.f98cac","order":2,"width":0,"height":0,"name":"","label":"Vandindhold Kælder (g/m3)","format":"{{msg.payload | number:1}}","layout":"row-spread","x":772,"y":725,"wires":[]},{"id":"f95cc6b1.beeed","type":"ui_text","z":"5dd2ed34.6894bc","group":"44e5433c.f98cac","order":4,"width":0,"height":0,"name":"","label":"Vandindhold Ude (g/m3)","format":"{{msg.payload | number:1}}","layout":"row-spread","x":762,"y":765,"wires":[]},{"id":"4b6580c1.f10c6","type":"ui_gauge","z":"5dd2ed34.6894bc","name":"","group":"44e5433c.f98cac","order":1,"width":"3","height":"3","gtype":"compass","title":"aH diff. (Ude-Kæl)","label":"g/m3","format":"{{value | number:1}}","min":"-10","max":"10","colors":["#00b500","#e6e600","#ca3838"],"seg1":"65","seg2":"70","x":742,"y":925,"wires":[]},{"id":"d7cbd3ee.50a16","type":"ui_text","z":"5dd2ed34.6894bc","group":"8eae17cc.67f588","order":3,"width":"3","height":"3","name":"","label":"Setpoint","format":"{{msg.payload | number:1}}","layout":"col-center","x":712,"y":885,"wires":[],"info":"__"},{"id":"8343ee88.6811a","type":"ui_chart","z":"5dd2ed34.6894bc","name":"","group":"44e5433c.f98cac","order":3,"width":"3","height":"3","label":"Forskel","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"bezier","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":932,"y":925,"wires":[[]]},{"id":"784a5652.4b54a8","type":"inject","z":"5dd2ed34.6894bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":true,"onceDelay":0.1,"x":230,"y":1020,"wires":[["2f51fa3f.e3658e","ee10749f.6f4c78"]]},{"id":"279da797.013538","type":"ui_text_input","z":"5dd2ed34.6894bc","name":"","label":"Dato","tooltip":"","group":"5bd990b3.dca3","order":1,"width":0,"height":0,"passthru":true,"mode":"text","delay":300,"topic":"","x":610,"y":1000,"wires":[[]]},{"id":"2f51fa3f.e3658e","type":"moment","z":"5dd2ed34.6894bc","name":"","topic":"","input":"","inputType":"msg","inTz":"Europe/Copenhagen","adjAmount":0,"adjType":"days","adjDir":"add","format":"DD/MM/Y","locale":"en_gb","output":"","outputType":"msg","outTz":"Europe/Copenhagen","x":440,"y":1000,"wires":[["279da797.013538"]]},{"id":"647de97b.5d0b7","type":"ui_text_input","z":"5dd2ed34.6894bc","name":"","label":"Tid","tooltip":"","group":"5bd990b3.dca3","order":2,"width":0,"height":0,"passthru":true,"mode":"text","delay":300,"topic":"","x":610,"y":1040,"wires":[[]]},{"id":"ee10749f.6f4c78","type":"moment","z":"5dd2ed34.6894bc","name":"","topic":"","input":"","inputType":"msg","inTz":"Europe/Copenhagen","adjAmount":0,"adjType":"days","adjDir":"add","format":"HH:mm:ss","locale":"en_gb","output":"","outputType":"msg","outTz":"Europe/Copenhagen","x":440,"y":1040,"wires":[["647de97b.5d0b7"]]},{"id":"4c9de6d7.98bc9","type":"ui_gauge","z":"5dd2ed34.6894bc","name":"","group":"8eae17cc.67f588","order":2,"width":"3","height":"3","gtype":"gage","title":"aH Tol","label":"%","format":"{{value | number:1}}","min":"0","max":"100","colors":["#53d6fd","#00a3d8","#006d8f"],"seg1":"65","seg2":"70","x":702,"y":845,"wires":[]},{"id":"eca7aaa7.d4c7e8","type":"ui_chart","z":"5dd2ed34.6894bc","name":"","group":"44e5433c.f98cac","order":6,"width":"6","height":"4","label":"Vandindhold (g/m3)","chartType":"line","legend":"true","xformat":"HH:mm:ss","interpolate":"bezier","nodata":"","dot":false,"ymin":"0","ymax":"20","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#ff2600","#3a88fe","#00fa00","#ff2600","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":1110,"y":480,"wires":[[]]},{"id":"c576495c.2a52d8","type":"ui_chart","z":"5dd2ed34.6894bc","name":"","group":"8eae17cc.67f588","order":4,"width":"6","height":"2","label":"On/Off","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"step","nodata":"","dot":false,"ymin":"0","ymax":"20","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#ff2600","#ff2600","#ff2600","#ff2600","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":1002,"y":805,"wires":[[]]},{"id":"f5869f70.a4834","type":"ui_chart","z":"5dd2ed34.6894bc","name":"","group":"1a5ecff2.92b44","order":3,"width":0,"height":0,"label":"Luftfugtighed Kælder","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"20","ymax":"100","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":1120,"y":400,"wires":[[]]},{"id":"d7586f4.5a02e1","type":"ui_chart","z":"5dd2ed34.6894bc","name":"","group":"b37cbe7b.05118","order":3,"width":0,"height":0,"label":"Luftfugtighed Ude","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"20","ymax":"100","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":1110,"y":440,"wires":[[]]},{"id":"b0c09932.02228","type":"inject","z":"5dd2ed34.6894bc","name":"","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":90,"y":540,"wires":[["ecbb9f58.459318"]]},{"id":"25b2494d.73122e","type":"netatmo-config-node","z":"","client_id”:”HIDDEN”,”client_secret”:”HIDDEN”,”username”:”HIDDEN”,”password”:”HIDDEN”},{“id":"1a5ecff2.92b44","type":"ui_group","z":"","name":"Kælder","tab":"c097c657.697cb8","order":2,"disp":true,"width":"6","collapse":false},{"id":"b37cbe7b.05118","type":"ui_group","z":"","name":"Ude","tab":"c097c657.697cb8","order":1,"disp":true,"width":"6","collapse":false},{"id":"44e5433c.f98cac","type":"ui_group","z":"","name":"Analyse","tab":"c097c657.697cb8","order":3,"disp":true,"width":"6","collapse":false},{"id":"8eae17cc.67f588","type":"ui_group","z":"","name":"Input","tab":"c097c657.697cb8","order":4,"disp":true,"width":"6","collapse":false},{"id":"5bd990b3.dca3","type":"ui_group","z":"","name":"Info","tab":"c097c657.697cb8","order":5,"disp":true,"width":"6","collapse":false},{"id":"c097c657.697cb8","type":"ui_tab","z":"","name":"Main","icon":"dashboard","disabled":false,"hidden":false}]

Please read How to share code or flow json and then EDIT your post and fix it so it can be imported.

Hi @Zenofund, thanks for your reply.
I did that now. Better?

Side question : what is the main purpose of this ventilator you are trying to automate?

I think you've missed the backticks on a BLANK line before and after your block of code.

Lol. If I cannot even figure out how to post here... aaargh.
The pupose of the vent is simple: i have a basement with a natural high humodity in sunmer time of 70’ish percent relative hum (Denmark).
The code will measure basement and outdoor relative humidity and temperature and calulate gram/m3 water contents in the air. If outdoor water contents are lower than inddoor, the vent should run and dehumidify the basement.

OK.
The timer component then. It controls the running time and turns off the vent. The humidity conditions still require vent to be running. Same goes to manual turn off. So in this situation the automatic "turn on" should be avoided somehow if I'm correct. How long this state last or what triggers the end of "must be off and can't be switched on by automatics" situation. It is not good practice to restrict manual control so manually turning to any state at any time should override every other logic.

Just go back and edit your original post - put three backticks on a line by themselves both before and after the code block and it will go into a single line window

Craig

OK just to summarise what you are trying to achieve

  1. A manual turn on mode - regardless of humidity levels etc - this will run indefinitely until turned off ?? or on a timed basis ? At the end of this manual cycle - does the system revert back to automatic control i.e. the high and low humidity settings will control on/off ?

  2. A manual off mode - no conditions can turn the system on untiil Manual mode is turned off - how does this happen to cease manual mode- timed condition, button on dash etc ?

  3. Full automatic mode - turn on at X humidity and Turn off at Y humidity

Are there any more cases for this ?

If it is this fairly suimple i would suggest you look at the DSM node - takes a bit of wrapping your head around but will let you setup a Finite State machine that sounds just rihgt for this application.

Craig

Wuhuuu backticks = tjeck

The idea is:
Regardless of what starts the vent, there should be a maximum runtime (30min for example) and a pause (2hr for example). These should be adjustable.

Hum condition is: absolute hum indoor < absolute outdoor * tolerance (%)

If the hum condition are ok AND the minimum pause is ok = start vent
If the runtime is over = turn off vent.
Any manual on/off should override

I have previously made something like this in arduino, and ended with a load of bool vars and a long if, else if... else if... statement. Thought it was rather unelegant and are wondering if there is some clever programming tricks that I have overlooked.

Yep check out the DSM node

It will take you a while to wrap your head around it - but i took a hybrid Arduino/Picaxe system that had horrible code with lots of exceptions and jumps//loops/delays and have slowly started moving ti to this model.

Craig

You will need to get all the stuff right in your head and sketch out the flow so you can see how it should run

So for instance your statement

Any Manual on/off should override

needs scoping - for instance this infers a Manual on stays on forever - whereas i think you would want a manual on that comes on for a timed period and then reverts back to automatic control (as an example)

Thanks a lot Craig. I'll definately check that out.
And, yeah, your totally right about the manual on.. should still be timed... :wink:

Hi all,
Had some busy weeks without time too look at this. However, summertime presents both great weather and some added spare time during summer-vacation.
Anyway. I have done a version of this on an Arduino, that runs in my basement now. However, I am not so happy with the setup, and thus I am wishing to do a revised version. I think that Node-red is right, I just cannot quite get it to do what I want... :slight_smile:
Although, I have managed to connect to netatmo devices in node-red, and thats a key, since that will leave measurements outside my hardware, and thus, leaving only the fan control to the raspi (and a sonoff)
I looked at the DSM, but I kind of cannot find the possibillity to define conditional states based of multiple inputs; for instance, so that when BOTH humidity is ok, and waittime is ok it will run, and not if just one of these..

Anyways, I have added a snip off my Arduino code, to let you see how I did it in Arduino. Maybe that sparks some tips with you guys that may help me in how to transition to Node-red:


/*----( SET STATE BOOLEANS START)----*/
  if (ahOut < ahInTol) {
    stateHum = HIGH;                      // Dew condition is met
  } else {
    stateHum = LOW;                       // Dew condition is not met
  }
  if (waitTime > waitMinimum) {
    stateWait = HIGH;                     // Waiting minimum condition is met
  } else {
    stateWait = LOW;                      // Waiting minimum condition is not met
  }
  if (runTime < runMaximum) {
    stateRun = HIGH;                      // Limit on runtime condition is met
  } else {
    stateRun = LOW;                       // Limit on runtime condition is not met
  }
  if (buttonstateOverride == HIGH) {
    stateOverride = !stateOverride;
    waitTime = 0;
    delay(100);
  }
  if (buttonstateDisplay == HIGH) {
    changeDisplayPageSelector++;
    delay(100);
  }
  if (digitalRead(buttonPlusPin) == HIGH)
  {
    if (changeDisplayPageSelector % 6 == 2 && humTolerance < 99)
    {
      humTolerance++;
    }
    else if (changeDisplayPageSelector % 6 == 3)
    {
      runMaximum = runMaximum + 60000;
    }
    else if (changeDisplayPageSelector % 6 == 4)
    {
      waitMinimum = waitMinimum + 60000 * 5; //WAITTIME
    }
    delay(200);
  }
  if (digitalRead(buttonMinusPin) == HIGH)
  {
    if (changeDisplayPageSelector % 6 == 2 && humTolerance > 0)
    {
      humTolerance--;
    }
    else if (changeDisplayPageSelector % 6 == 3)
    {
      runMaximum = runMaximum - 60000;
    }
    else if (changeDisplayPageSelector % 6 == 4) //WAITTIME
    {
      waitMinimum = waitMinimum - 60000 * 5;
    }
    delay(200);
  }

  /*----( SET STATE BOOLEANS END)----*/

  /*---------------------( Test for waitTime, runTime and humidity difference START)----*/

  if (stateOverride == HIGH /*&& stateRun == HIGH*/) //checks if the override button has been activated. If so, the relay is started. If not, the last else declaration will turn off the fan.
  {
    stateRelay = HIGH;
    //Serial.println("On");
  }
  else
  {

    if (stateWait == HIGH)
    { // Checks whether the function was on within the minimum pause threshold

      if (stateRun == HIGH)
      { // Has the function been on less than the maximum allowed runtime?
        if (stateHum == HIGH)
        {
          stateRelay = HIGH;    // Fan turns on.
          stateOverride = LOW;
          //Serial.println("...Fan is on..");
        }
     
      }
      else
      {
        stateRelay = LOW;                   // Fan turns off.
        stateOverride = LOW;
        //Serial.println("...Turning off fan..");
        waitTime = 0;                       // reset the counter to 0 so the counting starts over...
        runTime = 0;                      // JTA change
      }
    }
    else
    {
      stateRelay = LOW;                     // Fan turns off if none of the above conditions are met
      stateOverride = LOW;
      //Serial.println("Off");
      runTime = 0;                          // reset the counter to 0 so the counting starts over...
    }

  }

It works, but I would just like it to be less.. messy...

Any inputs are appreciated. I realise that I am less off a coder than I want to be :wink:

Part of the code in Node-red:

[{"id":"1dc88533.86361b","type":"dsm","z":"97772625.e69108","name":"If","sm_config":"{\n    \"currentState\": \"onestate\",\n    \"states\": {\n        \"onestate\": {\n            \"Toggle_Ah\": \"onestate\",\n            \"Toggle_Manual\": \"onestate\"\n        }\n    },\n    \"data\": {\n        \"Toggle_Ah\":  false,\n        \"Toggle_Manual\": false\n    },\n    \"methods\": {\n        \"Toggle_Ah\": \"sm.data.Toggle_Ah=msg.payload\",\n        \"Toggle_Manual\": \"sm.data.Toggle_Manual=msg.payload\",\n        \"onTransition\" : [\n            \"if (Toggle_Manual == true){\", //checks if the override button has been activated. If so, the relay is started. If not, the last else declaration will turn off the fan.\n                \"{msg.payload = true;}\",\n            \"else\",\n            \"{\",\n                \"if (stateWait == HIGH)\",// Checks whether the function was on within the minimum pause threshold\n                    \"{if (stateRun == HIGH)\", // Has the function been on less than the maximum allowed runtime?\n                        \"{if (stateHum == HIGH)\",\n                            \"{msg.payload = true;;\",    // Fan turns on.\n                            \"Toggle_Manual = false;\",\n                        \"}\",\n     \n                    \"},\"\n                    \"else\",\n                    \"{\",\n                        \"msg.payload = false;\",       // Fan turns off.\n                        \"Toggle_Manual = false;\",\n                        \"waitTime = 0;\",            // reset the counter to 0 so the counting starts over...\n                        \"runTime = 0; \",          // JTA change\n                    \"}\",\n                \"}\",\n                \"else\",\n                \"{\",\n                  \"msg.payload = false;\",       // Fan turns off if none of the above conditions are met\n                  \"Toggle_Manual = false;\",\n                  \"runTime = 0;\",            // reset the counter to 0 so the counting starts over...\n                \"}\",\n            \n              \"}\",\n\n        ],\n        //\"onTransition1\" :\"if (sm.data.indoor > sm.data.outdoor) {msg.payload = 'indoor > outdoor';} else if (sm.data.indoor < sm.data.outdoor) {msg.payload = 'indoor < outdoor';} else {msg.payload = 'indoor = outdoor';}\"\n    }\n}\n","x":570,"y":540,"wires":[["935809dd.6b07b"]]}]

Do a search in the forum for DSM - the author of the node has chimed in on lots of posts with tips and code snippets - worth a couple of hours of reading to get you over the line

Craig

Did a redo of the entire thing. Not quite there, but getting closer. A lot of trial and error.
Especially, the logic in the main component, and the timers to run, and to pause are causing me headaches.

[{"id":"854d80b8.4c26c8","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"bf725bcb.d224b8","type":"comment","z":"854d80b8.4c26c8","name":"HELPERS FOR TESTING","info":"","x":350,"y":560,"wires":[]},{"id":"3e78bcf8.23a05c","type":"comment","z":"854d80b8.4c26c8","name":"PANEL INPUT","info":"","x":310,"y":840,"wires":[]},{"id":"c7906194.3443","type":"comment","z":"854d80b8.4c26c8","name":"TIMERS","info":"","x":1060,"y":900,"wires":[]},{"id":"e787bd04.e2168","type":"inject","z":"854d80b8.4c26c8","name":"","topic":"","payload":"7","payloadType":"num","repeat":"","crontab":"","once":true,"onceDelay":"","x":250,"y":1140,"wires":[["5b39ee37.10f1e8","2377d224.f6ecf6"]]},{"id":"dcb98768.4c3018","type":"comment","z":"854d80b8.4c26c8","name":"timerInput","info":"","x":260,"y":1060,"wires":[]},{"id":"5b39ee37.10f1e8","type":"ui_numeric","z":"854d80b8.4c26c8","name":"","label":"setWaitTime","tooltip":"Time to wait between runs","group":"ee056679.c00758","order":12,"width":0,"height":0,"passthru":true,"topic":"","format":"{{value}}","min":"5","max":"180","step":"5","x":430,"y":1140,"wires":[["ffa5dfb9.cb9c4"]]},{"id":"2377d224.f6ecf6","type":"ui_numeric","z":"854d80b8.4c26c8","name":"","label":"setRunTime","tooltip":"Time to wait between runs","group":"ee056679.c00758","order":12,"width":0,"height":0,"passthru":true,"topic":"","format":"{{value}}","min":"5","max":"180","step":"5","x":430,"y":1200,"wires":[["d1a3ff94.743c08"]]},{"id":"c2cd1fd1.25d2e8","type":"inject","z":"854d80b8.4c26c8","name":"","topic":"","payload":"14","payloadType":"num","repeat":"","crontab":"","once":true,"onceDelay":"","x":250,"y":1200,"wires":[["2377d224.f6ecf6","5b39ee37.10f1e8"]]},{"id":"81a0b80d.2a81a","type":"comment","z":"854d80b8.4c26c8","name":"PANEL OUTPUT","info":"","x":1460,"y":320,"wires":[]},{"id":"ffa5dfb9.cb9c4","type":"function","z":"854d80b8.4c26c8","name":"Set Timer (later to be Minutes*60)","func":"global.set('setWaitTime', msg.payload * 1); // seconds to minutes\nreturn msg;","outputs":1,"noerr":0,"x":680,"y":1140,"wires":[[]]},{"id":"d1a3ff94.743c08","type":"function","z":"854d80b8.4c26c8","name":"Set Timer (later to be Minutes*60)","func":"global.set('setRunTime', msg.payload * 1); // seconds to minutes\nreturn msg;","outputs":1,"noerr":0,"x":680,"y":1200,"wires":[[]]},{"id":"8669658.7fd3b98","type":"mytimeout","z":"854d80b8.4c26c8","name":"timerWait","outtopic":"timerWait","outsafe":"on","outwarning":"","outunsafe":"off","warning":"0","timer":"6","debug":false,"ndebug":false,"ignoreCase":false,"repeat":false,"again":false,"x":1240,"y":1000,"wires":[["8b5215cc.843158"],["baca5ca8.d4a75"]]},{"id":"2d486d58.812b92","type":"switch","z":"854d80b8.4c26c8","name":"True/False","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":1070,"y":1000,"wires":[[],["8669658.7fd3b98"]]},{"id":"6f078e7b.845b4","type":"dsm","z":"854d80b8.4c26c8","name":"Main","sm_config":"{\n    \"currentState\": \"undefined\",\n    \"data\": {\n        \"timerWait\": false,\n        \"timerRun\": true,\n        \"toggle_Ah\": false,\n        \"toggle_Manual\": false\n    },\n    \"methods\": {\n        \n        \"timerWait\": \"sm.data.timerWait = msg.payload;\",\n        \"timerRun\": \"sm.data.timerRun = msg.payload;\",\n        \"toggle_Ah\": \"sm.data.toggle_Ah = msg.payload;\",\n        \"toggle_Manual\": \"sm.data.toggle_Manual = msg.payload;\",\n        \"onTransition\": [\n            \"if ((sm.data.toggle_Manual && sm.data.timerRun) && !sm.data.timerWait) {\",\n            \"   sm.currentState = true;\",\n            \"   sm.fill = 'green';\",\n            \"} else if ((sm.data.toggle_Ah && sm.data.timerRun) && !sm.data.timerWait){\",\n            \"   sm.currentState = true;\",\n            \"   sm.fill = 'green';\",\n            \"} else {\",\n            \"   sm.currentState = false;\",\n            \"   sm.fill = 'yellow';\",\n            \"}\",\n            \"msg.payload = sm.currentState;\",\n            \"output = true;\"\n        ],\n        \"status\": {\n            \"fill\": {\n                \"get\": \"sm.fill;\"\n            },\n            \"shape\": \"dot\",\n            \"text\": {\n                \"get\": \"sm.currentState;\"\n            }\n        }\n    }\n}","x":980,"y":720,"wires":[["9be4141d.71af58","293b58d6.e75ab8","a8ff0dc3.8a0cb8"]]},{"id":"a8ff0dc3.8a0cb8","type":"debug","z":"854d80b8.4c26c8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1270,"y":720,"wires":[]},{"id":"d40a2d12.4863e8","type":"function","z":"854d80b8.4c26c8","name":"renamer","func":"msg.topic = \"timerRun\"\nmsg.name = \"timerRun\"\nreturn msg;","outputs":1,"noerr":0,"x":1400,"y":940,"wires":[["6f078e7b.845b4"]]},{"id":"f6c86f8a.16534","type":"switch","z":"854d80b8.4c26c8","name":"True/False","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":730,"y":720,"wires":[["6f078e7b.845b4","9be4141d.71af58"],[]]},{"id":"293b58d6.e75ab8","type":"function","z":"854d80b8.4c26c8","name":"Test","func":"msg.color = (msg.payload === true)?\"lime\":\"grey\";\nmsg.topic = (msg.payload === true)?\n\"fa fa-chrome fa-spin fa-2x fa-fw\":\"fa fa-chrome fa-2x\";\nreturn msg;","outputs":1,"noerr":0,"x":1430,"y":360,"wires":[["c92ace41.7bd718"]]},{"id":"92bc3485.65f888","type":"change","z":"854d80b8.4c26c8","name":"","rules":[{"t":"change","p":"payload","pt":"msg","from":"on","fromt":"str","to":"true","tot":"bool"},{"t":"change","p":"payload","pt":"msg","from":"off","fromt":"str","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":1220,"y":940,"wires":[["d40a2d12.4863e8"]]},{"id":"774f783c.909b3","type":"ui_switch","z":"854d80b8.4c26c8","name":"","label":"toggle_Ah","tooltip":"The absolute humidity trigger. True if the water content outside is less than inside","group":"b37cbe7b.05118","order":5,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"toggle_Ah","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":500,"y":620,"wires":[["f6c86f8a.16534","c697c7cf.2073a"]]},{"id":"58bea6c7.2c2978","type":"ui_switch","z":"854d80b8.4c26c8","name":"","label":"toggle_Manual","tooltip":"","group":"b37cbe7b.05118","order":5,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"toggle_Manual","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":480,"y":720,"wires":[["f6c86f8a.16534"]]},{"id":"97d43745.870298","type":"ui_button","z":"854d80b8.4c26c8","name":"toggle_Manual","group":"ee056679.c00758","order":3,"width":0,"height":0,"passthru":false,"label":"ON","tooltip":"","color":"","bgcolor":"","icon":"","payload":"true","payloadType":"bool","topic":"toggle_Manual","x":320,"y":880,"wires":[["f6c86f8a.16534"]]},{"id":"c92ace41.7bd718","type":"ui_text","z":"854d80b8.4c26c8","group":"ee056679.c00758","order":1,"width":"2","height":"1","name":"","label":"FAN","format":"<font color={{msg.color}} ><i class=\"{{msg.topic}} \" style=\"font-size:2px;\"></i></font>","layout":"row-spread","x":1570,"y":360,"wires":[]},{"id":"58e10519.1ae174","type":"mytimeout","z":"854d80b8.4c26c8","name":"timerRun","outtopic":"timerRun","outsafe":"on","outwarning":"","outunsafe":"off","warning":"0","timer":"3","debug":false,"ndebug":false,"ignoreCase":false,"repeat":false,"again":false,"x":1060,"y":940,"wires":[["92bc3485.65f888"],["5275572d.d2809"]]},{"id":"f75264ca.a74a7","type":"inject","z":"854d80b8.4c26c8","name":"","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":310,"y":600,"wires":[["774f783c.909b3"]]},{"id":"1fe0fd5a.7d3a63","type":"inject","z":"854d80b8.4c26c8","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":310,"y":640,"wires":[["774f783c.909b3"]]},{"id":"945fb43e.c88318","type":"inject","z":"854d80b8.4c26c8","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":310,"y":740,"wires":[["58bea6c7.2c2978"]]},{"id":"3ffab50c.e24a62","type":"inject","z":"854d80b8.4c26c8","name":"","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":310,"y":700,"wires":[["58bea6c7.2c2978"]]},{"id":"63a6fd0e.283e2c","type":"function","z":"854d80b8.4c26c8","name":"Get global time settings","func":"// Need to change the on to\n// { \"payload\": \"on\", \"timeout\": 69, \"warning\": 10 }\n\nif(msg.payload == true) {\n    msg.payload = { \n        \"payload\": \"on\", \n        \"timeout\": global.get('setRunTime'),\n        \"warning\": 0\n    };\n} else {\n    msg.payload = {  \"payload\": \"off\" };\n}\nreturn msg;","outputs":1,"noerr":0,"x":730,"y":980,"wires":[["58e10519.1ae174"]]},{"id":"cf619d60.61014","type":"ui_text","z":"854d80b8.4c26c8","group":"ee056679.c00758","order":2,"width":"0","height":"0","name":"","label":"REMAINING RUN TIME","format":"{{msg.payload}}","layout":"row-spread","x":1690,"y":420,"wires":[]},{"id":"a60942b0.98258","type":"ui_text","z":"854d80b8.4c26c8","group":"ee056679.c00758","order":2,"width":"0","height":"0","name":"","label":"REMAINING WAIT TIME","format":"{{msg.payload}}","layout":"row-spread","x":1690,"y":480,"wires":[]},{"id":"c697c7cf.2073a","type":"function","z":"854d80b8.4c26c8","name":"Test","func":"msg.color = (msg.payload === true)?\"lime\":\"grey\";\nreturn msg;","outputs":1,"noerr":0,"x":1430,"y":540,"wires":[["6ae50df8.36c434"]]},{"id":"6ae50df8.36c434","type":"ui_text","z":"854d80b8.4c26c8","group":"ee056679.c00758","order":1,"width":"2","height":"1","name":"","label":"AH","format":"<font color={{msg.color}} ><i class=\"fa fa-circle\" style=\"font-size:36px;\"></i></font>","layout":"row-spread","x":1570,"y":540,"wires":[]},{"id":"59fab0fb.d34c9","type":"function","z":"854d80b8.4c26c8","name":"renamer","func":"msg.topic = \"timerWait\"\nmsg.name = \"timerWait\"\nreturn msg;","outputs":1,"noerr":0,"x":1560,"y":1000,"wires":[["6f078e7b.845b4"]]},{"id":"8b5215cc.843158","type":"change","z":"854d80b8.4c26c8","name":"","rules":[{"t":"change","p":"payload","pt":"msg","from":"on","fromt":"str","to":"true","tot":"bool"},{"t":"change","p":"payload","pt":"msg","from":"off","fromt":"str","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":1400,"y":1000,"wires":[["59fab0fb.d34c9"]]},{"id":"9be4141d.71af58","type":"rbe","z":"854d80b8.4c26c8","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":870,"y":880,"wires":[["63a6fd0e.283e2c","2d486d58.812b92"]]},{"id":"5275572d.d2809","type":"function","z":"854d80b8.4c26c8","name":"[s] to [hh:mm:ss]","func":"// Message from preceeding node is in seconds - should be less than 24 hours (86400 seconds).\nvar time = (new Date(msg.payload * 1000)).toUTCString().match(/(\\d\\d:\\d\\d:\\d\\d)/)[0];\n// Pass on the string to the next node, what ever that may be. (groov Data Store for example).\nmsg.payload = time;\nreturn msg;","outputs":1,"noerr":0,"x":1460,"y":420,"wires":[["cf619d60.61014"]]},{"id":"baca5ca8.d4a75","type":"function","z":"854d80b8.4c26c8","name":"[s] to [hh:mm:ss]","func":"// Message from preceeding node is in seconds - should be less than 24 hours (86400 seconds).\nvar time = (new Date(msg.payload * 1000)).toUTCString().match(/(\\d\\d:\\d\\d:\\d\\d)/)[0];\n// Pass on the string to the next node, what ever that may be. (groov Data Store for example).\nmsg.payload = time;\nreturn msg;","outputs":1,"noerr":0,"x":1460,"y":480,"wires":[["a60942b0.98258"]]},{"id":"73987b5c.b44f8c","type":"throttle","z":"854d80b8.4c26c8","name":"sensitivityThottle","throttleType":"time","timeLimit":"10","timeLimitType":"seconds","countLimit":0,"blockSize":0,"locked":false,"x":650,"y":900,"wires":[[]]},{"id":"7dcefdab.289b84","type":"comment","z":"854d80b8.4c26c8","name":"ISSUE","info":"How to get this variable (if true) to continue to start the fan after timerWait.","x":550,"y":580,"wires":[]},{"id":"58512333.5591cc","type":"comment","z":"854d80b8.4c26c8","name":"ISSUE","info":"This is a test to try to stop the timer to restart if true is sent while counting down.... not quite working, though","x":890,"y":840,"wires":[]},{"id":"a171bbaf.fcfc98","type":"comment","z":"854d80b8.4c26c8","name":"ISSUE","info":"Timer that should block the fan from starting to allow for a pause between runs, no matter the AH condition. However, manual override should allways override...","x":1230,"y":1060,"wires":[]},{"id":"debe9f67.37087","type":"comment","z":"854d80b8.4c26c8","name":"ISSUE","info":"Main block... some logic are not quite there yet...","x":990,"y":680,"wires":[]},{"id":"ee056679.c00758","type":"ui_group","z":"","name":"Panel","tab":"7b5010b.9c77c7","disp":true,"width":"6","collapse":false},{"id":"b37cbe7b.05118","type":"ui_group","z":"","name":"Test_Buttons","tab":"7b5010b.9c77c7","order":1,"disp":true,"width":"6","collapse":false},{"id":"7b5010b.9c77c7","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":1}]

I think I've got it :slight_smile: Yeah

[{"id":"bf81618d.3c40e8","type":"change","z":"854d80b8.4c26c8","name":"reset for manual","rules":[{"t":"set","p":"payload","pt":"msg","to":"{ \"payload\": \"off\" }","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":1140,"y":1040,"wires":[["8669658.7fd3b98"]]}]