From d12fede41e1224a5b7b6d22125e5c1697ba7a078 Mon Sep 17 00:00:00 2001 From: dentra Date: Tue, 12 Mar 2024 18:58:31 +0300 Subject: [PATCH] #26 cleanup obstolete climate and unify vports --- components/tion-api/tion-api-4s.cpp | 2 - components/tion/__init__.py | 639 ++++++------------ components/tion/binary_sensor/__init__.py | 6 +- .../tion/binary_sensor/tion_binary_sensor.h | 6 +- components/tion/button/__init__.py | 19 +- components/tion/button/tion_button.h | 25 +- components/tion/climate/__init__.py | 4 +- components/tion/climate/tion_climate.cpp | 3 +- components/tion/climate/tion_climate.h | 2 +- .../tion_climate_helpers.h} | 17 - components/tion/fan/tion_fan.h | 2 +- components/tion/number/tion_number.h | 2 +- components/tion/select/tion_select.h | 2 +- components/tion/sensor/tion_sensor.h | 2 +- components/tion/switch/__init__.py | 12 +- components/tion/switch/tion_switch.h | 2 +- components/tion/text_sensor/__init__.py | 8 +- .../tion/text_sensor/tion_text_sensor.h | 2 +- components/tion/tion_api_component.cpp | 88 --- components/tion/tion_api_component.h | 113 ---- components/tion/tion_climate_component.cpp | 208 ------ components/tion/tion_climate_component.h | 189 ------ components/tion/tion_climate_presets.cpp | 289 -------- components/tion/tion_climate_presets.h | 166 ----- components/tion/tion_component.cpp | 157 ++++- components/tion/tion_component.h | 221 +++--- components/tion/tion_controls.h | 62 -- components/tion/tion_defines.h | 7 - components/tion/tion_properties.h | 2 +- components/tion/tion_vport_ble.h | 6 +- components/tion/tion_vport_jtag.h | 6 +- components/tion/tion_vport_uart.h | 6 +- components/tion_3s/__init__.py | 48 -- components/tion_3s/climate/__init__.py | 22 - .../tion_3s/climate/tion_3s_climate.cpp | 91 --- components/tion_3s/climate/tion_3s_climate.h | 61 -- components/tion_3s/tion_3s.h | 33 - components/tion_3s_ble/__init__.py | 0 .../tion_3s_ble_vport.cpp} | 4 +- .../tion_3s_ble_vport.h} | 8 +- .../__init__.py => tion_3s_ble/vport.py} | 6 +- components/tion_3s_uart/tion_3s_uart.cpp | 15 - .../tion_3s_uart/tion_3s_uart_vport.cpp | 13 + .../{tion_3s_uart.h => tion_3s_uart_vport.h} | 2 +- components/tion_4s/__init__.py | 47 -- components/tion_4s/climate/__init__.py | 28 - .../tion_4s/climate/tion_4s_climate.cpp | 230 ------- components/tion_4s/climate/tion_4s_climate.h | 110 --- components/tion_4s/fan/__init__.py | 21 - components/tion_4s/fan/tion_4s_fan.cpp | 20 - components/tion_4s/fan/tion_4s_fan.h | 18 - components/tion_4s/tion_4s.h | 30 - components/tion_4s_ble/__init__.py | 0 .../tion_4s_ble_vport.cpp} | 4 +- .../tion_4s_ble_vport.h} | 8 +- .../__init__.py => tion_4s_ble/vport.py} | 6 +- ...ion_4s_uart.cpp => tion_4s_uart_vport.cpp} | 4 +- .../{tion_4s_uart.h => tion_4s_uart_vport.h} | 3 +- components/tion_lt/__init__.py | 130 ---- components/tion_lt/climate/__init__.py | 17 - .../tion_lt/climate/tion_lt_climate.cpp | 61 -- components/tion_lt/climate/tion_lt_climate.h | 49 -- components/tion_lt/tion_lt.cpp | 12 - components/tion_lt/tion_lt.h | 34 - components/tion_lt_ble/__init__.py | 0 .../tion_lt_ble_vport.cpp} | 4 +- .../tion_lt_ble_vport.h} | 8 +- .../__init__.py => tion_lt_ble/vport.py} | 6 +- components/tion_o2/__init__.py | 35 - components/tion_o2/climate/__init__.py | 22 - .../tion_o2/climate/tion_o2_climate.cpp | 70 -- components/tion_o2/climate/tion_o2_climate.h | 31 - components/tion_o2/tion_o2.cpp | 12 - components/tion_o2/tion_o2.h | 22 - components/tion_o2_proxy/__init__.py | 2 +- components/tion_o2_uart/__init__.py | 1 + .../tion_o2_uart_vport.cpp} | 7 +- .../tion_o2_uart_vport.h} | 9 +- .../__init__.py => tion_o2_uart/vport.py} | 2 +- tion.yaml.j2 | 278 ++++---- 80 files changed, 734 insertions(+), 3185 deletions(-) rename components/tion/{tion_helpers.h => climate/tion_climate_helpers.h} (56%) delete mode 100644 components/tion/tion_api_component.cpp delete mode 100644 components/tion/tion_api_component.h delete mode 100644 components/tion/tion_climate_component.cpp delete mode 100644 components/tion/tion_climate_component.h delete mode 100644 components/tion/tion_climate_presets.cpp delete mode 100644 components/tion/tion_climate_presets.h delete mode 100644 components/tion/tion_controls.h delete mode 100644 components/tion/tion_defines.h delete mode 100644 components/tion_3s/__init__.py delete mode 100644 components/tion_3s/climate/__init__.py delete mode 100644 components/tion_3s/climate/tion_3s_climate.cpp delete mode 100644 components/tion_3s/climate/tion_3s_climate.h delete mode 100644 components/tion_3s/tion_3s.h create mode 100644 components/tion_3s_ble/__init__.py rename components/{tion_3s/vport/tion_3s_vport.cpp => tion_3s_ble/tion_3s_ble_vport.cpp} (96%) rename components/{tion_3s/vport/tion_3s_vport.h => tion_3s_ble/tion_3s_ble_vport.h} (86%) rename components/{tion_3s/vport/__init__.py => tion_3s_ble/vport.py} (78%) delete mode 100644 components/tion_3s_uart/tion_3s_uart.cpp create mode 100644 components/tion_3s_uart/tion_3s_uart_vport.cpp rename components/tion_3s_uart/{tion_3s_uart.h => tion_3s_uart_vport.h} (92%) delete mode 100644 components/tion_4s/__init__.py delete mode 100644 components/tion_4s/climate/__init__.py delete mode 100644 components/tion_4s/climate/tion_4s_climate.cpp delete mode 100644 components/tion_4s/climate/tion_4s_climate.h delete mode 100644 components/tion_4s/fan/__init__.py delete mode 100644 components/tion_4s/fan/tion_4s_fan.cpp delete mode 100644 components/tion_4s/fan/tion_4s_fan.h delete mode 100644 components/tion_4s/tion_4s.h create mode 100644 components/tion_4s_ble/__init__.py rename components/{tion_4s/vport/tion_4s_vport.cpp => tion_4s_ble/tion_4s_ble_vport.cpp} (69%) rename components/{tion_4s/vport/tion_4s_vport.h => tion_4s_ble/tion_4s_ble_vport.h} (60%) rename components/{tion_4s/vport/__init__.py => tion_4s_ble/vport.py} (65%) rename components/tion_4s_uart/{tion_4s_uart.cpp => tion_4s_uart_vport.cpp} (91%) rename components/tion_4s_uart/{tion_4s_uart.h => tion_4s_uart_vport.h} (92%) delete mode 100644 components/tion_lt/__init__.py delete mode 100644 components/tion_lt/climate/__init__.py delete mode 100644 components/tion_lt/climate/tion_lt_climate.cpp delete mode 100644 components/tion_lt/climate/tion_lt_climate.h delete mode 100644 components/tion_lt/tion_lt.cpp delete mode 100644 components/tion_lt/tion_lt.h create mode 100644 components/tion_lt_ble/__init__.py rename components/{tion_lt/vport/tion_lt_vport.cpp => tion_lt_ble/tion_lt_ble_vport.cpp} (69%) rename components/{tion_lt/vport/tion_lt_vport.h => tion_lt_ble/tion_lt_ble_vport.h} (60%) rename components/{tion_lt/vport/__init__.py => tion_lt_ble/vport.py} (65%) delete mode 100644 components/tion_o2/__init__.py delete mode 100644 components/tion_o2/climate/__init__.py delete mode 100644 components/tion_o2/climate/tion_o2_climate.cpp delete mode 100644 components/tion_o2/climate/tion_o2_climate.h delete mode 100644 components/tion_o2/tion_o2.cpp delete mode 100644 components/tion_o2/tion_o2.h create mode 100644 components/tion_o2_uart/__init__.py rename components/{tion_o2/vport/tion_o2_vport.cpp => tion_o2_uart/tion_o2_uart_vport.cpp} (60%) rename components/{tion_o2/vport/tion_o2_vport.h => tion_o2_uart/tion_o2_uart_vport.h} (68%) rename components/{tion_o2/vport/__init__.py => tion_o2_uart/vport.py} (94%) diff --git a/components/tion-api/tion-api-4s.cpp b/components/tion-api/tion-api-4s.cpp index 534625e..83a9a9a 100644 --- a/components/tion-api/tion-api-4s.cpp +++ b/components/tion-api/tion-api-4s.cpp @@ -204,7 +204,6 @@ bool Tion4sApi::factory_reset(const TionState &state, uint32_t request_id) const return this->write_frame(FRAME_TYPE_STATE_SET, st_set, request_id); } -#ifdef TION_ENABLE_PRESETS bool Tion4sApi::set_turbo(uint16_t time, uint32_t request_id) const { TION_LOGD(TAG, "Request[%" PRIu32 "] Turbo %u", request_id, time); const struct { @@ -213,7 +212,6 @@ bool Tion4sApi::set_turbo(uint16_t time, uint32_t request_id) const { } PACKED turbo{.time = time, .err_code = 0}; return this->write_frame(FRAME_TYPE_TURBO_SET, turbo, request_id); }; -#endif #ifdef TION_ENABLE_HEARTBEAT bool Tion4sApi::send_heartbeat() const { diff --git a/components/tion/__init__.py b/components/tion/__init__.py index e4e9a2b..0935a89 100644 --- a/components/tion/__init__.py +++ b/components/tion/__init__.py @@ -3,14 +3,14 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import binary_sensor as esphome_binary_sensor -from esphome.components import button as esphome_button -from esphome.components import climate -from esphome.components import number as esphome_number -from esphome.components import select as esphome_select + +# from esphome.components import binary_sensor as esphome_binary_sensor +# from esphome.components import button as esphome_button +# from esphome.components import number as esphome_number +# from esphome.components import select as esphome_select +# from esphome.components import text_sensor as esphome_text_sensor from esphome.components import sensor as esphome_sensor from esphome.components import switch as esphome_switch -from esphome.components import text_sensor as esphome_text_sensor from esphome.const import ( CONF_ACCURACY_DECIMALS, CONF_DEVICE_CLASS, @@ -21,20 +21,9 @@ CONF_ID, CONF_POWER, CONF_STATE_CLASS, + CONF_TEMPERATURE, CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, - CONF_VERSION, - DEVICE_CLASS_DURATION, - DEVICE_CLASS_PROBLEM, - DEVICE_CLASS_TEMPERATURE, - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, - ENTITY_CATEGORY_NONE, - STATE_CLASS_MEASUREMENT, - UNIT_CELSIUS, - UNIT_CUBIC_METER_PER_HOUR, - UNIT_MINUTE, - UNIT_SECOND, ) from esphome.core import ID from esphome.cpp_generator import MockObjClass @@ -42,66 +31,37 @@ from .. import vport # pylint: disable=relative-beyond-top-level CODEOWNERS = ["@dentra"] -# ESP_PLATFORMS = [PLATFORM_ESP32] -# DEPENDENCIES = ["ble_client"] -AUTO_LOAD = [ - "etl", - "tion-api", - "sensor", - "switch", - "text_sensor", - "binary_sensor", - "number", - "select", - "climate", - "button", -] +AUTO_LOAD = ["etl", "tion-api"] _LOGGER = logging.getLogger(__name__) -ICON_AIR_FILTER = "mdi:air-filter" - CONF_TION_API_BASE_ID = "tion_api_base_id" CONF_TION_API_ID = "tion_api_id" CONF_TION_COMPONENT_CLASS = "tion_component_class" -CONF_BUZZER = "buzzer" -CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" -CONF_FILTER_TIME_LEFT = "filter_time_left" -CONF_BOOST_TIME = "boost_time" -CONF_BOOST_TIME_LEFT = "boost_time_left" CONF_PRESETS = "presets" -CONF_PRESET_MODE = "mode" CONF_PRESET_FAN_SPEED = "fan_speed" -CONF_PRESET_TARGET_TEMPERATURE = "target_temperature" +CONF_PRESET_TEMPERATURE = CONF_TEMPERATURE CONF_PRESET_GATE_POSITION = "gate_position" -CONF_RESET_FILTER = "reset_filter" -CONF_RESET_FILTER_CONFIRM = "reset_filter_confirm" +CONF_BUTTON_PRESETS = "button_presets" + CONF_STATE_TIMEOUT = "state_timeout" CONF_STATE_WARNOUT = "state_warnout" -CONF_FILTER_WARNOUT = "filter_warnout" -CONF_PRODUCTIVITY = "productivity" CONF_BATCH_TIMEOUT = "batch_timeout" -CONF_ERRORS = "errors" -CONF_WORK_TIME = "work_time" -UNIT_DAYS = "d" tion_ns = cg.esphome_ns.namespace("tion") -TionBoostTimeNumber = tion_ns.class_("TionBoostTimeNumber", esphome_number.Number) -TionBuzzerSwitchT = tion_ns.class_("TionBuzzerSwitch", esphome_switch.Switch) TionVPortApi = tion_ns.class_("TionVPortApi") -TionResetFilterButtonT = tion_ns.class_("TionResetFilterButton", esphome_button.Button) -TionResetFilterConfirmSwitchT = tion_ns.class_( - "TionResetFilterConfirmSwitch", esphome_switch.Switch -) -TionGatePosition = tion_ns.namespace("TionGatePosition") +TionApiComponent = tion_ns.class_("TionApiComponent", cg.Component) + +dentra_tion_ns = cg.global_ns.namespace("dentra").namespace("tion") +TionGatePosition = dentra_tion_ns.namespace("TionGatePosition") -PRESET_MODES = { - "off": climate.ClimateMode.CLIMATE_MODE_OFF, - "heat": climate.ClimateMode.CLIMATE_MODE_HEAT, - "fan_only": climate.ClimateMode.CLIMATE_MODE_FAN_ONLY, - "heat_cool": climate.ClimateMode.CLIMATE_MODE_HEAT_COOL, +BREEZER_TYPES = { + "o2": tion_ns.class_("TionO2ApiComponent", TionApiComponent), + "3s": tion_ns.class_("Tion3sApiComponent", TionApiComponent), + "4s": tion_ns.class_("Tion4sApiComponent", TionApiComponent), + "lt": tion_ns.class_("TionLtApiComponent", TionApiComponent), } PRESET_GATE_POSITIONS = { @@ -111,187 +71,48 @@ "mixed": TionGatePosition.MIXED, } -PRESETS = [] -PRESET_SCHEMA = cv.Schema( +CUSTOM_PRESET_SCHEMA = cv.Schema( { - cv.Optional(CONF_PRESET_MODE): cv.one_of(*PRESET_MODES, lower=True), - cv.Optional(CONF_PRESET_FAN_SPEED): cv.int_range(min=1, max=6), - cv.Optional(CONF_PRESET_TARGET_TEMPERATURE): cv.int_range(min=1, max=30), - cv.Optional(CONF_PRESET_GATE_POSITION): cv.one_of( + cv.Optional(CONF_POWER): cv.boolean, + cv.Optional(CONF_HEATER): cv.boolean, + cv.Optional(CONF_PRESET_FAN_SPEED, default=0): cv.int_range(min=0, max=6), + cv.Optional(CONF_PRESET_TEMPERATURE, default=0): cv.int_range(min=-30, max=30), + cv.Optional(CONF_PRESET_GATE_POSITION, default="none"): cv.one_of( *PRESET_GATE_POSITIONS, lower=True ), } ) -PRESETS_SCHEMA = cv.Schema( - { - cv.Optional("home"): cv.Any(PRESET_SCHEMA, None), - cv.Optional("away"): cv.Any(PRESET_SCHEMA, None), - cv.Optional("boost"): cv.Any(PRESET_SCHEMA, None), - cv.Optional("comfort"): cv.Any(PRESET_SCHEMA, None), - cv.Optional("eco"): cv.Any(PRESET_SCHEMA, None), - cv.Optional("sleep"): cv.Any(PRESET_SCHEMA, None), - cv.Optional("activity"): cv.Any(PRESET_SCHEMA, None), - } -) - -CUSTOM_PRESET_SCHEMA = cv.Schema( +BUTTON_PRESETS_SCHEMA = cv.Schema( { - cv.Optional(CONF_POWER, default=True): cv.boolean, - cv.Optional(CONF_HEATER, default=True): cv.boolean, - cv.Optional(CONF_PRESET_FAN_SPEED): cv.int_range(min=1, max=6), - cv.Optional(CONF_PRESET_TARGET_TEMPERATURE, default=0): cv.int_range( - min=-30, max=30 + cv.Required(CONF_PRESET_TEMPERATURE): cv.All( + cv.ensure_list(cv.int_range(min=1, max=25)), cv.Length(min=3, max=3) ), - cv.Optional(CONF_PRESET_GATE_POSITION, default="none"): cv.one_of( - *PRESET_GATE_POSITIONS, lower=True + cv.Required(CONF_PRESET_FAN_SPEED): cv.All( + cv.ensure_list(cv.int_range(min=1, max=6)), cv.Length(min=3, max=3) ), } ) -def declare_type(type): - from esphome.schema_extractors import schema_extractor - - @schema_extractor("declare_type") - def validator(value): - print("@@@@@", value) - return type +def check_type(key, typ): + def validator(config): + if key in config and config[CONF_TYPE] != typ: + raise cv.Invalid(f"{key} is not valid for the type {typ}") + return config return validator -def tion_schema_api( - tion_class: MockObjClass, - tion_api_class: MockObjClass, - tion_base_schema: cv.Schema = cv.Schema({}), - polling_interval: str = "60s", -): - return ( - tion_base_schema.extend( - { - cv.GenerateID(): cv.declare_id(tion_class), - cv.GenerateID(CONF_TION_API_BASE_ID): cv.declare_id(tion_api_class), - cv.GenerateID(CONF_TION_API_ID): cv.declare_id(TionVPortApi), - cv.Optional(CONF_STATE_TIMEOUT, default="3s"): cv.update_interval, - cv.Optional( - CONF_BATCH_TIMEOUT, default="200ms" - ): cv.positive_time_period_milliseconds, - cv.Optional(CONF_FORCE_UPDATE): cv.boolean, - cv.Required(CONF_PRESETS): cv.Schema( - {cv.string_strict: CUSTOM_PRESET_SCHEMA} - ), - } - ) - .extend(vport.VPORT_CLIENT_SCHEMA) - .extend(cv.polling_component_schema(polling_interval)) - ) - - -def tion_schema( - tion_class: MockObjClass, tion_api_class: MockObjClass, tion_base_schema: cv.Schema -): - """Declare base tion schema""" - return tion_schema_api(tion_class, tion_api_class, tion_base_schema).extend( - { - cv.Optional(CONF_ICON, default=ICON_AIR_FILTER): cv.icon, - cv.Optional(CONF_STATE_WARNOUT): esphome_binary_sensor.binary_sensor_schema( - device_class=DEVICE_CLASS_PROBLEM, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_BUZZER): esphome_switch.switch_schema( - TionBuzzerSwitchT.template(tion_class), - icon="mdi:volume-high", - entity_category=ENTITY_CATEGORY_CONFIG, - block_inverted=True, - ), - cv.Optional(CONF_OUTDOOR_TEMPERATURE): esphome_sensor.sensor_schema( - unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=0, - device_class=DEVICE_CLASS_TEMPERATURE, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_NONE, - ), - cv.Optional(CONF_VERSION): esphome_text_sensor.text_sensor_schema( - esphome_text_sensor.TextSensor, - icon="mdi:git", - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_FILTER_TIME_LEFT): esphome_sensor.sensor_schema( - unit_of_measurement=UNIT_DAYS, - accuracy_decimals=0, - icon=ICON_AIR_FILTER, - # device_class=DEVICE_CLASS_DURATION, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_NONE, - ), - cv.Optional(CONF_BOOST_TIME): esphome_number.number_schema( - TionBoostTimeNumber, - icon="mdi:clock-fast", - unit_of_measurement=UNIT_MINUTE, - entity_category=ENTITY_CATEGORY_CONFIG, - ), - cv.Optional(CONF_BOOST_TIME_LEFT): esphome_sensor.sensor_schema( - unit_of_measurement=UNIT_SECOND, - accuracy_decimals=1, - icon="mdi:clock-end", - device_class=DEVICE_CLASS_DURATION, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_NONE, - ), - cv.Optional(CONF_PRESETS): PRESETS_SCHEMA, - cv.Optional(CONF_RESET_FILTER): button.button_schema( - TionResetFilterButtonT.template(tion_class), - icon="mdi:wrench-cog", - entity_category=ENTITY_CATEGORY_CONFIG, - ), - cv.Optional(CONF_RESET_FILTER_CONFIRM): esphome_switch.switch_schema( - TionResetFilterConfirmSwitchT.template(tion_class), - icon="mdi:wrench-check", - entity_category=ENTITY_CATEGORY_CONFIG, - block_inverted=True, - ), - cv.Optional( - CONF_FILTER_WARNOUT - ): esphome_binary_sensor.binary_sensor_schema( - device_class=DEVICE_CLASS_PROBLEM, - entity_category=ENTITY_CATEGORY_NONE, - ), - cv.Optional(CONF_PRODUCTIVITY): esphome_sensor.sensor_schema( - unit_of_measurement=UNIT_CUBIC_METER_PER_HOUR, - accuracy_decimals=0, - icon="mdi:weather-windy", - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_ERRORS): esphome_text_sensor.text_sensor_schema( - icon="mdi:alert", - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_WORK_TIME): esphome_sensor.sensor_schema( - unit_of_measurement=UNIT_SECOND, - accuracy_decimals=0, - icon="mdi:power", - device_class=DEVICE_CLASS_DURATION, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - } - ) - - -def tion_vport_ble_schema(vport_class: MockObjClass, io_class: MockObjClass): - return vport.vport_ble_schema(vport_class, io_class) - - -async def setup_binary_sensor(config: dict, key: str, setter): - """Setup binary sensor""" - if key not in config: - return None - sens = await esphome_binary_sensor.new_binary_sensor(config[key]) - cg.add(setter(sens)) - cg.add_define(f"USE_TION_{key.upper()}") - return sens +# async def setup_binary_sensor(config: dict, key: str, setter): +# """Setup binary sensor""" +# if key not in config: +# return None +# sens = await esphome_binary_sensor.new_binary_sensor(config[key]) +# cg.add(setter(sens)) +# cg.add_define(f"USE_TION_{key.upper()}") +# return sens async def setup_switch(config: dict, key: str, setter, parent): @@ -304,190 +125,60 @@ async def setup_switch(config: dict, key: str, setter, parent): return swch -async def setup_sensor(config: dict, key: str, setter): - """Setup sensor""" - if key not in config: - return None - sens = await esphome_sensor.new_sensor(config[key]) - cg.add(setter(sens)) - cg.add_define(f"USE_TION_{key.upper()}") - return sens - - -async def setup_text_sensor(config: dict, key: str, setter): - """Setup text sensor""" - if key not in config: - return None - sens = await esphome_text_sensor.new_text_sensor(config[key]) - cg.add(setter(sens)) - cg.add_define(f"USE_TION_{key.upper()}") - return sens - - -async def setup_number( - config: dict, key: str, setter, min_value: int, max_value: int, step: int -): - """Setup number""" - if key not in config: - return None - numb = await esphome_number.new_number( - config[key], min_value=min_value, max_value=max_value, step=step - ) - cg.add(setter(numb)) - cg.add_define(f"USE_TION_{key.upper()}") - return numb - - -async def setup_select(config: dict, key: str, setter, parent, options): - """Setup select""" - if key not in config: - return None - conf = config[key] - slct = cg.new_Pvariable(conf[CONF_ID], parent) - await esphome_select.register_select(slct, conf, options=options) - cg.add(setter(slct)) - cg.add_define(f"USE_TION_{key.upper()}") - return slct - - -async def setup_presets(config, key, setter) -> bool: - """Setup presets""" - if key not in config: - return False - conf = config[key] - - for preset_name, preset in climate.CLIMATE_PRESETS.items(): - preset_name = preset_name.lower() - if preset_name == "none": - continue - if preset_name not in conf: - # use OFF to disable preset - cg.add(setter(preset, climate.ClimateMode.CLIMATE_MODE_OFF)) - continue - - options = conf[preset_name] or {} - mode = ( - PRESET_MODES[options[CONF_PRESET_MODE]] - if CONF_PRESET_MODE in options - else climate.ClimateMode.CLIMATE_MODE_AUTO # use AUTO to do not touch default mode - ) - fan_speed = options.get(CONF_PRESET_FAN_SPEED, 0) - target_temperature = options.get(CONF_PRESET_TARGET_TEMPERATURE, 0) - gate_position_str = options.get(CONF_PRESET_GATE_POSITION, "none") - gate_position = PRESET_GATE_POSITIONS[gate_position_str] - cg.add(setter(preset, mode, fan_speed, target_temperature, gate_position)) - - cg.add_build_flag("-DTION_ENABLE_PRESETS") - - return True - - -async def setup_button(config: dict, key: str, setter, parent): - """Setup button""" - if key not in config: - return None - butn = await button.new_button(config[key], parent) - cg.add(setter(butn)) - cg.add_define(f"USE_TION_{key.upper()}") - return butn - - -async def setup_tion_api(config: dict, component_source: str = None): - prt = await vport.vport_get_var(config) - api = cg.new_Pvariable( - config[CONF_TION_API_ID], - cg.TemplateArguments( - vport.vport_find(config).type.class_("frame_spec_type"), - config[CONF_TION_API_BASE_ID].type, - ), - prt, - ) - cg.add(prt.set_api(api)) - - var = cg.new_Pvariable(config[CONF_ID], api, prt.get_type()) - await cg.register_component(var, config) - if component_source: - cg.add(var.set_component_source(f"tion_{component_source}")) - - # cg.add_library("tion-api", None, "https://github.com/dentra/tion-api") - cg.add_build_flag("-DTION_ESPHOME") - - cg.add(var.set_state_timeout(config[CONF_STATE_TIMEOUT])) - cg.add(var.set_batch_timeout(config[CONF_BATCH_TIMEOUT])) - if CONF_FORCE_UPDATE in config: - cg.add(var.set_force_update(config[CONF_FORCE_UPDATE])) - if CONF_BOOST_TIME in config: - cg.add(var.set_boost_time(config[CONF_BOOST_TIME])) - - if CONF_PRESETS in config: - presets = set() - for preset in config[CONF_PRESETS]: - preset_name = str(preset).strip() - if preset_name.lower() == "none": - _LOGGER.warning("Preset 'none' is reserved") - continue - if preset_name.lower() in presets: - _LOGGER.warning("Preset '%s' is already exists", preset) - continue - preset = config[CONF_PRESETS][preset_name] - cg.add( - var.add_preset( - preset_name, - cg.StructInitializer( - "", - ("target_temperature", preset[CONF_PRESET_TARGET_TEMPERATURE]), - ("heater_state", preset[CONF_HEATER]), - ("power_state", preset[CONF_POWER]), - ("fan_speed", preset[CONF_PRESET_FAN_SPEED]), - ( - "gate_position", - PRESET_GATE_POSITIONS[preset[CONF_PRESET_GATE_POSITION]], - ), - ), - ) - ) - - presets.add(preset_name) - - return var - - -async def setup_tion_core(config, component_reg): - """Setup core component properties""" - - var = await setup_tion_api(config) - - await component_reg(var, config) - - await setup_switch(config, CONF_BUZZER, var.set_buzzer, var) - await setup_sensor(config, CONF_OUTDOOR_TEMPERATURE, var.set_outdoor_temperature) - await setup_sensor(config, CONF_FILTER_TIME_LEFT, var.set_filter_time_left) - await setup_text_sensor(config, CONF_VERSION, var.set_version) - has_presets = await setup_presets(config, CONF_PRESETS, var.update_preset) - if has_presets and "boost" in config[CONF_PRESETS]: - await setup_number(config, CONF_BOOST_TIME, var.set_boost_time, 1, 60, 1) - await setup_sensor(config, CONF_BOOST_TIME_LEFT, var.set_boost_time_left) - await setup_button(config, CONF_RESET_FILTER, var.set_reset_filter, var) - await setup_switch( - config, CONF_RESET_FILTER_CONFIRM, var.set_reset_filter_confirm, var - ) - await setup_binary_sensor(config, CONF_FILTER_WARNOUT, var.set_filter_warnout) - await setup_sensor(config, CONF_PRODUCTIVITY, var.set_productivity) - await setup_text_sensor(config, CONF_ERRORS, var.set_errors) - await setup_sensor(config, CONF_WORK_TIME, var.set_work_time) - await setup_binary_sensor(config, CONF_STATE_WARNOUT, var.set_state_warnout) - - cg.add_define("USE_TION_CLIMATE") - - return var - - -async def setup_tion_vport_ble(config): - var = await vport.setup_vport_ble(config) - return var - - -TionApiComponent = tion_ns.class_("TionApiComponent") +# async def setup_sensor(config: dict, key: str, setter): +# """Setup sensor""" +# if key not in config: +# return None +# sens = await esphome_sensor.new_sensor(config[key]) +# cg.add(setter(sens)) +# cg.add_define(f"USE_TION_{key.upper()}") +# return sens + + +# async def setup_text_sensor(config: dict, key: str, setter): +# """Setup text sensor""" +# if key not in config: +# return None +# sens = await esphome_text_sensor.new_text_sensor(config[key]) +# cg.add(setter(sens)) +# cg.add_define(f"USE_TION_{key.upper()}") +# return sens + + +# async def setup_number( +# config: dict, key: str, setter, min_value: int, max_value: int, step: int +# ): +# """Setup number""" +# if key not in config: +# return None +# numb = await esphome_number.new_number( +# config[key], min_value=min_value, max_value=max_value, step=step +# ) +# cg.add(setter(numb)) +# cg.add_define(f"USE_TION_{key.upper()}") +# return numb + + +# async def setup_select(config: dict, key: str, setter, parent, options): +# """Setup select""" +# if key not in config: +# return None +# conf = config[key] +# slct = cg.new_Pvariable(conf[CONF_ID], parent) +# await esphome_select.register_select(slct, conf, options=options) +# cg.add(setter(slct)) +# cg.add_define(f"USE_TION_{key.upper()}") +# return slct + + +# async def setup_button(config: dict, key: str, setter, parent): +# """Setup button""" +# if key not in config: +# return None +# butn = await esphome_button.new_button(config[key], parent) +# cg.add(setter(butn)) +# cg.add_define(f"USE_TION_{key.upper()}") +# return butn def pc_schema(schema: cv.Schema, pcfg: dict): @@ -551,11 +242,11 @@ def set_prop(var: cg.MockObj, key: str): cg.add(getattr(var, f"set_{key}")(val)) api: ID = config[CONF_TION_API_ID] - parent = await cg.get_variable(api) + paren = await cg.get_variable(api) if props and CONF_TION_COMPONENT_CLASS not in props: - var = await ctor(config, cg.TemplateArguments(get_pc_class()), parent, **kwargs) + var = await ctor(config, cg.TemplateArguments(get_pc_class()), paren, **kwargs) else: - var = await ctor(config, parent, **kwargs) + var = await ctor(config, paren, **kwargs) set_prop(var, CONF_ENTITY_CATEGORY) set_prop(var, CONF_DEVICE_CLASS) @@ -574,16 +265,120 @@ def set_prop(var: cg.MockObj, key: str): return var -# TionO2Api = tion_ns.class_("TionO2Api") -# TionO2ApiComponent = tion_ns.class_( -# "TionO2ApiComponent", cg.Component, TionApiComponent -# ) -# BREEZER_TYPES = {"o2": TionO2ApiComponent} -# CONFIG_SCHEMA = tion_schema_api(TionO2ApiComponent, TionO2Api).extend( -# { -# cv.Required(CONF_TYPE): cv.one_of(*BREEZER_TYPES.keys(), lower=True), -# } -# ) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TionApiComponent), + cv.Required(CONF_TYPE): cv.one_of(*BREEZER_TYPES, lower=True), + cv.Optional(CONF_BUTTON_PRESETS): BUTTON_PRESETS_SCHEMA, + cv.GenerateID(CONF_TION_API_ID): cv.declare_id(TionVPortApi), + cv.Optional(CONF_STATE_TIMEOUT, default="3s"): cv.update_interval, + cv.Optional( + CONF_BATCH_TIMEOUT, default="200ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_FORCE_UPDATE): cv.boolean, + cv.Optional(CONF_PRESETS): cv.Schema( + {cv.string_strict: CUSTOM_PRESET_SCHEMA} + ), + } + ) + .extend(vport.VPORT_CLIENT_SCHEMA) + .extend(cv.polling_component_schema("15s")), + check_type(CONF_BUTTON_PRESETS, "lt"), +) + + +async def _setup_tion_api(config: dict): + component_class: MockObjClass = BREEZER_TYPES[config[CONF_TYPE]] + + # get vport instance + prt = await vport.vport_get_var(config) + # create TionVPortApi wrapper + api = cg.new_Pvariable( + config[CONF_TION_API_ID], + cg.TemplateArguments( + vport.vport_find(config).type.class_("frame_spec_type"), + component_class.class_("Api"), + ), + prt, + ) + cg.add(prt.set_api(api)) + + component_id: ID = config[CONF_ID] + component_id.type = component_class + var = cg.new_Pvariable(config[CONF_ID], api, prt.get_type()) + await cg.register_component(var, config) + + cg.add(var.set_component_source(f"tion_{config[CONF_TYPE]}")) + + # cg.add_library("tion-api", None, "https://github.com/dentra/tion-api") + cg.add_build_flag("-DTION_ESPHOME") + + cg.add(var.set_state_timeout(config[CONF_STATE_TIMEOUT])) + cg.add(var.set_batch_timeout(config[CONF_BATCH_TIMEOUT])) + if CONF_FORCE_UPDATE in config: + cg.add(var.set_force_update(config[CONF_FORCE_UPDATE])) + + return var + + +def _setup_tion_api_presets(config: dict, var: cg.MockObj): + if CONF_PRESETS not in config: + return + + presets = set() + for preset in config[CONF_PRESETS]: + preset_name = str(preset).strip() + if preset_name.lower() == "none": + _LOGGER.warning("Preset 'none' is reserved") + continue + if preset_name.lower() in presets: + _LOGGER.warning("Preset '%s' is already exists", preset) + continue + preset = config[CONF_PRESETS][preset_name] + cg.add( + var.add_preset( + preset_name, + cg.StructInitializer( + "", + ("target_temperature", preset[CONF_PRESET_TEMPERATURE]), + ( + "heater_state", + int(preset[CONF_HEATER]) if CONF_HEATER in preset else -1, + ), + ( + "power_state", + int(preset[CONF_POWER]) if CONF_POWER in preset else -1, + ), + ("fan_speed", preset[CONF_PRESET_FAN_SPEED]), + ( + "gate_position", + PRESET_GATE_POSITIONS[preset[CONF_PRESET_GATE_POSITION]], + ), + ), + ) + ) + + presets.add(preset_name) + + +def _setup_tion_api_button_presets(config: dict, var: cg.MockObj): + if CONF_BUTTON_PRESETS not in config: + return + button_presets = config[CONF_BUTTON_PRESETS] + + cg.add( + var.set_button_presets( + cg.StructInitializer( + "", + ("tmp", button_presets[CONF_TEMPERATURE]), + ("fan", button_presets[CONF_PRESET_FAN_SPEED]), + ) + ) + ) + -# async def to_code(config: dict): -# print(config) +async def to_code(config: dict): + var = await _setup_tion_api(config) + _setup_tion_api_presets(config, var) + _setup_tion_api_button_presets(config, var) diff --git a/components/tion/binary_sensor/__init__.py b/components/tion/binary_sensor/__init__.py index 2e9cabf..9b3bfd6 100644 --- a/components/tion/binary_sensor/__init__.py +++ b/components/tion/binary_sensor/__init__.py @@ -4,7 +4,6 @@ CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, CONF_ICON, - DEVICE_CLASS_DAMPER, DEVICE_CLASS_OPENING, DEVICE_CLASS_PROBLEM, DEVICE_CLASS_RUNNING, @@ -37,7 +36,7 @@ # CONF_ICON: "mdi:air-filter", CONF_DEVICE_CLASS: DEVICE_CLASS_PROBLEM, }, - "gate_position": { + "gate": { CONF_DEVICE_CLASS: DEVICE_CLASS_OPENING, # CONF_DEVICE_CLASS: DEVICE_CLASS_DAMPER, }, @@ -69,7 +68,8 @@ "buzzer": "sound", "heat": "heater", "light": "led", - "gate": "gate_position", + "gate_position": "gate", + "gate_state": "gate", } CONFIG_SCHEMA = pc_schema( diff --git a/components/tion/binary_sensor/tion_binary_sensor.h b/components/tion/binary_sensor/tion_binary_sensor.h index 262b330..c419928 100644 --- a/components/tion/binary_sensor/tion_binary_sensor.h +++ b/components/tion/binary_sensor/tion_binary_sensor.h @@ -7,16 +7,12 @@ #include "esphome/components/binary_sensor/binary_sensor.h" -#include "../tion_api_component.h" +#include "../tion_component.h" #include "../tion_properties.h" namespace esphome { namespace tion { -// template constexpr std::false_type has_is_supported(long); -// template constexpr auto has_is_supported(int) -> decltype(T::is_supported(nullptr), std::true_type{}); -// template using has_is_supported = decltype(has_is_supported(0)); - // C - PropertyController template class TionBinarySensor : public binary_sensor::BinarySensor, public Component, public Parented { diff --git a/components/tion/button/__init__.py b/components/tion/button/__init__.py index 866bdd5..8e2e169 100644 --- a/components/tion/button/__init__.py +++ b/components/tion/button/__init__.py @@ -5,14 +5,12 @@ CONF_ENTITY_CATEGORY, CONF_ICON, CONF_ID, - CONF_TYPE, ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, ) from .. import ( CONF_TION_COMPONENT_CLASS, - TionResetFilterConfirmSwitchT, + check_type, get_pc_info, new_pc_component, pc_schema, @@ -22,6 +20,10 @@ AUTO_LOAD = ["switch"] +TionResetFilterConfirmSwitchT = tion_ns.class_( + "TionResetFilterConfirmSwitch", switch.Switch +) + CONF_RESET_FILTER_CONFIRM = "confirm" TionButton = tion_ns.class_("TionButton", button.Button, cg.Component) @@ -29,7 +31,7 @@ PROPERTIES = { "reset_filter": { CONF_TION_COMPONENT_CLASS: tion_ns.class_( - "TionResetFilterButton2", button.Button, cg.Component + "TionResetFilterButton", button.Button, cg.Component ), CONF_ICON: "mdi:wrench-cog", CONF_ENTITY_CATEGORY: ENTITY_CATEGORY_CONFIG, @@ -41,15 +43,6 @@ } -def check_type(key, typ): - def validator(config): - if key in config and config[CONF_TYPE] != typ: - raise cv.Invalid(f"{key} is not valid for the type {typ}") - return config - - return validator - - CONFIG_SCHEMA = cv.All( pc_schema( button.button_schema(TionButton).extend( diff --git a/components/tion/button/tion_button.h b/components/tion/button/tion_button.h index 6060f3c..04dd1b7 100644 --- a/components/tion/button/tion_button.h +++ b/components/tion/button/tion_button.h @@ -10,7 +10,7 @@ #include "esphome/components/switch/switch.h" #endif -#include "../tion_api_component.h" +#include "../tion_component.h" #include "../tion_properties.h" namespace esphome { @@ -42,9 +42,9 @@ template class TionButton : public button::Button, public Component, pu void press_action() override { C::press_action(this->parent_); } }; -class TionResetFilterButton2 : public TionButton { +class TionResetFilterButton : public TionButton { public: - explicit TionResetFilterButton2(TionApiComponent *api) : TionButton(api) {} + explicit TionResetFilterButton(TionApiComponent *api) : TionButton(api) {} #ifdef USE_SWITCH void setup() override { @@ -70,5 +70,24 @@ class TionResetFilterButton2 : public TionButton class TionResetFilterConfirmSwitch : public Parented, public switch_::Switch { + static_assert(std::is_base_of_v, "parent_t is not Component"); + + public: + explicit TionResetFilterConfirmSwitch(parent_t *parent) : Parented(parent) {} + void write_state(bool state) override { + constexpr const char *timeout_name = "tion_reset_confirm_timeout"; + if (state) { + App.scheduler.set_timeout(this->parent_, timeout_name, 10000, [this]() { this->publish_state(false); }); + } else { + App.scheduler.cancel_timeout(this->parent_, timeout_name); + } + this->publish_state(state); + } +}; +#endif + } // namespace tion } // namespace esphome diff --git a/components/tion/climate/__init__.py b/components/tion/climate/__init__.py index 4569e31..42f1852 100644 --- a/components/tion/climate/__init__.py +++ b/components/tion/climate/__init__.py @@ -3,10 +3,12 @@ from esphome.components import climate from esphome.const import CONF_ICON, CONF_ID -from .. import ICON_AIR_FILTER, new_pc_component, pc_schema, tion_ns +from .. import new_pc_component, pc_schema, tion_ns TionClimate = tion_ns.class_("TionClimate2", climate.Climate, cg.Component) +ICON_AIR_FILTER = "mdi:air-filter" + def climate_climate_schema(class_: cg.MockObj): return climate.CLIMATE_SCHEMA.extend( diff --git a/components/tion/climate/tion_climate.cpp b/components/tion/climate/tion_climate.cpp index ae079dc..356f491 100644 --- a/components/tion/climate/tion_climate.cpp +++ b/components/tion/climate/tion_climate.cpp @@ -1,7 +1,6 @@ #include "esphome/core/log.h" -#include "../tion_helpers.h" - +#include "tion_climate_helpers.h" #include "tion_climate.h" namespace esphome { diff --git a/components/tion/climate/tion_climate.h b/components/tion/climate/tion_climate.h index cb571a7..581816c 100644 --- a/components/tion/climate/tion_climate.h +++ b/components/tion/climate/tion_climate.h @@ -6,7 +6,7 @@ #include "esphome/components/climate/climate.h" -#include "../tion_api_component.h" +#include "../tion_component.h" namespace esphome { namespace tion { diff --git a/components/tion/tion_helpers.h b/components/tion/climate/tion_climate_helpers.h similarity index 56% rename from components/tion/tion_helpers.h rename to components/tion/climate/tion_climate_helpers.h index c25ece9..41753fc 100644 --- a/components/tion/tion_helpers.h +++ b/components/tion/climate/tion_climate_helpers.h @@ -2,12 +2,6 @@ #include "esphome/core/helpers.h" -#ifdef USE_CLIMATE -#include "esphome/components/climate/climate.h" -#endif - -#include "../tion-api/tion-api.h" - namespace esphome { namespace tion { @@ -22,16 +16,5 @@ inline std::string fan_speed_to_mode(uint8_t fan_speed) { return std::string(fan_mode); } -using TionGatePosition = dentra::tion::TionGatePosition; - -template struct TionPresetData { - uint8_t fan_speed; - int8_t target_temperature; - mode_type mode; - TionGatePosition gate_position; - bool is_initialized() const { return this->fan_speed != 0; } - bool is_enabled() const { return this->mode != off_value; } -} PACKED; - } // namespace tion } // namespace esphome diff --git a/components/tion/fan/tion_fan.h b/components/tion/fan/tion_fan.h index 077ff9e..19588e8 100644 --- a/components/tion/fan/tion_fan.h +++ b/components/tion/fan/tion_fan.h @@ -6,7 +6,7 @@ #include "esphome/components/fan/fan.h" -#include "../tion_api_component.h" +#include "../tion_component.h" namespace esphome { namespace tion { diff --git a/components/tion/number/tion_number.h b/components/tion/number/tion_number.h index c2c9afd..2efaeec 100644 --- a/components/tion/number/tion_number.h +++ b/components/tion/number/tion_number.h @@ -7,7 +7,7 @@ #include "esphome/components/number/number.h" -#include "../tion_api_component.h" +#include "../tion_component.h" #include "../tion_properties.h" namespace esphome { diff --git a/components/tion/select/tion_select.h b/components/tion/select/tion_select.h index eb21af2..e55c4ea 100644 --- a/components/tion/select/tion_select.h +++ b/components/tion/select/tion_select.h @@ -7,7 +7,7 @@ #include "esphome/components/select/select.h" -#include "../tion_api_component.h" +#include "../tion_component.h" #include "../tion_properties.h" namespace esphome { diff --git a/components/tion/sensor/tion_sensor.h b/components/tion/sensor/tion_sensor.h index 5dea529..58d3f2f 100644 --- a/components/tion/sensor/tion_sensor.h +++ b/components/tion/sensor/tion_sensor.h @@ -6,7 +6,7 @@ #include "esphome/components/sensor/sensor.h" -#include "../tion_api_component.h" +#include "../tion_component.h" #include "../tion_properties.h" namespace esphome { diff --git a/components/tion/switch/__init__.py b/components/tion/switch/__init__.py index 0a973a3..8560c6b 100644 --- a/components/tion/switch/__init__.py +++ b/components/tion/switch/__init__.py @@ -41,7 +41,7 @@ }, "recirculation": { CONF_DEVICE_CLASS: DEVICE_CLASS_OPENING, - # CONF_ICON: "mdi:valve", + # CONF_ICON: "mdi:valve", # mdi:air-conditioner }, "boost": { # CONF_TION_COMPONENT_CLASS: TionBoost, @@ -54,16 +54,6 @@ "light": "led", } - -def check_type(key, typ): - def validator(config): - if key in config and config[CONF_TYPE] != typ: - raise cv.Invalid(f"{key} is not valid for the type {typ}") - return config - - return validator - - CONFIG_SCHEMA = cv.All( pc_schema( switch.switch_schema(TionSwitch, block_inverted=True).extend( diff --git a/components/tion/switch/tion_switch.h b/components/tion/switch/tion_switch.h index d1160ae..956b602 100644 --- a/components/tion/switch/tion_switch.h +++ b/components/tion/switch/tion_switch.h @@ -7,7 +7,7 @@ #include "esphome/components/switch/switch.h" -#include "../tion_api_component.h" +#include "../tion_component.h" #include "../tion_properties.h" namespace esphome { diff --git a/components/tion/text_sensor/__init__.py b/components/tion/text_sensor/__init__.py index e140370..7eb1df8 100644 --- a/components/tion/text_sensor/__init__.py +++ b/components/tion/text_sensor/__init__.py @@ -1,11 +1,6 @@ import esphome.codegen as cg from esphome.components import text_sensor -from esphome.const import ( - CONF_DEVICE_CLASS, - CONF_ENTITY_CATEGORY, - CONF_ICON, - ENTITY_CATEGORY_DIAGNOSTIC, -) +from esphome.const import CONF_ENTITY_CATEGORY, CONF_ICON, ENTITY_CATEGORY_DIAGNOSTIC from .. import new_pc_component, pc_schema, tion_ns @@ -28,7 +23,6 @@ # aliases "hardware": "hardware_version", "firmware": "firmware_version", - "version": "firmware_version", } diff --git a/components/tion/text_sensor/tion_text_sensor.h b/components/tion/text_sensor/tion_text_sensor.h index 28094a6..708fdb3 100644 --- a/components/tion/text_sensor/tion_text_sensor.h +++ b/components/tion/text_sensor/tion_text_sensor.h @@ -6,7 +6,7 @@ #include "esphome/components/text_sensor/text_sensor.h" -#include "../tion_api_component.h" +#include "../tion_component.h" #include "../tion_properties.h" namespace esphome { diff --git a/components/tion/tion_api_component.cpp b/components/tion/tion_api_component.cpp deleted file mode 100644 index a0549b6..0000000 --- a/components/tion/tion_api_component.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include - -#include "esphome/core/log.h" -#include "tion_api_component.h" - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_api_component"; -static const char *const STATE_TIMEOUT = "state_timeout"; - -void TionApiComponent::call_setup() { - PollingComponent::call_setup(); - if (this->state_timeout_ >= this->get_update_interval()) { - ESP_LOGW(TAG, "Invalid state timout: %.1f s", this->state_timeout_ / 1000.0f); - this->state_timeout_ = 0; - } - if (this->state_timeout_ == 0) { - this->has_state_ = true; - } -} - -void TionApiComponent::dump_config() { - ESP_LOGCONFIG(TAG, "%s:", this->get_component_source()); - ESP_LOGCONFIG(TAG, " Update interval: %.1f s", this->get_update_interval() / 1000.0f); - ESP_LOGCONFIG(TAG, " Force update: %s", ONOFF(this->force_update_)); - ESP_LOGCONFIG(TAG, " State timeout: %.1f s", this->state_timeout_ / 1000.0f); - ESP_LOGCONFIG(TAG, " Batch timeout: %.1f s", this->batch_timeout_ / 1000.0f); -} - -void TionApiComponent::update() { - this->api_->request_state(); - this->state_check_schedule_(); -} - -void TionApiComponent::on_state_(const TionState &state, const uint32_t request_id) { - this->state_check_cancel_(); - this->defer([this]() { - const auto &state = this->state(); - this->state_callback_.call(&state); - }); - // this->state_callback_.call(&state); -} - -void TionApiComponent::state_check_schedule_() { - if (this->state_timeout_ > 0) { - this->set_timeout(STATE_TIMEOUT, this->state_timeout_, [this]() { - this->has_state_ = false; - this->state_check_report_(this->state_timeout_); - }); - } else if (!this->has_state_) { - this->state_check_report_(this->get_update_interval()); - } else { - this->has_state_ = false; - } -} - -void TionApiComponent::state_check_report_(uint32_t timeout) { - ESP_LOGW(TAG, "State was not received in %.1fs", timeout / 1000.0f); - this->state_callback_.call(nullptr); -} - -void TionApiComponent::state_check_cancel_() { - this->has_state_ = true; - if (this->state_timeout_ > 0) { - this->cancel_timeout(STATE_TIMEOUT); - } -} - -dentra::tion::TionStateCall *TionApiComponent::make_call() { - if (this->batch_call_) { - ESP_LOGD(TAG, "Continue batch update for %u ms", this->batch_timeout_); - } else { - ESP_LOGD(TAG, "Starting batch update for %u ms", this->batch_timeout_); - this->batch_call_ = new BatchStateCall(this->api_, this, this->batch_timeout_, [this]() { this->batch_write_(); }); - } - return this->batch_call_; -} - -void TionApiComponent::batch_write_() { - BatchStateCall *batch_call = this->batch_call_; - this->batch_call_ = nullptr; - delete batch_call; - this->state_check_schedule_(); -} - -} // namespace tion -} // namespace esphome diff --git a/components/tion/tion_api_component.h b/components/tion/tion_api_component.h deleted file mode 100644 index 41ec46f..0000000 --- a/components/tion/tion_api_component.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -#include -#include - -#include "esphome/core/defines.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" -#include "esphome/core/component.h" - -#ifdef USE_BINARY_SENSOR -#include "esphome/components/binary_sensor/binary_sensor.h" -#endif - -#include "../tion-api/tion-api.h" -#include "tion_vport.h" -#include "tion_batch_call.h" - -namespace esphome { -namespace tion { - -class TionApiComponent : public PollingComponent { - protected: - using TionApiBase = dentra::tion::TionApiBase; - using TionState = dentra::tion::TionState; - using TionStateCall = dentra::tion::TionStateCall; - using TionGatePosition = dentra::tion::TionGatePosition; - - constexpr static const auto *TAG = "tion_api_component"; - - public: - explicit TionApiComponent(TionApiBase *api, TionVPortType vport_type) : api_(api), vport_type_(vport_type) { - api->on_state_fn.set(*this); - } - - void dump_config() override; - void call_setup() override; - float get_setup_priority() const override { return setup_priority::AFTER_CONNECTION; } - - void update() override; - - /** - * Add a callback for the breezer state, each time the state of the device is updated, this callback will be called. - * When state is not returned in configured period - a callback called with nullptr. - * - * @param callback The callback to call. - */ - void add_on_state_callback(std::function &&callback) { - this->state_callback_.add(std::move(callback)); - } - - /** - * Add a callback for the breezer configuration, each time the configuration parameters of a device - * is updated (using perform() of a StateCall), this callback will be called, before any on_state callback. - * - * @param callback The callback to call. - */ - // void add_on_control_callback(std::function &&callback); - - void set_state_timeout(uint32_t state_timeout) { this->state_timeout_ = state_timeout; }; - void set_batch_timeout(uint32_t batch_timeout) { this->batch_timeout_ = batch_timeout; }; - void set_force_update(bool force_update) { this->force_update_ = force_update; }; - bool get_force_update() const { return this->force_update_; } - void add_preset(const std::string &name, const TionApiBase::PresetData &preset) { - this->api_->add_preset(name, preset); - } - - TionStateCall *make_call(); - - TionApiBase *api() { return this->api_; } - - bool has_state() const { return this->has_state_; } - - const dentra::tion::TionTraits &traits() const { return this->api_->traits(); } - const dentra::tion::TionState &state() const { return this->api_->get_state(); } - - void boost_enable(); - void boost_cancel(); - - protected: - TionApiBase *api_; - TionVPortType vport_type_; - bool has_state_{}; - bool force_update_{}; - BatchStateCall *batch_call_{}; - - uint32_t state_timeout_{}; - uint32_t batch_timeout_{}; - - CallbackManager state_callback_{}; - // CallbackManager control_callback_{}; - - void on_state_(const TionState &state, const uint32_t request_id); - void state_check_schedule_(); - void state_check_cancel_(); - void state_check_report_(uint32_t timeout); - void batch_write_(); -}; - -// T - TionApi implementation -template class TionApiComponentBase : public TionApiComponent { - // static_assert(std::is_base_of_v, - // "A is not derived from dentra::tion::TionApi"); - - public: - explicit TionApiComponentBase(A *api, TionVPortType vport_type) : TionApiComponent(api, vport_type) {} - - protected: - A *typed_api() { return reinterpret_cast(this->api_); } -}; - -} // namespace tion -} // namespace esphome diff --git a/components/tion/tion_climate_component.cpp b/components/tion/tion_climate_component.cpp deleted file mode 100644 index 5c35702..0000000 --- a/components/tion/tion_climate_component.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "esphome/core/defines.h" -#ifdef USE_CLIMATE -#include -#include -#include -#include -#include - -#include "esphome/core/log.h" -#include "esphome/core/preferences.h" -#include "esphome/core/helpers.h" -#include "esphome/components/climate/climate_mode.h" - -#include "tion_component.h" -#include "tion_climate_component.h" - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_climate_component"; - -TionClimateComponentBase::TionClimateComponentBase(TionVPortType vport_type) : vport_type_(vport_type) { - this->target_temperature = NAN; -#ifdef TION_ENABLE_PRESETS - this->preset = climate::CLIMATE_PRESET_NONE; -#endif -} - -void TionClimateComponentBase::call_setup() { - TionComponent::call_setup(); -#ifdef TION_ENABLE_PRESETS - this->setup_presets(); -#endif -} - -void TionClimateComponentBase::dump_settings(const char *TAG, const char *component) const { - LOG_CLIMATE(component, "", this); - LOG_UPDATE_INTERVAL(this); -#ifdef USE_TION_VERSION - LOG_TEXT_SENSOR(" ", "Version", this->version_); -#endif -#ifdef USE_TION_BUZZER - LOG_SWITCH(" ", "Buzzer", this->buzzer_); -#endif -#ifdef USE_TION_LED - LOG_SWITCH(" ", "Led", this->led_); -#endif -#ifdef USE_TION_OUTDOOR_TEMPERATURE - LOG_SENSOR(" ", "Outdoor Temperature", this->outdoor_temperature_); -#endif -#ifdef USE_TION_HEATER_POWER - LOG_SENSOR(" ", "Heater Power", this->heater_power_); -#endif -#ifdef USE_TION_PRODUCTIVITY - LOG_SENSOR(" ", "Productivity", this->productivity_); -#endif -#ifdef USE_TION_AIRFLOW_COUNTER - LOG_SENSOR(" ", "Airflow Counter", this->airflow_counter_); -#endif -#ifdef USE_TION_WORK_TIME - LOG_SENSOR(" ", "Work Time", this->work_time_); -#endif -#ifdef USE_TION_FILTER_TIME_LEFT - LOG_SENSOR(" ", "Filter Time Left", this->filter_time_left_); -#endif -#ifdef USE_TION_FILTER_WARNOUT - LOG_BINARY_SENSOR(" ", "Filter Warnout", this->filter_warnout_); -#endif -#ifdef USE_TION_RESET_FILTER - LOG_BUTTON(" ", "Reset Filter", this->reset_filter_); - LOG_SWITCH(" ", "Reset Filter Confirm", this->reset_filter_confirm_); -#endif -#ifdef USE_TION_STATE_WARNOUT - LOG_BINARY_SENSOR(" ", "State Warnout", this->state_warnout_); - ESP_LOGCONFIG(TAG, " State timeout: %.1fs", this->state_timeout_ / 1000.0f); -#endif - ESP_LOGCONFIG(TAG, " Batch timeout: %.1fs", this->batch_timeout_ / 1000.0f); -} - -climate::ClimateTraits TionClimateComponentBase::traits() { - auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(true); - traits.set_visual_min_temperature(TION_MIN_TEMPERATURE); - traits.set_visual_max_temperature(TION_MAX_TEMPERATURE); - traits.set_visual_temperature_step(1.0f); - traits.set_supported_modes({ - climate::CLIMATE_MODE_OFF, -#ifdef TION_ENABLE_CLIMATE_MODE_HEAT_COOL - climate::CLIMATE_MODE_HEAT_COOL, -#endif - climate::CLIMATE_MODE_HEAT, - climate::CLIMATE_MODE_FAN_ONLY, - }); - for (uint8_t i = 1, max = i + TION_MAX_FAN_SPEED; i < max; i++) { - traits.add_supported_custom_fan_mode(fan_speed_to_mode(i)); - } -#ifdef TION_ENABLE_PRESETS - this->add_presets(traits); -#endif - traits.set_supports_action(true); - return traits; -} - -void TionClimateComponentBase::control(const climate::ClimateCall &call) { - auto gate_position = TionGatePosition::NONE; - auto mode = this->mode; - auto fan_speed = fan_mode_to_speed(this->custom_fan_mode); - auto target_temperature = this->target_temperature; - - const bool has_changes = call.get_mode().has_value() || call.get_custom_fan_mode().has_value() || - call.get_target_temperature().has_value(); - -#ifdef TION_ENABLE_PRESETS - const auto old_preset = this->preset.value_or(climate::CLIMATE_PRESET_NONE); - if (this->has_presets()) { - if (call.get_preset().has_value()) { - const auto new_preset = *call.get_preset(); - auto *preset_data = this->presets_activate_preset_(new_preset, this, this); - if (preset_data) { - ESP_LOGD(TAG, "Set preset %s", LOG_STR_ARG(climate::climate_preset_to_string(new_preset))); - gate_position = preset_data->gate_position; - mode = preset_data->mode; - fan_speed = preset_data->fan_speed; - target_temperature = preset_data->target_temperature; - this->preset = new_preset; - // если буст, то больне ничеего не даем изменять и выходим - if (new_preset == climate::CLIMATE_PRESET_BOOST) { - if (has_changes) { - ESP_LOGW(TAG, "No more changes can be performed"); - if (call.get_mode().has_value()) { - ESP_LOGW(TAG, " Change of mode was skipped"); - } - if (call.get_custom_fan_mode().has_value()) { - ESP_LOGW(TAG, " Change of fan speed was skipped"); - } - if (call.get_target_temperature().has_value()) { - ESP_LOGW(TAG, " Change of target temperature was skipped"); - } - } - this->control_climate_state(mode, fan_speed, target_temperature, gate_position); - return; - } - } - } else if (old_preset == climate::CLIMATE_PRESET_BOOST && has_changes) { - auto *preset_data = this->presets_activate_preset_(this->presets_saved_preset_, this, this); - if (preset_data) { - ESP_LOGD(TAG, "Restored preset %s", - LOG_STR_ARG(climate::climate_preset_to_string(this->presets_saved_preset_))); - gate_position = preset_data->gate_position; - mode = preset_data->mode; - fan_speed = preset_data->fan_speed; - target_temperature = preset_data->target_temperature; - } - } - } - const auto new_preset = this->preset.value_or(climate::CLIMATE_PRESET_NONE); -#else - const auto old_preset = climate::CLIMATE_PRESET_NONE; - const auto new_preset = climate::CLIMATE_PRESET_NONE; -#endif - - if (call.get_mode().has_value()) { - mode = *call.get_mode(); - ESP_LOGD(TAG, "Set mode %s", LOG_STR_ARG(climate::climate_mode_to_string(mode))); - } - - if (call.get_custom_fan_mode().has_value()) { - fan_speed = fan_mode_to_speed(*call.get_custom_fan_mode()); - ESP_LOGD(TAG, "Set fan speed %u", fan_speed); - } - - if (call.get_target_temperature().has_value()) { - target_temperature = *call.get_target_temperature(); - ESP_LOGD(TAG, "Set target temperature %d °C", int(target_temperature)); - } - - if (!has_changes && old_preset == new_preset) { - ESP_LOGW(TAG, "No changes was performed"); - return; - } - -#ifdef TION_ENABLE_PRESETS - // в любом случае сбрасываем пресет если были изменения - if (has_changes) { - if (old_preset != climate::CLIMATE_PRESET_NONE) { - ESP_LOGD(TAG, "Reset preset"); - this->preset = climate::CLIMATE_PRESET_NONE; - } - } -#endif - - this->control_climate_state(mode, fan_speed, target_temperature, gate_position); -} - -void TionClimateComponentBase::set_fan_speed_(uint8_t fan_speed) { - if (fan_speed > 0 && fan_speed <= TION_MAX_FAN_SPEED) { - this->custom_fan_mode = fan_speed_to_mode(fan_speed); - } else { - auto ok_fan_speed = this->mode == climate::CLIMATE_MODE_OFF && fan_speed == 0; - if (!ok_fan_speed) { - ESP_LOGW(TAG, "Unsupported fan speed %u (max: %u)", fan_speed, TION_MAX_FAN_SPEED); - } - } -} - -} // namespace tion -} // namespace esphome -#endif // USE_CLIMATE diff --git a/components/tion/tion_climate_component.h b/components/tion/tion_climate_component.h deleted file mode 100644 index 8d8c170..0000000 --- a/components/tion/tion_climate_component.h +++ /dev/null @@ -1,189 +0,0 @@ -#pragma once -#include "esphome/core/defines.h" -#ifdef USE_CLIMATE - -#include "esphome/core/helpers.h" -#include "esphome/components/climate/climate.h" - -#include "../tion-api/tion-api.h" - -#include "tion_component.h" -#include "tion_climate_presets.h" -#include "tion_vport.h" -#include "tion_batch_call.h" - -namespace esphome { -namespace tion { - -class TionClimateComponentBase : public climate::Climate, public TionClimatePresets, public TionComponent { - public: - TionClimateComponentBase() = delete; - TionClimateComponentBase(const TionClimateComponentBase &) = delete; // non construction-copyable - TionClimateComponentBase &operator=(const TionClimateComponentBase &) = delete; // non copyable - - TionClimateComponentBase(TionVPortType vport_type); - - void call_setup() override; - void dump_settings(const char *tag, const char *component) const; - - climate::ClimateTraits traits() override; - void control(const climate::ClimateCall &call) override; - - virtual void control_climate_state(climate::ClimateMode mode, uint8_t fan_speed, float target_temperature, - TionGatePosition gate_position) = 0; - - uint8_t get_fan_speed() const { return fan_mode_to_speed(this->custom_fan_mode); } - - protected: - const TionVPortType vport_type_; - - void set_fan_speed_(uint8_t fan_speed); - -#ifdef TION_ENABLE_PRESETS - virtual bool enable_boost() { return this->presets_enable_boost_timer_(this, this); } - virtual void cancel_boost() { this->presets_cancel_boost_timer_(this); } -#endif -}; - -/** - * @param tion_api_type TionApi implementation. - */ -template class TionClimateComponent : public TionClimateComponentBase { - static_assert(std::is_base_of_v, - "tion_api_type is not derived from TionApiBase"); - - public: - explicit TionClimateComponent(tion_api_type *api, TionVPortType vport_type) - : TionClimateComponentBase(vport_type), api_(api) { - using this_t = std::remove_pointer_t; - this->api_->on_dev_info_fn.template set(*this); - this->api_->on_state_fn.template set(*this); - } - - void update() override { - this->api_->request_state(); - this->schedule_state_check_(); - } - - virtual void update_state(const dentra::tion::TionState &state) = 0; - - void on_state(const dentra::tion::TionState &state, const uint32_t request_id) { - this->update_state(state); - - this->cancel_state_check_(); -#ifdef USE_TION_OUTDOOR_TEMPERATURE - if (this->outdoor_temperature_) { - this->outdoor_temperature_->publish_state(state.outdoor_temperature); - } -#endif -#ifdef USE_TION_BUZZER - if (this->buzzer_) { - this->buzzer_->publish_state(state.sound_state); - } -#endif -#ifdef USE_TION_FILTER_TIME_LEFT - if (this->filter_time_left_) { - this->filter_time_left_->publish_state(state.filter_time_left_d()); - } -#endif -#ifdef USE_TION_FILTER_WARNOUT - if (this->filter_warnout_) { - this->filter_warnout_->publish_state(state.filter_state); - } -#endif -#ifdef USE_TION_WORK_TIME - if (this->work_time_) { - this->work_time_->publish_state(state.work_time); - } -#endif -#ifdef USE_TION_ERRORS - if (this->errors_) { - this->errors_->publish_state(this->api_->traits().errors_decoder(state.errors)); - } -#endif - // do not update state in batch mode - if (!this->batch_active_) { - this->state_ = state; - } - } - - void on_dev_info(const dentra::tion::tion_dev_info_t &info) { this->update_dev_info_(info); } - - protected: - tion_api_type *api_; - dentra::tion::TionState state_{}; - uint32_t request_id_{}; - bool batch_active_{}; - - BatchStateCall *state_call_{}; - - BatchStateCall *make_api_call() { - if (this->state_call_ == nullptr) { - state_call_ = new BatchStateCall(this->api_, this, this->batch_timeout_, [this]() { - this->state_call_->reset(); - this->schedule_state_check_(); - }); - } - return this->state_call_; - } - - void schedule_state_check_() { -#ifdef USE_TION_STATE_WARNOUT - if (this->state_warnout_ && this->state_timeout_ > 0) { - this->set_timeout("state_timeout", this->state_timeout_, [this]() { this->state_warnout_->publish_state(true); }); - } -#endif - } - - void cancel_state_check_() { -#ifdef USE_TION_STATE_WARNOUT - if (this->state_warnout_ && this->state_timeout_ > 0) { - this->state_warnout_->publish_state(false); - this->cancel_timeout("state_timeout"); - } -#endif - } -}; - -template class TionLtClimateComponent : public TionClimateComponent { - public: - explicit TionLtClimateComponent(tion_api_type *api, TionVPortType vport_type) - : TionClimateComponent(api, vport_type) { - using this_t = std::remove_pointer_t; - this->api_->on_state_fn.template set(*this); - } - - void on_state(const dentra::tion::TionState &state, const uint32_t request_id) { - TionClimateComponent::on_state(state, request_id); -#ifdef USE_TION_LED - if (this->led_) { - this->led_->publish_state(state.led_state); - } -#endif -#ifdef USE_TION_HEATER_POWER - if (this->heater_power_) { - this->heater_power_->publish_state(state.get_heater_power(this->api_->traits())); - } -#endif -#ifdef USE_TION_AIRFLOW_COUNTER - if (this->airflow_counter_) { - this->airflow_counter_->publish_state(state.counters.airflow()); - } -#endif -#ifdef USE_TION_PRODUCTIVITY - if (this->productivity_) { - this->productivity_->publish_state(state.productivity); - } -#endif - } -}; - -class TionBoostTimeNumber : public number::Number { - public: - protected: - void control(float value) override { this->publish_state(value); } -}; - -} // namespace tion -} // namespace esphome -#endif // USE_CLIMATE diff --git a/components/tion/tion_climate_presets.cpp b/components/tion/tion_climate_presets.cpp deleted file mode 100644 index a05081c..0000000 --- a/components/tion/tion_climate_presets.cpp +++ /dev/null @@ -1,289 +0,0 @@ -#include "esphome/core/defines.h" -#ifdef USE_CLIMATE -#ifdef TION_ENABLE_PRESETS -#include -#include "esphome/core/defines.h" -#include "esphome/core/log.h" -#include "esphome/core/application.h" - -#ifdef USE_API -#include "esphome/components/api/custom_api_device.h" -#endif - -#include "tion_climate_presets.h" - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_presets"; - -// boost time update interval -#define BOOST_TIME_UPDATE_INTERVAL_SEC 20 - -// application scheduler name -static const char *const ASH_BOOST = "tion-boost"; - -void TionClimatePresets::setup_presets() { - if (this->presets_boost_time_) { - this->presets_boost_rtc_ = global_preferences->make_preference(fnv1_hash("boost_time")); - uint8_t boost_time; - if (!this->presets_boost_rtc_.load(&boost_time)) { - boost_time = DEFAULT_BOOST_TIME_SEC / 60; - } - auto call = this->presets_boost_time_->make_call(); - call.set_value(boost_time); - call.perform(); - this->presets_boost_time_->add_on_state_callback([this](float state) { - const uint8_t boost_time = state; - this->presets_boost_rtc_.save(&boost_time); - }); - } - -#ifdef USE_API - this->presets_data_rtc_ = - global_preferences->make_preference(fnv1_hash("presets")); - if (this->presets_data_rtc_.load(&this->presets_data_)) { - this->presets_data_[0].fan_speed = 0; // reset initialization - ESP_LOGD(TAG, "Presets loaded"); - } - api::CustomAPIDevice api; - api.register_service(&TionClimatePresets::presets_update_service_, "update_preset", - {"preset", "mode", "fan_speed", "target_temperature", "gate_position"}); -#endif -} - -void TionClimatePresets::add_presets(climate::ClimateTraits &traits) { - traits.add_supported_preset(climate::CLIMATE_PRESET_NONE); - this->presets_for_each_([&traits](auto index) { traits.add_supported_preset(index); }); -} - -#ifdef USE_API -// esphome services allows only pass copy of strings -void TionClimatePresets::presets_update_service_(std::string preset_str, std::string mode_str, int32_t fan_speed, - int32_t target_temperature, std::string gate_position_str) { - climate::ClimatePreset preset = climate::ClimatePreset::CLIMATE_PRESET_NONE; - if (preset_str.length() > 0) { - auto preset_ch = std::tolower(preset_str[0]); - if (preset_ch == 'h') { // Home - preset = climate::ClimatePreset::CLIMATE_PRESET_HOME; - } else if (preset_ch == 'a') { // Away/Activity - if (preset_str.length() > 1) { - preset_ch = std::tolower(preset_str[1]); - if (preset_ch == 'w') { // aWay - preset = climate::ClimatePreset::CLIMATE_PRESET_AWAY; - } else if (preset_ch == 'c') { // aCtivity - preset = climate::ClimatePreset::CLIMATE_PRESET_ACTIVITY; - } - } - } else if (preset_ch == 'b') { // Boost - preset = climate::ClimatePreset::CLIMATE_PRESET_BOOST; - } else if (preset_ch == 'c') { // Comform - preset = climate::ClimatePreset::CLIMATE_PRESET_COMFORT; - } else if (preset_ch == 'e') { // Eco - preset = climate::ClimatePreset::CLIMATE_PRESET_ECO; - } else if (preset_ch == 's') { // Sleep - preset = climate::ClimatePreset::CLIMATE_PRESET_SLEEP; - } - } - - auto mode = climate::ClimateMode::CLIMATE_MODE_AUTO; - if (mode_str.length() > 0) { - auto mode_ch = std::tolower(mode_str[0]); - if (mode_ch == 'h') { // Heat - mode = climate::ClimateMode::CLIMATE_MODE_HEAT; - } else if (mode_ch == 'f') { // Fan_only - mode = climate::ClimateMode::CLIMATE_MODE_FAN_ONLY; - } else if (mode_ch == 'o') { // Off - mode = climate::ClimateMode::CLIMATE_MODE_OFF; - } - } - - TionGatePosition gate_position = TionGatePosition::NONE; - if (gate_position_str.length() > 0) { - auto gate_position_ch = std::tolower(gate_position_str[0]); - if (gate_position_ch == 'o') { // Outdoor - gate_position = TionGatePosition::OUTDOOR; - } else if (gate_position_ch == 'i') { // Indoor - gate_position = TionGatePosition::INDOOR; - } else if (gate_position_ch == 'm') { // Mixed - gate_position = TionGatePosition::MIXED; - } - } - - if (this->update_preset(preset, mode, fan_speed, target_temperature, gate_position)) { - if (this->presets_data_rtc_.save(&this->presets_data_)) { - ESP_LOGCONFIG(TAG, "Preset was updated:"); - this->presets_dump_preset_(TAG, preset); - } - } else { - ESP_LOGW(TAG, "Preset %s was't updated", preset_str.c_str()); - } -} -#endif // USE_API - -void TionClimatePresets::dump_presets(const char *TAG) const { - LOG_NUMBER(" ", "Boost Time", this->presets_boost_time_); - LOG_SENSOR(" ", "Boost Time Left", this->presets_boost_time_left_); - if (this->has_presets()) { - ESP_LOGCONFIG(TAG, " Presets (fan_speed, target_temperature, mode, gate_position):"); - this->presets_for_each_([TAG, this](auto index) { this->presets_dump_preset_(TAG, index); }); - } -} - -void TionClimatePresets::presets_dump_preset_(const char *tag, climate::ClimatePreset index) const { - auto gate_position_to_string = [](TionGatePosition gp) -> const char * { - switch (gp) { - case TionGatePosition::NONE: - return "none"; - case TionGatePosition::OUTDOOR: - return "outdoor"; - case TionGatePosition::INDOOR: - return "indoor"; - case TionGatePosition::MIXED: - return "mixed"; - default: - return "unknown"; - } - }; - const auto &preset_data = this->presets_data_[index]; - const auto *preset_str = LOG_STR_ARG(climate::climate_preset_to_string(index)); - const auto *mode_str = LOG_STR_ARG(climate::climate_mode_to_string(preset_data.mode)); - const auto *gate_pos_str = gate_position_to_string(preset_data.gate_position); - ESP_LOGCONFIG(tag, " %-8s: %u, %2d, %-8s, %s", preset_str, preset_data.fan_speed, preset_data.target_temperature, - mode_str, gate_pos_str); -} - -TionClimatePresetData *TionClimatePresets::presets_activate_preset_(climate::ClimatePreset new_preset, - Component *component, climate::Climate *climate) { - if (new_preset >= TION_MAX_PRESETS) { - ESP_LOGW(TAG, "Unknown preset number %u", new_preset); - return nullptr; - } - - const auto old_preset = climate->preset.value_or(climate::CLIMATE_PRESET_NONE); - - const auto *new_preset_name = LOG_STR_ARG(climate::climate_preset_to_string(new_preset)); - - if (new_preset == old_preset) { - ESP_LOGD(TAG, "Preset %s was not changed", new_preset_name); - return nullptr; - } - - // Проверим включен ли пресет для активации. Пресет NONE всегда включен, пропустим его. - if (new_preset != climate::CLIMATE_PRESET_NONE && !this->presets_data_[new_preset].is_enabled()) { - ESP_LOGW(TAG, "Preset %s is disabled", new_preset_name); - return nullptr; - } - - if (old_preset == climate::CLIMATE_PRESET_BOOST) { - if (this->presets_boost_time_left_ == nullptr || - (this->presets_boost_time_left_ && !std::isnan(this->presets_boost_time_left_->state))) { - ESP_LOGD(TAG, "Cancel boost preset"); - } - this->presets_cancel_boost_timer_(component); - } - - ESP_LOGD(TAG, "Activate preset %s", new_preset_name); - if (new_preset == climate::CLIMATE_PRESET_BOOST) { - if (!this->presets_enable_boost_timer_(component, climate)) { - return nullptr; - } - this->presets_saved_preset_ = old_preset; - // инициализируем дефолный пресет NONE чтобы можно было в него восстановиться в любом случае - if (!this->presets_data_[climate::CLIMATE_PRESET_NONE].is_initialized() && - old_preset != climate::CLIMATE_PRESET_NONE) { - this->presets_save_default_(climate); - } - } - - // если был пресет NONE, то сохраним его текущее состояние - if (old_preset == climate::CLIMATE_PRESET_NONE) { - this->presets_save_default_(climate); - } - - // дополнительно проверим, что пресет был предварительно сохранен (см. блок выше) - // в противном случае можем получить зимой, например, отстутсвие подогрева - // т.е. неинициализированный пресет не активируем - if (!this->presets_data_[new_preset].is_initialized()) { - ESP_LOGW(TAG, "Preset %s is not initialized", new_preset_name); - return nullptr; - } - - ESP_LOGV(TAG, "Preset data:"); - ESP_LOGV(TAG, " mode: %s", LOG_STR_ARG(climate::climate_mode_to_string(this->presets_data_[new_preset].mode))); - ESP_LOGV(TAG, " fan_speed: %u", this->presets_data_[new_preset].fan_speed); - ESP_LOGV(TAG, " target_temperature: %d", this->presets_data_[new_preset].target_temperature); - ESP_LOGV(TAG, " gate_position: %u", static_cast(this->get_gate_position())); - - return &this->presets_data_[new_preset]; -} - -void TionClimatePresets::presets_save_default_(climate::Climate *climate) { - this->presets_data_[climate::CLIMATE_PRESET_NONE].mode = climate->mode; - this->presets_data_[climate::CLIMATE_PRESET_NONE].target_temperature = climate->target_temperature; - this->presets_data_[climate::CLIMATE_PRESET_NONE].gate_position = this->get_gate_position(); - const auto fan_speed = fan_mode_to_speed(climate->custom_fan_mode); - // не сохраняем fan_speed=0, т.к. это маркер инициализации, - // а выключенное состояние в люом случае задается режимом. - if (fan_speed != 0) { - this->presets_data_[climate::CLIMATE_PRESET_NONE].fan_speed = fan_speed; - } -} - -bool TionClimatePresets::presets_enable_boost_timer_(Component *component, climate::Climate *climate) { - auto boost_time = this->get_boost_time_(); - if (boost_time == 0) { - ESP_LOGW(TAG, "Boost time is not configured"); - return false; - } - - // if boost_time_left not configured, just schedule stop boost after boost_time - if (this->presets_boost_time_left_ == nullptr) { - ESP_LOGD(TAG, "Schedule boost timeout for %" PRIu32 " s", boost_time); - App.scheduler.set_timeout(component, ASH_BOOST, boost_time * 1000, - [this, climate]() { this->presets_cancel_boost_(climate); }); - return true; - } - - // if boost_time_left is configured, schedule update it - ESP_LOGD(TAG, "Schedule boost interval up to %" PRIu32 " s", boost_time); - this->presets_boost_time_left_->publish_state(static_cast(boost_time)); - - App.scheduler.set_interval(component, ASH_BOOST, BOOST_TIME_UPDATE_INTERVAL_SEC * 1000, [this, climate]() { - const int32_t time_left = - static_cast(this->presets_boost_time_left_->state) - BOOST_TIME_UPDATE_INTERVAL_SEC; - ESP_LOGV(TAG, "Boost time left %" PRId32 " s", time_left); - if (time_left > 0) { - this->presets_boost_time_left_->publish_state(static_cast(time_left)); - } else { - this->presets_boost_time_left_->publish_state(NAN); - this->presets_cancel_boost_(climate); - } - }); - - return true; -} - -void TionClimatePresets::presets_cancel_boost_timer_(Component *component) { - if (this->presets_boost_time_left_ == nullptr) { - ESP_LOGV(TAG, "Cancel boost timeout"); - App.scheduler.cancel_timeout(component, ASH_BOOST); - } else { - ESP_LOGV(TAG, "Cancel boost interval"); - App.scheduler.cancel_interval(component, ASH_BOOST); - this->presets_boost_time_left_->publish_state(NAN); - } -} - -void TionClimatePresets::presets_cancel_boost_(climate::Climate *climate) { - const auto new_preset = this->presets_saved_preset_; - ESP_LOGD(TAG, "Boost finished, switch back to %s", LOG_STR_ARG(climate::climate_preset_to_string(new_preset))); - auto call = climate->make_call(); - call.set_preset(new_preset); - call.perform(); -} - -} // namespace tion -} // namespace esphome -#endif // TION_ENABLE_PRESETS -#endif // USE_CLIMATE diff --git a/components/tion/tion_climate_presets.h b/components/tion/tion_climate_presets.h deleted file mode 100644 index 9c1ee15..0000000 --- a/components/tion/tion_climate_presets.h +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once -#include "esphome/core/defines.h" -#ifdef USE_CLIMATE - -#include "esphome/components/sensor/sensor.h" -#ifdef USE_NUMBER -#include "esphome/components/number/number.h" -#endif - -#include "tion_defines.h" -#include "tion_helpers.h" - -namespace esphome { -namespace tion { - -#ifndef TION_ENABLE_PRESETS -class TionClimatePresets { - public: - void dump_presets(const char *tag) const {} -}; -#endif - -} // namespace tion -} // namespace esphome - -#ifdef TION_ENABLE_PRESETS - -// default boost time - 10 minutes -#define DEFAULT_BOOST_TIME_SEC (10 * 60) - -#define TION_MAX_PRESETS (climate::CLIMATE_PRESET_ACTIVITY + 1) - -namespace esphome { -namespace tion { - -using TionClimatePresetData = TionPresetData; - -class TionClimatePresets { - public: - void set_boost_time(number::Number *boost_time) { this->presets_boost_time_ = boost_time; } - void set_boost_time_left(sensor::Sensor *boost_time_left) { this->presets_boost_time_left_ = boost_time_left; } - - void setup_presets(); - void add_presets(climate::ClimateTraits &traits); - void dump_presets(const char *tag) const; - - virtual TionGatePosition get_gate_position() const = 0; - - /** - * Update default preset. - * @param preset preset to update. - * @param mode mode to update. set to climate::CLIMATE_MODE_AUTO for skip update. - * @param fan_speed fan speed to update. set to 0 for skip update. - * @param target_temperature target temperature to update. set to 0 for skip update. - */ - bool update_preset(climate::ClimatePreset preset, climate::ClimateMode mode, uint8_t fan_speed = 0, - int8_t target_temperature = 0, TionGatePosition gate_position = TionGatePosition::NONE) { - if (preset <= climate::CLIMATE_PRESET_NONE || preset > climate::CLIMATE_PRESET_ACTIVITY) { - return false; - } - - if (mode == climate::CLIMATE_MODE_OFF || mode == climate::CLIMATE_MODE_HEAT || - mode == climate::CLIMATE_MODE_FAN_ONLY) { - this->presets_data_[preset].mode = mode; - } - - if (fan_speed > 0 && fan_speed <= TION_MAX_FAN_SPEED) { - this->presets_data_[preset].fan_speed = fan_speed; - } - - if (target_temperature >= TION_MIN_TEMPERATURE && target_temperature <= TION_MAX_TEMPERATURE) { - this->presets_data_[preset].target_temperature = target_temperature; - } - - this->presets_data_[preset].gate_position = gate_position; - - return true; - } - - protected: - number::Number *presets_boost_time_{}; - sensor::Sensor *presets_boost_time_left_{}; - ESPPreferenceObject presets_boost_rtc_; - - bool presets_enable_boost_timer_(Component *component, climate::Climate *climate); - void presets_cancel_boost_timer_(Component *component); - // partially activate preset, caller must perform actions with returned result if it is not null. - TionClimatePresetData *presets_activate_preset_(climate::ClimatePreset new_preset, Component *component, - climate::Climate *climate); - // Cancel boost and activate saved preset with making climate call. - void presets_cancel_boost_(climate::Climate *climate); - -#ifdef USE_API - void presets_update_service_(std::string preset, std::string mode, int32_t fan_speed, int32_t target_temperature, - std::string gate_position); - ESPPreferenceObject presets_data_rtc_; -#endif - - climate::ClimatePreset presets_saved_preset_{climate::CLIMATE_PRESET_NONE}; - - TionClimatePresetData presets_data_[TION_MAX_PRESETS] = { - {}, // NONE, saved data - {.fan_speed = 2, - .target_temperature = 20, - .mode = climate::CLIMATE_MODE_HEAT, - .gate_position = TionGatePosition::NONE}, // HOME - {.fan_speed = 1, - .target_temperature = 10, - .mode = climate::CLIMATE_MODE_FAN_ONLY, - .gate_position = TionGatePosition::NONE}, // AWAY - {.fan_speed = TION_MAX_FAN_SPEED, - .target_temperature = 10, - .mode = climate::CLIMATE_MODE_FAN_ONLY, - .gate_position = TionGatePosition::NONE}, // BOOST - {.fan_speed = 2, - .target_temperature = 23, - .mode = climate::CLIMATE_MODE_HEAT, - .gate_position = TionGatePosition::NONE}, // COMFORT - {.fan_speed = 1, - .target_temperature = 16, - .mode = climate::CLIMATE_MODE_HEAT, - .gate_position = TionGatePosition::NONE}, // ECO - {.fan_speed = 1, - .target_temperature = 18, - .mode = climate::CLIMATE_MODE_HEAT, - .gate_position = TionGatePosition::NONE}, // SLEEP - {.fan_speed = 3, - .target_temperature = 18, - .mode = climate::CLIMATE_MODE_HEAT, - .gate_position = TionGatePosition::NONE}, // ACTIVITY - }; - - void presets_for_each_(const std::function &fn) const { - for (size_t i = climate::CLIMATE_PRESET_NONE + 1; i < TION_MAX_PRESETS; i++) { - if (this->presets_data_[i].is_enabled()) { - fn(static_cast(i)); - } - } - } - - bool has_presets() const { - auto has_presets = false; - this->presets_for_each_([&has_presets](auto index) { has_presets = true; }); - return has_presets; - } - - void presets_dump_preset_(const char *tag, climate::ClimatePreset index) const; - - void presets_save_default_(climate::Climate *climate); - - /// returns boost time in seconds. - uint32_t get_boost_time_() const { - if (this->presets_boost_time_ == nullptr) { - return DEFAULT_BOOST_TIME_SEC; - } - if (this->presets_boost_time_->traits.get_unit_of_measurement()[0] == 's') { - return this->presets_boost_time_->state; - } - return this->presets_boost_time_->state * 60; - } -}; - -} // namespace tion -} // namespace esphome -#endif // TION_ENABLE_PRESETS -#endif // USE_CLIMATE diff --git a/components/tion/tion_component.cpp b/components/tion/tion_component.cpp index b379639..590c866 100644 --- a/components/tion/tion_component.cpp +++ b/components/tion/tion_component.cpp @@ -1,35 +1,154 @@ #include +#include #include "esphome/core/log.h" - #include "tion_component.h" namespace esphome { namespace tion { -static const char *const TAG = "tion_component"; +static const char *const TAG = "tion_api_component"; +static const char *const STATE_TIMEOUT = "state_timeout"; + +void TionApiComponent::call_setup() { + PollingComponent::call_setup(); + if (this->state_timeout_ >= this->get_update_interval()) { + ESP_LOGW(TAG, "Invalid state timout: %.1f s", this->state_timeout_ / 1000.0f); + this->state_timeout_ = 0; + } + if (this->state_timeout_ == 0) { + this->has_state_ = true; + } +} + +void TionApiComponent::dump_config() { + ESP_LOGCONFIG(TAG, "%s:", this->get_component_source()); + ESP_LOGCONFIG(TAG, " Update interval: %.1f s", this->get_update_interval() / 1000.0f); + ESP_LOGCONFIG(TAG, " Force update: %s", ONOFF(this->force_update_)); + ESP_LOGCONFIG(TAG, " State timeout: %.1f s", this->state_timeout_ / 1000.0f); + ESP_LOGCONFIG(TAG, " Batch timeout: %.1f s", this->batch_timeout_ / 1000.0f); + if (this->traits().supports_antifrize) { + ESP_LOGCONFIG(TAG, " Antifrize: enabled"); + } +} + +void TionApiComponent::update() { + this->api_->request_state(); + this->state_check_schedule_(); +} + +void TionApiComponent::on_state_(const TionState &state, const uint32_t request_id) { + this->state_check_cancel_(); + this->defer([this]() { + const auto &state = this->state(); + this->state_callback_.call(&state); + }); + // this->state_callback_.call(&state); +} + +void TionApiComponent::state_check_schedule_() { + if (this->state_timeout_ > 0) { + this->set_timeout(STATE_TIMEOUT, this->state_timeout_, [this]() { + this->has_state_ = false; + this->state_check_report_(this->state_timeout_); + }); + } else if (!this->has_state_) { + this->state_check_report_(this->get_update_interval()); + } else { + this->has_state_ = false; + } +} + +void TionApiComponent::state_check_report_(uint32_t timeout) { + ESP_LOGW(TAG, "State was not received in %.1fs", timeout / 1000.0f); + this->state_callback_.call(nullptr); +} -void TionComponent::update_dev_info_(const dentra::tion::tion_dev_info_t &info) { -#ifdef USE_TION_VERSION - if (this->version_ != nullptr) { - this->version_->publish_state(str_snprintf("%04X", 4, info.firmware_version)); +void TionApiComponent::state_check_cancel_() { + this->has_state_ = true; + if (this->state_timeout_ > 0) { + this->cancel_timeout(STATE_TIMEOUT); } -#endif -#if TION_LOG_LEVEL >= TION_LOG_LEVEL_VERBOSE - const char *work_mode_str; - if (info.work_mode == dentra::tion::tion_dev_info_t::NORMAL) { - work_mode_str = "NORMAL"; - } else if (info.work_mode == dentra::tion::tion_dev_info_t::UPDATE) { - work_mode_str = "UPDATE"; +} + +dentra::tion::TionStateCall *TionApiComponent::make_call() { + if (this->batch_call_) { + ESP_LOGD(TAG, "Continue batch update for %u ms", this->batch_timeout_); } else { - work_mode_str = "UNKNOWN"; + ESP_LOGD(TAG, "Starting batch update for %u ms", this->batch_timeout_); + this->batch_call_ = new BatchStateCall(this->api_, this, this->batch_timeout_, [this]() { this->batch_write_(); }); + } + return this->batch_call_; +} + +void TionApiComponent::batch_write_() { + BatchStateCall *batch_call = this->batch_call_; + this->batch_call_ = nullptr; + delete batch_call; + this->state_check_schedule_(); +} + +#ifdef TION_ENABLE_SCHEDULER + +void Tion4sApiComponent::on_time(time_t time, uint32_t request_id) { + auto *c_tm = std::gmtime(&time); + char buf[20] = {}; + std::strftime(buf, sizeof(buf), "%F %T", c_tm); + ESP_LOGI(TAG, "Device UTC time: %s", buf); + c_tm = std::localtime(&time); + std::strftime(buf, sizeof(buf), "%F %T", c_tm); + ESP_LOGI(TAG, "Device local time: %s", buf); +} + +void Tion4sApiComponent::on_timer(uint8_t timer_id, const dentra::tion_4s::tion4s_timer_t &timer, uint32_t request_id) { + std::string schedule; + if (timer.schedule.monday && timer.schedule.tuesday && timer.schedule.wednesday && timer.schedule.thursday && + timer.schedule.friday && timer.schedule.saturday && timer.schedule.sunday) { + schedule = "MON-SUN"; + } else { + auto add_timer_week_day = [](std::string &s, bool day, const char *day_name) { + if (day) { + if (!s.empty()) { + s.append(", "); + } + s.append(day_name); + } + }; + add_timer_week_day(schedule, timer.schedule.monday, "MON"); + add_timer_week_day(schedule, timer.schedule.tuesday, "TUE"); + add_timer_week_day(schedule, timer.schedule.wednesday, "WED"); + add_timer_week_day(schedule, timer.schedule.thursday, "THU"); + add_timer_week_day(schedule, timer.schedule.friday, "FRI"); + add_timer_week_day(schedule, timer.schedule.saturday, "SAT"); + add_timer_week_day(schedule, timer.schedule.sunday, "SUN"); + } + + ESP_LOGI(TAG, "Timer[%u] %s at %02u:%02u is %s", timer_id, schedule.c_str(), timer.schedule.hours, + timer.schedule.minutes, ONOFF(timer.timer_state)); +} + +void Tion4sApiComponent::on_timers_state(const dentra::tion_4s::tion4s_timers_state_t &timers_state, + uint32_t request_id) { + for (int i = 0; i < tion4s_timers_state_t::TIMERS_COUNT; i++) { + ESP_LOGI(TAG, "Timer[%d] state %s", i, ONOFF(timers_state.timers[i].active)); + } +} + +void Tion4sApiComponent::dump_timers() { + this->api_->request_time(++this->request_id_); + for (uint8_t timer_id = 0; timer_id < dentra::tion_4s::tion4s_timers_state_t::TIMERS_COUNT; timer_id++) { + this->api_->request_timer(timer_id, ++this->request_id_); + } + this->api_->request_timers_state(++this->request_id_); +} + +void Tion4sApiComponent::reset_timers() { + const dentra::tion_4s::tion4s_timer_t timer{}; + for (uint8_t timer_id = 0; timer_id < dentra::tion_4s::tion4s_timers_state_t::TIMERS_COUNT; timer_id++) { + this->api_->write_timer(timer_id, timer, ++this->request_id_); } - ESP_LOGV(TAG, "Work Mode : %d (%s)", info.work_mode, work_mode_str); - ESP_LOGV(TAG, "Device type : %08" PRIX32, info.device_type); - ESP_LOGV(TAG, "Hardware version: %04X", info.hardware_version); - ESP_LOGV(TAG, "Firmware version: %04X", info.firmware_version); -#endif } +#endif // TION_ENABLE_SCHEDULER } // namespace tion } // namespace esphome diff --git a/components/tion/tion_component.h b/components/tion/tion_component.h index 526c510..036be52 100644 --- a/components/tion/tion_component.h +++ b/components/tion/tion_component.h @@ -1,125 +1,148 @@ #pragma once + +#include +#include + #include "esphome/core/defines.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" #include "esphome/core/component.h" -#include "esphome/core/preferences.h" -#include "esphome/components/sensor/sensor.h" -#include "esphome/components/switch/switch.h" -#include "esphome/components/text_sensor/text_sensor.h" +#ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" -#include "esphome/components/number/number.h" -#include "esphome/components/button/button.h" +#endif #include "../tion-api/tion-api.h" +#include "../tion-api/tion-api-o2.h" +#include "../tion-api/tion-api-3s.h" +#include "../tion-api/tion-api-4s.h" +#include "../tion-api/tion-api-lt.h" +#include "tion_vport.h" +#include "tion_batch_call.h" namespace esphome { namespace tion { -class TionComponent : public PollingComponent { + +class TionApiComponent : public PollingComponent { + protected: + using TionApiBase = dentra::tion::TionApiBase; + using TionState = dentra::tion::TionState; + using TionStateCall = dentra::tion::TionStateCall; + using TionGatePosition = dentra::tion::TionGatePosition; + public: -#ifdef USE_TION_VERSION - void set_version(text_sensor::TextSensor *version) { this->version_ = version; } -#endif -#ifdef USE_TION_BUZZER - void set_buzzer(switch_::Switch *buzzer) { this->buzzer_ = buzzer; } -#endif -#ifdef USE_TION_LED - void set_led(switch_::Switch *led) { this->led_ = led; } -#endif -#ifdef USE_TION_OUTDOOR_TEMPERATURE - void set_outdoor_temperature(sensor::Sensor *outdoor_temperature) { - this->outdoor_temperature_ = outdoor_temperature; + explicit TionApiComponent(TionApiBase *api) : api_(api) { + api->on_state_fn.set(*this); } -#endif -#ifdef USE_TION_HEATER_POWER - void set_heater_power(sensor::Sensor *heater_power) { this->heater_power_ = heater_power; } -#endif -#ifdef USE_TION_AIRFLOW_COUNTER - void set_airflow_counter(sensor::Sensor *airflow_counter) { this->airflow_counter_ = airflow_counter; } -#endif -#ifdef USE_TION_PRODUCTIVITY - void set_productivity(sensor::Sensor *productivity) { this->productivity_ = productivity; } -#endif -#ifdef USE_TION_FILTER_TIME_LEFT - void set_filter_time_left(sensor::Sensor *filter_time_left) { this->filter_time_left_ = filter_time_left; } -#endif -#ifdef USE_TION_FILTER_WARNOUT - void set_filter_warnout(binary_sensor::BinarySensor *filter_warnout) { this->filter_warnout_ = filter_warnout; } -#endif -#ifdef USE_TION_RESET_FILTER - void set_reset_filter(button::Button *reset_filter) { this->reset_filter_ = reset_filter; }; - void set_reset_filter_confirm(switch_::Switch *reset_filter_confirm) { - this->reset_filter_confirm_ = reset_filter_confirm; - }; -#endif -#ifdef USE_TION_STATE_WARNOUT - void set_state_warnout(binary_sensor::BinarySensor *state_warnout) { this->state_warnout_ = state_warnout; }; -#endif - void set_state_timeout(uint32_t state_timeout) { -#ifdef USE_TION_STATE_WARNOUT - this->state_timeout_ = state_timeout; -#endif - }; - void set_batch_timeout(uint32_t batch_timeout) { this->batch_timeout_ = batch_timeout; }; -#ifdef USE_TION_ERRORS - void set_errors(text_sensor::TextSensor *errors) { this->errors_ = errors; } -#endif + void dump_config() override; + void call_setup() override; + float get_setup_priority() const override { return setup_priority::AFTER_CONNECTION; } -#ifdef USE_TION_WORK_TIME - void set_work_time(sensor::Sensor *work_time) { this->work_time_ = work_time; } -#endif + void update() override; -#ifdef USE_TION_RESET_FILTER - bool is_reset_filter_confirmed() const { - return this->reset_filter_confirm_ == nullptr || this->reset_filter_confirm_->state; + /** + * Add a callback for the breezer state, each time the state of the device is updated, this callback will be called. + * When state is not returned in configured period - a callback called with nullptr. + * + * @param callback The callback to call. + */ + void add_on_state_callback(std::function &&callback) { + this->state_callback_.add(std::move(callback)); } -#endif + + /** + * Add a callback for the breezer configuration, each time the configuration parameters of a device + * is updated (using perform() of a StateCall), this callback will be called, before any on_state callback. + * + * @param callback The callback to call. + */ + // void add_on_control_callback(std::function &&callback); + + void set_state_timeout(uint32_t state_timeout) { this->state_timeout_ = state_timeout; }; + void set_batch_timeout(uint32_t batch_timeout) { this->batch_timeout_ = batch_timeout; }; + void set_force_update(bool force_update) { this->force_update_ = force_update; }; + bool get_force_update() const { return this->force_update_; } + void add_preset(const std::string &name, const TionApiBase::PresetData &preset) { + this->api_->add_preset(name, preset); + } + + TionStateCall *make_call(); + + TionApiBase *api() { return this->api_; } + + bool has_state() const { return this->has_state_; } + + const dentra::tion::TionTraits &traits() const { return this->api_->traits(); } + const dentra::tion::TionState &state() const { return this->api_->get_state(); } + + void boost_enable(); + void boost_cancel(); protected: -#ifdef USE_TION_VERSION - text_sensor::TextSensor *version_{}; -#endif -#ifdef USE_TION_BUZZER - switch_::Switch *buzzer_{}; -#endif -#ifdef USE_TION_LED - switch_::Switch *led_{}; -#endif -#ifdef USE_TION_OUTDOOR_TEMPERATURE - sensor::Sensor *outdoor_temperature_{}; -#endif -#ifdef USE_TION_HEATER_POWER - sensor::Sensor *heater_power_{}; -#endif -#ifdef USE_TION_AIRFLOW_COUNTER - sensor::Sensor *airflow_counter_{}; -#endif -#ifdef USE_TION_PRODUCTIVITY - sensor::Sensor *productivity_{}; -#endif -#ifdef USE_TION_FILTER_TIME_LEFT - sensor::Sensor *filter_time_left_{}; -#endif -#ifdef USE_TION_FILTER_WARNOUT - binary_sensor::BinarySensor *filter_warnout_{}; -#endif -#ifdef USE_TION_RESET_FILTER - button::Button *reset_filter_{}; - switch_::Switch *reset_filter_confirm_{}; -#endif -#ifdef USE_TION_STATE_WARNOUT - binary_sensor::BinarySensor *state_warnout_{}; + TionApiBase *api_; + bool has_state_{}; + bool force_update_{}; + BatchStateCall *batch_call_{}; + uint32_t state_timeout_{}; -#endif uint32_t batch_timeout_{}; -#ifdef USE_TION_ERRORS - text_sensor::TextSensor *errors_{}; + + CallbackManager state_callback_{}; + // CallbackManager control_callback_{}; + + void on_state_(const TionState &state, const uint32_t request_id); + void state_check_schedule_(); + void state_check_cancel_(); + void state_check_report_(uint32_t timeout); + void batch_write_(); +}; + +// T - TionApi implementation +template class TionApiComponentBase : public TionApiComponent { + public: + using Api = A; + + explicit TionApiComponentBase(Api *api, TionVPortType /*vport_type*/) : TionApiComponent(api) {} + + protected: + Api *typed_api() { return reinterpret_cast(this->api_); } +}; + +using TionO2ApiComponent = TionApiComponentBase; +using Tion3sApiComponent = TionApiComponentBase; + +class Tion4sApiComponent : public TionApiComponentBase { + public: + explicit Tion4sApiComponent(TionApiComponentBase::Api *api, TionVPortType vport_type) + : TionApiComponentBase(api, vport_type) { + if (vport_type == TionVPortType::VPORT_BLE) { + api->enable_native_boost_support(); + } +#ifdef TION_ENABLE_SCHEDULER + this->typed_api()->on_time.set(*this); + this->typed_api()->on_timer.set(*this); + this->typed_api()->on_timers_state.set(*this); #endif -#ifdef USE_TION_WORK_TIME - sensor::Sensor *work_time_{}; + } +#ifdef TION_ENABLE_SCHEDULER + void on_time(time_t time, uint32_t request_id); + void on_timer(uint8_t timer_id, const dentra::tion_4s::tion4s_timer_t &timer, uint32_t request_id); + void on_timers_state(const dentra::tion_4s::tion4s_timers_state_t &timers_state, uint32_t request_id); + void dump_timers(); + void reset_timers(); #endif +}; - void update_dev_info_(const dentra::tion::tion_dev_info_t &info); +class TionLtApiComponent : public TionApiComponentBase { + public: + explicit TionLtApiComponent(TionApiComponentBase::Api *api, TionVPortType vport_type) + : TionApiComponentBase(api, vport_type) {} + + void set_button_presets(const dentra::tion_lt::button_presets_t &button_presets) { + this->typed_api()->set_button_presets(button_presets); + } }; + } // namespace tion } // namespace esphome diff --git a/components/tion/tion_controls.h b/components/tion/tion_controls.h deleted file mode 100644 index 2e8e0e1..0000000 --- a/components/tion/tion_controls.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "esphome/core/defines.h" -#include "esphome/core/application.h" - -#ifdef USE_SWITCH -#include "esphome/components/switch/switch.h" -#endif -#ifdef USE_BUTTON -#include "esphome/components/button/button.h" -#endif - -namespace esphome { -namespace tion { - -#ifdef USE_SWITCH -template class TionBuzzerSwitch : public Parented, public switch_::Switch { - public: - explicit TionBuzzerSwitch(parent_t *parent) : Parented(parent) {} - void write_state(bool state) override { this->parent_->control_buzzer_state(state); } -}; - -template class TionLedSwitch : public Parented, public switch_::Switch { - public: - explicit TionLedSwitch(parent_t *parent) : Parented(parent) {} - void write_state(bool state) override { this->parent_->control_led_state(state); } -}; - -template class TionResetFilterConfirmSwitch : public Parented, public switch_::Switch { - static_assert(std::is_base_of_v, "parent_t is not Component"); - - public: - explicit TionResetFilterConfirmSwitch(parent_t *parent) : Parented(parent) {} - void write_state(bool state) override { - constexpr const char *timeout_name = "tion_reset_confirm_timeout"; - if (state) { - App.scheduler.set_timeout(this->parent_, timeout_name, 10000, [this]() { this->publish_state(false); }); - } else { - App.scheduler.cancel_timeout(this->parent_, timeout_name); - } - this->publish_state(state); - } -}; -#endif -#ifdef USE_BUTTON -template class TionResetFilterButton : public Parented, public button::Button { - // static_assert(std::is_base_of::value, "parent_t is not TionComponent"); - - public: - explicit TionResetFilterButton(parent_t *parent) : Parented(parent) {} - void press_action() override { -#ifdef USE_TION_RESET_FILTER - if (this->parent_->is_reset_filter_confirmed()) { - this->parent_->reset_filter(); - } -#endif - } -}; -#endif - -} // namespace tion -} // namespace esphome diff --git a/components/tion/tion_defines.h b/components/tion/tion_defines.h deleted file mode 100644 index 1ead06d..0000000 --- a/components/tion/tion_defines.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "../tion-api/tion-api-defines.h" - -#ifndef TION_MAX_FAN_SPEED -#define TION_MAX_FAN_SPEED 6 -#endif diff --git a/components/tion/tion_properties.h b/components/tion/tion_properties.h index c5a7aec..3ff12c6 100644 --- a/components/tion/tion_properties.h +++ b/components/tion/tion_properties.h @@ -4,7 +4,7 @@ #include "esphome/core/helpers.h" #include "../tion-api/tion-api.h" -#include "tion_api_component.h" +#include "tion_component.h" namespace esphome { namespace tion { diff --git a/components/tion/tion_vport_ble.h b/components/tion/tion_vport_ble.h index 64daa78..674761a 100644 --- a/components/tion/tion_vport_ble.h +++ b/components/tion/tion_vport_ble.h @@ -34,10 +34,10 @@ template class TionBleIO : public TionIO, pu bool write_(const uint8_t *data, size_t size) { return this->write_ble_data(data, size); } }; -template -class TionVPortBLEComponent : public vport::VPortBLEComponent { +template +class TionVPortBLEComponent : public vport::VPortBLEComponent { public: - TionVPortBLEComponent(io_t *io) : vport::VPortBLEComponent(io) {} + TionVPortBLEComponent(io_t *io) : vport::VPortBLEComponent(io) {} TionVPortType get_type() const { return TionVPortType::VPORT_BLE; } }; diff --git a/components/tion/tion_vport_jtag.h b/components/tion/tion_vport_jtag.h index b76ea6e..bee3441 100644 --- a/components/tion/tion_vport_jtag.h +++ b/components/tion/tion_vport_jtag.h @@ -87,9 +87,9 @@ template class TionJtagIO : public TionIO, public } }; -template -class TionVPortJTAGComponent : public vport::VPortUARTComponent { - using super_t = vport::VPortUARTComponent; +template +class TionVPortJTAGComponent : public vport::VPortUARTComponent { + using super_t = vport::VPortUARTComponent; public: explicit TionVPortJTAGComponent(io_t *io) : super_t(io) {} diff --git a/components/tion/tion_vport_uart.h b/components/tion/tion_vport_uart.h index f774043..b24ebb2 100644 --- a/components/tion/tion_vport_uart.h +++ b/components/tion/tion_vport_uart.h @@ -40,9 +40,9 @@ template class TionUartIO : public TionIO, public } }; -template -class TionVPortUARTComponent : public vport::VPortUARTComponent { - using super_t = vport::VPortUARTComponent; +template +class TionVPortUARTComponent : public vport::VPortUARTComponent { + using super_t = vport::VPortUARTComponent; public: explicit TionVPortUARTComponent(io_t *io) : super_t(io) { diff --git a/components/tion_3s/__init__.py b/components/tion_3s/__init__.py deleted file mode 100644 index 3863b79..0000000 --- a/components/tion_3s/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import select -from esphome.const import ENTITY_CATEGORY_CONFIG -from esphome.cpp_generator import MockObjClass - -from .. import tion # pylint: disable=relative-beyond-top-level - -CODEOWNERS = ["@dentra"] -AUTO_LOAD = ["tion", "select"] - -CONF_AIR_INTAKE = "air_intake" - -Tion3sApi = tion.tion_ns.class_("Tion3sApi") -Tion3sAirIntakeSelectT = tion.tion_ns.class_("Tion3sAirIntakeSelect", select.Select) - -OPTIONS_AIR_INTAKE = ["Outdoor", "Indoor", "Mixed"] - - -def tion_3s_schema(tion_class: MockObjClass, tion_base_schema: cv.Schema): - return tion.tion_schema(tion_class, Tion3sApi, tion_base_schema).extend( - { - cv.Optional(CONF_AIR_INTAKE): select.select_schema( - Tion3sAirIntakeSelectT.template(tion_class), - icon="mdi:air-conditioner", - entity_category=ENTITY_CATEGORY_CONFIG, - ), - } - ) - - -async def setup_tion_3s(config, compoent_reg): - """Code generation entry point""" - var = await tion.setup_tion_core(config, compoent_reg) - await tion.setup_select( - config, CONF_AIR_INTAKE, var.set_air_intake, var, OPTIONS_AIR_INTAKE - ) - - -Tion3sApiComponent = tion.tion_ns.class_( - "Tion3sApiComponent", cg.Component, tion.TionApiComponent -) - -CONFIG_SCHEMA = tion.tion_schema_api(Tion3sApiComponent, Tion3sApi) - - -async def to_code(config: dict): - await tion.setup_tion_api(config, "3s") diff --git a/components/tion_3s/climate/__init__.py b/components/tion_3s/climate/__init__.py deleted file mode 100644 index c7d74bf..0000000 --- a/components/tion_3s/climate/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -from esphome.cpp_types import PollingComponent -from esphome.components import climate - -from ... import tion # pylint: disable=relative-beyond-top-level -from .. import ( - tion_3s_schema, - setup_tion_3s, -) # pylint: disable=relative-beyond-top-level - -CODEOWNERS = ["@dentra"] -AUTO_LOAD = ["tion_3s"] - - -Tion3sClimate = tion.tion_ns.class_("Tion3sClimate", PollingComponent, climate.Climate) - -CONFIG_SCHEMA = tion_3s_schema(Tion3sClimate, climate.CLIMATE_SCHEMA) - - -async def to_code(config): - """Code generation entry point""" - # pylint: disable=unused-variable - var = await setup_tion_3s(config, climate.register_climate) diff --git a/components/tion_3s/climate/tion_3s_climate.cpp b/components/tion_3s/climate/tion_3s_climate.cpp deleted file mode 100644 index f020643..0000000 --- a/components/tion_3s/climate/tion_3s_climate.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include "esphome/core/log.h" -#include "esphome/core/defines.h" - -#include "tion_3s_climate.h" - -#ifdef TION_ENABLE_OFF_BEFORE_HEAT -#define TION_OPTION_STR_OFF_BEFORE_HEAT "enabled" -#else -#define TION_OPTION_STR_OFF_BEFORE_HEAT "disabled" -#endif - -#ifdef TION_ENABLE_ANTIFRIZE -#define TION_OPTION_STR_ANTIFRIZE "enabled" -#else -#define TION_OPTION_STR_ANTIFRIZE "disabled" -#endif - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_3s"; - -void Tion3sClimate::dump_config() { - this->dump_settings(TAG, "Tion 3S"); - LOG_SELECT(" ", "Air Intake", this->air_intake_); - ESP_LOGCONFIG(" ", "OFF befor HEAT: %s", TION_OPTION_STR_OFF_BEFORE_HEAT); - ESP_LOGCONFIG(" ", "Antifrize: %s", TION_OPTION_STR_ANTIFRIZE); - this->dump_presets(TAG); -} - -void Tion3sClimate::update_state(const tion::TionState &state) { - if (!state.power_state) { - this->mode = climate::CLIMATE_MODE_OFF; - this->action = climate::CLIMATE_ACTION_OFF; - } else if (state.heater_state) { - this->mode = climate::CLIMATE_MODE_HEAT; - this->action = this->mode == state.is_heating(this->api_->traits()) ? climate::CLIMATE_ACTION_HEATING - : climate::CLIMATE_ACTION_FAN; - } else { - this->mode = climate::CLIMATE_MODE_FAN_ONLY; - this->action = climate::CLIMATE_ACTION_FAN; - } - - this->current_temperature = state.current_temperature; - this->target_temperature = state.target_temperature; - this->set_fan_speed_(state.fan_speed); - this->publish_state(); -#ifdef USE_TION_VERSION - if (this->version_ && state.firmware_version > 0) { - this->version_->publish_state(str_snprintf("%04X", 4, state.firmware_version)); - } -#endif - if (this->air_intake_) { - auto air_intake = this->air_intake_->at(static_cast(state.gate_position)); - if (air_intake.has_value()) { - this->air_intake_->publish_state(*air_intake); - } - } -#ifdef USE_TION_PRODUCTIVITY - if (this->productivity_) { - this->productivity_->publish_state(state.productivity); - } -#endif -} - -void Tion3sClimate::control_climate_state(climate::ClimateMode mode, uint8_t fan_speed, float target_temperature, - TionGatePosition gate_position) { - auto *call = this->make_api_call(); - - call->set_fan_speed(fan_speed); - if (!std::isnan(target_temperature)) { - call->set_target_temperature(target_temperature); - } - - if (mode == climate::CLIMATE_MODE_OFF) { - call->set_power_state(false); - } else if (mode == climate::CLIMATE_MODE_HEAT_COOL) { - call->set_power_state(true); - } else { - call->set_power_state(true); - call->set_heater_state(mode == climate::CLIMATE_MODE_HEAT); - } - - call->set_gate_position(gate_position); - - call->perform(); -} - -} // namespace tion -} // namespace esphome diff --git a/components/tion_3s/climate/tion_3s_climate.h b/components/tion_3s/climate/tion_3s_climate.h deleted file mode 100644 index a0750e5..0000000 --- a/components/tion_3s/climate/tion_3s_climate.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "../tion_3s.h" -#include "../../tion/tion_climate_component.h" - -namespace esphome { -namespace tion { - -class Tion3sClimate : public TionClimateComponent { - using TionState = dentra::tion::TionState; - using TionGatePosition = dentra::tion::TionGatePosition; - - public: - explicit Tion3sClimate(Tion3sApi *api, TionVPortType vport_type) : TionClimateComponent(api, vport_type) {} - - void dump_config() override; - - void set_air_intake(select::Select *air_intake) { this->air_intake_ = air_intake; } - - // void on_ready() { - // TionClimateComponent::on_ready(); - // if (this->vport_type_ == TionVPortType::VPORT_UART && this->state_.firmware_version < 0x003C) { - // this->api_->request_command4(); - // } - // } - - void update_state(const tion::TionState &state) override; - - void reset_filter() { this->api_->reset_filter(this->state_); } - - void control_buzzer_state(bool state) { - auto *call = this->make_api_call(); - call->set_sound_state(state); - call->perform(); - } - - void control_gate_position(TionGatePosition gate_position) { - auto *call = this->make_api_call(); - call->set_gate_position(gate_position); - call->perform(); - } - - void control_climate_state(climate::ClimateMode mode, uint8_t fan_speed, float target_temperature, - TionGatePosition gate_position) override; - - TionGatePosition get_gate_position() const override { - if (!this->batch_active_ && this->air_intake_) { - auto active_index = this->air_intake_->active_index(); - if (active_index.has_value()) { - return static_cast(*active_index); - } - } - return this->state_.gate_position; - } - - protected: - select::Select *air_intake_{}; -}; - -} // namespace tion -} // namespace esphome diff --git a/components/tion_3s/tion_3s.h b/components/tion_3s/tion_3s.h deleted file mode 100644 index 13c7141..0000000 --- a/components/tion_3s/tion_3s.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "esphome/components/select/select.h" - -#include "../tion-api/tion-api-3s.h" -#include "../tion/tion_controls.h" -#include "../tion/tion_api_component.h" - -namespace esphome { -namespace tion { - -using namespace dentra::tion; - -using Tion3sApi = dentra::tion::Tion3sApi; - -template class Tion3sAirIntakeSelect : public select::Select, public Parented { - public: - explicit Tion3sAirIntakeSelect(parent_t *parent) : Parented(parent) {} - void control(const std::string &value) override { - auto opt = this->index_of(value); - if (opt.has_value()) { - this->parent_->control_gate_position(static_cast(*opt)); - } - } -}; - -class Tion3sApiComponent : public TionApiComponentBase { - public: - explicit Tion3sApiComponent(Tion3sApi *api, TionVPortType vport_type) : TionApiComponentBase(api, vport_type) {} -}; - -} // namespace tion -} // namespace esphome diff --git a/components/tion_3s_ble/__init__.py b/components/tion_3s_ble/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/components/tion_3s/vport/tion_3s_vport.cpp b/components/tion_3s_ble/tion_3s_ble_vport.cpp similarity index 96% rename from components/tion_3s/vport/tion_3s_vport.cpp rename to components/tion_3s_ble/tion_3s_ble_vport.cpp index f61705a..ce4ac73 100644 --- a/components/tion_3s/vport/tion_3s_vport.cpp +++ b/components/tion_3s_ble/tion_3s_ble_vport.cpp @@ -1,12 +1,12 @@ #include #include "esphome/core/log.h" -#include "tion_3s_vport.h" +#include "tion_3s_ble_vport.h" namespace esphome { namespace tion { -static const char *const TAG = "tion_3s_vport"; +static const char *const TAG = "tion_3s_ble_vport"; #define RTC_PAIR_STATE fnv1_hash("tion_3s") diff --git a/components/tion_3s/vport/tion_3s_vport.h b/components/tion_3s_ble/tion_3s_ble_vport.h similarity index 86% rename from components/tion_3s/vport/tion_3s_vport.h rename to components/tion_3s_ble/tion_3s_ble_vport.h index f66dca5..714a298 100644 --- a/components/tion_3s/vport/tion_3s_vport.h +++ b/components/tion_3s_ble/tion_3s_ble_vport.h @@ -2,9 +2,9 @@ #include "esphome/core/preferences.h" -#include "../../tion-api/tion-api-3s.h" -#include "../../tion-api/tion-api-ble-3s.h" -#include "../../tion/tion_vport_ble.h" +#include "../tion-api/tion-api-3s.h" +#include "../tion-api/tion-api-ble-3s.h" +#include "../tion/tion_vport_ble.h" namespace esphome { namespace tion { @@ -23,7 +23,7 @@ class Tion3sBleIO : public TionBleIO { Tion3sBleVPort *vport_{}; }; -class Tion3sBleVPort : public TionVPortBLEComponent { +class Tion3sBleVPort : public TionVPortBLEComponent { public: Tion3sBleVPort(Tion3sBleIO *io) : TionVPortBLEComponent(io) { this->io_->set_on_ready(Tion3sBleIO::on_ready_type::create(*this)); diff --git a/components/tion_3s/vport/__init__.py b/components/tion_3s_ble/vport.py similarity index 78% rename from components/tion_3s/vport/__init__.py rename to components/tion_3s_ble/vport.py index c8b0c09..9b9139f 100644 --- a/components/tion_3s/vport/__init__.py +++ b/components/tion_3s_ble/vport.py @@ -2,7 +2,7 @@ import esphome.config_validation as cv from esphome.const import PLATFORM_ESP32 -from ... import tion, vport # pylint: disable=relative-beyond-top-level +from .. import tion, vport # pylint: disable=relative-beyond-top-level CODEOWNERS = ["@dentra"] ESP_PLATFORMS = [PLATFORM_ESP32] @@ -14,7 +14,7 @@ Tion3sBleIO = tion.tion_ns.class_("Tion3sBleIO") Tion3sBleVPort = tion.tion_ns.class_("Tion3sBleVPort", cg.PollingComponent, vport.VPort) -CONFIG_SCHEMA = tion.tion_vport_ble_schema(Tion3sBleVPort, Tion3sBleIO).extend( +CONFIG_SCHEMA = vport.vport_ble_schema(Tion3sBleVPort, Tion3sBleIO).extend( { cv.Optional(CONF_EXPERIMENTAL_ALWAYS_PAIR, default=False): cv.boolean, } @@ -22,7 +22,7 @@ async def to_code(config): - var = await tion.setup_tion_vport_ble(config) + var = await vport.setup_vport_ble(config) cg.add(var.set_experimental_always_pair(config[CONF_EXPERIMENTAL_ALWAYS_PAIR])) vio = await cg.get_variable(config[vport.CONF_VPORT_IO_ID]) cg.add(vio.set_vport(var)) diff --git a/components/tion_3s_uart/tion_3s_uart.cpp b/components/tion_3s_uart/tion_3s_uart.cpp deleted file mode 100644 index 632119d..0000000 --- a/components/tion_3s_uart/tion_3s_uart.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "esphome/core/log.h" - -#include "tion_3s_uart.h" - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_3s_uart"; - -void Tion3sUartVPort::dump_config() { - VPORT_UART_LOG("Tion 3S UART"); -} - -} // namespace tion -} // namespace esphome diff --git a/components/tion_3s_uart/tion_3s_uart_vport.cpp b/components/tion_3s_uart/tion_3s_uart_vport.cpp new file mode 100644 index 0000000..54f033b --- /dev/null +++ b/components/tion_3s_uart/tion_3s_uart_vport.cpp @@ -0,0 +1,13 @@ +#include "esphome/core/log.h" + +#include "tion_3s_uart_vport.h" + +namespace esphome { +namespace tion { + +static const char *const TAG = "tion_3s_uart_vport"; + +void Tion3sUartVPort::dump_config() { VPORT_UART_LOG("Tion 3S UART"); } + +} // namespace tion +} // namespace esphome diff --git a/components/tion_3s_uart/tion_3s_uart.h b/components/tion_3s_uart/tion_3s_uart_vport.h similarity index 92% rename from components/tion_3s_uart/tion_3s_uart.h rename to components/tion_3s_uart/tion_3s_uart_vport.h index 8f45261..d6bc921 100644 --- a/components/tion_3s_uart/tion_3s_uart.h +++ b/components/tion_3s_uart/tion_3s_uart_vport.h @@ -12,7 +12,7 @@ namespace tion { using Tion3sUartIO = TionUartIO; -class Tion3sUartVPort : public TionVPortUARTComponent { +class Tion3sUartVPort : public TionVPortUARTComponent { public: explicit Tion3sUartVPort(Tion3sUartIO *io) : TionVPortUARTComponent(io) {} diff --git a/components/tion_4s/__init__.py b/components/tion_4s/__init__.py deleted file mode 100644 index 4649a02..0000000 --- a/components/tion_4s/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import switch -from esphome.const import CONF_ID, ENTITY_CATEGORY_CONFIG -from esphome.cpp_generator import MockObjClass - -from .. import vport # pylint: disable=relative-beyond-top-level -from .. import tion, tion_lt # pylint: disable=relative-beyond-top-level - -CODEOWNERS = ["@dentra"] -AUTO_LOAD = ["tion", "number"] - -CONF_RECIRCULATION = "recirculation" - -CONF_HEARTBEAT_INTERVAL = "heartbeat_interval" - -Tion4sApi = tion.tion_ns.class_("Tion4sApi") -TionRecirculationSwitchT = tion.tion_ns.class_("TionRecirculationSwitch", switch.Switch) - - -def tion_4s_schema(tion_class: MockObjClass, tion_base_schema: cv.Schema): - return tion_lt.tion_lt_base_schema(tion_class, Tion4sApi, tion_base_schema).extend( - { - cv.Optional(CONF_RECIRCULATION): switch.switch_schema( - TionRecirculationSwitchT.template(tion_class), - icon="mdi:air-conditioner", - entity_category=ENTITY_CATEGORY_CONFIG, - block_inverted=True, - ), - } - ) - - -async def setup_tion_4s(config, compoent_reg): - """Code generation entry point""" - var = await tion_lt.setup_tion_lt(config, compoent_reg) - await tion.setup_switch(config, CONF_RECIRCULATION, var.set_recirculation, var) - - -CONFIG_SCHEMA = tion.tion_schema_api( - tion.tion_ns.class_("Tion4sApiComponent", cg.Component, tion.TionApiComponent), - Tion4sApi, -) - - -async def to_code(config: dict): - await tion.setup_tion_api(config, "4s") diff --git a/components/tion_4s/climate/__init__.py b/components/tion_4s/climate/__init__.py deleted file mode 100644 index 3c51eee..0000000 --- a/components/tion_4s/climate/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import climate -from esphome.cpp_types import PollingComponent - -from ... import tion # pylint: disable=relative-beyond-top-level -from .. import ( # pylint: disable=relative-beyond-top-level - CONF_HEARTBEAT_INTERVAL, - setup_tion_4s, - tion_4s_schema, -) - -CODEOWNERS = ["@dentra"] -AUTO_LOAD = ["tion_4s"] - -Tion4sClimate = tion.tion_ns.class_("Tion4sClimate", PollingComponent, climate.Climate) - -CONFIG_SCHEMA = tion_4s_schema(Tion4sClimate, climate.CLIMATE_SCHEMA).extend( - { - cv.Optional(CONF_HEARTBEAT_INTERVAL, default="5s"): cv.update_interval, - } -) - - -async def to_code(config): - """Code generation entry point""" - var = await setup_tion_4s(config, climate.register_climate) - cg.add(var.set_heartbeat_interval(config[CONF_HEARTBEAT_INTERVAL])) diff --git a/components/tion_4s/climate/tion_4s_climate.cpp b/components/tion_4s/climate/tion_4s_climate.cpp deleted file mode 100644 index 23f9bc7..0000000 --- a/components/tion_4s/climate/tion_4s_climate.cpp +++ /dev/null @@ -1,230 +0,0 @@ -#include "esphome/core/defines.h" -#ifdef USE_OTA -#include "esphome/components/ota/ota_component.h" -#endif - -#include "esphome/core/log.h" - -#include -#include -#include -#include - -#include "tion_4s_climate.h" - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_4s"; - -using namespace dentra::tion_4s; - -void Tion4sClimate::dump_config() { - this->dump_settings(TAG, "Tion 4S"); - LOG_SWITCH(" ", "Recirculation", this->recirculation_); - this->dump_presets(TAG); -} - -void Tion4sClimate::setup() { - Tion4sClimateBase::setup(); - -#ifdef TION_ENABLE_HEARTBEAT - if (this->vport_type_ == TionVPortType::VPORT_UART) { - this->set_interval(this->heartbeat_interval_, [this]() { this->api_->send_heartbeat(); }); - -#ifdef USE_OTA - if (this->heartbeat_interval_ > 0) { - // additionally send heartbeat when OTA starts and before ESP restart. - ota::global_ota_component->add_on_state_callback([this](ota::OTAState state, float, uint8_t) { - if (state != ota::OTAState::OTA_IN_PROGRESS) { - this->api_->send_heartbeat(); - } - }); - } -#endif - } -#endif // TION_ENABLE_HEARTBEAT -} - -#ifdef TION_ENABLE_PRESETS -void Tion4sClimate::on_turbo(const tion4s_turbo_t &turbo, uint32_t request_id) { - if (this->vport_type_ != TionVPortType::VPORT_BLE) { - ESP_LOGW(TAG, "Only BLE supports native turbo mode. Please report."); - return; - } - - // change preset if turbo changed outside - const auto preset = this->preset.value_or(climate::CLIMATE_PRESET_NONE); - if (turbo.is_active) { - if (preset != climate::CLIMATE_PRESET_BOOST) { - this->presets_saved_preset_ = preset; - this->preset = climate::CLIMATE_PRESET_BOOST; - } - } else { - if (preset == climate::CLIMATE_PRESET_BOOST) { - this->presets_cancel_boost_(this); - } - } - - if (this->presets_boost_time_left_) { - if (turbo.turbo_time == 0) { - this->presets_boost_time_left_->publish_state(NAN); - } else { - const auto boost_time = this->get_boost_time_(); - const auto boost_time_one_percent = boost_time / 100; - this->presets_boost_time_left_->publish_state(static_cast(turbo.turbo_time) / - static_cast(boost_time_one_percent)); - } - } - - this->publish_state(); -} -#endif - -void Tion4sClimate::update_state(const tion::TionState &state) { - if (!state.power_state) { - this->mode = climate::CLIMATE_MODE_OFF; - this->action = climate::CLIMATE_ACTION_OFF; - } else if (state.heater_state) { - this->mode = climate::CLIMATE_MODE_HEAT; - this->action = - state.is_heating(this->api_->traits()) ? climate::CLIMATE_ACTION_HEATING : climate::CLIMATE_ACTION_FAN; - } else { - this->mode = climate::CLIMATE_MODE_FAN_ONLY; - this->action = climate::CLIMATE_ACTION_OFF; - } - - this->current_temperature = state.current_temperature; - this->target_temperature = state.target_temperature; - this->set_fan_speed_(state.fan_speed); - this->publish_state(); - - if (this->recirculation_) { - this->recirculation_->publish_state(state.gate_position != TionGatePosition::OUTDOOR); - } -} - -void Tion4sClimate::control_recirculation_state(bool state) { - auto *call = this->make_api_call(); - call->set_gate_position(state ? TionGatePosition::INDOOR : TionGatePosition::OUTDOOR); - call->perform(); -} - -void Tion4sClimate::control_climate_state(climate::ClimateMode mode, uint8_t fan_speed, float target_temperature, - TionGatePosition gate_position) { - auto *call = this->make_api_call(); - - call->set_fan_speed(fan_speed); - if (!std::isnan(target_temperature)) { - call->set_target_temperature(target_temperature); - } - - if (mode == climate::CLIMATE_MODE_OFF) { - call->set_power_state(false); - } else if (mode == climate::CLIMATE_MODE_HEAT_COOL) { - call->set_power_state(true); - } else { - call->set_power_state(true); - call->set_heater_state(mode == climate::CLIMATE_MODE_HEAT); - } - - call->set_gate_position(gate_position); - - call->perform(); -} - -#ifdef TION_ENABLE_PRESETS -bool Tion4sClimate::enable_boost() { - if (this->vport_type_ != TionVPortType::VPORT_BLE) { - return TionClimateComponent::enable_boost(); - } - - auto boost_time = this->get_boost_time_(); - if (boost_time == 0) { - return false; - } - - this->api_->set_turbo(boost_time, ++this->request_id_); - if (this->presets_boost_time_left_) { - this->presets_boost_time_left_->publish_state(100); - } - - return true; -} - -void Tion4sClimate::cancel_boost() { - if (this->vport_type_ != TionVPortType::VPORT_BLE) { - TionClimateComponent::cancel_boost(); - return; - } - this->api_->set_turbo(0, ++this->request_id_); -} -#endif - -#ifdef TION_ENABLE_SCHEDULER - -void Tion4sClimate::on_time(time_t time, uint32_t request_id) { - auto *c_tm = std::gmtime(&time); - char buf[20] = {}; - std::strftime(buf, sizeof(buf), "%F %T", c_tm); - ESP_LOGI(TAG, "Device UTC time: %s", buf); - c_tm = std::localtime(&time); - std::strftime(buf, sizeof(buf), "%F %T", c_tm); - ESP_LOGI(TAG, "Device local time: %s", buf); -} - -namespace { -void add_timer_week_day(std::string &s, bool day, const char *day_name) { - if (day) { - if (!s.empty()) { - s.append(", "); - } - s.append(day_name); - } -} -} // namespace - -void Tion4sClimate::on_timer(uint8_t timer_id, const tion4s_timer_t &timer, uint32_t request_id) { - std::string schedule; - if (timer.schedule.monday && timer.schedule.tuesday && timer.schedule.wednesday && timer.schedule.thursday && - timer.schedule.friday && timer.schedule.saturday && timer.schedule.sunday) { - schedule = "MON-SUN"; - } else { - add_timer_week_day(schedule, timer.schedule.monday, "MON"); - add_timer_week_day(schedule, timer.schedule.tuesday, "TUE"); - add_timer_week_day(schedule, timer.schedule.wednesday, "WED"); - add_timer_week_day(schedule, timer.schedule.thursday, "THU"); - add_timer_week_day(schedule, timer.schedule.friday, "FRI"); - add_timer_week_day(schedule, timer.schedule.saturday, "SAT"); - add_timer_week_day(schedule, timer.schedule.sunday, "SUN"); - } - - ESP_LOGI(TAG, "Timer[%u] %s at %02u:%02u is %s", timer_id, schedule.c_str(), timer.schedule.hours, - timer.schedule.minutes, ONOFF(timer.timer_state)); -} - -void Tion4sClimate::on_timers_state(const tion4s_timers_state_t &timers_state, uint32_t request_id) { - for (int i = 0; i < tion4s_timers_state_t::TIMERS_COUNT; i++) { - ESP_LOGI(TAG, "Timer[%d] state %s", i, ONOFF(timers_state.timers[i].active)); - } -} - -void Tion4sClimate::dump_timers() { - this->api_->request_time(++this->request_id_); - for (uint8_t timer_id = 0; timer_id < tion4s_timers_state_t::TIMERS_COUNT; timer_id++) { - this->api_->request_timer(timer_id, ++this->request_id_); - } - this->api_->request_timers_state(++this->request_id_); -} - -void Tion4sClimate::reset_timers() { - const tion4s_timer_t timer{}; - for (uint8_t timer_id = 0; timer_id < tion4s_timers_state_t::TIMERS_COUNT; timer_id++) { - this->api_->write_timer(timer_id, timer, ++this->request_id_); - } -} -#endif - -} // namespace tion -} // namespace esphome -// #endif // USE_TION_CLIMATE diff --git a/components/tion_4s/climate/tion_4s_climate.h b/components/tion_4s/climate/tion_4s_climate.h deleted file mode 100644 index 66f2273..0000000 --- a/components/tion_4s/climate/tion_4s_climate.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once -#include "esphome/core/defines.h" -// #ifdef USE_TION_CLIMATE - -#include "esphome/components/switch/switch.h" - -#include "../tion_4s.h" -#include "../../tion/tion_climate_component.h" - -namespace esphome { -namespace tion { - -using Tion4sClimateBase = TionLtClimateComponent; - -class Tion4sClimate : public Tion4sClimateBase { - public: - explicit Tion4sClimate(Tion4sApi *api, TionVPortType vport_type) : Tion4sClimateBase(api, vport_type) { -#ifdef TION_ENABLE_SCHEDULER - this->api_->on_time.set(*this); - this->api_->on_timer.set(*this); - this->api_->on_timers_state.set(*this); -#endif - } - - void dump_config() override; - void setup() override; - - void set_recirculation(switch_::Switch *recirculation) { this->recirculation_ = recirculation; } - -#ifdef TION_ENABLE_PRESETS - void update() override { - TionLtClimateComponent::update(); - if (this->vport_type_ == TionVPortType::VPORT_BLE) { - this->api_->request_turbo(); - } - } -#endif - - // #ifdef TION_ENABLE_SCHEDULER - // void on_ready() { - // TionClimateComponent::on_ready(); - // // scheduler specific init commands - // this->api_->request_time(); - // } - // #endif - -#ifdef TION_ENABLE_PRESETS - void on_turbo(const dentra::tion_4s::tion4s_turbo_t &turbo, uint32_t request_id); -#endif -#ifdef TION_ENABLE_SCHEDULER - void on_time(time_t time, uint32_t request_id); - void on_timer(uint8_t timer_id, const dentra::tion_4s::tion4s_timer_t &timer, uint32_t request_id); - void on_timers_state(const dentra::tion_4s::tion4s_timers_state_t &timers_state, uint32_t request_id); - void dump_timers(); - void reset_timers(); -#endif - void update_state(const tion::TionState &state) override; - - void reset_filter() const { this->api_->reset_filter(this->state_); } - - void control_buzzer_state(bool state) { - auto *call = this->make_api_call(); - call->set_sound_state(state); - call->perform(); - } - - void control_led_state(bool state) { - auto *call = this->make_api_call(); - call->set_led_state(state); - call->perform(); - } - - void control_recirculation_state(bool state); - void control_climate_state(climate::ClimateMode mode, uint8_t fan_speed, float target_temperature, - TionGatePosition gate_position) override; - - TionGatePosition get_gate_position() const override { - if (!this->batch_active_ && this->recirculation_) { - if (this->recirculation_->state) { - return TionGatePosition::INDOOR; - } - return TionGatePosition::OUTDOOR; - } - return this->state_.gate_position; - } - - void reset_errors() const { this->api_->reset_errors(this->state_); } - - void set_heartbeat_interval(uint32_t heartbeat_interval) { -#ifdef TION_ENABLE_HEARTBEAT - this->heartbeat_interval_ = heartbeat_interval; -#endif - } - - protected: - switch_::Switch *recirculation_{}; -#ifdef TION_ENABLE_PRESETS - bool enable_boost() override; - void cancel_boost() override; -#endif - -#ifdef TION_ENABLE_HEARTBEAT - uint32_t heartbeat_interval_{5000}; -#endif -}; - -} // namespace tion - -} // namespace esphome -// #endif // USE_TION_CLIMATE diff --git a/components/tion_4s/fan/__init__.py b/components/tion_4s/fan/__init__.py deleted file mode 100644 index 55ec4c6..0000000 --- a/components/tion_4s/fan/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -from esphome.cpp_types import PollingComponent -from esphome.components import fan - -from ... import tion # pylint: disable=relative-beyond-top-level -from .. import ( - tion_4s_schema, - setup_tion_4s, -) # pylint: disable=relative-beyond-top-level - -CODEOWNERS = ["@dentra"] -AUTO_LOAD = ["tion_4s"] - -Tion4sClimate = tion.tion_ns.class_("Tion4sFan", PollingComponent, fan.Fan) - -CONFIG_SCHEMA = tion_4s_schema(Tion4sClimate, fan.FAN_SCHEMA) - - -async def to_code(config): - """Code generation entry point""" - # pylint: disable=unused-variable - var = await setup_tion_4s(config, fan.register_fan) diff --git a/components/tion_4s/fan/tion_4s_fan.cpp b/components/tion_4s/fan/tion_4s_fan.cpp deleted file mode 100644 index d7dcd2b..0000000 --- a/components/tion_4s/fan/tion_4s_fan.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "esphome/core/defines.h" -#ifdef USE_FAN - -#include "esphome/core/log.h" - -#include -#include -#include -#include - -#include "tion_4s_fan.h" - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_4s_fan"; - -} -} // namespace esphome -#endif // USE_FAN diff --git a/components/tion_4s/fan/tion_4s_fan.h b/components/tion_4s/fan/tion_4s_fan.h deleted file mode 100644 index 9f25710..0000000 --- a/components/tion_4s/fan/tion_4s_fan.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include "esphome/core/defines.h" -#ifdef USE_FAN - -#include "../tion_4s.h" -#include "../../tion/tion_fan_component.h" - -namespace esphome { -namespace tion { - -// class Tion4sFan : public TionFanComponent { -// public: -// explicit Tion4sFan(Tion4sApi *api, TionVPortType vport_type) : TionFanComponent(api, vport_type) {} -// }; - -} // namespace tion -} // namespace esphome -#endif // USE_FAN diff --git a/components/tion_4s/tion_4s.h b/components/tion_4s/tion_4s.h deleted file mode 100644 index ffbab07..0000000 --- a/components/tion_4s/tion_4s.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "../tion-api/tion-api-4s.h" -#include "../tion/tion_controls.h" -#include "../tion/tion_api_component.h" - -namespace esphome { -namespace tion { - -using namespace dentra::tion; - -using Tion4sApi = dentra::tion_4s::Tion4sApi; - -template class TionRecirculationSwitch : public Parented, public switch_::Switch { - public: - explicit TionRecirculationSwitch(parent_t *parent) : Parented(parent) {} - void write_state(bool state) override { this->parent_->control_recirculation_state(state); } -}; - -class Tion4sApiComponent : public TionApiComponentBase { - public: - explicit Tion4sApiComponent(Tion4sApi *api, TionVPortType vport_type) : TionApiComponentBase(api, vport_type) { - if (this->vport_type_ == TionVPortType::VPORT_BLE) { - api->enable_native_boost_support(); - } - } -}; - -} // namespace tion -} // namespace esphome diff --git a/components/tion_4s_ble/__init__.py b/components/tion_4s_ble/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/components/tion_4s/vport/tion_4s_vport.cpp b/components/tion_4s_ble/tion_4s_ble_vport.cpp similarity index 69% rename from components/tion_4s/vport/tion_4s_vport.cpp rename to components/tion_4s_ble/tion_4s_ble_vport.cpp index 2539807..57077be 100644 --- a/components/tion_4s/vport/tion_4s_vport.cpp +++ b/components/tion_4s_ble/tion_4s_ble_vport.cpp @@ -1,10 +1,10 @@ #include "esphome/core/log.h" -#include "tion_4s_vport.h" +#include "tion_4s_ble_vport.h" namespace esphome { namespace tion { -static const char *const TAG = "tion_4s_vport"; +static const char *const TAG = "tion_4s_ble_vport"; void Tion4sBleVPort::dump_config() { TION_VPORT_BLE_LOG("Tion 4S BLE"); } diff --git a/components/tion_4s/vport/tion_4s_vport.h b/components/tion_4s_ble/tion_4s_ble_vport.h similarity index 60% rename from components/tion_4s/vport/tion_4s_vport.h rename to components/tion_4s_ble/tion_4s_ble_vport.h index 2eb0902..17a32e3 100644 --- a/components/tion_4s/vport/tion_4s_vport.h +++ b/components/tion_4s_ble/tion_4s_ble_vport.h @@ -1,15 +1,15 @@ #pragma once -#include "../../tion-api/tion-api-4s.h" -#include "../../tion-api/tion-api-ble-lt.h" -#include "../../tion/tion_vport_ble.h" +#include "../tion-api/tion-api-4s.h" +#include "../tion-api/tion-api-ble-lt.h" +#include "../tion/tion_vport_ble.h" namespace esphome { namespace tion { using Tion4sBleIO = esphome::tion::TionBleIO; -class Tion4sBleVPort : public TionVPortBLEComponent { +class Tion4sBleVPort : public TionVPortBLEComponent { public: Tion4sBleVPort(Tion4sBleIO *io) : TionVPortBLEComponent(io) {} diff --git a/components/tion_4s/vport/__init__.py b/components/tion_4s_ble/vport.py similarity index 65% rename from components/tion_4s/vport/__init__.py rename to components/tion_4s_ble/vport.py index e6c98ba..d708f46 100644 --- a/components/tion_4s/vport/__init__.py +++ b/components/tion_4s_ble/vport.py @@ -1,6 +1,6 @@ import esphome.codegen as cg from esphome.const import PLATFORM_ESP32 -from ... import tion, vport # pylint: disable=relative-beyond-top-level +from .. import tion, vport # pylint: disable=relative-beyond-top-level CODEOWNERS = ["@dentra"] ESP_PLATFORMS = [PLATFORM_ESP32] @@ -11,8 +11,8 @@ Tion4sBleVPort = tion.tion_ns.class_("Tion4sBleVPort", cg.PollingComponent, vport.VPort) Tion4sBleIO = tion.tion_ns.class_("Tion4sBleIO") -CONFIG_SCHEMA = tion.tion_vport_ble_schema(Tion4sBleVPort, Tion4sBleIO) +CONFIG_SCHEMA = vport.vport_ble_schema(Tion4sBleVPort, Tion4sBleIO) async def to_code(config): - await tion.setup_tion_vport_ble(config) + await vport.setup_vport_ble(config) diff --git a/components/tion_4s_uart/tion_4s_uart.cpp b/components/tion_4s_uart/tion_4s_uart_vport.cpp similarity index 91% rename from components/tion_4s_uart/tion_4s_uart.cpp rename to components/tion_4s_uart/tion_4s_uart_vport.cpp index 62373da..8ad9ebe 100644 --- a/components/tion_4s_uart/tion_4s_uart.cpp +++ b/components/tion_4s_uart/tion_4s_uart_vport.cpp @@ -4,12 +4,12 @@ #include "esphome/components/ota/ota_component.h" #endif -#include "tion_4s_uart.h" +#include "tion_4s_uart_vport.h" namespace esphome { namespace tion { -static const char *const TAG = "tion_4s_uart"; +static const char *const TAG = "tion_4s_uart_vport"; void Tion4sUartVPort::dump_config() { VPORT_UART_LOG("Tion 4S UART"); diff --git a/components/tion_4s_uart/tion_4s_uart.h b/components/tion_4s_uart/tion_4s_uart_vport.h similarity index 92% rename from components/tion_4s_uart/tion_4s_uart.h rename to components/tion_4s_uart/tion_4s_uart_vport.h index 5fe6cfa..cf44fd3 100644 --- a/components/tion_4s_uart/tion_4s_uart.h +++ b/components/tion_4s_uart/tion_4s_uart_vport.h @@ -3,7 +3,6 @@ #include "esphome/core/defines.h" #include "esphome/components/vport/vport_uart.h" -#include "../tion/tion_controls.h" #include "../tion/tion_vport_uart.h" #include "../tion-api/tion-api-4s.h" #include "../tion-api/tion-api-uart-4s.h" @@ -13,7 +12,7 @@ namespace tion { using Tion4sUartIO = TionUartIO; -class Tion4sUartVPort : public TionVPortUARTComponent { +class Tion4sUartVPort : public TionVPortUARTComponent { using TionState = dentra::tion::TionState; using TionGatePosition = dentra::tion::TionGatePosition; diff --git a/components/tion_lt/__init__.py b/components/tion_lt/__init__.py deleted file mode 100644 index 7af9b1f..0000000 --- a/components/tion_lt/__init__.py +++ /dev/null @@ -1,130 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import binary_sensor, sensor, switch -from esphome.const import ( - CONF_ENTITY_CATEGORY, - CONF_ICON, - CONF_INVERTED, - CONF_TEMPERATURE, - DEVICE_CLASS_OPENING, - DEVICE_CLASS_POWER, - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, - STATE_CLASS_MEASUREMENT, - STATE_CLASS_TOTAL_INCREASING, - UNIT_CUBIC_METER, - UNIT_WATT, -) -from esphome.cpp_generator import MockObjClass -from esphome.cpp_types import PollingComponent - -from .. import tion # pylint: disable=relative-beyond-top-level - -CODEOWNERS = ["@dentra"] -AUTO_LOAD = ["tion"] - -TionLtApi = tion.tion_ns.class_("TionLtApi") -TionLedSwitchT = tion.tion_ns.class_("TionLedSwitch", switch.Switch) - -CONF_LED = "led" -CONF_HEATER_POWER = "heater_power" -CONF_AIRFLOW_COUNTER = "airflow_counter" -CONF_GATE_STATE = "gate_state" - - -def tion_lt_base_schema( - tion_class: MockObjClass, tion_api_class: MockObjClass, tion_base_schema: cv.Schema -): - """Declare base tion lt schema""" - return tion.tion_schema(tion_class, tion_api_class, tion_base_schema).extend( - { - cv.Optional(CONF_LED): switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(TionLedSwitchT.template(tion_class)), - cv.Optional(CONF_ICON, default="mdi:led-on"): cv.icon, - cv.Optional( - CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_CONFIG - ): cv.entity_category, - cv.Optional(CONF_INVERTED): cv.invalid( - "Inverted mode is not supported" - ), - } - ), - cv.Optional(CONF_HEATER_POWER): sensor.sensor_schema( - unit_of_measurement=UNIT_WATT, - accuracy_decimals=0, - device_class=DEVICE_CLASS_POWER, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_AIRFLOW_COUNTER): sensor.sensor_schema( - unit_of_measurement=UNIT_CUBIC_METER, - accuracy_decimals=2, - icon="mdi:weather-windy", - state_class=STATE_CLASS_TOTAL_INCREASING, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - } - ) - - -def tion_lt_schema(tion_class: MockObjClass, tion_base_schema: cv.Schema): - return tion_lt_base_schema(tion_class, TionLtApi, tion_base_schema).extend( - { - cv.Optional(CONF_GATE_STATE): binary_sensor.binary_sensor_schema( - device_class=DEVICE_CLASS_OPENING, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - } - ) - - -async def setup_tion_lt(config, component_reg): - """Setup tion lt component properties""" - var = await tion.setup_tion_core(config, component_reg) - await tion.setup_switch(config, CONF_LED, var.set_led, var) - await tion.setup_sensor(config, CONF_HEATER_POWER, var.set_heater_power) - await tion.setup_sensor(config, CONF_AIRFLOW_COUNTER, var.set_airflow_counter) - await tion.setup_binary_sensor(config, CONF_GATE_STATE, var.set_gate_state) - return var - - -CONF_FAN_SPEED = "fan_speed" -CONF_BUTTON_PRESETS = "button_presets" - -TionLtApiComponent = tion.tion_ns.class_( - "TionLtApiComponent", cg.Component, tion.TionApiComponent -) - -BUTTON_PRESETS_SCHEMA = cv.Schema( - { - cv.Required(CONF_TEMPERATURE): cv.All( - cv.ensure_list(cv.int_range(min=1, max=25)), cv.Length(min=3, max=3) - ), - cv.Required(CONF_FAN_SPEED): cv.All( - cv.ensure_list(cv.int_range(min=1, max=6)), cv.Length(min=3, max=3) - ), - } -) - -CONFIG_SCHEMA = tion.tion_schema_api(TionLtApiComponent, TionLtApi).extend( - { - cv.Optional(CONF_BUTTON_PRESETS): BUTTON_PRESETS_SCHEMA, - } -) - - -async def to_code(config: dict): - var = await tion.setup_tion_api(config, "lt") - if CONF_BUTTON_PRESETS in config: - button_presets = config[CONF_BUTTON_PRESETS] - - cg.add( - var.set_button_presets( - cg.StructInitializer( - "", - ("tmp", button_presets[CONF_TEMPERATURE]), - ("fan", button_presets[CONF_FAN_SPEED]), - ) - ) - ) diff --git a/components/tion_lt/climate/__init__.py b/components/tion_lt/climate/__init__.py deleted file mode 100644 index 9db72f6..0000000 --- a/components/tion_lt/climate/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from esphome.cpp_types import PollingComponent -from esphome.components import climate -from .. import tion_lt_schema, setup_tion_lt -from ... import tion # pylint: disable=relative-beyond-top-level - -CODEOWNERS = ["@dentra"] -AUTO_LOAD = ["tion_lt"] - -TionLtClimate = tion.tion_ns.class_("TionLtClimate", PollingComponent, climate.Climate) - -CONFIG_SCHEMA = tion_lt_schema(TionLtClimate, climate.CLIMATE_SCHEMA) - - -async def to_code(config): - """Code generation entry point""" - # pylint: disable=unused-variable - var = await setup_tion_lt(config, climate.register_climate) diff --git a/components/tion_lt/climate/tion_lt_climate.cpp b/components/tion_lt/climate/tion_lt_climate.cpp deleted file mode 100644 index 6d4ef5a..0000000 --- a/components/tion_lt/climate/tion_lt_climate.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include - -#include "esphome/core/log.h" -#include "tion_lt_climate.h" - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_lt_climate"; - -void TionLtClimate::dump_config() { - this->dump_settings(TAG, "Tion Lite"); - LOG_BINARY_SENSOR(" ", "Gate State", this->gate_state_); - this->dump_presets(TAG); -} - -void TionLtClimate::update_state(const TionState &state) { - if (!state.power_state) { - this->mode = climate::CLIMATE_MODE_OFF; - this->action = climate::CLIMATE_ACTION_OFF; - } else if (state.heater_state) { - this->mode = climate::CLIMATE_MODE_HEAT; - this->action = - state.is_heating(this->api_->traits()) ? climate::CLIMATE_ACTION_HEATING : climate::CLIMATE_ACTION_FAN; - } else { - this->mode = climate::CLIMATE_MODE_FAN_ONLY; - this->action = climate::CLIMATE_ACTION_OFF; - } - - this->current_temperature = state.current_temperature; - this->target_temperature = state.target_temperature; - this->set_fan_speed_(state.fan_speed); - this->publish_state(); - - if (this->gate_state_) { - this->gate_state_->publish_state(state.get_gate_state()); - } -} - -void TionLtClimate::control_climate_state(climate::ClimateMode mode, uint8_t fan_speed, float target_temperature, - TionGatePosition /*gate_position*/) { - auto *call = this->make_api_call(); - call->set_fan_speed(fan_speed); - if (!std::isnan(target_temperature)) { - call->set_target_temperature(target_temperature); - } - - if (mode == climate::CLIMATE_MODE_OFF) { - call->set_power_state(false); - } else if (mode == climate::CLIMATE_MODE_HEAT_COOL) { - call->set_power_state(true); - } else { - call->set_power_state(true); - call->set_heater_state(mode == climate::CLIMATE_MODE_HEAT); - } - - call->perform(); -} - -} // namespace tion -} // namespace esphome diff --git a/components/tion_lt/climate/tion_lt_climate.h b/components/tion_lt/climate/tion_lt_climate.h deleted file mode 100644 index 8ce955f..0000000 --- a/components/tion_lt/climate/tion_lt_climate.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "../tion_lt.h" -#include "../../tion/tion_climate_component.h" - -namespace esphome { -namespace tion { - -class TionLtClimate : public TionLtClimateComponent { - using TionState = dentra::tion::TionState; - using TionGatePosition = dentra::tion::TionGatePosition; - - public: - explicit TionLtClimate(TionLtApi *api, TionVPortType vport_type) : TionLtClimateComponent(api, vport_type) {} - - void dump_config() override; - - void update_state(const TionState &state) override; - - void set_gate_state(binary_sensor::BinarySensor *gate_state) { this->gate_state_ = gate_state; } - - void reset_filter() const { this->api_->reset_filter(this->state_); } - - void control_buzzer_state(bool state) { - auto *call = this->make_api_call(); - call->set_sound_state(state); - call->perform(); - } - - void control_led_state(bool state) { - auto *call = this->make_api_call(); - call->set_led_state(state); - call->perform(); - } - - void control_climate_state(climate::ClimateMode mode, uint8_t fan_speed, float target_temperature, - TionGatePosition gate_position) override; - - TionGatePosition get_gate_position() const override { return TionGatePosition::NONE; } - - void reset_errors() const { this->api_->reset_errors(this->state_); } - - protected: - binary_sensor::BinarySensor *gate_state_{}; -}; - -} // namespace tion - -} // namespace esphome diff --git a/components/tion_lt/tion_lt.cpp b/components/tion_lt/tion_lt.cpp deleted file mode 100644 index 54bf37a..0000000 --- a/components/tion_lt/tion_lt.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include "esphome/core/log.h" -#include "tion_lt.h" - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_lt"; - -} // namespace tion -} // namespace esphome diff --git a/components/tion_lt/tion_lt.h b/components/tion_lt/tion_lt.h deleted file mode 100644 index bb6061b..0000000 --- a/components/tion_lt/tion_lt.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "esphome/core/defines.h" - -#include "../tion-api/tion-api-lt.h" -#include "../tion/tion_api_component.h" - -namespace esphome { -namespace tion { - -using namespace dentra::tion; - -// class TionLtBaseComponent { -// public: -// TionLtBaseComponent(TionLtApi *api) {} - -// void control_state(bool power_state, bool heater_state, uint8_t fan_speed, int8_t target_temperature, bool buzzer, -// bool led); - -// protected: -// uint32_t request_id_{}; -// }; - -class TionLtApiComponent : public TionApiComponentBase { - public: - explicit TionLtApiComponent(TionLtApi *api, TionVPortType vport_type) : TionApiComponentBase(api, vport_type) {} - - void set_button_presets(const dentra::tion_lt::button_presets_t &button_presets) { - this->typed_api()->set_button_presets(button_presets); - } -}; - -} // namespace tion -} // namespace esphome diff --git a/components/tion_lt_ble/__init__.py b/components/tion_lt_ble/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/components/tion_lt/vport/tion_lt_vport.cpp b/components/tion_lt_ble/tion_lt_ble_vport.cpp similarity index 69% rename from components/tion_lt/vport/tion_lt_vport.cpp rename to components/tion_lt_ble/tion_lt_ble_vport.cpp index 7ae9afe..24e467f 100644 --- a/components/tion_lt/vport/tion_lt_vport.cpp +++ b/components/tion_lt_ble/tion_lt_ble_vport.cpp @@ -1,10 +1,10 @@ #include "esphome/core/log.h" -#include "tion_lt_vport.h" +#include "tion_lt_ble_vport.h" namespace esphome { namespace tion { -static const char *const TAG = "tion_lt_vport"; +static const char *const TAG = "tion_lt_ble_vport"; void TionLtBleVPort::dump_config() { TION_VPORT_BLE_LOG("Tion LT BLE"); } diff --git a/components/tion_lt/vport/tion_lt_vport.h b/components/tion_lt_ble/tion_lt_ble_vport.h similarity index 60% rename from components/tion_lt/vport/tion_lt_vport.h rename to components/tion_lt_ble/tion_lt_ble_vport.h index f8a44a6..9f0844c 100644 --- a/components/tion_lt/vport/tion_lt_vport.h +++ b/components/tion_lt_ble/tion_lt_ble_vport.h @@ -1,15 +1,15 @@ #pragma once -#include "../../tion-api/tion-api-lt.h" -#include "../../tion-api/tion-api-ble-lt.h" -#include "../../tion/tion_vport_ble.h" +#include "../tion-api/tion-api-lt.h" +#include "../tion-api/tion-api-ble-lt.h" +#include "../tion/tion_vport_ble.h" namespace esphome { namespace tion { using TionLtBleIO = esphome::tion::TionBleIO; -class TionLtBleVPort : public TionVPortBLEComponent { +class TionLtBleVPort : public TionVPortBLEComponent { public: TionLtBleVPort(TionLtBleIO *io) : TionVPortBLEComponent(io) {} diff --git a/components/tion_lt/vport/__init__.py b/components/tion_lt_ble/vport.py similarity index 65% rename from components/tion_lt/vport/__init__.py rename to components/tion_lt_ble/vport.py index bcedea1..b64d126 100644 --- a/components/tion_lt/vport/__init__.py +++ b/components/tion_lt_ble/vport.py @@ -1,6 +1,6 @@ import esphome.codegen as cg from esphome.const import PLATFORM_ESP32 -from ... import tion, vport # pylint: disable=relative-beyond-top-level +from .. import tion, vport # pylint: disable=relative-beyond-top-level CODEOWNERS = ["@dentra"] ESP_PLATFORMS = [PLATFORM_ESP32] @@ -11,8 +11,8 @@ TionLtBleVPort = tion.tion_ns.class_("TionLtBleVPort", cg.PollingComponent, vport.VPort) TionLtBleIO = tion.tion_ns.class_("TionLtBleIO") -CONFIG_SCHEMA = tion.tion_vport_ble_schema(TionLtBleVPort, TionLtBleIO) +CONFIG_SCHEMA = vport.vport_ble_schema(TionLtBleVPort, TionLtBleIO) async def to_code(config): - await tion.setup_tion_vport_ble(config) + await vport.setup_vport_ble(config) diff --git a/components/tion_o2/__init__.py b/components/tion_o2/__init__.py deleted file mode 100644 index d7ae01a..0000000 --- a/components/tion_o2/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.const import CONF_ID -from esphome.cpp_generator import MockObjClass - -# pylint: disable-next=relative-beyond-top-level -from .. import tion, vport - -CODEOWNERS = ["@dentra"] -AUTO_LOAD = ["tion"] - - -TionO2Api = tion.tion_ns.class_("TionO2Api") - - -def tion_o2_schema(tion_class: MockObjClass, tion_base_schema: cv.Schema): - return tion.tion_schema(tion_class, TionO2Api, tion_base_schema) - - -async def setup_tion_o2(config, compoent_reg): - """Code generation entry point""" - # pylint: disable-next=unused-variable - var = await tion.setup_tion_core(config, compoent_reg) - cg.add_build_flag("-DTION_MAX_FAN_SPEED=4") - - -TionO2ApiComponent = tion.tion_ns.class_( - "TionO2ApiComponent", cg.Component, tion.TionApiComponent -) - -CONFIG_SCHEMA = tion.tion_schema_api(TionO2ApiComponent, TionO2Api) - - -async def to_code(config: dict): - await tion.setup_tion_api(config, "o2") diff --git a/components/tion_o2/climate/__init__.py b/components/tion_o2/climate/__init__.py deleted file mode 100644 index 3333dca..0000000 --- a/components/tion_o2/climate/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -from esphome.components import climate -from esphome.cpp_types import PollingComponent - -# pylint: disable-next=relative-beyond-top-level -from ... import tion - -# pylint: disable-next=relative-beyond-top-level -from .. import setup_tion_o2, tion_o2_schema - -CODEOWNERS = ["@dentra"] -AUTO_LOAD = ["tion_o2"] - - -TionO2Climate = tion.tion_ns.class_("TionO2Climate", PollingComponent, climate.Climate) - -CONFIG_SCHEMA = tion_o2_schema(TionO2Climate, climate.CLIMATE_SCHEMA) - - -async def to_code(config): - """Code generation entry point""" - # pylint: disable-next=unused-variable - var = await setup_tion_o2(config, climate.register_climate) diff --git a/components/tion_o2/climate/tion_o2_climate.cpp b/components/tion_o2/climate/tion_o2_climate.cpp deleted file mode 100644 index 6affb52..0000000 --- a/components/tion_o2/climate/tion_o2_climate.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include "esphome/core/log.h" -#include "esphome/core/defines.h" - -#include "tion_o2_climate.h" - -#ifdef TION_ENABLE_ANTIFRIZE -#define TION_OPTION_STR_ANTIFRIZE "enabled" -#else -#define TION_OPTION_STR_ANTIFRIZE "disabled" -#endif - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_o2_climate"; - -void TionO2Climate::dump_config() { - this->dump_settings(TAG, "Tion O2"); - ESP_LOGCONFIG(" ", "Antifrize: %s", TION_OPTION_STR_ANTIFRIZE); - this->dump_presets(TAG); -} - -void TionO2Climate::update_state(const TionState &state) { - if (!state.power_state) { - this->mode = climate::CLIMATE_MODE_OFF; - this->action = climate::CLIMATE_ACTION_OFF; - } else if (state.heater_state) { - this->mode = climate::CLIMATE_MODE_HEAT; - this->action = this->mode == state.is_heating(this->api_->traits()) ? climate::CLIMATE_ACTION_HEATING - : climate::CLIMATE_ACTION_FAN; - } else { - this->mode = climate::CLIMATE_MODE_FAN_ONLY; - this->action = climate::CLIMATE_ACTION_FAN; - } - - this->current_temperature = state.current_temperature; - this->target_temperature = state.target_temperature; - this->set_fan_speed_(state.fan_speed); - this->publish_state(); -#ifdef USE_TION_PRODUCTIVITY - if (this->productivity_) { - this->productivity_->publish_state(state.productivity); - } -#endif -} - -void TionO2Climate::control_climate_state(climate::ClimateMode mode, uint8_t fan_speed, float target_temperature, - TionGatePosition gate_position) { - auto *call = this->make_api_call(); - call->set_fan_speed(fan_speed); - - if (!std::isnan(target_temperature)) { - call->set_target_temperature(target_temperature); - } - - if (mode == climate::CLIMATE_MODE_OFF) { - call->set_power_state(false); - } else if (mode == climate::CLIMATE_MODE_HEAT_COOL) { - call->set_power_state(true); - } else { - call->set_power_state(true); - call->set_heater_state(mode == climate::CLIMATE_MODE_HEAT); - } - - call->perform(); -} - -} // namespace tion -} // namespace esphome diff --git a/components/tion_o2/climate/tion_o2_climate.h b/components/tion_o2/climate/tion_o2_climate.h deleted file mode 100644 index 2c1e29a..0000000 --- a/components/tion_o2/climate/tion_o2_climate.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "../tion_o2.h" -#include "../../tion/tion_climate_component.h" - -namespace esphome { -namespace tion { - -class TionO2Climate : public TionClimateComponent { - using TionState = dentra::tion::TionState; - using TionGatePosition = dentra::tion::TionGatePosition; - - public: - explicit TionO2Climate(TionO2Api *api, TionVPortType vport_type) : TionClimateComponent(api, vport_type) {} - - void dump_config() override; - - void update_state(const TionState &state) override; - - TionGatePosition get_gate_position() const override { return TionGatePosition::NONE; } - - void reset_filter() { this->api_->reset_filter(this->state_); } - - void control_climate_state(climate::ClimateMode mode, uint8_t fan_speed, float target_temperature, - TionGatePosition gate_position) override; - - void control_buzzer_state(bool state) {} -}; - -} // namespace tion -} // namespace esphome diff --git a/components/tion_o2/tion_o2.cpp b/components/tion_o2/tion_o2.cpp deleted file mode 100644 index 8317c48..0000000 --- a/components/tion_o2/tion_o2.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "esphome/core/log.h" -#include "esphome/core/defines.h" - -#include "tion_o2.h" - -namespace esphome { -namespace tion { - -static const char *const TAG = "tion_o2"; - -} // namespace tion -} // namespace esphome diff --git a/components/tion_o2/tion_o2.h b/components/tion_o2/tion_o2.h deleted file mode 100644 index 9b0434b..0000000 --- a/components/tion_o2/tion_o2.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "../tion-api/tion-api-o2.h" -#include "../tion/tion_api_component.h" - -namespace esphome { -namespace tion { - -using TionO2Api = dentra::tion_o2::TionO2Api; - -// class TionO2 : public dentra::tion_o2::TionO2Api { -// public: -// TionO2() : dentra::tion_o2::TionO2Api() {} -// }; - -class TionO2ApiComponent : public TionApiComponentBase { - public: - explicit TionO2ApiComponent(TionO2Api *api, TionVPortType vport_type) : TionApiComponentBase(api, vport_type) {} -}; - -} // namespace tion -} // namespace esphome diff --git a/components/tion_o2_proxy/__init__.py b/components/tion_o2_proxy/__init__.py index 21a05a5..54f1a97 100644 --- a/components/tion_o2_proxy/__init__.py +++ b/components/tion_o2_proxy/__init__.py @@ -26,7 +26,7 @@ async def to_code(config): - logging.warning("%s is not supported at this moment", config[CONF_PLATFORM]) + logging.error("tion_o2_proxy is not supported at this moment") return prt = await vport.vport_get_var(config) diff --git a/components/tion_o2_uart/__init__.py b/components/tion_o2_uart/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/components/tion_o2_uart/__init__.py @@ -0,0 +1 @@ + diff --git a/components/tion_o2/vport/tion_o2_vport.cpp b/components/tion_o2_uart/tion_o2_uart_vport.cpp similarity index 60% rename from components/tion_o2/vport/tion_o2_vport.cpp rename to components/tion_o2_uart/tion_o2_uart_vport.cpp index e8387ac..af31563 100644 --- a/components/tion_o2/vport/tion_o2_vport.cpp +++ b/components/tion_o2_uart/tion_o2_uart_vport.cpp @@ -1,15 +1,12 @@ #include "esphome/core/log.h" #include "esphome/core/defines.h" -#ifdef USE_OTA -#include "esphome/components/ota/ota_component.h" -#endif -#include "tion_o2_vport.h" +#include "tion_o2_uart_vport.h" namespace esphome { namespace tion { -static const char *const TAG = "tion_o2_uart"; +static const char *const TAG = "tion_o2_uart_vport"; void TionO2UartVPort::dump_config() { VPORT_UART_LOG("Tion O2 UART"); } diff --git a/components/tion_o2/vport/tion_o2_vport.h b/components/tion_o2_uart/tion_o2_uart_vport.h similarity index 68% rename from components/tion_o2/vport/tion_o2_vport.h rename to components/tion_o2_uart/tion_o2_uart_vport.h index 5663d97..7e9daa6 100644 --- a/components/tion_o2/vport/tion_o2_vport.h +++ b/components/tion_o2_uart/tion_o2_uart_vport.h @@ -2,17 +2,16 @@ #include "esphome/core/defines.h" -#include "../../tion/tion_controls.h" -#include "../../tion/tion_vport_uart.h" -#include "../../tion-api/tion-api-o2.h" -#include "../../tion-api/tion-api-uart-o2.h" +#include "../tion/tion_vport_uart.h" +#include "../tion-api/tion-api-o2.h" +#include "../tion-api/tion-api-uart-o2.h" namespace esphome { namespace tion { using TionO2UartIO = TionUartIO; -class TionO2UartVPort : public TionVPortUARTComponent { +class TionO2UartVPort : public TionVPortUARTComponent { public: explicit TionO2UartVPort(TionO2UartIO *io) : TionVPortUARTComponent(io) {} diff --git a/components/tion_o2/vport/__init__.py b/components/tion_o2_uart/vport.py similarity index 94% rename from components/tion_o2/vport/__init__.py rename to components/tion_o2_uart/vport.py index b816168..38bf7ef 100644 --- a/components/tion_o2/vport/__init__.py +++ b/components/tion_o2_uart/vport.py @@ -2,7 +2,7 @@ import esphome.config_validation as cv # pylint: disable-next=relative-beyond-top-level -from ... import tion, vport +from .. import tion, vport AUTO_LOAD = ["vport", "tion"] diff --git a/tion.yaml.j2 b/tion.yaml.j2 index ceefe13..a3275d0 100644 --- a/tion.yaml.j2 +++ b/tion.yaml.j2 @@ -73,10 +73,6 @@ packages: {{ "" if enable_energy == "true" else "# " -}} - packages/tion_energy.yaml {%- endif %} - {%- if type == "3s" and port == "uart" %} - ## Comment to disable uart proxy support. - - packages/tion_3s_proxy.yaml - {%- endif %} {%- if type == "o2" and port == "uart" %} ## Unomment to enable uart proxy support. # - packages/tion_o2_proxy.yaml @@ -94,6 +90,8 @@ packages: {%- if type == "3s" and port == "uart" %} ## Change to packages/esp32.yaml if you want to use ESP32. - packages/esp8266.yaml + ## Comment to disable uart proxy support. + - packages/tion_3s_proxy.yaml {%- endif %} {%- if type == "o2" and port == "uart" %} # Change to esp32_s2.yaml/esp32_c3.yaml/esp32_s3.yaml to use with ESP32-S2/C3/S3 chip. @@ -153,7 +151,7 @@ wifi: # Virtual port configuration vport: - - platform: tion_{{ type }}{{ "_uart" if port == "uart" and type != "o2" else "" }} + - platform: tion_{{ type }}_{{port}} id: tion_{{ port }}_vport {%- if port == "ble" %} ble_client_id: tion_{{ port }}_client @@ -178,147 +176,169 @@ vport: {%- endif %} # Main climate component configuration. +tion: + type: {{ type }} + id: tion_api + # Optional, use vport_id with mnultiple breezers. + vport_id: tion_{{ port }}_vport + # Optional, How often query device state. Default: 15s. + update_interval: {{ "15s" if port == "uart" else "60s" }} + # Optional, Timeout to combine update operations. Default: 200ms. + batch_timeout: 200ms + # Optional, Timeout to enable state problem sensor. Must be less than update_interval. Default: 3s. + state_timeout: {{ "3s" if port == "uart" else "5s" }} + # # Optional, Boost time initialized at startup. Default: 10min. + # boost_time: 10min + # Optional, Updates sensor values on any state response or only if they have been changed. Default: true. + force_update: false + + # Optional, Enable presets. Default: (no presets) + # You can define any number of presets with any name. + # In Home assistant climate entity presets with names home, away, boost, comfort, + # eco, sleep and activity are automatically translated to your native language and assign icons. + # Preset configuration: + # name_of_your_preset: + # fan_speed: 1 # remove or set to 0 to use current state + # temperature: 16 # remove or set to 0 to use current state + # heater: False # remove to use current state + # power: True # remove to use current state +{%- if type in ["3s", "4s"] %} + # gate_position: outdoor # remove to use current state, see below for available values + # Available gate_position: + # * outdoor - set gate position to outdoor air intake (3S and 4S only) + # * indoor - set gate position to indoor air intake (3S and 4S only) +{%- endif %} +{%- if type == "3s" %} + # * mixed - set gate position to mixed air intake (3S only) +{%- endif %} + # Uncoment next 4 lines to enable example presets. + # presets: + # home: { fan_speed: 2, target_temperature: 16, heater: False } + # away: { fan_speed: 1, target_temperature: 10, heater: False } + # sleep: { fan_speed: 1, target_temperature: 18 } + climate: - - id: !extend tion_climate - # Required, the name of climate entity - name: None # use friendly_name - # Optional, virtual port id. Default: autodetect - vport_id: tion_{{ port }}_vport - # Optional, Temperatire of air before heater, °C (celsius). - outdoor_temperature: - name: "Outdoor Temperature" - {%- if type != "o2" %} - # Optional, Buzzer control switch. - buzzer: - name: "Buzzer" - {%- endif %} - {%- if type == "4s" %} - # Optional, Led control switch. - led: - name: "Led" - {%- endif %} - {%- if type not in ["3s", "o2"] %} - # Optional, Heater power, W (watt). - heater_power: - name: "Heater power" - # Optional, Total airflow counter, m³ (cubic meters). - #airflow_counter: - # name: "Airflow counter" - {%- endif %} - {%- if type != "3s" %} - # Optional, Total work time counter, seconds. - #work_time: - # name: "Work Time" - {%- endif %} - {%- if type == "3s" %} + - platform: tion + name: # use friendly_name + +sensor: + # Temperatire of air before heater, °C (celsius). + - platform: tion + type: outdoor_temperature + name: Outdoor Temperature +{%- if type in ["4s", "lt"] %} + # Optional, Heater power, W (watt). + - platform: tion + type: heater_power + name: Heater power +{%- endif %} + # Current productivity, m³/h (cubic meters per hour). + - platform: tion + type: productivity + name: Productivity + # Filter time left counter. + - platform: tion + type: filter_time_left + name: Filter Time Left + # Boost time left sensor. + - platform: tion + type: boost_time_left + name: Boost Time Left + +switch: +{%- if type != "o2" %} + # Buzzer control switch. + - platform: tion + type: sound + name: Buzzer +{%- endif %} +{%- if type == "4s" %} + # Led control switch. + - platform: tion + type: led + name: Led +{%- endif %} +{%- if type == "4s" %} + # Recirculation control switch. + - platform: tion + type: recirculation + name: Recirculation +{%- endif %} + # Boost/Turbo switch. + - platform: tion + type: boost + name: Boost + +number: + # Configure boost time. + - platform: tion + type: boost_time + name: Boost Time + initial_value: 20min + restore_value: true + +select: +{%- if type == "3s" %} # Optional, Air Intake control. - air_intake: - name: "Air Intake" - {%- endif %} - {%- if type == "4s" %} - # Optional, Recirculation control switch. - recirculation: - name: "Recirculation" - {%- endif %} - {%- if type == "lt" %} - # Optional, Gate state control binary sensor. - gate_state: - name: "Gate State" - {%- endif %} - # Optional, Current productivity, m³/h (cubic meters per hour). - productivity: - name: "Productivity" - # Optional, Filter time left counter, days. - filter_time_left: - name: "Filter Time Left" - # Optional, Filter warning state. - filter_warnout: - name: "Filter Warnout" - # Optional, Tion firmware version. - version: - name: "Version" - # Optional, Enable presets. Default: (no presets) - # Available configurable presets: home, away, boost, comfort, eco, sleep, activity. - # Defaults: - # home : { fan_speed: 2, target_temperature: 20, gate_position: auto, mode: heat } - # away : { fan_speed: 1, target_temperature: 10, gate_position: auto, mode: fan_only } - # boost : { fan_speed: 6, target_temperature: 10, gate_position: auto, mode: fan_only } - # comfort : { fan_speed: 2, target_temperature: 23, gate_position: auto, mode: heat } - # eco : { fan_speed: 1, target_temperature: 16, gate_position: auto, mode: heat } - # sleep : { fan_speed: 1, target_temperature: 18, gate_position: auto, mode: heat } - # activity: { fan_speed: 3, target_temperature: 18, gate_position: auto, mode: heat } - # Boost is a special preset that runs for a period defined by boost_time and then switches back to the previous preset. - # Available mode: - # * off - define but disable preset - # * heat - enable heater - # * fan_only - fan only - # * heat_cool - do not change, use current - # Available gate_position (this option is optional and only if supports by your breezer): - # * auto - use current gate position - {%- if type in ["3s", "4s"] %} - # * outdoor - set gate position to outdoor air intake (3S and 4S only) - # * indoor - set gate position to indoor air intake (3S and 4S only) - {%- endif %} - {%- if type == "3s" %} - # * mixed - set gate position to mixed air intake (3S only) - {%- endif %} - presets: - # enable "home" preset and override default settings - home: { fan_speed: 2, target_temperature: 16, gate_position: outdoor, mode: heat } - # enable "away" preset with default settings - away: - # enable "boost" preset with default settings - boost: - # enable "sleep" preset and override default settings - sleep: { fan_speed: 1, target_temperature: 18, gate_position: outdoor, mode: heat } - # Optional, configure boost time - boost_time: - name: "Boost Time" - # Optional, display remaining boost time - boost_time_left: - name: "Boost Time Left" - # Optional, How often query device state. Default: 60s. - update_interval: {{ "30s" if port == "uart" else "60s" }} - {%- if type != "o2" %} - # Optional, reset filter button - reset_filter: - name: "Reset Filter" - # Optional, reset filter confirmation. When off then reset_filter will not work. - reset_filter_confirm: - name: "Reset Filter Confirm" - {%- endif %} - # Optional, Informs about get state problem from breezer. - state_warnout: - name: "State Warnout" - # Optional, Timeout when state_warnout will be informed. Default: 3s. - state_timeout: 3s - # Optional, Timeout to combine update operations. Default: 200ms. - batch_timeout: 200ms - # Optional, Reports Tion's errors (EC) and warnings (WS). - errors: - name: "Errors" + - platform: tion + type: air_intake + name: Air Intake +{%- endif %} -{%- if type == "3s" and port == "ble" %} +binary_sensor: + # Informs about state receiving problem from the breezer. + - platform: tion + type: state + name: State Warnout +{%- if type == "lt" %} + # Gate state control. + - platform: tion + type: gate + name: Gate State +{%- endif %} + # Filter warning state. + - platform: tion + type: filter + name: Filter Warnout + +text_sensor: + # Tion firmware version. + - platform: tion + type: firmware_version + name: Version + # Reports Tion's errors (EC) and warnings (WS). + - platform: tion + type: errors + name: Errors button: +{%- if type != "o2" %} + # Reset filter button. + - platform: tion + type: reset_filter + name: Reset Filter + # Reset filter confirmation. When off then reset_filter will not work. + confirm: + name: Reset Filter Confirm +{%- endif %} +{%- if type == "3s" and port == "ble" %} - platform: template - name: "Pair" + name: Pair + icon: mdi:bluetooth-connect on_press: lambda: id(tion_{{ port }}_vport).pair(); entity_category: config ## Uncomment next 5 lines to enable switch for enable reset pair functionality. # - platform: template - # name: "Reset Pair" + # name: Reset Pair + # icon: mdi:bluetooth-off # on_press: # lambda: id(tion_{{ port }}_vport).reset_pair(); # entity_category: config {%- endif %} - {%- if type in ["4s", "lt"] %} - ## Uncomment if you plan to use reset errors/warnings functionality. -## This feature is experimental and may partially work or no work as expected. -#button: +## This feature is experimental and may partially work or not to work as expected. # - platform: template # name: "Reset Errors" # on_press: