Bump pyuptimerobot to 21.8.1 (#53995)

* Bump pyuptimerobot to 21.08.0

* pylint

* bump to 21.8.1

* Uppdate strings

* Update homeassistant/components/uptimerobot/strings.json

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Joakim Sørensen 2021-08-05 12:13:47 +02:00 committed by GitHub
parent 1bc3c743db
commit 4a37ff2dda
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 73 additions and 102 deletions

View file

@ -1,44 +1,35 @@
"""The Uptime Robot integration.""" """The Uptime Robot integration."""
from __future__ import annotations from __future__ import annotations
import async_timeout from pyuptimerobot import UptimeRobot, UptimeRobotException, UptimeRobotMonitor
from pyuptimerobot import UptimeRobot
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import ( from .const import API_ATTR_OK, COORDINATOR_UPDATE_INTERVAL, DOMAIN, LOGGER, PLATFORMS
API_ATTR_MONITORS,
API_ATTR_OK,
API_ATTR_STAT,
CONNECTION_ERROR,
COORDINATOR_UPDATE_INTERVAL,
DOMAIN,
LOGGER,
PLATFORMS,
MonitorData,
)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Uptime Robot from a config entry.""" """Set up Uptime Robot from a config entry."""
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
uptime_robot_api = UptimeRobot() uptime_robot_api = UptimeRobot(
entry.data[CONF_API_KEY], async_get_clientsession(hass)
)
async def async_update_data() -> list[MonitorData]: async def async_update_data() -> list[UptimeRobotMonitor]:
"""Fetch data from API UptimeRobot API.""" """Fetch data from API UptimeRobot API."""
async with async_timeout.timeout(10): try:
monitors = await hass.async_add_executor_job( response = await uptime_robot_api.async_get_monitors()
uptime_robot_api.getMonitors, entry.data[CONF_API_KEY] except UptimeRobotException as exception:
) raise UpdateFailed(exception) from exception
if not monitors or monitors.get(API_ATTR_STAT) != API_ATTR_OK: else:
raise UpdateFailed(CONNECTION_ERROR) if response.status == API_ATTR_OK:
return [ monitors: list[UptimeRobotMonitor] = response.data
MonitorData.from_dict(monitor) return monitors
for monitor in monitors.get(API_ATTR_MONITORS, []) raise UpdateFailed(response.error.message)
]
hass.data[DOMAIN][entry.entry_id] = coordinator = DataUpdateCoordinator( hass.data[DOMAIN][entry.entry_id] = coordinator = DataUpdateCoordinator(
hass, hass,

View file

@ -50,7 +50,7 @@ async def async_setup_entry(
coordinator, coordinator,
BinarySensorEntityDescription( BinarySensorEntityDescription(
key=str(monitor.id), key=str(monitor.id),
name=monitor.name, name=monitor.friendly_name,
device_class=DEVICE_CLASS_CONNECTIVITY, device_class=DEVICE_CLASS_CONNECTIVITY,
), ),
target=monitor.url, target=monitor.url,

View file

@ -1,7 +1,7 @@
"""Config flow for Uptime Robot integration.""" """Config flow for Uptime Robot integration."""
from __future__ import annotations from __future__ import annotations
from pyuptimerobot import UptimeRobot from pyuptimerobot import UptimeRobot, UptimeRobotAccount, UptimeRobotException
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
@ -9,24 +9,26 @@ from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from .const import API_ATTR_OK, API_ATTR_STAT, DOMAIN, LOGGER from .const import API_ATTR_OK, DOMAIN, LOGGER
STEP_USER_DATA_SCHEMA = vol.Schema({vol.Required(CONF_API_KEY): str}) STEP_USER_DATA_SCHEMA = vol.Schema({vol.Required(CONF_API_KEY): str})
async def validate_input(hass: HomeAssistant, data: ConfigType) -> None: async def validate_input(hass: HomeAssistant, data: ConfigType) -> UptimeRobotAccount:
"""Validate the user input allows us to connect.""" """Validate the user input allows us to connect."""
uptime_robot_api = UptimeRobot(data[CONF_API_KEY], async_get_clientsession(hass))
uptime_robot_api = UptimeRobot() try:
response = await uptime_robot_api.async_get_account_details()
monitors = await hass.async_add_executor_job( except UptimeRobotException as exception:
uptime_robot_api.getMonitors, data[CONF_API_KEY] raise CannotConnect(exception) from exception
) else:
if response.status == API_ATTR_OK:
if not monitors or monitors.get(API_ATTR_STAT) != API_ATTR_OK: return response.data
raise CannotConnect("Error communicating with Uptime Robot API") raise CannotConnect(response.error.message)
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@ -43,14 +45,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
) )
try: try:
await validate_input(self.hass, user_input) account = await validate_input(self.hass, user_input)
except CannotConnect: except CannotConnect:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
LOGGER.exception("Unexpected exception") LOGGER.exception("Unexpected exception")
errors["base"] = "unknown" errors["base"] = "unknown"
else: else:
return self.async_create_entry(title="", data=user_input) return self.async_create_entry(title=account.email, data=user_input)
return self.async_show_form( return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
@ -65,9 +67,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
) )
return self.async_abort(reason="already_configured") return self.async_abort(reason="already_configured")
return self.async_create_entry( imported_config = {CONF_API_KEY: import_config[CONF_API_KEY]}
title="", data={CONF_API_KEY: import_config[CONF_API_KEY]}
) account = await validate_input(self.hass, imported_config)
return self.async_create_entry(title=account.email, data=imported_config)
class CannotConnect(HomeAssistantError): class CannotConnect(HomeAssistantError):

View file

@ -1,9 +1,7 @@
"""Constants for the Uptime Robot integration.""" """Constants for the Uptime Robot integration."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import Enum
from logging import Logger, getLogger from logging import Logger, getLogger
from typing import Final from typing import Final
@ -14,42 +12,8 @@ COORDINATOR_UPDATE_INTERVAL: timedelta = timedelta(seconds=60)
DOMAIN: Final = "uptimerobot" DOMAIN: Final = "uptimerobot"
PLATFORMS: Final = ["binary_sensor"] PLATFORMS: Final = ["binary_sensor"]
CONNECTION_ERROR: Final = "Error connecting to the Uptime Robot API"
ATTRIBUTION: Final = "Data provided by Uptime Robot" ATTRIBUTION: Final = "Data provided by Uptime Robot"
ATTR_TARGET: Final = "target" ATTR_TARGET: Final = "target"
API_ATTR_STAT: Final = "stat"
API_ATTR_OK: Final = "ok" API_ATTR_OK: Final = "ok"
API_ATTR_MONITORS: Final = "monitors"
class MonitorType(Enum):
"""Monitors type."""
HTTP = 1
keyword = 2
ping = 3
@dataclass
class MonitorData:
"""Dataclass for monitors."""
id: int
status: int
url: str
name: str
type: MonitorType
@staticmethod
def from_dict(monitor: dict) -> MonitorData:
"""Create a new monitor from a dict."""
return MonitorData(
id=monitor["id"],
status=monitor["status"],
url=monitor["url"],
name=monitor["friendly_name"],
type=MonitorType(monitor["type"]),
)

View file

@ -1,6 +1,8 @@
"""Base UptimeRobot entity.""" """Base UptimeRobot entity."""
from __future__ import annotations from __future__ import annotations
from pyuptimerobot import UptimeRobotMonitor
from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.const import ATTR_ATTRIBUTION
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 (
@ -8,7 +10,7 @@ from homeassistant.helpers.update_coordinator import (
DataUpdateCoordinator, DataUpdateCoordinator,
) )
from .const import ATTR_TARGET, ATTRIBUTION, DOMAIN, MonitorData from .const import ATTR_TARGET, ATTRIBUTION, DOMAIN
class UptimeRobotEntity(CoordinatorEntity): class UptimeRobotEntity(CoordinatorEntity):
@ -48,12 +50,12 @@ class UptimeRobotEntity(CoordinatorEntity):
return {} return {}
@property @property
def monitors(self) -> list[MonitorData]: def monitors(self) -> list[UptimeRobotMonitor]:
"""Return all monitors.""" """Return all monitors."""
return self.coordinator.data or [] return self.coordinator.data or []
@property @property
def monitor(self) -> MonitorData | None: def monitor(self) -> UptimeRobotMonitor | None:
"""Return the monitor for this entity.""" """Return the monitor for this entity."""
return next( return next(
( (
@ -67,7 +69,8 @@ class UptimeRobotEntity(CoordinatorEntity):
@property @property
def monitor_available(self) -> bool: def monitor_available(self) -> bool:
"""Returtn if the monitor is available.""" """Returtn if the monitor is available."""
return self.monitor.status == 2 if self.monitor else False status: bool = self.monitor.status == 2 if self.monitor else False
return status
@property @property
def available(self) -> bool: def available(self) -> bool:

View file

@ -3,7 +3,7 @@
"name": "Uptime Robot", "name": "Uptime Robot",
"documentation": "https://www.home-assistant.io/integrations/uptimerobot", "documentation": "https://www.home-assistant.io/integrations/uptimerobot",
"requirements": [ "requirements": [
"pyuptimerobot==0.0.5" "pyuptimerobot==21.8.1"
], ],
"codeowners": [ "codeowners": [
"@ludeeus" "@ludeeus"

View file

@ -12,7 +12,7 @@
"unknown": "[%key:common::config_flow::error::unknown%]" "unknown": "[%key:common::config_flow::error::unknown%]"
}, },
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]" "already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
} }
} }
} }

View file

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Device is already configured" "already_configured": "Account already configured"
}, },
"error": { "error": {
"cannot_connect": "Failed to connect", "cannot_connect": "Failed to connect",

View file

@ -1948,7 +1948,7 @@ pytradfri[async]==7.0.6
pytrafikverket==0.1.6.2 pytrafikverket==0.1.6.2
# homeassistant.components.uptimerobot # homeassistant.components.uptimerobot
pyuptimerobot==0.0.5 pyuptimerobot==21.8.1
# homeassistant.components.keyboard # homeassistant.components.keyboard
# pyuserinput==0.1.11 # pyuserinput==0.1.11

View file

@ -1077,7 +1077,7 @@ pytraccar==0.9.0
pytradfri[async]==7.0.6 pytradfri[async]==7.0.6
# homeassistant.components.uptimerobot # homeassistant.components.uptimerobot
pyuptimerobot==0.0.5 pyuptimerobot==21.8.1
# homeassistant.components.vera # homeassistant.components.vera
pyvera==0.3.13 pyvera==0.3.13

View file

@ -1,13 +1,10 @@
"""Test the Uptime Robot config flow.""" """Test the Uptime Robot config flow."""
from unittest.mock import patch from unittest.mock import patch
from pyuptimerobot import UptimeRobotApiResponse
from homeassistant import config_entries, setup from homeassistant import config_entries, setup
from homeassistant.components.uptimerobot.const import ( from homeassistant.components.uptimerobot.const import DOMAIN
API_ATTR_MONITORS,
API_ATTR_OK,
API_ATTR_STAT,
DOMAIN,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import ( from homeassistant.data_entry_flow import (
RESULT_TYPE_ABORT, RESULT_TYPE_ABORT,
@ -26,8 +23,13 @@ async def test_form(hass: HomeAssistant) -> None:
assert result["errors"] == {} assert result["errors"] == {}
with patch( with patch(
"pyuptimerobot.UptimeRobot.getMonitors", "pyuptimerobot.UptimeRobot.async_get_account_details",
return_value={API_ATTR_STAT: API_ATTR_OK, API_ATTR_MONITORS: []}, return_value=UptimeRobotApiResponse.from_dict(
{
"stat": "ok",
"account": {"email": "test@test.test"},
}
),
), patch( ), patch(
"homeassistant.components.uptimerobot.async_setup_entry", "homeassistant.components.uptimerobot.async_setup_entry",
return_value=True, return_value=True,
@ -39,7 +41,7 @@ async def test_form(hass: HomeAssistant) -> None:
await hass.async_block_till_done() await hass.async_block_till_done()
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
assert result2["title"] == "" assert result2["title"] == "test@test.test"
assert result2["data"] == {"api_key": "1234"} assert result2["data"] == {"api_key": "1234"}
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
@ -50,7 +52,10 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None:
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("pyuptimerobot.UptimeRobot.getMonitors", return_value=None): with patch(
"pyuptimerobot.UptimeRobot.async_get_account_details",
return_value=UptimeRobotApiResponse.from_dict({"stat": "fail", "error": {}}),
):
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"api_key": "1234"}, {"api_key": "1234"},
@ -63,8 +68,13 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None:
async def test_flow_import(hass): async def test_flow_import(hass):
"""Test an import flow.""" """Test an import flow."""
with patch( with patch(
"pyuptimerobot.UptimeRobot.getMonitors", "pyuptimerobot.UptimeRobot.async_get_account_details",
return_value={API_ATTR_STAT: API_ATTR_OK, API_ATTR_MONITORS: []}, return_value=UptimeRobotApiResponse.from_dict(
{
"stat": "ok",
"account": {"email": "test@test.test"},
}
),
), patch( ), patch(
"homeassistant.components.uptimerobot.async_setup_entry", "homeassistant.components.uptimerobot.async_setup_entry",
return_value=True, return_value=True,
@ -81,8 +91,8 @@ async def test_flow_import(hass):
assert result["data"] == {"api_key": "1234"} assert result["data"] == {"api_key": "1234"}
with patch( with patch(
"pyuptimerobot.UptimeRobot.getMonitors", "pyuptimerobot.UptimeRobot.async_get_account_details",
return_value={API_ATTR_STAT: API_ATTR_OK, API_ATTR_MONITORS: []}, return_value=UptimeRobotApiResponse.from_dict({"stat": "ok", "monitors": []}),
), patch( ), patch(
"homeassistant.components.uptimerobot.async_setup_entry", "homeassistant.components.uptimerobot.async_setup_entry",
return_value=True, return_value=True,