diff --git a/esphome/components/optolink/__init__.py b/esphome/components/optolink/__init__.py index 010f8e198d..2bdb253adb 100644 --- a/esphome/components/optolink/__init__.py +++ b/esphome/components/optolink/__init__.py @@ -14,7 +14,7 @@ from esphome.const import ( from esphome.core import CORE CODEOWNERS = ["@j0ta29"] -DEPENDENCIES = ["text_sensor"] +DEPENDENCIES = [] AUTO_LOAD = [] MULTI_CONF = False @@ -72,7 +72,7 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): - cg.add_library("VitoWiFi", "1.0.2") + cg.add_library("VitoWiFi", "1.1.2") cg.add_define( "VITOWIFI_PROTOCOL", cg.RawExpression(f"Optolink{config[CONF_PROTOCOL]}") diff --git a/esphome/components/optolink/binary_sensor/optolink_binary_sensor.h b/esphome/components/optolink/binary_sensor/optolink_binary_sensor.h index 5284c37b6c..4cdb5bd1f8 100644 --- a/esphome/components/optolink/binary_sensor/optolink_binary_sensor.h +++ b/esphome/components/optolink/binary_sensor/optolink_binary_sensor.h @@ -4,27 +4,27 @@ #include "esphome/components/binary_sensor/binary_sensor.h" #include "../optolink.h" -#include "../optolink_sensor_base.h" +#include "../datapoint_component.h" #include "VitoWiFi.h" namespace esphome { namespace optolink { -class OptolinkBinarySensor : public OptolinkSensorBase, +class OptolinkBinarySensor : public DatapointComponent, public esphome::binary_sensor::BinarySensor, public esphome::PollingComponent { public: - OptolinkBinarySensor(Optolink *optolink) : OptolinkSensorBase(optolink) { - bytes_ = 1; - div_ratio_ = 1; + OptolinkBinarySensor(Optolink *optolink) : DatapointComponent(optolink) { + set_bytes(1); + set_div_ratio(1); } protected: void setup() override { setup_datapoint(); } - void update() override { optolink_->read_value(datapoint_); } + void update() override { datapoint_read_request(); } const StringRef &get_component_name() override { return get_name(); } - void value_changed(uint8_t state) override { publish_state(state); }; + void datapoint_value_changed(uint8_t state) override { publish_state(state); }; }; } // namespace optolink } // namespace esphome diff --git a/esphome/components/optolink/optolink_sensor_base.cpp b/esphome/components/optolink/datapoint_component.cpp similarity index 57% rename from esphome/components/optolink/optolink_sensor_base.cpp rename to esphome/components/optolink/datapoint_component.cpp index 2af6a22471..bc963554c0 100644 --- a/esphome/components/optolink/optolink_sensor_base.cpp +++ b/esphome/components/optolink/datapoint_component.cpp @@ -1,14 +1,16 @@ #ifdef USE_ARDUINO -#include "optolink_sensor_base.h" +#include "datapoint_component.h" #include "optolink.h" +#include "esphome/components/api/api_server.h" namespace esphome { namespace optolink { -static const char *const TAG = "optolink.sensor_base"; +static const char *const TAG = "datapoint_component"; +static std::vector hass_subscriptions_; -void OptolinkSensorBase::setup_datapoint() { +void DatapointComponent::setup_datapoint() { switch (div_ratio_) { case 0: datapoint_ = new Datapoint(get_component_name().c_str(), "optolink", address_, writeable_); @@ -21,7 +23,8 @@ void OptolinkSensorBase::setup_datapoint() { dp_value.getString(print_buffer, sizeof(print_buffer)); ESP_LOGI(TAG, "recieved data for datapoint %s: %s", dp.getName(), print_buffer); #endif - value_changed((uint8_t *) buffer, bytes_); + datapoint_value_changed((uint8_t *) buffer, bytes_); + read_retries_ = 0; }); break; case 1: @@ -30,21 +33,24 @@ void OptolinkSensorBase::setup_datapoint() { datapoint_ = new Datapoint(get_component_name().c_str(), "optolink", address_, writeable_); datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) { ESP_LOGI(TAG, "recieved data for datapoint %s: %d", dp.getName(), dp_value.getU8()); - value_changed(dp_value.getU8()); + datapoint_value_changed(dp_value.getU8()); + read_retries_ = 0; }); break; case 2: datapoint_ = new Datapoint(get_component_name().c_str(), "optolink", address_, writeable_); datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) { ESP_LOGI(TAG, "recieved data for datapoint %s: %d", dp.getName(), dp_value.getU16()); - value_changed(dp_value.getU16()); + datapoint_value_changed(dp_value.getU16()); + read_retries_ = 0; }); break; case 4: datapoint_ = new Datapoint(get_component_name().c_str(), "optolink", address_, writeable_); datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) { ESP_LOGI(TAG, "recieved data for datapoint %s: %d", dp.getName(), dp_value.getU32()); - value_changed((uint32_t) dp_value.getU32()); + datapoint_value_changed((uint32_t) dp_value.getU32()); + read_retries_ = 0; }); break; default: @@ -57,14 +63,16 @@ void OptolinkSensorBase::setup_datapoint() { datapoint_ = new Datapoint(get_component_name().c_str(), "optolink", address_, writeable_); datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) { ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat()); - value_changed(dp_value.getFloat()); + datapoint_value_changed(dp_value.getFloat()); + read_retries_ = 0; }); break; case 2: datapoint_ = new Datapoint(get_component_name().c_str(), "optolink", address_, writeable_); datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) { ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat()); - value_changed(dp_value.getFloat()); + datapoint_value_changed(dp_value.getFloat()); + read_retries_ = 0; }); break; default: @@ -77,7 +85,8 @@ void OptolinkSensorBase::setup_datapoint() { datapoint_ = new Datapoint(get_component_name().c_str(), "optolink", address_, writeable_); datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) { ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat()); - value_changed(dp_value.getFloat()); + datapoint_value_changed(dp_value.getFloat()); + read_retries_ = 0; }); break; default: @@ -90,7 +99,8 @@ void OptolinkSensorBase::setup_datapoint() { datapoint_ = new Datapoint(get_component_name().c_str(), "optolink", address_, writeable_); datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) { ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat()); - value_changed(dp_value.getFloat()); + datapoint_value_changed(dp_value.getFloat()); + read_retries_ = 0; }); break; } @@ -101,7 +111,8 @@ void OptolinkSensorBase::setup_datapoint() { datapoint_ = new Datapoint(get_component_name().c_str(), "optolink", address_, writeable_); datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) { ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat()); - value_changed(dp_value.getFloat()); + datapoint_value_changed(dp_value.getFloat()); + read_retries_ = 0; }); break; } @@ -111,57 +122,81 @@ void OptolinkSensorBase::setup_datapoint() { } } -void OptolinkSensorBase::value_changed(float value) { +void DatapointComponent::datapoint_read_request() { + if (is_dp_value_writing_outstanding) { + ESP_LOGI(TAG, "read request for %s deferred due to outstanding write request", get_component_name().c_str()); + datapoint_write_request(dp_value_outstanding); + } else { + if (read_retries_ == 0 || read_retries_ >= MAX_RETRIES_UNTIL_RESET) { + if (optolink_->read_value(datapoint_)) { + read_retries_ = 1; + } + } else { + read_retries_++; + ESP_LOGW(TAG, "%d. read request for %s rejected - reduce update_interval!", read_retries_, + get_component_name().c_str()); + } + } +} + +void DatapointComponent::datapoint_value_changed(float value) { ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str()); } -void OptolinkSensorBase::value_changed(uint8_t value) { +void DatapointComponent::datapoint_value_changed(uint8_t value) { ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str()); } -void OptolinkSensorBase::value_changed(uint16_t value) { +void DatapointComponent::datapoint_value_changed(uint16_t value) { ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str()); } -void OptolinkSensorBase::value_changed(uint32_t value) { +void DatapointComponent::datapoint_value_changed(uint32_t value) { ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str()); } -void OptolinkSensorBase::value_changed(std::string value) { +void DatapointComponent::datapoint_value_changed(std::string value) { ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str()); } -void OptolinkSensorBase::value_changed(uint8_t *value, size_t length) { +void DatapointComponent::datapoint_value_changed(uint8_t *value, size_t length) { ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str()); } -void OptolinkSensorBase::update_datapoint(DPValue dp_value) { +void DatapointComponent::datapoint_write_request(DPValue dp_value) { if (!writeable_) { - optolink_->set_error("trying to control not writable datapoint %s", get_component_name().c_str()); + optolink_->set_state("trying to control not writable datapoint %s", get_component_name().c_str()); ESP_LOGE(TAG, "trying to control not writable datapoint %s", get_component_name().c_str()); } else if (datapoint_ != nullptr) { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_INFO char buffer[100]; dp_value.getString(buffer, sizeof(buffer)); - ESP_LOGI(TAG, "updating datapoint %s value: %s", datapoint_->getName(), buffer); + ESP_LOGI(TAG, "trying to update datapoint %s value: %s", get_component_name().c_str(), buffer); #endif - optolink_->write_value(datapoint_, dp_value); + + dp_value_outstanding = dp_value; + if (optolink_->write_value(datapoint_, dp_value_outstanding)) { + is_dp_value_writing_outstanding = false; + } else { + ESP_LOGW(TAG, "write request for %s rejected - reduce update_interval!", get_component_name().c_str()); + is_dp_value_writing_outstanding = true; + } } } -void OptolinkSensorBase::update_datapoint(float value) { +void DatapointComponent::write_datapoint_value(float value) { if (div_ratio_ > 1) { - update_datapoint(DPValue(value)); + datapoint_write_request(DPValue(value)); } else if (div_ratio_ == 1) { switch (bytes_) { case 1: - update_datapoint(DPValue((uint8_t) value)); + datapoint_write_request(DPValue((uint8_t) value)); break; case 2: - update_datapoint(DPValue((uint16_t) value)); + datapoint_write_request(DPValue((uint16_t) value)); break; case 4: - update_datapoint(DPValue((uint32_t) value)); + datapoint_write_request(DPValue((uint32_t) value)); break; default: unfitting_value_type(); @@ -172,43 +207,79 @@ void OptolinkSensorBase::update_datapoint(float value) { } } -void OptolinkSensorBase::update_datapoint(uint8_t value) { +void DatapointComponent::write_datapoint_value(uint8_t value) { if (bytes_ == 1 && div_ratio_ == 1) { - update_datapoint(DPValue(value)); + datapoint_write_request(DPValue(value)); } else { unfitting_value_type(); } } -void OptolinkSensorBase::update_datapoint(uint16_t value) { +void DatapointComponent::write_datapoint_value(uint16_t value) { if (bytes_ == 2 && div_ratio_ == 1) { - update_datapoint(DPValue(value)); + datapoint_write_request(DPValue(value)); } else { unfitting_value_type(); } } -void OptolinkSensorBase::update_datapoint(uint32_t value) { +void DatapointComponent::write_datapoint_value(uint32_t value) { if (bytes_ == 4 && div_ratio_ == 1) { - update_datapoint(DPValue(value)); + datapoint_write_request(DPValue(value)); } else { unfitting_value_type(); } } -void OptolinkSensorBase::update_datapoint(uint8_t *value, size_t length) { +void DatapointComponent::write_datapoint_value(uint8_t *value, size_t length) { if (bytes_ == length && div_ratio_ == 0) { - update_datapoint(DPValue(value, length)); + datapoint_write_request(DPValue(value, length)); } else { unfitting_value_type(); } } -void OptolinkSensorBase::unfitting_value_type() { - optolink_->set_error("Unfitting byte/div_ratio combination for sensor/component %s", get_component_name().c_str()); +void DatapointComponent::unfitting_value_type() { + optolink_->set_state("Unfitting byte/div_ratio combination for sensor/component %s", get_component_name().c_str()); ESP_LOGE(TAG, "Unfitting byte/div_ratio combination for sensor/component %s", get_component_name().c_str()); } +void DatapointComponent::set_optolink_state(const char *format, ...) { + va_list args; + va_start(args, format); + char buffer[128]; + std::vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + + optolink_->set_state(buffer); +} + +std::string DatapointComponent::get_optolink_state() { return optolink_->get_state(); } + +void DatapointComponent::subscribe_hass(std::string entity_id, std::function f) { + for (auto &subscription : hass_subscriptions_) { + if (subscription.entity_id == entity_id) { + subscription.callbacks.push_back(f); + return; + } + } + HassSubscription subscription{entity_id}; + subscription.callbacks.push_back(f); + hass_subscriptions_.push_back(subscription); + + api::global_api_server->subscribe_home_assistant_state( + entity_id, optional(), [this, entity_id](const std::string &state) { + ESP_LOGD(TAG, "received schedule plan from HASS entity '%s': %s", entity_id.c_str(), state.c_str()); + for (auto &subscription : hass_subscriptions_) { + if (subscription.entity_id == entity_id) { + for (auto callback : subscription.callbacks) { + callback(state); + } + } + } + }); +} + void conv2_100_F::encode(uint8_t *out, DPValue in) { int16_t tmp = floor((in.getFloat() * 100) + 0.5); out[1] = tmp >> 8; diff --git a/esphome/components/optolink/datapoint_component.h b/esphome/components/optolink/datapoint_component.h new file mode 100644 index 0000000000..bfa771a829 --- /dev/null +++ b/esphome/components/optolink/datapoint_component.h @@ -0,0 +1,94 @@ +#pragma once + +#ifdef USE_ARDUINO + +#include "esphome/core/log.h" +#include "esphome/core/string_ref.h" +#include "VitoWiFi.h" + +namespace esphome { +namespace optolink { + +class Optolink; + +struct HassSubscription { + std::string entity_id; + std::vector> callbacks; +}; + +class DatapointComponent { + public: + DatapointComponent(Optolink *optolink, bool writeable = false) : dp_value_outstanding((uint8_t) 0) { + optolink_ = optolink; + writeable_ = writeable; + } + + void set_address(uint32_t address) { address_ = address; } + void set_bytes(size_t bytes) { bytes_ = bytes; } + void set_writeable(bool writeable) { writeable_ = writeable; } + void set_div_ratio(size_t div_ratio) { div_ratio_ = div_ratio; } + + protected: + virtual const StringRef &get_component_name() = 0; + + uint32_t get_address() { return address_; } + + void setup_datapoint(); + + void datapoint_read_request(); + + virtual void datapoint_value_changed(float value); + virtual void datapoint_value_changed(uint8_t value); + virtual void datapoint_value_changed(uint16_t value); + virtual void datapoint_value_changed(uint32_t value); + virtual void datapoint_value_changed(std::string value); + virtual void datapoint_value_changed(uint8_t *value, size_t length); + + void write_datapoint_value(float value); + void write_datapoint_value(uint8_t value); + void write_datapoint_value(uint16_t value); + void write_datapoint_value(uint32_t value); + void write_datapoint_value(uint8_t *value, size_t length); + + void unfitting_value_type(); + void set_optolink_state(const char *format, ...); + std::string get_optolink_state(); + + void subscribe_hass(std::string entity_id, std::function f); + + private: + const size_t MAX_RETRIES_UNTIL_RESET = 10; + Optolink *optolink_; + IDatapoint *datapoint_ = nullptr; + size_t read_retries_ = 0; + size_t div_ratio_ = 0; + size_t bytes_; + uint32_t address_; + bool writeable_; + + bool is_dp_value_writing_outstanding = false; + DPValue dp_value_outstanding; + + void datapoint_write_request(DPValue dp_value); +}; + +// NOLINTNEXTLINE +class conv2_100_F : public DPType { + public: + void encode(uint8_t *out, DPValue in); + DPValue decode(const uint8_t *in); + size_t get_length() const { return 2; } +}; + +// NOLINTNEXTLINE +class conv4_1000_F : public DPType { + public: + void encode(uint8_t *out, DPValue in); + DPValue decode(const uint8_t *in); + const size_t getLength() const { return 4; } +}; + +} // namespace optolink +} // namespace esphome + +#endif diff --git a/esphome/components/optolink/number/optolink_number.cpp b/esphome/components/optolink/number/optolink_number.cpp index 676ca32b7c..38603864f4 100644 --- a/esphome/components/optolink/number/optolink_number.cpp +++ b/esphome/components/optolink/number/optolink_number.cpp @@ -9,11 +9,11 @@ namespace optolink { void OptolinkNumber::control(float value) { if (value > traits.get_max_value() || value < traits.get_min_value()) { - optolink_->set_error("datapoint value of number %s not in allowed range", get_component_name().c_str()); + set_optolink_state("datapoint value of number %s not in allowed range", get_component_name().c_str()); ESP_LOGE("OptolinkNumber", "datapoint value of number %s not in allowed range", get_component_name().c_str()); } else { ESP_LOGI("OptolinkNumber", "control of number %s to value %f", get_component_name().c_str(), value); - update_datapoint(value); + write_datapoint_value(value); publish_state(value); } }; diff --git a/esphome/components/optolink/number/optolink_number.h b/esphome/components/optolink/number/optolink_number.h index 097039878b..49bd75700b 100644 --- a/esphome/components/optolink/number/optolink_number.h +++ b/esphome/components/optolink/number/optolink_number.h @@ -3,28 +3,27 @@ #ifdef USE_ARDUINO #include "esphome/components/number/number.h" -#include "../optolink_sensor_base.h" +#include "../datapoint_component.h" #include "../optolink.h" #include "VitoWiFi.h" namespace esphome { namespace optolink { -class OptolinkNumber : public OptolinkSensorBase, public esphome::number::Number, public esphome::PollingComponent { +class OptolinkNumber : public DatapointComponent, public esphome::number::Number, public esphome::PollingComponent { public: - OptolinkNumber(Optolink *optolink) : OptolinkSensorBase(optolink, true) {} + OptolinkNumber(Optolink *optolink) : DatapointComponent(optolink, true) {} protected: void setup() override { setup_datapoint(); } - void update() override { optolink_->read_value(datapoint_); } + void update() override { datapoint_read_request(); } + void control(float value) override; const StringRef &get_component_name() override { return get_name(); } - void value_changed(float state) override { publish_state(state); }; - void value_changed(uint8_t state) override { publish_state(state); }; - void value_changed(uint16_t state) override { publish_state(state); }; - void value_changed(uint32_t state) override { publish_state(state); }; - - void control(float value) override; + void datapoint_value_changed(float state) override { publish_state(state); }; + void datapoint_value_changed(uint8_t state) override { publish_state(state); }; + void datapoint_value_changed(uint16_t state) override { publish_state(state); }; + void datapoint_value_changed(uint32_t state) override { publish_state(state); }; }; } // namespace optolink diff --git a/esphome/components/optolink/optolink.cpp b/esphome/components/optolink/optolink.cpp index 696eeda1eb..4921297c5f 100644 --- a/esphome/components/optolink/optolink.cpp +++ b/esphome/components/optolink/optolink.cpp @@ -25,7 +25,7 @@ void Optolink::comm_() { } void Optolink::setup() { - ESP_LOGI("Optolink", "setup"); + ESP_LOGI(TAG, "setup"); if (logger_enabled_) { VitoWiFi.setLogger(this); @@ -39,37 +39,54 @@ void Optolink::setup() { #endif } -void Optolink::loop() { VitoWiFi.loop(); } +void Optolink::loop() { + ESP_LOGD(TAG, "queue size: %d", VitoWiFi.queueSize()); + VitoWiFi.loop(); +} -void Optolink::set_error(const char *format, ...) { +void Optolink::set_state(const char *format, ...) { va_list args; va_start(args, format); char buffer[128]; std::vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); - error_ = buffer; + state_ = buffer; } -void Optolink::read_value(IDatapoint *datapoint) { +bool Optolink::read_value(IDatapoint *datapoint) { if (datapoint != nullptr) { - ESP_LOGI("Optolink", "requesting value of datapoint %s", datapoint->getName()); - VitoWiFi.readDatapoint(*datapoint); + ESP_LOGI(TAG, "requesting value of datapoint %s", datapoint->getName()); + if (!VitoWiFi.readDatapoint(*datapoint)) { + ESP_LOGE(TAG, "read request rejected due to queue overload - queue size: %d", VitoWiFi.queueSize()); + for (auto dp : datapoint->getCollection()) { + ESP_LOGD(TAG, "queued datapoint: %s", dp->getName()); + } + return false; + } } + return true; } -void Optolink::write_value(IDatapoint *datapoint, DPValue dp_value) { +bool Optolink::write_value(IDatapoint *datapoint, DPValue dp_value) { if (datapoint != nullptr) { char buffer[64]; dp_value.getString(buffer, sizeof(buffer)); - ESP_LOGI("Optolink", "sending value %s to datapoint %s", buffer, datapoint->getName()); - VitoWiFi.writeDatapoint(*datapoint, dp_value); + ESP_LOGI(TAG, "sending value %s of datapoint %s", buffer, datapoint->getName()); + if (!VitoWiFi.writeDatapoint(*datapoint, dp_value)) { + ESP_LOGE(TAG, "write request rejected due to queue overload - queue size: %d", VitoWiFi.queueSize()); + for (auto dp : datapoint->getCollection()) { + ESP_LOGE(TAG, "queued dp: %s", dp->getName()); + } + return false; + } } + return true; } size_t Optolink::write(uint8_t ch) { if (ch == '\n') { - ESP_LOGD("VitoWifi", "%s", log_buffer_.c_str()); + ESP_LOGD(TAG, "VitoWiFi: %s", log_buffer_.c_str()); log_buffer_.clear(); } else { log_buffer_.push_back(ch); diff --git a/esphome/components/optolink/optolink.h b/esphome/components/optolink/optolink.h index 620cdac0e8..358f9f2309 100644 --- a/esphome/components/optolink/optolink.h +++ b/esphome/components/optolink/optolink.h @@ -3,7 +3,6 @@ #ifdef USE_ARDUINO #include "esphome/core/component.h" -#include "esphome/components/text_sensor/text_sensor.h" #include "VitoWiFi.h" namespace esphome { @@ -11,7 +10,7 @@ namespace optolink { class Optolink : public esphome::Component, public Print { protected: - std::string error_ = "OK"; + std::string state_ = "OK"; std::string log_buffer_; bool logger_enabled_ = false; int rx_pin_; @@ -30,11 +29,11 @@ class Optolink : public esphome::Component, public Print { void set_rx_pin(int rx_pin) { rx_pin_ = rx_pin; } void set_tx_pin(int tx_pin) { tx_pin_ = tx_pin; } - void write_value(IDatapoint *datapoint, DPValue dp_value); - void read_value(IDatapoint *datapoint); + bool write_value(IDatapoint *datapoint, DPValue dp_value); + bool read_value(IDatapoint *datapoint); - void set_error(const char *format, ...); - std::string get_error() { return error_; } + void set_state(const char *format, ...); + std::string get_state() { return state_; } }; } // namespace optolink diff --git a/esphome/components/optolink/optolink_sensor_base.h b/esphome/components/optolink/optolink_sensor_base.h deleted file mode 100644 index dd5d89c925..0000000000 --- a/esphome/components/optolink/optolink_sensor_base.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#ifdef USE_ARDUINO - -#include "esphome/core/log.h" -#include "esphome/core/string_ref.h" -#include "VitoWiFi.h" - -namespace esphome { -namespace optolink { - -class Optolink; - -class OptolinkSensorBase { - public: - OptolinkSensorBase(Optolink *optolink, bool writeable = false) { - optolink_ = optolink; - writeable_ = writeable; - } - - void set_address(uint32_t address) { address_ = address; } - void set_bytes(int bytes) { bytes_ = bytes; } - void set_div_ratio(int div_ratio) { div_ratio_ = div_ratio; } - - protected: - Optolink *optolink_; - bool writeable_; - IDatapoint *datapoint_ = nullptr; - uint32_t address_; - size_t bytes_; - size_t div_ratio_ = 0; - - virtual const StringRef &get_component_name() = 0; - void setup_datapoint(); - virtual void value_changed(float value); - virtual void value_changed(uint8_t value); - virtual void value_changed(uint16_t value); - virtual void value_changed(uint32_t value); - virtual void value_changed(std::string value); - virtual void value_changed(uint8_t *value, size_t length); - void update_datapoint(float value); - void update_datapoint(uint8_t value); - void update_datapoint(uint16_t value); - void update_datapoint(uint32_t value); - void update_datapoint(uint8_t *value, size_t length); - - void unfitting_value_type(); - - private: - void update_datapoint(DPValue dp_value); -}; - -// NOLINTNEXTLINE -class conv2_100_F : public DPType { - public: - void encode(uint8_t *out, DPValue in); - DPValue decode(const uint8_t *in); - size_t get_length() const { return 2; } -}; - -// NOLINTNEXTLINE -class conv4_1000_F : public DPType { - public: - void encode(uint8_t *out, DPValue in); - DPValue decode(const uint8_t *in); - const size_t getLength() const { return 4; } -}; - -} // namespace optolink -} // namespace esphome - -#endif diff --git a/esphome/components/optolink/select/optolink_select.cpp b/esphome/components/optolink/select/optolink_select.cpp index 68fb6140bd..174cc4934b 100644 --- a/esphome/components/optolink/select/optolink_select.cpp +++ b/esphome/components/optolink/select/optolink_select.cpp @@ -11,45 +11,45 @@ void OptolinkSelect::control(const std::string &value) { for (auto it = mapping_->begin(); it != mapping_->end(); ++it) { if (it->second == value) { ESP_LOGI("OptolinkSelect", "control of select %s to value %s", get_component_name().c_str(), it->first.c_str()); - update_datapoint(std::stof(it->first)); + write_datapoint_value(std::stof(it->first)); publish_state(it->second); break; } if (it == mapping_->end()) { - optolink_->set_error("unknown value %s of select %s", value.c_str(), get_component_name().c_str()); + set_optolink_state("unknown value %s of select %s", value.c_str(), get_component_name().c_str()); ESP_LOGE("OptolinkSelect", "unknown value %s of select %s", value.c_str(), get_component_name().c_str()); } } }; -void OptolinkSelect::value_changed(std::string key) { +void OptolinkSelect::datapoint_value_changed(std::string key) { auto pos = mapping_->find(key); if (pos == mapping_->end()) { - optolink_->set_error("value %s not found in select %s", key.c_str(), get_component_name().c_str()); + set_optolink_state("value %s not found in select %s", key.c_str(), get_component_name().c_str()); ESP_LOGE("OptolinkSelect", "value %s not found in select %s", key.c_str(), get_component_name().c_str()); } else { publish_state(pos->second); } } -void OptolinkSelect::value_changed(uint8_t state) { +void OptolinkSelect::datapoint_value_changed(uint8_t state) { std::string key = std::to_string(state); - value_changed(key); + datapoint_value_changed(key); } -void OptolinkSelect::value_changed(uint16_t state) { +void OptolinkSelect::datapoint_value_changed(uint16_t state) { std::string key = std::to_string(state); - value_changed(key); + datapoint_value_changed(key); } -void OptolinkSelect::value_changed(uint32_t state) { +void OptolinkSelect::datapoint_value_changed(uint32_t state) { std::string key = std::to_string(state); - value_changed(key); + datapoint_value_changed(key); } -void OptolinkSelect::value_changed(float state) { +void OptolinkSelect::datapoint_value_changed(float state) { std::string key = std::to_string(state); - value_changed(key); + datapoint_value_changed(key); } } // namespace optolink diff --git a/esphome/components/optolink/select/optolink_select.h b/esphome/components/optolink/select/optolink_select.h index 20137b0d1d..f3512cf545 100644 --- a/esphome/components/optolink/select/optolink_select.h +++ b/esphome/components/optolink/select/optolink_select.h @@ -5,15 +5,15 @@ #include #include "esphome/components/select/select.h" #include "../optolink.h" -#include "../optolink_sensor_base.h" +#include "../datapoint_component.h" #include "VitoWiFi.h" namespace esphome { namespace optolink { -class OptolinkSelect : public OptolinkSensorBase, public esphome::select::Select, public esphome::PollingComponent { +class OptolinkSelect : public DatapointComponent, public esphome::select::Select, public esphome::PollingComponent { public: - OptolinkSelect(Optolink *optolink) : OptolinkSensorBase(optolink, true) {} + OptolinkSelect(Optolink *optolink) : DatapointComponent(optolink, true) {} void set_map(std::map *mapping) { mapping_ = mapping; @@ -26,16 +26,15 @@ class OptolinkSelect : public OptolinkSensorBase, public esphome::select::Select protected: void setup() override { setup_datapoint(); } - void update() override { optolink_->read_value(datapoint_); } + void update() override { datapoint_read_request(); } + void control(const std::string &value) override; const StringRef &get_component_name() override { return get_name(); } - void value_changed(std::string state) override; - void value_changed(uint8_t state) override; - void value_changed(uint16_t state) override; - void value_changed(uint32_t state) override; - void value_changed(float state) override; - - void control(const std::string &value) override; + void datapoint_value_changed(std::string state) override; + void datapoint_value_changed(uint8_t state) override; + void datapoint_value_changed(uint16_t state) override; + void datapoint_value_changed(uint32_t state) override; + void datapoint_value_changed(float state) override; private: std::map *mapping_ = nullptr; diff --git a/esphome/components/optolink/sensor/optolink_sensor.h b/esphome/components/optolink/sensor/optolink_sensor.h index 89474abcda..7538fd6390 100644 --- a/esphome/components/optolink/sensor/optolink_sensor.h +++ b/esphome/components/optolink/sensor/optolink_sensor.h @@ -4,27 +4,27 @@ #include "esphome/components/sensor/sensor.h" #include "../optolink.h" -#include "../optolink_sensor_base.h" +#include "../datapoint_component.h" #include "VitoWiFi.h" namespace esphome { namespace optolink { -class OptolinkSensor : public OptolinkSensorBase, public esphome::sensor::Sensor, public esphome::PollingComponent { +class OptolinkSensor : public DatapointComponent, public esphome::sensor::Sensor, public esphome::PollingComponent { public: - OptolinkSensor(Optolink *optolink) : OptolinkSensorBase(optolink) { + OptolinkSensor(Optolink *optolink) : DatapointComponent(optolink) { set_state_class(esphome::sensor::STATE_CLASS_MEASUREMENT); } protected: void setup() { setup_datapoint(); } - void update() override { optolink_->read_value(datapoint_); } + void update() override { datapoint_read_request(); } const StringRef &get_component_name() override { return get_name(); } - void value_changed(float state) override { publish_state(state); }; - void value_changed(uint8_t state) override { publish_state(state); }; - void value_changed(uint16_t state) override { publish_state(state); }; - void value_changed(uint32_t state) override { publish_state(state); }; + void datapoint_value_changed(float state) override { publish_state(state); }; + void datapoint_value_changed(uint8_t state) override { publish_state(state); }; + void datapoint_value_changed(uint16_t state) override { publish_state(state); }; + void datapoint_value_changed(uint32_t state) override { publish_state(state); }; }; } // namespace optolink } // namespace esphome diff --git a/esphome/components/optolink/switch/optolink_switch.cpp b/esphome/components/optolink/switch/optolink_switch.cpp index b334398f53..4f639b346c 100644 --- a/esphome/components/optolink/switch/optolink_switch.cpp +++ b/esphome/components/optolink/switch/optolink_switch.cpp @@ -9,11 +9,11 @@ namespace optolink { void OptolinkSwitch::write_state(bool value) { if (value != 0 && value != 1) { - optolink_->set_error("datapoint value of switch %s not 0 or 1", get_component_name().c_str()); + set_optolink_state("datapoint value of switch %s not 0 or 1", get_component_name().c_str()); ESP_LOGE("OptolinkSwitch", "datapoint value of switch %s not 0 or 1", get_component_name().c_str()); } else { ESP_LOGI("OptolinkSwitch", "control of switch %s to value %d", get_component_name().c_str(), value); - update_datapoint((uint8_t) value); + write_datapoint_value((uint8_t) value); publish_state(value); } }; diff --git a/esphome/components/optolink/switch/optolink_switch.h b/esphome/components/optolink/switch/optolink_switch.h index 6b5cffdf28..ce97179ea6 100644 --- a/esphome/components/optolink/switch/optolink_switch.h +++ b/esphome/components/optolink/switch/optolink_switch.h @@ -3,28 +3,27 @@ #ifdef USE_ARDUINO #include "esphome/components/switch/switch.h" -#include "../optolink_sensor_base.h" +#include "../datapoint_component.h" #include "../optolink.h" #include "VitoWiFi.h" namespace esphome { namespace optolink { -class OptolinkSwitch : public OptolinkSensorBase, public esphome::switch_::Switch, public esphome::PollingComponent { +class OptolinkSwitch : public DatapointComponent, public esphome::switch_::Switch, public esphome::PollingComponent { public: - OptolinkSwitch(Optolink *optolink) : OptolinkSensorBase(optolink, true) { - bytes_ = 1; - div_ratio_ = 1; + OptolinkSwitch(Optolink *optolink) : DatapointComponent(optolink, true) { + set_bytes(1); + set_div_ratio(1); } protected: void setup() override { setup_datapoint(); } - void update() override { optolink_->read_value(datapoint_); } + void update() override { datapoint_read_request(); } + void write_state(bool value) override; const StringRef &get_component_name() override { return get_name(); } - void value_changed(uint8_t state) override { publish_state(state); }; - - void write_state(bool value) override; + void datapoint_value_changed(uint8_t state) override { publish_state(state); }; }; } // namespace optolink diff --git a/esphome/components/optolink/text_sensor/optolink_text_sensor.cpp b/esphome/components/optolink/text_sensor/optolink_text_sensor.cpp index 212f726039..1225834a77 100644 --- a/esphome/components/optolink/text_sensor/optolink_text_sensor.cpp +++ b/esphome/components/optolink/text_sensor/optolink_text_sensor.cpp @@ -3,7 +3,7 @@ #include "esphome/core/log.h" #include "optolink_text_sensor.h" #include "../optolink.h" -#include "esphome/components/api/api_server.h" +#include "../datapoint_component.h" #include "VitoWiFi.h" namespace esphome { @@ -80,33 +80,34 @@ void OptolinkTextSensor::setup() { case MAP: break; case RAW: - div_ratio_ = 0; + set_div_ratio(0); break; case DAY_SCHEDULE: - div_ratio_ = 0; - bytes_ = 8; - address_ += (8 * dow_); + set_div_ratio(0); + set_bytes(8); + set_address(get_address() + 8 * dow_); break; case DAY_SCHEDULE_SYNCHRONIZED: - writeable_ = true; - div_ratio_ = 0; - bytes_ = 8; - address_ += (8 * dow_); - api::global_api_server->subscribe_home_assistant_state( - this->entity_id_, optional(), [this](const std::string &state) { - ESP_LOGD(TAG, "got time values from entity '%s': %s", this->entity_id_.c_str(), state.c_str()); - uint8_t *data = encode_time_string(state); - if (data) { - update_datapoint(data, 8); - } else { - ESP_LOGW(TAG, "not changing any value of datapoint %s", datapoint_->getName()); - } - }); + set_writeable(true); + set_div_ratio(0); + set_bytes(8); + set_address(get_address() + 8 * dow_); + ESP_LOGI(TAG, "subscribing to schedule plan from HASS entity '%s' for component %s", this->entity_id_.c_str(), + get_component_name().c_str()); + subscribe_hass(entity_id_, [this](const std::string &state) { + ESP_LOGD(TAG, "update for schedule plan for component %s: %s", get_component_name().c_str(), state.c_str()); + uint8_t *data = encode_time_string(state); + if (data) { + write_datapoint_value(data, 8); + } else { + ESP_LOGW(TAG, "not changing any value of datapoint %s", get_component_name().c_str()); + } + }); break; case DEVICE_INFO: set_entity_category(esphome::ENTITY_CATEGORY_DIAGNOSTIC); - bytes_ = 4; - address_ = 0x00f8; + set_bytes(4); + set_address(0x00f8); break; case STATE_INFO: set_entity_category(esphome::ENTITY_CATEGORY_DIAGNOSTIC); @@ -115,7 +116,15 @@ void OptolinkTextSensor::setup() { setup_datapoint(); }; -void OptolinkTextSensor::value_changed(uint8_t *value, size_t length) { +void OptolinkTextSensor::update() { + if (mode_ == STATE_INFO) { + publish_state(get_optolink_state()); + } else { + datapoint_read_request(); + } +} + +void OptolinkTextSensor::datapoint_value_changed(uint8_t *value, size_t length) { switch (mode_) { case RAW: publish_state(std::string((const char *) value)); @@ -146,7 +155,7 @@ void OptolinkTextSensor::value_changed(uint8_t *value, size_t length) { } }; -void OptolinkTextSensor::value_changed(uint32_t value) { +void OptolinkTextSensor::datapoint_value_changed(uint32_t value) { switch (mode_) { case DEVICE_INFO: { uint8_t *bytes = (uint8_t *) &value; diff --git a/esphome/components/optolink/text_sensor/optolink_text_sensor.h b/esphome/components/optolink/text_sensor/optolink_text_sensor.h index f9c9fb24e6..6dd1210dbb 100644 --- a/esphome/components/optolink/text_sensor/optolink_text_sensor.h +++ b/esphome/components/optolink/text_sensor/optolink_text_sensor.h @@ -4,7 +4,7 @@ #include "esphome/components/text_sensor/text_sensor.h" #include "../optolink.h" -#include "../optolink_sensor_base.h" +#include "../datapoint_component.h" #include "VitoWiFi.h" namespace esphome { @@ -12,11 +12,11 @@ namespace optolink { enum TextSensorMode { MAP, RAW, DAY_SCHEDULE, DAY_SCHEDULE_SYNCHRONIZED, DEVICE_INFO, STATE_INFO }; -class OptolinkTextSensor : public OptolinkSensorBase, +class OptolinkTextSensor : public DatapointComponent, public esphome::text_sensor::TextSensor, public esphome::PollingComponent { public: - OptolinkTextSensor(Optolink *optolink) : OptolinkSensorBase(optolink) {} + OptolinkTextSensor(Optolink *optolink) : DatapointComponent(optolink) {} void set_mode(TextSensorMode mode) { mode_ = mode; } void set_day_of_week(int dow) { dow_ = dow; } @@ -24,20 +24,14 @@ class OptolinkTextSensor : public OptolinkSensorBase, protected: void setup() override; - void update() override { - if (mode_ == STATE_INFO) { - publish_state(optolink_->get_error()); - } else { - optolink_->read_value(datapoint_); - } - } + void update() override; const StringRef &get_component_name() override { return get_name(); } - void value_changed(float state) override { publish_state(std::to_string(state)); }; - void value_changed(uint8_t state) override { publish_state(std::to_string(state)); }; - void value_changed(uint16_t state) override { publish_state(std::to_string(state)); }; - void value_changed(uint32_t state) override; - void value_changed(uint8_t *state, size_t length) override; + void datapoint_value_changed(float state) override { publish_state(std::to_string(state)); }; + void datapoint_value_changed(uint8_t state) override { publish_state(std::to_string(state)); }; + void datapoint_value_changed(uint16_t state) override { publish_state(std::to_string(state)); }; + void datapoint_value_changed(uint32_t state) override; + void datapoint_value_changed(uint8_t *state, size_t length) override; private: TextSensorMode mode_ = MAP; diff --git a/platformio.ini b/platformio.ini index 5c4c17572d..0c4caec4d6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -66,7 +66,7 @@ lib_deps = rweather/Crypto@0.4.0 ; dsmr dudanov/MideaUART@1.1.8 ; midea tonia/HeatpumpIR@1.0.23 ; heatpumpir - bertmelis/VitoWiFi@1.0.2 ; optolink + bertmelis/VitoWiFi@1.1.2 ; optolink build_flags = ${common.build_flags} -DUSE_ARDUINO