diff --git a/.strict-typing b/.strict-typing index f574aeb79d65b2..b51f3dd3de0602 100644 --- a/.strict-typing +++ b/.strict-typing @@ -135,6 +135,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.* diff --git a/homeassistant/components/velbus/__init__.py b/homeassistant/components/velbus/__init__.py index acc90116269f5e..bc9c24963f5a8f 100644 --- a/homeassistant/components/velbus/__init__.py +++ b/homeassistant/components/velbus/__init__.py @@ -3,14 +3,16 @@ 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 import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity +from homeassistant.helpers.typing import ConfigType from .const import ( CONF_INTERFACE, @@ -30,7 +32,7 @@ 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: @@ -77,7 +79,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 @@ -85,7 +87,7 @@ def check_entry_id(interface: str): "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( @@ -95,7 +97,7 @@ async def scan(call): 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( @@ -105,7 +107,7 @@ async def syn_clock(call): 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 @@ -147,36 +149,36 @@ 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() 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() + return str(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 - def device_info(self): + def device_info(self): # type: ignore """Return the device info.""" return { "identifiers": { diff --git a/homeassistant/components/velbus/climate.py b/homeassistant/components/velbus/climate.py index a065679c4e5f14..a68413ed4ce0cb 100644 --- a/homeassistant/components/velbus/climate.py +++ b/homeassistant/components/velbus/climate.py @@ -1,6 +1,8 @@ """Support for Velbus thermostat.""" from __future__ import annotations +from typing import Any + from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate.const import ( HVAC_MODE_HEAT, @@ -40,9 +42,9 @@ 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() + return float(self._channel.get_climate_target()) @property def preset_mode(self) -> str | None: @@ -56,7 +58,7 @@ def preset_mode(self) -> str | None: 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 diff --git a/homeassistant/components/velbus/config_flow.py b/homeassistant/components/velbus/config_flow.py index 3ec5af14397439..3facd8c6a33786 100644 --- a/homeassistant/components/velbus/config_flow.py +++ b/homeassistant/components/velbus/config_flow.py @@ -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 @@ 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 @@ def __init__(self) -> None: """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 @@ def _prt_in_configuration_exists(self, prt: str) -> bool: 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 @@ async def async_step_user(self, user_input=None): 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] diff --git a/homeassistant/components/velbus/cover.py b/homeassistant/components/velbus/cover.py index 43cec0adb248b8..0ff3e45760375d 100644 --- a/homeassistant/components/velbus/cover.py +++ b/homeassistant/components/velbus/cover.py @@ -51,7 +51,9 @@ def __init__(self, channel: VelbusChannel) -> None: @property def is_closed(self) -> bool | None: """Return if the cover is closed.""" - return self._channel.is_closed() + if val := self._channel.is_closed(): + return bool(val) + return None @property def current_cover_position(self) -> int | None: @@ -60,8 +62,9 @@ def current_cover_position(self) -> int | None: None is unknown, 0 is closed, 100 is fully open Velbus: 100 = closed, 0 = open """ - pos = self._channel.get_position() - return 100 - pos + if pos := self._channel.get_position(): + return int(100 - pos) + return None async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover.""" diff --git a/homeassistant/components/velbus/light.py b/homeassistant/components/velbus/light.py index 2c6cd8d87764bc..6bf0ca4fafe116 100644 --- a/homeassistant/components/velbus/light.py +++ b/homeassistant/components/velbus/light.py @@ -1,4 +1,10 @@ """Support for Velbus light.""" +from __future__ import annotations + +from typing import Any + +from velbusaio.channels import Channel as VelbusChannel + from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_FLASH, @@ -10,16 +16,24 @@ 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"): @@ -32,22 +46,22 @@ class VelbusLight(VelbusEntity, LightEntity): _attr_supported_feature = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION - def __init__(self, channel): + def __init__(self, channel: VelbusChannel) -> 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() + return bool(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 +81,7 @@ async def async_turn_on(self, **kwargs): ) 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", @@ -83,22 +97,22 @@ class VelbusButtonLight(VelbusEntity, LightEntity): _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): + 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_FLASH in kwargs: if kwargs[ATTR_FLASH] == FLASH_LONG: @@ -111,7 +125,7 @@ async def async_turn_on(self, **kwargs): 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) diff --git a/homeassistant/components/velbus/manifest.json b/homeassistant/components/velbus/manifest.json index 454b9d24d0777e..5fb3c58c3c7cda 100644 --- a/homeassistant/components/velbus/manifest.json +++ b/homeassistant/components/velbus/manifest.json @@ -2,7 +2,7 @@ "domain": "velbus", "name": "Velbus", "documentation": "https://www.home-assistant.io/integrations/velbus", - "requirements": ["velbus-aio==2021.11.0"], + "requirements": ["velbus-aio==2021.11.6"], "config_flow": true, "codeowners": ["@Cereal2nd", "@brefra"], "iot_class": "local_push" diff --git a/homeassistant/components/velbus/sensor.py b/homeassistant/components/velbus/sensor.py index 32f016b8ce333d..9f2248da5c33e8 100644 --- a/homeassistant/components/velbus/sensor.py +++ b/homeassistant/components/velbus/sensor.py @@ -1,22 +1,31 @@ """Support for Velbus sensors.""" from __future__ import annotations +from velbusaio.channels import Channel as VelbusChannel + 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,13 @@ async def async_setup_entry(hass, entry, async_add_entities): class VelbusSensor(VelbusEntity, SensorEntity): """Representation of a sensor.""" - def __init__(self, channel, counter=False): + def __init__(self, channel: VelbusChannel, 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 +54,7 @@ def unique_id(self): 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 +62,7 @@ def name(self): 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 +73,28 @@ def device_class(self): 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 diff --git a/homeassistant/components/velbus/switch.py b/homeassistant/components/velbus/switch.py index c7b4a80f1960c0..d7cda26eedc703 100644 --- a/homeassistant/components/velbus/switch.py +++ b/homeassistant/components/velbus/switch.py @@ -30,7 +30,7 @@ class VelbusSwitch(VelbusEntity, SwitchEntity): @property def is_on(self) -> bool: """Return true if the switch is on.""" - return self._channel.is_on() + return bool(self._channel.is_on()) async def async_turn_on(self, **kwargs: Any) -> None: """Instruct the switch to turn on.""" diff --git a/mypy.ini b/mypy.ini index 425cfd1aa579a5..0ec664b242bca2 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1496,6 +1496,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 diff --git a/requirements_all.txt b/requirements_all.txt index 46c766ee2bc051..739759c9c28946 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2366,7 +2366,7 @@ uvcclient==0.11.0 vallox-websocket-api==2.8.1 # homeassistant.components.velbus -velbus-aio==2021.11.0 +velbus-aio==2021.11.6 # homeassistant.components.venstar venstarcolortouch==0.15 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e7fd0019df7f1d..c53ec0672cdd4d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1373,7 +1373,7 @@ url-normalize==1.4.1 uvcclient==0.11.0 # homeassistant.components.velbus -velbus-aio==2021.11.0 +velbus-aio==2021.11.6 # homeassistant.components.venstar venstarcolortouch==0.15