Repository navigation
ha/customize: key the state-of-charge entry on the real soc capability - #28
Merged
Merged
Conversation
#27) The customizer's SoC entry was keyed `battery`, which is not an eBus capability and never has been: `capabilities/battery.json` exists in no commit on any branch of the specification, and `energy.ebus.capability.soc` has carried that name in the capability-types registry since 2026-05-22, six weeks before this table was written. State of charge lives in soc, so a conformant device typing its node that way resolved to the key `soc`, missed the table, and reached Home Assistant with neither a device_class nor a state_class on its SoC: precisely the ambiguous bare percent the entry exists to resolve. It stayed green because the defect was self-validating. The tests fabricated `energy.ebus.capability.battery` to exercise the entry and the example published that node type, so the example's self-check asserted device_class == "battery" and passed. Nothing in between touched a real capability name. Re-key the entry to `soc` and correct its properties to the ones the capability defines. The bigger half of the fix is that soe, total-energy-storage and loadup-headroom were not merely uncovered: unit inference sees Wh/kWh and says energy + total_increasing, and these are reservoir levels that FALL on discharge, so HA read every discharge as a meter reset and back-filled the drop as freshly consumed energy. They now carry energy_storage + measurement, HA's level counterpart. info's nameplate-capacity gets the same correction, being a constant rather than a register. state-of-charge, power and temperature are dropped: none is a property of any eBus capability, a battery's electrical power is meter/active-power (already covered), and eBus does not model pack temperature. Note this removes the `battery` key rather than aliasing it. A publisher that copied the old example's non-conformant node type should move to soc; the only known publisher of it was that example. Wider table-versus-catalog drift found in the same pass: imported-active-energy and exported-active-energy named meter properties that have never existed at any version (inert, since a Wh property already infers correctly, but drift of the same kind); the four cumulative reactive/apparent registers carried no state_class at all, so HA kept no long-term statistics for them (the absent device_class there is deliberate and stays, HA having none for varh/VAh); and power-factor-{a,b,c} were missing while the system-level power-factor was present, despite being equally unitless and so invisible to inference. Add tests/test_catalog_drift.py, which walks the table against the machine-readable catalogs in a sibling specification checkout so a name can no longer stop matching without something failing. It reads spec HEAD rather than the pinned synced_commit (the catalogs postdate that pin) and skips cleanly when no checkout is present, which is the case in CI today. The regression tests deliberately use a node id other than `soc`, since _capability_of falls back to the node id and a same-named node would pass even with $type resolution broken. Re-sync .ebus-spec.json, 48 commits behind: framework 0.5 -> 0.7, info and meter 0.1 -> 0.2, utility-meter 0.3 -> 0.6, capability-types 0.11 -> 0.19. The supports list is unchanged (no framework feature was added or removed across 0.5 -> 0.7). Two corrections beyond the numbers: grid, status, demand and power-quality are now pinned directly, the old notes rationale ("covered by pinning utility-meter") having lapsed when utility-meter 0.6 split them into standalone catalogs; and soc is pinned, the customizer now having first-class knowledge of it. Reported by @cayossarian, who also proposed the catalog check. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The registry gap this audit surfaced is fixed upstream (specification 922b9f8 registers utility-meter plus the three BESS child roles the data models already declared), so pin device-types 0.5 and re-point synced_commit at that commit rather than shipping a lockfile that goes stale the moment the upstream fix lands. drift-report.py is now down to the two documented framework exclusions (broker-hosting, rest-configuration), which are deliberate for role=library. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This was referenced Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #27.
The customizer's SoC entry was keyed
battery, which is not an eBus capability and never has been:capabilities/battery.jsonexists in no commit on any branch of the specification, andenergy.ebus.capability.sochas carried that name in the capability-types registry since 2026-05-22, six weeks before this table was written. A conformant device typing its nodeenergy.ebus.capability.soctherefore resolved to the keysoc, missed the table, and reached Home Assistant with neither adevice_classnor astate_classon its SoC: precisely the ambiguous bare percent the entry exists to resolve.It stayed green because the defect was self-validating. The tests fabricated
energy.ebus.capability.batteryto exercise the entry andexamples/ha-discovery-bridgepublished that node type, so the example's self-check asserteddevice_class == "battery"and passed.The bigger half
soe,total-energy-storageandloadup-headroomwere not merely uncovered. Inference seeskWhand stamps themenergy+total_increasing, and these are reservoir levels that fall on discharge, so HA read every discharge as a meter reset and back-filled the drop as freshly consumed energy: phantom kWh in the Energy dashboard. They now carryenergy_storage+measurement, HA's level counterpart.info/nameplate-capacitygets the same correction.state-of-charge,powerandtemperatureare dropped: none is a property of any eBus capability, a battery's electrical power ismeter/active-power(already covered), and eBus does not model pack temperature.Wider drift found in the same pass
meter/imported-active-energyandmeter/exported-active-energynamed properties the meter capability has never defined at any version. Inert (aWhproperty already infers correctly) but drift of the same kind. Removed.state_class, so HA kept no long-term statistics for them. The absentdevice_classthere is deliberate and stays (HA has none forvarh/VAh).power-factor-{a,b,c}were missing while the system-levelpower-factorwas present, despite being equally unitless and so invisible to inference.Preventing recurrence
tests/test_catalog_drift.pywalks_CAPABILITY_METAagainst the machine-readablecapabilities/*.jsoncatalogs in a siblingspecificationcheckout, so a name can no longer stop matching without something failing. It reads spec HEAD rather than the pinnedsynced_commit(the catalogs postdate that pin) and skips cleanly when no checkout is present, which is the case in CI today;EBUS_SPEC_DIRpoints it elsewhere.The regression tests deliberately use a node id other than
soc, since_capability_offalls back to the node id and a same-named node would pass even with$typeresolution broken.Lockfile
.ebus-spec.jsonwas 48 commits behind: framework 0.5 -> 0.7,infoandmeter0.1 -> 0.2,utility-meter0.3 -> 0.6,capability-types0.11 -> 0.19.supportsis unchanged (no framework feature was added or removed across 0.5 -> 0.7). Two corrections beyond the numbers:grid,status,demandandpower-qualityare now pinned directly, the oldnotesrationale having lapsed when utility-meter 0.6 split them into standalone catalogs; andsocis pinned.Compatibility note
This removes the
batterykey rather than aliasing it. A publisher that copied the old example's non-conformant node type should move tosoc; the only known publisher of it was that example.Verification
ruff checkandruff format --checkclean.batterykey and a bogus meter id is caught.drift-report.pyis now down to the two documented framework exclusions.Reported by @cayossarian, who also proposed the catalog check.
🤖 Generated with Claude Code