From 79fdb0d847e4ecd514e8586b7869f34355aef6fb Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Wed, 29 Jun 2022 11:43:51 +0200 Subject: [PATCH] Netgear add update entity (#72429) --- .coveragerc | 1 + homeassistant/components/netgear/__init__.py | 15 ++++ homeassistant/components/netgear/const.py | 9 ++- homeassistant/components/netgear/router.py | 10 +++ homeassistant/components/netgear/update.py | 81 ++++++++++++++++++++ 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/netgear/update.py diff --git a/.coveragerc b/.coveragerc index 981e2d06680..02c643ae757 100644 --- a/.coveragerc +++ b/.coveragerc @@ -793,6 +793,7 @@ omit = homeassistant/components/netgear/router.py homeassistant/components/netgear/sensor.py homeassistant/components/netgear/switch.py + homeassistant/components/netgear/update.py homeassistant/components/netgear_lte/* homeassistant/components/netio/switch.py homeassistant/components/neurio_energy/sensor.py diff --git a/homeassistant/components/netgear/__init__.py b/homeassistant/components/netgear/__init__.py index 6e56aef91c5..a996699ab9e 100644 --- a/homeassistant/components/netgear/__init__.py +++ b/homeassistant/components/netgear/__init__.py @@ -15,6 +15,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import ( DOMAIN, KEY_COORDINATOR, + KEY_COORDINATOR_FIRMWARE, KEY_COORDINATOR_LINK, KEY_COORDINATOR_SPEED, KEY_COORDINATOR_TRAFFIC, @@ -29,6 +30,7 @@ _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=30) SPEED_TEST_INTERVAL = timedelta(seconds=1800) +SCAN_INTERVAL_FIRMWARE = timedelta(seconds=18000) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -85,6 +87,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Fetch data from the router.""" return await router.async_get_speed_test() + async def async_check_firmware() -> dict[str, Any] | None: + """Check for new firmware of the router.""" + return await router.async_check_new_firmware() + async def async_update_utilization() -> dict[str, Any] | None: """Fetch data from the router.""" return await router.async_get_utilization() @@ -115,6 +121,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: update_method=async_update_speed_test, update_interval=SPEED_TEST_INTERVAL, ) + coordinator_firmware = DataUpdateCoordinator( + hass, + _LOGGER, + name=f"{router.device_name} Firmware", + update_method=async_check_firmware, + update_interval=SCAN_INTERVAL_FIRMWARE, + ) coordinator_utilization = DataUpdateCoordinator( hass, _LOGGER, @@ -133,6 +146,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if router.track_devices: await coordinator.async_config_entry_first_refresh() await coordinator_traffic_meter.async_config_entry_first_refresh() + await coordinator_firmware.async_config_entry_first_refresh() await coordinator_utilization.async_config_entry_first_refresh() await coordinator_link.async_config_entry_first_refresh() @@ -141,6 +155,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: KEY_COORDINATOR: coordinator, KEY_COORDINATOR_TRAFFIC: coordinator_traffic_meter, KEY_COORDINATOR_SPEED: coordinator_speed_test, + KEY_COORDINATOR_FIRMWARE: coordinator_firmware, KEY_COORDINATOR_UTIL: coordinator_utilization, KEY_COORDINATOR_LINK: coordinator_link, } diff --git a/homeassistant/components/netgear/const.py b/homeassistant/components/netgear/const.py index f9b9a93b767..eaa32362baf 100644 --- a/homeassistant/components/netgear/const.py +++ b/homeassistant/components/netgear/const.py @@ -5,7 +5,13 @@ from homeassistant.const import Platform DOMAIN = "netgear" -PLATFORMS = [Platform.BUTTON, Platform.DEVICE_TRACKER, Platform.SENSOR, Platform.SWITCH] +PLATFORMS = [ + Platform.BUTTON, + Platform.DEVICE_TRACKER, + Platform.SENSOR, + Platform.SWITCH, + Platform.UPDATE, +] CONF_CONSIDER_HOME = "consider_home" @@ -13,6 +19,7 @@ KEY_ROUTER = "router" KEY_COORDINATOR = "coordinator" KEY_COORDINATOR_TRAFFIC = "coordinator_traffic" KEY_COORDINATOR_SPEED = "coordinator_speed" +KEY_COORDINATOR_FIRMWARE = "coordinator_firmware" KEY_COORDINATOR_UTIL = "coordinator_utilization" KEY_COORDINATOR_LINK = "coordinator_link" diff --git a/homeassistant/components/netgear/router.py b/homeassistant/components/netgear/router.py index 57b0efa1bf2..a4f8a4df14e 100644 --- a/homeassistant/components/netgear/router.py +++ b/homeassistant/components/netgear/router.py @@ -250,6 +250,16 @@ class NetgearRouter: async with self._api_lock: await self.hass.async_add_executor_job(self._api.reboot) + async def async_check_new_firmware(self) -> None: + """Check for new firmware of the router.""" + async with self._api_lock: + return await self.hass.async_add_executor_job(self._api.check_new_firmware) + + async def async_update_new_firmware(self) -> None: + """Update the router to the latest firmware.""" + async with self._api_lock: + await self.hass.async_add_executor_job(self._api.update_new_firmware) + @property def port(self) -> int: """Port used by the API.""" diff --git a/homeassistant/components/netgear/update.py b/homeassistant/components/netgear/update.py new file mode 100644 index 00000000000..8d4a9b4912a --- /dev/null +++ b/homeassistant/components/netgear/update.py @@ -0,0 +1,81 @@ +"""Update entities for Netgear 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 .const import DOMAIN, KEY_COORDINATOR_FIRMWARE, KEY_ROUTER +from .router import NetgearRouter, NetgearRouterEntity + +LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up update entities for Netgear component.""" + router = hass.data[DOMAIN][entry.entry_id][KEY_ROUTER] + coordinator = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR_FIRMWARE] + entities = [NetgearUpdateEntity(coordinator, router)] + + async_add_entities(entities) + + +class NetgearUpdateEntity(NetgearRouterEntity, UpdateEntity): + """Update entity for a Netgear device.""" + + _attr_device_class = UpdateDeviceClass.FIRMWARE + _attr_supported_features = UpdateEntityFeature.INSTALL + + def __init__( + self, + coordinator: DataUpdateCoordinator, + router: NetgearRouter, + ) -> None: + """Initialize a Netgear device.""" + super().__init__(coordinator, router) + self._name = f"{router.device_name} Update" + self._unique_id = f"{router.serial_number}-update" + + @property + def installed_version(self) -> str | None: + """Version currently in use.""" + if self.coordinator.data is not None: + return self.coordinator.data.get("CurrentVersion") + return None + + @property + def latest_version(self) -> str | None: + """Latest version available for install.""" + if self.coordinator.data is not None: + new_version = self.coordinator.data.get("NewVersion") + if new_version is not None: + return new_version + return self.installed_version + + @property + def release_summary(self) -> str | None: + """Release summary.""" + if self.coordinator.data is not None: + return self.coordinator.data.get("ReleaseNote") + return None + + async def async_install( + self, version: str | None, backup: bool, **kwargs: Any + ) -> None: + """Install the latest firmware version.""" + await self._router.async_update_new_firmware() + + @callback + def async_update_device(self) -> None: + """Update the Netgear device."""