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.

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. if buf != '':
  92. self._buffer += buf
  93. if buf == "\n":
  94. if len(self._buffer) > 1:
  95. if self._buffer[-2] == "\r":
  96. self._buffer = self._buffer[:-2]
  97. # ignore if we just got \r\n with nothing else in the buffer.
  98. if len(self._buffer) != 0:
  99. got_line = True
  100. break
  101. else:
  102. self._buffer = self._buffer[:-1]
  103. if timeout > 0 and time.time() - start_time > timeout:
  104. break
  105. time.sleep(0.01)
  106. except (usb.core.USBError, FtdiError), err:
  107. self.close()
  108. raise util.CommError('Error reading from AD2USB device: {0}'.format(str(err)))
  109. else:
  110. if got_line:
  111. ret = self._buffer
  112. self._buffer = ''
  113. self.on_read(ret)
  114. return ret
  115. class SerialDevice(Device):
  116. BAUDRATE = 19200
  117. def __init__(self):
  118. Device.__init__(self)
  119. self._device = serial.Serial(timeout=0)
  120. self._read_thread = Device.ReadThread(self)
  121. self._buffer = ''
  122. self._running = False
  123. def __del__(self):
  124. pass
  125. def open(self, baudrate=BAUDRATE, interface=0, index=0):
  126. self._device.baudrate = baudrate
  127. self._device.port = interface
  128. try:
  129. self._device.open()
  130. self._running = True
  131. except (serial.SerialException, ValueError), err:
  132. self.on_close()
  133. raise util.NoDeviceError('Error opening AD2SERIAL device on port {0}.'.format(interface))
  134. else:
  135. self.on_open((None, "AD2SERIAL")) # TODO: Fixme.
  136. self._read_thread.start()
  137. def close(self):
  138. try:
  139. self._running = False
  140. self._read_thread.stop()
  141. self._device.close()
  142. except Exception, err:
  143. pass
  144. self.on_close()
  145. def write(self, data):
  146. try:
  147. self._device.write(data)
  148. except serial.SerialTimeoutException, err:
  149. pass
  150. else:
  151. self.on_write(data)
  152. def read_line(self, timeout=0.0):
  153. start_time = time.time()
  154. got_line = False
  155. ret = None
  156. try:
  157. while self._running:
  158. buf = self._device.read(1)
  159. if buf != '' and buf != "\xff": # WTF is this \xff and why is it in my buffer?!
  160. self._buffer += 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