mirror of
https://github.com/home-assistant/core.git
synced 2026-05-08 17:49:37 +01:00
Update UptimeRobot to API v3 (#153508)
This commit is contained in:
@@ -12,6 +12,7 @@ from homeassistant.components.binary_sensor import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import STATUS_UP
|
||||
from .coordinator import UptimeRobotConfigEntry
|
||||
from .entity import UptimeRobotEntity
|
||||
from .utils import new_device_listener
|
||||
@@ -53,4 +54,4 @@ class UptimeRobotBinarySensor(UptimeRobotEntity, BinarySensorEntity):
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return True if the entity is on."""
|
||||
return bool(self.monitor.status == 2)
|
||||
return bool(self.monitor.status == STATUS_UP)
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pyuptimerobot import (
|
||||
UptimeRobot,
|
||||
UptimeRobotAccount,
|
||||
UptimeRobotApiError,
|
||||
UptimeRobotApiResponse,
|
||||
UptimeRobotAuthenticationException,
|
||||
UptimeRobotException,
|
||||
@@ -19,7 +18,7 @@ from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import API_ATTR_OK, DOMAIN, LOGGER
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema({vol.Required(CONF_API_KEY): str})
|
||||
|
||||
@@ -34,7 +33,7 @@ class UptimeRobotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
) -> tuple[dict[str, str], UptimeRobotAccount | None]:
|
||||
"""Validate the user input allows us to connect."""
|
||||
errors: dict[str, str] = {}
|
||||
response: UptimeRobotApiResponse | UptimeRobotApiError | None = None
|
||||
response: UptimeRobotApiResponse | None = None
|
||||
key: str = data[CONF_API_KEY]
|
||||
if key.startswith(("ur", "m")):
|
||||
LOGGER.error("Wrong API key type detected, use the 'main' API key")
|
||||
@@ -51,16 +50,12 @@ class UptimeRobotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
except Exception as exception: # noqa: BLE001
|
||||
LOGGER.exception(exception)
|
||||
errors["base"] = "unknown"
|
||||
else:
|
||||
if response.status != API_ATTR_OK:
|
||||
errors["base"] = "unknown"
|
||||
LOGGER.error(response.error.message)
|
||||
|
||||
account: UptimeRobotAccount | None = (
|
||||
response.data
|
||||
if response and response.data and response.data.email
|
||||
else None
|
||||
)
|
||||
if TYPE_CHECKING:
|
||||
assert response is not None
|
||||
assert isinstance(response.data, UptimeRobotAccount)
|
||||
|
||||
account: UptimeRobotAccount | None = response.data if response else None
|
||||
|
||||
return errors, account
|
||||
|
||||
@@ -75,7 +70,7 @@ class UptimeRobotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
errors, account = await self._validate_input(user_input)
|
||||
if account:
|
||||
await self.async_set_unique_id(str(account.user_id))
|
||||
await self.async_set_unique_id(account.email)
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(title=account.email, data=user_input)
|
||||
|
||||
@@ -99,12 +94,13 @@ class UptimeRobotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
errors, account = await self._validate_input(user_input)
|
||||
if account:
|
||||
if self.context.get("unique_id") and self.context["unique_id"] != str(
|
||||
account.user_id
|
||||
if (
|
||||
self.context.get("unique_id")
|
||||
and self.context["unique_id"] != account.email
|
||||
):
|
||||
errors["base"] = "reauth_failed_matching_account"
|
||||
else:
|
||||
existing_entry = await self.async_set_unique_id(str(account.user_id))
|
||||
existing_entry = await self.async_set_unique_id(account.email)
|
||||
if existing_entry:
|
||||
self.hass.config_entries.async_update_entry(
|
||||
existing_entry, data=user_input
|
||||
@@ -134,7 +130,7 @@ class UptimeRobotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
errors, account = await self._validate_input(user_input)
|
||||
if account:
|
||||
await self.async_set_unique_id(str(account.user_id))
|
||||
await self.async_set_unique_id(account.email)
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_update_reload_and_abort(
|
||||
reconfigure_entry, data_updates=user_input
|
||||
|
||||
@@ -21,3 +21,6 @@ ATTRIBUTION: Final = "Data provided by UptimeRobot"
|
||||
ATTR_TARGET: Final = "target"
|
||||
|
||||
API_ATTR_OK: Final = "ok"
|
||||
|
||||
STATUS_UP = "UP"
|
||||
STATUS_DOWN = "DOWN"
|
||||
|
||||
@@ -17,7 +17,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import API_ATTR_OK, COORDINATOR_UPDATE_INTERVAL, DOMAIN, LOGGER
|
||||
from .const import COORDINATOR_UPDATE_INTERVAL, DOMAIN, LOGGER
|
||||
|
||||
type UptimeRobotConfigEntry = ConfigEntry[UptimeRobotDataUpdateCoordinator]
|
||||
|
||||
@@ -52,11 +52,6 @@ class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator[list[UptimeRobotMon
|
||||
except UptimeRobotException as exception:
|
||||
raise UpdateFailed(exception) from exception
|
||||
|
||||
if response.status != API_ATTR_OK:
|
||||
raise UpdateFailed(
|
||||
response.error.message if response.error else "Unknown error"
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(response.data, list)
|
||||
|
||||
|
||||
@@ -6,10 +6,13 @@ from typing import Any
|
||||
|
||||
from pyuptimerobot import UptimeRobotException
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .coordinator import UptimeRobotConfigEntry
|
||||
|
||||
TO_REDACT = {"email"}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
@@ -25,17 +28,16 @@ async def async_get_config_entry_diagnostics(
|
||||
else:
|
||||
if (details := response.data) is not None:
|
||||
account = {
|
||||
"up_monitors": details.up_monitors,
|
||||
"down_monitors": details.down_monitors,
|
||||
"paused_monitors": details.paused_monitors,
|
||||
"monitorsCount": details.monitorsCount,
|
||||
"email": details.email,
|
||||
}
|
||||
|
||||
return {
|
||||
"account": account,
|
||||
"account": async_redact_data(account, TO_REDACT),
|
||||
"monitors": [
|
||||
{
|
||||
"id": monitor.id,
|
||||
"type": str(monitor.type),
|
||||
"type": monitor.type,
|
||||
"interval": monitor.interval,
|
||||
"status": monitor.status,
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ class UptimeRobotEntity(CoordinatorEntity[UptimeRobotDataUpdateCoordinator]):
|
||||
self._monitor = monitor
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, str(self.monitor.id))},
|
||||
name=self.monitor.friendly_name,
|
||||
name=self.monitor.friendlyName,
|
||||
manufacturer="UptimeRobot Team",
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
model=self.monitor.type.name,
|
||||
model=self.monitor.type,
|
||||
configuration_url=f"https://uptimerobot.com/dashboard#{self.monitor.id}",
|
||||
)
|
||||
self._attr_extra_state_attributes = {
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pyuptimerobot"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["pyuptimerobot==22.2.0"]
|
||||
"requirements": ["pyuptimerobot==24.0.1"]
|
||||
}
|
||||
|
||||
@@ -17,14 +17,6 @@ from .coordinator import UptimeRobotConfigEntry
|
||||
from .entity import UptimeRobotEntity
|
||||
from .utils import new_device_listener
|
||||
|
||||
SENSORS_INFO = {
|
||||
0: "pause",
|
||||
1: "not_checked_yet",
|
||||
2: "up",
|
||||
8: "seems_down",
|
||||
9: "down",
|
||||
}
|
||||
|
||||
# Coordinator is used to centralize the data updates
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@@ -71,4 +63,7 @@ class UptimeRobotSensor(UptimeRobotEntity, SensorEntity):
|
||||
@property
|
||||
def native_value(self) -> str:
|
||||
"""Return the status of the monitor."""
|
||||
return SENSORS_INFO[self.monitor.status]
|
||||
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]
|
||||
|
||||
@@ -19,7 +19,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import API_ATTR_OK, DOMAIN
|
||||
from .const import DOMAIN, STATUS_DOWN, STATUS_UP
|
||||
from .coordinator import UptimeRobotConfigEntry
|
||||
from .entity import UptimeRobotEntity
|
||||
from .utils import new_device_listener
|
||||
@@ -63,12 +63,12 @@ class UptimeRobotSwitch(UptimeRobotEntity, SwitchEntity):
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return True if the entity is on."""
|
||||
return bool(self.monitor.status != 0)
|
||||
return bool(self.monitor.status == STATUS_UP)
|
||||
|
||||
async def _async_edit_monitor(self, **kwargs: Any) -> None:
|
||||
"""Edit monitor status."""
|
||||
try:
|
||||
response = await self.api.async_edit_monitor(**kwargs)
|
||||
await self.api.async_edit_monitor(**kwargs)
|
||||
except UptimeRobotAuthenticationException:
|
||||
self.coordinator.config_entry.async_start_reauth(self.hass)
|
||||
return
|
||||
@@ -76,22 +76,15 @@ class UptimeRobotSwitch(UptimeRobotEntity, SwitchEntity):
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="api_exception",
|
||||
translation_placeholders={"error": repr(exception)},
|
||||
translation_placeholders={"error": "Generic UptimeRobot exception"},
|
||||
) from exception
|
||||
|
||||
if response.status != API_ATTR_OK:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="api_exception",
|
||||
translation_placeholders={"error": response.error.message},
|
||||
)
|
||||
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn on switch."""
|
||||
await self._async_edit_monitor(id=self.monitor.id, status=0)
|
||||
"""Turn off switch."""
|
||||
await self._async_edit_monitor(monitor_id=self.monitor.id, status=STATUS_DOWN)
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn off switch."""
|
||||
await self._async_edit_monitor(id=self.monitor.id, status=1)
|
||||
"""Turn on switch."""
|
||||
await self._async_edit_monitor(monitor_id=self.monitor.id, status=STATUS_UP)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Utility functions for the UptimeRobot integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
from pyuptimerobot import UptimeRobotMonitor
|
||||
@@ -16,9 +18,6 @@ def new_device_listener(
|
||||
|
||||
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:
|
||||
|
||||
Generated
+1
-1
@@ -2670,7 +2670,7 @@ pytrafikverket==1.1.1
|
||||
pytrydan==0.8.0
|
||||
|
||||
# homeassistant.components.uptimerobot
|
||||
pyuptimerobot==22.2.0
|
||||
pyuptimerobot==24.0.1
|
||||
|
||||
# homeassistant.components.vera
|
||||
pyvera==0.3.16
|
||||
|
||||
Generated
+1
-1
@@ -2251,7 +2251,7 @@ pytrafikverket==1.1.1
|
||||
pytrydan==0.8.0
|
||||
|
||||
# homeassistant.components.uptimerobot
|
||||
pyuptimerobot==22.2.0
|
||||
pyuptimerobot==24.0.1
|
||||
|
||||
# homeassistant.components.vera
|
||||
pyvera==0.3.16
|
||||
|
||||
@@ -6,13 +6,7 @@ from enum import StrEnum
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyuptimerobot import (
|
||||
APIStatus,
|
||||
UptimeRobotAccount,
|
||||
UptimeRobotApiError,
|
||||
UptimeRobotApiResponse,
|
||||
UptimeRobotMonitor,
|
||||
)
|
||||
from pyuptimerobot import API_PATH_MONITORS, UptimeRobotApiResponse
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.uptimerobot.const import DOMAIN
|
||||
@@ -29,29 +23,31 @@ MOCK_UPTIMEROBOT_UNIQUE_ID = "1234567890"
|
||||
|
||||
MOCK_UPTIMEROBOT_ACCOUNT = {
|
||||
"email": MOCK_UPTIMEROBOT_EMAIL,
|
||||
"user_id": 1234567890,
|
||||
"up_monitors": 1,
|
||||
"monitorsCount": 1,
|
||||
}
|
||||
MOCK_UPTIMEROBOT_ERROR = {"message": "test error from API."}
|
||||
MOCK_UPTIMEROBOT_MONITOR = {
|
||||
"id": 1234,
|
||||
"friendly_name": "Test monitor",
|
||||
"status": 2,
|
||||
"type": 1,
|
||||
"friendlyName": "Test monitor",
|
||||
"interval": 300,
|
||||
"status": "UP",
|
||||
"type": "HTTP",
|
||||
"url": "http://example.com",
|
||||
}
|
||||
MOCK_UPTIMEROBOT_MONITOR_PAUSED = {
|
||||
"id": 1234,
|
||||
"friendly_name": "Test monitor",
|
||||
"status": 0,
|
||||
"type": 1,
|
||||
"friendlyName": "Test monitor",
|
||||
"interval": 300,
|
||||
"status": "PAUSED",
|
||||
"type": "HTTP",
|
||||
"url": "http://example.com",
|
||||
}
|
||||
MOCK_UPTIMEROBOT_MONITOR_2 = {
|
||||
"id": 5678,
|
||||
"friendly_name": "Test monitor 2",
|
||||
"status": 2,
|
||||
"type": 1,
|
||||
"friendlyName": "Test monitor 2",
|
||||
"interval": 300,
|
||||
"status": "UP",
|
||||
"type": "HTTP",
|
||||
"url": "http://example2.com",
|
||||
}
|
||||
|
||||
@@ -59,14 +55,14 @@ MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA = {
|
||||
"domain": DOMAIN,
|
||||
"title": MOCK_UPTIMEROBOT_EMAIL,
|
||||
"data": {"platform": DOMAIN, "api_key": MOCK_UPTIMEROBOT_API_KEY},
|
||||
"unique_id": MOCK_UPTIMEROBOT_UNIQUE_ID,
|
||||
"unique_id": MOCK_UPTIMEROBOT_EMAIL,
|
||||
"source": config_entries.SOURCE_USER,
|
||||
}
|
||||
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA_KEY_READ_ONLY = {
|
||||
"domain": DOMAIN,
|
||||
"title": MOCK_UPTIMEROBOT_EMAIL,
|
||||
"data": {"platform": DOMAIN, "api_key": MOCK_UPTIMEROBOT_API_KEY_READ_ONLY},
|
||||
"unique_id": MOCK_UPTIMEROBOT_UNIQUE_ID,
|
||||
"unique_id": MOCK_UPTIMEROBOT_EMAIL,
|
||||
"source": config_entries.SOURCE_USER,
|
||||
}
|
||||
|
||||
@@ -86,25 +82,21 @@ class MockApiResponseKey(StrEnum):
|
||||
|
||||
|
||||
def mock_uptimerobot_api_response(
|
||||
data: list[dict[str, Any]]
|
||||
| list[UptimeRobotMonitor]
|
||||
| UptimeRobotAccount
|
||||
| UptimeRobotApiError
|
||||
| None = None,
|
||||
status: APIStatus = APIStatus.OK,
|
||||
key: MockApiResponseKey = MockApiResponseKey.MONITORS,
|
||||
data: list[dict[str, Any]] | dict[str, Any],
|
||||
api_path: str = API_PATH_MONITORS,
|
||||
) -> UptimeRobotApiResponse:
|
||||
"""Mock API response for UptimeRobot."""
|
||||
|
||||
if api_path == API_PATH_MONITORS:
|
||||
data_dict = {"data": data}
|
||||
elif isinstance(data, dict):
|
||||
data_dict = data
|
||||
|
||||
return UptimeRobotApiResponse.from_dict(
|
||||
{
|
||||
"stat": {"error": APIStatus.FAIL}.get(key, status),
|
||||
key: data
|
||||
if data is not None
|
||||
else {
|
||||
"account": MOCK_UPTIMEROBOT_ACCOUNT,
|
||||
"error": MOCK_UPTIMEROBOT_ERROR,
|
||||
"monitors": [MOCK_UPTIMEROBOT_MONITOR],
|
||||
}.get(key, {}),
|
||||
"_method": "GET",
|
||||
"_api_path": api_path,
|
||||
**data_dict,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from pyuptimerobot import (
|
||||
UptimeRobotApiResponse,
|
||||
API_PATH_USER_ME,
|
||||
UptimeRobotAuthenticationException,
|
||||
UptimeRobotConnectionException,
|
||||
UptimeRobotException,
|
||||
)
|
||||
|
||||
@@ -20,8 +21,7 @@ from .common import (
|
||||
MOCK_UPTIMEROBOT_API_KEY,
|
||||
MOCK_UPTIMEROBOT_API_KEY_READ_ONLY,
|
||||
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA,
|
||||
MOCK_UPTIMEROBOT_UNIQUE_ID,
|
||||
MockApiResponseKey,
|
||||
MOCK_UPTIMEROBOT_EMAIL,
|
||||
mock_uptimerobot_api_response,
|
||||
)
|
||||
|
||||
@@ -40,7 +40,9 @@ async def test_user(hass: HomeAssistant) -> None:
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ACCOUNT),
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
api_path=API_PATH_USER_ME, data=MOCK_UPTIMEROBOT_ACCOUNT
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||
@@ -53,7 +55,7 @@ async def test_user(hass: HomeAssistant) -> None:
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["result"].unique_id == MOCK_UPTIMEROBOT_UNIQUE_ID
|
||||
assert result2["result"].unique_id == MOCK_UPTIMEROBOT_EMAIL
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == MOCK_UPTIMEROBOT_ACCOUNT["email"]
|
||||
assert result2["data"] == {CONF_API_KEY: MOCK_UPTIMEROBOT_API_KEY}
|
||||
@@ -71,7 +73,10 @@ async def test_user_key_read_only(hass: HomeAssistant) -> None:
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ACCOUNT),
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
api_path=API_PATH_USER_ME,
|
||||
data=MOCK_UPTIMEROBOT_ACCOUNT,
|
||||
),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -120,7 +125,7 @@ async def test_api_error(hass: HomeAssistant, caplog: pytest.LogCaptureFixture)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ERROR),
|
||||
side_effect=UptimeRobotConnectionException,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -128,8 +133,7 @@ async def test_api_error(hass: HomeAssistant, caplog: pytest.LogCaptureFixture)
|
||||
)
|
||||
|
||||
assert result2["errors"]
|
||||
assert result2["errors"]["base"] == "unknown"
|
||||
assert "test error from API." in caplog.text
|
||||
assert result2["errors"]["base"] == "cannot_connect"
|
||||
|
||||
|
||||
async def test_user_unique_id_already_exists(
|
||||
@@ -148,7 +152,10 @@ async def test_user_unique_id_already_exists(
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ACCOUNT),
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
api_path=API_PATH_USER_ME,
|
||||
data=MOCK_UPTIMEROBOT_ACCOUNT,
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||
@@ -182,7 +189,10 @@ async def test_reauthentication(
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ACCOUNT),
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
api_path=API_PATH_USER_ME,
|
||||
data=MOCK_UPTIMEROBOT_ACCOUNT,
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||
@@ -215,7 +225,7 @@ async def test_reauthentication_failure(
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ERROR),
|
||||
side_effect=UptimeRobotException,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||
@@ -231,7 +241,7 @@ async def test_reauthentication_failure(
|
||||
assert result2["step_id"] == "reauth_confirm"
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["errors"]
|
||||
assert result2["errors"]["base"] == "unknown"
|
||||
assert result2["errors"]["base"] == "cannot_connect"
|
||||
|
||||
|
||||
async def test_reauthentication_failure_no_existing_entry(
|
||||
@@ -252,7 +262,10 @@ async def test_reauthentication_failure_no_existing_entry(
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ACCOUNT),
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
api_path=API_PATH_USER_ME,
|
||||
data=MOCK_UPTIMEROBOT_ACCOUNT,
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||
@@ -286,8 +299,11 @@ async def test_reauthentication_failure_account_not_matching(
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
key=MockApiResponseKey.ACCOUNT,
|
||||
data={**MOCK_UPTIMEROBOT_ACCOUNT, "user_id": 1234567891},
|
||||
api_path=API_PATH_USER_ME,
|
||||
data={
|
||||
"email": "wrong_account",
|
||||
"monitorsCount": 1,
|
||||
},
|
||||
),
|
||||
),
|
||||
patch(
|
||||
@@ -327,7 +343,9 @@ async def test_reconfigure_successful(
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ACCOUNT),
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
api_path=API_PATH_USER_ME, data=MOCK_UPTIMEROBOT_ACCOUNT
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||
@@ -387,90 +405,14 @@ async def test_reconfigure_failed(
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ACCOUNT),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
user_input={CONF_API_KEY: new_key},
|
||||
)
|
||||
|
||||
assert result3["type"] is FlowResultType.ABORT
|
||||
assert result3["reason"] == "reconfigure_successful"
|
||||
|
||||
# changed entry
|
||||
assert config_entry.data[CONF_API_KEY] == new_key
|
||||
|
||||
|
||||
async def test_reconfigure_with_key_present(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test that the entry reconfigure fails with a key from another entry."""
|
||||
config_entry = MockConfigEntry(
|
||||
**{**MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA, "unique_id": None}
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
api_key_2 = "u0242ac120003-2"
|
||||
email_2 = "test2@test.test"
|
||||
user_id_2 = "abcdefghil"
|
||||
data2 = {
|
||||
"domain": DOMAIN,
|
||||
"title": email_2,
|
||||
"data": {"platform": DOMAIN, "api_key": api_key_2},
|
||||
"unique_id": user_id_2,
|
||||
"source": config_entries.SOURCE_USER,
|
||||
}
|
||||
config_entry_2 = MockConfigEntry(**{**data2, "unique_id": None})
|
||||
config_entry_2.add_to_hass(hass)
|
||||
|
||||
result = await config_entry.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
assert result["step_id"] == "reconfigure"
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=UptimeRobotApiResponse.from_dict(
|
||||
{
|
||||
"stat": "ok",
|
||||
"email": email_2,
|
||||
"user_id": user_id_2,
|
||||
"up_monitors": 1,
|
||||
}
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
api_path=API_PATH_USER_ME, data=MOCK_UPTIMEROBOT_ACCOUNT
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_API_KEY: api_key_2},
|
||||
)
|
||||
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["errors"] == {}
|
||||
assert result2["step_id"] == "reconfigure"
|
||||
|
||||
new_key = "u0242ac120003-new"
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.config_flow.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ACCOUNT),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyuptimerobot import UptimeRobotException
|
||||
from pyuptimerobot import API_PATH_USER_ME, UptimeRobotException
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
@@ -11,7 +11,6 @@ from .common import (
|
||||
MOCK_UPTIMEROBOT_ACCOUNT,
|
||||
MOCK_UPTIMEROBOT_API_KEY,
|
||||
MOCK_UPTIMEROBOT_EMAIL,
|
||||
MockApiResponseKey,
|
||||
mock_uptimerobot_api_response,
|
||||
setup_uptimerobot_integration,
|
||||
)
|
||||
@@ -30,7 +29,7 @@ async def test_entry_diagnostics(
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_account_details",
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
key=MockApiResponseKey.ACCOUNT,
|
||||
api_path=API_PATH_USER_ME,
|
||||
data=MOCK_UPTIMEROBOT_ACCOUNT,
|
||||
),
|
||||
):
|
||||
@@ -41,13 +40,12 @@ async def test_entry_diagnostics(
|
||||
)
|
||||
|
||||
assert result["account"] == {
|
||||
"down_monitors": 0,
|
||||
"paused_monitors": 0,
|
||||
"up_monitors": 1,
|
||||
"monitorsCount": 1,
|
||||
"email": "**REDACTED**",
|
||||
}
|
||||
|
||||
assert result["monitors"] == [
|
||||
{"id": 1234, "interval": 0, "status": 2, "type": "MonitorType.HTTP"}
|
||||
{"id": 1234, "interval": 300, "status": "UP", "type": "HTTP"}
|
||||
]
|
||||
|
||||
assert list(result.keys()) == ["account", "monitors"]
|
||||
|
||||
@@ -21,7 +21,6 @@ from .common import (
|
||||
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA_KEY_READ_ONLY,
|
||||
MOCK_UPTIMEROBOT_MONITOR,
|
||||
UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY,
|
||||
MockApiResponseKey,
|
||||
mock_uptimerobot_api_response,
|
||||
setup_uptimerobot_integration,
|
||||
)
|
||||
@@ -137,7 +136,7 @@ async def test_integration_reload(
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||
return_value=mock_uptimerobot_api_response(),
|
||||
return_value=mock_uptimerobot_api_response(data=[MOCK_UPTIMEROBOT_MONITOR]),
|
||||
):
|
||||
assert await hass.config_entries.async_reload(mock_entry.entry_id)
|
||||
freezer.tick(COORDINATOR_UPDATE_INTERVAL)
|
||||
@@ -170,7 +169,7 @@ async def test_update_errors(
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||
return_value=mock_uptimerobot_api_response(),
|
||||
return_value=mock_uptimerobot_api_response(data=[MOCK_UPTIMEROBOT_MONITOR]),
|
||||
):
|
||||
freezer.tick(COORDINATOR_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
@@ -180,7 +179,7 @@ async def test_update_errors(
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ERROR),
|
||||
side_effect=Exception("Unexpected error"),
|
||||
):
|
||||
freezer.tick(COORDINATOR_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
@@ -188,7 +187,7 @@ async def test_update_errors(
|
||||
assert (entity := hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY))
|
||||
assert entity.state == STATE_UNAVAILABLE
|
||||
|
||||
assert "Error fetching uptimerobot data: test error from API" in caplog.text
|
||||
assert "Error fetching uptimerobot data:" in caplog.text
|
||||
|
||||
|
||||
async def test_device_management(
|
||||
@@ -231,7 +230,7 @@ async def test_device_management(
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||
return_value=mock_uptimerobot_api_response(),
|
||||
return_value=mock_uptimerobot_api_response(data=[MOCK_UPTIMEROBOT_MONITOR]),
|
||||
):
|
||||
freezer.tick(COORDINATOR_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from pyuptimerobot import UptimeRobotAuthenticationException, UptimeRobotException
|
||||
from pyuptimerobot import (
|
||||
API_PATH_MONITOR_DETAIL,
|
||||
UptimeRobotAuthenticationException,
|
||||
UptimeRobotException,
|
||||
)
|
||||
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.components.uptimerobot.const import COORDINATOR_UPDATE_INTERVAL
|
||||
@@ -24,7 +28,6 @@ from .common import (
|
||||
MOCK_UPTIMEROBOT_MONITOR_2,
|
||||
MOCK_UPTIMEROBOT_MONITOR_PAUSED,
|
||||
UPTIMEROBOT_SWITCH_TEST_ENTITY,
|
||||
MockApiResponseKey,
|
||||
mock_uptimerobot_api_response,
|
||||
setup_uptimerobot_integration,
|
||||
)
|
||||
@@ -56,7 +59,9 @@ async def test_switch_off(hass: HomeAssistant) -> None:
|
||||
),
|
||||
patch(
|
||||
"pyuptimerobot.UptimeRobot.async_edit_monitor",
|
||||
return_value=mock_uptimerobot_api_response(),
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
api_path=API_PATH_MONITOR_DETAIL, data=MOCK_UPTIMEROBOT_MONITOR_PAUSED
|
||||
),
|
||||
),
|
||||
):
|
||||
assert await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
@@ -86,7 +91,10 @@ async def test_switch_on(hass: HomeAssistant) -> None:
|
||||
),
|
||||
patch(
|
||||
"pyuptimerobot.UptimeRobot.async_edit_monitor",
|
||||
return_value=mock_uptimerobot_api_response(),
|
||||
return_value=mock_uptimerobot_api_response(
|
||||
api_path=API_PATH_MONITOR_DETAIL,
|
||||
data=MOCK_UPTIMEROBOT_MONITOR,
|
||||
),
|
||||
),
|
||||
):
|
||||
assert await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
@@ -155,7 +163,7 @@ async def test_action_execution_failure(hass: HomeAssistant) -> None:
|
||||
assert exc_info.value.translation_domain == "uptimerobot"
|
||||
assert exc_info.value.translation_key == "api_exception"
|
||||
assert exc_info.value.translation_placeholders == {
|
||||
"error": "UptimeRobotException()"
|
||||
"error": "Generic UptimeRobot exception"
|
||||
}
|
||||
|
||||
|
||||
@@ -166,23 +174,25 @@ async def test_switch_api_failure(hass: HomeAssistant) -> None:
|
||||
assert (entity := hass.states.get(UPTIMEROBOT_SWITCH_TEST_ENTITY)) is not None
|
||||
assert entity.state == STATE_ON
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_edit_monitor",
|
||||
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ERROR),
|
||||
with (
|
||||
patch(
|
||||
"pyuptimerobot.UptimeRobot.async_edit_monitor",
|
||||
side_effect=UptimeRobotException,
|
||||
),
|
||||
pytest.raises(HomeAssistantError) as exc_info,
|
||||
):
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: UPTIMEROBOT_SWITCH_TEST_ENTITY},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: UPTIMEROBOT_SWITCH_TEST_ENTITY},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert exc_info.value.translation_domain == "uptimerobot"
|
||||
assert exc_info.value.translation_key == "api_exception"
|
||||
assert exc_info.value.translation_placeholders == {
|
||||
"error": "test error from API."
|
||||
}
|
||||
assert exc_info.value.translation_domain == "uptimerobot"
|
||||
assert exc_info.value.translation_key == "api_exception"
|
||||
assert exc_info.value.translation_placeholders == {
|
||||
"error": "Generic UptimeRobot exception"
|
||||
}
|
||||
|
||||
|
||||
async def test_switch_dynamic(hass: HomeAssistant) -> None:
|
||||
|
||||
Reference in New Issue
Block a user