Velbus typing part 2 (#59148)

This commit is contained in:
Maikel Punie 2021-11-11 13:46:35 +01:00 committed by GitHub
parent a079b4fd58
commit 6a21b241c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 120 additions and 57 deletions

View File

@ -137,6 +137,7 @@ homeassistant.components.uptime.*
homeassistant.components.uptimerobot.*
homeassistant.components.vacuum.*
homeassistant.components.vallox.*
homeassistant.components.velbus.*
homeassistant.components.vlc_telnet.*
homeassistant.components.water_heater.*
homeassistant.components.watttime.*

View File

@ -3,16 +3,18 @@ from __future__ import annotations
import logging
from velbusaio.channels import Channel as VelbusChannel
from velbusaio.controller import Velbus
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import device_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.typing import ConfigType
from .const import (
CONF_INTERFACE,
@ -32,7 +34,7 @@ CONFIG_SCHEMA = vol.Schema(
PLATFORMS = ["switch", "sensor", "binary_sensor", "cover", "climate", "light"]
async def async_setup(hass, config):
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Velbus platform."""
# Import from the configuration file if needed
if DOMAIN not in config:
@ -97,7 +99,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if hass.services.has_service(DOMAIN, SERVICE_SCAN):
return True
def check_entry_id(interface: str):
def check_entry_id(interface: str) -> str:
for entry in hass.config_entries.async_entries(DOMAIN):
if "port" in entry.data and entry.data["port"] == interface:
return entry.entry_id
@ -105,7 +107,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"The interface provided is not defined as a port in a Velbus integration"
)
async def scan(call):
async def scan(call: ServiceCall) -> None:
await hass.data[DOMAIN][call.data[CONF_INTERFACE]]["cntrl"].scan()
hass.services.async_register(
@ -115,7 +117,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
vol.Schema({vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id)}),
)
async def syn_clock(call):
async def syn_clock(call: ServiceCall) -> None:
await hass.data[DOMAIN][call.data[CONF_INTERFACE]]["cntrl"].sync_clock()
hass.services.async_register(
@ -125,7 +127,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
vol.Schema({vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id)}),
)
async def set_memo_text(call):
async def set_memo_text(call: ServiceCall) -> None:
"""Handle Memo Text service call."""
memo_text = call.data[CONF_MEMO_TEXT]
memo_text.hass = hass
@ -167,32 +169,32 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
class VelbusEntity(Entity):
"""Representation of a Velbus entity."""
def __init__(self, channel):
def __init__(self, channel: VelbusChannel) -> None:
"""Initialize a Velbus entity."""
self._channel = channel
@property
def unique_id(self):
def unique_id(self) -> str:
"""Get unique ID."""
if (serial := self._channel.get_module_serial()) == 0:
serial = self._channel.get_module_address()
if (serial := self._channel.get_module_serial()) == "":
serial = str(self._channel.get_module_address())
return f"{serial}-{self._channel.get_channel_number()}"
@property
def name(self):
def name(self) -> str:
"""Return the display name of this entity."""
return self._channel.get_name()
@property
def should_poll(self):
def should_poll(self) -> bool:
"""Disable polling."""
return False
async def async_added_to_hass(self):
async def async_added_to_hass(self) -> None:
"""Add listener for state changes."""
self._channel.on_status_update(self._on_update)
async def _on_update(self):
async def _on_update(self) -> None:
self.async_write_ha_state()
@property
@ -200,7 +202,7 @@ class VelbusEntity(Entity):
"""Return the device info."""
return DeviceInfo(
identifiers={
(DOMAIN, self._channel.get_module_address()),
(DOMAIN, str(self._channel.get_module_address())),
},
manufacturer="Velleman",
model=self._channel.get_module_type_name(),

View File

@ -1,4 +1,6 @@
"""Support for Velbus Binary Sensors."""
from velbusaio.channels import Button as VelbusButton
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -25,6 +27,8 @@ async def async_setup_entry(
class VelbusBinarySensor(VelbusEntity, BinarySensorEntity):
"""Representation of a Velbus Binary Sensor."""
_channel: VelbusButton
@property
def is_on(self) -> bool:
"""Return true if the sensor is on."""

View File

@ -1,6 +1,10 @@
"""Support for Velbus thermostat."""
from __future__ import annotations
from typing import Any
from velbusaio.channels import Temperature as VelbusTemp
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
HVAC_MODE_HEAT,
@ -33,6 +37,7 @@ async def async_setup_entry(
class VelbusClimate(VelbusEntity, ClimateEntity):
"""Representation of a Velbus thermostat."""
_channel: VelbusTemp
_attr_supported_features = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
_attr_temperature_unit = TEMP_CELSIUS
_attr_hvac_mode = HVAC_MODE_HEAT
@ -40,7 +45,7 @@ class VelbusClimate(VelbusEntity, ClimateEntity):
_attr_preset_modes = list(PRESET_MODES)
@property
def target_temperature(self) -> int | None:
def target_temperature(self) -> float | None:
"""Return the temperature we try to reach."""
return self._channel.get_climate_target()
@ -56,7 +61,7 @@ class VelbusClimate(VelbusEntity, ClimateEntity):
None,
)
async def async_set_temperature(self, **kwargs) -> None:
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperatures."""
if (temp := kwargs.get(ATTR_TEMPERATURE)) is None:
return

View File

@ -1,6 +1,8 @@
"""Config flow for the Velbus platform."""
from __future__ import annotations
from typing import Any
import velbusaio
from velbusaio.exceptions import VelbusConnectionFailed
import voluptuous as vol
@ -8,16 +10,17 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_NAME, CONF_PORT
from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.util import slugify
from .const import DOMAIN
@callback
def velbus_entries(hass: HomeAssistant):
def velbus_entries(hass: HomeAssistant) -> set[str]:
"""Return connections for Velbus domain."""
return {
(entry.data[CONF_PORT]) for entry in hass.config_entries.async_entries(DOMAIN)
entry.data[CONF_PORT] for entry in hass.config_entries.async_entries(DOMAIN)
}
@ -30,11 +33,11 @@ class VelbusConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Initialize the velbus config flow."""
self._errors: dict[str, str] = {}
def _create_device(self, name: str, prt: str):
def _create_device(self, name: str, prt: str) -> FlowResult:
"""Create an entry async."""
return self.async_create_entry(title=name, data={CONF_PORT: prt})
async def _test_connection(self, prt):
async def _test_connection(self, prt: str) -> bool:
"""Try to connect to the velbus with the port specified."""
try:
controller = velbusaio.controller.Velbus(prt)
@ -51,7 +54,9 @@ class VelbusConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return True
return False
async def async_step_user(self, user_input=None):
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Step when user initializes a integration."""
self._errors = {}
if user_input is not None:
@ -78,7 +83,7 @@ class VelbusConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors=self._errors,
)
async def async_step_import(self, user_input=None):
async def async_step_import(self, user_input: dict[str, Any]) -> FlowResult:
"""Import a config entry."""
user_input[CONF_NAME] = "Velbus Import"
prt = user_input[CONF_PORT]

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any
from velbusaio.channels import Channel as VelbusChannel
from velbusaio.channels import Blind as VelbusBlind
from homeassistant.components.cover import (
ATTR_POSITION,
@ -38,7 +38,9 @@ async def async_setup_entry(
class VelbusCover(VelbusEntity, CoverEntity):
"""Representation a Velbus cover."""
def __init__(self, channel: VelbusChannel) -> None:
_channel: VelbusBlind
def __init__(self, channel: VelbusBlind) -> None:
"""Initialize the dimmer."""
super().__init__(channel)
if self._channel.support_position():
@ -60,8 +62,7 @@ class VelbusCover(VelbusEntity, CoverEntity):
None is unknown, 0 is closed, 100 is fully open
Velbus: 100 = closed, 0 = open
"""
pos = self._channel.get_position()
return 100 - pos
return 100 - self._channel.get_position()
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""

View File

@ -1,4 +1,14 @@
"""Support for Velbus light."""
from __future__ import annotations
from typing import Any
from velbusaio.channels import (
Button as VelbusButton,
Channel as VelbusChannel,
Dimmer as VelbusDimmer,
)
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_FLASH,
@ -10,16 +20,24 @@ from homeassistant.components.light import (
SUPPORT_TRANSITION,
LightEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import VelbusEntity
from .const import DOMAIN
async def async_setup_entry(hass, entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Velbus switch based on config_entry."""
await hass.data[DOMAIN][entry.entry_id]["tsk"]
cntrl = hass.data[DOMAIN][entry.entry_id]["cntrl"]
entities = []
entities: list[Entity] = []
for channel in cntrl.get_all("light"):
entities.append(VelbusLight(channel))
for channel in cntrl.get_all("led"):
@ -30,24 +48,25 @@ async def async_setup_entry(hass, entry, async_add_entities):
class VelbusLight(VelbusEntity, LightEntity):
"""Representation of a Velbus light."""
_channel: VelbusDimmer
_attr_supported_feature = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION
def __init__(self, channel):
def __init__(self, channel: VelbusDimmer) -> None:
"""Initialize the dimmer."""
super().__init__(channel)
self._attr_name = self._channel.get_name()
@property
def is_on(self):
def is_on(self) -> bool:
"""Return true if the light is on."""
return self._channel.is_on()
@property
def brightness(self):
def brightness(self) -> int:
"""Return the brightness of the light."""
return int((self._channel.get_dimmer_state() * 255) / 100)
async def async_turn_on(self, **kwargs):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Instruct the Velbus light to turn on."""
if ATTR_BRIGHTNESS in kwargs:
# Make sure a low but non-zero value is not rounded down to zero
@ -67,7 +86,7 @@ class VelbusLight(VelbusEntity, LightEntity):
)
await getattr(self._channel, attr)(*args)
async def async_turn_off(self, **kwargs):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Instruct the velbus light to turn off."""
attr, *args = (
"set_dimmer_state",
@ -80,25 +99,21 @@ class VelbusLight(VelbusEntity, LightEntity):
class VelbusButtonLight(VelbusEntity, LightEntity):
"""Representation of a Velbus light."""
_channel: VelbusButton
_attr_entity_registry_enabled_default = False
_attr_supported_feature = SUPPORT_FLASH
def __init__(self, channel):
def __init__(self, channel: VelbusChannel) -> None:
"""Initialize the button light (led)."""
super().__init__(channel)
self._attr_name = f"LED {self._channel.get_name()}"
@property
def is_on(self):
def is_on(self) -> Any:
"""Return true if the light is on."""
return self._channel.is_on()
@property
def brightness(self):
"""Return the brightness of the light."""
return int((self._channel.get_dimmer_state() * 255) / 100)
async def async_turn_on(self, **kwargs):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Instruct the Velbus light to turn on."""
if ATTR_FLASH in kwargs:
if kwargs[ATTR_FLASH] == FLASH_LONG:
@ -111,7 +126,7 @@ class VelbusButtonLight(VelbusEntity, LightEntity):
attr, *args = "set_led_state", "on"
await getattr(self._channel, attr)(*args)
async def async_turn_off(self, **kwargs):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Instruct the velbus light to turn off."""
attr, *args = "set_led_state", "off"
await getattr(self._channel, attr)(*args)

View File

@ -1,22 +1,31 @@
"""Support for Velbus sensors."""
from __future__ import annotations
from velbusaio.channels import ButtonCounter, LightSensor, SensorNumber, Temperature
from homeassistant.components.sensor import (
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
SensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_TEMPERATURE,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import VelbusEntity
from .const import DOMAIN
async def async_setup_entry(hass, entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Velbus switch based on config_entry."""
await hass.data[DOMAIN][entry.entry_id]["tsk"]
cntrl = hass.data[DOMAIN][entry.entry_id]["cntrl"]
@ -31,13 +40,19 @@ async def async_setup_entry(hass, entry, async_add_entities):
class VelbusSensor(VelbusEntity, SensorEntity):
"""Representation of a sensor."""
def __init__(self, channel, counter=False):
_channel: ButtonCounter | Temperature | LightSensor | SensorNumber
def __init__(
self,
channel: ButtonCounter | Temperature | LightSensor | SensorNumber,
counter: bool = False,
) -> None:
"""Initialize a sensor Velbus entity."""
super().__init__(channel)
self._is_counter = counter
self._is_counter: bool = counter
@property
def unique_id(self):
def unique_id(self) -> str:
"""Return unique ID for counter sensors."""
unique_id = super().unique_id
if self._is_counter:
@ -45,7 +60,7 @@ class VelbusSensor(VelbusEntity, SensorEntity):
return unique_id
@property
def name(self):
def name(self) -> str:
"""Return the name for the sensor."""
name = super().name
if self._is_counter:
@ -53,7 +68,7 @@ class VelbusSensor(VelbusEntity, SensorEntity):
return name
@property
def device_class(self):
def device_class(self) -> str | None:
"""Return the device class of the sensor."""
if self._is_counter:
return DEVICE_CLASS_ENERGY
@ -64,28 +79,28 @@ class VelbusSensor(VelbusEntity, SensorEntity):
return None
@property
def native_value(self):
def native_value(self) -> float | int | None:
"""Return the state of the sensor."""
if self._is_counter:
return self._channel.get_counter_state()
return self._channel.get_state()
return float(self._channel.get_counter_state())
return float(self._channel.get_state())
@property
def native_unit_of_measurement(self):
def native_unit_of_measurement(self) -> str:
"""Return the unit this state is expressed in."""
if self._is_counter:
return self._channel.get_counter_unit()
return self._channel.get_unit()
return str(self._channel.get_counter_unit())
return str(self._channel.get_unit())
@property
def icon(self):
def icon(self) -> str | None:
"""Icon to use in the frontend."""
if self._is_counter:
return "mdi:counter"
return None
@property
def state_class(self):
def state_class(self) -> str:
"""Return the state class of this device."""
if self._is_counter:
return STATE_CLASS_TOTAL_INCREASING

View File

@ -1,6 +1,8 @@
"""Support for Velbus switches."""
from typing import Any
from velbusaio.channels import Relay as VelbusRelay
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -27,6 +29,8 @@ async def async_setup_entry(
class VelbusSwitch(VelbusEntity, SwitchEntity):
"""Representation of a switch."""
_channel: VelbusRelay
@property
def is_on(self) -> bool:
"""Return true if the switch is on."""

View File

@ -1518,6 +1518,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.velbus.*]
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.vlc_telnet.*]
check_untyped_defs = true
disallow_incomplete_defs = true