#!/usr/bin/env python3 """Build BullTron/Daly-style BLE frames observed in the Android app.""" from __future__ import annotations import argparse def crc16_modbus_swapped(hex_data: str) -> str: data = bytes.fromhex(hex_data) crc = 0xFFFF for byte in data: crc ^= byte for _ in range(8): if crc & 1: crc = (crc >> 1) ^ 0xA001 else: crc >>= 1 swapped = ((crc & 0xFF00) >> 8) | ((crc & 0x00FF) << 8) return f"{swapped:04X}" def read_frame(start: int, count: int) -> str: body = f"D203{start:04X}{count:04X}" return body + crc16_modbus_swapped(body) def write_single_frame(register: int, value: int) -> str: body = f"D206{register:04X}{value:04X}" return body + crc16_modbus_swapped(body) def write_multi_frame(start: int, count: int, payload_hex: str) -> str: payload = payload_hex.replace(" ", "").upper() body = f"D210{start:04X}{count:04X}{payload}" return body + crc16_modbus_swapped(body) def main() -> None: parser = argparse.ArgumentParser() sub = parser.add_subparsers(dest="cmd", required=True) p_read = sub.add_parser("read") p_read.add_argument("start", type=lambda x: int(x, 0)) p_read.add_argument("count", type=lambda x: int(x, 0)) p_write = sub.add_parser("write") p_write.add_argument("register", type=lambda x: int(x, 0)) p_write.add_argument("value", type=lambda x: int(x, 0)) p_multi = sub.add_parser("write-multi") p_multi.add_argument("start", type=lambda x: int(x, 0)) p_multi.add_argument("count", type=lambda x: int(x, 0)) p_multi.add_argument("payload_hex") args = parser.parse_args() if args.cmd == "read": print(read_frame(args.start, args.count)) elif args.cmd == "write": print(write_single_frame(args.register, args.value)) elif args.cmd == "write-multi": print(write_multi_frame(args.start, args.count, args.payload_hex)) if __name__ == "__main__": main()