import asyncio import logging from abc import abstractmethod from asyncio import StreamReader, StreamWriter from typing import Optional import serialx from aiorussound import RussoundError from aiorussound.const import ( DEFAULT_PORT, DEFAULT_BAUDRATE, TIMEOUT, ) _LOGGER = logging.getLogger(__package__) class RussoundConnectionHandler: def __init__(self) -> None: self.reader: Optional[StreamReader] = None self.writer: Optional[StreamWriter] = None async def write_str(self, cmd: str) -> None: """Send a string to the Russound client.""" await self.write(bytearray(f"{cmd}\r", "utf-8")) async def write(self, data: bytes) -> None: """Send raw bytes to the Russound client.""" if self.writer is None: raise RussoundError("Connection handler is not initialized.") self.writer.write(data) await self.writer.drain() @abstractmethod async def connect(self) -> None: raise NotImplementedError class RussoundTcpConnectionHandler(RussoundConnectionHandler): def __init__(self, host: str, port: int = DEFAULT_PORT) -> None: """Initialize the Russound object using the event loop, host and port provided. """ super().__init__() self.host = host self.port = port async def connect(self) -> None: _LOGGER.debug("Connecting to %s:%s", self.host, self.port) async with asyncio.timeout(TIMEOUT): reader, writer = await asyncio.open_connection(self.host, self.port) self.reader = reader self.writer = writer class RussoundSerialConnectionHandler(RussoundConnectionHandler): def __init__(self, port: str, baudrate: int = DEFAULT_BAUDRATE) -> None: """Initialize the Russound object using the event loop, port and baudrate provided.""" super().__init__() self.port = port self.baudrate = baudrate async def connect(self) -> None: _LOGGER.debug("Connecting to %s (baudrate: %s)", self.port, self.baudrate) async with asyncio.timeout(TIMEOUT): self.reader, self.writer = await serialx.open_serial_connection( url=self.port, baudrate=self.baudrate, )