mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Allow searching for person (#77339)
This commit is contained in:
parent
4ba8fb6457
commit
7d9ae0784e
@ -125,6 +125,38 @@ async def async_add_user_device_tracker(
|
|||||||
break
|
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 = {
|
CREATE_FIELDS = {
|
||||||
vol.Required(CONF_NAME): vol.All(str, vol.Length(min=1)),
|
vol.Required(CONF_NAME): vol.All(str, vol.Length(min=1)),
|
||||||
vol.Optional(CONF_USER_ID): vol.Any(str, None),
|
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()
|
await storage_collection.async_load()
|
||||||
|
|
||||||
hass.data[DOMAIN] = (yaml_collection, storage_collection)
|
hass.data[DOMAIN] = (yaml_collection, storage_collection, entity_component)
|
||||||
|
|
||||||
collection.StorageCollectionWebsocket(
|
collection.StorageCollectionWebsocket(
|
||||||
storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS
|
storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS
|
||||||
@ -412,6 +444,11 @@ class Person(RestoreEntity):
|
|||||||
"""Return a unique ID for the person."""
|
"""Return a unique ID for the person."""
|
||||||
return self._config[CONF_ID]
|
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):
|
async def async_added_to_hass(self):
|
||||||
"""Register device trackers."""
|
"""Register device trackers."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
@ -506,7 +543,7 @@ def ws_list_person(
|
|||||||
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg
|
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg
|
||||||
):
|
):
|
||||||
"""List persons."""
|
"""List persons."""
|
||||||
yaml, storage = hass.data[DOMAIN]
|
yaml, storage, _ = hass.data[DOMAIN]
|
||||||
connection.send_result(
|
connection.send_result(
|
||||||
msg[ATTR_ID], {"storage": storage.async_items(), "config": yaml.async_items()}
|
msg[ATTR_ID], {"storage": storage.async_items(), "config": yaml.async_items()}
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,7 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
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.components.homeassistant import scene
|
||||||
from homeassistant.core import HomeAssistant, callback, split_entity_id
|
from homeassistant.core import HomeAssistant, callback, split_entity_id
|
||||||
from homeassistant.helpers import device_registry, entity_registry
|
from homeassistant.helpers import device_registry, entity_registry
|
||||||
@ -36,6 +36,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
"group",
|
"group",
|
||||||
"scene",
|
"scene",
|
||||||
"script",
|
"script",
|
||||||
|
"person",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
vol.Required("item_id"): str,
|
vol.Required("item_id"): str,
|
||||||
@ -67,7 +68,7 @@ class Searcher:
|
|||||||
# These types won't be further explored. Config entries + Output types.
|
# These types won't be further explored. Config entries + Output types.
|
||||||
DONT_RESOLVE = {"scene", "automation", "script", "group", "config_entry", "area"}
|
DONT_RESOLVE = {"scene", "automation", "script", "group", "config_entry", "area"}
|
||||||
# These types exist as an entity and so need cleanup in results
|
# 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__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -183,6 +184,9 @@ class Searcher:
|
|||||||
for entity in script.scripts_with_entity(self.hass, entity_id):
|
for entity in script.scripts_with_entity(self.hass, entity_id):
|
||||||
self._add_or_resolve("entity", entity)
|
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
|
# Find devices
|
||||||
entity_entry = self._entity_reg.async_get(entity_id)
|
entity_entry = self._entity_reg.async_get(entity_id)
|
||||||
if entity_entry is not None:
|
if entity_entry is not None:
|
||||||
@ -251,6 +255,15 @@ class Searcher:
|
|||||||
for entity in scene.entities_in_scene(self.hass, scene_entity_id):
|
for entity in scene.entities_in_scene(self.hass, scene_entity_id):
|
||||||
self._add_or_resolve("entity", entity)
|
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
|
@callback
|
||||||
def _resolve_config_entry(self, config_entry_id) -> None:
|
def _resolve_config_entry(self, config_entry_id) -> None:
|
||||||
"""Resolve a config entry.
|
"""Resolve a config entry.
|
||||||
|
@ -783,3 +783,59 @@ async def test_person_storage_fixing_device_trackers(storage_collection):
|
|||||||
await storage_collection.async_load()
|
await storage_collection.async_load()
|
||||||
|
|
||||||
assert storage_collection.data["bla"]["device_trackers"] == []
|
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):
|
async def test_ws_api(hass, hass_ws_client):
|
||||||
"""Test WS API."""
|
"""Test WS API."""
|
||||||
assert await async_setup_component(hass, "search", {})
|
assert await async_setup_component(hass, "search", {})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user