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.

242 lines
6.7 KiB

  1. import usb.core
  2. import usb.util
  3. import time
  4. import threading
  5. import serial
  6. import traceback
  7. from pyftdi.pyftdi.ftdi import *
  8. from pyftdi.pyftdi.usbtools import *
  9. from . import util
  10. from .event import event
  11. class Device(object):
  12. on_open = event.Event('Called when the device has been opened')
  13. on_close = event.Event('Called when the device has been closed')
  14. on_read = event.Event('Called when a line has been read from the device')
  15. on_write = event.Event('Called when data has been written to the device')
  16. def __init__(self):
  17. pass
  18. def __del__(self):
  19. pass
  20. class ReadThread(threading.Thread):
  21. def __init__(self, device):
  22. threading.Thread.__init__(self)
  23. self._device = device
  24. self._running = False
  25. def stop(self):
  26. self._running = False
  27. def run(self):
  28. self._running = True
  29. while self._running:
  30. try:
  31. self._device.read_line()
  32. except util.CommError, err:
  33. self.stop()
  34. time.sleep(0.10)
  35. class USBDevice(Device):
  36. FTDI_VENDOR_ID = 0x0403
  37. FTDI_PRODUCT_ID = 0x6001
  38. BAUDRATE = 115200
  39. @staticmethod
  40. def find_all():
  41. devices = []
  42. try:
  43. devices = Ftdi.find_all([(USBDevice.FTDI_VENDOR_ID, USBDevice.FTDI_PRODUCT_ID)], nocache=True)
  44. except (usb.core.USBError, FtdiError), err:
  45. raise util.CommError('Error enumerating AD2USB devices: {0}'.format(str(err)))
  46. return devices
  47. def __init__(self, vid=FTDI_VENDOR_ID, pid=FTDI_PRODUCT_ID, serial=None, description=None):
  48. Device.__init__(self)
  49. self._vendor_id = vid
  50. self._product_id = pid
  51. self._serial_number = serial
  52. self._description = description
  53. self._buffer = ''
  54. self._device = Ftdi()
  55. self._running = False
  56. self._read_thread = Device.ReadThread(self)
  57. def open(self, baudrate=BAUDRATE, interface=0, index=0):
  58. self._running = True
  59. try:
  60. self._device.open(self._vendor_id,
  61. self._product_id,
  62. interface,
  63. index,
  64. self._serial_number,
  65. self._description)
  66. self._device.set_baudrate(baudrate)
  67. except (usb.core.USBError, FtdiError), err:
  68. self.on_close()
  69. raise util.CommError('Error opening AD2USB device: {0}'.format(str(err)))
  70. else:
  71. self._read_thread.start()
  72. self.on_open((self._serial_number, self._description))
  73. def close(self):
  74. try:
  75. self._running = False
  76. self._read_thread.stop()
  77. self._device.close()
  78. except (FtdiError, usb.core.USBError):
  79. pass
  80. self.on_close()
  81. def write(self, data):
  82. self._device.write_data(data)
  83. self.on_write(data)
  84. def read_line(self, timeout=0.0):
  85. start_time = time.time()
  86. got_line = False
  87. ret = None
  88. try:
  89. while self._running:
  90. buf = self._device.read_data(1)
  91. self._buffer += buf
  92. if buf == "\n":
  93. if len(self._buffer) > 1:
  94. if self._buffer[-2] == "\r":
  95. self._buffer = self._buffer[:-2]
  96. # ignore if we just got \r\n with nothing else in the buffer.
  97. if len(self._buffer) != 0:
  98. got_line = True
  99. break
  100. else:
  101. self._buffer = self._buffer[:-1]
  102. if timeout > 0 and time.time() - start_time > timeout:
  103. break
  104. time.sleep(0.01)
  105. except (usb.core.USBError, FtdiError), err:
  106. self.close()
  107. raise util.CommError('Error reading from AD2USB device: {0}'.format(str(err)))
  108. else:
  109. if got_line:
  110. ret = self._buffer
  111. self._buffer = ''
  112. self.on_read(ret)
  113. return ret
  114. class SerialDevice(Device):
  115. BAUDRATE = 19200
  116. def __init__(self):
  117. Device.__init__(self)
  118. self._device = serial.Serial(timeout=0)
  119. self._read_thread = Device.ReadThread(self)
  120. self._buffer = ''
  121. self._running = False
  122. def __del__(self):
  123. pass
  124. def open(self, baudrate=BAUDRATE, interface=0, index=0):
  125. self._device.baudrate = baudrate
  126. self._device.port = interface
  127. try:
  128. self._device.open()
  129. self._running = True
  130. except (serial.SerialException, ValueError), err:
  131. self.on_close()
  132. raise util.NoDeviceError('Error opening AD2SERIAL device on port {0}.'.format(interface))
  133. else:
  134. self.on_open((None, "AD2SERIAL")) # TODO: Fixme.
  135. self._read_thread.start()
  136. def close(self):
  137. try:
  138. self._running = False
  139. self._read_thread.stop()
  140. self._device.close()
  141. except Exception, err:
  142. pass
  143. self.on_close()
  144. def write(self, data):
  145. try:
  146. self._device.write(data)
  147. except serial.SerialTimeoutException, err:
  148. pass
  149. else:
  150. self.on_write(data)
  151. def read_line(self, timeout=0.0):
  152. start_time = time.time()
  153. got_line = False
  154. ret = None
  155. try:
  156. while self._running:
  157. buf = self._device.read(1)
  158. if buf != '' and buf != "\xff": # WTF is this \xff and why is it in my buffer?!
  159. self._buffer += buf
  160. #print '{0:x}'.format(ord(buf))
  161. if buf == "\n":
  162. if len(self._buffer) > 1:
  163. if self._buffer[-2] == "\r":
  164. self._buffer = self._buffer[:-2]
  165. # ignore if we just got \r\n with nothing else in the buffer.
  166. if len(self._buffer) != 0:
  167. got_line = True
  168. break
  169. else:
  170. self._buffer = self._buffer[:-1]
  171. if timeout > 0 and time.time() - start_time > timeout:
  172. break
  173. time.sleep(0.01)
  174. except serial.SerialException, err:
  175. self.close()
  176. raise util.CommError('Error reading from AD2SERIAL device: {0}'.format(str(err)))
  177. else:
  178. if got_line:
  179. ret = self._buffer
  180. self._buffer = ''
  181. self.on_read(ret)
  182. return ret