test_pv.py part 11 now prints a one-line pointer instead of the 12-row table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
4.4 KiB
TEL-14 — the PV sign, declared per entity
Owner report on 0.12.0: sensor.espressif_vermogen (ESPHome clamp) and
sensor.kwh_meter_actueel_vermogen (kWh meter in the PV circuit) read
positive for consumption, negative for production. At night the page said
"✓ PV production: 13 W" — standby draw. By day production would publish negative.
1. This reverses TEL-11 §2, and why
TEL-11 §2 refused a pv_invert on two grounds. "PV is read by nothing in the
control path" still holds: the flip lives in pv.total(), and test_pv.py
part 12 re-runs part 6's 40-cycle identity with a flip set. "A reversed sensor
announces itself" did not survive a real site: these sensors follow a different
convention, read a plausible small positive number at night, and the only other
fix is a template sensor, the place TEL-12 §2 says goes quietly wrong.
2. The shape: a parallel list, pv_invert_entities
Measured by tools/supervisor_shape_probe.py §H (Supervisor 2026.08.0, against
a 0.12.0 blob); re-run it for the transcript.
| shape | schema | why it lost / won |
|---|---|---|
(a) -sensor.x prefix |
accepted | invisible: the sign is a syntax only the description explains, in a field an installer fills with the entity picker's text |
| (b) parallel list | accepted, 0.12.0 blob unchanged | ships: its own labelled, translated field ("PV entities whose sign is reversed"); an installer lists the odd ones out |
(c) dicts on pv_entities |
refuses 0.12.0's stored strings | bricks the upgrade — TEL-12's trap again |
| (c2) dicts as a third key | accepted | per-entry labels render raw (entity, invert) — probe §F; and a third PV surface |
| (d) global bool | accepted | one sign cannot describe inverter + meter in one roster |
(b)'s known cost is a typo in the second list: pv.stray_inverts() turns it into
a named caution. [] in options: is load-bearing (§H, isolated counterfactual).
Default [] flips nothing, so every 0.12.0 install is byte-identical (part 12).
3. The negative caution: per entity, below −50 W
The owner's two meters draw ~13 W at night, so a correctly signed meter reads
about −13 W then. −50 W clears that with room for a few such devices behind one
entity; a reversed sensor by day reads hundreds to thousands of watts below
zero. It is tested per entity, not on the total (a deviation from the brief):
three correct meters at −20 W sum to −60 W and must stay quiet, while a reversed
−1500 W hidden inside a positive total must not. It is a caution, never bad;
a wrong threshold costs a spurious or missed caution, never data. The text says
ADD (unflipped) or REMOVE (already flipped).
4. Mutations
Fresh cp -a each, __pycache__ purged, CI image, :ro, --network none;
test_pv.py only unless stated. T1-T12 at 7559d21; M2/M5 are review #1628's,
red since B1 (merged into release/1.0 @ 43b2740).
| # | mutation | bytes | reds |
|---|---|---|---|
| T1 | flip ignored in total() |
−45 | 9 |
| T2 | flip applied to every member | −7 | 6 |
| T3 | 0.0 - w → -w |
−5 | 1 |
| T4 | flip written as abs() |
−1 | 2 |
| T5 | _read_pv() stops passing the flip |
−26 | 7 |
| T6 | caution threshold −50 → 0 W | −17 | 3 |
| T7 | [sign inverted] marker dropped |
−39 | 3 |
| T8 | negative caution made bad |
+1 | 1 |
| T9 | stray_inverts() always empty |
−50 | 1 |
| T10 | inverted() stops stripping |
−8 | 1 |
| T11 | ADD/REMOVE advice swapped | +4 | 2 |
| T12 | pv_invert_entities: [] deleted from options: |
−25 | 2 test_translations + 17 probe |
| M2 | stray_inverts() roster ignores pv_entity |
+15 | 2 (REPORTED CASE, PADDING) |
| M5 | _read_pv() flip set not stripped |
+25 | 1 (PADDING) |
5. History
OPT-10's sensor.goodwe_pv_power recorded since 2026-09-24 on an install with
such sensors carries the old sign; nothing rewrites it.
6. CHANGELOG entry for the next PKG cut
Not written into CHANGELOG.md: test_version_gate.py 11b/11d refuse an
## Unreleased heading (measured on this branch: 4 reds). For PKG to paste:
pv_invert_entities(TEL-14). A PV sensor that reads negative while producing — typically a kWh meter or power clamp in the PV circuit — can now be listed under PV entities whose sign is reversed in the Configuration tab; its sign is flipped before the sum and the status page marks it. Empty (the default) changes nothing. ⚠️ PV history recorded before you set it keeps the old sign.