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.

277 lines
7.8 KiB

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