From 1093a2b80892afc4039a72f686b2399a021bb8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 25 Jan 2022 14:49:27 +0100 Subject: [PATCH] Add diagnostics to UptimeRobot (#64905) --- .../components/uptimerobot/__init__.py | 7 +- .../components/uptimerobot/binary_sensor.py | 8 +- .../components/uptimerobot/diagnostics.py | 45 +++++++++++ tests/components/uptimerobot/common.py | 11 ++- .../uptimerobot/test_diagnostics.py | 78 +++++++++++++++++++ 5 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 homeassistant/components/uptimerobot/diagnostics.py create mode 100644 tests/components/uptimerobot/test_diagnostics.py diff --git a/homeassistant/components/uptimerobot/__init__.py b/homeassistant/components/uptimerobot/__init__.py index c4b2eb54b2f..06da8a1b4b1 100644 --- a/homeassistant/components/uptimerobot/__init__.py +++ b/homeassistant/components/uptimerobot/__init__.py @@ -57,6 +57,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator): """Data update coordinator for UptimeRobot.""" + data: list[UptimeRobotMonitor] + def __init__( self, hass: HomeAssistant, @@ -69,17 +71,16 @@ class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator): hass, LOGGER, name=DOMAIN, - update_method=self._async_update_data, update_interval=COORDINATOR_UPDATE_INTERVAL, ) self._config_entry_id = config_entry_id self._device_registry = dev_reg - self._api = api + self.api = api async def _async_update_data(self) -> list[UptimeRobotMonitor] | None: """Update data.""" try: - response = await self._api.async_get_monitors() + response = await self.api.async_get_monitors() except UptimeRobotAuthenticationException as exception: raise ConfigEntryAuthFailed(exception) from exception except UptimeRobotException as exception: diff --git a/homeassistant/components/uptimerobot/binary_sensor.py b/homeassistant/components/uptimerobot/binary_sensor.py index 43aed00708d..40f850c5376 100644 --- a/homeassistant/components/uptimerobot/binary_sensor.py +++ b/homeassistant/components/uptimerobot/binary_sensor.py @@ -9,17 +9,19 @@ from homeassistant.components.binary_sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from . import UptimeRobotDataUpdateCoordinator from .const import DOMAIN from .entity import UptimeRobotEntity async def async_setup_entry( - hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, ) -> None: """Set up the UptimeRobot binary_sensors.""" - coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + coordinator: UptimeRobotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] async_add_entities( [ UptimeRobotBinarySensor( diff --git a/homeassistant/components/uptimerobot/diagnostics.py b/homeassistant/components/uptimerobot/diagnostics.py new file mode 100644 index 00000000000..94710235ab7 --- /dev/null +++ b/homeassistant/components/uptimerobot/diagnostics.py @@ -0,0 +1,45 @@ +"""Diagnostics support for UptimeRobot.""" +from __future__ import annotations + +from typing import Any + +from pyuptimerobot import UptimeRobotException + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from . import UptimeRobotDataUpdateCoordinator +from .const import DOMAIN + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, + entry: ConfigEntry, +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + coordinator: UptimeRobotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + account: dict[str, Any] | str | None = None + try: + response = await coordinator.api.async_get_account_details() + except UptimeRobotException as err: + account = str(err) + else: + if (details := response.data) is not None: + account = { + "up_monitors": details.up_monitors, + "down_monitors": details.down_monitors, + "paused_monitors": details.paused_monitors, + } + + return { + "account": account, + "monitors": [ + { + "id": monitor.id, + "type": str(monitor.type), + "interval": monitor.interval, + "status": monitor.status, + } + for monitor in coordinator.data + ], + } diff --git a/tests/components/uptimerobot/common.py b/tests/components/uptimerobot/common.py index 224d2d32911..86198487851 100644 --- a/tests/components/uptimerobot/common.py +++ b/tests/components/uptimerobot/common.py @@ -20,10 +20,15 @@ from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry -MOCK_UPTIMEROBOT_API_KEY = "1234" +MOCK_UPTIMEROBOT_API_KEY = "0242ac120003" +MOCK_UPTIMEROBOT_EMAIL = "test@test.test" MOCK_UPTIMEROBOT_UNIQUE_ID = "1234567890" -MOCK_UPTIMEROBOT_ACCOUNT = {"email": "test@test.test", "user_id": 1234567890} +MOCK_UPTIMEROBOT_ACCOUNT = { + "email": MOCK_UPTIMEROBOT_EMAIL, + "user_id": 1234567890, + "up_monitors": 1, +} MOCK_UPTIMEROBOT_ERROR = {"message": "test error from API."} MOCK_UPTIMEROBOT_MONITOR = { "id": 1234, @@ -35,7 +40,7 @@ MOCK_UPTIMEROBOT_MONITOR = { MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA = { "domain": DOMAIN, - "title": "test@test.test", + "title": MOCK_UPTIMEROBOT_EMAIL, "data": {"platform": DOMAIN, "api_key": MOCK_UPTIMEROBOT_API_KEY}, "unique_id": MOCK_UPTIMEROBOT_UNIQUE_ID, "source": config_entries.SOURCE_USER, diff --git a/tests/components/uptimerobot/test_diagnostics.py b/tests/components/uptimerobot/test_diagnostics.py new file mode 100644 index 00000000000..a0780f64e90 --- /dev/null +++ b/tests/components/uptimerobot/test_diagnostics.py @@ -0,0 +1,78 @@ +"""Test UptimeRobot diagnostics.""" +import json +from unittest.mock import patch + +from aiohttp import ClientSession +from pyuptimerobot import UptimeRobotException + +from homeassistant.core import HomeAssistant + +from .common import ( + MOCK_UPTIMEROBOT_ACCOUNT, + MOCK_UPTIMEROBOT_API_KEY, + MOCK_UPTIMEROBOT_EMAIL, + MockApiResponseKey, + mock_uptimerobot_api_response, + setup_uptimerobot_integration, +) + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_entry_diagnostics( + hass: HomeAssistant, + hass_client: ClientSession, +) -> None: + """Test config entry diagnostics.""" + entry = await setup_uptimerobot_integration(hass) + + with patch( + "pyuptimerobot.UptimeRobot.async_get_account_details", + return_value=mock_uptimerobot_api_response( + key=MockApiResponseKey.ACCOUNT, + data=MOCK_UPTIMEROBOT_ACCOUNT, + ), + ): + + result = await get_diagnostics_for_config_entry( + hass, + hass_client, + entry, + ) + + assert result["account"] == { + "down_monitors": 0, + "paused_monitors": 0, + "up_monitors": 1, + } + + assert result["monitors"] == [ + {"id": 1234, "interval": 0, "status": 2, "type": "MonitorType.HTTP"} + ] + + assert list(result.keys()) == ["account", "monitors"] + + result_dump = json.dumps(result) + assert MOCK_UPTIMEROBOT_EMAIL not in result_dump + assert MOCK_UPTIMEROBOT_API_KEY not in result_dump + + +async def test_entry_diagnostics_exception( + hass: HomeAssistant, + hass_client: ClientSession, +) -> None: + """Test config entry diagnostics with exception.""" + entry = await setup_uptimerobot_integration(hass) + + with patch( + "pyuptimerobot.UptimeRobot.async_get_account_details", + side_effect=UptimeRobotException("Test exception"), + ): + + result = await get_diagnostics_for_config_entry( + hass, + hass_client, + entry, + ) + + assert result["account"] == "Test exception"