Browse Source

Linting.

pyserial_fix
Scott 11 years ago
parent
commit
28896caa64
8 changed files with 187 additions and 133 deletions
  1. +7
    -6
      alarmdecoder/__init__.py
  2. +45
    -34
      alarmdecoder/decoder.py
  3. +74
    -44
      alarmdecoder/devices.py
  4. +5
    -4
      alarmdecoder/messages.py
  5. +1
    -1
      alarmdecoder/tests/test_zonetracking.py
  6. +8
    -6
      alarmdecoder/util.py
  7. +37
    -34
      alarmdecoder/zonetracking.py
  8. +10
    -4
      setup.py

+ 7
- 6
alarmdecoder/__init__.py View File

@@ -1,7 +1,8 @@
from decoder import AlarmDecoder
import devices
import util
import messages
import zonetracking
from alarmdecoder.decoder import AlarmDecoder
import alarmdecoder.decoder
import alarmdecoder.devices
import alarmdecoder.util
import alarmdecoder.messages
import alarmdecoder.zonetracking


__all__ = ['decoder', 'devices', 'util', 'messages', 'zonetracking']
__all__ = ['AlarmDecoder', 'decoder', 'devices', 'util', 'messages', 'zonetracking']

+ 45
- 34
alarmdecoder/decoder.py View File

@@ -5,10 +5,9 @@ Provides the full AlarmDecoder class.
""" """


import time import time
import threading


from .event import event from .event import event
from .util import CommError, NoDeviceError
from .util import InvalidMessageError
from .messages import Message, ExpanderMessage, RFMessage, LRRMessage from .messages import Message, ExpanderMessage, RFMessage, LRRMessage
from .zonetracking import Zonetracker from .zonetracking import Zonetracker


@@ -45,13 +44,13 @@ class AlarmDecoder(object):
on_write = event.Event('Called when data has been written to the device.') on_write = event.Event('Called when data has been written to the device.')


# Constants # Constants
F1 = unichr(1) + unichr(1) + unichr(1)
KEY_F1 = unichr(1) + unichr(1) + unichr(1)
"""Represents panel function key #1""" """Represents panel function key #1"""
F2 = unichr(2) + unichr(2) + unichr(2)
KEY_F2 = unichr(2) + unichr(2) + unichr(2)
"""Represents panel function key #2""" """Represents panel function key #2"""
F3 = unichr(3) + unichr(3) + unichr(3)
KEY_F3 = unichr(3) + unichr(3) + unichr(3)
"""Represents panel function key #3""" """Represents panel function key #3"""
F4 = unichr(4) + unichr(4) + unichr(4)
KEY_F4 = unichr(4) + unichr(4) + unichr(4)
"""Represents panel function key #4""" """Represents panel function key #4"""


BATTERY_TIMEOUT = 30 BATTERY_TIMEOUT = 30
@@ -63,7 +62,8 @@ class AlarmDecoder(object):
""" """
Constructor Constructor


:param device: The low-level device used for this Alarm Decoder interface.
:param device: The low-level device used for this Alarm Decoder
interface.
:type device: Device :type device: Device
""" """
self._device = device self._device = device
@@ -92,7 +92,7 @@ class AlarmDecoder(object):
""" """
return self return self


def __exit__(self, type, value, traceback):
def __exit__(self, exc_type, exc_value, traceback):
""" """
Support for context manager __exit__. Support for context manager __exit__.
""" """
@@ -115,7 +115,8 @@ class AlarmDecoder(object):


:param baudrate: The baudrate used for the device. :param baudrate: The baudrate used for the device.
:type baudrate: int :type baudrate: int
:param no_reader_thread: Specifies whether or not the automatic reader thread should be started or not
:param no_reader_thread: Specifies whether or not the automatic reader
thread should be started or not
:type no_reader_thread: bool :type no_reader_thread: bool
""" """
self._wire_events() self._wire_events()
@@ -155,7 +156,8 @@ class AlarmDecoder(object):
""" """
config_string = '' config_string = ''


# HACK: Both of these methods are ugly.. but I can't think of an elegant way of doing it.
# HACK: Both of these methods are ugly.. but I can't think of an
# elegant way of doing it.


#config_string += 'ADDRESS={0}&'.format(self.address) #config_string += 'ADDRESS={0}&'.format(self.address)
#config_string += 'CONFIGBITS={0:x}&'.format(self.configbits) #config_string += 'CONFIGBITS={0:x}&'.format(self.configbits)
@@ -166,13 +168,20 @@ class AlarmDecoder(object):
#config_string += 'DEDUPLICATE={0}'.format('Y' if self.deduplicate else 'N') #config_string += 'DEDUPLICATE={0}'.format('Y' if self.deduplicate else 'N')


config_entries = [] config_entries = []
config_entries.append(('ADDRESS', '{0}'.format(self.address)))
config_entries.append(('CONFIGBITS', '{0:x}'.format(self.configbits)))
config_entries.append(('MASK', '{0:x}'.format(self.address_mask)))
config_entries.append(('EXP', ''.join(['Y' if z else 'N' for z in self.emulate_zone])))
config_entries.append(('REL', ''.join(['Y' if r else 'N' for r in self.emulate_relay])))
config_entries.append(('LRR', 'Y' if self.emulate_lrr else 'N'))
config_entries.append(('DEDUPLICATE', 'Y' if self.deduplicate else 'N'))
config_entries.append(('ADDRESS',
'{0}'.format(self.address)))
config_entries.append(('CONFIGBITS',
'{0:x}'.format(self.configbits)))
config_entries.append(('MASK',
'{0:x}'.format(self.address_mask)))
config_entries.append(('EXP',
''.join(['Y' if z else 'N' for z in self.emulate_zone])))
config_entries.append(('REL',
''.join(['Y' if r else 'N' for r in self.emulate_relay])))
config_entries.append(('LRR',
'Y' if self.emulate_lrr else 'N'))
config_entries.append(('DEDUPLICATE',
'Y' if self.deduplicate else 'N'))


config_string = '&'.join(['='.join(t) for t in config_entries]) config_string = '&'.join(['='.join(t) for t in config_entries])


@@ -199,7 +208,9 @@ class AlarmDecoder(object):
# #
# Format (expander index, channel) # Format (expander index, channel)
if isinstance(zone, tuple): if isinstance(zone, tuple):
zone = self._zonetracker._expander_to_zone(*zone)
expander_idx, channel = zone

zone = self._zonetracker.expander_to_zone(expander_idx, channel)


status = 2 if simulate_wire_problem else 1 status = 2 if simulate_wire_problem else 1


@@ -313,22 +324,22 @@ class AlarmDecoder(object):
""" """
_, config_string = data.split('>') _, config_string = data.split('>')
for setting in config_string.split('&'): for setting in config_string.split('&'):
k, v = setting.split('=')
if k == 'ADDRESS':
self.address = int(v)
elif k == 'CONFIGBITS':
self.configbits = int(v, 16)
elif k == 'MASK':
self.address_mask = int(v, 16)
elif k == 'EXP':
self.emulate_zone = [v[z] == 'Y' for z in range(5)]
elif k == 'REL':
self.emulate_relay = [v[r] == 'Y' for r in range(4)]
elif k == 'LRR':
self.emulate_lrr = (v == 'Y')
elif k == 'DEDUPLICATE':
self.deduplicate = (v == 'Y')
key, val = setting.split('=')
if key == 'ADDRESS':
self.address = int(val)
elif key == 'CONFIGBITS':
self.configbits = int(val, 16)
elif key == 'MASK':
self.address_mask = int(val, 16)
elif key == 'EXP':
self.emulate_zone = [val[z] == 'Y' for z in range(5)]
elif key == 'REL':
self.emulate_relay = [val[r] == 'Y' for r in range(4)]
elif key == 'LRR':
self.emulate_lrr = (val == 'Y')
elif key == 'DEDUPLICATE':
self.deduplicate = (val == 'Y')


self.on_config_received() self.on_config_received()




+ 74
- 44
alarmdecoder/devices.py View File

@@ -13,8 +13,7 @@ import serial.tools.list_ports
import socket import socket


from OpenSSL import SSL, crypto from OpenSSL import SSL, crypto
from pyftdi.pyftdi.ftdi import *
from pyftdi.pyftdi.usbtools import *
from pyftdi.pyftdi.ftdi import Ftdi, FtdiError
from .util import CommError, TimeoutError, NoDeviceError from .util import CommError, TimeoutError, NoDeviceError
from .event import event from .event import event


@@ -46,7 +45,7 @@ class Device(object):
""" """
return self return self


def __exit__(self, type, value, traceback):
def __exit__(self, exc_type, exc_value, traceback):
""" """
Support for context manager __exit__. Support for context manager __exit__.
""" """
@@ -96,7 +95,7 @@ class Device(object):
self._read_thread.stop() self._read_thread.stop()
self._device.close() self._device.close()


except:
except Exception:
pass pass


self.on_close() self.on_close()
@@ -136,14 +135,12 @@ class Device(object):
try: try:
self._device.read_line(timeout=self.READ_TIMEOUT) self._device.read_line(timeout=self.READ_TIMEOUT)


except TimeoutError, err:
except TimeoutError:
pass pass


except Exception, err:
except Exception:
self._running = False self._running = False


#raise err

time.sleep(0.01) time.sleep(0.01)




@@ -192,9 +189,11 @@ class USBDevice(Device):
@classmethod @classmethod
def find(cls, device=None): def find(cls, device=None):
""" """
Factory method that returns the requested USBDevice device, or the first device.
Factory method that returns the requested USBDevice device, or the
first device.


:param device: Tuple describing the USB device to open, as returned by find_all().
:param device: Tuple describing the USB device to open, as returned
by find_all().
:type device: tuple :type device: tuple


:returns: USBDevice object utilizing the specified device. :returns: USBDevice object utilizing the specified device.
@@ -236,7 +235,7 @@ class USBDevice(Device):
try: try:
cls.__detect_thread.stop() cls.__detect_thread.stop()


except:
except Exception:
pass pass


@property @property
@@ -305,28 +304,32 @@ class USBDevice(Device):
""" """
Constructor Constructor


:param interface: May specify either the serial number or the device index.
:param interface: May specify either the serial number or the device
index.
:type interface: str or int :type interface: str or int
""" """
Device.__init__(self) Device.__init__(self)


self._device = Ftdi() self._device = Ftdi()


self._interface = 0
self._device_number = 0 self._device_number = 0
self._serial_number = None self._serial_number = None
self.interface = interface
self._vendor_id = USBDevice.FTDI_VENDOR_ID self._vendor_id = USBDevice.FTDI_VENDOR_ID
self._product_id = USBDevice.FTDI_PRODUCT_ID self._product_id = USBDevice.FTDI_PRODUCT_ID
self._endpoint = 0 self._endpoint = 0
self._description = None self._description = None


self.interface = interface

def open(self, baudrate=BAUDRATE, no_reader_thread=False): def open(self, baudrate=BAUDRATE, no_reader_thread=False):
""" """
Opens the device. Opens the device.


:param baudrate: The baudrate to use. :param baudrate: The baudrate to use.
:type baudrate: int :type baudrate: int
:param no_reader_thread: Whether or not to automatically start the reader thread.
:param no_reader_thread: Whether or not to automatically start the
reader thread.
:type no_reader_thread: bool :type no_reader_thread: bool


:raises: NoDeviceError :raises: NoDeviceError
@@ -370,10 +373,10 @@ class USBDevice(Device):
try: try:
Device.close(self) Device.close(self)


# HACK: Probably should fork pyftdi and make this call in .close().
# HACK: Probably should fork pyftdi and make this call in .close()
self._device.usb_dev.attach_kernel_driver(self._device_number) self._device.usb_dev.attach_kernel_driver(self._device_number)


except:
except Exception:
pass pass


def write(self, data): def write(self, data):
@@ -416,7 +419,8 @@ class USBDevice(Device):


:param timeout: Read timeout :param timeout: Read timeout
:type timeout: float :type timeout: float
:param purge_buffer: Indicates whether to purge the buffer prior to reading.
:param purge_buffer: Indicates whether to purge the buffer prior to
reading.
:type purge_buffer: bool :type purge_buffer: bool


:returns: The line that was read. :returns: The line that was read.
@@ -427,6 +431,7 @@ class USBDevice(Device):
self._buffer = '' self._buffer = ''


def timeout_event(): def timeout_event():
"""Handles read timeout event"""
timeout_event.reading = False timeout_event.reading = False


timeout_event.reading = True timeout_event.reading = True
@@ -451,7 +456,8 @@ class USBDevice(Device):
if self._buffer[-2] == "\r": if self._buffer[-2] == "\r":
self._buffer = self._buffer[:-2] self._buffer = self._buffer[:-2]


# ignore if we just got \r\n with nothing else in the buffer.
# Ignore if we just got \r\n with nothing else
# in the buffer.
if len(self._buffer) != 0: if len(self._buffer) != 0:
got_line = True got_line = True
break break
@@ -531,17 +537,17 @@ class USBDevice(Device):
try: try:
current_devices = set(USBDevice.find_all()) current_devices = set(USBDevice.find_all())


new_devices = [d for d in current_devices if d not in last_devices]
removed_devices = [d for d in last_devices if d not in current_devices]
new_devices = [dev for dev in current_devices if dev not in last_devices]
removed_devices = [dev for dev in last_devices if dev not in current_devices]
last_devices = current_devices last_devices = current_devices


for d in new_devices:
self.on_attached(device=d)
for dev in new_devices:
self.on_attached(device=dev)


for d in removed_devices:
self.on_detached(device=d)
for dev in removed_devices:
self.on_detached(device=dev)


except CommError, err:
except CommError:
pass pass


time.sleep(0.25) time.sleep(0.25)
@@ -575,7 +581,7 @@ class SerialDevice(Device):
else: else:
devices = serial.tools.list_ports.comports() devices = serial.tools.list_ports.comports()


except SerialException, err:
except serial.SerialException, err:
raise CommError('Error enumerating serial devices: {0}'.format(str(err)), err) raise CommError('Error enumerating serial devices: {0}'.format(str(err)), err)


return devices return devices
@@ -610,7 +616,8 @@ class SerialDevice(Device):


self._port = interface self._port = interface
self._id = interface self._id = interface
self._device = serial.Serial(timeout=0, writeTimeout=0) # Timeout = non-blocking to match pyftdi.
# Timeout = non-blocking to match pyftdi.
self._device = serial.Serial(timeout=0, writeTimeout=0)


def open(self, baudrate=BAUDRATE, no_reader_thread=False): def open(self, baudrate=BAUDRATE, no_reader_thread=False):
""" """
@@ -618,7 +625,8 @@ class SerialDevice(Device):


:param baudrate: The baudrate to use with the device. :param baudrate: The baudrate to use with the device.
:type baudrate: int :type baudrate: int
:param no_reader_thread: Whether or not to automatically start the reader thread.
:param no_reader_thread: Whether or not to automatically start the
reader thread.
:type no_reader_thread: bool :type no_reader_thread: bool


:raises: NoDeviceError :raises: NoDeviceError
@@ -635,12 +643,13 @@ class SerialDevice(Device):
# Open the device and start up the reader thread. # Open the device and start up the reader thread.
try: try:
self._device.open() self._device.open()
self._device.baudrate = baudrate # NOTE: Setting the baudrate before opening the
# port caused issues with Moschip 7840/7820
# USB Serial Driver converter. (mos7840)
#
# Moving it to this point seems to resolve
# all issues with it.
# NOTE: Setting the baudrate before opening the
# port caused issues with Moschip 7840/7820
# USB Serial Driver converter. (mos7840)
#
# Moving it to this point seems to resolve
# all issues with it.
self._device.baudrate = baudrate


except (serial.SerialException, ValueError), err: except (serial.SerialException, ValueError), err:
raise NoDeviceError('Error opening device on port {0}.'.format(self._port), err) raise NoDeviceError('Error opening device on port {0}.'.format(self._port), err)
@@ -661,7 +670,7 @@ class SerialDevice(Device):
try: try:
Device.close(self) Device.close(self)


except:
except Exception:
pass pass


def write(self, data): def write(self, data):
@@ -708,13 +717,19 @@ class SerialDevice(Device):


:param timeout: The read timeout. :param timeout: The read timeout.
:type timeout: float :type timeout: float
:param purge_buffer: Indicates whether to purge the buffer prior to reading.
:param purge_buffer: Indicates whether to purge the buffer prior to
reading.
:type purge_buffer: bool :type purge_buffer: bool


:returns: The line read. :returns: The line read.
:raises: CommError, TimeoutError :raises: CommError, TimeoutError
""" """

if purge_buffer:
self._buffer = ''

def timeout_event(): def timeout_event():
"""Handles read timeout event"""
timeout_event.reading = False timeout_event.reading = False


timeout_event.reading = True timeout_event.reading = True
@@ -731,7 +746,8 @@ class SerialDevice(Device):
while timeout_event.reading: while timeout_event.reading:
buf = self._device.read(1) buf = self._device.read(1)


if buf != '' and buf != "\xff": # AD2SERIAL specifically apparently sends down \xFF on boot.
# NOTE: AD2SERIAL apparently sends down \xFF on boot.
if buf != '' and buf != "\xff":
self._buffer += buf self._buffer += buf


if buf == "\n": if buf == "\n":
@@ -739,7 +755,8 @@ class SerialDevice(Device):
if self._buffer[-2] == "\r": if self._buffer[-2] == "\r":
self._buffer = self._buffer[:-2] self._buffer = self._buffer[:-2]


# ignore if we just got \r\n with nothing else in the buffer.
# Ignore if we just got \r\n with nothing else
# in the buffer.
if len(self._buffer) != 0: if len(self._buffer) != 0:
got_line = True got_line = True
break break
@@ -854,7 +871,8 @@ class SocketDevice(Device):
@property @property
def ssl_ca(self): def ssl_ca(self):
""" """
Retrieves the SSL Certificate Authority certificate used for authentication.
Retrieves the SSL Certificate Authority certificate used for
authentication.


:returns: The CA path :returns: The CA path
""" """
@@ -891,7 +909,8 @@ class SocketDevice(Device):


:param baudrate: The baudrate to use :param baudrate: The baudrate to use
:type baudrate: int :type baudrate: int
:param no_reader_thread: Whether or not to automatically open the reader thread.
:param no_reader_thread: Whether or not to automatically open the reader
thread.
:type no_reader_thread: bool :type no_reader_thread: bool


:raises: NoDeviceError, CommError :raises: NoDeviceError, CommError
@@ -932,11 +951,12 @@ class SocketDevice(Device):
self._device.shutdown() self._device.shutdown()


else: else:
self._device.shutdown(socket.SHUT_RDWR) # Make sure that it closes immediately.
# Make sure that it closes immediately.
self._device.shutdown(socket.SHUT_RDWR)


Device.close(self) Device.close(self)


except Exception, ex:
except Exception:
pass pass


def write(self, data): def write(self, data):
@@ -987,7 +1007,8 @@ class SocketDevice(Device):


:param timeout: The read timeout. :param timeout: The read timeout.
:type timeout: float :type timeout: float
:param purge_buffer: Indicates whether to purge the buffer prior to reading.
:param purge_buffer: Indicates whether to purge the buffer prior to
reading.
:type purge_buffer: bool :type purge_buffer: bool


:returns: The line read from the device. :returns: The line read from the device.
@@ -998,6 +1019,7 @@ class SocketDevice(Device):
self._buffer = '' self._buffer = ''


def timeout_event(): def timeout_event():
"""Handles read timeout event"""
timeout_event.reading = False timeout_event.reading = False


timeout_event.reading = True timeout_event.reading = True
@@ -1022,7 +1044,8 @@ class SocketDevice(Device):
if self._buffer[-2] == "\r": if self._buffer[-2] == "\r":
self._buffer = self._buffer[:-2] self._buffer = self._buffer[:-2]


# ignore if we just got \r\n with nothing else in the buffer.
# Ignore if we just got \r\n with nothing else
# in the buffer.
if len(self._buffer) != 0: if len(self._buffer) != 0:
got_line = True got_line = True
break break
@@ -1051,6 +1074,10 @@ class SocketDevice(Device):
return ret return ret


def _init_ssl(self): def _init_ssl(self):
"""
Initializes our device as an SSL connection.
"""

try: try:
ctx = SSL.Context(SSL.TLSv1_METHOD) ctx = SSL.Context(SSL.TLSv1_METHOD)


@@ -1078,4 +1105,7 @@ class SocketDevice(Device):
raise CommError('Error setting up SSL connection.', err) raise CommError('Error setting up SSL connection.', err)


def _verify_ssl_callback(self, connection, x509, errnum, errdepth, ok): def _verify_ssl_callback(self, connection, x509, errnum, errdepth, ok):
"""
SSL verification callback.
"""
return ok return ok

+ 5
- 4
alarmdecoder/messages.py View File

@@ -110,12 +110,12 @@ class Message(BaseMessage):


:raises: InvalidMessageError :raises: InvalidMessageError
""" """
m = self._regex.match(data)
match = self._regex.match(data)


if m is None:
if match is None:
raise InvalidMessageError('Received invalid message: {0}'.format(data)) raise InvalidMessageError('Received invalid message: {0}'.format(data))


self.bitfield, self.numeric_code, self.panel_data, alpha = m.group(1, 2, 3, 4)
self.bitfield, self.numeric_code, self.panel_data, alpha = match.group(1, 2, 3, 4)
self.mask = int(self.panel_data[3:3+8], 16) self.mask = int(self.panel_data[3:3+8], 16)


is_bit_set = lambda bit: not self.bitfield[bit] == "0" is_bit_set = lambda bit: not self.bitfield[bit] == "0"
@@ -141,7 +141,8 @@ class Message(BaseMessage):
self.text = alpha.strip('"') self.text = alpha.strip('"')


if int(self.panel_data[19:21], 16) & 0x01 > 0: if int(self.panel_data[19:21], 16) & 0x01 > 0:
self.cursor_location = int(self.bitfield[21:23], 16) # Alpha character index that the cursor is on.
# Current cursor location on the alpha display.
self.cursor_location = int(self.bitfield[21:23], 16)




class ExpanderMessage(BaseMessage): class ExpanderMessage(BaseMessage):


+ 1
- 1
alarmdecoder/tests/test_zonetracking.py View File

@@ -26,7 +26,7 @@ class TestZonetracking(TestCase):


def _build_expander_message(self, msg): def _build_expander_message(self, msg):
msg = ExpanderMessage(msg) msg = ExpanderMessage(msg)
zone = self._zonetracker._expander_to_zone(msg.address, msg.channel)
zone = self._zonetracker.expander_to_zone(msg.address, msg.channel)


return zone, msg return zone, msg




+ 8
- 6
alarmdecoder/util.py View File

@@ -66,13 +66,13 @@ class Firmware(object):
""" """
Perform the actual firmware upload to the device. Perform the actual firmware upload to the device.
""" """
with open(filename) as f:
for line in f:
with open(filename) as upload_file:
for line in upload_file:
line = line.rstrip() line = line.rstrip()


if line[0] == ':': if line[0] == ':':
dev.write(line + "\r") dev.write(line + "\r")
res = dev.read_line(timeout=10.0)
dev.read_line(timeout=10.0)


if progress_callback is not None: if progress_callback is not None:
progress_callback(Firmware.STAGE_UPLOADING) progress_callback(Firmware.STAGE_UPLOADING)
@@ -81,9 +81,11 @@ class Firmware(object):


def read_until(pattern, timeout=0.0): def read_until(pattern, timeout=0.0):
""" """
Read characters until a specific pattern is found or the timeout is hit.
Read characters until a specific pattern is found or the timeout is
hit.
""" """
def timeout_event(): def timeout_event():
"""Handles the read timeout event."""
timeout_event.reading = False timeout_event.reading = False


timeout_event.reading = True timeout_event.reading = True
@@ -93,7 +95,6 @@ class Firmware(object):
timer = threading.Timer(timeout, timeout_event) timer = threading.Timer(timeout, timeout_event)
timer.start() timer.start()


buf = ''
position = 0 position = 0


while timeout_event.reading: while timeout_event.reading:
@@ -108,7 +109,7 @@ class Firmware(object):
else: else:
position = 0 position = 0


except Exception, err:
except Exception:
pass pass


if timer: if timer:
@@ -118,6 +119,7 @@ class Firmware(object):
raise TimeoutError('Timeout while waiting for line terminator.') raise TimeoutError('Timeout while waiting for line terminator.')


def stage_callback(stage): def stage_callback(stage):
"""Callback to update progress for the specified stage."""
if progress_callback is not None: if progress_callback is not None:
progress_callback(stage) progress_callback(stage)




+ 37
- 34
alarmdecoder/zonetracking.py View File

@@ -82,7 +82,7 @@ class Zonetracker(object):
""" """
if isinstance(message, ExpanderMessage): if isinstance(message, ExpanderMessage):
if message.type == ExpanderMessage.ZONE: if message.type == ExpanderMessage.ZONE:
zone = self._expander_to_zone(message.address, message.channel)
zone = self.expander_to_zone(message.address, message.channel)


status = Zone.CLEAR status = Zone.CLEAR
if message.value == 1: if message.value == 1:
@@ -90,9 +90,10 @@ class Zonetracker(object):
elif message.value == 2: elif message.value == 2:
status = Zone.CHECK status = Zone.CHECK


# NOTE: Expander zone faults are handled differently than regular messages.
# We don't include them in self._zones_faulted because they are not reported
# by the panel in it's rolling list of faults.
# NOTE: Expander zone faults are handled differently than
# regular messages. We don't include them in
# self._zones_faulted because they are not reported
# by the panel in it's rolling list of faults.
try: try:
self._update_zone(zone, status=status) self._update_zone(zone, status=status)


@@ -102,12 +103,13 @@ class Zonetracker(object):
else: else:
# Panel is ready, restore all zones. # Panel is ready, restore all zones.
# #
# NOTE: This will need to be updated to support panels with multiple partitions.
# In it's current state a ready on partition #1 will end up clearing all zones, even
# if they exist elsewhere and it shouldn't.
# NOTE: This will need to be updated to support panels with
# multiple partitions. In it's current state a ready on
# partition #1 will end up clearing all zones, even if they
# exist elsewhere and it shouldn't.
if message.ready: if message.ready:
for z in self._zones_faulted:
self._update_zone(z, Zone.CLEAR)
for zone in self._zones_faulted:
self._update_zone(zone, Zone.CLEAR)


self._last_zone_fault = 0 self._last_zone_fault = 0


@@ -121,17 +123,18 @@ class Zonetracker(object):
except ValueError: except ValueError:
zone = int(message.numeric_code, 16) zone = int(message.numeric_code, 16)


# NOTE: Odd case for ECP failures. Apparently they report as zone 191 (0xBF) regardless
# of whether or not the 3-digit mode is enabled... so we have to pull it out of the
# alpha message.
# NOTE: Odd case for ECP failures. Apparently they report as
# zone 191 (0xBF) regardless of whether or not the
# 3-digit mode is enabled... so we have to pull it out
# of the alpha message.
if zone == 191: if zone == 191:
zone_regex = re.compile('^CHECK (\d+).*$') zone_regex = re.compile('^CHECK (\d+).*$')


m = zone_regex.match(message.text)
if m is None:
match = zone_regex.match(message.text)
if match is None:
return return


zone = m.group(1)
zone = match.group(1)


# Add new zones and clear expired ones. # Add new zones and clear expired ones.
if zone in self._zones_faulted: if zone in self._zones_faulted:
@@ -152,6 +155,25 @@ class Zonetracker(object):


self._clear_expired_zones() self._clear_expired_zones()


def expander_to_zone(self, address, channel):
"""
Convert an address and channel into a zone number.

:param address: The expander address
:type address: int
:param channel: The channel
:type channel: int

:returns: The zone number associated with an address and channel.
"""

# TODO: This is going to need to be reworked to support the larger
# panels without fixed addressing on the expanders.

idx = address - 7 # Expanders start at address 7.

return address + channel + (idx * 7) + 1

def _clear_zones(self, zone): def _clear_zones(self, zone):
""" """
Clear all expired zones from our status list. Clear all expired zones from our status list.
@@ -277,22 +299,3 @@ class Zonetracker(object):
:returns: Whether or not the zone is expired. :returns: Whether or not the zone is expired.
""" """
return time.time() > self._zones[zone].timestamp + Zonetracker.EXPIRE return time.time() > self._zones[zone].timestamp + Zonetracker.EXPIRE

def _expander_to_zone(self, address, channel):
"""
Convert an address and channel into a zone number.

:param address: The expander address
:type address: int
:param channel: The channel
:type channel: int

:returns: The zone number associated with an address and channel.
"""

# TODO: This is going to need to be reworked to support the larger
# panels without fixed addressing on the expanders.

idx = address - 7 # Expanders start at address 7.

return address + channel + (idx * 7) + 1

+ 10
- 4
setup.py View File

@@ -1,12 +1,17 @@
"""Setup script"""

from setuptools import setup from setuptools import setup


def readme(): def readme():
with open('README.md') as f:
return f.read()
"""Returns the contents of README.md"""

with open('README.md') as readme_file:
return readme_file.read()


setup(name='alarmdecoder', setup(name='alarmdecoder',
version='0.5', version='0.5',
description='Python interface library for the Alarm Decoder (AD2) family of alarm devices, including: the AD2USB, AD2SERIAL and AD2PI.',
description='Python interface library for the Alarm Decoder (AD2) family '
'of alarm devices, including: the AD2USB, AD2SERIAL and AD2PI.',
long_description=readme(), long_description=readme(),
classifiers=[ classifiers=[
'Development Status :: 4 - Beta', 'Development Status :: 4 - Beta',
@@ -17,7 +22,8 @@ setup(name='alarmdecoder',
'Topic :: Home Automation', 'Topic :: Home Automation',
'Topic :: Security', 'Topic :: Security',
], ],
keywords='alarmdecoder alarm decoder ad2 ad2usb ad2serial ad2pi security ademco dsc',
keywords='alarmdecoder alarm decoder ad2 ad2usb ad2serial ad2pi security '
'ademco dsc',
url='http://github.com/nutechsoftware/alarmdecoder', url='http://github.com/nutechsoftware/alarmdecoder',
author='Nu Tech Software Solutions, Inc.', author='Nu Tech Software Solutions, Inc.',
author_email='general@support.nutech.com', author_email='general@support.nutech.com',


Loading…
Cancel
Save