Building a Home Automation and Security System with Python
In this article, I describe the process I used to create a home automation system using off-the-shelf products, Python and Linux. I start by describing the Serial I/O Kit, drive alert, water alarm, smoke detector and cameras that make up the hardware portion of the system. Next, I give a short description of the command-line programs and packages that are used by the Python program. I finish up by giving a brief run-down of the major parts of the Python program that ties all of the hardware and packages together.
The idea for this system was born when some of my neighbor's party guests parked in my drive for several minutes while they were trying to figure out if they were at the correct house. I was caught off-guard, because I didn't immediately notice that someone had pulled up. It became very clear to me that they could have entered my residence or shop unnoticed. I decided to create a system on my own, because I knew that a commercial security system that would monitor driveway traffic and capture images from the various cameras positioned around my property would be expensive.
Because I have coded several programs at work in Python, I have had firsthand experience with the rapid development provided by Python. It allowed me to concentrate on the problem at hand rather than the complicated language syntax and semantics. I would recommend Python for any program that crunches text-based data, because it is easy enough for beginning programmers, but powerful and flexible enough to handle larger tasks.
The Python home automation system is centered around an Isolated Serial I/O Kit (Figure 1) from Quality Kits, which is available assembled for an extra $10 US. The kit contains four inputs and eight relays. The inputs detect DC voltage from a source within the 6-24 volt range. The relays can be used to turn voltage to a source on or off, so they can be used to control the power to multiple 12v light bulbs or any other gadget that requires DC voltage within the relay's specified voltage range. The Serial I/O Kit uses simple read and write commands to a serial port connection for setting the relays and monitoring the inputs. The inputs and relay circuits are isolated, which means there is no direct connection between these circuits and the computer's serial connection. This prevents damage to the computer if something goes wrong with the Serial I/O box. Figure 2 shows how to connect the Mier Drive Alert and alarms to the Serial I/O box inputs.
The second major part of the system is the Mier Drive Alert (Figure 3). This system is reliable, and it also provides an adjustable timed 24-volt output when activity is detected. If you decide to use the Mier Drive Alert, connect one of the Serial I/O inputs to the terminals marked Neg and NO on the Mier Drive Alert controller. Otherwise, any drive alert system that provides a voltage output in the 6-24 volt range when activity is detected should work. If you are not sure that the unit you chose provides this type of output, you probably need to contact the company.
The Mier controller box detects very small changes in voltage, which are produced by the sensor probe due to changes in the earth's magnetic field when a metal object passes by the sensor probe. This is why the sensor probe cannot be connected directly to the Serial I/O Kit's input, because the voltage produced by the probe is much smaller than the input's minimum voltage detection value of 5 volts.
Other drive alert systems are available that use a beam or a pressure-activated switch and a rubber hose. This type was once pretty common at drive-throughs and full-service gas stations. I didn't choose the beam type, because it would detect anything that moves, and I doubt that a rubber hose would have held up very well on my gravel drive. The Mier unit produces an occasional false alarm due to inclement weather and lighting strikes, but these are minimal and could be reduced if the controller sensitivity were adjusted.
As we all know, water and house interiors don't mix, which is why I added this cheap and simple water-detection circuit to the mix. The circuit performed well in a test environment using a pan of water, but lacks any real-life testing. The circuit shown in Figure 4 was created from a schematic that is listed in the on-line Resources. I removed the timer, buzzer and other unnecessary components, leaving a single switching transistor and a resistor. The circuit can be powered by a DC adapter in the 6-9-volt range. The probes in my system are simply a short piece of scrap copper tubing, and they just sit on the floor of my shop bathroom several inches apart. I have multiple probes connected to the same water alarm circuit, which provides multiple monitoring points per circuit at the loss of being able to identify the exact location of the leak. I leave it up to you to determine the correct probe separation for your application, because I don't have any exact distances. The water alarm (Figure 5) that I created contains two transistors and thus two alarm circuits on the same board.
The smoke alarm has never been tested in a real emergency and was added as an afterthought. Do not replace any existing smoke alarms with this modified version, because it must be tampered with. The following reasons are very good reasons not to rely on it in case a fire occurs, because if your computer, the Serial I/O Kit connection, and/or your Internet connection is the first to go, the alarm is useless. If you decide to duplicate this portion of the project, do so at your own risk. The smoke detector used in this portion of the project is just a model with an exit light that should be available from your local home hardware center. It employs two 9-volt batteries; one to power the alarm, and the other to power the light. Now, you need to void the warranty by modifying the detector as follows. Begin by removing the cover and locating the exit light. Use a volt meter to determine the positive and negative connections to the light. Then just solder a couple of wires to the proper connection points and connect them to one of the Serial I/O Kit's inputs.
The system uses several relatively inexpensive Logitech Webcams along with a couple of network cameras. Any cam that's supported by video for Linux should work along with most network camera models. The best advice I can offer is to verify that the camera is supported by Linux and that a driver is available. Logitech Quickcam Pro models perform quite well, but not all of the Logitech models are supported by the same driver. The Quickcam Pro models use the pwc module available from saillard.org.
The system uses several command-line programs available for Linux that handle WAV-file playback, zip file creation and image file captures. The SOX package is a sound conversion and utility package for Linux. It provides the play command, which is used for playing the various alarm warning sounds. Several sound wrapper modules are available for Python, but I found that it was fairly easy to make a system call to the play command. The zip command is used to create zip files from the camera image captures. Python includes a zipfile module, but it was easier to use the command-line version, which easily can be replaced with another command, such as tar.
Image file captures are handled using Motion and Curl. The later is a fairly powerful program that will transfer data from a server. According to the man page, it handles http, https, ftp, telnet and some other formats. I make a system call to it to retrieve images from the D-Link network camera's HTTP server by using a command similar to this one, curl http://192.168.0.98/IMAGE.JPG -uusername:password -m2 >outputfile.jpg, where -m2 tells it to stop trying after two seconds. Motion creates MPEG motion capture and time-lapse movie files along with single JPEG image files at a preset rate. The simple HTTP server built into Motion provides an image stream, but it does not allow for single image retrieval using Curl. For more information on Motion, see the Linux Journal March 2005 article, “GNU Motion: Your Eye in the Sky for Computer Room Surveillance” by Phil Hollenback, because it does a good job explaining the details required to get it up and running.
Once you have a working Motion setup, you should change the snapshot_interval in Motion's config file to a one or a value acceptable to your application. Lower values are better, because they prevent duplicate image captures. Now set the snapshot_filename value to a temporary filename, such as, tempmotionimagefile, so that Motion creates only a single capture file. Leaving the default motion.conf snapshot_filename will work, but Motion will create a new file using the snapshot interval value, which can result in a very large number of stored image files. These settings cause Motion to create a symbolic link named lastsnap.jpg that always points to the latest snapshot file, which in the case of the settings above, always is overwritten with the most recent image.
Now that all of the hardware and required programs above are configured, installed and are functioning on their own, it is time to discuss the Python program that glues all of it together. I briefly touch on the most important parts of the program, along with any second-party modules that are required. The pyserial module contains a posix serial I/O implementation for Linux and is used to handle reads and writes to the Serial I/O Kit. A thread-enabled Python installation is required by the program, because it must perform several simultaneous operations in order to monitor input activity and so forth.
The MonitorInputs class is one of the most important, because it handles input monitoring by employing the GetInputStatus method (Listing 1). This method checks for input activity by writing the input name (I1 to I4) to the serial port. The write method is provided by the Serial class in the pyserial module. Notice that there is a Python list defined as Expect in the example code. This is a list of the expected output from the serial class read command executions that are required to clear and validate information that is returned from the write request. If an unexpected read result is obtained, the serial port connection is reset. This allows the program to recover from serial communication failures and short power losses to the Serial I/O Kit.