From 3a89b3152fe681fa429de13392f67a3fec2deb2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ab=C3=ADlio=20Costa?= Date: Thu, 2 Oct 2025 10:52:22 +0100 Subject: [PATCH] Move common Uptime Robot new device check logic to helper (#153094) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../components/uptimerobot/binary_sensor.py | 35 ++++++------ .../components/uptimerobot/sensor.py | 53 +++++++++---------- .../components/uptimerobot/switch.py | 39 +++++++------- homeassistant/components/uptimerobot/utils.py | 34 ++++++++++++ 4 files changed, 94 insertions(+), 67 deletions(-) create mode 100644 homeassistant/components/uptimerobot/utils.py diff --git a/homeassistant/components/uptimerobot/binary_sensor.py b/homeassistant/components/uptimerobot/binary_sensor.py index 0a0f973c6e00..0fed98ed4a6f 100644 --- a/homeassistant/components/uptimerobot/binary_sensor.py +++ b/homeassistant/components/uptimerobot/binary_sensor.py @@ -2,6 +2,8 @@ from __future__ import annotations +from pyuptimerobot import UptimeRobotMonitor + from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, BinarySensorEntity, @@ -12,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .coordinator import UptimeRobotConfigEntry from .entity import UptimeRobotEntity +from .utils import new_device_listener # Coordinator is used to centralize the data updates PARALLEL_UPDATES = 0 @@ -25,29 +28,23 @@ async def async_setup_entry( """Set up the UptimeRobot binary_sensors.""" coordinator = entry.runtime_data - known_devices: set[int] = set() - - def _check_device() -> None: - entities: list[UptimeRobotBinarySensor] = [] - for monitor in coordinator.data: - if monitor.id in known_devices: - continue - known_devices.add(monitor.id) - entities.append( - UptimeRobotBinarySensor( - coordinator, - BinarySensorEntityDescription( - key=str(monitor.id), - device_class=BinarySensorDeviceClass.CONNECTIVITY, - ), - monitor=monitor, - ) + def _add_new_entities(new_monitors: list[UptimeRobotMonitor]) -> None: + """Add entities for new monitors.""" + entities = [ + UptimeRobotBinarySensor( + coordinator, + BinarySensorEntityDescription( + key=str(monitor.id), + device_class=BinarySensorDeviceClass.CONNECTIVITY, + ), + monitor=monitor, ) + for monitor in new_monitors + ] if entities: async_add_entities(entities) - _check_device() - entry.async_on_unload(coordinator.async_add_listener(_check_device)) + entry.async_on_unload(new_device_listener(coordinator, _add_new_entities)) class UptimeRobotBinarySensor(UptimeRobotEntity, BinarySensorEntity): diff --git a/homeassistant/components/uptimerobot/sensor.py b/homeassistant/components/uptimerobot/sensor.py index 60866154ac0e..633ac8243ff1 100644 --- a/homeassistant/components/uptimerobot/sensor.py +++ b/homeassistant/components/uptimerobot/sensor.py @@ -2,6 +2,8 @@ from __future__ import annotations +from pyuptimerobot import UptimeRobotMonitor + from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, @@ -13,6 +15,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .coordinator import UptimeRobotConfigEntry from .entity import UptimeRobotEntity +from .utils import new_device_listener SENSORS_INFO = { 0: "pause", @@ -34,38 +37,32 @@ async def async_setup_entry( """Set up the UptimeRobot sensors.""" coordinator = entry.runtime_data - known_devices: set[int] = set() - - def _check_device() -> None: - entities: list[UptimeRobotSensor] = [] - for monitor in coordinator.data: - if monitor.id in known_devices: - continue - known_devices.add(monitor.id) - entities.append( - UptimeRobotSensor( - coordinator, - SensorEntityDescription( - key=str(monitor.id), - entity_category=EntityCategory.DIAGNOSTIC, - device_class=SensorDeviceClass.ENUM, - options=[ - "down", - "not_checked_yet", - "pause", - "seems_down", - "up", - ], - translation_key="monitor_status", - ), - monitor=monitor, - ) + def _add_new_entities(new_monitors: list[UptimeRobotMonitor]) -> None: + """Add entities for new monitors.""" + entities = [ + UptimeRobotSensor( + coordinator, + SensorEntityDescription( + key=str(monitor.id), + entity_category=EntityCategory.DIAGNOSTIC, + device_class=SensorDeviceClass.ENUM, + options=[ + "down", + "not_checked_yet", + "pause", + "seems_down", + "up", + ], + translation_key="monitor_status", + ), + monitor=monitor, ) + for monitor in new_monitors + ] if entities: async_add_entities(entities) - _check_device() - entry.async_on_unload(coordinator.async_add_listener(_check_device)) + entry.async_on_unload(new_device_listener(coordinator, _add_new_entities)) class UptimeRobotSensor(UptimeRobotEntity, SensorEntity): diff --git a/homeassistant/components/uptimerobot/switch.py b/homeassistant/components/uptimerobot/switch.py index 41a46e9ff5cf..b75f099db73b 100644 --- a/homeassistant/components/uptimerobot/switch.py +++ b/homeassistant/components/uptimerobot/switch.py @@ -4,7 +4,11 @@ from __future__ import annotations from typing import Any -from pyuptimerobot import UptimeRobotAuthenticationException, UptimeRobotException +from pyuptimerobot import ( + UptimeRobotAuthenticationException, + UptimeRobotException, + UptimeRobotMonitor, +) from homeassistant.components.switch import ( SwitchDeviceClass, @@ -18,6 +22,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import API_ATTR_OK, DOMAIN from .coordinator import UptimeRobotConfigEntry from .entity import UptimeRobotEntity +from .utils import new_device_listener # Limit the number of parallel updates to 1 PARALLEL_UPDATES = 1 @@ -31,29 +36,23 @@ async def async_setup_entry( """Set up the UptimeRobot switches.""" coordinator = entry.runtime_data - known_devices: set[int] = set() - - def _check_device() -> None: - entities: list[UptimeRobotSwitch] = [] - for monitor in coordinator.data: - if monitor.id in known_devices: - continue - known_devices.add(monitor.id) - entities.append( - UptimeRobotSwitch( - coordinator, - SwitchEntityDescription( - key=str(monitor.id), - device_class=SwitchDeviceClass.SWITCH, - ), - monitor=monitor, - ) + def _add_new_entities(new_monitors: list[UptimeRobotMonitor]) -> None: + """Add entities for new monitors.""" + entities = [ + UptimeRobotSwitch( + coordinator, + SwitchEntityDescription( + key=str(monitor.id), + device_class=SwitchDeviceClass.SWITCH, + ), + monitor=monitor, ) + for monitor in new_monitors + ] if entities: async_add_entities(entities) - _check_device() - entry.async_on_unload(coordinator.async_add_listener(_check_device)) + entry.async_on_unload(new_device_listener(coordinator, _add_new_entities)) class UptimeRobotSwitch(UptimeRobotEntity, SwitchEntity): diff --git a/homeassistant/components/uptimerobot/utils.py b/homeassistant/components/uptimerobot/utils.py new file mode 100644 index 000000000000..522324cf6f38 --- /dev/null +++ b/homeassistant/components/uptimerobot/utils.py @@ -0,0 +1,34 @@ +"""Utility functions for the UptimeRobot integration.""" + +from collections.abc import Callable + +from pyuptimerobot import UptimeRobotMonitor + +from .coordinator import UptimeRobotDataUpdateCoordinator + + +def new_device_listener( + coordinator: UptimeRobotDataUpdateCoordinator, + new_devices_callback: Callable[[list[UptimeRobotMonitor]], None], +) -> Callable[[], None]: + """Subscribe to coordinator updates to check for new devices.""" + known_devices: set[int] = set() + + def _check_devices() -> None: + """Check for new devices and call callback with any new monitors.""" + if not coordinator.data: + return + + new_monitors: list[UptimeRobotMonitor] = [] + for monitor in coordinator.data: + if monitor.id not in known_devices: + known_devices.add(monitor.id) + new_monitors.append(monitor) + + if new_monitors: + new_devices_callback(new_monitors) + + # Check for devices immediately + _check_devices() + + return coordinator.async_add_listener(_check_devices)