Support for local push in Risco integration (#75874)
* Local config flow * Local entities * Apply suggestions from code review Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Address code review comments * More type hints * Apply suggestions from code review Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * More annotations * Even more annonations * New entity naming * Move fixtures to conftest * Improve state tests for local * Remove mutable default arguments * Remove assertions for lack of state * Add missing file * Switch setup to fixtures * Use error fixtures in test_config_flow * Apply suggestions from code review Co-authored-by: Martin Hjelmare <marhje52@gmail.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
2497ff5a39
commit
635eda584d
16 changed files with 1596 additions and 480 deletions
|
@ -1,4 +1,11 @@
|
|||
"""Support for Risco alarm zones."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Mapping
|
||||
from typing import Any
|
||||
|
||||
from pyrisco.common import Zone
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
|
@ -9,6 +16,7 @@ from homeassistant.helpers import entity_platform
|
|||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import LocalData, RiscoDataUpdateCoordinator, is_local
|
||||
from .const import DATA_COORDINATOR, DOMAIN
|
||||
from .entity import RiscoEntity, binary_sensor_unique_id
|
||||
|
||||
|
@ -28,69 +36,117 @@ async def async_setup_entry(
|
|||
SERVICE_UNBYPASS_ZONE, {}, "async_unbypass_zone"
|
||||
)
|
||||
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][DATA_COORDINATOR]
|
||||
entities = [
|
||||
RiscoBinarySensor(coordinator, zone_id, zone)
|
||||
for zone_id, zone in coordinator.data.zones.items()
|
||||
]
|
||||
async_add_entities(entities, False)
|
||||
|
||||
|
||||
class RiscoBinarySensor(BinarySensorEntity, RiscoEntity):
|
||||
"""Representation of a Risco zone as a binary sensor."""
|
||||
|
||||
def __init__(self, coordinator, zone_id, zone):
|
||||
"""Init the zone."""
|
||||
super().__init__(coordinator)
|
||||
self._zone_id = zone_id
|
||||
self._zone = zone
|
||||
|
||||
def _get_data_from_coordinator(self):
|
||||
self._zone = self.coordinator.data.zones[self._zone_id]
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return device info for this device."""
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self.unique_id)},
|
||||
manufacturer="Risco",
|
||||
name=self.name,
|
||||
if is_local(config_entry):
|
||||
local_data: LocalData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
async_add_entities(
|
||||
RiscoLocalBinarySensor(
|
||||
local_data.system.id, zone_id, zone, local_data.zone_updates
|
||||
)
|
||||
for zone_id, zone in local_data.system.zones.items()
|
||||
)
|
||||
else:
|
||||
coordinator: RiscoDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
][DATA_COORDINATOR]
|
||||
async_add_entities(
|
||||
RiscoCloudBinarySensor(coordinator, zone_id, zone)
|
||||
for zone_id, zone in coordinator.data.zones.items()
|
||||
)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the zone."""
|
||||
return self._zone.name
|
||||
|
||||
class RiscoBinarySensor(BinarySensorEntity):
|
||||
"""Representation of a Risco zone as a binary sensor."""
|
||||
|
||||
_attr_device_class = BinarySensorDeviceClass.MOTION
|
||||
|
||||
def __init__(self, *, zone_id: int, zone: Zone, **kwargs: Any) -> None:
|
||||
"""Init the zone."""
|
||||
super().__init__(**kwargs)
|
||||
self._zone_id = zone_id
|
||||
self._zone = zone
|
||||
self._attr_has_entity_name = True
|
||||
self._attr_name = None
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique id for this zone."""
|
||||
return binary_sensor_unique_id(self._risco, self._zone_id)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
"""Return the state attributes."""
|
||||
return {"zone_id": self._zone_id, "bypassed": self._zone.bypassed}
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
def is_on(self) -> bool | None:
|
||||
"""Return true if sensor is on."""
|
||||
return self._zone.triggered
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of this sensor, from BinarySensorDeviceClass."""
|
||||
return BinarySensorDeviceClass.MOTION
|
||||
async def async_bypass_zone(self) -> None:
|
||||
"""Bypass this zone."""
|
||||
await self._bypass(True)
|
||||
|
||||
async def _bypass(self, bypass):
|
||||
async def async_unbypass_zone(self) -> None:
|
||||
"""Unbypass this zone."""
|
||||
await self._bypass(False)
|
||||
|
||||
async def _bypass(self, bypass: bool) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class RiscoCloudBinarySensor(RiscoBinarySensor, RiscoEntity):
|
||||
"""Representation of a Risco cloud zone as a binary sensor."""
|
||||
|
||||
def __init__(
|
||||
self, coordinator: RiscoDataUpdateCoordinator, zone_id: int, zone: Zone
|
||||
) -> None:
|
||||
"""Init the zone."""
|
||||
super().__init__(zone_id=zone_id, zone=zone, coordinator=coordinator)
|
||||
self._attr_unique_id = binary_sensor_unique_id(self._risco, zone_id)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._attr_unique_id)},
|
||||
manufacturer="Risco",
|
||||
name=self._zone.name,
|
||||
)
|
||||
|
||||
def _get_data_from_coordinator(self) -> None:
|
||||
self._zone = self.coordinator.data.zones[self._zone_id]
|
||||
|
||||
async def _bypass(self, bypass: bool) -> None:
|
||||
alarm = await self._risco.bypass_zone(self._zone_id, bypass)
|
||||
self._zone = alarm.zones[self._zone_id]
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_bypass_zone(self):
|
||||
"""Bypass this zone."""
|
||||
await self._bypass(True)
|
||||
|
||||
async def async_unbypass_zone(self):
|
||||
"""Unbypass this zone."""
|
||||
await self._bypass(False)
|
||||
class RiscoLocalBinarySensor(RiscoBinarySensor):
|
||||
"""Representation of a Risco local zone as a binary sensor."""
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
system_id: str,
|
||||
zone_id: int,
|
||||
zone: Zone,
|
||||
zone_updates: dict[int, Callable[[], Any]],
|
||||
) -> None:
|
||||
"""Init the zone."""
|
||||
super().__init__(zone_id=zone_id, zone=zone)
|
||||
self._system_id = system_id
|
||||
self._zone_updates = zone_updates
|
||||
self._attr_unique_id = f"{system_id}_zone_{zone_id}_local"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._attr_unique_id)},
|
||||
manufacturer="Risco",
|
||||
name=self._zone.name,
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to updates."""
|
||||
self._zone_updates[self._zone_id] = self.async_write_ha_state
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
**(super().extra_state_attributes or {}),
|
||||
"groups": self._zone.groups,
|
||||
}
|
||||
|
||||
async def _bypass(self, bypass: bool) -> None:
|
||||
await self._zone.bypass(bypass)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue