From 97d0d1395ca40ce6fe96ad4770edcf1a39a88430 Mon Sep 17 00:00:00 2001 From: Emmanuel Levijarvi Date: Sun, 20 Sep 2026 08:18:00 -0700 Subject: [PATCH 1/5] docs: what the heating elements actually do MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- CHANGELOG.rst | 19 ++ docs/explanation/heating-elements.rst | 210 ++++++++++++++++++++++ docs/explanation/index.rst | 1 + docs/reference/protocol/device_status.rst | 8 +- 4 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 docs/explanation/heating-elements.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 499c952..4380975 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,25 @@ Changelog Unreleased ========== +Documentation +------------- +- **New: what the heating elements actually do** + (``docs/explanation/heating-elements.rst``). 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, + and every ``he*DiffTempSetting`` reads 0. Measured on one unit by + commanding the modes and watching, the upper element starts **1.0 degC + below the setpoint** - bracketed by a descending sweep, off at 0.3 and + 0.8 degC and on within one poll at 1.0 and 1.2 - and that differential + is published nowhere in the status message. Also documents Energy + Saver's entry behaviour (the element engages although + ``heUpperOnTempSetting`` rests 33 degC below the tank), Electric's + upper-to-lower handover and the upper probe **falling** 1.9 degC while + the lower element runs, measured element power of 5,219 W against the + 5,000 W rating, and that a mode write may not take effect while a TOU + window is in force. ``device_status.rst``'s Heating Elements notes now + point at it. + Version 9.4.2 (2026-09-17) ========================== diff --git a/docs/explanation/heating-elements.rst b/docs/explanation/heating-elements.rst new file mode 100644 index 0000000..4312e46 --- /dev/null +++ b/docs/explanation/heating-elements.rst @@ -0,0 +1,210 @@ +==================== +The Heating Elements +==================== + +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. + +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. + +.. contents:: + :local: + :depth: 2 + + +The short version +================= + +.. list-table:: + :header-rows: 1 + :widths: 34 66 + + * - Observation + - Value + * - Element start differential + - The upper element starts once the zone is **1.0 degC (1.8 degF)** + below the setpoint. Bracketed: off at 0.3 and 0.8 degC, on within + one 10-second poll at 1.0 and 1.2. + * - Where that differential is published + - **Nowhere.** ``heUpperOnDiffTempSetting`` and its three siblings + read 0 on this unit while the behaviour above holds. + * - ``heUpperOnTempSetting`` by mode + - ``ELECTRIC`` and ``HIGH_DEMAND``: the setpoint, same as + ``heUpperOffTempSetting``. ``HEAT_PUMP`` and ``ENERGY_SAVER``: + 40.5 degC (104.9 degF), the device minimum. + * - Energy Saver on entry + - Switching into ``ENERGY_SAVER`` engages the upper element even + though ``heUpperOnTempSetting`` rests 33 degC below the tank. The + same 1.0 degC differential applies. + * - Electric ordering + - Upper element to the setpoint, then the lower element, never + both, as the protocol reference already states. The handover + takes one poll. + * - Element power + - 5,219 W upper, 5,200 W lower, metered with the compressor off. + The reference gives 5,000 W at 240 V. + * - Zone coupling + - While the lower element runs, the **upper** probe falls about + 1.9 degC (3.4 degF) before recovering. The zones are not + independent. + + +The start differential +====================== + +Four runs in ``ENERGY_SAVER``, each holding a fixed deficit and watching +for two minutes. The setpoint quantises to 0.5 degC, so the deficit is +set by choosing a setpoint step relative to the tank. + +.. list-table:: + :header-rows: 1 + :widths: 30 30 40 + + * - Deficit below setpoint + - Upper element + - Poll at which it engaged + * - 0.3 degC (0.54 degF) + - **off** for the whole watch + - n/a + * - 0.8 degC (1.44 degF) + - **off** for the whole watch + - n/a + * - 1.0 degC (1.80 degF) + - **on** + - first poll after the mode landed + * - 1.2 degC (2.16 degF) + - **on** + - first poll after the mode landed + +So the boundary lies in (0.8, 1.0] degC, and 1.0 degC is the device's +own storage granularity - settings are held in half-degree Celsius +steps - which makes a one-degree differential the natural parameter. + +Two further runs put the element on at 2.0 degC (3.6 degF) and 5.2 degC +(9.4 degF) short, in ``ENERGY_SAVER`` and ``ELECTRIC`` respectively, so +nothing above the boundary has been seen to decline. + +**The differential is not in the protocol.** A ``status --raw`` taken +between these runs shows:: + + "heUpperOnTempSetting": 81, # 40.5 degC + "heUpperOffTempSetting": 122, # 61.0 degC + "heUpperOnDiffTempSetting": 0, + "heUpperOffDiffTempSetting": 0, + "heLowerOnTDiffempSetting": 0, + "heLowerOffDiffTempSetting": 0, + +A consumer that wants to predict when an element will run therefore +cannot read it from the status message; it has to assume the 1.0 degC +above, and treat a tank within that of the setpoint as a case where the +device may go either way. + + +What the on-setting does and does not tell you +============================================== + +``heUpperOnTempSetting`` tracks the **mode**, not the moment: + +* In ``ELECTRIC`` and ``HIGH_DEMAND`` it equals ``heUpperOffTempSetting`` + and both equal the DHW setpoint. Taken literally that is a thermostat + with no hysteresis at all, which cannot be how the device behaves; the + 1.0 degC differential above is what it does instead. +* In ``HEAT_PUMP`` and ``ENERGY_SAVER`` it rests at 40.5 degC, the + device minimum, which is 33 degC below a normally charged tank. + +The second case is the one that misleads. In ``ENERGY_SAVER`` the upper +element **does** run, and it runs with the on-setting far below the tank +- so the element is not being driven by that field. Switching into the +mode with the tank 1.0 degC or more short brought the element on within +one poll in both runs tested, and ``heUpperOnTempSetting`` dropped from +the setpoint to 40.5 degC in the *same* status message. Watching that +field for an explanation of the element will not find one. + + +Electric: the handover, and what happens to the upper zone +=========================================================== + +The protocol reference states that elements do not operate +simultaneously in Electric mode. Confirmed, and the order is upper +first: in a six-minute run the upper element stopped and the lower one +appeared in the next poll, with no overlap. + +Two details a consumer modelling the tank should know: + +**The upper probe falls while the lower element runs.** It peaked at +63.3 degC (145.9 degF), fell to 61.4 (142.5) over the following three +minutes, then recovered to 62.3 (144.1) as the tank mixed. Anything +that treats "upper zone reached X" as monotonic during an Electric +recovery will be wrong by about 1.9 degC for several minutes. + +**The off point lags in both directions.** The upper element switched +off with the probe 0.8 degC (1.4 degF) *below* the setpoint, and the +probe then coasted 1.3 degC (2.3 degF) *past* it. The device's decision +and the probe are not the same instant. + + +Element power +============= + +Metered at the whole-unit power reading with the compressor off, so it +is the element alone: + +* upper element: 5,219 W (5,205-5,242 over 33 samples) +* lower element: 5,200 W + +The reference gives 3,755 W at 208 V or 5,000 W at 240 V. The measured +figure is 4 % above the 240 V rating on this unit, which is one unit at +one supply voltage - a nudge for anyone integrating energy, not a +correction to the specification. + +For the record, the upper zone rose at 0.89 degC/min (1.61 degF/min) +end to end over 5.8 minutes on a 63.2 gal tank, and the lower zone at +0.66-0.86 degC/min depending on where the fit starts; the probe lags the +element for the first 40-60 seconds, which is most of the difference. +Those are tank-specific and are offered as an order of magnitude only. + + +Writing the mode while a TOU window is in force +=============================================== + +Relevant when planning any of this: with TOU scheduling enabled and a +window in force, a mode write may not take effect. Measured on the same +unit, four minutes with no change and nothing logged, twice - and the +command was not discarded either: it took effect one poll after the TOU +switch was turned off, with nothing else commanded. + +Whether the device queues the command or accepts it and masks the +read-back is undetermined. Either way, **confirm a mode write by reading +the mode back, and do not treat an unconfirmed one as failed.** + +Reservation entries are a separate case and fire normally in a window; +see issue #141 for the documentation fix covering that. + + +Limits +====== + +One unit, one supply voltage, 2026-09-19 and 2026-09-20, ten bounded +runs in all. Specifically **not** established: + +* whether ``ELECTRIC`` and ``HIGH_DEMAND`` share the 1.0 degC + differential. Both have been seen to start an element at 1.0 degC or + more, but neither has been tested below it; only ``ENERGY_SAVER`` has + the negative observations that make the bracket a bracket. +* what engages the element **later** in an Energy Saver stint, as + opposed to on entry. +* anything about runs longer than six minutes. +* whether the differential is firmware-dependent. The unit reported + ``heUpperOnDiffTempSetting`` as 0 throughout; if another unit reports + it as non-zero, that field is the better answer and this page is a + description of one firmware's default. diff --git a/docs/explanation/index.rst b/docs/explanation/index.rst index 64a5a97..4891baf 100644 --- a/docs/explanation/index.rst +++ b/docs/explanation/index.rst @@ -9,6 +9,7 @@ Understanding-oriented deep dives into the library's design and advanced feature advanced-features architecture + heating-elements tank-energy tou-recovery-cap unknown-values diff --git a/docs/reference/protocol/device_status.rst b/docs/reference/protocol/device_status.rst index a38447b..6853e19 100644 --- a/docs/reference/protocol/device_status.rst +++ b/docs/reference/protocol/device_status.rst @@ -735,8 +735,14 @@ Technical Notes **Heating Elements:** * Upper and lower heating elements: 3,755W @ 208V or 5,000W @ 240V -* Elements do not operate simultaneously in Electric mode + (measured 5,219 W upper / 5,200 W lower on one 240 V unit) +* Elements do not operate simultaneously in Electric mode - upper first, + then lower, with the handover in a single status message * Heating elements activate for freeze protection when tank < 43°F (6°C) +* 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`. **Heat Pump Specifications:** From 3f373f65fbe2674c6efb9346324b1317fac8f7ca Mon Sep 17 00:00:00 2001 From: Emmanuel Levijarvi Date: Sun, 20 Sep 2026 08:38:33 -0700 Subject: [PATCH 2/5] docs: the start differential is mode-dependent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- CHANGELOG.rst | 10 ++-- docs/explanation/heating-elements.rst | 69 ++++++++++++++++++++------- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4380975..54e025d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,10 +12,12 @@ Documentation ``he*TempSetting`` fields do not describe when an element runs: in Electric and High Demand the on and off settings are the same value, and every ``he*DiffTempSetting`` reads 0. Measured on one unit by - commanding the modes and watching, the upper element starts **1.0 degC - below the setpoint** - bracketed by a descending sweep, off at 0.3 and - 0.8 degC and on within one poll at 1.0 and 1.2 - and that differential - is published nowhere in the status message. Also documents Energy + commanding the modes and watching, the start differential is + **mode-dependent**: ``ENERGY_SAVER`` needs **1.0 degC** below the + setpoint (off at 0.3 and 0.8, on within one poll at 1.0 and 1.2), + while ``ELECTRIC`` starts as soon as the zone is under the setpoint + at all (off only with the tank above it, on at 0.3). Neither + differential is published anywhere in the status message. Also documents Energy Saver's entry behaviour (the element engages although ``heUpperOnTempSetting`` rests 33 degC below the tank), Electric's upper-to-lower handover and the upper probe **falling** 1.9 degC while diff --git a/docs/explanation/heating-elements.rst b/docs/explanation/heating-elements.rst index 4312e46..d76c589 100644 --- a/docs/explanation/heating-elements.rst +++ b/docs/explanation/heating-elements.rst @@ -32,9 +32,11 @@ The short version * - Observation - Value * - Element start differential - - The upper element starts once the zone is **1.0 degC (1.8 degF)** - below the setpoint. Bracketed: off at 0.3 and 0.8 degC, on within - one 10-second poll at 1.0 and 1.2. + - **Mode-dependent.** In ``ENERGY_SAVER`` the upper element starts + once the zone is **1.0 degC (1.8 degF)** below the setpoint. In + ``ELECTRIC`` it starts as soon as the zone is under the setpoint + at all - the boundary is inside 0.3 degC, at or below the probe's + own resolution. Both are bracketed by runs on either side. * - Where that differential is published - **Nowhere.** ``heUpperOnDiffTempSetting`` and its three siblings read 0 on this unit while the behaviour above holds. @@ -44,8 +46,9 @@ The short version 40.5 degC (104.9 degF), the device minimum. * - Energy Saver on entry - Switching into ``ENERGY_SAVER`` engages the upper element even - though ``heUpperOnTempSetting`` rests 33 degC below the tank. The - same 1.0 degC differential applies. + though ``heUpperOnTempSetting`` rests 33 degC below the tank - + an entry effect rather than that thermostat, and the reason its + differential differs from Electric's. * - Electric ordering - Upper element to the setpoint, then the lower element, never both, as the protocol reference already states. The handover @@ -62,9 +65,12 @@ The short version The start differential ====================== -Four runs in ``ENERGY_SAVER``, each holding a fixed deficit and watching -for two minutes. The setpoint quantises to 0.5 degC, so the deficit is -set by choosing a setpoint step relative to the tank. +Two modes, two different answers. Each run holds a fixed deficit and +watches for one to two minutes; the setpoint quantises to 0.5 degC, so +the deficit is set by choosing a setpoint step relative to the tank. + +``ENERGY_SAVER`` +---------------- .. list-table:: :header-rows: 1 @@ -90,10 +96,41 @@ So the boundary lies in (0.8, 1.0] degC, and 1.0 degC is the device's own storage granularity - settings are held in half-degree Celsius steps - which makes a one-degree differential the natural parameter. -Two further runs put the element on at 2.0 degC (3.6 degF) and 5.2 degC -(9.4 degF) short, in ``ENERGY_SAVER`` and ``ELECTRIC`` respectively, so +A further run put the element on at 2.0 degC (3.6 degF) short, so nothing above the boundary has been seen to decline. +``ELECTRIC`` +------------ + +.. list-table:: + :header-rows: 1 + :widths: 30 30 40 + + * - Deficit below setpoint + - Upper element + - Poll at which it engaged + * - 0.6 degC **above** the setpoint + - **off** for the whole watch + - n/a + * - 0.3 degC (0.54 degF) + - **on** + - first poll after the mode landed + * - 0.4 degC (0.72 degF) + - **on** + - first poll after the mode landed + +**The two modes do not share a threshold.** At the same 0.3 degC +deficit, ``ELECTRIC`` runs the element and ``ENERGY_SAVER`` does not. +Electric's boundary is inside (0, 0.3] degC - at or below the tank +probe's own 0.1 degC resolution - which is what the thermostat reading +of its settings predicts, since it parks ``heUpperOnTempSetting`` at the +setpoint. Energy Saver's 1.0 degC is an entry effect and a different +mechanism. + +``HIGH_DEMAND`` shares Electric's settings and has been seen to start an +element at 5.2 degC short, but no run has approached its boundary; the +thermostat reading is an assumption there. + **The differential is not in the protocol.** A ``status --raw`` taken between these runs shows:: @@ -194,17 +231,15 @@ see issue #141 for the documentation fix covering that. Limits ====== -One unit, one supply voltage, 2026-09-19 and 2026-09-20, ten bounded -runs in all. Specifically **not** established: +One unit, one supply voltage, 2026-09-19 and 2026-09-20, thirteen +bounded runs in all. Specifically **not** established: -* whether ``ELECTRIC`` and ``HIGH_DEMAND`` share the 1.0 degC - differential. Both have been seen to start an element at 1.0 degC or - more, but neither has been tested below it; only ``ENERGY_SAVER`` has - the negative observations that make the bracket a bracket. +* ``HIGH_DEMAND``'s boundary. It shares Electric's settings and starts + an element well above the boundary, but no run has approached it. * what engages the element **later** in an Energy Saver stint, as opposed to on entry. * anything about runs longer than six minutes. -* whether the differential is firmware-dependent. The unit reported +* whether either differential is firmware-dependent. The unit reported ``heUpperOnDiffTempSetting`` as 0 throughout; if another unit reports it as non-zero, that field is the better answer and this page is a description of one firmware's default. From de342ed3aa6a491332078cd12a837dca6be5c825 Mon Sep 17 00:00:00 2001 From: Emmanuel Levijarvi Date: Sun, 20 Sep 2026 08:54:14 -0700 Subject: [PATCH 3/5] docs: bracket High Demand too, and put the three modes side by side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- CHANGELOG.rst | 10 ++-- docs/explanation/heating-elements.rst | 74 +++++++++++++++++++++------ 2 files changed, 63 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 54e025d..97e2203 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,11 +13,11 @@ Documentation Electric and High Demand the on and off settings are the same value, and every ``he*DiffTempSetting`` reads 0. Measured on one unit by commanding the modes and watching, the start differential is - **mode-dependent**: ``ENERGY_SAVER`` needs **1.0 degC** below the - setpoint (off at 0.3 and 0.8, on within one poll at 1.0 and 1.2), - while ``ELECTRIC`` starts as soon as the zone is under the setpoint - at all (off only with the tank above it, on at 0.3). Neither - differential is published anywhere in the status message. Also documents Energy + **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. + Neither differential is published anywhere in the status message. Also documents Energy Saver's entry behaviour (the element engages although ``heUpperOnTempSetting`` rests 33 degC below the tank), Electric's upper-to-lower handover and the upper probe **falling** 1.9 degC while diff --git a/docs/explanation/heating-elements.rst b/docs/explanation/heating-elements.rst index d76c589..01836d7 100644 --- a/docs/explanation/heating-elements.rst +++ b/docs/explanation/heating-elements.rst @@ -32,11 +32,10 @@ The short version * - Observation - Value * - Element start differential - - **Mode-dependent.** In ``ENERGY_SAVER`` the upper element starts - once the zone is **1.0 degC (1.8 degF)** below the setpoint. In - ``ELECTRIC`` it starts as soon as the zone is under the setpoint - at all - the boundary is inside 0.3 degC, at or below the probe's - own resolution. Both are bracketed by runs on either side. + - **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. * - Where that differential is published - **Nowhere.** ``heUpperOnDiffTempSetting`` and its three siblings read 0 on this unit while the behaviour above holds. @@ -119,17 +118,60 @@ nothing above the boundary has been seen to decline. - **on** - first poll after the mode landed -**The two modes do not share a threshold.** At the same 0.3 degC -deficit, ``ELECTRIC`` runs the element and ``ENERGY_SAVER`` does not. -Electric's boundary is inside (0, 0.3] degC - at or below the tank -probe's own 0.1 degC resolution - which is what the thermostat reading -of its settings predicts, since it parks ``heUpperOnTempSetting`` at the -setpoint. Energy Saver's 1.0 degC is an entry effect and a different -mechanism. - -``HIGH_DEMAND`` shares Electric's settings and has been seen to start an -element at 5.2 degC short, but no run has approached its boundary; the -thermostat reading is an assumption there. +``HIGH_DEMAND`` +--------------- + +.. list-table:: + :header-rows: 1 + :widths: 30 30 40 + + * - Deficit below setpoint + - Upper element + - Poll at which it engaged + * - 0.2 degC (0.36 degF) + - **off** for the whole watch + - n/a + * - 0.7 degC (1.26 degF) + - **on** + - first poll after the mode landed + +``heUpperOnTempSetting`` moved to the new setpoint in the same status +message the mode landed in, as it does in Electric. + +Putting the three together +-------------------------- + +.. list-table:: + :header-rows: 1 + :widths: 24 26 24 26 + + * - Mode + - Element off at + - Element on at + - Bracket + * - ``ELECTRIC`` + - 0.6 degC above setpoint + - 0.3, 0.4 degC + - (0, 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 can share one threshold; Energy Saver cannot +join them.** The first two brackets overlap only in (0.2, 0.3] degC, +which is consistent with their identical ``heUpperOnTempSetting`` +handling. Energy Saver's band does not intersect either: at 0.7 degC +short, High Demand runs the element and Energy Saver does not. + +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. **The differential is not in the protocol.** A ``status --raw`` taken between these runs shows:: From 6f1333cdb5354e8f00de2defe8e3e16256e10b82 Mon Sep 17 00:00:00 2001 From: Emmanuel Levijarvi Date: Sun, 20 Sep 2026 10:15:35 -0700 Subject: [PATCH 4/5] docs: these are entry thresholds, and two paragraphs said the wrong number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- CHANGELOG.rst | 5 +++- docs/explanation/heating-elements.rst | 42 +++++++++++++++++++-------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 97e2203..00d2ed0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,7 +16,10 @@ Documentation **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. + 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 + 0.2-0.3 degC short and 168 of 169 more than 2.2 degC short. Neither differential is published anywhere in the status message. Also documents Energy Saver's entry behaviour (the element engages although ``heUpperOnTempSetting`` rests 33 degC below the tank), Electric's diff --git a/docs/explanation/heating-elements.rst b/docs/explanation/heating-elements.rst index 01836d7..1de7d94 100644 --- a/docs/explanation/heating-elements.rst +++ b/docs/explanation/heating-elements.rst @@ -53,8 +53,9 @@ The short version both, as the protocol reference already states. The handover takes one poll. * - Element power - - 5,219 W upper, 5,200 W lower, metered with the compressor off. - The reference gives 5,000 W at 240 V. + - 5,200-5,242 W over three measurements (two upper, one lower), + metered with the compressor off. The reference gives 5,000 W at + 240 V. * - Zone coupling - While the lower element runs, the **upper** probe falls about 1.9 degC (3.4 degF) before recovering. The zones are not @@ -152,7 +153,7 @@ Putting the three together * - ``ELECTRIC`` - 0.6 degC above setpoint - 0.3, 0.4 degC - - (0, 0.3] degC + - at most 0.3 degC [#electric]_ * - ``HIGH_DEMAND`` - 0.2 degC - 0.7 degC @@ -162,11 +163,26 @@ Putting the three together - 1.0, 1.2 degC - (0.8, 1.0] degC -**Electric and High Demand can share one threshold; Energy Saver cannot -join them.** The first two brackets overlap only in (0.2, 0.3] degC, +**These are entry thresholds, not thermostats.** 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 - so a stint already running re-engages on something much larger +than the entry figure. Do not use one for the other. + +.. [#electric] Electric's own negative is 0.6 degC *above* the setpoint, + so nothing was observed between there and 0.3 below it. Its lower + edge comes from High Demand's negative, which itself held for only + 53 seconds as the tank drifted through it. + +**Electric and High Demand can share one entry threshold; Energy Saver +cannot join them.** The first two brackets overlap only in (0.2, 0.3] degC, which is consistent with their identical ``heUpperOnTempSetting`` handling. Energy Saver's band does not intersect either: at 0.7 degC -short, High Demand runs the element and Energy Saver does not. +short, High Demand runs the element and Energy Saver does not. One +caveat on that comparison: the Energy Saver runs had the compressor +running and the others did not, so mode is not the only difference +between them. 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 @@ -184,9 +200,10 @@ between these runs shows:: "heLowerOffDiffTempSetting": 0, A consumer that wants to predict when an element will run therefore -cannot read it from the status message; it has to assume the 1.0 degC -above, and treat a tank within that of the setpoint as a case where the -device may go either way. +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. What the on-setting does and does not tell you @@ -196,8 +213,8 @@ What the on-setting does and does not tell you * In ``ELECTRIC`` and ``HIGH_DEMAND`` it equals ``heUpperOffTempSetting`` and both equal the DHW setpoint. Taken literally that is a thermostat - with no hysteresis at all, which cannot be how the device behaves; the - 1.0 degC differential above is what it does instead. + with no hysteresis at all, which cannot be how the device behaves; on + entry to those modes the measured differential is 0.3 degC. * In ``HEAT_PUMP`` and ``ENERGY_SAVER`` it rests at 40.5 degC, the device minimum, which is 33 degC below a normally charged tank. @@ -238,7 +255,8 @@ Element power Metered at the whole-unit power reading with the compressor off, so it is the element alone: -* upper element: 5,219 W (5,205-5,242 over 33 samples) +* upper element: 5,219 W on one run (5,205-5,242 over 33 samples) and + 5,242 W on a second * lower element: 5,200 W The reference gives 3,755 W at 208 V or 5,000 W at 240 V. The measured From 4395026c0e35f6efc1b5196aac2d727ab4f390ca Mon Sep 17 00:00:00 2001 From: Emmanuel Levijarvi Date: Sun, 20 Sep 2026 11:35:19 -0700 Subject: [PATCH 5/5] docs: High Demand's bracket is its own, and the reference contradicted it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CHANGELOG.rst | 10 +- docs/explanation/heating-elements.rst | 114 ++++++++++++------- docs/reference/protocol/data_conversions.rst | 17 ++- docs/reference/protocol/device_status.rst | 9 +- 4 files changed, 100 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 00d2ed0..0447a29 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,10 +13,12 @@ Documentation Electric and High Demand the on and off settings are the same value, and every ``he*DiffTempSetting`` reads 0. Measured on one unit by commanding the modes and watching, the start differential is - **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 + **mode-dependent**, one bracket per mode: ``ELECTRIC`` at most + **0.3 degC** below the setpoint, ``HIGH_DEMAND`` within + **(0.2, 0.7] degC**, ``ENERGY_SAVER`` within **(0.8, 1.0] degC**. + Whether the first two share one threshold is an open question, not a + finding. 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 0.2-0.3 degC short and 168 of 169 more than 2.2 degC short. diff --git a/docs/explanation/heating-elements.rst b/docs/explanation/heating-elements.rst index 1de7d94..efb5609 100644 --- a/docs/explanation/heating-elements.rst +++ b/docs/explanation/heating-elements.rst @@ -10,12 +10,17 @@ 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. -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. +**The start thresholds** here were measured on one unit by commanding +the modes and watching - not inferred from historical logs. That +distinction matters: two earlier values 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. + +Two things on this page are *not* from commanded runs, and are labelled +where they appear: High Demand's **steady-state** re-engagement, which +is this unit's history, and the mid-stint behaviour it is contrasted +with. Everything else is a bounded run. .. contents:: :local: @@ -32,10 +37,12 @@ The short version * - Observation - Value * - Element start differential - - **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. + - **Mode-dependent, one bracket per mode.** ``ELECTRIC`` at most + **0.3 degC (0.54 degF)** below the setpoint; ``HIGH_DEMAND`` + somewhere in **(0.2, 0.7] degC** ((0.36, 1.26] degF); + ``ENERGY_SAVER`` in **(0.8, 1.0] degC** ((1.44, 1.80] degF). + Each bracket has a run on either side of it - except Electric's + lower edge, whose only negative sits *above* the setpoint. * - Where that differential is published - **Nowhere.** ``heUpperOnDiffTempSetting`` and its three siblings read 0 on this unit while the behaviour above holds. @@ -45,9 +52,11 @@ The short version 40.5 degC (104.9 degF), the device minimum. * - Energy Saver on entry - 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. + though ``heUpperOnTempSetting`` rests 33 degC below the tank, so + whatever drives it, that field is not it. Its bracket also sits + higher than the other two modes'. Why the two differ is **not** + established: the Energy Saver runs had the compressor running + and the others did not. * - Electric ordering - Upper element to the setpoint, then the lower element, never both, as the protocol reference already states. The handover @@ -65,9 +74,10 @@ The short version The start differential ====================== -Two modes, two different answers. Each run holds a fixed deficit and -watches for one to two minutes; the setpoint quantises to 0.5 degC, so -the deficit is set by choosing a setpoint step relative to the tank. +Three modes, three brackets, and no two of them the same width. Each +run holds a fixed deficit and watches for one to two minutes; the +setpoint quantises to 0.5 degC, so the deficit is set by choosing a +setpoint step relative to the tank. ``ENERGY_SAVER`` ---------------- @@ -171,23 +181,31 @@ short - so a stint already running re-engages on something much larger than the entry figure. Do not use one for the other. .. [#electric] Electric's own negative is 0.6 degC *above* the setpoint, - so nothing was observed between there and 0.3 below it. Its lower - edge comes from High Demand's negative, which itself held for only - 53 seconds as the tank drifted through it. - -**Electric and High Demand can share one entry threshold; Energy Saver -cannot join them.** The first two brackets overlap only in (0.2, 0.3] degC, -which is consistent with their identical ``heUpperOnTempSetting`` -handling. Energy Saver's band does not intersect either: at 0.7 degC + so nothing at all was observed between there and 0.3 below it. Its + bracket is open at the bottom: no run has shown Electric declining + to heat a tank that is short of the setpoint by any amount. + +**Electric and High Demand may share one entry threshold. That is an +inference, not a measurement.** Their brackets overlap in +(0.2, 0.3] degC and the two modes handle ``heUpperOnTempSetting`` +identically, which is a real argument for one number covering both - +but High Demand has never been watched between 0.2 and 0.7 degC, so +nothing here measures where its boundary falls inside that span. +Energy Saver's band does not intersect either of them: at 0.7 degC short, High Demand runs the element and Energy Saver does not. One -caveat on that comparison: the Energy Saver runs had the compressor -running and the others did not, so mode is not the only difference -between them. - -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. +caveat on that last comparison: the Energy Saver runs had the +compressor running and the others did not, so mode is not the only +difference between them. + +A consumer wanting one number per mode should take the **top of that +mode's own bracket** - 0.3 degC for ``ELECTRIC``, **0.7 degC** for +``HIGH_DEMAND``, 1.0 degC for ``ENERGY_SAVER``. Each is the **smallest +deficit at which that mode was seen to start an element**, so assuming +it contradicts no observation of that mode - where anything lower +contradicts that mode's own negative. Borrowing Electric's 0.3 degC for High Demand predicts an +element at, say, 0.4 degC short that High Demand has never been seen to +run; if your consumer is deciding whether hot water will be ready, +that is the expensive direction to be wrong in. **The differential is not in the protocol.** A ``status --raw`` taken between these runs shows:: @@ -201,9 +219,10 @@ between these runs shows:: 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. +figures above - 0.3 degC for ``ELECTRIC``, 0.7 for ``HIGH_DEMAND``, +1.0 for ``ENERGY_SAVER`` - and treat a tank inside the corresponding +bracket as a case where the device may go either way. High Demand's +bracket is the widest, so that "either way" span is widest there too. What the on-setting does and does not tell you @@ -214,7 +233,8 @@ What the on-setting does and does not tell you * In ``ELECTRIC`` and ``HIGH_DEMAND`` it equals ``heUpperOffTempSetting`` and both equal the DHW setpoint. Taken literally that is a thermostat with no hysteresis at all, which cannot be how the device behaves; on - entry to those modes the measured differential is 0.3 degC. + entry the measured differential is at most 0.3 degC in Electric and + within (0.2, 0.7] degC in High Demand. * In ``HEAT_PUMP`` and ``ENERGY_SAVER`` it rests at 40.5 degC, the device minimum, which is 33 degC below a normally charged tank. @@ -226,6 +246,10 @@ one poll in both runs tested, and ``heUpperOnTempSetting`` dropped from the setpoint to 40.5 degC in the *same* status message. Watching that field for an explanation of the element will not find one. +That is as far as the observation goes. It does not explain *why* +Energy Saver's bracket sits higher than Electric's; the runs differ in +compressor state as well as mode, and nothing here separates the two. + Electric: the handover, and what happens to the upper zone =========================================================== @@ -264,8 +288,10 @@ figure is 4 % above the 240 V rating on this unit, which is one unit at one supply voltage - a nudge for anyone integrating energy, not a correction to the specification. -For the record, the upper zone rose at 0.89 degC/min (1.61 degF/min) -end to end over 5.8 minutes on a 63.2 gal tank, and the lower zone at +For the record, the upper zone rose at 0.87-0.89 degC/min +(1.56-1.61 degF/min) end to end over 5.8 minutes on a 63.2 gal tank - +the spread is sampling resolution, the higher figure from 10-second +polls and the lower from a one-minute grid - and the lower zone at 0.66-0.86 degC/min depending on where the fit starts; the probe lags the element for the first 40-60 seconds, which is most of the difference. Those are tank-specific and are offered as an order of magnitude only. @@ -291,11 +317,15 @@ see issue #141 for the documentation fix covering that. Limits ====== -One unit, one supply voltage, 2026-09-19 and 2026-09-20, thirteen -bounded runs in all. Specifically **not** established: +One unit, one supply voltage, 2026-09-19 and 2026-09-20. **Twenty-two** +bounded runs in all: thirteen on the elements and the thresholds, nine +on mode writes and reservation entries under TOU. Specifically **not** +established: -* ``HIGH_DEMAND``'s boundary. It shares Electric's settings and starts - an element well above the boundary, but no run has approached it. +* **where inside (0.2, 0.7] degC ``HIGH_DEMAND``'s boundary falls.** It + has a run on each side, so it is bracketed - but that bracket is more + than twice Electric's width, and one run at 0.4 degC would halve it. + Until then, whether the two modes share a threshold is unresolved. * what engages the element **later** in an Energy Saver stint, as opposed to on entry. * anything about runs longer than six minutes. diff --git a/docs/reference/protocol/data_conversions.rst b/docs/reference/protocol/data_conversions.rst index 339ffd3..d2bebd4 100644 --- a/docs/reference/protocol/data_conversions.rst +++ b/docs/reference/protocol/data_conversions.rst @@ -164,6 +164,21 @@ Heating Element Control Temperatures Electric heating elements are controlled via thermostat ranges. Two sensors (upper and lower tank) allow two-stage heating: +.. warning:: + + **These fields do not predict when an element runs.** The + descriptions below are the protocol's own semantics for each field. + Measured against the device, they do not hold: in ``ELECTRIC`` and + ``HIGH_DEMAND`` the ON and OFF settings are **the same value** (no + hysteresis at all, which cannot be how the device behaves), 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 rather than the 2-5 degF below. + + The differential the device actually uses is mode-dependent and is + published nowhere in the status message. See + :doc:`/explanation/heating-elements` for the measured values. + .. list-table:: :header-rows: 1 :widths: 30 10 15 45 @@ -191,7 +206,7 @@ Electric heating elements are controlled via thermostat ranges. Two sensors (upp * - ``heUpperOnDiffTempSetting`` - div_10_celsius_delta - °F - - **Upper element differential** (ON-OFF hysteresis width). Temperature delta to prevent rapid cycling. Typically 2-5°F. This is a DELTA value, not an absolute temperature (0 delta = 0°F difference). + - **Upper element differential** (ON-OFF hysteresis width). Temperature delta to prevent rapid cycling. Nominally 2-5°F, but **measured as 0 on a 9.x-firmware unit in every mode**, while the device nonetheless applied a differential - see the warning above. This is a DELTA value, not an absolute temperature (0 delta = 0°F difference). * - ``heUpperOffDiffTempSetting`` - div_10_celsius_delta - °F diff --git a/docs/reference/protocol/device_status.rst b/docs/reference/protocol/device_status.rst index 6853e19..17885d0 100644 --- a/docs/reference/protocol/device_status.rst +++ b/docs/reference/protocol/device_status.rst @@ -740,9 +740,12 @@ Technical Notes then lower, with the handover in a single status message * Heating elements activate for freeze protection when tank < 43°F (6°C) * 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`. + runs: in Electric and High Demand the on and off settings are equal + and every ``he*DiffTempSetting`` reads 0. The measured start + differential is **mode-dependent** - at most 0.3 degC in + ``ELECTRIC``, within (0.2, 0.7] degC in ``HIGH_DEMAND``, and within + (0.8, 1.0] degC in ``ENERGY_SAVER``, all measured on entry to the + mode. See :doc:`/explanation/heating-elements`. **Heat Pump Specifications:**