Building a Home Automation and Security System with Python
Listing 1. Employing the GetInputStatus Method
def GetInputStatus(self, Input = None): self.ser.write(Input + '\r\n') Expect = [Input, Input, '\r', '\n'] cnt = 0 while cnt <= 3: a = self.ser.read() if a == '' or a != Expect[cnt]: return -1 cnt += 1 val= self.ser.read() Expect = ['\r', '\n', '#'] cnt = 0 while cnt <= 2: a = self.ser.read() if a == '' or a != Expect[cnt]: return -1 cnt += 1 if val == '1': return 1 else: return 0
When activity is detected, MonitorInputs checks to make sure that input activity has not occurred within the input activity timeout. The timeout is used to keep the alarm threads, which simply send a plain-text e-mail, from executing too many times during a single input activity voltage on condition. The timeout is not the best solution, because the smoke and/or water alarm would still send a new e-mail every 60 seconds. This is acceptable to me, because if I receive a water alarm e-mail while at work, I'm going to rush home. The unused Serial I/O Kit relays could be used to correct this shortcoming, because each input positive connection could be routed through a relay, which could be turned off to disable the alarm voltage.
Another solution is to signal the GetInputStatus method to ignore input activity on a specific input. Either method will work, but a remote trigger mechanism will be required in either case, because the serial port connection is maintained by the home automation program. A possible solution adds a server thread to the home automation program that would accept simple string commands from a client connection. This would allow a simple Python CGI script to send commands that could control input monitoring and/or the relay states. Pyro is a Python distributed object system that provides another more complex solution using an event server. This is very similar to the client/server approach, but Pyro is more robust and provides opportunities beyond the scope of this article. One of these solutions will probably find its way into a future upgrade to the home automation program.
Now that the program is monitoring for input activity, it needs to produce notifications, such as a warning sound or e-mail when activity is detected. Smoke and water alarm activity is handled by the generic threaded Alarm class, and drive alert activity is handled separately. The Alarm class plays a WAV file using the PlayWav class, and it also sends a notification e-mail using the MailAttachment class. The PlayWav class uses a popen call to the wavcmd value (sox play command) set in the configuration file. The PlayWav class is threaded to prevent a busy sound device from holding up the e-mail notifications. The end result of all of the threaded classes is that the input activity is monitored almost continuously with only slight delays.
The DriveAlert class handles detected input activity for the drive alert signal. This class employs the GetImage (Listing 2), PlayWav and SSHRemote threaded classes. A new GetImage instance is created for each camera command (camcmd) set in the configuration file, so that images can be collected from each camera at about the same time. The GetImage class makes a popen call to the camera command and waits until it has completed. This is repeated until the number of images set in the configuration file have been collected and saved in the directories defined in the camdir section of the configuration file. Once all of the images have been collected, the GetImage class uses the ZipIt class to create a zip file via a popen call to the zip command. When all of the image files are zipped up, the MailAttachment routine is used to e-mail the zip files. If you would like to stagger the images collected from the cameras, you can add a camera image delay section to the configuration file and modify the GetImage class by adding a call to the sleep function using the preset camera delay as input.
Listing 2. DriveAlert Class
class GetImage(threading.Thread): def __init__(self, cam = None, numImages = 1): self.cam = cam self.JobBegin = -1 self.camCmd = CamCOMMANDS[cam] self.numImages = NumCamImages[cam] self.Zip = None threading.Thread.__init__(self) def run(self): for i in range(self.numImages): self.JobBegin = int(time.strftime("%H%M%S",time.localtime(time.time()))) if QUIET == 0: print 'Getting %s image' %self.cam filename = time.strftime("%H%M%S", time.localtime(time.time())) + '.jpg' execcmd = self.camCmd %filename self.p = popen2.Popen3("exec " + execcmd, 1024) self.errReader = PipeReader(self.p.childerr); self.errReader.start() self.outReader = PipeReader(self.p.fromchild); self.outReader.start() try: self.p.wait() except OSError, (errno, errnostr): if QUIET == 0: print 'ERROR: GetImage self.p.wait Errno %s: %s' %(`errno`, `errnostr`) except: if QUIET == 0: print 'ERROR: self.p.wait Unknown error' time.sleep(IMAGE_DELAY) #Popen complete - create zipfiles self.Zip = ZipIt(self.cam) self.Zip.start() self.Zip.join() #Wait on zip file creation
I briefly mention the SSHRemote class because the name is ambiguous. This class could be used to execute any command by replacing the ssh remote command in the configuration file with another one. I currently use it to play some tunes on my shop machine to make it appear that someone is home. The ssh call executes another simple Python script on the remote machine, which uses the play command to play all WAV files in a specified directory.
This article shows how Linux, Python and some cheap off-the-shelf hardware can be used to create a home automation system in a reasonable amount of time. The article focuses on the main parts of the system and cannot possibly describe the setup of all of the required components in detail. I must also stress that this system has not been tested in a production environment and therefore comes with no guarantees, express or implied, as to its suitability for any of the purposes listed above, so use it at your own risk. I am looking forward to making future enhancements, such as a voice modem that will dial a preset number and play a message. This will supplement the unreliable e-mail notifications, which are often delayed. I hope this article sparks your interest in simple monitoring systems and the flexibility of the Serial I/O Kit used in this project.
Resources for this article: /article/8696.
Fred Stelter has a BS in Computer Science from Baylor University in Waco. When he's not writing code for a local company, he likes to pop some tires at the local mountain bike trails, work on his hot rod or occasionally hit the water for some kneeboarding.
|Designing Electronics with Linux||May 22, 2013|
|Dynamic DNS—an Object Lesson in Problem Solving||May 21, 2013|
|Using Salt Stack and Vagrant for Drupal Development||May 20, 2013|
|Making Linux and Android Get Along (It's Not as Hard as It Sounds)||May 16, 2013|
|Drupal Is a Framework: Why Everyone Needs to Understand This||May 15, 2013|
|Home, My Backup Data Center||May 13, 2013|
- Designing Electronics with Linux
- New Products
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Dynamic DNS—an Object Lesson in Problem Solving
- Using Salt Stack and Vagrant for Drupal Development
- Validate an E-Mail Address with PHP, the Right Way
- Tech Tip: Really Simple HTTP Server with Python
- Why Python?
- Build a Skype Server for Your Home Phone System
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Reply to comment | Linux Journal
35 min 29 sec ago
- Reply to comment | Linux Journal
1 hour 25 min ago
- Not free anymore
5 hours 27 min ago
9 hours 14 min ago
- Reply to comment | Linux Journal
9 hours 22 min ago
- Understanding the Linux Kernel
11 hours 37 min ago
14 hours 7 min ago
- Kernel Problem
1 day 9 min ago
- BASH script to log IPs on public web server
1 day 4 hours ago
1 day 8 hours ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi
It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?