mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 02:37:08 +00:00
Add coordinator to Aladdin Connect (#118781)
This commit is contained in:
parent
42414d55e0
commit
e9f01be090
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from genie_partner_sdk.client import AladdinConnectClient
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -12,10 +14,11 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .api import AsyncConfigEntryAuth
|
from .api import AsyncConfigEntryAuth
|
||||||
|
from .coordinator import AladdinConnectCoordinator
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [Platform.COVER]
|
PLATFORMS: list[Platform] = [Platform.COVER]
|
||||||
|
|
||||||
type AladdinConnectConfigEntry = ConfigEntry[AsyncConfigEntryAuth]
|
type AladdinConnectConfigEntry = ConfigEntry[AladdinConnectCoordinator]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@ -25,8 +28,13 @@ async def async_setup_entry(
|
|||||||
implementation = await async_get_config_entry_implementation(hass, entry)
|
implementation = await async_get_config_entry_implementation(hass, entry)
|
||||||
|
|
||||||
session = OAuth2Session(hass, entry, implementation)
|
session = OAuth2Session(hass, entry, implementation)
|
||||||
|
auth = AsyncConfigEntryAuth(async_get_clientsession(hass), session)
|
||||||
|
coordinator = AladdinConnectCoordinator(hass, AladdinConnectClient(auth))
|
||||||
|
|
||||||
entry.runtime_data = AsyncConfigEntryAuth(async_get_clientsession(hass), session)
|
await coordinator.async_setup()
|
||||||
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
|
entry.runtime_data = coordinator
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
|
38
homeassistant/components/aladdin_connect/coordinator.py
Normal file
38
homeassistant/components/aladdin_connect/coordinator.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
"""Define an object to coordinate fetching Aladdin Connect data."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from genie_partner_sdk.client import AladdinConnectClient
|
||||||
|
from genie_partner_sdk.model import GarageDoor
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AladdinConnectCoordinator(DataUpdateCoordinator[None]):
|
||||||
|
"""Aladdin Connect Data Update Coordinator."""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant, acc: AladdinConnectClient) -> None:
|
||||||
|
"""Initialize."""
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
logger=_LOGGER,
|
||||||
|
name=DOMAIN,
|
||||||
|
update_interval=timedelta(seconds=15),
|
||||||
|
)
|
||||||
|
self.acc = acc
|
||||||
|
self.doors: list[GarageDoor] = []
|
||||||
|
|
||||||
|
async def async_setup(self) -> None:
|
||||||
|
"""Fetch initial data."""
|
||||||
|
self.doors = await self.acc.get_doors()
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> None:
|
||||||
|
"""Fetch data from API endpoint."""
|
||||||
|
for door in self.doors:
|
||||||
|
await self.acc.update_door(door.device_id, door.door_number)
|
@ -1,9 +1,7 @@
|
|||||||
"""Cover Entity for Genie Garage Door."""
|
"""Cover Entity for Genie Garage Door."""
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from genie_partner_sdk.client import AladdinConnectClient
|
|
||||||
from genie_partner_sdk.model import GarageDoor
|
from genie_partner_sdk.model import GarageDoor
|
||||||
|
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
@ -11,52 +9,36 @@ from homeassistant.components.cover import (
|
|||||||
CoverEntity,
|
CoverEntity,
|
||||||
CoverEntityFeature,
|
CoverEntityFeature,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import PlatformNotReady
|
|
||||||
import homeassistant.helpers.device_registry as dr
|
import homeassistant.helpers.device_registry as dr
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import api
|
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
from .entity import AladdinConnectEntity
|
||||||
SCAN_INTERVAL = timedelta(seconds=15)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: AladdinConnectConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Aladdin Connect platform."""
|
"""Set up the Aladdin Connect platform."""
|
||||||
session: api.AsyncConfigEntryAuth = config_entry.runtime_data
|
coordinator = config_entry.runtime_data
|
||||||
acc = AladdinConnectClient(session)
|
|
||||||
doors = await acc.get_doors()
|
|
||||||
if doors is None:
|
|
||||||
raise PlatformNotReady("Error from Aladdin Connect getting doors")
|
|
||||||
device_registry = dr.async_get(hass)
|
|
||||||
doors_to_add = []
|
|
||||||
for door in doors:
|
|
||||||
existing = device_registry.async_get(door.unique_id)
|
|
||||||
if existing is None:
|
|
||||||
doors_to_add.append(door)
|
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(AladdinDevice(coordinator, door) for door in coordinator.doors)
|
||||||
(AladdinDevice(acc, door, config_entry) for door in doors_to_add),
|
remove_stale_devices(hass, config_entry)
|
||||||
)
|
|
||||||
remove_stale_devices(hass, config_entry, doors)
|
|
||||||
|
|
||||||
|
|
||||||
def remove_stale_devices(
|
def remove_stale_devices(
|
||||||
hass: HomeAssistant, config_entry: ConfigEntry, devices: list[GarageDoor]
|
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Remove stale devices from device registry."""
|
"""Remove stale devices from device registry."""
|
||||||
device_registry = dr.async_get(hass)
|
device_registry = dr.async_get(hass)
|
||||||
device_entries = dr.async_entries_for_config_entry(
|
device_entries = dr.async_entries_for_config_entry(
|
||||||
device_registry, config_entry.entry_id
|
device_registry, config_entry.entry_id
|
||||||
)
|
)
|
||||||
all_device_ids = {door.unique_id for door in devices}
|
all_device_ids = {door.unique_id for door in config_entry.runtime_data.doors}
|
||||||
|
|
||||||
for device_entry in device_entries:
|
for device_entry in device_entries:
|
||||||
device_id: str | None = None
|
device_id: str | None = None
|
||||||
@ -75,45 +57,38 @@ def remove_stale_devices(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AladdinDevice(CoverEntity):
|
class AladdinDevice(AladdinConnectEntity, CoverEntity):
|
||||||
"""Representation of Aladdin Connect cover."""
|
"""Representation of Aladdin Connect cover."""
|
||||||
|
|
||||||
_attr_device_class = CoverDeviceClass.GARAGE
|
_attr_device_class = CoverDeviceClass.GARAGE
|
||||||
_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
|
_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
|
||||||
_attr_has_entity_name = True
|
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, acc: AladdinConnectClient, device: GarageDoor, entry: ConfigEntry
|
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Aladdin Connect cover."""
|
"""Initialize the Aladdin Connect cover."""
|
||||||
self._acc = acc
|
super().__init__(coordinator, device)
|
||||||
self._device_id = device.device_id
|
|
||||||
self._number = device.door_number
|
|
||||||
|
|
||||||
self._attr_device_info = DeviceInfo(
|
|
||||||
identifiers={(DOMAIN, device.unique_id)},
|
|
||||||
name=device.name,
|
|
||||||
manufacturer="Overhead Door",
|
|
||||||
)
|
|
||||||
self._attr_unique_id = device.unique_id
|
self._attr_unique_id = device.unique_id
|
||||||
|
|
||||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||||
"""Issue open command to cover."""
|
"""Issue open command to cover."""
|
||||||
await self._acc.open_door(self._device_id, self._number)
|
await self.coordinator.acc.open_door(
|
||||||
|
self._device.device_id, self._device.door_number
|
||||||
|
)
|
||||||
|
|
||||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||||
"""Issue close command to cover."""
|
"""Issue close command to cover."""
|
||||||
await self._acc.close_door(self._device_id, self._number)
|
await self.coordinator.acc.close_door(
|
||||||
|
self._device.device_id, self._device.door_number
|
||||||
async def async_update(self) -> None:
|
)
|
||||||
"""Update status of cover."""
|
|
||||||
await self._acc.update_door(self._device_id, self._number)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_closed(self) -> bool | None:
|
def is_closed(self) -> bool | None:
|
||||||
"""Update is closed attribute."""
|
"""Update is closed attribute."""
|
||||||
value = self._acc.get_door_status(self._device_id, self._number)
|
value = self.coordinator.acc.get_door_status(
|
||||||
|
self._device.device_id, self._device.door_number
|
||||||
|
)
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
return bool(value == "closed")
|
return bool(value == "closed")
|
||||||
@ -121,7 +96,9 @@ class AladdinDevice(CoverEntity):
|
|||||||
@property
|
@property
|
||||||
def is_closing(self) -> bool | None:
|
def is_closing(self) -> bool | None:
|
||||||
"""Update is closing attribute."""
|
"""Update is closing attribute."""
|
||||||
value = self._acc.get_door_status(self._device_id, self._number)
|
value = self.coordinator.acc.get_door_status(
|
||||||
|
self._device.device_id, self._device.door_number
|
||||||
|
)
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
return bool(value == "closing")
|
return bool(value == "closing")
|
||||||
@ -129,7 +106,9 @@ class AladdinDevice(CoverEntity):
|
|||||||
@property
|
@property
|
||||||
def is_opening(self) -> bool | None:
|
def is_opening(self) -> bool | None:
|
||||||
"""Update is opening attribute."""
|
"""Update is opening attribute."""
|
||||||
value = self._acc.get_door_status(self._device_id, self._number)
|
value = self.coordinator.acc.get_door_status(
|
||||||
|
self._device.device_id, self._device.door_number
|
||||||
|
)
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
return bool(value == "opening")
|
return bool(value == "opening")
|
||||||
|
27
homeassistant/components/aladdin_connect/entity.py
Normal file
27
homeassistant/components/aladdin_connect/entity.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
"""Defines a base Aladdin Connect entity."""
|
||||||
|
|
||||||
|
from genie_partner_sdk.model import GarageDoor
|
||||||
|
|
||||||
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
from .coordinator import AladdinConnectCoordinator
|
||||||
|
|
||||||
|
|
||||||
|
class AladdinConnectEntity(CoordinatorEntity[AladdinConnectCoordinator]):
|
||||||
|
"""Defines a base Aladdin Connect entity."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the entity."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
self._device = device
|
||||||
|
self._attr_device_info = DeviceInfo(
|
||||||
|
identifiers={(DOMAIN, device.unique_id)},
|
||||||
|
name=device.name,
|
||||||
|
manufacturer="Overhead Door",
|
||||||
|
)
|
@ -4,7 +4,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import cast
|
|
||||||
|
|
||||||
from genie_partner_sdk.client import AladdinConnectClient
|
from genie_partner_sdk.client import AladdinConnectClient
|
||||||
from genie_partner_sdk.model import GarageDoor
|
from genie_partner_sdk.model import GarageDoor
|
||||||
@ -15,21 +14,19 @@ from homeassistant.components.sensor import (
|
|||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import PERCENTAGE
|
from homeassistant.const import PERCENTAGE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import api
|
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
|
||||||
from .const import DOMAIN
|
from .entity import AladdinConnectEntity
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
class AccSensorEntityDescription(SensorEntityDescription):
|
class AccSensorEntityDescription(SensorEntityDescription):
|
||||||
"""Describes AladdinConnect sensor entity."""
|
"""Describes AladdinConnect sensor entity."""
|
||||||
|
|
||||||
value_fn: Callable
|
value_fn: Callable[[AladdinConnectClient, str, int], float | None]
|
||||||
|
|
||||||
|
|
||||||
SENSORS: tuple[AccSensorEntityDescription, ...] = (
|
SENSORS: tuple[AccSensorEntityDescription, ...] = (
|
||||||
@ -45,52 +42,39 @@ SENSORS: tuple[AccSensorEntityDescription, ...] = (
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant,
|
||||||
|
entry: AladdinConnectConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Aladdin Connect sensor devices."""
|
"""Set up Aladdin Connect sensor devices."""
|
||||||
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
session: api.AsyncConfigEntryAuth = hass.data[DOMAIN][entry.entry_id]
|
async_add_entities(
|
||||||
acc = AladdinConnectClient(session)
|
AladdinConnectSensor(coordinator, door, description)
|
||||||
|
for description in SENSORS
|
||||||
entities = []
|
for door in coordinator.doors
|
||||||
doors = await acc.get_doors()
|
)
|
||||||
|
|
||||||
for door in doors:
|
|
||||||
entities.extend(
|
|
||||||
[AladdinConnectSensor(acc, door, description) for description in SENSORS]
|
|
||||||
)
|
|
||||||
|
|
||||||
async_add_entities(entities)
|
|
||||||
|
|
||||||
|
|
||||||
class AladdinConnectSensor(SensorEntity):
|
class AladdinConnectSensor(AladdinConnectEntity, SensorEntity):
|
||||||
"""A sensor implementation for Aladdin Connect devices."""
|
"""A sensor implementation for Aladdin Connect devices."""
|
||||||
|
|
||||||
entity_description: AccSensorEntityDescription
|
entity_description: AccSensorEntityDescription
|
||||||
_attr_has_entity_name = True
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
acc: AladdinConnectClient,
|
coordinator: AladdinConnectCoordinator,
|
||||||
device: GarageDoor,
|
device: GarageDoor,
|
||||||
description: AccSensorEntityDescription,
|
description: AccSensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a sensor for an Aladdin Connect device."""
|
"""Initialize a sensor for an Aladdin Connect device."""
|
||||||
self._device_id = device.device_id
|
super().__init__(coordinator, device)
|
||||||
self._number = device.door_number
|
|
||||||
self._acc = acc
|
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_unique_id = f"{device.unique_id}-{description.key}"
|
self._attr_unique_id = f"{device.unique_id}-{description.key}"
|
||||||
self._attr_device_info = DeviceInfo(
|
|
||||||
identifiers={(DOMAIN, device.unique_id)},
|
|
||||||
name=device.name,
|
|
||||||
manufacturer="Overhead Door",
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> float | None:
|
def native_value(self) -> float | None:
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return cast(
|
return self.entity_description.value_fn(
|
||||||
float,
|
self.coordinator.acc, self._device.device_id, self._device.door_number
|
||||||
self.entity_description.value_fn(self._acc, self._device_id, self._number),
|
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user