Bump pyuptimerobot to 25.0.0 (#169572)

This commit is contained in:
Simone Chemelli
2026-05-04 12:37:45 -04:00
committed by Paulus Schoutsen
parent 145c7435a5
commit c30ac2c0f3
11 changed files with 86 additions and 48 deletions
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["pyuptimerobot"],
"quality_scale": "gold",
"requirements": ["pyuptimerobot==24.0.1"]
"requirements": ["pyuptimerobot==25.0.0"]
}
@@ -61,9 +61,12 @@ class UptimeRobotSensor(UptimeRobotEntity, SensorEntity):
"""Representation of a UptimeRobot sensor."""
@property
def native_value(self) -> str:
def native_value(self) -> str | None:
"""Return the status of the monitor."""
if not self._monitor.status:
return None
status = self._monitor.status.lower()
# The API returns "paused"
# but the entity state will be "pause" to avoid a breaking change
return {"paused": "pause"}.get(status, status) # type: ignore[no-any-return]
return {"paused": "pause"}.get(status, status)
+9 -26
View File
@@ -4,11 +4,7 @@ from __future__ import annotations
from typing import Any
from pyuptimerobot import (
UptimeRobotAuthenticationException,
UptimeRobotException,
UptimeRobotMonitor,
)
from pyuptimerobot import UptimeRobotMonitor
from homeassistant.components.switch import (
SwitchDeviceClass,
@@ -16,13 +12,12 @@ from homeassistant.components.switch import (
SwitchEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN, STATUS_DOWN, STATUS_UP
from .const import STATUS_UP
from .coordinator import UptimeRobotConfigEntry
from .entity import UptimeRobotEntity
from .utils import new_device_listener
from .utils import new_device_listener, uptimerobot_api_call
# Limit the number of parallel updates to 1
PARALLEL_UPDATES = 1
@@ -65,26 +60,14 @@ class UptimeRobotSwitch(UptimeRobotEntity, SwitchEntity):
"""Return True if the entity is on."""
return bool(self._monitor.status == STATUS_UP)
async def _async_edit_monitor(self, **kwargs: Any) -> None:
"""Edit monitor status."""
try:
await self.api.async_edit_monitor(**kwargs)
except UptimeRobotAuthenticationException:
self.coordinator.config_entry.async_start_reauth(self.hass)
return
except UptimeRobotException as exception:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="api_exception",
translation_placeholders={"error": "Generic UptimeRobot exception"},
) from exception
await self.coordinator.async_request_refresh()
@uptimerobot_api_call
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off switch."""
await self._async_edit_monitor(monitor_id=self._monitor.id, status=STATUS_DOWN)
await self.api.async_pause_monitor(monitor_id=self._monitor.id)
await self.coordinator.async_request_refresh()
@uptimerobot_api_call
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on switch."""
await self._async_edit_monitor(monitor_id=self._monitor.id, status=STATUS_UP)
await self.api.async_start_monitor(monitor_id=self._monitor.id)
await self.coordinator.async_request_refresh()
+35 -2
View File
@@ -2,11 +2,44 @@
from __future__ import annotations
from collections.abc import Callable
from collections.abc import Awaitable, Callable, Coroutine
from functools import wraps
from typing import Any, Concatenate
from pyuptimerobot import UptimeRobotMonitor
from pyuptimerobot import (
UptimeRobotAuthenticationException,
UptimeRobotException,
UptimeRobotMonitor,
)
from homeassistant.exceptions import HomeAssistantError
from .const import DOMAIN
from .coordinator import UptimeRobotDataUpdateCoordinator
from .entity import UptimeRobotEntity
def uptimerobot_api_call[_T: UptimeRobotEntity, **_P](
func: Callable[Concatenate[_T, _P], Awaitable[None]],
) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]:
"""Catch UptimeRobot API call exceptions."""
@wraps(func)
async def cmd_wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None:
"""Wrap all command methods."""
try:
await func(self, *args, **kwargs)
except UptimeRobotAuthenticationException:
self.coordinator.config_entry.async_start_reauth(self.hass)
return
except UptimeRobotException as exception:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="api_exception",
translation_placeholders={"error": "Generic UptimeRobot exception"},
) from exception
return cmd_wrapper
def new_device_listener(
+1 -1
View File
@@ -2739,7 +2739,7 @@ pytrafikverket==1.1.1
pytrydan==0.8.0
# homeassistant.components.uptimerobot
pyuptimerobot==24.0.1
pyuptimerobot==25.0.0
# homeassistant.components.vera
pyvera==0.3.16
+1 -1
View File
@@ -2338,7 +2338,7 @@ pytrafikverket==1.1.1
pytrydan==0.8.0
# homeassistant.components.uptimerobot
pyuptimerobot==24.0.1
pyuptimerobot==25.0.0
# homeassistant.components.vera
pyvera==0.3.16
+2 -1
View File
@@ -6,7 +6,8 @@ from enum import StrEnum
from typing import Any
from unittest.mock import patch
from pyuptimerobot import API_PATH_MONITORS, UptimeRobotApiResponse
from pyuptimerobot import UptimeRobotApiResponse
from pyuptimerobot.const import API_PATH_MONITORS
from homeassistant import config_entries
from homeassistant.components.uptimerobot.const import DOMAIN
@@ -4,11 +4,11 @@ from unittest.mock import patch
import pytest
from pyuptimerobot import (
API_PATH_USER_ME,
UptimeRobotAuthenticationException,
UptimeRobotConnectionException,
UptimeRobotException,
)
from pyuptimerobot.const import API_PATH_USER_ME
from homeassistant import config_entries
from homeassistant.components.uptimerobot.const import DOMAIN
@@ -3,7 +3,8 @@
import json
from unittest.mock import patch
from pyuptimerobot import API_PATH_USER_ME, UptimeRobotException
from pyuptimerobot import UptimeRobotException
from pyuptimerobot.const import API_PATH_USER_ME
from homeassistant.core import HomeAssistant
+22 -2
View File
@@ -6,11 +6,12 @@ from pyuptimerobot import UptimeRobotAuthenticationException
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.components.uptimerobot.const import COORDINATOR_UPDATE_INTERVAL
from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util
from .common import (
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA,
MOCK_UPTIMEROBOT_MONITOR,
MOCK_UPTIMEROBOT_MONITOR_2,
STATE_UP,
@@ -19,7 +20,7 @@ from .common import (
setup_uptimerobot_integration,
)
from tests.common import async_fire_time_changed
from tests.common import MockConfigEntry, async_fire_time_changed
async def test_presentation(hass: HomeAssistant) -> None:
@@ -83,3 +84,22 @@ async def test_sensor_dynamic(hass: HomeAssistant) -> None:
assert (entity := hass.states.get(entity_id_2))
assert entity.state == STATE_UP
async def test_sensor_monitor_status_missing(
hass: HomeAssistant,
) -> None:
"""Test sensor becomes unknown when the monitor status is missing."""
monitor_without_status = {**MOCK_UPTIMEROBOT_MONITOR, "status": None}
mock_entry = MockConfigEntry(**MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA)
mock_entry.add_to_hass(hass)
with patch(
"pyuptimerobot.UptimeRobot.async_get_monitors",
return_value=mock_uptimerobot_api_response(data=[monitor_without_status]),
):
assert await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
assert (entity := hass.states.get(UPTIMEROBOT_SENSOR_TEST_ENTITY))
assert entity.state == STATE_UNKNOWN
+7 -10
View File
@@ -3,11 +3,8 @@
from unittest.mock import patch
import pytest
from pyuptimerobot import (
API_PATH_MONITOR_DETAIL,
UptimeRobotAuthenticationException,
UptimeRobotException,
)
from pyuptimerobot import UptimeRobotAuthenticationException, UptimeRobotException
from pyuptimerobot.const import API_PATH_MONITOR_DETAIL
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.components.uptimerobot.const import COORDINATOR_UPDATE_INTERVAL
@@ -58,7 +55,7 @@ async def test_switch_off(hass: HomeAssistant) -> None:
),
),
patch(
"pyuptimerobot.UptimeRobot.async_edit_monitor",
"pyuptimerobot.UptimeRobot.async_pause_monitor",
return_value=mock_uptimerobot_api_response(
api_path=API_PATH_MONITOR_DETAIL, data=MOCK_UPTIMEROBOT_MONITOR_PAUSED
),
@@ -90,7 +87,7 @@ async def test_switch_on(hass: HomeAssistant) -> None:
return_value=mock_uptimerobot_api_response(data=[MOCK_UPTIMEROBOT_MONITOR]),
),
patch(
"pyuptimerobot.UptimeRobot.async_edit_monitor",
"pyuptimerobot.UptimeRobot.async_start_monitor",
return_value=mock_uptimerobot_api_response(
api_path=API_PATH_MONITOR_DETAIL,
data=MOCK_UPTIMEROBOT_MONITOR,
@@ -122,7 +119,7 @@ async def test_authentication_error(
with (
patch(
"pyuptimerobot.UptimeRobot.async_edit_monitor",
"pyuptimerobot.UptimeRobot.async_start_monitor",
side_effect=UptimeRobotAuthenticationException,
),
patch(
@@ -148,7 +145,7 @@ async def test_action_execution_failure(hass: HomeAssistant) -> None:
with (
patch(
"pyuptimerobot.UptimeRobot.async_edit_monitor",
"pyuptimerobot.UptimeRobot.async_start_monitor",
side_effect=UptimeRobotException,
),
pytest.raises(HomeAssistantError) as exc_info,
@@ -176,7 +173,7 @@ async def test_switch_api_failure(hass: HomeAssistant) -> None:
with (
patch(
"pyuptimerobot.UptimeRobot.async_edit_monitor",
"pyuptimerobot.UptimeRobot.async_pause_monitor",
side_effect=UptimeRobotException,
),
pytest.raises(HomeAssistantError) as exc_info,