Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions homeassistant/components/reolink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from dataclasses import dataclass
from datetime import timedelta
import logging
from typing import Literal

from aiohttp import ClientConnectorError
import async_timeout
Expand Down Expand Up @@ -43,8 +44,8 @@ class ReolinkData:
"""Data for the Reolink integration."""

host: ReolinkHost
device_coordinator: DataUpdateCoordinator
firmware_coordinator: DataUpdateCoordinator
device_coordinator: DataUpdateCoordinator[None]
firmware_coordinator: DataUpdateCoordinator[str | Literal[False]]


async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
Expand Down Expand Up @@ -74,7 +75,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, host.stop)
)

async def async_device_config_update():
async def async_device_config_update() -> None:
"""Update the host state cache and renew the ONVIF-subscription."""
async with async_timeout.timeout(host.api.timeout):
try:
Expand All @@ -87,7 +88,7 @@ async def async_device_config_update():
async with async_timeout.timeout(host.api.timeout):
await host.renew()

async def async_check_firmware_update():
async def async_check_firmware_update() -> str | Literal[False]:
"""Check for firmware updates."""
if not host.api.supported(None, "update"):
return False
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/reolink/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkCoordinatorEntity
from .entity import ReolinkChannelCoordinatorEntity


@dataclass
Expand Down Expand Up @@ -113,7 +113,7 @@ async def async_setup_entry(
async_add_entities(entities)


class ReolinkBinarySensorEntity(ReolinkCoordinatorEntity, BinarySensorEntity):
class ReolinkBinarySensorEntity(ReolinkChannelCoordinatorEntity, BinarySensorEntity):
"""Base binary-sensor class for Reolink IP camera motion sensors."""

entity_description: ReolinkBinarySensorEntityDescription
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/reolink/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkCoordinatorEntity
from .entity import ReolinkChannelCoordinatorEntity


@dataclass
Expand Down Expand Up @@ -112,7 +112,7 @@ async def async_setup_entry(
)


class ReolinkButtonEntity(ReolinkCoordinatorEntity, ButtonEntity):
class ReolinkButtonEntity(ReolinkChannelCoordinatorEntity, ButtonEntity):
"""Base button entity class for Reolink IP cameras."""

entity_description: ReolinkButtonEntityDescription
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/reolink/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkCoordinatorEntity
from .entity import ReolinkChannelCoordinatorEntity

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -39,7 +39,7 @@ async def async_setup_entry(
async_add_entities(cameras)


class ReolinkCamera(ReolinkCoordinatorEntity, Camera):
class ReolinkCamera(ReolinkChannelCoordinatorEntity, Camera):
"""An implementation of a Reolink IP camera."""

_attr_supported_features: CameraEntityFeature = CameraEntityFeature.STREAM
Expand All @@ -51,7 +51,7 @@ def __init__(
stream: str,
) -> None:
"""Initialize Reolink camera stream."""
ReolinkCoordinatorEntity.__init__(self, reolink_data, channel)
ReolinkChannelCoordinatorEntity.__init__(self, reolink_data, channel)
Camera.__init__(self)

self._stream = stream
Expand Down
35 changes: 22 additions & 13 deletions homeassistant/components/reolink/entity.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Reolink parent entity class."""
from __future__ import annotations

from typing import TypeVar

from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import (
Expand All @@ -11,24 +13,20 @@
from . import ReolinkData
from .const import DOMAIN

_T = TypeVar("_T")

class ReolinkBaseCoordinatorEntity(CoordinatorEntity):
"""Parent class for entities that control the Reolink NVR itself, without a channel.

A camera connected directly to HomeAssistant without using a NVR is in the reolink API
basically a NVR with a single channel that has the camera connected to that channel.
"""
class ReolinkBaseCoordinatorEntity(CoordinatorEntity[DataUpdateCoordinator[_T]]):
"""Parent class fo Reolink entities."""

_attr_has_entity_name = True

def __init__(
self,
reolink_data: ReolinkData,
coordinator: DataUpdateCoordinator | None = None,
coordinator: DataUpdateCoordinator[_T],
) -> None:
"""Initialize ReolinkBaseCoordinatorEntity for a NVR entity without a channel."""
if coordinator is None:
coordinator = reolink_data.device_coordinator
Comment thread
cdce8p marked this conversation as resolved.
Outdated
"""Initialize ReolinkBaseCoordinatorEntity."""
super().__init__(coordinator)

self._host = reolink_data.host
Expand All @@ -52,17 +50,28 @@ def available(self) -> bool:
return self._host.api.session_active and super().available


class ReolinkCoordinatorEntity(ReolinkBaseCoordinatorEntity):
class ReolinkHostCoordinatorEntity(ReolinkBaseCoordinatorEntity[None]):
"""Parent class for entities that control the Reolink NVR itself, without a channel.

A camera connected directly to HomeAssistant without using a NVR is in the reolink API
basically a NVR with a single channel that has the camera connected to that channel.
"""

def __init__(self, reolink_data: ReolinkData) -> None:
"""Initialize ReolinkHostCoordinatorEntity."""
super().__init__(reolink_data, reolink_data.device_coordinator)


class ReolinkChannelCoordinatorEntity(ReolinkHostCoordinatorEntity):
"""Parent class for Reolink hardware camera entities connected to a channel of the NVR."""

def __init__(
self,
reolink_data: ReolinkData,
channel: int,
coordinator: DataUpdateCoordinator | None = None,
Comment thread
cdce8p marked this conversation as resolved.
Outdated
) -> None:
"""Initialize ReolinkCoordinatorEntity for a hardware camera connected to a channel of the NVR."""
super().__init__(reolink_data, coordinator)
"""Initialize ReolinkChannelCoordinatorEntity for a hardware camera connected to a channel of the NVR."""
super().__init__(reolink_data)

self._channel = channel

Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/reolink/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkCoordinatorEntity
from .entity import ReolinkChannelCoordinatorEntity


@dataclass
Expand Down Expand Up @@ -89,7 +89,7 @@ async def async_setup_entry(
)


class ReolinkLightEntity(ReolinkCoordinatorEntity, LightEntity):
class ReolinkLightEntity(ReolinkChannelCoordinatorEntity, LightEntity):
"""Base light entity class for Reolink IP cameras."""

entity_description: ReolinkLightEntityDescription
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/reolink/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkCoordinatorEntity
from .entity import ReolinkChannelCoordinatorEntity


@dataclass
Expand Down Expand Up @@ -191,7 +191,7 @@ async def async_setup_entry(
)


class ReolinkNumberEntity(ReolinkCoordinatorEntity, NumberEntity):
class ReolinkNumberEntity(ReolinkChannelCoordinatorEntity, NumberEntity):
"""Base number entity class for Reolink IP cameras."""

entity_description: ReolinkNumberEntityDescription
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/reolink/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkCoordinatorEntity
from .entity import ReolinkChannelCoordinatorEntity


@dataclass
Expand Down Expand Up @@ -86,7 +86,7 @@ async def async_setup_entry(
)


class ReolinkSelectEntity(ReolinkCoordinatorEntity, SelectEntity):
class ReolinkSelectEntity(ReolinkChannelCoordinatorEntity, SelectEntity):
"""Base select entity class for Reolink IP cameras."""

entity_description: ReolinkSelectEntityDescription
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/reolink/siren.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkCoordinatorEntity
from .entity import ReolinkChannelCoordinatorEntity


@dataclass
Expand Down Expand Up @@ -56,7 +56,7 @@ async def async_setup_entry(
)


class ReolinkSirenEntity(ReolinkCoordinatorEntity, SirenEntity):
class ReolinkSirenEntity(ReolinkChannelCoordinatorEntity, SirenEntity):
"""Base siren entity class for Reolink IP cameras."""

_attr_supported_features = (
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/reolink/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkBaseCoordinatorEntity, ReolinkCoordinatorEntity
from .entity import ReolinkChannelCoordinatorEntity, ReolinkHostCoordinatorEntity


@dataclass
Expand Down Expand Up @@ -172,7 +172,7 @@ async def async_setup_entry(
async_add_entities(entities)


class ReolinkSwitchEntity(ReolinkCoordinatorEntity, SwitchEntity):
class ReolinkSwitchEntity(ReolinkChannelCoordinatorEntity, SwitchEntity):
"""Base switch entity class for Reolink IP cameras."""

entity_description: ReolinkSwitchEntityDescription
Expand Down Expand Up @@ -207,7 +207,7 @@ async def async_turn_off(self, **kwargs: Any) -> None:
self.async_write_ha_state()


class ReolinkNVRSwitchEntity(ReolinkBaseCoordinatorEntity, SwitchEntity):
class ReolinkNVRSwitchEntity(ReolinkHostCoordinatorEntity, SwitchEntity):
"""Switch entity class for Reolink NVR features."""

entity_description: ReolinkNVRSwitchEntityDescription
Expand Down
9 changes: 4 additions & 5 deletions homeassistant/components/reolink/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import annotations

import logging
from typing import Any
from typing import Any, Literal

from reolink_aio.exceptions import ReolinkError

Expand Down Expand Up @@ -34,7 +34,9 @@ async def async_setup_entry(
async_add_entities([ReolinkUpdateEntity(reolink_data)])


class ReolinkUpdateEntity(ReolinkBaseCoordinatorEntity, UpdateEntity):
class ReolinkUpdateEntity(
ReolinkBaseCoordinatorEntity[str | Literal[False]], UpdateEntity
):
"""Update entity for a Netgear device."""

_attr_device_class = UpdateDeviceClass.FIRMWARE
Expand All @@ -59,9 +61,6 @@ def installed_version(self) -> str | None:
@property
def latest_version(self) -> str | None:
"""Latest version available for install."""
if self.coordinator.data is None:
return None

Comment on lines 62 to 64
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the check for new firmware fails, it will raise either a TimeoutError or UpdateFailed. Both will set last_updaate_success = False which in turn will set the available property to False. So this shouldn't be necessary.

async with async_timeout.timeout(host.api.timeout):
try:
return await host.api.check_new_firmware()
except ReolinkError as err:
raise UpdateFailed(
f"Error checking Reolink firmware update {host.api.nvr_name}"
) from err

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, this can indeed be removed.

if not self.coordinator.data:
return self.installed_version

Expand Down