Browse Source

Merge pull request #3 from nutechsoftware/dev

Dev
pyserial_fix
endlesscoil 10 years ago
parent
commit
752183e8ad
5 changed files with 169 additions and 36 deletions
  1. +2
    -0
      .gitignore
  2. +31
    -3
      alarmdecoder/decoder.py
  3. +44
    -8
      alarmdecoder/devices.py
  4. +3
    -0
      alarmdecoder/event/event.py
  5. +89
    -25
      alarmdecoder/messages.py

+ 2
- 0
.gitignore View File

@@ -2,5 +2,7 @@ build/
dist dist
tmp tmp
*.pyc *.pyc
*.pyo
*.egg-info *.egg-info
bin/ad2-test bin/ad2-test
*~

+ 31
- 3
alarmdecoder/decoder.py View File

@@ -7,6 +7,7 @@ Provides the main AlarmDecoder class.
""" """


import time import time
import re


from .event import event from .event import event
from .util import InvalidMessageError from .util import InvalidMessageError
@@ -39,6 +40,7 @@ class AlarmDecoder(object):
on_expander_message = event.Event("This event is called when an :py:class:`~alarmdecoder.messages.ExpanderMessage` is received.\n\n**Callback definition:** *def callback(device, message)*") on_expander_message = event.Event("This event is called when an :py:class:`~alarmdecoder.messages.ExpanderMessage` is received.\n\n**Callback definition:** *def callback(device, message)*")
on_lrr_message = event.Event("This event is called when an :py:class:`~alarmdecoder.messages.LRRMessage` is received.\n\n**Callback definition:** *def callback(device, message)*") on_lrr_message = event.Event("This event is called when an :py:class:`~alarmdecoder.messages.LRRMessage` is received.\n\n**Callback definition:** *def callback(device, message)*")
on_rfx_message = event.Event("This event is called when an :py:class:`~alarmdecoder.messages.RFMessage` is received.\n\n**Callback definition:** *def callback(device, message)*") on_rfx_message = event.Event("This event is called when an :py:class:`~alarmdecoder.messages.RFMessage` is received.\n\n**Callback definition:** *def callback(device, message)*")
on_sending_received = event.Event("This event is called when a !Sending.done message is received from the AlarmDecoder.\n\n**Callback definition:** *def callback(device, status, message)*")


# Low-level Events # Low-level Events
on_open = event.Event("This event is called when the device has been opened.\n\n**Callback definition:** *def callback(device)*") on_open = event.Event("This event is called when the device has been opened.\n\n**Callback definition:** *def callback(device)*")
@@ -55,6 +57,8 @@ class AlarmDecoder(object):
"""Represents panel function key #3""" """Represents panel function key #3"""
KEY_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"""
KEY_PANIC = unichr(5) + unichr(5) + unichr(5)
"""Represents a panic keypress"""


BATTERY_TIMEOUT = 30 BATTERY_TIMEOUT = 30
"""Default timeout (in seconds) before the battery status reverts.""" """Default timeout (in seconds) before the battery status reverts."""
@@ -66,7 +70,7 @@ class AlarmDecoder(object):
"""The keypad address in use by the device.""" """The keypad address in use by the device."""
configbits = 0xFF00 configbits = 0xFF00
"""The configuration bits set on the device.""" """The configuration bits set on the device."""
address_mask = 0x00000000
address_mask = 0xFFFFFFFF
"""The address mask configured on the device.""" """The address mask configured on the device."""
emulate_zone = [False for _ in range(5)] emulate_zone = [False for _ in range(5)]
"""List containing the devices zone emulation status.""" """List containing the devices zone emulation status."""
@@ -200,8 +204,9 @@ class AlarmDecoder(object):
:param data: data to send :param data: data to send
:type data: string :type data: string
""" """

if self._device: if self._device:
self._device.write(data)
self._device.write(str(data))


def get_config(self): def get_config(self):
""" """
@@ -294,7 +299,11 @@ class AlarmDecoder(object):


:returns: :py:class:`~alarmdecoder.messages.Message` :returns: :py:class:`~alarmdecoder.messages.Message`
""" """
if data is None:

if data is not None:
data = data.lstrip('\0')

if data is None or data == '':
raise InvalidMessageError() raise InvalidMessageError()


msg = None msg = None
@@ -318,6 +327,9 @@ class AlarmDecoder(object):
elif data.startswith('!CONFIG'): elif data.startswith('!CONFIG'):
self._handle_config(data) self._handle_config(data)


elif data.startswith('!Sending'):
self._handle_sending(data)

return msg return msg


def _handle_keypad_message(self, data): def _handle_keypad_message(self, data):
@@ -421,6 +433,22 @@ class AlarmDecoder(object):


self.on_config_received() self.on_config_received()


def _handle_sending(self, data):
"""
Handles results of a keypress send.

:param data: Sending string to parse
:type data: string
"""

matches = re.match('^!Sending(\.{1,5})done.*', data)
if matches is not None:
good_send = False
if len(matches.group(1)) < 5:
good_send = True

self.on_sending_received(status=good_send, message=data)

def _update_internal_states(self, message): def _update_internal_states(self, message):
""" """
Updates internal device states. Updates internal device states.


+ 44
- 8
alarmdecoder/devices.py View File

@@ -25,7 +25,7 @@ import socket


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




@@ -149,8 +149,19 @@ class Device(object):
except TimeoutError: except TimeoutError:
pass pass


except Exception:
except InvalidMessageError:
pass

except SSL.WantReadError:
pass

except CommError, err:
self._device.close()

except Exception, err:
self._device.close()
self._running = False self._running = False
raise




class USBDevice(Device): class USBDevice(Device):
@@ -234,7 +245,10 @@ class USBDevice(Device):
""" """
cls.__detect_thread = USBDevice.DetectThread(on_attached, on_detached) cls.__detect_thread = USBDevice.DetectThread(on_attached, on_detached)


cls.find_all()
try:
cls.find_all()
except CommError:
pass


cls.__detect_thread.start() cls.__detect_thread.start()


@@ -390,6 +404,9 @@ class USBDevice(Device):
except Exception: except Exception:
pass pass


def fileno(self):
raise NotImplementedError('USB devices do not support fileno()')

def write(self, data): def write(self, data):
""" """
Writes data to the device. Writes data to the device.
@@ -648,8 +665,8 @@ class SerialDevice(Device):
# all issues with it. # all issues with it.
self._device.baudrate = baudrate self._device.baudrate = baudrate


except (serial.SerialException, ValueError), err:
raise NoDeviceError('Error opening device on port {0}.'.format(self._port), err)
except (serial.SerialException, ValueError, OSError), err:
raise NoDeviceError('Error opening device on {0}.'.format(self._port), err)


else: else:
self._running = True self._running = True
@@ -670,6 +687,9 @@ class SerialDevice(Device):
except Exception: except Exception:
pass pass


def fileno(self):
return self._device.fileno()

def write(self, data): def write(self, data):
""" """
Writes data to the device. Writes data to the device.
@@ -908,9 +928,15 @@ class SocketDevice(Device):
self._init_ssl() self._init_ssl()


self._device.connect((self._host, self._port)) self._device.connect((self._host, self._port))
#self._device.setblocking(1)


if self._use_ssl: if self._use_ssl:
self._device.do_handshake()
while True:
try:
self._device.do_handshake()
break
except SSL.WantReadError:
pass


self._id = '{0}:{1}'.format(self._host, self._port) self._id = '{0}:{1}'.format(self._host, self._port)


@@ -939,11 +965,14 @@ class SocketDevice(Device):
# Make sure that it closes immediately. # Make sure that it closes immediately.
self._device.shutdown(socket.SHUT_RDWR) self._device.shutdown(socket.SHUT_RDWR)


Device.close(self)

except Exception: except Exception:
pass pass


Device.close(self)

def fileno(self):
return self._device.fileno()

def write(self, data): def write(self, data):
""" """
Writes data to the device. Writes data to the device.
@@ -1033,6 +1062,13 @@ class SocketDevice(Device):
except socket.error, err: except socket.error, err:
raise CommError('Error reading from device: {0}'.format(str(err)), err) raise CommError('Error reading from device: {0}'.format(str(err)), err)


except SSL.SysCallError, err:
errno, msg = err
raise CommError('SSL error while reading from device: {0} ({1})'.format(msg, errno))

except Exception:
raise

else: else:
if got_line: if got_line:
ret, self._buffer = self._buffer, '' ret, self._buffer = self._buffer, ''


+ 3
- 0
alarmdecoder/event/event.py View File

@@ -31,6 +31,9 @@ class EventHandler(object):
self.event = event self.event = event
self.obj = obj self.obj = obj


def __iter__(self):
return iter(self._getfunctionlist())

def _getfunctionlist(self): def _getfunctionlist(self):


"""(internal use) """ """(internal use) """


+ 89
- 25
alarmdecoder/messages.py View File

@@ -13,6 +13,7 @@ devices.
""" """


import re import re
import datetime


from .util import InvalidMessageError from .util import InvalidMessageError


@@ -25,11 +26,14 @@ class BaseMessage(object):
raw = None raw = None
"""The raw message text""" """The raw message text"""


timestamp = None
"""The timestamp of the message"""

def __init__(self): def __init__(self):
""" """
Constructor Constructor
""" """
pass
self.timestamp = datetime.datetime.now()


def __str__(self): def __str__(self):
""" """
@@ -37,6 +41,22 @@ class BaseMessage(object):
""" """
return self.raw return self.raw


def dict(self, **kwargs):
"""
Dictionary representation.
"""
return dict(
time=self.timestamp,
mesg=self.raw,
**kwargs
)

def __repr__(self):
"""
String representation.
"""
return repr(self.dict())



class Message(BaseMessage): class Message(BaseMessage):
""" """
@@ -102,12 +122,6 @@ class Message(BaseMessage):
if data is not None: if data is not None:
self._parse_message(data) self._parse_message(data)


def __str__(self):
"""
String conversion operator.
"""
return self.raw

def _parse_message(self, data): def _parse_message(self, data):
""" """
Parse the message from the device. Parse the message from the device.
@@ -151,6 +165,37 @@ class Message(BaseMessage):
# Current cursor location on the alpha display. # Current cursor location on the alpha display.
self.cursor_location = int(self.bitfield[21:23], 16) self.cursor_location = int(self.bitfield[21:23], 16)


def dict(self, **kwargs):
"""
Dictionary representation.
"""
return dict(
time = self.timestamp,
bitfield = self.bitfield,
numeric_code = self.numeric_code,
panel_data = self.panel_data,
mask = self.mask,
ready = self.ready,
armed_away = self.armed_away,
armed_home = self.armed_home,
backlight_on = self.backlight_on,
programming_mode = self.programming_mode,
beeps = self.beeps,
zone_bypassed = self.zone_bypassed,
ac_power = self.ac_power,
chime_on = self.chime_on,
alarm_event_occurred = self.alarm_event_occurred,
alarm_sounding = self.alarm_sounding,
battery_low = self.battery_low,
entry_delay_off = self.entry_delay_off,
fire_alarm = self.fire_alarm,
check_zone = self.check_zone,
perimeter_only = self.perimeter_only,
text = self.text,
cursor_location = self.cursor_location,
**kwargs
)



class ExpanderMessage(BaseMessage): class ExpanderMessage(BaseMessage):
""" """
@@ -183,12 +228,6 @@ class ExpanderMessage(BaseMessage):
if data is not None: if data is not None:
self._parse_message(data) self._parse_message(data)


def __str__(self):
"""
String conversion operator.
"""
return self.raw

def _parse_message(self, data): def _parse_message(self, data):
""" """
Parse the raw message from the device. Parse the raw message from the device.
@@ -217,6 +256,18 @@ class ExpanderMessage(BaseMessage):
else: else:
raise InvalidMessageError('Unknown expander message header: {0}'.format(data)) raise InvalidMessageError('Unknown expander message header: {0}'.format(data))


def dict(self, **kwargs):
"""
Dictionary representation.
"""
return dict(
time = self.timestamp,
address = self.address,
channel = self.channel,
value = self.value,
**kwargs
)



class RFMessage(BaseMessage): class RFMessage(BaseMessage):
""" """
@@ -246,12 +297,6 @@ class RFMessage(BaseMessage):
if data is not None: if data is not None:
self._parse_message(data) self._parse_message(data)


def __str__(self):
"""
String conversion operator.
"""
return self.raw

def _parse_message(self, data): def _parse_message(self, data):
""" """
Parses the raw message from the device. Parses the raw message from the device.
@@ -282,6 +327,19 @@ class RFMessage(BaseMessage):
except ValueError: except ValueError:
raise InvalidMessageError('Received invalid message: {0}'.format(data)) raise InvalidMessageError('Received invalid message: {0}'.format(data))


def dict(self, **kwargs):
"""
Dictionary representation.
"""
return dict(
time = self.timestamp,
serial_number = self.serial_number,
value = self.value,
battery = self.battery,
supervision = self.supervision,
**kwargs
)



class LRRMessage(BaseMessage): class LRRMessage(BaseMessage):
""" """
@@ -307,12 +365,6 @@ class LRRMessage(BaseMessage):
if data is not None: if data is not None:
self._parse_message(data) self._parse_message(data)


def __str__(self):
"""
String conversion operator.
"""
return self.raw

def _parse_message(self, data): def _parse_message(self, data):
""" """
Parses the raw message from the device. Parses the raw message from the device.
@@ -330,3 +382,15 @@ class LRRMessage(BaseMessage):


except ValueError: except ValueError:
raise InvalidMessageError('Received invalid message: {0}'.format(data)) raise InvalidMessageError('Received invalid message: {0}'.format(data))

def dict(self, **kwargs):
"""
Dictionary representation.
"""
return dict(
time = self.timestamp,
event_data = self.event_data,
event_type = self.event_type,
partition = self.partition,
**kwargs
)

Loading…
Cancel
Save