Allow searching for person (#77339)
This commit is contained in:
parent
4ba8fb6457
commit
7d9ae0784e
4 changed files with 140 additions and 4 deletions
|
@ -125,6 +125,38 @@ async def async_add_user_device_tracker(
|
|||
break
|
||||
|
||||
|
||||
@callback
|
||||
def persons_with_entity(hass: HomeAssistant, entity_id: str) -> list[str]:
|
||||
"""Return all persons that reference the entity."""
|
||||
if (
|
||||
DOMAIN not in hass.data
|
||||
or split_entity_id(entity_id)[0] != DEVICE_TRACKER_DOMAIN
|
||||
):
|
||||
return []
|
||||
|
||||
component: EntityComponent = hass.data[DOMAIN][2]
|
||||
|
||||
return [
|
||||
person_entity.entity_id
|
||||
for person_entity in component.entities
|
||||
if entity_id in cast(Person, person_entity).device_trackers
|
||||
]
|
||||
|
||||
|
||||
@callback
|
||||
def entities_in_person(hass: HomeAssistant, entity_id: str) -> list[str]:
|
||||
"""Return all entities belonging to a person."""
|
||||
if DOMAIN not in hass.data:
|
||||
return []
|
||||
|
||||
component: EntityComponent = hass.data[DOMAIN][2]
|
||||
|
||||
if (person_entity := component.get_entity(entity_id)) is None:
|
||||
return []
|
||||
|
||||
return cast(Person, person_entity).device_trackers
|
||||
|
||||
|
||||
CREATE_FIELDS = {
|
||||
vol.Required(CONF_NAME): vol.All(str, vol.Length(min=1)),
|
||||
vol.Optional(CONF_USER_ID): vol.Any(str, None),
|
||||
|
@ -318,7 +350,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
)
|
||||
await storage_collection.async_load()
|
||||
|
||||
hass.data[DOMAIN] = (yaml_collection, storage_collection)
|
||||
hass.data[DOMAIN] = (yaml_collection, storage_collection, entity_component)
|
||||
|
||||
collection.StorageCollectionWebsocket(
|
||||
storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS
|
||||
|
@ -412,6 +444,11 @@ class Person(RestoreEntity):
|
|||
"""Return a unique ID for the person."""
|
||||
return self._config[CONF_ID]
|
||||
|
||||
@property
|
||||
def device_trackers(self):
|
||||
"""Return the device trackers for the person."""
|
||||
return self._config[CONF_DEVICE_TRACKERS]
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register device trackers."""
|
||||
await super().async_added_to_hass()
|
||||
|
@ -506,7 +543,7 @@ def ws_list_person(
|
|||
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg
|
||||
):
|
||||
"""List persons."""
|
||||
yaml, storage = hass.data[DOMAIN]
|
||||
yaml, storage, _ = hass.data[DOMAIN]
|
||||
connection.send_result(
|
||||
msg[ATTR_ID], {"storage": storage.async_items(), "config": yaml.async_items()}
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ import logging
|
|||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import automation, group, script, websocket_api
|
||||
from homeassistant.components import automation, group, person, script, websocket_api
|
||||
from homeassistant.components.homeassistant import scene
|
||||
from homeassistant.core import HomeAssistant, callback, split_entity_id
|
||||
from homeassistant.helpers import device_registry, entity_registry
|
||||
|
@ -36,6 +36,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
"group",
|
||||
"scene",
|
||||
"script",
|
||||
"person",
|
||||
)
|
||||
),
|
||||
vol.Required("item_id"): str,
|
||||
|
@ -67,7 +68,7 @@ class Searcher:
|
|||
# These types won't be further explored. Config entries + Output types.
|
||||
DONT_RESOLVE = {"scene", "automation", "script", "group", "config_entry", "area"}
|
||||
# These types exist as an entity and so need cleanup in results
|
||||
EXIST_AS_ENTITY = {"script", "scene", "automation", "group"}
|
||||
EXIST_AS_ENTITY = {"script", "scene", "automation", "group", "person"}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -183,6 +184,9 @@ class Searcher:
|
|||
for entity in script.scripts_with_entity(self.hass, entity_id):
|
||||
self._add_or_resolve("entity", entity)
|
||||
|
||||
for entity in person.persons_with_entity(self.hass, entity_id):
|
||||
self._add_or_resolve("entity", entity)
|
||||
|
||||
# Find devices
|
||||
entity_entry = self._entity_reg.async_get(entity_id)
|
||||
if entity_entry is not None:
|
||||
|
@ -251,6 +255,15 @@ class Searcher:
|
|||
for entity in scene.entities_in_scene(self.hass, scene_entity_id):
|
||||
self._add_or_resolve("entity", entity)
|
||||
|
||||
@callback
|
||||
def _resolve_person(self, person_entity_id) -> None:
|
||||
"""Resolve a person.
|
||||
|
||||
Will only be called if person is an entry point.
|
||||
"""
|
||||
for entity in person.entities_in_person(self.hass, person_entity_id):
|
||||
self._add_or_resolve("entity", entity)
|
||||
|
||||
@callback
|
||||
def _resolve_config_entry(self, config_entry_id) -> None:
|
||||
"""Resolve a config entry.
|
||||
|
|
|
@ -783,3 +783,59 @@ async def test_person_storage_fixing_device_trackers(storage_collection):
|
|||
await storage_collection.async_load()
|
||||
|
||||
assert storage_collection.data["bla"]["device_trackers"] == []
|
||||
|
||||
|
||||
async def test_persons_with_entity(hass):
|
||||
"""Test finding persons with an entity."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"person",
|
||||
{
|
||||
"person": [
|
||||
{
|
||||
"id": "abcd",
|
||||
"name": "Paulus",
|
||||
"device_trackers": [
|
||||
"device_tracker.paulus_iphone",
|
||||
"device_tracker.paulus_ipad",
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "efgh",
|
||||
"name": "Anne Therese",
|
||||
"device_trackers": [
|
||||
"device_tracker.at_pixel",
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
assert person.persons_with_entity(hass, "device_tracker.paulus_iphone") == [
|
||||
"person.paulus"
|
||||
]
|
||||
|
||||
|
||||
async def test_entities_in_person(hass):
|
||||
"""Test finding entities tracked by person."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"person",
|
||||
{
|
||||
"person": [
|
||||
{
|
||||
"id": "abcd",
|
||||
"name": "Paulus",
|
||||
"device_trackers": [
|
||||
"device_tracker.paulus_iphone",
|
||||
"device_tracker.paulus_ipad",
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
assert person.entities_in_person(hass, "person.paulus") == [
|
||||
"device_tracker.paulus_iphone",
|
||||
"device_tracker.paulus_ipad",
|
||||
]
|
||||
|
|
|
@ -368,6 +368,36 @@ async def test_area_lookup(hass):
|
|||
}
|
||||
|
||||
|
||||
async def test_person_lookup(hass):
|
||||
"""Test searching persons."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"person",
|
||||
{
|
||||
"person": [
|
||||
{
|
||||
"id": "abcd",
|
||||
"name": "Paulus",
|
||||
"device_trackers": ["device_tracker.paulus_iphone"],
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
device_reg = dr.async_get(hass)
|
||||
entity_reg = er.async_get(hass)
|
||||
|
||||
searcher = search.Searcher(hass, device_reg, entity_reg, MOCK_ENTITY_SOURCES)
|
||||
assert searcher.async_search("entity", "device_tracker.paulus_iphone") == {
|
||||
"person": {"person.paulus"},
|
||||
}
|
||||
|
||||
searcher = search.Searcher(hass, device_reg, entity_reg, MOCK_ENTITY_SOURCES)
|
||||
assert searcher.async_search("entity", "person.paulus") == {
|
||||
"entity": {"device_tracker.paulus_iphone"},
|
||||
}
|
||||
|
||||
|
||||
async def test_ws_api(hass, hass_ws_client):
|
||||
"""Test WS API."""
|
||||
assert await async_setup_component(hass, "search", {})
|
||||
|
|
Loading…
Add table
Reference in a new issue