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.

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