From 10dae253e5c10b0f2feecf899d6d0fae426b1043 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 6 Mar 2021 00:37:56 +0100 Subject: [PATCH] Complete typing on Verisure integration (#47482) --- homeassistant/components/verisure/__init__.py | 31 +++++++------ .../verisure/alarm_control_panel.py | 41 +++++++++++------ .../components/verisure/binary_sensor.py | 33 ++++++++----- homeassistant/components/verisure/camera.py | 26 ++++++++--- homeassistant/components/verisure/lock.py | 34 +++++++++----- homeassistant/components/verisure/sensor.py | 46 +++++++++++-------- homeassistant/components/verisure/switch.py | 38 +++++++++------ 7 files changed, 159 insertions(+), 90 deletions(-) diff --git a/homeassistant/components/verisure/__init__.py b/homeassistant/components/verisure/__init__.py index ab061faccad..ccb479814ab 100644 --- a/homeassistant/components/verisure/__init__.py +++ b/homeassistant/components/verisure/__init__.py @@ -1,5 +1,8 @@ """Support for Verisure devices.""" +from __future__ import annotations + from datetime import timedelta +from typing import Any, Literal from jsonpath import jsonpath import verisure @@ -12,8 +15,10 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, HTTP_SERVICE_UNAVAILABLE, ) +from homeassistant.core import HomeAssistant from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.typing import ConfigType from homeassistant.util import Throttle from .const import ( @@ -78,7 +83,7 @@ CONFIG_SCHEMA = vol.Schema( DEVICE_SERIAL_SCHEMA = vol.Schema({vol.Required(ATTR_DEVICE_SERIAL): cv.string}) -def setup(hass, config): +def setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Verisure integration.""" global HUB # pylint: disable=global-statement HUB = VerisureHub(config[DOMAIN]) @@ -137,7 +142,7 @@ def setup(hass, config): class VerisureHub: """A Verisure hub wrapper class.""" - def __init__(self, domain_config): + def __init__(self, domain_config: ConfigType): """Initialize the Verisure hub.""" self.overview = {} self.imageseries = {} @@ -150,7 +155,7 @@ class VerisureHub: self.giid = domain_config.get(CONF_GIID) - def login(self): + def login(self) -> bool: """Login to Verisure.""" try: self.session.login() @@ -161,7 +166,7 @@ class VerisureHub: return self.set_giid() return True - def logout(self): + def logout(self) -> bool: """Logout from Verisure.""" try: self.session.logout() @@ -170,7 +175,7 @@ class VerisureHub: return False return True - def set_giid(self): + def set_giid(self) -> bool: """Set installation GIID.""" try: self.session.set_giid(self.giid) @@ -179,7 +184,7 @@ class VerisureHub: return False return True - def update_overview(self): + def update_overview(self) -> None: """Update the overview.""" try: self.overview = self.session.get_overview() @@ -192,34 +197,34 @@ class VerisureHub: raise @Throttle(timedelta(seconds=60)) - def update_smartcam_imageseries(self): + def update_smartcam_imageseries(self) -> None: """Update the image series.""" self.imageseries = self.session.get_camera_imageseries() @Throttle(timedelta(seconds=30)) - def smartcam_capture(self, device_id): + def smartcam_capture(self, device_id: str) -> None: """Capture a new image from a smartcam.""" self.session.capture_image(device_id) - def disable_autolock(self, device_id): + def disable_autolock(self, device_id: str) -> None: """Disable autolock.""" self.session.set_lock_config(device_id, auto_lock_enabled=False) - def enable_autolock(self, device_id): + def enable_autolock(self, device_id: str) -> None: """Enable autolock.""" self.session.set_lock_config(device_id, auto_lock_enabled=True) - def get(self, jpath, *args): + def get(self, jpath: str, *args) -> list[Any] | Literal[False]: """Get values from the overview that matches the jsonpath.""" res = jsonpath(self.overview, jpath % args) return res or [] - def get_first(self, jpath, *args): + def get_first(self, jpath: str, *args) -> Any | None: """Get first value from the overview that matches the jsonpath.""" res = self.get(jpath, *args) return res[0] if res else None - def get_image_info(self, jpath, *args): + def get_image_info(self, jpath: str, *args) -> list[Any] | Literal[False]: """Get values from the imageseries that matches the jsonpath.""" res = jsonpath(self.imageseries, jpath % args) return res or [] diff --git a/homeassistant/components/verisure/alarm_control_panel.py b/homeassistant/components/verisure/alarm_control_panel.py index fff58433a9c..c791bfc38dc 100644 --- a/homeassistant/components/verisure/alarm_control_panel.py +++ b/homeassistant/components/verisure/alarm_control_panel.py @@ -1,7 +1,13 @@ """Support for Verisure alarm control panels.""" -from time import sleep +from __future__ import annotations -import homeassistant.components.alarm_control_panel as alarm +from time import sleep +from typing import Any, Callable + +from homeassistant.components.alarm_control_panel import ( + FORMAT_NUMBER, + AlarmControlPanelEntity, +) from homeassistant.components.alarm_control_panel.const import ( SUPPORT_ALARM_ARM_AWAY, SUPPORT_ALARM_ARM_HOME, @@ -11,12 +17,19 @@ from homeassistant.const import ( STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, ) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import Entity from . import HUB as hub from .const import CONF_ALARM, CONF_CODE_DIGITS, CONF_GIID, LOGGER -def setup_platform(hass, config, add_entities, discovery_info=None): +def setup_platform( + hass: HomeAssistant, + config: dict[str, Any], + add_entities: Callable[[list[Entity], bool], None], + discovery_info: dict[str, Any] | None = None, +) -> None: """Set up the Verisure platform.""" alarms = [] if int(hub.config.get(CONF_ALARM, 1)): @@ -25,7 +38,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): add_entities(alarms) -def set_arm_state(state, code=None): +def set_arm_state(state: str, code: str | None = None) -> None: """Send set arm state command.""" transaction_id = hub.session.set_arm_state(code, state)[ "armStateChangeTransactionId" @@ -38,7 +51,7 @@ def set_arm_state(state, code=None): hub.update_overview() -class VerisureAlarm(alarm.AlarmControlPanelEntity): +class VerisureAlarm(AlarmControlPanelEntity): """Representation of a Verisure alarm status.""" def __init__(self): @@ -48,7 +61,7 @@ class VerisureAlarm(alarm.AlarmControlPanelEntity): self._changed_by = None @property - def name(self): + def name(self) -> str: """Return the name of the device.""" giid = hub.config.get(CONF_GIID) if giid is not None: @@ -61,7 +74,7 @@ class VerisureAlarm(alarm.AlarmControlPanelEntity): return "{} alarm".format(hub.session.installations[0]["alias"]) @property - def state(self): + def state(self) -> str | None: """Return the state of the device.""" return self._state @@ -71,16 +84,16 @@ class VerisureAlarm(alarm.AlarmControlPanelEntity): return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY @property - def code_format(self): + def code_format(self) -> str: """Return one or more digits/characters.""" - return alarm.FORMAT_NUMBER + return FORMAT_NUMBER @property - def changed_by(self): + def changed_by(self) -> str | None: """Return the last change triggered by.""" return self._changed_by - def update(self): + def update(self) -> None: """Update alarm status.""" hub.update_overview() status = hub.get_first("$.armState.statusType") @@ -94,14 +107,14 @@ class VerisureAlarm(alarm.AlarmControlPanelEntity): LOGGER.error("Unknown alarm state %s", status) self._changed_by = hub.get_first("$.armState.name") - def alarm_disarm(self, code=None): + def alarm_disarm(self, code: str | None = None) -> None: """Send disarm command.""" set_arm_state("DISARMED", code) - def alarm_arm_home(self, code=None): + def alarm_arm_home(self, code: str | None = None) -> None: """Send arm home command.""" set_arm_state("ARMED_HOME", code) - def alarm_arm_away(self, code=None): + def alarm_arm_away(self, code: str | None = None) -> None: """Send arm away command.""" set_arm_state("ARMED_AWAY", code) diff --git a/homeassistant/components/verisure/binary_sensor.py b/homeassistant/components/verisure/binary_sensor.py index 5a7f4386ece..e30f008dba9 100644 --- a/homeassistant/components/verisure/binary_sensor.py +++ b/homeassistant/components/verisure/binary_sensor.py @@ -1,13 +1,24 @@ """Support for Verisure binary sensors.""" +from __future__ import annotations + +from typing import Any, Callable + from homeassistant.components.binary_sensor import ( DEVICE_CLASS_CONNECTIVITY, BinarySensorEntity, ) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import Entity from . import CONF_DOOR_WINDOW, HUB as hub -def setup_platform(hass, config, add_entities, discovery_info=None): +def setup_platform( + hass: HomeAssistant, + config: dict[str, Any], + add_entities: Callable[[list[Entity], bool], None], + discovery_info: dict[str, Any] | None = None, +) -> None: """Set up the Verisure binary sensors.""" sensors = [] hub.update_overview() @@ -29,12 +40,12 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class VerisureDoorWindowSensor(BinarySensorEntity): """Representation of a Verisure door window sensor.""" - def __init__(self, device_label): + def __init__(self, device_label: str): """Initialize the Verisure door window sensor.""" self._device_label = device_label @property - def name(self): + def name(self) -> str: """Return the name of the binary sensor.""" return hub.get_first( "$.doorWindow.doorWindowDevice[?(@.deviceLabel=='%s')].area", @@ -42,7 +53,7 @@ class VerisureDoorWindowSensor(BinarySensorEntity): ) @property - def is_on(self): + def is_on(self) -> bool: """Return the state of the sensor.""" return ( hub.get_first( @@ -53,7 +64,7 @@ class VerisureDoorWindowSensor(BinarySensorEntity): ) @property - def available(self): + def available(self) -> bool: """Return True if entity is available.""" return ( hub.get_first( @@ -64,7 +75,7 @@ class VerisureDoorWindowSensor(BinarySensorEntity): ) # pylint: disable=no-self-use - def update(self): + def update(self) -> None: """Update the state of the sensor.""" hub.update_overview() @@ -73,26 +84,26 @@ class VerisureEthernetStatus(BinarySensorEntity): """Representation of a Verisure VBOX internet status.""" @property - def name(self): + def name(self) -> str: """Return the name of the binary sensor.""" return "Verisure Ethernet status" @property - def is_on(self): + def is_on(self) -> bool: """Return the state of the sensor.""" return hub.get_first("$.ethernetConnectedNow") @property - def available(self): + def available(self) -> bool: """Return True if entity is available.""" return hub.get_first("$.ethernetConnectedNow") is not None # pylint: disable=no-self-use - def update(self): + def update(self) -> None: """Update the state of the sensor.""" hub.update_overview() @property - def device_class(self): + def device_class(self) -> str: """Return the class of this device, from component DEVICE_CLASSES.""" return DEVICE_CLASS_CONNECTIVITY diff --git a/homeassistant/components/verisure/camera.py b/homeassistant/components/verisure/camera.py index a69e1fb95d8..ad6840c0614 100644 --- a/homeassistant/components/verisure/camera.py +++ b/homeassistant/components/verisure/camera.py @@ -1,22 +1,34 @@ """Support for Verisure cameras.""" +from __future__ import annotations + import errno import os +from typing import Any, Callable, Literal from homeassistant.components.camera import Camera from homeassistant.const import EVENT_HOMEASSISTANT_STOP +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import Entity from . import HUB as hub from .const import CONF_SMARTCAM, LOGGER -def setup_platform(hass, config, add_entities, discovery_info=None): +def setup_platform( + hass: HomeAssistant, + config: dict[str, Any], + add_entities: Callable[[list[Entity], bool], None], + discovery_info: dict[str, Any] | None = None, +) -> None | Literal[False]: """Set up the Verisure Camera.""" if not int(hub.config.get(CONF_SMARTCAM, 1)): return False + directory_path = hass.config.config_dir if not os.access(directory_path, os.R_OK): LOGGER.error("file path %s is not readable", directory_path) return False + hub.update_overview() smartcams = [ VerisureSmartcam(hass, device_label, directory_path) @@ -29,7 +41,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class VerisureSmartcam(Camera): """Representation of a Verisure camera.""" - def __init__(self, hass, device_label, directory_path): + def __init__(self, hass: HomeAssistant, device_label: str, directory_path: str): """Initialize Verisure File Camera component.""" super().__init__() @@ -39,7 +51,7 @@ class VerisureSmartcam(Camera): self._image_id = None hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, self.delete_image) - def camera_image(self): + def camera_image(self) -> bytes | None: """Return image response.""" self.check_imagelist() if not self._image: @@ -49,7 +61,7 @@ class VerisureSmartcam(Camera): with open(self._image, "rb") as file: return file.read() - def check_imagelist(self): + def check_imagelist(self) -> None: """Check the contents of the image list.""" hub.update_smartcam_imageseries() image_ids = hub.get_image_info( @@ -67,12 +79,12 @@ class VerisureSmartcam(Camera): ) hub.session.download_image(self._device_label, new_image_id, new_image_path) LOGGER.debug("Old image_id=%s", self._image_id) - self.delete_image(self) + self.delete_image() self._image_id = new_image_id self._image = new_image_path - def delete_image(self, event): + def delete_image(self) -> None: """Delete an old image.""" remove_image = os.path.join( self._directory_path, "{}{}".format(self._image_id, ".jpg") @@ -85,7 +97,7 @@ class VerisureSmartcam(Camera): raise @property - def name(self): + def name(self) -> str: """Return the name of this camera.""" return hub.get_first( "$.customerImageCameras[?(@.deviceLabel=='%s')].area", self._device_label diff --git a/homeassistant/components/verisure/lock.py b/homeassistant/components/verisure/lock.py index 228c8c6c176..b2e1cfb3db0 100644 --- a/homeassistant/components/verisure/lock.py +++ b/homeassistant/components/verisure/lock.py @@ -1,14 +1,24 @@ """Support for Verisure locks.""" +from __future__ import annotations + from time import monotonic, sleep +from typing import Any, Callable from homeassistant.components.lock import LockEntity from homeassistant.const import ATTR_CODE, STATE_LOCKED, STATE_UNLOCKED +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import Entity from . import HUB as hub from .const import CONF_CODE_DIGITS, CONF_DEFAULT_LOCK_CODE, CONF_LOCKS, LOGGER -def setup_platform(hass, config, add_entities, discovery_info=None): +def setup_platform( + hass: HomeAssistant, + config: dict[str, Any], + add_entities: Callable[[list[Entity], bool], None], + discovery_info: dict[str, Any] | None = None, +) -> None: """Set up the Verisure lock platform.""" locks = [] if int(hub.config.get(CONF_LOCKS, 1)): @@ -26,7 +36,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class VerisureDoorlock(LockEntity): """Representation of a Verisure doorlock.""" - def __init__(self, device_label): + def __init__(self, device_label: str): """Initialize the Verisure lock.""" self._device_label = device_label self._state = None @@ -36,19 +46,19 @@ class VerisureDoorlock(LockEntity): self._default_lock_code = hub.config.get(CONF_DEFAULT_LOCK_CODE) @property - def name(self): + def name(self) -> str: """Return the name of the lock.""" return hub.get_first( "$.doorLockStatusList[?(@.deviceLabel=='%s')].area", self._device_label ) @property - def state(self): + def state(self) -> str | None: """Return the state of the lock.""" return self._state @property - def available(self): + def available(self) -> bool: """Return True if entity is available.""" return ( hub.get_first( @@ -58,16 +68,16 @@ class VerisureDoorlock(LockEntity): ) @property - def changed_by(self): + def changed_by(self) -> str | None: """Last change triggered by.""" return self._changed_by @property - def code_format(self): + def code_format(self) -> str: """Return the required six digit code.""" return "^\\d{%s}$" % self._digits - def update(self): + def update(self) -> None: """Update lock status.""" if monotonic() - self._change_timestamp < 10: return @@ -88,11 +98,11 @@ class VerisureDoorlock(LockEntity): ) @property - def is_locked(self): + def is_locked(self) -> bool: """Return true if lock is locked.""" return self._state == STATE_LOCKED - def unlock(self, **kwargs): + def unlock(self, **kwargs) -> None: """Send unlock command.""" if self._state is None: return @@ -104,7 +114,7 @@ class VerisureDoorlock(LockEntity): self.set_lock_state(code, STATE_UNLOCKED) - def lock(self, **kwargs): + def lock(self, **kwargs) -> None: """Send lock command.""" if self._state == STATE_LOCKED: return @@ -116,7 +126,7 @@ class VerisureDoorlock(LockEntity): self.set_lock_state(code, STATE_LOCKED) - def set_lock_state(self, code, state): + def set_lock_state(self, code: str, state: str) -> None: """Send set lock state command.""" lock_state = "lock" if state == STATE_LOCKED else "unlock" transaction_id = hub.session.set_lock_state( diff --git a/homeassistant/components/verisure/sensor.py b/homeassistant/components/verisure/sensor.py index ac7c8f40e8d..6582dfc409a 100644 --- a/homeassistant/components/verisure/sensor.py +++ b/homeassistant/components/verisure/sensor.py @@ -1,12 +1,22 @@ """Support for Verisure sensors.""" +from __future__ import annotations + +from typing import Any, Callable + from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import Entity from . import HUB as hub from .const import CONF_HYDROMETERS, CONF_MOUSE, CONF_THERMOMETERS -def setup_platform(hass, config, add_entities, discovery_info=None): +def setup_platform( + hass: HomeAssistant, + config: dict[str, Any], + add_entities: Callable[[list[Entity], bool], None], + discovery_info: dict[str, Any] | None = None, +) -> None: """Set up the Verisure platform.""" sensors = [] hub.update_overview() @@ -47,12 +57,12 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class VerisureThermometer(Entity): """Representation of a Verisure thermometer.""" - def __init__(self, device_label): + def __init__(self, device_label: str): """Initialize the sensor.""" self._device_label = device_label @property - def name(self): + def name(self) -> str: """Return the name of the device.""" return ( hub.get_first( @@ -62,14 +72,14 @@ class VerisureThermometer(Entity): ) @property - def state(self): + def state(self) -> str | None: """Return the state of the device.""" return hub.get_first( "$.climateValues[?(@.deviceLabel=='%s')].temperature", self._device_label ) @property - def available(self): + def available(self) -> bool: """Return True if entity is available.""" return ( hub.get_first( @@ -80,12 +90,12 @@ class VerisureThermometer(Entity): ) @property - def unit_of_measurement(self): + def unit_of_measurement(self) -> str: """Return the unit of measurement of this entity.""" return TEMP_CELSIUS # pylint: disable=no-self-use - def update(self): + def update(self) -> None: """Update the sensor.""" hub.update_overview() @@ -93,12 +103,12 @@ class VerisureThermometer(Entity): class VerisureHygrometer(Entity): """Representation of a Verisure hygrometer.""" - def __init__(self, device_label): + def __init__(self, device_label: str): """Initialize the sensor.""" self._device_label = device_label @property - def name(self): + def name(self) -> str: """Return the name of the device.""" return ( hub.get_first( @@ -108,14 +118,14 @@ class VerisureHygrometer(Entity): ) @property - def state(self): + def state(self) -> str | None: """Return the state of the device.""" return hub.get_first( "$.climateValues[?(@.deviceLabel=='%s')].humidity", self._device_label ) @property - def available(self): + def available(self) -> bool: """Return True if entity is available.""" return ( hub.get_first( @@ -125,12 +135,12 @@ class VerisureHygrometer(Entity): ) @property - def unit_of_measurement(self): + def unit_of_measurement(self) -> str: """Return the unit of measurement of this entity.""" return PERCENTAGE # pylint: disable=no-self-use - def update(self): + def update(self) -> None: """Update the sensor.""" hub.update_overview() @@ -143,7 +153,7 @@ class VerisureMouseDetection(Entity): self._device_label = device_label @property - def name(self): + def name(self) -> str: """Return the name of the device.""" return ( hub.get_first( @@ -153,14 +163,14 @@ class VerisureMouseDetection(Entity): ) @property - def state(self): + def state(self) -> str | None: """Return the state of the device.""" return hub.get_first( "$.eventCounts[?(@.deviceLabel=='%s')].detections", self._device_label ) @property - def available(self): + def available(self) -> bool: """Return True if entity is available.""" return ( hub.get_first("$.eventCounts[?(@.deviceLabel=='%s')]", self._device_label) @@ -168,11 +178,11 @@ class VerisureMouseDetection(Entity): ) @property - def unit_of_measurement(self): + def unit_of_measurement(self) -> str: """Return the unit of measurement of this entity.""" return "Mice" # pylint: disable=no-self-use - def update(self): + def update(self) -> None: """Update the sensor.""" hub.update_overview() diff --git a/homeassistant/components/verisure/switch.py b/homeassistant/components/verisure/switch.py index 4615e0a2a49..e5e19bd6d13 100644 --- a/homeassistant/components/verisure/switch.py +++ b/homeassistant/components/verisure/switch.py @@ -1,45 +1,53 @@ """Support for Verisure Smartplugs.""" +from __future__ import annotations + from time import monotonic +from typing import Any, Callable, Literal from homeassistant.components.switch import SwitchEntity +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import Entity from . import CONF_SMARTPLUGS, HUB as hub -def setup_platform(hass, config, add_entities, discovery_info=None): +def setup_platform( + hass: HomeAssistant, + config: dict[str, Any], + add_entities: Callable[[list[Entity], bool], None], + discovery_info: dict[str, Any] | None = None, +) -> None | Literal[False]: """Set up the Verisure switch platform.""" if not int(hub.config.get(CONF_SMARTPLUGS, 1)): return False hub.update_overview() - switches = [] - switches.extend( - [ - VerisureSmartplug(device_label) - for device_label in hub.get("$.smartPlugs[*].deviceLabel") - ] - ) + switches = [ + VerisureSmartplug(device_label) + for device_label in hub.get("$.smartPlugs[*].deviceLabel") + ] + add_entities(switches) class VerisureSmartplug(SwitchEntity): """Representation of a Verisure smartplug.""" - def __init__(self, device_id): + def __init__(self, device_id: str): """Initialize the Verisure device.""" self._device_label = device_id self._change_timestamp = 0 self._state = False @property - def name(self): + def name(self) -> str: """Return the name or location of the smartplug.""" return hub.get_first( "$.smartPlugs[?(@.deviceLabel == '%s')].area", self._device_label ) @property - def is_on(self): + def is_on(self) -> bool: """Return true if on.""" if monotonic() - self._change_timestamp < 10: return self._state @@ -53,26 +61,26 @@ class VerisureSmartplug(SwitchEntity): return self._state @property - def available(self): + def available(self) -> bool: """Return True if entity is available.""" return ( hub.get_first("$.smartPlugs[?(@.deviceLabel == '%s')]", self._device_label) is not None ) - def turn_on(self, **kwargs): + def turn_on(self, **kwargs) -> None: """Set smartplug status on.""" hub.session.set_smartplug_state(self._device_label, True) self._state = True self._change_timestamp = monotonic() - def turn_off(self, **kwargs): + def turn_off(self, **kwargs) -> None: """Set smartplug status off.""" hub.session.set_smartplug_state(self._device_label, False) self._state = False self._change_timestamp = monotonic() # pylint: disable=no-self-use - def update(self): + def update(self) -> None: """Get the latest date of the smartplug.""" hub.update_overview()