| @@ -13,6 +13,7 @@ from .event import event | |||
| from .util import InvalidMessageError | |||
| from .messages import Message, ExpanderMessage, RFMessage, LRRMessage | |||
| from .zonetracking import Zonetracker | |||
| from .panels import PANEL_TYPES, ADEMCO, DSC | |||
| class AlarmDecoder(object): | |||
| @@ -80,6 +81,8 @@ class AlarmDecoder(object): | |||
| """The status of the devices LRR emulation.""" | |||
| deduplicate = False | |||
| """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): | |||
| """ | |||
| @@ -110,6 +113,7 @@ class AlarmDecoder(object): | |||
| self.emulate_relay = [False for x in range(4)] | |||
| self.emulate_lrr = False | |||
| self.deduplicate = False | |||
| self.mode = ADEMCO | |||
| def __enter__(self): | |||
| """ | |||
| @@ -222,20 +226,16 @@ class AlarmDecoder(object): | |||
| config_entries = [] | |||
| # HACK: This is ugly.. but I can't think of an elegant way of doing it. | |||
| 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(('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(('LRR', 'Y' if self.emulate_lrr else 'N')) | |||
| config_entries.append(('DEDUPLICATE', 'Y' if self.deduplicate else 'N')) | |||
| config_entries.append(('MODE', PANEL_TYPES.keys()[PANEL_TYPES.values().index(self.mode)])) | |||
| config_string = '&'.join(['='.join(t) for t in config_entries]) | |||
| @@ -430,6 +430,8 @@ class AlarmDecoder(object): | |||
| self.emulate_lrr = (val == 'Y') | |||
| elif key == 'DEDUPLICATE': | |||
| self.deduplicate = (val == 'Y') | |||
| elif key == 'MODE': | |||
| self.mode = PANEL_TYPES[val] | |||
| self.on_config_received() | |||
| @@ -16,6 +16,7 @@ import re | |||
| import datetime | |||
| from .util import InvalidMessageError | |||
| from .panels import PANEL_TYPES, ADEMCO, DSC | |||
| class BaseMessage(object): | |||
| @@ -95,13 +96,17 @@ class Message(BaseMessage): | |||
| """Indicates whether or not there are zones that require attention.""" | |||
| perimeter_only = False | |||
| """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 | |||
| """The numeric code associated with the message.""" | |||
| text = None | |||
| """The human-readable text to be displayed on the panel LCD.""" | |||
| cursor_location = -1 | |||
| """Current cursor location on the keypad.""" | |||
| mask = None | |||
| mask = 0xFFFFFFFF | |||
| """Address mask this message is intended for.""" | |||
| bitfield = None | |||
| """The bitfield associated with this message.""" | |||
| @@ -137,7 +142,6 @@ class Message(BaseMessage): | |||
| 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) | |||
| self.mask = int(self.panel_data[3:3+8], 16) | |||
| 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.check_zone = is_bit_set(15) | |||
| 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('"') | |||
| 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) | |||
| if self.panel_type == ADEMCO: | |||
| self.mask = int(self.panel_data[3:3+8], 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): | |||
| """ | |||
| @@ -4,6 +4,14 @@ Representations of Panels and their templates. | |||
| .. moduleauthor:: Scott Petersen <scott@nutech.com> | |||
| """ | |||
| ADEMCO = 0 | |||
| DSC = 1 | |||
| PANEL_TYPES = { | |||
| 'A': ADEMCO, | |||
| 'D': DSC, | |||
| } | |||
| VISTA20 = 0 | |||
| TEMPLATES = { | |||