Kaynağa Gözat

Merge pull request #3 from nutechsoftware/dev

Dev
pyserial_fix
endlesscoil 10 yıl önce
ebeveyn
işleme
752183e8ad
5 değiştirilmiş dosya ile 169 ekleme ve 36 silme
  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 Dosyayı Görüntüle

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

+ 31
- 3
alarmdecoder/decoder.py Dosyayı Görüntüle

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

import time
import re

from .event import event
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_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_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
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"""
KEY_F4 = unichr(4) + unichr(4) + unichr(4)
"""Represents panel function key #4"""
KEY_PANIC = unichr(5) + unichr(5) + unichr(5)
"""Represents a panic keypress"""

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

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

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

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

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

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

return msg

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

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):
"""
Updates internal device states.


+ 44
- 8
alarmdecoder/devices.py Dosyayı Görüntüle

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

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


@@ -149,8 +149,19 @@ class Device(object):
except TimeoutError:
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
raise


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

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

cls.__detect_thread.start()

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

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

def write(self, data):
"""
Writes data to the device.
@@ -648,8 +665,8 @@ class SerialDevice(Device):
# all issues with it.
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:
self._running = True
@@ -670,6 +687,9 @@ class SerialDevice(Device):
except Exception:
pass

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

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

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

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)

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

Device.close(self)

except Exception:
pass

Device.close(self)

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

def write(self, data):
"""
Writes data to the device.
@@ -1033,6 +1062,13 @@ class SocketDevice(Device):
except socket.error, 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:
if got_line:
ret, self._buffer = self._buffer, ''


+ 3
- 0
alarmdecoder/event/event.py Dosyayı Görüntüle

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

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

def _getfunctionlist(self):

"""(internal use) """


+ 89
- 25
alarmdecoder/messages.py Dosyayı Görüntüle

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

import re
import datetime

from .util import InvalidMessageError

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

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

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

def __str__(self):
"""
@@ -37,6 +41,22 @@ class BaseMessage(object):
"""
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):
"""
@@ -102,12 +122,6 @@ class Message(BaseMessage):
if data is not None:
self._parse_message(data)

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

def _parse_message(self, data):
"""
Parse the message from the device.
@@ -151,6 +165,37 @@ class Message(BaseMessage):
# Current cursor location on the alpha display.
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):
"""
@@ -183,12 +228,6 @@ class ExpanderMessage(BaseMessage):
if data is not None:
self._parse_message(data)

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

def _parse_message(self, data):
"""
Parse the raw message from the device.
@@ -217,6 +256,18 @@ class ExpanderMessage(BaseMessage):
else:
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):
"""
@@ -246,12 +297,6 @@ class RFMessage(BaseMessage):
if data is not None:
self._parse_message(data)

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

def _parse_message(self, data):
"""
Parses the raw message from the device.
@@ -282,6 +327,19 @@ class RFMessage(BaseMessage):
except ValueError:
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):
"""
@@ -307,12 +365,6 @@ class LRRMessage(BaseMessage):
if data is not None:
self._parse_message(data)

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

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

except ValueError:
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
)

Yükleniyor…
İptal
Kaydet