""" Reads raw response data from the Ultraheat unit. To test the connection use validate, which will return the model name. """ import logging from .const import ( DEFAULT_BAUDRATE_DATA_STREAM, DEFAULT_BAUDRATE_WAKE_UP, DEFAULT_TIMEOUT, ) import serialx import time _LOGGER = logging.getLogger(__name__) MAX_LINES_ULTRAHEAT_RESPONSE = 26 class UltraheatReader: def __init__( self, port, baudrate_wake_up=DEFAULT_BAUDRATE_WAKE_UP, baudrate_data_stream=DEFAULT_BAUDRATE_DATA_STREAM, timeout=DEFAULT_TIMEOUT, ) -> None: _LOGGER.debug("Initializing UltraheatReader on port: %s", port) self._port = port self.baudrate_wake_up = baudrate_wake_up self.baudrate_data_stream = baudrate_data_stream self.timeout = timeout def read(self): """Reads the device on the specified port, returning the full string""" with self._connect_serial() as conn: return self._get_data(conn) def _connect_serial(self): """Make the connection to the serial device""" return serialx.serial_for_url( self._port, baudrate=self.baudrate_wake_up, byte_size=7, parity=serialx.Parity.EVEN, stopbits=serialx.StopBits.TWO, read_timeout=self.timeout, xonxoff=0, rtscts=0, ) def _wake_up(self, conn) -> str: """Wake up the device and get the model number. Waking up should be done at 300 baud.""" # Sending /?! _LOGGER.debug("Waking up Ultraheat") conn.write( b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2f\x3f\x21\x0d\x0a" ) # checking if we can read the model (eg. 'LUGCUH50') _LOGGER.debug("Reading model at baudrate %s", self.baudrate_wake_up) start_time = time.time() data = conn.readline() elapsed_time = time.time() - start_time _LOGGER.debug("Got: %s. This took %s seconds", data, elapsed_time) model = data.decode("utf-8")[1:9] if model: _LOGGER.debug("Got model %s", model) else: _LOGGER.error("No model could be read") raise Exception("No model could be read") return model def _get_data(self, conn) -> tuple[str, str]: model = self._wake_up(conn) _LOGGER.debug( "Reading data at baudrate %s. %s bytes in buffer", self.baudrate_data_stream, conn.num_unread_bytes(), ) # Now switch to 2400 BAUD. This could be different for other models. Let me know if you experience problems. conn.baudrate = self.baudrate_data_stream ir_lines = "" ir_line = "" iteration = 0 # reading all lines (typically 25 lines) while "!" not in ir_line and iteration < MAX_LINES_ULTRAHEAT_RESPONSE: iteration += 1 start_time = time.time() data = conn.readline() elapsed_time = time.time() - start_time if len(data) == 0: _LOGGER.debug( "No data received after %s seconds. Empty data usually implies timeout on serial read. Stopping after %s lines of data", elapsed_time, iteration, ) break _LOGGER.debug( "Reading line # %s. Got: %s. This took %s seconds. %s bytes left in buffer.", iteration, data, elapsed_time, conn.num_unread_bytes(), ) ir_line = data.decode("utf-8") _LOGGER.debug("After decoding: `%s`", ir_line) ir_lines += ir_line _LOGGER.debug("Finished reading %s lines of data", iteration) return model, str(ir_lines)