| @@ -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) | |||