From 2150278cc70eeb61c7d301530f7c21b04a2c9e39 Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:30:54 +0100 Subject: [PATCH 01/12] Split HRU -> ECO 350 and ECO --- .../ithodaalderop/binary_sensor.py | 22 +++++-- custom_components/ithodaalderop/const.py | 22 +++---- .../ithodaalderop/definitions.py | 65 ++++++++++++++++++- custom_components/ithodaalderop/manifest.json | 2 +- custom_components/ithodaalderop/sensor.py | 57 +++++++++------- .../ithodaalderop/translations/en.json | 11 +++- .../ithodaalderop/translations/nl.json | 13 +++- 7 files changed, 140 insertions(+), 52 deletions(-) diff --git a/custom_components/ithodaalderop/binary_sensor.py b/custom_components/ithodaalderop/binary_sensor.py index 3aaeee6..f9c3d2b 100644 --- a/custom_components/ithodaalderop/binary_sensor.py +++ b/custom_components/ithodaalderop/binary_sensor.py @@ -24,7 +24,8 @@ ) from .definitions import ( AUTOTEMPBINARYSENSORS, - NONCVEBINARYSENSORS, + HRU350BINARYSENSORS, + HRUECOBINARYSENSORS, IthoBinarySensorEntityDescription, ) @@ -41,14 +42,21 @@ async def async_setup_entry( return sensors = [] - if config_entry.data[CONF_ADDON_TYPE] == "noncve": - for description in NONCVEBINARYSENSORS: - description.key = f"{MQTT_BASETOPIC["noncve"]}/{MQTT_STATETOPIC["noncve"]}" - sensors.append(IthoBinarySensor(description, config_entry)) - if config_entry.data[CONF_ADDON_TYPE] == "autotemp": for description in AUTOTEMPBINARYSENSORS: - description.key = f"{MQTT_BASETOPIC["autotemp"]}/{MQTT_STATETOPIC["autotemp"]}" + description.key = ( + f"{MQTT_BASETOPIC["autotemp"]}/{MQTT_STATETOPIC["autotemp"]}" + ) + sensors.append(IthoBinarySensor(description, config_entry)) + + if config_entry.data[CONF_ADDON_TYPE] == "hru350": + for description in HRU350BINARYSENSORS: + description.key = f"{MQTT_BASETOPIC["hru350"]}/{MQTT_STATETOPIC["hru350"]}" + sensors.append(IthoBinarySensor(description, config_entry)) + + if config_entry.data[CONF_ADDON_TYPE] == "hrueco": + for description in HRU350BINARYSENSORS: + description.key = f"{MQTT_BASETOPIC["hrueco"]}/{MQTT_STATETOPIC["hrueco"]}" sensors.append(IthoBinarySensor(description, config_entry)) async_add_entities(sensors) diff --git a/custom_components/ithodaalderop/const.py b/custom_components/ithodaalderop/const.py index b9121c9..2c89975 100644 --- a/custom_components/ithodaalderop/const.py +++ b/custom_components/ithodaalderop/const.py @@ -9,17 +9,13 @@ MANUFACTURER = "Itho Daalderop" ADDON_TYPES = { - "noncve": "HRU", - "cve": "CVE", "autotemp": "Autotemp", + "cve": "CVE", + "hru350": "HRU ECO 350", + "hrueco": "HRU ECO", "wpu": "WPU", } -CVE_TYPES = { - "noncve": ["noncve", "mdi:fan"], - "cve": ["cve", "mdi:fan"], -} - UNITTYPE_ICONS = { "%": "mdi:percent-outline", "hum": "mdi:water-percent", @@ -29,7 +25,8 @@ MQTT_BASETOPIC = { "autotemp": "ithotemp", "cve": "ithocve", - "noncve": "ithohru", + "hru350": "ithohru", + "hrueco": "ithohru", "wpu": "ithowpu", } @@ -37,7 +34,8 @@ "autotemp": "ithostatus", "cve": "ithostatus", "last_cmd": "lastcmd", - "noncve": "ithostatus", + "hru350": "ithostatus", + "hrueco": "ithostatus", "remote": "remotesinfo", "wpu": "ithostatus", } @@ -86,7 +84,7 @@ 5: "Manual operation", } -NONCVE_ACTUAL_MODE = { +HRU_ACTUAL_MODE = { 1: "low", 2: "medium", 3: "high", @@ -97,13 +95,13 @@ # Based on user-experience. No codelist availble in the Itho Servicetool # For any additions/feedback, please create an issue in the repo of the integration -NONCVE_GLOBAL_FAULT_CODE = { +HRU_GLOBAL_FAULT_CODE = { 0: "No error", 7: "Filters dirty", 11: "(External) Sensor error", } -NONCVE_RH_ERROR_CODE = { +HRU_RH_ERROR_CODE = { 239: "Not Available", 240: "Shorted Sensor", 241: "Open Sensor", diff --git a/custom_components/ithodaalderop/definitions.py b/custom_components/ithodaalderop/definitions.py index 4221705..a98210d 100644 --- a/custom_components/ithodaalderop/definitions.py +++ b/custom_components/ithodaalderop/definitions.py @@ -204,7 +204,7 @@ class IthoBinarySensorEntityDescription(BinarySensorEntityDescription): ), ) -NONCVEBINARYSENSORS: tuple[IthoBinarySensorEntityDescription, ...] = ( +HRU350BINARYSENSORS: tuple[IthoBinarySensorEntityDescription, ...] = ( IthoBinarySensorEntityDescription( json_field="Bypass position", translation_key="bypass_position", @@ -215,7 +215,7 @@ class IthoBinarySensorEntityDescription(BinarySensorEntityDescription): ), ) -NONCVESENSORS: tuple[IthoSensorEntityDescription, ...] = ( +HRU350SENSORS: tuple[IthoSensorEntityDescription, ...] = ( IthoSensorEntityDescription( json_field="Actual Mode", translation_key="actual_mode", @@ -298,6 +298,67 @@ class IthoBinarySensorEntityDescription(BinarySensorEntityDescription): ), ) +HRUECOBINARYSENSORS: tuple[IthoBinarySensorEntityDescription, ...] = ( + IthoBinarySensorEntityDescription( + json_field="Bypass position (pulse)", + translation_key="bypass_position", + device_class=BinarySensorDeviceClass.OPENING, + entity_category=EntityCategory.DIAGNOSTIC, + icon_off="mdi:valve-closed", + icon_on="mdi:valve-open", + ), +) + +HRUECOSENSORS: tuple[IthoSensorEntityDescription, ...] = ( + IthoSensorEntityDescription( + json_field="Airfilter counter", + translation_key="airfilter_counter", + native_unit_of_measurement=UnitOfTime.HOURS, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_category=EntityCategory.DIAGNOSTIC, + icon="mdi:counter", + ), + IthoSensorEntityDescription( + json_field="Drain fan speed (rpm)", + translation_key="drain_fan_speed", + native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + IthoSensorEntityDescription( + json_field="Temp of exhaust air (°C)", + translation_key="actual_exhaust_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="room temp (°C)", + translation_key="room_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Status", + translation_key="status", + ), + IthoSensorEntityDescription( + json_field="Supply fan speed (rpm)", + translation_key="supply_fan_speed", + native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + IthoSensorEntityDescription( + json_field="Temp of supply air (°C)", + translation_key="actual_supply_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), +) + WPUSENSORS: tuple[IthoSensorEntityDescription, ...] = ( IthoSensorEntityDescription( json_field="Boiler pump (%)", diff --git a/custom_components/ithodaalderop/manifest.json b/custom_components/ithodaalderop/manifest.json index 1ea46c6..76e3e04 100644 --- a/custom_components/ithodaalderop/manifest.json +++ b/custom_components/ithodaalderop/manifest.json @@ -14,5 +14,5 @@ "mqtt": ["ithohru/#"], "requirements": [ ], - "version": "2.1.0" + "version": "2.2.0-b1" } diff --git a/custom_components/ithodaalderop/sensor.py b/custom_components/ithodaalderop/sensor.py index 1a6bd35..2b60456 100644 --- a/custom_components/ithodaalderop/sensor.py +++ b/custom_components/ithodaalderop/sensor.py @@ -22,21 +22,23 @@ AUTOTEMP_MODE, CONF_ADDON_TYPE, DOMAIN, + HRU_ACTUAL_MODE, + HRU_GLOBAL_FAULT_CODE, + HRU_RH_ERROR_CODE, MANUFACTURER, MQTT_BASETOPIC, MQTT_STATETOPIC, - NONCVE_ACTUAL_MODE, - NONCVE_GLOBAL_FAULT_CODE, - NONCVE_RH_ERROR_CODE, UNITTYPE_ICONS, WPU_STATUS, ) + from .definitions import ( AUTOTEMPROOMSENSORS, AUTOTEMPSENSORS, CVESENSORS, LASTCMDSENSORS, - NONCVESENSORS, + HRU350SENSORS, + HRUECOSENSORS, WPUSENSORS, IthoSensorEntityDescription, ) @@ -97,17 +99,35 @@ async def async_setup_entry( return sensors = [] - if config_entry.data[CONF_ADDON_TYPE] == "noncve": - for description in NONCVESENSORS: - description.key = f"{MQTT_BASETOPIC["noncve"]}/{MQTT_STATETOPIC["noncve"]}" - sensors.append(IthoSensorFan(description, config_entry)) + if config_entry.data[CONF_ADDON_TYPE] == "autotemp": + for description in list(AUTOTEMPSENSORS): + description.key = ( + f"{MQTT_BASETOPIC["autotemp"]}/{MQTT_STATETOPIC["autotemp"]}" + ) + sensors.append(IthoSensorAutotemp(description, config_entry)) + + for description in _create_autotemprooms(config_entry): + description.key = ( + f"{MQTT_BASETOPIC["autotemp"]}/{MQTT_STATETOPIC["autotemp"]}" + ) + sensors.append(IthoSensorAutotempRoom(description, config_entry)) if config_entry.data[CONF_ADDON_TYPE] == "cve": for description in CVESENSORS: description.key = f"{MQTT_BASETOPIC["cve"]}/{MQTT_STATETOPIC["cve"]}" sensors.append(IthoSensorFan(description, config_entry)) - if config_entry.data[CONF_ADDON_TYPE] in ["cve", "noncve"]: + if config_entry.data[CONF_ADDON_TYPE] == "hru350": + for description in HRU350SENSORS: + description.key = f"{MQTT_BASETOPIC["hru350"]}/{MQTT_STATETOPIC["hru350"]}" + sensors.append(IthoSensorFan(description, config_entry)) + + if config_entry.data[CONF_ADDON_TYPE] == "hrueco": + for description in HRUECOSENSORS: + description.key = f"{MQTT_BASETOPIC["hrueco"]}/{MQTT_STATETOPIC["hrueco"]}" + sensors.append(IthoSensorFan(description, config_entry)) + + if config_entry.data[CONF_ADDON_TYPE] in ["cve", "hru350", "hrueco"]: for description in _create_remotes(config_entry): description.key = f"{MQTT_BASETOPIC[config_entry.data[CONF_ADDON_TYPE]]}/{MQTT_STATETOPIC["remote"]}" sensors.append(IthoSensorCO2Remote(description, config_entry)) @@ -121,19 +141,6 @@ async def async_setup_entry( description.key = f"{MQTT_BASETOPIC["wpu"]}/{MQTT_STATETOPIC["wpu"]}" sensors.append(IthoSensorWPU(description, config_entry)) - if config_entry.data[CONF_ADDON_TYPE] == "autotemp": - for description in list(AUTOTEMPSENSORS): - description.key = ( - f"{MQTT_BASETOPIC["autotemp"]}/{MQTT_STATETOPIC["autotemp"]}" - ) - sensors.append(IthoSensorAutotemp(description, config_entry)) - - for description in _create_autotemprooms(config_entry): - description.key = ( - f"{MQTT_BASETOPIC["autotemp"]}/{MQTT_STATETOPIC["autotemp"]}" - ) - sensors.append(IthoSensorAutotempRoom(description, config_entry)) - async_add_entities(sensors) @@ -345,7 +352,7 @@ def message_received(message): value = payload[json_field] if json_field == "Actual Mode": self._extra_state_attributes = {"Code": value} - value = NONCVE_ACTUAL_MODE.get(value, "Unknown mode") + value = HRU_ACTUAL_MODE.get(value, "Unknown mode") if json_field == "Airfilter counter": _last_maintenance = "" @@ -378,7 +385,7 @@ def message_received(message): if json_field == "Global fault code": _description = "" if str(value).isnumeric(): - _description = NONCVE_GLOBAL_FAULT_CODE.get( + _description = HRU_GLOBAL_FAULT_CODE.get( int(value), "Unknown fault code" ) @@ -389,7 +396,7 @@ def message_received(message): if json_field == "Highest received RH value (%RH)": _error_description = "" if isinstance(value, (int, float)) and float(value) > 100: - _error_description = NONCVE_RH_ERROR_CODE.get( + _error_description = HRU_RH_ERROR_CODE.get( int(value), "Unknown error" ) value = None diff --git a/custom_components/ithodaalderop/translations/en.json b/custom_components/ithodaalderop/translations/en.json index d68fcd2..2118dc7 100644 --- a/custom_components/ithodaalderop/translations/en.json +++ b/custom_components/ithodaalderop/translations/en.json @@ -51,6 +51,9 @@ "cv_return_temp": { "name": "CV Return Temperature" }, + "drain_fan_speed": { + "name": "Drain Fan Speed" + }, "empty_battery": { "name": "Empty Battery" }, @@ -117,6 +120,9 @@ "status": { "name": "Status" }, + "supply_fan_speed": { + "name": "Supply Fan Speed" + }, "temp": { "name": "Temperature" }, @@ -212,9 +218,10 @@ "selector": { "addonselect": { "options": { - "noncve": "Non-CVE (HRU)", - "cve": "CVE (ECO box)", "autotemp": "Autotemp floor heating", + "cve": "CVE (ECO box)", + "hru350": "HRU ECO 350", + "hrueco": "HRU ECO", "wpu": "WPU heatpump" } } diff --git a/custom_components/ithodaalderop/translations/nl.json b/custom_components/ithodaalderop/translations/nl.json index d792590..b49f3ba 100644 --- a/custom_components/ithodaalderop/translations/nl.json +++ b/custom_components/ithodaalderop/translations/nl.json @@ -51,6 +51,9 @@ "cv_return_temp": { "name": "CV Afvoer Temperatuur" }, + "drain_fan_speed": { + "name": "Afvoerentilator Snelheid" + }, "empty_battery": { "name": "Battery Leeg" }, @@ -117,6 +120,9 @@ "status": { "name": "Status" }, + "supply_fan_speed": { + "name": "Toevoerentilator Snelheid" + }, "temp": { "name": "Temperatuur" }, @@ -206,10 +212,11 @@ "selector": { "addonselect": { "options": { - "wpu": "WPU Warmtepomp", "autotemp": "Autotemp vloerverwarming", - "noncve": "Non-CVE (HRU)", - "cve": "CVE (ECO box)" + "cve": "CVE (ECO box)", + "hru350": "HRU ECO 350", + "hrueco": "HRU ECO", + "wpu": "WPU Warmtepomp" } } } From bd190522db54a2490719ae95b9c48a860404450f Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:33:25 +0100 Subject: [PATCH 02/12] Fix binary_sensor + ruff --- custom_components/ithodaalderop/binary_sensor.py | 2 +- custom_components/ithodaalderop/sensor.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/custom_components/ithodaalderop/binary_sensor.py b/custom_components/ithodaalderop/binary_sensor.py index f9c3d2b..8148bfd 100644 --- a/custom_components/ithodaalderop/binary_sensor.py +++ b/custom_components/ithodaalderop/binary_sensor.py @@ -55,7 +55,7 @@ async def async_setup_entry( sensors.append(IthoBinarySensor(description, config_entry)) if config_entry.data[CONF_ADDON_TYPE] == "hrueco": - for description in HRU350BINARYSENSORS: + for description in HRUECOBINARYSENSORS: description.key = f"{MQTT_BASETOPIC["hrueco"]}/{MQTT_STATETOPIC["hrueco"]}" sensors.append(IthoBinarySensor(description, config_entry)) diff --git a/custom_components/ithodaalderop/sensor.py b/custom_components/ithodaalderop/sensor.py index 2b60456..be130c7 100644 --- a/custom_components/ithodaalderop/sensor.py +++ b/custom_components/ithodaalderop/sensor.py @@ -31,14 +31,13 @@ UNITTYPE_ICONS, WPU_STATUS, ) - from .definitions import ( AUTOTEMPROOMSENSORS, AUTOTEMPSENSORS, CVESENSORS, - LASTCMDSENSORS, HRU350SENSORS, HRUECOSENSORS, + LASTCMDSENSORS, WPUSENSORS, IthoSensorEntityDescription, ) From 21bd83e61b2be67a34323a380d180f04582349bb Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:49:27 +0100 Subject: [PATCH 03/12] Fix typos --- custom_components/ithodaalderop/translations/nl.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/ithodaalderop/translations/nl.json b/custom_components/ithodaalderop/translations/nl.json index b49f3ba..c29d57e 100644 --- a/custom_components/ithodaalderop/translations/nl.json +++ b/custom_components/ithodaalderop/translations/nl.json @@ -52,7 +52,7 @@ "name": "CV Afvoer Temperatuur" }, "drain_fan_speed": { - "name": "Afvoerentilator Snelheid" + "name": "Afvoerventilator Snelheid" }, "empty_battery": { "name": "Battery Leeg" @@ -121,7 +121,7 @@ "name": "Status" }, "supply_fan_speed": { - "name": "Toevoerentilator Snelheid" + "name": "Toevoerventilator Snelheid" }, "temp": { "name": "Temperatuur" From dc66f751c4fc689f13cdd9d919ab3b94eec5331e Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Thu, 5 Dec 2024 21:49:53 +0100 Subject: [PATCH 04/12] Refactor HRU split - Now with extra config form --- custom_components/ithodaalderop/__init__.py | 1 + .../ithodaalderop/binary_sensor.py | 15 +- .../ithodaalderop/config_flow.py | 272 ++++++++++++------ custom_components/ithodaalderop/const.py | 18 +- custom_components/ithodaalderop/manifest.json | 2 +- custom_components/ithodaalderop/sensor.py | 29 +- .../ithodaalderop/translations/en.json | 18 +- .../ithodaalderop/translations/nl.json | 16 +- 8 files changed, 252 insertions(+), 119 deletions(-) diff --git a/custom_components/ithodaalderop/__init__.py b/custom_components/ithodaalderop/__init__.py index ced507f..e4f1cee 100644 --- a/custom_components/ithodaalderop/__init__.py +++ b/custom_components/ithodaalderop/__init__.py @@ -1,4 +1,5 @@ """Init package.""" + from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant diff --git a/custom_components/ithodaalderop/binary_sensor.py b/custom_components/ithodaalderop/binary_sensor.py index 8148bfd..c2ca0d7 100644 --- a/custom_components/ithodaalderop/binary_sensor.py +++ b/custom_components/ithodaalderop/binary_sensor.py @@ -17,6 +17,7 @@ _LOGGER, ADDON_TYPES, CONF_ADDON_TYPE, + CONF_HRU_DEVICE, DOMAIN, MANUFACTURER, MQTT_BASETOPIC, @@ -49,14 +50,14 @@ async def async_setup_entry( ) sensors.append(IthoBinarySensor(description, config_entry)) - if config_entry.data[CONF_ADDON_TYPE] == "hru350": - for description in HRU350BINARYSENSORS: - description.key = f"{MQTT_BASETOPIC["hru350"]}/{MQTT_STATETOPIC["hru350"]}" - sensors.append(IthoBinarySensor(description, config_entry)) + if config_entry.data[CONF_ADDON_TYPE] == "noncve": + if config_entry.data[CONF_HRU_DEVICE] == "hru_eco": + hru_sensors = HRUECOBINARYSENSORS + if config_entry.data[CONF_HRU_DEVICE] == "hru_eco_350": + hru_sensors = HRU350BINARYSENSORS - if config_entry.data[CONF_ADDON_TYPE] == "hrueco": - for description in HRUECOBINARYSENSORS: - description.key = f"{MQTT_BASETOPIC["hrueco"]}/{MQTT_STATETOPIC["hrueco"]}" + for description in hru_sensors: + description.key = f"{MQTT_BASETOPIC["noncve"]}/{MQTT_STATETOPIC["noncve"]}" sensors.append(IthoBinarySensor(description, config_entry)) async_add_entities(sensors) diff --git a/custom_components/ithodaalderop/config_flow.py b/custom_components/ithodaalderop/config_flow.py index 02d215b..49b29e2 100644 --- a/custom_components/ithodaalderop/config_flow.py +++ b/custom_components/ithodaalderop/config_flow.py @@ -3,6 +3,7 @@ Author: Jasper Slits """ + from collections.abc import Mapping from typing import Any @@ -22,12 +23,14 @@ CONF_AUTOTEMP_ROOM6, CONF_AUTOTEMP_ROOM7, CONF_AUTOTEMP_ROOM8, + CONF_HRU_DEVICE, CONF_REMOTE_1, CONF_REMOTE_2, CONF_REMOTE_3, CONF_REMOTE_4, CONF_REMOTE_5, DOMAIN, + HRU_DEVICES, ) @@ -51,115 +54,218 @@ async def async_step_user(self, user_input: Mapping[str, Any] | None = None): """Configure main step.""" if user_input is not None: self.config.update(user_input) - if user_input[CONF_ADDON_TYPE] in ["cve", "noncve"]: - return await self.async_step_remotes() if user_input[CONF_ADDON_TYPE] == "autotemp": return await self.async_step_rooms() - await self.async_set_unique_id(f"itho_wifi_addon_{self.config[CONF_ADDON_TYPE]}") + if user_input[CONF_ADDON_TYPE] == "cve": + return await self.async_step_remotes() + if user_input[CONF_ADDON_TYPE] == "noncve": + return await self.async_step_hru_device() + + await self.async_set_unique_id( + f"itho_wifi_addon_{self.config[CONF_ADDON_TYPE]}" + ) self._abort_if_unique_id_configured() return self.async_create_entry( - title="Itho WiFi Add-on for " + ADDON_TYPES[self.config[CONF_ADDON_TYPE]], - data=user_input + title="Itho WiFi Add-on for " + + ADDON_TYPES[self.config[CONF_ADDON_TYPE]], + data=user_input, ) options = list(ADDON_TYPES.keys()) - itho_schema = vol.Schema({ - vol.Required(CONF_ADDON_TYPE): selector({ - "select": { - "options": options, - "multiple": False, - "translation_key": "addonselect" - } - }), - }) + itho_schema = vol.Schema( + { + vol.Required(CONF_ADDON_TYPE): selector( + { + "select": { + "options": options, + "multiple": False, + "translation_key": "addonselect", + } + } + ), + } + ) - return self.async_show_form( - step_id="user", data_schema=itho_schema) + return self.async_show_form(step_id="user", data_schema=itho_schema) - async def async_step_reconfigure(self, user_input: Mapping[str, Any] | None = None): - """Reconfigure config flow.""" - entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) - assert entry - self.entry = entry - self.config.update(entry.data) - if self.config[CONF_ADDON_TYPE] in ["cve", "noncve"]: - return await self.async_step_remotes_reconfigure() - if self.config[CONF_ADDON_TYPE] == "autotemp": - return await self.async_step_rooms_reconfigure() - return self.async_update_reload_and_abort(self.entry, data=self.config, reason="reconfigure_successful") + async def async_step_rooms(self, user_input: Mapping[str, Any] | None = None): + """Configure rooms for autotemp.""" + if user_input is not None: + self.config.update(user_input) + await self.async_set_unique_id( + f"itho_wifi_addon_{self.config[CONF_ADDON_TYPE]}" + ) + self._abort_if_unique_id_configured() + return self.async_create_entry( + title="Itho WiFi Add-on for " + + ADDON_TYPES[self.config[CONF_ADDON_TYPE]], + data=self.config, + ) + + itho_schema = vol.Schema( + { + vol.Required(CONF_AUTOTEMP_ROOM1, default="Room 1"): str, + vol.Optional(CONF_AUTOTEMP_ROOM2, default="Room 2"): str, + vol.Optional(CONF_AUTOTEMP_ROOM3, default="Room 3"): str, + vol.Optional(CONF_AUTOTEMP_ROOM4, default="Room 4"): str, + vol.Optional(CONF_AUTOTEMP_ROOM5, default="Room 5"): str, + vol.Optional(CONF_AUTOTEMP_ROOM6, default="Room 6"): str, + vol.Optional(CONF_AUTOTEMP_ROOM7, default="Room 7"): str, + vol.Optional(CONF_AUTOTEMP_ROOM8, default="Room 8"): str, + } + ) + return self.async_show_form( + step_id="rooms", data_schema=itho_schema, last_step=True + ) async def async_step_remotes(self, user_input: Mapping[str, Any] | None = None): """Configure up to 5 remotes.""" if user_input is not None: self.config.update(user_input) - await self.async_set_unique_id(f"itho_wifi_addon_{self.config[CONF_ADDON_TYPE]}") + await self.async_set_unique_id( + f"itho_wifi_addon_{self.config[CONF_ADDON_TYPE]}" + ) self._abort_if_unique_id_configured() return self.async_create_entry( - title="Itho WiFi Add-on for " + ADDON_TYPES[self.config[CONF_ADDON_TYPE]], - data=self.config + title="Itho WiFi Add-on for " + + ADDON_TYPES[self.config[CONF_ADDON_TYPE]], + data=self.config, ) - itho_schema = vol.Schema({ - vol.Required(CONF_REMOTE_1, default="Remote 1"): str, - vol.Optional(CONF_REMOTE_2, default="Remote 2"): str, - vol.Optional(CONF_REMOTE_3, default="Remote 3"): str, - vol.Optional(CONF_REMOTE_4, default="Remote 4"): str, - vol.Optional(CONF_REMOTE_5, default="Remote 5"): str - }) - return self.async_show_form(step_id="remotes", data_schema=itho_schema, last_step=True) + itho_schema = vol.Schema( + { + vol.Required(CONF_REMOTE_1, default="Remote 1"): str, + vol.Optional(CONF_REMOTE_2, default="Remote 2"): str, + vol.Optional(CONF_REMOTE_3, default="Remote 3"): str, + vol.Optional(CONF_REMOTE_4, default="Remote 4"): str, + vol.Optional(CONF_REMOTE_5, default="Remote 5"): str, + } + ) + return self.async_show_form( + step_id="remotes", data_schema=itho_schema, last_step=True + ) - async def async_step_remotes_reconfigure(self, user_input: Mapping[str, Any] | None = None): - """Reconfigure up to 5 remotes.""" + async def async_step_hru_device(self, user_input: Mapping[str, Any] | None = None): + """Configure Non-CVE (HRU) Device.""" if user_input is not None: self.config.update(user_input) - return self.async_update_reload_and_abort(self.entry, data=self.config, reason="reconfigure_successful") + return await self.async_step_remotes() - itho_schema = vol.Schema({ - vol.Required(CONF_REMOTE_1, default=self._get_reconfigure_value(CONF_REMOTE_1, "Remote 1")): str, - vol.Optional(CONF_REMOTE_2, default=self._get_reconfigure_value(CONF_REMOTE_2, "Remote 2")): str, - vol.Optional(CONF_REMOTE_3, default=self._get_reconfigure_value(CONF_REMOTE_3, "Remote 3")): str, - vol.Optional(CONF_REMOTE_4, default=self._get_reconfigure_value(CONF_REMOTE_4, "Remote 4")): str, - vol.Optional(CONF_REMOTE_5, default=self._get_reconfigure_value(CONF_REMOTE_5, "Remote 5")): str, - }) - return self.async_show_form(step_id="remotes_reconfigure", data_schema=itho_schema, last_step=True) + options = list(HRU_DEVICES.keys()) + itho_schema = vol.Schema( + { + vol.Required(CONF_HRU_DEVICE): selector( + { + "select": { + "options": options, + "multiple": False, + "translation_key": "hrudeviceselect", + } + } + ), + } + ) - async def async_step_rooms(self, user_input: Mapping[str, Any] | None = None): - """Configure rooms for autotemp.""" + return self.async_show_form(step_id="hru_device", data_schema=itho_schema) + + async def async_step_reconfigure(self, user_input: Mapping[str, Any] | None = None): + """Reconfigure config flow.""" + entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) + assert entry + self.entry = entry + self.config.update(entry.data) + if self.config[CONF_ADDON_TYPE] == "autotemp": + return await self.async_step_rooms_reconfigure() + if self.config[CONF_ADDON_TYPE] in ["cve", "noncve"]: + return await self.async_step_remotes_reconfigure() + return self.async_update_reload_and_abort( + self.entry, data=self.config, reason="reconfigure_successful" + ) + + async def async_step_rooms_reconfigure( + self, user_input: Mapping[str, Any] | None = None + ): + """Reconfigure rooms for autotemp.""" if user_input is not None: self.config.update(user_input) - await self.async_set_unique_id(f"itho_wifi_addon_{self.config[CONF_ADDON_TYPE]}") - self._abort_if_unique_id_configured() - return self.async_create_entry( - title="Itho WiFi Add-on for " + ADDON_TYPES[self.config[CONF_ADDON_TYPE]], - data=self.config + return self.async_update_reload_and_abort( + self.entry, data=self.config, reason="reconfigure_successful" ) - itho_schema = vol.Schema({ - vol.Required(CONF_AUTOTEMP_ROOM1, default="Room 1"): str, - vol.Optional(CONF_AUTOTEMP_ROOM2, default="Room 2"): str, - vol.Optional(CONF_AUTOTEMP_ROOM3, default="Room 3"): str, - vol.Optional(CONF_AUTOTEMP_ROOM4, default="Room 4"): str, - vol.Optional(CONF_AUTOTEMP_ROOM5, default="Room 5"): str, - vol.Optional(CONF_AUTOTEMP_ROOM6, default="Room 6"): str, - vol.Optional(CONF_AUTOTEMP_ROOM7, default="Room 7"): str, - vol.Optional(CONF_AUTOTEMP_ROOM8, default="Room 8"): str, - }) - return self.async_show_form(step_id="rooms", data_schema=itho_schema, last_step=True) - - async def async_step_rooms_reconfigure(self, user_input: Mapping[str, Any] | None = None): - """Reconfigure rooms for autotemp.""" + itho_schema = vol.Schema( + { + vol.Required( + CONF_AUTOTEMP_ROOM1, + default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM1, "Room 1"), + ): str, + vol.Optional( + CONF_AUTOTEMP_ROOM2, + default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM2, "Room 2"), + ): str, + vol.Optional( + CONF_AUTOTEMP_ROOM3, + default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM3, "Room 3"), + ): str, + vol.Optional( + CONF_AUTOTEMP_ROOM4, + default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM4, "Room 4"), + ): str, + vol.Optional( + CONF_AUTOTEMP_ROOM5, + default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM5, "Room 5"), + ): str, + vol.Optional( + CONF_AUTOTEMP_ROOM6, + default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM6, "Room 6"), + ): str, + vol.Optional( + CONF_AUTOTEMP_ROOM7, + default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM7, "Room 7"), + ): str, + vol.Optional( + CONF_AUTOTEMP_ROOM8, + default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM8, "Room 8"), + ): str, + } + ) + return self.async_show_form( + step_id="rooms_reconfigure", data_schema=itho_schema, last_step=True + ) + + async def async_step_remotes_reconfigure( + self, user_input: Mapping[str, Any] | None = None + ): + """Reconfigure up to 5 remotes.""" if user_input is not None: self.config.update(user_input) - return self.async_update_reload_and_abort(self.entry, data=self.config, reason="reconfigure_successful") - - itho_schema = vol.Schema({ - vol.Required(CONF_AUTOTEMP_ROOM1, default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM1, "Room 1")): str, - vol.Optional(CONF_AUTOTEMP_ROOM2, default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM2, "Room 2")): str, - vol.Optional(CONF_AUTOTEMP_ROOM3, default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM3, "Room 3")): str, - vol.Optional(CONF_AUTOTEMP_ROOM4, default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM4, "Room 4")): str, - vol.Optional(CONF_AUTOTEMP_ROOM5, default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM5, "Room 5")): str, - vol.Optional(CONF_AUTOTEMP_ROOM6, default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM6, "Room 6")): str, - vol.Optional(CONF_AUTOTEMP_ROOM7, default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM7, "Room 7")): str, - vol.Optional(CONF_AUTOTEMP_ROOM8, default=self._get_reconfigure_value(CONF_AUTOTEMP_ROOM8, "Room 8")): str, - }) - return self.async_show_form(step_id="rooms_reconfigure", data_schema=itho_schema, last_step=True) + return self.async_update_reload_and_abort( + self.entry, data=self.config, reason="reconfigure_successful" + ) + + itho_schema = vol.Schema( + { + vol.Required( + CONF_REMOTE_1, + default=self._get_reconfigure_value(CONF_REMOTE_1, "Remote 1"), + ): str, + vol.Optional( + CONF_REMOTE_2, + default=self._get_reconfigure_value(CONF_REMOTE_2, "Remote 2"), + ): str, + vol.Optional( + CONF_REMOTE_3, + default=self._get_reconfigure_value(CONF_REMOTE_3, "Remote 3"), + ): str, + vol.Optional( + CONF_REMOTE_4, + default=self._get_reconfigure_value(CONF_REMOTE_4, "Remote 4"), + ): str, + vol.Optional( + CONF_REMOTE_5, + default=self._get_reconfigure_value(CONF_REMOTE_5, "Remote 5"), + ): str, + } + ) + return self.async_show_form( + step_id="remotes_reconfigure", data_schema=itho_schema, last_step=True + ) diff --git a/custom_components/ithodaalderop/const.py b/custom_components/ithodaalderop/const.py index 2c89975..9327564 100644 --- a/custom_components/ithodaalderop/const.py +++ b/custom_components/ithodaalderop/const.py @@ -11,11 +11,12 @@ ADDON_TYPES = { "autotemp": "Autotemp", "cve": "CVE", - "hru350": "HRU ECO 350", - "hrueco": "HRU ECO", + "noncve": "HRU", "wpu": "WPU", } +HRU_DEVICES = {"hru_eco": "HRU ECO", "hru_eco_350": "HRU ECO 350"} + UNITTYPE_ICONS = { "%": "mdi:percent-outline", "hum": "mdi:water-percent", @@ -25,8 +26,7 @@ MQTT_BASETOPIC = { "autotemp": "ithotemp", "cve": "ithocve", - "hru350": "ithohru", - "hrueco": "ithohru", + "noncve": "ithohru", "wpu": "ithowpu", } @@ -34,13 +34,13 @@ "autotemp": "ithostatus", "cve": "ithostatus", "last_cmd": "lastcmd", - "hru350": "ithostatus", - "hrueco": "ithostatus", + "noncve": "ithostatus", "remote": "remotesinfo", "wpu": "ithostatus", } CONF_ADDON_TYPE = "addontype" +CONF_HRU_DEVICE = "hru_device" CONF_AUTOTEMP_ROOM1 = "room1" CONF_AUTOTEMP_ROOM2 = "room2" @@ -84,7 +84,7 @@ 5: "Manual operation", } -HRU_ACTUAL_MODE = { +NONCVE_ACTUAL_MODE = { 1: "low", 2: "medium", 3: "high", @@ -95,13 +95,13 @@ # Based on user-experience. No codelist availble in the Itho Servicetool # For any additions/feedback, please create an issue in the repo of the integration -HRU_GLOBAL_FAULT_CODE = { +NONCVE_GLOBAL_FAULT_CODE = { 0: "No error", 7: "Filters dirty", 11: "(External) Sensor error", } -HRU_RH_ERROR_CODE = { +NONCVE_RH_ERROR_CODE = { 239: "Not Available", 240: "Shorted Sensor", 241: "Open Sensor", diff --git a/custom_components/ithodaalderop/manifest.json b/custom_components/ithodaalderop/manifest.json index 76e3e04..f396937 100644 --- a/custom_components/ithodaalderop/manifest.json +++ b/custom_components/ithodaalderop/manifest.json @@ -14,5 +14,5 @@ "mqtt": ["ithohru/#"], "requirements": [ ], - "version": "2.2.0-b1" + "version": "2.2.0-b2" } diff --git a/custom_components/ithodaalderop/sensor.py b/custom_components/ithodaalderop/sensor.py index be130c7..9555c13 100644 --- a/custom_components/ithodaalderop/sensor.py +++ b/custom_components/ithodaalderop/sensor.py @@ -21,13 +21,14 @@ AUTOTEMP_ERROR, AUTOTEMP_MODE, CONF_ADDON_TYPE, + CONF_HRU_DEVICE, DOMAIN, - HRU_ACTUAL_MODE, - HRU_GLOBAL_FAULT_CODE, - HRU_RH_ERROR_CODE, MANUFACTURER, MQTT_BASETOPIC, MQTT_STATETOPIC, + NONCVE_ACTUAL_MODE, + NONCVE_GLOBAL_FAULT_CODE, + NONCVE_RH_ERROR_CODE, UNITTYPE_ICONS, WPU_STATUS, ) @@ -116,17 +117,17 @@ async def async_setup_entry( description.key = f"{MQTT_BASETOPIC["cve"]}/{MQTT_STATETOPIC["cve"]}" sensors.append(IthoSensorFan(description, config_entry)) - if config_entry.data[CONF_ADDON_TYPE] == "hru350": - for description in HRU350SENSORS: - description.key = f"{MQTT_BASETOPIC["hru350"]}/{MQTT_STATETOPIC["hru350"]}" - sensors.append(IthoSensorFan(description, config_entry)) + if config_entry.data[CONF_ADDON_TYPE] == "noncve": + if config_entry.data[CONF_HRU_DEVICE] == "hru_eco": + hru_sensors = HRUECOSENSORS + if config_entry.data[CONF_HRU_DEVICE] == "hru_eco_350": + hru_sensors = HRU350SENSORS - if config_entry.data[CONF_ADDON_TYPE] == "hrueco": - for description in HRUECOSENSORS: - description.key = f"{MQTT_BASETOPIC["hrueco"]}/{MQTT_STATETOPIC["hrueco"]}" + for description in hru_sensors: + description.key = f"{MQTT_BASETOPIC["noncve"]}/{MQTT_STATETOPIC["noncve"]}" sensors.append(IthoSensorFan(description, config_entry)) - if config_entry.data[CONF_ADDON_TYPE] in ["cve", "hru350", "hrueco"]: + if config_entry.data[CONF_ADDON_TYPE] in ["cve", "noncve"]: for description in _create_remotes(config_entry): description.key = f"{MQTT_BASETOPIC[config_entry.data[CONF_ADDON_TYPE]]}/{MQTT_STATETOPIC["remote"]}" sensors.append(IthoSensorCO2Remote(description, config_entry)) @@ -351,7 +352,7 @@ def message_received(message): value = payload[json_field] if json_field == "Actual Mode": self._extra_state_attributes = {"Code": value} - value = HRU_ACTUAL_MODE.get(value, "Unknown mode") + value = NONCVE_ACTUAL_MODE.get(value, "Unknown mode") if json_field == "Airfilter counter": _last_maintenance = "" @@ -384,7 +385,7 @@ def message_received(message): if json_field == "Global fault code": _description = "" if str(value).isnumeric(): - _description = HRU_GLOBAL_FAULT_CODE.get( + _description = NONCVE_GLOBAL_FAULT_CODE.get( int(value), "Unknown fault code" ) @@ -395,7 +396,7 @@ def message_received(message): if json_field == "Highest received RH value (%RH)": _error_description = "" if isinstance(value, (int, float)) and float(value) > 100: - _error_description = HRU_RH_ERROR_CODE.get( + _error_description = NONCVE_RH_ERROR_CODE.get( int(value), "Unknown error" ) value = None diff --git a/custom_components/ithodaalderop/translations/en.json b/custom_components/ithodaalderop/translations/en.json index 2118dc7..f0c7d77 100644 --- a/custom_components/ithodaalderop/translations/en.json +++ b/custom_components/ithodaalderop/translations/en.json @@ -147,11 +147,18 @@ "step": { "user": { "title": "Add new Itho devices", - "description": "Select the device you want to configure. Repeat the configuration for every physical add-on present. This integration creates commonly used sensors for each device.", + "description": "Select the Itho WiFi Add-on you want to configure. Repeat the configuration for every physical add-on present. This integration creates commonly used sensors for each device.", "data": { "addontype": "Select the type of add-on to configure." } }, + "hru_device": { + "title": "Select Non-CVE (HRU) device model", + "description": "Select the model of your Itho Unit", + "data": { + "hru_device": "Itho HRU Model" + } + }, "reconfigure": { "title": "Reconfigure Itho devices", "description": "Select the device you want to reconfigure. Repeat the configuration for every physical add-on present.", @@ -220,10 +227,15 @@ "options": { "autotemp": "Autotemp floor heating", "cve": "CVE (ECO box)", - "hru350": "HRU ECO 350", - "hrueco": "HRU ECO", + "noncve": "Non-CVE (HRU)", "wpu": "WPU heatpump" } + }, + "hrudeviceselect": { + "options": { + "hru_eco": "HRU ECO", + "hru_eco_350": "HRU ECO 350" + } } } } diff --git a/custom_components/ithodaalderop/translations/nl.json b/custom_components/ithodaalderop/translations/nl.json index c29d57e..f09643c 100644 --- a/custom_components/ithodaalderop/translations/nl.json +++ b/custom_components/ithodaalderop/translations/nl.json @@ -152,6 +152,13 @@ "addontype": "Add-on type" } }, + "hru_device": { + "title": "Selecteer Non-CVE (HRU) model", + "description": "Selecteer het model van uw Itho unit", + "data": { + "hru_device": "Itho HRU Model" + } + }, "remotes": { "title": "Voeg nieuwe Itho afstandsbedieningen toe", "description": "Gebruik de namen zoals deze ook zijn geconfigureerd in de Add-on bij RF Devices. Klik op 'Verzenden' zonder iets te wijzigen om dit over te slaan.", @@ -214,10 +221,15 @@ "options": { "autotemp": "Autotemp vloerverwarming", "cve": "CVE (ECO box)", - "hru350": "HRU ECO 350", - "hrueco": "HRU ECO", + "noncve": "Non-CVE (HRU)", "wpu": "WPU Warmtepomp" } + }, + "hrudeviceselect": { + "options": { + "hru_eco": "HRU ECO", + "hru_eco_350": "HRU ECO 350" + } } } } From 6aefd74b77f33945afe8c733044901ad5454de48 Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Fri, 6 Dec 2024 08:50:34 +0100 Subject: [PATCH 05/12] Add migration path --- custom_components/ithodaalderop/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/custom_components/ithodaalderop/__init__.py b/custom_components/ithodaalderop/__init__.py index e4f1cee..69a9872 100644 --- a/custom_components/ithodaalderop/__init__.py +++ b/custom_components/ithodaalderop/__init__.py @@ -4,11 +4,20 @@ from homeassistant.const import Platform from homeassistant.core import HomeAssistant +from .const import CONF_ADDON_TYPE, CONF_HRU_DEVICE + PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the Itho Wifi add-on integration.""" + + if entry.data[CONF_ADDON_TYPE] == "noncve": + new_data = {**entry.data} + if new_data.get(CONF_HRU_DEVICE) is None: + new_data[CONF_HRU_DEVICE] = "hru_eco_350" + hass.config_entries.async_update_entry(entry, data=new_data) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True From a8262d6a9b6985808949b8644f806a04f0531b97 Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:50:49 +0100 Subject: [PATCH 06/12] Fixes - Fix Airfilter counter - Remove default disable for HRU ECO fan entities - Better migration handling --- custom_components/ithodaalderop/__init__.py | 10 ++++++---- custom_components/ithodaalderop/config_flow.py | 8 ++++++++ custom_components/ithodaalderop/const.py | 2 +- custom_components/ithodaalderop/definitions.py | 4 +--- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/custom_components/ithodaalderop/__init__.py b/custom_components/ithodaalderop/__init__.py index 69a9872..0191708 100644 --- a/custom_components/ithodaalderop/__init__.py +++ b/custom_components/ithodaalderop/__init__.py @@ -12,11 +12,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the Itho Wifi add-on integration.""" - if entry.data[CONF_ADDON_TYPE] == "noncve": + if ( + entry.data[CONF_ADDON_TYPE] == "noncve" + and entry.data.get(CONF_HRU_DEVICE) is None + ): new_data = {**entry.data} - if new_data.get(CONF_HRU_DEVICE) is None: - new_data[CONF_HRU_DEVICE] = "hru_eco_350" - hass.config_entries.async_update_entry(entry, data=new_data) + new_data[CONF_HRU_DEVICE] = "hru_eco_350" + hass.config_entries.async_update_entry(entry, data=new_data) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True diff --git a/custom_components/ithodaalderop/config_flow.py b/custom_components/ithodaalderop/config_flow.py index 49b29e2..27d0b1b 100644 --- a/custom_components/ithodaalderop/config_flow.py +++ b/custom_components/ithodaalderop/config_flow.py @@ -126,6 +126,14 @@ async def async_step_remotes(self, user_input: Mapping[str, Any] | None = None): f"itho_wifi_addon_{self.config[CONF_ADDON_TYPE]}" ) self._abort_if_unique_id_configured() + if self.config[CONF_ADDON_TYPE] == "noncve": + return self.async_create_entry( + title="Itho WiFi Add-on for " + + ADDON_TYPES[self.config[CONF_ADDON_TYPE]] + + " - " + + HRU_DEVICES[self.config[CONF_HRU_DEVICE]], + data=self.config, + ) return self.async_create_entry( title="Itho WiFi Add-on for " + ADDON_TYPES[self.config[CONF_ADDON_TYPE]], diff --git a/custom_components/ithodaalderop/const.py b/custom_components/ithodaalderop/const.py index 9327564..914232f 100644 --- a/custom_components/ithodaalderop/const.py +++ b/custom_components/ithodaalderop/const.py @@ -11,7 +11,7 @@ ADDON_TYPES = { "autotemp": "Autotemp", "cve": "CVE", - "noncve": "HRU", + "noncve": "Non-CVE", "wpu": "WPU", } diff --git a/custom_components/ithodaalderop/definitions.py b/custom_components/ithodaalderop/definitions.py index a98210d..a1978f8 100644 --- a/custom_components/ithodaalderop/definitions.py +++ b/custom_components/ithodaalderop/definitions.py @@ -311,7 +311,7 @@ class IthoBinarySensorEntityDescription(BinarySensorEntityDescription): HRUECOSENSORS: tuple[IthoSensorEntityDescription, ...] = ( IthoSensorEntityDescription( - json_field="Airfilter counter", + json_field="Air filter counter", translation_key="airfilter_counter", native_unit_of_measurement=UnitOfTime.HOURS, state_class=SensorStateClass.TOTAL_INCREASING, @@ -323,7 +323,6 @@ class IthoBinarySensorEntityDescription(BinarySensorEntityDescription): translation_key="drain_fan_speed", native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, state_class=SensorStateClass.MEASUREMENT, - entity_registry_enabled_default=False, ), IthoSensorEntityDescription( json_field="Temp of exhaust air (°C)", @@ -348,7 +347,6 @@ class IthoBinarySensorEntityDescription(BinarySensorEntityDescription): translation_key="supply_fan_speed", native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, state_class=SensorStateClass.MEASUREMENT, - entity_registry_enabled_default=False, ), IthoSensorEntityDescription( json_field="Temp of supply air (°C)", From e9deca8b2df8f3c4489a3a1614cefe6c0036e34b Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:12:59 +0100 Subject: [PATCH 07/12] hru split tweaks --- .../ithodaalderop/binary_sensor.py | 7 ++- custom_components/ithodaalderop/const.py | 14 ++++-- custom_components/ithodaalderop/sensor.py | 47 +++++++++++++++---- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/custom_components/ithodaalderop/binary_sensor.py b/custom_components/ithodaalderop/binary_sensor.py index c2ca0d7..a6b8647 100644 --- a/custom_components/ithodaalderop/binary_sensor.py +++ b/custom_components/ithodaalderop/binary_sensor.py @@ -19,6 +19,7 @@ CONF_ADDON_TYPE, CONF_HRU_DEVICE, DOMAIN, + HRU_DEVICES, MANUFACTURER, MQTT_BASETOPIC, MQTT_STATETOPIC, @@ -77,10 +78,14 @@ def __init__( """Initialize the binary sensor.""" self.entity_description = description + model = ADDON_TYPES[config_entry.data[CONF_ADDON_TYPE]] + if config_entry.data[CONF_ADDON_TYPE] == "noncve": + model = model + " - " + HRU_DEVICES[config_entry.data[CONF_HRU_DEVICE]] + self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, config_entry.data[CONF_ADDON_TYPE])}, manufacturer=MANUFACTURER, - model=ADDON_TYPES[config_entry.data[CONF_ADDON_TYPE]], + model=model, name="Itho Daalderop " + ADDON_TYPES[config_entry.data[CONF_ADDON_TYPE]], ) diff --git a/custom_components/ithodaalderop/const.py b/custom_components/ithodaalderop/const.py index 914232f..91b99f4 100644 --- a/custom_components/ithodaalderop/const.py +++ b/custom_components/ithodaalderop/const.py @@ -84,7 +84,7 @@ 5: "Manual operation", } -NONCVE_ACTUAL_MODE = { +NONCVE_HRUECO350_ACTUAL_MODE = { 1: "low", 2: "medium", 3: "high", @@ -95,13 +95,13 @@ # Based on user-experience. No codelist availble in the Itho Servicetool # For any additions/feedback, please create an issue in the repo of the integration -NONCVE_GLOBAL_FAULT_CODE = { +NONCVE_HRUECO350_GLOBAL_FAULT_CODE = { 0: "No error", 7: "Filters dirty", 11: "(External) Sensor error", } -NONCVE_RH_ERROR_CODE = { +NONCVE_HRUECO350_RH_ERROR_CODE = { 239: "Not Available", 240: "Shorted Sensor", 241: "Open Sensor", @@ -121,6 +121,14 @@ 255: "Unknown Error", } +NONCVE_HRUECO_STATUS = { + 0: "Normal", + 1: "Adjust frost valve", + 2: "Decelerate Supply fan", + 3: "Accelerate Exhaust fan", + 4: "Stop supply fan", +} + WPU_STATUS = { 0: "Init", 1: "Off", diff --git a/custom_components/ithodaalderop/sensor.py b/custom_components/ithodaalderop/sensor.py index 9555c13..985e833 100644 --- a/custom_components/ithodaalderop/sensor.py +++ b/custom_components/ithodaalderop/sensor.py @@ -23,12 +23,14 @@ CONF_ADDON_TYPE, CONF_HRU_DEVICE, DOMAIN, + HRU_DEVICES, MANUFACTURER, MQTT_BASETOPIC, MQTT_STATETOPIC, - NONCVE_ACTUAL_MODE, - NONCVE_GLOBAL_FAULT_CODE, - NONCVE_RH_ERROR_CODE, + NONCVE_HRUECO350_ACTUAL_MODE, + NONCVE_HRUECO350_GLOBAL_FAULT_CODE, + NONCVE_HRUECO350_RH_ERROR_CODE, + NONCVE_HRUECO_STATUS, UNITTYPE_ICONS, WPU_STATUS, ) @@ -163,10 +165,14 @@ def __init__( self.entity_description = description if use_base_sensor_device: + model = ADDON_TYPES[config_entry.data[CONF_ADDON_TYPE]] + if config_entry.data[CONF_ADDON_TYPE] == "noncve": + model = model + " - " + HRU_DEVICES[config_entry.data[CONF_HRU_DEVICE]] + self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, config_entry.data[CONF_ADDON_TYPE])}, manufacturer=MANUFACTURER, - model=ADDON_TYPES[config_entry.data[CONF_ADDON_TYPE]], + model=model, name=ADDON_TYPES[config_entry.data[CONF_ADDON_TYPE]], ) @@ -350,11 +356,13 @@ def message_received(message): value = None else: value = payload[json_field] + # HRU ECO 350 if json_field == "Actual Mode": self._extra_state_attributes = {"Code": value} - value = NONCVE_ACTUAL_MODE.get(value, "Unknown mode") + value = NONCVE_HRUECO350_ACTUAL_MODE.get(value, "Unknown mode") - if json_field == "Airfilter counter": + # HRU ECO 350 / HRU ECO + if json_field in ["Airfilter counter", "Air filter counter"]: _last_maintenance = "" _next_maintenance_estimate = "" if str(value).isnumeric(): @@ -372,6 +380,7 @@ def message_received(message): "Next Maintenance Estimate": _next_maintenance_estimate, } + # HRU ECO 350 if json_field == "Air Quality (%)": _error_description = "" if isinstance(value, (int, float)) and float(value) > 100: @@ -382,21 +391,23 @@ def message_received(message): "Error Description": _error_description, } + # HRU ECO 350 if json_field == "Global fault code": - _description = "" + _description = "Unknown fault code" if str(value).isnumeric(): - _description = NONCVE_GLOBAL_FAULT_CODE.get( - int(value), "Unknown fault code" + _description = NONCVE_HRUECO350_GLOBAL_FAULT_CODE.get( + int(value), _description ) self._extra_state_attributes = { "Description": _description, } + # HRU ECO 350 if json_field == "Highest received RH value (%RH)": _error_description = "" if isinstance(value, (int, float)) and float(value) > 100: - _error_description = NONCVE_RH_ERROR_CODE.get( + _error_description = NONCVE_HRUECO350_RH_ERROR_CODE.get( int(value), "Unknown error" ) value = None @@ -405,6 +416,19 @@ def message_received(message): "Error Description": _error_description, } + # HRU ECO + if json_field == "Status": + self._extra_state_attributes = { + "Code": value, + } + + _description = "Unknown status" + if str(value).isnumeric(): + _description = NONCVE_HRUECO_STATUS.get( + int(value), _description + ) + value = _description + self._attr_native_value = value self.async_write_ha_state() @@ -461,6 +485,9 @@ def message_received(message): else: value = payload[json_field] if json_field == "Status": + self._extra_state_attributes = { + "Code": value, + } value = WPU_STATUS.get(int(value), "Unknown status") if json_field == "ECO selected on thermostat": if value == 1: From 17e658a19037e0bf40a47aaa69c87e81bb41db22 Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:34:49 +0100 Subject: [PATCH 08/12] comments --- custom_components/ithodaalderop/__init__.py | 3 + .../ithodaalderop/config_flow.py | 60 +++++++++---------- .../ithodaalderop/definitions.py | 3 + 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/custom_components/ithodaalderop/__init__.py b/custom_components/ithodaalderop/__init__.py index 0191708..b5c918c 100644 --- a/custom_components/ithodaalderop/__init__.py +++ b/custom_components/ithodaalderop/__init__.py @@ -12,6 +12,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the Itho Wifi add-on integration.""" + # Migrate noncve config < v2.2.0 to 2.2.0+ + # < 2.2.0 Only 1 HRU was supported. This was the HRU ECO 350 + # So, if no hru_device is setup, this has to be the hru_eco_350 if ( entry.data[CONF_ADDON_TYPE] == "noncve" and entry.data.get(CONF_HRU_DEVICE) is None diff --git a/custom_components/ithodaalderop/config_flow.py b/custom_components/ithodaalderop/config_flow.py index 27d0b1b..b0eb5fe 100644 --- a/custom_components/ithodaalderop/config_flow.py +++ b/custom_components/ithodaalderop/config_flow.py @@ -118,6 +118,29 @@ async def async_step_rooms(self, user_input: Mapping[str, Any] | None = None): step_id="rooms", data_schema=itho_schema, last_step=True ) + async def async_step_hru_device(self, user_input: Mapping[str, Any] | None = None): + """Configure Non-CVE (HRU) Device.""" + if user_input is not None: + self.config.update(user_input) + return await self.async_step_remotes() + + options = list(HRU_DEVICES.keys()) + itho_schema = vol.Schema( + { + vol.Required(CONF_HRU_DEVICE): selector( + { + "select": { + "options": options, + "multiple": False, + "translation_key": "hrudeviceselect", + } + } + ), + } + ) + + return self.async_show_form(step_id="hru_device", data_schema=itho_schema) + async def async_step_remotes(self, user_input: Mapping[str, Any] | None = None): """Configure up to 5 remotes.""" if user_input is not None: @@ -126,17 +149,13 @@ async def async_step_remotes(self, user_input: Mapping[str, Any] | None = None): f"itho_wifi_addon_{self.config[CONF_ADDON_TYPE]}" ) self._abort_if_unique_id_configured() + + title = "Itho WiFi Add-on for " + ADDON_TYPES[self.config[CONF_ADDON_TYPE]] if self.config[CONF_ADDON_TYPE] == "noncve": - return self.async_create_entry( - title="Itho WiFi Add-on for " - + ADDON_TYPES[self.config[CONF_ADDON_TYPE]] - + " - " - + HRU_DEVICES[self.config[CONF_HRU_DEVICE]], - data=self.config, - ) + title = title + " - " + HRU_DEVICES[self.config[CONF_HRU_DEVICE]] + return self.async_create_entry( - title="Itho WiFi Add-on for " - + ADDON_TYPES[self.config[CONF_ADDON_TYPE]], + title=title, data=self.config, ) @@ -153,29 +172,6 @@ async def async_step_remotes(self, user_input: Mapping[str, Any] | None = None): step_id="remotes", data_schema=itho_schema, last_step=True ) - async def async_step_hru_device(self, user_input: Mapping[str, Any] | None = None): - """Configure Non-CVE (HRU) Device.""" - if user_input is not None: - self.config.update(user_input) - return await self.async_step_remotes() - - options = list(HRU_DEVICES.keys()) - itho_schema = vol.Schema( - { - vol.Required(CONF_HRU_DEVICE): selector( - { - "select": { - "options": options, - "multiple": False, - "translation_key": "hrudeviceselect", - } - } - ), - } - ) - - return self.async_show_form(step_id="hru_device", data_schema=itho_schema) - async def async_step_reconfigure(self, user_input: Mapping[str, Any] | None = None): """Reconfigure config flow.""" entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) diff --git a/custom_components/ithodaalderop/definitions.py b/custom_components/ithodaalderop/definitions.py index a1978f8..21d5b6f 100644 --- a/custom_components/ithodaalderop/definitions.py +++ b/custom_components/ithodaalderop/definitions.py @@ -5,6 +5,9 @@ A diagnostic entity provides device-specific metadata or operational insights that assist in understanding the device's internal state or functioning but is not directly related to the user's environment. """ +# TODO: Add support for HRU 200 and HRU 250/300 +# Labels (json_fields) can be found @ https://github.com/arjenhiemstra/ithowifi/tree/master/software/NRG_itho_wifi/main/devices + from __future__ import annotations from collections.abc import Callable From 6c6c7761caa7e3069dc66465fd19185dbfb21dae Mon Sep 17 00:00:00 2001 From: benjamin-dcs <78026082+benjamin-dcs@users.noreply.github.com> Date: Fri, 6 Dec 2024 20:22:07 +0100 Subject: [PATCH 09/12] Update manifest.json --- custom_components/ithodaalderop/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/ithodaalderop/manifest.json b/custom_components/ithodaalderop/manifest.json index f396937..e5d878b 100644 --- a/custom_components/ithodaalderop/manifest.json +++ b/custom_components/ithodaalderop/manifest.json @@ -14,5 +14,5 @@ "mqtt": ["ithohru/#"], "requirements": [ ], - "version": "2.2.0-b2" + "version": "2.2.0-b3" } From d6c98489177fbda7f15d1cabce3e3a7e6f3b0c81 Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:10:56 +0100 Subject: [PATCH 10/12] WIP 200 - 250/300 --- .../ithodaalderop/binary_sensor.py | 4 +- custom_components/ithodaalderop/const.py | 8 +- .../ithodaalderop/definitions.py | 96 ++++++++++++++++++- custom_components/ithodaalderop/sensor.py | 10 +- .../ithodaalderop/translations/en.json | 3 + .../ithodaalderop/translations/nl.json | 3 + 6 files changed, 117 insertions(+), 7 deletions(-) diff --git a/custom_components/ithodaalderop/binary_sensor.py b/custom_components/ithodaalderop/binary_sensor.py index a6b8647..5b8b14a 100644 --- a/custom_components/ithodaalderop/binary_sensor.py +++ b/custom_components/ithodaalderop/binary_sensor.py @@ -26,7 +26,7 @@ ) from .definitions import ( AUTOTEMPBINARYSENSORS, - HRU350BINARYSENSORS, + HRUECO350BINARYSENSORS, HRUECOBINARYSENSORS, IthoBinarySensorEntityDescription, ) @@ -55,7 +55,7 @@ async def async_setup_entry( if config_entry.data[CONF_HRU_DEVICE] == "hru_eco": hru_sensors = HRUECOBINARYSENSORS if config_entry.data[CONF_HRU_DEVICE] == "hru_eco_350": - hru_sensors = HRU350BINARYSENSORS + hru_sensors = HRUECO350BINARYSENSORS for description in hru_sensors: description.key = f"{MQTT_BASETOPIC["noncve"]}/{MQTT_STATETOPIC["noncve"]}" diff --git a/custom_components/ithodaalderop/const.py b/custom_components/ithodaalderop/const.py index 91b99f4..609ef70 100644 --- a/custom_components/ithodaalderop/const.py +++ b/custom_components/ithodaalderop/const.py @@ -15,7 +15,13 @@ "wpu": "WPU", } -HRU_DEVICES = {"hru_eco": "HRU ECO", "hru_eco_350": "HRU ECO 350"} +HRU_DEVICES = { + "hru_eco": "HRU ECO", + "hru_eco_200": "HRU ECO 200", + "hru_eco_250": "HRU ECO 250", + "hru_eco_300": "HRU ECO 300", + "hru_eco_350": "HRU ECO 350", +} UNITTYPE_ICONS = { "%": "mdi:percent-outline", diff --git a/custom_components/ithodaalderop/definitions.py b/custom_components/ithodaalderop/definitions.py index 21d5b6f..f5cf175 100644 --- a/custom_components/ithodaalderop/definitions.py +++ b/custom_components/ithodaalderop/definitions.py @@ -207,7 +207,99 @@ class IthoBinarySensorEntityDescription(BinarySensorEntityDescription): ), ) -HRU350BINARYSENSORS: tuple[IthoBinarySensorEntityDescription, ...] = ( +HRUECO200SENSORS: tuple[IthoSensorEntityDescription, ...] = ( + IthoSensorEntityDescription( + json_field="Actual speed (rpm)", + translation_key="actual_speed", + native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Air discharge temperature (°C)", + translation_key="air_discharge_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Average air outlet temperature (°C)", + translation_key="average_air_outlet_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Error", + translation_key="error", + entity_category=EntityCategory.DIAGNOSTIC, + ), + IthoSensorEntityDescription( + json_field="Filter use counter (hour)", + translation_key="filter_use_counter", + native_unit_of_measurement=UnitOfTime.HOURS, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_category=EntityCategory.DIAGNOSTIC, + icon="mdi:counter", + ), + IthoSensorEntityDescription( + json_field="Max. CO2 level (ppm)", + translation_key="max_co2_level", + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + icon="mdi:molecule-co2", + ), + IthoSensorEntityDescription( + json_field="Max. RH level (%)", + translation_key="max_rh_level", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + IthoSensorEntityDescription( + json_field="Room temp setpoint (°C)", + translation_key="room_temp_setpoint", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Outside temperature (°C)", + translation_key="outside_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Speed setpoint (rpm)", + translation_key="speed_setpoint", + native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + IthoSensorEntityDescription( + json_field="Total operation (hrs)", + translation_key="total_operation_time", + native_unit_of_measurement=UnitOfTime.HOURS, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_category=EntityCategory.DIAGNOSTIC, + ), + IthoSensorEntityDescription( + json_field="Ventilation setpoint (%)", + translation_key="ventilation_setpoint_percentage", + native_unit_of_measurement=PERCENTAGE, + ), +) + +HRUECO250300SENSORS: tuple[IthoSensorEntityDescription, ...] = ( + # IthoSensorEntityDescription( + # json_field="Actual Mode", + # translation_key="actual_mode", + # icon="mdi:knob", + # ), +) + +HRUECO350BINARYSENSORS: tuple[IthoBinarySensorEntityDescription, ...] = ( IthoBinarySensorEntityDescription( json_field="Bypass position", translation_key="bypass_position", @@ -218,7 +310,7 @@ class IthoBinarySensorEntityDescription(BinarySensorEntityDescription): ), ) -HRU350SENSORS: tuple[IthoSensorEntityDescription, ...] = ( +HRUECO350SENSORS: tuple[IthoSensorEntityDescription, ...] = ( IthoSensorEntityDescription( json_field="Actual Mode", translation_key="actual_mode", diff --git a/custom_components/ithodaalderop/sensor.py b/custom_components/ithodaalderop/sensor.py index 985e833..aac5552 100644 --- a/custom_components/ithodaalderop/sensor.py +++ b/custom_components/ithodaalderop/sensor.py @@ -38,7 +38,9 @@ AUTOTEMPROOMSENSORS, AUTOTEMPSENSORS, CVESENSORS, - HRU350SENSORS, + HRUECO200SENSORS, + HRUECO350SENSORS, + HRUECO250300SENSORS, HRUECOSENSORS, LASTCMDSENSORS, WPUSENSORS, @@ -122,8 +124,12 @@ async def async_setup_entry( if config_entry.data[CONF_ADDON_TYPE] == "noncve": if config_entry.data[CONF_HRU_DEVICE] == "hru_eco": hru_sensors = HRUECOSENSORS + if config_entry.data[CONF_HRU_DEVICE] == "hru_eco_200": + hru_sensors = HRUECO200SENSORS + if config_entry.data[CONF_HRU_DEVICE] in ["hru_eco_250", "hru_eco_300"]: + hru_sensors = HRUECO250300SENSORS if config_entry.data[CONF_HRU_DEVICE] == "hru_eco_350": - hru_sensors = HRU350SENSORS + hru_sensors = HRUECO350SENSORS for description in hru_sensors: description.key = f"{MQTT_BASETOPIC["noncve"]}/{MQTT_STATETOPIC["noncve"]}" diff --git a/custom_components/ithodaalderop/translations/en.json b/custom_components/ithodaalderop/translations/en.json index f0c7d77..8e62427 100644 --- a/custom_components/ithodaalderop/translations/en.json +++ b/custom_components/ithodaalderop/translations/en.json @@ -234,6 +234,9 @@ "hrudeviceselect": { "options": { "hru_eco": "HRU ECO", + "hru_eco_200": "HRU ECO 200", + "hru_eco_250": "HRU ECO 250", + "hru_eco_300": "HRU ECO 300", "hru_eco_350": "HRU ECO 350" } } diff --git a/custom_components/ithodaalderop/translations/nl.json b/custom_components/ithodaalderop/translations/nl.json index f09643c..6c1c21c 100644 --- a/custom_components/ithodaalderop/translations/nl.json +++ b/custom_components/ithodaalderop/translations/nl.json @@ -228,6 +228,9 @@ "hrudeviceselect": { "options": { "hru_eco": "HRU ECO", + "hru_eco_200": "HRU ECO 200", + "hru_eco_250": "HRU ECO 250", + "hru_eco_300": "HRU ECO 300", "hru_eco_350": "HRU ECO 350" } } From be9190d3bfafc1b0a1bae478e83eda41ca1c8247 Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Sat, 7 Dec 2024 11:03:35 +0100 Subject: [PATCH 11/12] Further HRUECO200 work Breaking: Renamed hru_device to noncve_model --- custom_components/ithodaalderop/__init__.py | 6 +-- .../ithodaalderop/binary_sensor.py | 10 ++--- .../ithodaalderop/config_flow.py | 20 +++++----- custom_components/ithodaalderop/const.py | 4 +- custom_components/ithodaalderop/sensor.py | 16 ++++---- .../ithodaalderop/translations/en.json | 36 +++++++++++++++-- .../ithodaalderop/translations/nl.json | 40 ++++++++++++++++--- 7 files changed, 98 insertions(+), 34 deletions(-) diff --git a/custom_components/ithodaalderop/__init__.py b/custom_components/ithodaalderop/__init__.py index b5c918c..a98d112 100644 --- a/custom_components/ithodaalderop/__init__.py +++ b/custom_components/ithodaalderop/__init__.py @@ -4,7 +4,7 @@ from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from .const import CONF_ADDON_TYPE, CONF_HRU_DEVICE +from .const import CONF_ADDON_TYPE, CONF_NONCVE_MODEL PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] @@ -17,10 +17,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # So, if no hru_device is setup, this has to be the hru_eco_350 if ( entry.data[CONF_ADDON_TYPE] == "noncve" - and entry.data.get(CONF_HRU_DEVICE) is None + and entry.data.get(CONF_NONCVE_MODEL) is None ): new_data = {**entry.data} - new_data[CONF_HRU_DEVICE] = "hru_eco_350" + new_data[CONF_NONCVE_MODEL] = "hru_eco_350" hass.config_entries.async_update_entry(entry, data=new_data) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/custom_components/ithodaalderop/binary_sensor.py b/custom_components/ithodaalderop/binary_sensor.py index 5b8b14a..859ab63 100644 --- a/custom_components/ithodaalderop/binary_sensor.py +++ b/custom_components/ithodaalderop/binary_sensor.py @@ -17,12 +17,12 @@ _LOGGER, ADDON_TYPES, CONF_ADDON_TYPE, - CONF_HRU_DEVICE, + CONF_NONCVE_MODEL, DOMAIN, - HRU_DEVICES, MANUFACTURER, MQTT_BASETOPIC, MQTT_STATETOPIC, + NONCVE_DEVICES, ) from .definitions import ( AUTOTEMPBINARYSENSORS, @@ -52,9 +52,9 @@ async def async_setup_entry( sensors.append(IthoBinarySensor(description, config_entry)) if config_entry.data[CONF_ADDON_TYPE] == "noncve": - if config_entry.data[CONF_HRU_DEVICE] == "hru_eco": + if config_entry.data[CONF_NONCVE_MODEL] == "hru_eco": hru_sensors = HRUECOBINARYSENSORS - if config_entry.data[CONF_HRU_DEVICE] == "hru_eco_350": + if config_entry.data[CONF_NONCVE_MODEL] == "hru_eco_350": hru_sensors = HRUECO350BINARYSENSORS for description in hru_sensors: @@ -80,7 +80,7 @@ def __init__( model = ADDON_TYPES[config_entry.data[CONF_ADDON_TYPE]] if config_entry.data[CONF_ADDON_TYPE] == "noncve": - model = model + " - " + HRU_DEVICES[config_entry.data[CONF_HRU_DEVICE]] + model = model + " - " + NONCVE_DEVICES[config_entry.data[CONF_NONCVE_MODEL]] self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, config_entry.data[CONF_ADDON_TYPE])}, diff --git a/custom_components/ithodaalderop/config_flow.py b/custom_components/ithodaalderop/config_flow.py index b0eb5fe..47c5a44 100644 --- a/custom_components/ithodaalderop/config_flow.py +++ b/custom_components/ithodaalderop/config_flow.py @@ -23,14 +23,14 @@ CONF_AUTOTEMP_ROOM6, CONF_AUTOTEMP_ROOM7, CONF_AUTOTEMP_ROOM8, - CONF_HRU_DEVICE, + CONF_NONCVE_MODEL, CONF_REMOTE_1, CONF_REMOTE_2, CONF_REMOTE_3, CONF_REMOTE_4, CONF_REMOTE_5, DOMAIN, - HRU_DEVICES, + NONCVE_DEVICES, ) @@ -59,7 +59,7 @@ async def async_step_user(self, user_input: Mapping[str, Any] | None = None): if user_input[CONF_ADDON_TYPE] == "cve": return await self.async_step_remotes() if user_input[CONF_ADDON_TYPE] == "noncve": - return await self.async_step_hru_device() + return await self.async_step_noncve_model() await self.async_set_unique_id( f"itho_wifi_addon_{self.config[CONF_ADDON_TYPE]}" @@ -118,28 +118,30 @@ async def async_step_rooms(self, user_input: Mapping[str, Any] | None = None): step_id="rooms", data_schema=itho_schema, last_step=True ) - async def async_step_hru_device(self, user_input: Mapping[str, Any] | None = None): + async def async_step_noncve_model( + self, user_input: Mapping[str, Any] | None = None + ): """Configure Non-CVE (HRU) Device.""" if user_input is not None: self.config.update(user_input) return await self.async_step_remotes() - options = list(HRU_DEVICES.keys()) + options = list(NONCVE_DEVICES.keys()) itho_schema = vol.Schema( { - vol.Required(CONF_HRU_DEVICE): selector( + vol.Required(CONF_NONCVE_MODEL): selector( { "select": { "options": options, "multiple": False, - "translation_key": "hrudeviceselect", + "translation_key": "noncve_model_select", } } ), } ) - return self.async_show_form(step_id="hru_device", data_schema=itho_schema) + return self.async_show_form(step_id="noncve_model", data_schema=itho_schema) async def async_step_remotes(self, user_input: Mapping[str, Any] | None = None): """Configure up to 5 remotes.""" @@ -152,7 +154,7 @@ async def async_step_remotes(self, user_input: Mapping[str, Any] | None = None): title = "Itho WiFi Add-on for " + ADDON_TYPES[self.config[CONF_ADDON_TYPE]] if self.config[CONF_ADDON_TYPE] == "noncve": - title = title + " - " + HRU_DEVICES[self.config[CONF_HRU_DEVICE]] + title = title + " - " + NONCVE_DEVICES[self.config[CONF_NONCVE_MODEL]] return self.async_create_entry( title=title, diff --git a/custom_components/ithodaalderop/const.py b/custom_components/ithodaalderop/const.py index 609ef70..5d3c649 100644 --- a/custom_components/ithodaalderop/const.py +++ b/custom_components/ithodaalderop/const.py @@ -15,7 +15,7 @@ "wpu": "WPU", } -HRU_DEVICES = { +NONCVE_DEVICES = { "hru_eco": "HRU ECO", "hru_eco_200": "HRU ECO 200", "hru_eco_250": "HRU ECO 250", @@ -46,7 +46,7 @@ } CONF_ADDON_TYPE = "addontype" -CONF_HRU_DEVICE = "hru_device" +CONF_NONCVE_MODEL = "noncve_model" CONF_AUTOTEMP_ROOM1 = "room1" CONF_AUTOTEMP_ROOM2 = "room2" diff --git a/custom_components/ithodaalderop/sensor.py b/custom_components/ithodaalderop/sensor.py index aac5552..bcf5f57 100644 --- a/custom_components/ithodaalderop/sensor.py +++ b/custom_components/ithodaalderop/sensor.py @@ -21,12 +21,12 @@ AUTOTEMP_ERROR, AUTOTEMP_MODE, CONF_ADDON_TYPE, - CONF_HRU_DEVICE, + CONF_NONCVE_MODEL, DOMAIN, - HRU_DEVICES, MANUFACTURER, MQTT_BASETOPIC, MQTT_STATETOPIC, + NONCVE_DEVICES, NONCVE_HRUECO350_ACTUAL_MODE, NONCVE_HRUECO350_GLOBAL_FAULT_CODE, NONCVE_HRUECO350_RH_ERROR_CODE, @@ -122,13 +122,13 @@ async def async_setup_entry( sensors.append(IthoSensorFan(description, config_entry)) if config_entry.data[CONF_ADDON_TYPE] == "noncve": - if config_entry.data[CONF_HRU_DEVICE] == "hru_eco": + if config_entry.data[CONF_NONCVE_MODEL] == "hru_eco": hru_sensors = HRUECOSENSORS - if config_entry.data[CONF_HRU_DEVICE] == "hru_eco_200": + if config_entry.data[CONF_NONCVE_MODEL] == "hru_eco_200": hru_sensors = HRUECO200SENSORS - if config_entry.data[CONF_HRU_DEVICE] in ["hru_eco_250", "hru_eco_300"]: + if config_entry.data[CONF_NONCVE_MODEL] in ["hru_eco_250", "hru_eco_300"]: hru_sensors = HRUECO250300SENSORS - if config_entry.data[CONF_HRU_DEVICE] == "hru_eco_350": + if config_entry.data[CONF_NONCVE_MODEL] == "hru_eco_350": hru_sensors = HRUECO350SENSORS for description in hru_sensors: @@ -173,7 +173,9 @@ def __init__( if use_base_sensor_device: model = ADDON_TYPES[config_entry.data[CONF_ADDON_TYPE]] if config_entry.data[CONF_ADDON_TYPE] == "noncve": - model = model + " - " + HRU_DEVICES[config_entry.data[CONF_HRU_DEVICE]] + model = ( + model + " - " + NONCVE_DEVICES[config_entry.data[CONF_NONCVE_MODEL]] + ) self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, config_entry.data[CONF_ADDON_TYPE])}, diff --git a/custom_components/ithodaalderop/translations/en.json b/custom_components/ithodaalderop/translations/en.json index 8e62427..ca25d81 100644 --- a/custom_components/ithodaalderop/translations/en.json +++ b/custom_components/ithodaalderop/translations/en.json @@ -18,6 +18,9 @@ "actual_mode": { "name": "Actual Mode" }, + "actual_speed": { + "name": "Actual Speed" + }, "actual_supply_fan": { "name": "Actual Supply Fan" }, @@ -27,12 +30,18 @@ "actual_temp": { "name": "Actual Temperature" }, + "air_discharge_temp": { + "name": "Air Discharge Temperature" + }, "airfilter_counter": { "name": "Airfilter Counter" }, "airquality": { "name": "Air Quality" }, + "average_air_outlet_temp": { + "name": "Average Air Outlet Temperature" + }, "balance": { "name": "Balance" }, @@ -66,6 +75,9 @@ "fan_speed": { "name": "Fan Speed" }, + "filter_use_counter": { + "name": "Filter Use Counter" + }, "flow_sensor": { "name": "Flow" }, @@ -90,9 +102,18 @@ "last_cmd_source": { "name": "Last Command Source" }, + "max_co2_level": { + "name": "Max CO2 Level" + }, + "max_rh_level": { + "name": "Max RH Level" + }, "mode": { "name": "Mode" }, + "outside_temp": { + "name": "Outside Temperature" + }, "power_kw": { "name": "Power (kW)" }, @@ -111,9 +132,15 @@ "room_temp": { "name": "Room Temperature" }, + "room_temp_setpoint": { + "name": "Room Temperature Setpoint" + }, "setpoint_temp": { "name": "Setpoint Temperature" }, + "speed_setpoint": { + "name": "Speed Setpoint" + }, "state": { "name": "State" }, @@ -138,6 +165,9 @@ "total_operation_time": { "name": "Total Operating Time" }, + "ventilation_setpoint_percentage": { + "name": "Ventilation Setpoint" + }, "well_pump_percent": { "name": "Well Pump" } @@ -152,11 +182,11 @@ "addontype": "Select the type of add-on to configure." } }, - "hru_device": { + "noncve_model": { "title": "Select Non-CVE (HRU) device model", "description": "Select the model of your Itho Unit", "data": { - "hru_device": "Itho HRU Model" + "noncve_model": "Model:" } }, "reconfigure": { @@ -231,7 +261,7 @@ "wpu": "WPU heatpump" } }, - "hrudeviceselect": { + "noncve_model_select": { "options": { "hru_eco": "HRU ECO", "hru_eco_200": "HRU ECO 200", diff --git a/custom_components/ithodaalderop/translations/nl.json b/custom_components/ithodaalderop/translations/nl.json index 6c1c21c..ffc713a 100644 --- a/custom_components/ithodaalderop/translations/nl.json +++ b/custom_components/ithodaalderop/translations/nl.json @@ -18,6 +18,9 @@ "actual_mode": { "name": "Huidige Mode" }, + "actual_speed": { + "name": "Actuele Snelheid" + }, "actual_supply_fan": { "name": "Actuele Aanvoer Ventilator" }, @@ -27,12 +30,18 @@ "actual_temp": { "name": "Actuele Temperatuur" }, + "air_discharge_temp": { + "name": "Luchtafvoertemperatuur" + }, "airfilter_counter": { "name": "Luchtfilter Teller" }, "airquality": { "name": "Luchtkwaliteit" }, + "average_air_outlet_temp": { + "name": "Gemiddelde Luchtuitlaattemperatuur" + }, "balance": { "name": "Balans" }, @@ -66,6 +75,9 @@ "fan_speed": { "name": "Ventilator Snelheid" }, + "filter_use_counter": { + "name": "Filter gebruiksteller" + }, "flow_sensor": { "name": "Stroomsnelheid" }, @@ -81,18 +93,27 @@ "highest_received_rh_value": { "name": "Hoogste RH waarde" }, + "humidity": { + "name": "Luchtvochtigheid" + }, "last_cmd_command": { "name": "Laatste Commando" }, "last_cmd_source": { "name": "Laatste Commando Bron" }, - "humidity": { - "name": "Luchtvochtigheid" + "max_co2_level": { + "name": "Max CO2 Niveau" + }, + "max_rh_level": { + "name": "Max RH Niveau" }, "mode": { "name": "Mode" }, + "outside_temp": { + "name": "Buitentemperatuur" + }, "power_kw": { "name": "Vermogen (kW)" }, @@ -111,6 +132,9 @@ "room_temp": { "name": "Kamertemperatuur" }, + "room_temp_setpoint": { + "name": "Ingestelde Kamertemperatuur" + }, "setpoint_temp": { "name": "Ingestelde temperatuur" }, @@ -120,6 +144,9 @@ "status": { "name": "Status" }, + "speed_setpoint": { + "name": "Ingestelde Snelheid" + }, "supply_fan_speed": { "name": "Toevoerventilator Snelheid" }, @@ -138,6 +165,9 @@ "total_operation_time": { "name": "Totale bedrijfstijd" }, + "ventilation_setpoint_percentage": { + "name": "Insteld Ventilatie Percentage" + }, "well_pump_percent": { "name": "Bronpomp" } @@ -152,11 +182,11 @@ "addontype": "Add-on type" } }, - "hru_device": { + "noncve_model": { "title": "Selecteer Non-CVE (HRU) model", "description": "Selecteer het model van uw Itho unit", "data": { - "hru_device": "Itho HRU Model" + "noncve_model": "Model:" } }, "remotes": { @@ -225,7 +255,7 @@ "wpu": "WPU Warmtepomp" } }, - "hrudeviceselect": { + "noncve_model_select": { "options": { "hru_eco": "HRU ECO", "hru_eco_200": "HRU ECO 200", From 391a284589125a3a7e0e5e253506f40fc0760de3 Mon Sep 17 00:00:00 2001 From: Benjamin Design & Consultancy Services <78026082+benjamin-dcs@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:44:10 +0100 Subject: [PATCH 12/12] Add translations --- custom_components/ithodaalderop/const.py | 33 ++-- .../ithodaalderop/definitions.py | 165 +++++++++++++++++- custom_components/ithodaalderop/sensor.py | 45 +++-- .../ithodaalderop/translations/en.json | 67 ++++++- .../ithodaalderop/translations/nl.json | 67 ++++++- 5 files changed, 338 insertions(+), 39 deletions(-) diff --git a/custom_components/ithodaalderop/const.py b/custom_components/ithodaalderop/const.py index 5d3c649..c3a9d38 100644 --- a/custom_components/ithodaalderop/const.py +++ b/custom_components/ithodaalderop/const.py @@ -90,24 +90,37 @@ 5: "Manual operation", } -NONCVE_HRUECO350_ACTUAL_MODE = { - 1: "low", - 2: "medium", - 3: "high", - 13: "timer", - 24: "auto", - 25: "autonight", +HRUECO250300_ERROR_CODE = { + 0: "No error", + 1: "W01 - Clean Filter", + 2: "W02 - Replace Filter", + 3: "W03 - Fan speed decreased", + 51: "B01 - Temperature outside NOT-OK", + 52: "B02 - Temperature blend NOT-OK", + 53: "B03 - Temperature waste NOT-OK", + 54: "B04 - Temperature return NOT-OK", + 55: "B05 - Temperature supply NOT-OK", + 101: "E01 - Fan is't working", +} + +HRUECO350_ACTUAL_MODE = { + 1: "Low", + 2: "Medium", + 3: "High", + 13: "Timer", + 24: "Auto", + 25: "Autonight", } # Based on user-experience. No codelist availble in the Itho Servicetool # For any additions/feedback, please create an issue in the repo of the integration -NONCVE_HRUECO350_GLOBAL_FAULT_CODE = { +HRUECO350_GLOBAL_FAULT_CODE = { 0: "No error", 7: "Filters dirty", 11: "(External) Sensor error", } -NONCVE_HRUECO350_RH_ERROR_CODE = { +HRUECO350_RH_ERROR_CODE = { 239: "Not Available", 240: "Shorted Sensor", 241: "Open Sensor", @@ -127,7 +140,7 @@ 255: "Unknown Error", } -NONCVE_HRUECO_STATUS = { +HRUECO_STATUS = { 0: "Normal", 1: "Adjust frost valve", 2: "Decelerate Supply fan", diff --git a/custom_components/ithodaalderop/definitions.py b/custom_components/ithodaalderop/definitions.py index f5cf175..9ffc98d 100644 --- a/custom_components/ithodaalderop/definitions.py +++ b/custom_components/ithodaalderop/definitions.py @@ -5,9 +5,6 @@ A diagnostic entity provides device-specific metadata or operational insights that assist in understanding the device's internal state or functioning but is not directly related to the user's environment. """ -# TODO: Add support for HRU 200 and HRU 250/300 -# Labels (json_fields) can be found @ https://github.com/arjenhiemstra/ithowifi/tree/master/software/NRG_itho_wifi/main/devices - from __future__ import annotations from collections.abc import Callable @@ -27,10 +24,12 @@ PERCENTAGE, REVOLUTIONS_PER_MINUTE, EntityCategory, + UnitOfElectricCurrent, UnitOfPower, UnitOfPressure, UnitOfTemperature, UnitOfTime, + UnitOfVolumeFlowRate, ) @@ -292,11 +291,161 @@ class IthoBinarySensorEntityDescription(BinarySensorEntityDescription): ) HRUECO250300SENSORS: tuple[IthoSensorEntityDescription, ...] = ( - # IthoSensorEntityDescription( - # json_field="Actual Mode", - # translation_key="actual_mode", - # icon="mdi:knob", - # ), + IthoSensorEntityDescription( + json_field="Absolute speed of the fan (%)", + translation_key="absolute_fanspeed", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + IthoSensorEntityDescription( + json_field="Current consumption of fan (mA)", + translation_key="current_consumption_fan", + device_class=SensorDeviceClass.CURRENT, + native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + IthoSensorEntityDescription( + json_field="Desired capacity (m3/h)", + translation_key="desired_capacity", + device_class=SensorDeviceClass.VOLUME_FLOW_RATE, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Desired current consumption of fan (mA)", + translation_key="desired_consumption_fan", + device_class=SensorDeviceClass.CURRENT, + native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + IthoSensorEntityDescription( + json_field="Error number", + translation_key="error_number", + entity_category=EntityCategory.DIAGNOSTIC, + ), + IthoSensorEntityDescription( + json_field="Highest measured CO2 (ppm)", + translation_key="highest_measured_co2_value", + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + icon="mdi:molecule-co2", + ), + IthoSensorEntityDescription( + json_field="Highest measured RH (%)", + translation_key="highest_measured_rh_value", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + IthoSensorEntityDescription( + json_field="Inlet temperature (°C)", + translation_key="inlet_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Measured blend temperature heated NTC (°C)", + translation_key="measured_blend_temp_heated", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Measured outside temperature (°C)", + translation_key="outside_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Measured temperature of mixed outside air (°C)", + translation_key="mixed_outside_air_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Measured waste temperature heated NTC (°C)", + translation_key="measured_waste_temp_heated", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Percentage that the bypass valve is open (%)", + translation_key="bypass_valve_open", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + IthoSensorEntityDescription( + json_field="Relative fanspeed (%)", + translation_key="relative_fanspeed", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="RPM of the motor (rpm)", + translation_key="motor_speed", + native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Status", + translation_key="status", + ), + IthoSensorEntityDescription( + json_field="Temperature of the blown out air of the house (°C)", + translation_key="blown_out_air_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="Temperature of the extracted air (°C)", + translation_key="extracted_air_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="The desired inlet temperature (°C)", + translation_key="desired_inlet_temp", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="The flow of the blown air (m3/h)", + translation_key="flow_blown_air", + device_class=SensorDeviceClass.VOLUME_FLOW_RATE, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, + state_class=SensorStateClass.MEASUREMENT, + ), + IthoSensorEntityDescription( + json_field="The flow of the inflated air (M3/h)", + translation_key="flow_inflated_air", + device_class=SensorDeviceClass.VOLUME_FLOW_RATE, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, + state_class=SensorStateClass.MEASUREMENT, + ), + # No native_unit_of_measurement available for mass flow + IthoSensorEntityDescription( + json_field="The mass flow of the air entering the house (kg/h)", + translation_key="mass_flow_air_enter_house_kgh", + state_class=SensorStateClass.MEASUREMENT, + ), + # No native_unit_of_measurement available for mass flow + IthoSensorEntityDescription( + json_field="The mass flow of the air leaving the house (kg/h)", + translation_key="mass_flow_air_leaving_house_kgh", + state_class=SensorStateClass.MEASUREMENT, + ), ) HRUECO350BINARYSENSORS: tuple[IthoBinarySensorEntityDescription, ...] = ( diff --git a/custom_components/ithodaalderop/sensor.py b/custom_components/ithodaalderop/sensor.py index bcf5f57..765739b 100644 --- a/custom_components/ithodaalderop/sensor.py +++ b/custom_components/ithodaalderop/sensor.py @@ -23,14 +23,15 @@ CONF_ADDON_TYPE, CONF_NONCVE_MODEL, DOMAIN, + HRUECO350_ACTUAL_MODE, + HRUECO350_GLOBAL_FAULT_CODE, + HRUECO350_RH_ERROR_CODE, + HRUECO250300_ERROR_CODE, + HRUECO_STATUS, MANUFACTURER, MQTT_BASETOPIC, MQTT_STATETOPIC, NONCVE_DEVICES, - NONCVE_HRUECO350_ACTUAL_MODE, - NONCVE_HRUECO350_GLOBAL_FAULT_CODE, - NONCVE_HRUECO350_RH_ERROR_CODE, - NONCVE_HRUECO_STATUS, UNITTYPE_ICONS, WPU_STATUS, ) @@ -367,7 +368,18 @@ def message_received(message): # HRU ECO 350 if json_field == "Actual Mode": self._extra_state_attributes = {"Code": value} - value = NONCVE_HRUECO350_ACTUAL_MODE.get(value, "Unknown mode") + value = HRUECO350_ACTUAL_MODE.get(value, "Unknown mode") + + # HRU ECO 350 + if json_field == "Air Quality (%)": + _error_description = "Unknown error code" + if isinstance(value, (int, float)) and float(value) > 100: + _error_description = "Unknown error" + value = None + + self._extra_state_attributes = { + "Error Description": _error_description, + } # HRU ECO 350 / HRU ECO if json_field in ["Airfilter counter", "Air filter counter"]: @@ -388,22 +400,23 @@ def message_received(message): "Next Maintenance Estimate": _next_maintenance_estimate, } - # HRU ECO 350 - if json_field == "Air Quality (%)": - _error_description = "" - if isinstance(value, (int, float)) and float(value) > 100: - _error_description = "Unknown error" - value = None + # HRU ECO 250/300 + if json_field == "Error number": + _description = "" + if str(value).isnumeric(): + _error_description = HRUECO250300_ERROR_CODE.get( + int(value), _description + ) self._extra_state_attributes = { - "Error Description": _error_description, + "Description": _description, } # HRU ECO 350 if json_field == "Global fault code": _description = "Unknown fault code" if str(value).isnumeric(): - _description = NONCVE_HRUECO350_GLOBAL_FAULT_CODE.get( + _description = HRUECO350_GLOBAL_FAULT_CODE.get( int(value), _description ) @@ -415,7 +428,7 @@ def message_received(message): if json_field == "Highest received RH value (%RH)": _error_description = "" if isinstance(value, (int, float)) and float(value) > 100: - _error_description = NONCVE_HRUECO350_RH_ERROR_CODE.get( + _error_description = HRUECO350_RH_ERROR_CODE.get( int(value), "Unknown error" ) value = None @@ -432,9 +445,7 @@ def message_received(message): _description = "Unknown status" if str(value).isnumeric(): - _description = NONCVE_HRUECO_STATUS.get( - int(value), _description - ) + _description = HRUECO_STATUS.get(int(value), _description) value = _description self._attr_native_value = value diff --git a/custom_components/ithodaalderop/translations/en.json b/custom_components/ithodaalderop/translations/en.json index ca25d81..efc1ecc 100644 --- a/custom_components/ithodaalderop/translations/en.json +++ b/custom_components/ithodaalderop/translations/en.json @@ -9,6 +9,9 @@ } }, "sensor": { + "absolute_fanspeed": { + "name": "Absolute Fan Speed" + }, "actual_exhaust_fan": { "name": "Actual Exhaust Fan" }, @@ -45,12 +48,21 @@ "balance": { "name": "Balance" }, + "blown_out_air_temp": { + "name": "Blown Out Air Temperature" + }, "boiler_pump_percent": { "name": "Boiler Pump" }, "boiler_temp_up": { "name": "Boiler Temp Up" }, + "bypass_valve_open": { + "name": "Bypass Valve Open" + }, + "current_consumption_fan": { + "name": "Current Fan Power Consumption" + }, "cv_pressure": { "name": "CV Pressure" }, @@ -60,6 +72,15 @@ "cv_return_temp": { "name": "CV Return Temperature" }, + "desired_capacity": { + "name": "Desired Capacity" + }, + "desired_consumption_fan": { + "name": "Desired Fan Power Consumption" + }, + "desired_inlet_temp": { + "name": "Desired Inlet Temperature" + }, "drain_fan_speed": { "name": "Drain Fan Speed" }, @@ -69,6 +90,12 @@ "error": { "name": "Error" }, + "error_number": { + "name": "Error Number" + }, + "extracted_air_temp": { + "name": "Extracted Air Temperature" + }, "fan_setpoint_rpm": { "name": "Fan Setpoint" }, @@ -78,6 +105,12 @@ "filter_use_counter": { "name": "Filter Use Counter" }, + "flow_blown_air": { + "name": "Airflow (Blown Out)" + }, + "flow_inflated_air": { + "name": "Airflow (Inflated)" + }, "flow_sensor": { "name": "Flow" }, @@ -87,30 +120,57 @@ "heat_demand": { "name": "Heat Demand Thermostat" }, + "highest_measured_co2_value": { + "name": "Highest Measured CO2 Level" + }, + "highest_measured_rh_value": { + "name": "Highest Measured Relative Humidity" + }, "highest_received_co2_value": { - "name": "Highest Received CO2 Value" + "name": "Highest Received CO2 Level" }, "highest_received_rh_value": { - "name": "Highest Received RH Value" + "name": "Highest Received Relative Humidity" }, "humidity": { "name": "Humidity" }, + "inlet_temp": { + "name": "Inlet Temperature" + }, "last_cmd_command": { "name": "Last Command" }, "last_cmd_source": { "name": "Last Command Source" }, + "mass_flow_air_enter_house_kgh": { + "name": "Mass Flow of Air Entering (kg/h)" + }, + "mass_flow_air_leaving_house_kgh": { + "name": "Mass Flow of Air Leaving (kg/h)" + }, "max_co2_level": { "name": "Max CO2 Level" }, "max_rh_level": { "name": "Max RH Level" }, + "measured_blend_temp_heated": { + "name": "Measured Blend Temperature (Heated)" + }, + "measured_waste_temp_heated": { + "name": "Measured Waste Temperature (Heated)" + }, + "mixed_outside_air_temp": { + "name": "Mixed Outside Air Temperature" + }, "mode": { "name": "Mode" }, + "motor_speed": { + "name": "Motor Speed" + }, "outside_temp": { "name": "Outside Temperature" }, @@ -120,6 +180,9 @@ "power_perc": { "name": "Power (%)" }, + "relative_fanspeed": { + "name": "Relative Fan Speed" + }, "remaining_override_timer": { "name": "Remaining Override Timer" }, diff --git a/custom_components/ithodaalderop/translations/nl.json b/custom_components/ithodaalderop/translations/nl.json index ffc713a..0b736a5 100644 --- a/custom_components/ithodaalderop/translations/nl.json +++ b/custom_components/ithodaalderop/translations/nl.json @@ -9,6 +9,9 @@ } }, "sensor": { + "absolute_fanspeed": { + "name": "Absolute Ventilatorsnelheid" + }, "actual_exhaust_fan": { "name": "Actuele Afvoer Ventilator" }, @@ -45,12 +48,21 @@ "balance": { "name": "Balans" }, + "blown_out_air_temp": { + "name": "Uitgeblazen Luchttemperatuur" + }, "boiler_pump_percent": { "name": "Boiler Pomp" }, "boiler_temp_up": { "name": "Boiler Temperatuur" }, + "bypass_valve_open": { + "name": "Bypassklep Open" + }, + "current_consumption_fan": { + "name": "Huidig Energieverbruik Ventilator" + }, "cv_pressure": { "name": "CV Druk" }, @@ -60,6 +72,15 @@ "cv_return_temp": { "name": "CV Afvoer Temperatuur" }, + "desired_capacity": { + "name": "Gewenste Capaciteit" + }, + "desired_consumption_fan": { + "name": "Gewenst Energieverbruik Ventilator" + }, + "desired_inlet_temp": { + "name": "Gewenste Inlaattemperatuur" + }, "drain_fan_speed": { "name": "Afvoerventilator Snelheid" }, @@ -69,6 +90,12 @@ "error": { "name": "Fout" }, + "error_number": { + "name": "Foutnummer" + }, + "extracted_air_temp": { + "name": "Afgevoerd Luchttemperatuur" + }, "fan_setpoint_rpm": { "name": "Ventilator Stand" }, @@ -78,6 +105,12 @@ "filter_use_counter": { "name": "Filter gebruiksteller" }, + "flow_blown_air": { + "name": "Luchtstroom (Uitgeblazen)" + }, + "flow_inflated_air": { + "name": "Luchtstroom (Ingeblazen)" + }, "flow_sensor": { "name": "Stroomsnelheid" }, @@ -87,30 +120,57 @@ "heat_demand": { "name": "Warmtevraag Thermostaat" }, + "highest_measured_co2_value": { + "name": "Hoogst Gemeten CO2-niveau" + }, + "highest_measured_rh_value": { + "name": "Hoogst Gemeten Relatieve Vochtigheid" + }, "highest_received_co2_value": { - "name": "Hoogste CO2 waarde" + "name": "Hoogste CO2-niveau" }, "highest_received_rh_value": { - "name": "Hoogste RH waarde" + "name": "Hoogste Relatieve Vochtigheid" }, "humidity": { "name": "Luchtvochtigheid" }, + "inlet_temp": { + "name": "Inlaattemperatuur" + }, "last_cmd_command": { "name": "Laatste Commando" }, "last_cmd_source": { "name": "Laatste Commando Bron" }, + "mass_flow_air_enter_house_kgh": { + "name": "Luchtmassastroom In (kg/u)" + }, + "mass_flow_air_leaving_house_kgh": { + "name": "Luchtmassastroom Uit (kg/u)" + }, "max_co2_level": { "name": "Max CO2 Niveau" }, "max_rh_level": { "name": "Max RH Niveau" }, + "measured_blend_temp_heated": { + "name": "Gemeten Mengtemperatuur (Verwarmd)" + }, + "measured_waste_temp_heated": { + "name": "Gemeten Afvaltemperatuur (Verwarmd)" + }, + "mixed_outside_air_temp": { + "name": "Gemengde Buitenluchttemperatuur" + }, "mode": { "name": "Mode" }, + "motor_speed": { + "name": "Motorsnelheid" + }, "outside_temp": { "name": "Buitentemperatuur" }, @@ -120,6 +180,9 @@ "power_perc": { "name": "Vermogen (%)" }, + "relative_fanspeed": { + "name": "Relatieve Ventilatorsnelheid" + }, "remaining_override_timer": { "name": "Resterende Override Timer" },