mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-10-30 09:41:57 +00:00
fix(homeassistant): resolve legacy device measurement keys
Resolve legacy underscore spellings for Home Assistant device measurements only when they uniquely match a registered EOS key; preserve canonical, custom, and ambiguous keys. Correct the schema examples to show canonical hyphenated keys. Add a synthetic regression test that exercises data acquisition without contacting Home Assistant.
This commit is contained in:
@@ -6,7 +6,7 @@ from typing import Optional, Union
|
||||
import pandas as pd
|
||||
import requests
|
||||
from loguru import logger
|
||||
from pydantic import Field, computed_field, field_validator
|
||||
from pydantic import Field, PrivateAttr, computed_field, field_validator
|
||||
|
||||
from akkudoktoreos.adapter.adapterabc import AdapterProvider
|
||||
from akkudoktoreos.config.configabc import SettingsBaseModel
|
||||
@@ -94,11 +94,15 @@ class HomeAssistantAdapterCommonSettings(SettingsBaseModel):
|
||||
device_measurement_entity_ids: Optional[dict[str, str]] = Field(
|
||||
default=None,
|
||||
json_schema_extra={
|
||||
"description": "Mapping of EOS measurement keys used by device (resource) simulations to Home Assistant entity IDs.",
|
||||
"description": (
|
||||
"Mapping of EOS device measurement keys to Home Assistant entity IDs. "
|
||||
"Registered keys such as 'battery1-soc-factor' are accepted; legacy "
|
||||
"underscore spellings are resolved when they uniquely match a registered key."
|
||||
),
|
||||
"examples": [
|
||||
{
|
||||
"ev11_soc_factor": "sensor.ev11_soc_factor",
|
||||
"battery1_soc_factor": "sensor.battery1_soc_factor",
|
||||
"ev11-soc-factor": "sensor.ev11_soc_factor",
|
||||
"battery1-soc-factor": "sensor.battery1_soc_factor",
|
||||
}
|
||||
],
|
||||
},
|
||||
@@ -219,6 +223,8 @@ class HomeAssistantAdapterCommonSettings(SettingsBaseModel):
|
||||
|
||||
|
||||
class HomeAssistantAdapter(AdapterProvider):
|
||||
_legacy_device_measurement_key_warning_emitted: bool = PrivateAttr(default=False)
|
||||
|
||||
@classmethod
|
||||
def provider_id(cls) -> str:
|
||||
"""Return the unique identifier for the adapter provider."""
|
||||
@@ -276,6 +282,22 @@ class HomeAssistantAdapter(AdapterProvider):
|
||||
solution_entity_ids = []
|
||||
return solution_entity_ids
|
||||
|
||||
@staticmethod
|
||||
def _resolve_device_measurement_key(measurement_key: str, registered_keys: set[str]) -> str:
|
||||
"""Resolve a legacy underscore alias to one registered device measurement key."""
|
||||
if measurement_key in registered_keys:
|
||||
return measurement_key
|
||||
|
||||
aliases = {key for key in registered_keys if key.replace("-", "_") == measurement_key}
|
||||
if len(aliases) == 1:
|
||||
return next(iter(aliases))
|
||||
if len(aliases) > 1:
|
||||
logger.warning(
|
||||
"Ambiguous legacy device measurement key '{}'; leaving it unchanged.",
|
||||
measurement_key,
|
||||
)
|
||||
return measurement_key
|
||||
|
||||
def _entity_id_from_resource_id(self, resource_id: str) -> str:
|
||||
return HOMEASSISTANT_ENTITY_ID_PREFIX + resource_id
|
||||
|
||||
@@ -461,10 +483,23 @@ class HomeAssistantAdapter(AdapterProvider):
|
||||
# Retrieve measurements necessary for device simulations
|
||||
entity_ids = self.config.adapter.homeassistant.device_measurement_entity_ids
|
||||
if entity_ids:
|
||||
registered_measurement_keys = set(self.config.devices.measurement_keys)
|
||||
for (
|
||||
measurement_key,
|
||||
entity_id,
|
||||
) in entity_ids.items():
|
||||
resolved_measurement_key = self._resolve_device_measurement_key(
|
||||
measurement_key, registered_measurement_keys
|
||||
)
|
||||
if resolved_measurement_key != measurement_key:
|
||||
if not self._legacy_device_measurement_key_warning_emitted:
|
||||
logger.warning(
|
||||
"Legacy device measurement key '{}' mapped to registered key '{}'.",
|
||||
measurement_key,
|
||||
resolved_measurement_key,
|
||||
)
|
||||
self._legacy_device_measurement_key_warning_emitted = True
|
||||
measurement_key = resolved_measurement_key
|
||||
if entity_id:
|
||||
try:
|
||||
state = self.get_entity_state(entity_id)
|
||||
|
||||
Reference in New Issue
Block a user