From 77c3bdb7d0010ddaaf173e333ada96583d23209b Mon Sep 17 00:00:00 2001 From: f34rdotcom Date: Fri, 6 Apr 2018 04:30:00 +0000 Subject: [PATCH] changed fire handeling to just use Fire bit and remove SYSTEM BATTERY LOW messages to avoid Ademco quirk. Removed processing of LRR messages as default behavior to avoid over processing of events from section #1 and LRR. --- alarmdecoder/decoder.py | 71 ++++++++++++----------------------------- test/test_ad2.py | 21 ++++-------- 2 files changed, 27 insertions(+), 65 deletions(-) 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 "')