Wednesday, January 5, 2011

Home Automation Part 2 (Now With Python)

Wow. It has been a while. It can be hard to find time as a student to work on personal projects let alone share them with the world, but I'm happy to report a few improvments on the state of cheap and easy home automation for the DIY minded.

Firstly, incase you didn't know...
Kernel modules are out. User-space python scripts are in.


Mike Lemay, the gentalman who provides the linux kernel module I wrote about last time has released an upgrade as a python module. This is great news! No more recompiling evrytime Canolical releases a kernel update, and no more (ok, well less) worrying about permissions.

And one of the best parts is that we can now use our cm19a to listen for commands from other wireless compliant x10 remotes and sensors!

Things are invoked a little differently now, but with a little bash magic we can massage data flow to work with any preexisting scripts you might have in place from the kernel module version of the driver. IE we'll make our own pseudo device in dev called cm19a0

start out by grabbing the latest python driver from Lemay's site

Following the linux tutorial on Lemay's site
and come back when you get to step 9

...

for step 9 we will be making some named pipes - our pseudo device, and we're going to put them in /dev

so in a terminal with root permissions type
mkfifo /dev/cm19a0
mkfifo /dev/cm19a1

cm19a0 will represent the commands going out from the transceiver
cm19a1 will represent the commands coming in from other x10 remotes and sensors

we're ready to fire up the driver.

as an aside you may have to adjust the permissions of the transceiver device on your system. step 10 on Mikes site will illustrate how to do this manually, but you will have to retype the commands he talks about every time your machine reboots. As an alternative you may want to set up a udev rule. Being able to set up udev rules is an invaluable skill for sysadmins and in general are very handy for keeping track of devices that aren't necessarily "plug n play"

There's plenty of info around the web on the particulars of writing udev rules, but for the bare minimum to get you going here's what you want to do.

gedit /etc/udev/rules.d/95-perso.rules

put this line in the file somewhere

ATTR{manufacturer}=="X10 Wireless Technology Inc", ATTR{product}=="USB Transceiver", GROUP="users", MODE="0666"

save and close

sudo service udev restart

to apply the rules.

95-perso.rules is the rules file that I use. the 95 at the beginning describes the order that udev will apple these rules. for devices like the usb transceiver I find it is best to wait until the system has sorted everything else out, so we make it the last thing the kernel deals with.


Now, in its basic syntax, we invoke the driver by running ( from the directory where the driver is located)

python pycm19a.py < /dev/cm19a0 > /dev/cm19a1

if you are having problems at this point, try going back and following steps 1-9 (or further for your satisfaction).

you should be able to enter a command in a new terminal in the usual fashion

echo +a1 > /dev/cm19a0

looks familiar, right?

now (in a different terminal) type
tail -f /dev/cm19a1

then use one of your remotes or sensors and turn off or on a device

if I turn off the lamp on a1 with mine, I see
...
+a1
...

cool?

and thats all well and good, but for those who want to take things a step farther you might try something like this...


python pycm19a.py < /dev/cm19a0 > /dev/cm19a1 2>>/dev/cm19a1 && cat /dev/cm19a1 | while read a ; do echo "$a : `date`" ; done > cm19a.log &

what does this do?
you may have noticed that commands sent via command line to the driver do not show up with commands from other remotes. so the first thing we want to do is include those commands
(thats the 2>>/dev/cm19a1) we then redirect that entire output again to a loop that appends the date and time each command was recieved or sent into a file called cm19a.log. Pretty neat huh? now you have a running log for your own edification of when any appliance has been turned off or on via any controller.

Ok. Last little tidbit for this post. once you have this command and you throw it in to a bash script to run at reboot. For me I prefer a userspace service.

this little bash script is based on a skeleton that I reuse over and over again for things I want to run as services but only under my normal user space.

copy the following code in to a file called liveHouse. chmod u+x liveHouse then you should be able to ./liveHouse start to start the service ./liveHouse stop to stop it and ./liveHouse to check to see if its running (if its running it will return its process number)



#!/bin/bash

#emulate the stop start action of /etc/init.d with out all the service overhead...

driver='/absolute/path/to/your/driver';
inFile='/dev/cm19a0';
outFile='/dev/cm19a1';
logFile='/absolute/path/to/where/you/want/to/log'
#if no argument is given, return the process number
if [[ $1 == '' ]] ; then
iam=$(ps ax | grep 'pycm19a.py' | grep -v 'grep' | awk '{print $1}');
if [[ $iam == '' ]] ; then
exit 0;
else
echo "$iam";
fi;
elif [[ $1 == 'start' ]] ; then
running=`ps aux | grep "$SHELL $0 start" | grep -v grep | wc -l `
devCheck=`ls -l $inFile $outFile | grep $USER | wc -l`;
if [[ $devCheck == 2 && $running == 2 ]] ; then
#if its not already running start it up
echo "starting liveHouse";
exec python "$driver" < "$inFile" > $outFile 2>>$outFile & cat $outFile | while read a ; do echo "$a : `date`" ; done > $logFile & echo > $inFile &
else
echo "liveHouse is already running or there was a problem with $inFile or $outFile";
exit 0;
fi


elif [[ $1 == 'stop' ]] ; then
echo "Stopping liveHouse";
killMe=$(ps ax | grep 'pycm19a.py' | grep -v 'grep' | awk '{print $1}') ;
kill -9 "$killMe";
fi;



NOTE YOU MAY WANT TO DOUBLE CHECK THE QUOTES ON THIS. THEY MAY NOT HAVE BEEN ENCODED CORRECTLY WHEN I PASTED FROM MY TERMINAL AND IT MAY CAUSE SYNTAX ERRORS IF YOU TRY TO RUN THIS


NEXT POST: throwing XBMC in to the mix!!!

No comments:

Post a Comment