diff --git a/.strict-typing b/.strict-typing index 0e7c6a8205da1..f128385834bbe 100644 --- a/.strict-typing +++ b/.strict-typing @@ -183,6 +183,7 @@ homeassistant.components.fronius.* homeassistant.components.frontend.* homeassistant.components.fully_kiosk.* homeassistant.components.generic_hygrostat.* +homeassistant.components.generic_thermostat.* homeassistant.components.geo_location.* homeassistant.components.geocaching.* homeassistant.components.gios.* diff --git a/homeassistant/components/generic_thermostat/climate.py b/homeassistant/components/generic_thermostat/climate.py index 03a984016683f..7bc6c63697c4d 100644 --- a/homeassistant/components/generic_thermostat/climate.py +++ b/homeassistant/components/generic_thermostat/climate.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio +from datetime import datetime, timedelta import logging import math from typing import Any @@ -36,10 +37,12 @@ STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN, + UnitOfTemperature, ) from homeassistant.core import ( DOMAIN as HA_DOMAIN, CoreState, + Event, HomeAssistant, State, callback, @@ -126,25 +129,25 @@ async def async_setup_platform( await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - name = config.get(CONF_NAME) - heater_entity_id = config.get(CONF_HEATER) - sensor_entity_id = config.get(CONF_SENSOR) - min_temp = config.get(CONF_MIN_TEMP) - max_temp = config.get(CONF_MAX_TEMP) - target_temp = config.get(CONF_TARGET_TEMP) - ac_mode = config.get(CONF_AC_MODE) - min_cycle_duration = config.get(CONF_MIN_DUR) - cold_tolerance = config.get(CONF_COLD_TOLERANCE) - hot_tolerance = config.get(CONF_HOT_TOLERANCE) - keep_alive = config.get(CONF_KEEP_ALIVE) - initial_hvac_mode = config.get(CONF_INITIAL_HVAC_MODE) - presets = { + name: str = config[CONF_NAME] + heater_entity_id: str = config[CONF_HEATER] + sensor_entity_id: str = config[CONF_SENSOR] + min_temp: float | None = config.get(CONF_MIN_TEMP) + max_temp: float | None = config.get(CONF_MAX_TEMP) + target_temp: float | None = config.get(CONF_TARGET_TEMP) + ac_mode: bool | None = config.get(CONF_AC_MODE) + min_cycle_duration: timedelta | None = config.get(CONF_MIN_DUR) + cold_tolerance: float = config[CONF_COLD_TOLERANCE] + hot_tolerance: float = config[CONF_HOT_TOLERANCE] + keep_alive: timedelta | None = config.get(CONF_KEEP_ALIVE) + initial_hvac_mode: HVACMode | None = config.get(CONF_INITIAL_HVAC_MODE) + presets: dict[str, float] = { key: config[value] for key, value in CONF_PRESETS.items() if value in config } - precision = config.get(CONF_PRECISION) - target_temperature_step = config.get(CONF_TEMP_STEP) + precision: float | None = config.get(CONF_PRECISION) + target_temperature_step: float | None = config.get(CONF_TEMP_STEP) unit = hass.config.units.temperature_unit - unique_id = config.get(CONF_UNIQUE_ID) + unique_id: str | None = config.get(CONF_UNIQUE_ID) async_add_entities( [ @@ -178,24 +181,24 @@ class GenericThermostat(ClimateEntity, RestoreEntity): def __init__( self, - name, - heater_entity_id, - sensor_entity_id, - min_temp, - max_temp, - target_temp, - ac_mode, - min_cycle_duration, - cold_tolerance, - hot_tolerance, - keep_alive, - initial_hvac_mode, - presets, - precision, - target_temperature_step, - unit, - unique_id, - ): + name: str, + heater_entity_id: str, + sensor_entity_id: str, + min_temp: float | None, + max_temp: float | None, + target_temp: float | None, + ac_mode: bool | None, + min_cycle_duration: timedelta | None, + cold_tolerance: float, + hot_tolerance: float, + keep_alive: timedelta | None, + initial_hvac_mode: HVACMode | None, + presets: dict[str, float], + precision: float | None, + target_temperature_step: float | None, + unit: UnitOfTemperature, + unique_id: str | None, + ) -> None: """Initialize the thermostat.""" self._attr_name = name self.heater_entity_id = heater_entity_id @@ -214,7 +217,7 @@ def __init__( else: self._attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF] self._active = False - self._cur_temp = None + self._cur_temp: float | None = None self._temp_lock = asyncio.Lock() self._min_temp = min_temp self._max_temp = max_temp @@ -254,7 +257,7 @@ async def async_added_to_hass(self) -> None: ) @callback - def _async_startup(*_): + def _async_startup(_: Event | None = None) -> None: """Init on startup.""" sensor_state = self.hass.states.get(self.sensor_entity_id) if sensor_state and sensor_state.state not in ( @@ -297,7 +300,7 @@ def _async_startup(*_): ): self._attr_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE) if not self._hvac_mode and old_state.state: - self._hvac_mode = old_state.state + self._hvac_mode = HVACMode(old_state.state) else: # No previous state, try and restore defaults @@ -315,14 +318,14 @@ def _async_startup(*_): self._hvac_mode = HVACMode.OFF @property - def precision(self): + def precision(self) -> float: """Return the precision of the system.""" if self._temp_precision is not None: return self._temp_precision return super().precision @property - def target_temperature_step(self): + def target_temperature_step(self) -> float: """Return the supported step of target temperature.""" if self._temp_target_temperature_step is not None: return self._temp_target_temperature_step @@ -330,17 +333,17 @@ def target_temperature_step(self): return self.precision @property - def current_temperature(self): + def current_temperature(self) -> float | None: """Return the sensor temperature.""" return self._cur_temp @property - def hvac_mode(self): + def hvac_mode(self) -> HVACMode | None: """Return current operation.""" return self._hvac_mode @property - def hvac_action(self): + def hvac_action(self) -> HVACAction: """Return the current running hvac operation if supported. Need to be one of CURRENT_HVAC_*. @@ -354,7 +357,7 @@ def hvac_action(self): return HVACAction.HEATING @property - def target_temperature(self): + def target_temperature(self) -> float | None: """Return the temperature we try to reach.""" return self._target_temp @@ -385,7 +388,7 @@ async def async_set_temperature(self, **kwargs: Any) -> None: self.async_write_ha_state() @property - def min_temp(self): + def min_temp(self) -> float: """Return the minimum temperature.""" if self._min_temp is not None: return self._min_temp @@ -394,7 +397,7 @@ def min_temp(self): return super().min_temp @property - def max_temp(self): + def max_temp(self) -> float: """Return the maximum temperature.""" if self._max_temp is not None: return self._max_temp @@ -414,7 +417,7 @@ async def _async_sensor_changed( await self._async_control_heating() self.async_write_ha_state() - async def _check_switch_initial_state(self): + async def _check_switch_initial_state(self) -> None: """Prevent the device from keep running if HVACMode.OFF.""" if self._hvac_mode == HVACMode.OFF and self._is_device_active: _LOGGER.warning( @@ -448,7 +451,9 @@ def _async_update_temp(self, state: State) -> None: except ValueError as ex: _LOGGER.error("Unable to update from sensor: %s", ex) - async def _async_control_heating(self, time=None, force=False): + async def _async_control_heating( + self, time: datetime | None = None, force: bool = False + ) -> None: """Check if we need to turn heating on or off.""" async with self._temp_lock: if not self._active and None not in ( @@ -490,6 +495,7 @@ async def _async_control_heating(self, time=None, force=False): if not long_enough: return + assert self._cur_temp is not None and self._target_temp is not None too_cold = self._target_temp >= self._cur_temp + self._cold_tolerance too_hot = self._cur_temp >= self._target_temp + self._hot_tolerance if self._is_device_active: @@ -514,21 +520,21 @@ async def _async_control_heating(self, time=None, force=False): await self._async_heater_turn_off() @property - def _is_device_active(self): + def _is_device_active(self) -> bool | None: """If the toggleable device is currently active.""" if not self.hass.states.get(self.heater_entity_id): return None return self.hass.states.is_state(self.heater_entity_id, STATE_ON) - async def _async_heater_turn_on(self): + async def _async_heater_turn_on(self) -> None: """Turn heater toggleable device on.""" data = {ATTR_ENTITY_ID: self.heater_entity_id} await self.hass.services.async_call( HA_DOMAIN, SERVICE_TURN_ON, data, context=self._context ) - async def _async_heater_turn_off(self): + async def _async_heater_turn_off(self) -> None: """Turn heater toggleable device off.""" data = {ATTR_ENTITY_ID: self.heater_entity_id} await self.hass.services.async_call( diff --git a/mypy.ini b/mypy.ini index f90dbeffd12e2..a47452f6012e3 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1591,6 +1591,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.generic_thermostat.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.geo_location.*] check_untyped_defs = true disallow_incomplete_defs = true