TEL-01: P1 ingestion - two transports, the EMS-owned sign derivation, and sensor.p1_sample_age_s #2
Closed
admin
wants to merge 2 commits from
TEL-01 into SAFETY-04
pull from: TEL-01
merge into: :SAFETY-04
:master
:release/1.0
:PRICE-02
:UX-46
:SAFETY-121
:SAFETY-119
:UX-45
:DOC-8
:deploy-0.18.2
:release-0.18.2
:SAFETY-118
:deploy-0.18.1
:release-0.18.1
:UX-44
:UX-43
:deploy-0.18.0
:release-0.18.0
:OPT-20
:PRICE-01
:MAINT-11
:opt15-price-run-fixes
:SAFETY-112
:OPT-17-forecast-entities
:deploy-0.17.0
:release-0.17.0
:MAINT-15
:SAFETY-107
:MAINT-14
:MAINT-13-full-pack-complete
:SAFETY-103
:deploy-0.16.0
:release-0.16.0
:safety-95-drain-end-reseed
:safety-96-stale-unit-site-total
:maint-10-local-start-hour
:safety-90-peak-guard-gap
:deploy-0.15.0
:release-0.15.0
:ci-14-infra-pin-3b4ff84
:safety-94-no-false-stale-or-refusal
:deploy-0.14.0
:release-0.14.0
:ci-13-infra-pin-0c04488
:safety-93-no-grid-charge-at-charge-end
:safety-91-held-battery-last
:deploy-0.13.0
:release-0.13.0
:safety-85-no-false-not-keeping-up
:safety-87-guard-uses-able-batteries
:safety-86-peak-over-hold
:safety-84-n2-maint-charge
:safety-83-peak-over-drain
:opt11-p4-solar-forecast
:safety-78-drain-at-two
:safety-81-load-too-high
:opt11-p3-price-runs
:safety-79-peak-margin
:opt11-p2-ha-entities
:UX-13-p3-batteries
:opt11-p1-modes
:UX-13-p5
:UX-15-setup-only
:UX-13-p4-setup
:UX-13-p2-installer
:SAFETY-73-per-unit-refusal
:UX-13-p1-home
:UX-13-P0-foundation
:SAFETY-52-refused-hold
:UX-11-short-safety-lines
:pin-bump-d637d0b
:UX-10-plain-leftovers
:pin-bump-ecd3864
:UX-08-2d-logs-docs
:UX-08-2c-refusals
:UX-09-2a-fixes
:UX-08-status-rows
:pin-bump-7081df4
:UX-08-plain-notifications
:SAFETY-40-clamp-alarm
:pin-bump-7689816
:SAFETY-34
:pin-bump-a1d0618
:TOOL-9
:CI-12
:TEL-14-pv-sign
:DOC-5-watchdog
:TEST-26
:SAFETY-62-refuse-unread-input
:safety-61-control-task-death
:doc-1-operator-docs
:PKG-18-cut-0.12.0
:TEL-12
:PKG-17
:TEL-11
:ci9-gateproof
:SAFETY-56
:SEC-2-release-procedure
:PKG-16-release-0.10.1
:SAFETY-57
:pkg15-changelog
:CI-9
:SAFETY-48
:TEL-10
:PKG-14
:SAFETY-47
:SAFETY-10
:SAFETY-45
:TARIFF-16
:TARIFF-18
:TARIFF-17
:TARIFF-11
:TARIFF-13
:TARIFF-15
:TARIFF-07
:TARIFF-14
:TARIFF-12
:TARIFF-10
:TARIFF-09
:TARIFF-08
:FCST-06
:FCST-05
:FCST-04
:INSTR-01
:SAFETY-44
:ARCH-02
:SAFETY-43
:TEST-21
:SAFETY-31-attribution
:UX-04
:PKG-13
:PKG-12
:PKG-09
:OPT-09
:UX-02
:PKG-07
:SAFETY-42
:SAFETY-13
:SAFETY-37
:SAFETY-39
:SAFETY-38
:CI-06
:PKG-06-version-0.7.0
:SAFETY-41-reassert
:TEL-07
:UX-01
:SAFETY-32
:SPIKE-04a
:MAINT-05
:SAFETY-30
:CFG-04
:DATA-01
:DATA-02
:SAFETY-24
:SAFETY-26
:OPT-04
:FCST-03
:SAFETY-07
:TARIFF-03
:TARIFF-02
:TARIFF-01
:TEST-06
:SAFETY-03
:SAFETY-18
:MAINT-04
:safety-02-latch-persistence
:safety-02-sign-watchdog
:safety-12-loop-floor
:CFG-01
:TEL-05
:TEL-04
:DEPLOY-01
:TEL-01
:SAFETY-04
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements TEL-01. Stacked on
SAFETY-04- base is that branch, notrelease/1.0, so this diff shows only TEL-01 work. Nothing here touchescontrol.py.What this is
A Belgian P1 meter publishes two unsigned registers, not one signed figure. Until now the add-on asked the installer to bridge that with a template sensor, which put the sign convention of the entire control loop in a text box. This moves it into the EMS:
net = import - export, derived once, in one place, with a test that fails if anyone inverts it.Two transports behind one contract, selected by
meter_source:ha_dsmr- HA WebSocket, subscribing to the DSMR integration's entitiesmqtt_p1- a configurable topic, strict JSON schema documented in DOCS.mdoff(default) - the existingmeter_entitypath, unchangedEverything downstream reads
P1Ingest, never a transport, so switching is a config edit.offbeing the default means no installed system changes behaviour until it opts in.The sample-age entity
sensor.p1_sample_age_s- seconds since the newest accepted telegram, published over MQTT discovery, refreshed every second rather than only when a telegram lands.The mechanism matters and is the reason SAFETY-01 asked for it: Home Assistant pushes a state only when the state changes. A meter sitting at a genuinely constant reading emits nothing at all, which is indistinguishable - to anything watching the value - from a meter that has died. Recomputing the age against the clock separates the two. On the ENV-01 rig that difference is a reproducible false trip roughly every six minutes.
The entity id deliberately breaks the
goodwe_prefix the other sensors use, because the firmware subscribes to that literal string. There is a warning comment on it inmqtt.pysaying so.Notable decisions
floor(epoch/900). That is genuinely clock-aligned and DST-proof for Belgium because every offset there is a whole number of hours. Markedponytail:with the ceiling named - a sub-hour offset (India +05:30) would need real tz maths.time.monotonic(), not the wall clock - an NTP step on a freshly booted Pi would otherwise fake or mask staleness.64954(an unsigned 16-bit register decoded without its sign) is rejected rather than averaged in as 65 kW.Tests
test_p1.py- 99 checks, plain asserts, bare interpreter, no meter, no framework. Followstest_control.py. Includes an end-to-end run of the HA transport against a fake Home Assistant websocket (real handshake,subscribe_events,get_states, per-entity events), which is the only thing that actually proves the protocol path works.Full suite:
test_control.py,test_arbiter.py,test_maintenance.py,test_p1.pyall pass.Non-vacuous: eight rules were deliberately broken one at a time and every one went red, then the file was restored byte-identical:
Not verifiable without a live meter
Real DSMR entity ids and units (the fixtures use plausible ones); whether a real P1 bridge's MQTT payload matches the documented schema; the 0.35 s debounce against real telegram timing; and MQTT broker reconnect against a real broker.
147456c2a2to0a617c54820a617c5482to4bd659c499Closing: the content of this branch is already on
release/1.0—git rev-list --count origin/release/1.0..returns 0.The work shipped, but it reached the release branch by a different route (a direct push, or carried in with another merge) rather than through this PR, so Gitea never auto-closed it. That is a process failure on my part: it left the PR list looking like nothing had shipped, and it orphans any review discussion held here.
Nothing is lost and nothing needs re-doing. Going forward, work lands by merging its PR.
Pull request closed