data_model: add extract_all() using Dashboard instruments as primary snapshot
Replaces the domain-based collect_snapshot with extract_all(), which iterates
all supported instruments via vw_dashboard.Dashboard. Each entry is stored as
{value, unit, str_state} grouped by component type. Units are validated against
a whitelist and normalised (d→days, °C→degC); unrecognised units are dropped.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import logging
|
||||
import re
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from volkswagencarnet.vw_const import Paths
|
||||
from volkswagencarnet.vw_dashboard import Dashboard
|
||||
from volkswagencarnet.vw_utilities import find_path
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -283,6 +285,73 @@ def extract_windows(vehicle) -> dict | None:
|
||||
return result or None
|
||||
|
||||
|
||||
_UNIT_WHITELIST: frozenset[str] = frozenset({"min", "%", "d", "kW", "°C", "km", "V", "W", "A"})
|
||||
_UNIT_CONVERSIONS: dict[str, str] = {"d": "days", "°C": "degC"}
|
||||
|
||||
|
||||
def _normalise_unit(unit: str) -> str:
|
||||
if unit not in _UNIT_WHITELIST:
|
||||
return ""
|
||||
return _UNIT_CONVERSIONS.get(unit, unit)
|
||||
|
||||
|
||||
def _unit_from_str_state(str_state: str, state) -> str:
|
||||
"""Parse the unit suffix from a str_state like '42 %' or '21.5 degC'."""
|
||||
state_repr = str(state)
|
||||
if str_state.startswith(state_repr):
|
||||
suffix = str_state[len(state_repr):].strip()
|
||||
if suffix:
|
||||
return suffix
|
||||
# fallback: last whitespace-separated token if it looks like a unit
|
||||
parts = str_state.rsplit(None, 1)
|
||||
if len(parts) == 2 and re.fullmatch(r"[^\d\s]\S*", parts[1]):
|
||||
return parts[1]
|
||||
return ""
|
||||
|
||||
|
||||
def extract_all(vehicle) -> dict | None:
|
||||
"""Read every supported instrument via the Dashboard and return a flat snapshot.
|
||||
|
||||
Structure: snapshot[component][attr] = {"value": state, "unit": unit, "str_state": str_state}
|
||||
Unit is taken from instrument.unit when available, otherwise parsed from str_state.
|
||||
"""
|
||||
snapshot: dict = {"ts": datetime.now(timezone.utc).isoformat()}
|
||||
try:
|
||||
dashboard = Dashboard(vehicle)
|
||||
except Exception:
|
||||
log.debug("extract_all: Dashboard setup failed", exc_info=True)
|
||||
return None
|
||||
|
||||
for instrument in dashboard.instruments:
|
||||
try:
|
||||
state = instrument.state
|
||||
if state is None:
|
||||
continue
|
||||
|
||||
try:
|
||||
str_state = str(instrument.str_state)
|
||||
except Exception:
|
||||
str_state = str(state)
|
||||
|
||||
if hasattr(instrument, "unit") and instrument.unit:
|
||||
raw_unit = instrument.unit
|
||||
else:
|
||||
raw_unit = _unit_from_str_state(str_state, state)
|
||||
unit = _normalise_unit(raw_unit)
|
||||
|
||||
component = instrument.component
|
||||
attr = instrument.attr
|
||||
snapshot.setdefault(component, {})[attr] = {
|
||||
"value": state,
|
||||
"unit": unit,
|
||||
"str_state": str_state,
|
||||
}
|
||||
except Exception:
|
||||
log.debug("extract_all: %s unavailable", instrument.attr, exc_info=True)
|
||||
|
||||
return snapshot or None
|
||||
|
||||
|
||||
ALL_DOMAINS: dict[str, Callable] = {
|
||||
"charging": extract_charging,
|
||||
"climatisation": extract_climatisation,
|
||||
|
||||
Reference in New Issue
Block a user