diff --git a/alarmdecoder/decoder.py b/alarmdecoder/decoder.py index 38fa046..c9dcaa3 100644 --- a/alarmdecoder/decoder.py +++ b/alarmdecoder/decoder.py @@ -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() diff --git a/alarmdecoder/devices.py b/alarmdecoder/devices.py index 586ece2..6fd7f2b 100644 --- a/alarmdecoder/devices.py +++ b/alarmdecoder/devices.py @@ -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): diff --git a/alarmdecoder/messages/lrr/__init__.py b/alarmdecoder/messages/lrr/__init__.py index d3a4dff..863cfe2 100644 --- a/alarmdecoder/messages/lrr/__init__.py +++ b/alarmdecoder/messages/lrr/__init__.py @@ -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'] diff --git a/alarmdecoder/messages/lrr/events.py b/alarmdecoder/messages/lrr/events.py index 615c01c..6d00b10 100644 --- a/alarmdecoder/messages/lrr/events.py +++ b/alarmdecoder/messages/lrr/events.py @@ -4,35 +4,76 @@ Constants and utility functions used for LRR event handling. .. moduleauthor:: Scott Petersen """ -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 diff --git a/alarmdecoder/messages/lrr/message.py b/alarmdecoder/messages/lrr/message.py index 218f3ad..b4e6132 100644 --- a/alarmdecoder/messages/lrr/message.py +++ b/alarmdecoder/messages/lrr/message.py @@ -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 diff --git a/alarmdecoder/messages/lrr/system.py b/alarmdecoder/messages/lrr/system.py index eda27d6..ff1bc1c 100644 --- a/alarmdecoder/messages/lrr/system.py +++ b/alarmdecoder/messages/lrr/system.py @@ -1,3 +1,8 @@ +""" +Primary system for handling LRR events. + +.. moduleauthor:: Scott Petersen +""" 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: diff --git a/alarmdecoder/states.py b/alarmdecoder/states.py index b33cf2e..28ecec1 100644 --- a/alarmdecoder/states.py +++ b/alarmdecoder/states.py @@ -1,4 +1,7 @@ class FireState: + """ + Fire alarm status + """ NONE = 0 ALARM = 1 ACKNOWLEDGED = 2 diff --git a/alarmdecoder/util.py b/alarmdecoder/util.py index 7a0a0ac..f7c00d9 100644 --- a/alarmdecoder/util.py +++ b/alarmdecoder/util.py @@ -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)