Code quality improvements at Home Connect (#126323)

Added types to all arguments and return values to all functions

Defined class members and its types outside the constructor

Improved logic at binary sensor
This commit is contained in:
J. Diego Rodríguez Royo 2024-09-23 13:33:19 +02:00 committed by GitHub
parent ec311ecd2b
commit b7ba789370
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 100 additions and 75 deletions

View file

@ -244,7 +244,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@Throttle(SCAN_INTERVAL)
async def update_all_devices(hass, entry):
async def update_all_devices(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Update all the devices."""
data = hass.data[DOMAIN]
hc_api = data[entry.entry_id]

View file

@ -1,14 +1,15 @@
"""API for Home Connect bound to HASS OAuth."""
from abc import abstractmethod
from asyncio import run_coroutine_threadsafe
import logging
from typing import Any
import homeconnect
from homeconnect.api import HomeConnectError
from homeconnect.api import HomeConnectAppliance, HomeConnectError
from homeassistant import config_entries, core
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_ICON,
@ -17,6 +18,7 @@ from homeassistant.const import (
PERCENTAGE,
UnitOfTime,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.helpers.dispatcher import dispatcher_send
@ -44,8 +46,8 @@ class ConfigEntryAuth(homeconnect.HomeConnectAPI):
def __init__(
self,
hass: core.HomeAssistant,
config_entry: config_entries.ConfigEntry,
hass: HomeAssistant,
config_entry: ConfigEntry,
implementation: config_entry_oauth2_flow.AbstractOAuth2Implementation,
) -> None:
"""Initialize Home Connect Auth."""
@ -65,11 +67,12 @@ class ConfigEntryAuth(homeconnect.HomeConnectAPI):
return self.session.token
def get_devices(self):
def get_devices(self) -> list[dict[str, Any]]:
"""Get a dictionary of devices."""
appl = self.get_appliances()
devices = []
for app in appl:
device: HomeConnectDevice
if app.type == "Dryer":
device = Dryer(self.hass, app)
elif app.type == "Washer":
@ -110,13 +113,15 @@ class HomeConnectDevice:
# for some devices, this is instead BSH_POWER_STANDBY
# see https://developer.home-connect.com/docs/settings/power_state
power_off_state = BSH_POWER_OFF
hass: HomeAssistant
appliance: HomeConnectAppliance
def __init__(self, hass, appliance):
def __init__(self, hass: HomeAssistant, appliance: HomeConnectAppliance) -> None:
"""Initialize the device class."""
self.hass = hass
self.appliance = appliance
def initialize(self):
def initialize(self) -> None:
"""Fetch the info needed to initialize the device."""
try:
self.appliance.get_status()
@ -137,17 +142,22 @@ class HomeConnectDevice:
}
self.appliance.listen_events(callback=self.event_callback)
def event_callback(self, appliance):
def event_callback(self, appliance: HomeConnectAppliance) -> None:
"""Handle event."""
_LOGGER.debug("Update triggered on %s", appliance.name)
_LOGGER.debug(self.appliance.status)
dispatcher_send(self.hass, SIGNAL_UPDATE_ENTITIES, appliance.haId)
@abstractmethod
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with info about the associated entities."""
raise NotImplementedError
class DeviceWithPrograms(HomeConnectDevice):
"""Device with programs."""
def get_programs_available(self):
def get_programs_available(self) -> list:
"""Get the available programs."""
try:
programs_available = self.appliance.get_programs_available()
@ -156,7 +166,7 @@ class DeviceWithPrograms(HomeConnectDevice):
programs_available = []
return programs_available
def get_program_switches(self):
def get_program_switches(self) -> list[dict[str, Any]]:
"""Get a dictionary with info about program switches.
There will be one switch for each program.
@ -164,7 +174,7 @@ class DeviceWithPrograms(HomeConnectDevice):
programs = self.get_programs_available()
return [{ATTR_DEVICE: self, "program_name": p} for p in programs]
def get_program_sensors(self):
def get_program_sensors(self) -> list[dict[str, Any]]:
"""Get a dictionary with info about program sensors.
There will be one of the four types of sensors for each
@ -192,7 +202,7 @@ class DeviceWithPrograms(HomeConnectDevice):
class DeviceWithOpState(HomeConnectDevice):
"""Device that has an operation state sensor."""
def get_opstate_sensor(self):
def get_opstate_sensor(self) -> list[dict[str, Any]]:
"""Get a list with info about operation state sensors."""
return [
@ -211,7 +221,7 @@ class DeviceWithOpState(HomeConnectDevice):
class DeviceWithDoor(HomeConnectDevice):
"""Device that has a door sensor."""
def get_door_entity(self):
def get_door_entity(self) -> dict[str, Any]:
"""Get a dictionary with info about the door binary sensor."""
return {
ATTR_DEVICE: self,
@ -224,7 +234,7 @@ class DeviceWithDoor(HomeConnectDevice):
class DeviceWithLight(HomeConnectDevice):
"""Device that has lighting."""
def get_light_entity(self):
def get_light_entity(self) -> dict[str, Any]:
"""Get a dictionary with info about the lighting."""
return {ATTR_DEVICE: self, ATTR_DESC: "Light", ATTR_AMBIENT: None}
@ -232,7 +242,7 @@ class DeviceWithLight(HomeConnectDevice):
class DeviceWithAmbientLight(HomeConnectDevice):
"""Device that has ambient lighting."""
def get_ambientlight_entity(self):
def get_ambientlight_entity(self) -> dict[str, Any]:
"""Get a dictionary with info about the ambient lighting."""
return {ATTR_DEVICE: self, ATTR_DESC: "AmbientLight", ATTR_AMBIENT: True}
@ -240,7 +250,7 @@ class DeviceWithAmbientLight(HomeConnectDevice):
class DeviceWithRemoteControl(HomeConnectDevice):
"""Device that has Remote Control binary sensor."""
def get_remote_control(self):
def get_remote_control(self) -> dict[str, Any]:
"""Get a dictionary with info about the remote control sensor."""
return {
ATTR_DEVICE: self,
@ -252,7 +262,7 @@ class DeviceWithRemoteControl(HomeConnectDevice):
class DeviceWithRemoteStart(HomeConnectDevice):
"""Device that has a Remote Start binary sensor."""
def get_remote_start(self):
def get_remote_start(self) -> dict[str, Any]:
"""Get a dictionary with info about the remote start sensor."""
return {
ATTR_DEVICE: self,
@ -270,7 +280,7 @@ class Dryer(
):
"""Dryer class."""
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
door_entity = self.get_door_entity()
remote_control = self.get_remote_control()
@ -295,7 +305,7 @@ class Dishwasher(
):
"""Dishwasher class."""
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
door_entity = self.get_door_entity()
remote_control = self.get_remote_control()
@ -321,7 +331,7 @@ class Oven(
power_off_state = BSH_POWER_STANDBY
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
door_entity = self.get_door_entity()
remote_control = self.get_remote_control()
@ -345,7 +355,7 @@ class Washer(
):
"""Washer class."""
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
door_entity = self.get_door_entity()
remote_control = self.get_remote_control()
@ -369,7 +379,7 @@ class WasherDryer(
):
"""WasherDryer class."""
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
door_entity = self.get_door_entity()
remote_control = self.get_remote_control()
@ -412,7 +422,7 @@ class Hood(
):
"""Hood class."""
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
remote_control = self.get_remote_control()
remote_start = self.get_remote_start()
@ -432,7 +442,7 @@ class Hood(
class FridgeFreezer(DeviceWithDoor):
"""Fridge/Freezer class."""
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
door_entity = self.get_door_entity()
return {"binary_sensor": [door_entity]}
@ -441,7 +451,7 @@ class FridgeFreezer(DeviceWithDoor):
class Refrigerator(DeviceWithDoor):
"""Refrigerator class."""
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
door_entity = self.get_door_entity()
return {"binary_sensor": [door_entity]}
@ -450,7 +460,7 @@ class Refrigerator(DeviceWithDoor):
class Freezer(DeviceWithDoor):
"""Freezer class."""
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
door_entity = self.get_door_entity()
return {"binary_sensor": [door_entity]}
@ -459,7 +469,7 @@ class Freezer(DeviceWithDoor):
class Hob(DeviceWithOpState, DeviceWithPrograms, DeviceWithRemoteControl):
"""Hob class."""
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
remote_control = self.get_remote_control()
op_state_sensor = self.get_opstate_sensor()
@ -477,7 +487,7 @@ class CookProcessor(DeviceWithOpState):
power_off_state = BSH_POWER_STANDBY
def get_entity_info(self):
def get_entity_info(self) -> dict[str, list[dict[str, Any]]]:
"""Get a dictionary with infos about the associated entities."""
op_state_sensor = self.get_opstate_sensor()
return {"sensor": op_state_sensor}

View file

@ -72,8 +72,8 @@ async def async_setup_entry(
) -> None:
"""Set up the Home Connect binary sensor."""
def get_entities():
entities = []
def get_entities() -> list[BinarySensorEntity]:
entities: list[BinarySensorEntity] = []
hc_api = hass.data[DOMAIN][config_entry.entry_id]
for device_dict in hc_api.devices:
entity_dicts = device_dict.get(CONF_ENTITIES, {}).get("binary_sensor", [])
@ -95,55 +95,59 @@ async def async_setup_entry(
class HomeConnectBinarySensor(HomeConnectEntity, BinarySensorEntity):
"""Binary sensor for Home Connect."""
def __init__(self, device, desc, sensor_type, device_class=None):
def __init__(
self,
device: HomeConnectDevice,
desc: str,
sensor_type: str,
device_class: BinarySensorDeviceClass | None = None,
) -> None:
"""Initialize the entity."""
super().__init__(device, desc)
self._state = None
self._device_class = device_class
self._attr_device_class = device_class
self._type = sensor_type
self._false_value_list = None
self._true_value_list = None
if self._type == "door":
self._update_key = BSH_DOOR_STATE
self._false_value_list = (BSH_DOOR_STATE_CLOSED, BSH_DOOR_STATE_LOCKED)
self._false_value_list = [BSH_DOOR_STATE_CLOSED, BSH_DOOR_STATE_LOCKED]
self._true_value_list = [BSH_DOOR_STATE_OPEN]
elif self._type == "remote_control":
self._update_key = BSH_REMOTE_CONTROL_ACTIVATION_STATE
self._false_value_list = [False]
self._true_value_list = [True]
elif self._type == "remote_start":
self._update_key = BSH_REMOTE_START_ALLOWANCE_STATE
self._false_value_list = [False]
self._true_value_list = [True]
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return bool(self._state)
@property
def available(self) -> bool:
"""Return true if the binary sensor is available."""
return self._state is not None
return self._attr_is_on is not None
async def async_update(self) -> None:
"""Update the binary sensor's status."""
state = self.device.appliance.status.get(self._update_key, {})
if not state:
self._state = None
elif state.get(ATTR_VALUE) in self._false_value_list:
self._state = False
elif state.get(ATTR_VALUE) in self._true_value_list:
self._state = True
self._attr_is_on = None
return
value = state.get(ATTR_VALUE)
if self._false_value_list and self._true_value_list:
if value in self._false_value_list:
self._attr_is_on = False
elif value in self._true_value_list:
self._attr_is_on = True
else:
_LOGGER.warning(
"Unexpected value for HomeConnect %s state: %s", self._type, state
)
self._attr_is_on = None
elif isinstance(value, bool):
self._attr_is_on = value
else:
_LOGGER.warning(
"Unexpected value for HomeConnect %s state: %s", self._type, state
)
self._state = None
_LOGGER.debug("Updated, new state: %s", self._state)
@property
def device_class(self):
"""Return the device class."""
return self._device_class
self._attr_is_on = None
_LOGGER.debug("Updated, new state: %s", self._attr_is_on)
class HomeConnectFridgeDoorBinarySensor(HomeConnectEntity, BinarySensorEntity):

View file

@ -30,7 +30,7 @@ class HomeConnectEntity(Entity):
name=device.appliance.name,
)
async def async_added_to_hass(self):
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
self.async_on_remove(
async_dispatcher_connect(
@ -39,13 +39,13 @@ class HomeConnectEntity(Entity):
)
@callback
def _update_callback(self, ha_id):
def _update_callback(self, ha_id: str) -> None:
"""Update data."""
if ha_id == self.device.appliance.haId:
self.async_entity_update()
@callback
def async_entity_update(self):
def async_entity_update(self) -> None:
"""Update the entity."""
_LOGGER.debug("Entity update triggered on %s", self)
self.async_schedule_update_ha_state(True)

View file

@ -70,9 +70,9 @@ async def async_setup_entry(
) -> None:
"""Set up the Home Connect light."""
def get_entities():
def get_entities() -> list[LightEntity]:
"""Get a list of entities."""
entities = []
entities: list[LightEntity] = []
hc_api = hass.data[DOMAIN][config_entry.entry_id]
for device_dict in hc_api.devices:
entity_dicts = device_dict.get(CONF_ENTITIES, {}).get("light", [])

View file

@ -97,9 +97,9 @@ async def async_setup_entry(
) -> None:
"""Set up the Home Connect sensor."""
def get_entities():
def get_entities() -> list[SensorEntity]:
"""Get a list of entities."""
entities = []
entities: list[SensorEntity] = []
hc_api: ConfigEntryAuth = hass.data[DOMAIN][config_entry.entry_id]
for device_dict in hc_api.devices:
entity_dicts = device_dict.get(CONF_ENTITIES, {}).get("sensor", [])
@ -122,7 +122,19 @@ async def async_setup_entry(
class HomeConnectSensor(HomeConnectEntity, SensorEntity):
"""Sensor class for Home Connect."""
def __init__(self, device, desc, key, unit, icon, device_class, sign=1):
_key: str
_sign: int
def __init__(
self,
device: HomeConnectDevice,
desc: str,
key: str,
unit: str,
icon: str,
device_class: SensorDeviceClass,
sign: int = 1,
) -> None:
"""Initialize the entity."""
super().__init__(device, desc)
self._key = key

View file

@ -61,15 +61,15 @@ async def async_setup_entry(
) -> None:
"""Set up the Home Connect switch."""
def get_entities():
def get_entities() -> list[SwitchEntity]:
"""Get a list of entities."""
entities = []
entities: list[SwitchEntity] = []
hc_api: ConfigEntryAuth = hass.data[DOMAIN][config_entry.entry_id]
for device_dict in hc_api.devices:
entity_dicts = device_dict.get(CONF_ENTITIES, {}).get("switch", [])
entity_list = [HomeConnectProgramSwitch(**d) for d in entity_dicts]
entity_list += [HomeConnectPowerSwitch(device_dict[CONF_DEVICE])]
entity_list += [HomeConnectChildLockSwitch(device_dict[CONF_DEVICE])]
entities.extend(HomeConnectProgramSwitch(**d) for d in entity_dicts)
entities.append(HomeConnectPowerSwitch(device_dict[CONF_DEVICE]))
entities.append(HomeConnectChildLockSwitch(device_dict[CONF_DEVICE]))
# Auto-discover entities
hc_device: HomeConnectDevice = device_dict[CONF_DEVICE]
entities.extend(
@ -77,7 +77,6 @@ async def async_setup_entry(
for description in SWITCHES
if description.on_key in hc_device.appliance.status
)
entities.extend(entity_list)
return entities
@ -88,7 +87,6 @@ class HomeConnectSwitch(HomeConnectEntity, SwitchEntity):
"""Generic switch class for Home Connect Binary Settings."""
entity_description: HomeConnectSwitchEntityDescription
_attr_available: bool = False
def __init__(
self,
@ -97,6 +95,7 @@ class HomeConnectSwitch(HomeConnectEntity, SwitchEntity):
) -> None:
"""Initialize the entity."""
self.entity_description = entity_description
self._attr_available = False
super().__init__(device=device, desc=entity_description.key)
async def async_turn_on(self, **kwargs: Any) -> None:
@ -148,7 +147,7 @@ class HomeConnectSwitch(HomeConnectEntity, SwitchEntity):
class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
"""Switch class for Home Connect."""
def __init__(self, device, program_name):
def __init__(self, device: HomeConnectDevice, program_name: str) -> None:
"""Initialize the entity."""
desc = " ".join(["Program", program_name.split(".")[-1]])
if device.appliance.type == "WasherDryer":
@ -191,7 +190,7 @@ class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
class HomeConnectPowerSwitch(HomeConnectEntity, SwitchEntity):
"""Power switch class for Home Connect."""
def __init__(self, device):
def __init__(self, device: HomeConnectDevice) -> None:
"""Initialize the entity."""
super().__init__(device, "Power")
@ -258,7 +257,7 @@ class HomeConnectPowerSwitch(HomeConnectEntity, SwitchEntity):
class HomeConnectChildLockSwitch(HomeConnectEntity, SwitchEntity):
"""Child lock switch class for Home Connect."""
def __init__(self, device) -> None:
def __init__(self, device: HomeConnectDevice) -> None:
"""Initialize the entity."""
super().__init__(device, "ChildLock")