Browse Source

Initial rename.

pyserial_fix
Scott Petersen 11 years ago
parent
commit
fa1eef5758
26 changed files with 205 additions and 203 deletions
  1. +12
    -12
      README.md
  2. +9
    -9
      bin/ad2-firmwareupload
  3. +2
    -2
      bin/ad2-sslterm
  4. +4
    -4
      docs/Makefile
  5. +14
    -14
      docs/conf.py
  6. +3
    -3
      docs/index.rst
  7. +2
    -2
      docs/make.bat
  8. +3
    -3
      docs/modules.rst
  9. +2
    -2
      docs/pyad2.event.rst
  10. +9
    -9
      docs/pyad2.rst
  11. +4
    -4
      examples/alarm_email.py
  12. +4
    -4
      examples/basics.py
  13. +5
    -5
      examples/detection.py
  14. +4
    -4
      examples/rf_device.py
  15. +4
    -4
      examples/serialport.py
  16. +4
    -4
      examples/socket_example.py
  17. +4
    -4
      examples/ssl_socket.py
  18. +9
    -9
      examples/virtual_zone_expander.py
  19. +2
    -2
      pyad2/__init__.py
  20. +8
    -8
      pyad2/ad2.py
  21. +8
    -6
      pyad2/devices.py
  22. +2
    -1
      pyad2/messages.py
  23. +77
    -77
      pyad2/tests/test_ad2.py
  24. +4
    -5
      pyad2/util.py
  25. +1
    -1
      pyad2/zonetracking.py
  26. +5
    -5
      setup.py

+ 12
- 12
README.md View File

@@ -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.

+ 9
- 9
bin/ad2-firmwareupload View File

@@ -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()



+ 2
- 2
bin/ad2-sslterm View File

@@ -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


+ 4
- 4
docs/Makefile View File

@@ -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:


+ 14
- 14
docs/conf.py View File

@@ -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.


+ 3
- 3
docs/index.rst View File

@@ -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


+ 2
- 2
docs/make.bat View File

@@ -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
)


+ 3
- 3
docs/modules.rst View File

@@ -1,7 +1,7 @@
pyad2
========
alarmdecoder
============

.. toctree::
:maxdepth: 4

pyad2
alarmdecoder

+ 2
- 2
docs/pyad2.event.rst View File

@@ -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:


+ 9
- 9
docs/pyad2.rst View File

@@ -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


+ 4
- 4
examples/alarm_email.py View File

@@ -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)


+ 4
- 4
examples/basics.py View File

@@ -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']



+ 5
- 5
examples/detection.py View File

@@ -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']



+ 4
- 4
examples/rf_device.py View File

@@ -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']



+ 4
- 4
examples/serialport.py View File

@@ -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']



+ 4
- 4
examples/socket_example.py View File

@@ -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']



+ 4
- 4
examples/ssl_socket.py View File

@@ -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']



+ 9
- 9
examples/virtual_zone_expander.py View File

@@ -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


+ 2
- 2
pyad2/__init__.py View File

@@ -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']

+ 8
- 8
pyad2/ad2.py View File

@@ -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)



+ 8
- 6
pyad2/devices.py View File

@@ -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


+ 2
- 1
pyad2/messages.py View File

@@ -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>
"""


+ 77
- 77
pyad2/tests/test_ad2.py View File

@@ -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)

+ 4
- 5
pyad2/util.py View File

@@ -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
- 1
pyad2/zonetracking.py View File

@@ -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>
"""


+ 5
- 5
setup.py View File

@@ -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',


Loading…
Cancel
Save