PetOS BLE Gateway
Allows controlling a PetOS BLE watch over Bluetooth Low Energy, providing tools to scan, connect, send actions, and show frames.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@PetOS BLE Gatewayplay the waving action on my pet"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
PetOS BLE Gateway
PetOS BLE Gateway is a local Node.js bridge for controlling PetOS ESP32 round watch devices over BLE.
It exposes three control surfaces over the same device session:
A browser dashboard for scanning, connecting, and sending pet commands.
An HTTP API for scripts and local tools.
A stdio MCP server for Codex, Claude, and other MCP clients.
Web UI / HTTP API / MCP tool
|
v
Node.js gateway
| BLE JSON + notify state
v
PetOS watch
|
+-- optional WiFi RLE upload endpointThe gateway keeps Node and watch state synchronized from BLE notifications: connection profile, WiFi IP/ports, active upload progress, active pet slot, current tab, and audio state.
Hardware/Firmware Assumptions
The current firmware family accepts JSON writes on:
Service UUID:
7f2a0001-4f6d-45f6-b805-2b0a7a0f9c01Write characteristic UUID:
7f2a0002-4f6d-45f6-b805-2b0a7a0f9c01Notify characteristic UUID:
7f2a0003-4f6d-45f6-b805-2b0a7a0f9c01
The gateway detects the connected device profile from BLE advertising:
PetOS-C3: 240x240 C3 round watch, 200px / 24-color pet package target, no audio/IMU controls.PetOS-S3: 360x360 SmartRing Plus, 360px / 48-color pet package target, audio, volume, brightness, IMU/auto-rotate, and pet scale controls.
Device capabilities are returned by /api/status and /api/capabilities. The Web UI, HTTP API, and MCP tools use those capabilities to disable or reject unsupported actions.
Related MCP server: blew BLE MCP
Requirements
macOS with Bluetooth enabled
Node.js 20+
A PetOS watch firmware advertising as
PetOS-C3orPetOS-S3
This project uses @abandonware/noble for BLE access. On macOS, the terminal or app running Node may need Bluetooth permission in System Settings.
Install
npm installRun The Web Gateway
npm startOpen:
http://127.0.0.1:8787The page lets you scan, connect, see the detected device profile, send named actions, show a fixed frame, switch pet slots, switch watch tabs, show or clear a pet speech bubble, update the watch text page, or write raw JSON.
It can also pack Codex pets into .idxrle packages, list saved packages with a preview frame, and upload a saved package to the watch over BLE or WiFi.
If a package does not fit the connected device profile, its upload button is disabled with the reason shown in the card.
CLI Smoke Tests
Scan:
npm run scan -- 7000Send an action:
npm run send -- '{"cmd":"pet.action","value":"review"}'Show a fixed frame:
npm run send -- '{"cmd":"pet.frame","value":12}'Upload a pet RLE package:
npm run upload -- /absolute/path/to/pet.idxrlePack a Codex pet into a watch-ready RLE package:
npm run pack -- cloud-strifePack and upload in one command:
npm run pack-upload -- cloud-strifeThe packer uses the standard Codex pet 8x9 sprite sheet layout, removes empty cells, and excludes run_right / run_left for the watch package.
When connected, pack defaults follow the device profile: C3 defaults to 200x200 / 24 colors, S3 defaults to 360x360 / 48 colors.
Packed packages are saved under packed/<pet>/. Running the same pet/settings again reuses the matching .idxrle; add --force to rebuild:
npm run pack -- cloud-strife --forceBLE JSON Protocol
Play an action:
{"cmd":"pet.action","value":"idle"}{"cmd":"pet.action","value":"review"}Show one fixed frame:
{"cmd":"pet.frame","value":12}Show or clear a pet speech bubble:
{"cmd":"pet.say","text":"Codex 收到了,开始思考"}{"cmd":"pet.bubble.clear"}Switch pet slot or UI tab:
{"cmd":"pet.slot","value":1}{"cmd":"ui.tab","value":"settings","animate":1}Show multiline text on the second watch page:
{"cmd":"watch.text","title":"Market","text":"#22c55e CPO +2.3%#\n#f97316 NVDA +1.1%#"}The watch firmware supports LVGL label recolor syntax for simple rich text:
#22c55e green text# #f97316 orange text#This is not HTML or Markdown. It supports multiline text and color spans, but not mixed font sizes, bold text, or embedded images.
Supported actions in the current firmware:
idlewavingjumpingfailedwaitingrunningreview
Upload a new pet package:
{"cmd":"rle.begin","size":561600}Then send binary BLE chunks:
"RLEC" + uint32_le(offset) + raw bytesFinish:
{"cmd":"rle.end"}The firmware writes to the selected pet slot (/pets/pet0.idxrle, /pets/pet1.idxrle, or /pets/pet2.idxrle). During upload the watch hides pet frames and shows a progress bar plus an abort button. After rle.end, the gateway waits for the watch notification:
{"event":"rle.complete","a":41,"b":778786}The web/CLI upload should only be treated as successful after this watch-side acknowledgement. If the upload is interrupted, upload the .idxrle again.
For larger S3 packages, the gateway can configure WiFi over BLE, receive the watch IP/ports from BLE notifications, and upload the .idxrle over the watch TCP pull protocol. BLE upload remains available as a fallback.
HTTP API
Status:
curl http://127.0.0.1:8787/api/statusCapabilities only:
curl http://127.0.0.1:8787/api/capabilitiesScan:
curl -X POST http://127.0.0.1:8787/api/scan \
-H 'content-type: application/json' \
-d '{"timeoutMs":7000}'Connect:
curl -X POST http://127.0.0.1:8787/api/connect \
-H 'content-type: application/json' \
-d '{}'Play an action:
curl -X POST http://127.0.0.1:8787/api/action/review \
-H 'content-type: application/json' \
-d '{}'Send raw JSON:
curl -X POST http://127.0.0.1:8787/api/send \
-H 'content-type: application/json' \
-d '{"cmd":"pet.frame","value":12}'Update the second watch page text:
curl -X POST http://127.0.0.1:8787/api/watch/text \
-H 'content-type: application/json' \
-d '{"title":"Market","text":"#22c55e CPO +2.3%#\n#f97316 NVDA +1.1%#"}'Show or clear the pet speech bubble:
curl -X POST http://127.0.0.1:8787/api/pet/say \
-H 'content-type: application/json' \
-d '{"text":"Codex 收到了,开始思考"}'
curl -X POST http://127.0.0.1:8787/api/pet/bubble/clear \
-H 'content-type: application/json' \
-d '{}'Switch pet slot or watch tab:
curl -X POST http://127.0.0.1:8787/api/pet/slot \
-H 'content-type: application/json' \
-d '{"value":1}'
curl -X POST http://127.0.0.1:8787/api/tab \
-H 'content-type: application/json' \
-d '{"value":"settings"}'Configure or disconnect watch WiFi:
curl -X POST http://127.0.0.1:8787/api/wifi/config \
-H 'content-type: application/json' \
-d '{"ssid":"YOUR_SSID","password":"YOUR_PASSWORD"}'
curl -X POST http://127.0.0.1:8787/api/wifi/disconnect \
-H 'content-type: application/json' \
-d '{}'Upload an .idxrle file:
curl -X POST 'http://127.0.0.1:8787/api/rle/upload?chunkSize=160&delayMs=10' \
-H 'content-type: application/octet-stream' \
--data-binary @/absolute/path/to/pet.idxrleList saved packages:
curl http://127.0.0.1:8787/api/rle/packagesUpload a saved package from packed/:
curl -X POST http://127.0.0.1:8787/api/rle/upload-file \
-H 'content-type: application/json' \
-d '{"file":"cloud-strife/cloud-strife_watch-no-lr_200_24.idxrle","chunkSize":160,"delayMs":10}'Use WiFi for the same saved package:
curl -X POST http://127.0.0.1:8787/api/rle/upload-file \
-H 'content-type: application/json' \
-d '{"file":"cloud-strife/cloud-strife_watch-no-lr_360_48.idxrle","transport":"wifi","slot":0}'Pack a Codex pet:
curl -X POST http://127.0.0.1:8787/api/rle/pack \
-H 'content-type: application/json' \
-d '{"name":"cloud-strife","colors":24,"size":200}'Pack and upload:
curl -X POST http://127.0.0.1:8787/api/rle/pack-upload \
-H 'content-type: application/json' \
-d '{"name":"cloud-strife","colors":24,"size":200,"chunkSize":160,"delayMs":10}'S3-only audio and display controls:
curl -X POST http://127.0.0.1:8787/api/audio/play/meizuo \
-H 'content-type: application/json' \
-d '{}'
curl -X POST http://127.0.0.1:8787/api/audio/volume \
-H 'content-type: application/json' \
-d '{"value":80}'
curl -X POST http://127.0.0.1:8787/api/display/brightness \
-H 'content-type: application/json' \
-d '{"value":80}'
curl -X POST http://127.0.0.1:8787/api/display/autorotate \
-H 'content-type: application/json' \
-d '{"enabled":true}'
curl -X POST http://127.0.0.1:8787/api/pet/scale \
-H 'content-type: application/json' \
-d '{"value":120}'MCP Server
Start the gateway first:
npm startThen run the MCP server:
npm run mcpFor an MCP client, use this command:
node /absolute/path/to/petos-ble-gateway/src/mcp-server.jsAvailable MCP tools:
petos_statuspetos_scanpetos_connectpetos_send_jsonpetos_play_actionpetos_select_petpetos_saypetos_clear_bubblepetos_open_tabpetos_show_framepetos_show_textpetos_upload_rlepetos_pack_rlepetos_pack_upload_petpetos_play_soundpetos_set_volumepetos_set_brightnesspetos_set_auto_rotatepetos_set_pet_scale
The MCP server calls the local gateway at http://127.0.0.1:8787 by default. Override with:
PETOS_GATEWAY_URL=http://127.0.0.1:8787 npm run mcpTroubleshooting
If the device does not appear in macOS Bluetooth settings, use this gateway or a BLE scanner such as nRF Connect/LightBlue. Ordinary BLE GATT devices often do not appear like keyboards or headphones.
If scanning hangs or returns no devices:
Make sure the watch firmware is advertising
PetOS-C3.Make sure the Node process has Bluetooth permission.
Try
npm run scan -- 10000.Restart the gateway after toggling Bluetooth permissions.
If action buttons appear to do nothing, check the log panel. A successful send looks like:
sent {"cmd":"pet.action","value":"review"}This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ilimei/petos-ble-gateway'
If you have feedback or need assistance with the MCP directory API, please join our Discord server