Sunday, May 1, 2022

Home Automation for a Hot Water Recirculating Pump

My bathroom is pretty far from the water heater. It took over a minute of running the hot water for it to actually get hot. That's a lot of water wasted every time I waited for hot water. I wanted hot water faster.

Contents

Recirculating Hot Water

Last year, as part of a bathroom remodel, I had a hot water recirculating system installed. This consisted of a return pipe from the bathroom and a recirculating pump at the water heater to pull water from the return pipe, thus bringing hot water to the bathroom without having to run water down the drain waiting for it to warm up.

Once the system was installed, I learned that the pump is not supposed to run all the time. In addition, the pump, while not terribly noisy, produced enough noise to be annoying, especially in the parts of the house adjacent to the garage where the water heater and pump were located. So I didn't want to run it all the time for that reason.

The installer gave me a timer. I set it up to run in the morning and the evening. My schedule wasn't precise enough to run the timer for just a short amount of time, so I had it set up to run for about an hour. This didn't work very well: besides the noise issue mentioned above, the temperature of the water dropped a noticeable amount during this period. I needed another solution.

Home Automation

My solution was to set up a home automation system with some outlets and some battery-powered pushbuttons and program it so that when one of the pushbuttons was pressed, it would turn on the outlet for a couple of minutes to run the recirculating pump. This has worked well.

Years ago I used a bunch of X10 switches and outlets. I even installed a blocker to isolate the X10 signals in my house from the incoming power line and a coupler to ensure the X10 signals from one 120V leg made it to devices on the other 120V leg. I eventually stopped using those devices and had not installed any other home automation until now.

After looking at what was available, I decided to use the following technologies for my new home automation system:
  • Home Assistant as the controller
    I chose this for two reasons:
    1. I don't want my system to depend on the cloud or to be sending data out to anyone. Home Assistant allows me to do everything myself and be isolated from the internet. My automation won't stop working when my internet connection or someone else's computers or software go down.
    2. I like to tinker. Home Assistant is highly customizable - as long as you are willing to fiddle with it.
  • Zigbee 3.0 devices
    • I looked at Zigbee and Z-wave and decided Zigbee looked like the better choice for number of compatible available devices.
    • I specifically did not want to use wifi devices.
Having made those two choices, the next choice was where to run Home Assistant and how to connect the Zigbee devices to it. I figured I would use a USB Zigbee coordinator. For the Home Assistant host, I considered running it on my desktop (which is always on), on my Synology NAS, or on a bespoke device such as a Raspberry Pi. I learned that Synology announced they would be removing support for external USB devices other than disks, so I eliminated that choice. I starting looking into using a Raspberry Pi and read multiple comments about high failure rates of the SD cards. Someone suggested attaching a USB SSD, which seemed like a good idea, but that would require more research and figuring out how to mount everything. About this time I discovered HA Blue, a nice little device based on the Odroid-N2 with 128GB of on-board eMMC, 4 USB ports, ethernet, and HDMI, all in a good-looking extruded aluminum case, and pre-loaded with Home Assistant. It's a little more expensive than some other options, but for me the added convenience of a pre-installed system and the nice case were worth the price.

Note: Home Assistant Blue has been discontinued and is being superseded by Home Assistant Yellow, which has a built-in Zigbee radio and more expansion slots.

Even after deciding on Zigbee, there were a few different available ways to set up the communication between the Zigbee devices and Home Assistant. After doing some reading, I settled on using zigbee2mqtt. It seems like one of the newer solutions, and one where I would have less trouble integrating a wider variety of devices.

Hardware

For my initial foray into home automation and based on my decisions above, I bought the following:
  • HA Blue bespoke Home Assistant controller pre-loaded with Home Assistant
  • SmartLight Zigbee CC2652P Coordinator v4 USB Adapter preflashed with CC2652P_E72_20210319 firmware to support zigbee2mqtt
  • Some Sonoff S31 Lite Zigbee outlet plugs
  • Sonoff SNZB-01 Zigbee switch
  • Some Linkind Zigbee switches and outlets
I used the Blakadder compatibility list to find devices that were compatible with zigbee2mqtt, then looked at which ones I could get and what they cost. The outlets and switches I bought were on the less expensive end of the range, costing less than $10 each, although the price has since gone up.

Initial Setup

Setting up the HA Blue system was straightforward:
  1. Plug it in to power and ethernet
  2. Look in my DHCP log to see what IP address it was assigned
  3. Open my web browser to port 8123 at that IP address
  4. Wait for it to run through its first-boot setup (about 10 minutes)
  5. Create an account for myself
I set up the Zigbee USB adapter (following a YouTUBE video (but beware, there have been some changes since that video was made):
  1. Plug in the Zigbee USB adapter
  2. Log into HA Blue using my account
  3. Enable Advanced mode in my profile
  4. Create user "mqtt" to handle mqtt stuff
  5. From the Add-on store, install Mosquito Broker
  6. Configure Mosquito Broker by adding the mqtt user, and start it
Once the Zigbee adapter was in place, I set up zigbee2mqtt:
  1. In the Add-on store screen, from the "..." menu, select Repository and add the URL for the zigbee2mqtt repository, then find the Zigbee2mqtt Hass.io Add-on near the bottom and select it
  2. Find the USB port the Zigbee adapter is connected to: in Supervisor, System, Host box, three-dot menu, Hardware is a list of devices in /dev; by plugging and unplugging the Zigbee adapter I could see that it shows up as device 1-1.2 with path /dev/bus/usb/001/004 and as /dev/ttyUSB0. Or you can just assume /dev/ttyUSB0.
  3. Edit the configuration on the zigbee2mqtt module and change the default pot from /dev/ttyACM0 to /dev/ttyUSB0, and change the username to mqtt
  4. Start the module
I also set up ssh to simplify future customizations:
  1. Install the Terminal & SSH Add-on and start it
  2. Open the Terminal & SSH Web UI, which is a web terminal, usable as an alternative to ssh
  3. In the Terminal & SSH Config network page, specify port 22
  4. In the Terminal & SSH Config page, add my public key to the authorized_keys array in single quotes
  5. Save, and restart the module
  6. ssh to the HA Blue as root
At this point I rebooted the HA Blue and looked in the Log for each module to make sure it was working properly.

The above description of setting up zigbee2mqtt is condensed, as I actually had a bit of trouble setting it up, including using an old zigbee2mqtt repository that I later replaced with the newer repository URL given above.

Adding Devices

With Zigbee configured on my HA system, I was ready to add my Zigbee switches and outlets.

In order to add a new Zigbee device to the network, the zigbee2mqtt module must be configured to permit devices to join. Initially I was doing this by directly editing the configuration of the zigbee2mqtt module and changing the value of the permit-join attribute to true. Once the new device had been added, I then edited the configuration again and changed permit-join back to false. Later, I discovered I could just use the Web UI for the zigbee2mqtt module and click on the "Permit join" button, which enables permit-join for 255 seconds with a count-down timer, after which it automatically turns it off.

With the HA Blue system beside me, I enabled permit-join. The LED in the Zigbee adapter started flashing green to indicate that it was in permit-join mode.

The first device I attached was a SONOFF SNZB-01 button:
  1. Pry off the back of the button, remove the paper battery insulation sheet, replace the battery and back
  2. Using a paper clip, press and hold the reset button for 5 seconds, until the red light flashes
  3. After a couple more seconds, the tile for Mosquitto Broker shows "1 device and 3 entities"
  4. Click on "1 device" to open a list of devices
  5. Click on the device to open its details page
  6. Click on the pencil icon by the hex name at the top of the page and rename the device and the entity IDs
  7. Press the button, it briefly shows "single" by the "action" line
  8. Double-click, it briefly shows "double" by the "action" line
Yay, my first Zigbee device is working!

I added a few more devices with basically the same process. Sometimes they would join just by enabling permit-join, but sometimes I also had to reset the device. I had some Sonoff devices and some Linkind devices, and I got them all working, although I did have one unexpected hiccup.

I had purchased a few Linkind outlets. The first one successfully joined my network, but the second one did not. After a few tries, I finally looked at the zigbee2mqtt log and saw that there were error messages saying the unit was not supported. (Lesson: if a new device doesn't join right away, look in the log file for errors!) Although the two outlets were sold under the same product name and looked the same, it turned out they had different model numbers: the unit that worked was ZS190000118 and the unit that failed to join was ZS190000108.

In order to add support for this slightly different flavor of Linkind outlet, I found and followed some instructions to support a new device.
  1. ssh into my HA Blue as root
  2. cd to config/zigbee2mqtt
  3. edit the new file ZS190000118.js
  4. In web browser, open https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/devices/linkind.js, look for Linkind ZS190000118, and copy that stanza into my yaml file (this assumed the description was compatible, which turned out to be true)
  5. Change zigbeeModel to ['ZB_ONOFFPlug_D0008'] (from the zigbee2mqtt log)
  6. Change model to 'ZS190000108' (from the zigbee2mqtt log)
  7. Add the rest of the boilerplate as specified in step 2 of the instructions
  8. Write out the new file
  9. Update the zigbee2mqtt config to add the new device: set advanced:log_level: debug (was warn); set external_converters: - ZS190000108.js
  10. Save, Restart

Programming

Once the hardware was all in place and working, the next step was to set up the programming. It looks like there are multiple ways this can be done, and as a programmer I figured it wouldn't be too hard to write some automation code, but then I discovered Node-RED, a graphical editor plugin.

I installed Node-RED from the Community section of the AddOns menu. I had a bit of trouble with the certificate stuff, but eventually got that working. I then created a flow such that when I pressed one of my buttons, it would turn on the pump for two minutes. I spent too much time trying to figure out how to do the whole thing using standard components, but eventually decided the standard components were not quite up to the task. I ended up using a few function components, in which I wrote a bit of Javascript code.

My buttons are connected to the input of the Add Time function component, which adds time to a counter each time a button is pressed, with a max value. The buttons are also wired to an on-outlet component that turns on the recirculating pump.

Here is the Add Time code:
// On Start flow.set("max_count", 120); // 2 minutes flow.set("button_increment", 80); // 1 minute and 20 seconds // On Message max_count = flow.get("max_count"); button_increment = flow.get("button_increment"); c = flow.get("counter") if (c < 0) { c = 0; } c = c + button_increment; if (c > max_count) { c = max_count; } node.status({fill:"blue",shape:"dot",text:"count:"+c}); flow.set("counter", c); return msg;
Once time has been added to the timer, there is another function that counts down to zero, the Count Down function. The input of the Count Down function is connected to a Ticker component that ticks once per second. The output of the Count Down function is connected to an off-outlet component that turns off the recirculating pump.

Here is the Count Down code:
// On Start flow.set("counter", 0) // On Message c = flow.get("counter") c = c - 1 flow.set("counter", c) if (c > 0) { node.status({fill:"green",shape:"dot",text:"count:"+c}); return {payload:{counter:c}}; } else if (c == 0) { node.status({fill:"yellow",shape:"dot",text:"stop"}); return {payload:"stop"}; } else { node.status({fill:"red",shape:"dot",text:"stopped"}); return {payload:"stopped"}; }
This worked well, but I wanted some kind of feedback so I knew when the pump was on. To get that, I added another smart outlet, into which I plugged a guide light. I then added a function component that monitored the state of the pump switch with a state-changed component, such that when the pump outlet turned on or off, the function would turn on or off the outlet with the guide light. The function also set the node status within Home Assistant so I could see on the Node-RED schematic when it was on or off.

Here is the Outlet State code:
// On Start flow.set("counter", 0) // On Message state = msg.payload; if (state == "on") { node.status({fill:"green",shape:"dot",text:"on"}); } else if (state == "off") { node.status({fill:"red",shape:"dot",text:"off"}); } return msg
After getting this all set up, I spent some time testing with different pump-on times and tweaked the values to be just long enough to get the initial hot water to the bathroom sinks. I'm pretty happy with how it is working now.