Document the TOU recovery cap - #132
Merged
Merged
Conversation
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Under an active Time-of-Use period the NWP500 stops a heat-pump recovery at about 90 %
dhw_charge_perinstead of running to the setpoint, leavingtank_lower_temperature~1.9 °C belowhp_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 thetank-energy.rstpattern: 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_temperaturereachedhp_upper_off_temp_settingon 2 of 137, against 179 of 196 reached cycles), and the device does not terminate on outlet temperature (2 of 136).tou_statusis not the testThe second commit corrects the first on this point.
tou_statusreports 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_settingoffsets while inside one and reverting at the end. Amongtou_statusTrue cycles: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_confirmedreads 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_statusemits sub-secondunknownblips 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_statusfield description insrc/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.