Remove previously deprecated Detected Object from unifiprotect ()

pull/88516/head
J. Nick Koston 2023-02-20 15:14:01 -06:00 committed by GitHub
parent fa2e7aa592
commit 35adb2f7e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 15 additions and 313 deletions
homeassistant/components/unifiprotect
tests/components/unifiprotect

View File

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

View File

@ -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."""

View File

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

View File

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

View File

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

View File

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