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.

1075 lines
28 KiB

  1. """
  2. Contains different types of devices belonging to the AD2 family.
  3. .. moduleauthor:: Scott Petersen <scott@nutech.com>
  4. """
  5. import usb.core, usb.util
  6. import time
  7. import threading
  8. import serial, serial.tools.list_ports
  9. import socket
  10. from OpenSSL import SSL, crypto
  11. from pyftdi.pyftdi.ftdi import *
  12. from pyftdi.pyftdi.usbtools import *
  13. from .util import CommError, TimeoutError, NoDeviceError
  14. from .event import event
  15. class Device(object):
  16. """
  17. Generic parent device to all AD2 products.
  18. """
  19. # Generic device events
  20. on_open = event.Event('Called when the device has been opened')
  21. on_close = event.Event('Called when the device has been closed')
  22. on_read = event.Event('Called when a line has been read from the device')
  23. on_write = event.Event('Called when data has been written to the device')
  24. def __init__(self):
  25. """
  26. Constructor
  27. """
  28. self._id = ''
  29. self._buffer = ''
  30. self._device = None
  31. self._running = False
  32. self._read_thread = Device.ReadThread(self)
  33. def __enter__(self):
  34. """
  35. Support for context manager __enter__.
  36. """
  37. return self
  38. def __exit__(self, type, value, traceback):
  39. """
  40. Support for context manager __exit__.
  41. """
  42. self.close()
  43. return False
  44. @property
  45. def id(self):
  46. """
  47. Retrieve the device ID.
  48. :returns: The identification string for the device.
  49. """
  50. return self._id
  51. @id.setter
  52. def id(self, value):
  53. """
  54. Sets the device ID.
  55. :param value: The device identification.
  56. :type value: str
  57. """
  58. self._id = value
  59. def is_reader_alive(self):
  60. """
  61. Indicates whether or not the reader thread is alive.
  62. :returns: Whether or not the reader thread is alive.
  63. """
  64. return self._read_thread.is_alive()
  65. def stop_reader(self):
  66. """
  67. Stops the reader thread.
  68. """
  69. self._read_thread.stop()
  70. def close(self):
  71. """
  72. Closes the device.
  73. """
  74. try:
  75. self._running = False
  76. self._read_thread.stop()
  77. self._device.close()
  78. except:
  79. pass
  80. self.on_close()
  81. class ReadThread(threading.Thread):
  82. """
  83. Reader thread which processes messages from the device.
  84. """
  85. READ_TIMEOUT = 10
  86. """Timeout for the reader thread."""
  87. def __init__(self, device):
  88. """
  89. Constructor
  90. :param device: The device used by the reader thread.
  91. :type device: devices.Device
  92. """
  93. threading.Thread.__init__(self)
  94. self._device = device
  95. self._running = False
  96. def stop(self):
  97. """
  98. Stops the running thread.
  99. """
  100. self._running = False
  101. def run(self):
  102. """
  103. The actual read process.
  104. """
  105. self._running = True
  106. while self._running:
  107. try:
  108. self._device.read_line(timeout=self.READ_TIMEOUT)
  109. except TimeoutError, err:
  110. pass
  111. except Exception, err:
  112. self._running = False
  113. #raise err
  114. time.sleep(0.01)
  115. class USBDevice(Device):
  116. """
  117. AD2USB device exposed with PyFTDI's interface.
  118. """
  119. # Constants
  120. FTDI_VENDOR_ID = 0x0403
  121. """Vendor ID used to recognize AD2USB devices."""
  122. FTDI_PRODUCT_ID = 0x6001
  123. """Product ID used to recognize AD2USB devices."""
  124. BAUDRATE = 115200
  125. """Default baudrate for AD2USB devices."""
  126. __devices = []
  127. @classmethod
  128. def find_all(cls, vid=FTDI_VENDOR_ID, pid=FTDI_PRODUCT_ID):
  129. """
  130. Returns all FTDI devices matching our vendor and product IDs.
  131. :returns: list of devices
  132. :raises: CommError
  133. """
  134. cls.__devices = []
  135. try:
  136. cls.__devices = Ftdi.find_all([(vid, pid)], nocache=True)
  137. except (usb.core.USBError, FtdiError), err:
  138. raise CommError('Error enumerating AD2USB devices: {0}'.format(str(err)), err)
  139. return cls.__devices
  140. @classmethod
  141. def devices(cls):
  142. """
  143. Returns a cached list of AD2USB devices located on the system.
  144. :returns: cached list of devices found.
  145. """
  146. return cls.__devices
  147. @classmethod
  148. def find(cls, device=None):
  149. """
  150. Factory method that returns the requested USBDevice device, or the first device.
  151. :param device: Tuple describing the USB device to open, as returned by find_all().
  152. :type device: tuple
  153. :returns: USBDevice object utilizing the specified device.
  154. :raises: NoDeviceError
  155. """
  156. cls.find_all()
  157. if len(cls.__devices) == 0:
  158. raise NoDeviceError('No AD2USB devices present.')
  159. if device is None:
  160. device = cls.__devices[0]
  161. vendor, product, sernum, ifcount, description = device
  162. return USBDevice(interface=sernum)
  163. @classmethod
  164. def start_detection(cls, on_attached=None, on_detached=None):
  165. """
  166. Starts the device detection thread.
  167. :param on_attached: function to be called when a device is attached.
  168. :type on_attached: function
  169. :param on_detached: function to be called when a device is detached.
  170. :type on_detached: function
  171. """
  172. cls.__detect_thread = USBDevice.DetectThread(on_attached, on_detached)
  173. cls.find_all()
  174. cls.__detect_thread.start()
  175. @classmethod
  176. def stop_detection(cls):
  177. """
  178. Stops the device detection thread.
  179. """
  180. try:
  181. cls.__detect_thread.stop()
  182. except:
  183. pass
  184. @property
  185. def interface(self):
  186. """
  187. Retrieves the interface used to connect to the device.
  188. :returns: the interface used to connect to the device.
  189. """
  190. return self._interface
  191. @interface.setter
  192. def interface(self, value):
  193. """
  194. Sets the interface used to connect to the device.
  195. :param value: May specify either the serial number or the device index.
  196. :type value: str or int
  197. """
  198. self._interface = value
  199. if isinstance(value, int):
  200. self._device_number = value
  201. else:
  202. self._serial_number = value
  203. @property
  204. def serial_number(self):
  205. """
  206. Retrieves the serial number of the device.
  207. :returns: The serial number of the device.
  208. """
  209. return self._serial_number
  210. @serial_number.setter
  211. def serial_number(self, value):
  212. """
  213. Sets the serial number of the device.
  214. :param value: The serial number of the device.
  215. :type value: string
  216. """
  217. self._serial_number = value
  218. @property
  219. def description(self):
  220. """
  221. Retrieves the description of the device.
  222. :returns: The description of the device.
  223. """
  224. return self._description
  225. @description.setter
  226. def description(self, value):
  227. """
  228. Sets the description of the device.
  229. :param value: The description of the device.
  230. :type value: string
  231. """
  232. self._description = value
  233. def __init__(self, interface=0):
  234. """
  235. Constructor
  236. :param interface: May specify either the serial number or the device index.
  237. :type interface: str or int
  238. """
  239. Device.__init__(self)
  240. self._device = Ftdi()
  241. self._device_number = 0
  242. self._serial_number = None
  243. self.interface = interface
  244. self._vendor_id = USBDevice.FTDI_VENDOR_ID
  245. self._product_id = USBDevice.FTDI_PRODUCT_ID
  246. self._endpoint = 0
  247. self._description = None
  248. def open(self, baudrate=BAUDRATE, no_reader_thread=False):
  249. """
  250. Opens the device.
  251. :param baudrate: The baudrate to use.
  252. :type baudrate: int
  253. :param no_reader_thread: Whether or not to automatically start the reader thread.
  254. :type no_reader_thread: bool
  255. :raises: NoDeviceError
  256. """
  257. # Set up defaults
  258. if baudrate is None:
  259. baudrate = USBDevice.BAUDRATE
  260. # Open the device and start up the thread.
  261. try:
  262. self._device.open(self._vendor_id,
  263. self._product_id,
  264. self._endpoint,
  265. self._device_number,
  266. self._serial_number,
  267. self._description)
  268. self._device.set_baudrate(baudrate)
  269. if not self._serial_number:
  270. self._serial_number = self._get_serial_number()
  271. self._id = self._serial_number
  272. except (usb.core.USBError, FtdiError), err:
  273. raise NoDeviceError('Error opening device: {0}'.format(str(err)), err)
  274. else:
  275. self._running = True
  276. self.on_open()
  277. if not no_reader_thread:
  278. self._read_thread.start()
  279. return self
  280. def close(self):
  281. """
  282. Closes the device.
  283. """
  284. try:
  285. Device.close(self)
  286. # HACK: Probably should fork pyftdi and make this call in .close().
  287. self._device.usb_dev.attach_kernel_driver(self._device_number)
  288. except:
  289. pass
  290. def write(self, data):
  291. """
  292. Writes data to the device.
  293. :param data: Data to write
  294. :type data: str
  295. :raises: CommError
  296. """
  297. try:
  298. self._device.write_data(data)
  299. self.on_write(data=data)
  300. except FtdiError, err:
  301. raise CommError('Error writing to device: {0}'.format(str(err)), err)
  302. def read(self):
  303. """
  304. Reads a single character from the device.
  305. :returns: The character read from the device.
  306. :raises: CommError
  307. """
  308. ret = None
  309. try:
  310. ret = self._device.read_data(1)
  311. except (usb.core.USBError, FtdiError), err:
  312. raise CommError('Error reading from device: {0}'.format(str(err)), err)
  313. return ret
  314. def read_line(self, timeout=0.0, purge_buffer=False):
  315. """
  316. Reads a line from the device.
  317. :param timeout: Read timeout
  318. :type timeout: float
  319. :param purge_buffer: Indicates whether to purge the buffer prior to reading.
  320. :type purge_buffer: bool
  321. :returns: The line that was read.
  322. :raises: CommError, TimeoutError
  323. """
  324. if purge_buffer:
  325. self._buffer = ''
  326. def timeout_event():
  327. timeout_event.reading = False
  328. timeout_event.reading = True
  329. got_line = False
  330. ret = None
  331. timer = None
  332. if timeout > 0:
  333. timer = threading.Timer(timeout, timeout_event)
  334. timer.start()
  335. try:
  336. while timeout_event.reading:
  337. buf = self._device.read_data(1)
  338. if buf != '':
  339. self._buffer += buf
  340. if buf == "\n":
  341. if len(self._buffer) > 1:
  342. if self._buffer[-2] == "\r":
  343. self._buffer = self._buffer[:-2]
  344. # ignore if we just got \r\n with nothing else in the buffer.
  345. if len(self._buffer) != 0:
  346. got_line = True
  347. break
  348. else:
  349. self._buffer = self._buffer[:-1]
  350. except (usb.core.USBError, FtdiError), err:
  351. if timer:
  352. timer.cancel()
  353. raise CommError('Error reading from device: {0}'.format(str(err)), err)
  354. else:
  355. if got_line:
  356. ret = self._buffer
  357. self._buffer = ''
  358. self.on_read(data=ret)
  359. if timer:
  360. if timer.is_alive():
  361. timer.cancel()
  362. else:
  363. raise TimeoutError('Timeout while waiting for line terminator.')
  364. return ret
  365. def _get_serial_number(self):
  366. """
  367. Retrieves the FTDI device serial number.
  368. :returns: string containing the device serial number.
  369. """
  370. return usb.util.get_string(self._device.usb_dev, 64, self._device.usb_dev.iSerialNumber)
  371. class DetectThread(threading.Thread):
  372. """
  373. Thread that handles detection of added/removed devices.
  374. """
  375. on_attached = event.Event('Called when an AD2USB device has been detected.')
  376. on_detached = event.Event('Called when an AD2USB device has been removed.')
  377. def __init__(self, on_attached=None, on_detached=None):
  378. """
  379. Constructor
  380. :param factory: AD2Factory object to use with the thread.
  381. :type factory: AD2Factory
  382. """
  383. threading.Thread.__init__(self)
  384. if on_attached:
  385. self.on_attached += on_attached
  386. if on_detached:
  387. self.on_detached += on_detached
  388. self._running = False
  389. def stop(self):
  390. """
  391. Stops the thread.
  392. """
  393. self._running = False
  394. def run(self):
  395. """
  396. The actual detection process.
  397. """
  398. self._running = True
  399. last_devices = set()
  400. while self._running:
  401. try:
  402. current_devices = set(USBDevice.find_all())
  403. new_devices = [d for d in current_devices if d not in last_devices]
  404. removed_devices = [d for d in last_devices if d not in current_devices]
  405. last_devices = current_devices
  406. for d in new_devices:
  407. self.on_attached(device=d)
  408. for d in removed_devices:
  409. self.on_detached(device=d)
  410. except CommError, err:
  411. pass
  412. time.sleep(0.25)
  413. class SerialDevice(Device):
  414. """
  415. AD2USB or AD2SERIAL device exposed with the pyserial interface.
  416. """
  417. # Constants
  418. BAUDRATE = 19200
  419. """Default baudrate for Serial devices."""
  420. @staticmethod
  421. def find_all(pattern=None):
  422. """
  423. Returns all serial ports present.
  424. :param pattern: Pattern to search for when retrieving serial ports.
  425. :type pattern: str
  426. :returns: list of devices
  427. :raises: CommError
  428. """
  429. devices = []
  430. try:
  431. if pattern:
  432. devices = serial.tools.list_ports.grep(pattern)
  433. else:
  434. devices = serial.tools.list_ports.comports()
  435. except SerialException, err:
  436. raise CommError('Error enumerating serial devices: {0}'.format(str(err)), err)
  437. return devices
  438. @property
  439. def interface(self):
  440. """
  441. Retrieves the interface used to connect to the device.
  442. :returns: the interface used to connect to the device.
  443. """
  444. return self._port
  445. @interface.setter
  446. def interface(self, value):
  447. """
  448. Sets the interface used to connect to the device.
  449. :param value: The name of the serial device.
  450. :type value: string
  451. """
  452. self._port = value
  453. def __init__(self, interface=None):
  454. """
  455. Constructor
  456. :param interface: The device to open.
  457. :type interface: str
  458. """
  459. Device.__init__(self)
  460. self._port = interface
  461. self._id = interface
  462. self._device = serial.Serial(timeout=0, writeTimeout=0) # Timeout = non-blocking to match pyftdi.
  463. def open(self, baudrate=BAUDRATE, no_reader_thread=False):
  464. """
  465. Opens the device.
  466. :param baudrate: The baudrate to use with the device.
  467. :type baudrate: int
  468. :param no_reader_thread: Whether or not to automatically start the reader thread.
  469. :type no_reader_thread: bool
  470. :raises: NoDeviceError
  471. """
  472. # Set up the defaults
  473. if baudrate is None:
  474. baudrate = SerialDevice.BAUDRATE
  475. if self._port is None:
  476. raise NoDeviceError('No device interface specified.')
  477. self._device.port = self._port
  478. # Open the device and start up the reader thread.
  479. try:
  480. self._device.open()
  481. self._device.baudrate = baudrate # NOTE: Setting the baudrate before opening the
  482. # port caused issues with Moschip 7840/7820
  483. # USB Serial Driver converter. (mos7840)
  484. #
  485. # Moving it to this point seems to resolve
  486. # all issues with it.
  487. except (serial.SerialException, ValueError), err:
  488. raise NoDeviceError('Error opening device on port {0}.'.format(self._port), err)
  489. else:
  490. self._running = True
  491. self.on_open()
  492. if not no_reader_thread:
  493. self._read_thread.start()
  494. return self
  495. def close(self):
  496. """
  497. Closes the device.
  498. """
  499. try:
  500. Device.close(self)
  501. except:
  502. pass
  503. def write(self, data):
  504. """
  505. Writes data to the device.
  506. :param data: The data to write.
  507. :type data: str
  508. :raises: CommError
  509. """
  510. try:
  511. self._device.write(data)
  512. except serial.SerialTimeoutException, err:
  513. pass
  514. except serial.SerialException, err:
  515. raise CommError('Error writing to device.', err)
  516. else:
  517. self.on_write(data=data)
  518. def read(self):
  519. """
  520. Reads a single character from the device.
  521. :returns: The character read from the device.
  522. :raises: CommError
  523. """
  524. ret = None
  525. try:
  526. ret = self._device.read(1)
  527. except serial.SerialException, err:
  528. raise CommError('Error reading from device: {0}'.format(str(err)), err)
  529. return ret
  530. def read_line(self, timeout=0.0, purge_buffer=False):
  531. """
  532. Reads a line from the device.
  533. :param timeout: The read timeout.
  534. :type timeout: float
  535. :param purge_buffer: Indicates whether to purge the buffer prior to reading.
  536. :type purge_buffer: bool
  537. :returns: The line read.
  538. :raises: CommError, TimeoutError
  539. """
  540. def timeout_event():
  541. timeout_event.reading = False
  542. timeout_event.reading = True
  543. got_line = False
  544. ret = None
  545. timer = None
  546. if timeout > 0:
  547. timer = threading.Timer(timeout, timeout_event)
  548. timer.start()
  549. try:
  550. while timeout_event.reading:
  551. buf = self._device.read(1)
  552. if buf != '' and buf != "\xff": # AD2SERIAL specifically apparently sends down \xFF on boot.
  553. self._buffer += buf
  554. if buf == "\n":
  555. if len(self._buffer) > 1:
  556. if self._buffer[-2] == "\r":
  557. self._buffer = self._buffer[:-2]
  558. # ignore if we just got \r\n with nothing else in the buffer.
  559. if len(self._buffer) != 0:
  560. got_line = True
  561. break
  562. else:
  563. self._buffer = self._buffer[:-1]
  564. except (OSError, serial.SerialException), err:
  565. if timer:
  566. timer.cancel()
  567. raise CommError('Error reading from device: {0}'.format(str(err)), err)
  568. else:
  569. if got_line:
  570. ret = self._buffer
  571. self._buffer = ''
  572. self.on_read(data=ret)
  573. if timer:
  574. if timer.is_alive():
  575. timer.cancel()
  576. else:
  577. raise TimeoutError('Timeout while waiting for line terminator.')
  578. return ret
  579. class SocketDevice(Device):
  580. """
  581. Device that supports communication with an AD2 that is exposed via ser2sock or another
  582. Serial to IP interface.
  583. """
  584. @property
  585. def interface(self):
  586. """
  587. Retrieves the interface used to connect to the device.
  588. :returns: the interface used to connect to the device.
  589. """
  590. return (self._host, self._port)
  591. @interface.setter
  592. def interface(self, value):
  593. """
  594. Sets the interface used to connect to the device.
  595. :param value: Tuple containing the host and port to use.
  596. :type value: tuple
  597. """
  598. self._host = value[0]
  599. self._port = value[1]
  600. @property
  601. def ssl(self):
  602. """
  603. Retrieves whether or not the device is using SSL.
  604. :returns: Whether or not the device is using SSL.
  605. """
  606. return self._use_ssl
  607. @ssl.setter
  608. def ssl(self, value):
  609. """
  610. Sets whether or not SSL communication is in use.
  611. :param value: Whether or not SSL communication is in use.
  612. :type value: bool
  613. """
  614. self._use_ssl = value
  615. @property
  616. def ssl_certificate(self):
  617. """
  618. Retrieves the SSL client certificate path used for authentication.
  619. :returns: The certificate path
  620. """
  621. return self._ssl_certificate
  622. @ssl_certificate.setter
  623. def ssl_certificate(self, value):
  624. """
  625. Sets the SSL client certificate to use for authentication.
  626. :param value: The path to the SSL certificate.
  627. :type value: str
  628. """
  629. self._ssl_certificate = value
  630. @property
  631. def ssl_key(self):
  632. """
  633. Retrieves the SSL client certificate key used for authentication.
  634. :returns: The key path
  635. """
  636. return self._ssl_key
  637. @ssl_key.setter
  638. def ssl_key(self, value):
  639. """
  640. Sets the SSL client certificate key to use for authentication.
  641. :param value: The path to the SSL key.
  642. :type value: str
  643. """
  644. self._ssl_key = value
  645. @property
  646. def ssl_ca(self):
  647. """
  648. Retrieves the SSL Certificate Authority certificate used for authentication.
  649. :returns: The CA path
  650. """
  651. return self._ssl_ca
  652. @ssl_ca.setter
  653. def ssl_ca(self, value):
  654. """
  655. Sets the SSL Certificate Authority certificate used for authentication.
  656. :param value: The path to the SSL CA certificate.
  657. :type value: str
  658. """
  659. self._ssl_ca = value
  660. def __init__(self, interface=("localhost", 10000)):
  661. """
  662. Constructor
  663. :param interface: Tuple containing the hostname and port of our target.
  664. :type interface: tuple
  665. """
  666. Device.__init__(self)
  667. self._host, self._port = interface
  668. self._use_ssl = False
  669. self._ssl_certificate = None
  670. self._ssl_key = None
  671. self._ssl_ca = None
  672. def open(self, baudrate=None, no_reader_thread=False):
  673. """
  674. Opens the device.
  675. :param baudrate: The baudrate to use
  676. :type baudrate: int
  677. :param no_reader_thread: Whether or not to automatically open the reader thread.
  678. :type no_reader_thread: bool
  679. :raises: NoDeviceError, CommError
  680. """
  681. try:
  682. self._device = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  683. if self._use_ssl:
  684. self._init_ssl()
  685. self._device.connect((self._host, self._port))
  686. if self._use_ssl:
  687. self._device.do_handshake()
  688. self._id = '{0}:{1}'.format(self._host, self._port)
  689. except socket.error, err:
  690. raise NoDeviceError('Error opening device at {0}:{1}'.format(self._host, self._port), err)
  691. else:
  692. self._running = True
  693. self.on_open()
  694. if not no_reader_thread:
  695. self._read_thread.start()
  696. return self
  697. def close(self):
  698. """
  699. Closes the device.
  700. """
  701. try:
  702. # TODO: Find a way to speed up this shutdown.
  703. if self.ssl:
  704. self._device.shutdown()
  705. else:
  706. self._device.shutdown(socket.SHUT_RDWR) # Make sure that it closes immediately.
  707. Device.close(self)
  708. except Exception, ex:
  709. pass
  710. def write(self, data):
  711. """
  712. Writes data to the device.
  713. :param data: The data to write.
  714. :type data: str
  715. :returns: The number of bytes sent.
  716. :raises: CommError
  717. """
  718. data_sent = None
  719. try:
  720. data_sent = self._device.send(data)
  721. if data_sent == 0:
  722. raise CommError('Error writing to device.')
  723. self.on_write(data=data)
  724. except (SSL.Error, socket.error), err:
  725. raise CommError('Error writing to device.', err)
  726. return data_sent
  727. def read(self):
  728. """
  729. Reads a single character from the device.
  730. :returns: The character read from the device.
  731. :raises: CommError
  732. """
  733. data = None
  734. try:
  735. data = self._device.recv(1)
  736. except socket.error, err:
  737. raise CommError('Error while reading from device: {0}'.format(str(err)), err)
  738. return data
  739. def read_line(self, timeout=0.0, purge_buffer=False):
  740. """
  741. Reads a line from the device.
  742. :param timeout: The read timeout.
  743. :type timeout: float
  744. :param purge_buffer: Indicates whether to purge the buffer prior to reading.
  745. :type purge_buffer: bool
  746. :returns: The line read from the device.
  747. :raises: CommError, TimeoutError
  748. """
  749. if purge_buffer:
  750. self._buffer = ''
  751. def timeout_event():
  752. timeout_event.reading = False
  753. timeout_event.reading = True
  754. got_line = False
  755. ret = None
  756. timer = None
  757. if timeout > 0:
  758. timer = threading.Timer(timeout, timeout_event)
  759. timer.start()
  760. try:
  761. while timeout_event.reading:
  762. buf = self._device.recv(1)
  763. if buf != '':
  764. self._buffer += buf
  765. if buf == "\n":
  766. if len(self._buffer) > 1:
  767. if self._buffer[-2] == "\r":
  768. self._buffer = self._buffer[:-2]
  769. # ignore if we just got \r\n with nothing else in the buffer.
  770. if len(self._buffer) != 0:
  771. got_line = True
  772. break
  773. else:
  774. self._buffer = self._buffer[:-1]
  775. except socket.error, err:
  776. if timer:
  777. timer.cancel()
  778. raise CommError('Error reading from device: {0}'.format(str(err)), err)
  779. else:
  780. if got_line:
  781. ret = self._buffer
  782. self._buffer = ''
  783. self.on_read(data=ret)
  784. if timer:
  785. if timer.is_alive():
  786. timer.cancel()
  787. else:
  788. raise TimeoutError('Timeout while waiting for line terminator.')
  789. return ret
  790. def _init_ssl(self):
  791. try:
  792. ctx = SSL.Context(SSL.TLSv1_METHOD)
  793. if isinstance(self.ssl_key, crypto.PKey):
  794. ctx.use_privatekey(self.ssl_key)
  795. else:
  796. ctx.use_privatekey_file(self.ssl_key)
  797. if isinstance(self.ssl_certificate, crypto.X509):
  798. ctx.use_certificate(self.ssl_certificate)
  799. else:
  800. ctx.use_certificate_file(self.ssl_certificate)
  801. if isinstance(self.ssl_ca, crypto.X509):
  802. store = ctx.get_cert_store()
  803. store.add_cert(self.ssl_ca)
  804. else:
  805. ctx.load_verify_locations(self.ssl_ca, None)
  806. ctx.set_verify(SSL.VERIFY_PEER, self._verify_ssl_callback)
  807. self._device = SSL.Connection(ctx, self._device)
  808. except SSL.Error, err:
  809. raise CommError('Error setting up SSL connection.', err)
  810. def _verify_ssl_callback(self, connection, x509, errnum, errdepth, ok):
  811. return ok