Created
April 8, 2021 07:41
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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()); | |
} | |
} | |
} | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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