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.

281 lines
7.9 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. pass
  36. time.sleep(0.01)
  37. class USBDevice(Device):
  38. FTDI_VENDOR_ID = 0x0403
  39. FTDI_PRODUCT_ID = 0x6001
  40. BAUDRATE = 115200
  41. @staticmethod
  42. def find_all():
  43. devices = []
  44. try:
  45. devices = Ftdi.find_all([(USBDevice.FTDI_VENDOR_ID, USBDevice.FTDI_PRODUCT_ID)], nocache=True)
  46. except (usb.core.USBError, FtdiError), err:
  47. raise util.CommError('Error enumerating AD2USB devices: {0}'.format(str(err)))
  48. return devices
  49. def __init__(self, vid=FTDI_VENDOR_ID, pid=FTDI_PRODUCT_ID, serial=None, description=None, interface=0):
  50. Device.__init__(self)
  51. self._vendor_id = vid
  52. self._product_id = pid
  53. self._serial_number = serial
  54. self._description = description
  55. self._buffer = ''
  56. self._device = Ftdi()
  57. self._running = False
  58. self._interface = interface
  59. self._read_thread = Device.ReadThread(self)
  60. def open(self, baudrate=BAUDRATE, interface=None, index=0):
  61. self._running = True
  62. if baudrate is None:
  63. baudrate = USBDevice.BAUDRATE
  64. if self._interface is None and interface is None:
  65. self._interface = 0
  66. if interface is not None:
  67. self._interface = interface
  68. if index is None:
  69. index = 0
  70. try:
  71. self._device.open(self._vendor_id,
  72. self._product_id,
  73. self._interface,
  74. index,
  75. self._serial_number,
  76. self._description)
  77. self._device.set_baudrate(baudrate)
  78. except (usb.core.USBError, FtdiError), err:
  79. self.on_close()
  80. raise util.CommError('Error opening AD2USB device: {0}'.format(str(err)))
  81. else:
  82. self._read_thread.start()
  83. self.on_open((self._serial_number, self._description))
  84. def close(self):
  85. try:
  86. self._running = False
  87. self._read_thread.stop()
  88. self._device.close()
  89. except (FtdiError, usb.core.USBError):
  90. pass
  91. self.on_close()
  92. def write(self, data):
  93. self._device.write_data(data)
  94. self.on_write(data)
  95. def read_line(self, timeout=0.0):
  96. start_time = time.time()
  97. got_line = False
  98. ret = None
  99. try:
  100. while self._running:
  101. buf = self._device.read_data(1)
  102. if buf != '':
  103. self._buffer += buf
  104. if buf == "\n":
  105. if len(self._buffer) > 1:
  106. if self._buffer[-2] == "\r":
  107. self._buffer = self._buffer[:-2]
  108. # ignore if we just got \r\n with nothing else in the buffer.
  109. if len(self._buffer) != 0:
  110. got_line = True
  111. break
  112. else:
  113. self._buffer = self._buffer[:-1]
  114. if timeout > 0 and time.time() - start_time > timeout:
  115. break
  116. time.sleep(0.01)
  117. except (usb.core.USBError, FtdiError), err:
  118. self.close()
  119. raise util.CommError('Error reading from AD2USB device: {0}'.format(str(err)))
  120. else:
  121. if got_line:
  122. ret = self._buffer
  123. self._buffer = ''
  124. self.on_read(ret)
  125. return ret
  126. class SerialDevice(Device):
  127. BAUDRATE = 19200
  128. @staticmethod
  129. def find_all():
  130. devices = []
  131. try:
  132. devices = serial.tools.list_ports.comports()
  133. except Exception, err:
  134. raise util.CommError('Error enumerating AD2SERIAL devices: {0}'.format(str(err)))
  135. return devices
  136. def __init__(self, interface=None):
  137. Device.__init__(self)
  138. self._device = serial.Serial(timeout=0) # Timeout = non-blocking to match pyftdi.
  139. self._read_thread = Device.ReadThread(self)
  140. self._buffer = ''
  141. self._running = False
  142. self._interface = interface
  143. def __del__(self):
  144. pass
  145. def open(self, baudrate=BAUDRATE, interface=None, index=None):
  146. if baudrate is None:
  147. baudrate = SerialDevice.BAUDRATE
  148. if self._interface is None and interface is None:
  149. raise util.NoDeviceError('No AD2SERIAL device interface specified.')
  150. if interface is not None:
  151. self._interface = interface
  152. self._device.baudrate = baudrate
  153. self._device.port = self._interface
  154. try:
  155. self._device.open()
  156. self._running = True
  157. except (serial.SerialException, ValueError), err:
  158. self.on_close()
  159. raise util.NoDeviceError('Error opening AD2SERIAL device on port {0}.'.format(interface))
  160. else:
  161. self.on_open((None, "AD2SERIAL")) # TODO: Fixme.
  162. self._read_thread.start()
  163. def close(self):
  164. try:
  165. self._running = False
  166. self._read_thread.stop()
  167. self._device.close()
  168. except Exception, err:
  169. pass
  170. self.on_close()
  171. def close_reader(self):
  172. self._read_thread.stop()
  173. def write(self, data):
  174. try:
  175. self._device.write(data)
  176. except serial.SerialTimeoutException, err:
  177. pass
  178. else:
  179. self.on_write(data)
  180. def read(self):
  181. return self._device.read(1)
  182. def read_line(self, timeout=0.0):
  183. start_time = time.time()
  184. got_line = False
  185. ret = None
  186. try:
  187. while self._running:
  188. buf = self._device.read(1)
  189. if buf != '' and buf != "\xff": # WTF is this \xff and why is it in my buffer?!
  190. self._buffer += buf
  191. if buf == "\n":
  192. if len(self._buffer) > 1:
  193. if self._buffer[-2] == "\r":
  194. self._buffer = self._buffer[:-2]
  195. # ignore if we just got \r\n with nothing else in the buffer.
  196. if len(self._buffer) != 0:
  197. got_line = True
  198. break
  199. else:
  200. self._buffer = self._buffer[:-1]
  201. if timeout > 0 and time.time() - start_time > timeout:
  202. raise util.TimeoutError('Timeout while waiting for line terminator.')
  203. except serial.SerialException, err:
  204. raise util.CommError('Error reading from AD2SERIAL device: {0}'.format(str(err)))
  205. else:
  206. if got_line:
  207. ret = self._buffer
  208. self._buffer = ''
  209. self.on_read(ret)
  210. return ret