diff --git a/README.md b/README.md index 7c1cb85..fe1141f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Known supported units: - RCV320 P1/P2 > [!NOTE] -> The listed units are known to have been tested with the integration. Basicly all unit that use the _Dantherm Residential_ app ought to work with the integration. +> The listed units are known to have been used with the integration. Basicly all units that use the _Dantherm Residential_ app ought to work with the integration > ([Google Play](https://play.google.com/store/apps/details?id=com.dantherm.ventilation) or [Apple Store](https://apps.apple.com/dk/app/dantherm-residential/id1368468353)). > If you know of any not included in the list, please feel free to contact me [here](https://github.com/Tvalley71/dantherm/discussions/new?category=general). @@ -27,8 +27,8 @@ Known supported units: | operation_mode | Operation mode sensor | | alarm | Alarm sensor | | fan_level | Fan level sensor | -| fan1_speed | Fan 1 speed sensor | -| fan2_speed | Fan 2 speed sensor | +| fan1_speed | Fan 1 speed sensor | +| fan2_speed | Fan 2 speed sensor | | humidity | Humidity sensor\* | | air_quality | Air quality sensor\* | | exhaust_temperature | Exhaust temperature sensor | @@ -54,7 +54,7 @@ _† The entity is disabled by default._ ### Installation > [!IMPORTANT] -> Installation directly through HACS is not yet available because the integration is not yet included. This process will take some time. In the meantime, please use the manual installation method or click the below **Open HACS Repository** button. +> Installation directly through HACS is not yet available because the integration is not yet official included into HACS. This process will take some time. In the meantime, please use the manual installation method or click the below **Open HACS Repository** button. Open your Home Assistant instance and open a repository inside the Home Assistant Community Store. @@ -86,8 +86,9 @@ After installation, add the Dantherm integration to your Home Assistant configur 3. Search for "Dantherm" and select it from the list of available integrations. 4. Follow the on-screen instructions to complete the integration setup. -![Skærmbillede 2024-05-04 090018](https://github.com/Tvalley71/dantherm/assets/83084467/f085a769-c55c-45f1-952e-6ee8884eaad1) -![Skærmbillede 2024-05-04 090125](https://github.com/Tvalley71/dantherm/assets/83084467/1a66e37c-3c0e-498d-995f-c2bb5c778f35) +![Skærmbillede 2024-05-04 090018](https://github.com/user-attachments/assets/a5c2faad-2b96-438b-a761-4e24075efbf3) +![Skærmbillede 2024-05-04 090125](https://github.com/user-attachments/assets/7869346c-04e0-4980-9536-bf2cdd27cbc0) + ### Support @@ -96,14 +97,19 @@ If you encounter any issues or have questions regarding the Dantherm integration ### Screenshots -![Skærmbillede 2024-05-04 090219](https://github.com/Tvalley71/dantherm/assets/83084467/fa9b31b6-5ec8-4c3b-a381-ef7061495560) +![Skærmbillede 2024-05-04 090219](https://github.com/user-attachments/assets/e8750622-f33c-4652-b3d5-33c2f3f13c54) + +![Skærmbillede 2024-08-04 084300](https://github.com/user-attachments/assets/1f1ce55b-4a9a-4b4c-b09d-4e18e34a08a2) +![Skærmbillede 2024-08-04 084328](https://github.com/user-attachments/assets/cb4c686b-ed84-42f2-896e-6c5f0b126f52) + +![Skærmbillede 2024-08-04 084347](https://github.com/user-attachments/assets/6ecca514-7595-4b64-8e1d-1e1fffa5aae4) +![Skærmbillede 2024-08-04 084404](https://github.com/user-attachments/assets/b84b9ac7-3586-40da-9a74-2808ced478e2) + +![Skærmbillede 2024-08-04 084430](https://github.com/user-attachments/assets/814bafd5-e03f-496f-98ce-7faafe2e4729) -![Skærmbillede 2024-05-13 070737](https://github.com/Tvalley71/dantherm/assets/83084467/d6493c4e-ab10-493d-b2ec-c4f192383192) -![Skærmbillede 2024-05-13 070838](https://github.com/Tvalley71/dantherm/assets/83084467/8032983f-f55e-425e-8c55-c8d2ae918ea7) -![Skærmbillede 2024-05-04 090422](https://github.com/Tvalley71/dantherm/assets/83084467/4b2665b1-6abe-491b-8c3b-e5b3322402ee) > [!NOTE] -> Preheater and HAC module functions are currently unsupported due to limited testing possibilities. If support for these functions are desired, please contact me for potential collaborative efforts to provide the support. +> The HAC module functions are currently unsupported due to limited testing possibilities. If support for these functions are desired, please contact me for potential collaborative efforts to provide the support. ### Languages @@ -261,35 +267,37 @@ Next, insert the following code into your dashboard. If your Home Assistant setu top: 45% left: 36% font-weight: bold - text-align: center; + font-style: italic + text-align: center font-size: 100% - type: state-label entity: sensor.dantherm_humidity style: top: 29% left: 38% - font-size: 125% + font-size: 100% - type: state-label entity: select.dantherm_fan_selection style: top: 29% left: 63% font-weight: bold + font-style: italic font-size: 100% ``` #### Helper template sensor. -![Skærmbillede 2024-05-04 094747](https://github.com/Tvalley71/dantherm/assets/83084467/49b4e3b5-e419-458d-ada8-ffc3a92e0395) +![Skærmbillede 2024-05-04 094747](https://github.com/user-attachments/assets/5fc0c6dc-a1e5-4579-8453-7837037b3f9a) #### Mushroom-chips card -Example of a Mushroom-chips card displaying the current state of operation and fan level, in the order automatic, week program, manual, and standby mode. +An example of a Mushroom-chips card showing the current state of operation and fan level in a single display. This can also be achieved with many of the other entities. -![Skærmbillede 2024-05-21 104804](https://github.com/Tvalley71/dantherm/assets/83084467/075df325-03e1-4855-bb74-a4cf90780266) +![Skærmbillede 2024-05-21 104804](https://github.com/user-attachments/assets/2e35c5f9-46cf-4a77-a13c-56992ecccf3e)
@@ -348,6 +356,16 @@ Alert chip displaying any current alert along with its descriptions. A hold acti
+> [!NOTE] +> Starting from version 2024.8 of Home Assistant, the new badges can be used to achieve same results as the Mushroom chips card. + +#### Dashboard Badges + +Here are some examples of badges added to the dashboard. The pop-up that appears when clicking on a badge will vary depending on the selected entities, either displaying information or enabling manipulation of the Dantherm unit. + +![Skærmbillede badge example](https://github.com/user-attachments/assets/77ae39a9-edb2-4648-bb88-feac6a997087) + + ## Disclaimer The trademark "Dantherm" is owned by Dantherm Group A/S, a leading supplier of climate control solutions. diff --git a/custom_components/dantherm/__init__.py b/custom_components/dantherm/__init__.py index fdb33cd..75d7ceb 100644 --- a/custom_components/dantherm/__init__.py +++ b/custom_components/dantherm/__init__.py @@ -66,9 +66,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): raise ConfigEntryNotReady(f"Timeout while connecting {host}") from ex hass.data[DOMAIN][entry.entry_id] = device - hass.async_create_task( - hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + return True diff --git a/custom_components/dantherm/const.py b/custom_components/dantherm/const.py index d1a3fb6..5509d69 100644 --- a/custom_components/dantherm/const.py +++ b/custom_components/dantherm/const.py @@ -82,8 +82,8 @@ class DataClass(Enum): Float32 = 8 -class OpMode(int): - """Dantherm mode of operation class.""" +class CurrentUnitMode(int): + """Dantherm current unit mode class.""" Standby = 0 Manual = 1 @@ -95,6 +95,29 @@ class OpMode(int): Night = 16 +class ActiveUnitMode(int): + """Dantherm active unit mode class.""" + + Automatic = 0x0002 + Manuel = 0x0004 + WeekProgram = 0x0008 + + Away = StartAway = 0x0010 + EndAway = 0x8010 + + Night = NightEnable = 0x0020 + NightDisable = 0x8020 + + Fireplace = StartFireplace = 0x0040 + EndFireplace = 0x8040 + + ManuelBypass = SelectManuelBypass = 0x0080 + DeselectManuelBypass = 0x8080 + + Summer = StartSummer = 0x0800 + EndSummer = 0x8800 + + STATE_STANDBY: Final = "standby" STATE_AUTOMATIC: Final = "automatic" STATE_MANUAL: Final = "manual" @@ -102,6 +125,7 @@ class OpMode(int): STATE_AWAY: Final = "away" STATE_SUMMER: Final = "summer" STATE_FIREPLACE: Final = "fireplace" +STATE_NIGHT: Final = "night" class BypassDamperState(int): @@ -202,6 +226,7 @@ class DanthermSelectEntityDescription(SelectEntityDescription): class DanthermSensorEntityDescription(SensorEntityDescription): """Dantherm Sensor Entity Description.""" + icon_zero: str | None = None data_address: int | None = None data_getinternal: str | None = None data_precision: int | None = None @@ -210,7 +235,6 @@ class DanthermSensorEntityDescription(SensorEntityDescription): data_exclude_if_above: int | None = None data_exclude_if_below: int | None = None data_entity: str | None = None - data_zero_icon: str | None = None data_class: DataClass = DataClass.UInt16 component_class: ComponentClass = None @@ -302,6 +326,7 @@ class DanthermSwitchEntityDescription(SwitchEntityDescription): "away", "summer", "fireplace", + "night", ], ), DanthermSelectEntityDescription( @@ -330,8 +355,8 @@ class DanthermSwitchEntityDescription(SwitchEntityDescription): DanthermSensorEntityDescription( key="alarm", icon="mdi:alert-circle-outline", + icon_zero="mdi:alert-circle-check-outline", data_getinternal="get_alarm", - data_zero_icon="mdi:alert-circle-check-outline", ), DanthermSensorEntityDescription( key="fan_level", @@ -340,22 +365,26 @@ class DanthermSwitchEntityDescription(SwitchEntityDescription): DanthermSensorEntityDescription( key="fan1_speed", icon="mdi:fan", + icon_zero="mdi:fan-off", data_class=DataClass.Float32, data_address=100, - data_zero_icon="mdi:fan-off", native_unit_of_measurement="rpm", data_precision=0, state_class=SensorStateClass.MEASUREMENT, + entity_registry_visible_default=True, + entity_registry_enabled_default=False, ), DanthermSensorEntityDescription( key="fan2_speed", icon="mdi:fan", + icon_zero="mdi:fan-off", data_class=DataClass.Float32, data_address=102, - data_zero_icon="mdi:fan-off", native_unit_of_measurement="rpm", data_precision=0, state_class=SensorStateClass.MEASUREMENT, + entity_registry_visible_default=True, + entity_registry_enabled_default=False, ), DanthermSensorEntityDescription( key="humidity", @@ -466,12 +495,11 @@ class DanthermSwitchEntityDescription(SwitchEntityDescription): DanthermSwitchEntityDescription( key="away_mode", data_setinternal="set_active_unit_mode", - state_seton=0x10, - state_setoff=0x8010, - data_getinternal="get_current_unit_mode", - state_on=OpMode.Away, + data_getinternal="get_away_mode", state_suspend_for=30, + state_on=ActiveUnitMode.StartAway, icon_on="mdi:bag-suitcase-outline", + state_off=ActiveUnitMode.EndAway, icon_off="mdi:bag-suitcase-off-outline", device_class=SwitchDeviceClass.SWITCH, ), @@ -480,9 +508,9 @@ class DanthermSwitchEntityDescription(SwitchEntityDescription): data_setinternal="set_active_unit_mode", data_getinternal="get_active_unit_mode", state_suspend_for=30, - state_on=0x20, + state_on=ActiveUnitMode.NightEnable, icon_on="mdi:sleep", - state_off=0x8020, + state_off=ActiveUnitMode.NightDisable, icon_off="mdi:sleep-off", device_class=SwitchDeviceClass.SWITCH, entity_category=EntityCategory.CONFIG, @@ -490,12 +518,11 @@ class DanthermSwitchEntityDescription(SwitchEntityDescription): DanthermSwitchEntityDescription( key="fireplace_mode", data_setinternal="set_active_unit_mode", - state_seton=0x40, - state_setoff=0x8040, - data_getinternal="get_current_unit_mode", - state_on=OpMode.Fireplace, + data_getinternal="get_fireplace_mode", state_suspend_for=30, + state_seton=ActiveUnitMode.StartFireplace, icon_on="mdi:fireplace", + state_setoff=ActiveUnitMode.EndFireplace, icon_off="mdi:fireplace-off", device_class=SwitchDeviceClass.SWITCH, ), @@ -504,9 +531,9 @@ class DanthermSwitchEntityDescription(SwitchEntityDescription): data_setinternal="set_active_unit_mode", data_getinternal="get_active_unit_mode", state_suspend_for=30, - state_on=0x80, + state_on=ActiveUnitMode.SelectManuelBypass, icon_on="mdi:hand-back-right-outline", - state_off=0x8080, + state_off=ActiveUnitMode.DeselectManuelBypass, icon_off="mdi:hand-back-right-off-outline", component_class=ComponentClass.Bypass, device_class=SwitchDeviceClass.SWITCH, @@ -514,12 +541,11 @@ class DanthermSwitchEntityDescription(SwitchEntityDescription): DanthermSwitchEntityDescription( key="summer_mode", data_setinternal="set_active_unit_mode", - state_seton=0x800, - state_setoff=0x8800, - data_getinternal="get_current_unit_mode", - state_on=OpMode.Summer, + data_getinternal="get_summer_mode", state_suspend_for=30, + state_seton=ActiveUnitMode.StartSummer, icon_on="mdi:weather-sunny", + state_setoff=ActiveUnitMode.EndSummer, icon_off="mdi:weather-sunny-off", device_class=SwitchDeviceClass.SWITCH, ), diff --git a/custom_components/dantherm/cover.py b/custom_components/dantherm/cover.py index 78ac7c4..84606cd 100644 --- a/custom_components/dantherm/cover.py +++ b/custom_components/dantherm/cover.py @@ -1,6 +1,5 @@ """Cover implementation.""" -from datetime import datetime import logging from typing import Any @@ -114,7 +113,7 @@ def native_value(self): return self._device.data.get(self.key, None) - async def async_update(self, now: datetime | None = None) -> None: + async def async_update(self) -> None: """Update the state of the cover.""" if self.entity_description.data_getinternal: diff --git a/custom_components/dantherm/device.py b/custom_components/dantherm/device.py index 57a7dc6..412efd1 100644 --- a/custom_components/dantherm/device.py +++ b/custom_components/dantherm/device.py @@ -20,12 +20,14 @@ STATE_AWAY, STATE_FIREPLACE, STATE_MANUAL, + STATE_NIGHT, STATE_STANDBY, STATE_SUMMER, STATE_WEEKPROGRAM, + ActiveUnitMode, BypassDamperState, + CurrentUnitMode, DataClass, - OpMode, ) _LOGGER = logging.getLogger(__name__) @@ -44,11 +46,11 @@ def __init__( async def async_added_to_hass(self): """Register entity for refresh interval.""" - self._device.async_add_refresh_entity(self) + await self._device.async_add_refresh_entity(self) async def async_will_remove_from_hass(self) -> None: """Unregister entity for refresh interval.""" - self._device.async_remove_refresh_entity(self) + await self._device.async_remove_refresh_entity(self) def suspend_refresh(self, seconds: int): """Suspend entity refresh for specified number of seconds.""" @@ -132,8 +134,12 @@ def __init__( self._active_unit_mode = None self._fan_level = None self._alarm = None + self._bypass_damper_enabled = False + self._manual_bypass_mode_enabled = False self._bypass_damper = None + self._filter_lifetime_enabled = False self._filter_lifetime = None + self._filter_remain_enabled = False self._filter_remain = None self._available = True self._read_errors = 0 @@ -214,18 +220,40 @@ async def async_install_entity(self, description: EntityDescription) -> bool: _LOGGER.debug("Excluding an entity=%s", description.key) return False - def async_add_refresh_entity(self, entity): + async def async_add_refresh_entity(self, entity): """Add entity for refresh.""" + # This is the first entity, set up interval. if not self._entities: self._entity_refresh_method = async_track_time_interval( self._hass, self.async_refresh_entities, self._scan_interval ) + if entity.key == "bypass_damper": + self._bypass_damper_enabled = True + elif entity.key == "manual_bypass_mode": + self._manual_bypass_mode_enabled = True + elif entity.key == "filter_lifetime": + self._filter_lifetime_enabled = True + elif entity.key == "filter_remain": + self._filter_remain_enabled = True + + _LOGGER.debug("Adding refresh entity=%s", entity.name) self._entities.append(entity) - def async_remove_refresh_entity(self, entity): + async def async_remove_refresh_entity(self, entity): """Remove entity for refresh.""" + + if entity.key == "bypass_damper": + self._bypass_damper_enabled = False + elif entity.key == "manual_bypass_mode": + self._manual_bypass_mode_enabled = False + elif entity.key == "filter_lifetime": + self._filter_lifetime_enabled = False + elif entity.key == "filter_remain": + self._filter_remain_enabled = False + + _LOGGER.debug("Removing refresh entity=%s", entity.name) self._entities.remove(entity) if not self._entities: @@ -255,14 +283,23 @@ async def async_refresh_entities(self, _now: int | None = None) -> None: self._alarm = await self._read_holding_uint32(516) _LOGGER.debug("Alarm = %s", self._alarm) - self._bypass_damper = await self._read_holding_int32(198) - _LOGGER.debug("Bypass damper = %s", self._bypass_damper) + if self._bypass_damper_enabled or self._manual_bypass_mode_enabled: + self._bypass_damper = await self._read_holding_int32(198) + _LOGGER.debug("Bypass damper = %s", self._bypass_damper) + else: + self._bypass_damper = None - self._filter_lifetime = await self._read_holding_uint32(556) - _LOGGER.debug("Filter lifetime = %s", self._filter_lifetime) + if self._filter_lifetime_enabled: + self._filter_lifetime = await self._read_holding_uint32(556) + _LOGGER.debug("Filter lifetime = %s", self._filter_lifetime) + else: + self._filter_lifetime = None - self._filter_remain = await self._read_holding_uint32(554) - _LOGGER.debug("Filter remain = %s", self._filter_remain) + if self._filter_remain_enabled: + self._filter_remain = await self._read_holding_uint32(554) + _LOGGER.debug("Filter remain = %s", self._filter_remain) + else: + self._filter_remain = None for entity in self._entities: await self.async_refresh_entity(entity) @@ -279,9 +316,7 @@ async def async_refresh_entity(self, entity: DanthermEntity) -> None: return _LOGGER.debug("Refresh entity=%s", entity.name) - await entity.async_update_ha_state(True) - entity.async_write_ha_state() @property def available(self) -> bool: @@ -307,27 +342,53 @@ def get_active_unit_mode(self): def get_operation_selection(self): """Get operation selection.""" - if self._active_unit_mode is None: + if self._active_unit_mode is None or self._current_unit_mode is None: return None - if self._active_unit_mode == 0: - return STATE_STANDBY - if self._active_unit_mode & 0x10 == 0x10: # away mode + + if self._current_unit_mode == CurrentUnitMode.Away: return STATE_AWAY - if self._active_unit_mode & 0x800 == 0x800: # summer mode + if self._current_unit_mode == CurrentUnitMode.Summer: return STATE_SUMMER - if self._active_unit_mode & 0x40 == 0x40: # boost fireplace mode + if self._current_unit_mode == CurrentUnitMode.Fireplace: return STATE_FIREPLACE - if self._active_unit_mode & 2 == 2: # demand mode + if self._current_unit_mode == CurrentUnitMode.Night: + return STATE_NIGHT + + if self._active_unit_mode == 0 or self._fan_level == 0: + return STATE_STANDBY + + if ( + self._active_unit_mode & ActiveUnitMode.Automatic + == ActiveUnitMode.Automatic + ): return STATE_AUTOMATIC - if self._active_unit_mode & 4 == 4: # manual mode - if self._fan_level == 0: - return STATE_STANDBY # if fan level is 0 return standby - return STATE_MANUAL # manual - if self._active_unit_mode & 8 == 8: # week program + + if self._active_unit_mode & ActiveUnitMode.Manuel == ActiveUnitMode.Manuel: + return STATE_MANUAL + + if ( + self._active_unit_mode & ActiveUnitMode.WeekProgram + == ActiveUnitMode.WeekProgram + ): return STATE_WEEKPROGRAM _LOGGER.debug("Unknown mode of operation=%s", self._active_unit_mode) - return STATE_MANUAL # manual + return STATE_MANUAL + + @property + def get_operation_mode_icon(self) -> str: + """Get operation mode icon.""" + + result = self.get_fan_level + if not result: + return "mdi:fan-off" + if result == 1: + return "mdi:fan-speed-1" + if result == 2: + return "mdi:fan-speed-2" + if result == 3: + return "mdi:fan-speed-3" + return "mdi:fan-plus" @property def get_fan_level_selection_icon(self) -> str: @@ -358,19 +419,19 @@ def get_fan_level_icon(self) -> str: return "mdi:fan-alert" result = self.get_current_unit_mode - if result == OpMode.Standby: + if result == CurrentUnitMode.Standby: return "mdi:fan-off" - if result == OpMode.Away: + if result == CurrentUnitMode.Away: return "mdi:bag-suitcase" - if result == OpMode.Summer: + if result == CurrentUnitMode.Summer: return "mdi:weather-sunny" - if result == OpMode.Fireplace: + if result == CurrentUnitMode.Fireplace: return "mdi:fire" - if result == OpMode.Night: + if result == CurrentUnitMode.Night: return "mdi:weather-night" - if result == OpMode.Automatic: + if result == CurrentUnitMode.Automatic: return "mdi:fan-auto" - if result == OpMode.WeekProgram: + if result == CurrentUnitMode.WeekProgram: return "mdi:fan-clock" result = self.get_operation_selection @@ -405,6 +466,49 @@ def get_bypass_damper_icon(self) -> str: return "mdi:valve-open" return "mdi:valve" + @property + def get_away_mode(self) -> bool | None: + """Get away mode.""" + + if self._current_unit_mode is None or self._active_unit_mode is None: + return None + + if ( + self._current_unit_mode == CurrentUnitMode.Away + or self._active_unit_mode & ActiveUnitMode.Away == ActiveUnitMode.Away + ): + return True + return False + + @property + def get_fireplace_mode(self) -> bool | None: + """Get fireplace mode.""" + + if self._current_unit_mode is None or self._active_unit_mode is None: + return None + + if ( + self._current_unit_mode == CurrentUnitMode.Fireplace + or self._active_unit_mode & ActiveUnitMode.Fireplace + == ActiveUnitMode.Fireplace + ): + return True + return False + + @property + def get_summer_mode(self) -> bool | None: + """Get summer mode.""" + + if self._current_unit_mode is None or self._active_unit_mode is None: + return None + + if ( + self._current_unit_mode == CurrentUnitMode.Summer + or self._active_unit_mode & ActiveUnitMode.Summer == ActiveUnitMode.Summer + ): + return True + return False + @property def get_filter_lifetime(self): """Get filter lifetime.""" @@ -441,21 +545,17 @@ async def set_operation_selection(self, value): """Set operation mode selection.""" if value == STATE_STANDBY: - await self.set_active_unit_mode(4) # manual mode + await self.set_active_unit_mode(ActiveUnitMode.Manuel) if self._fan_level != 0: await self.set_fan_level(0) elif value == STATE_AUTOMATIC: - await self.set_active_unit_mode(2) # demand mode + await self.set_active_unit_mode(ActiveUnitMode.Automatic) elif value == STATE_MANUAL: - await self.set_active_unit_mode(4) # manual mode + await self.set_active_unit_mode(ActiveUnitMode.Manuel) elif value == STATE_WEEKPROGRAM: - await self.set_active_unit_mode(8) # week program mode + await self.set_active_unit_mode(ActiveUnitMode.WeekProgram) elif value == STATE_AWAY: - await self.set_active_unit_mode(0x0010) # away mode - elif value == STATE_SUMMER: - await self.set_active_unit_mode(0x0800) # summer mode - elif value == STATE_FIREPLACE: - await self.set_active_unit_mode(0x0040) # boost fireplace mode + await self.set_active_unit_mode(ActiveUnitMode.StartAway) async def set_fan_level(self, value): """Set fan level.""" diff --git a/custom_components/dantherm/number.py b/custom_components/dantherm/number.py index 80e8977..ce4e21e 100644 --- a/custom_components/dantherm/number.py +++ b/custom_components/dantherm/number.py @@ -56,7 +56,7 @@ async def async_set_native_value(self, value: int) -> None: ) async def async_update(self) -> None: - """Read holding register.""" + """Update the state of the number.""" if hasattr(self._device, f"get_{self.key}_attrs"): self._attr_extra_state_attributes = getattr( diff --git a/custom_components/dantherm/select.py b/custom_components/dantherm/select.py index 07ca103..0d3c076 100644 --- a/custom_components/dantherm/select.py +++ b/custom_components/dantherm/select.py @@ -59,10 +59,9 @@ async def async_select_option(self, option: str) -> None: await self._device.write_holding_registers( description=self.entity_description, value=int(option) ) - self._attr_force_update = True async def async_update(self) -> None: - """Fetch new state data for the select.""" + """Update state of the select.""" if self.entity_description.data_getinternal: result = getattr(self._device, self.entity_description.data_getinternal) diff --git a/custom_components/dantherm/sensor.py b/custom_components/dantherm/sensor.py index 33b8722..6737bf9 100644 --- a/custom_components/dantherm/sensor.py +++ b/custom_components/dantherm/sensor.py @@ -52,13 +52,13 @@ def icon(self) -> str | None: result = super().icon if hasattr(self._device, f"get_{self.key}_icon"): result = getattr(self._device, f"get_{self.key}_icon") - elif self.entity_description.data_zero_icon and not self._attr_state: - result = self.entity_description.data_zero_icon + elif self.entity_description.icon_zero and not self.native_value: + result = self.entity_description.icon_zero return result async def async_update(self) -> None: - """Read holding register.""" + """Update the state of the sensor.""" if hasattr(self._device, f"get_{self.key}_attrs"): self._attr_extra_state_attributes = getattr( @@ -78,4 +78,4 @@ async def async_update(self) -> None: self._attr_available = False else: self._attr_available = True - self._device.data[self.key] = self._attr_state = result + self._device.data[self.key] = result diff --git a/custom_components/dantherm/switch.py b/custom_components/dantherm/switch.py index 36006ae..a8bf44d 100644 --- a/custom_components/dantherm/switch.py +++ b/custom_components/dantherm/switch.py @@ -83,7 +83,7 @@ async def async_turn_on(self, **kwargs): ) async def async_update(self) -> None: - """Read holding register.""" + """Update the state of the switch.""" if self.attr_suspend_refresh: if self.attr_suspend_refresh > datetime.now(): @@ -103,11 +103,11 @@ async def async_update(self) -> None: self._attr_available = False else: self._attr_available = True - if ( + if type(result) is bool: + self._attr_is_on = result + elif ( result & self.entity_description.state_on ) == self.entity_description.state_on: self._attr_is_on = True else: self._attr_is_on = False - - self._device.data[self.key] = self._attr_is_on diff --git a/custom_components/dantherm/translations/da.json b/custom_components/dantherm/translations/da.json index 7ba8459..fe98cfa 100644 --- a/custom_components/dantherm/translations/da.json +++ b/custom_components/dantherm/translations/da.json @@ -39,7 +39,8 @@ "week_program": "Ugeprogram", "away": "Rejse", "summer": "Sommer", - "fireplace": "Brændeovn" + "fireplace": "Brændeovn", + "night": "Nat" } }, "fan_level_selection": { diff --git a/custom_components/dantherm/translations/en.json b/custom_components/dantherm/translations/en.json index f47f43f..088280a 100644 --- a/custom_components/dantherm/translations/en.json +++ b/custom_components/dantherm/translations/en.json @@ -39,7 +39,8 @@ "week_program": "Week Program", "away": "Away", "summer": "Summer", - "fireplace": "Fireplace" + "fireplace": "Fireplace", + "night": "Night" } }, "fan_level_selection": { diff --git a/custom_components/dantherm/translations/fr.json b/custom_components/dantherm/translations/fr.json index d8657ee..17485f5 100644 --- a/custom_components/dantherm/translations/fr.json +++ b/custom_components/dantherm/translations/fr.json @@ -39,7 +39,8 @@ "week_program": "Programme hebdo", "away": "Absent", "summer": "Été", - "fireplace": "Cheminée" + "fireplace": "Cheminée", + "night": "Nuit" } }, "fan_level_selection": {