From cf6c38a75d5ead994b0cbcb335c772149b4b8c45 Mon Sep 17 00:00:00 2001 From: mkmer Date: Sun, 1 Dec 2024 23:16:16 +0000 Subject: [PATCH 01/13] add humidifier platform (first pass) --- .../components/honeywell/__init__.py | 2 +- .../components/honeywell/humidifier.py | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/honeywell/humidifier.py diff --git a/homeassistant/components/honeywell/__init__.py b/homeassistant/components/honeywell/__init__.py index a8ee5975914c6d..eb89ba2a6817d1 100644 --- a/homeassistant/components/honeywell/__init__.py +++ b/homeassistant/components/honeywell/__init__.py @@ -22,7 +22,7 @@ ) UPDATE_LOOP_SLEEP_TIME = 5 -PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.SWITCH] +PLATFORMS = [Platform.CLIMATE, Platform.HUMIDIFIER, Platform.SENSOR, Platform.SWITCH] MIGRATE_OPTIONS_KEYS = {CONF_COOL_AWAY_TEMPERATURE, CONF_HEAT_AWAY_TEMPERATURE} diff --git a/homeassistant/components/honeywell/humidifier.py b/homeassistant/components/honeywell/humidifier.py new file mode 100644 index 00000000000000..37b0b60a1620fa --- /dev/null +++ b/homeassistant/components/honeywell/humidifier.py @@ -0,0 +1,93 @@ +"""Support for Honeywell (de)humidifiers.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any + +from aiosomecomfort.device import Device + +from homeassistant.components.humidifier import ( + HumidifierDeviceClass, + HumidifierEntity, + HumidifierEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import HoneywellData +from .const import DOMAIN + +HUMIDIFIER_KEY = "humidifier" +DEHUMIDIFIER_KEY = "dehumidfier" + + +@dataclass(frozen=True, kw_only=True) +class HoneywellHumidifierEntityDescription(HumidifierEntityDescription): + """Describes a Honeywell sensor entity.""" + + current_humidity: Callable[[Device], Any] | None = None + humidity: Callable[[Device], Any] | None = None + + # value_fn: Callable[[Device], Any] + # unit_fn: Callable[[Device], Any] + + +HUMIDIFIERS: dict[str, HoneywellHumidifierEntityDescription] = { + "Humidifier": HoneywellHumidifierEntityDescription( + key=HUMIDIFIER_KEY, + current_humidity=lambda device: device.current_humidity, + humidity=lambda device: device.set_humidifier_setpoint, + device_class=HumidifierDeviceClass.HUMIDIFIER, + ), + "Dehumidifier": HoneywellHumidifierEntityDescription( + key=DEHUMIDIFIER_KEY, + current_humidity=lambda device: device.current_humidity, + humidity=lambda device: device.set_dehumidifier_setpoint, + device_class=HumidifierDeviceClass.DEHUMIDIFIER, + ), +} + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Honeywell (de)humidifier dynamically.""" + # hass_data = config_entry.runtime_data + data: HoneywellData = hass.data[DOMAIN][config_entry.entry_id] + entities: list = [] + for device in data.devices.values(): + if device.has_humidifer: + entities.append(HoneywellHumidifier(device, HUMIDIFIERS["Humidifier"])) + if device.has_dehumidifier: + entities.append(HoneywellHumidifier(device, HUMIDIFIERS["Dehumidifier"])) + + async_add_entities(entities) + + +class HoneywellHumidifier(HumidifierEntity): + """Representation of a Honeywell US (De)Humidifier.""" + + entity_description: HoneywellHumidifierEntityDescription + _attr_has_entity_name = True + + def __init__(self, device, description) -> None: + """Initialize the (De)Humidifier.""" + self._device = device + self.entity_description = description + self._attr_unique_id = f"{device.deviceid}_{description.key}" + self._attr_native_unit_of_measurement = description.unit_fn(device) + + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, device.deviceid)}, + name=device.name, + manufacturer="Honeywell", + ) + + +# Look at Tuya humidifier for help.... From aea14840c0f181952e9c0ca137a57aded0d17063 Mon Sep 17 00:00:00 2001 From: mkmer Date: Mon, 2 Dec 2024 19:21:12 +0000 Subject: [PATCH 02/13] finish humidifier functions --- .../components/honeywell/humidifier.py | 80 +++++++++++++++++-- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/honeywell/humidifier.py b/homeassistant/components/honeywell/humidifier.py index 37b0b60a1620fa..97f4dde6e8c63b 100644 --- a/homeassistant/components/honeywell/humidifier.py +++ b/homeassistant/components/honeywell/humidifier.py @@ -29,24 +29,39 @@ class HoneywellHumidifierEntityDescription(HumidifierEntityDescription): """Describes a Honeywell sensor entity.""" - current_humidity: Callable[[Device], Any] | None = None - humidity: Callable[[Device], Any] | None = None - - # value_fn: Callable[[Device], Any] - # unit_fn: Callable[[Device], Any] + current_humidity: Callable[[Device], Any] + current_set_humidity: Callable[[Device], Any] + max_humidity: Callable[[Device], Any] + min_humidity: Callable[[Device], Any] + set_humidity: Callable[[Device], Any] + mode: Callable[[Device], Any] + off: Callable[[Device], Any] + on: Callable[[Device], Any] HUMIDIFIERS: dict[str, HoneywellHumidifierEntityDescription] = { "Humidifier": HoneywellHumidifierEntityDescription( key=HUMIDIFIER_KEY, current_humidity=lambda device: device.current_humidity, - humidity=lambda device: device.set_humidifier_setpoint, + set_humidity=lambda device: device.set_humidifier_setpoint, + min_humidity=lambda device: device.humidifier_lower_limit, + max_humidity=lambda device: device.humidifier_upper_limit, + current_set_humidity=lambda device: device.humidifier_setpoint, + mode=lambda device: device.humidifer_mode, + off=lambda device: device.set_dehumidifer_off, + on=lambda device: device.set_dehumidifer_auto, device_class=HumidifierDeviceClass.HUMIDIFIER, ), "Dehumidifier": HoneywellHumidifierEntityDescription( key=DEHUMIDIFIER_KEY, current_humidity=lambda device: device.current_humidity, - humidity=lambda device: device.set_dehumidifier_setpoint, + set_humidity=lambda device: device.set_dehumidifier_setpoint, + min_humidity=lambda device: device.dehumidifier_lower_limit, + max_humidity=lambda device: device.dehumidifier_upper_limit, + current_set_humidity=lambda device: device.dehumidifier_setpoint, + mode=lambda device: device.dehumidifer_mode, + off=lambda device: device.set_dehumidifer_off, + on=lambda device: device.set_dehumidifer_auto, device_class=HumidifierDeviceClass.DEHUMIDIFIER, ), } @@ -82,12 +97,61 @@ def __init__(self, device, description) -> None: self.entity_description = description self._attr_unique_id = f"{device.deviceid}_{description.key}" self._attr_native_unit_of_measurement = description.unit_fn(device) - + self._set_humidity = description.current_set_humidity(device) + self._attr_min_humidity = description.min_humidity(device) + self._attr_max_humidity = description.max_humidity(device) + self._current_humidity = description.current_humidity(device) + self._mode = description.mode(device) self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, device.deviceid)}, name=device.name, manufacturer="Honeywell", ) + @property + def is_on(self) -> bool: + """Return the device is on or off.""" + return self.entity_description.mode(self._device) != 0 + + @property + def mode(self) -> str | None: + """Return the current mode.""" + return self.entity_description.mode(self._device) + + @property + def target_humidity(self) -> int | None: + """Return the humidity we try to reach.""" + if self._set_humidity is None: + return None + + humidity = self.entity_description.current_set_humidity(self._device) + if humidity is None: + return None + + return humidity + + @property + def current_humidity(self) -> int | None: + """Return the current humidity.""" + if self._current_humidity is None: + return None + return self.entity_description.current_humidity(self._device) + + def turn_on(self, **kwargs: Any) -> None: + """Turn the device on.""" + self.entity_description.on(self._device) + + def turn_off(self, **kwargs: Any) -> None: + """Turn the device off.""" + self.entity_description.off(self._device) + + def set_humidity(self, humidity: int) -> None: + """Set new target humidity.""" + if self._set_humidity is None: + raise RuntimeError( + "Cannot set humidity, device doesn't provide methods to set it" + ) + self.entity_description.set_humidity(self._device) + # Look at Tuya humidifier for help.... From 1e6cdc7222e2debb07899d0b84ddae625ffc2532 Mon Sep 17 00:00:00 2001 From: mkmer Date: Mon, 2 Dec 2024 19:30:20 +0000 Subject: [PATCH 03/13] set_humidity_setpoint missing value --- homeassistant/components/honeywell/humidifier.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/honeywell/humidifier.py b/homeassistant/components/honeywell/humidifier.py index 97f4dde6e8c63b..239257ca14d484 100644 --- a/homeassistant/components/honeywell/humidifier.py +++ b/homeassistant/components/honeywell/humidifier.py @@ -33,7 +33,7 @@ class HoneywellHumidifierEntityDescription(HumidifierEntityDescription): current_set_humidity: Callable[[Device], Any] max_humidity: Callable[[Device], Any] min_humidity: Callable[[Device], Any] - set_humidity: Callable[[Device], Any] + set_humidity: Callable[[Device, Any], Any] mode: Callable[[Device], Any] off: Callable[[Device], Any] on: Callable[[Device], Any] @@ -43,7 +43,7 @@ class HoneywellHumidifierEntityDescription(HumidifierEntityDescription): "Humidifier": HoneywellHumidifierEntityDescription( key=HUMIDIFIER_KEY, current_humidity=lambda device: device.current_humidity, - set_humidity=lambda device: device.set_humidifier_setpoint, + set_humidity=lambda device, humidity: device.set_humidifier_setpoint(humidity), min_humidity=lambda device: device.humidifier_lower_limit, max_humidity=lambda device: device.humidifier_upper_limit, current_set_humidity=lambda device: device.humidifier_setpoint, @@ -55,7 +55,9 @@ class HoneywellHumidifierEntityDescription(HumidifierEntityDescription): "Dehumidifier": HoneywellHumidifierEntityDescription( key=DEHUMIDIFIER_KEY, current_humidity=lambda device: device.current_humidity, - set_humidity=lambda device: device.set_dehumidifier_setpoint, + set_humidity=lambda device, humidity: device.set_dehumidifier_setpoint( + humidity + ), min_humidity=lambda device: device.dehumidifier_lower_limit, max_humidity=lambda device: device.dehumidifier_upper_limit, current_set_humidity=lambda device: device.dehumidifier_setpoint, @@ -151,7 +153,7 @@ def set_humidity(self, humidity: int) -> None: raise RuntimeError( "Cannot set humidity, device doesn't provide methods to set it" ) - self.entity_description.set_humidity(self._device) + self.entity_description.set_humidity(self._device, humidity) # Look at Tuya humidifier for help.... From 76697d97fdf8aaf31fd59236ed8eb7d9f7774b55 Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 4 Dec 2024 14:26:40 +0000 Subject: [PATCH 04/13] Use runtime_data --- homeassistant/components/honeywell/humidifier.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/honeywell/humidifier.py b/homeassistant/components/honeywell/humidifier.py index 239257ca14d484..1fb13a08774480 100644 --- a/homeassistant/components/honeywell/humidifier.py +++ b/homeassistant/components/honeywell/humidifier.py @@ -75,11 +75,10 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Honeywell (de)humidifier dynamically.""" - # hass_data = config_entry.runtime_data - data: HoneywellData = hass.data[DOMAIN][config_entry.entry_id] + data: HoneywellData = config_entry.runtime_data entities: list = [] for device in data.devices.values(): - if device.has_humidifer: + if device.has_humidifier: entities.append(HoneywellHumidifier(device, HUMIDIFIERS["Humidifier"])) if device.has_dehumidifier: entities.append(HoneywellHumidifier(device, HUMIDIFIERS["Dehumidifier"])) From 0bd93758c97c2f620653541c0e9ef2fe1da50fd3 Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 4 Dec 2024 17:37:28 +0000 Subject: [PATCH 05/13] Bump AIOSomecomfort Add strings Fix typos --- .../components/honeywell/humidifier.py | 37 ++++++++----------- .../components/honeywell/strings.json | 8 ++++ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/honeywell/humidifier.py b/homeassistant/components/honeywell/humidifier.py index 1fb13a08774480..cc85cafb490c27 100644 --- a/homeassistant/components/honeywell/humidifier.py +++ b/homeassistant/components/honeywell/humidifier.py @@ -22,7 +22,7 @@ from .const import DOMAIN HUMIDIFIER_KEY = "humidifier" -DEHUMIDIFIER_KEY = "dehumidfier" +DEHUMIDIFIER_KEY = "dehumidifier" @dataclass(frozen=True, kw_only=True) @@ -42,18 +42,20 @@ class HoneywellHumidifierEntityDescription(HumidifierEntityDescription): HUMIDIFIERS: dict[str, HoneywellHumidifierEntityDescription] = { "Humidifier": HoneywellHumidifierEntityDescription( key=HUMIDIFIER_KEY, + translation_key=HUMIDIFIER_KEY, current_humidity=lambda device: device.current_humidity, set_humidity=lambda device, humidity: device.set_humidifier_setpoint(humidity), min_humidity=lambda device: device.humidifier_lower_limit, max_humidity=lambda device: device.humidifier_upper_limit, current_set_humidity=lambda device: device.humidifier_setpoint, - mode=lambda device: device.humidifer_mode, - off=lambda device: device.set_dehumidifer_off, - on=lambda device: device.set_dehumidifer_auto, + mode=lambda device: device.humidifier_mode, + off=lambda device: device.set_humidifier_off(), + on=lambda device: device.set_humidifier_auto(), device_class=HumidifierDeviceClass.HUMIDIFIER, ), "Dehumidifier": HoneywellHumidifierEntityDescription( key=DEHUMIDIFIER_KEY, + translation_key=DEHUMIDIFIER_KEY, current_humidity=lambda device: device.current_humidity, set_humidity=lambda device, humidity: device.set_dehumidifier_setpoint( humidity @@ -61,9 +63,9 @@ class HoneywellHumidifierEntityDescription(HumidifierEntityDescription): min_humidity=lambda device: device.dehumidifier_lower_limit, max_humidity=lambda device: device.dehumidifier_upper_limit, current_set_humidity=lambda device: device.dehumidifier_setpoint, - mode=lambda device: device.dehumidifer_mode, - off=lambda device: device.set_dehumidifer_off, - on=lambda device: device.set_dehumidifer_auto, + mode=lambda device: device.dehumidifier_mode, + off=lambda device: device.set_dehumidifier_off(), + on=lambda device: device.set_dehumidifier_auto(), device_class=HumidifierDeviceClass.DEHUMIDIFIER, ), } @@ -97,12 +99,10 @@ def __init__(self, device, description) -> None: self._device = device self.entity_description = description self._attr_unique_id = f"{device.deviceid}_{description.key}" - self._attr_native_unit_of_measurement = description.unit_fn(device) self._set_humidity = description.current_set_humidity(device) self._attr_min_humidity = description.min_humidity(device) self._attr_max_humidity = description.max_humidity(device) self._current_humidity = description.current_humidity(device) - self._mode = description.mode(device) self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, device.deviceid)}, name=device.name, @@ -138,21 +138,14 @@ def current_humidity(self) -> int | None: return None return self.entity_description.current_humidity(self._device) - def turn_on(self, **kwargs: Any) -> None: + async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" - self.entity_description.on(self._device) + await self.entity_description.on(self._device) - def turn_off(self, **kwargs: Any) -> None: + async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" - self.entity_description.off(self._device) + await self.entity_description.off(self._device) - def set_humidity(self, humidity: int) -> None: + async def async_set_humidity(self, humidity: int) -> None: """Set new target humidity.""" - if self._set_humidity is None: - raise RuntimeError( - "Cannot set humidity, device doesn't provide methods to set it" - ) - self.entity_description.set_humidity(self._device, humidity) - - -# Look at Tuya humidifier for help.... + await self.entity_description.set_humidity(self._device, humidity) diff --git a/homeassistant/components/honeywell/strings.json b/homeassistant/components/honeywell/strings.json index a64f1a6fce06af..81596a527b628f 100644 --- a/homeassistant/components/honeywell/strings.json +++ b/homeassistant/components/honeywell/strings.json @@ -61,6 +61,14 @@ } } } + }, + "humidifier": { + "humidifier": { + "name": "Humidifier" + }, + "dehumidifier": { + "name": "Dehumidifier" + } } }, "exceptions": { From cec14ba484c11703ea6a0b67e88c92f0355e666f Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 4 Dec 2024 17:52:41 +0000 Subject: [PATCH 06/13] Add new properties to tests finish removal of hass.data[domain] usage --- tests/components/honeywell/conftest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/components/honeywell/conftest.py b/tests/components/honeywell/conftest.py index e48664db9aef0b..a38060e3f88985 100644 --- a/tests/components/honeywell/conftest.py +++ b/tests/components/honeywell/conftest.py @@ -127,7 +127,8 @@ def device(): mock_device.refresh = AsyncMock() mock_device.heat_away_temp = HEATAWAY mock_device.cool_away_temp = COOLAWAY - + mock_device.has_humidifier = False + mock_device.has_dehumidifier = False mock_device.raw_dr_data = {"CoolSetpLimit": None, "HeatSetpLimit": None} return mock_device @@ -149,6 +150,8 @@ def device_with_outdoor_sensor(): mock_device.temperature_unit = "C" mock_device.outdoor_temperature = OUTDOORTEMP mock_device.outdoor_humidity = OUTDOORHUMIDITY + mock_device.has_humidifier = False + mock_device.has_dehumidifier = False mock_device.raw_ui_data = { "SwitchOffAllowed": True, "SwitchAutoAllowed": True, @@ -188,6 +191,8 @@ def another_device(): mock_device.mac_address = "macaddress1" mock_device.outdoor_temperature = None mock_device.outdoor_humidity = None + mock_device.has_humidifier = False + mock_device.has_dehumidifier = False mock_device.raw_ui_data = { "SwitchOffAllowed": True, "SwitchAutoAllowed": True, From 14392b00dc96d7d2c7b8f356da0a36fee002c6cb Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 4 Dec 2024 19:23:56 +0000 Subject: [PATCH 07/13] Create type --- homeassistant/components/honeywell/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/honeywell/__init__.py b/homeassistant/components/honeywell/__init__.py index eb89ba2a6817d1..6cc8a531065cae 100644 --- a/homeassistant/components/honeywell/__init__.py +++ b/homeassistant/components/honeywell/__init__.py @@ -88,7 +88,8 @@ async def async_setup_entry( if len(devices) == 0: _LOGGER.debug("No devices found") return False - config_entry.runtime_data = HoneywellData(config_entry.entry_id, client, devices) + data = HoneywellData(config_entry.entry_id, client, devices) + config_entry.runtime_data = data await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) config_entry.async_on_unload(config_entry.add_update_listener(update_listener)) From 31e778a351d3aa1dd89566d2899d57f76ece89eb Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 4 Dec 2024 20:02:18 +0000 Subject: [PATCH 08/13] Use HoneywellConfigEntry type --- homeassistant/components/honeywell/humidifier.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/honeywell/humidifier.py b/homeassistant/components/honeywell/humidifier.py index cc85cafb490c27..034a4a9ce1a3e2 100644 --- a/homeassistant/components/honeywell/humidifier.py +++ b/homeassistant/components/honeywell/humidifier.py @@ -13,12 +13,11 @@ HumidifierEntity, HumidifierEntityDescription, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import HoneywellData +from . import HoneywellConfigEntry from .const import DOMAIN HUMIDIFIER_KEY = "humidifier" @@ -73,11 +72,11 @@ class HoneywellHumidifierEntityDescription(HumidifierEntityDescription): async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: HoneywellConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Honeywell (de)humidifier dynamically.""" - data: HoneywellData = config_entry.runtime_data + data = config_entry.runtime_data entities: list = [] for device in data.devices.values(): if device.has_humidifier: From a97d390dcb466b486aa97be4238c1b667dc6098c Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 4 Dec 2024 21:12:13 +0000 Subject: [PATCH 09/13] Fix Merge error --- homeassistant/components/honeywell/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/honeywell/__init__.py b/homeassistant/components/honeywell/__init__.py index 6cc8a531065cae..eb89ba2a6817d1 100644 --- a/homeassistant/components/honeywell/__init__.py +++ b/homeassistant/components/honeywell/__init__.py @@ -88,8 +88,7 @@ async def async_setup_entry( if len(devices) == 0: _LOGGER.debug("No devices found") return False - data = HoneywellData(config_entry.entry_id, client, devices) - config_entry.runtime_data = data + config_entry.runtime_data = HoneywellData(config_entry.entry_id, client, devices) await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) config_entry.async_on_unload(config_entry.add_update_listener(update_listener)) From 5def7da9275ff36837271800d3858581dcc3332a Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 4 Dec 2024 21:21:34 +0000 Subject: [PATCH 10/13] Mode not supported remove static where dynamic exist --- .../components/honeywell/humidifier.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/homeassistant/components/honeywell/humidifier.py b/homeassistant/components/honeywell/humidifier.py index 034a4a9ce1a3e2..320ff60510dcaa 100644 --- a/homeassistant/components/honeywell/humidifier.py +++ b/homeassistant/components/honeywell/humidifier.py @@ -98,10 +98,8 @@ def __init__(self, device, description) -> None: self._device = device self.entity_description = description self._attr_unique_id = f"{device.deviceid}_{description.key}" - self._set_humidity = description.current_set_humidity(device) self._attr_min_humidity = description.min_humidity(device) self._attr_max_humidity = description.max_humidity(device) - self._current_humidity = description.current_humidity(device) self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, device.deviceid)}, name=device.name, @@ -113,28 +111,14 @@ def is_on(self) -> bool: """Return the device is on or off.""" return self.entity_description.mode(self._device) != 0 - @property - def mode(self) -> str | None: - """Return the current mode.""" - return self.entity_description.mode(self._device) - @property def target_humidity(self) -> int | None: """Return the humidity we try to reach.""" - if self._set_humidity is None: - return None - - humidity = self.entity_description.current_set_humidity(self._device) - if humidity is None: - return None - - return humidity + return self.entity_description.current_set_humidity(self._device) @property def current_humidity(self) -> int | None: """Return the current humidity.""" - if self._current_humidity is None: - return None return self.entity_description.current_humidity(self._device) async def async_turn_on(self, **kwargs: Any) -> None: From b980349f7dede39a8c856e0d27b2bfe87f33d08f Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 4 Dec 2024 23:31:09 +0000 Subject: [PATCH 11/13] Add tests Fix stale docs --- tests/components/honeywell/__init__.py | 2 +- tests/components/honeywell/conftest.py | 16 ++++ tests/components/honeywell/test_climate.py | 2 +- tests/components/honeywell/test_humidity.py | 98 +++++++++++++++++++++ 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 tests/components/honeywell/test_humidity.py diff --git a/tests/components/honeywell/__init__.py b/tests/components/honeywell/__init__.py index 98fcaa551bfd56..94022667e0ea8f 100644 --- a/tests/components/honeywell/__init__.py +++ b/tests/components/honeywell/__init__.py @@ -1,4 +1,4 @@ -"""Tests for honeywell component.""" +"""Tests for Honeywell component.""" from unittest.mock import MagicMock diff --git a/tests/components/honeywell/conftest.py b/tests/components/honeywell/conftest.py index a38060e3f88985..dd3341aa75c959 100644 --- a/tests/components/honeywell/conftest.py +++ b/tests/components/honeywell/conftest.py @@ -129,6 +129,14 @@ def device(): mock_device.cool_away_temp = COOLAWAY mock_device.has_humidifier = False mock_device.has_dehumidifier = False + mock_device.humidifier_upper_limit = 60 + mock_device.humidifier_lower_limit = 10 + mock_device.humidifier_setpoint = 20 + mock_device.dehumidifier_mode = 1 + mock_device.dehumidifier_upper_limit = 55 + mock_device.dehumidifier_lower_limit = 15 + mock_device.dehumidifier_setpoint = 30 + mock_device.dehumidifier_mode = 1 mock_device.raw_dr_data = {"CoolSetpLimit": None, "HeatSetpLimit": None} return mock_device @@ -193,6 +201,14 @@ def another_device(): mock_device.outdoor_humidity = None mock_device.has_humidifier = False mock_device.has_dehumidifier = False + mock_device.humidifier_upper_limit = 60 + mock_device.humidifier_lower_limit = 10 + mock_device.humidifier_setpoint = 20 + mock_device.dehumidifier_mode = 1 + mock_device.dehumidifier_upper_limit = 55 + mock_device.dehumidifier_lower_limit = 15 + mock_device.dehumidifier_setpoint = 30 + mock_device.dehumidifier_mode = 1 mock_device.raw_ui_data = { "SwitchOffAllowed": True, "SwitchAutoAllowed": True, diff --git a/tests/components/honeywell/test_climate.py b/tests/components/honeywell/test_climate.py index 73c5ff33dbcafb..57cdfaa9a23eee 100644 --- a/tests/components/honeywell/test_climate.py +++ b/tests/components/honeywell/test_climate.py @@ -1,4 +1,4 @@ -"""Test the Whirlpool Sixth Sense climate domain.""" +"""Test the Honeywell climate domain.""" import datetime from unittest.mock import MagicMock diff --git a/tests/components/honeywell/test_humidity.py b/tests/components/honeywell/test_humidity.py new file mode 100644 index 00000000000000..5ecb527f097137 --- /dev/null +++ b/tests/components/honeywell/test_humidity.py @@ -0,0 +1,98 @@ +"""Test the Honeywell humidity domain.""" + +from unittest.mock import MagicMock + +from homeassistant.components.humidifier import ( + ATTR_CURRENT_HUMIDITY, + ATTR_HUMIDITY, + ATTR_MAX_HUMIDITY, + ATTR_MIN_HUMIDITY, + DOMAIN as HUMIDIFIER_DOMAIN, + SERVICE_SET_HUMIDITY, +) +from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON +from homeassistant.core import HomeAssistant + +from . import init_integration + + +async def test_humidifier( + hass: HomeAssistant, device: MagicMock, config_entry: MagicMock +) -> None: + """Test the setup of the climate entities when there are no additional options available.""" + device.has_humidifier = True + await init_integration(hass, config_entry) + entity_id = f"humidifier.{device.name}_humidifier" + assert hass.states.get(f"humidifier.{device.name}_dehumidifier") is None + state = hass.states.get(entity_id) + assert state + attributes = state.attributes + assert attributes[ATTR_MAX_HUMIDITY] == 60 + assert attributes[ATTR_MIN_HUMIDITY] == 10 + assert attributes[ATTR_CURRENT_HUMIDITY] == 50 + assert attributes[ATTR_HUMIDITY] == 20 + + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + device.set_humidifier_auto.assert_called_once() + + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + device.set_humidifier_off.assert_called_once() + + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_SET_HUMIDITY, + {ATTR_ENTITY_ID: entity_id, ATTR_HUMIDITY: 40}, + blocking=True, + ) + device.set_humidifier_setpoint.assert_called_once_with(40) + + +async def test_dehumidifier( + hass: HomeAssistant, device: MagicMock, config_entry: MagicMock +) -> None: + """Test the setup of the climate entities when there are no additional options available.""" + device.has_dehumidifier = True + await init_integration(hass, config_entry) + entity_id = f"humidifier.{device.name}_dehumidifier" + assert hass.states.get(f"humidifier.{device.name}_humidifier") is None + state = hass.states.get(entity_id) + assert state + attributes = state.attributes + assert attributes[ATTR_MAX_HUMIDITY] == 55 + assert attributes[ATTR_MIN_HUMIDITY] == 15 + assert attributes[ATTR_CURRENT_HUMIDITY] == 50 + assert attributes[ATTR_HUMIDITY] == 30 + + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + device.set_dehumidifier_auto.assert_called_once() + + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + device.set_dehumidifier_off.assert_called_once() + + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_SET_HUMIDITY, + {ATTR_ENTITY_ID: entity_id, ATTR_HUMIDITY: 40}, + blocking=True, + ) + device.set_dehumidifier_setpoint.assert_called_once_with(40) From b2c04313d871b5f3e83ac2e5726ca8564af42a6d Mon Sep 17 00:00:00 2001 From: mkmer Date: Thu, 5 Dec 2024 01:13:37 +0000 Subject: [PATCH 12/13] Typing add refs in strings snapshot for static vars --- .../components/honeywell/humidifier.py | 6 ++- .../components/honeywell/strings.json | 4 +- .../honeywell/snapshots/test_humidity.ambr | 23 ++++++++ tests/components/honeywell/test_humidity.py | 52 ++++++++++++------- 4 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 tests/components/honeywell/snapshots/test_humidity.ambr diff --git a/homeassistant/components/honeywell/humidifier.py b/homeassistant/components/honeywell/humidifier.py index 320ff60510dcaa..e94ba465c30288 100644 --- a/homeassistant/components/honeywell/humidifier.py +++ b/homeassistant/components/honeywell/humidifier.py @@ -26,7 +26,7 @@ @dataclass(frozen=True, kw_only=True) class HoneywellHumidifierEntityDescription(HumidifierEntityDescription): - """Describes a Honeywell sensor entity.""" + """Describes a Honeywell humidifier entity.""" current_humidity: Callable[[Device], Any] current_set_humidity: Callable[[Device], Any] @@ -93,7 +93,9 @@ class HoneywellHumidifier(HumidifierEntity): entity_description: HoneywellHumidifierEntityDescription _attr_has_entity_name = True - def __init__(self, device, description) -> None: + def __init__( + self, device: Device, description: HoneywellHumidifierEntityDescription + ) -> None: """Initialize the (De)Humidifier.""" self._device = device self.entity_description = description diff --git a/homeassistant/components/honeywell/strings.json b/homeassistant/components/honeywell/strings.json index 81596a527b628f..2538e7101a1b31 100644 --- a/homeassistant/components/honeywell/strings.json +++ b/homeassistant/components/honeywell/strings.json @@ -64,10 +64,10 @@ }, "humidifier": { "humidifier": { - "name": "Humidifier" + "name": "[%key:component::humidifier::title%]" }, "dehumidifier": { - "name": "Dehumidifier" + "name": "[%key:component::humidifier::entity_component::dehumidifier::name%]" } } }, diff --git a/tests/components/honeywell/snapshots/test_humidity.ambr b/tests/components/honeywell/snapshots/test_humidity.ambr new file mode 100644 index 00000000000000..f8419a84f22acb --- /dev/null +++ b/tests/components/honeywell/snapshots/test_humidity.ambr @@ -0,0 +1,23 @@ +# serializer version: 1 +# name: test_static_attributes + ReadOnlyDict({ + 'current_humidity': 50, + 'device_class': 'dehumidifier', + 'friendly_name': 'device1 Dehumidifier', + 'humidity': 30, + 'max_humidity': 55, + 'min_humidity': 15, + 'supported_features': , + }) +# --- +# name: test_static_attributes.1 + ReadOnlyDict({ + 'current_humidity': 50, + 'device_class': 'humidifier', + 'friendly_name': 'device1 Humidifier', + 'humidity': 20, + 'max_humidity': 60, + 'min_humidity': 10, + 'supported_features': , + }) +# --- diff --git a/tests/components/honeywell/test_humidity.py b/tests/components/honeywell/test_humidity.py index 5ecb527f097137..8aa79c6eb8111d 100644 --- a/tests/components/honeywell/test_humidity.py +++ b/tests/components/honeywell/test_humidity.py @@ -2,21 +2,21 @@ from unittest.mock import MagicMock +from syrupy.assertion import SnapshotAssertion + from homeassistant.components.humidifier import ( - ATTR_CURRENT_HUMIDITY, ATTR_HUMIDITY, - ATTR_MAX_HUMIDITY, - ATTR_MIN_HUMIDITY, DOMAIN as HUMIDIFIER_DOMAIN, SERVICE_SET_HUMIDITY, ) from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from . import init_integration -async def test_humidifier( +async def test_humidifier_service_calls( hass: HomeAssistant, device: MagicMock, config_entry: MagicMock ) -> None: """Test the setup of the climate entities when there are no additional options available.""" @@ -24,13 +24,6 @@ async def test_humidifier( await init_integration(hass, config_entry) entity_id = f"humidifier.{device.name}_humidifier" assert hass.states.get(f"humidifier.{device.name}_dehumidifier") is None - state = hass.states.get(entity_id) - assert state - attributes = state.attributes - assert attributes[ATTR_MAX_HUMIDITY] == 60 - assert attributes[ATTR_MIN_HUMIDITY] == 10 - assert attributes[ATTR_CURRENT_HUMIDITY] == 50 - assert attributes[ATTR_HUMIDITY] == 20 await hass.services.async_call( HUMIDIFIER_DOMAIN, @@ -57,7 +50,7 @@ async def test_humidifier( device.set_humidifier_setpoint.assert_called_once_with(40) -async def test_dehumidifier( +async def test_dehumidifier_service_calls( hass: HomeAssistant, device: MagicMock, config_entry: MagicMock ) -> None: """Test the setup of the climate entities when there are no additional options available.""" @@ -65,13 +58,6 @@ async def test_dehumidifier( await init_integration(hass, config_entry) entity_id = f"humidifier.{device.name}_dehumidifier" assert hass.states.get(f"humidifier.{device.name}_humidifier") is None - state = hass.states.get(entity_id) - assert state - attributes = state.attributes - assert attributes[ATTR_MAX_HUMIDITY] == 55 - assert attributes[ATTR_MIN_HUMIDITY] == 15 - assert attributes[ATTR_CURRENT_HUMIDITY] == 50 - assert attributes[ATTR_HUMIDITY] == 30 await hass.services.async_call( HUMIDIFIER_DOMAIN, @@ -96,3 +82,31 @@ async def test_dehumidifier( blocking=True, ) device.set_dehumidifier_setpoint.assert_called_once_with(40) + + +async def test_static_attributes( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + device: MagicMock, + config_entry: MagicMock, + snapshot: SnapshotAssertion, +) -> None: + """Test static humidifier attributes.""" + device.has_dehumidifier = True + device.has_humidifier = True + await init_integration(hass, config_entry) + + entity_id_dehumidifier = f"humidifier.{device.name}_dehumidifier" + entity_id_humidifier = f"humidifier.{device.name}_humidifier" + entry = entity_registry.async_get(entity_id_dehumidifier) + assert entry + + state = hass.states.get(entity_id_dehumidifier) + attributes = state.attributes + + assert attributes == snapshot() + + state = hass.states.get(entity_id_humidifier) + attributes = state.attributes + + assert attributes == snapshot() From 4aa371ed6ff334015dc631df6bb2a3a08cb865a9 Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 18 Dec 2024 13:47:20 +0000 Subject: [PATCH 13/13] Snapshot State, Name Snapshots --- .../honeywell/snapshots/test_humidity.ambr | 52 ++++++++++++------- tests/components/honeywell/test_humidity.py | 6 +-- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/tests/components/honeywell/snapshots/test_humidity.ambr b/tests/components/honeywell/snapshots/test_humidity.ambr index f8419a84f22acb..369167b8c1ea21 100644 --- a/tests/components/honeywell/snapshots/test_humidity.ambr +++ b/tests/components/honeywell/snapshots/test_humidity.ambr @@ -1,23 +1,39 @@ # serializer version: 1 -# name: test_static_attributes - ReadOnlyDict({ - 'current_humidity': 50, - 'device_class': 'dehumidifier', - 'friendly_name': 'device1 Dehumidifier', - 'humidity': 30, - 'max_humidity': 55, - 'min_humidity': 15, - 'supported_features': , +# name: test_static_attributes[dehumidifier] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'current_humidity': 50, + 'device_class': 'dehumidifier', + 'friendly_name': 'device1 Dehumidifier', + 'humidity': 30, + 'max_humidity': 55, + 'min_humidity': 15, + 'supported_features': , + }), + 'context': , + 'entity_id': 'humidifier.device1_dehumidifier', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', }) # --- -# name: test_static_attributes.1 - ReadOnlyDict({ - 'current_humidity': 50, - 'device_class': 'humidifier', - 'friendly_name': 'device1 Humidifier', - 'humidity': 20, - 'max_humidity': 60, - 'min_humidity': 10, - 'supported_features': , +# name: test_static_attributes[humidifier] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'current_humidity': 50, + 'device_class': 'humidifier', + 'friendly_name': 'device1 Humidifier', + 'humidity': 20, + 'max_humidity': 60, + 'min_humidity': 10, + 'supported_features': , + }), + 'context': , + 'entity_id': 'humidifier.device1_humidifier', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', }) # --- diff --git a/tests/components/honeywell/test_humidity.py b/tests/components/honeywell/test_humidity.py index 8aa79c6eb8111d..2e1f8cec6aa20c 100644 --- a/tests/components/honeywell/test_humidity.py +++ b/tests/components/honeywell/test_humidity.py @@ -102,11 +102,9 @@ async def test_static_attributes( assert entry state = hass.states.get(entity_id_dehumidifier) - attributes = state.attributes - assert attributes == snapshot() + assert state == snapshot(name="dehumidifier") state = hass.states.get(entity_id_humidifier) - attributes = state.attributes - assert attributes == snapshot() + assert state == snapshot(name="humidifier")