IoT devices can have a little cloud, as a treat
In this post:
Let’s see…where did I leave off in part 1? A local web interface for controlling a smart plug. No more useful than a simple IR remote-controlled plug, but so much more potential.
Unification⌗
The first step to making this more useful is controlling all 4 of my plugs from one interface. I considered writing an app (or webapp) that would connect directly to the plugs and present a list of their current state with on/off buttons, but when I’m already running a configured Home Assistant instance, that seems like unnecessary work.
If you recall from the previous post, I flashed Tasmota onto these plugs. Its primary method interfacing with other devices and applications is the MQTT protocol. This protocol requires a central server (“broker”) that all of the end devices connect to. These devices can publish and/or subscribe to data streams called “topics”. Other services, such as Home Assistant, can then read these messages, process them, and send messages. Conveniently enough, I was already running a broker (Mosquitto) alongside Home Assistant because of a previous (now-abandoned) project. [Note to self: write that one up later.]
In particular, the Tasmota firmware presents a few relevant topics:
I am using <id>
as a placeholder for the name assigned to the device during configuration so that they can be controlled individually.
stat/<id>/RESULT
- where the device publishes the results of running commands sent to itcmnd/<id>/POWER
- the device will switch the relay on or off depending on which message is sent here (“on” or “off”, intuitively)tele/<id>/LWT
- the device periodically sends messages about its status here, allowing subscribers to determine whether it is online and ready
First, I had to do was configure the plugs with the local IP address of the machine running Mosquitto and Home Assistant. Below I have copied out the part of my Home Assistant configuration files that registers one smart plug as a switch and then documented each line. The switch can turn the plug on or off when a UI toggle is activated, a script is run, or anything else:
switch:
- platform: mqtt
name: "Plug Name Here"
# Where HA should listen for success/failure messages
state_topic: "stat/<id>/RESULT"
# What data HA should pull from the RESULT message to indicate the current state
value_template: "{{ value_json.POWER }}"
# Where HA should send on/off commands
command_topic: "cmnd/<id>/POWER"
# Where HA should listen for ping messages
availability_topic: "tele/<id>/LWT"
# String in LWT that indicates the device is up
payload_available: "Online"
# String in LWT that indicates the device is down
payload_not_available: "Offline"
# Deliver each message at least once, but more if necessary
# 0 = 0-1 times (failures will be ignored...think of UDP)
# 1 = 1+ times (resend until there is an acknowledgement)
# 2 = 1 time (back-and-forth acknowledgement...think of TCP)
qos: 1
Now, Home Assistant will subscribe to the availability_topic
and state_topic
at startup and publish messages to the command_topic
when it wants to turn the plug on or off. Cool, and satisfyingly clicky when you’re using a mouse and the plug has a loud relay.
Back to the cloud⌗
That’s great and all, but what if I want to control these devices with Google Home instead of Home Assistant? And by that, I mean I wanted to do that, and I needed to figure out how.
This does require that Home Assistant is facing the big, scary unknown that is the internet in order for Google to reach it, but I have it locked down enough that I still feel better about doing that than allowing Tuya (or whoever else) to have devices on my LAN.
Luckily for me, Home Assistant already has a Google Assistant integration. They even provide the service for you if you have a Home Assistant Cloud subscription, but I don’t, so I had to make my own app in the Google developer console.
I’ll skip the setup because it’s long, boring, and covered in that link. However, I will note that I initially got an error in the Google Home app after authenticating with my Home Assistant service. It turns out that I had my Home Assistant instance behind Cloudflare, which was configured to block bots. I made an exception for Google and then it worked perfectly.
Anyway, I added the following to my configuration, restarted Home Assistant, and then refreshed my Google Home app.
google_assistant:
project_id: ...
service_account: ...
report_state: true
expose_by_default: false
entity_config:
switch.plug_name_here:
expose: true
It immediately showed the plug, and better yet, I could use my Google Home device to control it with voice commands. As anticlimactic as it is to have the exact same functionality as before all this work, it’s nice to know that I don’t have cheap devices with unknown firmware connected to my home network and talking to a cloud that I don’t even know who owns at this point.
Future-proofing⌗
This is all fine as long as the devices are on the same network as the machine running Mosquitto and Home Assistant. However, I knew that at some point in the near future, I would be moving again, and the server would be staying here alone. How could I still control these plugs?
Simple: open Mosquitto up to the internet alongside Home Assistant. This means the plugs can send MQTT messages to it from a different house, and Home Assistant can then communicate with Google just the same as before.
This does complicate things slightly because it’s probably best to require devices to authenticate when connecting to Mosquitto. That’s not hard to do, either. I just created a Mosquitto config file with the following contents:
listener 1883
allow_anonymous false
password_file mosquitto.passwd
Then I created a unique username and password for each of the plugs (probably unnecessary, but it felt right), used the bundled mosquitto_passwd
utility to hash them into my mosquitto.passwd
file, and configured each plug to use its new account through their Tasmota web UI.
Oh no, Home Assistant isn’t happy! I forgot to create an account for it. I generated one more username/password pair and added it to my Home Assistant configuration with this:
mqtt:
broker: <hostname of broker here...localhost would also work for me>
port: 1883
username: ...
password: ...
And now I can control my cloud-free devices using the cloud. But I don’t have to.
Stay tuned for my next (and probably final) post about Tastmota where I complicate my MQTT configuration by controlling an RGB light bulb.