From 23ccb75c1366ba527eb43d570c968bf541f1e69d Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Wed, 15 Jun 2022 20:18:52 +0200 Subject: [PATCH] Add "off reason" (`OR`) text sensor (Closes: #57) --- .github/workflows/ci.yaml | 4 ++ components/victron/sensor.py | 8 ++++ components/victron/text_sensor.py | 5 +++ components/victron/victron.cpp | 52 ++++++++++++++++++++++++- components/victron/victron.h | 8 ++++ debug-esp32-example.yaml | 56 +++++++++++++++++++++++++++ phoenix-inverter-esp8266-example.yaml | 4 ++ test-esp32.sh | 3 ++ tests/emulator-all-keys.yaml | 2 +- 9 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 debug-esp32-example.yaml create mode 100755 test-esp32.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1a025a4..8ae275f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -176,6 +176,8 @@ jobs: esphome -s external_components_source components config phoenix-inverter-esp8266-example.yaml - run: | esphome -s external_components_source components config debug-esp8266-example.yaml + - run: | + esphome -s external_components_source components config debug-esp32-example.yaml - run: | esphome -s external_components_source components config tests/emulator-all-keys.yaml esphome -s external_components_source components config tests/fake-bmv600.yaml @@ -232,3 +234,5 @@ jobs: esphome -s external_components_source components compile phoenix-charger-esp8266-example.yaml - run: | esphome -s external_components_source components compile phoenix-inverter-esp8266-example.yaml + - run: | + esphome -s external_components_source components compile debug-esp32-example.yaml diff --git a/components/victron/sensor.py b/components/victron/sensor.py index 67bf4cd..2dcd90e 100644 --- a/components/victron/sensor.py +++ b/components/victron/sensor.py @@ -82,6 +82,7 @@ CONF_MAX_AUXILIARY_BATTERY_VOLTAGE = "max_auxiliary_battery_voltage" CONF_AMOUNT_OF_DISCHARGED_ENERGY = "amount_of_discharged_energy" CONF_AMOUNT_OF_CHARGED_ENERGY = "amount_of_charged_energy" CONF_DC_MONITOR_MODE_ID = "dc_monitor_mode_id" +CONF_OFF_REASON_BITMASK = "off_reason_bitmask" UNIT_AMPERE_HOURS = "Ah" @@ -137,6 +138,7 @@ SENSORS = [ CONF_AMOUNT_OF_DISCHARGED_ENERGY, CONF_AMOUNT_OF_CHARGED_ENERGY, CONF_DC_MONITOR_MODE_ID, + CONF_OFF_REASON_BITMASK, ] @@ -446,6 +448,12 @@ CONFIG_SCHEMA = cv.Schema( accuracy_decimals=0, device_class=DEVICE_CLASS_EMPTY, ), + cv.Optional(CONF_OFF_REASON_BITMASK): sensor.sensor_schema( + unit_of_measurement=UNIT_EMPTY, + icon=ICON_EMPTY, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + ), } ) diff --git a/components/victron/text_sensor.py b/components/victron/text_sensor.py index 7cedd99..ef06dd9 100644 --- a/components/victron/text_sensor.py +++ b/components/victron/text_sensor.py @@ -23,6 +23,7 @@ CONF_ALARM_CONDITION_ACTIVE = "alarm_condition_active" CONF_ALARM_REASON = "alarm_reason" CONF_MODEL_DESCRIPTION = "model_description" CONF_DC_MONITOR_MODE = "dc_monitor_mode" +CONF_OFF_REASON = "off_reason" TEXT_SENSORS = [ CONF_CHARGING_MODE, @@ -39,6 +40,7 @@ TEXT_SENSORS = [ CONF_ALARM_REASON, CONF_MODEL_DESCRIPTION, CONF_DC_MONITOR_MODE, + CONF_OFF_REASON, ] @@ -84,6 +86,9 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_DC_MONITOR_MODE): text_sensor.TEXT_SENSOR_SCHEMA.extend( {cv.GenerateID(): cv.declare_id(text_sensor.TextSensor)} ), + cv.Optional(CONF_OFF_REASON): text_sensor.TEXT_SENSOR_SCHEMA.extend( + {cv.GenerateID(): cv.declare_id(text_sensor.TextSensor)} + ), } ) diff --git a/components/victron/victron.cpp b/components/victron/victron.cpp index 32f3489..d2eac5a 100644 --- a/components/victron/victron.cpp +++ b/components/victron/victron.cpp @@ -1,12 +1,33 @@ #include "victron.h" #include "esphome/core/log.h" #include // std::min +#include "esphome/core/helpers.h" namespace esphome { namespace victron { static const char *const TAG = "victron"; +static const uint8_t OFF_REASONS_SIZE = 16; +static const char *const OFF_REASONS[OFF_REASONS_SIZE] = { + "No input power", // 0000 0000 0000 0001 + "Switched off (power switch)", // 0000 0000 0000 0010 + "Switched off (device mode register)", // 0000 0000 0000 0100 + "Remote input", // 0000 0000 0000 1000 + "Protection active", // 0000 0000 0001 0000 + "Paygo", // 0000 0000 0010 0000 + "BMS", // 0000 0000 0100 0000 + "Engine shutdown detection", // 0000 0000 1000 0000 + "Analysing input voltage", // 0000 0001 0000 0000 + "Unknown: Bit 10", // 0000 0010 0000 0000 + "Unknown: Bit 11", // 0000 0100 0000 0000 + "Unknown: Bit 12", // 0000 1000 0000 0000 + "Unknown: Bit 13", // 0001 0000 0000 0000 + "Unknown: Bit 14", // 0010 0000 0000 0000 + "Unknown: Bit 15", // 0100 0000 0000 0000 + "Unknown: Bit 16", // 1000 0000 0000 0000 +}; + void VictronComponent::dump_config() { // NOLINT(google-readability-function-size,readability-function-size) ESP_LOGCONFIG(TAG, "Victron:"); LOG_BINARY_SENSOR(" ", "Load state", load_state_binary_sensor_); @@ -33,6 +54,7 @@ void VictronComponent::dump_config() { // NOLINT(google-readability-function-si LOG_SENSOR(" ", "Warning Code", warning_code_sensor_); LOG_SENSOR(" ", "Tracking Mode ID", tracking_mode_id_sensor_); LOG_SENSOR(" ", "Device Mode ID", device_mode_id_sensor_); + LOG_SENSOR(" ", "Off Reason Bitmask", off_reason_bitmask_sensor_); LOG_TEXT_SENSOR(" ", "Charging Mode", charging_mode_text_sensor_); LOG_TEXT_SENSOR(" ", "Error Text", error_text_sensor_); LOG_TEXT_SENSOR(" ", "Warning Text", warning_text_sensor_); @@ -41,6 +63,7 @@ void VictronComponent::dump_config() { // NOLINT(google-readability-function-si LOG_TEXT_SENSOR(" ", "Firmware Version", firmware_version_text_sensor_); LOG_TEXT_SENSOR(" ", "Firmware Version 24bit", firmware_version_24bit_text_sensor_); LOG_TEXT_SENSOR(" ", "Device Type", device_type_text_sensor_); + LOG_TEXT_SENSOR(" ", "Off Reason", off_reason_text_sensor_); LOG_SENSOR(" ", "Battery Temperature ", battery_temperature_sensor_); LOG_SENSOR(" ", "Instantaneous Power", instantaneous_power_sensor_); @@ -592,6 +615,26 @@ static const std::string device_type_text(int value) { } } +static const std::string off_reason_text(uint32_t mask) { + bool first = true; + std::string value_list = ""; + + if (mask) { + for (uint8_t i = 0; i < OFF_REASONS_SIZE; i++) { + if (mask & (1 << i)) { + if (first) { + first = false; + } else { + value_list.append(";"); + } + value_list.append(OFF_REASONS[i]); + } + } + } + + return value_list; +} + void VictronComponent::handle_value_() { int value; @@ -718,7 +761,14 @@ void VictronComponent::handle_value_() { return; } - // @TODO: "OR" Off reason + if (label_ == "OR") { + auto off_reason_bitmask = parse_hex(value_.substr(2, value_.size() - 2)); + if (off_reason_bitmask) { + this->publish_state_(off_reason_bitmask_sensor_, *off_reason_bitmask); + this->publish_state_(off_reason_text_sensor_, off_reason_text(*off_reason_bitmask)); + } + return; + } if (label_ == "H1") { // mAh -> Ah diff --git a/components/victron/victron.h b/components/victron/victron.h index 232f651..9221006 100644 --- a/components/victron/victron.h +++ b/components/victron/victron.h @@ -183,6 +183,9 @@ class VictronComponent : public uart::UARTDevice, public Component { void set_amount_of_charged_energy_sensor(sensor::Sensor *amount_of_charged_energy_sensor) { amount_of_charged_energy_sensor_ = amount_of_charged_energy_sensor; } + void set_off_reason_bitmask_sensor(sensor::Sensor *off_reason_bitmask_sensor) { + off_reason_bitmask_sensor_ = off_reason_bitmask_sensor; + } void set_alarm_condition_active_text_sensor(text_sensor::TextSensor *alarm_condition_active_text_sensor) { alarm_condition_active_text_sensor_ = alarm_condition_active_text_sensor; @@ -193,6 +196,9 @@ class VictronComponent : public uart::UARTDevice, public Component { void set_model_description_text_sensor(text_sensor::TextSensor *model_description_text_sensor) { model_description_text_sensor_ = model_description_text_sensor; } + void set_off_reason_text_sensor(text_sensor::TextSensor *off_reason_text_sensor) { + off_reason_text_sensor_ = off_reason_text_sensor; + } void dump_config() override; void loop() override; @@ -237,6 +243,7 @@ class VictronComponent : public uart::UARTDevice, public Component { sensor::Sensor *tracking_mode_id_sensor_{nullptr}; sensor::Sensor *device_mode_id_sensor_{nullptr}; sensor::Sensor *dc_monitor_mode_id_sensor_{nullptr}; + sensor::Sensor *off_reason_bitmask_sensor_{nullptr}; text_sensor::TextSensor *charging_mode_text_sensor_{nullptr}; text_sensor::TextSensor *error_text_sensor_{nullptr}; text_sensor::TextSensor *warning_text_sensor_{nullptr}; @@ -247,6 +254,7 @@ class VictronComponent : public uart::UARTDevice, public Component { text_sensor::TextSensor *device_type_text_sensor_{nullptr}; text_sensor::TextSensor *serial_number_text_sensor_{nullptr}; text_sensor::TextSensor *dc_monitor_mode_text_sensor_{nullptr}; + text_sensor::TextSensor *off_reason_text_sensor_{nullptr}; sensor::Sensor *battery_temperature_sensor_{nullptr}; sensor::Sensor *instantaneous_power_sensor_{nullptr}; diff --git a/debug-esp32-example.yaml b/debug-esp32-example.yaml new file mode 100644 index 0000000..8463bcd --- /dev/null +++ b/debug-esp32-example.yaml @@ -0,0 +1,56 @@ +substitutions: + name: victron-debug + external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main + tx_pin: GPIO16 + rx_pin: GPIO17 + +esphome: + name: ${name} + +esp32: + board: wemos_d1_mini32 + framework: + type: esp-idf + version: latest + +external_components: + - source: ${external_components_source} + refresh: 0s + +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password + +ota: +api: +logger: + +uart: + id: uart0 + baud_rate: 19200 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + debug: + direction: BOTH + dummy_receiver: true + after: + delimiter: "\n" + sequence: + - lambda: UARTDebug::log_string(direction, bytes); + +victron: + uart_id: uart0 + id: victron0 + throttle: 10s + +text_sensor: + - platform: victron + victron_id: victron0 + model_description: + name: "${name} model description" + firmware_version: + name: "${name} firmware version" + device_type: + name: "${name} device type" + serial_number: + name: "${name} serial number" diff --git a/phoenix-inverter-esp8266-example.yaml b/phoenix-inverter-esp8266-example.yaml index 1d1453e..305f024 100644 --- a/phoenix-inverter-esp8266-example.yaml +++ b/phoenix-inverter-esp8266-example.yaml @@ -59,6 +59,8 @@ sensor: name: "${name} ac out apparent power" warning_code: name: "${name} warning code" + off_reason_bitmask: + name: "${name} off reason bitmask" text_sensor: - platform: victron @@ -78,6 +80,8 @@ text_sensor: name: "${name} device mode" warning: name: "${name} warning" + off_reason: + name: "${name} off reason" binary_sensor: - platform: victron diff --git a/test-esp32.sh b/test-esp32.sh new file mode 100755 index 0000000..8c7c642 --- /dev/null +++ b/test-esp32.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +esphome -s external_components_source components ${1:-run} debug-esp32-example.yaml diff --git a/tests/emulator-all-keys.yaml b/tests/emulator-all-keys.yaml index f99922c..be7af22 100644 --- a/tests/emulator-all-keys.yaml +++ b/tests/emulator-all-keys.yaml @@ -71,7 +71,7 @@ interval: - uart.write: "LOAD\tON\r\n" - uart.write: "MODE\t2\r\n" - uart.write: "MPPT\t2\r\n" - - uart.write: "OR\t0x00000000\r\n" + - uart.write: "OR\t0x00000003\r\n" - uart.write: "P\t130\r\n" - uart.write: "PID\t0x204\r\n" - uart.write: "PPV\t130\r\n"