🔌 Bringing MQTT & HTTP to cheap budget-store remote switches

Summary

This blog post provides some insights on how I managed to bridge over support for MQTT & HTTP to some cheap discount-store remote switches for my repos:

The initial idea

Being very passionate about home automation and always on the lookout for new stuff to connect to the web, my eye quickly fell on these really cheap remote controlled switches available from Dutch/Belgian discount store Action. After some quick researching on the web I found out these so-called 'Homewizard' switches should be able to work with Homebridge (HomeKit node.js bridge/proxy).

It seemed however that this specific type of switch used a derivative of the standard Homewizard app (called Homewizard Lite) and could not be used with this repository. I now had €25 worth of regular RF switches and while I was satisfied with that for a while, I decided to give it another try and see what I could come up with myself.

Decoding Homewizard Lite

Let's dive into how the mobile app interacts with the switches so that they toggle state. Getting the correct request was easy enough: just monitor the network traffic with my iPhone as a target. Homewizard Lite communicates with a REST endpoint in the cloud at https://plug.homewizard.com/… where it sends some data to which in turn toggles the switch. As any good network request should, this call communicated over SSL with the endpoint but that made the work for me a little harder. Initially, I thought that I'd be blocked and had to abandon my idea since I could not decrypt SSL traffic in my monitor and thus would not see the correct request data. After some research on the web, I found out about mitmproxy.

Mitmproxy allowed me to use a man-in-the-middle flow so I could decrypt SSL traffic after installing a self-signed certificate generated by mitmproxy on my iPhone. After this I found out that Homewizard Lite communicated in the following way to list the user's plugs & set a plug's state:

GET https://plug.homewizard.com/plugs # Lists all plugs
POST https://plug.homewizard.com/plugs//devices//action --data {"action": "On"}

To authenticate with the endpoint a token was sent along in the request headers as X-SESSION-TOKEN. This one was a bit more difficult to figure out though. I could just copy over the value from the headers I grabbed in mitmproxy but the token's value changed over time (every few minutes). The token also seemed to go out to a different endpoint (https://cloud.homewizard.com/account/login) to get the value, since in the mobile app you authenticate with an email & password combination.

Using an HTTP Basic Authentication method I tried sending my credentials to that endpoint. I was almost there but the request wasn't resolving. The final piece of the puzzle fell when I found out I needed to hash my password with SHA1 before creating the Basic Authentication auth header and sending it to the token endpoint. Now I finally got back the token I needed to authenticate requests to my plugs without using the mobile app.

A simple REST microservice

With all the logic sorted out and a desk full of notes I quickly created a node express server which would act as a reverse proxy with friendlier endpoints and conventions to reflect changes to Homewizard plugs:

GET localhost:3000/token                    # Returns a new token to use as the session header
GET localhost:3000/plugs                    # Lists all plugs (internet connected ones)
GET localhost:3000/plugs/               # Lists all details about a plug
GET localhost:3000/plugs//location      # Lists the lat-long location of a plug
GET localhost:3000/plugs//isAvailable   # Outputs whether the plug is connected to the internet or not
GET localhost:3000/plugs//devices       # Lists all subdevices (RF switches) associated with the main device
POST localhost:3000/state/ --data {"deviceId": "5fah3…", "action": "on"}

Using a small piece of middleware, each request would first request a new token from /token and then merge it into the request headers so that using it, you wouldn't have to explicitly call the token endpoint before setting state or retrieving the plug list:

const getToken = require(`../routes/token`);

module.exports = async (req, res, next) => {
  const {token} = await getToken();
  req.headers[`x-session-token`] = token;
  next();
};

Using nodemon this ran amazing locally and I later decided this was an ideal use-case to deploy as a microservice in the cloud using Now. Because exposing your home to a cloud microservice might be tricky, a production build of the microservice will always require you to send along an X-PROXY-API-TOKEN in requests to the microservice.

This proxy API was now ready to put up on Github and use in any project (Home Assistant, aRest, Arduino, ESP8266, etc.) that allowed for REST actions. One of the simplest things would be to connect this to an Amazon Dash button for example.

An even simpler MQTT proxy

Since free microservices sleep very often after inactivity I had to look for something else in my own case and found that MQTT is tailored for home automation. I had never used MQTT before but it seemed fairly easy to do, being familiar with Websockets et al. I was stunned about the simplicity of MQTT using the mqtt module from npm. In about an hour I had replicated all the behavior and functionality from my proxy API to an MQTT proxy which just listened to the following topic:

mosquitto_pub -h localhost -t homewizard/switch/id/+/deviceId/+/setState -m on

Integration into Home Assistant

Home Assistant makes MQTT ridiculously easy. They provide an add-on called mosquitto-broker in Hass.io which acts as the broker on your network to publish/subscribe to MQTT topics:

mosquitto_pub -h hassio.local -t homewizard/switch/id/5fah3…/deviceId/dh36kd1…/setState -m on

Wiring up depends on the kind of equipment you have connected to the switches but there are components for light.mqtt, switch.mqtt, sensor.mqtt, etc. Three lines of configuration would allow me to toggle the string lights in my bedroom from Home Assistant:

- platform: mqtt
  name: 'Bedroom Stringlight'
  command_topic: 'homewizard/switch/id/5fah3…/deviceId/dh36kd1…/setState' # the proxy takes care of On|on|ON as action, so no need to set the message explicitly!

Going container-crazy

As a major downside, all this had to run locally which is not cool if you don't have an always-on laptop or computer to run the application as a cron or systemd unit. Creating a systemd unit for it on a spare Raspberry Pi is a possible solution, but instead, I chose (since the nature of the app allowed for it) to quickly create a Docker container, tailored to my Raspberry Pi's armhf architecture, and deploy that container with resinOS. Below is the very, very simple Dockerfile which will let you deploy the mqtt application in minutes:

FROM resin/raspberrypi3-alpine-node:slim

ENV API_URL=https://plug.homewizard.com/plugs
ENV BROKER=192.168.x.x
ENV HOMEWIZARD_PASS=
ENV HOMEWIZARD_USER=

WORKDIR /usr/src/homewizard-smartwares-api-proxy-mqtt
COPY . .

RUN npm install

CMD [ "npm", "run", "dev" ]

Conclusion

This project for me was a very challenging project but oh-so-much fun! I think this is one of the projects where I probably learned one of the most and got triggered by new, useful stuff like resinOS, MQTT & mitmproxy.

And of course, I ended up with a really cheap and cool personal solution to having 'smart lighting' for nearly every light in my room. Building up from here shouldn't even be too hard since I could snag a set of 3 plugs for €9.95 and connect them to my hub.

If you've read all the way through, thanks for that and I hope you learned something or are as eager as I was to deploy this at your home! 👋🏻

Questions? Remarks? Feel free to get me in a loop on Twitter @thibmaek or contact me via email.