Switch
Mountain Lizard IconMountain Lizard Icon PicoW Smart Switch
mountain-lizard
Making a smart WiFi switch with a Pico W. How to use MQTT to communicate with Home Assistant to trigger scenes and display state.

Optionally, you can expose devices directly to HomeKit using Homebridge.

Set up Homebridge

We’ll install Homebridge in docker, running it as a container.

I followed the instructions here, however note that docker now includes Docker Compose support, so rather than installing Docker Compose separately and running docker-compose in the terminal, you can now just use docker compose with the same parameters.

In summary the process is:

Create a folder on the Pi, e.g. homebridge, and create a docker-compose.yml file in it:

mkdir ~/homebridge
cd ~/homebridge
nano docker-compose.yml

Now paste the following configuration:

version: "2"
services:
  homebridge:
    image: oznu/homebridge:latest
    restart: always
    network_mode: host
    volumes:
      - ./volumes/homebridge:/homebridge
    logging:
      driver: json-file
      options:
        max-size: "10mb"
        max-file: "1"

ctrl+o to save, accept the docker-compose.yml filename, then ctrl+x to exit.

Bring up the Homebridge container with docker compose:

docker compose up -d

Log into the container, at http://<ip address of your server>:8581, you can also use hostname.local if you’ve set up a host name and mdns on the Pi.

Set up your username and password, then use the displayed QR code to add the homebridge instance as a hub/bridge to your Home. You may need to accept a dialog saying the device is not certified.

Publish to MQTT from Pico W

It’s probably easiest to use Thonny for this, since it lets us install packages.

I followed these instructions, however I found Thonny failed to install the umqtt package, giving an error about finding a .tar.gz file, so I instead just downloaded the file it was trying to access and extracted it, yielding a few folders where the only interesting one seemed to be simple.py.

So I set up a project in VS Code, by:

  1. Making a blank directory and opening in VS Code.

  2. Installing the Pico-W-Go extension

  3. Running Pico-W-Go > Configure Project command to configure project.

  4. Connecting the Pico W.

  5. Running Pico-W-Go > Connect to connect via serial - this should give a >>> prompt where you can run e.g. print("Hello") to check the board is running.

  6. Creating an example script in flash.py:

    from machine import Pin
    from time import sleep
    
    pin = Pin("LED", Pin.OUT)
    
    while True:
        pin.toggle()
        sleep(1)
    
  7. Running this script using Pico-W-Go > Run current file - this should cause the LED on the Pico W to flash on for a second, then off for a second, repeatedly.

Now we need to install the package via upip (note this is apparently being updated to mip (docs), but the Micropython version from pimoroni seems to have only upip).

Start up the board and run Pico-W-Go > Connect. At the >>> REPL prompt, run:

import network
import upip
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect(ssid = 'YOUR_WIFI_SSID', key = 'YOUR_WIFI_PASSWORD')
# If the installation fails, try the line below again. The line
# above should block until wifi is connected, or connection fails,
# so you might need to repeat that as well.
upip.install('umqtt.simple')

This should display something like:

Installing to: /lib/
Warning: micropython.org SSL certificate is not validated
Installing umqtt.simple 1.3.4 from https://micropython.org/pi/umqtt.simple/umqtt.simple-1.3.4.tar.gz

You can now use from umqtt.simple import MQTTClient to import the client as expected.

Now, we can use Pico-W-Go > Download project to retrieve the contents of the board (note download retrieves files from the board, upload sends to the board). If we do this we can see that the upip install has put the simple.py file we saw in the tar.gz earlier into lib/umqtt, so we can if wanted keep this file in our project to be uploaded alongside the rest of the code, avoiding the need for upip unless we need to update the library.

Continuing on in the Tom’s Hardware process works fine - however note that the subscription should just have a single call to client.subscribe(topic_sub) then one call to client.check_msg() per pass of the main loop (this is a non-blocking poll for messages).

Configuring homebridge-mqttthing

Log in to your Homebridge, then install homebridge-mqttthing by going to the plugins tab and searching for “homebridge-mqttthing”, then installing.

In the configuration UI, don’t worry too much about the settings, it’s easier to just sort out in json. Still in the plugins tab, click the wrench icon in the “Homebridge Mqttthing” card, and select “JSON Config”. Then enter the following, adapting the URL to the host (or ip) running Mosquitto (i.e. your Pi):

{
  "accessory": "mqttthing",
  "type": "statelessProgrammableSwitch",
  "name": "Switchy",
  "url": "cyberdeck.local:1883",
  "topics": {
    "getSwitch": [
      "switchy-a",
      "switchy-b",
      "switchy-c",
      "switchy-d",
      "switchy-e"
    ]
  },
  "restrictSwitchValues": [0]
}

Click “SAVE” in the editor, and you should be prompted to restart homebridge, do this by clicking the “power” icon near the top right.

When homebridge has restarted, I needed to remove it and re-add it to homekit to detect the switch. TODO: Seems like there must be a quicker way, although it’s not too hard to do.

Now you should be able to find the switch in Home, and set e.g. scenes to button presses.

Now we can look at adding in a “lightbulb” to control the screen backlight, add a new mqttthing accessory:

{
  "accessory": "mqttthing",
  "type": "lightbulb",
  "name": "bulby",
  "url": "cyberdeck.local:1883",
  "topics": {
    "getOn": "bulby-get-on",
    "setOn": "bulby-set-on",
    "getRGB": "bulby-get-rgb",
    "setRGB": "bulby-set-rgb"
  }
}