diff --git a/CODEOWNERS b/CODEOWNERS index da48761551..ca1da2f153 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -275,6 +275,7 @@ esphome/components/uart/* @esphome/core esphome/components/ufire_ec/* @pvizeli esphome/components/ufire_ise/* @pvizeli esphome/components/ultrasonic/* @OttoWinter +esphome/components/vbus/* @ssieb esphome/components/version/* @esphome/core esphome/components/wake_on_lan/* @willwill2will54 esphome/components/web_server_base/* @OttoWinter diff --git a/esphome/components/display_menu_base/__init__.py b/esphome/components/display_menu_base/__init__.py index eb66737fdb..d7326cdc65 100644 --- a/esphome/components/display_menu_base/__init__.py +++ b/esphome/components/display_menu_base/__init__.py @@ -8,6 +8,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_ON_VALUE, CONF_COMMAND, + CONF_CUSTOM, CONF_NUMBER, CONF_FORMAT, CONF_MODE, @@ -32,7 +33,6 @@ CONF_BACK = "back" CONF_TEXT = "text" CONF_SELECT = "select" CONF_SWITCH = "switch" -CONF_CUSTOM = "custom" CONF_ITEMS = "items" CONF_ON_TEXT = "on_text" CONF_OFF_TEXT = "off_text" diff --git a/esphome/components/vbus/__init__.py b/esphome/components/vbus/__init__.py new file mode 100644 index 0000000000..70f130e23b --- /dev/null +++ b/esphome/components/vbus/__init__.py @@ -0,0 +1,32 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import uart +from esphome.const import CONF_ID + +CODEOWNERS = ["@ssieb"] + +DEPENDENCIES = ["uart"] + +MULTI_CONF = True + +vbus_ns = cg.esphome_ns.namespace("vbus") +VBus = vbus_ns.class_("VBus", uart.UARTDevice, cg.Component) + +CONF_VBUS_ID = "vbus_id" + +CONF_DELTASOL_BS_PLUS = "deltasol_bs_plus" +CONF_DELTASOL_C = "deltasol_c" +CONF_DELTASOL_CS2 = "deltasol_cs2" +CONF_DELTASOL_CS_PLUS = "deltasol_cs_plus" + +CONFIG_SCHEMA = uart.UART_DEVICE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(VBus), + } +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) diff --git a/esphome/components/vbus/binary_sensor/__init__.py b/esphome/components/vbus/binary_sensor/__init__.py new file mode 100644 index 0000000000..9901fb2724 --- /dev/null +++ b/esphome/components/vbus/binary_sensor/__init__.py @@ -0,0 +1,296 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import ( + CONF_ID, + CONF_BINARY_SENSORS, + CONF_COMMAND, + CONF_CUSTOM, + CONF_DEST, + CONF_LAMBDA, + CONF_MODEL, + CONF_SOURCE, + DEVICE_CLASS_PROBLEM, + ENTITY_CATEGORY_DIAGNOSTIC, +) +from .. import ( + vbus_ns, + VBus, + CONF_VBUS_ID, + CONF_DELTASOL_BS_PLUS, + CONF_DELTASOL_C, + CONF_DELTASOL_CS2, + CONF_DELTASOL_CS_PLUS, +) + +DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusBSensor", cg.Component) +DeltaSol_C = vbus_ns.class_("DeltaSolCBSensor", cg.Component) +DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2BSensor", cg.Component) +DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusBSensor", cg.Component) +VBusCustom = vbus_ns.class_("VBusCustomBSensor", cg.Component) +VBusCustomSub = vbus_ns.class_("VBusCustomSubBSensor", cg.Component) + +CONF_RELAY1 = "relay1" +CONF_RELAY2 = "relay2" +CONF_SENSOR1_ERROR = "sensor1_error" +CONF_SENSOR2_ERROR = "sensor2_error" +CONF_SENSOR3_ERROR = "sensor3_error" +CONF_SENSOR4_ERROR = "sensor4_error" +CONF_COLLECTOR_MAX = "collector_max" +CONF_COLLECTOR_MIN = "collector_min" +CONF_COLLECTOR_FROST = "collector_frost" +CONF_TUBE_COLLECTOR = "tube_collector" +CONF_RECOOLING = "recooling" +CONF_HQM = "hqm" + +CONFIG_SCHEMA = cv.typed_schema( + { + CONF_DELTASOL_BS_PLUS: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_BS_Plus), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_RELAY1): binary_sensor.binary_sensor_schema(), + cv.Optional(CONF_RELAY2): binary_sensor.binary_sensor_schema(), + cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_COLLECTOR_MAX): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_COLLECTOR_MIN): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_COLLECTOR_FROST): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_TUBE_COLLECTOR): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_RECOOLING): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_HQM): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ), + CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_C), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ), + CONF_DELTASOL_CS2: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_CS2), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ), + CONF_DELTASOL_CS_PLUS: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_CS_Plus), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ), + CONF_CUSTOM: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(VBusCustom), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_COMMAND): cv.uint16_t, + cv.Optional(CONF_SOURCE): cv.uint16_t, + cv.Optional(CONF_DEST): cv.uint16_t, + cv.Optional(CONF_BINARY_SENSORS): cv.ensure_list( + binary_sensor.binary_sensor_schema().extend( + { + cv.GenerateID(): cv.declare_id(VBusCustomSub), + cv.Required(CONF_LAMBDA): cv.lambda_, + } + ) + ), + } + ), + }, + key=CONF_MODEL, + lower=True, + space="_", +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + if config[CONF_MODEL] == CONF_DELTASOL_BS_PLUS: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x4221)) + cg.add(var.set_dest(0x0010)) + if CONF_RELAY1 in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_RELAY1]) + cg.add(var.set_relay1_bsensor(sens)) + if CONF_RELAY2 in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_RELAY2]) + cg.add(var.set_relay2_bsensor(sens)) + if CONF_SENSOR1_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR]) + cg.add(var.set_s1_error_bsensor(sens)) + if CONF_SENSOR2_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR]) + cg.add(var.set_s2_error_bsensor(sens)) + if CONF_SENSOR3_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR]) + cg.add(var.set_s3_error_bsensor(sens)) + if CONF_SENSOR4_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR]) + cg.add(var.set_s4_error_bsensor(sens)) + if CONF_COLLECTOR_MAX in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_COLLECTOR_MAX]) + cg.add(var.set_collector_max_bsensor(sens)) + if CONF_COLLECTOR_MIN in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_COLLECTOR_MIN]) + cg.add(var.set_collector_min_bsensor(sens)) + if CONF_COLLECTOR_FROST in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_COLLECTOR_FROST]) + cg.add(var.set_collector_frost_bsensor(sens)) + if CONF_TUBE_COLLECTOR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_TUBE_COLLECTOR]) + cg.add(var.set_tube_collector_bsensor(sens)) + if CONF_RECOOLING in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_RECOOLING]) + cg.add(var.set_recooling_bsensor(sens)) + if CONF_HQM in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_HQM]) + cg.add(var.set_hqm_bsensor(sens)) + + elif config[CONF_MODEL] == CONF_DELTASOL_C: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x4212)) + cg.add(var.set_dest(0x0010)) + if CONF_SENSOR1_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR]) + cg.add(var.set_s1_error_bsensor(sens)) + if CONF_SENSOR2_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR]) + cg.add(var.set_s2_error_bsensor(sens)) + if CONF_SENSOR3_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR]) + cg.add(var.set_s3_error_bsensor(sens)) + if CONF_SENSOR4_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR]) + cg.add(var.set_s4_error_bsensor(sens)) + + elif config[CONF_MODEL] == CONF_DELTASOL_CS2: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x1121)) + cg.add(var.set_dest(0x0010)) + if CONF_SENSOR1_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR]) + cg.add(var.set_s1_error_bsensor(sens)) + if CONF_SENSOR2_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR]) + cg.add(var.set_s2_error_bsensor(sens)) + if CONF_SENSOR3_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR]) + cg.add(var.set_s3_error_bsensor(sens)) + if CONF_SENSOR4_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR]) + cg.add(var.set_s4_error_bsensor(sens)) + + elif config[CONF_MODEL] == CONF_DELTASOL_CS_PLUS: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x2211)) + cg.add(var.set_dest(0x0010)) + if CONF_SENSOR1_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR]) + cg.add(var.set_s1_error_bsensor(sens)) + if CONF_SENSOR2_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR]) + cg.add(var.set_s2_error_bsensor(sens)) + if CONF_SENSOR3_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR]) + cg.add(var.set_s3_error_bsensor(sens)) + if CONF_SENSOR4_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR]) + cg.add(var.set_s4_error_bsensor(sens)) + + elif config[CONF_MODEL] == CONF_CUSTOM: + if CONF_COMMAND in config: + cg.add(var.set_command(config[CONF_COMMAND])) + if CONF_SOURCE in config: + cg.add(var.set_source(config[CONF_SOURCE])) + if CONF_DEST in config: + cg.add(var.set_dest(config[CONF_DEST])) + bsensors = [] + for conf in config[CONF_BINARY_SENSORS]: + bsens = await binary_sensor.new_binary_sensor(conf) + lambda_ = await cg.process_lambda( + conf[CONF_LAMBDA], + [(cg.std_vector.template(cg.uint8), "x")], + return_type=cg.bool_, + ) + cg.add(bsens.set_message_parser(lambda_)) + bsensors.append(bsens) + cg.add(var.set_bsensors(bsensors)) + + vbus = await cg.get_variable(config[CONF_VBUS_ID]) + cg.add(vbus.register_listener(var)) diff --git a/esphome/components/vbus/binary_sensor/vbus_binary_sensor.cpp b/esphome/components/vbus/binary_sensor/vbus_binary_sensor.cpp new file mode 100644 index 0000000000..6edbae22ba --- /dev/null +++ b/esphome/components/vbus/binary_sensor/vbus_binary_sensor.cpp @@ -0,0 +1,142 @@ +#include "vbus_binary_sensor.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace vbus { + +static const char *const TAG = "vbus.binary_sensor"; + +void DeltaSolBSPlusBSensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol BS Plus:"); + LOG_BINARY_SENSOR(" ", "Relay 1 On", this->relay1_bsensor_); + LOG_BINARY_SENSOR(" ", "Relay 2 On", this->relay2_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 2 Error", this->s2_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 3 Error", this->s3_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 4 Error", this->s4_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Option Collector Max", this->collector_max_bsensor_); + LOG_BINARY_SENSOR(" ", "Option Collector Min", this->collector_min_bsensor_); + LOG_BINARY_SENSOR(" ", "Option Collector Frost", this->collector_frost_bsensor_); + LOG_BINARY_SENSOR(" ", "Option Tube Collector", this->tube_collector_bsensor_); + LOG_BINARY_SENSOR(" ", "Option Recooling", this->recooling_bsensor_); + LOG_BINARY_SENSOR(" ", "Option Heat Quantity Measurement", this->hqm_bsensor_); +} + +void DeltaSolBSPlusBSensor::handle_message(std::vector &message) { + if (this->relay1_bsensor_ != nullptr) + this->relay1_bsensor_->publish_state(message[10] & 1); + if (this->relay2_bsensor_ != nullptr) + this->relay2_bsensor_->publish_state(message[10] & 2); + if (this->s1_error_bsensor_ != nullptr) + this->s1_error_bsensor_->publish_state(message[11] & 1); + if (this->s2_error_bsensor_ != nullptr) + this->s2_error_bsensor_->publish_state(message[11] & 2); + if (this->s3_error_bsensor_ != nullptr) + this->s3_error_bsensor_->publish_state(message[11] & 4); + if (this->s4_error_bsensor_ != nullptr) + this->s4_error_bsensor_->publish_state(message[11] & 8); + if (this->collector_max_bsensor_ != nullptr) + this->collector_max_bsensor_->publish_state(message[15] & 1); + if (this->collector_min_bsensor_ != nullptr) + this->collector_min_bsensor_->publish_state(message[15] & 2); + if (this->collector_frost_bsensor_ != nullptr) + this->collector_frost_bsensor_->publish_state(message[15] & 4); + if (this->tube_collector_bsensor_ != nullptr) + this->tube_collector_bsensor_->publish_state(message[15] & 8); + if (this->recooling_bsensor_ != nullptr) + this->recooling_bsensor_->publish_state(message[15] & 0x10); + if (this->hqm_bsensor_ != nullptr) + this->hqm_bsensor_->publish_state(message[15] & 0x20); +} + +void DeltaSolCBSensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol C:"); + LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 2 Error", this->s2_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 3 Error", this->s3_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 4 Error", this->s4_error_bsensor_); +} + +void DeltaSolCBSensor::handle_message(std::vector &message) { + if (this->s1_error_bsensor_ != nullptr) + this->s1_error_bsensor_->publish_state(message[10] & 1); + if (this->s2_error_bsensor_ != nullptr) + this->s2_error_bsensor_->publish_state(message[10] & 2); + if (this->s3_error_bsensor_ != nullptr) + this->s3_error_bsensor_->publish_state(message[10] & 4); + if (this->s4_error_bsensor_ != nullptr) + this->s4_error_bsensor_->publish_state(message[10] & 8); +} + +void DeltaSolCS2BSensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol CS2:"); + LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 2 Error", this->s2_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 3 Error", this->s3_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 4 Error", this->s4_error_bsensor_); +} + +void DeltaSolCS2BSensor::handle_message(std::vector &message) { + if (this->s1_error_bsensor_ != nullptr) + this->s1_error_bsensor_->publish_state(message[18] & 1); + if (this->s2_error_bsensor_ != nullptr) + this->s2_error_bsensor_->publish_state(message[18] & 2); + if (this->s3_error_bsensor_ != nullptr) + this->s3_error_bsensor_->publish_state(message[18] & 4); + if (this->s4_error_bsensor_ != nullptr) + this->s4_error_bsensor_->publish_state(message[18] & 8); +} + +void DeltaSolCSPlusBSensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol CS Plus:"); + LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 2 Error", this->s2_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 3 Error", this->s3_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 4 Error", this->s4_error_bsensor_); +} + +void DeltaSolCSPlusBSensor::handle_message(std::vector &message) { + if (this->s1_error_bsensor_ != nullptr) + this->s1_error_bsensor_->publish_state(message[20] & 1); + if (this->s2_error_bsensor_ != nullptr) + this->s2_error_bsensor_->publish_state(message[20] & 2); + if (this->s3_error_bsensor_ != nullptr) + this->s3_error_bsensor_->publish_state(message[20] & 4); + if (this->s4_error_bsensor_ != nullptr) + this->s4_error_bsensor_->publish_state(message[20] & 8); +} + +void VBusCustomBSensor::dump_config() { + ESP_LOGCONFIG(TAG, "VBus Custom Binary Sensor:"); + if (this->source_ == 0xffff) { + ESP_LOGCONFIG(TAG, " Source address: ANY"); + } else { + ESP_LOGCONFIG(TAG, " Source address: 0x%04x", this->source_); + } + if (this->dest_ == 0xffff) { + ESP_LOGCONFIG(TAG, " Dest address: ANY"); + } else { + ESP_LOGCONFIG(TAG, " Dest address: 0x%04x", this->dest_); + } + if (this->command_ == 0xffff) { + ESP_LOGCONFIG(TAG, " Command: ANY"); + } else { + ESP_LOGCONFIG(TAG, " Command: 0x%04x", this->command_); + } + ESP_LOGCONFIG(TAG, " Binary Sensors:"); + for (VBusCustomSubBSensor *bsensor : this->bsensors_) + LOG_BINARY_SENSOR(" ", "-", bsensor); +} + +void VBusCustomBSensor::handle_message(std::vector &message) { + for (VBusCustomSubBSensor *bsensor : this->bsensors_) + bsensor->parse_message(message); +} + +void VBusCustomSubBSensor::parse_message(std::vector &message) { + this->publish_state(this->message_parser_(message)); +} + +} // namespace vbus +} // namespace esphome diff --git a/esphome/components/vbus/binary_sensor/vbus_binary_sensor.h b/esphome/components/vbus/binary_sensor/vbus_binary_sensor.h new file mode 100644 index 0000000000..c0a823a0ab --- /dev/null +++ b/esphome/components/vbus/binary_sensor/vbus_binary_sensor.h @@ -0,0 +1,115 @@ +#pragma once + +#include "../vbus.h" +#include "esphome/components/binary_sensor/binary_sensor.h" + +namespace esphome { +namespace vbus { + +class DeltaSolBSPlusBSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_relay1_bsensor(binary_sensor::BinarySensor *bsensor) { this->relay1_bsensor_ = bsensor; } + void set_relay2_bsensor(binary_sensor::BinarySensor *bsensor) { this->relay2_bsensor_ = bsensor; } + void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; } + void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; } + void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; } + void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; } + void set_collector_max_bsensor(binary_sensor::BinarySensor *bsensor) { this->collector_max_bsensor_ = bsensor; } + void set_collector_min_bsensor(binary_sensor::BinarySensor *bsensor) { this->collector_min_bsensor_ = bsensor; } + void set_collector_frost_bsensor(binary_sensor::BinarySensor *bsensor) { this->collector_frost_bsensor_ = bsensor; } + void set_tube_collector_bsensor(binary_sensor::BinarySensor *bsensor) { this->tube_collector_bsensor_ = bsensor; } + void set_recooling_bsensor(binary_sensor::BinarySensor *bsensor) { this->recooling_bsensor_ = bsensor; } + void set_hqm_bsensor(binary_sensor::BinarySensor *bsensor) { this->hqm_bsensor_ = bsensor; } + + protected: + binary_sensor::BinarySensor *relay1_bsensor_{nullptr}; + binary_sensor::BinarySensor *relay2_bsensor_{nullptr}; + binary_sensor::BinarySensor *s1_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s2_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s3_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s4_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *collector_max_bsensor_{nullptr}; + binary_sensor::BinarySensor *collector_min_bsensor_{nullptr}; + binary_sensor::BinarySensor *collector_frost_bsensor_{nullptr}; + binary_sensor::BinarySensor *tube_collector_bsensor_{nullptr}; + binary_sensor::BinarySensor *recooling_bsensor_{nullptr}; + binary_sensor::BinarySensor *hqm_bsensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + +class DeltaSolCBSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; } + void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; } + void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; } + void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; } + + protected: + binary_sensor::BinarySensor *s1_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s2_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s3_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s4_error_bsensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + +class DeltaSolCS2BSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; } + void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; } + void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; } + void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; } + + protected: + binary_sensor::BinarySensor *s1_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s2_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s3_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s4_error_bsensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + +class DeltaSolCSPlusBSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; } + void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; } + void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; } + void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; } + + protected: + binary_sensor::BinarySensor *s1_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s2_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s3_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s4_error_bsensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + +class VBusCustomSubBSensor; + +class VBusCustomBSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_bsensors(std::vector bsensors) { this->bsensors_ = std::move(bsensors); }; + + protected: + std::vector bsensors_; + void handle_message(std::vector &message) override; +}; + +class VBusCustomSubBSensor : public binary_sensor::BinarySensor, public Component { + public: + void set_message_parser(message_parser_t parser) { this->message_parser_ = std::move(parser); }; + void parse_message(std::vector &message); + + protected: + message_parser_t message_parser_; +}; + +} // namespace vbus +} // namespace esphome diff --git a/esphome/components/vbus/sensor/__init__.py b/esphome/components/vbus/sensor/__init__.py new file mode 100644 index 0000000000..bce28758ce --- /dev/null +++ b/esphome/components/vbus/sensor/__init__.py @@ -0,0 +1,568 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor +from esphome.const import ( + CONF_ID, + CONF_COMMAND, + CONF_CUSTOM, + CONF_DEST, + CONF_LAMBDA, + CONF_MODEL, + CONF_SENSORS, + CONF_SOURCE, + CONF_TIME, + CONF_VERSION, + DEVICE_CLASS_DURATION, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_TEMPERATURE, + ENTITY_CATEGORY_DIAGNOSTIC, + ICON_PERCENT, + ICON_RADIATOR, + ICON_THERMOMETER, + ICON_TIMER, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_HOUR, + UNIT_MINUTE, + UNIT_PERCENT, + UNIT_WATT_HOURS, +) +from .. import ( + vbus_ns, + VBus, + CONF_VBUS_ID, + CONF_DELTASOL_BS_PLUS, + CONF_DELTASOL_C, + CONF_DELTASOL_CS2, + CONF_DELTASOL_CS_PLUS, +) + +DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusSensor", cg.Component) +DeltaSol_C = vbus_ns.class_("DeltaSolCSensor", cg.Component) +DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2Sensor", cg.Component) +DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusSensor", cg.Component) +VBusCustom = vbus_ns.class_("VBusCustomSensor", cg.Component) +VBusCustomSub = vbus_ns.class_("VBusCustomSubSensor", cg.Component) + +CONF_FLOW_RATE = "flow_rate" +CONF_HEAT_QUANTITY = "heat_quantity" +CONF_OPERATING_HOURS = "operating_hours" +CONF_OPERATING_HOURS_1 = "operating_hours_1" +CONF_OPERATING_HOURS_2 = "operating_hours_2" +CONF_PUMP_SPEED = "pump_speed" +CONF_PUMP_SPEED_1 = "pump_speed_1" +CONF_PUMP_SPEED_2 = "pump_speed_2" +CONF_TEMPERATURE_1 = "temperature_1" +CONF_TEMPERATURE_2 = "temperature_2" +CONF_TEMPERATURE_3 = "temperature_3" +CONF_TEMPERATURE_4 = "temperature_4" +CONF_TEMPERATURE_5 = "temperature_5" + +CONFIG_SCHEMA = cv.typed_schema( + { + CONF_DELTASOL_BS_PLUS: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_BS_Plus), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PUMP_SPEED_1): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PUMP_SPEED_2): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_OPERATING_HOURS_1): sensor.sensor_schema( + unit_of_measurement=UNIT_HOUR, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_OPERATING_HOURS_2): sensor.sensor_schema( + unit_of_measurement=UNIT_HOUR, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema( + unit_of_measurement=UNIT_WATT_HOURS, + icon=ICON_RADIATOR, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TIME): sensor.sensor_schema( + unit_of_measurement=UNIT_MINUTE, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_VERSION): sensor.sensor_schema( + accuracy_decimals=2, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ), + CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_C), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PUMP_SPEED_1): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PUMP_SPEED_2): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_OPERATING_HOURS_1): sensor.sensor_schema( + unit_of_measurement=UNIT_HOUR, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_OPERATING_HOURS_2): sensor.sensor_schema( + unit_of_measurement=UNIT_HOUR, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema( + unit_of_measurement=UNIT_WATT_HOURS, + icon=ICON_RADIATOR, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TIME): sensor.sensor_schema( + unit_of_measurement=UNIT_MINUTE, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ), + CONF_DELTASOL_CS2: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_CS2), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PUMP_SPEED): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_OPERATING_HOURS): sensor.sensor_schema( + unit_of_measurement=UNIT_HOUR, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema( + unit_of_measurement=UNIT_WATT_HOURS, + icon=ICON_RADIATOR, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_VERSION): sensor.sensor_schema( + accuracy_decimals=2, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ), + CONF_DELTASOL_CS_PLUS: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_CS_Plus), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_5): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PUMP_SPEED_1): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PUMP_SPEED_2): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_OPERATING_HOURS_1): sensor.sensor_schema( + unit_of_measurement=UNIT_HOUR, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_OPERATING_HOURS_2): sensor.sensor_schema( + unit_of_measurement=UNIT_HOUR, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema( + unit_of_measurement=UNIT_WATT_HOURS, + icon=ICON_RADIATOR, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TIME): sensor.sensor_schema( + unit_of_measurement=UNIT_MINUTE, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_VERSION): sensor.sensor_schema( + accuracy_decimals=2, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_FLOW_RATE): sensor.sensor_schema( + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ), + CONF_CUSTOM: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(VBusCustom), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_COMMAND): cv.uint16_t, + cv.Optional(CONF_SOURCE): cv.uint16_t, + cv.Optional(CONF_DEST): cv.uint16_t, + cv.Optional(CONF_SENSORS): cv.ensure_list( + sensor.sensor_schema().extend( + { + cv.GenerateID(): cv.declare_id(VBusCustomSub), + cv.Required(CONF_LAMBDA): cv.lambda_, + } + ) + ), + } + ), + }, + key=CONF_MODEL, + lower=True, + space="_", +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + if config[CONF_MODEL] == CONF_DELTASOL_BS_PLUS: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x4221)) + cg.add(var.set_dest(0x0010)) + if CONF_TEMPERATURE_1 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1]) + cg.add(var.set_temperature1_sensor(sens)) + if CONF_TEMPERATURE_2 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2]) + cg.add(var.set_temperature2_sensor(sens)) + if CONF_TEMPERATURE_3 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3]) + cg.add(var.set_temperature3_sensor(sens)) + if CONF_TEMPERATURE_4 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4]) + cg.add(var.set_temperature4_sensor(sens)) + if CONF_PUMP_SPEED_1 in config: + sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_1]) + cg.add(var.set_pump_speed1_sensor(sens)) + if CONF_PUMP_SPEED_2 in config: + sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_2]) + cg.add(var.set_pump_speed2_sensor(sens)) + if CONF_OPERATING_HOURS_1 in config: + sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_1]) + cg.add(var.set_operating_hours1_sensor(sens)) + if CONF_OPERATING_HOURS_2 in config: + sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_2]) + cg.add(var.set_operating_hours2_sensor(sens)) + if CONF_HEAT_QUANTITY in config: + sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY]) + cg.add(var.set_heat_quantity_sensor(sens)) + if CONF_TIME in config: + sens = await sensor.new_sensor(config[CONF_TIME]) + cg.add(var.set_time_sensor(sens)) + if CONF_VERSION in config: + sens = await sensor.new_sensor(config[CONF_VERSION]) + cg.add(var.set_version_sensor(sens)) + + elif config[CONF_MODEL] == CONF_DELTASOL_C: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x4212)) + cg.add(var.set_dest(0x0010)) + if CONF_TEMPERATURE_1 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1]) + cg.add(var.set_temperature1_sensor(sens)) + if CONF_TEMPERATURE_2 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2]) + cg.add(var.set_temperature2_sensor(sens)) + if CONF_TEMPERATURE_3 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3]) + cg.add(var.set_temperature3_sensor(sens)) + if CONF_TEMPERATURE_4 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4]) + cg.add(var.set_temperature4_sensor(sens)) + if CONF_PUMP_SPEED_1 in config: + sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_1]) + cg.add(var.set_pump_speed1_sensor(sens)) + if CONF_PUMP_SPEED_2 in config: + sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_2]) + cg.add(var.set_pump_speed2_sensor(sens)) + if CONF_OPERATING_HOURS_1 in config: + sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_1]) + cg.add(var.set_operating_hours1_sensor(sens)) + if CONF_OPERATING_HOURS_2 in config: + sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_2]) + cg.add(var.set_operating_hours2_sensor(sens)) + if CONF_HEAT_QUANTITY in config: + sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY]) + cg.add(var.set_heat_quantity_sensor(sens)) + if CONF_TIME in config: + sens = await sensor.new_sensor(config[CONF_TIME]) + cg.add(var.set_time_sensor(sens)) + + elif config[CONF_MODEL] == CONF_DELTASOL_CS2: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x1121)) + cg.add(var.set_dest(0x0010)) + if CONF_TEMPERATURE_1 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1]) + cg.add(var.set_temperature1_sensor(sens)) + if CONF_TEMPERATURE_2 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2]) + cg.add(var.set_temperature2_sensor(sens)) + if CONF_TEMPERATURE_3 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3]) + cg.add(var.set_temperature3_sensor(sens)) + if CONF_TEMPERATURE_4 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4]) + cg.add(var.set_temperature4_sensor(sens)) + if CONF_PUMP_SPEED in config: + sens = await sensor.new_sensor(config[CONF_PUMP_SPEED]) + cg.add(var.set_pump_speed_sensor(sens)) + if CONF_OPERATING_HOURS in config: + sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS]) + cg.add(var.set_operating_hours_sensor(sens)) + if CONF_HEAT_QUANTITY in config: + sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY]) + cg.add(var.set_heat_quantity_sensor(sens)) + if CONF_VERSION in config: + sens = await sensor.new_sensor(config[CONF_VERSION]) + cg.add(var.set_version_sensor(sens)) + + if config[CONF_MODEL] == CONF_DELTASOL_CS_PLUS: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x2211)) + cg.add(var.set_dest(0x0010)) + if CONF_TEMPERATURE_1 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1]) + cg.add(var.set_temperature1_sensor(sens)) + if CONF_TEMPERATURE_2 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2]) + cg.add(var.set_temperature2_sensor(sens)) + if CONF_TEMPERATURE_3 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3]) + cg.add(var.set_temperature3_sensor(sens)) + if CONF_TEMPERATURE_4 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4]) + cg.add(var.set_temperature4_sensor(sens)) + if CONF_TEMPERATURE_5 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_5]) + cg.add(var.set_temperature5_sensor(sens)) + if CONF_PUMP_SPEED_1 in config: + sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_1]) + cg.add(var.set_pump_speed1_sensor(sens)) + if CONF_PUMP_SPEED_2 in config: + sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_2]) + cg.add(var.set_pump_speed2_sensor(sens)) + if CONF_OPERATING_HOURS_1 in config: + sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_1]) + cg.add(var.set_operating_hours1_sensor(sens)) + if CONF_OPERATING_HOURS_2 in config: + sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_2]) + cg.add(var.set_operating_hours2_sensor(sens)) + if CONF_HEAT_QUANTITY in config: + sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY]) + cg.add(var.set_heat_quantity_sensor(sens)) + if CONF_TIME in config: + sens = await sensor.new_sensor(config[CONF_TIME]) + cg.add(var.set_time_sensor(sens)) + if CONF_VERSION in config: + sens = await sensor.new_sensor(config[CONF_VERSION]) + cg.add(var.set_version_sensor(sens)) + if CONF_FLOW_RATE in config: + sens = await sensor.new_sensor(config[CONF_FLOW_RATE]) + cg.add(var.set_flow_rate_sensor(sens)) + + elif config[CONF_MODEL] == CONF_CUSTOM: + if CONF_COMMAND in config: + cg.add(var.set_command(config[CONF_COMMAND])) + if CONF_SOURCE in config: + cg.add(var.set_source(config[CONF_SOURCE])) + if CONF_DEST in config: + cg.add(var.set_dest(config[CONF_DEST])) + sensors = [] + for conf in config[CONF_SENSORS]: + sens = await sensor.new_sensor(conf) + lambda_ = await cg.process_lambda( + conf[CONF_LAMBDA], + [(cg.std_vector.template(cg.uint8), "x")], + return_type=cg.float_, + ) + cg.add(sens.set_message_parser(lambda_)) + sensors.append(sens) + cg.add(var.set_sensors(sensors)) + + vbus = await cg.get_variable(config[CONF_VBUS_ID]) + cg.add(vbus.register_listener(var)) diff --git a/esphome/components/vbus/sensor/vbus_sensor.cpp b/esphome/components/vbus/sensor/vbus_sensor.cpp new file mode 100644 index 0000000000..57d5a355ad --- /dev/null +++ b/esphome/components/vbus/sensor/vbus_sensor.cpp @@ -0,0 +1,208 @@ +#include "vbus_sensor.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace vbus { + +static const char *const TAG = "vbus.sensor"; + +static inline uint16_t get_u16(std::vector &message, int start) { + return (message[start + 1] << 8) + message[start]; +} + +static inline int16_t get_i16(std::vector &message, int start) { + return (int16_t)((message[start + 1] << 8) + message[start]); +} + +void DeltaSolBSPlusSensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol BS Plus:"); + LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_); + LOG_SENSOR(" ", "Temperature 2", this->temperature2_sensor_); + LOG_SENSOR(" ", "Temperature 3", this->temperature3_sensor_); + LOG_SENSOR(" ", "Temperature 4", this->temperature4_sensor_); + LOG_SENSOR(" ", "Pump Speed 1", this->pump_speed1_sensor_); + LOG_SENSOR(" ", "Pump Speed 2", this->pump_speed2_sensor_); + LOG_SENSOR(" ", "Operating Hours 1", this->operating_hours1_sensor_); + LOG_SENSOR(" ", "Operating Hours 2", this->operating_hours2_sensor_); + LOG_SENSOR(" ", "Heat Quantity", this->heat_quantity_sensor_); + LOG_SENSOR(" ", "System Time", this->time_sensor_); + LOG_SENSOR(" ", "FW Version", this->version_sensor_); +} + +void DeltaSolBSPlusSensor::handle_message(std::vector &message) { + if (this->temperature1_sensor_ != nullptr) + this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f); + if (this->temperature2_sensor_ != nullptr) + this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f); + if (this->temperature3_sensor_ != nullptr) + this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f); + if (this->temperature4_sensor_ != nullptr) + this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f); + if (this->pump_speed1_sensor_ != nullptr) + this->pump_speed1_sensor_->publish_state(message[8]); + if (this->pump_speed2_sensor_ != nullptr) + this->pump_speed2_sensor_->publish_state(message[9]); + if (this->operating_hours1_sensor_ != nullptr) + this->operating_hours1_sensor_->publish_state(get_u16(message, 16)); + if (this->operating_hours2_sensor_ != nullptr) + this->operating_hours2_sensor_->publish_state(get_u16(message, 18)); + if (this->heat_quantity_sensor_ != nullptr) { + this->heat_quantity_sensor_->publish_state(get_u16(message, 20) + get_u16(message, 22) * 1000 + + get_u16(message, 24) * 1000000); + } + if (this->time_sensor_ != nullptr) + this->time_sensor_->publish_state(get_u16(message, 12)); + if (this->version_sensor_ != nullptr) + this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f); +} + +void DeltaSolCSensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol C:"); + LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_); + LOG_SENSOR(" ", "Temperature 2", this->temperature2_sensor_); + LOG_SENSOR(" ", "Temperature 3", this->temperature3_sensor_); + LOG_SENSOR(" ", "Temperature 4", this->temperature4_sensor_); + LOG_SENSOR(" ", "Pump Speed 1", this->pump_speed1_sensor_); + LOG_SENSOR(" ", "Pump Speed 2", this->pump_speed2_sensor_); + LOG_SENSOR(" ", "Operating Hours 1", this->operating_hours1_sensor_); + LOG_SENSOR(" ", "Operating Hours 2", this->operating_hours2_sensor_); + LOG_SENSOR(" ", "Heat Quantity", this->heat_quantity_sensor_); + LOG_SENSOR(" ", "System Time", this->time_sensor_); +} + +void DeltaSolCSensor::handle_message(std::vector &message) { + if (this->temperature1_sensor_ != nullptr) + this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f); + if (this->temperature2_sensor_ != nullptr) + this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f); + if (this->temperature3_sensor_ != nullptr) + this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f); + if (this->temperature4_sensor_ != nullptr) + this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f); + if (this->pump_speed1_sensor_ != nullptr) + this->pump_speed1_sensor_->publish_state(message[8]); + if (this->pump_speed2_sensor_ != nullptr) + this->pump_speed2_sensor_->publish_state(message[9]); + if (this->operating_hours1_sensor_ != nullptr) + this->operating_hours1_sensor_->publish_state(get_u16(message, 12)); + if (this->operating_hours2_sensor_ != nullptr) + this->operating_hours2_sensor_->publish_state(get_u16(message, 14)); + if (this->heat_quantity_sensor_ != nullptr) { + this->heat_quantity_sensor_->publish_state(get_u16(message, 16) + get_u16(message, 18) * 1000 + + get_u16(message, 20) * 1000000); + } + if (this->time_sensor_ != nullptr) + this->time_sensor_->publish_state(get_u16(message, 22)); +} + +void DeltaSolCS2Sensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol CS2:"); + LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_); + LOG_SENSOR(" ", "Temperature 2", this->temperature2_sensor_); + LOG_SENSOR(" ", "Temperature 3", this->temperature3_sensor_); + LOG_SENSOR(" ", "Temperature 4", this->temperature4_sensor_); + LOG_SENSOR(" ", "Pump Speed", this->pump_speed_sensor_); + LOG_SENSOR(" ", "Operating Hours", this->operating_hours_sensor_); + LOG_SENSOR(" ", "Heat Quantity", this->heat_quantity_sensor_); + LOG_SENSOR(" ", "FW Version", this->version_sensor_); +} + +void DeltaSolCS2Sensor::handle_message(std::vector &message) { + if (this->temperature1_sensor_ != nullptr) + this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f); + if (this->temperature2_sensor_ != nullptr) + this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f); + if (this->temperature3_sensor_ != nullptr) + this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f); + if (this->temperature4_sensor_ != nullptr) + this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f); + if (this->pump_speed_sensor_ != nullptr) + this->pump_speed_sensor_->publish_state(message[12]); + if (this->operating_hours_sensor_ != nullptr) + this->operating_hours_sensor_->publish_state(get_u16(message, 14)); + if (this->heat_quantity_sensor_ != nullptr) + this->heat_quantity_sensor_->publish_state((get_u16(message, 26) << 16) + get_u16(message, 24)); + if (this->version_sensor_ != nullptr) + this->version_sensor_->publish_state(get_u16(message, 28) * 0.01f); +} + +void DeltaSolCSPlusSensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol CS Plus:"); + LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_); + LOG_SENSOR(" ", "Temperature 2", this->temperature2_sensor_); + LOG_SENSOR(" ", "Temperature 3", this->temperature3_sensor_); + LOG_SENSOR(" ", "Temperature 4", this->temperature4_sensor_); + LOG_SENSOR(" ", "Temperature 5", this->temperature5_sensor_); + LOG_SENSOR(" ", "Pump Speed 1", this->pump_speed1_sensor_); + LOG_SENSOR(" ", "Pump Speed 2", this->pump_speed2_sensor_); + LOG_SENSOR(" ", "Operating Hours 1", this->operating_hours1_sensor_); + LOG_SENSOR(" ", "Operating Hours 2", this->operating_hours2_sensor_); + LOG_SENSOR(" ", "Heat Quantity", this->heat_quantity_sensor_); + LOG_SENSOR(" ", "System Time", this->time_sensor_); + LOG_SENSOR(" ", "FW Version", this->version_sensor_); + LOG_SENSOR(" ", "Flow Rate", this->flow_rate_sensor_); +} + +void DeltaSolCSPlusSensor::handle_message(std::vector &message) { + if (this->temperature1_sensor_ != nullptr) + this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f); + if (this->temperature2_sensor_ != nullptr) + this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f); + if (this->temperature3_sensor_ != nullptr) + this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f); + if (this->temperature4_sensor_ != nullptr) + this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f); + if (this->temperature5_sensor_ != nullptr) + this->temperature5_sensor_->publish_state(get_i16(message, 36) * 0.1f); + if (this->pump_speed1_sensor_ != nullptr) + this->pump_speed1_sensor_->publish_state(message[8]); + if (this->pump_speed2_sensor_ != nullptr) + this->pump_speed2_sensor_->publish_state(message[12]); + if (this->operating_hours1_sensor_ != nullptr) + this->operating_hours1_sensor_->publish_state(get_u16(message, 10)); + if (this->operating_hours2_sensor_ != nullptr) + this->operating_hours2_sensor_->publish_state(get_u16(message, 14)); + if (this->heat_quantity_sensor_ != nullptr) + this->heat_quantity_sensor_->publish_state((get_u16(message, 30) << 16) + get_u16(message, 28)); + if (this->time_sensor_ != nullptr) + this->time_sensor_->publish_state(get_u16(message, 12)); + if (this->version_sensor_ != nullptr) + this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f); + if (this->flow_rate_sensor_ != nullptr) + this->flow_rate_sensor_->publish_state(get_u16(message, 38)); +} + +void VBusCustomSensor::dump_config() { + ESP_LOGCONFIG(TAG, "VBus Custom Sensor:"); + if (this->source_ == 0xffff) { + ESP_LOGCONFIG(TAG, " Source address: ANY"); + } else { + ESP_LOGCONFIG(TAG, " Source address: 0x%04x", this->source_); + } + if (this->dest_ == 0xffff) { + ESP_LOGCONFIG(TAG, " Dest address: ANY"); + } else { + ESP_LOGCONFIG(TAG, " Dest address: 0x%04x", this->dest_); + } + if (this->command_ == 0xffff) { + ESP_LOGCONFIG(TAG, " Command: ANY"); + } else { + ESP_LOGCONFIG(TAG, " Command: 0x%04x", this->command_); + } + ESP_LOGCONFIG(TAG, " Sensors:"); + for (VBusCustomSubSensor *sensor : this->sensors_) + LOG_SENSOR(" ", "-", sensor); +} + +void VBusCustomSensor::handle_message(std::vector &message) { + for (VBusCustomSubSensor *sensor : this->sensors_) + sensor->parse_message(message); +} + +void VBusCustomSubSensor::parse_message(std::vector &message) { + this->publish_state(this->message_parser_(message)); +} + +} // namespace vbus +} // namespace esphome diff --git a/esphome/components/vbus/sensor/vbus_sensor.h b/esphome/components/vbus/sensor/vbus_sensor.h new file mode 100644 index 0000000000..6ba752b68c --- /dev/null +++ b/esphome/components/vbus/sensor/vbus_sensor.h @@ -0,0 +1,151 @@ +#pragma once + +#include "../vbus.h" +#include "esphome/components/sensor/sensor.h" + +namespace esphome { +namespace vbus { + +class DeltaSolBSPlusSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; } + void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; } + void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; } + void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; } + void set_pump_speed1_sensor(sensor::Sensor *sensor) { this->pump_speed1_sensor_ = sensor; } + void set_pump_speed2_sensor(sensor::Sensor *sensor) { this->pump_speed2_sensor_ = sensor; } + void set_operating_hours1_sensor(sensor::Sensor *sensor) { this->operating_hours1_sensor_ = sensor; } + void set_operating_hours2_sensor(sensor::Sensor *sensor) { this->operating_hours2_sensor_ = sensor; } + void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; } + void set_time_sensor(sensor::Sensor *sensor) { this->time_sensor_ = sensor; } + void set_version_sensor(sensor::Sensor *sensor) { this->version_sensor_ = sensor; } + + protected: + sensor::Sensor *temperature1_sensor_{nullptr}; + sensor::Sensor *temperature2_sensor_{nullptr}; + sensor::Sensor *temperature3_sensor_{nullptr}; + sensor::Sensor *temperature4_sensor_{nullptr}; + sensor::Sensor *pump_speed1_sensor_{nullptr}; + sensor::Sensor *pump_speed2_sensor_{nullptr}; + sensor::Sensor *operating_hours1_sensor_{nullptr}; + sensor::Sensor *operating_hours2_sensor_{nullptr}; + sensor::Sensor *heat_quantity_sensor_{nullptr}; + sensor::Sensor *time_sensor_{nullptr}; + sensor::Sensor *version_sensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + +class DeltaSolCSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; } + void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; } + void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; } + void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; } + void set_pump_speed1_sensor(sensor::Sensor *sensor) { this->pump_speed1_sensor_ = sensor; } + void set_pump_speed2_sensor(sensor::Sensor *sensor) { this->pump_speed2_sensor_ = sensor; } + void set_operating_hours1_sensor(sensor::Sensor *sensor) { this->operating_hours1_sensor_ = sensor; } + void set_operating_hours2_sensor(sensor::Sensor *sensor) { this->operating_hours2_sensor_ = sensor; } + void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; } + void set_time_sensor(sensor::Sensor *sensor) { this->time_sensor_ = sensor; } + + protected: + sensor::Sensor *temperature1_sensor_{nullptr}; + sensor::Sensor *temperature2_sensor_{nullptr}; + sensor::Sensor *temperature3_sensor_{nullptr}; + sensor::Sensor *temperature4_sensor_{nullptr}; + sensor::Sensor *pump_speed1_sensor_{nullptr}; + sensor::Sensor *pump_speed2_sensor_{nullptr}; + sensor::Sensor *operating_hours1_sensor_{nullptr}; + sensor::Sensor *operating_hours2_sensor_{nullptr}; + sensor::Sensor *heat_quantity_sensor_{nullptr}; + sensor::Sensor *time_sensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + +class DeltaSolCS2Sensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; } + void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; } + void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; } + void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; } + void set_pump_speed_sensor(sensor::Sensor *sensor) { this->pump_speed_sensor_ = sensor; } + void set_operating_hours_sensor(sensor::Sensor *sensor) { this->operating_hours_sensor_ = sensor; } + void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; } + void set_version_sensor(sensor::Sensor *sensor) { this->version_sensor_ = sensor; } + + protected: + sensor::Sensor *temperature1_sensor_{nullptr}; + sensor::Sensor *temperature2_sensor_{nullptr}; + sensor::Sensor *temperature3_sensor_{nullptr}; + sensor::Sensor *temperature4_sensor_{nullptr}; + sensor::Sensor *pump_speed_sensor_{nullptr}; + sensor::Sensor *operating_hours_sensor_{nullptr}; + sensor::Sensor *heat_quantity_sensor_{nullptr}; + sensor::Sensor *version_sensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + +class DeltaSolCSPlusSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; } + void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; } + void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; } + void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; } + void set_temperature5_sensor(sensor::Sensor *sensor) { this->temperature5_sensor_ = sensor; } + void set_pump_speed1_sensor(sensor::Sensor *sensor) { this->pump_speed1_sensor_ = sensor; } + void set_pump_speed2_sensor(sensor::Sensor *sensor) { this->pump_speed2_sensor_ = sensor; } + void set_operating_hours1_sensor(sensor::Sensor *sensor) { this->operating_hours1_sensor_ = sensor; } + void set_operating_hours2_sensor(sensor::Sensor *sensor) { this->operating_hours2_sensor_ = sensor; } + void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; } + void set_time_sensor(sensor::Sensor *sensor) { this->time_sensor_ = sensor; } + void set_version_sensor(sensor::Sensor *sensor) { this->version_sensor_ = sensor; } + void set_flow_rate_sensor(sensor::Sensor *sensor) { this->flow_rate_sensor_ = sensor; } + + protected: + sensor::Sensor *temperature1_sensor_{nullptr}; + sensor::Sensor *temperature2_sensor_{nullptr}; + sensor::Sensor *temperature3_sensor_{nullptr}; + sensor::Sensor *temperature4_sensor_{nullptr}; + sensor::Sensor *temperature5_sensor_{nullptr}; + sensor::Sensor *pump_speed1_sensor_{nullptr}; + sensor::Sensor *pump_speed2_sensor_{nullptr}; + sensor::Sensor *operating_hours1_sensor_{nullptr}; + sensor::Sensor *operating_hours2_sensor_{nullptr}; + sensor::Sensor *heat_quantity_sensor_{nullptr}; + sensor::Sensor *time_sensor_{nullptr}; + sensor::Sensor *version_sensor_{nullptr}; + sensor::Sensor *flow_rate_sensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + +class VBusCustomSubSensor; + +class VBusCustomSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_sensors(std::vector sensors) { this->sensors_ = std::move(sensors); }; + + protected: + std::vector sensors_; + void handle_message(std::vector &message) override; +}; + +class VBusCustomSubSensor : public sensor::Sensor, public Component { + public: + void set_message_parser(message_parser_t parser) { this->message_parser_ = std::move(parser); }; + void parse_message(std::vector &message); + + protected: + message_parser_t message_parser_; +}; + +} // namespace vbus +} // namespace esphome diff --git a/esphome/components/vbus/vbus.cpp b/esphome/components/vbus/vbus.cpp new file mode 100644 index 0000000000..c9758891cc --- /dev/null +++ b/esphome/components/vbus/vbus.cpp @@ -0,0 +1,124 @@ +#include "vbus.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace vbus { + +static const char *const TAG = "vbus"; + +void VBus::dump_config() { + ESP_LOGCONFIG(TAG, "VBus:"); + check_uart_settings(9600); +} + +static void septet_spread(uint8_t *data, int start, int count, uint8_t septet) { + for (int i = 0; i < count; i++, septet >>= 1) { + if (septet & 1) + data[start + i] |= 0x80; + } +} + +static bool checksum(const uint8_t *data, int start, int count) { + uint8_t csum = 0x7f; + for (int i = 0; i < count; i++) + csum = (csum - data[start + i]) & 0x7f; + return csum == 0; +} + +void VBus::loop() { + if (!available()) + return; + + while (available()) { + uint8_t c; + read_byte(&c); + + if (c == 0xaa) { + this->state_ = 1; + this->buffer_.clear(); + continue; + } + if (c & 0x80) { + this->state_ = 0; + continue; + } + if (this->state_ == 0) + continue; + + if (this->state_ == 1) { + this->buffer_.push_back(c); + if (this->buffer_.size() == 7) { + this->protocol_ = this->buffer_[4]; + this->source_ = (this->buffer_[3] << 8) + this->buffer_[2]; + this->dest_ = (this->buffer_[1] << 8) + this->buffer_[0]; + this->command_ = (this->buffer_[6] << 8) + this->buffer_[5]; + } + if ((this->protocol_ == 0x20) && (this->buffer_.size() == 15)) { + this->state_ = 0; + if (!checksum(this->buffer_.data(), 0, 15)) { + ESP_LOGE(TAG, "P2 checksum failed"); + continue; + } + septet_spread(this->buffer_.data(), 7, 6, this->buffer_[13]); + uint16_t id = (this->buffer_[8] << 8) + this->buffer_[7]; + uint32_t value = + (this->buffer_[12] << 24) + (this->buffer_[11] << 16) + (this->buffer_[10] << 8) + this->buffer_[9]; + ESP_LOGV(TAG, "P1 C%04x %04x->%04x: %04x %04x (%d)", this->command_, this->source_, this->dest_, id, value, + value); + } else if ((this->protocol_ == 0x10) && (this->buffer_.size() == 9)) { + if (!checksum(this->buffer_.data(), 0, 9)) { + ESP_LOGE(TAG, "P1 checksum failed"); + this->state_ = 0; + continue; + } + this->frames_ = this->buffer_[7]; + if (this->frames_) { + this->state_ = 2; + this->cframe_ = 0; + this->fbcount_ = 0; + this->buffer_.clear(); + } else { + this->state_ = 0; + ESP_LOGD(TAG, "P1 empty message"); + } + } + continue; + } + + if (this->state_ == 2) { + this->fbytes_[this->fbcount_++] = c; + if (this->fbcount_ < 6) + continue; + this->fbcount_ = 0; + if (!checksum(this->fbytes_, 0, 6)) { + ESP_LOGE(TAG, "frame checksum failed"); + continue; + } + septet_spread(this->fbytes_, 0, 4, this->fbytes_[4]); + for (int i = 0; i < 4; i++) + this->buffer_.push_back(this->fbytes_[i]); + if (++this->cframe_ < this->frames_) + continue; + ESP_LOGV(TAG, "P2 C%04x %04x->%04x: %s", this->command_, this->source_, this->dest_, + format_hex(this->buffer_).c_str()); + for (auto &listener : this->listeners_) + listener->on_message(this->command_, this->source_, this->dest_, this->buffer_); + this->state_ = 0; + continue; + } + } +} + +void VBusListener::on_message(uint16_t command, uint16_t source, uint16_t dest, std::vector &message) { + if ((this->command_ != 0xffff) && (this->command_ != command)) + return; + if ((this->source_ != 0xffff) && (this->source_ != source)) + return; + if ((this->dest_ != 0xffff) && (this->dest_ != dest)) + return; + this->handle_message(message); +} + +} // namespace vbus +} // namespace esphome diff --git a/esphome/components/vbus/vbus.h b/esphome/components/vbus/vbus.h new file mode 100644 index 0000000000..7e97b5049a --- /dev/null +++ b/esphome/components/vbus/vbus.h @@ -0,0 +1,52 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/uart/uart.h" + +namespace esphome { +namespace vbus { + +using message_parser_t = std::function &)>; + +class VBus; + +class VBusListener { + public: + void set_command(uint16_t command) { this->command_ = command; } + void set_source(uint16_t source) { this->source_ = source; } + void set_dest(uint16_t dest) { this->dest_ = dest; } + + void on_message(uint16_t command, uint16_t source, uint16_t dest, std::vector &message); + + protected: + uint16_t command_{0xffff}; + uint16_t source_{0xffff}; + uint16_t dest_{0xffff}; + + virtual void handle_message(std::vector &message) = 0; +}; + +class VBus : public uart::UARTDevice, public Component { + public: + void dump_config() override; + void loop() override; + float get_setup_priority() const override { return setup_priority::DATA; } + + void register_listener(VBusListener *listener) { this->listeners_.push_back(listener); } + + protected: + int state_{0}; + std::vector buffer_; + uint8_t protocol_; + uint16_t source_; + uint16_t dest_; + uint16_t command_; + uint8_t frames_; + uint8_t cframe_; + uint8_t fbytes_[6]; + int fbcount_; + std::vector listeners_{}; +}; + +} // namespace vbus +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index b02088ffbb..dde5e9016a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -141,6 +141,7 @@ CONF_CURRENT = "current" CONF_CURRENT_OPERATION = "current_operation" CONF_CURRENT_RESISTOR = "current_resistor" CONF_CURRENT_TEMPERATURE_STATE_TOPIC = "current_temperature_state_topic" +CONF_CUSTOM = "custom" CONF_CUSTOM_FAN_MODE = "custom_fan_mode" CONF_CUSTOM_FAN_MODES = "custom_fan_modes" CONF_CUSTOM_PRESET = "custom_preset" @@ -167,6 +168,7 @@ CONF_DEFAULT_TRANSITION_LENGTH = "default_transition_length" CONF_DELAY = "delay" CONF_DELIMITER = "delimiter" CONF_DELTA = "delta" +CONF_DEST = "dest" CONF_DEVICE = "device" CONF_DEVICE_CLASS = "device_class" CONF_DEVICE_FACTOR = "device_factor" @@ -874,6 +876,7 @@ UNIT_EMPTY = "" UNIT_G = "G" UNIT_HECTOPASCAL = "hPa" UNIT_HERTZ = "Hz" +UNIT_HOUR = "h" UNIT_KELVIN = "K" UNIT_KILOGRAM = "kg" UNIT_KILOMETER = "km" diff --git a/tests/test3.yaml b/tests/test3.yaml index 2856a3c7d7..6578def72d 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -287,6 +287,9 @@ uart: modbus: uart_id: uart1 +vbus: + uart_id: uart4 + ota: safe_mode: true port: 3286 @@ -799,6 +802,11 @@ sensor: id: adc128s102_channel_0 channel: 0 + - platform: vbus + model: deltasol c + temperature_1: + name: Temperature 1 + time: - platform: homeassistant @@ -904,6 +912,11 @@ binary_sensor: then: - pzemac.reset_energy: pzemac1 + - platform: vbus + model: deltasol_bs_plus + relay1: + name: Relay 1 On + globals: - id: my_global_string type: std::string diff --git a/tests/test5.yaml b/tests/test5.yaml index 23046939d5..5f72579d08 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -66,6 +66,9 @@ mqtt: ESP_LOGD("Mqtt Test", "testing/sensor/testing_sensor/state=[%s]", x.c_str()); # yamllint enable rule:line-length +vbus: + - uart_id: uart2 + binary_sensor: - platform: gpio pin: GPIO0 @@ -183,6 +186,22 @@ binary_sensor: id: key1 key: 1 + - platform: vbus + model: deltasol_bs_plus + relay2: + name: Relay 2 On + sensor1_error: + name: Sensor 1 Error + + - platform: vbus + model: custom + command: 0x100 + source: 0x1234 + dest: 0x10 + binary_sensors: + - id: vcustom_b + name: VBus Custom Binary Sensor + lambda: return x[0] & 1; tlc5947: data_pin: GPIO12 @@ -478,6 +497,27 @@ sensor: max_flow_rate: name: Max Flow Rate + - platform: vbus + model: deltasol c + temperature_3: + name: Temperature 3 + operating_hours_1: + name: Operating Hours 1 + heat_quantity: + name: Heat Quantity + time: + name: System Time + + - platform: vbus + model: custom + command: 0x100 + source: 0x1234 + dest: 0x10 + sensors: + - id: vcustom + name: VBus Custom Sensor + lambda: return x[0] / 10.0; + script: - id: automation_test then: