mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-11-13 01:03:45 +01:00
fix(config): keep dict entries with "/" in keys during config migration
The automatic field migration recursed into every dict and addressed each
leaf by a "/"-joined path. Keys that themselves contain "/" - e.g. the config
paths used as keys of adapter.homeassistant.config_entity_ids - were split
into path elements. set_nested_value() then created a "devices" entry with
an empty string default and failed further down, so every version migration
replaced the mapping by {"devices": ""}.
Migrate a dict whose keys contain "/" as one value instead of per key.
Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Bobby Noelte
co-authored by
Claude Opus 5.5
parent
93ccb8d592
commit
7767028a14
@@ -497,7 +497,15 @@ def _migrate_matching_fields(
|
||||
if full_path in migrated_source_paths:
|
||||
continue
|
||||
|
||||
if isinstance(value, dict):
|
||||
if isinstance(value, dict) and any("/" in str(k) for k in value):
|
||||
# Keys containing "/" (e.g. config paths used as mapping keys) can not be
|
||||
# addressed by a "/"-separated path - migrate the whole dict as one value.
|
||||
try:
|
||||
target_model.set_nested_value(full_path, value)
|
||||
count += 1
|
||||
except Exception:
|
||||
skipped_paths.append(full_path)
|
||||
elif isinstance(value, dict):
|
||||
count += _migrate_matching_fields(
|
||||
value, target_model, migrated_source_paths, skipped_paths, full_path
|
||||
)
|
||||
|
||||
@@ -264,3 +264,38 @@ class TestDeviceMigration:
|
||||
assert migrated.devices.batteries is not None
|
||||
assert migrated.devices.batteries["house"].levelized_cost_of_storage_amt_kwh == 0.123
|
||||
assert raw == original
|
||||
|
||||
|
||||
class TestSlashKeyedDictMigration:
|
||||
"""Dict entries whose keys contain '/' must survive migration unchanged."""
|
||||
|
||||
ENTITY_IDS = {
|
||||
"devices/electric_vehicles/ev11/max_soc_percentage": "sensor.ev_max_soc",
|
||||
"devices/electric_vehicles/ev11/min_soc_deadline_datetime": "sensor.ev_departure",
|
||||
"devices/electric_vehicles/ev11/min_soc_percentage": "sensor.ev_target_soc",
|
||||
}
|
||||
|
||||
def test_migrate_config_data_keeps_slash_keys(self):
|
||||
raw = {"adapter": {"homeassistant": {"config_entity_ids": dict(self.ENTITY_IDS)}}}
|
||||
migrated = migrate_config_data(raw)
|
||||
assert migrated.adapter.homeassistant.config_entity_ids == self.ENTITY_IDS
|
||||
assert not any(p.startswith("adapter/homeassistant") for p in configmigrate.skipped_paths)
|
||||
|
||||
def test_migrate_config_file_keeps_slash_keys(self, config_default_dirs):
|
||||
config_default_dir_user, _, _, _ = config_default_dirs
|
||||
config_file = config_default_dir_user.joinpath(ConfigEOS.CONFIG_FILE_NAME)
|
||||
config_file.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"general": {"version": "0.0.0-old"},
|
||||
"adapter": {"homeassistant": {"config_entity_ids": self.ENTITY_IDS}},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
assert configmigrate.migrate_config_file(config_file, config_file.with_suffix(".bak"))
|
||||
|
||||
migrated = json.loads(config_file.read_text(encoding="utf-8"))
|
||||
assert migrated["general"]["version"] == __version__
|
||||
assert migrated["adapter"]["homeassistant"]["config_entity_ids"] == self.ENTITY_IDS
|
||||
|
||||
Reference in New Issue
Block a user