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.

158 lines
4.2 KiB

  1. """
  2. Provides utility classes for the `AlarmDecoder`_ (AD2) devices.
  3. .. _AlarmDecoder: http://www.alarmdecoder.com
  4. .. moduleauthor:: Scott Petersen <scott@nutech.com>
  5. """
  6. import time
  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 `AlarmDecoder`_ 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. # FIXME: Rewrite this monstrosity.
  40. @staticmethod
  41. def upload(dev, filename, progress_callback=None):
  42. """
  43. Uploads firmware to an `AlarmDecoder`_ device.
  44. :param filename: firmware filename
  45. :type filename: string
  46. :param progress_callback: callback function used to report progress
  47. :type progress_callback: function
  48. :raises: :py:class:`~alarmdecoder.util.NoDeviceError`, :py:class:`~alarmdecoder.util.TimeoutError`
  49. """
  50. def do_upload():
  51. """
  52. Perform the actual firmware upload to the device.
  53. """
  54. with open(filename) as upload_file:
  55. for line in upload_file:
  56. line = line.rstrip()
  57. if line[0] == ':':
  58. dev.write(line + "\r")
  59. 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
  66. hit.
  67. """
  68. def timeout_event():
  69. """Handles the read timeout event."""
  70. timeout_event.reading = False
  71. timeout_event.reading = True
  72. timer = None
  73. if timeout > 0:
  74. timer = threading.Timer(timeout, timeout_event)
  75. timer.start()
  76. position = 0
  77. while timeout_event.reading:
  78. try:
  79. char = dev.read()
  80. if char is not None and char != '':
  81. if char == pattern[position]:
  82. position = position + 1
  83. if position == len(pattern):
  84. break
  85. else:
  86. position = 0
  87. except Exception:
  88. pass
  89. if timer:
  90. if timer.is_alive():
  91. timer.cancel()
  92. else:
  93. raise TimeoutError('Timeout while waiting for line terminator.')
  94. def stage_callback(stage):
  95. """Callback to update progress for the specified stage."""
  96. if progress_callback is not None:
  97. progress_callback(stage)
  98. if dev is None:
  99. raise NoDeviceError('No device specified for firmware upload.')
  100. stage_callback(Firmware.STAGE_START)
  101. if dev.is_reader_alive():
  102. # Close the reader thread and wait for it to die, otherwise
  103. # it interferes with our reading.
  104. dev.stop_reader()
  105. while dev._read_thread.is_alive():
  106. stage_callback(Firmware.STAGE_WAITING)
  107. time.sleep(1)
  108. time.sleep(2)
  109. # Reboot the device and wait for the boot loader.
  110. stage_callback(Firmware.STAGE_BOOT)
  111. dev.write("=")
  112. read_until('......', timeout=15.0)
  113. # Get ourselves into the boot loader and wait for indication
  114. # that it's ready for the firmware upload.
  115. stage_callback(Firmware.STAGE_LOAD)
  116. dev.write("=")
  117. read_until('!load', timeout=15.0)
  118. # And finally do the upload.
  119. do_upload()
  120. stage_callback(Firmware.STAGE_DONE)