About

Epever solar chargers seem to be popular and they provide an open API that allows interfacing conveniently without needing to hack into the controller itself. This guide will present a PCB design involving an ESP8266 micro-controller and a MAX485 TTL converter that will be used to periodically poll an MPPT Epever solar charger and then publish the gathered data to an MQTT broker.

Requirements

  • Epever MPPT solar controller; the hardware and software realization prezented on this page should work with any Epever MPPT solar controller due to Epever having an universal API,
  • ESP8266, we use WeMoS D1 Mini Pro modules due to their small size, low cost, low energy consumption and with just enough features to act as a controller for any device - other creators often chose the ESP32 but we found it unnecessarily large and packed with features that a simple data relay does not need,
  • MAX485 TTL converter; this is needed to interface with the ESP8266 serial port in order to be able to talk to the Epever MPPT solar controller,

  • RJ-45 Ethernet cable; the Ethernet cable is used to connect the Epever MPPT solar controller to the MAX485 converter. The cable will be butchered and only four wires will be used anyway such that the cheapest cable should suffice.
  • The usual crafting equipment, PCBs, Dremel, soldering iron, etc,

Diagram

For this project the ESP and MAX485 TTL will be entirely powered by the Epever $5V$ output via the RJ-45 cable that connects to the Epever controller.

The correct wiring between the RJ-45 Ethernet cable and the Epever controller is as follows:

MAX485 TTL RJ-45 Ethernet Cable Color Comment
Vcc ($5V$) orange It has been noted by some that the Epever controller provides $7.5V$ but for the Epever 40A the power provided via the EJ-45 jack seems to be $5V$ which is perfect for powering the ESP8266 as well as the MAX485 TTL module.
GND brown
A green Used for the Modbus protocol over RS485.
B blue

The wiring between the MAX485 TTL and the ESP is as follows:

ESP MAX485 TTL Comments
Vcc ($5V$) Vcc ($5V$) Vcc and GND are actually connected to the other side of the module that connects to the Epever controller via the RJ-45 Ethernet cable and they do not correspond to any MAX485 specific markings on the converter such as DI, DE, RE, RO that have different meanings and are connected to other pins on the ESP.
GND GND
DI TX DI and RO on the MAX485 converter are connected to the serial pins on the ESP.
RO RX
RE D1 (GPIO) These two GPIO pins are used as part of the Modbus protocol and are held low or high respectively depending on whether the ESP is transmitting or receiving.
DE D2 (GPIO)

Here is some optional wiring that is not specific to this project but will enable some features that are accounted for in the Arduino code:

  • on the ESP board itself connect D0 to RST via a $\le 1k\Omega$ resistor in order to enable the ESP to go into deep sleep; since we are going to be polling the Epever solar controller, realistically speaking a data resolution of 1 minute should be sufficient such that between reading the Epever solar controller, the ESP can go into deep sleep thereby sparing even more energy.

Realization

Note that in this image, the WeMoS D1 Mini Pro is removed from its socket.

The circuit is not that difficult to make but we were mindful with some things:

  • the ESP is not directly soldered onto the PCB but rather risers are used making the WeMoS D1 Mini Pro module removable and this is cool because aside from hotswapping the module in case of damages, the WeMoS can be retrieved for programming simply by pulling it out of the sockets,
  • the MAX485 similarly is socketed using GPIO pin strips,
  • the blue painter's tape on the Ethernet cable is not essential but we desired to keep the wires that were not used such that they have been sticky taped onto the cable itself,
  • the wires used from the Ethernet cable are similarly soldered to a GPIO pin header that connects to a socket on the board making the Ethernet cable removable,
  • no project box was used, but struts were used (observable via the screws and the bottom white plastic) to mount the project next to the Epever solar controller; similarly, both the MAX485 and the ESP emit some light signals via their LEDs such that it is nicer to allow that to be observed,
  • the resistor on the PCB connects D0 to RST of the ESP in order to allow deep sleeping

Arduino Code

The code represents a rework of the original by Don M/glitterkitty but with some changes marked in the header of the sketch.

Within the configuration section marked in the code, some changes have to be made in order to set:

  • the wireless network parameters to connect to,
  • authentication parameters for OTA updates,
  • parameters for the MQTT broker,
  • whether to use deep sleep (depending on whether the optional wiring has been made),
  • the minimal amount of sleep to be carried out by the ESP

the project aims to have all other parameters (including the sleep time) configurable dynamically via MQTT.

The code is designed to perform the following actions, in order:

  1. get the readings from the Epever solar controller,
  2. connect Wifi, OTA and subscribe to a given MQTT topic (defined by the MQTT_TOPIC_SUB in the code),
  3. publish a JSON nibblet to the MQTT broker indicating that the ESP is online,
  4. publish all the data from the Epever solar controller to various topics depending on context,
  5. publish a JSON nibblet to the MQTT broker indicating that the ESP is now waiting,
  6. terminate all connections and go to sleep

Originally, toggling the load of the Epever solar controller did not work too well, as observed within some of the comments of the original author and this has been traced to two problems:

  • the fact that Arduino does not seem to flush the TCP buffers when disconnecting from the Wifi network (in fact, the original code did not even bother to disconnect, but just went into sleep and then restarted the MQTT connection to the broker),
  • a race-condition, somewhat based on the former where the ESP goes too fast into sleep or deep sleep without waiting for messages on the subscribed topic nor waiting for the messages to be processed; both of these have been addressed and with the idea in mind to avoid the MQTT "retained" messages feature.

That being said, the following logic is approachable when wanting to update the ESP monitor settings, in order:

  1. configure the MQTT_SUBSCRIBE_WAIT during the sketch upload that will define the amount of time that the ESP will wait to receive messages from the MQTT broker,
  2. wait for the monitor to publish the waiting payload,
  3. send a settings update message to the configured MQTT_TOPIC_SUB topic such that the ESP can update its settings
/*************************************************************************/
/*      Copyright (C) 2019 Don M/glitterkitty - License: GNU GPLv3       */
/*    Copyright (C) 2023 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
// v1.0                                                                  //
// Reworked from: https://github.com/glitterkitty/EpEverSolarMonitor     //
//                                                                       //
// Current documentation @                                               //
//    https://grimore.org/hardware/epever/generic_controller_monitor     //
//                                                                       //
// Differences & Highlights:                                             //
//   * added OTA update capabilities,                                    //
//   * settings are made via MQTT instead of hardware (debug),           //
//   * used a JSON objects intead of MQTT topics,                        //
//   * made deep sleep optional,                                         //
//   * ensured re-entrancy when resuming out of suspend,                 //
//   * made time to process MQTT messages configurable,                  //
//   * added some compatiblity with ESP32,                               //
//   * made multiple parameters configurable,                            //
//   * small retouches and aesthetics                                    //
//                                                                       //
// About:                                                                //
// This is an Arduino sketch for retrieving metrics off a solar panel    //
// controller made by EpEver and should work for most MPPT models.       //
//                                                                       //
// The original documentation by glitterkitty mentioned that the EPEVER  //
// outputs +7.5V on the orange-white RJ45 connector but at least for the //
// EPEVER 40A the voltage between the orange and the brown wire of the   //
// RJ45 connector has been measured to be +5.11V which is great because  //
// no stepdown is needed and the EPEVER can power both the MAX485 TTL    //
// as well as the ESP. In any case, the voltage should be measured       //
// again before implementing the circuit diagram just to make sure.      //
//                                                                       //
// Essential wiring diagram:                                             //
//                                                                       //
//                    +---------+                                        //
//                    |         |                                        //
//   orange +<--------+--- 5V --+------------------------> + VCC 5V      //
//                    |         + DI <-------------------> + TX          //
//    green +<--------+ A       + DE <-------------------> + D2          //
//        EPEVER      |         |                         ESP            //
//         RJ45       | MAX485  |                       8266 / 32        //
//     blue +<--------+ B       + RE <-------------------> + D1          //
//                    |         + RO <-------------------> + RX          //
//    brown +<--------+-- GND --+------------------------> + GND         //
//                    |         |                                        //
//                    +---------+                                        //
//                                                                       //
// Optional wiring:                                                      //
//   * connect the ESP D0 GPIO to RST via <= 1kOhm resistor and then set //
//     the variable USE_DEEP_SLEEP in this sketch to true in order to    //
//     allow the ESP to go into deep sleep mode and consume less power.  //
//                                                                       //
// Software usage:                                                       //
// The sketch will generate JSON payloads and send them to the following //
// MQTT topics:                                                          //
//                                                                       //
// MQTT_TOPIC_PUB / panel | battery | load | energy | extra | monitor    //
//                                                                       //
// where panel, battery, load, energy, extra and monitor are various     //
// subtopics of the configurable MQTT_TOPIC_PUB topic. For example to    //
// just listen for battery notifications, subscribe to the following     //
// MQTT topic: MQTT_TOPIC_PUB/battery                                    //
//                                                                       //
// The the sketch will subscribe to the topic defined by MQTT_TOPIC_SUB  //
// and listen to the following JSON grammar:                             //
// {                                                                     //
//   "action" : "settings" | "switch"                                    //
//   "sleep" (when "action" is "settings") : time in seconds             //
//   "switch" (when "action" is "switch") : "on" | "off"                 //
// }                                                                     //
//                                                                       //
// For example, to set the sleep time between metric updates to about    //
// 5 minutes, send the following JSON payload on the MQTT_TOPIC_SUB      //
// topic:                                                                //
// {                                                                     //
//   "action" : "settings"                                               //
//   "sleep" : 120                                                       //
// }                                                                     //
//                                                                       //
// Note that for the switch to work, the Epever device has to be set to  //
// "manual mode". From the manual, that seems to be setting the load     //
// setting for the first timer to 117 and the second should display 2, n //
// and then the load can be toggled using this sketch.                   //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//                           configuration                               //
///////////////////////////////////////////////////////////////////////////
// Whether to send data over the serial port.
#define SERIAL_DATA 1
// RS-485 module pin DE maps to D2 GPIO on ESP.
#define MAX485_DE D2
// RS-485 module pin RE maps to D1 GPIO on ESP.
#define MAX485_RE D1
 
#define STA_SSID ""
#define STA_PASSWORD ""
#define MQTT_SERVER ""
#define MQTT_PORT 1883
#define MQTT_CLIENT_ID "EpEver Solar Monitor"
#define MQTT_TOPIC_PUB "epever-40a/talk"
#define MQTT_TOPIC_SUB "epever-40a/hear"
// Seconds to wait for MQTT message to be delivered and processed.
#define MQTT_SUBSCRIBE_WAIT 10
 
// The OTA hostname.
//#define OTA_HOSTNAME
// The OTA port on which updates take place.
//#define OTA_PORT 8266
// The authentication password to use for OTA updates.
// This should be set to the unsalted MD5 hash of the plaintext password.
//#define OTA_PASSWORD_HASH
 
// Whether to use deep sleep or not (requires hardware modifications).
//#define USE_DEEP_SLEEP 1
// the minimal amount that the ESP should sleep for.
#define MIN_SLEEP_SECONDS 60
 
///////////////////////////////////////////////////////////////////////////
//           general variable declarations and libraries                 //
///////////////////////////////////////////////////////////////////////////
#include <ModbusMaster.h>
#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#elif defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h>
#include <ESPmDNS.h>
#endif
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <ArduinoOTA.h>
#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP_EEPROM.h>
#elif defined(ARDUINO_ARCH_ESP32)
#include <EEPROM.h>
#endif
 
ModbusMaster node;
WiFiClient wifi_client;
PubSubClient mqtt_client(wifi_client);
bool loadState = true;
int sleepSeconds;
const int JSON_DOCUMENT_SIZE = 512;
 
StaticJsonDocument<JSON_DOCUMENT_SIZE> controllerStatusPayload;
StaticJsonDocument<JSON_DOCUMENT_SIZE> epeverMetricsPayload;
StaticJsonDocument<JSON_DOCUMENT_SIZE> epeverControlPayload;
char tmpJsonPayloadBuffer[JSON_DOCUMENT_SIZE];
 
///////////////////////////////////////////////////////////////////////////
//                        modbus data definitions                        //
///////////////////////////////////////////////////////////////////////////
// ModBus Register Locations
// Start of live-data
#define LIVE_DATA 0x3100
// 16 registers
#define LIVE_DATA_CNT 16
// just for reference, not used in code
#define PANEL_VOLTS 0x00
#define PANEL_AMPS 0x01
#define PANEL_POWER_L 0x02
#define PANEL_POWER_H 0x03
#define BATT_VOLTS 0x04
#define BATT_AMPS 0x05
#define BATT_POWER_L 0x06
#define BATT_POWER_H 0x07
// dummy * 4
#define LOAD_VOLTS 0x0C
#define LOAD_AMPS 0x0D
#define LOAD_POWER_L 0x0E
#define LOAD_POWER_H 0x0F
// D7-0 Sec, D15-8 Min : D7-0 Hour, D15-8 Day : D7-0 Month, D15-8 Year
#define RTC_CLOCK 0x9013
// 3 regs
#define RTC_CLOCK_CNT 3
// State of Charge in percent, 1 reg
#define BATTERY_SOC 0x311A
// Battery current L
#define BATTERY_CURRENT_L 0x331B
// Battery current H
#define BATTERY_CURRENT_H 0x331C
// start of statistical data
#define STATISTICS 0x3300
// 22 registers
#define STATISTICS_CNT 22
// just for reference, not used in code
// Maximum input volt (PV) today
#define PV_MAX 0x00
// Minimum input volt (PV) today
#define PV_MIN 0x01
// Maximum battery volt today
#define BATT_MAX 0x02
// Minimum battery volt today
#define BATT_MIN 0x03
// Consumed energy today L
#define CONS_ENERGY_DAY_L 0x04
// Consumed energy today H
#define CONS_ENGERY_DAY_H 0x05
// Consumed energy this month L
#define CONS_ENGERY_MON_L 0x06
// Consumed energy this month H
#define CONS_ENGERY_MON_H 0x07
// Consumed energy this year L
#define CONS_ENGERY_YEAR_L 0x08
// Consumed energy this year H
#define CONS_ENGERY_YEAR_H 0x09
// Total consumed energy L
#define CONS_ENGERY_TOT_L 0x0A
// Total consumed energy  H
#define CONS_ENGERY_TOT_H 0x0B
// Generated energy today L
#define GEN_ENERGY_DAY_L 0x0C
// Generated energy today H
#define GEN_ENERGY_DAY_H 0x0D
// Generated energy this month L
#define GEN_ENERGY_MON_L 0x0E
// Generated energy this month H
#define GEN_ENERGY_MON_H 0x0F
// Generated energy this year L
#define GEN_ENERGY_YEAR_L 0x10
// Generated energy this year H
#define GEN_ENERGY_YEAR_H 0x11
// Total generated energy L
#define GEN_ENERGY_TOT_L 0x12
// Total Generated energy  H
#define GEN_ENERGY_TOT_H 0x13
// Carbon dioxide reduction L
#define CO2_REDUCTION_L 0x14
// Carbon dioxide reduction H
#define CO2_REDUCTION_H 0x15
// r/w load switch state
#define LOAD_STATE 0x02
#define STATUS_FLAGS 0x3200
// Battery status register
#define STATUS_BATTERY 0x00
// Charging equipment status register
#define STATUS_CHARGER 0x01
 
///////////////////////////////////////////////////////////////////////////
//         datastructures, also for buffer to values conversion          //
///////////////////////////////////////////////////////////////////////////
// clock
union {
  struct {
    uint8_t s;
    uint8_t m;
    uint8_t h;
    uint8_t d;
    uint8_t M;
    uint8_t y;
  } r;
  uint16_t buf[3];
} rtc;
 
// live data
union {
  struct {
 
    int16_t pV;
    int16_t pI;
    int32_t pP;
 
    int16_t bV;
    int16_t bI;
    int32_t bP;
 
 
    uint16_t dummy[4];
 
    int16_t lV;
    int16_t lI;
    int32_t lP;
 
  } l;
  uint16_t buf[16];
} live;
 
// statistics
union {
  struct {
 
    // 4*1 = 4
    uint16_t pVmax;
    uint16_t pVmin;
    uint16_t bVmax;
    uint16_t bVmin;
 
    // 4*2 = 8
    uint32_t consEnerDay;
    uint32_t consEnerMon;
    uint32_t consEnerYear;
    uint32_t consEnerTotal;
 
    // 4*2 = 8
    uint32_t genEnerDay;
    uint32_t genEnerMon;
    uint32_t genEnerYear;
    uint32_t genEnerTotal;
 
    // 1*2 = 2
    uint32_t c02Reduction;
 
  } s;
  uint16_t buf[22];
} stats;
 
// these are too far away for the union conversion trick
uint16_t batterySOC = 0;
int32_t batteryCurrent = 0;
 
// battery status
struct {
  uint8_t volt;        // d3-d0  Voltage:     00H Normal, 01H Overvolt, 02H UnderVolt, 03H Low Volt Disconnect, 04H Fault
  uint8_t temp;        // d7-d4  Temperature: 00H Normal, 01H Over warning settings, 02H Lower than the warning settings
  uint8_t resistance;  // d8     abnormal 1, normal 0
  uint8_t rated_volt;  // d15    1-Wrong identification for rated voltage
} status_batt;
 
char batt_volt_status[][20] = {
  "Normal",
  "Overvolt",
  "Low Volt Disconnect",
  "Fault"
};
 
char batt_temp_status[][16] = {
  "Normal",
  "Over WarnTemp",
  "Below WarnTemp"
};
 
// charging equipment status (not fully impl. yet)
//uint8_t charger_operation = 0;
//uint8_t charger_state = 0;
//uint8_t charger_input = 0;
uint8_t charger_mode = 0;
 
//char charger_input_status[][20] = {
//  "Normal",
//  "No power connected",
//  "Higher volt input",
//  "Input volt error"
//};
 
char charger_charging_status[][12] = {
  "Off",
  "Float",
  "Boost",
  "Equlization"
};
 
///////////////////////////////////////////////////////////////////////////
//                           ModBus helper functions                     //
///////////////////////////////////////////////////////////////////////////
void preTransmission() {
  digitalWrite(MAX485_RE, 1);
  digitalWrite(MAX485_DE, 1);
 
  digitalWrite(LED_BUILTIN, LOW);
}
 
void postTransmission() {
  digitalWrite(MAX485_RE, 0);
  digitalWrite(MAX485_DE, 0);
 
  digitalWrite(LED_BUILTIN, HIGH);
}
 
///////////////////////////////////////////////////////////////////////////
//                            MQTT event handling                        //
///////////////////////////////////////////////////////////////////////////
void mqtt_reconnect() {
  // Loop until we're reconnected
  Serial.println("Checking MQT connection...");
  while (!mqtt_client.connected()) {
    Serial.print("MQTT Reconnecting: ");
 
    // Attempt to connect
    if (mqtt_client.connect(MQTT_CLIENT_ID)) {
      Serial.println("success");
 
      Serial.print("Subscribing MQTT: ");
      mqtt_client.subscribe(MQTT_TOPIC_SUB);
      Serial.println(MQTT_TOPIC_SUB);
    } else {
      Serial.print("failure, rc=");
      Serial.print(mqtt_client.state());
      Serial.println(" try again in 1 second");
 
      delay(1000);
    }
  }
}
 
// control load on / off here, setting sleep duration
//
void mqtt_callback(char* topic, byte* payload, unsigned int length) {
  uint8_t i, result;
 
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
 
  payload[length] = '\0';
 
  // ignore messages not sent on the subscribed topic
  if (strncmp(topic, MQTT_TOPIC_SUB, strlen(MQTT_TOPIC_SUB)) != 0) {
    return;
  }
 
  // Parse the payload sent to the MQTT topic as a JSON document.
  Serial.print("Deserializing message: ");
  DeserializationError error = deserializeJson(epeverControlPayload, payload);
  if (error) {
    Serial.print("failed, error=");
    Serial.println(error.c_str());
    return;
  } else {
    Serial.println("success");
  }
 
  if (!epeverControlPayload.containsKey("action")) {
    epeverControlPayload.clear();
    return;
  }
 
  if (epeverControlPayload["action"] == "switch") {
    // Switch - but i can't seem to switch a coil directly here ?!?
    if (epeverControlPayload["status"] == "on") {
      loadState = true;
    }
 
    if (epeverControlPayload["status"] == "off") {
      loadState = false;
    }
 
    Serial.print("Setting load state:");
    node.clearResponseBuffer();
    node.writeSingleCoil(0x0001, 1);
    result = node.writeSingleCoil(0x0002, loadState);
    if (result == node.ku8MBSuccess) {
      Serial.println("success");
    } else {
      Serial.println("failure");
      Serial.print("Miss write loadState, ret val: ");
      Serial.println(result, HEX);
    }
  }
 
  if (epeverControlPayload["action"] == "settings") {
    if (epeverControlPayload.containsKey("sleep")) {
      // input sanitization
      int seconds = (unsigned int)epeverControlPayload["sleep"];
      if (seconds == sleepSeconds) {
        Serial.println("no change");
        return;
      }
 
      if (seconds < MIN_SLEEP_SECONDS) {
        sleepSeconds = MIN_SLEEP_SECONDS;
      } else {
        sleepSeconds = seconds;
      }
 
      EEPROM.put(0, sleepSeconds);
      if (!EEPROM.commit()) {
        Serial.println("Failure setting sleep seconds.");
        return;
      }
 
      Serial.print("Set sleep seconds to: ");
      Serial.println(sleepSeconds);
    }
 
    epeverControlPayload.clear();
    return;
  }
}
 
///////////////////////////////////////////////////////////////////////////
//                           Arduino functions                           //
///////////////////////////////////////////////////////////////////////////
void setup() {
  Serial.begin(115200);  // DO NOT CHANGE!
  while (!Serial)
    ;
  Serial.println();
  Serial.println("Hello World! I'm an EpEver Solar Monitor!");
 
  // init modbus in receive mode
  pinMode(MAX485_RE, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  digitalWrite(MAX485_RE, 0);
  digitalWrite(MAX485_DE, 0);
 
  // modbus callbacks
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
  // EPEver Device ID 1
  node.begin(1, Serial);
 
  // Connect D0 to RST to wake up
#ifdef USE_DEEP_SLEEP
  pinMode(D0, WAKEUP_PULLUP);
#endif
 
  // variable handling
  EEPROM.begin(16);
  EEPROM.get(0, sleepSeconds);
  if (sleepSeconds < MIN_SLEEP_SECONDS) {
    sleepSeconds = 60;
    EEPROM.put(0, sleepSeconds);
    if (!EEPROM.commit()) {
      Serial.println("Unable to set default sleep.");
    }
  }
 
  // Initialize the LED_BUILTIN pin as an output, low active
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
}
 
void loop() {
  uint8_t i, result;
 
  // Turn on LED
  digitalWrite(LED_BUILTIN, HIGH);
 
  // Clear old data
  memset(rtc.buf, 0, sizeof(rtc.buf));
  memset(live.buf, 0, sizeof(live.buf));
  memset(stats.buf, 0, sizeof(stats.buf));
 
  // Read registers for clock
  node.clearResponseBuffer();
  result = node.readHoldingRegisters(RTC_CLOCK, RTC_CLOCK_CNT);
  if (result == node.ku8MBSuccess) {
    rtc.buf[0] = node.getResponseBuffer(0);
    rtc.buf[1] = node.getResponseBuffer(1);
    rtc.buf[2] = node.getResponseBuffer(2);
 
  } else {
    Serial.print("Miss read rtc-data, ret val:");
    Serial.println(result, HEX);
  }
 
  // Read LIVE-Data
  node.clearResponseBuffer();
  result = node.readInputRegisters(LIVE_DATA, LIVE_DATA_CNT);
 
  if (result == node.ku8MBSuccess) {
 
    for (i = 0; i < LIVE_DATA_CNT; i++) live.buf[i] = node.getResponseBuffer(i);
 
  } else {
    Serial.print("Miss read liva-data, ret val:");
    Serial.println(result, HEX);
  }
 
  // Statistical Data
  node.clearResponseBuffer();
  result = node.readInputRegisters(STATISTICS, STATISTICS_CNT);
  if (result == node.ku8MBSuccess) {
    for (i = 0; i < STATISTICS_CNT; i++) {
      stats.buf[i] = node.getResponseBuffer(i);
    }
  } else {
    Serial.print("Miss read statistics, ret val:");
    Serial.println(result, HEX);
  }
 
  // Battery SOC
  node.clearResponseBuffer();
  result = node.readInputRegisters(BATTERY_SOC, 1);
  if (result == node.ku8MBSuccess) {
    batterySOC = node.getResponseBuffer(0);
  } else {
    Serial.print("Miss read batterySOC, ret val:");
    Serial.println(result, HEX);
  }
 
  // Battery Net Current = Icharge - Iload
  node.clearResponseBuffer();
  result = node.readInputRegisters(BATTERY_CURRENT_L, 2);
  if (result == node.ku8MBSuccess) {
    batteryCurrent = node.getResponseBuffer(0);
    batteryCurrent |= node.getResponseBuffer(1) << 16;
  } else {
    Serial.print("Miss read batteryCurrent, ret val:");
    Serial.println(result, HEX);
  }
 
  // State of the Load Switch
  node.clearResponseBuffer();
  result = node.readCoils(LOAD_STATE, 1);
  if (result == node.ku8MBSuccess) {
    loadState = node.getResponseBuffer(0);
  } else {
    Serial.print("Miss read loadState, ret val:");
    Serial.println(result, HEX);
  }
 
  // Read Status Flags
  node.clearResponseBuffer();
  result = node.readInputRegisters(0x3200, 2);
  if (result == node.ku8MBSuccess) {
    uint16_t temp = node.getResponseBuffer(0);
    Serial.print("Batt Flags : ");
    Serial.println(temp);
 
    status_batt.volt = temp & 0b1111;
    status_batt.temp = (temp >> 4) & 0b1111;
    status_batt.resistance = (temp >> 8) & 0b1;
    status_batt.rated_volt = (temp >> 15) & 0b1;
 
    temp = node.getResponseBuffer(1);
    Serial.print("Chrg Flags : ");
    Serial.println(temp, HEX);
 
    //for(i=0; i<16; i++) Serial.print( (temp >> (15-i) ) & 1 );
    //Serial.println();
 
    //charger_input     = ( temp & 0b0000000000000000 ) >> 15 ;
    charger_mode = (temp & 0b0000000000001100) >> 2;
    //charger_input     = ( temp & 0b0000000000000000 ) >> 12 ;
    //charger_operation = ( temp & 0b0000000000000000 ) >> 0 ;
 
    //Serial.print( "charger_input : "); Serial.println( charger_input );
    Serial.print("charger_mode  : ");
    Serial.println(charger_mode);
    //Serial.print( "charger_oper  : "); Serial.println( charger_operation );
    //Serial.print( "charger_state : "); Serial.println( charger_state );
  } else {
    Serial.print("Miss read ChargeState, ret val:");
    Serial.println(result, HEX);
  }
 
  // Print out to serial
#ifdef SERIAL_DATA
  Serial.printf("\n\nTime:  20%02d-%02d-%02d   %02d:%02d:%02d   \n", rtc.r.y, rtc.r.M, rtc.r.d, rtc.r.h, rtc.r.m, rtc.r.s);
  Serial.print("\nLive-Data:           Volt        Amp       Watt  ");
  Serial.printf("\n  Panel:            %7.3f    %7.3f    %7.3f ", live.l.pV / 100.f, live.l.pI / 100.f, live.l.pP / 100.0f);
  Serial.printf("\n  Batt:             %7.3f    %7.3f    %7.3f ", live.l.bV / 100.f, live.l.bI / 100.f, live.l.bP / 100.0f);
  Serial.printf("\n  Load:             %7.3f    %7.3f    %7.3f ", live.l.lV / 100.f, live.l.lI / 100.f, live.l.lP / 100.0f);
  Serial.println();
  Serial.printf("\n  Battery Current:  %7.3f  A ", batteryCurrent / 100.f);
  Serial.printf("\n  Battery SOC:      %7.0f  %% ", batterySOC / 1.0f);
  Serial.printf("\n  Load Switch:          %s   ", (loadState == 1 ? " On" : "Off"));
  Serial.print("\n\nStatistics:  ");
  Serial.printf("\n  Panel:       min: %7.3f   max: %7.3f   V", stats.s.pVmin / 100.f, stats.s.pVmax / 100.f);
  Serial.printf("\n  Battery:     min: %7.3f   max: %7.3f   V", stats.s.bVmin / 100.f, stats.s.bVmax / 100.f);
  Serial.println();
  Serial.printf("\n  Consumed:    day: %7.3f   mon: %7.3f   year: %7.3f  total: %7.3f   kWh",
                stats.s.consEnerDay / 100.f, stats.s.consEnerMon / 100.f, stats.s.consEnerYear / 100.f, stats.s.consEnerTotal / 100.f);
  Serial.printf("\n  Generated:   day: %7.3f   mon: %7.3f   year: %7.3f  total: %7.3f   kWh",
                stats.s.genEnerDay / 100.f, stats.s.genEnerMon / 100.f, stats.s.genEnerYear / 100.f, stats.s.genEnerTotal / 100.f);
  Serial.printf("\n  CO2-Reduction:    %7.3f  t ", stats.s.c02Reduction / 100.f);
  Serial.println();
  Serial.print("\nStatus:");
  Serial.printf("\n    batt.volt:         %s   ", batt_volt_status[status_batt.volt]);
  Serial.printf("\n    batt.temp:         %s   ", batt_temp_status[status_batt.temp]);
  Serial.printf("\n    charger.charging:  %s   ", charger_charging_status[charger_mode]);
  Serial.println();
  Serial.println();
#endif
 
  // Start WiFi connection.
  digitalWrite(LED_BUILTIN, LOW);
  WiFi.mode(WIFI_STA);
  WiFi.begin(STA_SSID, STA_PASSWORD);
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }
 
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
 
  // Port defaults to 8266
#ifdef OTA_PORT
  ArduinoOTA.setPort(OTA_PORT);
#endif
 
  // Hostname defaults to esp8266-[ChipID]
#ifdef OTA_HOSTNAME
  if (strlen(OTA_HOSTNAME) != 0) {
    ArduinoOTA.setHostname(OTA_HOSTNAME);
  }
#endif
 
  // No authentication by default
#ifdef OTA_PASSWORD_HASH
  if (strlen(OTA_PASSWORD_HASH) != 0) {
    ArduinoOTA.setPasswordHash(OTA_PASSWORD_HASH);
  }
#endif
 
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else {  // U_FS
      type = "filesystem";
    }
 
    // NOTE: if updating FS this would be the place to unmount FS using FS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
  });
  ArduinoOTA.begin();
 
  // Establish/keep mqtt connection
  mqtt_client.setServer(MQTT_SERVER, MQTT_PORT);
  mqtt_client.setCallback(mqtt_callback);
  mqtt_reconnect();
  digitalWrite(LED_BUILTIN, HIGH);
 
  // Once connected, publish an announcement.
  controllerStatusPayload["solar"]["monitor"]["status"] = "online";
  serializeJson(controllerStatusPayload, tmpJsonPayloadBuffer);
  controllerStatusPayload.clear();
  mqtt_client.publish((String(MQTT_TOPIC_PUB) + "/" + "status").c_str(), tmpJsonPayloadBuffer);
 
  controllerStatusPayload["solar"]["monitor"]["status"] = "waiting";
  serializeJson(controllerStatusPayload, tmpJsonPayloadBuffer);
  controllerStatusPayload.clear();
  mqtt_client.publish((String(MQTT_TOPIC_PUB) + "/" + "status").c_str(), tmpJsonPayloadBuffer);
 
  // Wait for MQTT subscription processing
  Serial.println("Waiting for MQTT and OTA events.");
  unsigned int now = millis();
  while (millis() - now < MQTT_SUBSCRIBE_WAIT * 1000) {
    // Loop for MQTT.
    if (!mqtt_client.loop() || WiFi.status() != WL_CONNECTED) {
      break;
    }
    // Loop for OTA.
    ArduinoOTA.handle();
    delay(100);
  }
  Serial.println("Done waiting for MQTT and OTA events.");
 
  // Publish to MQTT
  Serial.print("Publishing to MQTT: ");
 
  // Panel
  epeverMetricsPayload["solar"]["panel"]["V"] = String(live.l.pV / 100.f, 2);
  epeverMetricsPayload["solar"]["panel"]["I"] = String(live.l.pI / 100.f, 2);
  epeverMetricsPayload["solar"]["panel"]["P"] = String(live.l.pP / 100.f, 2);
  epeverMetricsPayload["solar"]["panel"]["minV"] = String(stats.s.pVmin / 100.f, 3);
  epeverMetricsPayload["solar"]["panel"]["maxV"] = String(stats.s.pVmax / 100.f, 3);
 
  serializeJson(epeverMetricsPayload, tmpJsonPayloadBuffer);
  epeverMetricsPayload.clear();
  mqtt_client.publish((String(MQTT_TOPIC_PUB) + "/" + "panel").c_str(), tmpJsonPayloadBuffer);
 
  // Battery
  epeverMetricsPayload["solar"]["battery"]["V"] = String(live.l.bV / 100.f, 2);
  epeverMetricsPayload["solar"]["battery"]["I"] = String(live.l.bI / 100.f, 2);
  epeverMetricsPayload["solar"]["battery"]["P"] = String(live.l.bP / 100.f, 2);
  epeverMetricsPayload["solar"]["battery"]["minV"] = String(stats.s.bVmin / 100.f, 2);
  epeverMetricsPayload["solar"]["battery"]["maxV"] = String(stats.s.bVmax / 100.f, 2);
  epeverMetricsPayload["solar"]["battery"]["SOC"] = String(batterySOC / 1.0f, 2);
  epeverMetricsPayload["solar"]["battery"]["netI"] = String(batteryCurrent / 100.0f, 2);
  epeverMetricsPayload["solar"]["battery"]["status"]["voltage"].set(batt_volt_status[status_batt.volt]);
  epeverMetricsPayload["solar"]["battery"]["status"]["temperature"].set(batt_temp_status[status_batt.temp]);
 
  serializeJson(epeverMetricsPayload, tmpJsonPayloadBuffer);
  epeverMetricsPayload.clear();
  mqtt_client.publish((String(MQTT_TOPIC_PUB) + "/" + "battery").c_str(), tmpJsonPayloadBuffer);
 
  // Load
  epeverMetricsPayload["solar"]["load"]["V"] = String(live.l.lV / 100.f, 2);
  epeverMetricsPayload["solar"]["load"]["I"] = String(live.l.lI / 100.f, 2);
  epeverMetricsPayload["solar"]["load"]["P"] = String(live.l.lP / 100.f, 2);
  // pimatic state topic does not work with integers or floats ?!?
  switch (loadState) {
    case 1:
      epeverMetricsPayload["solar"]["load"]["state"].set("on");
      break;
    default:
      epeverMetricsPayload["solar"]["load"]["state"].set("off");
      break;
  }
 
  serializeJson(epeverMetricsPayload, tmpJsonPayloadBuffer);
  epeverMetricsPayload.clear();
  mqtt_client.publish((String(MQTT_TOPIC_PUB) + "/" + "load").c_str(), tmpJsonPayloadBuffer);
 
  // Energy
  epeverMetricsPayload["solar"]["energy"]["consumed_day"] = String(stats.s.consEnerDay / 100.f, 3);
  epeverMetricsPayload["solar"]["energy"]["consumed_all"] = String(stats.s.consEnerTotal / 100.f, 3);
  epeverMetricsPayload["solar"]["energy"]["generated_day"] = String(stats.s.genEnerDay / 100.f, 3);
  epeverMetricsPayload["solar"]["energy"]["generated_all"] = String(stats.s.genEnerTotal / 100.f, 3);
 
  serializeJson(epeverMetricsPayload, tmpJsonPayloadBuffer);
  epeverMetricsPayload.clear();
  mqtt_client.publish((String(MQTT_TOPIC_PUB) + "/" + "energy").c_str(), tmpJsonPayloadBuffer);
 
  // Extra
  epeverMetricsPayload["solar"]["extra"]["CO2"]["t"] = String(stats.s.c02Reduction / 100.f, 2);
  //epever_serialize_s( "solar/status/charger_input", charger_input_status[ charger_input ]
  epeverMetricsPayload["solar"]["extra"]["charger_mode"] = charger_charging_status[charger_mode];
  char buf[21];
  sprintf(buf, "20%02d-%02d-%02d %02d:%02d:%02d",
          rtc.r.y, rtc.r.M, rtc.r.d, rtc.r.h, rtc.r.m, rtc.r.s);
  epeverMetricsPayload["solar"]["extra"]["time"] = buf;
 
  serializeJson(epeverMetricsPayload, tmpJsonPayloadBuffer);
  epeverMetricsPayload.clear();
  mqtt_client.publish((String(MQTT_TOPIC_PUB) + "/" + "extra").c_str(), tmpJsonPayloadBuffer);
 
  // Settings
  epeverMetricsPayload["solar"]["monitor"]["settings"]["sleep"].set(sleepSeconds);
 
  serializeJson(epeverMetricsPayload, tmpJsonPayloadBuffer);
  epeverMetricsPayload.clear();
  mqtt_client.publish((String(MQTT_TOPIC_PUB) + "/" + "settings").c_str(), tmpJsonPayloadBuffer);
 
  Serial.println("done");
 
  controllerStatusPayload["solar"]["monitor"]["status"] = "offline";
  serializeJson(controllerStatusPayload, tmpJsonPayloadBuffer);
  controllerStatusPayload.clear();
  mqtt_client.publish((String(MQTT_TOPIC_PUB) + "/" + "status").c_str(), tmpJsonPayloadBuffer);
 
  // Ensure all messages are sent
  mqtt_client.unsubscribe(MQTT_TOPIC_SUB);
  mqtt_client.disconnect();
  while (mqtt_client.state() != -1) {
    delay(100);
  }
 
  // disconnect wifi
  WiFi.disconnect(true);
 
  // power down MAX485_DE
  // low active
  digitalWrite(MAX485_RE, 0);
  digitalWrite(MAX485_DE, 0);
 
  // Sleep
  Serial.print("\nSleep for ");
  Serial.print(sleepSeconds);
  Serial.println(" Seconds");
  digitalWrite(LED_BUILTIN, LOW);
#ifdef USE_DEEP_SLEEP
  if (USE_DEEP_SLEEP) {
    ESP.deepSleep(sleepSeconds * 1000000);
  } else {
    delay(sleepSeconds * 1000);
  }
#else
  delay(sleepSeconds * 1000);
#endif
}

With the code uploaded to the ESP the WeMoS can be plugged back into the socket and when the Ethernet cable is connected the ESP should start transmitting the data to an MQTT broker. Note that it is entirely possible to have both the monitor that was designed connected to the Epever solar controller via Ethernet and concomitantly connect to the WeMoS board via USB to a computer in order to observe the data on the serial port or re-program the ESP.

WeMoS

Note that when flashing the sketch to the WeMoS, more than likely you need to set the board type to:

  • LOLIN(WEMOS) D1 Mini (clone)

or the sketch will be unable to memorize settings and errors might be printed out on the serial console mentioning that the EEPROM could not be written.

Data

The following section enumerates the data sent by the code. The data is serialized to JSON with two decimals for floating point values yet converted to strings to work around Arduino limitations. All the data is transmitted on various MQTT sub-topics of MQTT_TOPIC_PUB:

  • panel - the solar panel metrics,
  • battery - the battery metrics,
  • load - the current load,
  • energy - some energy related data,
  • extra - additional data,
  • monitor - data pertaining to the monitor itself (this device described in this document)

For instance, just for testing, to subscribe to all sub-topics and assuming that MQTT_TOPIC_PUB is the default epever-40a/talk, then mosquitto_sub can be issued as follows:

mosquitto_sub -t 'epever-40a/talk/+' -h localhost

The following sections show some example data transmitted on the topics as section titles.

panel

{
   "solar":{
      "panel":{
         "V":"0.15",
         "I":"0.00",
         "P":"0.00",
         "minV":"0.120",
         "maxV":"21.390"
      }
   }
}

battery

{
   "solar":{
      "battery":{
         "V":"13.12",
         "I":"0.00",
         "P":"0.00",
         "minV":"11.20",
         "maxV":"14.30",
         "SOC":"73.00",
         "netI":"-0.67",
         "status":{
            "voltage":"Normal",
            "temperature":"Normal"
         }
      }
   }
}

load

{
   "solar":{
      "load":{
         "V":"13.12",
         "I":"0.59",
         "P":"7.74",
         "state":"on"
      }
   }
}

energy

{
   "solar":{
      "energy":{
         "consumed_day":"0.220",
         "consumed_all":"0.730",
         "generated_day":"0.210",
         "generated_all":"1.070"
      }
   }
}

extra

{
   "solar":{
      "extra":{
         "CO2":{
            "t":"0.00"
         },
         "charger_mode":"Off",
         "time":"2013-01-06 17:13:57"
      }
   }
}

monitor

{
   "solar":{
      "monitor":{
         "settings":{
            "sleep":60
         }
      }
   }
}

Node-Red

Here is a possible dashboard that processed the data sent by the monitor:

The dashboard shows the battery and panel base metrics such as voltage, current and wattage, most of these being pulled directly from the JSON objects that the monitor published to the MQTT broker.

Perhaps the most interesting to see here are the metrics displayed in the Efficiency section. One such metric is the Input vs Output (A) that is the representation of the data pulled from the battery MQTT subtopic and of the sub-property netI. netI of the JSON object that can be described as a measure of amperes flowing through the Epever solar controller and can quickly show whether the solar array setup is consuming, respectively generating energy depending on whether the netI value is negative, respectively positive. In the image above, the value seems to be negative, namely $-0.66A$ meaning that the whole setup is, in fact, consuming half an Ampere which seems to be correct given that the image was taken during night time when the consumers rely on the battery to sustain themselves.

Another interesting metric is the Generated Power (W) vs. Sun Elevation (deg) because it can be used to debug the solar array setup conveniently.

The chart plots two series, the current power output of the solar panels (in green) and the current elevation of the sun above or below the horizon (orange).

Looking at the chart it is interesting to devise why the most power being generated takes place at around 4pm and it is also somewhat possible to determine the orientation of the solar panels themselves. It is interesting, but the data does seem to match perfectly with the solar panel orientation relative to the sun during the day. We used static solar panels, not motorized ones, and given their static orientation, as well as the trajectory of the sun, it seems obvious that the most power would be generated during the afternoon. In any case, it must be said that power generation and storage is cumulative such that one would need to integrate the area under the Watt curve in order to determine the total amount of power generated over the whole day.

The Node-Red flow in the following section includes some exotic nodes such as:

  • node-red-contrib-redis; we have found Redis to be an indispensable storage medium that is highly compatible with JavaScript due to its key-value design as well as providing a less bloated storage option compared to a database,
  • node-red-contrib-sun-position is a cool and documented module by RedMatic/Hypnos3 that provides a very convenient way of retrieving the current sun elevation amongst other statistics pertaining to the sun and moon,

Flow

[
    {
        "id": "0c71d7c156291f81",
        "type": "tab",
        "label": "Solar",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "43316a9d83a2d1c5",
        "type": "group",
        "z": "0c71d7c156291f81",
        "name": "MQTT Connectivity",
        "style": {
            "label": true
        },
        "nodes": [
            "31ca1bd362c9920a",
            "218e7afa7eb97cfa",
            "af8ae684feca6f96",
            "32a15dec7972474c",
            "3c16b5cc86a7573a"
        ],
        "x": 54,
        "y": 39,
        "w": 362,
        "h": 222
    },
    {
        "id": "188827854f36f6f9",
        "type": "group",
        "z": "0c71d7c156291f81",
        "name": "Settings",
        "style": {
            "label": true
        },
        "nodes": [
            "1c058b1f8278dd24",
            "3082948c1eb929bb",
            "398337a8d08d5f50",
            "45d0c9fdf8dfecc0",
            "281b7fd3a6a5e952",
            "44b5b1d6bf1afc9a",
            "24a9ee0682777cec",
            "7ed8396faf65527e",
            "5478e53386979ee2",
            "68f6ab3a62d4cb6d",
            "81dd9a5a0c11ea14",
            "27ba80a1376a5657",
            "d43257e89b9c665d",
            "a21701f9a23ef74e",
            "fd38855634aadc66",
            "a97c99dc848b1cd9",
            "d2ebc4a6205d1a4a",
            "fd8ab747371d8127",
            "3b088c8c7757c298",
            "9957edec9bbf0f74",
            "0b1cee2e609ad711",
            "368064192294170f",
            "b6bf5fdadf2f16d6",
            "07e3be91304ca577",
            "fde241b8790dff11"
        ],
        "x": 54,
        "y": 479,
        "w": 932,
        "h": 722
    },
    {
        "id": "41e0ca36ab088b06",
        "type": "group",
        "z": "0c71d7c156291f81",
        "name": "Controller Load Switch",
        "style": {
            "label": true
        },
        "nodes": [
            "636fe34fd10e61fd",
            "53202812f5b7e8b9",
            "959b6470f91360a8",
            "92bf127bf074c713",
            "192208e68d9a2f71",
            "4e78e565c335317b",
            "5db19d2fe731de9c",
            "d978f144c26b9951",
            "f5b989e1ddc46713",
            "53db38d1a30af8e3",
            "7f744125b78bd5f6",
            "bba8c0155dd56001",
            "ebc601c4127fd4b0",
            "7dc8cba55f4ea2a6",
            "792e8351c67337e8",
            "f4d7de6e35cc6689",
            "1dc82ed439f5e633",
            "e0de4137375fbe1c",
            "25514aa57a06bbd8",
            "468feb011ff38f89",
            "3d0feb3c85f758cd",
            "956b7f9e4ed8e254",
            "0dfccc5fdfc84e44",
            "4c07e0cc9184dcc3",
            "3dfb7cc559902c0d",
            "6425e623d113f38c",
            "32ba210ab2a02102",
            "e56cdda4d749deb5",
            "8efc7748f3d5fed1",
            "c9279b012026601d",
            "5cd229f70b7338e4"
        ],
        "x": 54,
        "y": 1239,
        "w": 1032,
        "h": 882
    },
    {
        "id": "0368dad51c195288",
        "type": "group",
        "z": "0c71d7c156291f81",
        "name": "Charts",
        "style": {
            "label": true
        },
        "nodes": [
            "98c62b275c81bb7e",
            "e57766d53a9f53e9",
            "0d5632e4d7efacee",
            "aea8a9041645a3a0",
            "3474a625ad2bc385",
            "b5c412a0d99fb957",
            "d1acc889618287d5",
            "0e51d432c6ad04e9",
            "592aa764042cc054",
            "e72e66e0f6902494",
            "548aed35d5b92d3d",
            "6b0d431da58807f7",
            "9cf2a62852ee5b41",
            "ae0439d4c6d4ccfd",
            "d93c44240d8a293f",
            "111ac78c519436d3",
            "4ab1b149d413d1e7",
            "1def045df1f1b403",
            "05e80ca5efb3a704",
            "d61ea73077afddc2",
            "414ba84b4e8e7717",
            "bdc38b66f677cd45",
            "1a43cb7bb9cc7306",
            "88587a282917d5e0",
            "138582e5336ca242",
            "2d8e41bafc41e64f",
            "2d7139ac4dbc4d48",
            "fdc8a434f2a5814b",
            "cf1724a9366a7d4a",
            "2a976d1e050c1464"
        ],
        "x": 1054,
        "y": 79,
        "w": 1232,
        "h": 882
    },
    {
        "id": "f0a7056e4ff89ec9",
        "type": "group",
        "z": "0c71d7c156291f81",
        "name": "Solar Monitor Waiting Settings",
        "style": {
            "label": true
        },
        "nodes": [
            "2d9b1cea9886c2da",
            "c6358b504e71c7b6",
            "defcaaf74bdd754a",
            "68387caaab696257",
            "d8a9da5ca489a451",
            "817044b97ed1806f"
        ],
        "x": 54,
        "y": 299,
        "w": 592,
        "h": 142
    },
    {
        "id": "31ca1bd362c9920a",
        "type": "mqtt in",
        "z": "0c71d7c156291f81",
        "g": "43316a9d83a2d1c5",
        "name": "",
        "topic": "epever-40a/talk/+",
        "qos": "0",
        "datatype": "auto-detect",
        "broker": "725ed69c.6d76a8",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 180,
        "y": 160,
        "wires": [
            [
                "af8ae684feca6f96",
                "3c16b5cc86a7573a"
            ]
        ]
    },
    {
        "id": "98c62b275c81bb7e",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "debug 4",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1220,
        "y": 280,
        "wires": []
    },
    {
        "id": "e57766d53a9f53e9",
        "type": "json",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 1190,
        "y": 360,
        "wires": [
            [
                "4ab1b149d413d1e7",
                "98c62b275c81bb7e"
            ]
        ]
    },
    {
        "id": "218e7afa7eb97cfa",
        "type": "mqtt out",
        "z": "0c71d7c156291f81",
        "g": "43316a9d83a2d1c5",
        "name": "",
        "topic": "epever-40a/hear",
        "qos": "",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "725ed69c.6d76a8",
        "x": 240,
        "y": 220,
        "wires": []
    },
    {
        "id": "0d5632e4d7efacee",
        "type": "ui_chart",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "group": "0e0468a7bcaa4a7a",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Panel Voltage (V)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "86400",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#80ff80",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "x": 1790,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "aea8a9041645a3a0",
        "type": "ui_chart",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "group": "0e0468a7bcaa4a7a",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Panel Current (A)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "86400",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#80ff80",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "x": 1790,
        "y": 180,
        "wires": [
            [
                "cf1724a9366a7d4a"
            ]
        ]
    },
    {
        "id": "3474a625ad2bc385",
        "type": "ui_chart",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "group": "0e0468a7bcaa4a7a",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Panel Power (W)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "86400",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#80ff80",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "x": 1790,
        "y": 240,
        "wires": [
            []
        ]
    },
    {
        "id": "b5c412a0d99fb957",
        "type": "change",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.solar.panel.V",
                "tot": "msg",
                "dc": true
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1580,
        "y": 120,
        "wires": [
            [
                "0d5632e4d7efacee"
            ]
        ]
    },
    {
        "id": "d1acc889618287d5",
        "type": "change",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.solar.panel.I",
                "tot": "msg",
                "dc": true
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1580,
        "y": 180,
        "wires": [
            [
                "aea8a9041645a3a0"
            ]
        ]
    },
    {
        "id": "0e51d432c6ad04e9",
        "type": "change",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.solar.panel.P",
                "tot": "msg",
                "dc": true
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1580,
        "y": 240,
        "wires": [
            [
                "3474a625ad2bc385",
                "fdc8a434f2a5814b"
            ]
        ]
    },
    {
        "id": "1c058b1f8278dd24",
        "type": "ui_slider",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "",
        "label": "Update Frequency (s)",
        "tooltip": "",
        "group": "9e0ac0fcf4e571a8",
        "order": 4,
        "width": 0,
        "height": 0,
        "passthru": false,
        "outs": "end",
        "topic": "topic",
        "topicType": "msg",
        "min": "60",
        "max": "300",
        "step": 1,
        "x": 200,
        "y": 640,
        "wires": [
            [
                "45d0c9fdf8dfecc0"
            ]
        ]
    },
    {
        "id": "af8ae684feca6f96",
        "type": "link out",
        "z": "0c71d7c156291f81",
        "g": "43316a9d83a2d1c5",
        "name": "link out 1",
        "mode": "link",
        "links": [
            "592aa764042cc054",
            "2d9b1cea9886c2da",
            "f5b989e1ddc46713",
            "d43257e89b9c665d"
        ],
        "x": 295,
        "y": 160,
        "wires": []
    },
    {
        "id": "592aa764042cc054",
        "type": "link in",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "link in 1",
        "links": [
            "af8ae684feca6f96"
        ],
        "x": 1095,
        "y": 360,
        "wires": [
            [
                "e57766d53a9f53e9"
            ]
        ]
    },
    {
        "id": "32a15dec7972474c",
        "type": "link in",
        "z": "0c71d7c156291f81",
        "g": "43316a9d83a2d1c5",
        "name": "link in 3",
        "links": [
            "3082948c1eb929bb",
            "fb6e1142d67d70b6",
            "956b7f9e4ed8e254"
        ],
        "x": 95,
        "y": 220,
        "wires": [
            [
                "218e7afa7eb97cfa"
            ]
        ]
    },
    {
        "id": "3082948c1eb929bb",
        "type": "link out",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "link out 2",
        "mode": "link",
        "links": [
            "32a15dec7972474c"
        ],
        "x": 585,
        "y": 1040,
        "wires": []
    },
    {
        "id": "398337a8d08d5f50",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "debug 5",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 680,
        "y": 1100,
        "wires": []
    },
    {
        "id": "45d0c9fdf8dfecc0",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "function 2",
        "func": "let value = JSON.stringify(\n    {\n        \"action\" : \"settings\",\n        \"sleep\" : msg.payload\n    }\n)\nmsg = {}\nmsg.payload = [\"solar/settings\", value ]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 400,
        "y": 640,
        "wires": [
            [
                "281b7fd3a6a5e952"
            ]
        ]
    },
    {
        "id": "281b7fd3a6a5e952",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "server": "a37a41f9.a98da8",
        "command": "SET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 560,
        "y": 640,
        "wires": [
            [
                "07e3be91304ca577"
            ]
        ]
    },
    {
        "id": "44b5b1d6bf1afc9a",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "server": "a37a41f9.a98da8",
        "command": "GET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 220,
        "y": 1040,
        "wires": [
            [
                "5478e53386979ee2",
                "fd8ab747371d8127"
            ]
        ]
    },
    {
        "id": "24a9ee0682777cec",
        "type": "inject",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "init",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[ \"solar/settings\" ]",
        "payloadType": "json",
        "x": 150,
        "y": 520,
        "wires": [
            [
                "7ed8396faf65527e"
            ]
        ]
    },
    {
        "id": "7ed8396faf65527e",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "server": "a37a41f9.a98da8",
        "command": "GET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 300,
        "y": 520,
        "wires": [
            [
                "68f6ab3a62d4cb6d"
            ]
        ]
    },
    {
        "id": "5478e53386979ee2",
        "type": "json",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "",
        "property": "payload",
        "action": "str",
        "pretty": false,
        "x": 490,
        "y": 1040,
        "wires": [
            [
                "3082948c1eb929bb"
            ]
        ]
    },
    {
        "id": "68f6ab3a62d4cb6d",
        "type": "json",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 450,
        "y": 520,
        "wires": [
            [
                "81dd9a5a0c11ea14",
                "d2ebc4a6205d1a4a"
            ]
        ]
    },
    {
        "id": "81dd9a5a0c11ea14",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "function 4",
        "func": "msg.payload = msg.payload.sleep;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 600,
        "y": 520,
        "wires": [
            [
                "1c058b1f8278dd24"
            ]
        ]
    },
    {
        "id": "e72e66e0f6902494",
        "type": "ui_chart",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "group": "6ef616c0de00894f",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Battery Voltage (V)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "86400",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "x": 1810,
        "y": 500,
        "wires": [
            []
        ]
    },
    {
        "id": "548aed35d5b92d3d",
        "type": "ui_chart",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "group": "6ef616c0de00894f",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Battery Current (A)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "86400",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "x": 1810,
        "y": 560,
        "wires": [
            []
        ]
    },
    {
        "id": "6b0d431da58807f7",
        "type": "ui_chart",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "group": "6ef616c0de00894f",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Battery Power (W)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "86400",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "x": 1810,
        "y": 620,
        "wires": [
            []
        ]
    },
    {
        "id": "9cf2a62852ee5b41",
        "type": "change",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.solar.battery.V",
                "tot": "msg",
                "dc": true
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1600,
        "y": 500,
        "wires": [
            [
                "e72e66e0f6902494",
                "111ac78c519436d3"
            ]
        ]
    },
    {
        "id": "ae0439d4c6d4ccfd",
        "type": "change",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.solar.battery.I",
                "tot": "msg",
                "dc": true
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1600,
        "y": 560,
        "wires": [
            [
                "548aed35d5b92d3d"
            ]
        ]
    },
    {
        "id": "d93c44240d8a293f",
        "type": "change",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.solar.battery.P",
                "tot": "msg",
                "dc": true
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1600,
        "y": 620,
        "wires": [
            [
                "6b0d431da58807f7"
            ]
        ]
    },
    {
        "id": "111ac78c519436d3",
        "type": "ui_gauge",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "group": "6ef616c0de00894f",
        "order": 3,
        "width": 0,
        "height": 0,
        "gtype": "gage",
        "title": "Battery Voltage (V)",
        "label": "V",
        "format": "{{value}}",
        "min": "11",
        "max": "14",
        "colors": [
            "#ff0000",
            "#e6e600",
            "#80ff00"
        ],
        "seg1": "",
        "seg2": "",
        "x": 1810,
        "y": 680,
        "wires": []
    },
    {
        "id": "4ab1b149d413d1e7",
        "type": "switch",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "hask",
                "v": "solar",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 1330,
        "y": 360,
        "wires": [
            [
                "1def045df1f1b403",
                "05e80ca5efb3a704"
            ],
            []
        ]
    },
    {
        "id": "3c16b5cc86a7573a",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "43316a9d83a2d1c5",
        "name": "debug 8",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 310,
        "y": 80,
        "wires": []
    },
    {
        "id": "1def045df1f1b403",
        "type": "switch",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "property": "payload.solar",
        "propertyType": "msg",
        "rules": [
            {
                "t": "hask",
                "v": "battery",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 1430,
        "y": 560,
        "wires": [
            [
                "9cf2a62852ee5b41",
                "ae0439d4c6d4ccfd",
                "d93c44240d8a293f",
                "414ba84b4e8e7717"
            ],
            []
        ]
    },
    {
        "id": "05e80ca5efb3a704",
        "type": "switch",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "property": "payload.solar",
        "propertyType": "msg",
        "rules": [
            {
                "t": "hask",
                "v": "panel",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 1410,
        "y": 180,
        "wires": [
            [
                "b5c412a0d99fb957",
                "d1acc889618287d5",
                "0e51d432c6ad04e9"
            ],
            []
        ]
    },
    {
        "id": "d61ea73077afddc2",
        "type": "ui_gauge",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "group": "56ef03ec967ddd2d",
        "order": 4,
        "width": 0,
        "height": 0,
        "gtype": "gage",
        "title": "Input vs. Output (A)",
        "label": "A",
        "format": "{{value}}",
        "min": "-10",
        "max": 10,
        "colors": [
            "#ff0000",
            "#e6e600",
            "#80ff00"
        ],
        "seg1": "",
        "seg2": "",
        "x": 1810,
        "y": 740,
        "wires": []
    },
    {
        "id": "414ba84b4e8e7717",
        "type": "change",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.solar.battery.netI",
                "tot": "msg",
                "dc": true
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1600,
        "y": 740,
        "wires": [
            [
                "d61ea73077afddc2"
            ]
        ]
    },
    {
        "id": "bdc38b66f677cd45",
        "type": "sun-position",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "positionConfig": "9fa8b179879daba5",
        "rules": [],
        "onlyOnChange": "true",
        "topic": "",
        "outputs": 1,
        "start": "",
        "startType": "none",
        "startOffset": 0,
        "startOffsetType": "none",
        "startOffsetMultiplier": 60000,
        "end": "",
        "endType": "none",
        "endOffset": 0,
        "endOffsetType": "none",
        "endOffsetMultiplier": 60000,
        "x": 1450,
        "y": 860,
        "wires": [
            [
                "2d8e41bafc41e64f"
            ]
        ]
    },
    {
        "id": "1a43cb7bb9cc7306",
        "type": "ui_chart",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "group": "56ef03ec967ddd2d",
        "order": 5,
        "width": 0,
        "height": 0,
        "label": "Generated Power (W) vs. Sun Elevation (deg)",
        "chartType": "line",
        "legend": "true",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "86400",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#11ff00",
            "#f59a19",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "x": 2080,
        "y": 860,
        "wires": [
            []
        ]
    },
    {
        "id": "88587a282917d5e0",
        "type": "change",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "watt",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1790,
        "y": 920,
        "wires": [
            [
                "1a43cb7bb9cc7306"
            ]
        ]
    },
    {
        "id": "138582e5336ca242",
        "type": "change",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "elevation",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1790,
        "y": 860,
        "wires": [
            [
                "1a43cb7bb9cc7306"
            ]
        ]
    },
    {
        "id": "2d8e41bafc41e64f",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "function 5",
        "func": "msg.payload = msg.payload.altitude\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1620,
        "y": 860,
        "wires": [
            [
                "138582e5336ca242"
            ]
        ]
    },
    {
        "id": "2d7139ac4dbc4d48",
        "type": "link in",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "link in 4",
        "links": [
            "fdc8a434f2a5814b"
        ],
        "x": 1335,
        "y": 860,
        "wires": [
            [
                "bdc38b66f677cd45",
                "88587a282917d5e0"
            ]
        ]
    },
    {
        "id": "fdc8a434f2a5814b",
        "type": "link out",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "link out 3",
        "mode": "link",
        "links": [
            "2d7139ac4dbc4d48"
        ],
        "x": 1725,
        "y": 300,
        "wires": []
    },
    {
        "id": "cf1724a9366a7d4a",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "debug 9",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 2020,
        "y": 180,
        "wires": []
    },
    {
        "id": "2a976d1e050c1464",
        "type": "inject",
        "z": "0c71d7c156291f81",
        "g": "0368dad51c195288",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[]",
        "payloadType": "jsonata",
        "x": 1990,
        "y": 800,
        "wires": [
            [
                "1a43cb7bb9cc7306"
            ]
        ]
    },
    {
        "id": "636fe34fd10e61fd",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "debug 10",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 800,
        "y": 1400,
        "wires": []
    },
    {
        "id": "53202812f5b7e8b9",
        "type": "ui_switch",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "",
        "label": "Controller Load",
        "tooltip": "",
        "group": "9e0ac0fcf4e571a8",
        "order": 4,
        "width": 0,
        "height": 0,
        "passthru": false,
        "decouple": "true",
        "topic": "topic",
        "topicType": "msg",
        "style": "",
        "onvalue": "true",
        "onvalueType": "bool",
        "onicon": "",
        "oncolor": "",
        "offvalue": "false",
        "offvalueType": "bool",
        "officon": "",
        "offcolor": "",
        "animate": false,
        "x": 160,
        "y": 1480,
        "wires": [
            [
                "959b6470f91360a8"
            ]
        ]
    },
    {
        "id": "959b6470f91360a8",
        "type": "switch",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "true"
            },
            {
                "t": "false"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 330,
        "y": 1480,
        "wires": [
            [
                "bba8c0155dd56001"
            ],
            [
                "92bf127bf074c713"
            ],
            []
        ]
    },
    {
        "id": "92bf127bf074c713",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "Off",
        "func": "let value = JSON.stringify(\n    {\n        \"action\": \"switch\",\n        \"state\": \"off\"\n    }\n)\nmsg = {}\nmsg.payload = [\"solar/load\", value]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 490,
        "y": 1520,
        "wires": [
            [
                "f4d7de6e35cc6689"
            ]
        ]
    },
    {
        "id": "192208e68d9a2f71",
        "type": "inject",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 240,
        "y": 1600,
        "wires": [
            [
                "92bf127bf074c713"
            ]
        ]
    },
    {
        "id": "4e78e565c335317b",
        "type": "inject",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 240,
        "y": 1380,
        "wires": [
            []
        ]
    },
    {
        "id": "5db19d2fe731de9c",
        "type": "switch",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "",
        "property": "payload.solar.load",
        "propertyType": "msg",
        "rules": [
            {
                "t": "hask",
                "v": "state",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 350,
        "y": 1680,
        "wires": [
            [
                "53db38d1a30af8e3",
                "7f744125b78bd5f6"
            ],
            []
        ]
    },
    {
        "id": "d978f144c26b9951",
        "type": "json",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 210,
        "y": 1680,
        "wires": [
            [
                "5db19d2fe731de9c"
            ]
        ]
    },
    {
        "id": "f5b989e1ddc46713",
        "type": "link in",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "link in 5",
        "links": [
            "af8ae684feca6f96"
        ],
        "x": 105,
        "y": 1680,
        "wires": [
            [
                "d978f144c26b9951"
            ]
        ]
    },
    {
        "id": "53db38d1a30af8e3",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "debug 12",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 500,
        "y": 1740,
        "wires": []
    },
    {
        "id": "7f744125b78bd5f6",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "function 6",
        "func": "let state = msg.payload.solar.load.state\nmsg = {}\nswitch (state) {\n    case \"on\":\n        msg.payload = true;\n    break;\n    case \"off\":\n        msg.payload = false;\n    break;\n}\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 500,
        "y": 1680,
        "wires": [
            [
                "53202812f5b7e8b9"
            ]
        ]
    },
    {
        "id": "bba8c0155dd56001",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "On",
        "func": "let value = JSON.stringify(\n    {\n        \"action\": \"switch\",\n        \"state\": \"on\"\n    }\n)\nmsg = {}\nmsg.payload = [\"solar/load\", value]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 490,
        "y": 1440,
        "wires": [
            [
                "7dc8cba55f4ea2a6",
                "f4d7de6e35cc6689"
            ]
        ]
    },
    {
        "id": "2d9b1cea9886c2da",
        "type": "link in",
        "z": "0c71d7c156291f81",
        "g": "f0a7056e4ff89ec9",
        "name": "link in 2",
        "links": [
            "af8ae684feca6f96"
        ],
        "x": 95,
        "y": 400,
        "wires": [
            [
                "817044b97ed1806f"
            ]
        ]
    },
    {
        "id": "c6358b504e71c7b6",
        "type": "switch",
        "z": "0c71d7c156291f81",
        "g": "f0a7056e4ff89ec9",
        "name": "",
        "property": "payload.solar.monitor.status",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "waiting",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 340,
        "y": 400,
        "wires": [
            [
                "68387caaab696257",
                "defcaaf74bdd754a"
            ],
            []
        ]
    },
    {
        "id": "defcaaf74bdd754a",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "f0a7056e4ff89ec9",
        "name": "function 3",
        "func": "msg = {}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 490,
        "y": 400,
        "wires": [
            [
                "d8a9da5ca489a451"
            ]
        ]
    },
    {
        "id": "68387caaab696257",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "f0a7056e4ff89ec9",
        "name": "debug 11",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 470,
        "y": 340,
        "wires": []
    },
    {
        "id": "d8a9da5ca489a451",
        "type": "link out",
        "z": "0c71d7c156291f81",
        "g": "f0a7056e4ff89ec9",
        "name": "link out 5",
        "mode": "link",
        "links": [
            "27ba80a1376a5657",
            "82e4ed4aaf01deb4",
            "25514aa57a06bbd8"
        ],
        "x": 605,
        "y": 400,
        "wires": []
    },
    {
        "id": "27ba80a1376a5657",
        "type": "link in",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "link in 6",
        "links": [
            "d8a9da5ca489a451"
        ],
        "x": 115,
        "y": 980,
        "wires": [
            [
                "3b088c8c7757c298"
            ]
        ]
    },
    {
        "id": "817044b97ed1806f",
        "type": "json",
        "z": "0c71d7c156291f81",
        "g": "f0a7056e4ff89ec9",
        "name": "",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 200,
        "y": 400,
        "wires": [
            [
                "c6358b504e71c7b6"
            ]
        ]
    },
    {
        "id": "ebc601c4127fd4b0",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "server": "a37a41f9.a98da8",
        "command": "GET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 340,
        "y": 1280,
        "wires": [
            [
                "1dc82ed439f5e633"
            ]
        ]
    },
    {
        "id": "d43257e89b9c665d",
        "type": "link in",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "link in 8",
        "links": [
            "af8ae684feca6f96"
        ],
        "x": 115,
        "y": 780,
        "wires": [
            [
                "a21701f9a23ef74e"
            ]
        ]
    },
    {
        "id": "a21701f9a23ef74e",
        "type": "json",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 210,
        "y": 780,
        "wires": [
            [
                "fd38855634aadc66"
            ]
        ]
    },
    {
        "id": "fd38855634aadc66",
        "type": "switch",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "",
        "property": "payload.solar.monitor.settings",
        "propertyType": "msg",
        "rules": [
            {
                "t": "hask",
                "v": "sleep",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 350,
        "y": 780,
        "wires": [
            [
                "a97c99dc848b1cd9"
            ],
            []
        ]
    },
    {
        "id": "a97c99dc848b1cd9",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "function 8",
        "func": "msg.payload = msg.payload.solar.monitor.settings.sleep\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 500,
        "y": 780,
        "wires": [
            [
                "1c058b1f8278dd24"
            ]
        ]
    },
    {
        "id": "7dc8cba55f4ea2a6",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "debug 13",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 820,
        "y": 1700,
        "wires": []
    },
    {
        "id": "792e8351c67337e8",
        "type": "inject",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "init",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[ \"solar/load\" ]",
        "payloadType": "json",
        "x": 190,
        "y": 1280,
        "wires": [
            [
                "ebc601c4127fd4b0"
            ]
        ]
    },
    {
        "id": "d2ebc4a6205d1a4a",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "debug 14",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 720,
        "y": 760,
        "wires": []
    },
    {
        "id": "f4d7de6e35cc6689",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "server": "a37a41f9.a98da8",
        "command": "SET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 660,
        "y": 1480,
        "wires": [
            [
                "8efc7748f3d5fed1"
            ]
        ]
    },
    {
        "id": "1dc82ed439f5e633",
        "type": "json",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 490,
        "y": 1280,
        "wires": [
            [
                "e0de4137375fbe1c"
            ]
        ]
    },
    {
        "id": "e0de4137375fbe1c",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "function 9",
        "func": "switch (msg.payload.state) {\n    case \"on\":\n        msg.payload = true;\n        break;\n    case \"off\":\n        msg.payload = false;\n        break;\n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 640,
        "y": 1280,
        "wires": [
            [
                "636fe34fd10e61fd",
                "53202812f5b7e8b9"
            ]
        ]
    },
    {
        "id": "25514aa57a06bbd8",
        "type": "link in",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "link in 9",
        "links": [
            "d8a9da5ca489a451"
        ],
        "x": 125,
        "y": 1900,
        "wires": [
            [
                "e56cdda4d749deb5"
            ]
        ]
    },
    {
        "id": "468feb011ff38f89",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "server": "a37a41f9.a98da8",
        "command": "GET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 240,
        "y": 1960,
        "wires": [
            [
                "3d0feb3c85f758cd",
                "6425e623d113f38c"
            ]
        ]
    },
    {
        "id": "3d0feb3c85f758cd",
        "type": "json",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "",
        "property": "payload",
        "action": "str",
        "pretty": false,
        "x": 490,
        "y": 1960,
        "wires": [
            [
                "956b7f9e4ed8e254"
            ]
        ]
    },
    {
        "id": "956b7f9e4ed8e254",
        "type": "link out",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "link out 6",
        "mode": "link",
        "links": [
            "32a15dec7972474c"
        ],
        "x": 585,
        "y": 1960,
        "wires": []
    },
    {
        "id": "0dfccc5fdfc84e44",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "debug 15",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 560,
        "y": 2020,
        "wires": []
    },
    {
        "id": "fd8ab747371d8127",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "function 10",
        "func": "msg = {}\nmsg.payload = [\"solar/settings/sent\", \"yes\"]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 510,
        "y": 1160,
        "wires": [
            [
                "398337a8d08d5f50",
                "0b1cee2e609ad711"
            ]
        ]
    },
    {
        "id": "3b088c8c7757c298",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "server": "a37a41f9.a98da8",
        "command": "GET",
        "name": "",
        "topic": "solar/settings/sent",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 280,
        "y": 980,
        "wires": [
            [
                "368064192294170f"
            ]
        ]
    },
    {
        "id": "9957edec9bbf0f74",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "debug 16",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 640,
        "y": 920,
        "wires": []
    },
    {
        "id": "0b1cee2e609ad711",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "server": "a37a41f9.a98da8",
        "command": "SET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 680,
        "y": 1160,
        "wires": [
            []
        ]
    },
    {
        "id": "368064192294170f",
        "type": "switch",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "yes",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "no",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 490,
        "y": 980,
        "wires": [
            [],
            [
                "9957edec9bbf0f74",
                "fde241b8790dff11"
            ],
            []
        ]
    },
    {
        "id": "4c07e0cc9184dcc3",
        "type": "switch",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "yes",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "no",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 490,
        "y": 1900,
        "wires": [
            [],
            [
                "3dfb7cc559902c0d",
                "5cd229f70b7338e4"
            ],
            []
        ]
    },
    {
        "id": "3dfb7cc559902c0d",
        "type": "debug",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "debug 17",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 640,
        "y": 1820,
        "wires": []
    },
    {
        "id": "6425e623d113f38c",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "function 11",
        "func": "msg = {}\nmsg.payload = [\"solar/load/sent\", \"yes\"]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 490,
        "y": 2080,
        "wires": [
            [
                "32ba210ab2a02102",
                "0dfccc5fdfc84e44"
            ]
        ]
    },
    {
        "id": "32ba210ab2a02102",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "server": "a37a41f9.a98da8",
        "command": "SET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 660,
        "y": 2080,
        "wires": [
            []
        ]
    },
    {
        "id": "e56cdda4d749deb5",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "server": "a37a41f9.a98da8",
        "command": "GET",
        "name": "",
        "topic": "solar/load/sent",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 290,
        "y": 1900,
        "wires": [
            [
                "4c07e0cc9184dcc3"
            ]
        ]
    },
    {
        "id": "b6bf5fdadf2f16d6",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "server": "a37a41f9.a98da8",
        "command": "SET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 900,
        "y": 640,
        "wires": [
            []
        ]
    },
    {
        "id": "07e3be91304ca577",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "function 12",
        "func": "msg = {}\nmsg.payload = [\"solar/settings/sent\", \"no\"]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 730,
        "y": 640,
        "wires": [
            [
                "b6bf5fdadf2f16d6"
            ]
        ]
    },
    {
        "id": "8efc7748f3d5fed1",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "function 13",
        "func": "msg = {}\nmsg.payload = [\"solar/settings/sent\", \"no\"]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 830,
        "y": 1480,
        "wires": [
            [
                "c9279b012026601d"
            ]
        ]
    },
    {
        "id": "c9279b012026601d",
        "type": "redis-command",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "server": "a37a41f9.a98da8",
        "command": "SET",
        "name": "",
        "topic": "",
        "params": "[]",
        "paramsType": "json",
        "payloadType": "json",
        "block": false,
        "x": 1000,
        "y": 1480,
        "wires": [
            []
        ]
    },
    {
        "id": "fde241b8790dff11",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "188827854f36f6f9",
        "name": "function 14",
        "func": "msg = {}\nmsg.payload = [ \"solar/settings\" ]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 690,
        "y": 980,
        "wires": [
            [
                "44b5b1d6bf1afc9a"
            ]
        ]
    },
    {
        "id": "5cd229f70b7338e4",
        "type": "function",
        "z": "0c71d7c156291f81",
        "g": "41e0ca36ab088b06",
        "name": "function 15",
        "func": "msg = {}\nmsg.payload = [ \"solar/load\" ]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 690,
        "y": 1900,
        "wires": [
            [
                "468feb011ff38f89"
            ]
        ]
    },
    {
        "id": "725ed69c.6d76a8",
        "type": "mqtt-broker",
        "name": "",
        "broker": "",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": false,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    },
    {
        "id": "0e0468a7bcaa4a7a",
        "type": "ui_group",
        "name": "Panel",
        "tab": "8c460da79935c16f",
        "order": 4,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "9e0ac0fcf4e571a8",
        "type": "ui_group",
        "name": "Control",
        "tab": "8c460da79935c16f",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "a37a41f9.a98da8",
        "type": "redis-config",
        "name": "Local",
        "options": "{}",
        "cluster": false,
        "optionsType": "json"
    },
    {
        "id": "6ef616c0de00894f",
        "type": "ui_group",
        "name": "Battery",
        "tab": "8c460da79935c16f",
        "order": 3,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "56ef03ec967ddd2d",
        "type": "ui_group",
        "name": "Efficiency",
        "tab": "8c460da79935c16f",
        "order": 4,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "9fa8b179879daba5",
        "type": "position-config",
        "name": "",
        "isValide": "true",
        "angleType": "deg",
        "timeZoneOffset": "99",
        "timeZoneDST": "0",
        "stateTimeFormat": "3",
        "stateDateFormat": "12",
        "contextStore": ""
    },
    {
        "id": "8c460da79935c16f",
        "type": "ui_tab",
        "name": "Solar",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    }
]

hardware/creating_an_epever_solar_controller_monitor.txt ยท Last modified: 2023/06/12 01:04 by office

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.