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.

267 lines
11 KiB

  1. import time
  2. from unittest import TestCase
  3. from mock import Mock, MagicMock, patch
  4. from alarmdecoder.decoder import AlarmDecoder
  5. from alarmdecoder.devices import USBDevice
  6. from alarmdecoder.messages import Message, RFMessage, LRRMessage, ExpanderMessage
  7. from alarmdecoder.event.event import Event, EventHandler
  8. from alarmdecoder.zonetracking import Zonetracker
  9. class TestAlarmDecoder(TestCase):
  10. def setUp(self):
  11. self._panicked = False
  12. self._relay_changed = False
  13. self._power_changed = False
  14. self._alarmed = False
  15. self._bypassed = False
  16. self._battery = False
  17. self._fire = False
  18. self._armed = False
  19. self._got_config = False
  20. self._message_received = False
  21. self._rfx_message_received = False
  22. self._lrr_message_received = False
  23. self._device = Mock(spec=USBDevice)
  24. self._device.on_open = EventHandler(Event(), self._device)
  25. self._device.on_close = EventHandler(Event(), self._device)
  26. self._device.on_read = EventHandler(Event(), self._device)
  27. self._device.on_write = EventHandler(Event(), self._device)
  28. self._decoder = AlarmDecoder(self._device)
  29. self._decoder._zonetracker = Mock(spec=Zonetracker)
  30. self._decoder._zonetracker.on_fault = EventHandler(Event(), self._decoder._zonetracker)
  31. self._decoder._zonetracker.on_restore = EventHandler(Event(), self._decoder._zonetracker)
  32. self._decoder.on_panic += self.on_panic
  33. self._decoder.on_relay_changed += self.on_relay_changed
  34. self._decoder.on_power_changed += self.on_power_changed
  35. self._decoder.on_alarm += self.on_alarm
  36. self._decoder.on_bypass += self.on_bypass
  37. self._decoder.on_low_battery += self.on_battery
  38. self._decoder.on_fire += self.on_fire
  39. self._decoder.on_arm += self.on_arm
  40. self._decoder.on_disarm += self.on_disarm
  41. self._decoder.on_config_received += self.on_config
  42. self._decoder.on_message += self.on_message
  43. self._decoder.on_rfx_message += self.on_rfx_message
  44. self._decoder.on_lrr_message += self.on_lrr_message
  45. self._decoder.address_mask = int('ffffffff', 16)
  46. self._decoder.open()
  47. def tearDown(self):
  48. pass
  49. def on_panic(self, sender, *args, **kwargs):
  50. self._panicked = kwargs['status']
  51. def on_relay_changed(self, sender, *args, **kwargs):
  52. self._relay_changed = True
  53. def on_power_changed(self, sender, *args, **kwargs):
  54. self._power_changed = kwargs['status']
  55. def on_alarm(self, sender, *args, **kwargs):
  56. self._alarmed = kwargs['status']
  57. def on_bypass(self, sender, *args, **kwargs):
  58. self._bypassed = kwargs['status']
  59. def on_battery(self, sender, *args, **kwargs):
  60. self._battery = kwargs['status']
  61. def on_fire(self, sender, *args, **kwargs):
  62. self._fire = kwargs['status']
  63. def on_arm(self, sender, *args, **kwargs):
  64. self._armed = True
  65. def on_disarm(self, sender, *args, **kwargs):
  66. self._armed = False
  67. def on_config(self, sender, *args, **kwargs):
  68. self._got_config = True
  69. def on_message(self, sender, *args, **kwargs):
  70. self._message_received = True
  71. def on_rfx_message(self, sender, *args, **kwargs):
  72. self._rfx_message_received = True
  73. def on_lrr_message(self, sender, *args, **kwargs):
  74. self._lrr_message_received = True
  75. def test_open(self):
  76. self._decoder.open()
  77. self._device.open.assert_any_calls()
  78. def test_close(self):
  79. self._decoder.open()
  80. self._decoder.close()
  81. self._device.close.assert_any_calls()
  82. def test_send(self):
  83. self._decoder.send('test')
  84. self._device.write.assert_called_with('test')
  85. def test_get_config(self):
  86. self._decoder.get_config()
  87. self._device.write.assert_called_with("C\r")
  88. def test_save_config(self):
  89. self._decoder.save_config()
  90. self._device.write.assert_any_calls()
  91. def test_reboot(self):
  92. self._decoder.reboot()
  93. self._device.write.assert_called_with('=')
  94. def test_fault(self):
  95. self._decoder.fault_zone(1)
  96. self._device.write.assert_called_with("L{0:02}{1}\r".format(1, 1))
  97. def test_fault_wireproblem(self):
  98. self._decoder.fault_zone(1, simulate_wire_problem=True)
  99. self._device.write.assert_called_with("L{0:02}{1}\r".format(1, 2))
  100. def test_clear_zone(self):
  101. self._decoder.clear_zone(1)
  102. self._device.write.assert_called_with("L{0:02}0\r".format(1))
  103. def test_message(self):
  104. msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  105. self.assertIsInstance(msg, Message)
  106. self._decoder._on_read(self, data='[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  107. self.assertTrue(self._message_received)
  108. def test_message_kpe(self):
  109. msg = self._decoder._handle_message('!KPM:[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  110. self.assertIsInstance(msg, Message)
  111. self._decoder._on_read(self, data='[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  112. self.assertTrue(self._message_received)
  113. def test_expander_message(self):
  114. msg = self._decoder._handle_message('!EXP:07,01,01')
  115. self.assertIsInstance(msg, ExpanderMessage)
  116. def test_relay_message(self):
  117. self._decoder.open()
  118. msg = self._decoder._handle_message('!REL:12,01,01')
  119. self.assertIsInstance(msg, ExpanderMessage)
  120. self.assertEquals(self._relay_changed, True)
  121. def test_rfx_message(self):
  122. msg = self._decoder._handle_message('!RFX:0180036,80')
  123. self.assertIsInstance(msg, RFMessage)
  124. self.assertTrue(self._rfx_message_received)
  125. def test_panic(self):
  126. self._decoder.open()
  127. msg = self._decoder._handle_message('!LRR:012,1,ALARM_PANIC')
  128. self.assertEquals(self._panicked, True)
  129. msg = self._decoder._handle_message('!LRR:012,1,CANCEL')
  130. self.assertEquals(self._panicked, False)
  131. self.assertIsInstance(msg, LRRMessage)
  132. def test_config_message(self):
  133. self._decoder.open()
  134. msg = self._decoder._handle_message('!CONFIG>ADDRESS=18&CONFIGBITS=ff00&LRR=N&EXP=NNNNN&REL=NNNN&MASK=ffffffff&DEDUPLICATE=N')
  135. self.assertEquals(self._decoder.address, 18)
  136. self.assertEquals(self._decoder.configbits, int('ff00', 16))
  137. self.assertEquals(self._decoder.address_mask, int('ffffffff', 16))
  138. self.assertEquals(self._decoder.emulate_zone, [False for x in range(5)])
  139. self.assertEquals(self._decoder.emulate_relay, [False for x in range(4)])
  140. self.assertEquals(self._decoder.emulate_lrr, False)
  141. self.assertEquals(self._decoder.deduplicate, False)
  142. self.assertEquals(self._got_config, True)
  143. def test_power_changed_event(self):
  144. msg = self._decoder._handle_message('[0000000100000000----],000,[f707000600e5800c0c020000]," "')
  145. self.assertEquals(self._power_changed, False) # Not set first time we hit it.
  146. msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  147. self.assertEquals(self._power_changed, False)
  148. msg = self._decoder._handle_message('[0000000100000000----],000,[f707000600e5800c0c020000]," "')
  149. self.assertEquals(self._power_changed, True)
  150. def test_alarm_event(self):
  151. msg = self._decoder._handle_message('[0000000000100000----],000,[f707000600e5800c0c020000]," "')
  152. self.assertEquals(self._alarmed, False) # Not set first time we hit it.
  153. msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  154. self.assertEquals(self._alarmed, False)
  155. msg = self._decoder._handle_message('[0000000000100000----],000,[f707000600e5800c0c020000]," "')
  156. self.assertEquals(self._alarmed, True)
  157. def test_zone_bypassed_event(self):
  158. msg = self._decoder._handle_message('[0000001000000000----],000,[f707000600e5800c0c020000]," "')
  159. self.assertEquals(self._bypassed, False) # Not set first time we hit it.
  160. msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  161. self.assertEquals(self._bypassed, False)
  162. msg = self._decoder._handle_message('[0000001000000000----],000,[f707000600e5800c0c020000]," "')
  163. self.assertEquals(self._bypassed, True)
  164. def test_armed_away_event(self):
  165. msg = self._decoder._handle_message('[0100000000000000----],000,[f707000600e5800c0c020000]," "')
  166. self.assertEquals(self._armed, False) # Not set first time we hit it.
  167. msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  168. self.assertEquals(self._armed, False)
  169. msg = self._decoder._handle_message('[0100000000000000----],000,[f707000600e5800c0c020000]," "')
  170. self.assertEquals(self._armed, True)
  171. self._armed = False
  172. msg = self._decoder._handle_message('[0010000000000000----],000,[f707000600e5800c0c020000]," "')
  173. self.assertEquals(self._armed, False) # Not set first time we hit it.
  174. msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  175. self.assertEquals(self._armed, False)
  176. msg = self._decoder._handle_message('[0010000000000000----],000,[f707000600e5800c0c020000]," "')
  177. self.assertEquals(self._armed, True)
  178. def test_battery_low_event(self):
  179. msg = self._decoder._handle_message('[0000000000010000----],000,[f707000600e5800c0c020000]," "')
  180. self.assertEquals(self._battery, True)
  181. # force the timeout to expire.
  182. with patch.object(time, 'time', return_value=self._decoder._battery_status[1] + 35):
  183. msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  184. self.assertEquals(self._battery, False)
  185. def test_fire_alarm_event(self):
  186. msg = self._decoder._handle_message('[0000000000000100----],000,[f707000600e5800c0c020000]," "')
  187. self.assertEquals(self._fire, True)
  188. # force the timeout to expire.
  189. with patch.object(time, 'time', return_value=self._decoder._battery_status[1] + 35):
  190. msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  191. self.assertEquals(self._fire, False)
  192. def test_hit_for_faults(self):
  193. self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000],"Hit * for faults "')
  194. self._decoder._device.write.assert_called_with('*')
  195. def test_zonetracker_update(self):
  196. msg = self._decoder._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "')
  197. self._decoder._zonetracker.update.assert_called_with(msg)