Skip to content

Document the TOU recovery cap - #132

Merged
eman merged 2 commits into
mainfrom
docs/tou-recovery-cap
Sep 6, 2026
Merged

Document the TOU recovery cap#132
eman merged 2 commits into
mainfrom
docs/tou-recovery-cap

Conversation

@eman

@eman eman commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Under an active Time-of-Use period the NWP500 stops a heat-pump recovery at about 90 % dhw_charge_per instead of running to the setpoint, leaving tank_lower_temperature ~1.9 °C below hp_lower_off_temp_setting. Nothing in the protocol announces it and no error is raised — so code that waits for the setpoint waits forever.

Adds docs/explanation/tou-recovery-cap.rst, following the tank-energy.rst pattern: a measured claim with its evidence, since it is the same shape of finding — device behaviour that contradicts the obvious reading of a field.

Evidence

333 recovery cycles (15–480 min), 2025-12-28 to 2026-09-06, one unit. 196 reached the lower-off setpoint; 137 did not, and their shortfall is bimodal rather than a spread: median 3.42 °F with 59 cycles at exactly that value, then a tail past 8 °F that is ordinary interruption.

3.42 °F is 1.9 °C, 19 steps of the 0.1 °C probe quantum. It holds across eight setpoints from 140.0 to 147.2 °F and all nine months observed — an offset in the control logic, not a proportional or seasonal effect.

Two candidate mechanisms were tested and refuted: the upper zone does not satisfy its own cut-out first (tank_upper_temperature reached hp_upper_off_temp_setting on 2 of 137, against 179 of 196 reached cycles), and the device does not terminate on outlet temperature (2 of 136).

tou_status is not the test

The second commit corrects the first on this point. tou_status reports only that TOU scheduling is enabled — outside an expensive period an enabled schedule does nothing, which is why the flag alone read as "necessary but not sufficient": 98 cycles reached setpoint with it True, because they ran off-peak.

The device marks a period itself, applying non-zero *_diff_temp_setting offsets while inside one and reverting at the end. Among tou_status True cycles:

at cycle end capped reached other
inside a period 42 (62 %) 12 14
outside a period 16 (12 %) 86 34

Test it at the cycle end, where the cap bites — a recovery beginning off-peak and running into a period is still capped (43 of 59 caught, against 17 testing the start).

What the page says it does not know

Being inside a period is a strong predictor, not a deterministic one: 12 cycles reached setpoint from inside one and 16 were capped from outside. The page names the likely cause (the marker is sampled at one instant, so a boundary crossed mid-recovery is missed) and what would settle it (configure_tou_schedule_confirmed reads back the schedule the device holds). The 90 % ceiling is one unit under one schedule; whether it is firmware-fixed, schedule-derived or configurable is unknown.

It also records that tou_status emits sub-second unknown blips on reconnect, and that a low change count means the state was held, not that data was lost — the month with 5 changes was the month TOU was active nearly continuously.

Also

A pointer in the tou_status field description in src/nwp500/models/status.py, since that is where a developer hits this in practice.

Docs build succeeds; 738 tests pass.

Consumer-side handling in eman/dhw-sensor-apps#170.

eman added 2 commits September 6, 2026 08:56
Under an active TOU schedule the NWP500 stops a heat-pump recovery at
about 90% dhw_charge_per rather than running to the setpoint, leaving
tank_lower_temperature ~1.9 degC below hp_lower_off_temp_setting. Nothing
in the protocol announces it and no error is raised, so code that waits
for the setpoint waits forever.

Measured over 333 recovery cycles (15-480 min) from 2025-12-28 to
2026-09-06 on one unit. 196 reached the lower-off setpoint; 137 did not,
and their shortfall is bimodal rather than a spread: median 3.42 degF
with 59 cycles at exactly that value, then a tail past 8 degF that is
ordinary interruption. 3.42 degF is 1.9 degC, 19 steps of the 0.1 degC
probe quantum, and it holds across eight setpoints from 140.0 to 147.2
degF and across all nine months observed -- an offset in the control
logic, not a proportional or seasonal effect.

tou_status separates the two populations completely: 59/59 of the capped
cycles had it True with tou_override_status False, against 98/196 of the
cycles that reached setpoint. All 59 ran in HEAT_PUMP mode, so this is
not ENERGY_SAVER or VACATION behaviour.

Two candidate mechanisms were tested and refuted. The upper zone did not
satisfy its own cut-out first (tank_upper_temperature reached
hp_upper_off_temp_setting on 2 of 137, against 179 of 196 reached
cycles), and the device does not terminate on outlet temperature
(dhw_outlet_temperature reached the setpoint on 2 of 136).

The page records what is not established as well: tou_status True is
necessary but not sufficient, since 98 cycles reached setpoint with it
True, and the 90% ceiling is one unit under one schedule -- whether it is
fixed in firmware, derived from the schedule, or configurable is unknown.

Also notes that tou_status emits sub-second `unknown` blips on reconnect
and that a low change count means the state was held, not that data was
lost: the month with 5 changes was the month TOU was active nearly
continuously.
tou_status reports only that TOU scheduling is *enabled*. It says nothing
about whether an expensive period is in force, and the cap bites only
when one is -- outside a period an enabled schedule does nothing. The
page presented the flag as the precondition, which is why the numbers
read as "necessary but not sufficient": 98 cycles reached the setpoint
with it True, because they ran off-peak.

The device marks a period itself, applying non-zero *_diff_temp_setting
offsets while inside one and reverting them at the end (observed toggling
at 21:00 and 04:00 UTC daily). Among tou_status True cycles that marker
separates the outcomes:

    inside a period    42 capped (62 %), 12 reached, 14 other
    outside a period   16 capped (12 %), 86 reached, 34 other

Test it at the cycle *end*, where the cap bites: a recovery that begins
off-peak and runs into a period is still capped, and testing the start
catches 17 of 59 against 43 testing the end.

Still not deterministic -- 12 reached from inside a period and 16 were
capped from outside -- and the page now says so, along with the likely
cause (the marker is sampled at one instant and a boundary crossed
mid-recovery is not captured) and what would settle it
(configure_tou_schedule_confirmed reads back the schedule the device
holds).
@eman
eman merged commit c043f53 into main Sep 6, 2026
8 checks passed
@eman
eman deleted the docs/tou-recovery-cap branch September 6, 2026 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant