Add switch platform to UptimeRobot (#65394)
* Add switch platfor mto UptimeRobot * Add tests * Apply review comment * review comments part 2 * review comments part 3 * Fix tests after swapping logic on/off * Fix reauth test * Check for read-only key * Fix reauth for switch platform * mypy * cleanup * cleanup part 2 * Fixes + review comments * Tests * Apply more review comments * Required changes * fix test * Remove if * 100% tests coverage * Check readonly key in config_flow * Fix strings & translation * Add guard for 'monitor' keys * allign tests * Wrong API key message reworded
This commit is contained in:
parent
5bb271c9fb
commit
35261a9089
13 changed files with 357 additions and 42 deletions
|
@ -26,9 +26,12 @@ from .const import API_ATTR_OK, COORDINATOR_UPDATE_INTERVAL, DOMAIN, LOGGER, PLA
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up UptimeRobot from a config entry."""
|
"""Set up UptimeRobot from a config entry."""
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
uptime_robot_api = UptimeRobot(
|
key: str = entry.data[CONF_API_KEY]
|
||||||
entry.data[CONF_API_KEY], async_get_clientsession(hass)
|
if key.startswith("ur") or key.startswith("m"):
|
||||||
)
|
raise ConfigEntryAuthFailed(
|
||||||
|
"Wrong API key type detected, use the 'main' API key"
|
||||||
|
)
|
||||||
|
uptime_robot_api = UptimeRobot(key, async_get_clientsession(hass))
|
||||||
dev_reg = await async_get_registry(hass)
|
dev_reg = await async_get_registry(hass)
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = coordinator = UptimeRobotDataUpdateCoordinator(
|
hass.data[DOMAIN][entry.entry_id] = coordinator = UptimeRobotDataUpdateCoordinator(
|
||||||
|
@ -58,6 +61,7 @@ class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator):
|
||||||
"""Data update coordinator for UptimeRobot."""
|
"""Data update coordinator for UptimeRobot."""
|
||||||
|
|
||||||
data: list[UptimeRobotMonitor]
|
data: list[UptimeRobotMonitor]
|
||||||
|
config_entry: ConfigEntry
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -23,18 +23,16 @@ async def async_setup_entry(
|
||||||
"""Set up the UptimeRobot binary_sensors."""
|
"""Set up the UptimeRobot binary_sensors."""
|
||||||
coordinator: UptimeRobotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator: UptimeRobotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
UptimeRobotBinarySensor(
|
||||||
UptimeRobotBinarySensor(
|
coordinator,
|
||||||
coordinator,
|
BinarySensorEntityDescription(
|
||||||
BinarySensorEntityDescription(
|
key=str(monitor.id),
|
||||||
key=str(monitor.id),
|
name=monitor.friendly_name,
|
||||||
name=monitor.friendly_name,
|
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
),
|
||||||
),
|
monitor=monitor,
|
||||||
monitor=monitor,
|
)
|
||||||
)
|
for monitor in coordinator.data
|
||||||
for monitor in coordinator.data
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Validate the user input allows us to connect."""
|
"""Validate the user input allows us to connect."""
|
||||||
errors: dict[str, str] = {}
|
errors: dict[str, str] = {}
|
||||||
response: UptimeRobotApiResponse | UptimeRobotApiError | None = None
|
response: UptimeRobotApiResponse | UptimeRobotApiError | None = None
|
||||||
uptime_robot_api = UptimeRobot(
|
key: str = data[CONF_API_KEY]
|
||||||
data[CONF_API_KEY], async_get_clientsession(self.hass)
|
if key.startswith("ur") or key.startswith("m"):
|
||||||
)
|
LOGGER.error("Wrong API key type detected, use the 'main' API key")
|
||||||
|
errors["base"] = "not_main_key"
|
||||||
|
return errors, None
|
||||||
|
uptime_robot_api = UptimeRobot(key, async_get_clientsession(self.hass))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = await uptime_robot_api.async_get_account_details()
|
response = await uptime_robot_api.async_get_account_details()
|
||||||
|
|
|
@ -13,7 +13,7 @@ LOGGER: Logger = getLogger(__package__)
|
||||||
COORDINATOR_UPDATE_INTERVAL: timedelta = timedelta(seconds=10)
|
COORDINATOR_UPDATE_INTERVAL: timedelta = timedelta(seconds=10)
|
||||||
|
|
||||||
DOMAIN: Final = "uptimerobot"
|
DOMAIN: Final = "uptimerobot"
|
||||||
PLATFORMS: Final = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
PLATFORMS: Final = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SWITCH]
|
||||||
|
|
||||||
ATTRIBUTION: Final = "Data provided by UptimeRobot"
|
ATTRIBUTION: Final = "Data provided by UptimeRobot"
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,9 @@ from pyuptimerobot import UptimeRobotMonitor
|
||||||
|
|
||||||
from homeassistant.helpers.device_registry import DeviceEntryType
|
from homeassistant.helpers.device_registry import DeviceEntryType
|
||||||
from homeassistant.helpers.entity import DeviceInfo, EntityDescription
|
from homeassistant.helpers.entity import DeviceInfo, EntityDescription
|
||||||
from homeassistant.helpers.update_coordinator import (
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
CoordinatorEntity,
|
|
||||||
DataUpdateCoordinator,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
from . import UptimeRobotDataUpdateCoordinator
|
||||||
from .const import ATTR_TARGET, ATTRIBUTION, DOMAIN
|
from .const import ATTR_TARGET, ATTRIBUTION, DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,10 +15,11 @@ class UptimeRobotEntity(CoordinatorEntity):
|
||||||
"""Base UptimeRobot entity."""
|
"""Base UptimeRobot entity."""
|
||||||
|
|
||||||
_attr_attribution = ATTRIBUTION
|
_attr_attribution = ATTRIBUTION
|
||||||
|
coordinator: UptimeRobotDataUpdateCoordinator
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: DataUpdateCoordinator,
|
coordinator: UptimeRobotDataUpdateCoordinator,
|
||||||
description: EntityDescription,
|
description: EntityDescription,
|
||||||
monitor: UptimeRobotMonitor,
|
monitor: UptimeRobotMonitor,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -40,6 +39,7 @@ class UptimeRobotEntity(CoordinatorEntity):
|
||||||
ATTR_TARGET: self.monitor.url,
|
ATTR_TARGET: self.monitor.url,
|
||||||
}
|
}
|
||||||
self._attr_unique_id = str(self.monitor.id)
|
self._attr_unique_id = str(self.monitor.id)
|
||||||
|
self.api = coordinator.api
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _monitors(self) -> list[UptimeRobotMonitor]:
|
def _monitors(self) -> list[UptimeRobotMonitor]:
|
||||||
|
|
|
@ -38,19 +38,17 @@ async def async_setup_entry(
|
||||||
"""Set up the UptimeRobot sensors."""
|
"""Set up the UptimeRobot sensors."""
|
||||||
coordinator: UptimeRobotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator: UptimeRobotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
UptimeRobotSensor(
|
||||||
UptimeRobotSensor(
|
coordinator,
|
||||||
coordinator,
|
SensorEntityDescription(
|
||||||
SensorEntityDescription(
|
key=str(monitor.id),
|
||||||
key=str(monitor.id),
|
name=monitor.friendly_name,
|
||||||
name=monitor.friendly_name,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
device_class="uptimerobot__monitor_status",
|
||||||
device_class="uptimerobot__monitor_status",
|
),
|
||||||
),
|
monitor=monitor,
|
||||||
monitor=monitor,
|
)
|
||||||
)
|
for monitor in coordinator.data
|
||||||
for monitor in coordinator.data
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"description": "You need to supply a read-only API key from UptimeRobot",
|
"description": "You need to supply the 'main' API key from UptimeRobot",
|
||||||
"data": {
|
"data": {
|
||||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reauth_confirm": {
|
"reauth_confirm": {
|
||||||
"title": "[%key:common::config_flow::title::reauth%]",
|
"title": "[%key:common::config_flow::title::reauth%]",
|
||||||
"description": "You need to supply a new read-only API key from UptimeRobot",
|
"description": "You need to supply a new 'main' API key from UptimeRobot",
|
||||||
"data": {
|
"data": {
|
||||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
|
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]",
|
"unknown": "[%key:common::config_flow::error::unknown%]",
|
||||||
|
"not_main_key": "Wrong API key type detected, use the 'main' API key",
|
||||||
"reauth_failed_matching_account": "The API key you provided does not match the account ID for existing configuration."
|
"reauth_failed_matching_account": "The API key you provided does not match the account ID for existing configuration."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
|
75
homeassistant/components/uptimerobot/switch.py
Normal file
75
homeassistant/components/uptimerobot/switch.py
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
"""UptimeRobot switch platform."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pyuptimerobot import UptimeRobotAuthenticationException, UptimeRobotException
|
||||||
|
|
||||||
|
from homeassistant.components.switch import (
|
||||||
|
SwitchDeviceClass,
|
||||||
|
SwitchEntity,
|
||||||
|
SwitchEntityDescription,
|
||||||
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import UptimeRobotDataUpdateCoordinator
|
||||||
|
from .const import API_ATTR_OK, DOMAIN, LOGGER
|
||||||
|
from .entity import UptimeRobotEntity
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
|
) -> None:
|
||||||
|
"""Set up the UptimeRobot switches."""
|
||||||
|
coordinator: UptimeRobotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
async_add_entities(
|
||||||
|
UptimeRobotSwitch(
|
||||||
|
coordinator,
|
||||||
|
SwitchEntityDescription(
|
||||||
|
key=str(monitor.id),
|
||||||
|
name=f"{monitor.friendly_name} Active",
|
||||||
|
device_class=SwitchDeviceClass.SWITCH,
|
||||||
|
),
|
||||||
|
monitor=monitor,
|
||||||
|
)
|
||||||
|
for monitor in coordinator.data
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UptimeRobotSwitch(UptimeRobotEntity, SwitchEntity):
|
||||||
|
"""Representation of a UptimeRobot switch."""
|
||||||
|
|
||||||
|
_attr_icon = "mdi:cog"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return True if the entity is on."""
|
||||||
|
return bool(self.monitor.status != 0)
|
||||||
|
|
||||||
|
async def _async_edit_monitor(self, **kwargs: Any) -> None:
|
||||||
|
"""Edit monitor status."""
|
||||||
|
try:
|
||||||
|
response = await self.api.async_edit_monitor(**kwargs)
|
||||||
|
except UptimeRobotAuthenticationException:
|
||||||
|
LOGGER.debug("API authentication error, calling reauth")
|
||||||
|
self.coordinator.config_entry.async_start_reauth(self.hass)
|
||||||
|
return
|
||||||
|
except UptimeRobotException as exception:
|
||||||
|
LOGGER.error("API exception: %s", exception)
|
||||||
|
return
|
||||||
|
|
||||||
|
if response.status != API_ATTR_OK:
|
||||||
|
LOGGER.error("API exception: %s", response.error.message, exc_info=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn off switch."""
|
||||||
|
await self._async_edit_monitor(id=self.monitor.id, status=1)
|
|
@ -9,6 +9,7 @@
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "Failed to connect",
|
"cannot_connect": "Failed to connect",
|
||||||
"invalid_api_key": "Invalid API key",
|
"invalid_api_key": "Invalid API key",
|
||||||
|
"not_main_key": "Wrong API key type detected, use the 'main' API key",
|
||||||
"reauth_failed_matching_account": "The API key you provided does not match the account ID for existing configuration.",
|
"reauth_failed_matching_account": "The API key you provided does not match the account ID for existing configuration.",
|
||||||
"unknown": "Unexpected error"
|
"unknown": "Unexpected error"
|
||||||
},
|
},
|
||||||
|
@ -17,14 +18,14 @@
|
||||||
"data": {
|
"data": {
|
||||||
"api_key": "API Key"
|
"api_key": "API Key"
|
||||||
},
|
},
|
||||||
"description": "You need to supply a new read-only API key from UptimeRobot",
|
"description": "You need to supply a new 'main' API key from UptimeRobot",
|
||||||
"title": "Reauthenticate Integration"
|
"title": "Reauthenticate Integration"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"data": {
|
"data": {
|
||||||
"api_key": "API Key"
|
"api_key": "API Key"
|
||||||
},
|
},
|
||||||
"description": "You need to supply a read-only API key from UptimeRobot"
|
"description": "You need to supply the 'main' API key from UptimeRobot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
MOCK_UPTIMEROBOT_API_KEY = "0242ac120003"
|
MOCK_UPTIMEROBOT_API_KEY = "u0242ac120003"
|
||||||
|
MOCK_UPTIMEROBOT_API_KEY_READ_ONLY = "ur0242ac120003"
|
||||||
MOCK_UPTIMEROBOT_EMAIL = "test@test.test"
|
MOCK_UPTIMEROBOT_EMAIL = "test@test.test"
|
||||||
MOCK_UPTIMEROBOT_UNIQUE_ID = "1234567890"
|
MOCK_UPTIMEROBOT_UNIQUE_ID = "1234567890"
|
||||||
|
|
||||||
|
@ -37,6 +38,14 @@ MOCK_UPTIMEROBOT_MONITOR = {
|
||||||
"type": 1,
|
"type": 1,
|
||||||
"url": "http://example.com",
|
"url": "http://example.com",
|
||||||
}
|
}
|
||||||
|
MOCK_UPTIMEROBOT_MONITOR_PAUSED = {
|
||||||
|
"id": 1234,
|
||||||
|
"friendly_name": "Test monitor",
|
||||||
|
"status": 0,
|
||||||
|
"type": 1,
|
||||||
|
"url": "http://example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA = {
|
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA = {
|
||||||
"domain": DOMAIN,
|
"domain": DOMAIN,
|
||||||
|
@ -45,11 +54,19 @@ MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA = {
|
||||||
"unique_id": MOCK_UPTIMEROBOT_UNIQUE_ID,
|
"unique_id": MOCK_UPTIMEROBOT_UNIQUE_ID,
|
||||||
"source": config_entries.SOURCE_USER,
|
"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,
|
||||||
|
"source": config_entries.SOURCE_USER,
|
||||||
|
}
|
||||||
|
|
||||||
STATE_UP = "up"
|
STATE_UP = "up"
|
||||||
|
|
||||||
UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY = "binary_sensor.test_monitor"
|
UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY = "binary_sensor.test_monitor"
|
||||||
UPTIMEROBOT_SENSOR_TEST_ENTITY = "sensor.test_monitor"
|
UPTIMEROBOT_SENSOR_TEST_ENTITY = "sensor.test_monitor"
|
||||||
|
UPTIMEROBOT_SWITCH_TEST_ENTITY = "switch.test_monitor_active"
|
||||||
|
|
||||||
|
|
||||||
class MockApiResponseKey(str, Enum):
|
class MockApiResponseKey(str, Enum):
|
||||||
|
|
|
@ -18,6 +18,7 @@ from homeassistant.data_entry_flow import (
|
||||||
from .common import (
|
from .common import (
|
||||||
MOCK_UPTIMEROBOT_ACCOUNT,
|
MOCK_UPTIMEROBOT_ACCOUNT,
|
||||||
MOCK_UPTIMEROBOT_API_KEY,
|
MOCK_UPTIMEROBOT_API_KEY,
|
||||||
|
MOCK_UPTIMEROBOT_API_KEY_READ_ONLY,
|
||||||
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA,
|
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA,
|
||||||
MOCK_UPTIMEROBOT_UNIQUE_ID,
|
MOCK_UPTIMEROBOT_UNIQUE_ID,
|
||||||
MockApiResponseKey,
|
MockApiResponseKey,
|
||||||
|
@ -56,6 +57,29 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_read_only(hass: HomeAssistant) -> None:
|
||||||
|
"""Test we get the form."""
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_get_account_details",
|
||||||
|
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ACCOUNT),
|
||||||
|
):
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{CONF_API_KEY: MOCK_UPTIMEROBOT_API_KEY_READ_ONLY},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result2["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result2["errors"]["base"] == "not_main_key"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"exception,error_key",
|
"exception,error_key",
|
||||||
[
|
[
|
||||||
|
|
|
@ -19,6 +19,7 @@ from homeassistant.util import dt
|
||||||
|
|
||||||
from .common import (
|
from .common import (
|
||||||
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA,
|
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA,
|
||||||
|
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA_KEY_READ_ONLY,
|
||||||
MOCK_UPTIMEROBOT_MONITOR,
|
MOCK_UPTIMEROBOT_MONITOR,
|
||||||
UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY,
|
UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY,
|
||||||
MockApiResponseKey,
|
MockApiResponseKey,
|
||||||
|
@ -62,6 +63,39 @@ async def test_reauthentication_trigger_in_setup(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauthentication_trigger_key_read_only(
|
||||||
|
hass: HomeAssistant, caplog: LogCaptureFixture
|
||||||
|
):
|
||||||
|
"""Test reauthentication trigger."""
|
||||||
|
mock_config_entry = MockConfigEntry(
|
||||||
|
**MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA_KEY_READ_ONLY
|
||||||
|
)
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
flows = hass.config_entries.flow.async_progress()
|
||||||
|
|
||||||
|
assert mock_config_entry.state == config_entries.ConfigEntryState.SETUP_ERROR
|
||||||
|
assert (
|
||||||
|
mock_config_entry.reason
|
||||||
|
== "Wrong API key type detected, use the 'main' API key"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(flows) == 1
|
||||||
|
flow = flows[0]
|
||||||
|
assert flow["step_id"] == "reauth_confirm"
|
||||||
|
assert flow["handler"] == DOMAIN
|
||||||
|
assert flow["context"]["source"] == config_entries.SOURCE_REAUTH
|
||||||
|
assert flow["context"]["entry_id"] == mock_config_entry.entry_id
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Config entry 'test@test.test' for uptimerobot integration could not authenticate"
|
||||||
|
in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_reauthentication_trigger_after_setup(
|
async def test_reauthentication_trigger_after_setup(
|
||||||
hass: HomeAssistant, caplog: LogCaptureFixture
|
hass: HomeAssistant, caplog: LogCaptureFixture
|
||||||
):
|
):
|
||||||
|
|
160
tests/components/uptimerobot/test_switch.py
Normal file
160
tests/components/uptimerobot/test_switch.py
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
"""Test UptimeRobot switch."""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from pyuptimerobot import UptimeRobotAuthenticationException
|
||||||
|
|
||||||
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
STATE_OFF,
|
||||||
|
STATE_ON,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from .common import (
|
||||||
|
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA,
|
||||||
|
MOCK_UPTIMEROBOT_MONITOR,
|
||||||
|
MOCK_UPTIMEROBOT_MONITOR_PAUSED,
|
||||||
|
UPTIMEROBOT_SWITCH_TEST_ENTITY,
|
||||||
|
MockApiResponseKey,
|
||||||
|
mock_uptimerobot_api_response,
|
||||||
|
setup_uptimerobot_integration,
|
||||||
|
)
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_presentation(hass: HomeAssistant) -> None:
|
||||||
|
"""Test the presenstation of UptimeRobot sensors."""
|
||||||
|
await setup_uptimerobot_integration(hass)
|
||||||
|
|
||||||
|
entity = hass.states.get(UPTIMEROBOT_SWITCH_TEST_ENTITY)
|
||||||
|
|
||||||
|
assert entity.state == STATE_ON
|
||||||
|
assert entity.attributes["icon"] == "mdi:cog"
|
||||||
|
assert entity.attributes["target"] == MOCK_UPTIMEROBOT_MONITOR["url"]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_off(hass: HomeAssistant) -> None:
|
||||||
|
"""Test entity unaviable on update failure."""
|
||||||
|
|
||||||
|
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=[MOCK_UPTIMEROBOT_MONITOR_PAUSED]
|
||||||
|
),
|
||||||
|
), patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_edit_monitor",
|
||||||
|
return_value=mock_uptimerobot_api_response(),
|
||||||
|
):
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: UPTIMEROBOT_SWITCH_TEST_ENTITY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
entity = hass.states.get(UPTIMEROBOT_SWITCH_TEST_ENTITY)
|
||||||
|
assert entity.state == STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_on(hass: HomeAssistant) -> None:
|
||||||
|
"""Test entity unaviable on update failure."""
|
||||||
|
|
||||||
|
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=[MOCK_UPTIMEROBOT_MONITOR]),
|
||||||
|
), patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_edit_monitor",
|
||||||
|
return_value=mock_uptimerobot_api_response(),
|
||||||
|
):
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: UPTIMEROBOT_SWITCH_TEST_ENTITY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
entity = hass.states.get(UPTIMEROBOT_SWITCH_TEST_ENTITY)
|
||||||
|
assert entity.state == STATE_ON
|
||||||
|
|
||||||
|
|
||||||
|
async def test_authentication_error(hass: HomeAssistant, caplog) -> None:
|
||||||
|
"""Test authentication error turning switch on/off."""
|
||||||
|
await setup_uptimerobot_integration(hass)
|
||||||
|
|
||||||
|
entity = hass.states.get(UPTIMEROBOT_SWITCH_TEST_ENTITY)
|
||||||
|
assert entity.state == STATE_ON
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_edit_monitor",
|
||||||
|
side_effect=UptimeRobotAuthenticationException,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.config_entries.ConfigEntry.async_start_reauth"
|
||||||
|
) as config_entry_reauth:
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: UPTIMEROBOT_SWITCH_TEST_ENTITY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert config_entry_reauth.assert_called
|
||||||
|
|
||||||
|
|
||||||
|
async def test_refresh_data(hass: HomeAssistant, caplog) -> None:
|
||||||
|
"""Test authentication error turning switch on/off."""
|
||||||
|
await setup_uptimerobot_integration(hass)
|
||||||
|
|
||||||
|
entity = hass.states.get(UPTIMEROBOT_SWITCH_TEST_ENTITY)
|
||||||
|
assert entity.state == STATE_ON
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.helpers.update_coordinator.DataUpdateCoordinator.async_request_refresh"
|
||||||
|
) as coordinator_refresh:
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: UPTIMEROBOT_SWITCH_TEST_ENTITY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert coordinator_refresh.assert_called
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_api_failure(hass: HomeAssistant, caplog) -> None:
|
||||||
|
"""Test general exception turning switch on/off."""
|
||||||
|
await setup_uptimerobot_integration(hass)
|
||||||
|
|
||||||
|
entity = hass.states.get(UPTIMEROBOT_SWITCH_TEST_ENTITY)
|
||||||
|
assert entity.state == STATE_ON
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_edit_monitor",
|
||||||
|
return_value=mock_uptimerobot_api_response(key=MockApiResponseKey.ERROR),
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: UPTIMEROBOT_SWITCH_TEST_ENTITY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "API exception" in caplog.text
|
Loading…
Add table
Add a link
Reference in a new issue