Formatted.

This commit is contained in:
Victor Chang
2024-06-06 22:01:06 -07:00
parent 2e0c6f853a
commit b3d1a1fda4
3 changed files with 779 additions and 748 deletions

View File

@@ -1,6 +1,7 @@
#include "esphome/core/log.h"
#include "emporia_vue_utility.h" #include "emporia_vue_utility.h"
#include "esphome/core/log.h"
namespace esphome { namespace esphome {
namespace emporia_vue_utility { namespace emporia_vue_utility {
@@ -15,8 +16,9 @@ void EmporiaVueUtility::setup() {
} }
void EmporiaVueUtility::update() { void EmporiaVueUtility::update() {
// This seems to be called incessantly instead of at the set update interval... // This seems to be called incessantly instead of at the set update
// ESP_LOGD(TAG, "Got update call with an instructed interval of %d sec", this->update_interval_); // interval... ESP_LOGD(TAG, "Got update call with an instructed interval of
// %d sec", this->update_interval_);
} }
void EmporiaVueUtility::loop() { void EmporiaVueUtility::loop() {
@@ -34,12 +36,9 @@ void EmporiaVueUtility::loop() {
/* sanity checks! */ /* sanity checks! */
if (next_meter_request > if (next_meter_request >
now + (INITIAL_STARTUP_DELAY + METER_REJOIN_INTERVAL)) { now + (INITIAL_STARTUP_DELAY + METER_REJOIN_INTERVAL)) {
ESP_LOGD(TAG, ESP_LOGD(TAG, "Time jumped back (%lld > %lld + %lld); resetting",
"Time jumped back (%lld > %lld + %lld); resetting", (long long)next_meter_request, (long long)now,
(long long) next_meter_request, (long long)(INITIAL_STARTUP_DELAY + METER_REJOIN_INTERVAL));
(long long) now,
(long long) (INITIAL_STARTUP_DELAY +
METER_REJOIN_INTERVAL));
next_meter_request = next_meter_join = 0; next_meter_request = next_meter_join = 0;
} }
@@ -52,7 +51,8 @@ void EmporiaVueUtility::loop() {
if (now < last_meter_reading + int(update_interval_ / 4)) { if (now < last_meter_reading + int(update_interval_ / 4)) {
// Sometimes a duplicate message is sent in quick succession. // Sometimes a duplicate message is sent in quick succession.
// Ignoring the duplicate. // Ignoring the duplicate.
ESP_LOGD(TAG, "Got extra message %ds after the previous message.", now - last_meter_reading); ESP_LOGD(TAG, "Got extra message %ds after the previous message.",
now - last_meter_reading);
break; break;
} }
last_reading_has_error = 0; last_reading_has_error = 0;
@@ -104,7 +104,8 @@ void EmporiaVueUtility::loop() {
break; break;
case 'e': case 'e':
// Unknown response type, but we can ignore. // Unknown response type, but we can ignore.
ESP_LOGI(TAG, "Got 'e'-type message with value: %d", input_buffer.data[4]); ESP_LOGI(TAG, "Got 'e'-type message with value: %d",
input_buffer.data[4]);
break; break;
default: default:
ESP_LOGE(TAG, "Unhandled response type '%c'", msg_type); ESP_LOGE(TAG, "Unhandled response type '%c'", msg_type);
@@ -139,11 +140,16 @@ void EmporiaVueUtility::loop() {
return; return;
} }
if (startup_step == 0) send_version_req(); if (startup_step == 0)
else if (startup_step == 1) send_mac_req(); send_version_req();
else if (startup_step == 2) send_install_code_req(); else if (startup_step == 1)
else if (startup_step == 3) send_meter_join(); send_mac_req();
else send_meter_request(); else if (startup_step == 2)
send_install_code_req();
else if (startup_step == 3)
send_meter_join();
else
send_meter_request();
} }
} }

View File

@@ -1,8 +1,8 @@
#pragma once #pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h" #include "esphome/components/uart/uart.h"
#include "esphome/core/component.h"
// Extra meter reading response debugging // Extra meter reading response debugging
#define DEBUG_VUE_RESPONSE true #define DEBUG_VUE_RESPONSE true
@@ -74,13 +74,15 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
byte msg_type; byte msg_type;
uint8_t data_len; uint8_t data_len;
byte unknown0; // Payload Byte 0 : Always 0x18 byte unknown0; // Payload Byte 0 : Always 0x18
byte increment; // Payload Byte 1 : Increments on each reading and rolls over byte increment; // Payload Byte 1 : Increments on each reading and rolls
// over
byte unknown2[5]; // Payload Bytes 2 to 6 byte unknown2[5]; // Payload Bytes 2 to 6
uint32_t import_wh; // Payload Bytes 7 to 10 uint32_t import_wh; // Payload Bytes 7 to 10
byte unknown11[6]; // Payload Bytes 11 to 16 byte unknown11[6]; // Payload Bytes 11 to 16
uint32_t export_wh; // Payload Bytes 17 to 20 uint32_t export_wh; // Payload Bytes 17 to 20
byte unknown21[19]; // Payload Bytes 21 to 39 byte unknown21[19]; // Payload Bytes 21 to 39
uint32_t watts; // Payload Bytes 40 to 43 : Starts with 0x2A, only use the last 24 bits. uint32_t watts; // Payload Bytes 40 to 43 : Starts with 0x2A, only use the
// last 24 bits.
} __attribute__((packed)); } __attribute__((packed));
// A Mac Address or install code response // A Mac Address or install code response
@@ -128,13 +130,23 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
// The most recent cost unit // The most recent cost unit
uint16_t cost_unit = 0; uint16_t cost_unit = 0;
void set_update_interval(uint32_t update_interval) { update_interval_ = update_interval / 1000; } void set_update_interval(uint32_t update_interval) {
update_interval_ = update_interval / 1000;
}
void set_power_sensor(sensor::Sensor *sensor) { power_sensor_ = sensor; } void set_power_sensor(sensor::Sensor *sensor) { power_sensor_ = sensor; }
void set_power_export_sensor(sensor::Sensor *sensor) { power_export_sensor_ = sensor; } void set_power_export_sensor(sensor::Sensor *sensor) {
void set_power_import_sensor(sensor::Sensor *sensor) { power_import_sensor_ = sensor; } power_export_sensor_ = sensor;
}
void set_power_import_sensor(sensor::Sensor *sensor) {
power_import_sensor_ = sensor;
}
void set_energy_sensor(sensor::Sensor *sensor) { energy_sensor_ = sensor; } void set_energy_sensor(sensor::Sensor *sensor) { energy_sensor_ = sensor; }
void set_energy_export_sensor(sensor::Sensor *sensor) { energy_export_sensor_ = sensor; } void set_energy_export_sensor(sensor::Sensor *sensor) {
void set_energy_import_sensor(sensor::Sensor *sensor) { energy_import_sensor_ = sensor; } energy_export_sensor_ = sensor;
}
void set_energy_import_sensor(sensor::Sensor *sensor) {
energy_import_sensor_ = sensor;
}
void setup() override; void setup() override;
void update() override; void update() override;
void loop() override; void loop() override;
@@ -145,8 +157,10 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
// Turn the wifi led on/off // Turn the wifi led on/off
void led_wifi(bool state) { void led_wifi(bool state) {
#if USE_LED_PINS #if USE_LED_PINS
if (state) digitalWrite(LED_PIN_WIFI, 0); if (state)
else digitalWrite(LED_PIN_WIFI, 1); digitalWrite(LED_PIN_WIFI, 0);
else
digitalWrite(LED_PIN_WIFI, 1);
#endif #endif
return; return;
} }
@@ -154,8 +168,10 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
// Turn the link led on/off // Turn the link led on/off
void led_link(bool state) { void led_link(bool state) {
#if USE_LED_PINS #if USE_LED_PINS
if (state) digitalWrite(LED_PIN_LINK, 0); if (state)
else digitalWrite(LED_PIN_LINK, 1); digitalWrite(LED_PIN_LINK, 0);
else
digitalWrite(LED_PIN_LINK, 1);
#endif #endif
return; return;
} }
@@ -180,7 +196,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
} }
if (pos > 0 && logit) { if (pos > 0 && logit) {
ESP_LOGE(TAG, "Skipped input:"); ESP_LOGE(TAG, "Skipped input:");
ESP_LOG_BUFFER_HEXDUMP(TAG, input_buffer.data, pos-1, ESP_LOG_ERROR); ESP_LOG_BUFFER_HEXDUMP(TAG, input_buffer.data, pos - 1, ESP_LOG_ERROR);
} }
pos = 0; pos = 0;
data_len = 0; data_len = 0;
@@ -199,7 +215,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
switch (prev_pos) { switch (prev_pos) {
case 0: case 0:
if (c != 0x24 ) { // 0x24 == "$", the start of a message if (c != 0x24) { // 0x24 == "$", the start of a message
ESP_LOGE(TAG, "Invalid input at position %d: 0x%x", pos, c); ESP_LOGE(TAG, "Invalid input at position %d: 0x%x", pos, c);
dump_serial_input(true); dump_serial_input(true);
pos = 0; pos = 0;
@@ -207,7 +223,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
} }
break; break;
case 1: case 1:
if (c != 0x01 ) { // 0x01 means "response" if (c != 0x01) { // 0x01 means "response"
ESP_LOGE(TAG, "Invalid input at position %d 0x%x", pos, c); ESP_LOGE(TAG, "Invalid input at position %d 0x%x", pos, c);
dump_serial_input(true); dump_serial_input(true);
pos = 0; pos = 0;
@@ -227,7 +243,6 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
return 0; return 0;
default: default:
if (pos < data_len + 5) { if (pos < data_len + 5) {
; ;
} else if (c == 0x0d) { // 0x0d == "/r", which should end a message } else if (c == 0x0d) { // 0x0d == "/r", which should end a message
return pos; return pos;
@@ -273,11 +288,13 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
// Setup Meter Divisor // Setup Meter Divisor
if ((mr2->meter_div > 10) || (mr2->meter_div < 1)) { if ((mr2->meter_div > 10) || (mr2->meter_div < 1)) {
ESP_LOGW(TAG, "Unreasonable MeterDiv value %d, ignoring", mr2->meter_div); ESP_LOGW(TAG, "Unreasonable MeterDiv value %d, ignoring",
mr2->meter_div);
last_reading_has_error = 1; last_reading_has_error = 1;
ask_for_bug_report(); ask_for_bug_report();
} else if ((meter_div != 0) && (mr2->meter_div != meter_div)) { } else if ((meter_div != 0) && (mr2->meter_div != meter_div)) {
ESP_LOGW(TAG, "MeterDiv value changed from %d to %d", meter_div, mr2->meter_div); ESP_LOGW(TAG, "MeterDiv value changed from %d to %d", meter_div,
mr2->meter_div);
last_reading_has_error = 1; last_reading_has_error = 1;
meter_div = mr2->meter_div; meter_div = mr2->meter_div;
} else { } else {
@@ -285,33 +302,34 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
} }
// Setup Cost Unit // Setup Cost Unit
cost_unit = ((mr2->cost_unit & 0x00FF) << 8) cost_unit =
+ ((mr2->cost_unit & 0xFF00) >> 8); ((mr2->cost_unit & 0x00FF) << 8) + ((mr2->cost_unit & 0xFF00) >> 8);
watt_hours = parse_meter_watt_hours_v2(mr2); watt_hours = parse_meter_watt_hours_v2(mr2);
watts = parse_meter_watts_v2(mr2); watts = parse_meter_watts_v2(mr2);
// Extra debugging of non-zero bytes, only on first packet or if DEBUG_VUE_RESPONSE is true // Extra debugging of non-zero bytes, only on first packet or if
// DEBUG_VUE_RESPONSE is true
if ((DEBUG_VUE_RESPONSE) || (last_meter_reading == 0)) { if ((DEBUG_VUE_RESPONSE) || (last_meter_reading == 0)) {
ESP_LOGD(TAG, "Meter Divisor: %d", meter_div); ESP_LOGD(TAG, "Meter Divisor: %d", meter_div);
ESP_LOGD(TAG, "Meter Cost Unit: %d", cost_unit); ESP_LOGD(TAG, "Meter Cost Unit: %d", cost_unit);
ESP_LOGD(TAG, "Meter Flags: %02x %02x", mr2->maybe_flags[0], mr2->maybe_flags[1]); ESP_LOGD(TAG, "Meter Flags: %02x %02x", mr2->maybe_flags[0],
mr2->maybe_flags[1]);
ESP_LOGD(TAG, "Meter Energy Flags: %02x", (byte)mr2->watt_hours); ESP_LOGD(TAG, "Meter Energy Flags: %02x", (byte)mr2->watt_hours);
ESP_LOGD(TAG, "Meter Power Flags: %02x", (byte)mr2->watts); ESP_LOGD(TAG, "Meter Power Flags: %02x", (byte)mr2->watts);
// Unlike the other values, ms_since_reset is in our native byte order // Unlike the other values, ms_since_reset is in our native byte order
ESP_LOGD(TAG, "Meter Timestamp: %.f", float(mr2->timestamp) / 1000.0 ); ESP_LOGD(TAG, "Meter Timestamp: %.f", float(mr2->timestamp) / 1000.0);
ESP_LOGD(TAG, "Meter Energy: %.3fkWh", watt_hours / 1000.0 ); ESP_LOGD(TAG, "Meter Energy: %.3fkWh", watt_hours / 1000.0);
ESP_LOGD(TAG, "Meter Power: %3.0fW", watts); ESP_LOGD(TAG, "Meter Power: %3.0fW", watts);
for (int x = 1 ; x < pos / 4 ; x++) { for (int x = 1; x < pos / 4; x++) {
int y = x * 4; int y = x * 4;
if ( (input_buffer.data[y]) if ((input_buffer.data[y]) || (input_buffer.data[y + 1]) ||
|| (input_buffer.data[y+1]) (input_buffer.data[y + 2]) || (input_buffer.data[y + 3])) {
|| (input_buffer.data[y+2]) ESP_LOGD(
|| (input_buffer.data[y+3])) { TAG, "Meter Response Bytes %3d to %3d: %02x %02x %02x %02x",
ESP_LOGD(TAG, "Meter Response Bytes %3d to %3d: %02x %02x %02x %02x", y-4, y-1, y - 4, y - 1, input_buffer.data[y], input_buffer.data[y + 1],
input_buffer.data[y], input_buffer.data[y+1], input_buffer.data[y + 2], input_buffer.data[y + 3]);
input_buffer.data[y+2], input_buffer.data[y+3]);
} }
} }
} }
@@ -320,7 +338,8 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
// Quick validate, look for a magic number. // Quick validate, look for a magic number.
if (input_buffer.data[44] != 0x2A) { if (input_buffer.data[44] != 0x2A) {
ESP_LOGE(TAG, "Byte 44 was %02x instead of %02x", input_buffer.data[44], 0x2A); ESP_LOGE(TAG, "Byte 44 was %02x instead of %02x", input_buffer.data[44],
0x2A);
last_reading_has_error = 1; last_reading_has_error = 1;
return; return;
} }
@@ -332,18 +351,21 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
void ask_for_bug_report() { void ask_for_bug_report() {
ESP_LOGE(TAG, "If you continue to see this, try asking for help at"); ESP_LOGE(TAG, "If you continue to see this, try asking for help at");
ESP_LOGE(TAG, " https://community.home-assistant.io/t/emporia-vue-utility-connect/378347"); ESP_LOGE(TAG,
ESP_LOGE(TAG, "and include a few lines above this message and the data below until \"EOF\":"); " "
"https://community.home-assistant.io/t/"
"emporia-vue-utility-connect/378347");
ESP_LOGE(TAG,
"and include a few lines above this message and the data below "
"until \"EOF\":");
ESP_LOGE(TAG, "Full packet:"); ESP_LOGE(TAG, "Full packet:");
for (int x = 1 ; x < pos / 4 ; x++) { for (int x = 1; x < pos / 4; x++) {
int y = x * 4; int y = x * 4;
if ( (input_buffer.data[y]) if ((input_buffer.data[y]) || (input_buffer.data[y + 1]) ||
|| (input_buffer.data[y+1]) (input_buffer.data[y + 2]) || (input_buffer.data[y + 3])) {
|| (input_buffer.data[y+2]) ESP_LOGE(TAG, " Meter Response Bytes %3d to %3d: %02x %02x %02x %02x",
|| (input_buffer.data[y+3])) { y - 4, y - 1, input_buffer.data[y], input_buffer.data[y + 1],
ESP_LOGE(TAG, " Meter Response Bytes %3d to %3d: %02x %02x %02x %02x", y-4, y-1, input_buffer.data[y + 2], input_buffer.data[y + 3]);
input_buffer.data[y], input_buffer.data[y+1],
input_buffer.data[y+2], input_buffer.data[y+3]);
} }
} }
ESP_LOGI(TAG, "MGM Firmware Version: %d", mgm_firmware_ver); ESP_LOGI(TAG, "MGM Firmware Version: %d", mgm_firmware_ver);
@@ -369,12 +391,11 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
int8_t x; int8_t x;
watt_hours_raw = endian_swap(mr->watt_hours); watt_hours_raw = endian_swap(mr->watt_hours);
if ( if ((watt_hours_raw == 4194304) // "missing data" message (0x00 40 00 00)
(watt_hours_raw == 4194304) // "missing data" message (0x00 40 00 00)
|| (watt_hours_raw == 0)) { || (watt_hours_raw == 0)) {
ESP_LOGI(TAG, "Watt-hours value missing"); ESP_LOGI(TAG, "Watt-hours value missing");
last_reading_has_error = 1; last_reading_has_error = 1;
return(0); return (0);
} }
// Handle if a meter divisor is in effect // Handle if a meter divisor is in effect
@@ -382,8 +403,8 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
if (!not_first_run) { if (!not_first_run) {
// Initialize watt-hour filter on first run // Initialize watt-hour filter on first run
for (x = MAX_WH_CHANGE_ARY ; x != 0 ; x--) { for (x = MAX_WH_CHANGE_ARY; x != 0; x--) {
history[x-1] = watt_hours; history[x - 1] = watt_hours;
} }
not_first_run = 1; not_first_run = 1;
} }
@@ -400,8 +421,8 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
history_avg = 0; history_avg = 0;
// Calculate avg watt_hours over previous N samples // Calculate avg watt_hours over previous N samples
for (x = MAX_WH_CHANGE_ARY ; x != 0 ; x--) { for (x = MAX_WH_CHANGE_ARY; x != 0; x--) {
history_avg += history[x-1] / MAX_WH_CHANGE_ARY; history_avg += history[x - 1] / MAX_WH_CHANGE_ARY;
} }
// Get the difference of current value from avg // Get the difference of current value from avg
@@ -409,7 +430,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
ESP_LOGE(TAG, "Unreasonable watt-hours of %f, +%f from moving avg", ESP_LOGE(TAG, "Unreasonable watt-hours of %f, +%f from moving avg",
watt_hours, watt_hours - history_avg); watt_hours, watt_hours - history_avg);
last_reading_has_error = 1; last_reading_has_error = 1;
return(watt_hours); return (watt_hours);
} }
// Get the difference from previously reported value // Get the difference from previously reported value
@@ -440,7 +461,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
energy_sensor_->publish_state(watt_hours); energy_sensor_->publish_state(watt_hours);
} }
return(watt_hours); return (watt_hours);
} }
float parse_meter_watt_hours_v7(struct MeterReadingV7 *mr) { float parse_meter_watt_hours_v7(struct MeterReadingV7 *mr) {
@@ -455,27 +476,32 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
int32_t consumed_diff = int32_t(consumed) - int32_t(prev_consumed); int32_t consumed_diff = int32_t(consumed) - int32_t(prev_consumed);
int32_t returned_diff = int32_t(returned) - int32_t(prev_returned); int32_t returned_diff = int32_t(returned) - int32_t(prev_returned);
// Sometimes the reported value is far larger than it should be. Let's ignore it. // Sometimes the reported value is far larger than it should be. Let's
if (std::abs(consumed_diff) > MAX_WH_CHANGE || std::abs(returned_diff) > MAX_WH_CHANGE) { // ignore it.
ESP_LOGW(TAG, "Reported watt-hour change is too large vs previous reading. Skipping."); if (std::abs(consumed_diff) > MAX_WH_CHANGE ||
// The `prev_consumed` and `prev_returned` will still be given the current reading std::abs(returned_diff) > MAX_WH_CHANGE) {
// even if the value is erroneous. ESP_LOGW(TAG,
"Reported watt-hour change is too large vs previous reading. "
"Skipping.");
// The `prev_consumed` and `prev_returned` will still be given the current
// reading even if the value is erroneous.
// //
// This approach should handle two scenarios: // This approach should handle two scenarios:
// 1) Some sort of outage causes a long gap between the previous reading (or is 0 after // 1) Some sort of outage causes a long gap between the previous reading
// a reboot) and the current reading. In this case, the difference from the previous // (or is 0 after a reboot) and the current reading. In this case, the
// reading can be "too" large, but actually be expected. // difference from the previous reading can be "too" large, but actually
// 2) I have seen erroneous blips of a single sample with a value that is way too big. // be expected. 2) I have seen erroneous blips of a single sample with a
// value that is way too big.
// //
// The code handles scenario #1 by ignoring the current reading but then continuing on // The code handles scenario #1 by ignoring the current reading but then
// as normal after. // continuing on as normal after. The code handles scenario #2 by ignoring
// The code handles scenario #2 by ignoring the current reading, then ignoring the // the current reading, then ignoring the followup reading, then
// followup reading, then continuing on as normal. // continuing on as normal.
// //
// At worst, two consecutive samples will be ignored. // At worst, two consecutive samples will be ignored.
prev_consumed = consumed; prev_consumed = consumed;
prev_returned = returned; prev_returned = returned;
return(0); return (0);
} }
net = consumed - returned; net = consumed - returned;
@@ -493,7 +519,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
prev_consumed = consumed; prev_consumed = consumed;
prev_returned = returned; prev_returned = returned;
return(net); return (net);
} }
/* /*
@@ -514,7 +540,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
if (watts_raw == 0x800000) { if (watts_raw == 0x800000) {
// Exactly "negative zero", which means "missing data" // Exactly "negative zero", which means "missing data"
ESP_LOGI(TAG, "Instant Watts value missing"); ESP_LOGI(TAG, "Instant Watts value missing");
return(0); return (0);
} else if (watts_raw & 0xC00000) { } else if (watts_raw & 0xC00000) {
// This is either more than 12MW being returned, // This is either more than 12MW being returned,
// or it's a negative number in 1's complement. // or it's a negative number in 1's complement.
@@ -555,7 +581,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
} }
} }
} }
return(watts); return (watts);
} }
/* /*
@@ -591,7 +617,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
} }
} }
} }
return(watts); return (watts);
} }
void handle_resp_meter_join() { void handle_resp_meter_join() {
@@ -607,17 +633,12 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
struct Addr *mac; struct Addr *mac;
mac = &input_buffer.addr; mac = &input_buffer.addr;
snprintf(mgm_mac_address, sizeof(mgm_mac_address), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", snprintf(mgm_mac_address, sizeof(mgm_mac_address),
mac->addr[7], "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", mac->addr[7],
mac->addr[6], mac->addr[6], mac->addr[5], mac->addr[4], mac->addr[3],
mac->addr[5], mac->addr[2], mac->addr[1], mac->addr[0]);
mac->addr[4],
mac->addr[3],
mac->addr[2],
mac->addr[1],
mac->addr[0]);
ESP_LOGI(TAG, "MGM Mac Address: %s", mgm_mac_address); ESP_LOGI(TAG, "MGM Mac Address: %s", mgm_mac_address);
return(0); return (0);
} }
int handle_resp_install_code() { int handle_resp_install_code() {
@@ -625,17 +646,12 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
struct Addr *code; struct Addr *code;
code = &input_buffer.addr; code = &input_buffer.addr;
snprintf(mgm_install_code, sizeof(mgm_install_code), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", snprintf(mgm_install_code, sizeof(mgm_install_code),
code->addr[0], "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", code->addr[0],
code->addr[1], code->addr[1], code->addr[2], code->addr[3], code->addr[4],
code->addr[2], code->addr[5], code->addr[6], code->addr[7]);
code->addr[3],
code->addr[4],
code->addr[5],
code->addr[6],
code->addr[7]);
ESP_LOGI(TAG, "MGM Install Code: %s (secret)", mgm_install_code); ESP_LOGI(TAG, "MGM Install Code: %s (secret)", mgm_install_code);
return(0); return (0);
} }
int handle_resp_firmware_ver() { int handle_resp_firmware_ver() {
@@ -645,25 +661,30 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
mgm_firmware_ver = ver->value; mgm_firmware_ver = ver->value;
ESP_LOGI(TAG, "MGM Firmware Version: %d", mgm_firmware_ver); ESP_LOGI(TAG, "MGM Firmware Version: %d", mgm_firmware_ver);
return(0); return (0);
} }
void send_meter_request() { void send_meter_request() {
const byte msg[] = { 0x24, 0x72, 0x0d }; const byte msg[] = {0x24, 0x72, 0x0d};
ESP_LOGD(TAG, "Sending request for meter reading"); ESP_LOGD(TAG, "Sending request for meter reading");
write_array(msg, sizeof(msg)); write_array(msg, sizeof(msg));
led_link(false); led_link(false);
} }
void send_meter_join() { void send_meter_join() {
const byte msg[] = { 0x24, 0x6a, 0x0d }; const byte msg[] = {0x24, 0x6a, 0x0d};
ESP_LOGI(TAG, "MGM Firmware Version: %d", mgm_firmware_ver); ESP_LOGI(TAG, "MGM Firmware Version: %d", mgm_firmware_ver);
ESP_LOGI(TAG, "MGM Mac Address: %s", mgm_mac_address); ESP_LOGI(TAG, "MGM Mac Address: %s", mgm_mac_address);
ESP_LOGI(TAG, "MGM Install Code: %s (secret)", mgm_install_code); ESP_LOGI(TAG, "MGM Install Code: %s (secret)", mgm_install_code);
ESP_LOGI(TAG, "Trying to re-join the meter. If you continue to see this message"); ESP_LOGI(
ESP_LOGI(TAG, "you may need to move the device closer to your power meter or"); TAG,
ESP_LOGI(TAG, "contact your utililty and ask them to reprovision the device."); "Trying to re-join the meter. If you continue to see this message");
ESP_LOGI(TAG, "Also confirm that the above mac address & install code match"); ESP_LOGI(TAG,
"you may need to move the device closer to your power meter or");
ESP_LOGI(TAG,
"contact your utililty and ask them to reprovision the device.");
ESP_LOGI(TAG,
"Also confirm that the above mac address & install code match");
ESP_LOGI(TAG, "what is printed on your device."); ESP_LOGI(TAG, "what is printed on your device.");
ESP_LOGE(TAG, "You can also file a bug at"); ESP_LOGE(TAG, "You can also file a bug at");
ESP_LOGE(TAG, " https://forms.gle/duMdU2i7wWHdbK5TA"); ESP_LOGE(TAG, " https://forms.gle/duMdU2i7wWHdbK5TA");
@@ -672,21 +693,21 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
} }
void send_mac_req() { void send_mac_req() {
const byte msg[] = { 0x24, 0x6d, 0x0d }; const byte msg[] = {0x24, 0x6d, 0x0d};
ESP_LOGD(TAG, "Sending mac addr request"); ESP_LOGD(TAG, "Sending mac addr request");
write_array(msg, sizeof(msg)); write_array(msg, sizeof(msg));
led_wifi(false); led_wifi(false);
} }
void send_install_code_req() { void send_install_code_req() {
const byte msg[] = { 0x24, 0x69, 0x0d }; const byte msg[] = {0x24, 0x69, 0x0d};
ESP_LOGD(TAG, "Sending install code request"); ESP_LOGD(TAG, "Sending install code request");
write_array(msg, sizeof(msg)); write_array(msg, sizeof(msg));
led_wifi(false); led_wifi(false);
} }
void send_version_req() { void send_version_req() {
const byte msg[] = { 0x24, 0x66, 0x0d }; const byte msg[] = {0x24, 0x66, 0x0d};
ESP_LOGD(TAG, "Sending firmware version request"); ESP_LOGD(TAG, "Sending firmware version request");
write_array(msg, sizeof(msg)); write_array(msg, sizeof(msg));
led_wifi(false); led_wifi(false);
@@ -701,6 +722,7 @@ class EmporiaVueUtility : public PollingComponent, public uart::UARTDevice {
delay(100); delay(100);
} }
} }
private: private:
uint32_t update_interval_; uint32_t update_interval_;
sensor::Sensor *power_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr};

View File

@@ -10,7 +10,7 @@ from esphome.const import (
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
DEVICE_CLASS_ENERGY, DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING STATE_CLASS_TOTAL_INCREASING,
) )
DEPENDENCIES = ["uart"] DEPENDENCIES = ["uart"]
@@ -29,8 +29,10 @@ ENERGY_SENSOR_TYPES = {
ALL_SENSOR_TYPES = {**POWER_SENSOR_TYPES, **ENERGY_SENSOR_TYPES} ALL_SENSOR_TYPES = {**POWER_SENSOR_TYPES, **ENERGY_SENSOR_TYPES}
emporia_vue_utility_ns = cg.esphome_ns.namespace('emporia_vue_utility') emporia_vue_utility_ns = cg.esphome_ns.namespace("emporia_vue_utility")
EmporiaVueUtility = emporia_vue_utility_ns.class_('EmporiaVueUtility', cg.PollingComponent, uart.UARTDevice) EmporiaVueUtility = emporia_vue_utility_ns.class_(
"EmporiaVueUtility", cg.PollingComponent, uart.UARTDevice
)
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema( cv.Schema(
@@ -53,13 +55,14 @@ CONFIG_SCHEMA = cv.All(
accuracy_decimals=0, accuracy_decimals=0,
) )
for name in ENERGY_SENSOR_TYPES for name in ENERGY_SENSOR_TYPES
} },
} }
) )
.extend(cv.polling_component_schema('30s')) .extend(cv.polling_component_schema("30s"))
.extend(uart.UART_DEVICE_SCHEMA) .extend(uart.UART_DEVICE_SCHEMA)
) )
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config) await cg.register_component(var, config)