@@ -1,31 +1,31 @@ | |||
pyAD2 - Python library for the AD2 device family | |||
================================================ | |||
alarmdecoder - Python library for the Alarm Decoder (AD2) device family | |||
======================================================================= | |||
This Python module aims to provide a consistent interface for all of the AD2 | |||
product line, including the AD2USB, AD2SERIAL and AD2PI devices. This also | |||
includes devices that have been exposed via [ser2sock](http://github.com/nutechsoftware/ser2sock) and supports encryption | |||
via SSL/TLS. | |||
This Python module aims to provide a consistent interface for all of the Alarm | |||
Decoder product line, including the AD2USB, AD2SERIAL and AD2PI devices. This | |||
also includes devices that have been exposed via [ser2sock](http://github.com/nutechsoftware/ser2sock) and supports | |||
encryption via SSL/TLS. | |||
Installation | |||
------------ | |||
pyAD2 can be installed through pip: | |||
pip install pyad2 | |||
alarmdecoder can be installed through pip: | |||
pip install alarmdecoder | |||
or from source: | |||
python setup.py install | |||
Requirements | |||
------------ | |||
* [pyopenssl](https://launchpad.net/pyopenssl) | |||
* [pyftdi](https://github.com/eblot/pyftdi) >= 0.9.0 | |||
* [pyusb](http://sourceforge.net/apps/trac/pyusb/) >= 1.0.0b1 | |||
* [pyserial](http://pyserial.sourceforge.net/) >= 2.7 | |||
* [pyftdi](https://github.com/eblot/pyftdi) >= 0.9.0 | |||
* [pyopenssl](https://launchpad.net/pyopenssl) | |||
Documentation | |||
------------- | |||
API documentation can be found [here](http://github.com/nutechsoftware/pyad2/tree/master/docs/_build/html). | |||
API documentation can be found [here](http://github.com/nutechsoftware/alarmdecoder/tree/master/docs/_build/html). | |||
Examples | |||
-------- | |||
@@ -36,4 +36,4 @@ Basic usage: | |||
``` | |||
Please see the [examples](http://github.com/nutechsoftware/pyad2/tree/master/examples) directory for more. | |||
Please see the [examples](http://github.com/nutechsoftware/alarmdecoder/tree/master/examples) directory for more. |
@@ -1,25 +1,25 @@ | |||
#!/usr/bin/env python | |||
import sys, time | |||
import pyad2 | |||
import alarmdecoder | |||
def handle_firmware(stage): | |||
if stage == pyad2.util.Firmware.STAGE_START: | |||
if stage == alarmdecoder.util.Firmware.STAGE_START: | |||
handle_firmware.wait_tick = 0 | |||
handle_firmware.upload_tick = 0 | |||
elif stage == pyad2.util.Firmware.STAGE_WAITING: | |||
elif stage == alarmdecoder.util.Firmware.STAGE_WAITING: | |||
if handle_firmware.wait_tick == 0: | |||
sys.stdout.write('Waiting for device.') | |||
handle_firmware.wait_tick += 1 | |||
sys.stdout.write('.') | |||
sys.stdout.flush() | |||
elif stage == pyad2.util.Firmware.STAGE_BOOT: | |||
elif stage == alarmdecoder.util.Firmware.STAGE_BOOT: | |||
if handle_firmware.wait_tick > 0: print "" | |||
print "Rebooting device.." | |||
elif stage == pyad2.util.Firmware.STAGE_LOAD: | |||
elif stage == alarmdecoder.util.Firmware.STAGE_LOAD: | |||
print 'Waiting for boot loader..' | |||
elif stage == pyad2.util.Firmware.STAGE_UPLOADING: | |||
elif stage == alarmdecoder.util.Firmware.STAGE_UPLOADING: | |||
if handle_firmware.upload_tick == 0: | |||
sys.stdout.write('Uploading firmware.') | |||
@@ -28,7 +28,7 @@ def handle_firmware(stage): | |||
if handle_firmware.upload_tick % 30 == 0: | |||
sys.stdout.write('.') | |||
sys.stdout.flush() | |||
elif stage == pyad2.util.Firmware.STAGE_DONE: | |||
elif stage == alarmdecoder.util.Firmware.STAGE_DONE: | |||
print "\r\nDone!" | |||
def main(): | |||
@@ -45,11 +45,11 @@ def main(): | |||
print "Flashing device: {0}\r\nFirmware: {1}".format(device, firmware) | |||
dev = pyad2.devices.SerialDevice(interface=device) | |||
dev = alarmdecoder.devices.SerialDevice(interface=device) | |||
dev.open(baudrate=19200) | |||
time.sleep(3) | |||
pyad2.util.Firmware.upload(dev, firmware, handle_firmware) | |||
alarmdecoder.util.Firmware.upload(dev, firmware, handle_firmware) | |||
dev.close() | |||
@@ -1,6 +1,6 @@ | |||
#!/usr/bin/env python | |||
import pyad2 | |||
import alarmdecoder | |||
import sys, select | |||
import termios, tty | |||
import time | |||
@@ -23,7 +23,7 @@ def main(): | |||
try: | |||
print "Opening connection to {0}:{1}\r".format(host, port) | |||
dev = pyad2.devices.SocketDevice(interface=(host, int(port))) | |||
dev = alarmdecoder.devices.SocketDevice(interface=(host, int(port))) | |||
dev.ssl = True | |||
dev.ssl_certificate = client_cert | |||
dev.ssl_key = client_key | |||
@@ -85,17 +85,17 @@ qthelp: | |||
@echo | |||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \ | |||
".qhcp project file in $(BUILDDIR)/qthelp, like this:" | |||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyad2.qhcp" | |||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/alarmdecoder.qhcp" | |||
@echo "To view the help file:" | |||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyad2.qhc" | |||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/alarmdecoder.qhc" | |||
devhelp: | |||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp | |||
@echo | |||
@echo "Build finished." | |||
@echo "To view the help file:" | |||
@echo "# mkdir -p $$HOME/.local/share/devhelp/pyad2" | |||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyad2" | |||
@echo "# mkdir -p $$HOME/.local/share/devhelp/alarmdecoder" | |||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/alarmdecoder" | |||
@echo "# devhelp" | |||
epub: | |||
@@ -1,6 +1,6 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# pyad2 documentation build configuration file, created by | |||
# alarmdecoder documentation build configuration file, created by | |||
# sphinx-quickstart on Sat Jun 8 14:38:46 2013. | |||
# | |||
# This file is execfile()d with the current directory set to its containing dir. | |||
@@ -40,8 +40,8 @@ source_suffix = '.rst' | |||
master_doc = 'index' | |||
# General information about the project. | |||
project = u'pyad2' | |||
copyright = u'2013, Author' | |||
project = u'alarmdecoder' | |||
copyright = u'2013, Nu Tech Software Solutions, Inc.' | |||
# The version info for the project you're documenting, acts as replacement for | |||
# |version| and |release|, also used in various other places throughout the | |||
@@ -167,7 +167,7 @@ html_static_path = ['_static'] | |||
#html_file_suffix = None | |||
# Output file base name for HTML help builder. | |||
htmlhelp_basename = 'pyad2doc' | |||
htmlhelp_basename = 'alarmdecoderdoc' | |||
# -- Options for LaTeX output -------------------------------------------------- | |||
@@ -186,8 +186,8 @@ latex_elements = { | |||
# Grouping the document tree into LaTeX files. List of tuples | |||
# (source start file, target name, title, author, documentclass [howto/manual]). | |||
latex_documents = [ | |||
('index', 'pyad2.tex', u'pyad2 Documentation', | |||
u'Author', 'manual'), | |||
('index', 'alarmdecoder.tex', u'AlarmDecoder Documentation', | |||
u'Nu Tech Software Solutions, Inc.', 'manual'), | |||
] | |||
# The name of an image file (relative to this directory) to place at the top of | |||
@@ -216,8 +216,8 @@ latex_documents = [ | |||
# One entry per manual page. List of tuples | |||
# (source start file, name, description, authors, manual section). | |||
man_pages = [ | |||
('index', 'pyad2', u'pyad2 Documentation', | |||
[u'Author'], 1) | |||
('index', 'alarmdecoder', u'AlarmDecoder Documentation', | |||
[u'Nu Tech Software Solutions, Inc.'], 1) | |||
] | |||
# If true, show URL addresses after external links. | |||
@@ -230,8 +230,8 @@ man_pages = [ | |||
# (source start file, target name, title, author, | |||
# dir menu entry, description, category) | |||
texinfo_documents = [ | |||
('index', 'pyad2', u'pyad2 Documentation', | |||
u'Author', 'pyad2', 'One line description of project.', | |||
('index', 'alarmdecoder', u'AlarmDecoder Documentation', | |||
u'Nu Tech Software Solutions, Inc.', 'alarmdecoder', 'Python library for the Alarm Decoder (AD2) device family.', | |||
'Miscellaneous'), | |||
] | |||
@@ -251,10 +251,10 @@ texinfo_documents = [ | |||
# -- Options for Epub output --------------------------------------------------- | |||
# Bibliographic Dublin Core info. | |||
epub_title = u'pyad2' | |||
epub_author = u'Author' | |||
epub_publisher = u'Author' | |||
epub_copyright = u'2013, Author' | |||
epub_title = u'alarmdecoder' | |||
epub_author = u'Nu Tech Software Solutions, Inc.' | |||
epub_publisher = u'Nu Tech Software Solutions, Inc.' | |||
epub_copyright = u'2013, Nu Tech Software Solutions, Inc.' | |||
# The language of the text. It defaults to the language option | |||
# or en if the language is not set. | |||
@@ -1,9 +1,9 @@ | |||
.. pyad2 documentation master file, created by | |||
.. alarmdecoder documentation master file, created by | |||
sphinx-quickstart on Sat Jun 8 14:38:46 2013. | |||
You can adapt this file completely to your liking, but it should at least | |||
contain the root `toctree` directive. | |||
Welcome to pyad2's documentation! | |||
Welcome to alarmdecoder's documentation! | |||
==================================== | |||
Contents: | |||
@@ -11,7 +11,7 @@ Contents: | |||
.. toctree:: | |||
:maxdepth: 4 | |||
pyad2 | |||
alarmdecoder | |||
Indices and tables | |||
@@ -115,9 +115,9 @@ if "%1" == "qthelp" ( | |||
echo. | |||
echo.Build finished; now you can run "qcollectiongenerator" with the ^ | |||
.qhcp project file in %BUILDDIR%/qthelp, like this: | |||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyad2.qhcp | |||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\alarmdecoder.qhcp | |||
echo.To view the help file: | |||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyad2.ghc | |||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\alarmdecoder.ghc | |||
goto end | |||
) | |||
@@ -1,7 +1,7 @@ | |||
pyad2 | |||
======== | |||
alarmdecoder | |||
============ | |||
.. toctree:: | |||
:maxdepth: 4 | |||
pyad2 | |||
alarmdecoder |
@@ -4,7 +4,7 @@ event Package | |||
:mod:`event` Package | |||
-------------------- | |||
.. automodule:: pyad2.event | |||
.. automodule:: alarmdecoder.event | |||
:members: | |||
:undoc-members: | |||
:show-inheritance: | |||
@@ -12,7 +12,7 @@ event Package | |||
:mod:`event` Module | |||
------------------- | |||
.. automodule:: pyad2.event.event | |||
.. automodule:: alarmdecoder.event.event | |||
:members: | |||
:undoc-members: | |||
:show-inheritance: | |||
@@ -1,10 +1,10 @@ | |||
pyad2 Package | |||
alarmdecoder Package | |||
================ | |||
:mod:`ad2` Module | |||
:mod:`alarmdecoder` Module | |||
-------------------- | |||
.. automodule:: pyad2.ad2 | |||
.. automodule:: alarmdecoder.alarmdecoder | |||
:members: | |||
:undoc-members: | |||
:show-inheritance: | |||
@@ -12,7 +12,7 @@ pyad2 Package | |||
:mod:`devices` Module | |||
--------------------- | |||
.. automodule:: pyad2.devices | |||
.. automodule:: alarmdecoder.devices | |||
:members: | |||
:undoc-members: | |||
:show-inheritance: | |||
@@ -20,7 +20,7 @@ pyad2 Package | |||
:mod:`util` Module | |||
------------------ | |||
.. automodule:: pyad2.util | |||
.. automodule:: alarmdecoder.util | |||
:members: | |||
:undoc-members: | |||
:show-inheritance: | |||
@@ -28,7 +28,7 @@ pyad2 Package | |||
:mod:`zonetracking` Module | |||
-------------------------- | |||
.. automodule:: pyad2.zonetracking | |||
.. automodule:: alarmdecoder.zonetracking | |||
:members: | |||
:undoc-members: | |||
:show-inheritance: | |||
@@ -36,7 +36,7 @@ pyad2 Package | |||
:mod:`panels` Module | |||
-------------------- | |||
.. automodule:: pyad2.panels | |||
.. automodule:: alarmdecoder.panels | |||
:members: | |||
:undoc-members: | |||
:show-inheritance: | |||
@@ -44,7 +44,7 @@ pyad2 Package | |||
:mod:`messages` Module | |||
---------------------- | |||
.. automodule:: pyad2.messages | |||
.. automodule:: alarmdecoder.messages | |||
:members: | |||
:undoc-members: | |||
:show-inheritance: | |||
@@ -54,5 +54,5 @@ Subpackages | |||
.. toctree:: | |||
pyad2.event | |||
alarmdecoder.event | |||
@@ -1,8 +1,8 @@ | |||
import time | |||
import smtplib | |||
from email.mime.text import MIMEText | |||
from pyad2 import AD2 | |||
from pyad2.devices import USBDevice | |||
from alarmdecoder import AlarmDecoder | |||
from alarmdecoder.devices import USBDevice | |||
# Configuration values | |||
SUBJECT = "Alarm Decoder - ALARM" | |||
@@ -20,7 +20,7 @@ def main(): | |||
""" | |||
try: | |||
# Retrieve the first USB device | |||
device = AD2(USBDevice.find()) | |||
device = AlarmDecoder(USBDevice.find()) | |||
# Set up an event handler and open the device | |||
device.on_alarm += handle_alarm | |||
@@ -33,7 +33,7 @@ def main(): | |||
def handle_alarm(sender, *args, **kwargs): | |||
""" | |||
Handles alarm events from the AD2. | |||
Handles alarm events from the AlarmDecoder. | |||
""" | |||
status = kwargs['status'] | |||
text = "Alarm status: {0}".format(status) | |||
@@ -1,6 +1,6 @@ | |||
import time | |||
from pyad2 import AD2 | |||
from pyad2.devices import USBDevice | |||
from alarmdecoder import AlarmDecoder | |||
from alarmdecoder.devices import USBDevice | |||
def main(): | |||
""" | |||
@@ -8,7 +8,7 @@ def main(): | |||
""" | |||
try: | |||
# Retrieve the first USB device | |||
device = AD2(USBDevice.find()) | |||
device = AlarmDecoder(USBDevice.find()) | |||
# Set up an event handler and open the device | |||
device.on_message += handle_message | |||
@@ -21,7 +21,7 @@ def main(): | |||
def handle_message(sender, *args, **kwargs): | |||
""" | |||
Handles message events from the AD2. | |||
Handles message events from the AlarmDecoder. | |||
""" | |||
msg = kwargs['message'] | |||
@@ -1,6 +1,6 @@ | |||
import time | |||
from pyad2 import AD2 | |||
from pyad2.devices import USBDevice | |||
from alarmdecoder import AlarmDecoder | |||
from alarmdecoder.devices import USBDevice | |||
__devices = {} | |||
@@ -33,12 +33,12 @@ def main(): | |||
def create_device(device_args): | |||
""" | |||
Creates an AD2 from the specified USB device arguments. | |||
Creates an AlarmDecoder from the specified USB device arguments. | |||
:param device_args: Tuple containing information on the USB device to open. | |||
:type device_args: Tuple (vid, pid, serialnumber, interface_count, description) | |||
""" | |||
device = AD2(USBDevice.find(device_args)) | |||
device = AlarmDecoder(USBDevice.find(device_args)) | |||
device.on_message += handle_message | |||
device.open() | |||
@@ -46,7 +46,7 @@ def create_device(device_args): | |||
def handle_message(sender, *args, **kwargs): | |||
""" | |||
Handles message events from the AD2. | |||
Handles message events from the AlarmDecoder. | |||
""" | |||
msg = kwargs['message'] | |||
@@ -1,6 +1,6 @@ | |||
import time | |||
from pyad2 import AD2 | |||
from pyad2.devices import USBDevice | |||
from alarmdecoder import AlarmDecoder | |||
from alarmdecoder.devices import USBDevice | |||
RF_DEVICE_SERIAL_NUMBER = '0252254' | |||
@@ -18,7 +18,7 @@ def main(): | |||
""" | |||
try: | |||
# Retrieve the first USB device | |||
device = AD2(USBDevice.find()) | |||
device = AlarmDecoder(USBDevice.find()) | |||
# Set up an event handler and open the device | |||
device.on_rfx_message += handle_rfx | |||
@@ -31,7 +31,7 @@ def main(): | |||
def handle_rfx(sender, *args, **kwargs): | |||
""" | |||
Handles RF message events from the AD2. | |||
Handles RF message events from the AlarmDecoder. | |||
""" | |||
msg = kwargs['message'] | |||
@@ -1,6 +1,6 @@ | |||
import time | |||
from pyad2 import AD2 | |||
from pyad2.devices import SerialDevice | |||
from alarmdecoder import AlarmDecoder | |||
from alarmdecoder.devices import SerialDevice | |||
# Configuration values | |||
SERIAL_DEVICE = '/dev/ttyUSB0' | |||
@@ -12,7 +12,7 @@ def main(): | |||
""" | |||
try: | |||
# Retrieve the specified serial device. | |||
device = AD2(SerialDevice(interface=SERIAL_DEVICE)) | |||
device = AlarmDecoder(SerialDevice(interface=SERIAL_DEVICE)) | |||
# Set up an event handler and open the device | |||
device.on_message += handle_message | |||
@@ -28,7 +28,7 @@ def main(): | |||
def handle_message(sender, *args, **kwargs): | |||
""" | |||
Handles message events from the AD2. | |||
Handles message events from the AlarmDecoder. | |||
""" | |||
msg = kwargs['message'] | |||
@@ -1,6 +1,6 @@ | |||
import time | |||
from pyad2 import AD2 | |||
from pyad2.devices import SocketDevice | |||
from alarmdecoder import AlarmDecoder | |||
from alarmdecoder.devices import SocketDevice | |||
# Configuration values | |||
HOSTNAME = 'localhost' | |||
@@ -13,7 +13,7 @@ def main(): | |||
""" | |||
try: | |||
# Retrieve an AD2 device that has been exposed with ser2sock on localhost:10000. | |||
device = AD2(SocketDevice(interface=(HOSTNAME, PORT))) | |||
device = AlarmDecoder(SocketDevice(interface=(HOSTNAME, PORT))) | |||
# Set up an event handler and open the device | |||
device.on_message += handle_message | |||
@@ -26,7 +26,7 @@ def main(): | |||
def handle_message(sender, *args, **kwargs): | |||
""" | |||
Handles message events from the AD2. | |||
Handles message events from the AlarmDecoder. | |||
""" | |||
msg = kwargs['message'] | |||
@@ -1,6 +1,6 @@ | |||
import time | |||
from pyad2 import AD2 | |||
from pyad2.devices import SocketDevice | |||
from alarmdecoder import AlarmDecoder | |||
from alarmdecoder.devices import SocketDevice | |||
# Configuration values | |||
HOSTNAME = 'localhost' | |||
@@ -27,7 +27,7 @@ def main(): | |||
ssl_device.ssl_key = SSL_KEY # Client private key | |||
ssl_device.ssl_certificate = SSL_CERT # Client certificate | |||
device = AD2(ssl_device) | |||
device = AlarmDecoder(ssl_device) | |||
# Set up an event handler and open the device | |||
device.on_message += handle_message | |||
@@ -40,7 +40,7 @@ def main(): | |||
def handle_message(sender, *args, **kwargs): | |||
""" | |||
Handles message events from the AD2. | |||
Handles message events from the AlarmDecoder. | |||
""" | |||
msg = kwargs['message'] | |||
@@ -1,6 +1,6 @@ | |||
import time | |||
from pyad2 import AD2 | |||
from pyad2.devices import USBDevice | |||
from alarmdecoder import AlarmDecoder | |||
from alarmdecoder.devices import USBDevice | |||
# Configuration values | |||
TARGET_ZONE = 41 | |||
@@ -12,23 +12,23 @@ def main(): | |||
restores it. | |||
This is an advanced feature that allows you to emulate a virtual zone. When | |||
the AD2 is configured to emulate a relay expander we can fault and restore | |||
those zones programmatically at will. These events can also be seen by others, | |||
such as home automation platforms which allows you to connect other devices or | |||
services and monitor them as you would any pyhysical zone. | |||
the Alarm Decoder is configured to emulate a relay expander we can fault and | |||
restore those zones programmatically at will. These events can also be seen by | |||
others, such as home automation platforms which allows you to connect other | |||
devices or services and monitor them as you would any pyhysical zone. | |||
For example, you could connect a ZigBee device and receiver and fault or | |||
restore it's zone(s) based on the data received. | |||
In order for this to happen you need to perform a couple configuration steps: | |||
1. Enable zone expander emulation on your AD2 device by hitting '!' in a | |||
terminal and going through the prompts. | |||
1. Enable zone expander emulation on your Alarm Decoder device by hitting '!' | |||
in a terminal and going through the prompts. | |||
2. Enable the zone expander in your panel programming. | |||
""" | |||
try: | |||
# Retrieve the first USB device | |||
device = AD2(USBDevice.find()) | |||
device = AlarmDecoder(USBDevice.find()) | |||
# Set up an event handlers and open the device | |||
device.on_zone_fault += handle_zone_fault | |||
@@ -1,7 +1,7 @@ | |||
from ad2 import AD2 | |||
from decoder import AlarmDecoder | |||
import devices | |||
import util | |||
import messages | |||
import zonetracking | |||
__all__ = ['ad2', 'devices', 'util', 'messages', 'zonetracking'] | |||
__all__ = ['decoder', 'devices', 'util', 'messages', 'zonetracking'] |
@@ -1,5 +1,5 @@ | |||
""" | |||
Provides the full AD2 class and factory. | |||
Provides the full AlarmDecoder class. | |||
.. moduleauthor:: Scott Petersen <scott@nutech.com> | |||
""" | |||
@@ -12,9 +12,9 @@ from .util import CommError, NoDeviceError | |||
from .messages import Message, ExpanderMessage, RFMessage, LRRMessage | |||
from .zonetracking import Zonetracker | |||
class AD2(object): | |||
class AlarmDecoder(object): | |||
""" | |||
High-level wrapper around AD2 devices. | |||
High-level wrapper around Alarm Decoder (AD2) devices. | |||
""" | |||
# High-level Events | |||
@@ -62,7 +62,7 @@ class AD2(object): | |||
""" | |||
Constructor | |||
:param device: The low-level device used for this AD2 interface. | |||
:param device: The low-level device used for this Alarm Decoder interface. | |||
:type device: Device | |||
""" | |||
self._device = device | |||
@@ -102,7 +102,7 @@ class AD2(object): | |||
@property | |||
def id(self): | |||
""" | |||
The ID of the AD2 device. | |||
The ID of the Alarm Decoder device. | |||
:returns: The identification string for the device. | |||
""" | |||
@@ -134,7 +134,7 @@ class AD2(object): | |||
def send(self, data): | |||
""" | |||
Sends data to the AD2 device. | |||
Sends data to the Alarm Decoder device. | |||
:param data: The data to send. | |||
:type data: str | |||
@@ -371,14 +371,14 @@ class AD2(object): | |||
if message.battery_low == self._battery_status[0]: | |||
self._battery_status = (self._battery_status[0], time.time()) | |||
else: | |||
if message.battery_low == True or time.time() > self._battery_status[1] + AD2.BATTERY_TIMEOUT: | |||
if message.battery_low == True or time.time() > self._battery_status[1] + AlarmDecoder.BATTERY_TIMEOUT: | |||
self._battery_status = (message.battery_low, time.time()) | |||
self.on_low_battery(status=self._battery_status) | |||
if message.fire_alarm == self._fire_status[0]: | |||
self._fire_status = (self._fire_status[0], time.time()) | |||
else: | |||
if message.fire_alarm == True or time.time() > self._fire_status[1] + AD2.FIRE_TIMEOUT: | |||
if message.fire_alarm == True or time.time() > self._fire_status[1] + AlarmDecoder.FIRE_TIMEOUT: | |||
self._fire_status = (message.fire_alarm, time.time()) | |||
self.on_fire(status=self._fire_status) | |||
@@ -1,5 +1,5 @@ | |||
""" | |||
Contains different types of devices belonging to the AD2 family. | |||
Contains different types of devices belonging to the Alarm Decoder (AD2) family. | |||
.. moduleauthor:: Scott Petersen <scott@nutech.com> | |||
""" | |||
@@ -18,7 +18,7 @@ from .event import event | |||
class Device(object): | |||
""" | |||
Generic parent device to all AD2 products. | |||
Generic parent device to all Alarm Decoder (AD2) products. | |||
""" | |||
# Generic device events | |||
@@ -494,8 +494,10 @@ class USBDevice(Device): | |||
""" | |||
Constructor | |||
:param factory: AD2Factory object to use with the thread. | |||
:type factory: AD2Factory | |||
:param on_attached: Function to call when a device is attached. | |||
:type on_attached: function | |||
:param on_detached: Function to call when a device is detached. | |||
:type on_detached: function | |||
""" | |||
threading.Thread.__init__(self) | |||
@@ -763,8 +765,8 @@ class SerialDevice(Device): | |||
class SocketDevice(Device): | |||
""" | |||
Device that supports communication with an AD2 that is exposed via ser2sock or another | |||
Serial to IP interface. | |||
Device that supports communication with an Alarm Decoder (AD2) that is | |||
exposed via ser2sock or another Serial to IP interface. | |||
""" | |||
@property | |||
@@ -1,5 +1,6 @@ | |||
""" | |||
Message representations received from the panel through the AD2 devices. | |||
Message representations received from the panel through the Alarm Decoder (AD2) | |||
devices. | |||
.. moduleauthor:: Scott Petersen <scott@nutech.com> | |||
""" | |||
@@ -3,13 +3,13 @@ import time | |||
from unittest import TestCase | |||
from mock import Mock, MagicMock, patch | |||
from ..ad2 import AD2 | |||
from ..decoder import AlarmDecoder | |||
from ..devices import USBDevice | |||
from ..messages import Message, RFMessage, LRRMessage, ExpanderMessage | |||
from ..event.event import Event, EventHandler | |||
from ..zonetracking import Zonetracker | |||
class TestAD2(TestCase): | |||
class TestAlarmDecoder(TestCase): | |||
def setUp(self): | |||
self._panicked = False | |||
self._relay_changed = False | |||
@@ -30,28 +30,28 @@ class TestAD2(TestCase): | |||
self._device.on_read = EventHandler(Event(), self._device) | |||
self._device.on_write = EventHandler(Event(), self._device) | |||
self._ad2 = AD2(self._device) | |||
self._ad2._zonetracker = Mock(spec=Zonetracker) | |||
self._ad2._zonetracker.on_fault = EventHandler(Event(), self._ad2._zonetracker) | |||
self._ad2._zonetracker.on_restore = EventHandler(Event(), self._ad2._zonetracker) | |||
self._ad2.on_panic += self.on_panic | |||
self._ad2.on_relay_changed += self.on_relay_changed | |||
self._ad2.on_power_changed += self.on_power_changed | |||
self._ad2.on_alarm += self.on_alarm | |||
self._ad2.on_bypass += self.on_bypass | |||
self._ad2.on_low_battery += self.on_battery | |||
self._ad2.on_fire += self.on_fire | |||
self._ad2.on_arm += self.on_arm | |||
self._ad2.on_disarm += self.on_disarm | |||
self._ad2.on_config_received += self.on_config | |||
self._ad2.on_message += self.on_message | |||
self._ad2.on_rfx_message += self.on_rfx_message | |||
self._ad2.on_lrr_message += self.on_lrr_message | |||
self._ad2.address_mask = int('ffffffff', 16) | |||
self._ad2.open() | |||
self._decoder = AlarmDecoder(self._device) | |||
self._decoder._zonetracker = Mock(spec=Zonetracker) | |||
self._decoder._zonetracker.on_fault = EventHandler(Event(), self._decoder._zonetracker) | |||
self._decoder._zonetracker.on_restore = EventHandler(Event(), self._decoder._zonetracker) | |||
self._decoder.on_panic += self.on_panic | |||
self._decoder.on_relay_changed += self.on_relay_changed | |||
self._decoder.on_power_changed += self.on_power_changed | |||
self._decoder.on_alarm += self.on_alarm | |||
self._decoder.on_bypass += self.on_bypass | |||
self._decoder.on_low_battery += self.on_battery | |||
self._decoder.on_fire += self.on_fire | |||
self._decoder.on_arm += self.on_arm | |||
self._decoder.on_disarm += self.on_disarm | |||
self._decoder.on_config_received += self.on_config | |||
self._decoder.on_message += self.on_message | |||
self._decoder.on_rfx_message += self.on_rfx_message | |||
self._decoder.on_lrr_message += self.on_lrr_message | |||
self._decoder.address_mask = int('ffffffff', 16) | |||
self._decoder.open() | |||
def tearDown(self): | |||
pass | |||
@@ -96,170 +96,170 @@ class TestAD2(TestCase): | |||
self._lrr_message_received = True | |||
def test_open(self): | |||
self._ad2.open() | |||
self._decoder.open() | |||
self._device.open.assert_any_calls() | |||
def test_close(self): | |||
self._ad2.open() | |||
self._decoder.open() | |||
self._ad2.close() | |||
self._decoder.close() | |||
self._device.close.assert_any_calls() | |||
def test_send(self): | |||
self._ad2.send('test') | |||
self._decoder.send('test') | |||
self._device.write.assert_called_with('test') | |||
def test_get_config(self): | |||
self._ad2.get_config() | |||
self._decoder.get_config() | |||
self._device.write.assert_called_with("C\r") | |||
def test_save_config(self): | |||
self._ad2.save_config() | |||
self._decoder.save_config() | |||
self._device.write.assert_any_calls() | |||
def test_reboot(self): | |||
self._ad2.reboot() | |||
self._decoder.reboot() | |||
self._device.write.assert_called_with('=') | |||
def test_fault(self): | |||
self._ad2.fault_zone(1) | |||
self._decoder.fault_zone(1) | |||
self._device.write.assert_called_with("L{0:02}{1}\r".format(1, 1)) | |||
def test_fault_wireproblem(self): | |||
self._ad2.fault_zone(1, simulate_wire_problem=True) | |||
self._decoder.fault_zone(1, simulate_wire_problem=True) | |||
self._device.write.assert_called_with("L{0:02}{1}\r".format(1, 2)) | |||
def test_clear_zone(self): | |||
self._ad2.clear_zone(1) | |||
self._decoder.clear_zone(1) | |||
self._device.write.assert_called_with("L{0:02}0\r".format(1)) | |||
def test_message(self): | |||
msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertIsInstance(msg, Message) | |||
self._ad2._on_read(self, data='[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self._decoder._on_read(self, data='[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertTrue(self._message_received) | |||
def test_message_kpe(self): | |||
msg = self._ad2._handle_message('!KPE:[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('!KPE:[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertIsInstance(msg, Message) | |||
self._ad2._on_read(self, data='[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self._decoder._on_read(self, data='[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertTrue(self._message_received) | |||
def test_expander_message(self): | |||
msg = self._ad2._handle_message('!EXP:07,01,01') | |||
msg = self._decoder._handle_message('!EXP:07,01,01') | |||
self.assertIsInstance(msg, ExpanderMessage) | |||
def test_relay_message(self): | |||
self._ad2.open() | |||
msg = self._ad2._handle_message('!REL:12,01,01') | |||
self._decoder.open() | |||
msg = self._decoder._handle_message('!REL:12,01,01') | |||
self.assertIsInstance(msg, ExpanderMessage) | |||
self.assertEquals(self._relay_changed, True) | |||
def test_rfx_message(self): | |||
msg = self._ad2._handle_message('!RFX:0180036,80') | |||
msg = self._decoder._handle_message('!RFX:0180036,80') | |||
self.assertIsInstance(msg, RFMessage) | |||
self.assertTrue(self._rfx_message_received) | |||
def test_panic(self): | |||
self._ad2.open() | |||
self._decoder.open() | |||
msg = self._ad2._handle_message('!LRR:012,1,ALARM_PANIC') | |||
msg = self._decoder._handle_message('!LRR:012,1,ALARM_PANIC') | |||
self.assertEquals(self._panicked, True) | |||
msg = self._ad2._handle_message('!LRR:012,1,CANCEL') | |||
msg = self._decoder._handle_message('!LRR:012,1,CANCEL') | |||
self.assertEquals(self._panicked, False) | |||
self.assertIsInstance(msg, LRRMessage) | |||
def test_config_message(self): | |||
self._ad2.open() | |||
self._decoder.open() | |||
msg = self._ad2._handle_message('!CONFIG>ADDRESS=18&CONFIGBITS=ff00&LRR=N&EXP=NNNNN&REL=NNNN&MASK=ffffffff&DEDUPLICATE=N') | |||
self.assertEquals(self._ad2.address, 18) | |||
self.assertEquals(self._ad2.configbits, int('ff00', 16)) | |||
self.assertEquals(self._ad2.address_mask, int('ffffffff', 16)) | |||
self.assertEquals(self._ad2.emulate_zone, [False for x in range(5)]) | |||
self.assertEquals(self._ad2.emulate_relay, [False for x in range(4)]) | |||
self.assertEquals(self._ad2.emulate_lrr, False) | |||
self.assertEquals(self._ad2.deduplicate, False) | |||
msg = self._decoder._handle_message('!CONFIG>ADDRESS=18&CONFIGBITS=ff00&LRR=N&EXP=NNNNN&REL=NNNN&MASK=ffffffff&DEDUPLICATE=N') | |||
self.assertEquals(self._decoder.address, 18) | |||
self.assertEquals(self._decoder.configbits, int('ff00', 16)) | |||
self.assertEquals(self._decoder.address_mask, int('ffffffff', 16)) | |||
self.assertEquals(self._decoder.emulate_zone, [False for x in range(5)]) | |||
self.assertEquals(self._decoder.emulate_relay, [False for x in range(4)]) | |||
self.assertEquals(self._decoder.emulate_lrr, False) | |||
self.assertEquals(self._decoder.deduplicate, False) | |||
self.assertEquals(self._got_config, True) | |||
def test_power_changed_event(self): | |||
msg = self._ad2._handle_message('[0000000100000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000100000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._power_changed, False) # Not set first time we hit it. | |||
msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._power_changed, False) | |||
msg = self._ad2._handle_message('[0000000100000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000100000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._power_changed, True) | |||
def test_alarm_event(self): | |||
msg = self._ad2._handle_message('[0000000000100000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000100000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._alarmed, False) # Not set first time we hit it. | |||
msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._alarmed, False) | |||
msg = self._ad2._handle_message('[0000000000100000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000100000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._alarmed, True) | |||
def test_zone_bypassed_event(self): | |||
msg = self._ad2._handle_message('[0000001000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000001000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._bypassed, False) # Not set first time we hit it. | |||
msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._bypassed, False) | |||
msg = self._ad2._handle_message('[0000001000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000001000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._bypassed, True) | |||
def test_armed_away_event(self): | |||
msg = self._ad2._handle_message('[0100000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0100000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._armed, False) # Not set first time we hit it. | |||
msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._armed, False) | |||
msg = self._ad2._handle_message('[0100000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0100000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._armed, True) | |||
self._armed = False | |||
msg = self._ad2._handle_message('[0010000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0010000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._armed, False) # Not set first time we hit it. | |||
msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._armed, False) | |||
msg = self._ad2._handle_message('[0010000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0010000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._armed, True) | |||
def test_battery_low_event(self): | |||
msg = self._ad2._handle_message('[0000000000010000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000010000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._battery[0], True) | |||
# force the timeout to expire. | |||
with patch.object(time, 'time', return_value=self._battery[1] + 35): | |||
msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._battery[0], False) | |||
def test_fire_alarm_event(self): | |||
msg = self._ad2._handle_message('[0000000000000100----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000000100----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._fire[0], True) | |||
# force the timeout to expire. | |||
with patch.object(time, 'time', return_value=self._fire[1] + 35): | |||
msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self.assertEquals(self._fire[0], False) | |||
def test_hit_for_faults(self): | |||
self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000],"Hit * for faults "') | |||
self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000],"Hit * for faults "') | |||
self._ad2._device.write.assert_called_with('*') | |||
self._decoder._device.write.assert_called_with('*') | |||
def test_zonetracker_update(self): | |||
msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self._ad2._zonetracker.update.assert_called_with(msg) | |||
msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||
self._decoder._zonetracker.update.assert_called_with(msg) |
@@ -1,10 +1,9 @@ | |||
""" | |||
Provides utility classes for the AD2 devices. | |||
Provides utility classes for the Alarm Decoder (AD2) devices. | |||
.. moduleauthor:: Scott Petersen <scott@nutech.com> | |||
""" | |||
import ad2 | |||
import time | |||
import threading | |||
@@ -34,7 +33,7 @@ class InvalidMessageError(Exception): | |||
class Firmware(object): | |||
""" | |||
Represents firmware for the AD2 devices. | |||
Represents firmware for the Alarm Decoder devices. | |||
""" | |||
# Constants | |||
@@ -48,14 +47,14 @@ class Firmware(object): | |||
@staticmethod | |||
def upload(dev, filename, progress_callback=None): | |||
""" | |||
Uploads firmware to an AD2 device. | |||
Uploads firmware to an Alarm Decoder device. | |||
:param filename: The firmware filename | |||
:type filename: str | |||
:param progress_callback: Callback function used to report progress. | |||
:type progress_callback: function | |||
:raises: util.NoDeviceError, util.TimeoutError | |||
:raises: NoDeviceError, TimeoutError | |||
""" | |||
def do_upload(): | |||
@@ -1,5 +1,5 @@ | |||
""" | |||
Provides zone tracking functionality for the AD2 device family. | |||
Provides zone tracking functionality for the Alarm Decoder (AD2) device family. | |||
.. moduleauthor:: Scott Petersen <scott@nutech.com> | |||
""" | |||
@@ -4,9 +4,9 @@ def readme(): | |||
with open('README.md') as f: | |||
return f.read() | |||
setup(name='pyad2', | |||
setup(name='alarmdecoder', | |||
version='0.5', | |||
description='Python interface library for the AD2 family of alarm devices.', | |||
description='Python interface library for the Alarm Decoder (AD2) family of alarm devices, including: the AD2USB, AD2SERIAL and AD2PI.', | |||
long_description=readme(), | |||
classifiers=[ | |||
'Development Status :: 4 - Beta', | |||
@@ -17,12 +17,12 @@ setup(name='pyad2', | |||
'Topic :: Home Automation', | |||
'Topic :: Security', | |||
], | |||
keywords='alarm data ad2 ad2usb ad2serial ad2pi security ademco dsc', | |||
url='http://github.com/nutechsoftware/pyad2', | |||
keywords='alarmdecoder alarm decoder ad2 ad2usb ad2serial ad2pi security ademco dsc', | |||
url='http://github.com/nutechsoftware/alarmdecoder', | |||
author='Nu Tech Software Solutions, Inc.', | |||
author_email='general@support.nutech.com', | |||
license='MIT', | |||
packages=['pyad2', 'pyad2.event'], | |||
packages=['alarmdecoder', 'alarmdecoder.event'], | |||
install_requires=[ | |||
'pyopenssl', | |||
'pyusb>=1.0.0b1', | |||