Add detailed status for UptimeRobot (#64879)
Co-authored-by: Joakim Sørensen <hi@ludeeus.dev>
This commit is contained in:
parent
eb5c6076af
commit
3f12ce06af
10 changed files with 178 additions and 23 deletions
|
@ -999,8 +999,8 @@ homeassistant/components/updater/* @home-assistant/core
|
|||
tests/components/updater/* @home-assistant/core
|
||||
homeassistant/components/upnp/* @StevenLooman @ehendrix23
|
||||
tests/components/upnp/* @StevenLooman @ehendrix23
|
||||
homeassistant/components/uptimerobot/* @ludeeus
|
||||
tests/components/uptimerobot/* @ludeeus
|
||||
homeassistant/components/uptimerobot/* @ludeeus @chemelli74
|
||||
tests/components/uptimerobot/* @ludeeus @chemelli74
|
||||
homeassistant/components/usb/* @bdraco
|
||||
tests/components/usb/* @bdraco
|
||||
homeassistant/components/usgs_earthquakes_feed/* @exxamalte
|
||||
|
|
|
@ -13,7 +13,7 @@ LOGGER: Logger = getLogger(__package__)
|
|||
COORDINATOR_UPDATE_INTERVAL: timedelta = timedelta(seconds=10)
|
||||
|
||||
DOMAIN: Final = "uptimerobot"
|
||||
PLATFORMS: Final = [Platform.BINARY_SENSOR]
|
||||
PLATFORMS: Final = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
ATTRIBUTION: Final = "Data provided by UptimeRobot"
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"pyuptimerobot==21.11.0"
|
||||
],
|
||||
"codeowners": [
|
||||
"@ludeeus"
|
||||
"@ludeeus", "@chemelli74"
|
||||
],
|
||||
"quality_scale": "platinum",
|
||||
"iot_class": "cloud_polling",
|
||||
|
|
68
homeassistant/components/uptimerobot/sensor.py
Normal file
68
homeassistant/components/uptimerobot/sensor.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
"""UptimeRobot sensor platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TypedDict
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import UptimeRobotDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .entity import UptimeRobotEntity
|
||||
|
||||
|
||||
class StatusValue(TypedDict):
|
||||
"""Sensor details."""
|
||||
|
||||
value: str
|
||||
icon: str
|
||||
|
||||
|
||||
SENSORS_INFO = {
|
||||
0: StatusValue(value="pause", icon="mdi:television-pause"),
|
||||
1: StatusValue(value="not_checked_yet", icon="mdi:television"),
|
||||
2: StatusValue(value="up", icon="mdi:television-shimmer"),
|
||||
8: StatusValue(value="seems_down", icon="mdi:television-off"),
|
||||
9: StatusValue(value="down", icon="mdi:television-off"),
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the UptimeRobot sensors."""
|
||||
coordinator: UptimeRobotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
[
|
||||
UptimeRobotSensor(
|
||||
coordinator,
|
||||
SensorEntityDescription(
|
||||
key=str(monitor.id),
|
||||
name=monitor.friendly_name,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
device_class="uptimerobot__monitor_status",
|
||||
),
|
||||
monitor=monitor,
|
||||
)
|
||||
for monitor in coordinator.data
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class UptimeRobotSensor(UptimeRobotEntity, SensorEntity):
|
||||
"""Representation of a UptimeRobot sensor."""
|
||||
|
||||
@property
|
||||
def native_value(self) -> str:
|
||||
"""Return the status of the monitor."""
|
||||
return SENSORS_INFO[self.monitor.status]["value"]
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Return the status of the monitor."""
|
||||
return SENSORS_INFO[self.monitor.status]["icon"]
|
11
homeassistant/components/uptimerobot/strings.sensor.json
Normal file
11
homeassistant/components/uptimerobot/strings.sensor.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"state": {
|
||||
"uptimerobot__monitor_status": {
|
||||
"pause": "Pause",
|
||||
"not_checked_yet": "Not checked yet",
|
||||
"up": "Up",
|
||||
"seems_down": "Seems down",
|
||||
"down": "Down"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"state": {
|
||||
"uptimerobot__monitor_status": {
|
||||
"down": "Down",
|
||||
"not_checked_yet": "Not checked yet",
|
||||
"pause": "Pause",
|
||||
"seems_down": "Seems down",
|
||||
"up": "Up"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,7 +46,10 @@ MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA = {
|
|||
"source": config_entries.SOURCE_USER,
|
||||
}
|
||||
|
||||
UPTIMEROBOT_TEST_ENTITY = "binary_sensor.test_monitor"
|
||||
STATE_UP = "up"
|
||||
|
||||
UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY = "binary_sensor.test_monitor"
|
||||
UPTIMEROBOT_SENSOR_TEST_ENTITY = "sensor.test_monitor"
|
||||
|
||||
|
||||
class MockApiResponseKey(str, Enum):
|
||||
|
@ -94,7 +97,8 @@ async def setup_uptimerobot_integration(hass: HomeAssistant) -> MockConfigEntry:
|
|||
assert await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(UPTIMEROBOT_TEST_ENTITY).state == STATE_ON
|
||||
assert hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state == STATE_ON
|
||||
assert hass.states.get(UPTIMEROBOT_SENSOR_TEST_ENTITY).state == STATE_UP
|
||||
assert mock_entry.state == config_entries.ConfigEntryState.LOADED
|
||||
|
||||
return mock_entry
|
||||
|
|
|
@ -15,7 +15,7 @@ from homeassistant.util import dt
|
|||
|
||||
from .common import (
|
||||
MOCK_UPTIMEROBOT_MONITOR,
|
||||
UPTIMEROBOT_TEST_ENTITY,
|
||||
UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY,
|
||||
setup_uptimerobot_integration,
|
||||
)
|
||||
|
||||
|
@ -26,7 +26,7 @@ async def test_presentation(hass: HomeAssistant) -> None:
|
|||
"""Test the presenstation of UptimeRobot binary_sensors."""
|
||||
await setup_uptimerobot_integration(hass)
|
||||
|
||||
entity = hass.states.get(UPTIMEROBOT_TEST_ENTITY)
|
||||
entity = hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY)
|
||||
|
||||
assert entity.state == STATE_ON
|
||||
assert entity.attributes["device_class"] == BinarySensorDeviceClass.CONNECTIVITY
|
||||
|
@ -38,7 +38,7 @@ async def test_unaviable_on_update_failure(hass: HomeAssistant) -> None:
|
|||
"""Test entity unaviable on update failure."""
|
||||
await setup_uptimerobot_integration(hass)
|
||||
|
||||
entity = hass.states.get(UPTIMEROBOT_TEST_ENTITY)
|
||||
entity = hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY)
|
||||
assert entity.state == STATE_ON
|
||||
|
||||
with patch(
|
||||
|
@ -48,5 +48,5 @@ async def test_unaviable_on_update_failure(hass: HomeAssistant) -> None:
|
|||
async_fire_time_changed(hass, dt.utcnow() + COORDINATOR_UPDATE_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity = hass.states.get(UPTIMEROBOT_TEST_ENTITY)
|
||||
entity = hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY)
|
||||
assert entity.state == STATE_UNAVAILABLE
|
||||
|
|
|
@ -20,7 +20,7 @@ from homeassistant.util import dt
|
|||
from .common import (
|
||||
MOCK_UPTIMEROBOT_CONFIG_ENTRY_DATA,
|
||||
MOCK_UPTIMEROBOT_MONITOR,
|
||||
UPTIMEROBOT_TEST_ENTITY,
|
||||
UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY,
|
||||
MockApiResponseKey,
|
||||
mock_uptimerobot_api_response,
|
||||
setup_uptimerobot_integration,
|
||||
|
@ -68,7 +68,7 @@ async def test_reauthentication_trigger_after_setup(
|
|||
"""Test reauthentication trigger."""
|
||||
mock_config_entry = await setup_uptimerobot_integration(hass)
|
||||
|
||||
binary_sensor = hass.states.get(UPTIMEROBOT_TEST_ENTITY)
|
||||
binary_sensor = hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY)
|
||||
assert mock_config_entry.state == config_entries.ConfigEntryState.LOADED
|
||||
assert binary_sensor.state == STATE_ON
|
||||
|
||||
|
@ -81,7 +81,10 @@ async def test_reauthentication_trigger_after_setup(
|
|||
await hass.async_block_till_done()
|
||||
|
||||
flows = hass.config_entries.flow.async_progress()
|
||||
assert hass.states.get(UPTIMEROBOT_TEST_ENTITY).state == STATE_UNAVAILABLE
|
||||
assert (
|
||||
hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state
|
||||
== STATE_UNAVAILABLE
|
||||
)
|
||||
|
||||
assert "Authentication failed while fetching uptimerobot data" in caplog.text
|
||||
|
||||
|
@ -107,7 +110,7 @@ async def test_integration_reload(hass: HomeAssistant):
|
|||
|
||||
entry = hass.config_entries.async_get_entry(mock_entry.entry_id)
|
||||
assert entry.state == config_entries.ConfigEntryState.LOADED
|
||||
assert hass.states.get(UPTIMEROBOT_TEST_ENTITY).state == STATE_ON
|
||||
assert hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state == STATE_ON
|
||||
|
||||
|
||||
async def test_update_errors(hass: HomeAssistant, caplog: LogCaptureFixture):
|
||||
|
@ -120,7 +123,10 @@ async def test_update_errors(hass: HomeAssistant, caplog: LogCaptureFixture):
|
|||
):
|
||||
async_fire_time_changed(hass, dt.utcnow() + COORDINATOR_UPDATE_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(UPTIMEROBOT_TEST_ENTITY).state == STATE_UNAVAILABLE
|
||||
assert (
|
||||
hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state
|
||||
== STATE_UNAVAILABLE
|
||||
)
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||
|
@ -128,7 +134,7 @@ async def test_update_errors(hass: HomeAssistant, caplog: LogCaptureFixture):
|
|||
):
|
||||
async_fire_time_changed(hass, dt.utcnow() + COORDINATOR_UPDATE_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(UPTIMEROBOT_TEST_ENTITY).state == STATE_ON
|
||||
assert hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state == STATE_ON
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||
|
@ -136,7 +142,10 @@ async def test_update_errors(hass: HomeAssistant, caplog: LogCaptureFixture):
|
|||
):
|
||||
async_fire_time_changed(hass, dt.utcnow() + COORDINATOR_UPDATE_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(UPTIMEROBOT_TEST_ENTITY).state == STATE_UNAVAILABLE
|
||||
assert (
|
||||
hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state
|
||||
== STATE_UNAVAILABLE
|
||||
)
|
||||
|
||||
assert "Error fetching uptimerobot data: test error from API" in caplog.text
|
||||
|
||||
|
@ -152,8 +161,8 @@ async def test_device_management(hass: HomeAssistant):
|
|||
assert devices[0].identifiers == {(DOMAIN, "1234")}
|
||||
assert devices[0].name == "Test monitor"
|
||||
|
||||
assert hass.states.get(UPTIMEROBOT_TEST_ENTITY).state == STATE_ON
|
||||
assert hass.states.get(f"{UPTIMEROBOT_TEST_ENTITY}_2") is None
|
||||
assert hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state == STATE_ON
|
||||
assert hass.states.get(f"{UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY}_2") is None
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||
|
@ -169,8 +178,10 @@ async def test_device_management(hass: HomeAssistant):
|
|||
assert devices[0].identifiers == {(DOMAIN, "1234")}
|
||||
assert devices[1].identifiers == {(DOMAIN, "12345")}
|
||||
|
||||
assert hass.states.get(UPTIMEROBOT_TEST_ENTITY).state == STATE_ON
|
||||
assert hass.states.get(f"{UPTIMEROBOT_TEST_ENTITY}_2").state == STATE_ON
|
||||
assert hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state == STATE_ON
|
||||
assert (
|
||||
hass.states.get(f"{UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY}_2").state == STATE_ON
|
||||
)
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||
|
@ -183,5 +194,5 @@ async def test_device_management(hass: HomeAssistant):
|
|||
assert len(devices) == 1
|
||||
assert devices[0].identifiers == {(DOMAIN, "1234")}
|
||||
|
||||
assert hass.states.get(UPTIMEROBOT_TEST_ENTITY).state == STATE_ON
|
||||
assert hass.states.get(f"{UPTIMEROBOT_TEST_ENTITY}_2") is None
|
||||
assert hass.states.get(UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY).state == STATE_ON
|
||||
assert hass.states.get(f"{UPTIMEROBOT_BINARY_SENSOR_TEST_ENTITY}_2") is None
|
||||
|
|
50
tests/components/uptimerobot/test_sensor.py
Normal file
50
tests/components/uptimerobot/test_sensor.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
"""Test UptimeRobot sensor."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyuptimerobot import UptimeRobotAuthenticationException
|
||||
|
||||
from homeassistant.components.uptimerobot.const import COORDINATOR_UPDATE_INTERVAL
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util import dt
|
||||
|
||||
from .common import (
|
||||
MOCK_UPTIMEROBOT_MONITOR,
|
||||
STATE_UP,
|
||||
UPTIMEROBOT_SENSOR_TEST_ENTITY,
|
||||
setup_uptimerobot_integration,
|
||||
)
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
SENSOR_ICON = "mdi:television-shimmer"
|
||||
|
||||
|
||||
async def test_presentation(hass: HomeAssistant) -> None:
|
||||
"""Test the presenstation of UptimeRobot sensors."""
|
||||
await setup_uptimerobot_integration(hass)
|
||||
|
||||
entity = hass.states.get(UPTIMEROBOT_SENSOR_TEST_ENTITY)
|
||||
|
||||
assert entity.state == STATE_UP
|
||||
assert entity.attributes["icon"] == SENSOR_ICON
|
||||
assert entity.attributes["target"] == MOCK_UPTIMEROBOT_MONITOR["url"]
|
||||
|
||||
|
||||
async def test_unaviable_on_update_failure(hass: HomeAssistant) -> None:
|
||||
"""Test entity unaviable on update failure."""
|
||||
await setup_uptimerobot_integration(hass)
|
||||
|
||||
entity = hass.states.get(UPTIMEROBOT_SENSOR_TEST_ENTITY)
|
||||
assert entity.state == STATE_UP
|
||||
|
||||
with patch(
|
||||
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||
side_effect=UptimeRobotAuthenticationException,
|
||||
):
|
||||
async_fire_time_changed(hass, dt.utcnow() + COORDINATOR_UPDATE_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity = hass.states.get(UPTIMEROBOT_SENSOR_TEST_ENTITY)
|
||||
assert entity.state == STATE_UNAVAILABLE
|
Loading…
Add table
Reference in a new issue