Enable strict typing for generic_hygrostat (#107272)

This commit is contained in:
Marc Mueller 2024-01-08 09:59:31 +01:00 committed by GitHub
parent 265f587768
commit 5ae419367e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 67 deletions

View File

@ -177,6 +177,7 @@ homeassistant.components.fritzbox_callmonitor.*
homeassistant.components.fronius.* homeassistant.components.fronius.*
homeassistant.components.frontend.* homeassistant.components.frontend.*
homeassistant.components.fully_kiosk.* homeassistant.components.fully_kiosk.*
homeassistant.components.generic_hygrostat.*
homeassistant.components.geo_location.* homeassistant.components.geo_location.*
homeassistant.components.geocaching.* homeassistant.components.geocaching.*
homeassistant.components.gios.* homeassistant.components.gios.*

View File

@ -2,7 +2,10 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from collections.abc import Callable
from datetime import datetime, timedelta
import logging import logging
from typing import TYPE_CHECKING, Any
from homeassistant.components.humidifier import ( from homeassistant.components.humidifier import (
ATTR_HUMIDITY, ATTR_HUMIDITY,
@ -27,7 +30,13 @@ from homeassistant.const import (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
STATE_UNKNOWN, STATE_UNKNOWN,
) )
from homeassistant.core import DOMAIN as HA_DOMAIN, HomeAssistant, callback from homeassistant.core import (
DOMAIN as HA_DOMAIN,
Event,
HomeAssistant,
State,
callback,
)
from homeassistant.helpers import condition from homeassistant.helpers import condition
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
@ -72,22 +81,22 @@ async def async_setup_platform(
"""Set up the generic hygrostat platform.""" """Set up the generic hygrostat platform."""
if discovery_info: if discovery_info:
config = discovery_info config = discovery_info
name = config[CONF_NAME] name: str = config[CONF_NAME]
switch_entity_id = config[CONF_HUMIDIFIER] switch_entity_id: str = config[CONF_HUMIDIFIER]
sensor_entity_id = config[CONF_SENSOR] sensor_entity_id: str = config[CONF_SENSOR]
min_humidity = config.get(CONF_MIN_HUMIDITY) min_humidity: int | None = config.get(CONF_MIN_HUMIDITY)
max_humidity = config.get(CONF_MAX_HUMIDITY) max_humidity: int | None = config.get(CONF_MAX_HUMIDITY)
target_humidity = config.get(CONF_TARGET_HUMIDITY) target_humidity: int | None = config.get(CONF_TARGET_HUMIDITY)
device_class = config.get(CONF_DEVICE_CLASS) device_class: HumidifierDeviceClass | None = config.get(CONF_DEVICE_CLASS)
min_cycle_duration = config.get(CONF_MIN_DUR) min_cycle_duration: timedelta | None = config.get(CONF_MIN_DUR)
sensor_stale_duration = config.get(CONF_STALE_DURATION) sensor_stale_duration: timedelta | None = config.get(CONF_STALE_DURATION)
dry_tolerance = config[CONF_DRY_TOLERANCE] dry_tolerance: float = config[CONF_DRY_TOLERANCE]
wet_tolerance = config[CONF_WET_TOLERANCE] wet_tolerance: float = config[CONF_WET_TOLERANCE]
keep_alive = config.get(CONF_KEEP_ALIVE) keep_alive: timedelta | None = config.get(CONF_KEEP_ALIVE)
initial_state = config.get(CONF_INITIAL_STATE) initial_state: bool | None = config.get(CONF_INITIAL_STATE)
away_humidity = config.get(CONF_AWAY_HUMIDITY) away_humidity: int | None = config.get(CONF_AWAY_HUMIDITY)
away_fixed = config.get(CONF_AWAY_FIXED) away_fixed: bool | None = config.get(CONF_AWAY_FIXED)
unique_id = config.get(CONF_UNIQUE_ID) unique_id: str | None = config.get(CONF_UNIQUE_ID)
async_add_entities( async_add_entities(
[ [
@ -120,28 +129,28 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
def __init__( def __init__(
self, self,
name, name: str,
switch_entity_id, switch_entity_id: str,
sensor_entity_id, sensor_entity_id: str,
min_humidity, min_humidity: int | None,
max_humidity, max_humidity: int | None,
target_humidity, target_humidity: int | None,
device_class, device_class: HumidifierDeviceClass | None,
min_cycle_duration, min_cycle_duration: timedelta | None,
dry_tolerance, dry_tolerance: float,
wet_tolerance, wet_tolerance: float,
keep_alive, keep_alive: timedelta | None,
initial_state, initial_state: bool | None,
away_humidity, away_humidity: int | None,
away_fixed, away_fixed: bool | None,
sensor_stale_duration, sensor_stale_duration: timedelta | None,
unique_id, unique_id: str | None,
): ) -> None:
"""Initialize the hygrostat.""" """Initialize the hygrostat."""
self._name = name self._name = name
self._switch_entity_id = switch_entity_id self._switch_entity_id = switch_entity_id
self._sensor_entity_id = sensor_entity_id self._sensor_entity_id = sensor_entity_id
self._device_class = device_class self._device_class = device_class or HumidifierDeviceClass.HUMIDIFIER
self._min_cycle_duration = min_cycle_duration self._min_cycle_duration = min_cycle_duration
self._dry_tolerance = dry_tolerance self._dry_tolerance = dry_tolerance
self._wet_tolerance = wet_tolerance self._wet_tolerance = wet_tolerance
@ -149,7 +158,7 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
self._state = initial_state self._state = initial_state
self._saved_target_humidity = away_humidity or target_humidity self._saved_target_humidity = away_humidity or target_humidity
self._active = False self._active = False
self._cur_humidity = None self._cur_humidity: float | None = None
self._humidity_lock = asyncio.Lock() self._humidity_lock = asyncio.Lock()
self._min_humidity = min_humidity self._min_humidity = min_humidity
self._max_humidity = max_humidity self._max_humidity = max_humidity
@ -159,14 +168,12 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
self._away_humidity = away_humidity self._away_humidity = away_humidity
self._away_fixed = away_fixed self._away_fixed = away_fixed
self._sensor_stale_duration = sensor_stale_duration self._sensor_stale_duration = sensor_stale_duration
self._remove_stale_tracking = None self._remove_stale_tracking: Callable[[], None] | None = None
self._is_away = False self._is_away = False
if not self._device_class:
self._device_class = HumidifierDeviceClass.HUMIDIFIER
self._attr_action = HumidifierAction.IDLE self._attr_action = HumidifierAction.IDLE
self._attr_unique_id = unique_id self._attr_unique_id = unique_id
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Run when entity about to be added.""" """Run when entity about to be added."""
await super().async_added_to_hass() await super().async_added_to_hass()
@ -185,7 +192,7 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
) )
) )
async def _async_startup(event): async def _async_startup(event: Event | None) -> None:
"""Init on startup.""" """Init on startup."""
sensor_state = self.hass.states.get(self._sensor_entity_id) sensor_state = self.hass.states.get(self._sensor_entity_id)
if sensor_state is None or sensor_state.state in ( if sensor_state is None or sensor_state.state in (
@ -234,39 +241,39 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
return await super().async_will_remove_from_hass() return await super().async_will_remove_from_hass()
@property @property
def available(self): def available(self) -> bool:
"""Return True if entity is available.""" """Return True if entity is available."""
return self._active return self._active
@property @property
def extra_state_attributes(self): def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return the optional state attributes.""" """Return the optional state attributes."""
if self._saved_target_humidity: if self._saved_target_humidity:
return {ATTR_SAVED_HUMIDITY: self._saved_target_humidity} return {ATTR_SAVED_HUMIDITY: self._saved_target_humidity}
return None return None
@property @property
def name(self): def name(self) -> str:
"""Return the name of the hygrostat.""" """Return the name of the hygrostat."""
return self._name return self._name
@property @property
def is_on(self): def is_on(self) -> bool | None:
"""Return true if the hygrostat is on.""" """Return true if the hygrostat is on."""
return self._state return self._state
@property @property
def current_humidity(self): def current_humidity(self) -> int | None:
"""Return the measured humidity.""" """Return the measured humidity."""
return self._cur_humidity return int(self._cur_humidity) if self._cur_humidity is not None else None
@property @property
def target_humidity(self): def target_humidity(self) -> int | None:
"""Return the humidity we try to reach.""" """Return the humidity we try to reach."""
return self._target_humidity return self._target_humidity
@property @property
def mode(self): def mode(self) -> str | None:
"""Return the current mode.""" """Return the current mode."""
if self._away_humidity is None: if self._away_humidity is None:
return None return None
@ -275,18 +282,18 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
return MODE_NORMAL return MODE_NORMAL
@property @property
def available_modes(self): def available_modes(self) -> list[str] | None:
"""Return a list of available modes.""" """Return a list of available modes."""
if self._away_humidity: if self._away_humidity:
return [MODE_NORMAL, MODE_AWAY] return [MODE_NORMAL, MODE_AWAY]
return None return None
@property @property
def device_class(self): def device_class(self) -> HumidifierDeviceClass:
"""Return the device class of the humidifier.""" """Return the device class of the humidifier."""
return self._device_class return self._device_class
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn hygrostat on.""" """Turn hygrostat on."""
if not self._active: if not self._active:
return return
@ -294,7 +301,7 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
await self._async_operate(force=True) await self._async_operate(force=True)
self.async_write_ha_state() self.async_write_ha_state()
async def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn hygrostat off.""" """Turn hygrostat off."""
if not self._active: if not self._active:
return return
@ -306,7 +313,7 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
async def async_set_humidity(self, humidity: int) -> None: async def async_set_humidity(self, humidity: int) -> None:
"""Set new target humidity.""" """Set new target humidity."""
if humidity is None: if humidity is None:
return return # type: ignore[unreachable]
if self._is_away and self._away_fixed: if self._is_away and self._away_fixed:
self._saved_target_humidity = humidity self._saved_target_humidity = humidity
@ -318,7 +325,7 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
self.async_write_ha_state() self.async_write_ha_state()
@property @property
def min_humidity(self): def min_humidity(self) -> int:
"""Return the minimum humidity.""" """Return the minimum humidity."""
if self._min_humidity: if self._min_humidity:
return self._min_humidity return self._min_humidity
@ -327,7 +334,7 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
return super().min_humidity return super().min_humidity
@property @property
def max_humidity(self): def max_humidity(self) -> int:
"""Return the maximum humidity.""" """Return the maximum humidity."""
if self._max_humidity: if self._max_humidity:
return self._max_humidity return self._max_humidity
@ -335,7 +342,9 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
# Get default humidity from super class # Get default humidity from super class
return super().max_humidity return super().max_humidity
async def _async_sensor_changed(self, entity_id, old_state, new_state): async def _async_sensor_changed(
self, entity_id: str, old_state: State | None, new_state: State | None
) -> None:
"""Handle ambient humidity changes.""" """Handle ambient humidity changes."""
if new_state is None: if new_state is None:
return return
@ -353,18 +362,21 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
await self._async_operate() await self._async_operate()
self.async_write_ha_state() self.async_write_ha_state()
async def _async_sensor_not_responding(self, now=None): async def _async_sensor_not_responding(self, now: datetime | None = None) -> None:
"""Handle sensor stale event.""" """Handle sensor stale event."""
state = self.hass.states.get(self._sensor_entity_id)
_LOGGER.debug( _LOGGER.debug(
"Sensor has not been updated for %s", "Sensor has not been updated for %s",
now - self.hass.states.get(self._sensor_entity_id).last_updated, now - state.last_updated if now and state else "---",
) )
_LOGGER.warning("Sensor is stalled, call the emergency stop") _LOGGER.warning("Sensor is stalled, call the emergency stop")
await self._async_update_humidity("Stalled") await self._async_update_humidity("Stalled")
@callback @callback
def _async_switch_changed(self, entity_id, old_state, new_state): def _async_switch_changed(
self, entity_id: str, old_state: State | None, new_state: State | None
) -> None:
"""Handle humidifier switch state changes.""" """Handle humidifier switch state changes."""
if new_state is None: if new_state is None:
return return
@ -379,7 +391,7 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
async def _async_update_humidity(self, humidity): async def _async_update_humidity(self, humidity: str) -> None:
"""Update hygrostat with latest state from sensor.""" """Update hygrostat with latest state from sensor."""
try: try:
self._cur_humidity = float(humidity) self._cur_humidity = float(humidity)
@ -390,7 +402,9 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
if self._is_device_active: if self._is_device_active:
await self._async_device_turn_off() await self._async_device_turn_off()
async def _async_operate(self, time=None, force=False): async def _async_operate(
self, time: datetime | None = None, force: bool = False
) -> None:
"""Check if we need to turn humidifying on or off.""" """Check if we need to turn humidifying on or off."""
async with self._humidity_lock: async with self._humidity_lock:
if not self._active and None not in ( if not self._active and None not in (
@ -432,12 +446,15 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
if force: if force:
# Ignore the tolerance when switched on manually # Ignore the tolerance when switched on manually
dry_tolerance = 0 dry_tolerance: float = 0
wet_tolerance = 0 wet_tolerance: float = 0
else: else:
dry_tolerance = self._dry_tolerance dry_tolerance = self._dry_tolerance
wet_tolerance = self._wet_tolerance wet_tolerance = self._wet_tolerance
if TYPE_CHECKING:
assert self._target_humidity is not None
assert self._cur_humidity is not None
too_dry = self._target_humidity - self._cur_humidity >= dry_tolerance too_dry = self._target_humidity - self._cur_humidity >= dry_tolerance
too_wet = self._cur_humidity - self._target_humidity >= wet_tolerance too_wet = self._cur_humidity - self._target_humidity >= wet_tolerance
if self._is_device_active: if self._is_device_active:
@ -461,16 +478,16 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
await self._async_device_turn_off() await self._async_device_turn_off()
@property @property
def _is_device_active(self): def _is_device_active(self) -> bool:
"""If the toggleable device is currently active.""" """If the toggleable device is currently active."""
return self.hass.states.is_state(self._switch_entity_id, STATE_ON) return self.hass.states.is_state(self._switch_entity_id, STATE_ON)
async def _async_device_turn_on(self): async def _async_device_turn_on(self) -> None:
"""Turn humidifier toggleable device on.""" """Turn humidifier toggleable device on."""
data = {ATTR_ENTITY_ID: self._switch_entity_id} data = {ATTR_ENTITY_ID: self._switch_entity_id}
await self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_ON, data) await self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_ON, data)
async def _async_device_turn_off(self): async def _async_device_turn_off(self) -> None:
"""Turn humidifier toggleable device off.""" """Turn humidifier toggleable device off."""
data = {ATTR_ENTITY_ID: self._switch_entity_id} data = {ATTR_ENTITY_ID: self._switch_entity_id}
await self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_OFF, data) await self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_OFF, data)

View File

@ -1531,6 +1531,16 @@ disallow_untyped_defs = true
warn_return_any = true warn_return_any = true
warn_unreachable = true warn_unreachable = true
[mypy-homeassistant.components.generic_hygrostat.*]
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.*] [mypy-homeassistant.components.geo_location.*]
check_untyped_defs = true check_untyped_defs = true
disallow_incomplete_defs = true disallow_incomplete_defs = true