A clone of: https://github.com/nutechsoftware/alarmdecoder This is requires as they dropped support for older firmware releases w/o building in backward compatibility code, and they had previously hardcoded pyserial to a python2 only version.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
6.1 KiB

  1. from unittest import TestCase
  2. from mock import Mock, MagicMock
  3. from alarmdecoder import AlarmDecoder
  4. from alarmdecoder.panels import ADEMCO
  5. from alarmdecoder.messages import Message, ExpanderMessage
  6. from alarmdecoder.zonetracking import Zonetracker, Zone
  7. class TestZonetracking(TestCase):
  8. def setUp(self):
  9. self._alarmdecoder = Mock(spec=AlarmDecoder)
  10. self._alarmdecoder.mode = ADEMCO
  11. self._zonetracker = Zonetracker(self._alarmdecoder)
  12. self._zonetracker.on_fault += self.fault_event
  13. self._zonetracker.on_restore += self.restore_event
  14. self._faulted = False
  15. self._restored = False
  16. def tearDown(self):
  17. pass
  18. ### Library events
  19. def fault_event(self, sender, *args, **kwargs):
  20. self._faulted = True
  21. def restore_event(self, sender, *args, **kwargs):
  22. self._restored = True
  23. ### Util
  24. def _build_expander_message(self, msg):
  25. msg = ExpanderMessage(msg)
  26. zone = self._zonetracker.expander_to_zone(msg.address, msg.channel)
  27. return zone, msg
  28. ### Tests
  29. def test_zone_fault(self):
  30. zone, msg = self._build_expander_message('!EXP:07,01,01')
  31. self._zonetracker.update(msg)
  32. self.assertEqual(self._zonetracker._zones[zone].status, Zone.FAULT)
  33. self.assertTrue(self._faulted)
  34. def test_zone_restore(self):
  35. zone, msg = self._build_expander_message('!EXP:07,01,01')
  36. self._zonetracker.update(msg)
  37. zone, msg = self._build_expander_message('!EXP:07,01,00')
  38. self._zonetracker.update(msg)
  39. self.assertEqual(self._zonetracker._zones[zone].status, Zone.CLEAR)
  40. self.assertTrue(self._restored)
  41. def test_message_ready(self):
  42. msg = Message('[0000000000000010----],001,[f707000600e5800c0c020000]," "')
  43. self._zonetracker.update(msg)
  44. self.assertEqual(len(self._zonetracker._zones_faulted), 1)
  45. msg = Message('[1000000000000000----],000,[f707000600e5800c0c020000]," "')
  46. self._zonetracker.update(msg)
  47. self.assertEqual(len(self._zonetracker._zones_faulted), 0)
  48. def test_message_fault_text(self):
  49. msg = Message('[0000000000000000----],001,[f707000600e5800c0c020000],"FAULT 1 "')
  50. self._zonetracker.update(msg)
  51. self.assertEqual(len(self._zonetracker._zones_faulted), 1)
  52. def test_ECP_failure(self):
  53. msg = Message('[0000000000000010----],0bf,[f707000600e5800c0c020000],"CHECK 1 "')
  54. self._zonetracker.update(msg)
  55. self.assertEqual(self._zonetracker._zones['1'].status, Zone.CHECK)
  56. def test_zone_restore_skip(self):
  57. panel_messages = [
  58. '[0000000000000000----],001,[f707000600e5800c0c020000],"FAULT 1 "',
  59. '[0000000000000000----],002,[f707000600e5800c0c020000],"FAULT 2 "',
  60. '[0000000000000000----],001,[f707000600e5800c0c020000],"FAULT 1 "',
  61. '[0000000000000000----],001,[f707000600e5800c0c020000],"FAULT 1 "'
  62. ]
  63. for m in panel_messages:
  64. msg = Message(m)
  65. self._zonetracker.update(msg)
  66. self.assertIn(1, self._zonetracker._zones_faulted)
  67. self.assertNotIn(2, self._zonetracker._zones_faulted)
  68. def test_zone_out_of_order_fault(self):
  69. panel_messages = [
  70. '[0000000000000010----],001,[f707000600e5800c0c020000],"FAULT 1 "',
  71. '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "',
  72. '[0000000000000010----],003,[f707000600e5800c0c020000],"FAULT 3 "',
  73. '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "',
  74. ]
  75. for m in panel_messages:
  76. msg = Message(m)
  77. self._zonetracker.update(msg)
  78. self.assertIn(1, self._zonetracker._zones_faulted)
  79. self.assertIn(3, self._zonetracker._zones_faulted)
  80. self.assertIn(4, self._zonetracker._zones_faulted)
  81. def test_zone_multi_zone_skip_restore(self):
  82. panel_messages = [
  83. '[0000000000000010----],001,[f707000600e5800c0c020000],"FAULT 1 "',
  84. '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "',
  85. '[0000000000000010----],002,[f707000600e5800c0c020000],"FAULT 2 "',
  86. '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "',
  87. '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "',
  88. ]
  89. for m in panel_messages:
  90. msg = Message(m)
  91. self._zonetracker.update(msg)
  92. self.assertNotIn(1, self._zonetracker._zones_faulted)
  93. self.assertNotIn(2, self._zonetracker._zones_faulted)
  94. self.assertIn(4, self._zonetracker._zones_faulted)
  95. def test_zone_timeout_restore(self):
  96. panel_messages = [
  97. '[0000000000000010----],001,[f707000600e5800c0c020000],"FAULT 1 "',
  98. '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "',
  99. '[0000000000000010----],002,[f707000600e5800c0c020000],"FAULT 2 "',
  100. '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "',
  101. '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "',
  102. ]
  103. for m in panel_messages:
  104. msg = Message(m)
  105. self._zonetracker.update(msg)
  106. self.assertIn(4, self._zonetracker._zones_faulted)
  107. self._zonetracker._zones[4].timestamp -= 35 # forcefully expire the zone
  108. # generic message to force an update.
  109. msg = Message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  110. self._zonetracker.update(msg)
  111. self.assertNotIn(4, self._zonetracker._zones_faulted)