"""Device-to-host message payloads: ``OK`` / ``ERR`` / ``RX`` / ``TX_DONE``.""" from __future__ import annotations import enum import struct from dataclasses import dataclass from donglora.errors import ErrorCode from donglora.info import Info from donglora.modulation import Modulation, decode_modulation, encode_modulation # Message type identifiers TYPE_OK: int = 0x80 TYPE_ERR: int = 0x81 TYPE_RX: int = 0xC0 TYPE_TX_DONE: int = 0xC1 # ── SET_CONFIG response payload ──────────────────────────────────── class SetConfigResultCode(enum.IntEnum): APPLIED = 0 ALREADY_MATCHED = 1 LOCKED_MISMATCH = 2 class Owner(enum.IntEnum): NONE = 0 MINE = 1 OTHER = 2 @dataclass(frozen=True) class SetConfigResult: result: SetConfigResultCode owner: Owner current: Modulation def encode(self) -> bytes: return bytes([int(self.result), int(self.owner)]) + encode_modulation(self.current) @classmethod def decode(cls, data: bytes) -> SetConfigResult: if len(data) < 2: raise ValueError("SetConfigResult too short") return cls( result=SetConfigResultCode(data[0]), owner=Owner(data[1]), current=decode_modulation(data[2:]), ) # ── TX_DONE payload ──────────────────────────────────────────────── class TxResult(enum.IntEnum): TRANSMITTED = 0 CHANNEL_BUSY = 1 CANCELLED = 2 @dataclass(frozen=True) class TxDone: result: TxResult airtime_us: int def encode(self) -> bytes: return bytes([int(self.result)]) + struct.pack(" TxDone: if len(data) != 5: raise ValueError(f"TxDone wire size must be 5, got {len(data)}") result = TxResult(data[0]) (airtime_us,) = struct.unpack_from(" bytes: return ( struct.pack(" RxEvent: if len(data) < cls.METADATA_SIZE: raise ValueError(f"RxEvent payload too short: {len(data)}") (rssi_raw,) = struct.unpack_from(" bytes: return struct.pack(" ErrorCode | int: if len(data) != 2: raise ValueError(f"ERR payload must be 2 bytes, got {len(data)}") (raw,) = struct.unpack_from("