Washerbot, the Next Gen of Washer Monitoring

Ok, so I built plugmon a while ago. It worked great. I loved it – super reliable, none of the fiddly nonsense I’ve had to work through with my vibration sensor-based dryer monitoring solution even. Sadly, the Etekcity smart plugs I used before, which used the (really nice) VeSync API no longer seem to be able to be purchased, easily at least.

So, what to do? If the code is to be useful long-term, we’ll need to change the platform to something that’s actually able to be purchased. Without question there’s no shortage of smart plugs available. So, what are desirable features that I’m after when looking for a different platform?

Naturally, I’m after some sort of way to easily talk to the plugs to read data. Of course, it also (should at least) goes without saying that we need a plug that offers the ability to monitor power use, as well as exposing some sort of API to allow use to get at that data without using the vendor’s app directly.

There’s a ton of options out there, but eventually, I landed with the Kasa (formerly TP-Link) plugs. Why? Two things really pushed them over the top. First of course was remote API-style access to the plug’s power monitoring data. But with the Kasa plugs one didn’t even need to go outside the home LAN to capture the data.

At the end of things, I kept the majority of code from jcostom/plugmon, grabbed a few bits of code from other projects I’ve worked on, and in about 30 minutes, the Washerbot was born.

It’s a shame that the VeSync plugs are now so difficult (impossible?) to come by. The API was reasonably easy to work with, and they weren’t terribly expensive either. I’m hopeful that TP-Link / Kasa Smart will be around for longer. I really like the “no outside connectivity” needed part of the python-kasa module as well.

Some may point out that there was a brief dust-up a couple of years ago with TP-Link, when they announced their intention to stop allowing local access to their devices, and you’d be right to do so. Fortunately, TP-Link was smart enough to take the not-so-subtle hints from the community, and walked that change back.

Without further ado, head over to GitHub and check out the new Washerbot code & container. Obviously, you’ll need one of the TP-Link plugs that provides energy use stats, like the KP115.

The Dryer Update…

[Any Amazon Links below are Non-Affiliate Links that just go to Amazon Smile]

So, if you think back a bit, you may recall that I was using a Pi 4 for my IoT project that monitored the dryer, shooting out Telegram group messages to the whole family when the dryer was done with the laundry.

Times being what they are, it’s pretty difficult to come by a new Raspberry Pi these days, as I’m sure many of you know. I needed the power of the Pi 4 for something else, at least on a temporary basis. Meanwhile, back at the ranch, a couple of months prior, I’d received a ping from the Micro Center about 45 minutes away informing me that they had a handful of Pi Zero 2 W’s on hand. Those little suckers are super hard to find, so I snapped up my max of 2, along with the GPU I’d been dying to lay hands on for the longest time. For those who care, I finally got an EVGA 3080. Pandemics and supply-chain constraint conditions suck, by the way, in case you were wondering my position on that issue.

So, having my Pi Zero 2 W in the drawer ready to roll, I unscrewed the box from the way that housed the Pi 4, fitted the sensor I had directly onto the Pi Zero 2 W, and scaled down from a 2-project-box solution down to 1 box. Sadly, it sucked. But, it wasn’t the hardware’s fault. In reality it was totally a self-inflicted condition.

I modified (slightly) the pins on the old 801s sensor I had, fitted it onto that new Pi Zero 2W (since it didn’t have any GPIO pin headers soldered on), and sort of Rube-Goldberged it together using 3M VHB tape inside the project box. Total hack job. I thought about using a bunch of hot glue, but then I thought better of it. Why not solder? Honestly? I suck at soldering. One of these days I’ll get around to getting good at it. But that’s not today.

It was wildly unstable. The sensor kept on moving, losing contact with the side of the GPIO holes, it was awful. I all but gave up. I had a brief flirtation with the Aqara Smart Hub and one of their Zigbee Vibration sensors, and believe me, when I say brief, I mean like 12 hours. It just wasn’t fit for the job.

My grand plan with that was to mimic what I was doing over on the washer – write some Python code and run it in a container to query an API somewhere in the cloud every X seconds to see if the thing was vibrating or not, then based on that, work out the state of the dryer to determine if the dryer had started or stopped and then act accordingly. But alas, since step 2 in this plan was a klunker, steps 3 through infinity? Yeah, those never happened.

So, back to the drawing board. I found that I couldn’t easily lay hands on a new 801s again, and the project for the Pi4 was now finished, so I had that back. I did find a new vibe sensor – the SW-420. 3 pins instead of 4, but it’s still a digital output that works fine with the Pi, and my existing code worked as-is, so who cares, right? Yeah, I classed the thing up quite a bit more this time too. This time, instead of shoving the Pi inside a project box that’s mounted on the wall running from the SD card, I opted to run in one of those snazzy Argon One M.2 SSD cases booting Ubuntu 22.04 from an M.2 SSD in the basement of the case. I’ve got that sitting on a lovely little shelf mounted just above and behind the dryer, with my 3 GPIO leads running out of the top of the case, directly into the small project box that’s attached to the front of the dryer, inside which is the sensor, which is stuck to the inside of the box using 3M VHB tape. The box itself is stuck to the dryer using VHB tape as well.

In the end, all’s well that ends well. I’ve had to do a good bit more tuning on the SW-420 sensor. It’s been a bit more fiddly than the old 801s was. That one was definitely a plug and play affair. This has required a bit of adjustment on the little potentiometer that’s built into the sensor. Not too bad though. I’ve invested probably a total of 15 minutes of time standing next to the dryer, staring at telemetry, while the dryer is running, or not. But in the end, it’s all working, and the notifications are happening once again.

Ditching Technical Debt. Embracing Simplicity.

I work in networking. I’ve been doing that for a long time now. Along that journey, I’ve also had occasional detours into worlds like generic IT and data security as well. I also do volunteer work at a nonprofit. Plus, like many of you who work in tech, there’s stuff that lives at the home(s) of relatives that you maintain because you’re that sort of person.

Sometimes, you do it cheap, sometimes you do it right, and sometimes you do it somewhere in-between. Like where you’ve got DHCP-assigned WAN interfaces everywhere because everywhere has home-user type Internet services, or less-expensive business-class occasionally. Anyhow, you can’t always count on having the same IP in the same place twice. BUT, you want things to be secured, and you don’t just want wide-open port forwards with plain old Dynamic DNS.

How things used to work, in the IPsec days…

You’ve got some Juniper SRX firewalls you’ve bought for lab work & study previously, you want to make use of them with IPsec VPNs, but to do it right, you really need static IPs. So, what do you do? You fake it. You just pretend you’ve got static IPs on the tunnel endpoints and configure it up. The tunnels come up, you post up your BGP sessions between your st0.0 IFLs, announce some routes, put some reasonable security policies in place. Yes, I did have security policies in there. I was born at night, but it wasn’t last night, guys. But how did I keep it working with IPs changing all the time?

Here’s how I was solving that problem up until fairly recently. I’ve been hacking away at my DNS-o-Matic and DNS Made Easy updaters for a while now. The DME updater was much better, IMHO, as it directly updated a single, private zone that only I ever cared about rather than rely on someone else to sit in the middle and do the updates for me. Plus, I wrote the whole thing from the ground up using DME’s API docs, so I knew exactly how it worked, inside & out. No excuses for it doing anything I didn’t understand, and honestly, I’m really happy with how well it’s been working. It’s been a great opportunity to get better at Python, in particular doing things in a more “Pythonic” way, rather than trying to “just get it done”, or worse, trying to make it work the way I used to do things in Perl or PHP years ago. Is it iconic? Not even close, but it does works pretty darn well.

So, with these containers all ran on Intel NUCs under Ubuntu Linux at each site. There were 1 more container on each of these NUCs as part of this operation. I had a set of Telegram Bots that talked to each other as part of this network to inform each other of site IP changes. So, if HOME changed its IP, the bot at HOME sent a message to the group that included the bots for NONPROFIT and INLAWS. Those bots saw note that the IP had changed and they should go find out the new IP of HOME, so they can update their tunnel endpoints. This in turn fired off a function that used the Junos PyEZ API module to update the IPsec tunnel endpoint IPs.

Did it all work? Yes, believe it or not, this actually all worked. Was it pretty fragile and not for the faint of heart? Oh yeah, for sure. Would I recommend doing it? Not a chance. So much so that I’m not even going to share the code, apart from the DDNS updaters. The other stuff is definitely hackjob territory. So, since it was so fragile and had the tendency to break, what did I do? Well, the first few times, I drove and fixed. Which frankly, sucked. After that, I installed an OpenVPN container at each of the locations. Later, I replaced those with linuxserver/wireguard containers. But, after it all broke like twice in about a month, I’d just about had enough. I cried Uncle and decided I was going to look for some other way to do this.

And that’s when my old pal Bhupen mentioned Tailscale to me. I was already into Wireguard. So making it easier, faster, and more useful were all on my short list. Drop the tailscale client on the NUC, get it logged in announcing the local subnet into the tailnet (their name for the VPN instance), making it a “subnet router”, approve the route announcement in the portal and it’s going. I’ve got control over key expiry too. Security policy (naturally) moved from the SRX down to the tailscale gateways, but their ACL language wasn’t too difficult to wrangle. It’s all JSON, so it’s reasonably straightforward.

The new Tailscale VPNs
The new Tailscale VPNs

So, with all the scripts gone and the IPsec stripped away, what’s it all look like? Well, we also added 1 more site into the mix as well – the in-laws vacation place. They bought a place and I stuck a Raspberry Pi up there for future IOT use. Not entirely sure about the “what” yet, but they just updated the HVAC, and it’s all smart stuff, so I expect there will be instrumentation. Maybe something that spits out time series info to Influxdb or somesuch. Who knows? Or Perhaps HomeKit/Homebridge stuff. Time will tell.

In the time since I made the diagrams and wrote this up, things have also changed slightly on the homefront.. I’ve deployed a 2nd subnet router at Home. In the Tailscale docs, they say all over the place not to deploy two subnet routers with the same IP space, and generally speaking, it’s with good reason – traffic destined for those prefixes announced by those routers will be round-robin’d back and forth between them. In my case, since they’re on the same physical subnet, this is essentially ECMP routing, so no big deal. I haven’t validated if they’re really getting the hashing correct, but haven’t really noticed any ill effects yet, so I haven’t shut off the 2nd subnet router yet.

So, by dropping all the BGP sessions, IPsec tunnels, Python scripts, Telegram bots, and Docker containers, things have become much simpler, and much more stable. I’m really happy with Tailscale. So much so that I ended up subscribing at the Personal Pro tier. Great bunch of folks – can’t help but recommend them.

UPDATE: This ended up sparking a bunch of sidebar conversations. Go read what I had to say as a followup

A Journey To A Smarter Dryer

This one was much more difficult. A lot more difficult.

If you recall from my post about the washer, I was able to pull off some fairly useful stuff without a ton of effort. Read a smart plug’s API to see how much power the washer is using to figure out when it turned on, then wait for it to turn off again, then let the fam know that the washer finished, and go take action so that the laundry doesn’t sit around for days, get funky and need to get re-washed. This was of course pretty easy simply because we were able to rely on the fact that the 120V motor in the washer draws well under 15A, the top end of the smart plugs I’m using, the Etekcity ESW15.

Sadly, when we moved into our house, we had an electric dryer. We’ve got natural gas in the house. Heck, in the same room even for the furnace and water heater even. But, back when the last washer sprang a leak and we needed a new washer in a hurry, and unfortunately at the time it was going to be months to get the matching gas dryer back in stock, so we just punted and stuck with the electric model. Sadly, this means for us this means we can’t take the same approach we did with the washer, since nobody makes a smart plug that works on 240V AC 30A circuits.

Unwilling to settle for relying on setting timers with Alexa, having to remind the kids to set timers, or just plain forgetting to do it, I started Googling about, looking for ways to go about monitoring the dryer. Monitoring energy use is the natural fit. When the dryer is in use, it’s consuming loads of energy, and when the clothes are dry, the energy use falls right off. This really shouldn’t be that hard to figure out, right? Right? Sadly, it was.

My next move was to play around with a split core current transformer clamp, and build a circuit with a burden resistor, reading the thing with a microcontroller. I read about the whole process in a handful of places online and it didn’t seem to ridiculous to build the circuit, so I sourced the parts. I got a little breadboard, some jumper wires, the resistors, capacitors, and the CT sensor clamp, and a sacrificial extension cord, which I’d use for my proof of concept test. You see, the CT clamp goes around a single conductor, not the whole cable assembly, so I needed to modify the cable slightly. Relax, the real cable was one of those “flat, side-by-side” types, so it would only mean peeling them apart, not really cutting anything. Sadly, I never made it to that phase. During my POC phase, I was able to get readings back from the sensor, but they never made sense. I was using an ESP32 microcontroller with MicroPython, so maybe that’s related. Or maybe I had a bum CT clamp. Or something else was wrong. We’ll never know, since I gave up after several evenings of bashing my head against the desk.

Failing at the “point” solution of energy monitoring, I moved on to looking at whole-house power monitoring. Hey, if we can’t kill this fly with a newspaper, let’s try a missile, right? Sense landed at the top of the pile. It had the API I was after, though they sort of keep that on the DL. Not in love with that, since those sometimes disappear. If I’m going to drop a couple of hundred bucks on something to use the API for something, it better not just disappear on a whim someday. Plus, our panel is in my home office, recessed in the wall, and there’s not exactly a clean way to get the Sense WiFi antenna back out without it looking really weird. I could make it clean, but then there’d just be a random RP-SMA antenna sticking out of my wall. Interesting decor choice. Sure to be a selling point when we sell the house some day.

Which brings me to the vibration sensor. I was reading one day, still searching, and I came across Shmoopty, and my problems were (half) solved. Sure, I had a Pi Zero W already laying around and I could have just built exactly what he had done, but what’s the fun in that? Remember, I’m already invested. It’s overkill time. So, I ordered up a couple of those 801s vibration sensors and got to work. You know, it was surprisingly hard to get one that met my needs at the time. Why? Most of the 801s units out there are analog-only. Since I’m using a Raspberry Pi, I wanted a digital output, so I didn’t need to mess around with the need for extra ADC (Analog to Digital Conversion) circuitry, just to read a simple sensor. So, I had to order from AliExpress and wait the long wait for shipping from China.

After my sensors finally turned up, I worked out the arrangement of my project boxes and so forth in the laundry room. I landed on a wall-mounted box for the Pi with a 1m pair of wires connected to the sensor, with the sensor inside another small box, which is stuck to the top of the dryer using a little strip of 3M VHB tape. Shmoopty’s Python made it easy to figure out how to read the sensor, so I was happy to be able to draw my inspiration from that. His approach is to keep it small, run on a Pi Zero W, even make it renter-friendly, while mine is more of a “go big” approach – building a Docker container to run it inside of.

Well, at the end of it all, it shares a lot of common philosophy with the plugmon tool, in that it loops infinitely, looking for start and stop conditions. Instead of watching power consumption, it’s watching for the dryer to start vibrating a lot. When that starts an appreciable amount of time, the dryer is declared to be “on”. Once it transitions back to “off”, it fires an event that causes a Telegram message to get sent to the family group chat, again, much like when the washer finishes!

Well, if you’ve made it this far, you’re ready to go check it out. So, get going and get reading. Smarten up that laundry room, report back what you did, and how you did it!

The Robots are Speaking…

So many applications and processes involve notifications. As we begin to automate processes, it becomes increasingly important to know if tasks we’ve initiated succeeded or not. That’s where automated notifications come into play. In my case, often times, these take the form of chatbots that send messages to let me know that something happened, or that a process either succeeded or failed.

I’ve settled on Telegram as my platform of choice for this. What’s attractive about Telegram? It’s available for a wide variety of platforms – iOS/iPadOS, Android, macOS, Linux, Windows, there’s even a web client.

So, how does one create a bot? You have a conversation with the BotFather. BotFather is a Telegram bot that creates bots (how meta, right?) for you. The bot walks you through the whole process. When the process is complete, you’re given a token – this is a secret value that serves as the “keys” to the bot. Once you’re done this process, you’re almost ready to start using the bot in your automations. Next, you start having a chat with the bot, just like you would with another human. Send the /start command to the bot to start it up. Once you’ve done that, you want to figure out the chat_id for that chat. This is pretty straight forward to do, fortunately.

To get the chat_id value for a private chat with your bot, you visit this URL:

https://api.telegram.org/bot[your-bot-token]/getUpdates

Want to use the bot to notify multiple people? Create a Group Chat and add the bot to it. Send a message to the group and reload that URL up above. Check the updates for the Group Chat messages, and look for the chat_id for the Group Chat in there. How can you tell the difference between a regular chat with the bot vs a Group Chat that the bot belongs to? Easy. All chat_id values are 32-bit integers, but Group Chats ID values are negative, while individual chats are positive. Easy, right?

Ok, so you’ve got the Token, and a chat_id value. It’s time to put those bits to work. Here’s an example, in Python, using the python-telegram-bot module, which you can install using pip install python-telegram-bot.

#!/usr/bin/env python3

import telegram

myToken = '123456789:ABC-123456-foobarbaz9876543210'
chatID = -123456789

bot = telegram.Bot(token=myToken)
bot.sendMessage(chat_id=chatID, text="Hello World!")

This is just one of many ways possible to send notifications. Find the one that works best for you and your workflows and go with it! If you want to take a shot at using Telegram, give their docs a read before you start.