# 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.