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.

168 lines
4.4 KiB

  1. import usb.core
  2. import usb.util
  3. import time
  4. import threading
  5. from pyftdi.pyftdi.ftdi import *
  6. from pyftdi.pyftdi.usbtools import *
  7. from . import util
  8. from .event import event
  9. class Device(object):
  10. on_open = event.Event('Called when the device has been opened')
  11. on_close = event.Event('Called when the device has been closed')
  12. on_read = event.Event('Called when a line has been read from the device')
  13. on_write = event.Event('Called when data has been written to the device')
  14. def __init__(self):
  15. pass
  16. def __del__(self):
  17. pass
  18. class ReadThread(threading.Thread):
  19. def __init__(self, device):
  20. threading.Thread.__init__(self)
  21. self._device = device
  22. self._running = False
  23. def stop(self):
  24. self._running = False
  25. def run(self):
  26. self._running = True
  27. while self._running:
  28. try:
  29. self._device.read_line()
  30. except util.CommError, err:
  31. self.stop()
  32. time.sleep(0.25)
  33. class USBDevice(Device):
  34. FTDI_VENDOR_ID = 0x0403
  35. FTDI_PRODUCT_ID = 0x6001
  36. BAUDRATE = 115200
  37. @staticmethod
  38. def find_all():
  39. devices = []
  40. try:
  41. devices = Ftdi.find_all([(USBDevice.FTDI_VENDOR_ID, USBDevice.FTDI_PRODUCT_ID)], nocache=True)
  42. except (usb.core.USBError, FtdiError), err:
  43. raise util.CommError('Error enumerating AD2USB devices: {0}'.format(str(err)))
  44. return devices
  45. def __init__(self, vid=FTDI_VENDOR_ID, pid=FTDI_PRODUCT_ID, serial=None, description=None):
  46. Device.__init__(self)
  47. self._vendor_id = vid
  48. self._product_id = pid
  49. self._serial_number = serial
  50. self._description = description
  51. self._buffer = ''
  52. self._device = Ftdi()
  53. self._running = False
  54. self._read_thread = Device.ReadThread(self)
  55. def open(self, baudrate=BAUDRATE, interface=0, index=0):
  56. self._running = True
  57. try:
  58. self._device.open(self._vendor_id,
  59. self._product_id,
  60. interface,
  61. index,
  62. self._serial_number,
  63. self._description)
  64. self._device.set_baudrate(baudrate)
  65. except (usb.core.USBError, FtdiError), err:
  66. self.on_close()
  67. raise util.CommError('Error opening AD2USB device: {0}'.format(str(err)))
  68. else:
  69. self._read_thread.start()
  70. self.on_open((self._serial_number, self._description))
  71. def close(self):
  72. try:
  73. self._running = False
  74. self._read_thread.stop()
  75. self._device.close()
  76. except (FtdiError, usb.core.USBError):
  77. pass
  78. self.on_close()
  79. def write(self, data):
  80. self._device.write_data(data)
  81. self.on_write(data)
  82. def read_line(self, timeout=0.0):
  83. start_time = time.time()
  84. got_line = False
  85. ret = None
  86. try:
  87. while self._running:
  88. buf = self._device.read_data(1)
  89. self._buffer += buf
  90. if buf == "\n":
  91. if len(self._buffer) > 1:
  92. if self._buffer[-2] == "\r":
  93. self._buffer = self._buffer[:-2]
  94. # ignore if we just got \r\n with nothing else in the buffer.
  95. if len(self._buffer) != 0:
  96. got_line = True
  97. break
  98. else:
  99. self._buffer = self._buffer[:-1]
  100. if timeout > 0 and time.time() - start_time > timeout:
  101. break
  102. time.sleep(0.01)
  103. except (usb.core.USBError, FtdiError), err:
  104. self.close()
  105. raise util.CommError('Error reading from AD2USB device: {0}'.format(str(err)))
  106. else:
  107. if got_line:
  108. ret = self._buffer
  109. self._buffer = ''
  110. self.on_read(ret)
  111. return ret
  112. class SerialDevice(Device):
  113. BAUDRATE = 9600
  114. def __init__(self):
  115. Device.__init__(self)
  116. def __del__(self):
  117. pass
  118. def open(self, baudrate=BAUDRATE, interface="COM1", index=0):
  119. pass
  120. def close(self):
  121. pass
  122. def write(self, data):
  123. pass
  124. def read_line(self, data):
  125. pass