2022-07-20 10:06:52 +00:00
|
|
|
"""The repairs integration."""
|
2022-07-08 03:49:07 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2022-07-21 09:11:51 +00:00
|
|
|
import functools as ft
|
2022-07-12 17:26:06 +00:00
|
|
|
from typing import Any
|
|
|
|
|
2022-07-08 03:49:07 +00:00
|
|
|
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
|
2022-08-05 10:07:51 +00:00
|
|
|
import voluptuous as vol
|
2022-07-08 03:49:07 +00:00
|
|
|
|
2022-07-12 17:26:06 +00:00
|
|
|
from homeassistant import data_entry_flow
|
2022-07-08 03:49:07 +00:00
|
|
|
from homeassistant.core import HomeAssistant, callback
|
2022-07-12 17:26:06 +00:00
|
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
|
|
from homeassistant.helpers.integration_platform import (
|
|
|
|
async_process_integration_platforms,
|
|
|
|
)
|
2022-07-21 09:11:51 +00:00
|
|
|
from homeassistant.util.async_ import run_callback_threadsafe
|
2022-07-08 03:49:07 +00:00
|
|
|
|
2022-07-12 17:26:06 +00:00
|
|
|
from .const import DOMAIN
|
2022-07-08 03:49:07 +00:00
|
|
|
from .issue_registry import async_get as async_get_issue_registry
|
2022-07-20 10:06:52 +00:00
|
|
|
from .models import IssueSeverity, RepairsFlow, RepairsProtocol
|
2022-07-12 17:26:06 +00:00
|
|
|
|
|
|
|
|
2022-08-05 10:07:51 +00:00
|
|
|
class ConfirmRepairFlow(RepairsFlow):
|
|
|
|
"""Handler for an issue fixing flow without any side effects."""
|
|
|
|
|
|
|
|
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:
|
|
|
|
return self.async_create_entry(title="", data={})
|
|
|
|
|
2022-08-08 22:46:48 +00:00
|
|
|
issue_registry = async_get_issue_registry(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,
|
|
|
|
)
|
2022-08-05 10:07:51 +00:00
|
|
|
|
|
|
|
|
2022-07-20 10:06:52 +00:00
|
|
|
class RepairsFlowManager(data_entry_flow.FlowManager):
|
|
|
|
"""Manage repairs flows."""
|
2022-07-12 17:26:06 +00:00
|
|
|
|
|
|
|
async def async_create_flow(
|
|
|
|
self,
|
|
|
|
handler_key: Any,
|
|
|
|
*,
|
|
|
|
context: dict[str, Any] | None = None,
|
|
|
|
data: dict[str, Any] | None = None,
|
2022-07-20 10:06:52 +00:00
|
|
|
) -> RepairsFlow:
|
|
|
|
"""Create a flow. platform is a repairs module."""
|
2022-07-12 17:26:06 +00:00
|
|
|
assert data and "issue_id" in data
|
|
|
|
issue_id = data["issue_id"]
|
|
|
|
|
|
|
|
issue_registry = async_get_issue_registry(self.hass)
|
|
|
|
issue = issue_registry.async_get_issue(handler_key, issue_id)
|
|
|
|
if issue is None or not issue.is_fixable:
|
|
|
|
raise data_entry_flow.UnknownStep
|
|
|
|
|
2022-08-05 10:07:51 +00:00
|
|
|
if "platforms" not in self.hass.data[DOMAIN]:
|
|
|
|
await async_process_repairs_platforms(self.hass)
|
|
|
|
|
|
|
|
platforms: dict[str, RepairsProtocol] = self.hass.data[DOMAIN]["platforms"]
|
|
|
|
if handler_key not in platforms:
|
2022-08-05 11:16:29 +00:00
|
|
|
flow: RepairsFlow = ConfirmRepairFlow()
|
|
|
|
else:
|
|
|
|
platform = platforms[handler_key]
|
|
|
|
flow = await platform.async_create_fix_flow(self.hass, issue_id, issue.data)
|
2022-08-05 10:07:51 +00:00
|
|
|
|
2022-08-05 11:16:29 +00:00
|
|
|
flow.issue_id = issue_id
|
|
|
|
flow.data = issue.data
|
|
|
|
return flow
|
2022-07-12 17:26:06 +00:00
|
|
|
|
|
|
|
async def async_finish_flow(
|
|
|
|
self, flow: data_entry_flow.FlowHandler, result: data_entry_flow.FlowResult
|
|
|
|
) -> data_entry_flow.FlowResult:
|
|
|
|
"""Complete a fix flow."""
|
|
|
|
async_delete_issue(self.hass, flow.handler, flow.init_data["issue_id"])
|
|
|
|
if "result" not in result:
|
|
|
|
result["result"] = None
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
@callback
|
|
|
|
def async_setup(hass: HomeAssistant) -> None:
|
2022-07-20 10:06:52 +00:00
|
|
|
"""Initialize repairs."""
|
|
|
|
hass.data[DOMAIN]["flow_manager"] = RepairsFlowManager(hass)
|
2022-07-12 17:26:06 +00:00
|
|
|
|
|
|
|
|
2022-07-20 10:06:52 +00:00
|
|
|
async def async_process_repairs_platforms(hass: HomeAssistant) -> None:
|
|
|
|
"""Start processing repairs platforms."""
|
2022-07-12 17:26:06 +00:00
|
|
|
hass.data[DOMAIN]["platforms"] = {}
|
|
|
|
|
2022-07-20 10:06:52 +00:00
|
|
|
await async_process_integration_platforms(hass, DOMAIN, _register_repairs_platform)
|
2022-07-12 17:26:06 +00:00
|
|
|
|
|
|
|
|
2022-07-20 10:06:52 +00:00
|
|
|
async def _register_repairs_platform(
|
|
|
|
hass: HomeAssistant, integration_domain: str, platform: RepairsProtocol
|
2022-07-12 17:26:06 +00:00
|
|
|
) -> None:
|
2022-07-20 10:06:52 +00:00
|
|
|
"""Register a repairs platform."""
|
2022-07-12 17:26:06 +00:00
|
|
|
if not hasattr(platform, "async_create_fix_flow"):
|
2022-07-20 10:06:52 +00:00
|
|
|
raise HomeAssistantError(f"Invalid repairs platform {platform}")
|
2022-07-12 17:26:06 +00:00
|
|
|
hass.data[DOMAIN]["platforms"][integration_domain] = platform
|
2022-07-08 03:49:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
@callback
|
|
|
|
def async_create_issue(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
domain: str,
|
|
|
|
issue_id: str,
|
|
|
|
*,
|
2022-07-27 20:53:51 +00:00
|
|
|
issue_domain: str | None = None,
|
2022-07-08 03:49:07 +00:00
|
|
|
breaks_in_ha_version: str | None = None,
|
2022-08-05 11:16:29 +00:00
|
|
|
data: dict[str, str | int | float | None] | None = None,
|
2022-07-12 17:26:06 +00:00
|
|
|
is_fixable: bool,
|
2022-08-05 08:11:20 +00:00
|
|
|
is_persistent: bool = False,
|
2022-07-08 03:49:07 +00:00
|
|
|
learn_more_url: str | None = None,
|
|
|
|
severity: IssueSeverity,
|
|
|
|
translation_key: str,
|
|
|
|
translation_placeholders: dict[str, str] | None = None,
|
|
|
|
) -> None:
|
|
|
|
"""Create an issue, or replace an existing one."""
|
|
|
|
# Verify the breaks_in_ha_version is a valid version string
|
|
|
|
if breaks_in_ha_version:
|
|
|
|
AwesomeVersion(
|
|
|
|
breaks_in_ha_version,
|
|
|
|
ensure_strategy=AwesomeVersionStrategy.CALVER,
|
|
|
|
find_first_match=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
issue_registry = async_get_issue_registry(hass)
|
|
|
|
issue_registry.async_get_or_create(
|
|
|
|
domain,
|
|
|
|
issue_id,
|
2022-07-27 20:53:51 +00:00
|
|
|
issue_domain=issue_domain,
|
2022-07-08 03:49:07 +00:00
|
|
|
breaks_in_ha_version=breaks_in_ha_version,
|
2022-08-05 11:16:29 +00:00
|
|
|
data=data,
|
2022-07-12 17:26:06 +00:00
|
|
|
is_fixable=is_fixable,
|
2022-08-05 08:11:20 +00:00
|
|
|
is_persistent=is_persistent,
|
2022-07-08 03:49:07 +00:00
|
|
|
learn_more_url=learn_more_url,
|
|
|
|
severity=severity,
|
|
|
|
translation_key=translation_key,
|
|
|
|
translation_placeholders=translation_placeholders,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-07-21 09:11:51 +00:00
|
|
|
def create_issue(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
domain: str,
|
|
|
|
issue_id: str,
|
|
|
|
*,
|
|
|
|
breaks_in_ha_version: str | None = None,
|
2022-08-05 11:16:29 +00:00
|
|
|
data: dict[str, str | int | float | None] | None = None,
|
2022-07-21 09:11:51 +00:00
|
|
|
is_fixable: bool,
|
2022-08-05 08:11:20 +00:00
|
|
|
is_persistent: bool = False,
|
2022-07-21 09:11:51 +00:00
|
|
|
learn_more_url: str | None = None,
|
|
|
|
severity: IssueSeverity,
|
|
|
|
translation_key: str,
|
|
|
|
translation_placeholders: dict[str, str] | None = None,
|
|
|
|
) -> None:
|
|
|
|
"""Create an issue, or replace an existing one."""
|
|
|
|
return run_callback_threadsafe(
|
|
|
|
hass.loop,
|
|
|
|
ft.partial(
|
|
|
|
async_create_issue,
|
|
|
|
hass,
|
|
|
|
domain,
|
|
|
|
issue_id,
|
|
|
|
breaks_in_ha_version=breaks_in_ha_version,
|
2022-08-05 11:16:29 +00:00
|
|
|
data=data,
|
2022-07-21 09:11:51 +00:00
|
|
|
is_fixable=is_fixable,
|
2022-08-05 08:11:20 +00:00
|
|
|
is_persistent=is_persistent,
|
2022-07-21 09:11:51 +00:00
|
|
|
learn_more_url=learn_more_url,
|
|
|
|
severity=severity,
|
|
|
|
translation_key=translation_key,
|
|
|
|
translation_placeholders=translation_placeholders,
|
|
|
|
),
|
|
|
|
).result()
|
|
|
|
|
|
|
|
|
2022-07-08 03:49:07 +00:00
|
|
|
@callback
|
|
|
|
def async_delete_issue(hass: HomeAssistant, domain: str, issue_id: str) -> None:
|
|
|
|
"""Delete an issue.
|
|
|
|
|
|
|
|
It is not an error to delete an issue that does not exist.
|
|
|
|
"""
|
|
|
|
issue_registry = async_get_issue_registry(hass)
|
|
|
|
issue_registry.async_delete(domain, issue_id)
|
|
|
|
|
|
|
|
|
2022-07-21 09:11:51 +00:00
|
|
|
def delete_issue(hass: HomeAssistant, domain: str, issue_id: str) -> None:
|
|
|
|
"""Delete an issue.
|
|
|
|
|
|
|
|
It is not an error to delete an issue that does not exist.
|
|
|
|
"""
|
|
|
|
return run_callback_threadsafe(
|
|
|
|
hass.loop, async_delete_issue, hass, domain, issue_id
|
|
|
|
).result()
|
|
|
|
|
|
|
|
|
2022-07-08 03:49:07 +00:00
|
|
|
@callback
|
2022-07-19 11:58:39 +00:00
|
|
|
def async_ignore_issue(
|
|
|
|
hass: HomeAssistant, domain: str, issue_id: str, ignore: bool
|
|
|
|
) -> None:
|
|
|
|
"""Ignore an issue.
|
2022-07-08 03:49:07 +00:00
|
|
|
|
|
|
|
Will raise if the issue does not exist.
|
|
|
|
"""
|
|
|
|
issue_registry = async_get_issue_registry(hass)
|
2022-07-19 11:58:39 +00:00
|
|
|
issue_registry.async_ignore(domain, issue_id, ignore)
|