From cbf7629dc832a128863d109bacf543f62b91b989 Mon Sep 17 00:00:00 2001 From: agent Date: Wed, 29 Jul 2026 03:15:40 +0000 Subject: [PATCH] =?UTF-8?q?feat(bridge):=20build=208=20notify=20=E8=B6=85?= =?UTF-8?q?=E6=97=B6+connect=20subscribe=20=E9=80=89=E9=A1=B9=EF=BC=88OTA?= =?UTF-8?q?=20=E5=87=80=E8=BF=9E=E6=8E=A5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ppclock/bridge.py | 6 ++++-- tools/bridge_server.py | 28 +++++++++++++++++----------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/ppclock/bridge.py b/src/ppclock/bridge.py index 33fea65..95c916b 100644 --- a/src/ppclock/bridge.py +++ b/src/ppclock/bridge.py @@ -46,12 +46,13 @@ class BridgeTransport: def __init__(self, address: str | None = None, timeout: float = 10.0, *, host: str, port: int = DEFAULT_PORT, - name_prefix: str = "NRF-"): + name_prefix: str = "NRF-", subscribe: bool = True): self.address = address self.timeout = timeout self.host = host self.port = port self.name_prefix = name_prefix + self.subscribe = subscribe # OTA 用 False(净连接,避开订阅抖动) self._reader: asyncio.StreamReader | None = None self._writer: asyncio.StreamWriter | None = None self._reader_task: asyncio.Task | None = None @@ -77,7 +78,8 @@ class BridgeTransport: raise BridgeError(f"bridge 未发现 {self.name_prefix!r} 前缀设备") self.address = devices[0]["address"] await self._rpc("connect", 90.0 + self.timeout, - address=self.address, timeout=self.timeout) + address=self.address, timeout=self.timeout, + subscribe=self.subscribe) except Exception: await self._teardown() raise diff --git a/tools/bridge_server.py b/tools/bridge_server.py index 9fb550e..6829799 100644 --- a/tools/bridge_server.py +++ b/tools/bridge_server.py @@ -27,7 +27,7 @@ import platform import sys PROTOCOL_VERSION = 1 -SERVER_BUILD = 7 # 2026-07-29: OTA 分阶段事件(mem_dev/gpio_map/block_sent/status)+写超时,定位卡点 +SERVER_BUILD = 8 # 2026-07-29: start/stop_notify 10s 超时 + connect subscribe 选项(OTA 净连接) EPD_SERVICE_UUID = "13187b10-eba9-a3ba-044e-83d3217d9a38" EPD_CHAR_UUID = "4b646063-6264-f3a7-8941-e65356ea82fe" @@ -86,7 +86,7 @@ class BleSession: out.append({"name": name, "address": dev.address, "rssi": adv.rssi}) return {"devices": out} - async def connect(self, address: str, timeout: float = 15.0): + async def connect(self, address: str, timeout: float = 15.0, subscribe: bool = True): from bleak import BleakClient, BleakScanner await self.close() # Windows/WinRT 按地址直连常解析失败——先扫描拿到 BLEDevice(含地址类型/广播数据)再连 @@ -105,13 +105,16 @@ class BleSession: self.client = BleakClient(dev, timeout=timeout) await self.client.connect() # notify 全订阅:331f(主)+ 1f1f(部分固件)+ EPD(设备 ID 也可能经此应答) + # subscribe=False 用于 OTA 净连接(避免订阅抖动影响烧录链路) self.notify_char = [] - for uuid in (ALT_NOTIFY_UUID, RXTX_CHAR_UUID, EPD_CHAR_UUID): - try: - await self.client.start_notify(uuid, self._on_notify) - self.notify_char.append(uuid) - except Exception: - continue + if subscribe: + for uuid in (ALT_NOTIFY_UUID, RXTX_CHAR_UUID, EPD_CHAR_UUID): + try: + await asyncio.wait_for( + self.client.start_notify(uuid, self._on_notify), 10) + self.notify_char.append(uuid) + except Exception: + continue table = self._gatt_table() return {"address": address, "name": dev.name, "notify_char": self.notify_char, "gatt": table} @@ -167,7 +170,8 @@ class BleSession: await asyncio.wait_for( self.client.write_gatt_char(char, data, response=True), to) - await self.client.start_notify(SPOTA_SERV_STATUS_UUID, _on_status) + await asyncio.wait_for( + self.client.start_notify(SPOTA_SERV_STATUS_UUID, _on_status), 10) try: await w(SPOTA_MEM_DEV_UUID, MEM_DEV_SPI_FLASH) yield {"event": "ota_stage", "stage": "mem_dev_ok"} @@ -188,7 +192,8 @@ class BleSession: yield {"event": "ota_stage", "stage": "reboot_sent"} finally: try: - await self.client.stop_notify(SPOTA_SERV_STATUS_UUID) + await asyncio.wait_for( + self.client.stop_notify(SPOTA_SERV_STATUS_UUID), 5) except Exception: pass @@ -252,7 +257,8 @@ class BridgeServer: if op == "scan": return await self.ble.scan(float(req.get("timeout", 5.0))) if op == "connect": - return await self.ble.connect(req["address"], float(req.get("timeout", 15.0))) + return await self.ble.connect(req["address"], float(req.get("timeout", 15.0)), + bool(req.get("subscribe", True))) if op == "write": return await self.ble.write(req["char"], bytes.fromhex(req["data"]), bool(req.get("response", True)))