Update doorbird error notification to be a repair flow (#122987)

This commit is contained in:
J. Nick Koston 2024-08-01 01:31:22 -05:00 committed by Franck Nijhof
parent 3b462906d9
commit cb37ae6608
No known key found for this signature in database
GPG key ID: D62583BA8AB11CA3
5 changed files with 153 additions and 15 deletions

View file

@ -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)

View file

@ -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"],

View 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)

View file

@ -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": {

View 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"