Function return type (tried to send a message of type string)

Function node accepts an input msg.payload = humidity % (i.e. integer 0-100)

in the On Start tab, I have:

var fanSwitch           = {payload: "OFF", topic: 'bathroom/fanSwitch'};
flow.set('humidityThreshold', 90);
flow.set('prevFanSwitch', "OFF");
flow.set('fanSwitch.payload',fanSwitch);
flow.set('fanSwitch.topic', "bathroom/fanSwitch");

In the On Message tab, I have:

var humidity            = msg.payload;
var humidityThreshold   = flow.get('humidityThreshold') || 90;
var requiredFanState    = String(flow.get('requiredFanState')) || "humidityTrigger";
var startTimer          = flow.get('startTimer') || 0;
var prevFanState        = String(flow.get('prevFanState')) ||  "humidityTrigger";
var fanSwitch           = String(flow.get('fanSwitch'))
var prevFanSwitch       = String(flow.get('prevFanSwitch')) || "OFF";

// node.warn(msg.topic);
// node.warn(msg.payload);

//set defaults for messages: payload & topic
//FanSwitch is subscribed by the ESP8266 controlling the fan relay. It has valid values of 'ON' and 'OFF'


if (requiredFanState == 'humidityTrigger') { //changed to humidity
    node.warn('A1 prevFanState='+prevFanState);
    if (requiredFanState != prevFanState){ //notify once per change only
        node.warn('AA1 - fan controlled by humidity level :'+ humidityThreshold+'%');
        prevFanState = requiredFanState;
        node.warn('AA2 - humidityTrigger first time through');
    }
    node.warn("B - humidity=" + humidity +" - prevFanSwitch=" + prevFanSwitch);
    //monitor threshold humidity
    if ((humidity >= humidityThreshold) && (prevFanSwitch == "OFF")) { //only issue OFF
        node.warn('C1 - humid but last state was switch OFF');
        fanSwitch.payload = "ON";
        prevFanSwitch = "ON";
    } else if ((humidity < humidityThreshold) && (prevFanSwitch == "ON")){
        node.warn('C2 - not humid but last state was switch ON');
        fanSwitch.payload = "OFF";
        prevFanSwitch = "OFF";
    }
    startTimer = 0;
    
} else if ((requiredFanState == 'fanOff') && (prevFanState != 'fanOff')) { //the FIRST fanOff message only
    node.warn('D - fanOff but first time through');
    prevFanState = requiredFanState;
    fanSwitch.payload   = "OFF";
    startTimer = 0;
    
} else if ((requiredFanState == 'fanOn10') && (startTimer == 0)  && (prevFanState != 'fanOn10')) { //The FIRST period message only
    node.warn('E - fanOn10 but first time through');
    prevFanState = requiredFanState;
    startTimer          =  Date.now();
    fanSwitch.payload   = "ON";
}

//Set persistent variables (whether changed or not)
flow.set('humidity', humidity);  
flow.set('humidityThreshold', humidityThreshold);
flow.set('requiredFanState', requiredFanState);
flow.set('startTimer', startTimer);
flow.set('prevFanState', prevFanState);
flow.set('prevFanSwitch', prevFanSwitch);
node.warn(fanSwitch.payload + ' - ' + fanSwitch.topic);
return fanSwitch;

Irrespective of the fan Mode (fanOff | fanOn10 | humidityTrigger)
the debug panel warns:

22/01/2022, 00:00:56node: Humidity threshold checkfunction : (error)
"Function tried to send a message of type string"

in On Start, I set up the local variable as a JSON object
var fanSwitch = {payload: "OFF", topic: 'bathroom/fanSwitch'};
and I assign that fanSwitch object (payload & topic) to the flow variable fanSwitch

In the main body of the function, in the default humidityTrigger mode, when humidity exceeds the threshold , I set the fanSwitch.payload to "ON" and where it doesn't, I set it to "OFF"

In fanOff, I set the payload to "OFF"
In fanOn10, i set it to "ON" (then another timer function resets the timer and sets fanSwitch to "OFF" again and returns to humidityTrigger mode after a period)

At the end of the function , I return fanSwitch which I rightly or wrongly assume sends a message named fanSwitch, with a JSON object of payload of "ON" or "OFF" and a topic "bathroom/fanSwitch". This message is what is subscribed to by the fan control ESP8266

I can see that the penultimate line node.warn(fanSwitch.payload + ' - ' + fanSwitch.topic);
returns undefined for both params but can't see why?
I guess one of my assumptions is wrong, but which one?

Here's a longer sample of the Debug console:

22/01/2022, 00:26:39node: Humidity threshold checkfunction : (warn)
"A1 prevFanState=humidityTrigger"
22/01/2022, 00:26:39node: Humidity threshold checkfunction : (warn)
"B - humidity=52.59 - prevFanSwitch=ON"
22/01/2022, 00:26:39node: Humidity threshold checkfunction : (warn)
"C2 - not humid but last state was switch ON"
22/01/2022, 00:26:39node: Humidity threshold checkfunction : (warn)
"undefined - undefined"
22/01/2022, 00:26:39node: Humidity threshold checkfunction : (error)
"Function tried to send a message of type string"
22/01/2022, 00:26:39node: Humidity threshold checkfunction : (warn)
"A1 prevFanState=humidityTrigger"
22/01/2022, 00:26:39node: Humidity threshold checkfunction : (warn)
"B - humidity=95 - prevFanSwitch=OFF"
22/01/2022, 00:26:39node: Humidity threshold checkfunction : (warn)
"C1 - humid but last state was switch OFF"
22/01/2022, 00:26:39node: Humidity threshold checkfunction : (warn)
"undefined - undefined"
22/01/2022, 00:26:39node: Humidity threshold checkfunction : (error)
"Function tried to send a message of type string"
22/01/2022, 00:26:47node: Humidity threshold checkfunction : (warn)
"A1 prevFanState=humidityTrigger"
22/01/2022, 00:26:47node: Humidity threshold checkfunction : (warn)
"B - humidity=95 - prevFanSwitch=ON"
22/01/2022, 00:26:47node: Humidity threshold checkfunction : (warn)
"undefined - undefined"
22/01/2022, 00:26:47node: Humidity threshold checkfunction : (error)
"Function tried to send a message of type string"
22/01/2022, 00:26:49node: Humidity threshold checkfunction : (warn)
"A1 prevFanState=humidityTrigger"
22/01/2022, 00:26:49node: Humidity threshold checkfunction : (warn)
"B - humidity=52.61 - prevFanSwitch=ON"
22/01/2022, 00:26:49node: Humidity threshold checkfunction : (warn)
"C2 - not humid but last state was switch ON"
22/01/2022, 00:26:49node: Humidity threshold checkfunction : (warn)
"undefined - undefined"
22/01/2022, 00:26:49node: Humidity threshold checkfunction : (error)
"Function tried to send a message of type string"
22/01/2022, 00:26:55node: Humidity threshold checkfunction : (warn)
"A1 prevFanState=humidityTrigger"
22/01/2022, 00:26:55node: Humidity threshold checkfunction : (warn)
"B - humidity=95 - prevFanSwitch=OFF"
22/01/2022, 00:26:55node: Humidity threshold checkfunction : (warn)
"C1 - humid but last state was switch OFF"
22/01/2022, 00:26:55node: Humidity threshold checkfunction : (warn)
"undefined - undefined"
22/01/2022, 00:26:55node: Humidity threshold checkfunction : (error)
"Function tried to send a message of type string"
22/01/2022, 00:26:59node: Humidity threshold checkfunction : (warn)
"A1 prevFanState=humidityTrigger"
22/01/2022, 00:26:59node: Humidity threshold checkfunction : (warn)
"B - humidity=52.59 - prevFanSwitch=ON"
22/01/2022, 00:26:59node: Humidity threshold checkfunction : (warn)
"C2 - not humid but last state was switch ON"

Here you set an object fanSwitch to flow.fanSwitch.payload which is changed to a string due to ...

(Which I don't understand)

Tbh, this code is a spaghetti mess (sorry)

Do you really need to store fanSwitch in on startup? You can simply declare it in the on message (and/or retrieve it from flow)

And without seeing your entire flow, I can't tell if context is even needed at all!

For starters, I'd NOT do this...

Instead, I'd do

flow.set('fanSwitch',fanSwitch)`

As at this point, fanSwitch is an object with a payload property.

If you are still struggling in the morning (GMT) I'll have a proper look.

Thanks Steve.

I'm afraid I had what I thought was a nearly working program and in trying to fix some issues, I kept adding mods,which is where a lot of the strange spaghetti code came from.

I followed your advice - basically starting from scratch and kept a closer eye on what I was trying to achieve. I think this is better code - it certainly works, but I'd appreciate your thoughts on it.

Part of the complexity for me is keeping a global state, where there are two sets of controls for the mode; one on the ESP+Sensor and one on the NodeRed Dashboard. Keeping state with flow variables did the trick and getting and setting them at the start and end of functions was invaluable advice (earlier thread, I think)

Thanks for your patience :smile:

[
    {
        "id": "8b72e4c9a2f439e5",
        "type": "tab",
        "label": "Bathroom",
        "disabled": false,
        "info": ""
    },
    {
        "id": "ef3ff83eaafada3b",
        "type": "mqtt in",
        "z": "8b72e4c9a2f439e5",
        "name": "Temperature",
        "topic": "esp/dht/b/temp",
        "qos": "1",
        "datatype": "json",
        "broker": "d232adcfdae4f31a",
        "nl": false,
        "rap": true,
        "rh": 0,
        "x": 390,
        "y": 80,
        "wires": [
            [
                "f84e0f488406be5a",
                "2e77932274aae2d4",
                "38469f49d714040b"
            ]
        ]
    },
    {
        "id": "f84e0f488406be5a",
        "type": "ui_gauge",
        "z": "8b72e4c9a2f439e5",
        "name": "BathroomTemp",
        "group": "1bc7dc824c2874aa",
        "order": 2,
        "width": "1",
        "height": "1",
        "gtype": "gage",
        "title": "",
        "label": "°C",
        "format": "{{value}}",
        "min": 0,
        "max": "30",
        "colors": [
            "#00b500",
            "#e6e600",
            "#ca3838"
        ],
        "seg1": "18",
        "seg2": "25",
        "className": "",
        "x": 620,
        "y": 80,
        "wires": []
    },
    {
        "id": "2e77932274aae2d4",
        "type": "ui_chart",
        "z": "8b72e4c9a2f439e5",
        "name": "BathroomTempHistory",
        "group": "1bc7dc824c2874aa",
        "order": 1,
        "width": 0,
        "height": 0,
        "label": "Temp",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "0",
        "ymax": "35",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "className": "",
        "x": 640,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "1e4ad1236e8a9363",
        "type": "mqtt in",
        "z": "8b72e4c9a2f439e5",
        "d": true,
        "name": "",
        "topic": "esp/dht/#",
        "qos": "2",
        "datatype": "auto",
        "broker": "d232adcfdae4f31a",
        "nl": false,
        "rap": true,
        "rh": 0,
        "x": 400,
        "y": 1100,
        "wires": [
            [
                "9a5951e05defcdf9"
            ]
        ]
    },
    {
        "id": "9a5951e05defcdf9",
        "type": "debug",
        "z": "8b72e4c9a2f439e5",
        "d": true,
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 930,
        "y": 1100,
        "wires": []
    },
    {
        "id": "38469f49d714040b",
        "type": "debug",
        "z": "8b72e4c9a2f439e5",
        "name": "Temperature",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 610,
        "y": 40,
        "wires": []
    },
    {
        "id": "c9570e0491311b68",
        "type": "ui_slider",
        "z": "8b72e4c9a2f439e5",
        "name": "Fan-on timer (mins)",
        "label": "slider",
        "tooltip": "",
        "group": "b19d6a6aef08844e",
        "order": 2,
        "width": 0,
        "height": 0,
        "passthru": true,
        "outs": "end",
        "topic": "fanTimerInterval",
        "topicType": "msg",
        "min": "1",
        "max": 10,
        "step": 1,
        "className": "",
        "x": 1070,
        "y": 620,
        "wires": [
            [
                "ab887a36f677fbad",
                "0f12394758bd3e82"
            ]
        ]
    },
    {
        "id": "d41385b7559b848f",
        "type": "comment",
        "z": "8b72e4c9a2f439e5",
        "name": "",
        "info": "Passes SHT30 sensor's temperature reading and shows it on the dashboard as a large historical trace and a small instance dial/value",
        "x": 220,
        "y": 80,
        "wires": []
    },
    {
        "id": "08b8d633b10ab20e",
        "type": "comment",
        "z": "8b72e4c9a2f439e5",
        "name": "",
        "info": "Passes SHT30 sensor's humidity reading and shows it on the dashboard as a large historical trace and a small instance dial/value\n\nThe function node measures the humidity against a threshold value (90%) and if the reading exceeds that it turns the fan on by publishing a FanState message (picked up by the ESP8266 that controls the fan)",
        "x": 220,
        "y": 240,
        "wires": []
    },
    {
        "id": "31d050f97f5c1129",
        "type": "comment",
        "z": "8b72e4c9a2f439e5",
        "name": "",
        "info": "The timer triggers every 5s to make the function check if the fan mode is 'fanOn10' and if so whether the time it's been on exceeds the interval value. If the time has been exceeded it sets the timer back to 0, the mode to 'humidityTrigger' and it publishes the mode on 'esp/dht/b/fanStateNR', so the ESP with the sensor can sync its mode and LED states with the dashboard\n\nThe interval has a default of 300k ms (5min). The slider on the Dashboard chan change this flow variable between 1 & 10 mins.",
        "x": 220,
        "y": 400,
        "wires": []
    },
    {
        "id": "ad1983652e476e88",
        "type": "comment",
        "z": "8b72e4c9a2f439e5",
        "name": "",
        "info": "The dashboard includes a 1-10 slider. The function node sets the flow interval variable with this value * 60000.",
        "x": 1100,
        "y": 780,
        "wires": []
    },
    {
        "id": "7b1515e4b537caab",
        "type": "mqtt in",
        "z": "8b72e4c9a2f439e5",
        "name": "Humidity",
        "topic": "esp/dht/b/humid",
        "qos": "0",
        "datatype": "json",
        "broker": "d232adcfdae4f31a",
        "nl": false,
        "rap": false,
        "rh": 0,
        "x": 400,
        "y": 240,
        "wires": [
            [
                "38986d99d372b31d",
                "e0729cfdcfcaa9a9",
                "ad7ba5c36dda50e6",
                "5a3b1c94b2f52243"
            ]
        ]
    },
    {
        "id": "480ff91b0d684a1c",
        "type": "mqtt out",
        "z": "8b72e4c9a2f439e5",
        "name": "FanSwitch",
        "topic": "bathroom/fanSwitch",
        "qos": "2",
        "retain": "false",
        "respTopic": "",
        "contentType": "text/plain",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "d232adcfdae4f31a",
        "x": 1050,
        "y": 320,
        "wires": []
    },
    {
        "id": "6a684d4e3c6d91d7",
        "type": "debug",
        "z": "8b72e4c9a2f439e5",
        "name": "FanSwitchReport",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1070,
        "y": 440,
        "wires": []
    },
    {
        "id": "38986d99d372b31d",
        "type": "debug",
        "z": "8b72e4c9a2f439e5",
        "name": "Humidity",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 600,
        "y": 200,
        "wires": []
    },
    {
        "id": "e0729cfdcfcaa9a9",
        "type": "ui_gauge",
        "z": "8b72e4c9a2f439e5",
        "name": "BathroomHumidity",
        "group": "e4779998dd3b61c9",
        "order": 2,
        "width": "1",
        "height": "1",
        "gtype": "gage",
        "title": "",
        "label": "%",
        "format": "{{value}}",
        "min": 0,
        "max": "100",
        "colors": [
            "#00b500",
            "#ce5c00",
            "#a40000"
        ],
        "seg1": "70",
        "seg2": "90",
        "className": "",
        "x": 630,
        "y": 240,
        "wires": []
    },
    {
        "id": "ad7ba5c36dda50e6",
        "type": "ui_chart",
        "z": "8b72e4c9a2f439e5",
        "name": "BathroomHumidityHistory",
        "group": "e4779998dd3b61c9",
        "order": 1,
        "width": 0,
        "height": 0,
        "label": "Humidity",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "className": "",
        "x": 650,
        "y": 280,
        "wires": [
            []
        ]
    },
    {
        "id": "360e440fcbb6dbd4",
        "type": "mqtt in",
        "z": "8b72e4c9a2f439e5",
        "name": "FanStateIn",
        "topic": "esp/dht/b/fanState",
        "qos": "2",
        "datatype": "auto",
        "broker": "d232adcfdae4f31a",
        "nl": false,
        "rap": true,
        "rh": 0,
        "x": 400,
        "y": 480,
        "wires": [
            [
                "f1c8b9956d494710",
                "92b01a2852bbd2c4"
            ]
        ]
    },
    {
        "id": "fca1aba52eac712a",
        "type": "debug",
        "z": "8b72e4c9a2f439e5",
        "name": "DashboardMode",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1370,
        "y": 520,
        "wires": []
    },
    {
        "id": "d279dabf5f459856",
        "type": "ui_dropdown",
        "z": "8b72e4c9a2f439e5",
        "name": "FanMode",
        "label": "Fan mode",
        "tooltip": "",
        "place": "Select option",
        "group": "b19d6a6aef08844e",
        "order": 3,
        "width": 0,
        "height": 0,
        "passthru": false,
        "multiple": false,
        "options": [
            {
                "label": "Turn fan off",
                "value": "fanOff",
                "type": "str"
            },
            {
                "label": "Turn fan on for a few mins",
                "value": "fanOn10",
                "type": "str"
            },
            {
                "label": "Fan on Auto (humidity > 90%)",
                "value": "humidityTrigger",
                "type": "str"
            }
        ],
        "payload": "",
        "topic": "DashSync",
        "topicType": "msg",
        "className": "",
        "x": 1040,
        "y": 520,
        "wires": [
            [
                "fca1aba52eac712a",
                "0f12394758bd3e82"
            ]
        ]
    },
    {
        "id": "2501240f4c2d1391",
        "type": "mqtt out",
        "z": "8b72e4c9a2f439e5",
        "name": "FanStateNR",
        "topic": "esp/dht/b/fanStateNR",
        "qos": "2",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "d232adcfdae4f31a",
        "x": 1050,
        "y": 660,
        "wires": []
    },
    {
        "id": "f1c8b9956d494710",
        "type": "debug",
        "z": "8b72e4c9a2f439e5",
        "name": "ESPfanState",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 610,
        "y": 560,
        "wires": []
    },
    {
        "id": "ae18377fbc948a88",
        "type": "debug",
        "z": "8b72e4c9a2f439e5",
        "name": "FanStateNR",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1050,
        "y": 700,
        "wires": []
    },
    {
        "id": "ab887a36f677fbad",
        "type": "debug",
        "z": "8b72e4c9a2f439e5",
        "name": "TimerInterval(ms)",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1370,
        "y": 660,
        "wires": []
    },
    {
        "id": "62535dbc893fc724",
        "type": "comment",
        "z": "8b72e4c9a2f439e5",
        "name": "",
        "info": "The ESP8266 with the humidity sensor also has a button to toggle between turning the fan OFF, ON for a period or let it be controlled by the humidity readings. This node uses the FanState subscription to monitor which state the ESP8266 is in and reacts accordingly\n\nThe function looks for fan mode changes and really only reacts if it detects fanOn10 (timed mode).  In this case, it starts the timer flow variable",
        "x": 220,
        "y": 480,
        "wires": []
    },
    {
        "id": "02437886c7b3dc14",
        "type": "comment",
        "z": "8b72e4c9a2f439e5",
        "name": "",
        "info": "The function that checks expiry of the timer interval feeds the fan mode through to the dropdown mode selector to make sure it relects the current mode. Particularly where time has expired and the mode falls back from fanOn10 to humidityTrigger \n\nThe function sets the flow variable for the fan mode, when changed from the dashboard",
        "x": 1040,
        "y": 480,
        "wires": []
    },
    {
        "id": "92b01a2852bbd2c4",
        "type": "function",
        "z": "8b72e4c9a2f439e5",
        "name": "Fan state switch",
        "func": "var requiredFanState    = String(flow.get('requiredFanState')) || \"humidityTrigger\";\nvar startTimer          = flow.get('startTimer') || 0;\nvar prevFanState        = String(context.get('prevFanState')) ||  \"humidityTrigger\";\nvar fanStateNR          = {payload: null, topic: 'esp/dht/b/fanStateNR'};\nvar fanSwitch          = {payload: null, topic: 'esp/dht/b/fanSwitch'};\n\nrequiredFanState        = msg.payload; \nfanStateNR.payload      = requiredFanState;\n\nif ((requiredFanState == 'humidityTrigger') && (prevFanState != 'humidityTrigger')) {\n    prevFanState = requiredFanState;\n    fanSwitch.payload   = \"OFF\";\n} else if ((requiredFanState == 'fanOff') && (prevFanState != 'fanOff')) {\n    prevFanState = requiredFanState;\n    fanSwitch.payload   = \"OFF\";\n} else if ((requiredFanState == 'fanOn10') && (startTimer == 0)  && (prevFanState != 'fanOn10')) {\n    prevFanState = requiredFanState;\n    startTimer          =  Date.now();\n    flow.set('startTimer', Date.now()); \n    fanSwitch.payload   = \"ON\";\n}    \n\n//Set persistent variables (whether changed or not)\nflow.set('requiredFanState', requiredFanState);\nflow.set('startTimer', startTimer);\ncontext.set('prevFanState', prevFanState);\n\nreturn [ fanSwitch, fanStateNR ];",
        "outputs": 2,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 620,
        "y": 480,
        "wires": [
            [
                "480ff91b0d684a1c",
                "6a684d4e3c6d91d7",
                "d279dabf5f459856",
                "fe56edaa6c390ad0"
            ],
            [
                "2501240f4c2d1391",
                "ae18377fbc948a88"
            ]
        ]
    },
    {
        "id": "5a3b1c94b2f52243",
        "type": "function",
        "z": "8b72e4c9a2f439e5",
        "name": "Humidity threshold check",
        "func": "//Gather persistent variables\nvar humidity            = msg.payload;\nconst humidityThreshold = 80;\n\nvar requiredFanState    = flow.get('requiredFanState') || \"humidityTrigger\"; //all tests\nvar startTimer          = flow.get('startTimer') || 0; //all fanOn10 tests\nvar prevFanState        = flow.get('prevFanState') ||  \"humidityTrigger\"; // all tests\nvar prevFanSwitch       = flow.get('prevFanSwitch') || \"OFF\"; //tests to ensure only publish fanSwitch once on change\n\n//--------------fanOff\nif ((requiredFanState == 'fanOff') && (prevFanState != 'fanOff')) { //the FIRST fanOff message only\n    node.warn('A - fanOff but first time through');\n    prevFanState        = 'fanOff';\n    fanSwitch           = \"OFF\";\n    startTimer          = 0;\n    node.warn(fanSwitch);\n    msg.payload         = fanSwitch;\n\n} else if ((requiredFanState == 'fanOff') && (prevFanState == 'fanOff')) { //---for debug only---\n    node.warn('B - fanOff again');\n    prevFanState        = 'fanOff';\n\n//--------------fanOn10   \n} else if ((requiredFanState == 'fanOn10') && (prevFanState != 'fanOn10')) { //The FIRST fanOn10 message only\n    node.warn('C - fanOn10 but first time through');\n    prevFanState        = 'fanOn10';\n    startTimer          =  Date.now();\n    fanSwitch           = \"ON\";\n    node.warn(fanSwitch);\n    msg.payload         = fanSwitch;\n\n} else if ((requiredFanState == 'fanOn10') && (startTimer == 0)  && (prevFanState == 'fanOn10')) { //---for debug only---\n    node.warn('D - fanOn10 again but timer not started');\n    return;\n    \n} else if ((requiredFanState == 'fanOn10') && (startTimer > 0)  && (prevFanState == 'fanOn10')) { //---for debug only---\n    node.warn('E - fanOn10 again and timer was already started');\n    return;\n\n//--------------humidityTrigger\n} else if ((requiredFanState == 'humidityTrigger') && (prevFanState != 'humidityTrigger')) { //The FIRST humidityTrigger message only\n    node.warn('F - humidityTrigger but first time through');\n    prevFanState        = 'humidityTrigger';\n    fanSwitch           = \"OFF\";\n    startTimer          = 0;\n    msg.payload         = \"OFF\";\n\n} else if ((requiredFanState == 'humidityTrigger') && (prevFanState == 'humidityTrigger')) { //All subsequent humidityTrigger messages\n    node.warn('G - humidityTrigger again');\n    if ((humidity >= humidityThreshold) && (prevFanSwitch == \"OFF\")) { //only issue fanSwitch=ON once\n        node.warn('H - humid but last state was switch OFF');\n        fanSwitch       = \"ON\";\n        prevFanSwitch   = \"ON\";\n        prevFanState    = 'humidityTrigger';\n        msg.payload     = \"ON\";\n \n    } else if ((humidity >= humidityThreshold) && (prevFanSwitch == \"ON\")){ //only issue fanSwitch=OFF once\n        node.warn('I - still humid and last state was switch ON');\n        return;\n        \n    } else if ((humidity < humidityThreshold) && (prevFanSwitch == \"ON\")){ //only issue fanSwitch=OFF once\n        node.warn('K - not humid but last state was switch ON');\n        fanSwitch       = \"OFF\";\n        prevFanSwitch   = \"OFF\";\n        prevFanState    = 'humidityTrigger';\n        msg.payload     = \"OFF\";\n        \n    } else if ((humidity < humidityThreshold) && (prevFanSwitch == \"OFF\")) { //only issue fanSwitch=ON once\n        node.warn('J - not humid and last state was switch OFF');\n        return;\n    }\n        \n}\n\nflow.set('requiredFanState', requiredFanState);\nflow.set('startTimer', startTimer);\nflow.set('prevFanState', prevFanState);\nflow.set('prevFanSwitch', prevFanSwitch);\n//if (msg.payload/1){msg.payload = \"oh dear!\";}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "// Code added here will be run once\n// whenever the node is started.\nvar fanSwitch           = {payload: \"OFF\", topic: \"bathroom/fanSwitch\"};\nflow.set('humidityThreshold', 90);\nflow.set('prevFanSwitch', \"OFF\");\nflow.set('fanSwitch.payload', fanSwitch);\nflow.set('fanSwitch.topic', \"bathroom/fanSwitch\");\n",
        "finalize": "",
        "libs": [],
        "x": 650,
        "y": 320,
        "wires": [
            [
                "480ff91b0d684a1c",
                "6a684d4e3c6d91d7",
                "fe56edaa6c390ad0"
            ]
        ]
    },
    {
        "id": "0f12394758bd3e82",
        "type": "function",
        "z": "8b72e4c9a2f439e5",
        "name": "Dashboard management",
        "func": "//message comes from dashboard timer slider or mode select DD\nvar DDfanState;\n\nif (msg.payload * 1){  //it's a number from the timerslider\n  flow.set('fanTimerInterval', msg.payload*60000);\n} else  { //it's a string // it's a string from the mode dropdown\n  flow.set('requiredFanState', msg.payload);\n  return msg;\n}\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1390,
        "y": 620,
        "wires": [
            [
                "92b01a2852bbd2c4",
                "52e6fe2c7206f423"
            ]
        ]
    },
    {
        "id": "f271a7787eaad9c6",
        "type": "inject",
        "z": "8b72e4c9a2f439e5",
        "name": "Check timer",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "5",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payloadType": "date",
        "x": 390,
        "y": 400,
        "wires": [
            [
                "86ef298a4cc5b27f"
            ]
        ]
    },
    {
        "id": "86ef298a4cc5b27f",
        "type": "function",
        "z": "8b72e4c9a2f439e5",
        "name": "Fan Interval checker",
        "func": "//Gather persistent variables\nvar requiredFanState    = flow.get('requiredFanState') || \"humidityTrigger\";\nvar startTimer          = flow.get('startTimer') || 0;\nvar fanTimerInterval    = flow.get('fanTimerInterval') || 300000; //milliseconds (1 minute = 60000ms)\nvar fanSwitch           = {payload: \"\"};\nvar fanStateNR          = {payload: \"\"};\nvar DashSync            = {payload: \"\"};\n\n//If the timer has expired, reset the timer, \n//send a message to turn the fan off and change the persistent \n//flow variable to reflect the resumed default mode\n\nif (requiredFanState == 'fanOn10') { //show timer progress for debug\n        var progress = (Date.now()-startTimer);\n    if ((progress >=  fanTimerInterval) && (startTimer > 0)) { //timer interval spent\n        node.warn('Resetting timer! and changing mode to: humidityTrigger');\n        startTimer          = 0; //local\n        flow.set('startTimer',0); //flow\n        \n        fanSwitch.payload   = 'OFF';\n        \n        requiredFanState    = \"humidityTrigger\"; //local\n        flow.set('requiredFanState',\"humidityTrigger\"); //flow\n        \n        fanStateNR.payload  = \"humidityTrigger\"; //ready to tell the ESP+Sensor\n        DashSync.payload    = \"humidityTrigger\"; //ready to tell the dashboard dropdown\n        \n        \n        flow.set('fanTimerInterval',fanTimerInterval);\n        \n    } else if ((progress <  fanTimerInterval) && (startTimer > 0)) {\n        node.warn('timer progress:'+ progress + 'to:'+ fanTimerInterval); // progress up to the point the timer is spent\n    }\n    \n    flow.set('requiredFanState',requiredFanState); //set the flow params on exit, even if there's no change\n    flow.set('fanTimerInterval',fanTimerInterval);\n\n    return [ DashSync, fanSwitch, fanStateNR ];\n} \n\n\n",
        "outputs": 3,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 640,
        "y": 400,
        "wires": [
            [
                "d279dabf5f459856"
            ],
            [
                "480ff91b0d684a1c",
                "fe56edaa6c390ad0",
                "6a684d4e3c6d91d7"
            ],
            [
                "2501240f4c2d1391"
            ]
        ]
    },
    {
        "id": "52e6fe2c7206f423",
        "type": "debug",
        "z": "8b72e4c9a2f439e5",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1630,
        "y": 620,
        "wires": []
    },
    {
        "id": "801c9fbdb8f5a756",
        "type": "inject",
        "z": "8b72e4c9a2f439e5",
        "name": "High humidity (95%)",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "esp/dht/b/humid",
        "payload": "95",
        "payloadType": "num",
        "x": 380,
        "y": 300,
        "wires": [
            [
                "5a3b1c94b2f52243",
                "e0729cfdcfcaa9a9"
            ]
        ]
    },
    {
        "id": "fe56edaa6c390ad0",
        "type": "ui_text",
        "z": "8b72e4c9a2f439e5",
        "group": "b19d6a6aef08844e",
        "order": 1,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "Fan: ",
        "format": "{{msg.payload}}",
        "layout": "row-left",
        "className": "",
        "x": 1030,
        "y": 400,
        "wires": []
    },
    {
        "id": "25c2307d5cc7f15a",
        "type": "inject",
        "z": "8b72e4c9a2f439e5",
        "name": "Low humidity (50%)",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "esp/dht/b/humid",
        "payload": "50",
        "payloadType": "num",
        "x": 380,
        "y": 340,
        "wires": [
            [
                "e0729cfdcfcaa9a9",
                "5a3b1c94b2f52243"
            ]
        ]
    },
    {
        "id": "d232adcfdae4f31a",
        "type": "mqtt-broker",
        "name": "Mosquitto",
        "broker": "192.168.7.245",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "protocolVersion": "5",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "sessionExpiry": ""
    },
    {
        "id": "1bc7dc824c2874aa",
        "type": "ui_group",
        "name": "Temp",
        "tab": "31c7884d06c62833",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "b19d6a6aef08844e",
        "type": "ui_group",
        "name": "Fan",
        "tab": "31c7884d06c62833",
        "order": 3,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "e4779998dd3b61c9",
        "type": "ui_group",
        "name": "Humidity",
        "tab": "31c7884d06c62833",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "31c7884d06c62833",
        "type": "ui_tab",
        "name": "Bathroom",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    }
]

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