From bf3a16eed41cd79f2b3403c36f495abc392d8136 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Sat, 24 Jul 2021 06:48:32 +0200 Subject: [PATCH] Use class attributes in devolo Home Control (#53360) --- .../devolo_home_control/binary_sensor.py | 28 +++---- .../components/devolo_home_control/climate.py | 64 ++++++---------- .../components/devolo_home_control/cover.py | 28 ++++--- .../devolo_home_control/devolo_device.py | 76 ++++++------------- .../components/devolo_home_control/light.py | 6 +- .../components/devolo_home_control/sensor.py | 44 +++++------ .../components/devolo_home_control/switch.py | 15 ++-- 7 files changed, 94 insertions(+), 167 deletions(-) diff --git a/homeassistant/components/devolo_home_control/binary_sensor.py b/homeassistant/components/devolo_home_control/binary_sensor.py index c8ce1c3585d..c19d74b4c33 100644 --- a/homeassistant/components/devolo_home_control/binary_sensor.py +++ b/homeassistant/components/devolo_home_control/binary_sensor.py @@ -81,33 +81,28 @@ class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity): element_uid=element_uid, ) - self._device_class = DEVICE_CLASS_MAPPING.get( + self._attr_device_class = DEVICE_CLASS_MAPPING.get( self._binary_sensor_property.sub_type or self._binary_sensor_property.sensor_type ) - if self._device_class is None: + if self._attr_device_class is None: if device_instance.binary_sensor_property.get(element_uid).sub_type != "": - self._name += f" {device_instance.binary_sensor_property.get(element_uid).sub_type}" + self._attr_name += f" {device_instance.binary_sensor_property.get(element_uid).sub_type}" else: - self._name += f" {device_instance.binary_sensor_property.get(element_uid).sensor_type}" + self._attr_name += f" {device_instance.binary_sensor_property.get(element_uid).sensor_type}" self._value = self._binary_sensor_property.state if element_uid.startswith("devolo.WarningBinaryFI:"): - self._device_class = DEVICE_CLASS_PROBLEM - self._enabled_default = False + self._attr_device_class = DEVICE_CLASS_PROBLEM + self._attr_entity_registry_enabled_default = False @property def is_on(self) -> bool: """Return the state.""" return bool(self._value) - @property - def device_class(self) -> str | None: - """Return device class.""" - return self._device_class - class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity): """Representation of a remote control within devolo Home Control.""" @@ -131,12 +126,7 @@ class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity): ) self._key = key - self._state = False - - @property - def is_on(self) -> bool: - """Return the state.""" - return self._state + self._attr_is_on = False def _sync(self, message: tuple) -> None: """Update the binary sensor state.""" @@ -144,11 +134,11 @@ class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity): message[0] == self._remote_control_property.element_uid and message[1] == self._key ): - self._state = True + self._attr_is_on = True elif ( message[0] == self._remote_control_property.element_uid and message[1] == 0 ): - self._state = False + self._attr_is_on = False else: self._generic_message(message) self.schedule_update_ha_state() diff --git a/homeassistant/components/devolo_home_control/climate.py b/homeassistant/components/devolo_home_control/climate.py index 6b890544da5..ff4d8a01198 100644 --- a/homeassistant/components/devolo_home_control/climate.py +++ b/homeassistant/components/devolo_home_control/climate.py @@ -3,6 +3,9 @@ from __future__ import annotations from typing import Any +from devolo_home_control_api.devices.zwave import Zwave +from devolo_home_control_api.homecontrol import HomeControl + from homeassistant.components.climate import ( ATTR_TEMPERATURE, HVAC_MODE_HEAT, @@ -47,6 +50,25 @@ async def async_setup_entry( class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntity): """Representation of a climate/thermostat device within devolo Home Control.""" + def __init__( + self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str + ) -> None: + """Initialize a climate entity within devolo Home Control.""" + super().__init__( + homecontrol=homecontrol, + device_instance=device_instance, + element_uid=element_uid, + ) + + self._attr_hvac_mode = HVAC_MODE_HEAT + self._attr_hvac_modes = [HVAC_MODE_HEAT] + self._attr_min_temp = self._multi_level_switch_property.min + self._attr_max_temp = self._multi_level_switch_property.max + self._attr_precision = PRECISION_TENTHS + self._attr_supported_features = SUPPORT_TARGET_TEMPERATURE + self._attr_target_temperature_step = PRECISION_HALVES + self._attr_temperature_unit = TEMP_CELSIUS + @property def current_temperature(self) -> float | None: """Return the current temperature.""" @@ -67,48 +89,6 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit """Return the target temperature.""" return self._value - @property - def target_temperature_step(self) -> float: - """Return the precision of the target temperature.""" - return PRECISION_HALVES - - @property - def hvac_mode(self) -> str: - """Return the supported HVAC mode.""" - return HVAC_MODE_HEAT - - @property - def hvac_modes(self) -> list[str]: - """Return the list of available hvac operation modes.""" - return [HVAC_MODE_HEAT] - - @property - def min_temp(self) -> float: - """Return the minimum set temperature value.""" - min_temp: float = self._multi_level_switch_property.min - return min_temp - - @property - def max_temp(self) -> float: - """Return the maximum set temperature value.""" - max_temp: float = self._multi_level_switch_property.max - return max_temp - - @property - def precision(self) -> float: - """Return the precision of the set temperature.""" - return PRECISION_TENTHS - - @property - def supported_features(self) -> int: - """Flag supported features.""" - return SUPPORT_TARGET_TEMPERATURE - - @property - def temperature_unit(self) -> str: - """Return the supported unit of temperature.""" - return TEMP_CELSIUS - def set_hvac_mode(self, hvac_mode: str) -> None: """Do nothing as devolo devices do not support changing the hvac mode.""" diff --git a/homeassistant/components/devolo_home_control/cover.py b/homeassistant/components/devolo_home_control/cover.py index b2ea2f66b67..7a1a93596d3 100644 --- a/homeassistant/components/devolo_home_control/cover.py +++ b/homeassistant/components/devolo_home_control/cover.py @@ -3,6 +3,9 @@ from __future__ import annotations from typing import Any +from devolo_home_control_api.devices.zwave import Zwave +from devolo_home_control_api.homecontrol import HomeControl + from homeassistant.components.cover import ( DEVICE_CLASS_BLIND, SUPPORT_CLOSE, @@ -42,26 +45,31 @@ async def async_setup_entry( class DevoloCoverDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, CoverEntity): """Representation of a cover device within devolo Home Control.""" + def __init__( + self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str + ) -> None: + """Initialize a climate entity within devolo Home Control.""" + super().__init__( + homecontrol=homecontrol, + device_instance=device_instance, + element_uid=element_uid, + ) + + self._attr_device_class = DEVICE_CLASS_BLIND + self._attr_supported_features = ( + SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION + ) + @property def current_cover_position(self) -> int: """Return the current position. 0 is closed. 100 is open.""" return self._value - @property - def device_class(self) -> str: - """Return the class of the device.""" - return DEVICE_CLASS_BLIND - @property def is_closed(self) -> bool: """Return if the blind is closed or not.""" return not bool(self._value) - @property - def supported_features(self) -> int: - """Flag supported features.""" - return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION - def open_cover(self, **kwargs: Any) -> None: """Open the blind.""" self._multi_level_switch_property.set(100) diff --git a/homeassistant/components/devolo_home_control/devolo_device.py b/homeassistant/components/devolo_home_control/devolo_device.py index e8eec3ca7dc..781799cbf37 100644 --- a/homeassistant/components/devolo_home_control/devolo_device.py +++ b/homeassistant/components/devolo_home_control/devolo_device.py @@ -6,7 +6,7 @@ import logging from devolo_home_control_api.devices.zwave import Zwave from devolo_home_control_api.homecontrol import HomeControl -from homeassistant.helpers.entity import DeviceInfo, Entity +from homeassistant.helpers.entity import Entity from .const import DOMAIN from .subscriber import Subscriber @@ -22,30 +22,34 @@ class DevoloDeviceEntity(Entity): ) -> None: """Initialize a devolo device entity.""" self._device_instance = device_instance - self._unique_id = element_uid self._homecontrol = homecontrol - self._name: str = device_instance.settings_property[ + + self._attr_available = ( + device_instance.is_online() + ) # This is not doing I/O. It fetches an internal state of the API + self._attr_name: str = device_instance.settings_property[ "general_device_settings" ].name - self._area = device_instance.settings_property["general_device_settings"].zone - self._device_class: str | None = None - self._value: int - self._unit = "" - self._enabled_default = True - - # This is not doing I/O. It fetches an internal state of the API - self._available: bool = device_instance.is_online() - - # Get the brand and model information - self._brand = device_instance.brand - self._model = device_instance.name + self._attr_should_poll = False + self._attr_unique_id = element_uid + self._attr_device_info = { + "identifiers": {(DOMAIN, self._device_instance.uid)}, + "name": self._attr_name, + "manufacturer": device_instance.brand, + "model": device_instance.name, + "suggested_area": device_instance.settings_property[ + "general_device_settings" + ].zone, + } self.subscriber: Subscriber | None = None self.sync_callback = self._sync + self._value: int + self._unit = "" async def async_added_to_hass(self) -> None: """Call when entity is added to hass.""" - self.subscriber = Subscriber(self._name, callback=self.sync_callback) + self.subscriber = Subscriber(self._attr_name, callback=self.sync_callback) self._homecontrol.publisher.register( self._device_instance.uid, self.subscriber, self.sync_callback ) @@ -56,45 +60,9 @@ class DevoloDeviceEntity(Entity): self._device_instance.uid, self.subscriber ) - @property - def unique_id(self) -> str: - """Return the unique ID of the entity.""" - return self._unique_id - - @property - def device_info(self) -> DeviceInfo: - """Return the device info.""" - return { - "identifiers": {(DOMAIN, self._device_instance.uid)}, - "name": self._name, - "manufacturer": self._brand, - "model": self._model, - "suggested_area": self._area, - } - - @property - def entity_registry_enabled_default(self) -> bool: - """Return if the entity should be enabled when first added to the entity registry.""" - return self._enabled_default - - @property - def should_poll(self) -> bool: - """Return the polling state.""" - return False - - @property - def name(self) -> str: - """Return the display name of this entity.""" - return self._name - - @property - def available(self) -> bool: - """Return the online state.""" - return self._available - def _sync(self, message: tuple) -> None: """Update the state.""" - if message[0] == self._unique_id: + if message[0] == self._attr_unique_id: self._value = message[1] else: self._generic_message(message) @@ -106,6 +74,6 @@ class DevoloDeviceEntity(Entity): self._value = message[1] elif len(message) == 3 and message[2] == "status": # Maybe the API wants to tell us, that the device went on- or offline. - self._available = self._device_instance.is_online() + self._attr_available = self._device_instance.is_online() else: _LOGGER.debug("No valid message received: %s", message) diff --git a/homeassistant/components/devolo_home_control/light.py b/homeassistant/components/devolo_home_control/light.py index 27c637cf114..28da95c8902 100644 --- a/homeassistant/components/devolo_home_control/light.py +++ b/homeassistant/components/devolo_home_control/light.py @@ -53,6 +53,7 @@ class DevoloLightDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, LightEntity): element_uid=element_uid, ) + self._attr_supported_features = SUPPORT_BRIGHTNESS self._binary_switch_property = device_instance.binary_switch_property.get( element_uid.replace("Dimmer", "BinarySwitch") ) @@ -67,11 +68,6 @@ class DevoloLightDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, LightEntity): """Return the state of the light.""" return bool(self._value) - @property - def supported_features(self) -> int: - """Return the supported features.""" - return SUPPORT_BRIGHTNESS - def turn_on(self, **kwargs: Any) -> None: """Turn device on.""" if kwargs.get(ATTR_BRIGHTNESS) is not None: diff --git a/homeassistant/components/devolo_home_control/sensor.py b/homeassistant/components/devolo_home_control/sensor.py index af67c6cd78a..7cb8cc8e837 100644 --- a/homeassistant/components/devolo_home_control/sensor.py +++ b/homeassistant/components/devolo_home_control/sensor.py @@ -77,21 +77,11 @@ async def async_setup_entry( class DevoloMultiLevelDeviceEntity(DevoloDeviceEntity, SensorEntity): """Abstract representation of a multi level sensor within devolo Home Control.""" - @property - def device_class(self) -> str | None: - """Return device class.""" - return self._device_class - @property def state(self) -> int: """Return the state of the sensor.""" return self._value - @property - def unit_of_measurement(self) -> str: - """Return the unit of measurement of this entity.""" - return self._unit - class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity): """Representation of a generic multi level sensor within devolo Home Control.""" @@ -113,18 +103,18 @@ class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity): element_uid=element_uid, ) - self._device_class = DEVICE_CLASS_MAPPING.get( + self._attr_device_class = DEVICE_CLASS_MAPPING.get( self._multi_level_sensor_property.sensor_type ) + self._attr_unit_of_measurement = self._multi_level_sensor_property.unit self._value = self._multi_level_sensor_property.value - self._unit = self._multi_level_sensor_property.unit - if self._device_class is None: - self._name += f" {self._multi_level_sensor_property.sensor_type}" + if self._attr_device_class is None: + self._attr_name += f" {self._multi_level_sensor_property.sensor_type}" if element_uid.startswith("devolo.VoltageMultiLevelSensor:"): - self._enabled_default = False + self._attr_entity_registry_enabled_default = False class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity): @@ -141,10 +131,10 @@ class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity): element_uid=element_uid, ) - self._device_class = DEVICE_CLASS_MAPPING.get("battery") + self._attr_device_class = DEVICE_CLASS_MAPPING.get("battery") + self._attr_unit_of_measurement = PERCENTAGE self._value = device_instance.battery_level - self._unit = PERCENTAGE class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity): @@ -166,7 +156,10 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity): ) self._sensor_type = consumption - self._device_class = DEVICE_CLASS_MAPPING.get(consumption) + self._attr_device_class = DEVICE_CLASS_MAPPING.get(consumption) + self._attr_unit_of_measurement = getattr( + device_instance.consumption_property[element_uid], f"{consumption}_unit" + ) if consumption == "total": self._attr_state_class = STATE_CLASS_MEASUREMENT @@ -177,27 +170,24 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity): self._value = getattr( device_instance.consumption_property[element_uid], consumption ) - self._unit = getattr( - device_instance.consumption_property[element_uid], f"{consumption}_unit" - ) - self._name += f" {consumption}" + self._attr_name += f" {consumption}" @property def unique_id(self) -> str: """Return the unique ID of the entity.""" - return f"{self._unique_id}_{self._sensor_type}" + return f"{self._attr_unique_id}_{self._sensor_type}" def _sync(self, message: tuple) -> None: """Update the consumption sensor state.""" - if message[0] == self._unique_id and message[2] != "total_since": + if message[0] == self._attr_unique_id and message[2] != "total_since": self._value = getattr( - self._device_instance.consumption_property[self._unique_id], + self._device_instance.consumption_property[self._attr_unique_id], self._sensor_type, ) - elif message[0] == self._unique_id and message[2] == "total_since": + elif message[0] == self._attr_unique_id and message[2] == "total_since": self._attr_last_reset = self._device_instance.consumption_property[ - self._unique_id + self._attr_unique_id ].total_since else: self._generic_message(message) diff --git a/homeassistant/components/devolo_home_control/switch.py b/homeassistant/components/devolo_home_control/switch.py index c9dabf23c39..4896d66b805 100644 --- a/homeassistant/components/devolo_home_control/switch.py +++ b/homeassistant/components/devolo_home_control/switch.py @@ -51,29 +51,24 @@ class DevoloSwitch(DevoloDeviceEntity, SwitchEntity): element_uid=element_uid, ) self._binary_switch_property = self._device_instance.binary_switch_property.get( - self._unique_id + self._attr_unique_id ) - self._is_on: bool = self._binary_switch_property.state - - @property - def is_on(self) -> bool: - """Return the state.""" - return self._is_on + self._attr_is_on = self._binary_switch_property.state def turn_on(self, **kwargs: Any) -> None: """Switch on the device.""" - self._is_on = True self._binary_switch_property.set(state=True) def turn_off(self, **kwargs: Any) -> None: """Switch off the device.""" - self._is_on = False self._binary_switch_property.set(state=False) def _sync(self, message: tuple) -> None: """Update the binary switch state and consumption.""" if message[0].startswith("devolo.BinarySwitch"): - self._is_on = self._device_instance.binary_switch_property[message[0]].state + self._attr_is_on = self._device_instance.binary_switch_property[ + message[0] + ].state else: self._generic_message(message) self.schedule_update_ha_state()