From 0028aa89c8b5517f22772a9dd94c2e17658e0ba3 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 10 Feb 2023 23:33:44 +0100 Subject: [PATCH 01/13] Reolink host firmware update --- .coveragerc | 1 + homeassistant/components/reolink/__init__.py | 33 +++++++-- homeassistant/components/reolink/entity.py | 55 +++++++++------ homeassistant/components/reolink/update.py | 73 ++++++++++++++++++++ 4 files changed, 133 insertions(+), 29 deletions(-) create mode 100644 homeassistant/components/reolink/update.py diff --git a/.coveragerc b/.coveragerc index 771333c1c32558..ced9e114f1c867 100644 --- a/.coveragerc +++ b/.coveragerc @@ -967,6 +967,7 @@ omit = homeassistant/components/reolink/entity.py homeassistant/components/reolink/host.py homeassistant/components/reolink/number.py + homeassistant/components/reolink/update.py homeassistant/components/repetier/__init__.py homeassistant/components/repetier/sensor.py homeassistant/components/rest/notify.py diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py index 0ff0861f65f2ff..86300b8b79c86f 100644 --- a/homeassistant/components/reolink/__init__.py +++ b/homeassistant/components/reolink/__init__.py @@ -23,8 +23,9 @@ _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.BINARY_SENSOR, Platform.CAMERA, Platform.NUMBER] -DEVICE_UPDATE_INTERVAL = 60 +PLATFORMS = [Platform.BINARY_SENSOR, Platform.CAMERA, Platform.NUMBER, Platform.UPDATE] +DEVICE_UPDATE_INTERVAL = timedelta(seconds=60) +FIRMWARE_UPDATE_INTERVAL = timedelta(hours=12) @dataclass @@ -33,6 +34,7 @@ class ReolinkData: host: ReolinkHost device_coordinator: DataUpdateCoordinator + firmware_coordinator: DataUpdateCoordinator async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: @@ -75,23 +77,42 @@ async def async_device_config_update(): async with async_timeout.timeout(host.api.timeout): await host.renew() - coordinator_device_config_update = DataUpdateCoordinator( + async def async_check_firmware_update(): + """Check for firmware updates.""" + 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 + + device_coordinator = DataUpdateCoordinator( hass, _LOGGER, name=f"reolink.{host.api.nvr_name}", update_method=async_device_config_update, - update_interval=timedelta(seconds=DEVICE_UPDATE_INTERVAL), + update_interval=DEVICE_UPDATE_INTERVAL, + ) + firmware_coordinator = DataUpdateCoordinator( + hass, + _LOGGER, + name=f"reolink.{host.api.nvr_name}.firmware", + update_method=async_device_config_update, + update_interval=FIRMWARE_UPDATE_INTERVAL, ) # Fetch initial data so we have data when entities subscribe try: - await coordinator_device_config_update.async_config_entry_first_refresh() + await device_coordinator.async_config_entry_first_refresh() + await firmware_coordinator.async_config_entry_first_refresh() except ConfigEntryNotReady: await host.stop() raise hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = ReolinkData( host=host, - device_coordinator=coordinator_device_config_update, + device_coordinator=device_coordinator, + firmware_coordinator=firmware_coordinator, ) await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) diff --git a/homeassistant/components/reolink/entity.py b/homeassistant/components/reolink/entity.py index bcf39814c9a3c6..f91d835f1329ef 100644 --- a/homeassistant/components/reolink/entity.py +++ b/homeassistant/components/reolink/entity.py @@ -3,25 +3,50 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.helpers.update_coordinator import CoordinatorEntity, DataUpdateCoordinator from . import ReolinkData from .const import DOMAIN -class ReolinkCoordinatorEntity(CoordinatorEntity): +class ReolinkBaseCoordinatorEntity(CoordinatorEntity): """Parent class for Reolink hardware camera entities.""" - def __init__(self, reolink_data: ReolinkData, channel: int) -> None: + def __init__(self, reolink_data: ReolinkData, coordinator: DataUpdateCoordinator) -> None: """Initialize ReolinkCoordinatorEntity for a hardware camera.""" - coordinator = reolink_data.device_coordinator super().__init__(coordinator) self._host = reolink_data.host - self._channel = channel http_s = "https" if self._host.api.use_https else "http" - conf_url = f"{http_s}://{self._host.api.host}:{self._host.api.port}" + self._conf_url = f"{http_s}://{self._host.api.host}:{self._host.api.port}" + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, self._host.unique_id)}, + connections={(CONNECTION_NETWORK_MAC, self._host.api.mac_address)}, + name=self._host.api.nvr_name, + model=self._host.api.model, + manufacturer=self._host.api.manufacturer, + hw_version=self._host.api.hardware_version, + sw_version=self._host.api.sw_version, + configuration_url=conf_url, + ) + + @property + def available(self) -> bool: + """Return True if entity is available.""" + return self._host.api.session_active and super().available + + +class ReolinkCoordinatorEntity(ReolinkBaseCoordinatorEntity): + """Parent class for Reolink hardware camera entities.""" + + def __init__(self, reolink_data: ReolinkData, channel: int) -> None: + """Initialize ReolinkCoordinatorEntity for a hardware camera.""" + coordinator = reolink_data.device_coordinator + super().__init__(reolink_data, coordinator) + + self._channel = channel + if self._host.api.is_nvr: self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, f"{self._host.unique_id}_ch{self._channel}")}, @@ -29,21 +54,5 @@ def __init__(self, reolink_data: ReolinkData, channel: int) -> None: name=self._host.api.camera_name(self._channel), model=self._host.api.camera_model(self._channel), manufacturer=self._host.api.manufacturer, - configuration_url=conf_url, + configuration_url=self._conf_url, ) - else: - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, self._host.unique_id)}, - connections={(CONNECTION_NETWORK_MAC, self._host.api.mac_address)}, - name=self._host.api.nvr_name, - model=self._host.api.model, - manufacturer=self._host.api.manufacturer, - hw_version=self._host.api.hardware_version, - sw_version=self._host.api.sw_version, - configuration_url=conf_url, - ) - - @property - def available(self) -> bool: - """Return True if entity is available.""" - return self._host.api.session_active and super().available diff --git a/homeassistant/components/reolink/update.py b/homeassistant/components/reolink/update.py new file mode 100644 index 00000000000000..198aa9a2cbb6d0 --- /dev/null +++ b/homeassistant/components/reolink/update.py @@ -0,0 +1,73 @@ +"""Update entities for Reolink devices.""" +from __future__ import annotations + +import logging +from typing import Any + +from homeassistant.components.update import ( + UpdateDeviceClass, + UpdateEntity, + UpdateEntityFeature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from . import ReolinkData +from .const import DOMAIN +from .entity import ReolinkBaseCoordinatorEntity + +LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up update entities for Reolink component.""" + reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] + async_add_entities([ReolinkUpdateEntity(reolink_data)]) + + +class ReolinkUpdateEntity(ReolinkBaseCoordinatorEntity, UpdateEntity): + """Update entity for a Netgear device.""" + + _attr_has_entity_name = True + _attr_device_class = UpdateDeviceClass.FIRMWARE + _attr_supported_features = UpdateEntityFeature.INSTALL + + def __init__( + self, + reolink_data: ReolinkData, + ) -> None: + """Initialize a Netgear device.""" + super().__init__(reolink_data, reolink_data.firmware_coordinator) + + self._attr_name = "Update" + self._attr_unique_id = f"{self._host.unique_id}_update" + + @property + def installed_version(self) -> str | None: + """Version currently in use.""" + return self._host.api.sw_version + + @property + def latest_version(self) -> str | None: + """Latest version available for install.""" + if not self.coordinator.data: + return self.installed_version + + return self.coordinator.data + + @property + def release_url(self) -> str: + """Reolink firmware dowload page.""" + return "https://reolink.com/download-center/" + + async def async_install( + self, version: str | None, backup: bool, **kwargs: Any + ) -> None: + """Install the latest firmware version.""" + await self._host.api.update_firmware() From 7a6e252e26df8c4b6e3213c9d2980f70cc6d5106 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 10 Feb 2023 23:40:38 +0100 Subject: [PATCH 02/13] styling --- homeassistant/components/reolink/__init__.py | 2 +- homeassistant/components/reolink/entity.py | 11 ++++++++--- homeassistant/components/reolink/update.py | 5 ++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py index 86300b8b79c86f..10b29469b500b1 100644 --- a/homeassistant/components/reolink/__init__.py +++ b/homeassistant/components/reolink/__init__.py @@ -98,7 +98,7 @@ async def async_check_firmware_update(): hass, _LOGGER, name=f"reolink.{host.api.nvr_name}.firmware", - update_method=async_device_config_update, + update_method=async_check_firmware_update, update_interval=FIRMWARE_UPDATE_INTERVAL, ) # Fetch initial data so we have data when entities subscribe diff --git a/homeassistant/components/reolink/entity.py b/homeassistant/components/reolink/entity.py index f91d835f1329ef..2743d10d8b949b 100644 --- a/homeassistant/components/reolink/entity.py +++ b/homeassistant/components/reolink/entity.py @@ -3,7 +3,10 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.update_coordinator import CoordinatorEntity, DataUpdateCoordinator +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, +) from . import ReolinkData from .const import DOMAIN @@ -12,7 +15,9 @@ class ReolinkBaseCoordinatorEntity(CoordinatorEntity): """Parent class for Reolink hardware camera entities.""" - def __init__(self, reolink_data: ReolinkData, coordinator: DataUpdateCoordinator) -> None: + def __init__( + self, reolink_data: ReolinkData, coordinator: DataUpdateCoordinator + ) -> None: """Initialize ReolinkCoordinatorEntity for a hardware camera.""" super().__init__(coordinator) @@ -28,7 +33,7 @@ def __init__(self, reolink_data: ReolinkData, coordinator: DataUpdateCoordinator manufacturer=self._host.api.manufacturer, hw_version=self._host.api.hardware_version, sw_version=self._host.api.sw_version, - configuration_url=conf_url, + configuration_url=self._conf_url, ) @property diff --git a/homeassistant/components/reolink/update.py b/homeassistant/components/reolink/update.py index 198aa9a2cbb6d0..2a48629b5c4bb4 100644 --- a/homeassistant/components/reolink/update.py +++ b/homeassistant/components/reolink/update.py @@ -10,9 +10,8 @@ UpdateEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from . import ReolinkData from .const import DOMAIN @@ -63,7 +62,7 @@ def latest_version(self) -> str | None: @property def release_url(self) -> str: - """Reolink firmware dowload page.""" + """Reolink firmware download page.""" return "https://reolink.com/download-center/" async def async_install( From 3b9f1b1ba93118d06cbf5eada5c3e96f1784bf02 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 10 Feb 2023 23:42:46 +0100 Subject: [PATCH 03/13] fix tests --- tests/components/reolink/test_config_flow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/components/reolink/test_config_flow.py b/tests/components/reolink/test_config_flow.py index 10391c320677b3..a5de5d5acb819f 100644 --- a/tests/components/reolink/test_config_flow.py +++ b/tests/components/reolink/test_config_flow.py @@ -36,6 +36,7 @@ def get_mock_info(error=None, user_level="admin"): host_mock.get_host_data = AsyncMock(return_value=None) else: host_mock.get_host_data = AsyncMock(side_effect=error) + host_mock.check_new_firmware = AsyncMock(return_value=False) host_mock.unsubscribe = AsyncMock(return_value=True) host_mock.logout = AsyncMock(return_value=True) host_mock.mac_address = TEST_MAC From 58d475226e47e4c1190f0d5e494303b1eaa56232 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Sun, 12 Feb 2023 12:43:49 +0100 Subject: [PATCH 04/13] Move _attr_has_entity_name to the base reolink entity class --- homeassistant/components/reolink/binary_sensor.py | 1 - homeassistant/components/reolink/camera.py | 1 - homeassistant/components/reolink/entity.py | 2 ++ homeassistant/components/reolink/number.py | 1 - homeassistant/components/reolink/update.py | 1 - 5 files changed, 2 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/reolink/binary_sensor.py b/homeassistant/components/reolink/binary_sensor.py index 5e7718f4180d0b..541ad9ec998955 100644 --- a/homeassistant/components/reolink/binary_sensor.py +++ b/homeassistant/components/reolink/binary_sensor.py @@ -116,7 +116,6 @@ async def async_setup_entry( class ReolinkBinarySensorEntity(ReolinkCoordinatorEntity, BinarySensorEntity): """Base binary-sensor class for Reolink IP camera motion sensors.""" - _attr_has_entity_name = True entity_description: ReolinkBinarySensorEntityDescription def __init__( diff --git a/homeassistant/components/reolink/camera.py b/homeassistant/components/reolink/camera.py index 5ccada7269d349..d14906a578204c 100644 --- a/homeassistant/components/reolink/camera.py +++ b/homeassistant/components/reolink/camera.py @@ -43,7 +43,6 @@ class ReolinkCamera(ReolinkCoordinatorEntity, Camera): """An implementation of a Reolink IP camera.""" _attr_supported_features: CameraEntityFeature = CameraEntityFeature.STREAM - _attr_has_entity_name = True def __init__( self, diff --git a/homeassistant/components/reolink/entity.py b/homeassistant/components/reolink/entity.py index 2743d10d8b949b..66c1f2f7736337 100644 --- a/homeassistant/components/reolink/entity.py +++ b/homeassistant/components/reolink/entity.py @@ -15,6 +15,8 @@ class ReolinkBaseCoordinatorEntity(CoordinatorEntity): """Parent class for Reolink hardware camera entities.""" + _attr_has_entity_name = True + def __init__( self, reolink_data: ReolinkData, coordinator: DataUpdateCoordinator ) -> None: diff --git a/homeassistant/components/reolink/number.py b/homeassistant/components/reolink/number.py index 3f8860876ae098..e9b692fffe6153 100644 --- a/homeassistant/components/reolink/number.py +++ b/homeassistant/components/reolink/number.py @@ -88,7 +88,6 @@ async def async_setup_entry( class ReolinkNumberEntity(ReolinkCoordinatorEntity, NumberEntity): """Base number entity class for Reolink IP cameras.""" - _attr_has_entity_name = True entity_description: ReolinkNumberEntityDescription def __init__( diff --git a/homeassistant/components/reolink/update.py b/homeassistant/components/reolink/update.py index 2a48629b5c4bb4..16490122b785ed 100644 --- a/homeassistant/components/reolink/update.py +++ b/homeassistant/components/reolink/update.py @@ -33,7 +33,6 @@ async def async_setup_entry( class ReolinkUpdateEntity(ReolinkBaseCoordinatorEntity, UpdateEntity): """Update entity for a Netgear device.""" - _attr_has_entity_name = True _attr_device_class = UpdateDeviceClass.FIRMWARE _attr_supported_features = UpdateEntityFeature.INSTALL From 84d730928c736578411891bb799ef652aeed6dfa Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 13 Feb 2023 10:34:42 +0100 Subject: [PATCH 05/13] use asyncio gather --- homeassistant/components/reolink/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py index 10b29469b500b1..b05bc3836becc4 100644 --- a/homeassistant/components/reolink/__init__.py +++ b/homeassistant/components/reolink/__init__.py @@ -103,8 +103,10 @@ async def async_check_firmware_update(): ) # Fetch initial data so we have data when entities subscribe try: - await device_coordinator.async_config_entry_first_refresh() - await firmware_coordinator.async_config_entry_first_refresh() + await asyncio.gather( + device_coordinator.async_config_entry_first_refresh() + firmware_coordinator.async_config_entry_first_refresh() + ) except ConfigEntryNotReady: await host.stop() raise From bb4bb660744099db7fba6aea3efbac0afb9d547e Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 13 Feb 2023 10:36:31 +0100 Subject: [PATCH 06/13] move static --- homeassistant/components/reolink/update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/reolink/update.py b/homeassistant/components/reolink/update.py index 16490122b785ed..a279f2c1685016 100644 --- a/homeassistant/components/reolink/update.py +++ b/homeassistant/components/reolink/update.py @@ -35,6 +35,7 @@ class ReolinkUpdateEntity(ReolinkBaseCoordinatorEntity, UpdateEntity): _attr_device_class = UpdateDeviceClass.FIRMWARE _attr_supported_features = UpdateEntityFeature.INSTALL + _attr_name = "Update" def __init__( self, @@ -43,7 +44,6 @@ def __init__( """Initialize a Netgear device.""" super().__init__(reolink_data, reolink_data.firmware_coordinator) - self._attr_name = "Update" self._attr_unique_id = f"{self._host.unique_id}_update" @property From de2091f5d96a33e73812c071e570c9eb0577baca Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 13 Feb 2023 10:45:41 +0100 Subject: [PATCH 07/13] Update update.py --- homeassistant/components/reolink/update.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/reolink/update.py b/homeassistant/components/reolink/update.py index a279f2c1685016..c9024cb678ed6e 100644 --- a/homeassistant/components/reolink/update.py +++ b/homeassistant/components/reolink/update.py @@ -35,6 +35,7 @@ class ReolinkUpdateEntity(ReolinkBaseCoordinatorEntity, UpdateEntity): _attr_device_class = UpdateDeviceClass.FIRMWARE _attr_supported_features = UpdateEntityFeature.INSTALL + _attr_release_url = "https://reolink.com/download-center/" _attr_name = "Update" def __init__( @@ -54,16 +55,14 @@ 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 + if not self.coordinator.data: return self.installed_version return self.coordinator.data - @property - def release_url(self) -> str: - """Reolink firmware download page.""" - return "https://reolink.com/download-center/" - async def async_install( self, version: str | None, backup: bool, **kwargs: Any ) -> None: From 0b091fcf077b17b6d05e7187aaeb2a5c27841958 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 13 Feb 2023 11:02:58 +0100 Subject: [PATCH 08/13] Update docstrings --- homeassistant/components/reolink/entity.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/reolink/entity.py b/homeassistant/components/reolink/entity.py index 66c1f2f7736337..fb689713a3863c 100644 --- a/homeassistant/components/reolink/entity.py +++ b/homeassistant/components/reolink/entity.py @@ -13,14 +13,16 @@ class ReolinkBaseCoordinatorEntity(CoordinatorEntity): - """Parent class for Reolink hardware camera entities.""" + """Parent class for entities that control the Reolink NVR itself, withouth a channel.""" _attr_has_entity_name = True def __init__( - self, reolink_data: ReolinkData, coordinator: DataUpdateCoordinator + self, reolink_data: ReolinkData, coordinator: DataUpdateCoordinator | None = None ) -> None: - """Initialize ReolinkCoordinatorEntity for a hardware camera.""" + """Initialize ReolinkBaseCoordinatorEntity for a NVR entity withouth a channel.""" + if coordinator is None: + coordinator = reolink_data.device_coordinator super().__init__(coordinator) self._host = reolink_data.host @@ -45,11 +47,10 @@ def available(self) -> bool: class ReolinkCoordinatorEntity(ReolinkBaseCoordinatorEntity): - """Parent class for Reolink hardware camera entities.""" + """Parent class for Reolink hardware camera entities connected to a channel.""" - def __init__(self, reolink_data: ReolinkData, channel: int) -> None: - """Initialize ReolinkCoordinatorEntity for a hardware camera.""" - coordinator = reolink_data.device_coordinator + def __init__(self, reolink_data: ReolinkData, channel: int, coordinator: DataUpdateCoordinator | None = None) -> None: + """Initialize ReolinkCoordinatorEntity for a hardware camera connected to a channel.""" super().__init__(reolink_data, coordinator) self._channel = channel From 391e3677d42671fc0e50db7d5c6174c877fd11f7 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 13 Feb 2023 11:14:11 +0100 Subject: [PATCH 09/13] add extra docstring explanation --- homeassistant/components/reolink/entity.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/reolink/entity.py b/homeassistant/components/reolink/entity.py index fb689713a3863c..a32c248e4b2cb7 100644 --- a/homeassistant/components/reolink/entity.py +++ b/homeassistant/components/reolink/entity.py @@ -13,7 +13,12 @@ class ReolinkBaseCoordinatorEntity(CoordinatorEntity): - """Parent class for entities that control the Reolink NVR itself, withouth a channel.""" + """ + Parent class for entities that control the Reolink NVR itself, withouth a channel. + + A camera connected directly to HomeAssistant withouth using a NVR is in the reolink API + basically a NVR with a single channel that has the camera connected to that channel. + """ _attr_has_entity_name = True @@ -47,10 +52,10 @@ def available(self) -> bool: class ReolinkCoordinatorEntity(ReolinkBaseCoordinatorEntity): - """Parent class for Reolink hardware camera entities connected to a channel.""" + """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) -> None: - """Initialize ReolinkCoordinatorEntity for a hardware camera connected to a channel.""" + """Initialize ReolinkCoordinatorEntity for a hardware camera connected to a channel of the NVR.""" super().__init__(reolink_data, coordinator) self._channel = channel From 08eef57563d26374706aac36a02c17987399a2fd Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 13 Feb 2023 11:23:07 +0100 Subject: [PATCH 10/13] fix styling --- homeassistant/components/reolink/__init__.py | 4 ++-- homeassistant/components/reolink/entity.py | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py index b05bc3836becc4..2d3fc52eb30a97 100644 --- a/homeassistant/components/reolink/__init__.py +++ b/homeassistant/components/reolink/__init__.py @@ -104,8 +104,8 @@ async def async_check_firmware_update(): # Fetch initial data so we have data when entities subscribe try: await asyncio.gather( - device_coordinator.async_config_entry_first_refresh() - firmware_coordinator.async_config_entry_first_refresh() + device_coordinator.async_config_entry_first_refresh(), + firmware_coordinator.async_config_entry_first_refresh(), ) except ConfigEntryNotReady: await host.stop() diff --git a/homeassistant/components/reolink/entity.py b/homeassistant/components/reolink/entity.py index a32c248e4b2cb7..5f983ab3494598 100644 --- a/homeassistant/components/reolink/entity.py +++ b/homeassistant/components/reolink/entity.py @@ -13,19 +13,20 @@ class ReolinkBaseCoordinatorEntity(CoordinatorEntity): - """ - Parent class for entities that control the Reolink NVR itself, withouth a channel. - - A camera connected directly to HomeAssistant withouth using a NVR is in the reolink API + """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. """ _attr_has_entity_name = True def __init__( - self, reolink_data: ReolinkData, coordinator: DataUpdateCoordinator | None = None + self, + reolink_data: ReolinkData, + coordinator: DataUpdateCoordinator | None = None, ) -> None: - """Initialize ReolinkBaseCoordinatorEntity for a NVR entity withouth a channel.""" + """Initialize ReolinkBaseCoordinatorEntity for a NVR entity without a channel.""" if coordinator is None: coordinator = reolink_data.device_coordinator super().__init__(coordinator) @@ -54,7 +55,12 @@ def available(self) -> bool: class ReolinkCoordinatorEntity(ReolinkBaseCoordinatorEntity): """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) -> None: + def __init__( + self, + reolink_data: ReolinkData, + channel: int, + coordinator: DataUpdateCoordinator | None = None, + ) -> None: """Initialize ReolinkCoordinatorEntity for a hardware camera connected to a channel of the NVR.""" super().__init__(reolink_data, coordinator) From 6ae9192a5bfc70e9c435b14d3d1d59d9f58d9997 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 13 Feb 2023 11:28:56 +0100 Subject: [PATCH 11/13] raise HomeAssistantError instead of ReolinkError --- homeassistant/components/reolink/update.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/reolink/update.py b/homeassistant/components/reolink/update.py index c9024cb678ed6e..3bfb6d76dbfd94 100644 --- a/homeassistant/components/reolink/update.py +++ b/homeassistant/components/reolink/update.py @@ -4,6 +4,8 @@ import logging from typing import Any +from reolink_aio.exceptions import ReolinkError + from homeassistant.components.update import ( UpdateDeviceClass, UpdateEntity, @@ -11,6 +13,7 @@ ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import ReolinkData @@ -67,4 +70,7 @@ async def async_install( self, version: str | None, backup: bool, **kwargs: Any ) -> None: """Install the latest firmware version.""" - await self._host.api.update_firmware() + try: + await self._host.api.update_firmware() + except ReolinkError as err: + raise HomeAssistantError("Error trying to update Reolink firmware: %s", err) from err From ef6a1d9ca80884fc17ded078b9facd6a371f129b Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 13 Feb 2023 11:33:34 +0100 Subject: [PATCH 12/13] fix styling --- homeassistant/components/reolink/update.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/reolink/update.py b/homeassistant/components/reolink/update.py index 3bfb6d76dbfd94..4840f77b855dbb 100644 --- a/homeassistant/components/reolink/update.py +++ b/homeassistant/components/reolink/update.py @@ -73,4 +73,6 @@ async def async_install( try: await self._host.api.update_firmware() except ReolinkError as err: - raise HomeAssistantError("Error trying to update Reolink firmware: %s", err) from err + raise HomeAssistantError( + "Error trying to update Reolink firmware: %s", err + ) from err From aca0d70ec4e590d6f9c3adb6a9e5dc8fdb04ca4f Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 13 Feb 2023 11:43:49 +0100 Subject: [PATCH 13/13] fix string formatting --- homeassistant/components/reolink/update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/reolink/update.py b/homeassistant/components/reolink/update.py index 4840f77b855dbb..71ca16ca68d0f7 100644 --- a/homeassistant/components/reolink/update.py +++ b/homeassistant/components/reolink/update.py @@ -74,5 +74,5 @@ async def async_install( await self._host.api.update_firmware() except ReolinkError as err: raise HomeAssistantError( - "Error trying to update Reolink firmware: %s", err + f"Error trying to update Reolink firmware: {err}" ) from err