|
1 | | -"""Plugwise connections.""" |
| 1 | +"""Base class for serial or socket connections to USB-Stick.""" |
| 2 | +import logging |
| 3 | +import queue |
| 4 | +import threading |
| 5 | +import time |
| 6 | + |
| 7 | +from ..constants import SLEEP_TIME |
| 8 | +from ..messages.requests import NodeRequest |
| 9 | + |
| 10 | +_LOGGER = logging.getLogger(__name__) |
| 11 | + |
| 12 | + |
| 13 | +class StickConnection: |
| 14 | + """Generic Plugwise stick connection.""" |
| 15 | + |
| 16 | + def __init__(self, port, parser): |
| 17 | + """Initialize StickConnection.""" |
| 18 | + self.port = port |
| 19 | + self.parser = parser |
| 20 | + self.run_reader_thread = False |
| 21 | + self.run_writer_thread = False |
| 22 | + self._is_connected = False |
| 23 | + self._writer = None |
| 24 | + |
| 25 | + self._reader_thread = None |
| 26 | + self._write_queue = None |
| 27 | + self._writer_thread = None |
| 28 | + |
| 29 | + ################################################ |
| 30 | + ### Open connection ### |
| 31 | + ################################################ |
| 32 | + |
| 33 | + def connect(self) -> bool: |
| 34 | + """Open the connection.""" |
| 35 | + if not self._is_connected: |
| 36 | + self._open_connection() |
| 37 | + return self._is_connected |
| 38 | + |
| 39 | + ################################################ |
| 40 | + ### Reader ### |
| 41 | + ################################################ |
| 42 | + |
| 43 | + def _reader_start(self, name): |
| 44 | + """Start the reader thread to receive data.""" |
| 45 | + self._reader_thread = threading.Thread(None, self._reader_deamon, name, (), {}) |
| 46 | + self.run_reader_thread = True |
| 47 | + self._reader_thread.start() |
| 48 | + |
| 49 | + def _reader_deamon(self): |
| 50 | + """Thread to collect available data from connection.""" |
| 51 | + while self.run_reader_thread: |
| 52 | + data = self._read_data() |
| 53 | + if data: |
| 54 | + self.parser(data) |
| 55 | + time.sleep(0.01) |
| 56 | + _LOGGER.debug("Reader daemon stopped") |
| 57 | + |
| 58 | + ################################################ |
| 59 | + ### Writer ### |
| 60 | + ################################################ |
| 61 | + |
| 62 | + def _writer_start(self, name: str): |
| 63 | + """Start the writer thread to send data.""" |
| 64 | + self._write_queue = queue.Queue() |
| 65 | + self._writer_thread = threading.Thread(None, self._writer_daemon, name, (), {}) |
| 66 | + self._writer_thread.daemon = True |
| 67 | + self.run_writer_thread = True |
| 68 | + self._writer_thread.start() |
| 69 | + |
| 70 | + def _writer_daemon(self): |
| 71 | + """Thread to write data from queue to existing connection.""" |
| 72 | + while self.run_writer_thread: |
| 73 | + try: |
| 74 | + (message, callback) = self._write_queue.get(block=True, timeout=1) |
| 75 | + except queue.Empty: |
| 76 | + time.sleep(SLEEP_TIME) |
| 77 | + else: |
| 78 | + _LOGGER.debug( |
| 79 | + "Sending %s to plugwise stick (%s)", |
| 80 | + message.__class__.__name__, |
| 81 | + message.serialize(), |
| 82 | + ) |
| 83 | + self._write_data(message.serialize()) |
| 84 | + time.sleep(SLEEP_TIME) |
| 85 | + if callback: |
| 86 | + callback() |
| 87 | + _LOGGER.debug("Writer daemon stopped") |
| 88 | + |
| 89 | + def send(self, message: NodeRequest, callback=None): |
| 90 | + """Add message to write queue.""" |
| 91 | + self._write_queue.put_nowait((message, callback)) |
| 92 | + |
| 93 | + ################################################ |
| 94 | + ### Connection state ### |
| 95 | + ################################################ |
| 96 | + |
| 97 | + def is_connected(self): |
| 98 | + """Return connection state.""" |
| 99 | + return self._is_connected |
| 100 | + |
| 101 | + def read_thread_alive(self): |
| 102 | + """Return state of write thread.""" |
| 103 | + return self._reader_thread.isAlive() if self.run_reader_thread else False |
| 104 | + |
| 105 | + def write_thread_alive(self): |
| 106 | + """Return state of write thread.""" |
| 107 | + return self._writer_thread.isAlive() if self.run_writer_thread else False |
| 108 | + |
| 109 | + ################################################ |
| 110 | + ### Close connection ### |
| 111 | + ################################################ |
| 112 | + |
| 113 | + def disconnect(self): |
| 114 | + """Close the connection.""" |
| 115 | + if self._is_connected: |
| 116 | + self._is_connected = False |
| 117 | + self.run_writer_thread = False |
| 118 | + self.run_reader_thread = False |
| 119 | + max_wait = 5 * SLEEP_TIME |
| 120 | + while self._writer_thread.isAlive(): |
| 121 | + time.sleep(SLEEP_TIME) |
| 122 | + max_wait -= SLEEP_TIME |
| 123 | + self._close_connection() |
0 commit comments