The TOU Recovery Cap¶
When a Time-of-Use schedule is active, the NWP500 stops a heat-pump
recovery at about 90 % charge instead of running to the setpoint. The
tank finishes roughly 1.9 degC below hp_lower_off_temp_setting and
stays there. Nothing in the protocol announces this, and no error is
raised - the compressor simply stops early.
This matters for anything that waits for the tank to reach its setpoint: under an active TOU window that instant never arrives.
The short version¶
Observation |
Value |
|---|---|
Charge at termination |
|
Shortfall below setpoint |
|
Precondition |
An expensive TOU period in force, un-overridden. |
Frequency |
59 of 333 recoveries (18 %) over 260 days |
A normal recovery, for contrast, ends with dhw_charge_per at 100 %
(p10 99) and the lower probe at or above the setpoint.
Evidence¶
Measured across 333 heat-pump recovery cycles of 15-480 minutes, from 2025-12-28 to 2026-09-06, on one unit. 196 reached the lower-off setpoint; 137 did not.
The shortfall of the 137 is bimodal, not a spread:
shortfall below hp_lower_off_temp_setting, cycles that never reached it
min 0.18 p10 1.66 p25 3.42 median 3.42 p75 4.32
p90 31.03 max 58.86 (degF)
most common values: 3.42 degF x 59 cycles 3.24 x 6 3.60 x 6
The spike at 3.42 degF is 1.9 degC exactly, and 19 steps of the 0.1 degC tank-probe quantum. It holds across eight different setpoints from 140.0 to 147.2 degF and appears in all nine months observed, so it is an offset in the control logic rather than a proportional effect or a seasonal one. The long tail beyond 8 degF is ordinary interruption - a draw starting mid-recovery, a mode change - and is unrelated.
tou_status looks like a clean separator, and is not one:
Population |
n |
|
|
|---|---|---|---|
Stopped 3.42 degF short |
59 |
59 / 59 (100 %) |
0 / 59 |
Other unreached |
78 |
49 / 78 (63 %) |
6 / 78 |
Reached setpoint |
196 |
98 / 196 (50 %) |
27 / 196 |
All 59 ran in HEAT_PUMP mode throughout, so this is not
ENERGY_SAVER or VACATION behaviour.
But read that table carefully: 98 cycles reached the setpoint with
tou_status True. The flag only reports that TOU scheduling is
enabled. It says nothing about whether the recovery ran inside an
expensive period, and the cap bites only when it did - outside one, an
enabled schedule does nothing at all.
The device marks a period itself. While inside one it applies non-zero
*_diff_temp_setting offsets and reverts them at the end; on the unit
measured this toggles at 21:00 and 04:00 UTC daily. Using that as the
in-period test, 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 moment the cycle ended, not when it began: a recovery that starts off-peak and runs into a period is still capped, and testing the start catches 17 of 59 against 43 testing the end.
Two plausible explanations were tested and refuted:
The upper zone satisfied its own cut-out first. No -
tank_upper_temperaturereachedhp_upper_off_temp_settingon only 2 of 137 unreached cycles, against 179 of 196 reached ones. The upper probe finishes short too, by a median of 4.5 degF.The device terminates on outlet temperature. No -
dhw_outlet_temperaturereached the setpoint on only 2 of 136.
What is not yet established¶
Being inside a period is a strong predictor but not a deterministic one:
12 cycles reached the setpoint from inside one, and 16 were capped from
outside. Some of that is the marker’s own resolution - it is sampled at
the cycle end, and a period boundary crossed mid-recovery is not captured
- but it has not been reconciled against the schedule the device was
actually holding, which configure_tou_schedule_confirmed can read
back.
The 90 % figure has been observed on one unit under one TOU schedule. It is not known whether the ceiling is fixed in firmware, derived from the schedule, or configurable.
A note on reading tou_status over time: the field emits brief
unknown values on integration reconnect - sub-second blips that
return immediately to the value they interrupted - so a raw state history
contains entries that are not transitions. Stores that keep only boolean
values drop these, which is the right behaviour and leaves a faithful
record of the genuine changes.
Do not read a low record count as lost data. It means the state was
held: on the unit measured, tou_status logged 165 changes in one
month and 5 in another, and the sparse month was the one in which TOU was
active nearly continuously. Forward-filling the genuine transitions is
correct. It can be corroborated against the *_diff_temp_setting
fields, which toggle away from zero at the start of each TOU window and
back at the end.
Working with the cap¶
Do not wait for the setpoint. Code that treats “tank reached
hp_lower_off_temp_setting” as the completion signal will block
indefinitely on a capped recovery. Watch dhw_charge_per plateauing,
or the compressor stopping, instead.
Do not read a capped cycle as a fault or as degraded capacity. The
appliance is doing what the schedule told it to. A recovery that ends at
90 % charge with tou_status True is a normal outcome.
When measuring recovery duration, treat capped cycles separately. They are not censored observations of a full recovery - they are complete observations of a different, shorter target. Pooling them with uncapped recoveries biases any duration estimate downward; discarding them biases it upward, because capped cycles are systematically shorter (median 161 minutes against 235 for cycles that ran to setpoint).
To force a full recovery, set tou_override_status - none of the
59 capped cycles had it set, and 27 of the cycles that ran to setpoint
under an active TOU schedule did.