Remove previously deprecated Detected Object from unifiprotect (#85656)
parent
fa2e7aa592
commit
35adb2f7e8
homeassistant/components/unifiprotect
tests/components/unifiprotect
|
@ -27,7 +27,6 @@ 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,
|
||||
|
@ -122,7 +121,6 @@ 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,94 +2,24 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import partial
|
||||
from itertools import chain
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import 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 import entity_registry as er, issue_registry as ir
|
||||
from homeassistant.helpers.issue_registry import (
|
||||
IssueSeverity,
|
||||
async_get as async_get_issue_registry,
|
||||
)
|
||||
from homeassistant.helpers.issue_registry import async_get as async_get_issue_registry
|
||||
|
||||
from .const import CONF_ALLOW_EA, DOMAIN
|
||||
from .const import CONF_ALLOW_EA
|
||||
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."""
|
||||
|
||||
|
|
|
@ -53,7 +53,6 @@ from .utils import async_dispatch_id as _ufpd, async_get_light_motion_current
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
OBJECT_TYPE_NONE = "none"
|
||||
DEVICE_CLASS_DETECTION = "unifiprotect__detection"
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -524,14 +523,6 @@ NVR_DISABLED_SENSORS: tuple[ProtectSensorEntityDescription, ...] = (
|
|||
)
|
||||
|
||||
EVENT_SENSORS: tuple[ProtectSensorEventEntityDescription, ...] = (
|
||||
ProtectSensorEventEntityDescription(
|
||||
key="detected_object",
|
||||
name="Detected Object",
|
||||
device_class=DEVICE_CLASS_DETECTION,
|
||||
entity_registry_enabled_default=False,
|
||||
ufp_value="is_smart_detected",
|
||||
ufp_event_obj="last_smart_detect_event",
|
||||
),
|
||||
ProtectSensorEventEntityDescription(
|
||||
key="smart_obj_licenseplate",
|
||||
name="License Plate Detected",
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
from contextlib import contextmanager
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from unifi_discovery import AIOUnifiScanner, UnifiDevice, UnifiService
|
||||
|
||||
DEVICE_HOSTNAME = "unvr"
|
||||
|
@ -27,6 +28,8 @@ UNIFI_DISCOVERY_PARTIAL = UnifiDevice(
|
|||
services={UnifiService.Protect: True},
|
||||
)
|
||||
|
||||
pytest.register_assert_rewrite("tests.components.unifiprotect.utils")
|
||||
|
||||
|
||||
def _patch_discovery(device=None, no_device=False):
|
||||
mock_aio_discovery = MagicMock(auto_spec=AIOUnifiScanner)
|
||||
|
|
|
@ -3,11 +3,10 @@ from __future__ import annotations
|
|||
|
||||
from copy import copy
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import Mock
|
||||
|
||||
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,12 +14,10 @@ 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.const import 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
|
||||
|
||||
|
@ -186,154 +183,3 @@ async def test_deprecate_smart_no_automations(
|
|||
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: WebSocketGenerator,
|
||||
doorbell: Camera,
|
||||
) -> None:
|
||||
"""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: WebSocketGenerator,
|
||||
doorbell: Camera,
|
||||
) -> None:
|
||||
"""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
|
||||
|
|
|
@ -15,10 +15,7 @@ from pyunifiprotect.data import (
|
|||
)
|
||||
from pyunifiprotect.data.nvr import EventMetadata, LicensePlateMetadata
|
||||
|
||||
from homeassistant.components.unifiprotect.const import (
|
||||
ATTR_EVENT_SCORE,
|
||||
DEFAULT_ATTRIBUTION,
|
||||
)
|
||||
from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION
|
||||
from homeassistant.components.unifiprotect.sensor import (
|
||||
ALL_DEVICES_SENSORS,
|
||||
CAMERA_DISABLED_SENSORS,
|
||||
|
@ -27,7 +24,6 @@ from homeassistant.components.unifiprotect.sensor import (
|
|||
MOTION_TRIP_SENSORS,
|
||||
NVR_DISABLED_SENSORS,
|
||||
NVR_SENSORS,
|
||||
OBJECT_TYPE_NONE,
|
||||
SENSE_SENSORS,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
|
@ -62,11 +58,11 @@ async def test_sensor_camera_remove(
|
|||
|
||||
ufp.api.bootstrap.nvr.system_info.ustorage = None
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 12)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 24, 12)
|
||||
await remove_entities(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 12, 9)
|
||||
await adopt_devices(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 12)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 24, 12)
|
||||
|
||||
|
||||
async def test_sensor_sensor_remove(
|
||||
|
@ -320,7 +316,7 @@ async def test_sensor_setup_camera(
|
|||
"""Test sensor entity setup for camera devices."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 12)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 24, 12)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
|
@ -399,22 +395,6 @@ async def test_sensor_setup_camera(
|
|||
assert state.state == "-50"
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
# Detected Object
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, doorbell, EVENT_SENSORS[0]
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == OBJECT_TYPE_NONE
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_sensor_setup_camera_with_last_trip_time(
|
||||
hass: HomeAssistant,
|
||||
|
@ -426,7 +406,7 @@ async def test_sensor_setup_camera_with_last_trip_time(
|
|||
"""Test sensor entity setup for camera devices with last trip time."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 25)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 24, 24)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
|
@ -448,52 +428,6 @@ async def test_sensor_setup_camera_with_last_trip_time(
|
|||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_sensor_update_motion(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera, fixed_now: datetime
|
||||
) -> None:
|
||||
"""Test sensor motion entity."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 12)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, doorbell, EVENT_SENSORS[0]
|
||||
)
|
||||
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
event = Event(
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
end=None,
|
||||
score=100,
|
||||
smart_detect_types=[SmartDetectObjectType.PERSON],
|
||||
smart_detect_event_ids=[],
|
||||
camera_id=doorbell.id,
|
||||
api=ufp.api,
|
||||
)
|
||||
|
||||
new_camera = doorbell.copy()
|
||||
new_camera.is_smart_detected = True
|
||||
new_camera.last_smart_detect_event_id = event.id
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = event
|
||||
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.api.bootstrap.events = {event.id: event}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == SmartDetectObjectType.PERSON.value
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
assert state.attributes[ATTR_EVENT_SCORE] == 100
|
||||
|
||||
|
||||
async def test_sensor_update_alarm(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, sensor_all: Sensor, fixed_now: datetime
|
||||
) -> None:
|
||||
|
@ -581,10 +515,10 @@ async def test_camera_update_licenseplate(
|
|||
)
|
||||
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 24, 13)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 23, 13)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, camera, EVENT_SENSORS[1]
|
||||
Platform.SENSOR, camera, EVENT_SENSORS[0]
|
||||
)
|
||||
|
||||
event_metadata = EventMetadata(
|
||||
|
|
Loading…
Reference in New Issue