import asyncio import aioserial import aiomqtt import pyubx2 from command import command from handler import MQTTHandler, task class StreamWrapper: def __init__(self): self.buffer = bytearray() def read(self, n=1): if not self.buffer: raise BlockingIOError out = self.buffer[:n] del self.buffer[:n] return bytes(out) def readline(self): """Return bytes up to and including the first newline.""" newline_index = self.buffer.find(b"\n") if newline_index == -1: # No newline yet, mimic non-blocking behavior raise BlockingIOError # Include the newline line = self.buffer[: newline_index + 1] del self.buffer[: newline_index + 1] return bytes(line) def extend(self, chunk): self.buffer.extend(chunk) class UBXHandler(MQTTHandler): def __init__( self, mqtt_client: aiomqtt.Client, handler_id: str, serial_port: aioserial.AioSerial, ): super().__init__(mqtt_client, handler_id) self.serial_port = serial_port async def read_serial(self): buffer = StreamWrapper() ubr = pyubx2.UBXReader(buffer, parsing=True) while True: chunk = await self.serial_port.read_async(256) if chunk: buffer.extend(chunk) try: while True: raw, parsed = ubr.read() if raw is None: break yield parsed except BlockingIOError: pass # ordinary behaviour except (pyubx2.UBXStreamError, Exception) as e: print("Some kinda error: ", type(e), e) else: await asyncio.sleep(0) @task async def handle_ubx_messages(self): async for message in self.read_serial(): if isinstance(message, pyubx2.UBXMessage): for name, value in vars(message).items(): # Skip 'private' members if name.startswith("_"): continue topic = f"{self.topic_base}/{message.identity}/{name}" await self.mqtt_client.publish(topic, value, qos=1, retain=True) @command({"type": "number"}, description="An example command") async def example_cmd(args): print(f"Executing command with args {args}")