diff --git a/esphome/components/optolink/__init__.py b/esphome/components/optolink/__init__.py index e532b3e358..45f1fd306c 100644 --- a/esphome/components/optolink/__init__.py +++ b/esphome/components/optolink/__init__.py @@ -3,18 +3,21 @@ from esphome import pins import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import ( + CONF_ADDRESS, + CONF_BYTES, CONF_DIV_RATIO, CONF_ID, CONF_LOGGER, CONF_PROTOCOL, CONF_RX_PIN, CONF_TX_PIN, + CONF_TYPE, CONF_UPDATE_INTERVAL, ) from esphome.core import CORE CODEOWNERS = ["@j0ta29"] -DEPENDENCIES = [] +DEPENDENCIES = ["sensor"] AUTO_LOAD = [] MULTI_CONF = False @@ -32,6 +35,73 @@ DAY_OF_WEEK = { } CONF_DAY_OF_WEEK = "day_of_week" + +def check_address_for_types(types_address_needed): + def validator_(config): + 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 for this types: {types_address_needed}" + ) + if not address_needed and address_defined: + raise cv.Invalid( + f"{CONF_ADDRESS} is only allowed for this types: {types_address_needed}" + ) + return config + + return validator_ + + +def check_bytes_for_types(types_bytes_needed): + def validator_(config): + 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 for this types: {types_bytes_needed}" + ) + if not bytes_needed and bytes_defined: + raise cv.Invalid( + f"{CONF_BYTES} is only allowed for this types: {types_bytes_needed}" + ) + + 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 types: {types_bytes_range_1_to_9}" + ) + + types_bytes_day_schedule = ["DAY_SCHEDULE"] + 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 types: {types_bytes_day_schedule}" + ) + + return config + + return validator_ + + +def check_dow_for_types(types_dow_needed): + def validator_(config): + 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 for this types: {types_dow_needed}" + ) + 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 for this types: {types_dow_needed}" + ) + return config + + return validator_ + + OptolinkComponent = optolink_ns.class_("Optolink", cg.Component) SENSOR_BASE_SCHEMA = cv.Schema( { diff --git a/esphome/components/optolink/datapoint_component.cpp b/esphome/components/optolink/datapoint_component.cpp index e52eebb4f3..7543667e73 100644 --- a/esphome/components/optolink/datapoint_component.cpp +++ b/esphome/components/optolink/datapoint_component.cpp @@ -258,6 +258,8 @@ void DatapointComponent::set_optolink_state_(const char *format, ...) { std::string DatapointComponent::get_optolink_state_() { return optolink_->get_state(); } +int DatapointComponent::get_optolink_queue_size_() { return optolink_->get_queue_size(); }; + 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 fbb5dbead5..47b9b0c659 100644 --- a/esphome/components/optolink/datapoint_component.h +++ b/esphome/components/optolink/datapoint_component.h @@ -48,6 +48,7 @@ class DatapointComponent { void unfitting_value_type_(); void set_optolink_state_(const char *format, ...); std::string get_optolink_state_(); + int get_optolink_queue_size_(); private: const size_t max_retries_until_reset_ = 10; diff --git a/esphome/components/optolink/optolink.cpp b/esphome/components/optolink/optolink.cpp index 825ff58618..7644592d59 100644 --- a/esphome/components/optolink/optolink.cpp +++ b/esphome/components/optolink/optolink.cpp @@ -32,10 +32,9 @@ void Optolink::setup() { #endif } -void Optolink::loop() { - // ESP_LOGD(TAG, "queue size: %d", VitoWiFi.queueSize()); - VitoWiFi.loop(); -} +void Optolink::loop() { VitoWiFi.loop(); } + +int Optolink::get_queue_size() { return VitoWiFi.queueSize(); } void Optolink::set_state(const char *format, ...) { va_list args; diff --git a/esphome/components/optolink/optolink.h b/esphome/components/optolink/optolink.h index 358f9f2309..6f85dafec9 100644 --- a/esphome/components/optolink/optolink.h +++ b/esphome/components/optolink/optolink.h @@ -3,6 +3,7 @@ #ifdef USE_ARDUINO #include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" #include "VitoWiFi.h" namespace esphome { @@ -34,6 +35,8 @@ class Optolink : public esphome::Component, public Print { void set_state(const char *format, ...); std::string get_state() { return state_; } + + int get_queue_size(); }; } // namespace optolink diff --git a/esphome/components/optolink/sensor/__init__.py b/esphome/components/optolink/sensor/__init__.py index 4c91a4666f..19604877e3 100644 --- a/esphome/components/optolink/sensor/__init__.py +++ b/esphome/components/optolink/sensor/__init__.py @@ -7,27 +7,42 @@ from esphome.const import ( CONF_DIV_RATIO, CONF_ID, CONF_MIN_VALUE, + CONF_TYPE, +) +from .. import ( + CONF_OPTOLINK_ID, + SENSOR_BASE_SCHEMA, + check_address_for_types, + check_bytes_for_types, + optolink_ns, ) -from .. import CONF_OPTOLINK_ID, SENSOR_BASE_SCHEMA, optolink_ns DEPENDENCIES = ["optolink"] CODEOWNERS = ["@j0ta29"] +SensorType = optolink_ns.enum("SensorType") +TYPE = { + "DATAPOINT": SensorType.SENSOR_TYPE_DATAPOINT, + "QUEUE_SIZE": SensorType.SENSOR_TYPE_QUEUE_SIZE, +} OptolinkSensor = optolink_ns.class_( "OptolinkSensor", sensor.Sensor, cg.PollingComponent ) -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( sensor.SENSOR_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(OptolinkSensor), - cv.Required(CONF_ADDRESS): cv.hex_uint32_t, - cv.Required(CONF_BYTES): cv.one_of(1, 2, 4, int=True), + cv.Optional(CONF_TYPE, default="DATAPOINT"): cv.enum(TYPE, upper=True), + cv.Optional(CONF_ADDRESS): cv.hex_uint32_t, + cv.Optional(CONF_BYTES): cv.one_of(1, 2, 4, int=True), cv.Optional(CONF_MIN_VALUE): cv.float_, } ) .extend(cv.COMPONENT_SCHEMA) - .extend(SENSOR_BASE_SCHEMA) + .extend(SENSOR_BASE_SCHEMA), + check_address_for_types(["DATAPOINT"]), + check_bytes_for_types(["DATAPOINT"]), ) @@ -38,8 +53,12 @@ async def to_code(config): await cg.register_component(var, config) await sensor.register_sensor(var, config) - cg.add(var.set_address(config[CONF_ADDRESS])) - cg.add(var.set_bytes(config[CONF_BYTES])) - cg.add(var.set_div_ratio(config[CONF_DIV_RATIO])) + cg.add(var.set_type(config[CONF_TYPE])) + if CONF_ADDRESS in config: + cg.add(var.set_address(config[CONF_ADDRESS])) + if CONF_BYTES in config: + cg.add(var.set_bytes(config[CONF_BYTES])) + if CONF_DIV_RATIO in config: + cg.add(var.set_div_ratio(config[CONF_DIV_RATIO])) if CONF_MIN_VALUE in config: cg.add(var.set_min_value(config[CONF_MIN_VALUE])) diff --git a/esphome/components/optolink/sensor/optolink_sensor.cpp b/esphome/components/optolink/sensor/optolink_sensor.cpp index 15407c25d7..1f9ac262e7 100644 --- a/esphome/components/optolink/sensor/optolink_sensor.cpp +++ b/esphome/components/optolink/sensor/optolink_sensor.cpp @@ -1,6 +1,7 @@ #ifdef USE_ARDUINO #include "optolink_sensor.h" +#include "esphome/core/application.h" #include "../optolink.h" namespace esphome { @@ -8,7 +9,26 @@ namespace optolink { static const char *const TAG = "optolink.sensor"; -void OptolinkSensor::set_min_value(float min_value) { min_value_ = -29.3; } +void OptolinkSensor::setup() { + switch (type_) { + case SENSOR_TYPE_DATAPOINT: + setup_datapoint_(); + break; + case SENSOR_TYPE_QUEUE_SIZE: + break; + } +}; + +void OptolinkSensor::update() { + switch (type_) { + case SENSOR_TYPE_DATAPOINT: + datapoint_read_request_(); + break; + case SENSOR_TYPE_QUEUE_SIZE: + publish_state(get_optolink_queue_size_()); + break; + } +} // NOLINTBEGIN void OptolinkSensor::datapoint_value_changed(uint8_t value) { diff --git a/esphome/components/optolink/sensor/optolink_sensor.h b/esphome/components/optolink/sensor/optolink_sensor.h index cb42146b3d..7903a75013 100644 --- a/esphome/components/optolink/sensor/optolink_sensor.h +++ b/esphome/components/optolink/sensor/optolink_sensor.h @@ -10,17 +10,20 @@ namespace esphome { namespace optolink { +enum SensorType { SENSOR_TYPE_DATAPOINT, SENSOR_TYPE_QUEUE_SIZE }; + class OptolinkSensor : public DatapointComponent, public esphome::sensor::Sensor, public esphome::PollingComponent { public: OptolinkSensor(Optolink *optolink) : DatapointComponent(optolink) { set_state_class(esphome::sensor::STATE_CLASS_MEASUREMENT); } - void set_min_value(float min_value); + void set_type(SensorType type) { type_ = type; } + void set_min_value(float min_value) { min_value_ = min_value; } protected: - void setup() override { setup_datapoint_(); } - void update() override { datapoint_read_request_(); } + void setup() override; + void update() override; const StringRef &get_component_name() override { return get_name(); } void datapoint_value_changed(float value) override { publish_state(value); }; @@ -29,6 +32,7 @@ class OptolinkSensor : public DatapointComponent, public esphome::sensor::Sensor void datapoint_value_changed(uint32_t value) override; private: + SensorType type_ = SENSOR_TYPE_DATAPOINT; float min_value_ = -FLT_MAX; }; } // namespace optolink diff --git a/esphome/components/optolink/text_sensor/__init__.py b/esphome/components/optolink/text_sensor/__init__.py index a41817bb6b..d0201f0c96 100644 --- a/esphome/components/optolink/text_sensor/__init__.py +++ b/esphome/components/optolink/text_sensor/__init__.py @@ -12,6 +12,9 @@ from esphome.const import ( from .. import ( CONF_DAY_OF_WEEK, DAY_OF_WEEK, + check_address_for_types, + check_bytes_for_types, + check_dow_for_types, optolink_ns, CONF_OPTOLINK_ID, SENSOR_BASE_SCHEMA, @@ -25,7 +28,6 @@ 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, } @@ -34,103 +36,6 @@ OptolinkTextSensor = optolink_ns.class_( "OptolinkTextSensor", text_sensor.TextSensor, cg.PollingComponent ) - -def check_address(): - def validator_(config): - types_address_needed = [ - "MAP", - "RAW", - "DAY_SCHEDULE", - "DAY_SCHEDULE_SYNCHRONIZED", - ] - 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 for this types: {types_address_needed}" - ) - if not address_needed and address_defined: - raise cv.Invalid( - f"{CONF_ADDRESS} is only allowed for this types: {types_address_needed}" - ) - return config - - return validator_ - - -def check_bytes(): - def validator_(config): - 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 for this types: {types_bytes_needed}" - ) - if not bytes_needed and bytes_defined: - raise cv.Invalid( - f"{CONF_BYTES} is only allowed for this types: {types_bytes_needed}" - ) - - 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 types: {types_bytes_range_1_to_9}" - ) - - 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 types: {types_bytes_day_schedule}" - ) - - return config - - return validator_ - - -def check_dow(): - def validator_(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 for this types: {types_dow_needed}" - ) - 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 for this types: {types_dow_needed}" - ) - return config - - return validator_ - - -def check_entity_id(): - def validator_(config): - types_entitiy_id_needed = ["DAY_SCHEDULE_SYNCHRONIZED"] - if ( - config[CONF_TYPE] in types_entitiy_id_needed - and CONF_ENTITY_ID not in config - ): - raise cv.Invalid( - f"{CONF_ENTITY_ID} is required for this types: {types_entitiy_id_needed}" - ) - if ( - 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 for this types: {types_entitiy_id_needed}" - ) - return config - - return validator_ - - CONFIG_SCHEMA = cv.All( text_sensor.TEXT_SENSOR_SCHEMA.extend( { @@ -144,10 +49,9 @@ CONFIG_SCHEMA = cv.All( ) .extend(cv.COMPONENT_SCHEMA) .extend(SENSOR_BASE_SCHEMA), - check_address(), - check_bytes(), - check_dow(), - check_entity_id(), + check_address_for_types(["MAP", "RAW", "DAY_SCHEDULE"]), + check_bytes_for_types(["MAP", "RAW", "DAY_SCHEDULE"]), + check_dow_for_types(["DAY_SCHEDULE"]), ) @@ -161,7 +65,8 @@ async def to_code(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_DIV_RATIO in config: + 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: diff --git a/tests/components/optolink/common.yaml b/tests/components/optolink/common.yaml index d04cdd2be1..5da2a9eddb 100644 --- a/tests/components/optolink/common.yaml +++ b/tests/components/optolink/common.yaml @@ -13,6 +13,12 @@ sensor: icon: mdi:thermometer-chevron-down device_class: temperature update_interval: 120s + - platform: optolink + type: QUEUE_SIZE + name: Optolink Queue Size + icon: mdi:queue-first-in-last-out + entity_category: diagnostic + update_interval: 1s text_sensor: - platform: optolink