Improve risco typing (#108041)

This commit is contained in:
Marc Mueller 2024-01-15 23:32:58 +01:00 committed by GitHub
parent 369ed5b701
commit e8b962ea89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 38 deletions

View File

@ -1,4 +1,6 @@
"""The Risco integration."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass, field
from datetime import timedelta

View File

@ -6,6 +6,7 @@ import logging
from typing import Any
from pyrisco.common import Partition
from pyrisco.local.partition import Partition as LocalPartition
from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntity,
@ -132,7 +133,7 @@ class RiscoAlarm(AlarmControlPanelEntity):
return None
def _validate_code(self, code):
def _validate_code(self, code: str | None) -> bool:
"""Validate given code."""
return code == self._code
@ -159,7 +160,7 @@ class RiscoAlarm(AlarmControlPanelEntity):
"""Send arm custom bypass command."""
await self._arm(STATE_ALARM_ARMED_CUSTOM_BYPASS, code)
async def _arm(self, mode, code):
async def _arm(self, mode: str, code: str | None) -> None:
if self.code_arm_required and not self._validate_code(code):
_LOGGER.warning("Wrong code entered for %s", mode)
return
@ -205,7 +206,7 @@ class RiscoCloudAlarm(RiscoAlarm, RiscoCloudEntity):
def _get_data_from_coordinator(self) -> None:
self._partition = self.coordinator.data.partitions[self._partition_id]
async def _call_alarm_method(self, method, *args):
async def _call_alarm_method(self, method: str, *args: Any) -> None:
alarm = await getattr(self._risco, method)(self._partition_id, *args)
self._partition = alarm.partitions[self._partition_id]
self.async_write_ha_state()
@ -220,7 +221,7 @@ class RiscoLocalAlarm(RiscoAlarm):
self,
system_id: str,
partition_id: int,
partition: Partition,
partition: LocalPartition,
partition_updates: dict[int, Callable[[], Any]],
code: str,
options: dict[str, Any],

View File

@ -4,7 +4,8 @@ from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from pyrisco.common import Zone
from pyrisco.cloud.zone import Zone as CloudZone
from pyrisco.local.zone import Zone as LocalZone
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
@ -53,7 +54,7 @@ class RiscoCloudBinarySensor(RiscoCloudZoneEntity, BinarySensorEntity):
_attr_name = None
def __init__(
self, coordinator: RiscoDataUpdateCoordinator, zone_id: int, zone: Zone
self, coordinator: RiscoDataUpdateCoordinator, zone_id: int, zone: CloudZone
) -> None:
"""Init the zone."""
super().__init__(coordinator=coordinator, suffix="", zone_id=zone_id, zone=zone)
@ -70,7 +71,7 @@ class RiscoLocalBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity):
_attr_device_class = BinarySensorDeviceClass.MOTION
_attr_name = None
def __init__(self, system_id: str, zone_id: int, zone: Zone) -> None:
def __init__(self, system_id: str, zone_id: int, zone: LocalZone) -> None:
"""Init the zone."""
super().__init__(system_id=system_id, suffix="", zone_id=zone_id, zone=zone)
@ -93,7 +94,7 @@ class RiscoLocalAlarmedBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity):
_attr_translation_key = "alarmed"
def __init__(self, system_id: str, zone_id: int, zone: Zone) -> None:
def __init__(self, system_id: str, zone_id: int, zone: LocalZone) -> None:
"""Init the zone."""
super().__init__(
system_id=system_id,
@ -113,7 +114,7 @@ class RiscoLocalArmedBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity):
_attr_translation_key = "armed"
def __init__(self, system_id: str, zone_id: int, zone: Zone) -> None:
def __init__(self, system_id: str, zone_id: int, zone: LocalZone) -> None:
"""Init the zone."""
super().__init__(
system_id=system_id,

View File

@ -63,7 +63,9 @@ HA_STATES = [
]
async def validate_cloud_input(hass: core.HomeAssistant, data) -> dict[str, str]:
async def validate_cloud_input(
hass: core.HomeAssistant, data: dict[str, Any]
) -> dict[str, str]:
"""Validate the user input allows us to connect to Risco Cloud.
Data has the keys from CLOUD_SCHEMA with values provided by the user.
@ -124,16 +126,20 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Define the config flow to handle options."""
return RiscoOptionsFlowHandler(config_entry)
async def async_step_user(self, user_input=None):
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the initial step."""
return self.async_show_menu(
step_id="user",
menu_options=["cloud", "local"],
)
async def async_step_cloud(self, user_input=None):
async def async_step_cloud(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Configure a cloud based alarm."""
errors = {}
errors: dict[str, str] = {}
if user_input is not None:
if not self._reauth_entry:
await self.async_set_unique_id(user_input[CONF_USERNAME])
@ -168,14 +174,16 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self._reauth_entry = await self.async_set_unique_id(entry_data[CONF_USERNAME])
return await self.async_step_cloud()
async def async_step_local(self, user_input=None):
async def async_step_local(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Configure a local based alarm."""
errors = {}
errors: dict[str, str] = {}
if user_input is not None:
try:
info = await validate_local_input(self.hass, user_input)
except CannotConnectError:
_LOGGER.debug("Cannot connect", exc_info=1)
except CannotConnectError as ex:
_LOGGER.debug("Cannot connect", exc_info=ex)
errors["base"] = "cannot_connect"
except UnauthorizedError:
errors["base"] = "invalid_auth"
@ -208,7 +216,7 @@ class RiscoOptionsFlowHandler(config_entries.OptionsFlow):
self.config_entry = config_entry
self._data = {**DEFAULT_OPTIONS, **config_entry.options}
def _options_schema(self):
def _options_schema(self) -> vol.Schema:
return vol.Schema(
{
vol.Required(
@ -224,7 +232,9 @@ class RiscoOptionsFlowHandler(config_entries.OptionsFlow):
}
)
async def async_step_init(self, user_input=None):
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Manage the options."""
if user_input is not None:
self._data = {**self._data, **user_input}
@ -232,7 +242,9 @@ class RiscoOptionsFlowHandler(config_entries.OptionsFlow):
return self.async_show_form(step_id="init", data_schema=self._options_schema())
async def async_step_risco_to_ha(self, user_input=None):
async def async_step_risco_to_ha(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Map Risco states to HA states."""
if user_input is not None:
self._data[CONF_RISCO_STATES_TO_HA] = user_input
@ -250,7 +262,9 @@ class RiscoOptionsFlowHandler(config_entries.OptionsFlow):
return self.async_show_form(step_id="risco_to_ha", data_schema=options)
async def async_step_ha_to_risco(self, user_input=None):
async def async_step_ha_to_risco(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Map HA states to Risco states."""
if user_input is not None:
self._data[CONF_HA_STATES_TO_RISCO] = user_input

View File

@ -3,7 +3,9 @@ from __future__ import annotations
from typing import Any
from pyrisco.common import Zone
from pyrisco import RiscoCloud
from pyrisco.cloud.zone import Zone as CloudZone
from pyrisco.local.zone import Zone as LocalZone
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -14,7 +16,7 @@ from . import RiscoDataUpdateCoordinator, zone_update_signal
from .const import DOMAIN
def zone_unique_id(risco, zone_id: int) -> str:
def zone_unique_id(risco: RiscoCloud, zone_id: int) -> str:
"""Return unique id for a cloud zone."""
return f"{risco.site_uuid}_zone_{zone_id}"
@ -36,7 +38,7 @@ class RiscoCloudEntity(CoordinatorEntity[RiscoDataUpdateCoordinator]):
self.async_write_ha_state()
@property
def _risco(self):
def _risco(self) -> RiscoCloud:
"""Return the Risco API object."""
return self.coordinator.risco
@ -52,7 +54,7 @@ class RiscoCloudZoneEntity(RiscoCloudEntity):
coordinator: RiscoDataUpdateCoordinator,
suffix: str,
zone_id: int,
zone: Zone,
zone: CloudZone,
**kwargs: Any,
) -> None:
"""Init the zone."""
@ -84,7 +86,7 @@ class RiscoLocalZoneEntity(Entity):
system_id: str,
suffix: str,
zone_id: int,
zone: Zone,
zone: LocalZone,
**kwargs: Any,
) -> None:
"""Init the zone."""

View File

@ -2,8 +2,11 @@
from __future__ import annotations
from collections.abc import Collection, Mapping
from datetime import datetime
from typing import Any
from pyrisco.cloud.event import Event
from homeassistant.components.binary_sensor import DOMAIN as BS_DOMAIN
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.config_entries import ConfigEntry
@ -66,22 +69,23 @@ async def async_setup_entry(
class RiscoSensor(CoordinatorEntity[RiscoEventsDataUpdateCoordinator], SensorEntity):
"""Sensor for Risco events."""
_entity_registry: er.EntityRegistry
def __init__(
self,
coordinator: RiscoEventsDataUpdateCoordinator,
category_id: int | None,
excludes: Collection[int] | None,
excludes: Collection[int],
name: str,
entry_id: str,
) -> None:
"""Initialize sensor."""
super().__init__(coordinator)
self._event = None
self._event: Event | None = None
self._category_id = category_id
self._excludes = excludes
self._name = name
self._entry_id = entry_id
self._entity_registry: er.EntityRegistry | None = None
self._attr_unique_id = f"events_{name}_{self.coordinator.risco.site_uuid}"
self._attr_name = f"Risco {self.coordinator.risco.site_name} {name} Events"
self._attr_device_class = SensorDeviceClass.TIMESTAMP
@ -91,7 +95,7 @@ class RiscoSensor(CoordinatorEntity[RiscoEventsDataUpdateCoordinator], SensorEnt
await super().async_added_to_hass()
self._entity_registry = er.async_get(self.hass)
def _handle_coordinator_update(self):
def _handle_coordinator_update(self) -> None:
events = self.coordinator.data
for event in reversed(events):
if event.category_id in self._excludes:
@ -103,14 +107,14 @@ class RiscoSensor(CoordinatorEntity[RiscoEventsDataUpdateCoordinator], SensorEnt
self.async_write_ha_state()
@property
def native_value(self):
def native_value(self) -> datetime | None:
"""Value of sensor."""
if self._event is None:
return None
return dt_util.parse_datetime(self._event.time).replace(
tzinfo=dt_util.DEFAULT_TIME_ZONE
)
if res := dt_util.parse_datetime(self._event.time):
return res.replace(tzinfo=dt_util.DEFAULT_TIME_ZONE)
return None
@property
def extra_state_attributes(self) -> Mapping[str, Any] | None:

View File

@ -1,6 +1,8 @@
"""Support for bypassing Risco alarm zones."""
from __future__ import annotations
from typing import Any
from pyrisco.common import Zone
from homeassistant.components.switch import SwitchEntity
@ -58,11 +60,11 @@ class RiscoCloudSwitch(RiscoCloudZoneEntity, SwitchEntity):
"""Return true if the zone is bypassed."""
return self._zone.bypassed
async def async_turn_on(self, **kwargs):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
await self._bypass(True)
async def async_turn_off(self, **kwargs):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
await self._bypass(False)
@ -92,11 +94,11 @@ class RiscoLocalSwitch(RiscoLocalZoneEntity, SwitchEntity):
"""Return true if the zone is bypassed."""
return self._zone.bypassed
async def async_turn_on(self, **kwargs):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
await self._bypass(True)
async def async_turn_off(self, **kwargs):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
await self._bypass(False)