diff --git a/esphome/components/optolink/__init__.py b/esphome/components/optolink/__init__.py index 1fba368228..998ebaca0d 100644 --- a/esphome/components/optolink/__init__.py +++ b/esphome/components/optolink/__init__.py @@ -21,6 +21,17 @@ MULTI_CONF = False optolink_ns = cg.esphome_ns.namespace("optolink") CONF_OPTOLINK_ID = "optolink_id" +DAY_OF_WEEK = { + "MONDAY": 0, + "TUESDAY": 1, + "WEDNESDAY": 2, + "THURSDAY": 3, + "FRIDAY": 4, + "SATURDAY": 5, + "SUNDAY": 6, +} +CONF_DAY_OF_WEEK = "day_of_week" + OptolinkComponent = optolink_ns.class_("Optolink", cg.Component) CONF_OPTOLINK_ID = "optolink_id" SENSOR_BASE_SCHEMA = cv.Schema( diff --git a/esphome/components/optolink/datapoint_component.cpp b/esphome/components/optolink/datapoint_component.cpp index a831565e27..e52eebb4f3 100644 --- a/esphome/components/optolink/datapoint_component.cpp +++ b/esphome/components/optolink/datapoint_component.cpp @@ -11,9 +11,6 @@ namespace optolink { static const char *const TAG = "optolink.datapoint_component"; -// NOLINTNEXTLINE -static std::vector hass_subscriptions_; - void DatapointComponent::setup_datapoint_() { switch (div_ratio_) { case 0: @@ -261,38 +258,6 @@ void DatapointComponent::set_optolink_state_(const char *format, ...) { std::string DatapointComponent::get_optolink_state_() { return optolink_->get_state(); } -void DatapointComponent::subscribe_hass_(const std::string &entity_id, const std::function &f) { - for (auto &subscription : hass_subscriptions_) { - if (subscription.entity_id == entity_id) { - subscription.callbacks.push_back(f); - return; - } - } - // NOLINTNEXTLINE - HassSubscription subscription{entity_id, ""}; - subscription.callbacks.push_back(f); - hass_subscriptions_.push_back(subscription); - -#ifdef USE_API - if (api::global_api_server != nullptr) { - api::global_api_server->subscribe_home_assistant_state( - entity_id, optional(), [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.last_state != state) { - if (subscription.entity_id == entity_id) { - subscription.last_state = state; - for (const auto &callback : subscription.callbacks) { - callback(state); - } - } - } - } - }); - } -#endif -} - 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 index ca926a5e8d..fbb5dbead5 100644 --- a/esphome/components/optolink/datapoint_component.h +++ b/esphome/components/optolink/datapoint_component.h @@ -11,12 +11,6 @@ namespace optolink { class Optolink; -struct HassSubscription { - std::string entity_id; - std::string last_state; - std::vector> callbacks; -}; - class DatapointComponent { public: DatapointComponent(Optolink *optolink, bool writeable = false) : dp_value_outstanding_((uint8_t) 0) { @@ -55,8 +49,6 @@ class DatapointComponent { void set_optolink_state_(const char *format, ...); std::string get_optolink_state_(); - void subscribe_hass_(const std::string &entity_id, const std::function &f); - private: const size_t max_retries_until_reset_ = 10; Optolink *optolink_; diff --git a/esphome/components/optolink/helpers.cpp b/esphome/components/optolink/helpers.cpp new file mode 100644 index 0000000000..a25cb0f543 --- /dev/null +++ b/esphome/components/optolink/helpers.cpp @@ -0,0 +1,89 @@ +#ifdef USE_ARDUINO + +#include "helpers.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace optolink { + +static const char *const TAG = "optolink.helpers"; + +void rtrim(std::string &s) { + if (s.empty()) + return; + + std::string::iterator p; + for (p = s.end(); p != s.begin() && *--p == ' ';) + ; + + if (*p != ' ') + p++; + + s.erase(p, s.end()); +} + +std::string decode_day_schedule(uint8_t *input) { + char buffer[49]; + for (int i = 0; i < 8; i++) { + int hour = input[i] >> 3; + int minute = (input[i] & 0b111) * 10; + if (input[i] != 0xFF) { + sprintf(buffer + i * 6, "%02d:%02d ", hour, minute); + } else { + sprintf(buffer + i * 6, " "); + } + } + return std::string(buffer); +} + +uint8_t *encode_day_schedule(const std::string &input, uint8_t *output) { + char buffer[49]; + strncpy(buffer, input.c_str(), sizeof(buffer)); + buffer[sizeof(buffer) - 1] = 0x00; + Time time_values[8]; + Time prev_time = {0, 0}; + int time_count = 0; + + char *token = strtok(buffer, " "); + while (token && time_count < 8) { + Time current_time; + // NOLINTNEXTLINE + if (sscanf(token, "%d:%d", ¤t_time.hours, ¤t_time.minutes) == 2) { + if (check_time_values(current_time) && check_time_sequence(prev_time, current_time)) { + time_values[time_count++] = current_time; + prev_time = current_time; + } else { + ESP_LOGE( + TAG, + "Time values should be in the format hh:mm and in increasing order within the range of 00:00 to 23:59"); + return nullptr; + } + } else { + ESP_LOGE(TAG, "Invalid time format"); + return nullptr; + } + token = strtok(nullptr, " "); + } + + if (time_count % 2) { + ESP_LOGE(TAG, "Number of time values must be even"); + return nullptr; + } + + while (time_count < 8) { + time_values[time_count++] = {31, 70}; + } + + for (int i = 0; i < 8; i++) { + Time time = time_values[i]; + output[i] = (time.hours << 3) + (time.minutes / 10); + } + + return output; +} + +} // namespace optolink +} // namespace esphome + +#endif diff --git a/esphome/components/optolink/helpers.h b/esphome/components/optolink/helpers.h new file mode 100644 index 0000000000..cb46ea44d9 --- /dev/null +++ b/esphome/components/optolink/helpers.h @@ -0,0 +1,31 @@ +#ifdef USE_ARDUINO + +#include +#include + +namespace esphome { +namespace optolink { + +struct Time { + int hours; + int minutes; +}; + +inline bool check_time_sequence(const Time &t1, const Time &t2) { + return t2.hours > t1.hours || (t2.hours == t1.hours && t2.minutes >= t1.minutes); +} + +inline bool check_time_values(const Time &time) { + return (time.hours >= 0 && time.hours <= 23) && (time.minutes >= 0 && time.minutes <= 59); +} + +void rtrim(std::string &s); + +std::string decode_day_schedule(uint8_t *input); + +uint8_t *encode_day_schedule(const std::string &input, uint8_t *output); + +} // namespace optolink +} // namespace esphome + +#endif diff --git a/esphome/components/optolink/text/__init__.py b/esphome/components/optolink/text/__init__.py new file mode 100644 index 0000000000..6cd6261f34 --- /dev/null +++ b/esphome/components/optolink/text/__init__.py @@ -0,0 +1,62 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import text +from esphome.const import ( + CONF_ADDRESS, + CONF_BYTES, + CONF_DIV_RATIO, + CONF_ENTITY_ID, + CONF_ID, + CONF_TYPE, + CONF_MODE, +) +from .. import ( + CONF_DAY_OF_WEEK, + DAY_OF_WEEK, + optolink_ns, + CONF_OPTOLINK_ID, + SENSOR_BASE_SCHEMA, +) + +DEPENDENCIES = ["optolink", "api"] +CODEOWNERS = ["@j0ta29"] + +TextType = optolink_ns.enum("TextType") +TYPE = {"DAY_SCHEDULE": TextType.TEXT_TYPE_DAY_SCHEDULE} + +OptolinkText = optolink_ns.class_("OptolinkText", text.Text, cg.PollingComponent) + + +CONFIG_SCHEMA = cv.All( + text.TEXT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(OptolinkText), + cv.Optional(CONF_MODE, default="TEXT"): cv.enum(text.TEXT_MODES), + cv.Required(CONF_TYPE): cv.enum(TYPE, upper=True), + cv.Required(CONF_ADDRESS): cv.hex_uint32_t, + cv.Required(CONF_BYTES): cv.one_of(56, int=True), + cv.Required(CONF_DAY_OF_WEEK): cv.enum(DAY_OF_WEEK, upper=True), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(SENSOR_BASE_SCHEMA), +) + + +async def to_code(config): + component = await cg.get_variable(config[CONF_OPTOLINK_ID]) + var = cg.new_Pvariable(config[CONF_ID], component) + + await cg.register_component(var, config) + await text.register_text(var, config) + + cg.add(var.set_type(config[CONF_TYPE])) + if CONF_ADDRESS in config: + cg.add(var.set_address(config[CONF_ADDRESS])) + cg.add(var.set_div_ratio(config[CONF_DIV_RATIO])) + if CONF_BYTES in config: + cg.add(var.set_bytes(config[CONF_BYTES])) + if CONF_DAY_OF_WEEK in config: + cg.add(var.set_day_of_week(config[CONF_DAY_OF_WEEK])) + if CONF_ENTITY_ID in config: + cg.add(var.set_entity_id(config[CONF_ENTITY_ID])) diff --git a/esphome/components/optolink/text/optolink_text.cpp b/esphome/components/optolink/text/optolink_text.cpp new file mode 100644 index 0000000000..e54a0d9eb0 --- /dev/null +++ b/esphome/components/optolink/text/optolink_text.cpp @@ -0,0 +1,58 @@ +#ifdef USE_ARDUINO + +#include "esphome/core/log.h" +#include "optolink_text.h" +#include "../optolink.h" +#include "../datapoint_component.h" +#include "../helpers.h" + +namespace esphome { +namespace optolink { + +static const char *const TAG = "optolink.text"; + +void OptolinkText::setup() { + switch (type_) { + case TEXT_TYPE_DAY_SCHEDULE: + set_writeable(true); + set_div_ratio(0); + set_bytes(8); + set_address(get_address_() + 8 * dow_); + traits.set_max_length(48); + traits.set_pattern("(((([0-1]?[0-9]|2[0-3]):[0-5]0)( |$)){2})*"); + break; + } + setup_datapoint_(); +}; + +void OptolinkText::control(const std::string &value) { + ESP_LOGE(TAG, "control %s", value.c_str()); + + ESP_LOGD(TAG, "update for schedule plan for component %s: %s", get_component_name().c_str(), value.c_str()); + uint8_t buffer[8]; + uint8_t *data = encode_day_schedule(value, buffer); + if (data) { + write_datapoint_value_(data, 8); + } else { + ESP_LOGW(TAG, "not changing any value of datapoint %s", get_component_name().c_str()); + } +} + +void OptolinkText::datapoint_value_changed(uint8_t *value, size_t length) { + switch (type_) { + case TEXT_TYPE_DAY_SCHEDULE: + if (length == 8) { + auto schedule = decode_day_schedule(value); + rtrim(schedule); + publish_state(schedule); + } else { + unfitting_value_type_(); + } + break; + } +}; + +} // namespace optolink +} // namespace esphome + +#endif diff --git a/esphome/components/optolink/text/optolink_text.h b/esphome/components/optolink/text/optolink_text.h new file mode 100644 index 0000000000..3c2b12c161 --- /dev/null +++ b/esphome/components/optolink/text/optolink_text.h @@ -0,0 +1,37 @@ +#pragma once + +#ifdef USE_ARDUINO + +#include "esphome/components/text/text.h" +#include "../optolink.h" +#include "../datapoint_component.h" + +namespace esphome { +namespace optolink { + +enum TextType { TEXT_TYPE_DAY_SCHEDULE }; + +class OptolinkText : public DatapointComponent, public esphome::text::Text, public esphome::PollingComponent { + public: + OptolinkText(Optolink *optolink) : DatapointComponent(optolink) {} + + void set_type(TextType type) { type_ = type; } + void set_day_of_week(int dow) { dow_ = dow; } + + protected: + void setup() override; + void update() override { datapoint_read_request_(); } + void control(const std::string &value); + + const StringRef &get_component_name() override { return get_name(); } + void datapoint_value_changed(uint8_t *value, size_t length) override; + + private: + TextType type_; + int dow_ = 0; +}; + +} // namespace optolink +} // namespace esphome + +#endif diff --git a/esphome/components/optolink/text_sensor/__init__.py b/esphome/components/optolink/text_sensor/__init__.py index 376fdd66c8..257cf030a0 100644 --- a/esphome/components/optolink/text_sensor/__init__.py +++ b/esphome/components/optolink/text_sensor/__init__.py @@ -7,32 +7,28 @@ from esphome.const import ( CONF_DIV_RATIO, CONF_ENTITY_ID, CONF_ID, - CONF_MODE, + CONF_TYPE, +) +from .. import ( + CONF_DAY_OF_WEEK, + DAY_OF_WEEK, + optolink_ns, + CONF_OPTOLINK_ID, + SENSOR_BASE_SCHEMA, ) -from .. import optolink_ns, CONF_OPTOLINK_ID, SENSOR_BASE_SCHEMA DEPENDENCIES = ["optolink", "api"] CODEOWNERS = ["@j0ta29"] -TextSensorMode = optolink_ns.enum("TextSensorMode") -MODE = { - "MAP": TextSensorMode.MAP, - "RAW": TextSensorMode.RAW, - "DAY_SCHEDULE": TextSensorMode.DAY_SCHEDULE, - "DAY_SCHEDULE_SYNCHRONIZED": TextSensorMode.DAY_SCHEDULE_SYNCHRONIZED, - "DEVICE_INFO": TextSensorMode.DEVICE_INFO, - "STATE_INFO": TextSensorMode.STATE_INFO, +TextSensorType = optolink_ns.enum("TextSensorType") +TYPE = { + "MAP": TextSensorType.TEXT_SENSOR_TYPE_MAP, + "RAW": TextSensorType.TEXT_SENSOR_TYPE_RAW, + "DAY_SCHEDULE": TextSensorType.TEXT_SENSOR_TYPE_DAY_SCHEDULE, + "DAY_SCHEDULE_SYNCHRONIZED": TextSensorType.TEXT_SENSOR_TYPE_DAY_SCHEDULE_SYNCHRONIZED, + "DEVICE_INFO": TextSensorType.TEXT_SENSOR_TYPE_DEVICE_INFO, + "STATE_INFO": TextSensorType.TEXT_SENSOR_TYPE_STATE_INFO, } -DAY_OF_WEEK = { - "MONDAY": 0, - "TUESDAY": 1, - "WEDNESDAY": 2, - "THURSDAY": 3, - "FRIDAY": 4, - "SATURDAY": 5, - "SUNDAY": 6, -} -CONF_DAY_OF_WEEK = "day_of_week" OptolinkTextSensor = optolink_ns.class_( "OptolinkTextSensor", text_sensor.TextSensor, cg.PollingComponent @@ -41,21 +37,21 @@ OptolinkTextSensor = optolink_ns.class_( def check_address(): def validator_(config): - modes_address_needed = [ + types_address_needed = [ "MAP", "RAW", "DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED", ] - address_needed = config[CONF_MODE] in modes_address_needed + address_needed = config[CONF_TYPE] in types_address_needed address_defined = CONF_ADDRESS in config if address_needed and not address_defined: raise cv.Invalid( - f"{CONF_ADDRESS} is required in this modes: {modes_address_needed}" + f"{CONF_ADDRESS} is required for this types: {types_address_needed}" ) if not address_needed and address_defined: raise cv.Invalid( - f"{CONF_ADDRESS} is only allowed in this modes mode: {modes_address_needed}" + f"{CONF_ADDRESS} is only allowed for this types: {types_address_needed}" ) return config @@ -64,32 +60,32 @@ def check_address(): def check_bytes(): def validator_(config): - modes_bytes_needed = ["MAP", "RAW", "DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED"] - bytes_needed = config[CONF_MODE] in modes_bytes_needed + types_bytes_needed = ["MAP", "RAW", "DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED"] + bytes_needed = config[CONF_TYPE] in types_bytes_needed bytes_defined = CONF_BYTES in config if bytes_needed and not bytes_defined: raise cv.Invalid( - f"{CONF_BYTES} is required in this modes: {modes_bytes_needed}" + f"{CONF_BYTES} is required for this types: {types_bytes_needed}" ) if not bytes_needed and bytes_defined: raise cv.Invalid( - f"{CONF_BYTES} is only allowed in this modes: {modes_bytes_needed}" + f"{CONF_BYTES} is only allowed for this types: {types_bytes_needed}" ) - modes_bytes_range_1_to_9 = ["MAP", "RAW"] - if config[CONF_MODE] in modes_bytes_range_1_to_9 and config[ + types_bytes_range_1_to_9 = ["MAP", "RAW"] + if config[CONF_TYPE] in types_bytes_range_1_to_9 and config[ CONF_BYTES ] not in range(0, 10): raise cv.Invalid( - f"{CONF_BYTES} must be between 1 and 9 for this modes: {modes_bytes_range_1_to_9}" + f"{CONF_BYTES} must be between 1 and 9 for this types: {types_bytes_range_1_to_9}" ) - modes_bytes_day_schedule = ["DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED"] - if config[CONF_MODE] in modes_bytes_day_schedule and config[CONF_BYTES] not in [ + types_bytes_day_schedule = ["DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED"] + if config[CONF_TYPE] in types_bytes_day_schedule and config[CONF_BYTES] not in [ 56 ]: raise cv.Invalid( - f"{CONF_BYTES} must be 56 for this modes: {modes_bytes_day_schedule}" + f"{CONF_BYTES} must be 56 for this types: {types_bytes_day_schedule}" ) return config @@ -99,14 +95,14 @@ def check_bytes(): def check_dow(): def validator_(config): - modes_dow_needed = ["DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED"] - if config[CONF_MODE] in modes_dow_needed and CONF_DAY_OF_WEEK not in config: + types_dow_needed = ["DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED"] + if config[CONF_TYPE] in types_dow_needed and CONF_DAY_OF_WEEK not in config: raise cv.Invalid( - f"{CONF_DAY_OF_WEEK} is required in this modes: {modes_dow_needed}" + f"{CONF_DAY_OF_WEEK} is required for this types: {types_dow_needed}" ) - if config[CONF_MODE] not in modes_dow_needed and CONF_DAY_OF_WEEK in config: + if config[CONF_TYPE] not in types_dow_needed and CONF_DAY_OF_WEEK in config: raise cv.Invalid( - f"{CONF_DAY_OF_WEEK} is only allowed in this modes: {modes_dow_needed}" + f"{CONF_DAY_OF_WEEK} is only allowed for this types: {types_dow_needed}" ) return config @@ -115,20 +111,20 @@ def check_dow(): def check_entity_id(): def validator_(config): - modes_entitiy_id_needed = ["DAY_SCHEDULE_SYNCHRONIZED"] + types_entitiy_id_needed = ["DAY_SCHEDULE_SYNCHRONIZED"] if ( - config[CONF_MODE] in modes_entitiy_id_needed + config[CONF_TYPE] in types_entitiy_id_needed and CONF_ENTITY_ID not in config ): raise cv.Invalid( - f"{CONF_ENTITY_ID} is required in this modes: {modes_entitiy_id_needed}" + f"{CONF_ENTITY_ID} is required for this types: {types_entitiy_id_needed}" ) if ( - config[CONF_MODE] not in modes_entitiy_id_needed + config[CONF_TYPE] not in types_entitiy_id_needed and CONF_ENTITY_ID in config ): raise cv.Invalid( - f"{CONF_ENTITY_ID} is only allowed in this modes: {modes_entitiy_id_needed}" + f"{CONF_ENTITY_ID} is only allowed for this types: {types_entitiy_id_needed}" ) return config @@ -139,7 +135,7 @@ CONFIG_SCHEMA = cv.All( text_sensor.TEXT_SENSOR_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(OptolinkTextSensor), - cv.Required(CONF_MODE): cv.enum(MODE, upper=True), + cv.Required(CONF_TYPE): cv.enum(TYPE, upper=True), cv.Optional(CONF_ADDRESS): cv.hex_uint32_t, cv.Optional(CONF_BYTES): cv.uint8_t, cv.Optional(CONF_DAY_OF_WEEK): cv.enum(DAY_OF_WEEK, upper=True), @@ -162,7 +158,7 @@ async def to_code(config): await cg.register_component(var, config) await text_sensor.register_text_sensor(var, config) - cg.add(var.set_mode(config[CONF_MODE])) + cg.add(var.set_type(config[CONF_TYPE])) if CONF_ADDRESS in config: cg.add(var.set_address(config[CONF_ADDRESS])) cg.add(var.set_div_ratio(config[CONF_DIV_RATIO])) diff --git a/esphome/components/optolink/text_sensor/optolink_text_sensor.cpp b/esphome/components/optolink/text_sensor/optolink_text_sensor.cpp index 07628c7493..0de105b4f8 100644 --- a/esphome/components/optolink/text_sensor/optolink_text_sensor.cpp +++ b/esphome/components/optolink/text_sensor/optolink_text_sensor.cpp @@ -4,107 +4,31 @@ #include "optolink_text_sensor.h" #include "../optolink.h" #include "../datapoint_component.h" +#include "../helpers.h" namespace esphome { namespace optolink { static const char *const TAG = "optolink.text_sensor"; -struct Time { - int hours; - int minutes; -}; - -bool check_time_sequence(const Time &t1, const Time &t2) { - return t2.hours > t1.hours || (t2.hours == t1.hours && t2.minutes >= t1.minutes); -} - -bool check_time_values(const Time &time) { - return (time.hours >= 0 && time.hours <= 23) && (time.minutes >= 0 && time.minutes <= 59); -} - -uint8_t *encode_time_string(const std::string &input) { - char buffer[49]; - strncpy(buffer, input.c_str(), sizeof(buffer)); - buffer[sizeof(buffer) - 1] = 0x00; - Time time_values[8]; - Time prev_time = {0, 0}; - int time_count = 0; - - char *token = strtok(buffer, " "); - while (token && time_count < 8) { - Time current_time; - // NOLINTNEXTLINE - if (sscanf(token, "%d:%d", ¤t_time.hours, ¤t_time.minutes) == 2) { - if (check_time_values(current_time) && check_time_sequence(prev_time, current_time)) { - time_values[time_count++] = current_time; - prev_time = current_time; - } else { - ESP_LOGE( - TAG, - "Time values should be in the format hh:mm and in increasing order within the range of 00:00 to 23:59"); - return nullptr; - } - } else { - ESP_LOGE(TAG, "Invalid time format"); - return nullptr; - } - token = strtok(nullptr, " "); - } - - if (time_count % 2) { - ESP_LOGE(TAG, "Number of time values must be even"); - return nullptr; - } - - while (time_count < 8) { - time_values[time_count++] = {31, 70}; - } - - static uint8_t data[8]; - for (int i = 0; i < 8; i++) { - Time time = time_values[i]; - data[i] = (time.hours << 3) + (time.minutes / 10); - } - - return data; -} - void OptolinkTextSensor::setup() { - switch (mode_) { - case MAP: + switch (type_) { + case TEXT_SENSOR_TYPE_MAP: break; - case RAW: + case TEXT_SENSOR_TYPE_RAW: set_div_ratio(0); break; - case DAY_SCHEDULE: + case TEXT_SENSOR_TYPE_DAY_SCHEDULE: set_div_ratio(0); set_bytes(8); set_address(get_address_() + 8 * dow_); break; - case DAY_SCHEDULE_SYNCHRONIZED: - 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: + case TEXT_SENSOR_TYPE_DEVICE_INFO: set_entity_category(esphome::ENTITY_CATEGORY_DIAGNOSTIC); set_bytes(4); set_address(0x00f8); break; - case STATE_INFO: + case TEXT_SENSOR_TYPE_STATE_INFO: set_entity_category(esphome::ENTITY_CATEGORY_DIAGNOSTIC); return; // no datapoint setup! } @@ -112,7 +36,7 @@ void OptolinkTextSensor::setup() { }; void OptolinkTextSensor::update() { - if (mode_ == STATE_INFO) { + if (type_ == TEXT_SENSOR_TYPE_STATE_INFO) { publish_state(get_optolink_state_()); } else { datapoint_read_request_(); @@ -120,39 +44,30 @@ void OptolinkTextSensor::update() { } void OptolinkTextSensor::datapoint_value_changed(uint8_t *value, size_t length) { - switch (mode_) { - case RAW: + switch (type_) { + case TEXT_SENSOR_TYPE_RAW: publish_state(std::string((const char *) value)); break; - case DAY_SCHEDULE: - case DAY_SCHEDULE_SYNCHRONIZED: + case TEXT_SENSOR_TYPE_DAY_SCHEDULE: if (length == 8) { - char buffer[6 * length + 1]; - for (int i = 0; i < 8; i++) { - int hour = value[i] >> 3; - int minute = (value[i] & 0b111) * 10; - if (value[i] != 0xFF) { - sprintf(buffer + i * 6, "%02d:%02d ", hour, minute); - } else { - sprintf(buffer + i * 6, " "); - } - } - publish_state(buffer); + auto schedule = decode_day_schedule(value); + rtrim(schedule); + publish_state(schedule); } else { unfitting_value_type_(); } break; - case DEVICE_INFO: - case STATE_INFO: - case MAP: + case TEXT_SENSOR_TYPE_DEVICE_INFO: + case TEXT_SENSOR_TYPE_STATE_INFO: + case TEXT_SENSOR_TYPE_MAP: unfitting_value_type_(); break; } }; void OptolinkTextSensor::datapoint_value_changed(uint32_t value) { - switch (mode_) { - case DEVICE_INFO: { + switch (type_) { + case TEXT_SENSOR_TYPE_DEVICE_INFO: { uint8_t *bytes = (uint8_t *) &value; uint16_t tmp = esphome::byteswap(*((uint16_t *) bytes)); std::string geraetekennung = esphome::format_hex_pretty(&tmp, 1); diff --git a/esphome/components/optolink/text_sensor/optolink_text_sensor.h b/esphome/components/optolink/text_sensor/optolink_text_sensor.h index fe97a68434..a819320e1d 100644 --- a/esphome/components/optolink/text_sensor/optolink_text_sensor.h +++ b/esphome/components/optolink/text_sensor/optolink_text_sensor.h @@ -9,7 +9,13 @@ namespace esphome { namespace optolink { -enum TextSensorMode { MAP, RAW, DAY_SCHEDULE, DAY_SCHEDULE_SYNCHRONIZED, DEVICE_INFO, STATE_INFO }; +enum TextSensorType { + TEXT_SENSOR_TYPE_MAP, + TEXT_SENSOR_TYPE_RAW, + TEXT_SENSOR_TYPE_DAY_SCHEDULE, + TEXT_SENSOR_TYPE_DEVICE_INFO, + TEXT_SENSOR_TYPE_STATE_INFO +}; class OptolinkTextSensor : public DatapointComponent, public esphome::text_sensor::TextSensor, @@ -17,7 +23,7 @@ class OptolinkTextSensor : public DatapointComponent, public: OptolinkTextSensor(Optolink *optolink) : DatapointComponent(optolink) {} - void set_mode(TextSensorMode mode) { mode_ = mode; } + void set_type(TextSensorType type) { type_ = type; } void set_day_of_week(int dow) { dow_ = dow; } void set_entity_id(const std::string &entity_id) { entity_id_ = entity_id; } @@ -33,7 +39,7 @@ class OptolinkTextSensor : public DatapointComponent, void datapoint_value_changed(uint8_t *value, size_t length) override; private: - TextSensorMode mode_ = MAP; + TextSensorType type_ = TEXT_SENSOR_TYPE_MAP; int dow_ = 0; std::string entity_id_; };