diff --git a/src/mcbluetooth_esp32/protocol.py b/src/mcbluetooth_esp32/protocol.py index 07a5b98..1e911d2 100644 --- a/src/mcbluetooth_esp32/protocol.py +++ b/src/mcbluetooth_esp32/protocol.py @@ -138,11 +138,15 @@ class Response: def from_json(cls, line: str) -> Response: """Parse a JSON line known to be a response.""" obj = json.loads(line) + raw_data = obj.get("data", {}) + # Firmware may return a bare string on some error paths — normalise to dict + if isinstance(raw_data, str): + raw_data = {"error": raw_data} return cls( type=MsgType(obj["type"]), id=obj["id"], status=Status(obj["status"]), - data=obj.get("data", {}), + data=raw_data if isinstance(raw_data, dict) else {}, ) diff --git a/src/mcbluetooth_esp32/serial_client.py b/src/mcbluetooth_esp32/serial_client.py index 343170f..230b784 100644 --- a/src/mcbluetooth_esp32/serial_client.py +++ b/src/mcbluetooth_esp32/serial_client.py @@ -263,7 +263,7 @@ class SerialClient: _client: SerialClient | None = None -async def get_client() -> SerialClient: +def get_client() -> SerialClient: """Get the singleton serial client. Raises: diff --git a/src/mcbluetooth_esp32/tools/ble.py b/src/mcbluetooth_esp32/tools/ble.py index a548eec..ea1ca1e 100644 --- a/src/mcbluetooth_esp32/tools/ble.py +++ b/src/mcbluetooth_esp32/tools/ble.py @@ -35,7 +35,7 @@ def register_tools(mcp: FastMCP) -> None: Status dict from the ESP32. """ try: - client = await get_client() + client = get_client() response = await client.send_command(CMD_BLE_ENABLE) return response.data except Exception as exc: @@ -52,7 +52,7 @@ def register_tools(mcp: FastMCP) -> None: Status dict from the ESP32. """ try: - client = await get_client() + client = get_client() response = await client.send_command(CMD_BLE_DISABLE) return response.data except Exception as exc: @@ -76,7 +76,7 @@ def register_tools(mcp: FastMCP) -> None: Status dict from the ESP32. """ try: - client = await get_client() + client = get_client() params: dict[str, Any] = { "enable": enable, "interval_ms": interval_ms, @@ -108,7 +108,7 @@ def register_tools(mcp: FastMCP) -> None: Status dict from the ESP32. """ try: - client = await get_client() + client = get_client() params: dict[str, Any] = {} if name is not None: params["name"] = name @@ -141,7 +141,7 @@ def register_tools(mcp: FastMCP) -> None: Dict containing the assigned service handle. """ try: - client = await get_client() + client = get_client() params: dict[str, Any] = { "uuid": uuid, "primary": primary, @@ -175,7 +175,7 @@ def register_tools(mcp: FastMCP) -> None: Dict containing the assigned characteristic handle. """ try: - client = await get_client() + client = get_client() params: dict[str, Any] = { "service_handle": service_handle, "uuid": uuid, @@ -207,7 +207,7 @@ def register_tools(mcp: FastMCP) -> None: Status dict from the ESP32. """ try: - client = await get_client() + client = get_client() params: dict[str, Any] = { "char_handle": char_handle, "value": value, @@ -231,7 +231,7 @@ def register_tools(mcp: FastMCP) -> None: Status dict from the ESP32. """ try: - client = await get_client() + client = get_client() params: dict[str, Any] = {"char_handle": char_handle} response = await client.send_command(CMD_GATT_NOTIFY, params) return response.data @@ -250,7 +250,7 @@ def register_tools(mcp: FastMCP) -> None: Status dict from the ESP32. """ try: - client = await get_client() + client = get_client() response = await client.send_command(CMD_GATT_CLEAR) return response.data except Exception as exc: diff --git a/src/mcbluetooth_esp32/tools/classic.py b/src/mcbluetooth_esp32/tools/classic.py index 11bd624..f85ed8a 100644 --- a/src/mcbluetooth_esp32/tools/classic.py +++ b/src/mcbluetooth_esp32/tools/classic.py @@ -30,7 +30,7 @@ def register_tools(mcp: FastMCP) -> None: Response data from the ESP32 including current BT state. """ try: - client = await get_client() + client = get_client() response = await client.send_command(CMD_CLASSIC_ENABLE) if response.status == Status.OK: return {"status": "ok", **response.data} @@ -49,7 +49,7 @@ def register_tools(mcp: FastMCP) -> None: Response data from the ESP32 confirming BT is disabled. """ try: - client = await get_client() + client = get_client() response = await client.send_command(CMD_CLASSIC_DISABLE) if response.status == Status.OK: return {"status": "ok", **response.data} @@ -75,7 +75,7 @@ def register_tools(mcp: FastMCP) -> None: Response data confirming the new discoverable state. """ try: - client = await get_client() + client = get_client() params = {"discoverable": discoverable, "timeout": timeout} response = await client.send_command(CMD_CLASSIC_SET_DISCOVERABLE, params) if response.status == Status.OK: @@ -110,7 +110,7 @@ def register_tools(mcp: FastMCP) -> None: Response data with the pairing result from the ESP32. """ try: - client = await get_client() + client = get_client() params: dict[str, Any] = {"address": address, "accept": accept} if passkey is not None: params["passkey"] = passkey diff --git a/src/mcbluetooth_esp32/tools/connection.py b/src/mcbluetooth_esp32/tools/connection.py index edf9a93..993e88b 100644 --- a/src/mcbluetooth_esp32/tools/connection.py +++ b/src/mcbluetooth_esp32/tools/connection.py @@ -32,9 +32,10 @@ def register_tools(mcp: FastMCP) -> None: Connection status including port, baudrate, and whether the device responded with a boot event. """ - try: - client = get_client() - except NotConnected: + from ..serial_client import get_client_or_none + + client = get_client_or_none() + if client is None: client = init_client(port=port, baudrate=baudrate) try: @@ -45,7 +46,7 @@ def register_tools(mcp: FastMCP) -> None: # Give the ESP32 a moment to send its boot event boot_received = False try: - event = await asyncio.wait_for(client.wait_event("boot"), timeout=2.0) + event = await client.event_queue.wait_for(event_name="boot", timeout=2.0) boot_received = event is not None except TimeoutError: pass