Controlling Insteon Devices with Linux and Python

Having received some Insteon 2477D Dimmer Switches from my Father-in-Law for Christmas I immediately knew I had to figure out a way to interface them with Linux to incorporate into the rest of my homebrew home automation system. After doing some googling around I found a few sites with enough bits of information to get me started.

First I knew I needed a controller, a device to interface between my Linux computer and the Dual-Band (RF and Powerline) switches. Research brought me to purchase the 2413S Powerlinc Modem. This is a serial device that allows two way communication through the serial port on your computer, there is also a USB version (2413U) as well but I chose the serial as I was sure I would have no issues interfacing programmatically to a serial port. Concerning the 2413U I did see a review from a purchaser who commented that it worked fine in Linux as it was simply the 2413S with a Serial-to-USB adapter built in. To be safe though, I went with the tried and true serial port, though this guide will probably work for the USB version as well with slight modification (alternatively to buying the USB version a Serial-to-USB adapter can be used with the 2413S to connect it a USB port). This information may also work with other Powerlinc Modem versions but there is no promises made, also this will most likely NOT work with any Powerlinc Controllers, as those devices are fish of a different color.

I will note that the Powerlinc 2413S is not the latest controller device from Insteon, rather the Insteon Hub is their new flagship controller. The reason why I did not purchase the Hub is it incorporates the controller and the interface software. This means that all interfacing to control the Insteon devices is done via the Hub, rather than a seperate controller (like a computer) which is I wanted since I am using a Linux server to control everything else in my home. Though I did find some articles on interfacing with the Hub via HTTP calls, I just worry that Insteon will someday kill this via encryption with a firmware update.

After receiving my 2413S Powerlinc Modem (hence forth the PLM) and replacings the switches of the lights I wanted to control with the Insteon 2477D switches, I plugged the PLM into the wall and to the serial port of my Dell Poweredge 2950 and started hacking away.

Now being an Insteon Noob, I was unaware that the devices needed to be linked prior to controlling them. If they are not linked you will still get responses from the devices to the PLM, even acknowledgements of commands, but the switch will not turn on or off. The linking process involves holding down the button on the switch (or other Insteon device) for 3 seconds, it will then beep and the lights will change, then hold down the button on the PLM for 3 seconds, both devices will beep and the lights will return to normal. Repeat this starting with the controller first. Now the devices are linked both ways, meaning that the PLM can send commands to the switch (or other Insteon device) and the switch can send status information to the PLM, i.e. when someone manually turns on/off the switch it can send PLM that status information.

Before we get into the nuts and bolts of scripting the switches, lets first look at what an Insteon command/response looks like. A standard command is 8 bytes long (0 through 7), each byte represented by two hexadecimal characters. A response is 9 bytes long, again each byte is two hexadecimal characters. If you do not know what hexadecimal is, its not critical for you to understand to be able to follow this post, but will be helpful so I encourage you to go read up.

If we break down the pieces we will be sending in a standard command, it will look something like this:

Byte 0: 0x02, representing the decimal number 2, which is the start for all Insteon commands.

Byte 1: 0x62, representing the decimal number 98, which is the signal for a standard command.
Byte 2: 0xXX, where XX is a range from 00 to FF representing the upper address of your Insteon device, these addresses are printed on the devices in the format XX.XX.XX
Byte 3: 0xXX, where XX is a range from 00 to FF representing the middle address of your Insteon device, these addresses are printed on the devices in the format XX.XX.XX
Byte 4: 0xXX, where XX is a range from 00 to FF representing the lower address of your Insteon device, these addresses are printed on the devices in the format XX.XX.XX
Byte 5: 0x0F, representing the decimal number 15, which is a standard Flag, more on Flags later
Byte 6: 0xXX, where XX is a range from 00 to FF representing the First part of the command, this represents is the actual command, like on, off, faston, fastoff, dim, etc
Byte 7: 0xXX, where XX is a range from 00 to FF representing the Second part of the command, this represents the value of the command, for instance for the on command, you must pass a value from 00 to FF to represent the percentage to turn on (for a dimming switch) where 00 (decimal 0) is 0% and FF (decimal 255) is 100%. For instance if you wanted to turn on the light to 50% brightness, you would pass 80 (128 decimal) which is half way between 0 and 255. Some commands, like off, do not need this value (since off is inherently 0% brightness), so you pass 00.

There are tons and tons of Insteon commands to use for bytes 6 and 7 to control your devices, this webpage is a good primer.

The response you’ll receive back from your PLM will be 9 bytes long (0 to 8), and will be composed of identical values to your command from bytes 0 to 7, the 9th byte will be an ACK (Acknowledge) or a NAK (Negative Acknowledge) to let you know if the command was successful or not.

Byte 0: 0x02, Same as passed in the command
Byte 1: 0x62, Same as passed in the command
Byte 2: 0xXX, Same as passed in the command
Byte 3: 0xXX, Same as passed in the command
Byte 4: 0xXX, Same as passed in the command
Byte 5: 0x0F, Same as passed in the command
Byte 6: 0xXX, Same as passed in the command
Byte 7: 0xXX, Same as passed in the command
Byte 8: 0x06 OR 0x02, Acknowledgement (0x06) or Negative Acknowledgement (0x02) of the command

Once again, these commands and responses are for standard commands, which is what you will be sending most of the time. A later guide will cover handing extended commands and broadcasts.

Since I am very fond of Python (and because all of my home automation system (except for a small embedded portion written in C) is written in Python) I decided to use Python to write a short control script. Being that every Linux distro should come with Python pre-installed (or at least very easily installable), the script should work for most anyone. The only dependency is the installation of the PySerial package.

#!/usr/bin/env python

import serial # The PySerial package needs to be installed
import sys

SPORT = "/dev/ttyS0" # Change this as needed to match your serial port, i.e. /dev/ttyS1, /dev/ttyUSB0, etc

if len(sys.argv) is 5:
    ser = serial.Serial(SPORT, 19200, timeout=1) # Setting up a serial port
    if sys.argv[4] == 'on':
        data = bytearray(b'\x02\x62' + sys.argv[1].decode("hex") + sys.argv[2].decode("hex") +    sys.argv[3].decode("hex") + b'\x0F\x12\xFF')
    elif sys.argv[4] == 'off':
        data = bytearray(b'\x02\x62' + sys.argv[1].decode("hex") + sys.argv[2].decode("hex") + sys.argv[3].decode("hex") + b'\x0F\x14\x00')
    print "Command:"
    for i,b in enumerate(data):
        print "%d : %s" % (i,hex(b).upper())
    ser.write(data)
    response = bytearray(ser.read(9))
    print "Response:"
    for i,b in enumerate(response):
        print "%d : %s" % (i,hex(b).upper())
    ser.close()
else:
    print "Syntax: " + sys.argv[0] + " XX XX XX command\nWhere XX XX XX is the address of the switch (ex. 29 11 B2) and command is 'on' OR 'off'"
    sys.exit(1)

Copy this code and save it to a file, I’ll assume you name it “insteon.py”. Running the script should produce output similar to below. As you can see the syntax is to provide 4 arguements, the first 3 being the address of the switch seperated by spaces instead of dots, and then the last arguement is one of the two commands this switch supports (on or off).

[user@linux]$ python insteon.py 29 11 B2 on
Command:
0 : 0X2
1 : 0X62
2 : 0X29
3 : 0X11
4 : 0XB2
5 : 0XF
6 : 0X12
7 : 0XFF
Response:
0 : 0X2
1 : 0X62
2 : 0X29
3 : 0X11
4 : 0XB2
5 : 0XF
6 : 0X12
7 : 0XFF
8 : 0X6

This command made the script pass the following eight byte array to the PLM via the serial port:

Byte 0: 0x02 (The start of all Insteon commands)
Byte 1: 0x62 (Telling the PLM that the following will be a standard command)
Byte 2: 0x29 (The upper address of the switch)
Byte 3: 0x11 (The middle address of the switch)
Byte 4: 0xB2 (The lower address of the switch)
Byte 5: 0x0F (A standard flag, again more on flags later)
Byte 6: 0x12 (The On command)
Byte 7: 0xFF (A lit up byte, all ones, 255 in decimal, meaning 100% brightness for the On command)

The response back was as we expected:

Byte 0: 0x02 (Echoing the command)
Byte 1: 0x62 (Echoing the command)
Byte 2: 0x29 (Echoing the command)
Byte 3: 0x11 (Echoing the command)
Byte 4: 0xB2 (Echoing the command)
Byte 5: 0x0F (Echoing the command)
Byte 6: 0x12 (Echoing the command)
Byte 7: 0xFF (Echoing the command)
Byte 8: 0x06 (Acknowledgement of a successful command, had this command failed this would be 0x02 or Negative Acknowledgement)

Now that you have a simple python script to control your lights the sky’s the limit. Take a look here which provides an extensive list of Insteon commands that you can add to this script, allowing more control. Since now you know what the Insteon protocol looks like, and how to program it, write you own control software.