Use class attributes in devolo Home Control (#53360)

This commit is contained in:
Guido Schmitz 2021-07-24 06:48:32 +02:00 committed by GitHub
parent 7fa8586b17
commit bf3a16eed4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 94 additions and 167 deletions

View File

@ -81,33 +81,28 @@ class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity):
element_uid=element_uid, 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 self._binary_sensor_property.sub_type
or self._binary_sensor_property.sensor_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 != "": 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: 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 self._value = self._binary_sensor_property.state
if element_uid.startswith("devolo.WarningBinaryFI:"): if element_uid.startswith("devolo.WarningBinaryFI:"):
self._device_class = DEVICE_CLASS_PROBLEM self._attr_device_class = DEVICE_CLASS_PROBLEM
self._enabled_default = False self._attr_entity_registry_enabled_default = False
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
"""Return the state.""" """Return the state."""
return bool(self._value) return bool(self._value)
@property
def device_class(self) -> str | None:
"""Return device class."""
return self._device_class
class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity): class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity):
"""Representation of a remote control within devolo Home Control.""" """Representation of a remote control within devolo Home Control."""
@ -131,12 +126,7 @@ class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity):
) )
self._key = key self._key = key
self._state = False self._attr_is_on = False
@property
def is_on(self) -> bool:
"""Return the state."""
return self._state
def _sync(self, message: tuple) -> None: def _sync(self, message: tuple) -> None:
"""Update the binary sensor state.""" """Update the binary sensor state."""
@ -144,11 +134,11 @@ class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity):
message[0] == self._remote_control_property.element_uid message[0] == self._remote_control_property.element_uid
and message[1] == self._key and message[1] == self._key
): ):
self._state = True self._attr_is_on = True
elif ( elif (
message[0] == self._remote_control_property.element_uid and message[1] == 0 message[0] == self._remote_control_property.element_uid and message[1] == 0
): ):
self._state = False self._attr_is_on = False
else: else:
self._generic_message(message) self._generic_message(message)
self.schedule_update_ha_state() self.schedule_update_ha_state()

View File

@ -3,6 +3,9 @@ from __future__ import annotations
from typing import Any 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 ( from homeassistant.components.climate import (
ATTR_TEMPERATURE, ATTR_TEMPERATURE,
HVAC_MODE_HEAT, HVAC_MODE_HEAT,
@ -47,6 +50,25 @@ async def async_setup_entry(
class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntity): class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntity):
"""Representation of a climate/thermostat device within devolo Home Control.""" """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 @property
def current_temperature(self) -> float | None: def current_temperature(self) -> float | None:
"""Return the current temperature.""" """Return the current temperature."""
@ -67,48 +89,6 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit
"""Return the target temperature.""" """Return the target temperature."""
return self._value 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: def set_hvac_mode(self, hvac_mode: str) -> None:
"""Do nothing as devolo devices do not support changing the hvac mode.""" """Do nothing as devolo devices do not support changing the hvac mode."""

View File

@ -3,6 +3,9 @@ from __future__ import annotations
from typing import Any 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 ( from homeassistant.components.cover import (
DEVICE_CLASS_BLIND, DEVICE_CLASS_BLIND,
SUPPORT_CLOSE, SUPPORT_CLOSE,
@ -42,26 +45,31 @@ async def async_setup_entry(
class DevoloCoverDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, CoverEntity): class DevoloCoverDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, CoverEntity):
"""Representation of a cover device within devolo Home Control.""" """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 @property
def current_cover_position(self) -> int: def current_cover_position(self) -> int:
"""Return the current position. 0 is closed. 100 is open.""" """Return the current position. 0 is closed. 100 is open."""
return self._value return self._value
@property
def device_class(self) -> str:
"""Return the class of the device."""
return DEVICE_CLASS_BLIND
@property @property
def is_closed(self) -> bool: def is_closed(self) -> bool:
"""Return if the blind is closed or not.""" """Return if the blind is closed or not."""
return not bool(self._value) 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: def open_cover(self, **kwargs: Any) -> None:
"""Open the blind.""" """Open the blind."""
self._multi_level_switch_property.set(100) self._multi_level_switch_property.set(100)

View File

@ -6,7 +6,7 @@ import logging
from devolo_home_control_api.devices.zwave import Zwave from devolo_home_control_api.devices.zwave import Zwave
from devolo_home_control_api.homecontrol import HomeControl 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 .const import DOMAIN
from .subscriber import Subscriber from .subscriber import Subscriber
@ -22,30 +22,34 @@ class DevoloDeviceEntity(Entity):
) -> None: ) -> None:
"""Initialize a devolo device entity.""" """Initialize a devolo device entity."""
self._device_instance = device_instance self._device_instance = device_instance
self._unique_id = element_uid
self._homecontrol = homecontrol 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" "general_device_settings"
].name ].name
self._area = device_instance.settings_property["general_device_settings"].zone self._attr_should_poll = False
self._device_class: str | None = None self._attr_unique_id = element_uid
self._value: int self._attr_device_info = {
self._unit = "" "identifiers": {(DOMAIN, self._device_instance.uid)},
self._enabled_default = True "name": self._attr_name,
"manufacturer": device_instance.brand,
# This is not doing I/O. It fetches an internal state of the API "model": device_instance.name,
self._available: bool = device_instance.is_online() "suggested_area": device_instance.settings_property[
"general_device_settings"
# Get the brand and model information ].zone,
self._brand = device_instance.brand }
self._model = device_instance.name
self.subscriber: Subscriber | None = None self.subscriber: Subscriber | None = None
self.sync_callback = self._sync self.sync_callback = self._sync
self._value: int
self._unit = ""
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
"""Call when entity is added to hass.""" """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._homecontrol.publisher.register(
self._device_instance.uid, self.subscriber, self.sync_callback self._device_instance.uid, self.subscriber, self.sync_callback
) )
@ -56,45 +60,9 @@ class DevoloDeviceEntity(Entity):
self._device_instance.uid, self.subscriber 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: def _sync(self, message: tuple) -> None:
"""Update the state.""" """Update the state."""
if message[0] == self._unique_id: if message[0] == self._attr_unique_id:
self._value = message[1] self._value = message[1]
else: else:
self._generic_message(message) self._generic_message(message)
@ -106,6 +74,6 @@ class DevoloDeviceEntity(Entity):
self._value = message[1] self._value = message[1]
elif len(message) == 3 and message[2] == "status": elif len(message) == 3 and message[2] == "status":
# Maybe the API wants to tell us, that the device went on- or offline. # 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: else:
_LOGGER.debug("No valid message received: %s", message) _LOGGER.debug("No valid message received: %s", message)

View File

@ -53,6 +53,7 @@ class DevoloLightDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, LightEntity):
element_uid=element_uid, element_uid=element_uid,
) )
self._attr_supported_features = SUPPORT_BRIGHTNESS
self._binary_switch_property = device_instance.binary_switch_property.get( self._binary_switch_property = device_instance.binary_switch_property.get(
element_uid.replace("Dimmer", "BinarySwitch") element_uid.replace("Dimmer", "BinarySwitch")
) )
@ -67,11 +68,6 @@ class DevoloLightDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, LightEntity):
"""Return the state of the light.""" """Return the state of the light."""
return bool(self._value) return bool(self._value)
@property
def supported_features(self) -> int:
"""Return the supported features."""
return SUPPORT_BRIGHTNESS
def turn_on(self, **kwargs: Any) -> None: def turn_on(self, **kwargs: Any) -> None:
"""Turn device on.""" """Turn device on."""
if kwargs.get(ATTR_BRIGHTNESS) is not None: if kwargs.get(ATTR_BRIGHTNESS) is not None:

View File

@ -77,21 +77,11 @@ async def async_setup_entry(
class DevoloMultiLevelDeviceEntity(DevoloDeviceEntity, SensorEntity): class DevoloMultiLevelDeviceEntity(DevoloDeviceEntity, SensorEntity):
"""Abstract representation of a multi level sensor within devolo Home Control.""" """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 @property
def state(self) -> int: def state(self) -> int:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self._value return self._value
@property
def unit_of_measurement(self) -> str:
"""Return the unit of measurement of this entity."""
return self._unit
class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity): class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity):
"""Representation of a generic multi level sensor within devolo Home Control.""" """Representation of a generic multi level sensor within devolo Home Control."""
@ -113,18 +103,18 @@ class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity):
element_uid=element_uid, 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._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._value = self._multi_level_sensor_property.value
self._unit = self._multi_level_sensor_property.unit
if self._device_class is None: if self._attr_device_class is None:
self._name += f" {self._multi_level_sensor_property.sensor_type}" self._attr_name += f" {self._multi_level_sensor_property.sensor_type}"
if element_uid.startswith("devolo.VoltageMultiLevelSensor:"): if element_uid.startswith("devolo.VoltageMultiLevelSensor:"):
self._enabled_default = False self._attr_entity_registry_enabled_default = False
class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity): class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity):
@ -141,10 +131,10 @@ class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity):
element_uid=element_uid, 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._value = device_instance.battery_level
self._unit = PERCENTAGE
class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity): class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity):
@ -166,7 +156,10 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity):
) )
self._sensor_type = consumption 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": if consumption == "total":
self._attr_state_class = STATE_CLASS_MEASUREMENT self._attr_state_class = STATE_CLASS_MEASUREMENT
@ -177,27 +170,24 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity):
self._value = getattr( self._value = getattr(
device_instance.consumption_property[element_uid], consumption 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 @property
def unique_id(self) -> str: def unique_id(self) -> str:
"""Return the unique ID of the entity.""" """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: def _sync(self, message: tuple) -> None:
"""Update the consumption sensor state.""" """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._value = getattr(
self._device_instance.consumption_property[self._unique_id], self._device_instance.consumption_property[self._attr_unique_id],
self._sensor_type, 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._attr_last_reset = self._device_instance.consumption_property[
self._unique_id self._attr_unique_id
].total_since ].total_since
else: else:
self._generic_message(message) self._generic_message(message)

View File

@ -51,29 +51,24 @@ class DevoloSwitch(DevoloDeviceEntity, SwitchEntity):
element_uid=element_uid, element_uid=element_uid,
) )
self._binary_switch_property = self._device_instance.binary_switch_property.get( 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 self._attr_is_on = self._binary_switch_property.state
@property
def is_on(self) -> bool:
"""Return the state."""
return self._is_on
def turn_on(self, **kwargs: Any) -> None: def turn_on(self, **kwargs: Any) -> None:
"""Switch on the device.""" """Switch on the device."""
self._is_on = True
self._binary_switch_property.set(state=True) self._binary_switch_property.set(state=True)
def turn_off(self, **kwargs: Any) -> None: def turn_off(self, **kwargs: Any) -> None:
"""Switch off the device.""" """Switch off the device."""
self._is_on = False
self._binary_switch_property.set(state=False) self._binary_switch_property.set(state=False)
def _sync(self, message: tuple) -> None: def _sync(self, message: tuple) -> None:
"""Update the binary switch state and consumption.""" """Update the binary switch state and consumption."""
if message[0].startswith("devolo.BinarySwitch"): 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: else:
self._generic_message(message) self._generic_message(message)
self.schedule_update_ha_state() self.schedule_update_ha_state()