Add static typing to devolo_home_control (#52396)

This commit is contained in:
Guido Schmitz 2021-07-02 18:37:18 +02:00 committed by GitHub
parent e435ac6fcd
commit 8c7ef5b1b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 186 additions and 84 deletions

View File

@ -25,6 +25,7 @@ homeassistant.components.canary.*
homeassistant.components.cover.* homeassistant.components.cover.*
homeassistant.components.device_automation.* homeassistant.components.device_automation.*
homeassistant.components.device_tracker.* homeassistant.components.device_tracker.*
homeassistant.components.devolo_home_control.*
homeassistant.components.dnsip.* homeassistant.components.dnsip.*
homeassistant.components.dsmr.* homeassistant.components.dsmr.*
homeassistant.components.dunehd.* homeassistant.components.dunehd.*

View File

@ -1,6 +1,10 @@
"""The devolo_home_control integration.""" """The devolo_home_control integration."""
from __future__ import annotations
import asyncio import asyncio
from functools import partial from functools import partial
from types import MappingProxyType
from typing import Any
from devolo_home_control_api.exceptions.gateway import GatewayOfflineError from devolo_home_control_api.exceptions.gateway import GatewayOfflineError
from devolo_home_control_api.homecontrol import HomeControl from devolo_home_control_api.homecontrol import HomeControl
@ -9,7 +13,7 @@ from devolo_home_control_api.mydevolo import Mydevolo
from homeassistant.components import zeroconf from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, EVENT_HOMEASSISTANT_STOP from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant from homeassistant.core import Event, HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from .const import ( from .const import (
@ -37,7 +41,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
gateway_ids = await hass.async_add_executor_job(mydevolo.get_gateway_ids) gateway_ids = await hass.async_add_executor_job(mydevolo.get_gateway_ids)
if GATEWAY_SERIAL_PATTERN.match(entry.unique_id): if entry.unique_id and GATEWAY_SERIAL_PATTERN.match(entry.unique_id):
uuid = await hass.async_add_executor_job(mydevolo.uuid) uuid = await hass.async_add_executor_job(mydevolo.uuid)
hass.config_entries.async_update_entry(entry, unique_id=uuid) hass.config_entries.async_update_entry(entry, unique_id=uuid)
@ -60,7 +64,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.config_entries.async_setup_platforms(entry, PLATFORMS) hass.config_entries.async_setup_platforms(entry, PLATFORMS)
def shutdown(event): def shutdown(event: Event) -> None:
for gateway in hass.data[DOMAIN][entry.entry_id]["gateways"]: for gateway in hass.data[DOMAIN][entry.entry_id]["gateways"]:
gateway.websocket_disconnect( gateway.websocket_disconnect(
f"websocket disconnect requested by {EVENT_HOMEASSISTANT_STOP}" f"websocket disconnect requested by {EVENT_HOMEASSISTANT_STOP}"
@ -88,7 +92,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload return unload
def configure_mydevolo(conf: dict) -> Mydevolo: def configure_mydevolo(conf: dict[str, Any] | MappingProxyType[str, Any]) -> Mydevolo:
"""Configure mydevolo.""" """Configure mydevolo."""
mydevolo = Mydevolo() mydevolo = Mydevolo()
mydevolo.user = conf[CONF_USERNAME] mydevolo.user = conf[CONF_USERNAME]

View File

@ -1,4 +1,9 @@
"""Platform for binary sensor integration.""" """Platform for binary sensor integration."""
from __future__ import annotations
from devolo_home_control_api.devices.zwave import Zwave
from devolo_home_control_api.homecontrol import HomeControl
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
DEVICE_CLASS_DOOR, DEVICE_CLASS_DOOR,
DEVICE_CLASS_HEAT, DEVICE_CLASS_HEAT,
@ -11,6 +16,7 @@ from homeassistant.components.binary_sensor import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .devolo_device import DevoloDeviceEntity from .devolo_device import DevoloDeviceEntity
@ -26,10 +32,10 @@ DEVICE_CLASS_MAPPING = {
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
"""Get all binary sensor and multi level sensor devices and setup them via config entry.""" """Get all binary sensor and multi level sensor devices and setup them via config entry."""
entities = [] entities: list[BinarySensorEntity] = []
for gateway in hass.data[DOMAIN][entry.entry_id]["gateways"]: for gateway in hass.data[DOMAIN][entry.entry_id]["gateways"]:
for device in gateway.binary_sensor_devices: for device in gateway.binary_sensor_devices:
@ -61,7 +67,9 @@ async def async_setup_entry(
class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity): class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity):
"""Representation of a binary sensor within devolo Home Control.""" """Representation of a binary sensor within devolo Home Control."""
def __init__(self, homecontrol, device_instance, element_uid): def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
) -> None:
"""Initialize a devolo binary sensor.""" """Initialize a devolo binary sensor."""
self._binary_sensor_property = device_instance.binary_sensor_property.get( self._binary_sensor_property = device_instance.binary_sensor_property.get(
element_uid element_uid
@ -91,12 +99,12 @@ class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity):
self._enabled_default = False self._enabled_default = False
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return the state.""" """Return the state."""
return self._value return bool(self._value)
@property @property
def device_class(self): def device_class(self) -> str | None:
"""Return device class.""" """Return device class."""
return self._device_class return self._device_class
@ -104,7 +112,13 @@ class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity):
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."""
def __init__(self, homecontrol, device_instance, element_uid, key): def __init__(
self,
homecontrol: HomeControl,
device_instance: Zwave,
element_uid: str,
key: int,
) -> None:
"""Initialize a devolo remote control.""" """Initialize a devolo remote control."""
self._remote_control_property = device_instance.remote_control_property.get( self._remote_control_property = device_instance.remote_control_property.get(
element_uid element_uid
@ -120,11 +134,11 @@ class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity):
self._state = False self._state = False
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return the state.""" """Return the state."""
return self._state return self._state
def _sync(self, message): def _sync(self, message: tuple) -> None:
"""Update the binary sensor state.""" """Update the binary sensor state."""
if ( if (
message[0] == self._remote_control_property.element_uid message[0] == self._remote_control_property.element_uid

View File

@ -1,6 +1,8 @@
"""Platform for climate integration.""" """Platform for climate integration."""
from __future__ import annotations from __future__ import annotations
from typing import Any
from homeassistant.components.climate import ( from homeassistant.components.climate import (
ATTR_TEMPERATURE, ATTR_TEMPERATURE,
HVAC_MODE_HEAT, HVAC_MODE_HEAT,
@ -11,13 +13,14 @@ from homeassistant.components.climate import (
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PRECISION_HALVES, PRECISION_TENTHS from homeassistant.const import PRECISION_HALVES, PRECISION_TENTHS
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .devolo_multi_level_switch import DevoloMultiLevelSwitchDeviceEntity from .devolo_multi_level_switch import DevoloMultiLevelSwitchDeviceEntity
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
"""Get all cover devices and setup them via config entry.""" """Get all cover devices and setup them via config entry."""
entities = [] entities = []
@ -82,12 +85,14 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit
@property @property
def min_temp(self) -> float: def min_temp(self) -> float:
"""Return the minimum set temperature value.""" """Return the minimum set temperature value."""
return self._multi_level_switch_property.min min_temp: float = self._multi_level_switch_property.min
return min_temp
@property @property
def max_temp(self) -> float: def max_temp(self) -> float:
"""Return the maximum set temperature value.""" """Return the maximum set temperature value."""
return self._multi_level_switch_property.max max_temp: float = self._multi_level_switch_property.max
return max_temp
@property @property
def precision(self) -> float: def precision(self) -> float:
@ -95,7 +100,7 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit
return PRECISION_TENTHS return PRECISION_TENTHS
@property @property
def supported_features(self): def supported_features(self) -> int:
"""Flag supported features.""" """Flag supported features."""
return SUPPORT_TARGET_TEMPERATURE return SUPPORT_TARGET_TEMPERATURE
@ -107,6 +112,6 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit
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."""
def set_temperature(self, **kwargs): def set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature.""" """Set new target temperature."""
self._multi_level_switch_property.set(kwargs[ATTR_TEMPERATURE]) self._multi_level_switch_property.set(kwargs[ATTR_TEMPERATURE])

View File

@ -1,9 +1,15 @@
"""Config flow to configure the devolo home control integration.""" """Config flow to configure the devolo home control integration."""
from __future__ import annotations
from typing import Any
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.typing import DiscoveryInfoType from homeassistant.helpers.typing import DiscoveryInfoType
from . import configure_mydevolo from . import configure_mydevolo
@ -16,16 +22,18 @@ class DevoloHomeControlFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1 VERSION = 1
def __init__(self): def __init__(self) -> None:
"""Initialize devolo Home Control flow.""" """Initialize devolo Home Control flow."""
self.data_schema = { self.data_schema = {
vol.Required(CONF_USERNAME): str, vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str, vol.Required(CONF_PASSWORD): str,
} }
self._reauth_entry = None self._reauth_entry: ConfigEntry | None = None
self._url = DEFAULT_MYDEVOLO self._url = DEFAULT_MYDEVOLO
async def async_step_user(self, user_input=None): async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initiated by the user.""" """Handle a flow initiated by the user."""
if self.show_advanced_options: if self.show_advanced_options:
self.data_schema[vol.Required(CONF_MYDEVOLO, default=self._url)] = str self.data_schema[vol.Required(CONF_MYDEVOLO, default=self._url)] = str
@ -36,7 +44,9 @@ class DevoloHomeControlFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
except CredentialsInvalid: except CredentialsInvalid:
return self._show_form(step_id="user", errors={"base": "invalid_auth"}) return self._show_form(step_id="user", errors={"base": "invalid_auth"})
async def async_step_zeroconf(self, discovery_info: DiscoveryInfoType): async def async_step_zeroconf(
self, discovery_info: DiscoveryInfoType
) -> FlowResult:
"""Handle zeroconf discovery.""" """Handle zeroconf discovery."""
# Check if it is a gateway # Check if it is a gateway
if discovery_info.get("properties", {}).get("MT") in SUPPORTED_MODEL_TYPES: if discovery_info.get("properties", {}).get("MT") in SUPPORTED_MODEL_TYPES:
@ -44,7 +54,9 @@ class DevoloHomeControlFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_zeroconf_confirm() return await self.async_step_zeroconf_confirm()
return self.async_abort(reason="Not a devolo Home Control gateway.") return self.async_abort(reason="Not a devolo Home Control gateway.")
async def async_step_zeroconf_confirm(self, user_input=None): async def async_step_zeroconf_confirm(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initiated by zeroconf.""" """Handle a flow initiated by zeroconf."""
if user_input is None: if user_input is None:
return self._show_form(step_id="zeroconf_confirm") return self._show_form(step_id="zeroconf_confirm")
@ -55,7 +67,7 @@ class DevoloHomeControlFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
step_id="zeroconf_confirm", errors={"base": "invalid_auth"} step_id="zeroconf_confirm", errors={"base": "invalid_auth"}
) )
async def async_step_reauth(self, user_input): async def async_step_reauth(self, user_input: dict[str, Any]) -> FlowResult:
"""Handle reauthentication.""" """Handle reauthentication."""
self._reauth_entry = self.hass.config_entries.async_get_entry( self._reauth_entry = self.hass.config_entries.async_get_entry(
self.context["entry_id"] self.context["entry_id"]
@ -67,7 +79,9 @@ class DevoloHomeControlFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
} }
return await self.async_step_reauth_confirm() return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(self, user_input=None): async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initiated by reauthentication.""" """Handle a flow initiated by reauthentication."""
if user_input is None: if user_input is None:
return self._show_form(step_id="reauth_confirm") return self._show_form(step_id="reauth_confirm")
@ -82,7 +96,7 @@ class DevoloHomeControlFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
step_id="reauth_confirm", errors={"base": "reauth_failed"} step_id="reauth_confirm", errors={"base": "reauth_failed"}
) )
async def _connect_mydevolo(self, user_input): async def _connect_mydevolo(self, user_input: dict[str, Any]) -> FlowResult:
"""Connect to mydevolo.""" """Connect to mydevolo."""
user_input[CONF_MYDEVOLO] = user_input.get(CONF_MYDEVOLO, self._url) user_input[CONF_MYDEVOLO] = user_input.get(CONF_MYDEVOLO, self._url)
mydevolo = configure_mydevolo(conf=user_input) mydevolo = configure_mydevolo(conf=user_input)
@ -118,7 +132,9 @@ class DevoloHomeControlFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="reauth_successful") return self.async_abort(reason="reauth_successful")
@callback @callback
def _show_form(self, step_id, errors=None): def _show_form(
self, step_id: str, errors: dict[str, str] | None = None
) -> FlowResult:
"""Show the form to the user.""" """Show the form to the user."""
return self.async_show_form( return self.async_show_form(
step_id=step_id, step_id=step_id,

View File

@ -1,4 +1,8 @@
"""Platform for cover integration.""" """Platform for cover integration."""
from __future__ import annotations
from typing import Any
from homeassistant.components.cover import ( from homeassistant.components.cover import (
DEVICE_CLASS_BLIND, DEVICE_CLASS_BLIND,
SUPPORT_CLOSE, SUPPORT_CLOSE,
@ -8,13 +12,14 @@ from homeassistant.components.cover import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .devolo_multi_level_switch import DevoloMultiLevelSwitchDeviceEntity from .devolo_multi_level_switch import DevoloMultiLevelSwitchDeviceEntity
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
"""Get all cover devices and setup them via config entry.""" """Get all cover devices and setup them via config entry."""
entities = [] entities = []
@ -38,33 +43,33 @@ class DevoloCoverDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, CoverEntity):
"""Representation of a cover device within devolo Home Control.""" """Representation of a cover device within devolo Home Control."""
@property @property
def current_cover_position(self): 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 @property
def device_class(self): def device_class(self) -> str:
"""Return the class of the device.""" """Return the class of the device."""
return DEVICE_CLASS_BLIND return DEVICE_CLASS_BLIND
@property @property
def is_closed(self): 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 @property
def supported_features(self): def supported_features(self) -> int:
"""Flag supported features.""" """Flag supported features."""
return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION
def open_cover(self, **kwargs): 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)
def close_cover(self, **kwargs): def close_cover(self, **kwargs: Any) -> None:
"""Close the blind.""" """Close the blind."""
self._multi_level_switch_property.set(0) self._multi_level_switch_property.set(0)
def set_cover_position(self, **kwargs): def set_cover_position(self, **kwargs: Any) -> None:
"""Set the blind to the given position.""" """Set the blind to the given position."""
self._multi_level_switch_property.set(kwargs["position"]) self._multi_level_switch_property.set(kwargs["position"])

View File

@ -1,7 +1,12 @@
"""Base class for a device entity integrated in devolo Home Control.""" """Base class for a device entity integrated in devolo Home Control."""
from __future__ import annotations
import logging import logging
from homeassistant.helpers.entity import Entity 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 .const import DOMAIN from .const import DOMAIN
from .subscriber import Subscriber from .subscriber import Subscriber
@ -12,26 +17,30 @@ _LOGGER = logging.getLogger(__name__)
class DevoloDeviceEntity(Entity): class DevoloDeviceEntity(Entity):
"""Abstract representation of a device within devolo Home Control.""" """Abstract representation of a device within devolo Home Control."""
def __init__(self, homecontrol, device_instance, element_uid): def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
) -> 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._unique_id = element_uid
self._homecontrol = homecontrol self._homecontrol = homecontrol
self._name = device_instance.settings_property["general_device_settings"].name self._name: str = device_instance.settings_property[
"general_device_settings"
].name
self._area = device_instance.settings_property["general_device_settings"].zone self._area = device_instance.settings_property["general_device_settings"].zone
self._device_class = None self._device_class: str | None = None
self._value = None self._value: int
self._unit = None self._unit = ""
self._enabled_default = True self._enabled_default = True
# This is not doing I/O. It fetches an internal state of the API # This is not doing I/O. It fetches an internal state of the API
self._available = device_instance.is_online() self._available: bool = device_instance.is_online()
# Get the brand and model information # Get the brand and model information
self._brand = device_instance.brand self._brand = device_instance.brand
self._model = device_instance.name self._model = device_instance.name
self.subscriber = None self.subscriber: Subscriber | None = None
self.sync_callback = self._sync self.sync_callback = self._sync
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
@ -48,12 +57,12 @@ class DevoloDeviceEntity(Entity):
) )
@property @property
def unique_id(self): def unique_id(self) -> str:
"""Return the unique ID of the entity.""" """Return the unique ID of the entity."""
return self._unique_id return self._unique_id
@property @property
def device_info(self): def device_info(self) -> DeviceInfo:
"""Return the device info.""" """Return the device info."""
return { return {
"identifiers": {(DOMAIN, self._device_instance.uid)}, "identifiers": {(DOMAIN, self._device_instance.uid)},
@ -69,12 +78,12 @@ class DevoloDeviceEntity(Entity):
return self._enabled_default return self._enabled_default
@property @property
def should_poll(self): def should_poll(self) -> bool:
"""Return the polling state.""" """Return the polling state."""
return False return False
@property @property
def name(self): def name(self) -> str:
"""Return the display name of this entity.""" """Return the display name of this entity."""
return self._name return self._name
@ -83,7 +92,7 @@ class DevoloDeviceEntity(Entity):
"""Return the online state.""" """Return the online state."""
return self._available return self._available
def _sync(self, message): def _sync(self, message: tuple) -> None:
"""Update the state.""" """Update the state."""
if message[0] == self._unique_id: if message[0] == self._unique_id:
self._value = message[1] self._value = message[1]
@ -91,7 +100,7 @@ class DevoloDeviceEntity(Entity):
self._generic_message(message) self._generic_message(message)
self.schedule_update_ha_state() self.schedule_update_ha_state()
def _generic_message(self, message): def _generic_message(self, message: tuple) -> None:
"""Handle generic messages.""" """Handle generic messages."""
if len(message) == 3 and message[2] == "battery_level": if len(message) == 3 and message[2] == "battery_level":
self._value = message[1] self._value = message[1]

View File

@ -1,11 +1,16 @@
"""Base class for multi level switches in devolo Home Control.""" """Base class for multi level switches in devolo Home Control."""
from devolo_home_control_api.devices.zwave import Zwave
from devolo_home_control_api.homecontrol import HomeControl
from .devolo_device import DevoloDeviceEntity from .devolo_device import DevoloDeviceEntity
class DevoloMultiLevelSwitchDeviceEntity(DevoloDeviceEntity): class DevoloMultiLevelSwitchDeviceEntity(DevoloDeviceEntity):
"""Representation of a multi level switch device within devolo Home Control. Something like a dimmer or a thermostat.""" """Representation of a multi level switch device within devolo Home Control. Something like a dimmer or a thermostat."""
def __init__(self, homecontrol, device_instance, element_uid): def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
) -> None:
"""Initialize a multi level switch within devolo Home Control.""" """Initialize a multi level switch within devolo Home Control."""
super().__init__( super().__init__(
homecontrol=homecontrol, homecontrol=homecontrol,

View File

@ -1,4 +1,11 @@
"""Platform for light integration.""" """Platform for light integration."""
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.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
SUPPORT_BRIGHTNESS, SUPPORT_BRIGHTNESS,
@ -6,13 +13,14 @@ from homeassistant.components.light import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .devolo_multi_level_switch import DevoloMultiLevelSwitchDeviceEntity from .devolo_multi_level_switch import DevoloMultiLevelSwitchDeviceEntity
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
"""Get all light devices and setup them via config entry.""" """Get all light devices and setup them via config entry."""
entities = [] entities = []
@ -35,7 +43,9 @@ async def async_setup_entry(
class DevoloLightDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, LightEntity): class DevoloLightDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, LightEntity):
"""Representation of a light within devolo Home Control.""" """Representation of a light within devolo Home Control."""
def __init__(self, homecontrol, device_instance, element_uid): def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
) -> None:
"""Initialize a devolo multi level switch.""" """Initialize a devolo multi level switch."""
super().__init__( super().__init__(
homecontrol=homecontrol, homecontrol=homecontrol,
@ -48,21 +58,21 @@ class DevoloLightDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, LightEntity):
) )
@property @property
def brightness(self): def brightness(self) -> int:
"""Return the brightness value of the light.""" """Return the brightness value of the light."""
return round(self._value / 100 * 255) return round(self._value / 100 * 255)
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return the state of the light.""" """Return the state of the light."""
return bool(self._value) return bool(self._value)
@property @property
def supported_features(self): def supported_features(self) -> int:
"""Return the supported features.""" """Return the supported features."""
return SUPPORT_BRIGHTNESS return SUPPORT_BRIGHTNESS
def turn_on(self, **kwargs) -> 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:
self._multi_level_switch_property.set( self._multi_level_switch_property.set(
@ -76,7 +86,7 @@ class DevoloLightDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, LightEntity):
# If there is no binary switch attached to the device, turn it on to 100 %. # If there is no binary switch attached to the device, turn it on to 100 %.
self._multi_level_switch_property.set(100) self._multi_level_switch_property.set(100)
def turn_off(self, **kwargs) -> None: def turn_off(self, **kwargs: Any) -> None:
"""Turn device off.""" """Turn device off."""
if self._binary_switch_property is not None: if self._binary_switch_property is not None:
self._binary_switch_property.set(False) self._binary_switch_property.set(False)

View File

@ -1,4 +1,9 @@
"""Platform for sensor integration.""" """Platform for sensor integration."""
from __future__ import annotations
from devolo_home_control_api.devices.zwave import Zwave
from devolo_home_control_api.homecontrol import HomeControl
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY,
DEVICE_CLASS_ENERGY, DEVICE_CLASS_ENERGY,
@ -12,6 +17,7 @@ from homeassistant.components.sensor import (
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .devolo_device import DevoloDeviceEntity from .devolo_device import DevoloDeviceEntity
@ -28,10 +34,10 @@ DEVICE_CLASS_MAPPING = {
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
"""Get all sensor devices and setup them via config entry.""" """Get all sensor devices and setup them via config entry."""
entities = [] entities: list[SensorEntity] = []
for gateway in hass.data[DOMAIN][entry.entry_id]["gateways"]: for gateway in hass.data[DOMAIN][entry.entry_id]["gateways"]:
for device in gateway.multi_level_sensor_devices: for device in gateway.multi_level_sensor_devices:
@ -71,17 +77,17 @@ 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 @property
def device_class(self) -> str: def device_class(self) -> str | None:
"""Return device class.""" """Return device class."""
return self._device_class return self._device_class
@property @property
def state(self): def state(self) -> int:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self._value return self._value
@property @property
def unit_of_measurement(self): def unit_of_measurement(self) -> str:
"""Return the unit of measurement of this entity.""" """Return the unit of measurement of this entity."""
return self._unit return self._unit
@ -91,10 +97,10 @@ class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity):
def __init__( def __init__(
self, self,
homecontrol, homecontrol: HomeControl,
device_instance, device_instance: Zwave,
element_uid, element_uid: str,
): ) -> None:
"""Initialize a devolo multi level sensor.""" """Initialize a devolo multi level sensor."""
self._multi_level_sensor_property = device_instance.multi_level_sensor_property[ self._multi_level_sensor_property = device_instance.multi_level_sensor_property[
element_uid element_uid
@ -123,7 +129,9 @@ class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity):
class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity): class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity):
"""Representation of a battery entity within devolo Home Control.""" """Representation of a battery entity within devolo Home Control."""
def __init__(self, homecontrol, device_instance, element_uid): def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
) -> None:
"""Initialize a battery sensor.""" """Initialize a battery sensor."""
super().__init__( super().__init__(
@ -141,7 +149,13 @@ class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity):
class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity): class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity):
"""Representation of a consumption entity within devolo Home Control.""" """Representation of a consumption entity within devolo Home Control."""
def __init__(self, homecontrol, device_instance, element_uid, consumption): def __init__(
self,
homecontrol: HomeControl,
device_instance: Zwave,
element_uid: str,
consumption: str,
) -> None:
"""Initialize a devolo consumption sensor.""" """Initialize a devolo consumption sensor."""
super().__init__( super().__init__(
@ -163,11 +177,11 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity):
self._name += f" {consumption}" self._name += f" {consumption}"
@property @property
def unique_id(self): 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._unique_id}_{self._sensor_type}"
def _sync(self, message): def _sync(self, message: tuple) -> None:
"""Update the consumption sensor state.""" """Update the consumption sensor state."""
if message[0] == self._unique_id: if message[0] == self._unique_id:
self._value = getattr( self._value = getattr(

View File

@ -1,6 +1,7 @@
"""Subscriber for devolo home control API publisher.""" """Subscriber for devolo home control API publisher."""
import logging import logging
from typing import Callable
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -8,12 +9,12 @@ _LOGGER = logging.getLogger(__name__)
class Subscriber: class Subscriber:
"""Subscriber class for the publisher in mprm websocket class.""" """Subscriber class for the publisher in mprm websocket class."""
def __init__(self, name, callback): def __init__(self, name: str, callback: Callable) -> None:
"""Initiate the subscriber.""" """Initiate the subscriber."""
self.name = name self.name = name
self.callback = callback self.callback = callback
def update(self, message): def update(self, message: str) -> None:
"""Trigger hass to update the device.""" """Trigger hass to update the device."""
_LOGGER.debug('%s got message "%s"', self.name, message) _LOGGER.debug('%s got message "%s"', self.name, message)
self.callback(message) self.callback(message)

View File

@ -1,14 +1,22 @@
"""Platform for switch integration.""" """Platform for switch integration."""
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.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .devolo_device import DevoloDeviceEntity from .devolo_device import DevoloDeviceEntity
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
"""Get all devices and setup the switch devices via config entry.""" """Get all devices and setup the switch devices via config entry."""
entities = [] entities = []
@ -33,7 +41,9 @@ async def async_setup_entry(
class DevoloSwitch(DevoloDeviceEntity, SwitchEntity): class DevoloSwitch(DevoloDeviceEntity, SwitchEntity):
"""Representation of a switch.""" """Representation of a switch."""
def __init__(self, homecontrol, device_instance, element_uid): def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
) -> None:
"""Initialize an devolo Switch.""" """Initialize an devolo Switch."""
super().__init__( super().__init__(
homecontrol=homecontrol, homecontrol=homecontrol,
@ -43,7 +53,8 @@ class DevoloSwitch(DevoloDeviceEntity, SwitchEntity):
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._unique_id
) )
self._is_on = self._binary_switch_property.state self._is_on: bool = self._binary_switch_property.state
self._consumption: float | None
if hasattr(self._device_instance, "consumption_property"): if hasattr(self._device_instance, "consumption_property"):
self._consumption = self._device_instance.consumption_property.get( self._consumption = self._device_instance.consumption_property.get(
@ -53,26 +64,26 @@ class DevoloSwitch(DevoloDeviceEntity, SwitchEntity):
self._consumption = None self._consumption = None
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return the state.""" """Return the state."""
return self._is_on return self._is_on
@property @property
def current_power_w(self): def current_power_w(self) -> float | None:
"""Return the current consumption.""" """Return the current consumption."""
return self._consumption return self._consumption
def turn_on(self, **kwargs): def turn_on(self, **kwargs: Any) -> None:
"""Switch on the device.""" """Switch on the device."""
self._is_on = True self._is_on = True
self._binary_switch_property.set(state=True) self._binary_switch_property.set(state=True)
def turn_off(self, **kwargs): def turn_off(self, **kwargs: Any) -> None:
"""Switch off the device.""" """Switch off the device."""
self._is_on = False self._is_on = False
self._binary_switch_property.set(state=False) self._binary_switch_property.set(state=False)
def _sync(self, message): 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._is_on = self._device_instance.binary_switch_property[message[0]].state

View File

@ -286,6 +286,17 @@ no_implicit_optional = true
warn_return_any = true warn_return_any = true
warn_unreachable = true warn_unreachable = true
[mypy-homeassistant.components.devolo_home_control.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.dnsip.*] [mypy-homeassistant.components.dnsip.*]
check_untyped_defs = true check_untyped_defs = true
disallow_incomplete_defs = true disallow_incomplete_defs = true
@ -1063,9 +1074,6 @@ ignore_errors = true
[mypy-homeassistant.components.denonavr.*] [mypy-homeassistant.components.denonavr.*]
ignore_errors = true ignore_errors = true
[mypy-homeassistant.components.devolo_home_control.*]
ignore_errors = true
[mypy-homeassistant.components.dhcp.*] [mypy-homeassistant.components.dhcp.*]
ignore_errors = true ignore_errors = true

View File

@ -42,7 +42,6 @@ IGNORED_MODULES: Final[list[str]] = [
"homeassistant.components.deconz.*", "homeassistant.components.deconz.*",
"homeassistant.components.demo.*", "homeassistant.components.demo.*",
"homeassistant.components.denonavr.*", "homeassistant.components.denonavr.*",
"homeassistant.components.devolo_home_control.*",
"homeassistant.components.dhcp.*", "homeassistant.components.dhcp.*",
"homeassistant.components.directv.*", "homeassistant.components.directv.*",
"homeassistant.components.doorbird.*", "homeassistant.components.doorbird.*",