mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 00:37:13 +00:00
Add minor version support to storage.Store (#59882)
This commit is contained in:
parent
cc3f179796
commit
d18c250acf
@ -20,14 +20,14 @@ STORAGE_VERSION = 4
|
|||||||
class OnboadingStorage(Store):
|
class OnboadingStorage(Store):
|
||||||
"""Store onboarding data."""
|
"""Store onboarding data."""
|
||||||
|
|
||||||
async def _async_migrate_func(self, old_version, old_data):
|
async def _async_migrate_func(self, old_major_version, old_minor_version, old_data):
|
||||||
"""Migrate to the new version."""
|
"""Migrate to the new version."""
|
||||||
# From version 1 -> 2, we automatically mark the integration step done
|
# From version 1 -> 2, we automatically mark the integration step done
|
||||||
if old_version < 2:
|
if old_major_version < 2:
|
||||||
old_data["done"].append(STEP_INTEGRATION)
|
old_data["done"].append(STEP_INTEGRATION)
|
||||||
if old_version < 3:
|
if old_major_version < 3:
|
||||||
old_data["done"].append(STEP_CORE_CONFIG)
|
old_data["done"].append(STEP_CORE_CONFIG)
|
||||||
if old_version < 4:
|
if old_major_version < 4:
|
||||||
old_data["done"].append(STEP_ANALYTICS)
|
old_data["done"].append(STEP_ANALYTICS)
|
||||||
return old_data
|
return old_data
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ UPDATE_FIELDS = {
|
|||||||
class PersonStore(Store):
|
class PersonStore(Store):
|
||||||
"""Person storage."""
|
"""Person storage."""
|
||||||
|
|
||||||
async def _async_migrate_func(self, old_version, old_data):
|
async def _async_migrate_func(self, old_major_version, old_minor_version, old_data):
|
||||||
"""Migrate to the new version.
|
"""Migrate to the new version.
|
||||||
|
|
||||||
Migrate storage to use format of collection helper.
|
Migrate storage to use format of collection helper.
|
||||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
import inspect
|
||||||
from json import JSONEncoder
|
from json import JSONEncoder
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -75,11 +76,13 @@ class Store:
|
|||||||
key: str,
|
key: str,
|
||||||
private: bool = False,
|
private: bool = False,
|
||||||
*,
|
*,
|
||||||
encoder: type[JSONEncoder] | None = None,
|
|
||||||
atomic_writes: bool = False,
|
atomic_writes: bool = False,
|
||||||
|
encoder: type[JSONEncoder] | None = None,
|
||||||
|
minor_version: int = 1,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize storage class."""
|
"""Initialize storage class."""
|
||||||
self.version = version
|
self.version = version
|
||||||
|
self.minor_version = minor_version
|
||||||
self.key = key
|
self.key = key
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self._private = private
|
self._private = private
|
||||||
@ -99,8 +102,8 @@ class Store:
|
|||||||
async def async_load(self) -> dict | list | None:
|
async def async_load(self) -> dict | list | None:
|
||||||
"""Load data.
|
"""Load data.
|
||||||
|
|
||||||
If the expected version does not match the given version, the migrate
|
If the expected version and minor version do not match the given versions, the
|
||||||
function will be invoked with await migrate_func(version, config).
|
migrate function will be invoked with migrate_func(version, minor_version, config).
|
||||||
|
|
||||||
Will ensure that when a call comes in while another one is in progress,
|
Will ensure that when a call comes in while another one is in progress,
|
||||||
the second call will wait and return the result of the first call.
|
the second call will wait and return the result of the first call.
|
||||||
@ -137,7 +140,15 @@ class Store:
|
|||||||
|
|
||||||
if data == {}:
|
if data == {}:
|
||||||
return None
|
return None
|
||||||
if data["version"] == self.version:
|
|
||||||
|
# Add minor_version if not set
|
||||||
|
if "minor_version" not in data:
|
||||||
|
data["minor_version"] = 1
|
||||||
|
|
||||||
|
if (
|
||||||
|
data["version"] == self.version
|
||||||
|
and data["minor_version"] == self.minor_version
|
||||||
|
):
|
||||||
stored = data["data"]
|
stored = data["data"]
|
||||||
else:
|
else:
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
@ -146,13 +157,29 @@ class Store:
|
|||||||
data["version"],
|
data["version"],
|
||||||
self.version,
|
self.version,
|
||||||
)
|
)
|
||||||
stored = await self._async_migrate_func(data["version"], data["data"])
|
if len(inspect.signature(self._async_migrate_func).parameters) == 2:
|
||||||
|
# pylint: disable-next=no-value-for-parameter
|
||||||
|
stored = await self._async_migrate_func(data["version"], data["data"])
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
stored = await self._async_migrate_func(
|
||||||
|
data["version"], data["minor_version"], data["data"]
|
||||||
|
)
|
||||||
|
except NotImplementedError:
|
||||||
|
if data["version"] != self.version:
|
||||||
|
raise
|
||||||
|
stored = data["data"]
|
||||||
|
|
||||||
return stored
|
return stored
|
||||||
|
|
||||||
async def async_save(self, data: dict | list) -> None:
|
async def async_save(self, data: dict | list) -> None:
|
||||||
"""Save data."""
|
"""Save data."""
|
||||||
self._data = {"version": self.version, "key": self.key, "data": data}
|
self._data = {
|
||||||
|
"version": self.version,
|
||||||
|
"minor_version": self.minor_version,
|
||||||
|
"key": self.key,
|
||||||
|
"data": data,
|
||||||
|
}
|
||||||
|
|
||||||
if self.hass.state == CoreState.stopping:
|
if self.hass.state == CoreState.stopping:
|
||||||
self._async_ensure_final_write_listener()
|
self._async_ensure_final_write_listener()
|
||||||
@ -163,7 +190,12 @@ 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."""
|
||||||
self._data = {"version": self.version, "key": self.key, "data_func": data_func}
|
self._data = {
|
||||||
|
"version": self.version,
|
||||||
|
"minor_version": self.minor_version,
|
||||||
|
"key": self.key,
|
||||||
|
"data_func": data_func,
|
||||||
|
}
|
||||||
|
|
||||||
self._async_cleanup_delay_listener()
|
self._async_cleanup_delay_listener()
|
||||||
self._async_ensure_final_write_listener()
|
self._async_ensure_final_write_listener()
|
||||||
@ -248,7 +280,7 @@ class Store:
|
|||||||
atomic_writes=self._atomic_writes,
|
atomic_writes=self._atomic_writes,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _async_migrate_func(self, old_version, old_data):
|
async def _async_migrate_func(self, old_major_version, old_minor_version, old_data):
|
||||||
"""Migrate to the new version."""
|
"""Migrate to the new version."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@ -169,6 +169,7 @@ async def test_agent_user_id_storage(hass, hass_storage):
|
|||||||
|
|
||||||
hass_storage["google_assistant"] = {
|
hass_storage["google_assistant"] = {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
"key": "google_assistant",
|
"key": "google_assistant",
|
||||||
"data": {"agent_user_ids": {"agent_1": {}}},
|
"data": {"agent_user_ids": {"agent_1": {}}},
|
||||||
}
|
}
|
||||||
@ -178,6 +179,7 @@ async def test_agent_user_id_storage(hass, hass_storage):
|
|||||||
|
|
||||||
assert hass_storage["google_assistant"] == {
|
assert hass_storage["google_assistant"] == {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
"key": "google_assistant",
|
"key": "google_assistant",
|
||||||
"data": {"agent_user_ids": {"agent_1": {}}},
|
"data": {"agent_user_ids": {"agent_1": {}}},
|
||||||
}
|
}
|
||||||
@ -188,6 +190,7 @@ async def test_agent_user_id_storage(hass, hass_storage):
|
|||||||
|
|
||||||
assert hass_storage["google_assistant"] == {
|
assert hass_storage["google_assistant"] == {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
"key": "google_assistant",
|
"key": "google_assistant",
|
||||||
"data": data,
|
"data": data,
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,9 @@ from homeassistant.util import dt
|
|||||||
from tests.common import async_fire_time_changed
|
from tests.common import async_fire_time_changed
|
||||||
|
|
||||||
MOCK_VERSION = 1
|
MOCK_VERSION = 1
|
||||||
|
MOCK_VERSION_2 = 2
|
||||||
|
MOCK_MINOR_VERSION_1 = 1
|
||||||
|
MOCK_MINOR_VERSION_2 = 2
|
||||||
MOCK_KEY = "storage-test"
|
MOCK_KEY = "storage-test"
|
||||||
MOCK_DATA = {"hello": "world"}
|
MOCK_DATA = {"hello": "world"}
|
||||||
MOCK_DATA2 = {"goodbye": "cruel world"}
|
MOCK_DATA2 = {"goodbye": "cruel world"}
|
||||||
@ -28,6 +31,30 @@ def store(hass):
|
|||||||
yield storage.Store(hass, MOCK_VERSION, MOCK_KEY)
|
yield storage.Store(hass, MOCK_VERSION, MOCK_KEY)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def store_v_1_1(hass):
|
||||||
|
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
||||||
|
yield storage.Store(
|
||||||
|
hass, MOCK_VERSION, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def store_v_1_2(hass):
|
||||||
|
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
||||||
|
yield storage.Store(
|
||||||
|
hass, MOCK_VERSION, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_2
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def store_v_2_1(hass):
|
||||||
|
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
||||||
|
yield storage.Store(
|
||||||
|
hass, MOCK_VERSION_2, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_loading(hass, store):
|
async def test_loading(hass, store):
|
||||||
"""Test we can save and load data."""
|
"""Test we can save and load data."""
|
||||||
await store.async_save(MOCK_DATA)
|
await store.async_save(MOCK_DATA)
|
||||||
@ -78,6 +105,7 @@ async def test_saving_with_delay(hass, store, hass_storage):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"data": MOCK_DATA,
|
"data": MOCK_DATA,
|
||||||
}
|
}
|
||||||
@ -101,6 +129,7 @@ async def test_saving_on_final_write(hass, hass_storage):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"data": MOCK_DATA,
|
"data": MOCK_DATA,
|
||||||
}
|
}
|
||||||
@ -148,6 +177,7 @@ async def test_loading_while_delay(hass, store, hass_storage):
|
|||||||
await store.async_save({"delay": "no"})
|
await store.async_save({"delay": "no"})
|
||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"data": {"delay": "no"},
|
"data": {"delay": "no"},
|
||||||
}
|
}
|
||||||
@ -155,6 +185,7 @@ async def test_loading_while_delay(hass, store, hass_storage):
|
|||||||
store.async_delay_save(lambda: {"delay": "yes"}, 1)
|
store.async_delay_save(lambda: {"delay": "yes"}, 1)
|
||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"data": {"delay": "no"},
|
"data": {"delay": "no"},
|
||||||
}
|
}
|
||||||
@ -170,6 +201,7 @@ async def test_writing_while_writing_delay(hass, store, hass_storage):
|
|||||||
await store.async_save({"delay": "no"})
|
await store.async_save({"delay": "no"})
|
||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"data": {"delay": "no"},
|
"data": {"delay": "no"},
|
||||||
}
|
}
|
||||||
@ -178,6 +210,7 @@ async def test_writing_while_writing_delay(hass, store, hass_storage):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"data": {"delay": "no"},
|
"data": {"delay": "no"},
|
||||||
}
|
}
|
||||||
@ -196,6 +229,7 @@ async def test_multiple_delay_save_calls(hass, store, hass_storage):
|
|||||||
await store.async_save({"delay": "no"})
|
await store.async_save({"delay": "no"})
|
||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"data": {"delay": "no"},
|
"data": {"delay": "no"},
|
||||||
}
|
}
|
||||||
@ -204,6 +238,7 @@ async def test_multiple_delay_save_calls(hass, store, hass_storage):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"data": {"delay": "no"},
|
"data": {"delay": "no"},
|
||||||
}
|
}
|
||||||
@ -221,6 +256,7 @@ async def test_multiple_save_calls(hass, store, hass_storage):
|
|||||||
await asyncio.gather(*tasks)
|
await asyncio.gather(*tasks)
|
||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"data": {"savecount": 5},
|
"data": {"savecount": 5},
|
||||||
}
|
}
|
||||||
@ -252,6 +288,7 @@ async def test_migrator_existing_config(hass, store, hass_storage):
|
|||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"data": data,
|
"data": data,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,5 +314,125 @@ async def test_migrator_transforming_config(hass, store, hass_storage):
|
|||||||
assert hass_storage[store.key] == {
|
assert hass_storage[store.key] == {
|
||||||
"key": MOCK_KEY,
|
"key": MOCK_KEY,
|
||||||
"version": MOCK_VERSION,
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
"data": data,
|
"data": data,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_minor_version_default(hass, store, hass_storage):
|
||||||
|
"""Test minor version default."""
|
||||||
|
|
||||||
|
await store.async_save(MOCK_DATA)
|
||||||
|
assert hass_storage[store.key]["minor_version"] == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_minor_version(hass, store_v_1_2, hass_storage):
|
||||||
|
"""Test minor version."""
|
||||||
|
|
||||||
|
await store_v_1_2.async_save(MOCK_DATA)
|
||||||
|
assert hass_storage[store_v_1_2.key]["minor_version"] == MOCK_MINOR_VERSION_2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_migrate_major_not_implemented_raises(hass, store, store_v_2_1):
|
||||||
|
"""Test migrating between major versions fails if not implemented."""
|
||||||
|
|
||||||
|
await store_v_2_1.async_save(MOCK_DATA)
|
||||||
|
with pytest.raises(NotImplementedError):
|
||||||
|
await store.async_load()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_migrate_minor_not_implemented(
|
||||||
|
hass, hass_storage, store_v_1_1, store_v_1_2
|
||||||
|
):
|
||||||
|
"""Test migrating between minor versions does not fail if not implemented."""
|
||||||
|
|
||||||
|
assert store_v_1_1.key == store_v_1_2.key
|
||||||
|
|
||||||
|
await store_v_1_1.async_save(MOCK_DATA)
|
||||||
|
assert hass_storage[store_v_1_1.key] == {
|
||||||
|
"key": MOCK_KEY,
|
||||||
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": MOCK_MINOR_VERSION_1,
|
||||||
|
"data": MOCK_DATA,
|
||||||
|
}
|
||||||
|
data = await store_v_1_2.async_load()
|
||||||
|
assert hass_storage[store_v_1_1.key]["data"] == data
|
||||||
|
|
||||||
|
await store_v_1_2.async_save(MOCK_DATA)
|
||||||
|
assert hass_storage[store_v_1_2.key] == {
|
||||||
|
"key": MOCK_KEY,
|
||||||
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": MOCK_MINOR_VERSION_2,
|
||||||
|
"data": MOCK_DATA,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_migration(hass, hass_storage, store_v_1_2):
|
||||||
|
"""Test migration."""
|
||||||
|
calls = 0
|
||||||
|
|
||||||
|
class CustomStore(storage.Store):
|
||||||
|
async def _async_migrate_func(
|
||||||
|
self, old_major_version, old_minor_version, old_data: dict
|
||||||
|
):
|
||||||
|
nonlocal calls
|
||||||
|
calls += 1
|
||||||
|
assert old_major_version == store_v_1_2.version
|
||||||
|
assert old_minor_version == store_v_1_2.minor_version
|
||||||
|
return old_data
|
||||||
|
|
||||||
|
await store_v_1_2.async_save(MOCK_DATA)
|
||||||
|
assert hass_storage[store_v_1_2.key] == {
|
||||||
|
"key": MOCK_KEY,
|
||||||
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": MOCK_MINOR_VERSION_2,
|
||||||
|
"data": MOCK_DATA,
|
||||||
|
}
|
||||||
|
assert calls == 0
|
||||||
|
|
||||||
|
legacy_store = CustomStore(hass, 2, store_v_1_2.key, minor_version=1)
|
||||||
|
data = await legacy_store.async_load()
|
||||||
|
assert calls == 1
|
||||||
|
assert hass_storage[store_v_1_2.key]["data"] == data
|
||||||
|
|
||||||
|
await legacy_store.async_save(MOCK_DATA)
|
||||||
|
assert hass_storage[legacy_store.key] == {
|
||||||
|
"key": MOCK_KEY,
|
||||||
|
"version": 2,
|
||||||
|
"minor_version": 1,
|
||||||
|
"data": MOCK_DATA,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_legacy_migration(hass, hass_storage, store_v_1_2):
|
||||||
|
"""Test legacy migration method signature."""
|
||||||
|
calls = 0
|
||||||
|
|
||||||
|
class LegacyStore(storage.Store):
|
||||||
|
async def _async_migrate_func(self, old_version, old_data: dict):
|
||||||
|
nonlocal calls
|
||||||
|
calls += 1
|
||||||
|
assert old_version == store_v_1_2.version
|
||||||
|
return old_data
|
||||||
|
|
||||||
|
await store_v_1_2.async_save(MOCK_DATA)
|
||||||
|
assert hass_storage[store_v_1_2.key] == {
|
||||||
|
"key": MOCK_KEY,
|
||||||
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": MOCK_MINOR_VERSION_2,
|
||||||
|
"data": MOCK_DATA,
|
||||||
|
}
|
||||||
|
assert calls == 0
|
||||||
|
|
||||||
|
legacy_store = LegacyStore(hass, 2, store_v_1_2.key, minor_version=1)
|
||||||
|
data = await legacy_store.async_load()
|
||||||
|
assert calls == 1
|
||||||
|
assert hass_storage[store_v_1_2.key]["data"] == data
|
||||||
|
|
||||||
|
await legacy_store.async_save(MOCK_DATA)
|
||||||
|
assert hass_storage[legacy_store.key] == {
|
||||||
|
"key": MOCK_KEY,
|
||||||
|
"version": 2,
|
||||||
|
"minor_version": 1,
|
||||||
|
"data": MOCK_DATA,
|
||||||
|
}
|
||||||
|
@ -444,6 +444,7 @@ async def test_updating_configuration(hass, hass_storage):
|
|||||||
},
|
},
|
||||||
"key": "core.config",
|
"key": "core.config",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
}
|
}
|
||||||
hass_storage["core.config"] = dict(core_data)
|
hass_storage["core.config"] = dict(core_data)
|
||||||
await config_util.async_process_ha_core_config(
|
await config_util.async_process_ha_core_config(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user