@@ -92,6 +92,8 @@ class AlarmDecoder(object): | |||
"""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.""" | |||
emulate_com = False | |||
"""The status of the devices COM emulation.""" | |||
#Version Information | |||
serial_number = 0xFFFFFFFF | |||
@@ -101,10 +103,6 @@ class AlarmDecoder(object): | |||
version_flags = "" | |||
"""Device flags enabled""" | |||
FIRE_STATE_NONE = 0 | |||
FIRE_STATE_FIRE = 1 | |||
FIRE_STATE_ACKNOWLEDGED = 2 | |||
def __init__(self, device, ignore_message_states=False): | |||
""" | |||
Constructor | |||
@@ -112,6 +110,8 @@ class AlarmDecoder(object): | |||
:param device: The low-level device used for this `AlarmDecoder`_ | |||
interface. | |||
:type device: Device | |||
:param ignore_message_states: Ignore regular panel messages when updating internal states | |||
:type ignore_message_states: bool | |||
""" | |||
self._device = device | |||
self._zonetracker = Zonetracker(self) | |||
@@ -142,6 +142,7 @@ class AlarmDecoder(object): | |||
self.emulate_lrr = False | |||
self.deduplicate = False | |||
self.mode = ADEMCO | |||
self.emulate_com = False | |||
self.serial_number = 0xFFFFFFFF | |||
self.version_number = 'Unknown' | |||
@@ -284,6 +285,12 @@ class AlarmDecoder(object): | |||
self.send("C{0}\r".format(self.get_config_string())) | |||
def get_config_string(self): | |||
""" | |||
Build a configuration string that's compatible with the AlarmDecoder configuration | |||
command from the current values in the object. | |||
:returns: string | |||
""" | |||
config_entries = [] | |||
# HACK: This is ugly.. but I can't think of an elegant way of doing it. | |||
@@ -297,6 +304,7 @@ class AlarmDecoder(object): | |||
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', list(PANEL_TYPES)[list(PANEL_TYPES.values()).index(self.mode)])) | |||
config_entries.append(('COM', 'Y' if self.emulate_com else 'N')) | |||
config_string = '&'.join(['='.join(t) for t in config_entries]) | |||
@@ -515,6 +523,8 @@ class AlarmDecoder(object): | |||
self.deduplicate = (val == 'Y') | |||
elif key == 'MODE': | |||
self.mode = PANEL_TYPES[val] | |||
elif key == 'COM': | |||
self.emulate_com = (val == 'Y') | |||
self.on_config_received() | |||
@@ -560,6 +570,8 @@ class AlarmDecoder(object): | |||
:param message: message to use to update | |||
:type message: :py:class:`~alarmdecoder.messages.Message` | |||
:param status: power status, overrides message bits. | |||
:type status: bool | |||
:returns: bool indicating the new status | |||
""" | |||
@@ -584,6 +596,10 @@ class AlarmDecoder(object): | |||
:param message: message to use to update | |||
:type message: :py:class:`~alarmdecoder.messages.Message` | |||
:param status: alarm status, overrides message bits. | |||
:type status: bool | |||
:param user: user associated with alarm event | |||
:type user: string | |||
:returns: bool indicating the new status | |||
""" | |||
@@ -611,6 +627,10 @@ class AlarmDecoder(object): | |||
:param message: message to use to update | |||
:type message: :py:class:`~alarmdecoder.messages.Message` | |||
:param status: bypass status, overrides message bits. | |||
:type status: bool | |||
:param zone: zone associated with bypass event | |||
:type zone: int | |||
:returns: bool indicating the new status | |||
""" | |||
@@ -640,6 +660,10 @@ class AlarmDecoder(object): | |||
:param message: message to use to update | |||
:type message: :py:class:`~alarmdecoder.messages.Message` | |||
:param status: armed status, overrides message bits | |||
:type status: bool | |||
:param status_stay: armed stay status, overrides message bits | |||
:type status_stay: bool | |||
:returns: bool indicating the new status | |||
""" | |||
@@ -670,6 +694,8 @@ class AlarmDecoder(object): | |||
:param message: message to use to update | |||
:type message: :py:class:`~alarmdecoder.messages.Message` | |||
:param status: battery status, overrides message bits | |||
:type status: bool | |||
:returns: boolean indicating the new status | |||
""" | |||
@@ -696,6 +722,8 @@ class AlarmDecoder(object): | |||
:param message: message to use to update | |||
:type message: :py:class:`~alarmdecoder.messages.Message` | |||
:param status: fire status, overrides message bits | |||
:type status: bool | |||
:returns: boolean indicating the new status | |||
""" | |||
@@ -807,7 +835,6 @@ class AlarmDecoder(object): | |||
Internal handler for opening the device. | |||
""" | |||
self.get_config() | |||
self.get_version() | |||
self.on_open() | |||
@@ -495,6 +495,11 @@ class USBDevice(Device): | |||
pass | |||
def fileno(self): | |||
""" | |||
File number not supported for USB devices. | |||
:raises: NotImplementedError | |||
""" | |||
raise NotImplementedError('USB devices do not support fileno()') | |||
def write(self, data): | |||
@@ -788,6 +793,11 @@ class SerialDevice(Device): | |||
pass | |||
def fileno(self): | |||
""" | |||
Returns the file number associated with the device | |||
:returns: int | |||
""" | |||
return self._device.fileno() | |||
def write(self, data): | |||
@@ -1103,6 +1113,11 @@ class SocketDevice(Device): | |||
Device.close(self) | |||
def fileno(self): | |||
""" | |||
Returns the file number associated with the device | |||
:returns: int | |||
""" | |||
return self._device.fileno() | |||
def write(self, data): | |||
@@ -1,9 +1,9 @@ | |||
from .message import LRRMessage | |||
from .system import LRRSystem | |||
from .events import get_event_description, LRR_EVENT_TYPE, LRR_CID_EVENT, LRR_DSC_EVENT, LRR_ADEMCO_EVENT, \ | |||
from .events import get_event_description, get_event_source, LRR_EVENT_TYPE, LRR_EVENT_STATUS, LRR_CID_EVENT, LRR_DSC_EVENT, LRR_ADEMCO_EVENT, \ | |||
LRR_ALARMDECODER_EVENT, LRR_UNKNOWN_EVENT, LRR_CID_MAP, LRR_DSC_MAP, LRR_ADEMCO_MAP, \ | |||
LRR_ALARMDECODER_MAP, LRR_UNKNOWN_MAP | |||
__all__ = ['get_event_description', 'LRRMessage', 'LRR_EVENT_TYPE', 'LRR_CID_EVENT', 'LRR_DSC_EVENT', | |||
__all__ = ['get_event_description', 'get_event_source', 'LRRMessage', 'LRR_EVENT_TYPE', 'LRR_EVENT_STATUS', 'LRR_CID_EVENT', 'LRR_DSC_EVENT', | |||
'LRR_ADEMCO_EVENT', 'LRR_ALARMDECODER_EVENT', 'LRR_UNKNOWN_EVENT', 'LRR_CID_MAP', | |||
'LRR_DSC_MAP', 'LRR_ADEMCO_MAP', 'LRR_ALARMDECODER_MAP', 'LRR_UNKNOWN_MAP'] |
@@ -4,35 +4,76 @@ Constants and utility functions used for LRR event handling. | |||
.. moduleauthor:: Scott Petersen <scott@nutech.com> | |||
""" | |||
def get_event_description(event_type, value): | |||
description = 'Unknown' | |||
lookup_map = None | |||
def get_event_description(event_type, event_code): | |||
""" | |||
Retrieves the human-readable description of an LRR event. | |||
:param event_type: Base LRR event type. Use LRR_EVENT_TYPE.* | |||
:type event_type: int | |||
:param event_code: LRR event code | |||
:type event_code: int | |||
if event_type in LRR_TYPE_MAP.keys(): | |||
lookup_map = LRR_TYPE_MAP[event_type] | |||
:returns: string | |||
""" | |||
description = 'Unknown' | |||
lookup_map = LRR_TYPE_MAP.get(event_type, None) | |||
if value in lookup_map.keys(): | |||
description = lookup_map[value] | |||
if lookup_map is not None: | |||
description = lookup_map.get(event_code, description) | |||
return description | |||
def get_event_source(prefix): | |||
""" | |||
Retrieves the LRR_EVENT_TYPE corresponding to the prefix provided.abs | |||
:param prefix: Prefix to convert to event type | |||
:type prefix: string | |||
:returns: int | |||
""" | |||
source = LRR_EVENT_TYPE.UNKNOWN | |||
if prefix == 'CID': | |||
source = LRR_EVENT_TYPE.CID | |||
elif prefix == 'DSC': | |||
source = LRR_EVENT_TYPE.DSC | |||
elif prefix == 'AD2': | |||
source = LRR_EVENT_TYPE.ALARMDECODER | |||
elif prefix == 'ADEMCO': | |||
source = LRR_EVENT_TYPE.ADEMCO | |||
return source | |||
class LRR_EVENT_TYPE: | |||
""" | |||
Base LRR event types | |||
""" | |||
CID = 1 | |||
DSC = 2 | |||
ADEMCO = 3 | |||
ALARMDECODER = 4 | |||
UNKNOWN = 5 | |||
class LRR_EVENT_STATUS: | |||
""" | |||
LRR event status codes | |||
""" | |||
TRIGGER = 1 | |||
RESTORE = 3 | |||
class LRR_CID_EVENT: | |||
""" | |||
ContactID event codes | |||
""" | |||
MEDICAL = 0x100 | |||
MEDICAL_PENDANT = 0x101 | |||
MEDICAL_FAIL_TO_REPORT = 0x102 | |||
# 103-108: ? | |||
TAMPER_ZONE = 0x109 # Where did we find this? | |||
TAMPER_ZONE = 0x109 # NOTE: Where did we find this? | |||
FIRE = 0x110 | |||
FIRE_SMOKE = 0x111 | |||
FIRE_COMBUSTION = 0x112 | |||
@@ -231,7 +272,8 @@ class LRR_CID_EVENT: | |||
STATUS_PANIC_ALARM_RESET = 0x465 | |||
ACCESS_SERVICE_ONOFF_PREMISES = 0x466 | |||
# 467-469: ? | |||
OPENCLOSE_PARTIAL_CLOSING = 0x470 # HACK: This is from DSC, and is named far too closely to 0 | |||
OPENCLOSE_PARTIAL_CLOSING = 0x470 # HACK: This is from our DSC firmware implementation, | |||
# and is named far too closely to 0x480. | |||
# 471-479: ? | |||
OPENCLOSE_PARTIAL_CLOSE = 0x480 | |||
# 481-500: ? | |||
@@ -343,6 +385,9 @@ class LRR_CID_EVENT: | |||
class LRR_DSC_EVENT: | |||
""" | |||
DSC event codes | |||
""" | |||
ZONE_EXPANDER_SUPERVISORY_ALARM = 0x04c | |||
ZONE_EXPANDER_SUPERVISORY_RESTORE = 0x04d | |||
AUX_INPUT_ALARM = 0x051 | |||
@@ -354,18 +399,28 @@ class LRR_DSC_EVENT: | |||
class LRR_ADEMCO_EVENT: | |||
""" | |||
ADEMCO event codes | |||
""" | |||
pass | |||
class LRR_ALARMDECODER_EVENT: | |||
""" | |||
AlarmDecoder event codes | |||
""" | |||
CUSTOM_PROG_MSG = 0x0 | |||
CUSTOM_PROG_KEY = 0x1 | |||
class LRR_UNKNOWN_EVENT: | |||
""" | |||
Unknown event codes. Realistically there shouldn't ever be anything here. | |||
""" | |||
pass | |||
# Map of ContactID event codes to human-readable text. | |||
LRR_CID_MAP = { | |||
LRR_CID_EVENT.MEDICAL: 'Medical Emergency: Non-specific', | |||
LRR_CID_EVENT.MEDICAL_PENDANT: 'Emergency Assistance Request', | |||
@@ -640,6 +695,7 @@ LRR_CID_MAP = { | |||
LRR_CID_EVENT.OTHER_NO_READ_LOG: 'Other: No Read Log', | |||
} | |||
# Map of DSC event codes to human-readable text. | |||
LRR_DSC_MAP = { | |||
LRR_DSC_EVENT.ZONE_EXPANDER_SUPERVISORY_ALARM: 'Zone Expander Supervisory Alarm', | |||
LRR_DSC_EVENT.ZONE_EXPANDER_SUPERVISORY_RESTORE: 'Zone Expander Supervisory Restore', | |||
@@ -651,6 +707,7 @@ LRR_DSC_MAP = { | |||
LRR_DSC_EVENT.REPORT_DSC_USER_LOG_EVENT: 'Report DSC User Log Event', | |||
} | |||
# Map of ADEMCO event codes to human-readable text. | |||
LRR_ADEMCO_MAP = { | |||
} | |||
@@ -660,10 +717,12 @@ LRR_ALARMDECODER_MAP = { | |||
LRR_ALARMDECODER_EVENT.CUSTOM_PROG_KEY: 'Custom Programming Key' | |||
} | |||
# Map of UNKNOWN event codes to human-readable text. | |||
LRR_UNKNOWN_MAP = { | |||
} | |||
# Map of event type codes to text maps. | |||
LRR_TYPE_MAP = { | |||
LRR_EVENT_TYPE.CID: LRR_CID_MAP, | |||
LRR_EVENT_TYPE.DSC: LRR_DSC_MAP, | |||
@@ -672,6 +731,7 @@ LRR_TYPE_MAP = { | |||
LRR_EVENT_TYPE.UNKNOWN: LRR_UNKNOWN_MAP, | |||
} | |||
# LRR events that should be considered Fire events. | |||
LRR_FIRE_EVENTS = [ | |||
LRR_CID_EVENT.FIRE, | |||
LRR_CID_EVENT.FIRE_SMOKE, | |||
@@ -681,9 +741,10 @@ LRR_FIRE_EVENTS = [ | |||
LRR_CID_EVENT.FIRE_PULL_STATION, | |||
LRR_CID_EVENT.FIRE_DUCT, | |||
LRR_CID_EVENT.FIRE_FLAME, | |||
LRR_CID_EVENT.OPENCLOSE_CANCEL_BY_USER | |||
LRR_CID_EVENT.OPENCLOSE_CANCEL_BY_USER # HACK: Don't really like having this here | |||
] | |||
# LRR events that should be considered Alarm events. | |||
LRR_ALARM_EVENTS = [ | |||
LRR_CID_EVENT.BURGLARY, | |||
LRR_CID_EVENT.BURGLARY_PERIMETER, | |||
@@ -704,23 +765,27 @@ LRR_ALARM_EVENTS = [ | |||
LRR_CID_EVENT.ALARM_LOW_TEMP, | |||
LRR_CID_EVENT.ALARM_LOSS_OF_AIR_FLOW, | |||
LRR_CID_EVENT.ALARM_CARBON_MONOXIDE, | |||
LRR_CID_EVENT.OPENCLOSE_CANCEL_BY_USER | |||
LRR_CID_EVENT.OPENCLOSE_CANCEL_BY_USER # HACK: Don't really like having this here | |||
] | |||
# LRR events that should be considered Power events. | |||
LRR_POWER_EVENTS = [ | |||
LRR_CID_EVENT.TROUBLE_AC_LOSS | |||
] | |||
# LRR events that should be considered Bypass events. | |||
LRR_BYPASS_EVENTS = [ | |||
LRR_CID_EVENT.BYPASS_ZONE, | |||
LRR_CID_EVENT.BYPASS_24HOUR_ZONE, | |||
LRR_CID_EVENT.BYPASS_BURGLARY | |||
] | |||
# LRR events that should be considered Battery events. | |||
LRR_BATTERY_EVENTS = [ | |||
LRR_CID_EVENT.TROUBLE_LOW_BATTERY | |||
] | |||
# LRR events that should be considered Panic events. | |||
LRR_PANIC_EVENTS = [ | |||
LRR_CID_EVENT.MEDICAL, | |||
LRR_CID_EVENT.MEDICAL_PENDANT, | |||
@@ -731,9 +796,10 @@ LRR_PANIC_EVENTS = [ | |||
LRR_CID_EVENT.PANIC_AUDIBLE, | |||
LRR_CID_EVENT.PANIC_DURESS_ACCESS_GRANTED, | |||
LRR_CID_EVENT.PANIC_DURESS_EGRESS_GRANTED, | |||
LRR_CID_EVENT.OPENCLOSE_CANCEL_BY_USER # Canceled panic | |||
LRR_CID_EVENT.OPENCLOSE_CANCEL_BY_USER # HACK: Don't really like having this here | |||
] | |||
# LRR events that should be considered Arm events. | |||
LRR_ARM_EVENTS = [ | |||
LRR_CID_EVENT.OPENCLOSE, | |||
LRR_CID_EVENT.OPENCLOSE_BY_USER, | |||
@@ -742,10 +808,11 @@ LRR_ARM_EVENTS = [ | |||
LRR_CID_EVENT.OPENCLOSE_REMOTE_ARMDISARM, | |||
LRR_CID_EVENT.OPENCLOSE_QUICK_ARM, | |||
LRR_CID_EVENT.OPENCLOSE_KEYSWITCH, | |||
LRR_CID_EVENT.OPENCLOSE_ARMED_STAY, | |||
LRR_CID_EVENT.OPENCLOSE_ARMED_STAY, # HACK: Not sure if I like having these in here. | |||
LRR_CID_EVENT.OPENCLOSE_KEYSWITCH_ARMED_STAY | |||
] | |||
# LRR events that should be considered Arm Stay events. | |||
LRR_STAY_EVENTS = [ | |||
LRR_CID_EVENT.OPENCLOSE_ARMED_STAY, | |||
LRR_CID_EVENT.OPENCLOSE_KEYSWITCH_ARMED_STAY | |||
@@ -12,7 +12,7 @@ devices. | |||
from .. import BaseMessage | |||
from ...util import InvalidMessageError | |||
from .events import LRR_EVENT_TYPE, get_event_description | |||
from .events import LRR_EVENT_TYPE, get_event_description, get_event_source | |||
class LRRMessage(BaseMessage): | |||
@@ -70,18 +70,22 @@ class LRRMessage(BaseMessage): | |||
_, values = data.split(':') | |||
values = values.split(',') | |||
# Handle older-format events | |||
if len(values) <= 3: | |||
self.event_data, self.partition, self.event_type = values | |||
self.version = 1 | |||
# Newer-format events | |||
else: | |||
self.event_data, self.partition, self.event_type, self.report_code = values | |||
self.version = 2 | |||
event_type_data = self.event_type.split('_') | |||
self.event_prefix = event_type_data[0] | |||
self.event_source = _get_event_source(self.event_prefix) | |||
self.event_status = int(event_type_data[1][0]) | |||
self.event_code = int(event_type_data[1][1:], 16) | |||
self.event_prefix = event_type_data[0] # Ex: CID | |||
self.event_source = get_event_source(self.event_prefix) # Ex: LRR_EVENT_TYPE.CID | |||
self.event_status = int(event_type_data[1][0]) # Ex: 1 or 3 | |||
self.event_code = int(event_type_data[1][1:], 16) # Ex: 0x100 = Medical | |||
# replace last 2 digits of event_code with report_code, if applicable. | |||
if not self.skip_report_override and self.report_code not in ['00', 'ff']: | |||
@@ -94,7 +98,7 @@ class LRRMessage(BaseMessage): | |||
def dict(self, **kwargs): | |||
""" | |||
Dictionary representation. | |||
Dictionary representation | |||
""" | |||
return dict( | |||
time = self.timestamp, | |||
@@ -109,18 +113,3 @@ class LRRMessage(BaseMessage): | |||
event_description = self.event_description, | |||
**kwargs | |||
) | |||
def _get_event_source(prefix): | |||
source = LRR_EVENT_TYPE.UNKNOWN | |||
if prefix == 'CID': | |||
source = LRR_EVENT_TYPE.CID | |||
elif prefix == 'DSC': | |||
source = LRR_EVENT_TYPE.DSC | |||
elif prefix == 'AD2': | |||
source = LRR_EVENT_TYPE.ALARMDECODER | |||
elif prefix == 'ADEMCO': | |||
source = LRR_EVENT_TYPE.ADEMCO | |||
return source |
@@ -1,3 +1,8 @@ | |||
""" | |||
Primary system for handling LRR events. | |||
.. moduleauthor:: Scott Petersen <scott@nutech.com> | |||
""" | |||
from .events import LRR_EVENT_TYPE, LRR_EVENT_STATUS, LRR_CID_EVENT | |||
from .events import LRR_FIRE_EVENTS, LRR_POWER_EVENTS, LRR_BYPASS_EVENTS, LRR_BATTERY_EVENTS, \ | |||
@@ -5,44 +10,60 @@ from .events import LRR_FIRE_EVENTS, LRR_POWER_EVENTS, LRR_BYPASS_EVENTS, LRR_BA | |||
class LRRSystem(object): | |||
""" | |||
Handles LRR events and triggers higher-level events in the AlarmDecoder object. | |||
""" | |||
def __init__(self, alarmdecoder_object): | |||
""" | |||
Constructor | |||
:param alarmdecoder_object: Main AlarmDecoder object | |||
:type alarmdecoder_object: :py:class:`~alarmdecoder.AlarmDecoder` | |||
""" | |||
self._alarmdecoder = alarmdecoder_object | |||
def update(self, message): | |||
handled = False | |||
""" | |||
Updates the states in the primary AlarmDecoder object based on | |||
the LRR message provided. | |||
:param message: LRR message object | |||
:type message: :py:class:`~alarmdecoder.messages.LRRMessage` | |||
""" | |||
# Firmware version < 2.2a.8.6 | |||
if message.version == 1: | |||
if msg.event_type == 'ALARM_PANIC': | |||
if message.event_type == 'ALARM_PANIC': | |||
self._alarmdecoder._update_panic_status(True) | |||
handled = True | |||
elif msg.event_type == 'CANCEL': | |||
elif message.event_type == 'CANCEL': | |||
self._alarmdecoder._update_panic_status(False) | |||
handled = True | |||
# Firmware version >= 2.2a.8.6 | |||
elif message.version == 2: | |||
source = message.event_source | |||
if source == LRR_EVENT_TYPE.CID: | |||
handled = self._handle_cid_message(message) | |||
self._handle_cid_message(message) | |||
elif source == LRR_EVENT_TYPE.DSC: | |||
handled = self._handle_dsc_message(message) | |||
self._handle_dsc_message(message) | |||
elif source == LRR_EVENT_TYPE.ADEMCO: | |||
handled = self._handle_ademco_message(message) | |||
self._handle_ademco_message(message) | |||
elif source == LRR_EVENT_TYPE.ALARMDECODER: | |||
handled = self._handle_alarmdecoder_message(message) | |||
self._handle_alarmdecoder_message(message) | |||
elif source == LRR_EVENT_TYPE.UNKNOWN: | |||
handled = self._handle_unknown_message(message) | |||
self._handle_unknown_message(message) | |||
else: | |||
pass | |||
return handled | |||
def _handle_cid_message(self, message): | |||
handled = False | |||
""" | |||
Handles ContactID LRR events. | |||
:param message: LRR message object | |||
:type message: :py:class:`~alarmdecoder.messages.LRRMessage` | |||
""" | |||
status = self._get_event_status(message) | |||
if status is None: | |||
print("Unknown LRR event status: {0}".format(message)) | |||
return | |||
if message.event_code in LRR_FIRE_EVENTS: | |||
@@ -50,7 +71,6 @@ class LRRSystem(object): | |||
status = False | |||
self._alarmdecoder._update_fire_status(status=status) | |||
handled = True | |||
if message.event_code in LRR_ALARM_EVENTS: | |||
kwargs = {} | |||
@@ -60,27 +80,21 @@ class LRRSystem(object): | |||
kwargs[field_name] = int(message.event_data) | |||
self._alarmdecoder._update_alarm_status(status=status, **kwargs) | |||
handled = True | |||
if message.event_code in LRR_POWER_EVENTS: | |||
self._alarmdecoder._update_power_status(status=status) | |||
handled = True | |||
if message.event_code in LRR_BYPASS_EVENTS: | |||
zone = int(message.event_data) | |||
self._alarmdecoder._update_zone_bypass_status(status=status, zone=zone) | |||
handled = True | |||
self._alarmdecoder._update_zone_bypass_status(status=status, zone=int(message.event_data)) | |||
if message.event_code in LRR_BATTERY_EVENTS: | |||
self._alarmdecoder._update_battery_status(status=status) | |||
handled = True | |||
if message.event_code in LRR_PANIC_EVENTS: | |||
if message.event_code == LRR_CID_EVENT.OPENCLOSE_CANCEL_BY_USER: | |||
status = False | |||
self._alarmdecoder._update_panic_status(status=status) | |||
handled = True | |||
if message.event_code in LRR_ARM_EVENTS: | |||
# NOTE: status on OPENCLOSE messages is backwards. | |||
@@ -93,25 +107,53 @@ class LRRSystem(object): | |||
status = not status | |||
self._alarmdecoder._update_armed_status(status=status, status_stay=status_stay) | |||
handled = True | |||
return handled | |||
def _handle_dsc_message(self, message): | |||
return False | |||
""" | |||
Handles DSC LRR events. | |||
:param message: LRR message object | |||
:type message: :py:class:`~alarmdecoder.messages.LRRMessage` | |||
""" | |||
pass | |||
def _handle_ademco_message(self, message): | |||
return False | |||
""" | |||
Handles ADEMCO LRR events. | |||
:param message: LRR message object | |||
:type message: :py:class:`~alarmdecoder.messages.LRRMessage` | |||
""" | |||
pass | |||
def _handle_alarmdecoder_message(self, message): | |||
return False | |||
""" | |||
Handles AlarmDecoder LRR events. | |||
:param message: LRR message object | |||
:type message: :py:class:`~alarmdecoder.messages.LRRMessage` | |||
""" | |||
pass | |||
def _handle_unknown_message(self, message): | |||
""" | |||
Handles UNKNOWN LRR events. | |||
:param message: LRR message object | |||
:type message: :py:class:`~alarmdecoder.messages.LRRMessage` | |||
""" | |||
# TODO: Log this somewhere useful. | |||
return False | |||
pass | |||
def _get_event_status(self, message): | |||
""" | |||
Retrieves the boolean status of an LRR message. | |||
:param message: LRR message object | |||
:type message: :py:class:`~alarmdecoder.messages.LRRMessage` | |||
:returns: Boolean indicating whether the event was triggered or restored. | |||
""" | |||
status = None | |||
if message.event_status == LRR_EVENT_STATUS.TRIGGER: | |||
@@ -1,4 +1,7 @@ | |||
class FireState: | |||
""" | |||
Fire alarm status | |||
""" | |||
NONE = 0 | |||
ALARM = 1 | |||
ACKNOWLEDGED = 2 |
@@ -58,6 +58,15 @@ class UploadChecksumError(UploadError): | |||
def bytes_available(device): | |||
""" | |||
Determines the number of bytes available for reading from an | |||
AlarmDecoder device | |||
:param device: the AlarmDecoder device | |||
:type device: :py:class:`~alarmdecoder.devices.Device` | |||
:returns: int | |||
""" | |||
bytes_avail = 0 | |||
if isinstance(device, alarmdecoder.devices.SerialDevice): | |||
@@ -71,6 +80,14 @@ def bytes_available(device): | |||
return bytes_avail | |||
def read_firmware_file(file_path): | |||
""" | |||
Reads a firmware file into a dequeue for processing. | |||
:param file_path: Path to the firmware file | |||
:type file_path: string | |||
:returns: deque | |||
""" | |||
data_queue = deque() | |||
with open(file_path) as firmware_handle: | |||
@@ -99,6 +116,14 @@ class Firmware(object): | |||
@staticmethod | |||
def read(device): | |||
""" | |||
Reads data from the specified device. | |||
:param device: the AlarmDecoder device | |||
:type device: :py:class:`~alarmdecoder.devices.Device` | |||
:returns: string | |||
""" | |||
response = None | |||
bytes_avail = bytes_available(device) | |||