Bump reolink-aio to 0.7.14 and improve typing of Reolink (#103129)

* Improve typing

* fix mypy

* Further improve typing

* Restore Literal typing

* Bump reolink_aio to 0.7.13

* Bump reolink-aio to 0.7.14
pull/103324/head
starkillerOG 2023-11-03 17:05:27 +01:00 committed by GitHub
parent c1d979dc07
commit 1df69f52e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 37 additions and 29 deletions

View File

@ -10,6 +10,7 @@ from typing import Literal
from reolink_aio.api import RETRY_ATTEMPTS from reolink_aio.api import RETRY_ATTEMPTS
from reolink_aio.exceptions import CredentialsInvalidError, ReolinkError from reolink_aio.exceptions import CredentialsInvalidError, ReolinkError
from reolink_aio.software_version import NewSoftwareVersion
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform
@ -45,7 +46,9 @@ class ReolinkData:
host: ReolinkHost host: ReolinkHost
device_coordinator: DataUpdateCoordinator[None] device_coordinator: DataUpdateCoordinator[None]
firmware_coordinator: DataUpdateCoordinator[str | Literal[False]] firmware_coordinator: DataUpdateCoordinator[
str | Literal[False] | NewSoftwareVersion
]
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
@ -86,7 +89,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
async with asyncio.timeout(host.api.timeout * (RETRY_ATTEMPTS + 2)): async with asyncio.timeout(host.api.timeout * (RETRY_ATTEMPTS + 2)):
await host.renew() await host.renew()
async def async_check_firmware_update() -> str | Literal[False]: async def async_check_firmware_update() -> str | Literal[
False
] | NewSoftwareVersion:
"""Check for firmware updates.""" """Check for firmware updates."""
if not host.api.supported(None, "update"): if not host.api.supported(None, "update"):
return False return False
@ -153,7 +158,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
return True return True
async def entry_update_listener(hass: HomeAssistant, config_entry: ConfigEntry): async def entry_update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
"""Update the configuration of the host entity.""" """Update the configuration of the host entity."""
await hass.config_entries.async_reload(config_entry.entry_id) await hass.config_entries.async_reload(config_entry.entry_id)

View File

@ -32,7 +32,7 @@ from .entity import ReolinkChannelCoordinatorEntity
class ReolinkBinarySensorEntityDescriptionMixin: class ReolinkBinarySensorEntityDescriptionMixin:
"""Mixin values for Reolink binary sensor entities.""" """Mixin values for Reolink binary sensor entities."""
value: Callable[[Host, int | None], bool] value: Callable[[Host, int], bool]
@dataclass @dataclass
@ -43,7 +43,7 @@ class ReolinkBinarySensorEntityDescription(
icon: str = "mdi:motion-sensor" icon: str = "mdi:motion-sensor"
icon_off: str = "mdi:motion-sensor-off" icon_off: str = "mdi:motion-sensor-off"
supported: Callable[[Host, int | None], bool] = lambda host, ch: True supported: Callable[[Host, int], bool] = lambda host, ch: True
BINARY_SENSORS = ( BINARY_SENSORS = (
@ -169,6 +169,6 @@ class ReolinkBinarySensorEntity(ReolinkChannelCoordinatorEntity, BinarySensorEnt
) )
) )
async def _async_handle_event(self, event): async def _async_handle_event(self, event: str) -> None:
"""Handle incoming event for motion detection.""" """Handle incoming event for motion detection."""
self.async_write_ha_state() self.async_write_ha_state()

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import asyncio import asyncio
from collections.abc import Mapping from collections.abc import Mapping
import logging import logging
from typing import Any from typing import Any, Literal
import aiohttp import aiohttp
from aiohttp.web import Request from aiohttp.web import Request
@ -81,7 +81,7 @@ class ReolinkHost:
return self._unique_id return self._unique_id
@property @property
def api(self): def api(self) -> Host:
"""Return the API object.""" """Return the API object."""
return self._api return self._api
@ -313,7 +313,7 @@ class ReolinkHost:
"""Call the API of the camera device to update the internal states.""" """Call the API of the camera device to update the internal states."""
await self._api.get_states() await self._api.get_states()
async def disconnect(self): async def disconnect(self) -> None:
"""Disconnect from the API, so the connection will be released.""" """Disconnect from the API, so the connection will be released."""
try: try:
await self._api.unsubscribe() await self._api.unsubscribe()
@ -335,7 +335,7 @@ class ReolinkHost:
err, err,
) )
async def _async_start_long_polling(self, initial=False): async def _async_start_long_polling(self, initial=False) -> None:
"""Start ONVIF long polling task.""" """Start ONVIF long polling task."""
if self._long_poll_task is None: if self._long_poll_task is None:
try: try:
@ -364,7 +364,7 @@ class ReolinkHost:
self._lost_subscription = False self._lost_subscription = False
self._long_poll_task = asyncio.create_task(self._async_long_polling()) self._long_poll_task = asyncio.create_task(self._async_long_polling())
async def _async_stop_long_polling(self): async def _async_stop_long_polling(self) -> None:
"""Stop ONVIF long polling task.""" """Stop ONVIF long polling task."""
if self._long_poll_task is not None: if self._long_poll_task is not None:
self._long_poll_task.cancel() self._long_poll_task.cancel()
@ -372,7 +372,7 @@ class ReolinkHost:
await self._api.unsubscribe(sub_type=SubType.long_poll) await self._api.unsubscribe(sub_type=SubType.long_poll)
async def stop(self, event=None): async def stop(self, event=None) -> None:
"""Disconnect the API.""" """Disconnect the API."""
if self._cancel_poll is not None: if self._cancel_poll is not None:
self._cancel_poll() self._cancel_poll()
@ -433,7 +433,7 @@ class ReolinkHost:
else: else:
self._lost_subscription = False self._lost_subscription = False
async def _renew(self, sub_type: SubType) -> None: async def _renew(self, sub_type: Literal[SubType.push, SubType.long_poll]) -> None:
"""Execute the renew of the subscription.""" """Execute the renew of the subscription."""
if not self._api.subscribed(sub_type): if not self._api.subscribed(sub_type):
_LOGGER.debug( _LOGGER.debug(
@ -512,8 +512,10 @@ class ReolinkHost:
_LOGGER.debug("Registered webhook: %s", event_id) _LOGGER.debug("Registered webhook: %s", event_id)
def unregister_webhook(self): def unregister_webhook(self) -> None:
"""Unregister the webhook for motion events.""" """Unregister the webhook for motion events."""
if self.webhook_id is None:
return
_LOGGER.debug("Unregistering webhook %s", self.webhook_id) _LOGGER.debug("Unregistering webhook %s", self.webhook_id)
webhook.async_unregister(self._hass, self.webhook_id) webhook.async_unregister(self._hass, self.webhook_id)
self.webhook_id = None self.webhook_id = None

View File

@ -38,8 +38,8 @@ class ReolinkLightEntityDescription(
"""A class that describes light entities.""" """A class that describes light entities."""
supported_fn: Callable[[Host, int], bool] = lambda api, ch: True supported_fn: Callable[[Host, int], bool] = lambda api, ch: True
get_brightness_fn: Callable[[Host, int], int] | None = None get_brightness_fn: Callable[[Host, int], int | None] | None = None
set_brightness_fn: Callable[[Host, int, float], Any] | None = None set_brightness_fn: Callable[[Host, int, int], Any] | None = None
LIGHT_ENTITIES = ( LIGHT_ENTITIES = (
@ -127,13 +127,13 @@ class ReolinkLightEntity(ReolinkChannelCoordinatorEntity, LightEntity):
if self.entity_description.get_brightness_fn is None: if self.entity_description.get_brightness_fn is None:
return None return None
return round( bright_pct = self.entity_description.get_brightness_fn(
255 self._host.api, self._channel
* (
self.entity_description.get_brightness_fn(self._host.api, self._channel)
/ 100.0
)
) )
if bright_pct is None:
return None
return round(255 * bright_pct / 100.0)
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn light off.""" """Turn light off."""

View File

@ -18,5 +18,5 @@
"documentation": "https://www.home-assistant.io/integrations/reolink", "documentation": "https://www.home-assistant.io/integrations/reolink",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["reolink_aio"], "loggers": ["reolink_aio"],
"requirements": ["reolink-aio==0.7.12"] "requirements": ["reolink-aio==0.7.14"]
} }

View File

@ -26,7 +26,7 @@ from .entity import ReolinkChannelCoordinatorEntity
class ReolinkNumberEntityDescriptionMixin: class ReolinkNumberEntityDescriptionMixin:
"""Mixin values for Reolink number entities.""" """Mixin values for Reolink number entities."""
value: Callable[[Host, int], float] value: Callable[[Host, int], float | None]
method: Callable[[Host, int, float], Any] method: Callable[[Host, int, float], Any]
@ -354,7 +354,7 @@ class ReolinkNumberEntity(ReolinkChannelCoordinatorEntity, NumberEntity):
) )
@property @property
def native_value(self) -> float: def native_value(self) -> float | None:
"""State of the number entity.""" """State of the number entity."""
return self.entity_description.value(self._host.api, self._channel) return self.entity_description.value(self._host.api, self._channel)

View File

@ -44,7 +44,7 @@ class ReolinkSensorEntityDescription(
class ReolinkHostSensorEntityDescriptionMixin: class ReolinkHostSensorEntityDescriptionMixin:
"""Mixin values for Reolink host sensor entities.""" """Mixin values for Reolink host sensor entities."""
value: Callable[[Host], int] value: Callable[[Host], int | None]
@dataclass @dataclass

View File

@ -35,7 +35,8 @@ async def async_setup_entry(
class ReolinkUpdateEntity( class ReolinkUpdateEntity(
ReolinkBaseCoordinatorEntity[str | Literal[False]], UpdateEntity ReolinkBaseCoordinatorEntity[str | Literal[False] | NewSoftwareVersion],
UpdateEntity,
): ):
"""Update entity for a Netgear device.""" """Update entity for a Netgear device."""

View File

@ -2322,7 +2322,7 @@ renault-api==0.2.0
renson-endura-delta==1.6.0 renson-endura-delta==1.6.0
# homeassistant.components.reolink # homeassistant.components.reolink
reolink-aio==0.7.12 reolink-aio==0.7.14
# homeassistant.components.idteck_prox # homeassistant.components.idteck_prox
rfk101py==0.0.1 rfk101py==0.0.1

View File

@ -1733,7 +1733,7 @@ renault-api==0.2.0
renson-endura-delta==1.6.0 renson-endura-delta==1.6.0
# homeassistant.components.reolink # homeassistant.components.reolink
reolink-aio==0.7.12 reolink-aio==0.7.14
# homeassistant.components.rflink # homeassistant.components.rflink
rflink==0.0.65 rflink==0.0.65