Sunday, March 3, 2024

Hot Tub Monitor, Part 1

 Today I am working on a better hot tub monitor.  My hot tub has three separate pumps. I want to be able to monitor its energy consumption in greater detail, and to have it send me alerts in the event that the heater fails.

To make it fun (and cheap) I decided to use the Shelly I4 Plus module coupled with the I/O Addon. To this I added three DS18B20 temperature sensors and a couple of AC current switches (two shown below, two more to be added later).  The current switches are self-powered and act like a relay contact that closes when the current to each motor is above a few hundred milliamps.  I already have a current monitor, so I don't need to know the actual current - all I'm looking for here is state monitoring.



You can run Tasmota on these Shelly devices, but for this I don't need it.  I do, however, want to be able to control the update rate.  Initially I am just going to report state changes on a fixed interval, but the intent is to implement reporting on state change with a minimum reporting rate if temperature or state has not changed.  This will allow me to get an immediate report when the motors start, but not overwhelm my time-series database with a lot of duplicate data.  To accomplish this I will use the built-in scripting that the Shelly Gen 3 modules support.  I will improve this script later to implement the report-by-exception but to keep it simple I want to show an example of how the scripts work.


function updateTemps() {
  Shelly.call("Temperature.Getstatus", { id: 100 },
    function (response) {
       MQTT.publish("hottub/t1", JSON.stringify(response.tF), 0, false);
    }
  )
   
   Shelly.call("Temperature.Getstatus", { id: 101 },
    function (response) {
       MQTT.publish("hottub/t2", JSON.stringify(response.tF), 0, false);
    }
  )
   
   Shelly.call("Temperature.Getstatus", { id: 102 },
    function (response) {
       MQTT.publish("hottub/t3", JSON.stringify(response.tF), 0, false);
    }
  )
}


let tempTimer = null;

function start() {
    print("starting");
    tempTimer = Timer.set(1000, true, updateTemps, null);
}
 
function stop() {
  print("stopping");
  Timer.clear(tempTimer)
}

The device is already configured to connect to my MQTT broker.  Updates are done by making an RPC call to the Temperature.GetStatus function in the Shelly.  Each sensor has a unique ID; the three external sensors are Inumbers 100, 101, and 102.  You can get a sensor list by calling SensorAddon.GetPeripherals which returns a little bit of JSON:

{
    "digital_in": {},
    "ds18b20": {
        "temperature:100": {
            "addr": "40:118:243:67:212:9:96:228"
        },
        "temperature:101": {
            "addr": "40:204:240:67:212:47:73:182"
        },
        "temperature:102": {
            "addr": "40:242:0:67:212:231:49:22"
        }
    },
    "dht22": {},
    "analog_in": {},
    "voltmeter": {}
}

All of these RPC calls are accessible through the either your browser or curl, so it's easy to figure out the returned message structure (but the documentation is also very clear).

Happy hacking!