Skip to content

docs: what the heating elements actually do - #142

Merged
eman merged 5 commits into
mainfrom
docs/heating-elements
Sep 20, 2026
Merged

eman merged 5 commits into
mainfrom
docs/heating-elements

Conversation

@eman

@eman eman commented Sep 20, 2026 •

Copy link
Copy Markdown
Owner

The NWP500 publishes four element thermostat settings and a differential for
each. Read literally they do not describe when the elements run: in Electric and
High Demand the ON and OFF settings are the same value, in Heat Pump and
Energy Saver the ON setting rests at the 40.5 degC device minimum while the
element still runs, and every he*DiffTempSetting reads 0.

This adds docs/explanation/heating-elements.rst with the measured behaviour,
and fixes the reference pages that said otherwise.

The start differential is mode-dependent

Each mode has a run on either side of its bracket, except Electric's lower edge
— its only negative sits above the setpoint.

Mode Element off at Element on at Bracket
ELECTRIC 0.6 degC above setpoint 0.3, 0.4 degC at most 0.3 degC
HIGH_DEMAND 0.2 degC 0.7 degC (0.2, 0.7] degC
ENERGY_SAVER 0.3, 0.8 degC 1.0, 1.2 degC (0.8, 1.0] degC

Electric and High Demand overlap in (0.2, 0.3] and handle heUpperOnTempSetting
identically, so they may share one threshold — but High Demand has never
been watched between 0.2 and 0.7, so that is an inference and the page says so.
A consumer should take the top of each mode's own bracket.

These are entry thresholds, not thermostats. They measure what happens
within a poll of switching into a mode. In High Demand's steady state this
unit's history has the element on in 1 of 47 minutes 0.2–0.3 degC short and
168 of 169 more than 2.2 degC short.

Also measured

  • Energy Saver on entry engages the element although heUpperOnTempSetting
    rests 33 degC below the tank. Why its bracket sits higher than Electric's is
    not established — those runs had the compressor running and the others did
    not.
  • Electric's handover: upper to setpoint, then lower, never both, one poll.
  • Zone coupling: the upper probe falls 1.9 degC while the lower element
    runs, then recovers. Nothing should treat the upper zone as monotonic.
  • The off point lags both ways: element off 0.8 degC below setpoint, probe
    then coasts 1.3 degC past it.
  • Element power 5,200–5,242 W metered with the compressor off, against a
    5,000 W @ 240 V rating.
  • A mode write may not take effect while a TOU window is in force — four
    minutes, nothing logged, twice — and is not discarded either: it landed one
    poll after TOU was switched off. Confirm mode writes by reading them back.
    Reservation entries fire normally in a window (docs: reservations are NOT suspended by TOU — measured on the device (schedule-operation.rst) #141).

Reference pages corrected

data_conversions.rst described these fields as literal ON/OFF thermostats with
a "typically 2–5°F" differential, contradicting the new page. It now carries a
warning and the differential row records that the field measured 0 in every
mode. device_status.rst's note no longer collapses the result to one number.

Scope

One unit, one supply voltage, 2026-09-19 and 2026-09-20, 22 bounded runs —
thirteen on the elements and thresholds, nine on mode writes and reservations
under TOU. Not established: where inside (0.2, 0.7] High Demand's boundary
falls; what engages the element later in an Energy Saver stint; anything past
six minutes; whether any of it is firmware-dependent.

Docs build clean (no new Sphinx warnings against baseline).

Refs #141

The four he*TempSetting fields do not describe when an element runs. In
Electric and High Demand the on and off settings are the same value,
which would be a thermostat with no hysteresis, and every
he*DiffTempSetting reads 0. Something has to bridge that, and nothing
in the status message says what.

Measured on one unit by commanding the modes and watching, rather than
read off a historical log: the upper element starts 1.0 degC below the
setpoint, bracketed by a descending sweep — off at 0.3 and 0.8 degC, on
within one 10-second poll at 1.0 and 1.2. 1.0 degC is the device's own
storage granularity, which makes it the natural parameter.

The page also covers what misled us first. Energy Saver engages the
element on entry although heUpperOnTempSetting rests 33 degC below the
tank, so watching that field explains nothing. Electric hands over from
upper to lower in a single message, and while the lower element runs
the *upper* probe falls about 1.9 degC before recovering — the zones
are not independent, and anything treating "upper reached X" as
monotonic will be wrong for several minutes. Element power metered at
5,219 W upper and 5,200 W lower against the 5,000 W at 240 V rating.
And a mode write may not take effect while a TOU window is in force,
which matters to anyone planning a mode change at all.

Limits are stated: one unit, one supply voltage, ten bounded runs over
two days. Electric and High Demand have positives at 1.0 degC and no
negatives below it, so only Energy Saver's bracket is a bracket; and if
another unit reports a non-zero he*DiffTempSetting, that field is the
better answer and this page describes one firmware's default.

Built clean: sphinx reports no warnings for the new or edited pages.
The page asserted one differential for every mode, measured in Energy
Saver, and flagged Electric and High Demand as an assumption. The
assumption was wrong.

At the same 0.3 degC deficit, Electric runs the upper element and
Energy Saver does not. Electric is now bracketed too — off with the
tank 0.6 degC above the setpoint, on at 0.3 and 0.4 degC below it — so
its boundary is inside (0, 0.3] degC, at or below the probe's own
resolution. That is exactly what the thermostat reading of its settings
predicts, since Electric parks heUpperOnTempSetting at the setpoint.
Energy Saver's 1.0 degC is an entry effect and a different mechanism,
which is why the two differ.

High Demand shares Electric's settings and starts an element well above
the boundary, but no run has approached it; that one stays an
assumption and is listed as such.
High Demand was the one mode left as an assumption: it shares
Electric's settings, so the page assumed it shares the threshold. Now
measured — off at 0.2 degC short, on at 0.7 — which brackets it to
(0.2, 0.7].

Against Electric's (0, 0.3] and Energy Saver's (0.8, 1.0], that settles
the grouping. Electric and High Demand overlap only in (0.2, 0.3], so
one threshold fits both, consistent with their identical
heUpperOnTempSetting handling. Energy Saver's band intersects neither:
at 0.7 degC short High Demand runs the element and Energy Saver does
not.

The page now gives one number per mode for a consumer that wants one —
0.3 degC for Electric and High Demand, 1.0 for Energy Saver, each the
top of its bracket and so consistent with every observation.

heUpperOnTempSetting was seen following the new setpoint in the same
status message the High Demand mode landed in, which is the mechanism
the thermostat reading rests on.
…umber

A review of the companion work found this page contradicting itself. The
summary and the per-mode tables give ELECTRIC and HIGH_DEMAND 0.3 degC,
and two later paragraphs told a consumer to assume 1.0 degC for exactly
those modes — the Energy Saver figure. Fixed.

More substantially, the page called the 0.3 degC a thermostat. It is an
entry threshold: every run measured what happens within a poll of
switching into a mode. In High Demand's steady state this unit's
history has the upper element on in 1 of 47 minutes 0.2-0.3 degC short
and 168 of 169 minutes more than 2.2 degC short, which is nothing like
a thermostat at the setpoint. The page says so now, and says not to use
one figure for the other.

Also: Electric's bracket rests on High Demand's negative rather than
its own (its only negative is above the setpoint), and that negative
held for 53 seconds as the tank drifted through it — both now
footnoted rather than presented as equivalent exposure. The element
power range covers all three measurements instead of pairing the upper
mean from one run with the lower from another and dropping the third.
And the cross-mode sentence notes that the Energy Saver runs had the
compressor running while the others did not.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Several documentation statements overstate the High Demand threshold and leave contradictory reference text and sample-count inconsistencies.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 7 Medium severity · 3 Low severity

Open (10)
What changed in this PR

Adds measured documentation of NWP500 heating-element behavior and links it from the protocol reference.

Changes:

  • Adds heating-element thresholds, handover, coupling, power, TOU behavior, and limits.
  • Registers the new explanation page.
  • Updates protocol notes and changelog.
File Description
docs/​explanation/​heating-elements.rst New measured behavior reference
docs/​explanation/​index.rst Adds the page to the explanation index
docs/​reference/​protocol/​device_status.rst Links and summarizes findings
CHANGELOG.rst Records the documentation addition

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +5 to +11
The NWP500 publishes four element thermostat settings -
``heUpperOnTempSetting``, ``heUpperOffTempSetting`` and their lower-zone
pair - plus a differential for each. Read literally they do not describe
when the elements actually run: in Electric and High Demand the on and
off settings are **the same value**, and every ``he*DiffTempSetting``
reads 0. The device nevertheless has a differential, and this page
gives the measured one.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. data_conversions.rst now opens the Heating Element Control Temperatures section with a warning that the field descriptions below are the protocol's own semantics and do not hold against the device, naming all three ways they fail. The heUpperOnDiffTempSetting row's "Typically 2-5°F" now reads "Nominally 2-5°F, but measured as 0 on a 9.x-firmware unit in every mode, while the device nonetheless applied a differential". Two reference pages contradicting each other was the worst outcome here.

Comment thread docs/explanation/heating-elements.rst Outdated
Comment on lines +13 to +18
Everything here was measured on one unit by commanding the modes and
watching, not inferred from historical logs. That distinction matters:
two earlier values for the start threshold came from "the smallest
deficit ever seen to start an element" in a passive record, and both
were wrong, because such a figure measures where the tank happened to
sit rather than where the device decides.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Narrowed to "The start thresholds here were measured on one unit by commanding the modes and watching", plus a new paragraph naming the two passages that are not from commanded runs — High Demand's steady-state re-engagement and the mid-stint behaviour it is contrasted with — so the reader can tell which is which.

Comment thread docs/explanation/heating-elements.rst Outdated
Comment on lines +35 to +38
- **Mode-dependent.** ``ENERGY_SAVER`` needs **1.0 degC
(1.8 degF)** below the setpoint. ``ELECTRIC`` and ``HIGH_DEMAND``
need about **0.3 degC (0.54 degF)**. All three are bracketed by
runs on either side.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, and this was the important one. The table now reports HIGH_DEMAND as (0.2, 0.7] degC rather than 0.3. Merging it with Electric came from overlapping brackets plus identical heUpperOnTempSetting handling, which is an argument for a shared threshold, not a measurement of one — and I had written it into a summary table as the latter. The Limits bullet claiming no run approached High Demand's boundary was stale from before those runs and is replaced with the real gap: where inside (0.2, 0.7] it falls. Also carried into the consuming repo's model, which was applying Electric's number to both modes (eman/dhw-sensor-apps#263).

Comment thread docs/explanation/heating-elements.rst Outdated
The start differential
======================

Two modes, two different answers. Each run holds a fixed deficit and

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: "Three modes, three brackets, and no two of them the same width." The sentence predated the High Demand table.

Comment thread docs/explanation/heating-elements.rst Outdated
Comment on lines +187 to +190
A consumer wanting one number per mode can take **0.3 degC** for
Electric and High Demand and **1.0 degC** for Energy Saver. Both are the
top of their bracket, which is the value consistent with every
observation.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The guidance now says to take the top of each mode's own bracket — 0.3 / 0.7 / 1.0 degC — defined as the smallest deficit at which that mode was seen to start an element, and spells out the asymmetry: borrowing Electric's 0.3 predicts an element at 0.4 degC short that High Demand has never been seen to run, which is the expensive direction if the consumer is deciding whether hot water will be ready.

Comment thread docs/explanation/heating-elements.rst Outdated
Comment on lines +202 to +206
A consumer that wants to predict when an element will run therefore
cannot read it from the status message; it has to assume the per-mode
figures above - 0.3 degC for ``ELECTRIC`` and ``HIGH_DEMAND``, 1.0 degC
for ``ENERGY_SAVER`` - and treat a tank inside the corresponding
bracket as a case where the device may go either way.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the same pass — 0.3 for ELECTRIC, 0.7 for HIGH_DEMAND, 1.0 for ENERGY_SAVER, with a note that High Demand's bracket is the widest so its "may go either way" span is widest too. The "What the on-setting does and does not tell you" bullet repeated the merged figure as well; that is fixed.

Comment on lines +742 to +745
* The ``he*TempSetting`` fields do **not** describe when an element
runs: in Electric and High Demand the on and off settings are equal,
every ``he*DiffTempSetting`` reads 0, and the measured differential is
1.0 degC below the setpoint. See :doc:`/explanation/heating-elements`.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The note now gives all three brackets and says the differential is mode-dependent and measured on entry, instead of a single 1.0 degC — which was Energy Saver's number applied to every mode.

Comment thread CHANGELOG.rst Outdated
Comment on lines +16 to +21
**mode-dependent**, and all three modes are bracketed by runs on
either side: ``ELECTRIC`` and ``HIGH_DEMAND`` need about **0.3 degC**
below the setpoint, ``ENERGY_SAVER`` needs **1.0 degC**. At 0.7 degC
short, High Demand runs the element and Energy Saver does not. These
are **entry** thresholds, not thermostats: in High Demand's steady
state this unit's history has the element on in 1 of 47 minutes

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The changelog now gives one bracket per mode and adds that whether Electric and High Demand share a threshold is an open question, not a finding.

Comment thread docs/explanation/heating-elements.rst Outdated
Comment on lines +47 to +50
- Switching into ``ENERGY_SAVER`` engages the upper element even
though ``heUpperOnTempSetting`` rests 33 degC below the tank -
an entry effect rather than that thermostat, and the reason its
differential differs from Electric's.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The summary row now stops at the observation — the element engages although heUpperOnTempSetting rests 33 degC below the tank, so that field is not what drives it — and states plainly that why the modes differ is not established. Added the same to the body paragraph: the runs differ in compressor state as well as mode, and nothing here separates the two.

Comment thread docs/explanation/heating-elements.rst Outdated
Comment on lines +294 to +295
One unit, one supply voltage, 2026-09-19 and 2026-09-20, thirteen
bounded runs in all. Specifically **not** established:

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, and both numbers were wrong. It is 22: thirteen on the elements and thresholds, nine on mode writes and reservation entries under TOU (one JSONL file per run). The page and the PR body now both say so, with the split spelled out.

…d it

The review is right on every count. The page measured three modes and
then reported two numbers, merging Electric and High Demand onto
0.3 degC. High Demand's own runs are off at 0.2 and on at 0.7, so its
bracket is (0.2, 0.7] — more than twice Electric's width, with nothing
watched in between. The brackets overlap and the two modes park
`heUpperOnTempSetting` identically, so they may well share a threshold;
that is an argument, and the page presented it as a measurement.

Corrected in the summary table, the comparison, the consumer guidance,
`device_status.rst` and the changelog. Each mode now carries the
smallest deficit at which it was seen to start an element, and the
Limits section asks the question that would settle it: one run at
0.4 degC.

Also from the review:

- `data_conversions.rst` still described these fields as literal ON/OFF
  thermostats with a "typically 2-5°F" differential, which is exactly
  what this page disproves. Readers had two reference pages
  contradicting each other. It now carries a warning, and the
  differential row says the field measured 0 in every mode.
- The blanket "everything here was measured by commanding the modes"
  was false — High Demand's steady-state figures are history. Narrowed,
  and the two history-derived passages are labelled.
- "Two modes, two different answers" preceded three tables.
- Energy Saver's entry behaviour was written as the *reason* its
  threshold differs from Electric's. It is an observation; the runs
  differ in compressor state as well as mode, as the page itself says
  two paragraphs later.
- The run count was thirteen on the page and ten in the PR body. It is
  22: thirteen on the elements, nine on TOU mode writes and
  reservations.

Unprompted, on the same principle: the upper-zone rate was quoted as a
single 1.61 degF/min when it is 1.56-1.61 depending on sampling
resolution.

Refs #142
@eman
eman merged commit 2e4f078 into main Sep 20, 2026
8 checks passed
@eman
eman deleted the docs/heating-elements branch September 20, 2026 18:40
eman added a commit that referenced this pull request Sep 20, 2026
The warning added with the mid-stint figures said this unit's element
binary sensors "assert one-minute elements the power meter does not
see", with 9 of 112 apparent starts drawing 500-750 W or less. That is
wrong, and an adversarial review of the analysis behind it caught it.

The elements were real. Pulling raw samples: on 2026-04-12 the flag
went on at 22:21:58 and off at 22:22:18, and currentPower inside that
window reads 5558 W and 5547 W, with the compressor steady at ~470 W
either side. A 20-second element at full power. The "538 W" that made
it look phantom was the last sample at or before the minute boundary,
taken by a one-minute resample that used a backward as-of join instead
of a maximum. currentPower samples at a median 30 s with 24 % of gaps
over a minute, so a sub-minute burst falls between the samples such a
resample keeps.

What is true, and more useful, is that element runs come in two
lengths. Of 128 raw upper-element runs, 31 (24 %) are under a minute,
median 9 s, and together carry 0.8 kWh across nine months against
97.9 kWh for the other 97. A 20-second burst moves the upper probe
about 0.1 degC, at its own resolution — which is why "the tank did not
move" looked like corroboration and was not evidence either way.

So the note now describes the sampling hazard rather than accusing the
sensor: resample power with a maximum or work from raw edges, and
filter on duration or energy when predicting recovery, because it is
the sustained runs that heat the tank.

No mid-stint figure changes: the review confirmed the Energy Saver and
High Demand results are identical with or without the power filter.

Refs #142
eman added a commit that referenced this pull request Sep 21, 2026
…aims

A commanded run on 2026-09-21 answers what the recorded history on this
page could not. With the setpoint fixed for almost the whole record,
"the element engages at heUpperOnTempSetting" and "it engages a fixed
gap below the setpoint" made the same prediction. So the setpoint was
lowered to 42.0 degC and the unit left in ENERGY_SAVER. A fixed gap
predicted engagement near 22 degC; it engaged three times with the
upper probe at 39.8. The on-setting is what drives it — with two
cautions stated: every engagement came while water moved through the
tank, and readings of 40.4, 40.1 and 40.0 did not trigger, so the
working threshold may sit a little under the published 40.5.

Three further behaviours from the same run, each with its n:
- The element runs to ~0.9 degC under the setpoint-tracking
  heUpperOffTempSetting, not to a fixed point. Only engagement is fixed.
- Raising the setpoint in ENERGY_SAVER starts the element, through the
  entry rule (n = 1). A controller writing setpoints in that mode can
  start a 5 kW element.
- Whether water movement is required, or temperature suffices, cannot
  be separated: all three engagements had it, one from a shower and two
  from a recirculation pump on a cooled loop.

Two retractions.

The short-element warning said a 20-second burst moves the upper probe
~0.1 degC, "below its own resolution. They heat nothing." That divided
by the whole tank's heat capacity; the upper element heats the upper
zone, a third of it. A 20 s burst is ~0.3 degC, and measured 41-58 s
bursts moved it 0.7-1.5 degC. The counts are also restated on a clean
basis — upper element only, commanded test days excluded, at the
fitted 5,148 W: 115 runs, 20 under a minute.

And the page said heUpperOnTempSetting rests at 40.5 degC in both
HEAT_PUMP and ENERGY_SAVER. True of Energy Saver (96 % of minutes on
this unit); in Heat Pump only 48.5 %, and the other values match the
current setpoint in only 6 % of minutes, so it is not tracking the
setpoint either. Marked as unreliable in Heat Pump mode.

Refs #142
eman added a commit that referenced this pull request Sep 21, 2026
Review on #143 found four places that said the published thermostat
description holds later in a stint, then gave High Demand — which
re-engages ~2.1 degC *below* its ON setting — as a counterexample in
the next clause. The evidence supports the ON-setting description for
ENERGY_SAVER only.

- The page's opening said the four fields do not describe when the
  elements run, which contradicted the row beneath it saying they are
  the rule later in a stint. It now says how far they hold by mode and
  moment: not on entry to any element mode; yes later in ENERGY_SAVER
  (history, confirmed by a commanded run); no later in HIGH_DEMAND
  (history only); ELECTRIC uncharacterised.
- CHANGELOG, data_conversions.rst and device_status.rst said "the
  reference description is the rule" / "the descriptions below hold" /
  "later in a stint they do". Each is now scoped to the mode.
- data_conversions.rst also still said the ON setting sits 33 degC
  below the tank in HEAT_PUMP and ENERGY_SAVER. This PR already found
  that true of Energy Saver and unreliable in Heat Pump; scoped to
  match.

The fifth comment, on the changelog's short-element counts, was
already addressed in 9ad7f34.

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

2 participants