Add person reload service (#30493)
This commit is contained in:
parent
35e19eec18
commit
30076d1843
6 changed files with 121 additions and 8 deletions
|
@ -19,13 +19,26 @@ from homeassistant.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
|
SERVICE_RELOAD,
|
||||||
STATE_HOME,
|
STATE_HOME,
|
||||||
STATE_NOT_HOME,
|
STATE_NOT_HOME,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import Event, HomeAssistant, State, callback, split_entity_id
|
from homeassistant.core import (
|
||||||
from homeassistant.helpers import collection, config_validation as cv, entity_registry
|
Event,
|
||||||
|
HomeAssistant,
|
||||||
|
ServiceCall,
|
||||||
|
State,
|
||||||
|
callback,
|
||||||
|
split_entity_id,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers import (
|
||||||
|
collection,
|
||||||
|
config_validation as cv,
|
||||||
|
entity_registry,
|
||||||
|
service,
|
||||||
|
)
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.event import async_track_state_change
|
from homeassistant.helpers.event import async_track_state_change
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
|
@ -303,6 +316,17 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType):
|
||||||
|
|
||||||
hass.bus.async_listen(EVENT_USER_REMOVED, _handle_user_removed)
|
hass.bus.async_listen(EVENT_USER_REMOVED, _handle_user_removed)
|
||||||
|
|
||||||
|
async def async_reload_yaml(call: ServiceCall):
|
||||||
|
"""Reload YAML."""
|
||||||
|
conf = await entity_component.async_prepare_reload(skip_reset=True)
|
||||||
|
if conf is None:
|
||||||
|
return
|
||||||
|
await yaml_collection.async_load(await filter_yaml_data(hass, conf[DOMAIN]))
|
||||||
|
|
||||||
|
service.async_register_admin_service(
|
||||||
|
hass, DOMAIN, SERVICE_RELOAD, async_reload_yaml
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
2
homeassistant/components/person/services.yaml
Normal file
2
homeassistant/components/person/services.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
reload:
|
||||||
|
description: Reload the person configuration.
|
|
@ -115,16 +115,27 @@ class YamlCollection(ObservableCollection):
|
||||||
"""Offer a fake CRUD interface on top of static YAML."""
|
"""Offer a fake CRUD interface on top of static YAML."""
|
||||||
|
|
||||||
async def async_load(self, data: List[dict]) -> None:
|
async def async_load(self, data: List[dict]) -> None:
|
||||||
"""Load the storage Manager."""
|
"""Load the YAML collection. Overrides existing data."""
|
||||||
|
old_ids = set(self.data)
|
||||||
|
|
||||||
for item in data:
|
for item in data:
|
||||||
item_id = item[CONF_ID]
|
item_id = item[CONF_ID]
|
||||||
|
|
||||||
if self.id_manager.has_id(item_id):
|
if item_id in old_ids:
|
||||||
|
old_ids.remove(item_id)
|
||||||
|
event = CHANGE_UPDATED
|
||||||
|
elif self.id_manager.has_id(item_id):
|
||||||
self.logger.warning("Duplicate ID '%s' detected, skipping", item_id)
|
self.logger.warning("Duplicate ID '%s' detected, skipping", item_id)
|
||||||
continue
|
continue
|
||||||
|
else:
|
||||||
|
event = CHANGE_ADDED
|
||||||
|
|
||||||
self.data[item_id] = item
|
self.data[item_id] = item
|
||||||
await self.notify_change(CHANGE_ADDED, item[CONF_ID], item)
|
await self.notify_change(event, item[CONF_ID], item)
|
||||||
|
|
||||||
|
for item_id in old_ids:
|
||||||
|
self.data.pop(item_id)
|
||||||
|
await self.notify_change(CHANGE_REMOVED, item_id, None)
|
||||||
|
|
||||||
|
|
||||||
class StorageCollection(ObservableCollection):
|
class StorageCollection(ObservableCollection):
|
||||||
|
|
|
@ -290,7 +290,7 @@ class EntityComponent:
|
||||||
if entity_id in platform.entities:
|
if entity_id in platform.entities:
|
||||||
await platform.async_remove_entity(entity_id)
|
await platform.async_remove_entity(entity_id)
|
||||||
|
|
||||||
async def async_prepare_reload(self):
|
async def async_prepare_reload(self, *, skip_reset=False):
|
||||||
"""Prepare reloading this entity component.
|
"""Prepare reloading this entity component.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -310,7 +310,8 @@ class EntityComponent:
|
||||||
if conf is None:
|
if conf is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
await self._async_reset()
|
if not skip_reset:
|
||||||
|
await self._async_reset()
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
def _async_init_entity_platform(
|
def _async_init_entity_platform(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""The tests for the person component."""
|
"""The tests for the person component."""
|
||||||
import logging
|
import logging
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -16,9 +17,10 @@ from homeassistant.const import (
|
||||||
ATTR_LATITUDE,
|
ATTR_LATITUDE,
|
||||||
ATTR_LONGITUDE,
|
ATTR_LONGITUDE,
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
|
SERVICE_RELOAD,
|
||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import CoreState, State
|
from homeassistant.core import Context, CoreState, State
|
||||||
from homeassistant.helpers import collection, entity_registry
|
from homeassistant.helpers import collection, entity_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
@ -703,3 +705,59 @@ async def test_add_user_device_tracker(hass, storage_setup, hass_read_only_user)
|
||||||
"device_tracker.on_create",
|
"device_tracker.on_create",
|
||||||
"device_tracker.added",
|
"device_tracker.added",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reload(hass, hass_admin_user):
|
||||||
|
"""Test reloading the YAML config."""
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
DOMAIN,
|
||||||
|
{
|
||||||
|
DOMAIN: [
|
||||||
|
{"name": "Person 1", "id": "id-1"},
|
||||||
|
{"name": "Person 2", "id": "id-2"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(hass.states.async_entity_ids()) == 2
|
||||||
|
|
||||||
|
state_1 = hass.states.get("person.person_1")
|
||||||
|
state_2 = hass.states.get("person.person_2")
|
||||||
|
state_3 = hass.states.get("person.person_3")
|
||||||
|
|
||||||
|
assert state_1 is not None
|
||||||
|
assert state_1.name == "Person 1"
|
||||||
|
assert state_2 is not None
|
||||||
|
assert state_2.name == "Person 2"
|
||||||
|
assert state_3 is None
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.config.load_yaml_config_file",
|
||||||
|
autospec=True,
|
||||||
|
return_value={
|
||||||
|
DOMAIN: [
|
||||||
|
{"name": "Person 1-updated", "id": "id-1"},
|
||||||
|
{"name": "Person 3", "id": "id-3"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
), patch("homeassistant.config.find_config_file", return_value=""):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_RELOAD,
|
||||||
|
blocking=True,
|
||||||
|
context=Context(user_id=hass_admin_user.id),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(hass.states.async_entity_ids()) == 2
|
||||||
|
|
||||||
|
state_1 = hass.states.get("person.person_1")
|
||||||
|
state_2 = hass.states.get("person.person_2")
|
||||||
|
state_3 = hass.states.get("person.person_3")
|
||||||
|
|
||||||
|
assert state_1 is not None
|
||||||
|
assert state_1.name == "Person 1-updated"
|
||||||
|
assert state_2 is None
|
||||||
|
assert state_3 is not None
|
||||||
|
assert state_3.name == "Person 3"
|
||||||
|
|
|
@ -118,6 +118,23 @@ async def test_yaml_collection():
|
||||||
{"id": "mock-2", "name": "Mock 2"},
|
{"id": "mock-2", "name": "Mock 2"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Test loading new data. Mock 1 is updated, 2 removed, 3 added.
|
||||||
|
await coll.async_load(
|
||||||
|
[{"id": "mock-1", "name": "Mock 1-updated"}, {"id": "mock-3", "name": "Mock 3"}]
|
||||||
|
)
|
||||||
|
assert len(changes) == 5
|
||||||
|
assert changes[2] == (
|
||||||
|
collection.CHANGE_UPDATED,
|
||||||
|
"mock-1",
|
||||||
|
{"id": "mock-1", "name": "Mock 1-updated"},
|
||||||
|
)
|
||||||
|
assert changes[3] == (
|
||||||
|
collection.CHANGE_ADDED,
|
||||||
|
"mock-3",
|
||||||
|
{"id": "mock-3", "name": "Mock 3"},
|
||||||
|
)
|
||||||
|
assert changes[4] == (collection.CHANGE_REMOVED, "mock-2", None,)
|
||||||
|
|
||||||
|
|
||||||
async def test_yaml_collection_skipping_duplicate_ids():
|
async def test_yaml_collection_skipping_duplicate_ids():
|
||||||
"""Test YAML collection skipping duplicate IDs."""
|
"""Test YAML collection skipping duplicate IDs."""
|
||||||
|
|
Loading…
Add table
Reference in a new issue