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.

150 lines
4.0 KiB

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