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.

186 lines
5.0 KiB

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