From 87551b78801e76a380bb1b8302441033ee188ce0 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Thu, 14 Apr 2022 22:19:42 +0200 Subject: [PATCH] Improve typing of deCONZ climate platform (#69882) * Improve typing of deCONZ climate platform homeassistant/components/deconz/climate.py:153: error: Dict entry 0 has incompatible type "str": "bool"; expected "str": "str" [dict-item] homeassistant/components/deconz/climate.py:154: error: Dict entry 1 has incompatible type "str": "bool"; expected "str": "str" [dict-item] homeassistant/components/deconz/climate.py:176: error: Argument 1 to "get" of "Mapping" has incompatible type "Optional[Literal['off', 'low', 'medium', 'high', 'on', 'auto', 'smart']]"; expected "str" [arg-type] homeassistant/components/deconz/climate.py:200: error: Argument 1 to "get" of "Mapping" has incompatible type "Optional[Literal['off', 'auto', 'cool', 'heat', 'emergency heating', 'precooling', 'fan only', 'dry', 'sleep']]"; expected "str" [arg-type] homeassistant/components/deconz/climate.py:218: error: Argument 1 to "set_config" of "Thermostat" has incompatible type "**Dict[str, str]"; expected "Optional[int]" [arg-type] homeassistant/components/deconz/climate.py:218: error: Argument 1 to "set_config" of "Thermostat" has incompatible type "**Dict[str, str]"; expected "Optional[bool]" [arg-type] homeassistant/components/deconz/climate.py:218: error: Argument 1 to "set_config" of "Thermostat" has incompatible type "**Dict[str, str]"; expected "Optional[List[str]]" [arg-type] homeassistant/components/deconz/climate.py:225: error: Argument 1 to "get" of "Mapping" has incompatible type "Optional[Literal['holiday', 'auto', 'manual', 'comfort', 'eco', 'boost', 'complex']]"; expected "str" [arg-type] homeassistant/components/deconz/climate.py:244: error: Unused "type: ignore" comment homeassistant/components/deconz/climate.py:250: error: Unused "type: ignore" comment homeassistant/components/deconz/climate.py:253: error: Unused "type: ignore" comment * Simplify populating supported_hvac_modes Fix tests --- .strict-typing | 1 + homeassistant/components/deconz/climate.py | 54 +++++++++++----------- mypy.ini | 14 ++++-- script/hassfest/mypy_config.py | 1 - tests/components/deconz/test_climate.py | 6 +-- 5 files changed, 41 insertions(+), 35 deletions(-) diff --git a/.strict-typing b/.strict-typing index 8662e77108e..5a91ed7ef51 100644 --- a/.strict-typing +++ b/.strict-typing @@ -64,6 +64,7 @@ homeassistant.components.crownstone.* homeassistant.components.cpuspeed.* homeassistant.components.deconz homeassistant.components.deconz.alarm_control_panel +homeassistant.components.deconz.climate homeassistant.components.deconz.config_flow homeassistant.components.deconz.diagnostics homeassistant.components.deconz.gateway diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index 45ed8d51aa5..843114eba46 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -65,7 +65,7 @@ FAN_MODE_TO_DECONZ = { DECONZ_TO_FAN_MODE = {value: key for key, value in FAN_MODE_TO_DECONZ.items()} -HVAC_MODE_TO_DECONZ = { +HVAC_MODE_TO_DECONZ: dict[str, str] = { HVAC_MODE_AUTO: THERMOSTAT_MODE_AUTO, HVAC_MODE_COOL: THERMOSTAT_MODE_COOL, HVAC_MODE_HEAT: THERMOSTAT_MODE_HEAT, @@ -147,17 +147,15 @@ class DeconzThermostat(DeconzDevice, ClimateEntity): """Set up thermostat device.""" super().__init__(device, gateway) - self._hvac_mode_to_deconz = dict(HVAC_MODE_TO_DECONZ) - if not device.mode: - self._hvac_mode_to_deconz = { - HVAC_MODE_HEAT: True, - HVAC_MODE_OFF: False, - } - elif "coolsetpoint" not in device.raw["config"]: - self._hvac_mode_to_deconz.pop(HVAC_MODE_COOL) - self._deconz_to_hvac_mode = { - value: key for key, value in self._hvac_mode_to_deconz.items() - } + self.supported_hvac_modes = [ + HVAC_MODE_HEAT, + HVAC_MODE_OFF, + ] + if device.mode: + self.supported_hvac_modes.append(HVAC_MODE_AUTO) + + if "coolsetpoint" in device.raw["config"]: + self.supported_hvac_modes.append(HVAC_MODE_COOL) self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE @@ -172,9 +170,9 @@ class DeconzThermostat(DeconzDevice, ClimateEntity): @property def fan_mode(self) -> str: """Return fan operation.""" - return DECONZ_TO_FAN_MODE.get( - self._device.fan_mode, FAN_ON if self._device.state_on else FAN_OFF - ) + if self._device.fan_mode in DECONZ_TO_FAN_MODE: + return DECONZ_TO_FAN_MODE[self._device.fan_mode] + return DECONZ_TO_FAN_MODE[FAN_ON if self._device.state_on else FAN_OFF] @property def fan_modes(self) -> list[str]: @@ -196,36 +194,36 @@ class DeconzThermostat(DeconzDevice, ClimateEntity): Need to be one of HVAC_MODE_*. """ - return self._deconz_to_hvac_mode.get( - self._device.mode, - HVAC_MODE_HEAT if self._device.state_on else HVAC_MODE_OFF, - ) + if self._device.mode in self.supported_hvac_modes: + return HVAC_MODE_TO_DECONZ[self._device.mode] + return HVAC_MODE_HEAT if self._device.state_on else HVAC_MODE_OFF @property def hvac_modes(self) -> list[str]: """Return the list of available hvac operation modes.""" - return list(self._hvac_mode_to_deconz) + return self.supported_hvac_modes async def async_set_hvac_mode(self, hvac_mode: str) -> None: """Set new target hvac mode.""" - if hvac_mode not in self._hvac_mode_to_deconz: + if hvac_mode not in self.supported_hvac_modes: raise ValueError(f"Unsupported HVAC mode {hvac_mode}") - data = {"mode": self._hvac_mode_to_deconz[hvac_mode]} - if len(self._hvac_mode_to_deconz) == 2: # Only allow turn on and off thermostat - data = {"on": self._hvac_mode_to_deconz[hvac_mode]} - - await self._device.set_config(**data) + if len(self.supported_hvac_modes) == 2: # Only allow turn on and off thermostat + await self._device.set_config(on=hvac_mode != HVAC_MODE_OFF) + else: + await self._device.set_config(mode=HVAC_MODE_TO_DECONZ[hvac_mode]) # Preset control @property def preset_mode(self) -> str | None: """Return preset mode.""" - return DECONZ_TO_PRESET_MODE.get(self._device.preset) + if self._device.preset in DECONZ_TO_PRESET_MODE: + return DECONZ_TO_PRESET_MODE[self._device.preset] + return None @property - def preset_modes(self) -> list: + def preset_modes(self) -> list[str]: """Return the list of available preset modes.""" return list(PRESET_MODE_TO_DECONZ) diff --git a/mypy.ini b/mypy.ini index 66a7671ea35..822e45149ae 100644 --- a/mypy.ini +++ b/mypy.ini @@ -506,6 +506,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.deconz.climate] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.deconz.config_flow] check_untyped_defs = true disallow_incomplete_defs = true @@ -2601,9 +2612,6 @@ ignore_errors = true [mypy-homeassistant.components.deconz.binary_sensor] ignore_errors = true -[mypy-homeassistant.components.deconz.climate] -ignore_errors = true - [mypy-homeassistant.components.deconz.cover] ignore_errors = true diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index 1c0fe6be6e5..8314a9ae3e0 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -24,7 +24,6 @@ IGNORED_MODULES: Final[list[str]] = [ "homeassistant.components.conversation", "homeassistant.components.conversation.default_agent", "homeassistant.components.deconz.binary_sensor", - "homeassistant.components.deconz.climate", "homeassistant.components.deconz.cover", "homeassistant.components.deconz.fan", "homeassistant.components.deconz.light", diff --git a/tests/components/deconz/test_climate.py b/tests/components/deconz/test_climate.py index a21d981900c..dd951eae21a 100644 --- a/tests/components/deconz/test_climate.py +++ b/tests/components/deconz/test_climate.py @@ -204,9 +204,9 @@ async def test_climate_device_without_cooling_support( climate_thermostat = hass.states.get("climate.thermostat") assert climate_thermostat.state == HVAC_MODE_AUTO assert climate_thermostat.attributes["hvac_modes"] == [ - HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF, + HVAC_MODE_AUTO, ] assert climate_thermostat.attributes["current_temperature"] == 22.6 assert climate_thermostat.attributes["temperature"] == 22.0 @@ -378,10 +378,10 @@ async def test_climate_device_with_cooling_support( climate_thermostat = hass.states.get("climate.zen_01") assert climate_thermostat.state == HVAC_MODE_HEAT assert climate_thermostat.attributes["hvac_modes"] == [ - HVAC_MODE_AUTO, - HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF, + HVAC_MODE_AUTO, + HVAC_MODE_COOL, ] assert climate_thermostat.attributes["current_temperature"] == 23.2 assert climate_thermostat.attributes["temperature"] == 22.2