"""高层命令:帧序列编排。协议规范 docs/protocol.md。""" from __future__ import annotations import datetime from . import protocol as P async def set_time(t, dt: datetime.datetime | None = None): dt = dt or datetime.datetime.now() await t.write_rxtx(P.build_set_time(dt)) await t.delay(0.3) await t.write_rxtx(P.simple_command("commit")) async def set_mode(t, mode: str): """模式切换:setri 语义——命令后 300ms 补 e2(ble.js:3956-3962)。""" await t.write_rxtx(P.mode_frame(mode)) await t.delay(0.3) await t.write_rxtx(P.simple_command("commit")) async def toggle(t, name: str): """单字节切换类命令(invert/font/rotate180/hour_format/clock_color),补 e2。""" await t.write_rxtx(P.simple_command(name)) await t.delay(0.3) await t.write_rxtx(P.simple_command("commit")) async def clear(t, param: int = 0x00): for frame in P.clear_frames(param): await t.write_epd(frame) async def upload_image(t, bw: bytes, red: bytes | None, *, small: bool, slot: int | None = None, block_delay: float = 0.0): """传图:可选槽位选择 → bw 平面块 → 红平面块 → 300ms → 01/AA。""" if slot is not None: await t.write_rxtx(P.slot_select_frame(slot)) await t.delay(0.3) for frame in P.image_blocks(bw, small=small, red=False): await t.write_epd(frame) if block_delay: await t.delay(block_delay) if red is not None: for frame in P.image_blocks(red, small=small, red=True): await t.write_epd(frame) if block_delay: await t.delay(block_delay) await t.delay(0.3) await t.write_epd(P.end_frame(small=small)) async def countdown_set(t, d: datetime.date, mode: str, prefix: str | None = None, show_calendar: bool = False): """倒计时:前缀文字位图(可选)→ 日期帧 → E2。 show_calendar=True 走 upload_rili 序列:990* → FA → e10103 → E2(timedjs2.js:308-312)。""" from . import textbitmap as TB if prefix: for frame in TB.prefix_frames(prefix, "calendar" if show_calendar else mode): await t.write_rxtx(frame) await t.delay(1.0) if show_calendar: await t.write_rxtx(bytes([0xFA])) await t.delay(1.0) await t.write_rxtx(P.mode_frame("calendar1")) await t.delay(1.0) await t.write_rxtx(P.simple_command("commit")) return await t.write_rxtx(P.countdown_date_frame(d, mode)) await t.delay(1.0) await t.write_rxtx(P.simple_command("commit")) async def countdown_off(t): for frame in P.countdown_off_frames(): await t.write_rxtx(frame) await t.delay(1.0) async def parking(t, number: str): await t.write_rxtx(P.parking_frame(number)) await t.delay(0.5) await t.write_rxtx(P.simple_command("commit")) async def sleep(t, on: bool, start_h: int, end_h: int): await t.write_rxtx(P.sleep_frame(on, start_h, end_h)) async def rotation(t, count: int | None = None, interval: int | None = None, unit: str = "min"): if count is not None: await t.write_rxtx(P.rotation_count_frame(count)) if interval is not None: await t.write_rxtx(P.rotation_interval_frame(interval, unit)) async def activate(t, code_hex: str): await t.write_rxtx(P.activation_frame(code_hex)) await t.delay(0.4) await t.write_rxtx(P.simple_command("commit")) async def activate_auto(t, mac: bytes | None = None) -> dict: """离线自动激活(firmware-analysis §4.3):MAC→本地 keygen→EF 下发。 mac=None 时经 EFEF 设备 ID 反解(字节序无歧义);返回 {device_id, mac, code}。""" device_id = None if mac is None: device_id = await t.request_device_id() mac = P.recover_mac_from_device_id(device_id) code = P.keygen_code(mac) await activate(t, code.hex()) return {"device_id": device_id, "mac": mac.hex(), "code": code.hex()} async def get_device_id(t) -> str: return await t.request_device_id() async def wifi(t, ssid: str, password: str, city: str = ""): await t.write_rxtx(P.wifi_frame(ssid, password, city)) async def lut(t, value: int): await t.write_rxtx(P.lut_frame(value)) async def raw(t, data: bytes, channel: str = "rxtx"): if channel == "epd": await t.write_epd(data) else: await t.write_rxtx(data)