Monday, January 4, 2016

Simple IoT security system using Raspberry Pi 2B + Razberry + Fibaro Motion Sensor (FGMS-001)

In this article I'll describe how I created a simple home-brew burglar detection system to send me a mail when someone enters my house (so I can call the police). First my choice for the components is explained. Next how these components combine to achieve the functionality wanted. Based on this article you should be able to avoid certain issues I encountered and have a nice suggestion for a simple relatively cheap burglar detection system.

My purpose was to create a simple security system based on a Raspberry Pi. A Raspberry Pi is a tiny computer which can run a Debian like Linux distribution called Rasbian. I wanted to avoid going to low-level into sensor configuration and programming. That's why I decided early on to use an extension board and not directly attach the sensors to the Raspberry Pi. I decided to go for the Razberry. I also looked at the GrovePi and Arduino. Both are still too low-level for my tastes though. The Razberry is an extension board for the Raspberry which provides a Z-Wave controller chip. Z-Wave is a wireless protocol popular in the area of home automation. This was an attractive option since if in the future I would want to use additional sensors or maybe even use a commercial home automation system, I could very well get compatibility out of the box. For the sensor, I decided on the Fibaro FGMS-001 Motion Sensor. This is a multi-sensor which allows detection of motion, temperature, luminiscence and vibrations. It can even detect tampering and earthquakes (which is relevant since I live in the Dutch city of Groningen).

Z-Wave.Me (the company providing the Razberry), provides software for the Razberry called Z-Way. There are several alternatives. One of the most popular seems to be Domoticz which is provided with OpenZWave. Domoticz allows quite extensive home automation but I was having difficulty getting the sensor to work with OpenZWave so I decided to go with Z-Way. Z-Way supported the sensor out of the box. With the Z-Way server however it was difficult to automate actions based on sensor values. How I solved this is also described in this article.


How to get the environment up

I started out with a Raspberry which had Rasbian pre-installed. Installing Z-Way was easy. After the installation was completed, I went to https://find.z-wave.me and it indicated my Z-Way server. This allowed me to set the admin password and login. I could access the Z-Way web interface from http://192.168.178.10:8083/smarthome. The expert interface was available at http://192.168.178.10:8083/expert and the API at http://192.168.178.10:8083/ZWaveAPI/Run.

Next I had to add the sensor. The Z-Wave controller and the sensor both need to get to know each other. this is called inclusion. The Z-Way software had a handy wizard to install the Fibaro sensor I bought. The sensor could be opened and the button inside pressed three times. the eye of the sensor would turn blue in order to indicate it was ready for inclusion or exclusion. After the sensor was known to the Z-Wave controller and the Z-Way software, I could start looking at sensor data.




So far pretty straightforward and no coding needed (the installation of the Z-Way software does not count). The motion detection sensor worked nicely. Now on to the not so straightforward part

Send me a mail when someone enters the room

Calling JavaScript from Z-Way

Automating actions based on sensor value changes was not straightforward. Under configuration, there are several apps in Z-Way. One of the apps allows you to load a custom JavaScript file. This file has to be present in your automation folder (/opt/z-way-server/automation) on your Raspberry. The JavaScript file is loaded when the server is started.


In order to debug this Javascript, you can look at /var/log/z-way-server.log on the Raspberry. You can also look at the Event log in the smarthome interface. It helps to have debugPrint statements in your JavaScript code.


When the server is starting, the JavaScript file is loaded. The zway specific classes are not always immediately available yet though. You need the 'zway' class to be able to access devices. You need to access the device if you want to act on sensor value changes (bind functions).The solution for this I found here and is as follows:

 this.bindFunc1 = function (zwayName) {  
   if (zwayName != "zway")  
    return; // you want to bind to default zway instance  
   
   var devices = global.ZWave[zwayName].zway.devices;  
   
   //sensor specific code  
 };  
   
 // process all active bindings  
 if (global.ZWave) {  
   global.ZWave().forEach(this.bindFunc1);  
 }  
   
 // and listen for future ones  
 global.controller.on("ZWave.register", this.bindFunc1);  

Call a Python script from JavaScript

Next challenge was calling a Python script from the JavaScript code. Why Python and not JavaScript? Well, I wanted to write files to the file system, send mails and do other things which might be not straightforward in the Google V8 engine which Z-Way uses. Python allows easy independent testing and debugging of a script without having to fiddle with the V8 debugger.

In /opt/z-way-server/automation there is a file called .syscommands. In this files you have to add the commands which are allowed to be executed from JavaScript. In my case I added lines like:

/usr/bin/python /opt/z-way-server/automation/trigger.py
/usr/bin/python /opt/z-way-server/automation/savemeasure.py

The trigger.py script I used for burglar detection and the savemeasure.py I used for temperature, luminescence and battery measures.

In the JavaScript code I could now call these scripts like:

system('/usr/bin/python /opt/z-way-server/automation/trigger.py');

I could also add additional parameters like

system('/usr/bin/python /opt/z-way-server/automation/savemeasure.py' ,'Temperature',this.value);

Find the sensors

It was nice I could start a JavaScript file and execute Python scripts from it. What I wanted however was to do something when something happened. In order to achieve this, I needed to access my devices.

First I needed to get to obtain information on devices. Z-Way provides a nice API. http://192.168.178.10:8083/ZWaveAPI/Run/devices showed me all the devices. I could also browse the expert interface at http://192.168.178.10:8083/expert (it provided me with more information and control than the smarthome interface). The below locations of the sensors are of course environment specific. It does give you some idea though where to look.

Motion sensor

To access the motion sensor value I could go to
http://192.168.178.10:8083/ZWaveAPI/Run.devices[3].instances[0].commandClasses[48].data[1].level.value

Instead of commandClasses[48] you can also go to SensorBinary
http://192.168.178.10:8083/ZWaveAPI/Run.devices[3].instances[0].SensorBinary.data[1].level.value
(which gave 'false' when not triggered)

The available command classes are visible in the expert interface


Battery

The battery was a separate commandClass. The value could be obtained from:
http://192.168.178.10:8083/ZWaveAPI/Run.devices[3].instances[0].Battery.data.last.value

Other sensors

The device contained multiple sensors, the other sensors were available under commandClass 49; sensorMultilevel.


Temperature was available at
http://192.168.178.10:8083/ZWaveAPI/Run.devices[3].instances[0].SensorMultilevel.data[1].val

Luminescence was available at
http://192.168.178.10:8083/ZWaveAPI/Run.devices[3].instances[0].SensorMultilevel.data[3].val

Trigger on change

My JavaScript code ended up looking like:

 this.bindFunc1 = function (zwayName) {  
   if (zwayName != "zway")  
    return; // you want to bind to default zway instance  
   
   var devices = global.ZWave[zwayName].zway.devices;  
   
  zway.devices[3].instances[0].commandClasses[48].data[1].level.bind(function() {  
   if (this.value == true)  
     debugPrint("Executing trigger");  
     system('/usr/bin/python /opt/z-way-server/automation/trigger.py');  
 });  
   
  zway.devices[3].instances[0].commandClasses[49].data[1].val.bind(function() {  
   debugPrint("Saving measure");  
   system('/usr/bin/python /opt/z-way-server/automation/savemeasure.py','Temperature',this.value);  
 });  
   
  zway.devices[3].instances[0].commandClasses[49].data[3].val.bind(function() {  
   debugPrint("Saving measure");  
   system('/usr/bin/python /opt/z-way-server/automation/savemeasure.py','Luminescence',this.value);  
 });  
   
  zway.devices[3].instances[0].Battery.data.last.bind(function() {  
   debugPrint("Saving measure");  
   system('/usr/bin/python /opt/z-way-server/automation/savemeasure.py','Battery',this.value);  
 });  
   
 };  
 // process all active bindings  
 if (global.ZWave) {  
   global.ZWave().forEach(this.bindFunc1);  
 }  
   
 // and listen for future ones  
 global.controller.on("ZWave.register", this.bindFunc1);  

Send a mail and saving measures

I found that making Z-Way API calls from the Python code via HTTP did not work well. My Z-Way server CPU regularly got to 100% and did not respond anymore. It is better to do the triggering from JavaScript and pass values along to the Python script (see savemeasure.py below) to avoid this issue.

trigger.py

My trigger.py script looked like ('apt-get install python' first of course to get Python on your Raspberry):

 import time  
 import datetime  
   
 ts = time.time()  
 st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')  
   
 def send_email(user, pwd, recipient, subject, body):  
   import smtplib  
   
   gmail_user = user  
   gmail_pwd = pwd  
   FROM = user  
   TO = recipient if type(recipient) is list else [recipient]  
   SUBJECT = subject  
   TEXT = body  
   
   # Prepare actual message  
   message = """\From: %s\nTo: %s\nSubject: %s\n\n%s  
   """ % (FROM, ", ".join(TO), SUBJECT, TEXT)  
   try:  
     server = smtplib.SMTP("smtp.gmail.com", 587)  
     server.ehlo()  
     server.starttls()  
     server.login(gmail_user, gmail_pwd)  
     server.sendmail(FROM, TO, message)  
     server.close()  
     print 'successfully sent the mail'  
   except:  
     print "failed to send mail"  
   
 send_email('xxx@gmail.com','pass','target@mail.nl','Intrusion detected','Intrusion detected at '+st)  
   
 f = open('/opt/z-way-server/automation/intrusions.log', 'a')  
 f.write(st+"\n")  
 f.close()  

Of course do not forget to allow 'less secure apps' to access to your GMail account if you use one (see here) or implement mail-server authentication in your script which is considered secure by Google. The result was I got a lot of mails so I turned the mails off but will enable them when I'm out on vacation for example. 

savemeasure.py

My save measure.py script looked as followed:

 import time  
 import datetime  
 import sys  
   
 ts = time.time()  
 st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')  
   
 f = open('/opt/z-way-server/automation/measure.log', 'a')  
 f.write(st+"\t"+sys.argv[1]+"\t"+sys.argv[2]+"\n")  
 f.close()  

Thus I end up here with 2 log files. One for intrusions; intrusions.log (motion detector gets triggered) and the other for sensor values; measure.log.

Finally

It took me about 2 days to get everything working. I encountered several issues. With this article I hope you will be able to get started faster and make the right choices in hard- and software for your home automation system. It was fun though to look into this and get it working.

Maybe in the future I'll add a button to turn it on and off (and make it really cool!). I'm using the Z-Wave protocol, it should not be difficult to find something to make this possible. Also it would be nice to do something with the earthquake detection and tamper detection.

The next steps are of course to process the sensor data. Stay tuned!

No comments:

Post a Comment