So, I was listening to the Hackaday Podcast last week, and Elliot was saying how easy it is to control 433 MHz light switches with these cheap OOK transmitter modules. As I was listening, I realized I already had everything I needed to put this system together. The office lights were already on a 433 MHz switch I use to turn them off when I forget and head up to bed. And somewhere in a box with a dozen other random RF modules were two of those 433 MHz deals.

I decided to try to make this a zero-time hack, too, because I've got other stuff going on at the moment. It ended up taking a couple of hours, but mostly because I did a little experimenting along the way that will probably help with another effort.

Step 1: Capture the Codes

I fired up GQRX with an RTL-SDR dongle and recorded audio of AM-decoded transmissions from the light-control transmitter. They look like this:

I ended up writing python code to trigger on the leading edge of this pattern and average a number of them because I need that functionality for another project. Then, I thresholded the result, and calculated the run lengths of the on and off times. The dwell times are in a 3:1 ratio, so I called them "dit" and "dah" in the code, because the timing reminded me of Morse. Rather than coming up with some fancy encoding for the bitstream, I had the python code dump out in-lined C++ that I could paste right into arduino-land.  It's terribly ugly, but it works, and looks like this:

...   
    digitalWrite(RF_PIN, 1);
    delayMicroseconds(dit);
    digitalWrite(RF_PIN, 0);
    delayMicroseconds(dah);
    digitalWrite(RF_PIN, 1);
    delayMicroseconds(dah);
    digitalWrite(RF_PIN, 0);
    delayMicroseconds(dit);
    digitalWrite(RF_PIN, 1);
...

 But there are many, many more lines :-)

Step 2: Program the Arduino

I found an unused arduino nano around and connected it to the 433 MHz TX module with three jumpers (+5, GND, DATA). The arduino code listens to the serial port. If it receives a '0', it sends the code to turn off the light, while a '1' sends the on-code. Again, I'll just link the code here for anyone interested.

Step 3: Monitor DBUS for Screensaver Status

I wrote some more quick python code to monitor the cinnamon screensaver on my desktop Linux Mint 19 install. This code polls the screensaver status about once a second. If a change in status is detected, it turns the office lights on or off (as appropriate) by sending a "1" or "0" to the serial port where the arduino nano is connected. This is the whole code, but here's a link anyway.

#!/usr/bin/env python3

import dbus
import time
import serial

dev = '/dev/ttyUSB0'
port = serial.Serial(dev, 9600)

old_val = 0
while True:
    bus = dbus.SessionBus()
    screensaver = bus.get_object('org.cinnamon.ScreenSaver',
                                 '/org/cinnamon/ScreenSaver')
    interface = dbus.Interface(screensaver, 'org.cinnamon.ScreenSaver')
    val = interface.GetActive()
    if val != old_val:
        if val:
            port.write(bytes('0', 'utf-8'))
            time.sleep(1)
            port.write(bytes('0', 'utf-8'))
        else:
            port.write(bytes('1', 'utf-8'))
            time.sleep(1)
            port.write(bytes('1', 'utf-8'))
    old_val = val
    time.sleep(1)

I send the on-codes twice because there's no feedback to tell that the lights actually came on, and there are lots of random signals on 433 MHz that could interfere with a single TX. Ideally, I could monitor the light in the room and see if it increased, but that's too complex for right now.

I'll have to set this up to run automatically at start-up, and probably make a few adjustments, but it seems to work OK for now.

Step 4: Clean it all up

Yeah, right. Maybe when it stops working :-)