mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 17:27:10 +00:00
Deprecate the UniFi Protect Detected Object sensor (#83480)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
b96330df03
commit
3aa759fc49
@ -27,6 +27,7 @@ from .const import (
|
||||
from .data import ProtectData, async_ufp_instance_for_config_entry_ids
|
||||
from .discovery import async_start_discovery
|
||||
from .migrate import async_migrate_data
|
||||
from .repairs import async_create_repairs
|
||||
from .services import async_cleanup_services, async_setup_services
|
||||
from .utils import (
|
||||
_async_unifi_mac_from_hass,
|
||||
@ -121,6 +122,7 @@ async def _async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, data_service: ProtectData
|
||||
) -> None:
|
||||
await async_migrate_data(hass, entry, data_service.api)
|
||||
await async_create_repairs(hass, entry, data_service.api)
|
||||
|
||||
await data_service.async_setup()
|
||||
if not data_service.last_update_success:
|
||||
|
@ -2,20 +2,93 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import cast
|
||||
from functools import partial
|
||||
from itertools import chain
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.automation import (
|
||||
EVENT_AUTOMATION_RELOADED,
|
||||
automations_with_entity,
|
||||
)
|
||||
from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow
|
||||
from homeassistant.components.script import scripts_with_entity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.issue_registry import async_get as async_get_issue_registry
|
||||
from homeassistant.helpers import entity_registry as er, issue_registry as ir
|
||||
from homeassistant.helpers.issue_registry import (
|
||||
IssueSeverity,
|
||||
async_get as async_get_issue_registry,
|
||||
)
|
||||
|
||||
from .const import CONF_ALLOW_EA
|
||||
from .const import CONF_ALLOW_EA, DOMAIN
|
||||
from .utils import async_create_api_client
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_create_repairs(
|
||||
hass: HomeAssistant, entry: ConfigEntry, protect: ProtectApiClient
|
||||
) -> None:
|
||||
"""Create any additional repairs for deprecations."""
|
||||
|
||||
await _deprecate_smart_sensor(hass, entry, protect)
|
||||
entry.async_on_unload(
|
||||
hass.bus.async_listen(
|
||||
EVENT_AUTOMATION_RELOADED,
|
||||
partial(_deprecate_smart_sensor, hass, entry, protect),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def _deprecate_smart_sensor(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
protect: ProtectApiClient,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
entity_registry = er.async_get(hass)
|
||||
automations: dict[str, list[str]] = {}
|
||||
scripts: dict[str, list[str]] = {}
|
||||
for entity in er.async_entries_for_config_entry(entity_registry, entry.entry_id):
|
||||
if (
|
||||
entity.domain == Platform.SENSOR
|
||||
and entity.disabled_by is None
|
||||
and "detected_object" in entity.unique_id
|
||||
):
|
||||
entity_automations = automations_with_entity(hass, entity.entity_id)
|
||||
entity_scripts = scripts_with_entity(hass, entity.entity_id)
|
||||
if entity_automations:
|
||||
automations[entity.entity_id] = entity_automations
|
||||
if entity_scripts:
|
||||
scripts[entity.entity_id] = entity_scripts
|
||||
|
||||
if automations or scripts:
|
||||
items = sorted(
|
||||
set(
|
||||
chain.from_iterable(list(automations.values()) + list(scripts.values()))
|
||||
)
|
||||
)
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecate_smart_sensor",
|
||||
is_fixable=False,
|
||||
breaks_in_ha_version="2023.3.0",
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecate_smart_sensor",
|
||||
translation_placeholders={"items": "* `" + "`\n* `".join(items) + "`\n"},
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug("No found usages of Detected Object sensor")
|
||||
ir.async_delete_issue(hass, DOMAIN, "deprecate_smart_sensor")
|
||||
|
||||
|
||||
class EAConfirm(RepairsFlow):
|
||||
"""Handler for an issue fixing flow."""
|
||||
|
@ -76,6 +76,10 @@
|
||||
"title": "Setup error using Early Access version",
|
||||
"description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}"
|
||||
},
|
||||
"deprecate_smart_sensor": {
|
||||
"title": "Smart Detection Sensor Deprecated",
|
||||
"description": "The unified \"Detected Object\" sensor for smart detections is now deprecated. It has been replaced with individual smart detection binary sensors for each smart detection type.\n\nBelow are the detected automations or scripts that use one or more of the deprecated entities:\n{items}\nThe above list may be incomplete and it does not include any template usages inside of dashboards. Please update any templates, automations or scripts accordingly."
|
||||
},
|
||||
"deprecated_service_set_doorbell_message": {
|
||||
"title": "set_doorbell_message is Deprecated",
|
||||
"fix_flow": {
|
||||
|
@ -51,6 +51,10 @@
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"deprecate_smart_sensor": {
|
||||
"description": "The unified \"Detected Object\" sensor for smart detections is now deprecated. It has been replaced with individual smart detection binary sensors for each smart detection type.\n\nBelow are the detected automations or scripts that use one or more of the deprecated entities:\n{items}\nThe above list may be incomplete and it does not include any template usages inside of dashboards. Please update any templates, automations or scripts accordingly.",
|
||||
"title": "Smart Detection Sensor Deprecated"
|
||||
},
|
||||
"deprecated_service_set_doorbell_message": {
|
||||
"fix_flow": {
|
||||
"step": {
|
||||
|
@ -4,10 +4,11 @@ from __future__ import annotations
|
||||
|
||||
from copy import copy
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import Mock
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from pyunifiprotect.data import Version
|
||||
from pyunifiprotect.data import Camera, Version
|
||||
|
||||
from homeassistant.components.automation import DOMAIN as AUTOMATION_DOMAIN
|
||||
from homeassistant.components.repairs.issue_handler import (
|
||||
async_process_repairs_platforms,
|
||||
)
|
||||
@ -15,8 +16,12 @@ from homeassistant.components.repairs.websocket_api import (
|
||||
RepairsFlowIndexView,
|
||||
RepairsFlowResourceView,
|
||||
)
|
||||
from homeassistant.components.script import DOMAIN as SCRIPT_DOMAIN
|
||||
from homeassistant.components.unifiprotect.const import DOMAIN
|
||||
from homeassistant.const import SERVICE_RELOAD, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .utils import MockUFPFixture, init_entry
|
||||
|
||||
@ -124,3 +129,198 @@ async def test_ea_warning_fix(
|
||||
data = await resp.json()
|
||||
|
||||
assert data["type"] == "create_entry"
|
||||
|
||||
|
||||
async def test_deprecate_smart_default(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera
|
||||
):
|
||||
"""Test Deprecate Sensor repair does not exist by default (new installs)."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
await async_process_repairs_platforms(hass)
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert msg["success"]
|
||||
issue = None
|
||||
for i in msg["result"]["issues"]:
|
||||
if i["issue_id"] == "deprecate_smart_sensor":
|
||||
issue = i
|
||||
assert issue is None
|
||||
|
||||
|
||||
async def test_deprecate_smart_no_automations(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera
|
||||
):
|
||||
"""Test Deprecate Sensor repair exists for existing installs."""
|
||||
|
||||
registry = er.async_get(hass)
|
||||
registry.async_get_or_create(
|
||||
Platform.SENSOR,
|
||||
DOMAIN,
|
||||
f"{doorbell.mac}_detected_object",
|
||||
config_entry=ufp.entry,
|
||||
)
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
await async_process_repairs_platforms(hass)
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert msg["success"]
|
||||
issue = None
|
||||
for i in msg["result"]["issues"]:
|
||||
if i["issue_id"] == "deprecate_smart_sensor":
|
||||
issue = i
|
||||
assert issue is None
|
||||
|
||||
|
||||
async def _load_automation(hass: HomeAssistant, entity_id: str):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
AUTOMATION_DOMAIN,
|
||||
{
|
||||
AUTOMATION_DOMAIN: [
|
||||
{
|
||||
"alias": "test1",
|
||||
"trigger": [
|
||||
{"platform": "state", "entity_id": entity_id},
|
||||
{
|
||||
"platform": "event",
|
||||
"event_type": "state_changed",
|
||||
"event_data": {"entity_id": entity_id},
|
||||
},
|
||||
],
|
||||
"condition": {
|
||||
"condition": "state",
|
||||
"entity_id": entity_id,
|
||||
"state": "on",
|
||||
},
|
||||
"action": [
|
||||
{
|
||||
"service": "test.script",
|
||||
"data": {"entity_id": entity_id},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def test_deprecate_smart_automation(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera
|
||||
):
|
||||
"""Test Deprecate Sensor repair exists for existing installs."""
|
||||
|
||||
registry = er.async_get(hass)
|
||||
entry = registry.async_get_or_create(
|
||||
Platform.SENSOR,
|
||||
DOMAIN,
|
||||
f"{doorbell.mac}_detected_object",
|
||||
config_entry=ufp.entry,
|
||||
)
|
||||
await _load_automation(hass, entry.entity_id)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
await async_process_repairs_platforms(hass)
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert msg["success"]
|
||||
issue = None
|
||||
for i in msg["result"]["issues"]:
|
||||
if i["issue_id"] == "deprecate_smart_sensor":
|
||||
issue = i
|
||||
assert issue is not None
|
||||
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value={AUTOMATION_DOMAIN: []},
|
||||
):
|
||||
await hass.services.async_call(AUTOMATION_DOMAIN, SERVICE_RELOAD, blocking=True)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json({"id": 2, "type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert msg["success"]
|
||||
issue = None
|
||||
for i in msg["result"]["issues"]:
|
||||
if i["issue_id"] == "deprecate_smart_sensor":
|
||||
issue = i
|
||||
assert issue is None
|
||||
|
||||
|
||||
async def _load_script(hass: HomeAssistant, entity_id: str):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
SCRIPT_DOMAIN,
|
||||
{
|
||||
SCRIPT_DOMAIN: {
|
||||
"test": {
|
||||
"sequence": {
|
||||
"service": "test.script",
|
||||
"data": {"entity_id": entity_id},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def test_deprecate_smart_script(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera
|
||||
):
|
||||
"""Test Deprecate Sensor repair exists for existing installs."""
|
||||
|
||||
registry = er.async_get(hass)
|
||||
entry = registry.async_get_or_create(
|
||||
Platform.SENSOR,
|
||||
DOMAIN,
|
||||
f"{doorbell.mac}_detected_object",
|
||||
config_entry=ufp.entry,
|
||||
)
|
||||
await _load_script(hass, entry.entity_id)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
await async_process_repairs_platforms(hass)
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert msg["success"]
|
||||
issue = None
|
||||
for i in msg["result"]["issues"]:
|
||||
if i["issue_id"] == "deprecate_smart_sensor":
|
||||
issue = i
|
||||
assert issue is not None
|
||||
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value={SCRIPT_DOMAIN: {}},
|
||||
):
|
||||
await hass.services.async_call(SCRIPT_DOMAIN, SERVICE_RELOAD, blocking=True)
|
||||
await hass.config_entries.async_reload(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json({"id": 2, "type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert msg["success"]
|
||||
issue = None
|
||||
for i in msg["result"]["issues"]:
|
||||
if i["issue_id"] == "deprecate_smart_sensor":
|
||||
issue = i
|
||||
assert issue is None
|
||||
|
Loading…
x
Reference in New Issue
Block a user