Home alarm on Rpi + PiFace Digital V2
Features:
-video stream from usb cameras
-motion detection and automatic image recording
-control via web browser
-input with delay (front door)
-tamper line input
-sends a email message of alarm activity
-after rebooting the system is set to the last state of alarm
-login any activity alarm
We will need a few useful programs:
$ sudo apt-get install monit
$ sudo apt-get install screen
$ sudo apt-get install chkconfig
Install Motion for cameras:
$ sudo apt-get install motion
$ sudo chmod 777 /etc/motion/motion.conf
$ sudo mkdir /home/pi/ftp
$ sudo chmod 777 /home/pi/ftp
$ sudo mkdir /home/pi/ftp/cam1
$ sudo chmod 777 /home/pi/ftp/cam1
$ sudo mkdir /home/pi/ftp/cam2
$ sudo chmod 777 /home/pi/ftp/cam2
Install pifacedigitalio
$ sudo apt install python3-pip
$ sudo pip3 install pifacecommon
$ sudo pip3 install pifacedigitalio
It is good to once every few days to restart the system, and once a day, you can execute the command:
sudo sh -c "sync; echo 3 > /proc/sys/vm/drop_caches"
We can, of course, write the script run from Cron:
$ sudo mkdir /usr/local/bin/myservice
$ sudo nano /usr/local/bin/myservice/freemem.sh
#!/bin/sh
sudo sh -c "sync; echo 3 > /proc/sys/vm/drop_caches"
we set the permissions:
$ chmod 755 freemem.sh
we must edit crontab:
$ sudo nano /etc/crontab
10 12 * * 7 root sudo /sbin/reboot
1 12 * * 1-7 root /usr/local/bin/myservice/freemem.sh
Description of electrical connections PiFace Digital:
Output 0 - external siren.
Output 1 - internal siren for tamper line.
Output 2 - logical "1" = alarm is disarm, logical "0" = alarm is arm.
Input 0 - delayed line.
Input 1 - line without delay.
Input 2 - arm.
Input 3 - disarm.
Input 5 - tamper line input.
Input 6 - arm status (arm/disarm).
Input 7 - alarm status (active/inactive).
Connect in_5 to 'NO' relay K0, use shottky diode BYV10-40 in5--|>|--relay / observe polarity!
Connect in_6 to out_2, use shottky diode BYV10-40 in6--|>|--out2 / observe polarity!
Connect in_7 to out_0, use shottky diode BYV10-40 in7--|>|--out0 / observe polarity!
Power for the siren connect to 'C' (common) relay K0 input. The terminal '+' of the siren connect to 'NO' relay K0 input. The mass of the siren to the mass of the system.
Alarm script in python:
Note: If you copy the code, be sure to swap space at the beginning of the line to the appropriate number of tabs!
$ sudo nano /usr/local/bin/myservice/alarm.py
#!/usr/bin/env python3
# PiFace Digital
# home alarm by fuse
# v.2.1
# 14.08.2022
# Python 3
# out 0 - siren
# out 1 - internal siren (sabotage alarm)
# out 2 - logic 1 = disarm alarm, logic 0 = arm alarm
# in 0 - line A (delayed)
# in 1 - line B
# in 2 - arm alarm
# in 3 - disarm alarm
# connect in_5 to siren relay, use shottky diode BYV10-40 in5--|>|--relay / observe polarity!!!
# connect in_6 to out_2, use shottky diode BYV10-40 in6--|>|--out2 / observe polarity!!!
# connect in_7 to out_0, use shottky diode BYV10-40 in7--|>|--out0 / observe polarity!!!
# in 5 - sabotage siren line input
# in 6 - arm status
# in 7 - alarm status
# http://address:port/?output_port=arm - arm alarm
# http://address:port/?output_port=disarm - disarm alarm
# http://address:port/?output_port=wipe - wipe log file
# http://address:port/?output_port=rst - reboot rpi
import sys
import subprocess
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse
import urllib.request
import pifacedigitalio as alarm
from threading import Timer
import time
import logging
import argparse
import smtplib, ssl
from email.mime.text import MIMEText
from os import system
msg_email = 'email@gmail.com'
msg_pwd = 'password'
msg = MIMEText('Alarm email system.')
msg['From'] = msg_email
msg['To'] = msg_email
msg['Subject'] = 'Alarm - START'
try:
logging.basicConfig(filename='/var/www/alarmpy.log', format='%(asctime)s %(message)s', datefmt='%y/%m/%d %H:%M:%S', level=logging.INFO)
except:
logging.basicConfig(filename='/var/tmp/alarmpy.log', format='%(asctime)s %(message)s', datefmt='%y/%m/%d %H:%M:%S', level=logging.INFO)
password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
alarm.init()
JSON_FORMAT = "{{\"Arm\":{input6},\"Sir\":{output0},\"Act\":{output8}}}"#,\"Ca1\":{output9}}}"
DEFAULT_PORT = 8000
OUTPUT_PORT_GET_STRING = "output_port"
GET_IP_CMD = "hostname -I"
class PiFaceWebHandler(BaseHTTPRequestHandler):
def do_HEAD(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_AUTHHEAD(self):
pass
#self.send_response(401)
#self.send_header('WWW-Authenticate', 'Basic realm=\"Alarm\"')
#self.send_header('Content-type', 'text/html')
#self.end_headers()
def do_POST(self):
pass
def do_COPY(self):
pass
def do_SELECT(self):
pass
def do_INPUT(self):
pass
def do_CONFIGURE(self):
pass
def do_PUT(self):
pass
def do_DELETE(self):
pass
def do_SET(self):
pass
# handles PiFace web control requests
def do_GET(self):
# present frontpage with user authentication
if self.headers['Authorization'] == None:
pass
#self.do_AUTHHEAD()
#self.wfile.write(bytes('no auth header received', 'UTF-8'))
elif self.headers['Authorization'] == 'Basic xyz': # xyz = user:password encode in base64
output_value = self.pifacedigital.output_port.value
input_value = self.pifacedigital.input_port.value
# parse the query string
query_components=""
if "?output_port=" in self.path:
qs = urllib.parse.urlparse(self.path).query
query_components = urllib.parse.parse_qs(qs)
# set the output
if OUTPUT_PORT_GET_STRING in query_components:
new_output_value = output_value
if query_components["output_port"][0] == "arm":
new_output_value = 4
logging.info ('ARM')
try:
#send_mail_event('ARM - alarm system')
f1 = open('/var/log/alarmstatus.txt','w')
f1.write('1')
f1.close()
except:
logging.info ('ERR:write_status_1')
elif query_components["output_port"][0] == "disarm":
new_output_value = 0
logging.info ('DISARM')
try:
#send_mail_event('DISARM - alarm system')
f1 = open('/var/log/alarmstatus.txt','w')
f1.write('0')
f1.close()
except:
logging.info ('ERR:write_status_0')
try:
f2 = open('/var/log/alarmactive.txt','w')
f2.write('0')
f2.close()
except:
logging.info ('ERR:write_active_0')
elif query_components["output_port"][0] == "wipe":
try:
open("/var/www/alarmpy.log","w").close()
except:
logging.info ('ERR:wipe')
elif query_components["output_port"][0] == "rst":
try:
logging.info ('REBOOT')
command = "/usr/bin/sudo /sbin/reboot"
process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
output = process.communicate()[0]
print (output)
except:
logging.info ('ERR:reboot')
else:
logging.info ('COMMAND:unknown')
output_value = self.set_output_port(new_output_value, output_value)
output_value = self.pifacedigital.output_port.value
input_value = self.pifacedigital.input_port.value
output_value0 = output_value & 0b00000001
if output_value0 != 0:
output_value0 = 1
input_value6 = input_value & 0b01000000
if input_value6 != 0:
input_value6 = 1
try:
f2r = open('/var/log/alarmactive.txt','r')
act_value = f2r.read(1)
f2r.close()
except:
act_value = 2
# reply with JSON
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(bytes(JSON_FORMAT.format(input6=input_value6,output0=output_value0,output8=act_value,), 'UTF-8'))
else:
pass
#self.do_AUTHHEAD()
#self.wfile.write(bytes('not authenticated', 'UTF-8'))
def set_output_port(self, new_value, old_value=0):
# sets the output port value to new_value, defaults to old_value
port_value = old_value
try:
port_value = int(new_value) # dec
except ValueError:
port_value = int(new_value, 16) # hex
finally:
self.pifacedigital.output_port.value = port_value
return port_value
def get_my_ip():
# returns this computers IP address as a string
ip = subprocess.check_output(GET_IP_CMD, shell=True).decode('utf-8')[:-1]
return ip.strip()
def turn_on_alarm_a(event):
try:
if alarm.digital_read(6):
if not alarm.digital_read(7):
logging.info ('A:delay_on')
ta_delay = Timer(10.0, delay_line_a) # set the length of delay for line A
ta_delay.start()
else:
logging.info ('A:already_on')
except:
logging.info ('ERR:turn_on_alarm_a')
def turn_on_alarm_b(event):
try:
if alarm.digital_read(6):
if not alarm.digital_read(7):
event.chip.leds[0].turn_on()
logging.info ('B:on')
tb = Timer(60.0, turn_off_alarm_auto_timer) # set the length of alarm for line B
tb.start()
else:
logging.info ('B:already_on')
try:
send_mail_event('ACTIVE B! - alarm system')
f2ab = open('/var/log/alarmactive.txt','w')
f2ab.write('1')
f2ab.close()
except:
logging.info ('ERR:write_active_1')
except:
logging.info ('ERR:turn_on_alarm_b')
def disarm_alarm(event):
try:
if alarm.digital_read(5):
event.chip.leds[1].turn_off()
logging.info ('S:ok')
else:
logging.info ('S:active')
if alarm.digital_read(6):
event.chip.leds[0].turn_off()
event.chip.leds[2].turn_off()
logging.info ('DISARM')
try:
#send_mail_event('DISARM - alarm system')
f1a = open('/var/log/alarmstatus.txt','w')
f1a.write('0')
f1a.close()
except:
logging.info ('ERR:write_status_0')
try:
f2a = open('/var/log/alarmactive.txt','w')
f2a.write('0')
f2a.close()
except:
logging.info ('ERR:write_active_0')
else:
logging.info ('DISARM:already')
try:
f2a = open('/var/log/alarmactive.txt','w')
f2a.write('0')
f2a.close()
except:
logging.info ('ERR:write_active_0')
except:
logging.info ('ERR:disarm')
def arm_alarm(event):
try:
if not alarm.digital_read(6):
logging.info ('ARM:will_be')
time.sleep(1)
event.chip.leds[0].turn_off()
event.chip.leds[2].turn_on()
logging.info ('ARM')
try:
#send_mail_event('ARM - alarm system')
f1a = open('/var/log/alarmstatus.txt','w')
f1a.write('1')
f1a.close()
except:
logging.info ('ERR:write_status_1')
else:
logging.info ('ARM:already')
except:
logging.info ('ERR:arm')
def delay_line_a():
try:
if alarm.digital_read(6):
pifacedigital.leds[0].turn_on()
logging.info ('A:on')
ta = Timer(60.0, turn_off_alarm_auto_timer) # set the length of alarm for line A
ta.start()
try:
send_mail_event('ACTIVE A! - alarm system')
f2aa = open('/var/log/alarmactive.txt','w')
f2aa.write('1')
f2aa.close()
except:
logging.info ('ERR:write_active_1')
except:
logging.info ('ERR:delay_line_a')
def turn_off_alarm_auto_timer():
try:
if alarm.digital_read(7):
pifacedigital.leds[0].turn_off()
logging.info ('AUTOTIMER:off')
except:
logging.info ('ERR:turn_off_auto_timer')
def send_mail_after_start_timer():
send_mail_event('Alarm - START')
def sabotage_line(event):
try:
if not alarm.digital_read(7):
if not alarm.digital_read(5):
event.chip.leds[1].turn_on()
logging.info ('S:on')
except:
logging.info ('ERR:s_on')
def send_mail_event(msg_subject):
logging.info (msg_subject)
msg = MIMEText('Alarm email system.')
msg['From'] = msg_email
msg['To'] = msg_email
msg['Subject'] = msg_subject
try:
ssl_context = ssl.create_default_context()
server = smtplib.SMTP_SSL('smtp.gmail.com', 465, context=ssl_context)
server.login(msg_email, msg_pwd)
server.sendmail(msg_email, msg_email, msg.as_string())
server.quit()
except Exception as e:
logging.info (str(e)) #('Alarm:err_send_mail')
if __name__ == "__main__":
# get the port
if len(sys.argv) > 1:
port = int(sys.argv[1])
else:
port = DEFAULT_PORT
# set up PiFace Digital
PiFaceWebHandler.pifacedigital = alarm.PiFaceDigital()
logging.info ('BOOT')
# run the server
server_address = ('', port)
pifacedigital = alarm.PiFaceDigital()
listener = alarm.InputEventListener(chip=pifacedigital)
listener.register(0, alarm.IODIR_RISING_EDGE, turn_on_alarm_a) # _RISING_ for reverse logic, _FALLING_ for normal logic
listener.register(1, alarm.IODIR_RISING_EDGE, turn_on_alarm_b) # _RISING_ for reverse logic, _FALLING_ for normal logic
listener.register(5, alarm.IODIR_RISING_EDGE, sabotage_line)
listener.register(2, alarm.IODIR_FALLING_EDGE, arm_alarm)
listener.register(3, alarm.IODIR_FALLING_EDGE, disarm_alarm)
listener.activate()
try:
ts = Timer(25.0, send_mail_after_start_timer)
ts.start()
f = open('/var/log/alarmstatus.txt','r')
status = f.read(1)
if status == "1":
try:
pifacedigital.output_port.value = 0x04
logging.info ('ARM:at_boot')
except:
logging.info ('ERR:arm_at_boot')
else:
try:
pifacedigital.output_port.value = 0x00
logging.info ('DISARM:at_boot')
except:
logging.info ('ERR:disarm_at_boot')
except:
logging.info ('BOOT:unknow_status')
try:
httpd = HTTPServer(server_address, PiFaceWebHandler)
httpd.serve_forever()
except KeyboardInterrupt:
logging.info ('^C received, shutting down server')
httpd.socket.close()
we set the permissions:
$ chmod 755 alarm.py
$ sudo nano /lib/systemd/system/alarm.service
[Unit]
Description=Alarm Service
After=multi-user.target
[Service]
Type=idle
User=pi
ExecStart=sudo /usr/bin/python /usr/local/bin/myservice/alarm.py
Restart=always
RestartSec=0
$ sudo chmod 644 /lib/systemd/system/alarm.service
$ sudo systemctl daemon-reload
$ sudo systemctl enable alarm.service
[Install]
WantedBy=multi-user.target
Zoom and unzoom script for Microsoft LifeCam Cinema
$ sudo nano /usr/local/bin/myservice/zoom.sh
#!/bin/sh
v4l2-ctl -d /dev/video0 --set-ctrl zoom_absolute=10
v4l2-ctl -d /dev/video0 --set-ctrl pan_absolute=115200
v4l2-ctl -d /dev/video0 --set-ctrl tilt_absolute=-28000
we set the permissions:
$ chmod 755 zoom.sh
$ sudo nano /usr/local/bin/myservice/unzoom.sh
#!/bin/sh
v4l2-ctl -d /dev/video0 --set-ctrl zoom_absolute=0
v4l2-ctl -d /dev/video0 --set-ctrl pan_absolute=0
v4l2-ctl -d /dev/video0 --set-ctrl tilt_absolute=0
we set the permissions:
$ chmod 755 unzoom.sh
Web pages - all files must be in the /var/www:
index.html:
<html>
<head>
</head>
<body>
<body style="width:600px">
<iframe src="status.html" name="status" width="600px" height="25px" frameBorder="0" marginwidth="0" marginheight="0">
<p>Your browser does not support iframes.</p>
</iframe>
<div>
<iframe src="button.html" name="button" width="130px" height="640px" frameBorder="0" marginwidth="0" marginheight="0">
<p>Your browser does not support iframes.</p>
</iframe>
<iframe src="http://camerastreamurl:8081" name="camera" width="360px" height="640px" frameBorder="0" marginwidth="0" marginheight="0">
<p>Your browser does not support iframes.</p>
</iframe>
</div>
</body>
</html>
status.html:
<html>
<head>
<meta http-equiv="refresh" content="2">
</head>
<body>
<body style="width:550px">
<iframe src="http://yoururl:8000" name="status" width="550px" height="25px" frameBorder="0">
<p>Your browser does not support iframes.</p>
</iframe>
</body>
</html>
button.html:
<html>
<head>
</head>
<body>
<body style="width:125px">
<iframe src="" name="empty" style="display:none;"></iframe>
<iframe src="" name="empty1" style="display:none;"></iframe>
<iframe src="" name="empty2" style="display:none;"></iframe>
<iframe src="" name="empty3" style="display:none;"></iframe>
<form action="http://yoururl:8000" method="get" target="empty">
<div>
<input type="submit" name="output_port" value="disarm" style="height:50;width:120;font-weight:bold;">
<input type="submit" name="output_port" value="arm" style="height:50;width:120;font-weight:bold;">
<a href="http://yoururl:8080/0/detection/start" target="empty1"><input type="button" value="start cam1" style="height:50;width:120;font-weight:bold;"></a>
<a href="http://yoururl:8080/0/detection/pause" target="empty2"><input type="button" value="pause cam1" style="height:50;width:120;font-weight:bold;"></a>
<a href="http://yoururl:8080/0/action/snapshot" target="empty3"><input type="button" value="snapshot cam1" style="height:50;width:120;font-weight:bold;"></a>
<a href="alarmpy.log" target="_blank"><input type="button" value="event log" style="height:50; width:120; font-weight:bold;"></a>
<input type="submit" name="output_port" value="wipe" style="height:50; width:120; font-weight:bold;">
<input type="submit" name="output_port" value="zoom" style="height:50; width:120; font-weight:bold;">
<input type="submit" name="output_port" value="unzoom" style="height:50; width:120; font-weight:bold;">
<input type="submit" name="output_port" value="rst" style="height:50; width:120; font-weight:bold;">
</div>
</form>
</body>
</html>
Comments
Post a Comment