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.

1077 lines
28 KiB

  1. """
  2. Contains different types of devices belonging to the Alarm Decoder (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 Alarm Decoder (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 on_attached: Function to call when a device is attached.
  381. :type on_attached: function
  382. :param on_detached: Function to call when a device is detached.
  383. :type on_detached: function
  384. """
  385. threading.Thread.__init__(self)
  386. if on_attached:
  387. self.on_attached += on_attached
  388. if on_detached:
  389. self.on_detached += on_detached
  390. self._running = False
  391. def stop(self):
  392. """
  393. Stops the thread.
  394. """
  395. self._running = False
  396. def run(self):
  397. """
  398. The actual detection process.
  399. """
  400. self._running = True
  401. last_devices = set()
  402. while self._running:
  403. try:
  404. current_devices = set(USBDevice.find_all())
  405. new_devices = [d for d in current_devices if d not in last_devices]
  406. removed_devices = [d for d in last_devices if d not in current_devices]
  407. last_devices = current_devices
  408. for d in new_devices:
  409. self.on_attached(device=d)
  410. for d in removed_devices:
  411. self.on_detached(device=d)
  412. except CommError, err:
  413. pass
  414. time.sleep(0.25)
  415. class SerialDevice(Device):
  416. """
  417. AD2USB or AD2SERIAL device exposed with the pyserial interface.
  418. """
  419. # Constants
  420. BAUDRATE = 19200
  421. """Default baudrate for Serial devices."""
  422. @staticmethod
  423. def find_all(pattern=None):
  424. """
  425. Returns all serial ports present.
  426. :param pattern: Pattern to search for when retrieving serial ports.
  427. :type pattern: str
  428. :returns: list of devices
  429. :raises: CommError
  430. """
  431. devices = []
  432. try:
  433. if pattern:
  434. devices = serial.tools.list_ports.grep(pattern)
  435. else:
  436. devices = serial.tools.list_ports.comports()
  437. except SerialException, err:
  438. raise CommError('Error enumerating serial devices: {0}'.format(str(err)), err)
  439. return devices
  440. @property
  441. def interface(self):
  442. """
  443. Retrieves the interface used to connect to the device.
  444. :returns: the interface used to connect to the device.
  445. """
  446. return self._port
  447. @interface.setter
  448. def interface(self, value):
  449. """
  450. Sets the interface used to connect to the device.
  451. :param value: The name of the serial device.
  452. :type value: string
  453. """
  454. self._port = value
  455. def __init__(self, interface=None):
  456. """
  457. Constructor
  458. :param interface: The device to open.
  459. :type interface: str
  460. """
  461. Device.__init__(self)
  462. self._port = interface
  463. self._id = interface
  464. self._device = serial.Serial(timeout=0, writeTimeout=0) # Timeout = non-blocking to match pyftdi.
  465. def open(self, baudrate=BAUDRATE, no_reader_thread=False):
  466. """
  467. Opens the device.
  468. :param baudrate: The baudrate to use with the device.
  469. :type baudrate: int
  470. :param no_reader_thread: Whether or not to automatically start the reader thread.
  471. :type no_reader_thread: bool
  472. :raises: NoDeviceError
  473. """
  474. # Set up the defaults
  475. if baudrate is None:
  476. baudrate = SerialDevice.BAUDRATE
  477. if self._port is None:
  478. raise NoDeviceError('No device interface specified.')
  479. self._device.port = self._port
  480. # Open the device and start up the reader thread.
  481. try:
  482. self._device.open()
  483. self._device.baudrate = baudrate # NOTE: Setting the baudrate before opening the
  484. # port caused issues with Moschip 7840/7820
  485. # USB Serial Driver converter. (mos7840)
  486. #
  487. # Moving it to this point seems to resolve
  488. # all issues with it.
  489. except (serial.SerialException, ValueError), err:
  490. raise NoDeviceError('Error opening device on port {0}.'.format(self._port), err)
  491. else:
  492. self._running = True
  493. self.on_open()
  494. if not no_reader_thread:
  495. self._read_thread.start()
  496. return self
  497. def close(self):
  498. """
  499. Closes the device.
  500. """
  501. try:
  502. Device.close(self)
  503. except:
  504. pass
  505. def write(self, data):
  506. """
  507. Writes data to the device.
  508. :param data: The data to write.
  509. :type data: str
  510. :raises: CommError
  511. """
  512. try:
  513. self._device.write(data)
  514. except serial.SerialTimeoutException, err:
  515. pass
  516. except serial.SerialException, err:
  517. raise CommError('Error writing to device.', err)
  518. else:
  519. self.on_write(data=data)
  520. def read(self):
  521. """
  522. Reads a single character from the device.
  523. :returns: The character read from the device.
  524. :raises: CommError
  525. """
  526. ret = None
  527. try:
  528. ret = self._device.read(1)
  529. except serial.SerialException, err:
  530. raise CommError('Error reading from device: {0}'.format(str(err)), err)
  531. return ret
  532. def read_line(self, timeout=0.0, purge_buffer=False):
  533. """
  534. Reads a line from the device.
  535. :param timeout: The read timeout.
  536. :type timeout: float
  537. :param purge_buffer: Indicates whether to purge the buffer prior to reading.
  538. :type purge_buffer: bool
  539. :returns: The line read.
  540. :raises: CommError, TimeoutError
  541. """
  542. def timeout_event():
  543. timeout_event.reading = False
  544. timeout_event.reading = True
  545. got_line = False
  546. ret = None
  547. timer = None
  548. if timeout > 0:
  549. timer = threading.Timer(timeout, timeout_event)
  550. timer.start()
  551. try:
  552. while timeout_event.reading:
  553. buf = self._device.read(1)
  554. if buf != '' and buf != "\xff": # AD2SERIAL specifically apparently sends down \xFF on boot.
  555. self._buffer += buf
  556. if buf == "\n":
  557. if len(self._buffer) > 1:
  558. if self._buffer[-2] == "\r":
  559. self._buffer = self._buffer[:-2]
  560. # ignore if we just got \r\n with nothing else in the buffer.
  561. if len(self._buffer) != 0:
  562. got_line = True
  563. break
  564. else:
  565. self._buffer = self._buffer[:-1]
  566. except (OSError, serial.SerialException), err:
  567. if timer:
  568. timer.cancel()
  569. raise CommError('Error reading from device: {0}'.format(str(err)), err)
  570. else:
  571. if got_line:
  572. ret = self._buffer
  573. self._buffer = ''
  574. self.on_read(data=ret)
  575. if timer:
  576. if timer.is_alive():
  577. timer.cancel()
  578. else:
  579. raise TimeoutError('Timeout while waiting for line terminator.')
  580. return ret
  581. class SocketDevice(Device):
  582. """
  583. Device that supports communication with an Alarm Decoder (AD2) that is
  584. exposed via ser2sock or another Serial to IP interface.
  585. """
  586. @property
  587. def interface(self):
  588. """
  589. Retrieves the interface used to connect to the device.
  590. :returns: the interface used to connect to the device.
  591. """
  592. return (self._host, self._port)
  593. @interface.setter
  594. def interface(self, value):
  595. """
  596. Sets the interface used to connect to the device.
  597. :param value: Tuple containing the host and port to use.
  598. :type value: tuple
  599. """
  600. self._host = value[0]
  601. self._port = value[1]
  602. @property
  603. def ssl(self):
  604. """
  605. Retrieves whether or not the device is using SSL.
  606. :returns: Whether or not the device is using SSL.
  607. """
  608. return self._use_ssl
  609. @ssl.setter
  610. def ssl(self, value):
  611. """
  612. Sets whether or not SSL communication is in use.
  613. :param value: Whether or not SSL communication is in use.
  614. :type value: bool
  615. """
  616. self._use_ssl = value
  617. @property
  618. def ssl_certificate(self):
  619. """
  620. Retrieves the SSL client certificate path used for authentication.
  621. :returns: The certificate path
  622. """
  623. return self._ssl_certificate
  624. @ssl_certificate.setter
  625. def ssl_certificate(self, value):
  626. """
  627. Sets the SSL client certificate to use for authentication.
  628. :param value: The path to the SSL certificate.
  629. :type value: str
  630. """
  631. self._ssl_certificate = value
  632. @property
  633. def ssl_key(self):
  634. """
  635. Retrieves the SSL client certificate key used for authentication.
  636. :returns: The key path
  637. """
  638. return self._ssl_key
  639. @ssl_key.setter
  640. def ssl_key(self, value):
  641. """
  642. Sets the SSL client certificate key to use for authentication.
  643. :param value: The path to the SSL key.
  644. :type value: str
  645. """
  646. self._ssl_key = value
  647. @property
  648. def ssl_ca(self):
  649. """
  650. Retrieves the SSL Certificate Authority certificate used for authentication.
  651. :returns: The CA path
  652. """
  653. return self._ssl_ca
  654. @ssl_ca.setter
  655. def ssl_ca(self, value):
  656. """
  657. Sets the SSL Certificate Authority certificate used for authentication.
  658. :param value: The path to the SSL CA certificate.
  659. :type value: str
  660. """
  661. self._ssl_ca = value
  662. def __init__(self, interface=("localhost", 10000)):
  663. """
  664. Constructor
  665. :param interface: Tuple containing the hostname and port of our target.
  666. :type interface: tuple
  667. """
  668. Device.__init__(self)
  669. self._host, self._port = interface
  670. self._use_ssl = False
  671. self._ssl_certificate = None
  672. self._ssl_key = None
  673. self._ssl_ca = None
  674. def open(self, baudrate=None, no_reader_thread=False):
  675. """
  676. Opens the device.
  677. :param baudrate: The baudrate to use
  678. :type baudrate: int
  679. :param no_reader_thread: Whether or not to automatically open the reader thread.
  680. :type no_reader_thread: bool
  681. :raises: NoDeviceError, CommError
  682. """
  683. try:
  684. self._device = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  685. if self._use_ssl:
  686. self._init_ssl()
  687. self._device.connect((self._host, self._port))
  688. if self._use_ssl:
  689. self._device.do_handshake()
  690. self._id = '{0}:{1}'.format(self._host, self._port)
  691. except socket.error, err:
  692. raise NoDeviceError('Error opening device at {0}:{1}'.format(self._host, self._port), err)
  693. else:
  694. self._running = True
  695. self.on_open()
  696. if not no_reader_thread:
  697. self._read_thread.start()
  698. return self
  699. def close(self):
  700. """
  701. Closes the device.
  702. """
  703. try:
  704. # TODO: Find a way to speed up this shutdown.
  705. if self.ssl:
  706. self._device.shutdown()
  707. else:
  708. self._device.shutdown(socket.SHUT_RDWR) # Make sure that it closes immediately.
  709. Device.close(self)
  710. except Exception, ex:
  711. pass
  712. def write(self, data):
  713. """
  714. Writes data to the device.
  715. :param data: The data to write.
  716. :type data: str
  717. :returns: The number of bytes sent.
  718. :raises: CommError
  719. """
  720. data_sent = None
  721. try:
  722. data_sent = self._device.send(data)
  723. if data_sent == 0:
  724. raise CommError('Error writing to device.')
  725. self.on_write(data=data)
  726. except (SSL.Error, socket.error), err:
  727. raise CommError('Error writing to device.', err)
  728. return data_sent
  729. def read(self):
  730. """
  731. Reads a single character from the device.
  732. :returns: The character read from the device.
  733. :raises: CommError
  734. """
  735. data = None
  736. try:
  737. data = self._device.recv(1)
  738. except socket.error, err:
  739. raise CommError('Error while reading from device: {0}'.format(str(err)), err)
  740. return data
  741. def read_line(self, timeout=0.0, purge_buffer=False):
  742. """
  743. Reads a line from the device.
  744. :param timeout: The read timeout.
  745. :type timeout: float
  746. :param purge_buffer: Indicates whether to purge the buffer prior to reading.
  747. :type purge_buffer: bool
  748. :returns: The line read from the device.
  749. :raises: CommError, TimeoutError
  750. """
  751. if purge_buffer:
  752. self._buffer = ''
  753. def timeout_event():
  754. timeout_event.reading = False
  755. timeout_event.reading = True
  756. got_line = False
  757. ret = None
  758. timer = None
  759. if timeout > 0:
  760. timer = threading.Timer(timeout, timeout_event)
  761. timer.start()
  762. try:
  763. while timeout_event.reading:
  764. buf = self._device.recv(1)
  765. if buf != '':
  766. self._buffer += buf
  767. if buf == "\n":
  768. if len(self._buffer) > 1:
  769. if self._buffer[-2] == "\r":
  770. self._buffer = self._buffer[:-2]
  771. # ignore if we just got \r\n with nothing else in the buffer.
  772. if len(self._buffer) != 0:
  773. got_line = True
  774. break
  775. else:
  776. self._buffer = self._buffer[:-1]
  777. except socket.error, err:
  778. if timer:
  779. timer.cancel()
  780. raise CommError('Error reading from device: {0}'.format(str(err)), err)
  781. else:
  782. if got_line:
  783. ret = self._buffer
  784. self._buffer = ''
  785. self.on_read(data=ret)
  786. if timer:
  787. if timer.is_alive():
  788. timer.cancel()
  789. else:
  790. raise TimeoutError('Timeout while waiting for line terminator.')
  791. return ret
  792. def _init_ssl(self):
  793. try:
  794. ctx = SSL.Context(SSL.TLSv1_METHOD)
  795. if isinstance(self.ssl_key, crypto.PKey):
  796. ctx.use_privatekey(self.ssl_key)
  797. else:
  798. ctx.use_privatekey_file(self.ssl_key)
  799. if isinstance(self.ssl_certificate, crypto.X509):
  800. ctx.use_certificate(self.ssl_certificate)
  801. else:
  802. ctx.use_certificate_file(self.ssl_certificate)
  803. if isinstance(self.ssl_ca, crypto.X509):
  804. store = ctx.get_cert_store()
  805. store.add_cert(self.ssl_ca)
  806. else:
  807. ctx.load_verify_locations(self.ssl_ca, None)
  808. ctx.set_verify(SSL.VERIFY_PEER, self._verify_ssl_callback)
  809. self._device = SSL.Connection(ctx, self._device)
  810. except SSL.Error, err:
  811. raise CommError('Error setting up SSL connection.', err)
  812. def _verify_ssl_callback(self, connection, x509, errnum, errdepth, ok):
  813. return ok