mqttdevicemanager/ubxhandler.py

83 lines
2.5 KiB
Python

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}")