Bump entity_registry store version to 1.2 (#59912)
* Bump entity_registry store version to 1.2 * Migrate also when importing yaml * Adjust tests * Satisfy pylint * Fix typing
This commit is contained in:
parent
45d41e584f
commit
c557da028a
4 changed files with 84 additions and 32 deletions
|
@ -36,7 +36,7 @@ from homeassistant.core import (
|
||||||
valid_entity_id,
|
valid_entity_id,
|
||||||
)
|
)
|
||||||
from homeassistant.exceptions import MaxLengthExceeded
|
from homeassistant.exceptions import MaxLengthExceeded
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr, storage
|
||||||
from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED
|
from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
|
@ -58,7 +58,8 @@ DISABLED_HASS = "hass"
|
||||||
DISABLED_INTEGRATION = "integration"
|
DISABLED_INTEGRATION = "integration"
|
||||||
DISABLED_USER = "user"
|
DISABLED_USER = "user"
|
||||||
|
|
||||||
STORAGE_VERSION = 1
|
STORAGE_VERSION_MAJOR = 1
|
||||||
|
STORAGE_VERSION_MINOR = 2
|
||||||
STORAGE_KEY = "core.entity_registry"
|
STORAGE_KEY = "core.entity_registry"
|
||||||
|
|
||||||
# Attributes relevant to describing entity
|
# Attributes relevant to describing entity
|
||||||
|
@ -147,6 +148,16 @@ class RegistryEntry:
|
||||||
hass.states.async_set(self.entity_id, STATE_UNAVAILABLE, attrs)
|
hass.states.async_set(self.entity_id, STATE_UNAVAILABLE, attrs)
|
||||||
|
|
||||||
|
|
||||||
|
class EntityRegistryStore(storage.Store):
|
||||||
|
"""Store entity registry data."""
|
||||||
|
|
||||||
|
async def _async_migrate_func(
|
||||||
|
self, old_major_version: int, old_minor_version: int, old_data: dict
|
||||||
|
) -> dict:
|
||||||
|
"""Migrate to the new version."""
|
||||||
|
return await _async_migrate(old_major_version, old_minor_version, old_data)
|
||||||
|
|
||||||
|
|
||||||
class EntityRegistry:
|
class EntityRegistry:
|
||||||
"""Class to hold a registry of entities."""
|
"""Class to hold a registry of entities."""
|
||||||
|
|
||||||
|
@ -155,8 +166,12 @@ class EntityRegistry:
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.entities: dict[str, RegistryEntry]
|
self.entities: dict[str, RegistryEntry]
|
||||||
self._index: dict[tuple[str, str, str], str] = {}
|
self._index: dict[tuple[str, str, str], str] = {}
|
||||||
self._store = hass.helpers.storage.Store(
|
self._store = EntityRegistryStore(
|
||||||
STORAGE_VERSION, STORAGE_KEY, atomic_writes=True
|
hass,
|
||||||
|
STORAGE_VERSION_MAJOR,
|
||||||
|
STORAGE_KEY,
|
||||||
|
atomic_writes=True,
|
||||||
|
minor_version=STORAGE_VERSION_MINOR,
|
||||||
)
|
)
|
||||||
self.hass.bus.async_listen(
|
self.hass.bus.async_listen(
|
||||||
EVENT_DEVICE_REGISTRY_UPDATED, self.async_device_modified
|
EVENT_DEVICE_REGISTRY_UPDATED, self.async_device_modified
|
||||||
|
@ -509,11 +524,12 @@ class EntityRegistry:
|
||||||
"""Load the entity registry."""
|
"""Load the entity registry."""
|
||||||
async_setup_entity_restore(self.hass, self)
|
async_setup_entity_restore(self.hass, self)
|
||||||
|
|
||||||
data = await self.hass.helpers.storage.async_migrator(
|
data = await storage.async_migrator(
|
||||||
|
self.hass,
|
||||||
self.hass.config.path(PATH_REGISTRY),
|
self.hass.config.path(PATH_REGISTRY),
|
||||||
self._store,
|
self._store,
|
||||||
old_conf_load_func=load_yaml,
|
old_conf_load_func=load_yaml,
|
||||||
old_conf_migrate_func=_async_migrate,
|
old_conf_migrate_func=_async_migrate_yaml_to_json,
|
||||||
)
|
)
|
||||||
entities: dict[str, RegistryEntry] = OrderedDict()
|
entities: dict[str, RegistryEntry] = OrderedDict()
|
||||||
|
|
||||||
|
@ -526,22 +542,22 @@ class EntityRegistry:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entities[entity["entity_id"]] = RegistryEntry(
|
entities[entity["entity_id"]] = RegistryEntry(
|
||||||
area_id=entity.get("area_id"),
|
area_id=entity["area_id"],
|
||||||
capabilities=entity.get("capabilities") or {},
|
capabilities=entity["capabilities"],
|
||||||
config_entry_id=entity.get("config_entry_id"),
|
config_entry_id=entity["config_entry_id"],
|
||||||
device_class=entity.get("device_class"),
|
device_class=entity["device_class"],
|
||||||
device_id=entity.get("device_id"),
|
device_id=entity["device_id"],
|
||||||
disabled_by=entity.get("disabled_by"),
|
disabled_by=entity["disabled_by"],
|
||||||
entity_category=entity.get("entity_category"),
|
entity_category=entity["entity_category"],
|
||||||
entity_id=entity["entity_id"],
|
entity_id=entity["entity_id"],
|
||||||
icon=entity.get("icon"),
|
icon=entity["icon"],
|
||||||
name=entity.get("name"),
|
name=entity["name"],
|
||||||
original_icon=entity.get("original_icon"),
|
original_icon=entity["original_icon"],
|
||||||
original_name=entity.get("original_name"),
|
original_name=entity["original_name"],
|
||||||
platform=entity["platform"],
|
platform=entity["platform"],
|
||||||
supported_features=entity.get("supported_features", 0),
|
supported_features=entity["supported_features"],
|
||||||
unique_id=entity["unique_id"],
|
unique_id=entity["unique_id"],
|
||||||
unit_of_measurement=entity.get("unit_of_measurement"),
|
unit_of_measurement=entity["unit_of_measurement"],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.entities = entities
|
self.entities = entities
|
||||||
|
@ -703,13 +719,43 @@ def async_config_entry_disabled_by_changed(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _async_migrate(entities: dict[str, Any]) -> dict[str, list[dict[str, Any]]]:
|
async def _async_migrate(
|
||||||
|
old_major_version: int, old_minor_version: int, data: dict
|
||||||
|
) -> dict:
|
||||||
|
"""Migrate to the new version."""
|
||||||
|
if old_major_version < 2 and old_minor_version < 2:
|
||||||
|
# From version 1.1
|
||||||
|
for entity in data["entities"]:
|
||||||
|
# Populate all keys
|
||||||
|
entity["area_id"] = entity.get("area_id")
|
||||||
|
entity["capabilities"] = entity.get("capabilities") or {}
|
||||||
|
entity["config_entry_id"] = entity.get("config_entry_id")
|
||||||
|
entity["device_class"] = entity.get("device_class")
|
||||||
|
entity["device_id"] = entity.get("device_id")
|
||||||
|
entity["disabled_by"] = entity.get("disabled_by")
|
||||||
|
entity["entity_category"] = entity.get("entity_category")
|
||||||
|
entity["icon"] = entity.get("icon")
|
||||||
|
entity["name"] = entity.get("name")
|
||||||
|
entity["original_icon"] = entity.get("original_icon")
|
||||||
|
entity["original_name"] = entity.get("original_name")
|
||||||
|
entity["platform"] = entity["platform"]
|
||||||
|
entity["supported_features"] = entity.get("supported_features", 0)
|
||||||
|
entity["unit_of_measurement"] = entity.get("unit_of_measurement")
|
||||||
|
if old_major_version > 1:
|
||||||
|
raise NotImplementedError
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_migrate_yaml_to_json(
|
||||||
|
entities: dict[str, Any]
|
||||||
|
) -> dict[str, list[dict[str, Any]]]:
|
||||||
"""Migrate the YAML config file to storage helper format."""
|
"""Migrate the YAML config file to storage helper format."""
|
||||||
return {
|
entities_1_1 = {
|
||||||
"entities": [
|
"entities": [
|
||||||
{"entity_id": entity_id, **info} for entity_id, info in entities.items()
|
{"entity_id": entity_id, **info} for entity_id, info in entities.items()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
return await _async_migrate(1, 1, entities_1_1)
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
|
|
@ -18,7 +18,7 @@ async def async_get(hass: HomeAssistant) -> str:
|
||||||
"""Get unique ID for the hass instance."""
|
"""Get unique ID for the hass instance."""
|
||||||
store = storage.Store(hass, DATA_VERSION, DATA_KEY, True)
|
store = storage.Store(hass, DATA_VERSION, DATA_KEY, True)
|
||||||
|
|
||||||
data: dict[str, str] | None = await storage.async_migrator( # type: ignore
|
data: dict[str, str] | None = await storage.async_migrator(
|
||||||
hass,
|
hass,
|
||||||
hass.config.path(LEGACY_UUID_FILE),
|
hass.config.path(LEGACY_UUID_FILE),
|
||||||
store,
|
store,
|
||||||
|
|
|
@ -13,7 +13,6 @@ from typing import Any
|
||||||
|
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_FINAL_WRITE
|
from homeassistant.const import EVENT_HOMEASSISTANT_FINAL_WRITE
|
||||||
from homeassistant.core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback
|
||||||
from homeassistant.helpers.event import async_call_later
|
|
||||||
from homeassistant.loader import MAX_LOAD_CONCURRENTLY, bind_hass
|
from homeassistant.loader import MAX_LOAD_CONCURRENTLY, bind_hass
|
||||||
from homeassistant.util import json as json_util
|
from homeassistant.util import json as json_util
|
||||||
|
|
||||||
|
@ -28,13 +27,13 @@ STORAGE_SEMAPHORE = "storage_semaphore"
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
async def async_migrator(
|
async def async_migrator(
|
||||||
hass,
|
hass: HomeAssistant,
|
||||||
old_path,
|
old_path: str,
|
||||||
store,
|
store: Store,
|
||||||
*,
|
*,
|
||||||
old_conf_load_func=None,
|
old_conf_load_func: Callable | None = None,
|
||||||
old_conf_migrate_func=None,
|
old_conf_migrate_func: Callable | None = None,
|
||||||
):
|
) -> Any:
|
||||||
"""Migrate old data to a store and then load data.
|
"""Migrate old data to a store and then load data.
|
||||||
|
|
||||||
async def old_conf_migrate_func(old_data)
|
async def old_conf_migrate_func(old_data)
|
||||||
|
@ -157,10 +156,12 @@ class Store:
|
||||||
stored = data["data"]
|
stored = data["data"]
|
||||||
else:
|
else:
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Migrating %s storage from %s to %s",
|
"Migrating %s storage from %s.%s to %s.%s",
|
||||||
self.key,
|
self.key,
|
||||||
data["version"],
|
data["version"],
|
||||||
|
data["minor_version"],
|
||||||
self.version,
|
self.version,
|
||||||
|
self.minor_version,
|
||||||
)
|
)
|
||||||
if len(inspect.signature(self._async_migrate_func).parameters) == 2:
|
if len(inspect.signature(self._async_migrate_func).parameters) == 2:
|
||||||
# pylint: disable-next=no-value-for-parameter
|
# pylint: disable-next=no-value-for-parameter
|
||||||
|
@ -195,6 +196,9 @@ class Store:
|
||||||
@callback
|
@callback
|
||||||
def async_delay_save(self, data_func: Callable[[], dict], delay: float = 0) -> None:
|
def async_delay_save(self, data_func: Callable[[], dict], delay: float = 0) -> None:
|
||||||
"""Save data with an optional delay."""
|
"""Save data with an optional delay."""
|
||||||
|
# pylint: disable-next=import-outside-toplevel
|
||||||
|
from homeassistant.helpers.event import async_call_later
|
||||||
|
|
||||||
self._data = {
|
self._data = {
|
||||||
"version": self.version,
|
"version": self.version,
|
||||||
"minor_version": self.minor_version,
|
"minor_version": self.minor_version,
|
||||||
|
|
|
@ -223,7 +223,8 @@ def test_is_registered(registry):
|
||||||
async def test_loading_extra_values(hass, hass_storage):
|
async def test_loading_extra_values(hass, hass_storage):
|
||||||
"""Test we load extra data from the registry."""
|
"""Test we load extra data from the registry."""
|
||||||
hass_storage[er.STORAGE_KEY] = {
|
hass_storage[er.STORAGE_KEY] = {
|
||||||
"version": er.STORAGE_VERSION,
|
"version": er.STORAGE_VERSION_MAJOR,
|
||||||
|
"minor_version": 1,
|
||||||
"data": {
|
"data": {
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
|
@ -387,7 +388,8 @@ async def test_migration(hass):
|
||||||
async def test_loading_invalid_entity_id(hass, hass_storage):
|
async def test_loading_invalid_entity_id(hass, hass_storage):
|
||||||
"""Test we autofix invalid entity IDs."""
|
"""Test we autofix invalid entity IDs."""
|
||||||
hass_storage[er.STORAGE_KEY] = {
|
hass_storage[er.STORAGE_KEY] = {
|
||||||
"version": er.STORAGE_VERSION,
|
"version": er.STORAGE_VERSION_MAJOR,
|
||||||
|
"minor_version": er.STORAGE_VERSION_MINOR,
|
||||||
"data": {
|
"data": {
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue