diff --git a/alarmdecoder/decoder.py b/alarmdecoder/decoder.py index d06fd56..3392e53 100644 --- a/alarmdecoder/decoder.py +++ b/alarmdecoder/decoder.py @@ -123,7 +123,7 @@ class AlarmDecoder(object): version_flags = "" """Device flags enabled""" - def __init__(self, device, ignore_message_states=False): + def __init__(self, device, ignore_message_states=False, ignore_lrr_states=True): """ Constructor @@ -132,12 +132,15 @@ class AlarmDecoder(object): :type device: Device :param ignore_message_states: Ignore regular panel messages when updating internal states :type ignore_message_states: bool + :param ignore_lrr_states: Ignore LRR panel messages when updating internal states + :type ignore_lrr_states: bool """ self._device = device self._zonetracker = Zonetracker(self) self._lrr_system = LRRSystem(self) self._ignore_message_states = ignore_message_states + self._ignore_lrr_states = ignore_lrr_states self._battery_timeout = AlarmDecoder.BATTERY_TIMEOUT self._fire_timeout = AlarmDecoder.FIRE_TIMEOUT self._power_status = None @@ -146,10 +149,7 @@ class AlarmDecoder(object): self._bypass_status = {} self._armed_status = None self._armed_stay = False - self._fire_status = (False, 0) - self._fire_alarming = False - self._fire_alarming_changed = 0 - self._fire_state = FireState.NONE + self._fire_status = False self._battery_status = (False, 0) self._panic_status = False self._relay_status = {} @@ -470,8 +470,6 @@ class AlarmDecoder(object): if self._internal_address_mask & msg.mask > 0: if not self._ignore_message_states: self._update_internal_states(msg) - else: - self._update_fire_status(status=None) self.on_message(message=msg) @@ -519,7 +517,8 @@ class AlarmDecoder(object): """ msg = LRRMessage(data) - self._lrr_system.update(msg) + if not self._ignore_lrr_states: + self._lrr_system.update(msg) self.on_lrr_message(message=msg) return msg @@ -812,54 +811,26 @@ class AlarmDecoder(object): :returns: boolean indicating the new status """ - is_lrr = status is not None fire_status = status + last_status = self._fire_status if isinstance(message, Message): - fire_status = message.fire_alarm - - last_status, last_update = self._fire_status - - if self._fire_state == FireState.NONE: - # Always move to a FIRE state if detected - if fire_status == True: - self._fire_state = FireState.ALARM - self._fire_status = (fire_status, time.time()) - - self.on_fire(status=FireState.ALARM) - - elif self._fire_state == FireState.ALARM: - # If we've received an LRR CANCEL message, move to ACKNOWLEDGED - if is_lrr and fire_status == False: - self._fire_state = FireState.ACKNOWLEDGED - self._fire_status = (fire_status, time.time()) - self.on_fire(status=FireState.ACKNOWLEDGED) + # Quirk in Ademco panels. The fire bit drops on "SYSTEM LO BAT" messages. + # needs to be done for different languages. + if self.mode == ADEMCO and message.text.startswith("SYSTEM"): + fire_status = last_status else: - # Handle bouncing status changes and timeout in order to revert back to NONE. - if last_status != fire_status or fire_status == True: - self._fire_status = (fire_status, time.time()) - - if fire_status == False and time.time() > last_update + self._fire_timeout: - self._fire_state = FireState.NONE - self.on_fire(status=FireState.NONE) - - elif self._fire_state == FireState.ACKNOWLEDGED: - # If we've received a second LRR FIRE message after a CANCEL, revert back to FIRE and trigger another event. - if is_lrr and fire_status == True: - self._fire_state = FireState.ALARM - self._fire_status = (fire_status, time.time()) - - self.on_fire(status=FireState.ALARM) - else: - # Handle bouncing status changes and timeout in order to revert back to NONE. - if last_status != fire_status or fire_status == True: - self._fire_status = (fire_status, time.time()) + fire_status = message.fire_alarm + + if fire_status is None: + return - if fire_status != True and time.time() > last_update + self._fire_timeout: - self._fire_state = FireState.NONE - self.on_fire(status=FireState.NONE) + if fire_status != self._fire_status: + self._fire_status, old_status = fire_status, self._fire_status - return self._fire_state == FireState.ALARM + if old_status is not None: + self.on_fire(status=self._fire_status) + return self._fire_status def _update_panic_status(self, status=None): """ diff --git a/test/test_ad2.py b/test/test_ad2.py index 9f53535..1448473 100644 --- a/test/test_ad2.py +++ b/test/test_ad2.py @@ -304,32 +304,23 @@ class TestAlarmDecoder(TestCase): self.assertFalse(self._battery) def test_fire_alarm_event(self): - self._fire = FireState.NONE + msg = self._decoder._handle_message(b'[0000000000000000----],000,[f707000600e5800c0c020000]," "') + self.assertTrue(self._fire) # Not set the first time we hit it. msg = self._decoder._handle_message(b'[0000000000000100----],000,[f707000600e5800c0c020000]," "') - self.assertEquals(self._fire, FireState.ALARM) - - # force the timeout to expire. - with patch.object(time, 'time', return_value=self._decoder._fire_status[1] + 35): - msg = self._decoder._handle_message(b'[0000000000000000----],000,[f707000600e5800c0c020000]," "') - self.assertEquals(self._fire, FireState.NONE) + self.assertTrue(self._fire) def test_fire_lrr(self): - self._fire = FireState.NONE + self._fire = False msg = self._decoder._handle_message(b'!LRR:095,1,CID_1110,ff') # Fire: Non-specific self.assertIsInstance(msg, LRRMessage) - self.assertEquals(self._fire, FireState.ALARM) + self.assertTrue(self._fire) msg = self._decoder._handle_message(b'!LRR:001,1,CID_1406,ff') # Open/Close: Cancel self.assertIsInstance(msg, LRRMessage) - self.assertEquals(self._fire, FireState.ACKNOWLEDGED) - - # force the timeout to expire. - with patch.object(time, 'time', return_value=self._decoder._fire_status[1] + 35): - msg = self._decoder._handle_message(b'[0000000000000000----],000,[f707000600e5800c0c020000]," "') - self.assertEquals(self._fire, FireState.NONE) + self.assertFalse(self._fire) def test_hit_for_faults(self): self._decoder._handle_message(b'[0000000000000000----],000,[f707000600e5800c0c020000],"Hit * for faults "')