Controls the central heating system and the lights.

Dependencies:   1-wire clock crypto fram log lpc1768 net web wiz mbed

Files at this revision

API Documentation at this revision

Comitter:
andrewboyson
Date:
Sat Nov 12 10:03:38 2022 +0000
Parent:
7:e55c6a61e15b
Commit message:
Updated LPC1768 library

Changed in this revision

heating/hall-led.c Show annotated file Show diff for this revision Revisions of this file
heating/hall-pb.c Show annotated file Show diff for this revision Revisions of this file
heating/old-radiator.c Show annotated file Show diff for this revision Revisions of this file
heating/old-radiator.h Show annotated file Show diff for this revision Revisions of this file
heating/radiator.c Show annotated file Show diff for this revision Revisions of this file
heating/radiator.h Show annotated file Show diff for this revision Revisions of this file
lpc1768.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
web-this/home/web-home-html.c Show annotated file Show diff for this revision Revisions of this file
web-this/radiator/web-radiator-ajax.c Show annotated file Show diff for this revision Revisions of this file
web-this/radiator/web-radiator-html.c Show annotated file Show diff for this revision Revisions of this file
web-this/radiator/web-radiator-script.inc Show annotated file Show diff for this revision Revisions of this file
web-this/radiator/web-radiator-script.js Show annotated file Show diff for this revision Revisions of this file
diff -r e55c6a61e15b -r 8ac076ce51af heating/hall-led.c
--- a/heating/hall-led.c	Mon Jun 06 11:10:04 2022 +0000
+++ b/heating/hall-led.c	Sat Nov 12 10:03:38 2022 +0000
@@ -12,8 +12,8 @@
 #define HALL_LED_SET FIO0SET(10)
 #define HALL_LED_CLR FIO0CLR(10)
 
-#define    WINTER_FLASH_MS   100
-#define    SUMMER_FLASH_MS   500
+#define    POSITIVE_FLASH_MS   100
+#define    NEGATIVE_FLASH_MS   500
 
 void HallLedInit()
 {
@@ -24,12 +24,12 @@
     static uint32_t hallLedFlashMsTimer = 0;
     if (HallPbOverrideMode)
     {
-        if (RadiatorPump) HALL_LED_SET; else HALL_LED_CLR;
+        if (RadiatorsOn) HALL_LED_SET; else HALL_LED_CLR;
         hallLedFlashMsTimer = MsTimerCount;
     }
     else
     {
-        int flashRate = RadiatorGetWinter() ? WINTER_FLASH_MS : SUMMER_FLASH_MS;
+        int flashRate = RadiatorGetHotWaterProtectOn() ? POSITIVE_FLASH_MS : NEGATIVE_FLASH_MS;
         if (MsTimerRepetitive(&hallLedFlashMsTimer, flashRate))
         {
             static bool flash = false;
diff -r e55c6a61e15b -r 8ac076ce51af heating/hall-pb.c
--- a/heating/hall-pb.c	Mon Jun 06 11:10:04 2022 +0000
+++ b/heating/hall-pb.c	Sat Nov 12 10:03:38 2022 +0000
@@ -10,7 +10,7 @@
 #define HALL_PB_PIN FIO0PIN(05) // P0.05 == p29;
 
 #define        DEBOUNCE_MS    20
-#define      LONG_PRESS_MS  3000
+#define      LONG_PRESS_MS  2000
 #define INACTIVE_REVERT_MS 30000
 
 static int ms  = 0;
@@ -67,7 +67,7 @@
     if (!buttonIsPressed && buttonWasPressed && !buttonWasLongPressed)
     {
         if (HallPbOverrideMode) RadiatorChgOverride();
-        else                    RadiatorChgWinter();
+        else                    RadiatorChgHotWaterProtectOn();
     }
     buttonWasPressed     = buttonIsPressed;
     buttonWasLongPressed = buttonIsLongPressed;
diff -r e55c6a61e15b -r 8ac076ce51af heating/old-radiator.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/heating/old-radiator.c	Sat Nov 12 10:03:38 2022 +0000
@@ -0,0 +1,164 @@
+/*#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "gpio.h"
+#include "program.h"
+#include "ds18b20.h"
+#include "settings.h"
+#include "boiler.h"
+#include "radiator.h"
+#include "clktime.h"
+#include "clk.h"
+#include "led.h"
+
+#define RADIATOR_PUMP_DIR FIO2DIR(03) // P2.03 == p23;
+#define RADIATOR_PUMP_PIN FIO2PIN(03)
+#define RADIATOR_PUMP_SET FIO2SET(03)
+#define RADIATOR_PUMP_CLR FIO2CLR(03)
+
+static bool      htgOverride = false;
+static char      htgWinter;
+static char*     hallRom;
+static uint8_t   overrideCancelHour;
+static uint8_t   overrideCancelMinute;
+static  int16_t  nightTemperature;
+static  int16_t  frostTemperature;
+static char      hotWaterProtectOn;
+static  int8_t   hotWaterProtectTemp;
+
+bool     RadiatorGetWinter              () { return (bool)htgWinter;              } 
+bool     RadiatorGetOverride            () { return       htgOverride;            } 
+bool     RadiatorGetHotWaterProtectOn   () { return (bool)hotWaterProtectOn;      }
+uint16_t RadiatorGetHallDS18B20Value    () { return DS18B20ValueFromRom(hallRom); }
+int      RadiatorGetOverrideCancelHour  () { return  (int)overrideCancelHour;     }
+int      RadiatorGetOverrideCancelMinute() { return  (int)overrideCancelMinute;   }
+int      RadiatorGetNightTemperature    () { return  (int)nightTemperature;       } 
+int      RadiatorGetFrostTemperature    () { return  (int)frostTemperature;       } 
+int      RadiatorGetHotWaterProtectTemp () { return  (int)hotWaterProtectTemp;    }
+
+static void  setWinter              ( bool  v) {                             htgWinter            =     (char)v; SetRadiatorWinter      (&htgWinter           ); }
+static void  setHallRom             ( char* v) {                             memcpy(hallRom,  v, 8);             SetHallRom             ( hallRom             ); }
+void RadiatorSetOverrideCancelHour  ( int   v) { if (v > 23 || v < 0) v = 0; overrideCancelHour   =  (uint8_t)v; SetOverrideCancelHour  (&overrideCancelHour  ); }
+void RadiatorSetOverrideCancelMinute( int   v) { if (v > 59 || v < 0) v = 0; overrideCancelMinute =  (uint8_t)v; SetOverrideCancelMinute(&overrideCancelMinute); }
+void RadiatorSetNightTemperature    ( int   v) { if (v > 99 || v < 0) v = 0; nightTemperature     =  (int16_t)v; SetNightTemperature    (&nightTemperature    ); }
+void RadiatorSetFrostTemperature    ( int   v) { if (v > 99 || v < 0) v = 0; frostTemperature     =  (int16_t)v; SetFrostTemperature    (&frostTemperature    ); }
+
+static bool outputBeforeOverride = false;
+static void makeOutputBeforeOverride()
+{
+    //See if the temperature is too low
+    int  hallTemp16ths = DS18B20ValueFromRom(hallRom);
+    int nightTemp16ths = nightTemperature << 4;
+    int frostTemp16ths = frostTemperature << 4;
+
+    static bool tooCold = false; //This is static to ride through invalid temperature reads
+    
+    static bool nightTooCold = false;
+    static bool frostTooCold = false;
+    
+    if (DS18B20IsValidValue(hallTemp16ths))
+    {
+        if (hallTemp16ths < frostTemp16ths) frostTooCold = true;
+        if (hallTemp16ths > frostTemp16ths) frostTooCold = false;
+        if (hallTemp16ths < nightTemp16ths) nightTooCold = true; //Set   at 289 (18.06) rather than 288 (18.00)
+        if (hallTemp16ths > nightTemp16ths) nightTooCold = false;//Reset at 287 (17.94). This prevent it following the flashing.
+    }
+    
+    outputBeforeOverride = (htgWinter && ProgramTimerOutput) || (htgWinter && nightTooCold) || frostTooCold;
+}
+static void autoCancelOverride()
+{
+    
+    //Remove override at 11pm
+    if (ClkTimeIsSet())
+    {
+        struct tm tm;
+        ClkNowTmLocal(&tm);
+        static bool cancelWasDue = false;
+        bool cancelIsDue = tm.tm_hour == overrideCancelHour && tm.tm_min == overrideCancelMinute;
+        if (cancelIsDue && !cancelWasDue && htgOverride) htgOverride = false;
+        cancelWasDue = cancelIsDue;
+    }
+    
+    //Remove override if no longer required
+    static bool previousOutput = false;
+    if (previousOutput != outputBeforeOverride && htgOverride) htgOverride = false;
+    previousOutput = outputBeforeOverride;
+}
+bool RadiatorPump = false;
+static void makeOutputWithOverride()
+{
+    RadiatorPump = htgOverride ? !outputBeforeOverride : outputBeforeOverride ;
+    if (hotWaterProtectOn && BoilerGetTankDS18B20Value() < ((int16_t)hotWaterProtectTemp << 4)) RadiatorPump = false; //Prevent the heating from robbing all the hot water for showers
+}
+
+void RadiatorSetWinter(bool value) //Summer is false, Winter is true
+{
+    if (htgWinter == (char)value) return; //Ignore no change
+    setWinter(value);                     //Change to the new value
+    
+    bool prevOutputBeforeOverride = outputBeforeOverride;
+    makeOutputBeforeOverride();
+    
+    if (htgOverride) //Only deal with an override that is already set; if it wasn't set don't change it
+    {
+        if (htgWinter) //Summer -> Winter
+        {
+            if (outputBeforeOverride != prevOutputBeforeOverride) htgOverride = false; //Adjust the override to leave the heat as it was - off or on.
+        }
+        else //Winter -> Summer
+        {
+            htgOverride = false; //turn off the heat.
+        }
+    }
+        
+    makeOutputWithOverride();
+}
+void RadiatorSetOverride(bool value)
+{
+    htgOverride = value;
+    makeOutputBeforeOverride();
+    makeOutputWithOverride();
+}
+
+void RadiatorChgWinter  (){ RadiatorSetWinter  (!RadiatorGetWinter  ()); }
+void RadiatorChgOverride(){ RadiatorSetOverride(!RadiatorGetOverride()); }
+void RadiatorChgHotWaterProtectOn  ()       {                             hotWaterProtectOn   = !hotWaterProtectOn; makeOutputWithOverride(); SetHotWaterProtectOn  (&hotWaterProtectOn  ); }
+void RadiatorSetHotWaterProtectTemp( int v) { if (v > 99 || v < 0) v = 0; hotWaterProtectTemp = (int8_t)v;          makeOutputWithOverride(); SetHotWaterProtectTemp(&hotWaterProtectTemp); }
+int RadiatorInit()
+{
+    hallRom = DS18B20Roms + 8 * DS18B20RomCount;
+    DS18B20RomSetters[DS18B20RomCount] = setHallRom;
+    DS18B20RomNames[DS18B20RomCount] = "Hall";
+    DS18B20RomCount++;
+
+    int  address;
+    int8_t  def1;
+    int16_t def2;
+    GetRadiatorWinter      (&htgWinter           );
+    GetHallRom             ( hallRom             );
+    GetOverrideCancelHour  (&overrideCancelHour  );
+    GetOverrideCancelMinute(&overrideCancelMinute);
+    GetNightTemperature    (&nightTemperature    );
+    GetFrostTemperature    (&frostTemperature    );
+    GetHotWaterProtectOn   (&hotWaterProtectOn   );
+    GetHotWaterProtectTemp (&hotWaterProtectTemp );
+    
+    RADIATOR_PUMP_DIR = 1; //Set the direction to 1 == output
+    
+    return 0;
+}
+void RadiatorMain()
+{
+    //Make the radiator output
+    makeOutputBeforeOverride();
+    autoCancelOverride(); //Do this after making the output as it uses that information
+    makeOutputWithOverride();
+    
+    //Pump output
+    if (RadiatorPump) RADIATOR_PUMP_SET;
+    else              RADIATOR_PUMP_CLR;
+
+}
+*/
\ No newline at end of file
diff -r e55c6a61e15b -r 8ac076ce51af heating/old-radiator.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/heating/old-radiator.h	Sat Nov 12 10:03:38 2022 +0000
@@ -0,0 +1,22 @@
+/*#include <stdbool.h>
+#include <stdint.h>
+
+extern bool     RadiatorGetWinter           (void); extern void RadiatorSetWinter          (bool value); extern void RadiatorChgWinter           (void);
+extern bool     RadiatorGetOverride         (void); extern void RadiatorSetOverride        (bool value); extern void RadiatorChgOverride         (void);
+extern bool     RadiatorGetHotWaterProtectOn(void);                                                      extern void RadiatorChgHotWaterProtectOn(void);
+
+extern uint16_t RadiatorGetHallDS18B20Value(void);
+
+extern int      RadiatorGetOverrideCancelHour  (void); extern void RadiatorSetOverrideCancelHour  (int value);
+extern int      RadiatorGetOverrideCancelMinute(void); extern void RadiatorSetOverrideCancelMinute(int value);
+extern int      RadiatorGetNightTemperature    (void); extern void RadiatorSetNightTemperature    (int value);
+extern int      RadiatorGetFrostTemperature    (void); extern void RadiatorSetFrostTemperature    (int value);
+extern int      RadiatorGetHotWaterProtectTemp (void); extern void RadiatorSetHotWaterProtectTemp (int value);
+
+
+
+extern bool  RadiatorPump;
+
+extern int   RadiatorInit(void);
+extern void  RadiatorMain(void);
+*/
\ No newline at end of file
diff -r e55c6a61e15b -r 8ac076ce51af heating/radiator.c
--- a/heating/radiator.c	Mon Jun 06 11:10:04 2022 +0000
+++ b/heating/radiator.c	Sat Nov 12 10:03:38 2022 +0000
@@ -44,18 +44,19 @@
 void RadiatorSetNightTemperature    ( int   v) { if (v > 99 || v < 0) v = 0; nightTemperature     =  (int16_t)v; SetNightTemperature    (&nightTemperature    ); }
 void RadiatorSetFrostTemperature    ( int   v) { if (v > 99 || v < 0) v = 0; frostTemperature     =  (int16_t)v; SetFrostTemperature    (&frostTemperature    ); }
 
-static bool outputBeforeOverride = false;
-static void makeOutputBeforeOverride()
+static bool getProgramRequest()
+{
+    return htgWinter && ProgramTimerOutput;
+}
+static bool getTooCold()
 {
     //See if the temperature is too low
     int  hallTemp16ths = DS18B20ValueFromRom(hallRom);
     int nightTemp16ths = nightTemperature << 4;
     int frostTemp16ths = frostTemperature << 4;
-
-    static bool tooCold = false; //This is static to ride through invalid temperature reads
     
-    static bool nightTooCold = false;
-    static bool frostTooCold = false;
+    static bool nightTooCold = false; //This is static to ride through invalid temperature reads
+    static bool frostTooCold = false; //This is static to ride through invalid temperature reads
     
     if (DS18B20IsValidValue(hallTemp16ths))
     {
@@ -65,7 +66,11 @@
         if (hallTemp16ths > nightTemp16ths) nightTooCold = false;//Reset at 287 (17.94). This prevent it following the flashing.
     }
     
-    outputBeforeOverride = (htgWinter && ProgramTimerOutput) || (htgWinter && nightTooCold) || frostTooCold;
+    return (htgWinter && nightTooCold) || frostTooCold;
+}
+static bool getTankTooColdForShowers()
+{
+    return hotWaterProtectOn && BoilerGetTankDS18B20Value() < ((int16_t)hotWaterProtectTemp << 4); //Prevent the heating from robbing all the hot water for showers
 }
 static void autoCancelOverride()
 {
@@ -82,50 +87,52 @@
     }
     
     //Remove override if no longer required
-    static bool previousOutput = false;
-    if (previousOutput != outputBeforeOverride && htgOverride) htgOverride = false;
-    previousOutput = outputBeforeOverride;
+    bool thisProgramRequest = getProgramRequest();
+    static bool previousProgramRequest = false;
+    if (previousProgramRequest != thisProgramRequest && htgOverride) htgOverride = false;
+    previousProgramRequest = thisProgramRequest;
 }
+bool RadiatorsOn = false;
 bool RadiatorPump = false;
-static void makeOutputWithOverride()
+static void makeOutputs()
 {
-    RadiatorPump = htgOverride ? !outputBeforeOverride : outputBeforeOverride ;
-    if (hotWaterProtectOn && BoilerGetTankDS18B20Value() < ((int16_t)hotWaterProtectTemp << 4)) RadiatorPump = false; //Prevent the heating from robbing all the hot water for showers
+    bool programRequest = getProgramRequest();
+    RadiatorsOn = htgOverride ? !programRequest : programRequest ;
+    RadiatorPump = RadiatorsOn && !getTankTooColdForShowers();
 }
 
 void RadiatorSetWinter(bool value) //Summer is false, Winter is true
 {
     if (htgWinter == (char)value) return; //Ignore no change
-    setWinter(value);                     //Change to the new value
     
-    bool prevOutputBeforeOverride = outputBeforeOverride;
-    makeOutputBeforeOverride();
+    bool programRequestBeforeSettingWinter = getProgramRequest();
+    setWinter(value);                     //Change to the new value
+    bool programRequestAfterSettingWinter = getProgramRequest();
+    
     
     if (htgOverride) //Only deal with an override that is already set; if it wasn't set don't change it
     {
         if (htgWinter) //Summer -> Winter
         {
-            if (outputBeforeOverride != prevOutputBeforeOverride) htgOverride = false; //Adjust the override to leave the heat as it was - off or on.
+            if (programRequestAfterSettingWinter != programRequestBeforeSettingWinter) htgOverride = false; //Adjust the override to leave the heat as it was - off or on.
         }
         else //Winter -> Summer
         {
             htgOverride = false; //turn off the heat.
         }
     }
-        
-    makeOutputWithOverride();
+    makeOutputs();
 }
 void RadiatorSetOverride(bool value)
 {
     htgOverride = value;
-    makeOutputBeforeOverride();
-    makeOutputWithOverride();
+    makeOutputs();
 }
 
 void RadiatorChgWinter  (){ RadiatorSetWinter  (!RadiatorGetWinter  ()); }
 void RadiatorChgOverride(){ RadiatorSetOverride(!RadiatorGetOverride()); }
-void RadiatorChgHotWaterProtectOn  ()       {                             hotWaterProtectOn   = !hotWaterProtectOn; makeOutputWithOverride(); SetHotWaterProtectOn  (&hotWaterProtectOn  ); }
-void RadiatorSetHotWaterProtectTemp( int v) { if (v > 99 || v < 0) v = 0; hotWaterProtectTemp = (int8_t)v;          makeOutputWithOverride(); SetHotWaterProtectTemp(&hotWaterProtectTemp); }
+void RadiatorChgHotWaterProtectOn  ()       {                             hotWaterProtectOn   = !hotWaterProtectOn; SetHotWaterProtectOn  (&hotWaterProtectOn  ); }
+void RadiatorSetHotWaterProtectTemp( int v) { if (v > 99 || v < 0) v = 0; hotWaterProtectTemp = (int8_t)v;          SetHotWaterProtectTemp(&hotWaterProtectTemp); }
 int RadiatorInit()
 {
     hallRom = DS18B20Roms + 8 * DS18B20RomCount;
@@ -151,12 +158,9 @@
 }
 void RadiatorMain()
 {
-    //Make the radiator output
-    makeOutputBeforeOverride();
-    autoCancelOverride(); //Do this after making the output as it uses that information
-    makeOutputWithOverride();
+    autoCancelOverride();
+    makeOutputs();
     
-    //Pump output
     if (RadiatorPump) RADIATOR_PUMP_SET;
     else              RADIATOR_PUMP_CLR;
 
diff -r e55c6a61e15b -r 8ac076ce51af heating/radiator.h
--- a/heating/radiator.h	Mon Jun 06 11:10:04 2022 +0000
+++ b/heating/radiator.h	Sat Nov 12 10:03:38 2022 +0000
@@ -13,8 +13,7 @@
 extern int      RadiatorGetFrostTemperature    (void); extern void RadiatorSetFrostTemperature    (int value);
 extern int      RadiatorGetHotWaterProtectTemp (void); extern void RadiatorSetHotWaterProtectTemp (int value);
 
-
-
+extern bool  RadiatorsOn;
 extern bool  RadiatorPump;
 
 extern int   RadiatorInit(void);
diff -r e55c6a61e15b -r 8ac076ce51af lpc1768.lib
--- a/lpc1768.lib	Mon Jun 06 11:10:04 2022 +0000
+++ b/lpc1768.lib	Sat Nov 12 10:03:38 2022 +0000
@@ -1,1 +1,1 @@
-https://os.mbed.com/users/andrewboyson/code/lpc1768/#3ae450c74c5e
+https://os.mbed.com/users/andrewboyson/code/lpc1768/#2f8af23950de
diff -r e55c6a61e15b -r 8ac076ce51af mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sat Nov 12 10:03:38 2022 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/mbed_official/code/mbed/builds/65be27845400
\ No newline at end of file
diff -r e55c6a61e15b -r 8ac076ce51af web-this/home/web-home-html.c
--- a/web-this/home/web-home-html.c	Mon Jun 06 11:10:04 2022 +0000
+++ b/web-this/home/web-home-html.c	Sat Nov 12 10:03:38 2022 +0000
@@ -6,7 +6,7 @@
     HttpOk("text/html; charset=UTF-8", "no-cache", NULL, NULL);
     WebAddHeader("Home", NULL, "radiator.js");
     
-    HttpAddText("<a href='/program' class='hamburger'>\r\n");
+    HttpAddText("<a href='/radiator' class='hamburger'>\r\n");
         HttpAddText("<div class='bar'  ></div>\r\n");
         HttpAddText("<div class='space'></div>\r\n");
         HttpAddText("<div class='bar'  ></div>\r\n");
@@ -18,11 +18,13 @@
     
     HttpAddText("<div id='ajax-date-local'></div>\r\n");
     HttpAddText("<br/>\r\n");
-    WebAddAjaxLabelledSuffix("Hall temperature", "ajax-hall-html", "&deg;C");
+    WebAddAjaxLabelledSuffix("Hall temperature"  , "ajax-hall-html", "&deg;C");
+    HttpAddText("<br/>\r\n");
+    WebAddAjaxInputToggle   ("Radiators"         , "ajax-radiators-on-toggle", "htg-chg-override");
     HttpAddText("<br/>\r\n");
-    WebAddAjaxInputToggle("Winter mode", "ajax-mode-toggle"    , "htg-chg-mode"    );
+    WebAddAjaxInputToggle   ("Hot water priority", "ajax-hw-prot-toggle"     , "htg-chg-hw-prot" );
     HttpAddText("<br/>\r\n");
-    WebAddAjaxInputToggle("Radiators"  , "ajax-radiator-toggle", "htg-chg-override");
+    WebAddAjaxInputToggle   ("Winter mode"       , "ajax-mode-toggle"        , "htg-chg-mode"    );
     HttpAddText("<br/>\r\n");
 
     WebAddEnd();
diff -r e55c6a61e15b -r 8ac076ce51af web-this/radiator/web-radiator-ajax.c
--- a/web-this/radiator/web-radiator-ajax.c	Mon Jun 06 11:10:04 2022 +0000
+++ b/web-this/radiator/web-radiator-ajax.c	Sat Nov 12 10:03:38 2022 +0000
@@ -21,6 +21,7 @@
     if (RadiatorGetOverride()         ) byte |= 0x04;
     if (RadiatorPump                  ) byte |= 0x08;
     if (RadiatorGetHotWaterProtectOn()) byte |= 0x10;
+    if (RadiatorsOn                   ) byte |= 0x20;
     HttpAddByteAsHex(byte);    HttpAddChar('\n');
     
     HttpAddByteAsHex (RadiatorGetOverrideCancelHour()  ); HttpAddChar('\n');
diff -r e55c6a61e15b -r 8ac076ce51af web-this/radiator/web-radiator-html.c
--- a/web-this/radiator/web-radiator-html.c	Mon Jun 06 11:10:04 2022 +0000
+++ b/web-this/radiator/web-radiator-html.c	Sat Nov 12 10:03:38 2022 +0000
@@ -10,7 +10,7 @@
     WebAddH1("Radiator");
 
     WebAddH2("Output");
-    WebAddAjaxLed           ("Radiator pump",     "ajax-radiator-toggle");
+    WebAddAjaxLed           ("Radiator pump",     "ajax-radiator-pump-toggle");
     
     WebAddH2("Inputs");
     WebAddAjaxLabelledSuffix("Hall temperature",  "ajax-hall-html", "&deg;C");
diff -r e55c6a61e15b -r 8ac076ce51af web-this/radiator/web-radiator-script.inc
--- a/web-this/radiator/web-radiator-script.inc	Mon Jun 06 11:10:04 2022 +0000
+++ b/web-this/radiator/web-radiator-script.inc	Sat Nov 12 10:03:38 2022 +0000
@@ -8,6 +8,7 @@
 "let radiatorOverride     = false;\n"
 "let radiatorPump         = false;\n"
 "let hotWaterProtectOn    = false;\n"
+"let radiatorsOn          = false;\n"
 "let overrideCancelHour = '';\n"
 "let overrideCancelMinute = '';\n"
 "let nightSetPoint        = '';\n"
@@ -24,6 +25,7 @@
 "    radiatorOverride     = Ajax.hexToBit        (lines[2],  2);\n"
 "    radiatorPump         = Ajax.hexToBit        (lines[2],  3);\n"
 "    hotWaterProtectOn    = Ajax.hexToBit        (lines[2],  4);\n"
+"    radiatorsOn          = Ajax.hexToBit        (lines[2],  5);\n"
 "    overrideCancelHour   = Ajax.hexToSignedInt8 (lines[3]);\n"
 "    overrideCancelMinute = Ajax.hexToSignedInt8 (lines[4]);\n"
 "    nightSetPoint        = Ajax.hexToSignedInt16(lines[5]);\n"
@@ -38,8 +40,9 @@
 "    elem = Ajax.getElementOrNull('ajax-program-toggle'        ); if (elem) elem.setAttribute('dir', programTimerOutput ? 'rtl' : 'ltr');\n"
 "    elem = Ajax.getElementOrNull('ajax-mode-toggle'           ); if (elem) elem.setAttribute('dir', radiatorMode       ? 'rtl' : 'ltr');\n"
 "    elem = Ajax.getElementOrNull('ajax-override-toggle'       ); if (elem) elem.setAttribute('dir', radiatorOverride   ? 'rtl' : 'ltr');\n"
-"    elem = Ajax.getElementOrNull('ajax-radiator-toggle'       ); if (elem) elem.setAttribute('dir', radiatorPump       ? 'rtl' : 'ltr');\n"
+"    elem = Ajax.getElementOrNull('ajax-radiator-pump-toggle'  ); if (elem) elem.setAttribute('dir', radiatorPump       ? 'rtl' : 'ltr');\n"
 "    elem = Ajax.getElementOrNull('ajax-hw-prot-toggle'        ); if (elem) elem.setAttribute('dir', hotWaterProtectOn  ? 'rtl' : 'ltr');\n"
+"    elem = Ajax.getElementOrNull('ajax-radiators-on-toggle'   ); if (elem) elem.setAttribute('dir', radiatorsOn        ? 'rtl' : 'ltr');\n"
 "    elem = Ajax.getElementOrNull('ajax-override-cancel-minute'); if (elem)\n"
 "    {\n"
 "        elem.value = String(overrideCancelHour*100 + overrideCancelMinute).padStart(4, '0');\n"
diff -r e55c6a61e15b -r 8ac076ce51af web-this/radiator/web-radiator-script.js
--- a/web-this/radiator/web-radiator-script.js	Mon Jun 06 11:10:04 2022 +0000
+++ b/web-this/radiator/web-radiator-script.js	Sat Nov 12 10:03:38 2022 +0000
@@ -8,6 +8,7 @@
 let radiatorOverride     = false;
 let radiatorPump         = false;
 let hotWaterProtectOn    = false;
+let radiatorsOn          = false;
 let overrideCancelHour = '';
 let overrideCancelMinute = '';
 let nightSetPoint        = '';
@@ -24,6 +25,7 @@
     radiatorOverride     = Ajax.hexToBit        (lines[2],  2);
     radiatorPump         = Ajax.hexToBit        (lines[2],  3);
     hotWaterProtectOn    = Ajax.hexToBit        (lines[2],  4);
+    radiatorsOn          = Ajax.hexToBit        (lines[2],  5);
     overrideCancelHour   = Ajax.hexToSignedInt8 (lines[3]);
     overrideCancelMinute = Ajax.hexToSignedInt8 (lines[4]);
     nightSetPoint        = Ajax.hexToSignedInt16(lines[5]);
@@ -38,8 +40,9 @@
     elem = Ajax.getElementOrNull('ajax-program-toggle'        ); if (elem) elem.setAttribute('dir', programTimerOutput ? 'rtl' : 'ltr');
     elem = Ajax.getElementOrNull('ajax-mode-toggle'           ); if (elem) elem.setAttribute('dir', radiatorMode       ? 'rtl' : 'ltr');
     elem = Ajax.getElementOrNull('ajax-override-toggle'       ); if (elem) elem.setAttribute('dir', radiatorOverride   ? 'rtl' : 'ltr');
-    elem = Ajax.getElementOrNull('ajax-radiator-toggle'       ); if (elem) elem.setAttribute('dir', radiatorPump       ? 'rtl' : 'ltr');
+    elem = Ajax.getElementOrNull('ajax-radiator-pump-toggle'  ); if (elem) elem.setAttribute('dir', radiatorPump       ? 'rtl' : 'ltr');
     elem = Ajax.getElementOrNull('ajax-hw-prot-toggle'        ); if (elem) elem.setAttribute('dir', hotWaterProtectOn  ? 'rtl' : 'ltr');
+    elem = Ajax.getElementOrNull('ajax-radiators-on-toggle'   ); if (elem) elem.setAttribute('dir', radiatorsOn        ? 'rtl' : 'ltr');
     elem = Ajax.getElementOrNull('ajax-override-cancel-minute'); if (elem)
     {
         elem.value = String(overrideCancelHour*100 + overrideCancelMinute).padStart(4, '0');