HomeKit Air Purifier

I’ve been on a bit of a home automation kick lately, and I’ve been using it to learn new things.

I recently bought an air purifier, and it seemed ripe for the hacking, considering it had 4 functions.

  1. Low Fan Speed

  2. Medium Fan Speed

  3. High Fan Speed

  4. UV Filter

I had a Raspberry Pi Zero and a 4 channel relay laying around that I meant to do something with, but never did. I figured this would be the perfect project.

Goal:

Control my air purifier via HomeBridge in the Home app on my iPhone.

Materials:

Raspberry Pi Zero

4 Channel Relay

Air Purifier

What I Did:

First I needed to figure out how the air purifier works. So I took it apart.

Inside it was very simple, I don’t know what I’m doing but there’s a switch that bridges power to 3 different connections on what looked like a transformer inside the case. When I touched the connections to each other, the fan went low, med and high. Easy. I wired the transformer up to the relay and bridged the connection across all the relays.

The UV filter had a separate board in it with an LED in it, I removed the board because I wouldn’t be needing it anymore and I put the connections into the final relay on the board.

LED board for UV purification, and the fan speed switch in the back.

LED board for UV purification, and the fan speed switch in the back.

All cozy in the air purifier.

All cozy in the air purifier.

Here’s a really awful schematic of how I wired everything up.

Here’s a really awful schematic of how I wired everything up.

Controlling the relay:

At this point I needed to get the relay to control the Air Purifier so I wrote a simple Python script.

# gpio_setup.py
# Sets rules for the GPIO Pins controlling the Air Purifier
import wiringpi
import time

wiringpi.wiringPiSetup()

# Set all pins as outputs
wiringpi.pinMode(25, 1) # High Fan Speed relay pin
wiringpi.pinMode(24, 1) # Low Fan Speed relay pin
wiringpi.pinMode(23, 1) # Medium Fan Speed relay pin

# Gets the value of the GPIO pin (0 is enabled, 1 is disabled)
fan_speed_high = wiringpi.digitalRead(25)
fan_speed_low = wiringpi.digitalRead(24)
fan_speed_med = wiringpi.digitalRead(23)

def disable_function(mode):
    if mode == 'low':
        wiringpi.digitalWrite(24, 1)
        print("low disabled")
    elif mode == 'med':
        wiringpi.digitalWrite(23, 1)
        print("med disabled")
    elif mode == 'high':
        wiringpi.digitalWrite(25, 1)
        print("high disabled")
    else:
        print('invalid function passed')

while True:
    if fan_speed_high == 0: # checks to see if the fan speed is set to high
        disable_function('low')
        disable_function('med')
        print("Fan speed high enabled.")
    elif fan_speed_med == 0:
        disable_function('low')
        disable_function('high')
        print("Fan speed med enabled.")
    elif fan_speed_low == 0:
        disable_function('med')
        disable_function('high')
        print("Fan speed low enabled.")
    time.sleep(1)

This script worked. Kind of. The issue was that low, med and high were never meant to be turned on together, so I tried to write some logic in the script that made sure they were never turned on at the same time but it didn’t work fully. I needed a way to get the state of each pin at the same time and compare it to the previous state it was in to flip the pin that was on prior to the change.

With the help of my friend Todd, and a couple whiteboards he wrote a script that worked.

# gpio_setup.py
# Sets rules for the GPIO Pins controlling the Air Purifier
import wiringpi
import time

wiringpi.wiringPiSetup()

# Set all pins as outputs
wiringpi.pinMode(25, 1) # High Fan Speed relay pin
wiringpi.pinMode(24, 1) # Low Fan Speed relay pin
wiringpi.pinMode(23, 1) # Medium Fan Speed relay pin

# Gets the value of the GPIO pin (0 is enabled, 1 is disabled)
fan_speed_high = wiringpi.digitalRead(25)
fan_speed_low = wiringpi.digitalRead(24)
fan_speed_med = wiringpi.digitalRead(23)

pins = [23, 24, 25]

def get_fan_state():
    pin_state = []
    for pin in pins:
        x = wiringpi.digitalRead(pin)
        pin_state.append(x)
    return pin_state


def what_changed(previous_state, new_state):
    for i in range(len(new_state)):
        if abs(previous_state[i] - new_state[i]) == 1:
            new_state[i] = 0
        else:
            new_state[i] = 1
    for pin, i in zip(pins, new_state):
         wiringpi.digitalWrite(pin, i)
    monitor_pins(new_state)

def monitor_pins(fan_state):
    while True:
        if fan_state == get_fan_state():
            print('nothing changed; Fan ', fan_state)
            print(fan_state)
            time.sleep(2)
        elif fan_state is not get_fan_state():
            print('state changed!')
            what_changed(fan_state, get_fan_state())
            time.sleep(2)
monitor_pins(get_fan_state())

Now when the state of the fan speed was changed, the script rolled back the last state and enabled the most recent speed that was selected. Success.

Getting it into HomeBridge:

Now that all the hard work was done, I had to find a plugin for HomeBridge that could interface with GPIO pins. I also had to figure out how to get HomeBridge running on the RPI Zero W.

I found this online - http://theonlysiteever.com/setting-up-a-raspberry-pi-zero-w-for-homebridge

I was able to get HomeBridge on the RPI Zero W successfully following this guide, but the plugin I found didn’t work.

https://www.npmjs.com/package/homebridge-gpio-wpi2 - homebridge-gpio-wpi2

It wasn’t working because the version of Node that was installed was too old, and didn’t work with the script. With a massive trial and error session, I got everything working by fully uninstalling all the plugins including HomeBridge, and installing Node this way.

wget -O - https://raw.githubusercontent.com/sdesalas/node-pi-zero/master/install-node-v11.5.0.sh | bash

Thanks to sdesalas on GitHub. This simplified the process immensely. After all this I had a working HomeBridge install, and the homebridge-gpio-wpi2 plugin running on the RPI.

Home App screenshot