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