Skip to content

Instantly share code, notes, and snippets.

@RoganDawes
Created April 8, 2021 07:41
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RoganDawes/190cafcd8e2545ccd93628f2f9bfa82c to your computer and use it in GitHub Desktop.
Save RoganDawes/190cafcd8e2545ccd93628f2f9bfa82c to your computer and use it in GitHub Desktop.
ESPHome configuration for CurrentCost Envi XML parsing and reporting. Uses the RapidXML Header-only library to parse the XML. Works on an ESP32, ESP8266 seems insufficient for the task.
#include <string>
#include "esphome.h"
#include "rapidxml/rapidxml.hpp"
static const char *TAG = "currentcost";
class CurrentCostSensor : public Component, public UARTDevice {
public:
Sensor *ch1 = new Sensor();
Sensor *ch2 = new Sensor();
Sensor *ch3 = new Sensor();
CurrentCostSensor(UARTComponent *parent) : UARTDevice(parent) {}
void setup() override {
// nothing to do here
}
int readline(int readch, char *buffer, int len)
{
static int pos = 0;
int rpos;
if (readch > 0) {
switch (readch) {
case '\r': // Return on CR
case '\n': // Return on LF
rpos = pos;
pos = 0; // Reset position index ready for next time
return rpos;
default:
if (pos < len-1) {
buffer[pos++] = readch;
buffer[pos] = 0;
}
}
}
// No end of line has been found, so return -pos to show how far we have got.
return -pos;
}
boolean expect_node(rapidxml::xml_node<> *node, std::string name) {
if (node && node->name() != name) {
ESP_LOGD(TAG, "Unexpected node: '%s', expected '%s'", node->name(), name.c_str());
return false;
}
return true;
}
void loop() override {
const int max_line_length = 1024;
static char buffer[max_line_length];
while (available()) {
int got = readline(read(), buffer, max_line_length);
if (got > 0) {
ESP_LOGD(TAG, "Read: '%s'", buffer);
try {
rapidxml::xml_document<> *xml_doc = new rapidxml::xml_document<>();
xml_doc->parse<0> (buffer);
rapidxml::xml_node<> *node, *watts;
node = xml_doc->first_node();
if (!expect_node(node, "msg")) return;
node = node->first_node();
if (!expect_node(node, "src")) return;
node = node->next_sibling();
if (!expect_node(node, "dsb")) return;
node = node->next_sibling();
if (!expect_node(node, "time")) return;
node = node->next_sibling();
if (!expect_node(node, "tmpr")) return;
node = node->next_sibling();
if (!expect_node(node, "sensor")) return;
node = node->next_sibling();
if (!expect_node(node, "id")) return;
node = node->next_sibling();
if (!expect_node(node, "type")) return;
node = node->next_sibling();
if (!expect_node(node, "ch1")) return;
watts = node->first_node();
if (!expect_node(watts, "watts")) return;
ch1->publish_state(atof(watts->value()));
node = node->next_sibling();
if (!expect_node(node, "ch2")) return;
watts = node->first_node();
if (!expect_node(watts, "watts")) return;
ch2->publish_state(atof(watts->value()));
node = node->next_sibling();
if (!expect_node(node, "ch3")) return;
watts = node->first_node();
if (!expect_node(watts, "watts")) return;
ch3->publish_state(atof(watts->value()));
} catch (rapidxml::parse_error& e) {
ESP_LOGD(TAG, "Parse error: %s", e.what());
} catch (std::exception& e) {
ESP_LOGD(TAG, "Caught exception: %s", e.what());
}
}
}
}
};
esphome:
name: currentcost
platform: esp32
board: esp32dev
platformio_options:
upload_speed: 460800
build_flags: -DRAPIDXML_STATIC_POOL_SIZE=1024 -DRAPIDXML_DYNAMIC_POOL_SIZE=1024
build_unflags: -fno-exceptions
includes:
- rapidxml
- currentcost.h
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable logging
logger:
# Enable Home Assistant API
api:
ota:
time:
- platform: homeassistant
id: homeassistant_time
text_sensor:
- platform: version
name: $name ESPHome Version
- platform: wifi_info
ip_address:
name: $name IP
ssid:
name: $name SSID
bssid:
name: $name BSSID
switch:
- platform: restart
name: $name Restart
binary_sensor:
- platform: status
name: $name Status
sensor:
- platform: uptime
name: $name uptime
- platform: wifi_signal
name: $name WiFi Signal
update_interval: 60s
- platform: custom
lambda: |-
auto currentcost = new CurrentCostSensor(id(uart_bus));
App.register_component(currentcost);
return {currentcost->ch1, currentcost->ch2, currentcost->ch3};
sensors:
- id: "ch1"
name: "currentcost_ch1"
unit_of_measurement: W
accuracy_decimals: 0
- id: "ch2"
name: "currentcost_ch2"
unit_of_measurement: W
accuracy_decimals: 0
- id: "ch3"
name: "currentcost_ch3"
unit_of_measurement: W
accuracy_decimals: 0
uart:
id: uart_bus
rx_pin: GPIO3
baud_rate: 57600
rx_buffer_size: 1024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment