Wemos in a Pelonis
This post will cover the use of the ESP8266 clone form factor of the more popular Wemos D1 Mini. This clone development module has the same pinout and interfaces as the Wemos D1 Mini. The Arduino IDE was set to use the NodeMCU 1.0 ESP12E Module for the code below, although the LOLIN(WEMOS) D1 mini (clone) was avaliable. This board selection didn’t present any issues when loading the firmware.
The board is inexpensive, a 10 pack can run around $25. It doesn’t have all of the GPIOs of a full ESP32 dev board, or some of the peripherals of the ESP32-Cam board. But the low cost and targeted use make it a viable option.
This post is intended as a demonstration only, please do not repeat these steps on any heating equipment you have at your disposal. The work done here is intended to provide a foundation for further work unrelated to space heating. I can’t stress enough the dangers of mishandling mains appliances, you stand a serious risk to you or someone you know if you carelessly attempt this, so don’t. You have been warned.
In this post, the module was installed in a space heater. It monitors the space heater control buttons. The module can also mimic input to the space heater to operate it. Based on the space heater operating state, the module monitors the energy use of the space heater. The module interfaces with a wireless network and communicates with a MQTT broker. Node-Red is used to interface with the module through the MQTT broker. Node-Red has flows that track the module readings and presents an interface for user control.
Here is the pinout of the module, it should be pointed out that the silk screening on the module is misleading. This can present issues for developers that aren’t aware of the GPIO to digital and analog pin numbering scheme.
Since the space heater inputs operated at a 5 volt logic, a level shifter was placed between the module and space heater controller. Since the space heater provided an adequate 5 volt supply, this was fed to the module. Here is a pinout of the connections.
The green line connects the module D1 pin (GPIO 5) to the space heater on/off button. The yellow line connects the module D6 pin (GPIO 12) to the space heater mode button. Again, these traverse a level shifter to keep the module GPIO pins at 3.3 volts and the space heater pins at 5 volts. Here is a photo of the components installed in the space heater control panel.
After reassembly, the space heater appears the same as it had before the modification.
Here is the Arduino code that was programmed on the module.
/** Header Title: Wemos ESP8266 with MQTT interfaced Pelonis Space Heater Controller Version: 11 Filename: Wemos-184_MQTT_Pelonis-Space-Heater-Controller_ver11.ino Wemos draws 80mA ~ 400mW Date: 10/14/2023 Author: Patrick Gilfeather - CloudACM */ // Libraries and Declarations #include <Arduino.h> #include <ESP8266WiFi.h> #include <PubSubClient.h> WiFiClient espClient; PubSubClient MQTTclient(espClient); long laststats = 0; int programflag = 0; // NTP Libraries, Declarations, and Variables #include <NTPClient.h> #include <WiFiUdp.h> WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org"); //Week Days String weekDays[7]={"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; //Month names String months[12]={"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; // Variables // MQTT Broker const char* mqtt_server = "<ip_of_mqtt_broker>"; // Put your MQTT Broker here // Your WiFi credentials const char* ssid = "<wifi_network>"; // Put your SSID here const char* password = "<wifi_password>"; // Put your PASSWORD here unsigned long lasttimeupdate = 0; // Time counter for periodic NTP time checks unsigned long lasttimereboot = 0; // Time counter for periodic reboots to avoid millis rollover ADC_MODE(ADC_VCC); // See comment block below for more details /* see - http://arduino.esp8266.com/Arduino/versions/2.0.0-rc2/doc/libraries.html ESP.getVcc() may be used to measure supply voltage. ESP needs to reconfigure the ADC at startup in order for this feature to be available. Add the following line to the top of your sketch to use getVcc: ADC_MODE(ADC_VCC); TOUT pin has to be disconnected in this mode. Note that by default ADC is configured to read from TOUT pin using analogRead(A0), and ESP.getVCC() is not available. */ // Pelonis Space Heater Button Interfaces int OnOff = 5; int Mode = 12; // State of Pelonis Space Heater Buttons int OnOffState = 0; int ModeState = 0; // Reference State of Pelonis Space Heater Buttons int LastOnOffState = 0; int LastModeState = 0; // Pelonis Space Heater Counters, Variables, and Constants int DeBounceDelay = 250; unsigned long PanelCountdown = 0; unsigned long PanelClock = millis(); int PowerState = 0; int OperationState = 0; int PanelState = 0; /** Wattage Usage Calculations Based on Amp meter readings High Mode = 12.5 Amps with 120 Volt AC Source Low Mode = 7 Amps with 120 Volt AC Source MQTT Status message are sent every 5 seconds To cacluate watt hours used the mode amp value is multiplied by the source voltage ie. 7 * 120 = 840 Watts The result is then devided by the amount of time that has passed since the last reading ie. 840 / (60 * 12) = 1.1667 Watts used in 5 seconds The Watts used in 5 seconds becomes the factor to add to the variable WattsUsed High Mode = 2.08 Low Mode = 1.17 */ float WattsUsed = 0; // MQTT Functions void callback(char* topic, byte* message, unsigned int length) { String messageTemp; for (int i = 0; i < length; i++) { messageTemp += (char)message[i]; } if (String(topic) == "Wemos-184/UpdateTime") { if(messageTemp == "CheckTime"){ timeClient.update(); } } if (String(topic) == "Wemos-184/Reboot") { if(messageTemp == "Reboot"){ ESP.restart(); } } if (String(topic) == "Wemos-184/OnOff") { if(messageTemp == "Pushed"){ pinMode(OnOff, OUTPUT); digitalWrite(Mode, LOW); delay(DeBounceDelay); digitalWrite(Mode, HIGH); pinMode(OnOff, INPUT); if (PowerState == 1) { PowerState = 0; PanelState = 0; } else { PowerState = PowerState + 1; PanelState = 1; PanelClock = millis(); PanelCountdown = PanelClock; } } } if (String(topic) == "Wemos-184/Mode") { if (PowerState == 1) { if(messageTemp == "Pushed"){ if (PanelState == 1) { PanelClock = millis(); PanelCountdown = PanelClock; pinMode(Mode, OUTPUT); digitalWrite(Mode, LOW); delay(DeBounceDelay); digitalWrite(Mode, HIGH); pinMode(Mode, INPUT); if (OperationState == 3) { OperationState = 0; } else { OperationState = OperationState + 1; } } else { PanelState = 1; PanelClock = millis(); PanelCountdown = PanelClock; pinMode(Mode, OUTPUT); digitalWrite(Mode, LOW); delay(DeBounceDelay); digitalWrite(Mode, HIGH); pinMode(Mode, INPUT); } } else { // Ignore If Power State Is Off } } } } void reconnect() { // Loop until we're reconnected while (!MQTTclient.connected()) { // Attempt to connect if (MQTTclient.connect("Wemos-184")) { // Subscribe // Do you not subscribe to my methods? // Wemos-184/# for everything, or Wemos-184/Uptime for just the Uptime MQTTclient.subscribe("Wemos-184/#"); } else { // Wait 5 seconds before retrying delay(5000); } } } void UpdateStats() { long stats = millis(); if (stats - laststats > 5000) { laststats = stats; MQTTclient.publish("Wemos-184/Firmware", "Wemos-184_MQTT_Pelonis-Space-Heater-Controller_ver11"); String StringUptime = String(millis()); MQTTclient.publish("Wemos-184/Uptime", StringUptime.c_str()); String StringHWAddress = String(WiFi.macAddress()); MQTTclient.publish("Wemos-184/HWAddress", StringHWAddress.c_str()); String StringWifiSignal = String(WiFi.RSSI()); MQTTclient.publish("Wemos-184/WifiSignal",StringWifiSignal.c_str()); String StringPowerState = String(PowerState); MQTTclient.publish("Wemos-184/PowerState",StringPowerState.c_str()); String StringOperationState = String(OperationState); MQTTclient.publish("Wemos-184/OperationState",StringOperationState.c_str()); String StringPanelState = String(PanelState); MQTTclient.publish("Wemos-184/PanelState",StringPanelState.c_str()); if ((OperationState == 0) && (PowerState == 1)) { WattsUsed = WattsUsed + 2.08; } else if ((OperationState == 1) && (PowerState == 1)) { WattsUsed = WattsUsed + 1.17; } else { } WattsUsed = WattsUsed; String StringWattsUsed = String(WattsUsed); MQTTclient.publish("Wemos-184/WattsUsed",StringWattsUsed.c_str()); String StringFreeHeapSize = String(ESP.getFreeHeap()); MQTTclient.publish("Wemos-184/FreeHeapSize",StringFreeHeapSize.c_str()); String StringHeapFragmentation = String(ESP.getHeapFragmentation()); MQTTclient.publish("Wemos-184/HeapFragmentation",StringHeapFragmentation.c_str()); String StringMaxFreeBlockSize = String(ESP.getMaxFreeBlockSize()); MQTTclient.publish("Wemos-184/MaxFreeBlockSize",StringMaxFreeBlockSize.c_str()); String StringSketchSize = String(ESP.getSketchSize()); MQTTclient.publish("Wemos-184/SketchSize",StringSketchSize.c_str()); String StringFreeSketchSpace = String(ESP.getFreeSketchSpace()); MQTTclient.publish("Wemos-184/FreeSketchSpace",StringFreeSketchSpace.c_str()); String StringCpuFreqMHz = String(ESP.getCpuFreqMHz()); MQTTclient.publish("Wemos-184/CpuFreqMHz",StringCpuFreqMHz.c_str()); String StringChipId = String(ESP.getChipId()); MQTTclient.publish("Wemos-184/ChipId",StringChipId.c_str()); String StringVcc = String(ESP.getVcc()); MQTTclient.publish("Wemos-184/Vcc",StringVcc.c_str()); //Get a Time Structure String formattedTime = timeClient.getFormattedTime(); String StringformattedTime = String(formattedTime); MQTTclient.publish("Wemos-184/Time",StringformattedTime.c_str()); //Get a Date Structure time_t epochTime = timeClient.getEpochTime(); struct tm *ptm = gmtime ((time_t *)&epochTime); int monthDay = ptm->tm_mday; int currentMonth = ptm->tm_mon+1; String currentMonthName = months[currentMonth-1]; int currentYear = ptm->tm_year+1900; //Publish complete date: String StringcurrentDate = String(currentMonth) + "/" + String(monthDay) + "/" + String(currentYear); MQTTclient.publish("Wemos-184/Date",StringcurrentDate.c_str()); //Publish Epoch: String StringEpochTime = String(timeClient.getEpochTime()); MQTTclient.publish("Wemos-184/EpochTime",StringEpochTime.c_str()); } } void InputCondition() { OnOffState = digitalRead(OnOff); if (OnOffState != LastOnOffState) { if (OnOffState == HIGH) { MQTTclient.publish("Wemos-184/OnOffState", "0"); LastOnOffState = OnOffState; PanelClock = millis(); PanelCountdown = PanelClock; } else { MQTTclient.publish("Wemos-184/OnOffState", "1"); LastOnOffState = OnOffState; if (PowerState == 1) { PowerState = 0; PanelState = 0; } else { PowerState = PowerState + 1; PanelState = 1; PanelClock = millis(); PanelCountdown = PanelClock; } } delay(DeBounceDelay); } ModeState = digitalRead(Mode); if (PowerState == 1) { if (ModeState != LastModeState) { if (PanelState == 1) { PanelClock = millis(); PanelCountdown = PanelClock; if (ModeState == HIGH) { MQTTclient.publish("Wemos-184/ModeState", "0"); LastModeState = ModeState; } else { MQTTclient.publish("Wemos-184/ModeState", "1"); LastModeState = ModeState; if (OperationState == 3) { OperationState = 0; } else { OperationState = OperationState + 1; } } } else { PanelState = 1; PanelClock = millis(); PanelCountdown = PanelClock; if (ModeState == HIGH) { MQTTclient.publish("Wemos-184/ModeState", "0"); LastModeState = ModeState; } else { MQTTclient.publish("Wemos-184/ModeState", "1"); LastModeState = ModeState; } } delay(DeBounceDelay); } } } // Setup Function void setup() { // Iinitialize the Pelonis Space Heater Button pins as inputs pinMode(OnOff, INPUT); pinMode(Mode, INPUT); delay(1000); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { // Just wait it out delay(250); } MQTTclient.setServer(mqtt_server, 1883); MQTTclient.setCallback(callback); timeClient.begin(); // Initialize a NTPClient to get time timeClient.setTimeOffset(-25200); // Set offset time in seconds to adjust for your timezone, for example: // GMT -7 = -25200 (see - https://time.gov/) delay(1000); timeClient.update(); } // Main Loop Function void loop() { if (!MQTTclient.connected()) { reconnect(); } MQTTclient.loop(); UpdateStats(); InputCondition(); PanelClock = millis(); if (PanelClock - PanelCountdown > 60000) { PanelCountdown = PanelClock; PanelState = 0; } // Update time from NTP source every 1 day (24 * 60 * 60 * 1000 = 86400000 milli-seconds) unsigned long timeupdate = millis(); if (timeupdate - lasttimeupdate > 86400000) { lasttimeupdate = timeupdate; timeClient.update(); } // Reboot microcontroller every 30 day to avoid millis() rollover (30 * 24 * 60 * 60 * 1000 = 2592000000 milli-seconds) unsigned long timereboot = millis(); if (timereboot - lasttimereboot > 2592000000) { // Reboot command ESP.restart(); } } /** Footer */
The Node-Red flows are similar to earlier posting on this blog. However, the energy usage flows use a math function to provide a daily average of watt hours used.
Here are the details for the watt usage flows.
[ { "id": "417952fce84bb17d", "type": "ui_text", "z": "4d44da15033849c8", "group": "6d9b77eeca28b65c", "order": 13, "width": 0, "height": 0, "name": "", "label": "Watts Used", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 2570, "y": 660, "wires": [] }, { "id": "308bdda3b45f03b1", "type": "ui_text", "z": "4d44da15033849c8", "group": "6d9b77eeca28b65c", "order": 14, "width": 0, "height": 0, "name": "", "label": "Daily Ave Watt Hour Used", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 2610, "y": 700, "wires": [] }, { "id": "de1f326ceb1b70f8", "type": "function", "z": "4d44da15033849c8", "name": "Daily Ave Math", "func": "totalwatts=msg.payload;\nmsg.payload = (totalwatts / 24).toFixed(2);\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 2320, "y": 700, "wires": [ [ "308bdda3b45f03b1" ] ] }, { "id": "b172fdf0e41a9c07", "type": "mqtt in", "z": "4d44da15033849c8", "name": "", "topic": "Wemos-184/WattsUsed", "qos": "0", "datatype": "auto", "broker": "54b01280.e7076c", "nl": false, "rap": false, "inputs": 0, "x": 1880, "y": 660, "wires": [ [ "417952fce84bb17d", "de1f326ceb1b70f8", "dc6a31b914cc4228" ] ] }, { "id": "6d9b77eeca28b65c", "type": "ui_group", "name": "Pelonis Space Heater", "tab": "6135d078965eafde", "order": 3, "disp": true, "width": "7", "collapse": false, "className": "" }, { "id": "54b01280.e7076c", "type": "mqtt-broker", "name": "MQTT-BROKER-NAME", "broker": "MQTT-BROKER-IP", "port": "MQTT-BROKER-PORT", "clientid": "", "autoConnect": true, "usetls": false, "protocolVersion": "3", "keepalive": "60", "cleansession": true, "birthTopic": "", "birthQos": "0", "birthPayload": "", "birthMsg": {}, "closeTopic": "", "closePayload": "", "closeMsg": {}, "willTopic": "", "willQos": "0", "willPayload": "", "willMsg": {}, "sessionExpiry": "" }, { "id": "6135d078965eafde", "type": "ui_tab", "name": "Space Heater", "icon": "dashboard", "order": 5, "disabled": false, "hidden": false } ]
Some of the other features handled by Node-Red are storing the values published by the module to the MQTT broker. These variables are referenced in functions to control the heater and store readings into a log file.
Here are the details for the variable flows.
[ { "id": "dcdba04f.d1f238", "type": "tab", "label": "Flow 1", "disabled": false, "info": "" }, { "id": "b14d4720.902cb", "type": "mqtt out", "z": "dcdba04f.d1f238", "name": "Wemos-184 OnOff", "topic": "Wemos-184/OnOff", "qos": "0", "retain": "", "broker": "5e4afd44.ddb3d4", "x": 790, "y": 140, "wires": [] }, { "id": "889ecbae.8d1748", "type": "ui_button", "z": "dcdba04f.d1f238", "name": "On / Off", "group": "700e09da.cbb21", "order": 6, "width": "2", "height": "1", "passthru": true, "label": "On / Off", "tooltip": "", "color": "#585858", "bgcolor": "#242424", "icon": "", "payload": "Pushed", "payloadType": "str", "topic": "Wemos-184/OnOff", "x": 600, "y": 140, "wires": [ [ "b14d4720.902cb" ] ] }, { "id": "484aa462.962d6c", "type": "link in", "z": "dcdba04f.d1f238", "name": "Pelonis Power Control In", "links": [ "bf40758e.dda6d8" ], "x": 395, "y": 140, "wires": [ [ "889ecbae.8d1748" ] ] }, { "id": "f9755330.554a2", "type": "change", "z": "dcdba04f.d1f238", "name": "Set Flow Variable PorchTemp", "rules": [ { "t": "set", "p": "PorchTemp", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 480, "y": 240, "wires": [ [] ] }, { "id": "dedebb81.bb3958", "type": "change", "z": "dcdba04f.d1f238", "name": "Set Flow Variable OfficeTemp", "rules": [ { "t": "set", "p": "OfficeTemp", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 480, "y": 280, "wires": [ [] ] }, { "id": "8cf66698.e9d418", "type": "change", "z": "dcdba04f.d1f238", "name": "Set Flow Variable PelonisOnlineStatus", "rules": [ { "t": "set", "p": "PelonisOnlineStatus", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 510, "y": 320, "wires": [ [] ] }, { "id": "b7a38c6.47a637", "type": "change", "z": "dcdba04f.d1f238", "name": "Set Flow Variable PelonisLatency", "rules": [ { "t": "set", "p": "PelonisLatency", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 500, "y": 360, "wires": [ [] ] }, { "id": "2ee2eb14.7c50fc", "type": "change", "z": "dcdba04f.d1f238", "name": "Set Flow Variable PelonisPowerState", "rules": [ { "t": "set", "p": "PelonisPowerState", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 510, "y": 400, "wires": [ [] ] }, { "id": "8459827c.117da8", "type": "change", "z": "dcdba04f.d1f238", "name": "Set Flow Variable PelonisPanelState", "rules": [ { "t": "set", "p": "PelonisPanelState", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 510, "y": 440, "wires": [ [] ] }, { "id": "ccfce7c6.d8bd4", "type": "change", "z": "dcdba04f.d1f238", "name": "Set Flow Variable PelonisOpState", "rules": [ { "t": "set", "p": "PelonisOpState", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 500, "y": 480, "wires": [ [] ] }, { "id": "8deb0a5f.cd36d8", "type": "change", "z": "dcdba04f.d1f238", "name": "Set Flow Variable PelonisWattsUsed", "rules": [ { "t": "set", "p": "PelonisWattsUsed", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 510, "y": 520, "wires": [ [] ] }, { "id": "daf35ed2.e24558", "type": "change", "z": "dcdba04f.d1f238", "name": "Set Flow Variable PelonisBypass", "rules": [ { "t": "set", "p": "PelonisBypass", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 500, "y": 560, "wires": [ [] ] }, { "id": "9d87ef2c.3d452", "type": "inject", "z": "dcdba04f.d1f238", "name": "Trigger Every 1 Minute", "topic": "", "payloadType": "date", "repeat": "60", "crontab": "", "once": false, "onceDelay": 0.1, "x": 270, "y": 640, "wires": [ [ "48fa77c2.35eff", "856c8c1d.4ff598" ] ] }, { "id": "48fa77c2.35eff", "type": "function", "z": "dcdba04f.d1f238", "name": "Gather Variables and Format Row", "func": "var d = new Date();\nvar pt = flow.get('PorchTemp');\nvar ot = flow.get('OfficeTemp');\nvar pos = flow.get('PelonisOnlineStatus');\nvar plt = flow.get('PelonisLatency');\nvar pps = flow.get('PelonisPowerState');\nvar ppa = flow.get('PelonisPanelState');\nvar pop = flow.get('PelonisOpState');\nvar pwu = flow.get('PelonisWattsUsed');\nvar row = {d, pt, ot, pos, plt, pps, ppa, pop, pwu};\nmsg.payload = row;\nreturn msg;", "outputs": 1, "noerr": 0, "x": 620, "y": 640, "wires": [ [ "ca89c83f.63f318" ] ] }, { "id": "856c8c1d.4ff598", "type": "function", "z": "dcdba04f.d1f238", "name": "Gather Variables and Determine Action Rev 7", "func": "// Working With Time- Node-Red Programming\n// See https://stevesnoderedguide.com/working-with-time\n\nvar TimeNow = new Date();\nvar HourNow = TimeNow.getHours(); \nvar NumHour = Number(HourNow);\nvar DayNow = TimeNow.getDay();\nvar Sunday = 0;\nvar Monday = 1;\nvar Tuesday = 2;\nvar Wednesday = 3;\nvar Thursday = 4;\nvar Friday = 5;\nvar Saturday = 6;\n\nvar WeekdayMorningHour = 4;\nvar WeekendMorningHour = 6;\nvar EveningHour = 22;\n\n// Day Time Operation with median at 71 ( hysteresis is 70 to 72 )\n// 68 is the typical recommended baseline for cost savings\nvar DayThermo = 71;\nvar HysteresisDayHigh = DayThermo + 1;\nvar HysteresisDayLow = DayThermo - 1;\n\n// Night Time Operation with median at 65 ( hysteresis is 64 to 66 )\n// 58 is the typical recommended baseline for cost savings\nvar NightThermo = 65;\nvar HysteresisNightHigh = NightThermo + 1;\nvar HysteresisNightLow = NightThermo - 1;\n\nvar TempOffice = flow.get('OfficeTemp');\nvar NumOfficeTemp = Number(TempOffice);\nvar OnlineStatus = flow.get('PelonisOnlineStatus');\nvar PowerState = flow.get('PelonisPowerState');\n\n\nif (\n OnlineStatus == \"On\" \n && PowerState == \"1\" \n && NumOfficeTemp >= HysteresisDayHigh \n && NumHour >= WeekdayMorningHour \n && NumHour < EveningHour \n && (DayNow < Saturday && DayNow > Sunday))\n NewPowerState = \"Off\";\n\nif (\n OnlineStatus == \"On\" \n && PowerState == \"0\" \n && NumOfficeTemp < HysteresisDayLow \n && NumHour >= WeekdayMorningHour \n && NumHour < EveningHour \n && (DayNow < Saturday && DayNow > Sunday))\n NewPowerState = \"On\";\n \n\nif (\n OnlineStatus == \"On\" \n && PowerState == \"1\" \n && NumOfficeTemp >= HysteresisNightHigh \n && (NumHour < WeekdayMorningHour || NumHour >= EveningHour) \n && (DayNow < Saturday && DayNow > Sunday))\n NewPowerState = \"Off\";\n\nif (\n OnlineStatus == \"On\" \n && PowerState == \"0\" \n && NumOfficeTemp < HysteresisNightLow \n && (NumHour < WeekdayMorningHour || NumHour >= EveningHour) \n && (DayNow < Saturday && DayNow > Sunday))\n NewPowerState = \"On\";\n\n\nif (\n OnlineStatus == \"On\" \n && PowerState == \"1\" \n && NumOfficeTemp >= HysteresisDayHigh \n && NumHour >= WeekendMorningHour \n && NumHour < EveningHour \n && (DayNow < Monday || DayNow > Friday))\n NewPowerState = \"Off\";\n\nif (\n OnlineStatus == \"On\" \n && PowerState == \"0\" \n && NumOfficeTemp < HysteresisDayLow \n && NumHour >= WeekendMorningHour \n && NumHour < EveningHour \n && (DayNow < Monday || DayNow > Friday))\n NewPowerState = \"On\";\n \n\nif (\n OnlineStatus == \"On\" \n && PowerState == \"1\" \n && NumOfficeTemp >= HysteresisNightHigh \n && (NumHour < WeekendMorningHour || NumHour >= EveningHour) \n && (DayNow < Monday || DayNow > Friday))\n NewPowerState = \"Off\";\n\nif (\n OnlineStatus == \"On\" \n && PowerState == \"0\" \n && NumOfficeTemp < HysteresisNightLow \n && (NumHour < WeekendMorningHour || NumHour >= EveningHour) \n && (DayNow < Monday || DayNow > Friday))\n NewPowerState = \"On\";\n\nmsg.payload = NewPowerState;\nreturn msg;", "outputs": 1, "noerr": 0, "x": 650, "y": 680, "wires": [ [ "5d6c2db8.a6f28c" ] ] }, { "id": "5d6c2db8.a6f28c", "type": "rbe", "z": "dcdba04f.d1f238", "name": "State Changes Only", "func": "rbe", "gap": "", "start": "", "inout": "out", "property": "payload", "x": 980, "y": 680, "wires": [ [ "756e1fd.279966" ] ] }, { "id": "756e1fd.279966", "type": "change", "z": "dcdba04f.d1f238", "name": "Chnage to Pushed Message", "rules": [ { "t": "change", "p": "payload", "pt": "msg", "from": "On", "fromt": "str", "to": "Pushed", "tot": "str" }, { "t": "change", "p": "payload", "pt": "msg", "from": "Off", "fromt": "str", "to": "Pushed", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 1240, "y": 680, "wires": [ [ "bf40758e.dda6d8" ] ] }, { "id": "ca89c83f.63f318", "type": "file", "z": "dcdba04f.d1f238", "name": "", "filename": "/home/user/Node-Red Data Log/TempLog.csv", "appendNewline": true, "createDir": false, "overwriteFile": "false", "encoding": "none", "x": 1170, "y": 640, "wires": [ [] ] }, { "id": "bf40758e.dda6d8", "type": "link out", "z": "dcdba04f.d1f238", "name": "Pelonis Power Control Out", "links": [ "484aa462.962d6c" ], "x": 1435, "y": 680, "wires": [] }, { "id": "5e4afd44.ddb3d4", "type": "mqtt-broker", "name": "MQTT-BROKER-NAME", "broker": "MQTT-BROKER-IP", "port": "MQTT-BROKER-PORT", "clientid": "", "usetls": false, "compatmode": false, "keepalive": "60", "cleansession": true, "birthTopic": "", "birthQos": "0", "birthPayload": "", "closeTopic": "", "closePayload": "", "willTopic": "", "willQos": "0", "willPayload": "" }, { "id": "700e09da.cbb21", "type": "ui_group", "name": "Pelonis Space Heater", "tab": "af2ff0ee.cec43", "order": 3, "disp": true, "width": "7", "collapse": false }, { "id": "af2ff0ee.cec43", "type": "ui_tab", "name": "Space Heater", "icon": "dashboard", "order": 5, "disabled": false, "hidden": false } ]
Stay warm, just not too warm.