Wemos in a Pelonis

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.

Comments are closed.