Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7385d23
docs: specify priority zero meter configuration
CircuitSetup Aug 25, 2026
eb470e9
docs: record priority zero configuration task report
CircuitSetup Aug 25, 2026
e09028a
docs: remove scratch task report
CircuitSetup Aug 25, 2026
5f95cf2
feat: model meter and circuit configuration
CircuitSetup Aug 25, 2026
0271e0d
fix: align meter configuration validation contracts
CircuitSetup Aug 25, 2026
e0ee5cf
test: complete meter configuration validation coverage
CircuitSetup Aug 25, 2026
00d2366
fix: reject non-integer meter timing values
CircuitSetup Aug 25, 2026
bfa2819
feat: add voltage transformer presets
CircuitSetup Aug 25, 2026
2b61f69
fix: harden voltage transformer catalog validation
CircuitSetup Aug 25, 2026
f6dd298
fix: enforce voltage transformer catalog schema
CircuitSetup Aug 25, 2026
ce6e1a1
fix: normalize voltage overflow validation
CircuitSetup Aug 25, 2026
c97b831
feat: detect meter configuration capabilities
CircuitSetup Aug 25, 2026
f002cad
fix: make device builder connection state atomic
CircuitSetup Aug 25, 2026
be3430f
fix: protect device builder readiness cleanup
CircuitSetup Aug 25, 2026
b889b13
fix: invalidate interrupted device builder connects
CircuitSetup Aug 25, 2026
ae8848c
feat: persist verified meter configuration metadata
CircuitSetup Aug 25, 2026
c5706fc
feat: parse bounded meter configuration fields
CircuitSetup Aug 25, 2026
a55ec4b
fix: harden bounded configuration parser
CircuitSetup Aug 25, 2026
ae245d1
fix: harden configuration lexical scanning
CircuitSetup Aug 25, 2026
213a817
fix: close configuration parser authority bypasses
CircuitSetup Aug 25, 2026
3b9ab4a
fix: reject unsupported configuration key forms
CircuitSetup Aug 25, 2026
643ff77
fix: close final configuration parser bypasses
CircuitSetup Aug 25, 2026
5a96497
refactor: separate meter and voltage topology
CircuitSetup Aug 25, 2026
d499bc9
fix: preserve verified voltage topology identity
CircuitSetup Aug 25, 2026
831cfab
fix: verify helper voltage topology provenance
CircuitSetup Aug 25, 2026
c73cb5d
fix: validate voltage topology grammar
CircuitSetup Aug 25, 2026
b767262
fix: validate voltage block envelope
CircuitSetup Aug 25, 2026
1b2b421
docs: fix design document whitespace
CircuitSetup Aug 25, 2026
0563501
feat: inventory complete meter configuration
CircuitSetup Aug 25, 2026
c0e6cbb
fix: harden meter configuration inventory
CircuitSetup Aug 25, 2026
93cbab8
fix: map stored voltage calibration by group
CircuitSetup Aug 25, 2026
1a4b91e
fix: map voltage gains by group suffix
CircuitSetup Aug 25, 2026
f81c80b
fix: align meter inventory contracts
CircuitSetup Aug 25, 2026
21fe360
docs: apply meter configuration scope amendment
CircuitSetup Aug 25, 2026
028dc15
fix: infer scalar voltage reference coverage
CircuitSetup Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@
VerifiedOffsetGroup,
VerifiedPowerOffsetGroup,
)
from .topology import topology_from_config
from .topology import (
topology_from_config,
voltage_reference_fingerprint_for_meter,
voltage_reference_topology_from_config,
)

DEFAULT_EVIDENCE_TIMEOUT = 35.0

Expand All @@ -69,6 +73,9 @@
type CalibrationSnapshotReader = Callable[
[str, MeterTopology], Awaitable[ESPHomeConfigSnapshot]
]
type TrustedVoltageFingerprintReader = Callable[
[str, ESPHomeConfigDocument, MeterTopology], Awaitable[str | None]
]

_CONFIGURATION_ID = re.compile(r"[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?\.yaml")

Expand Down Expand Up @@ -182,6 +189,8 @@ def __init__(
restart_restore_timeout: float = 120.0,
restart_backoff_initial: float = 0.25,
calibration_snapshot_reader: CalibrationSnapshotReader | None = None,
trusted_voltage_fingerprint_reader: TrustedVoltageFingerprintReader
| None = None,
) -> None:
if sample_count < 1 or zero_concurrency < 1:
raise ValueError("sample count and zero concurrency must be positive")
Expand All @@ -204,6 +213,7 @@ def __init__(
self._restart_restore_timeout = restart_restore_timeout
self._restart_backoff_initial = restart_backoff_initial
self._calibration_snapshot_reader = calibration_snapshot_reader
self._trusted_voltage_fingerprint_reader = trusted_voltage_fingerprint_reader
self._operation_sequences: dict[str, int] = {}

async def async_verify_after_restart(
Expand Down Expand Up @@ -348,6 +358,7 @@ async def async_verify_after_restart(
topology_project_name=pending.topology.project_name,
topology_connection_type=pending.topology.connection_type,
topology_voltage_layout=pending.topology.voltage_layout,
topology_voltage_fingerprint=pending.voltage_topology_fingerprint,
connection_generation=generation,
groups=groups,
verification_id=uuid4().hex,
Expand Down Expand Up @@ -438,15 +449,28 @@ async def _calibration_origin(
raise ValueError("authoritative configuration hash is invalid")
if _CONFIGURATION_ID.fullmatch(snapshot.configuration) is None:
raise ValueError("authoritative configuration filename is invalid")
document = ESPHomeConfigDocument.parse(snapshot.content)
try:
source_topology = topology_from_config(
ESPHomeConfigDocument.parse(snapshot.content),
document,
native_project_name=binding.topology.project_name,
)
except ValueError as error:
raise ValueError(
"authoritative configuration topology is invalid"
) from error
trusted_voltage_fingerprint = (
await self._trusted_voltage_fingerprint_reader(
lease.mac, document, source_topology
)
if self._trusted_voltage_fingerprint_reader is not None
else None
)
source_voltage_fingerprint = voltage_reference_topology_from_config(
document,
source_topology,
trusted_fingerprint=trusted_voltage_fingerprint,
).fingerprint
if not _same_topology_identity(source_topology, binding.topology):
raise ValueError(
"authoritative configuration topology does not match the session"
Expand All @@ -456,13 +480,18 @@ async def _calibration_origin(
snapshot.configuration != pending.config_filename
or snapshot.sha256 != pending.config_sha256
or not _same_topology_identity(source_topology, pending.topology)
or (
pending.voltage_topology_fingerprint
or voltage_reference_fingerprint_for_meter(source_topology)
)
!= source_voltage_fingerprint
):
raise ValueError(
"authoritative configuration changed since calibration began"
)
return pending
return self.sessions._begin_calibration_origin(
lease, session, binding, snapshot
lease, session, binding, snapshot, source_voltage_fingerprint
)

async def async_zero_all_references(
Expand Down
Loading
Loading