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.

318 lines
14 KiB

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