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.

139 lines
3.7 KiB

  1. """
  2. Provides utility classes for the AD2USB devices.
  3. """
  4. import ad2usb
  5. import time
  6. import traceback
  7. import threading
  8. class NoDeviceError(Exception):
  9. """
  10. No devices found.
  11. """
  12. pass
  13. class CommError(Exception):
  14. """
  15. There was an error communicating with the device.
  16. """
  17. pass
  18. class TimeoutError(Exception):
  19. """
  20. There was a timeout while trying to communicate with the device.
  21. """
  22. pass
  23. class InvalidMessageError(Exception):
  24. """
  25. The format of the panel message was invalid.
  26. """
  27. pass
  28. class Firmware(object):
  29. """
  30. Represents firmware for the AD2USB/AD2SERIAL devices.
  31. """
  32. # Constants
  33. STAGE_START = 0
  34. STAGE_WAITING = 1
  35. STAGE_BOOT = 2
  36. STAGE_LOAD = 3
  37. STAGE_UPLOADING = 4
  38. STAGE_DONE = 5
  39. @staticmethod
  40. def upload(dev, filename, progress_callback=None):
  41. """
  42. Uploads firmware to an AD2USB/AD2SERIAL device.
  43. """
  44. def do_upload():
  45. """
  46. Perform the actual firmware upload to the device.
  47. """
  48. with open(filename) as f:
  49. for line in f:
  50. line = line.rstrip()
  51. if line[0] == ':':
  52. dev.write(line + "\r")
  53. res = dev.read_line(timeout=10.0)
  54. if progress_callback is not None:
  55. progress_callback(Firmware.STAGE_UPLOADING)
  56. time.sleep(0.05)
  57. def read_until(pattern, timeout=0.0):
  58. """
  59. Read characters until a specific pattern is found or the timeout is hit.
  60. """
  61. def timeout_event():
  62. timeout_event.reading = False
  63. timeout_event.reading = True
  64. timer = None
  65. if timeout > 0:
  66. timer = threading.Timer(timeout, timeout_event)
  67. timer.start()
  68. buf = ''
  69. position = 0
  70. while timeout_event.reading:
  71. try:
  72. char = dev.read()
  73. if char is not None and char != '':
  74. if char == pattern[position]:
  75. position = position + 1
  76. if position == len(pattern):
  77. break
  78. else:
  79. position = 0
  80. except Exception, err:
  81. pass
  82. if timer:
  83. if timer.is_alive():
  84. timer.cancel()
  85. else:
  86. raise TimeoutError('Timeout while waiting for line terminator.')
  87. def stage_callback(stage):
  88. if progress_callback is not None:
  89. progress_callback(stage)
  90. if dev is None:
  91. raise NoDeviceError('No device specified for firmware upload.')
  92. stage_callback(Firmware.STAGE_START)
  93. if dev.is_reader_alive():
  94. # Close the reader thread and wait for it to die, otherwise
  95. # it interferes with our reading.
  96. dev.stop_reader()
  97. while dev._read_thread.is_alive():
  98. stage_callback(Firmware.STAGE_WAITING)
  99. time.sleep(1)
  100. # Reboot the device and wait for the boot loader.
  101. stage_callback(Firmware.STAGE_BOOT)
  102. dev.write("=")
  103. read_until('!boot', timeout=15.0)
  104. # Get ourselves into the boot loader and wait for indication
  105. # that it's ready for the firmware upload.
  106. stage_callback(Firmware.STAGE_LOAD)
  107. dev.write("=")
  108. read_until('!load', timeout=15.0)
  109. # And finally do the upload.
  110. do_upload()
  111. stage_callback(Firmware.STAGE_DONE)