@@ -13,6 +13,7 @@ from .event import event | |||||
from .util import InvalidMessageError | 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 | ||||
from .panels import PANEL_TYPES, ADEMCO, DSC | |||||
class AlarmDecoder(object): | class AlarmDecoder(object): | ||||
@@ -80,6 +81,8 @@ class AlarmDecoder(object): | |||||
"""The status of the devices LRR emulation.""" | """The status of the devices LRR emulation.""" | ||||
deduplicate = False | deduplicate = False | ||||
"""The status of message deduplication as configured on the device.""" | """The status of message deduplication as configured on the device.""" | ||||
mode = ADEMCO | |||||
"""The panel mode that the AlarmDecoder is in. Currently supports ADEMCO and DSC.""" | |||||
def __init__(self, device): | def __init__(self, device): | ||||
""" | """ | ||||
@@ -110,6 +113,7 @@ class AlarmDecoder(object): | |||||
self.emulate_relay = [False for x in range(4)] | self.emulate_relay = [False for x in range(4)] | ||||
self.emulate_lrr = False | self.emulate_lrr = False | ||||
self.deduplicate = False | self.deduplicate = False | ||||
self.mode = ADEMCO | |||||
def __enter__(self): | def __enter__(self): | ||||
""" | """ | ||||
@@ -222,20 +226,16 @@ class AlarmDecoder(object): | |||||
config_entries = [] | config_entries = [] | ||||
# HACK: This is ugly.. but I can't think of an elegant way of doing it. | # HACK: This is ugly.. but I can't think of an elegant way of doing it. | ||||
config_entries.append(('ADDRESS', | config_entries.append(('ADDRESS', '{0}'.format(self.address))) | ||||
'{0}'.format(self.address))) | config_entries.append(('CONFIGBITS', '{0:x}'.format(self.configbits))) | ||||
config_entries.append(('CONFIGBITS', | config_entries.append(('MASK', '{0:x}'.format(self.address_mask))) | ||||
'{0:x}'.format(self.configbits))) | |||||
config_entries.append(('MASK', | |||||
'{0:x}'.format(self.address_mask))) | |||||
config_entries.append(('EXP', | config_entries.append(('EXP', | ||||
''.join(['Y' if z else 'N' for z in self.emulate_zone]))) | ''.join(['Y' if z else 'N' for z in self.emulate_zone]))) | ||||
config_entries.append(('REL', | config_entries.append(('REL', | ||||
''.join(['Y' if r else 'N' for r in self.emulate_relay]))) | ''.join(['Y' if r else 'N' for r in self.emulate_relay]))) | ||||
config_entries.append(('LRR', | config_entries.append(('LRR', 'Y' if self.emulate_lrr else 'N')) | ||||
'Y' if self.emulate_lrr else 'N')) | config_entries.append(('DEDUPLICATE', 'Y' if self.deduplicate else 'N')) | ||||
config_entries.append(('DEDUPLICATE', | config_entries.append(('MODE', PANEL_TYPES.keys()[PANEL_TYPES.values().index(self.mode)])) | ||||
'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]) | ||||
@@ -430,6 +430,8 @@ class AlarmDecoder(object): | |||||
self.emulate_lrr = (val == 'Y') | self.emulate_lrr = (val == 'Y') | ||||
elif key == 'DEDUPLICATE': | elif key == 'DEDUPLICATE': | ||||
self.deduplicate = (val == 'Y') | self.deduplicate = (val == 'Y') | ||||
elif key == 'MODE': | |||||
self.mode = PANEL_TYPES[val] | |||||
self.on_config_received() | self.on_config_received() | ||||
@@ -16,6 +16,7 @@ import re | |||||
import datetime | import datetime | ||||
from .util import InvalidMessageError | from .util import InvalidMessageError | ||||
from .panels import PANEL_TYPES, ADEMCO, DSC | |||||
class BaseMessage(object): | class BaseMessage(object): | ||||
@@ -95,13 +96,17 @@ class Message(BaseMessage): | |||||
"""Indicates whether or not there are zones that require attention.""" | """Indicates whether or not there are zones that require attention.""" | ||||
perimeter_only = False | perimeter_only = False | ||||
"""Indicates whether or not the perimeter is armed.""" | """Indicates whether or not the perimeter is armed.""" | ||||
system_fault = False | |||||
"""Indicates whether a system fault has occurred.""" | |||||
panel_type = ADEMCO | |||||
"""Indicates which panel type was the source of this message.""" | |||||
numeric_code = None | numeric_code = None | ||||
"""The numeric code associated with the message.""" | """The numeric code associated with the message.""" | ||||
text = None | text = None | ||||
"""The human-readable text to be displayed on the panel LCD.""" | """The human-readable text to be displayed on the panel LCD.""" | ||||
cursor_location = -1 | cursor_location = -1 | ||||
"""Current cursor location on the keypad.""" | """Current cursor location on the keypad.""" | ||||
mask = None | mask = 0xFFFFFFFF | ||||
"""Address mask this message is intended for.""" | """Address mask this message is intended for.""" | ||||
bitfield = None | bitfield = None | ||||
"""The bitfield associated with this message.""" | """The bitfield associated with this message.""" | ||||
@@ -137,7 +142,6 @@ class Message(BaseMessage): | |||||
raise InvalidMessageError('Received invalid message: {0}'.format(data)) | raise InvalidMessageError('Received invalid message: {0}'.format(data)) | ||||
header, self.bitfield, self.numeric_code, self.panel_data, alpha = match.group(1, 2, 3, 4, 5) | header, self.bitfield, self.numeric_code, self.panel_data, alpha = match.group(1, 2, 3, 4, 5) | ||||
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" | ||||
@@ -158,12 +162,18 @@ class Message(BaseMessage): | |||||
self.fire_alarm = is_bit_set(14) | self.fire_alarm = is_bit_set(14) | ||||
self.check_zone = is_bit_set(15) | self.check_zone = is_bit_set(15) | ||||
self.perimeter_only = is_bit_set(16) | self.perimeter_only = is_bit_set(16) | ||||
# bits 17-20 unused. | self.system_fault = is_bit_set(17) | ||||
if self.bitfield[18] in PANEL_TYPES.keys(): | |||||
self.panel_type = PANEL_TYPES[self.bitfield[18]] | |||||
# pos 20-21 - Unused. | |||||
self.text = alpha.strip('"') | self.text = alpha.strip('"') | ||||
if int(self.panel_data[19:21], 16) & 0x01 > 0: | if self.panel_type == ADEMCO: | ||||
# Current cursor location on the alpha display. | self.mask = int(self.panel_data[3:3+8], 16) | ||||
self.cursor_location = int(self.bitfield[21:23], 16) | if int(self.panel_data[19:21], 16) & 0x01 > 0: | ||||
# Current cursor location on the alpha display. | |||||
self.cursor_location = int(self.bitfield[21:23], 16) | |||||
def dict(self, **kwargs): | def dict(self, **kwargs): | ||||
""" | """ | ||||
@@ -4,6 +4,14 @@ Representations of Panels and their templates. | |||||
.. moduleauthor:: Scott Petersen <scott@nutech.com> | .. moduleauthor:: Scott Petersen <scott@nutech.com> | ||||
""" | """ | ||||
ADEMCO = 0 | |||||
DSC = 1 | |||||
PANEL_TYPES = { | |||||
'A': ADEMCO, | |||||
'D': DSC, | |||||
} | |||||
VISTA20 = 0 | VISTA20 = 0 | ||||
TEMPLATES = { | TEMPLATES = { | ||||