Improve nuki typing (#86736)

* Use NukiCoordinator

* Make NukiEntity generic

* Remove unnecessary ABC
pull/86758/head
Marc Mueller 2023-01-26 22:03:36 +01:00 committed by GitHub
parent 138a522d2e
commit caa1ba7e13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 49 deletions

View File

@ -1,7 +1,10 @@
"""The nuki component."""
from __future__ import annotations
from collections import defaultdict
from datetime import timedelta
import logging
from typing import Generic, TypeVar
import async_timeout
from pynuki import NukiBridge, NukiLock, NukiOpener
@ -31,6 +34,8 @@ from .const import (
)
from .helpers import parse_id
_NukiDeviceT = TypeVar("_NukiDeviceT", bound=NukiDevice)
_LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.BINARY_SENSOR, Platform.LOCK, Platform.SENSOR]
@ -109,38 +114,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class NukiEntity(CoordinatorEntity[DataUpdateCoordinator[None]]):
"""An entity using CoordinatorEntity.
The CoordinatorEntity class provides:
should_poll
async_update
async_added_to_hass
available
"""
def __init__(
self, coordinator: DataUpdateCoordinator[None], nuki_device: NukiDevice
) -> None:
"""Pass coordinator to CoordinatorEntity."""
super().__init__(coordinator)
self._nuki_device = nuki_device
@property
def device_info(self):
"""Device info for Nuki entities."""
return {
"identifiers": {(DOMAIN, parse_id(self._nuki_device.nuki_id))},
"name": self._nuki_device.name,
"manufacturer": "Nuki Home Solutions GmbH",
"model": self._nuki_device.device_type_str.capitalize(),
"sw_version": self._nuki_device.firmware_version,
"via_device": (DOMAIN, self.coordinator.bridge_id),
}
class NukiCoordinator(DataUpdateCoordinator):
class NukiCoordinator(DataUpdateCoordinator[None]):
"""Data Update Coordinator for the Nuki integration."""
def __init__(self, hass, bridge, locks, openers):
@ -217,3 +191,32 @@ class NukiCoordinator(DataUpdateCoordinator):
break
return events
class NukiEntity(CoordinatorEntity[NukiCoordinator], Generic[_NukiDeviceT]):
"""An entity using CoordinatorEntity.
The CoordinatorEntity class provides:
should_poll
async_update
async_added_to_hass
available
"""
def __init__(self, coordinator: NukiCoordinator, nuki_device: _NukiDeviceT) -> None:
"""Pass coordinator to CoordinatorEntity."""
super().__init__(coordinator)
self._nuki_device = nuki_device
@property
def device_info(self):
"""Device info for Nuki entities."""
return {
"identifiers": {(DOMAIN, parse_id(self._nuki_device.nuki_id))},
"name": self._nuki_device.name,
"manufacturer": "Nuki Home Solutions GmbH",
"model": self._nuki_device.device_type_str.capitalize(),
"sw_version": self._nuki_device.firmware_version,
"via_device": (DOMAIN, self.coordinator.bridge_id),
}

View File

@ -1,6 +1,8 @@
"""Doorsensor Support for the Nuki Lock."""
from __future__ import annotations
from pynuki.constants import STATE_DOORSENSOR_OPENED
from pynuki.device import NukiDevice
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
@ -9,9 +11,8 @@ from homeassistant.components.binary_sensor import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import NukiEntity
from . import NukiCoordinator, NukiEntity
from .const import ATTR_NUKI_ID, DATA_COORDINATOR, DATA_LOCKS, DOMAIN as NUKI_DOMAIN
@ -20,7 +21,7 @@ async def async_setup_entry(
) -> None:
"""Set up the Nuki lock binary sensor."""
data = hass.data[NUKI_DOMAIN][entry.entry_id]
coordinator: DataUpdateCoordinator[None] = data[DATA_COORDINATOR]
coordinator: NukiCoordinator = data[DATA_COORDINATOR]
entities = []
@ -31,7 +32,7 @@ async def async_setup_entry(
async_add_entities(entities)
class NukiDoorsensorEntity(NukiEntity, BinarySensorEntity):
class NukiDoorsensorEntity(NukiEntity[NukiDevice], BinarySensorEntity):
"""Representation of a Nuki Lock Doorsensor."""
_attr_has_entity_name = True

View File

@ -1,11 +1,12 @@
"""Nuki.io lock platform."""
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any
from abc import abstractmethod
from typing import Any, TypeVar
from pynuki import NukiLock, NukiOpener
from pynuki.constants import MODE_OPENER_CONTINUOUS
from pynuki.device import NukiDevice
from requests.exceptions import RequestException
import voluptuous as vol
@ -14,9 +15,8 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import NukiEntity
from . import NukiCoordinator, NukiEntity
from .const import (
ATTR_BATTERY_CRITICAL,
ATTR_ENABLE,
@ -30,13 +30,15 @@ from .const import (
)
from .helpers import CannotConnect
_NukiDeviceT = TypeVar("_NukiDeviceT", bound=NukiDevice)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the Nuki lock platform."""
data = hass.data[NUKI_DOMAIN][entry.entry_id]
coordinator: DataUpdateCoordinator[None] = data[DATA_COORDINATOR]
coordinator: NukiCoordinator = data[DATA_COORDINATOR]
entities: list[NukiDeviceEntity] = [
NukiLockEntity(coordinator, lock) for lock in data[DATA_LOCKS]
@ -64,7 +66,7 @@ async def async_setup_entry(
)
class NukiDeviceEntity(NukiEntity, LockEntity, ABC):
class NukiDeviceEntity(NukiEntity[_NukiDeviceT], LockEntity):
"""Representation of a Nuki device."""
_attr_has_entity_name = True
@ -101,11 +103,9 @@ class NukiDeviceEntity(NukiEntity, LockEntity, ABC):
"""Open the door latch."""
class NukiLockEntity(NukiDeviceEntity):
class NukiLockEntity(NukiDeviceEntity[NukiLock]):
"""Representation of a Nuki lock."""
_nuki_device: NukiLock
@property
def is_locked(self) -> bool:
"""Return true if lock is locked."""
@ -144,11 +144,9 @@ class NukiLockEntity(NukiDeviceEntity):
raise CannotConnect from err
class NukiOpenerEntity(NukiDeviceEntity):
class NukiOpenerEntity(NukiDeviceEntity[NukiOpener]):
"""Representation of a Nuki opener."""
_nuki_device: NukiOpener
@property
def is_locked(self) -> bool:
"""Return true if either ring-to-open or continuous mode is enabled."""

View File

@ -1,4 +1,7 @@
"""Battery sensor for the Nuki Lock."""
from __future__ import annotations
from pynuki.device import NukiDevice
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.config_entries import ConfigEntry
@ -23,7 +26,7 @@ async def async_setup_entry(
)
class NukiBatterySensor(NukiEntity, SensorEntity):
class NukiBatterySensor(NukiEntity[NukiDevice], SensorEntity):
"""Representation of a Nuki Lock Battery sensor."""
_attr_has_entity_name = True