mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Improve risco typing (#108041)
This commit is contained in:
parent
369ed5b701
commit
e8b962ea89
@ -1,4 +1,6 @@
|
|||||||
"""The Risco integration."""
|
"""The Risco integration."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -6,6 +6,7 @@ import logging
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pyrisco.common import Partition
|
from pyrisco.common import Partition
|
||||||
|
from pyrisco.local.partition import Partition as LocalPartition
|
||||||
|
|
||||||
from homeassistant.components.alarm_control_panel import (
|
from homeassistant.components.alarm_control_panel import (
|
||||||
AlarmControlPanelEntity,
|
AlarmControlPanelEntity,
|
||||||
@ -132,7 +133,7 @@ class RiscoAlarm(AlarmControlPanelEntity):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _validate_code(self, code):
|
def _validate_code(self, code: str | None) -> bool:
|
||||||
"""Validate given code."""
|
"""Validate given code."""
|
||||||
return code == self._code
|
return code == self._code
|
||||||
|
|
||||||
@ -159,7 +160,7 @@ class RiscoAlarm(AlarmControlPanelEntity):
|
|||||||
"""Send arm custom bypass command."""
|
"""Send arm custom bypass command."""
|
||||||
await self._arm(STATE_ALARM_ARMED_CUSTOM_BYPASS, code)
|
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):
|
if self.code_arm_required and not self._validate_code(code):
|
||||||
_LOGGER.warning("Wrong code entered for %s", mode)
|
_LOGGER.warning("Wrong code entered for %s", mode)
|
||||||
return
|
return
|
||||||
@ -205,7 +206,7 @@ class RiscoCloudAlarm(RiscoAlarm, RiscoCloudEntity):
|
|||||||
def _get_data_from_coordinator(self) -> None:
|
def _get_data_from_coordinator(self) -> None:
|
||||||
self._partition = self.coordinator.data.partitions[self._partition_id]
|
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)
|
alarm = await getattr(self._risco, method)(self._partition_id, *args)
|
||||||
self._partition = alarm.partitions[self._partition_id]
|
self._partition = alarm.partitions[self._partition_id]
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
@ -220,7 +221,7 @@ class RiscoLocalAlarm(RiscoAlarm):
|
|||||||
self,
|
self,
|
||||||
system_id: str,
|
system_id: str,
|
||||||
partition_id: int,
|
partition_id: int,
|
||||||
partition: Partition,
|
partition: LocalPartition,
|
||||||
partition_updates: dict[int, Callable[[], Any]],
|
partition_updates: dict[int, Callable[[], Any]],
|
||||||
code: str,
|
code: str,
|
||||||
options: dict[str, Any],
|
options: dict[str, Any],
|
||||||
|
@ -4,7 +4,8 @@ from __future__ import annotations
|
|||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from typing import Any
|
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 (
|
from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorDeviceClass,
|
BinarySensorDeviceClass,
|
||||||
@ -53,7 +54,7 @@ class RiscoCloudBinarySensor(RiscoCloudZoneEntity, BinarySensorEntity):
|
|||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, coordinator: RiscoDataUpdateCoordinator, zone_id: int, zone: Zone
|
self, coordinator: RiscoDataUpdateCoordinator, zone_id: int, zone: CloudZone
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Init the zone."""
|
"""Init the zone."""
|
||||||
super().__init__(coordinator=coordinator, suffix="", zone_id=zone_id, zone=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_device_class = BinarySensorDeviceClass.MOTION
|
||||||
_attr_name = None
|
_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."""
|
"""Init the zone."""
|
||||||
super().__init__(system_id=system_id, suffix="", zone_id=zone_id, zone=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"
|
_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."""
|
"""Init the zone."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
system_id=system_id,
|
system_id=system_id,
|
||||||
@ -113,7 +114,7 @@ class RiscoLocalArmedBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity):
|
|||||||
|
|
||||||
_attr_translation_key = "armed"
|
_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."""
|
"""Init the zone."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
system_id=system_id,
|
system_id=system_id,
|
||||||
|
@ -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.
|
"""Validate the user input allows us to connect to Risco Cloud.
|
||||||
|
|
||||||
Data has the keys from CLOUD_SCHEMA with values provided by the user.
|
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."""
|
"""Define the config flow to handle options."""
|
||||||
return RiscoOptionsFlowHandler(config_entry)
|
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."""
|
"""Handle the initial step."""
|
||||||
return self.async_show_menu(
|
return self.async_show_menu(
|
||||||
step_id="user",
|
step_id="user",
|
||||||
menu_options=["cloud", "local"],
|
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."""
|
"""Configure a cloud based alarm."""
|
||||||
errors = {}
|
errors: dict[str, str] = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
if not self._reauth_entry:
|
if not self._reauth_entry:
|
||||||
await self.async_set_unique_id(user_input[CONF_USERNAME])
|
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])
|
self._reauth_entry = await self.async_set_unique_id(entry_data[CONF_USERNAME])
|
||||||
return await self.async_step_cloud()
|
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."""
|
"""Configure a local based alarm."""
|
||||||
errors = {}
|
errors: dict[str, str] = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
try:
|
try:
|
||||||
info = await validate_local_input(self.hass, user_input)
|
info = await validate_local_input(self.hass, user_input)
|
||||||
except CannotConnectError:
|
except CannotConnectError as ex:
|
||||||
_LOGGER.debug("Cannot connect", exc_info=1)
|
_LOGGER.debug("Cannot connect", exc_info=ex)
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
except UnauthorizedError:
|
except UnauthorizedError:
|
||||||
errors["base"] = "invalid_auth"
|
errors["base"] = "invalid_auth"
|
||||||
@ -208,7 +216,7 @@ class RiscoOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self.config_entry = config_entry
|
self.config_entry = config_entry
|
||||||
self._data = {**DEFAULT_OPTIONS, **config_entry.options}
|
self._data = {**DEFAULT_OPTIONS, **config_entry.options}
|
||||||
|
|
||||||
def _options_schema(self):
|
def _options_schema(self) -> vol.Schema:
|
||||||
return vol.Schema(
|
return vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(
|
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."""
|
"""Manage the options."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
self._data = {**self._data, **user_input}
|
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())
|
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."""
|
"""Map Risco states to HA states."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
self._data[CONF_RISCO_STATES_TO_HA] = user_input
|
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)
|
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."""
|
"""Map HA states to Risco states."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
self._data[CONF_HA_STATES_TO_RISCO] = user_input
|
self._data[CONF_HA_STATES_TO_RISCO] = user_input
|
||||||
|
@ -3,7 +3,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any
|
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.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
@ -14,7 +16,7 @@ from . import RiscoDataUpdateCoordinator, zone_update_signal
|
|||||||
from .const import DOMAIN
|
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 unique id for a cloud zone."""
|
||||||
return f"{risco.site_uuid}_zone_{zone_id}"
|
return f"{risco.site_uuid}_zone_{zone_id}"
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ class RiscoCloudEntity(CoordinatorEntity[RiscoDataUpdateCoordinator]):
|
|||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _risco(self):
|
def _risco(self) -> RiscoCloud:
|
||||||
"""Return the Risco API object."""
|
"""Return the Risco API object."""
|
||||||
return self.coordinator.risco
|
return self.coordinator.risco
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ class RiscoCloudZoneEntity(RiscoCloudEntity):
|
|||||||
coordinator: RiscoDataUpdateCoordinator,
|
coordinator: RiscoDataUpdateCoordinator,
|
||||||
suffix: str,
|
suffix: str,
|
||||||
zone_id: int,
|
zone_id: int,
|
||||||
zone: Zone,
|
zone: CloudZone,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Init the zone."""
|
"""Init the zone."""
|
||||||
@ -84,7 +86,7 @@ class RiscoLocalZoneEntity(Entity):
|
|||||||
system_id: str,
|
system_id: str,
|
||||||
suffix: str,
|
suffix: str,
|
||||||
zone_id: int,
|
zone_id: int,
|
||||||
zone: Zone,
|
zone: LocalZone,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Init the zone."""
|
"""Init the zone."""
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Collection, Mapping
|
from collections.abc import Collection, Mapping
|
||||||
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from pyrisco.cloud.event import Event
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import DOMAIN as BS_DOMAIN
|
from homeassistant.components.binary_sensor import DOMAIN as BS_DOMAIN
|
||||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@ -66,22 +69,23 @@ async def async_setup_entry(
|
|||||||
class RiscoSensor(CoordinatorEntity[RiscoEventsDataUpdateCoordinator], SensorEntity):
|
class RiscoSensor(CoordinatorEntity[RiscoEventsDataUpdateCoordinator], SensorEntity):
|
||||||
"""Sensor for Risco events."""
|
"""Sensor for Risco events."""
|
||||||
|
|
||||||
|
_entity_registry: er.EntityRegistry
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: RiscoEventsDataUpdateCoordinator,
|
coordinator: RiscoEventsDataUpdateCoordinator,
|
||||||
category_id: int | None,
|
category_id: int | None,
|
||||||
excludes: Collection[int] | None,
|
excludes: Collection[int],
|
||||||
name: str,
|
name: str,
|
||||||
entry_id: str,
|
entry_id: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize sensor."""
|
"""Initialize sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self._event = None
|
self._event: Event | None = None
|
||||||
self._category_id = category_id
|
self._category_id = category_id
|
||||||
self._excludes = excludes
|
self._excludes = excludes
|
||||||
self._name = name
|
self._name = name
|
||||||
self._entry_id = entry_id
|
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_unique_id = f"events_{name}_{self.coordinator.risco.site_uuid}"
|
||||||
self._attr_name = f"Risco {self.coordinator.risco.site_name} {name} Events"
|
self._attr_name = f"Risco {self.coordinator.risco.site_name} {name} Events"
|
||||||
self._attr_device_class = SensorDeviceClass.TIMESTAMP
|
self._attr_device_class = SensorDeviceClass.TIMESTAMP
|
||||||
@ -91,7 +95,7 @@ class RiscoSensor(CoordinatorEntity[RiscoEventsDataUpdateCoordinator], SensorEnt
|
|||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
self._entity_registry = er.async_get(self.hass)
|
self._entity_registry = er.async_get(self.hass)
|
||||||
|
|
||||||
def _handle_coordinator_update(self):
|
def _handle_coordinator_update(self) -> None:
|
||||||
events = self.coordinator.data
|
events = self.coordinator.data
|
||||||
for event in reversed(events):
|
for event in reversed(events):
|
||||||
if event.category_id in self._excludes:
|
if event.category_id in self._excludes:
|
||||||
@ -103,14 +107,14 @@ class RiscoSensor(CoordinatorEntity[RiscoEventsDataUpdateCoordinator], SensorEnt
|
|||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self):
|
def native_value(self) -> datetime | None:
|
||||||
"""Value of sensor."""
|
"""Value of sensor."""
|
||||||
if self._event is None:
|
if self._event is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return dt_util.parse_datetime(self._event.time).replace(
|
if res := dt_util.parse_datetime(self._event.time):
|
||||||
tzinfo=dt_util.DEFAULT_TIME_ZONE
|
return res.replace(tzinfo=dt_util.DEFAULT_TIME_ZONE)
|
||||||
)
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Support for bypassing Risco alarm zones."""
|
"""Support for bypassing Risco alarm zones."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from pyrisco.common import Zone
|
from pyrisco.common import Zone
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity
|
from homeassistant.components.switch import SwitchEntity
|
||||||
@ -58,11 +60,11 @@ class RiscoCloudSwitch(RiscoCloudZoneEntity, SwitchEntity):
|
|||||||
"""Return true if the zone is bypassed."""
|
"""Return true if the zone is bypassed."""
|
||||||
return self._zone.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."""
|
"""Turn the entity on."""
|
||||||
await self._bypass(True)
|
await self._bypass(True)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs):
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity off."""
|
"""Turn the entity off."""
|
||||||
await self._bypass(False)
|
await self._bypass(False)
|
||||||
|
|
||||||
@ -92,11 +94,11 @@ class RiscoLocalSwitch(RiscoLocalZoneEntity, SwitchEntity):
|
|||||||
"""Return true if the zone is bypassed."""
|
"""Return true if the zone is bypassed."""
|
||||||
return self._zone.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."""
|
"""Turn the entity on."""
|
||||||
await self._bypass(True)
|
await self._bypass(True)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs):
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity off."""
|
"""Turn the entity off."""
|
||||||
await self._bypass(False)
|
await self._bypass(False)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user