Update doorbird error notification to be a repair flow (#122987)
This commit is contained in:
parent
3b462906d9
commit
cb37ae6608
5 changed files with 153 additions and 15 deletions
|
@ -3,11 +3,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
|
||||
from aiohttp import ClientResponseError
|
||||
from doorbirdpy import DoorBird
|
||||
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_NAME,
|
||||
|
@ -17,6 +17,7 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
@ -30,6 +31,8 @@ CONF_CUSTOM_URL = "hass_url_override"
|
|||
|
||||
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the DoorBird component."""
|
||||
|
@ -68,7 +71,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: DoorBirdConfigEntry) ->
|
|||
door_bird_data = DoorBirdData(door_station, info, event_entity_ids)
|
||||
door_station.update_events(events)
|
||||
# Subscribe to doorbell or motion events
|
||||
if not await _async_register_events(hass, door_station):
|
||||
if not await _async_register_events(hass, door_station, entry):
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(_update_listener))
|
||||
|
@ -84,24 +87,30 @@ async def async_unload_entry(hass: HomeAssistant, entry: DoorBirdConfigEntry) ->
|
|||
|
||||
|
||||
async def _async_register_events(
|
||||
hass: HomeAssistant, door_station: ConfiguredDoorBird
|
||||
hass: HomeAssistant, door_station: ConfiguredDoorBird, entry: DoorBirdConfigEntry
|
||||
) -> bool:
|
||||
"""Register events on device."""
|
||||
issue_id = f"doorbird_schedule_error_{entry.entry_id}"
|
||||
try:
|
||||
await door_station.async_register_events()
|
||||
except ClientResponseError:
|
||||
persistent_notification.async_create(
|
||||
except ClientResponseError as ex:
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
(
|
||||
"Doorbird configuration failed. Please verify that API "
|
||||
"Operator permission is enabled for the Doorbird user. "
|
||||
"A restart will be required once permissions have been "
|
||||
"verified."
|
||||
),
|
||||
title="Doorbird Configuration Failure",
|
||||
notification_id="doorbird_schedule_error",
|
||||
DOMAIN,
|
||||
issue_id,
|
||||
severity=ir.IssueSeverity.ERROR,
|
||||
translation_key="error_registering_events",
|
||||
data={"entry_id": entry.entry_id},
|
||||
is_fixable=True,
|
||||
translation_placeholders={
|
||||
"error": str(ex),
|
||||
"name": door_station.name or entry.data[CONF_NAME],
|
||||
},
|
||||
)
|
||||
_LOGGER.debug("Error registering DoorBird events", exc_info=True)
|
||||
return False
|
||||
else:
|
||||
ir.async_delete_issue(hass, DOMAIN, issue_id)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -111,4 +120,4 @@ async def _update_listener(hass: HomeAssistant, entry: DoorBirdConfigEntry) -> N
|
|||
door_station = entry.runtime_data.door_station
|
||||
door_station.update_events(entry.options[CONF_EVENTS])
|
||||
# Subscribe to doorbell or motion events
|
||||
await _async_register_events(hass, door_station)
|
||||
await _async_register_events(hass, door_station, entry)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "DoorBird",
|
||||
"codeowners": ["@oblogic7", "@bdraco", "@flacjacket"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["http"],
|
||||
"dependencies": ["http", "repairs"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/doorbird",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["doorbirdpy"],
|
||||
|
|
55
homeassistant/components/doorbird/repairs.py
Normal file
55
homeassistant/components/doorbird/repairs.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
"""Repairs for DoorBird."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.repairs import RepairsFlow
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
|
||||
class DoorBirdReloadConfirmRepairFlow(RepairsFlow):
|
||||
"""Handler to show doorbird error and reload."""
|
||||
|
||||
def __init__(self, entry_id: str) -> None:
|
||||
"""Initialize the flow."""
|
||||
self.entry_id = entry_id
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> data_entry_flow.FlowResult:
|
||||
"""Handle the first step of a fix flow."""
|
||||
return await self.async_step_confirm()
|
||||
|
||||
async def async_step_confirm(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> data_entry_flow.FlowResult:
|
||||
"""Handle the confirm step of a fix flow."""
|
||||
if user_input is not None:
|
||||
self.hass.config_entries.async_schedule_reload(self.entry_id)
|
||||
return self.async_create_entry(data={})
|
||||
|
||||
issue_registry = ir.async_get(self.hass)
|
||||
description_placeholders = None
|
||||
if issue := issue_registry.async_get_issue(self.handler, self.issue_id):
|
||||
description_placeholders = issue.translation_placeholders
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="confirm",
|
||||
data_schema=vol.Schema({}),
|
||||
description_placeholders=description_placeholders,
|
||||
)
|
||||
|
||||
|
||||
async def async_create_fix_flow(
|
||||
hass: HomeAssistant,
|
||||
issue_id: str,
|
||||
data: dict[str, str | int | float | None] | None,
|
||||
) -> RepairsFlow:
|
||||
"""Create flow."""
|
||||
assert data is not None
|
||||
entry_id = data["entry_id"]
|
||||
assert isinstance(entry_id, str)
|
||||
return DoorBirdReloadConfirmRepairFlow(entry_id=entry_id)
|
|
@ -11,6 +11,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"error_registering_events": {
|
||||
"title": "DoorBird {name} configuration failure",
|
||||
"fix_flow": {
|
||||
"step": {
|
||||
"confirm": {
|
||||
"title": "[%key:component::doorbird::issues::error_registering_events::title%]",
|
||||
"description": "Configuring DoorBird {name} failed with error: `{error}`. Please enable the API Operator permission for the DoorBird user and continue to reload the integration."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
|
|
61
tests/components/doorbird/test_repairs.py
Normal file
61
tests/components/doorbird/test_repairs.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
"""Test repairs for doorbird."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from http import HTTPStatus
|
||||
|
||||
from homeassistant.components.doorbird.const import DOMAIN
|
||||
from homeassistant.components.repairs.issue_handler import (
|
||||
async_process_repairs_platforms,
|
||||
)
|
||||
from homeassistant.components.repairs.websocket_api import (
|
||||
RepairsFlowIndexView,
|
||||
RepairsFlowResourceView,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import mock_not_found_exception
|
||||
from .conftest import DoorbirdMockerType
|
||||
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
async def test_change_schedule_fails(
|
||||
hass: HomeAssistant,
|
||||
doorbird_mocker: DoorbirdMockerType,
|
||||
hass_client: ClientSessionGenerator,
|
||||
) -> None:
|
||||
"""Test a doorbird when change_schedule fails."""
|
||||
assert await async_setup_component(hass, "repairs", {})
|
||||
doorbird_entry = await doorbird_mocker(
|
||||
favorites_side_effect=mock_not_found_exception()
|
||||
)
|
||||
assert doorbird_entry.entry.state is ConfigEntryState.SETUP_RETRY
|
||||
issue_reg = ir.async_get(hass)
|
||||
assert len(issue_reg.issues) == 1
|
||||
issue = list(issue_reg.issues.values())[0]
|
||||
issue_id = issue.issue_id
|
||||
assert issue.domain == DOMAIN
|
||||
|
||||
await async_process_repairs_platforms(hass)
|
||||
client = await hass_client()
|
||||
|
||||
url = RepairsFlowIndexView.url
|
||||
resp = await client.post(url, json={"handler": DOMAIN, "issue_id": issue_id})
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
flow_id = data["flow_id"]
|
||||
placeholders = data["description_placeholders"]
|
||||
assert "404" in placeholders["error"]
|
||||
assert data["step_id"] == "confirm"
|
||||
|
||||
url = RepairsFlowResourceView.url.format(flow_id=flow_id)
|
||||
resp = await client.post(url)
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data["type"] == "create_entry"
|
Loading…
Add table
Reference in a new issue