From bfdbe6ce3b7ff135c4f06c2361ba3a7614175633 Mon Sep 17 00:00:00 2001 From: Divoom Date: Mon, 18 May 2026 14:42:23 +0800 Subject: [PATCH 1/4] docs(mcp): transp/hier, NET_PIC uniqueness, analog pointer docs Document transp 100 for visible layers (avoid AI-default 0) and hier 0=auto/1=bottom/2=top. Add net-gallery disp uniqueness table and troubleshooting notes. Sync guide-key-points, skill refs, JSON schema, tool descriptions, and ocean dial asset script. Co-authored-by: Cursor --- README.md | 9 +- docs/README.md | 2 + docs/disp-usage.md | 41 ++++++ docs/reference/guide-key-points.en.md | 15 ++ docs/reference/guide-key-points.zh.md | 16 +++ docs/safety-and-troubleshooting.md | 18 ++- docs/tool-examples.md | 33 +++++ resources/guide-quick-reference.md | 15 ++ resources/skill-quick-reference.md | 19 +++ resources/watchface-config.schema.json | 12 +- scripts/gen_ocean_analog_dial_assets.py | 181 ++++++++++++++++++++++++ src/index.ts | 4 +- 12 files changed, 354 insertions(+), 11 deletions(-) create mode 100644 scripts/gen_ocean_analog_dial_assets.py diff --git a/README.md b/README.md index a7c4ee5..6a67153 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Your local clone path (e.g. `D:\divoom-watchface-visual-editor`) is machine-spec ## Implemented tools - `watchface_get_local` → `Device/GetLocalClockInfo` -- `watchface_patch_local` → `Device/PatchLocalClockInfo` +- `watchface_patch_local` → `Device/PatchLocalClockInfo` (default `/divoom_api`); optional `dialAssetsPath` switches to multipart `POST /patch_local_clock` (same dial/tar.gz rules as `watchface_create_local_clock`) - `watchface_get_fonts_local` → `Device/GetLocalFontList` - `watchface_get_store_market_list` → `Device/GetStoreClockMarketList` - `watchface_set_clock_select` → `Channel/SetClockSelectId` @@ -35,7 +35,7 @@ Your local clone path (e.g. `D:\divoom-watchface-visual-editor`) is machine-spec - `watchface_onoff_screen` → `Channel/OnOffScreen` (1=on, 0=off) - `watchface_replace_dial_bg_file` → `POST /replace_clock_dial_bg` - `watchface_upload_file` → `POST /upload` -- `watchface_create_local_clock` → `POST /create_local_clock` +- `watchface_create_local_clock` → `POST /create_local_clock` (multipart: single dial image **or** `tar.gz`; JSON `DialAssets`/`UseDialAssetBundle` selects mode, default auto-detect gzip) - `watchface_reset_local_then_cloud` → `Device/ResetLocalClockFromServer` - `watchface_raw_command` → generic `POST /divoom_api` - `watchface_protocol_quick_reference` → key protocol constraints for the model @@ -82,7 +82,8 @@ npm run release:check - `docs/README.md` — documentation index - `docs/quick-start.md` — minimal setup -- `docs/tool-examples.md` — tool usage examples +- `docs/tool-examples.md` — tool usage examples (includes §5b analog pointer layout) +- `docs/disp-usage.md` — choosing `disp` ids (pointer layout `131/132/233`; net-gallery uniqueness `13/125–130/173–175`) - `docs/html-visual-editor.md` — using the visual editor with MCP - `docs/safety-and-troubleshooting.md` — safety and FAQs - `docs/reference/` — condensed protocol rules (EN/ZH) @@ -145,7 +146,7 @@ Benefits: - Small MCP install suitable for all AI clients - Non-developers can use the visual UI to understand `ItemList`, then let the AI apply patches -- Clear split between WYSIWYG editing and automated MCP writes +- Clear split between WYSIWYG editing and automated MCP writes ## Alignment with upstream docs diff --git a/docs/README.md b/docs/README.md index e7c74ba..d362aa3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,6 +9,8 @@ 5. `reference/`:协议关键规则提炼(中英) 6. `examples/`:请求/响应示例与目录清单 +补充:**`disp-usage.md`** 中含「图像元素唯一性」及网络图库 `disp`(`13` / `125–130` / `173–175`)与固件常量对照表,建模 **`ItemList` 前应阅读**。 + ## 目标能力 - 修改本地表盘内容(`watchface_patch_local`) diff --git a/docs/disp-usage.md b/docs/disp-usage.md index 7e7a3ec..bbea3aa 100644 --- a/docs/disp-usage.md +++ b/docs/disp-usage.md @@ -48,6 +48,31 @@ - 其它时间显示 **下一个日出**(跨过日落自动滚到次日)。 面向用户时建议描述为「日出/日落时间(按当前时间自动切换)」。 +## 图像元素唯一性(网络图库系列) + +**在同一本地表盘的 `ItemList` 里,图像类元素每种 `disp` 原则上只应出现一行。** 若同一 `disp` +被配置了多行(或等价的多实例),固件侧往往 **以后面的条目为准**,前面的会被覆盖或失效, +表现为「只显示最后一次那张图 / 最后一次绑定」。 + +下列 **网络图库** 槽位(固件侧 `DIVOOM_CLOCK_DISP_SUPPORT_NET*_PIC`)尤其典型——AGENT 生成表盘时 +**各自至多一行**,不要重复添加: + +| `disp` | 编辑器符号 | 中文说明 | 固件常量(参考) | +|---:|---|---|---| +| 13 | `NET_PIC` | 网络图库 | `DIVOOM_CLOCK_DISP_SUPPORT_NET_PIC` | +| 173 | `NET2_PIC` | 网络图库2 | `DIVOOM_CLOCK_DISP_SUPPORT_NET2_PIC` | +| 174 | `NET3_PIC` | 网络图库3 | `DIVOOM_CLOCK_DISP_SUPPORT_NET3_PIC` | +| 175 | `NET4_PIC` | 网络图库4 | `DIVOOM_CLOCK_DISP_SUPPORT_NET4_PIC` | +| 125 | `NET5_PIC` | 网络图库5 | `DIVOOM_CLOCK_DISP_SUPPORT_NET5_PIC` | +| 126 | `NET6_PIC` | 网络图库6 | `DIVOOM_CLOCK_DISP_SUPPORT_NET6_PIC` | +| 127 | `NET7_PIC` | 网络图库7 | `DIVOOM_CLOCK_DISP_SUPPORT_NET7_PIC` | +| 128 | `NET8_PIC` | 网络图库8 | `DIVOOM_CLOCK_DISP_SUPPORT_NET8_PIC` | +| 129 | `NET9_PIC` | 网络图库9 | `DIVOOM_CLOCK_DISP_SUPPORT_NET9_PIC` | +| 130 | `NET10_PIC` | 网络图库10 | `DIVOOM_CLOCK_DISP_SUPPORT_NET10_PIC` | + +其它带本地 / 网络 **`image_addr`** 的槽位也建议遵循「**每种 `disp` 至多一行**」,除非编辑器或 +官方文档明确允许多实例。 + ## 与字体目录配合 ```text @@ -63,6 +88,22 @@ ItemList[i] is asset-driven (hints.likelyUsesRasterOrAssetLayer) 详见 `docs/font-usage.md`。 +## 模拟指针类 `disp`(131 / 132 / 233) + +**`HOUR_POINT_IMAGE`(131)、`MIN_POINT_IMAGE`(132)、`SECOND_POINT_IMAGE`(233)** +用于「_RESOURCE 指针图」驱动模拟时钟,而不是数字时间字符串。 + +- **`ItemList[i].image_addr`**(或 PATCH 时的 **`bundle_image`**)指向 tar 里的叶子;每张图 + 的尺寸必须等于该条的 **`w`×`h`**。 +- **旋转枢轴**在图层矩形 **`(x,y,w,h)`** 的几何中心一侧(设备按指针类型旋转);因此三根针 + 应共用同一 **`x,y,w,h`**,且素材以**正方形**最为稳妥。 +- 指针美术:**枢轴在画布中心**,针尖指向 **12 点(屏幕上方)**;禁止把指针画在 **`800×1280`** + 全屏图的任意偏移位置来代替「方形图层」——会出现三根针围绕不同中心旋转的现象。 +- **`transp`**:**需要能看见的元素必须显式 `transp: 100`**。模型生成 JSON 时常默认为 **`0`**,设备上会 **整块透明看不见**(误以为坐标错了)。 +- **`hier`**(叠层,仅 **`0` / `1` / `2`**):**`0`** = 自动;**`1`** = **底层**;**`2`** = **顶层**。秒针通常 **`2`**,时针常 **`1`**,分针可用 **`0`**,按真机微调。 +- 参考排版统计:`watchface_disp_catalog` 中上述 `disp` 的 **`typography.box.p50`**。 +- 实操示例:`docs/tool-examples.md` 第 **5b** 节。 + ## `typography` 排版统计(v0.1.4+) 同步编辑器 bundle 后,`divoom://disp/catalog` 里每个 `disp` 可能多一段 **`typography`**: diff --git a/docs/reference/guide-key-points.en.md b/docs/reference/guide-key-points.en.md index f67b10f..a45647d 100644 --- a/docs/reference/guide-key-points.en.md +++ b/docs/reference/guide-key-points.en.md @@ -123,6 +123,21 @@ and is conventionally the same value as the matching `item_id` (e.g. `"item_1"`, `"time_main"`). Empty strings cause `ItemList[i]: missing or empty string "item_id"`. +### `transp` and `hier` (visibility / z-order — LLMs often fail here) + +- **`transp`:** Treat as **on-screen visibility / opacity**. Anything that must **appear** needs **`transp: 100`**. **Many AI-generated payloads omit the field or set `0`, which makes the layer invisible on-device** — looks like broken coordinates when it is only transparency. +- **`hier`:** Only three levels — **`0`** automatic ordering, **`1`** **bottom** (painted first), **`2`** **top** (painted last, above others). **There is no `hier: 3`.** Typical analog clock: hour **`1`**, minute **`0`**, second **`2`**; tune on hardware. + +### Image / net-gallery slot uniqueness + +In a single dial **`ItemList`**, keep **at most one row per image-backed `disp`**. +Duplicate **`disp`** rows tend to behave as **last-write-wins**, overwriting earlier bindings. + +The **network gallery** slots (`DIVOOM_CLOCK_DISP_SUPPORT_NET_PIC`, …) are the usual examples: +**`disp` 13** (`NET_PIC`), **`173–175`** (`NET2_PIC`–`NET4_PIC`), **`125–130`** +(`NET5_PIC`–`NET10_PIC`). Full table: **`docs/disp-usage.md`** (Chinese section +「图像元素唯一性(网络图库系列)」). + ### `alig` values (firmware-native) - `3` = center diff --git a/docs/reference/guide-key-points.zh.md b/docs/reference/guide-key-points.zh.md index 892e8dc..16071a3 100644 --- a/docs/reference/guide-key-points.zh.md +++ b/docs/reference/guide-key-points.zh.md @@ -115,6 +115,22 @@ weather.gif (如 `"item_1"`, `"time_main"`)。空串会触发 `ItemList[i]: missing or empty string "item_id"`。 +### `transp` 与 `hier`(透明度 / 叠层,AI 易错) + +- **`transp`**:控制图层**是否看得见**,可理解为不透明度。**凡是需要显示的元素都必须显式写 `transp: 100`**。 + 许多自动生成会把缺失字段或未用意落成 **`0`**,在真机上表现为 **整块透明、指针/贴图「不见了」**(易被误判为坐标错误)。 +- **`hier`**:**仅有三档** — **`0`**:自动叠层顺序;**`1`**:**底层**(先绘制);**`2`**:**顶层**(后绘制,压住下层)。 + **不存在 `3`、`4`…**。模拟指针常见组合:时针 **`hier: 1`**、秒针 **`hier: 2`**、分针 **`hier: 0`**(自动),以真机为准微调。 + +### 图像槽位与网络图库唯一性 + +同一表盘的 **`ItemList` 中,每种图像相关 `disp` 原则上只保留一行**。若同一 **`disp`** +出现多行,固件侧常见行为是 **以后者为准**,前面的绑定或显示会被覆盖。 + +**网络图库**类槽位(固件 `DIVOOM_CLOCK_DISP_SUPPORT_NET_PIC` 等)尤其典型:`disp` +**13**(`NET_PIC`)、**173–175**(`NET2_PIC`–`NET4_PIC`)、**125–130**(`NET5_PIC`–`NET10_PIC`)。 +完整对照表见 **`docs/disp-usage.md`**「图像元素唯一性(网络图库系列)」。 + ### `alig` 取值(与固件一致) - `3` = 居中 diff --git a/docs/safety-and-troubleshooting.md b/docs/safety-and-troubleshooting.md index fce1d9a..7b9ca8c 100644 --- a/docs/safety-and-troubleshooting.md +++ b/docs/safety-and-troubleshooting.md @@ -72,7 +72,19 @@ tar.gz 内部元素槽位(`ItemList[i].image_addr` / - 检查请求是否包含 `ReturnCode: 0` - 对照工具参数字段名(驼峰命名) -### 3) patch 不生效 +### 3) 重复的图像槽位 / 网络图库 `disp` + +现象: + +- 配置了多张「网络图库」或同一图像类 `disp` 的多行,只看到最后一处生效,前面的像被覆盖。 + +说明: + +- 同一表盘的 **`ItemList` 里,每种图像相关 `disp` 原则上只保留一行**;重复时 **后面的条目会覆盖前面的**。 +- 网络图库系列参见 `docs/disp-usage.md` **「图像元素唯一性(网络图库系列)」**(`disp` + **13 / 125–130 / 173–175** 等)。 + +### 4) patch 不生效 排查: @@ -81,7 +93,7 @@ tar.gz 内部元素槽位(`ItemList[i].image_addr` / - 是否被后续流程覆盖(例如切换表盘后再回读) - 是否命中了空 `ItemList` 场景(先 `watchface_get_local` 检查) -### 4) 图片相关失败 +### 5) 图片相关失败 通用排查: @@ -107,7 +119,7 @@ tar.gz 内部元素槽位(`ItemList[i].image_addr` / `DialAssets` 字段与第二段实际形态不一致,要么改 `DialAssets:bundle` 要么发单图。 -### 5) 创建表盘失败 +### 6) 创建表盘失败 排查: diff --git a/docs/tool-examples.md b/docs/tool-examples.md index 9d9c289..c5ba1a8 100644 --- a/docs/tool-examples.md +++ b/docs/tool-examples.md @@ -100,6 +100,9 @@ - `font` 建议先通过 `watchface_get_fonts_local` 读取可用 id - `item_id` **必须为非空字符串**(固件 `NEED_STR("item_id")`),`ItemIdList` 内每项也必须非空,通常等同于 `item_id` +- **图像槽位**:同一表盘中 **每种图像相关 `disp` 只放一行**;若重复,**后面的条目会覆盖前面的**。 + **网络图库**(`disp` **13**、**125–130**、**173–175** 等,`DIVOOM_CLOCK_DISP_SUPPORT_NET*_PIC`) + 尤其典型——详见 `docs/disp-usage.md`「图像元素唯一性(网络图库系列)」。 --- @@ -167,6 +170,36 @@ weather_pack.bin - 不要把元素图作为独立的多段 multipart 上传——固件每次请求只接受一个文件。 - `image_addr` 跳过本地叶子名时(比如已经是 `http(s)` URL),那张图无需进 tar。 - 不要在 tar 内建子目录、不要使用相对路径或绝对路径,叶子名 ≤ 95 字节。 +- **同一 `disp` 的图像槽位不要重复多行**(网络图库见 `docs/disp-usage.md`),否则后者覆盖前者。 + +--- + +## 5b) 模拟指针(`HOUR_POINT_IMAGE` / `MIN_POINT_IMAGE` / `SECOND_POINT_IMAGE`) + +对应 `disp`:**131**(时针)、**132**(分针)、**233**(秒针)。这类槽位走 **`image_addr` + +`clock_bg.tar.gz` 元素图**,但布局和「整屏贴图」完全不同: + +1. **图层几何**:三根指针应使用**完全相同**的 **`x` / `y` / `w` / `h`**,把方块中心对准表盘 + 物理轴心(太阳 / 圆心)。固件在该矩形中心附近旋转指针;常见取值是 **正方形** + `w = h`,约在 **180~600** 区间(编辑器模板的中位数多在 **200~310**,见 + `watchface_disp_catalog` 里对应 `typography.box`)。 +2. **素材像素**:每张指针 PNG(或 WebP/JPEG)的分辨率必须与 **`w`×`h` 一致**。指针画在 + **图像正中心**为枢轴,默认朝向 **12 点方向(竖直向上)**;不要用 **`800×1280`** + 的全屏画布画一根针——否则旋转中心会偏离视觉圆心,三根针的「轴帽」会散落在画面各处。 +3. **叠放顺序 `hier`(仅三档)**:**`0`** = 自动排序;**`1`** = **底层**(先画);**`2`** = + **顶层**(后画,压住下层)。没有 `3`、`4`… 等扩展档位。模拟表盘常见写法:时针 **`hier: 1`**(底层)、 + 秒针 **`hier: 2`**(顶层盖住时针分针)、分针 **`hier: 0`**(交给自动顺序);最终以真机为准微调。 +4. **透明度 `transp`(务必写对)**:表示「显示出来」的强度,可理解为 **不透明度**:正常能看见的元素请写 **`100`**。 + **大量 AI / 模板会把未给出的字段落成 `0`——在设备上会变成完全看不见(指针、贴图「失踪」)**。生成 `ItemList` + 时只要元素需要显示,就应 **显式设置 `transp: 100`**,不要省略或填 **`0`**(除非刻意隐藏)。 +5. **`alig`**:多数上架模板用 **`4`(左)**;表盘轴心在画布正中时,也可用 **`3`(居中)** + 配合 **`x = cx − w/2`,`y = cy − h/2`**。以真机为准微调。 + +修正错误布局时:准备好新的 `clock_bg.tar.gz`(可含更新后的 `clock_bg.jpg`),用 +`watchface_patch_local` 传 **`dialAssetsPath`**,并在 **`ItemPatchList[].patch`** 里带上 +**`bundle_image`**(以及 **`image_addr`**,与叶子文件名一致)。 + +仓库内可参考脚本:`scripts/gen_ocean_analog_dial_assets.py`(生成海洋底图 + 方形指针打包示例)。 --- diff --git a/resources/guide-quick-reference.md b/resources/guide-quick-reference.md index aa430b8..0f9c5e1 100644 --- a/resources/guide-quick-reference.md +++ b/resources/guide-quick-reference.md @@ -167,6 +167,21 @@ Validation lives in `wf_unpack_disp_items` (firmware Other recognized keys: `sep`, `image_id`, `image_addr`, `animation`, `angle`, `hier`, `transp`, and (bundle only) `bundle_image`. +**Analog pointer slots (`disp` 131 / 132 / 233 — `*_POINT_IMAGE`):** use one **square** +layer (`w == h`) shared by hour/minute/second; each `image_addr` bitmap must match **`w`×`h`** with the +hand pivot at the **image center** and the hand painted pointing to **12 o'clock**. Do **not** use a +full **`800×1280`** hand sprite — rotation will not share the dial center. + +**`transp`:** Use **`100`** for every layer that must be visible. **AI generators often emit `0`, which makes the layer invisible on-device** (looks like bad coordinates). + +**`hier`:** Only **`0`** (auto), **`1`** (bottom / drawn first), **`2`** (top / drawn last). **No `hier: 3+`.** Typical analog clock: hour **`1`**, minute **`0`**, second **`2`**. + +**Image / net-gallery slots (`NET_PIC` family):** In one dial, **do not duplicate the same +`disp`** for asset-backed image slots — firmware tends to keep **only the last row**, overwriting +earlier ones. Especially **`disp` 13 (`NET_PIC`)**, **`125–130` (`NET5_PIC` … `NET10_PIC`)**, and +**`173–175` (`NET2_PIC` … `NET4_PIC`)** (`DIVOOM_CLOCK_DISP_SUPPORT_NET*_PIC`): **at most one row each**. +See `docs/disp-usage.md` (Chinese) for the full table. + `ItemIdList` is a parallel array of strings; each entry **must be non-empty** and is conventionally the same string as the matching `item_id` (e.g. `"item_1"`, `"time_main"`). Empty strings on either side cause diff --git a/resources/skill-quick-reference.md b/resources/skill-quick-reference.md index d249c7a..037cee2 100644 --- a/resources/skill-quick-reference.md +++ b/resources/skill-quick-reference.md @@ -117,6 +117,25 @@ In bundle mode, leaf basenames inside the tar must exactly match the only) `bundle_image`. Any other key is silently ignored. Skip `item_id` unless renaming a slot. +## `transp` & `hier` (AI generators often get this wrong) + +- **`transp`:** Treat as **opacity for visibility**. For anything that should **show on screen**, set **`transp: 100`**. **Many LLMs default missing/`transp: 0`, which renders fully invisible on device** — users think layout is broken when it is only transparency. +- **`hier`:** Only **`0` = auto**, **`1` = bottom layer**, **`2` = top layer**. There is **no `hier: 3`**. Stack analog hands e.g. hour **`1`**, minute **`0`**, second **`2`**. + +## Analog pointer slots (`disp` 131 / 132 / 233) + +Square **`w = h`** layer shared by hour/minute/second; bitmap **`w`×`h`** with pivot at **center**, +hand toward **12 o'clock**. Avoid **`800×1280`** hand sprites. Use **`transp: 100`**; use **`hier`** +**`0`/`1`/`2`** so the second hand can paint on top (**`2`**). Details: `docs/tool-examples.md` §5b. + +## Image elements / net-gallery uniqueness + +Across a single dial **`ItemList`**, treat **image-backed `disp` types as at most one row per +`disp`**. If duplicated, **later rows override earlier ones**. This is especially strict for the +**network gallery** slots: **`disp` 13** (`NET_PIC`), **`173–175`** (`NET2_PIC`–`NET4_PIC`), +**`125–130`** (`NET5_PIC`–`NET10_PIC`) — firmware symbols `DIVOOM_CLOCK_DISP_SUPPORT_NET*_PIC`. +Full table: **`docs/disp-usage.md`** (section “图像元素唯一性(网络图库系列)”). + ## Sunrise / sunset slot (`disp = 204`) The current firmware shows a single value rather than toggling: diff --git a/resources/watchface-config.schema.json b/resources/watchface-config.schema.json index 1927021..7262255 100644 --- a/resources/watchface-config.schema.json +++ b/resources/watchface-config.schema.json @@ -51,8 +51,16 @@ }, "sep": { "type": "number", "description": "Character spacing where applicable." }, "angle": { "type": "number" }, - "hier": { "type": "integer", "description": "Layer/stack ordering hint." }, - "transp": { "type": "number", "minimum": 0, "maximum": 100 }, + "hier": { + "type": "integer", + "description": "Z-order: 0 = automatic; 1 = bottom layer (drawn first); 2 = top layer (drawn last). Values above 2 are not meaningful on typical firmware — use only 0/1/2." + }, + "transp": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "Visibility / opacity: use 100 for any element that must be visible. AI-generated configs often use 0 (or omit), which makes layers invisible on-device." + }, "animation": { "type": "number" }, "image_id": { "type": "number" }, "image_addr": { "type": "string" }, diff --git a/scripts/gen_ocean_analog_dial_assets.py b/scripts/gen_ocean_analog_dial_assets.py new file mode 100644 index 0000000..254416c --- /dev/null +++ b/scripts/gen_ocean_analog_dial_assets.py @@ -0,0 +1,181 @@ +""" +Generate ocean-themed dial backdrop + square PNG hands for Divoom POINT_IMAGE slots. + +Firmware rotates analog hands around the center of the layer box (x,y,w,h). Each +hand image must be exactly w×h with the pivot at the image center and the hand +drawn pointing toward 12 o'clock (up, -Y). Do not use full-screen 800×1280 hand +sprites — see docs/tool-examples.md. +""" + +from __future__ import annotations + +import math +import tarfile +from pathlib import Path + +from PIL import Image, ImageDraw, ImageFilter + + +WIDTH = 800 +HEIGHT = 1280 +# Visual dial center (sun); ring ticks align here — NOT geometric canvas center. +CX = 400 +CY = int(HEIGHT * 0.36) +HAND_BOX = 560 # w=h; covers ring radius ~280 + + +def _draw_ocean_background() -> Image.Image: + img = Image.new("RGB", (WIDTH, HEIGHT), "#0B2A53") + px = img.load() + + for y in range(HEIGHT): + if y < HEIGHT * 0.55: + t = y / (HEIGHT * 0.55) + r = int(8 + (85 - 8) * t) + g = int(45 + (156 - 45) * t) + b = int(95 + (203 - 95) * t) + else: + t = (y - HEIGHT * 0.55) / (HEIGHT * 0.45) + r = int(15 + (5 - 15) * t) + g = int(84 + (45 - 84) * t) + b = int(120 + (80 - 120) * t) + for x in range(WIDTH): + px[x, y] = (r, g, b) + + sun = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0)) + sun_draw = ImageDraw.Draw(sun) + sun_center = (CX, CY) + for radius, alpha in [(150, 60), (110, 100), (78, 180)]: + sun_draw.ellipse( + ( + sun_center[0] - radius, + sun_center[1] - radius, + sun_center[0] + radius, + sun_center[1] + radius, + ), + fill=(255, 210, 125, alpha), + ) + img = Image.alpha_composite(img.convert("RGBA"), sun).convert("RGB") + + reflection = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0)) + refl_draw = ImageDraw.Draw(reflection) + horizon_y = CY + 95 + for i in range(22): + y = horizon_y + i * 22 + half_w = max(18, 170 - i * 6) + alpha = max(16, 120 - i * 4) + refl_draw.ellipse( + (CX - half_w, y - 6, CX + half_w, y + 6), + fill=(255, 228, 160, alpha), + ) + reflection = reflection.filter(ImageFilter.GaussianBlur(1.5)) + img = Image.alpha_composite(img.convert("RGBA"), reflection).convert("RGB") + draw = ImageDraw.Draw(img) + + for i in range(12): + y = int(HEIGHT * 0.58 + i * 52) + color = (140 - i * 6, 210 - i * 8, 230 - i * 7) + draw.arc((60, y - 20, WIDTH - 60, y + 28), start=0, end=180, fill=color, width=2) + draw.arc((110, y + 8, WIDTH - 110, y + 42), start=180, end=360, fill=color, width=2) + + # Hour ticks around dial center (CX, CY). + ring = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0)) + ring_draw = ImageDraw.Draw(ring) + outer_r = 280 + inner_r = 245 + for h in range(12): + ang = math.radians(h * 30 - 90) + x1 = CX + int(inner_r * math.cos(ang)) + y1 = CY + int(inner_r * math.sin(ang)) + x2 = CX + int(outer_r * math.cos(ang)) + y2 = CY + int(outer_r * math.sin(ang)) + ring_draw.line((x1, y1, x2, y2), fill=(230, 246, 255, 200), width=4) + + faint = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0)) + fd = ImageDraw.Draw(faint) + fd.ellipse( + (CX - outer_r - 8, CY - outer_r - 8, CX + outer_r + 8, CY + outer_r + 8), + outline=(255, 255, 255, 55), + width=3, + ) + ring = Image.alpha_composite(ring, faint) + img = Image.alpha_composite(img.convert("RGBA"), ring).convert("RGB") + draw = ImageDraw.Draw(img) + + bird_color = (22, 40, 58) + draw.arc((620, 100, 700, 150), 210, 360, fill=bird_color, width=5) + draw.arc((690, 95, 780, 150), 180, 330, fill=bird_color, width=5) + draw.arc((655, 122, 730, 168), 200, 350, fill=(35, 62, 82), width=3) + + return img + + +def _hand_square( + size: int, + length: int, + width: int, + tail: int, + rgba: tuple[int, int, int, int], +) -> Image.Image: + """Square PNG; pivot at center; hand points up (12 o'clock).""" + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + cx = cy = size // 2 + y_tip = cy - length + y_tail = cy + tail + + draw.line((cx, y_tail, cx, y_tip), fill=rgba, width=width) + head_w = max(10, width + 8) + draw.polygon( + [ + (cx, y_tip - 16), + (cx - head_w, y_tip + 12), + (cx + head_w, y_tip + 12), + ], + fill=rgba, + ) + cap_r = max(8, width + 2) + draw.ellipse((cx - cap_r, cy - cap_r, cx + cap_r, cy + cap_r), fill=rgba) + return img + + +def build(out_dir: Path) -> dict[str, Path]: + out_dir.mkdir(parents=True, exist_ok=True) + bg_path = out_dir / "clock_bg.jpg" + hour_path = out_dir / "hour_hand.png" + min_path = out_dir / "minute_hand.png" + sec_path = out_dir / "second_hand.png" + bundle_path = out_dir / "clock_bg.tar.gz" + + _draw_ocean_background().save(bg_path, "JPEG", quality=90, optimize=True) + + s = HAND_BOX + _hand_square(s, length=118, width=18, tail=38, rgba=(245, 250, 255, 245)).save( + hour_path, "PNG" + ) + _hand_square(s, length=168, width=12, tail=42, rgba=(236, 246, 255, 245)).save( + min_path, "PNG" + ) + _hand_square(s, length=188, width=5, tail=55, rgba=(255, 99, 99, 250)).save(sec_path, "PNG") + + with tarfile.open(bundle_path, "w:gz", format=tarfile.USTAR_FORMAT) as tf: + tf.add(bg_path, arcname="clock_bg.jpg") + tf.add(hour_path, arcname="hour_hand.png") + tf.add(min_path, arcname="minute_hand.png") + tf.add(sec_path, arcname="second_hand.png") + + return { + "clock_bg": bg_path, + "hour_hand": hour_path, + "minute_hand": min_path, + "second_hand": sec_path, + "bundle": bundle_path, + "hand_xywh": (CX - s // 2, CY - s // 2, s, s), + } + + +if __name__ == "__main__": + od = Path(__file__).resolve().parents[1] / "resources" / "generated" / "ocean_analog_dial" + meta = build(od) + for k, v in meta.items(): + print(f"{k}: {v}") diff --git a/src/index.ts b/src/index.ts index 4510a5b..d23f026 100644 --- a/src/index.ts +++ b/src/index.ts @@ -141,7 +141,7 @@ const tools: Tool[] = [ { name: "watchface_patch_local", description: - "Patch local dial via Device/PatchLocalClockInfo with precheck. Defaults to POST /divoom_api (JSON only) for pure metadata edits. Prefer ItemPatchList (per-index field diff) — DO NOT include item_id inside patch.* unless the user explicitly asks to rename a slot, since the firmware will overwrite the device-side item_id and break menu/config bindings. When dialAssetsPath is set, switches to multipart POST /patch_local_clock: first JSON part (Device/PatchLocalClockInfo, optional DialAssets), second part single JPEG/WebP dial backdrop or clock_bg.tar.gz bundle. Element slots inside the tarball must be JPEG, WebP, or PNG (validated by firmware wf_validate_bundle_slot_image_file). Use ItemPatchList[].patch.bundle_image= to bind a tar leaf to that slot's img_addr; supplying ItemList alone is a full-table replace and should be avoided unless the row count actually changes. Multipart framing (Content-Length / boundary / manual construction): see watchface_upload_file description and resources/skill-quick-reference.md.", + "Patch local dial via Device/PatchLocalClockInfo with precheck. Defaults to POST /divoom_api (JSON only) for pure metadata edits. Prefer ItemPatchList (per-index field diff) — DO NOT include item_id inside patch.* unless the user explicitly asks to rename a slot, since the firmware will overwrite the device-side item_id and break menu/config bindings. When dialAssetsPath is set, switches to multipart POST /patch_local_clock: first JSON part (Device/PatchLocalClockInfo, optional DialAssets), second part single JPEG/WebP dial backdrop or clock_bg.tar.gz bundle. Element slots inside the tarball must be JPEG, WebP, or PNG (validated by firmware wf_validate_bundle_slot_image_file). Use ItemPatchList[].patch.bundle_image= to bind a tar leaf to that slot's img_addr; supplying ItemList alone is a full-table replace and should be avoided unless the row count actually changes. For analog pointer fixes (disp 131/132/233): patch shared square x/y/w/h, bundle_image leaves matching w×h PNGs; set transp 100 for visibility (AI often uses 0 → invisible); hier only 0=auto, 1=bottom, 2=top — see docs/tool-examples.md §5b. Avoid duplicate rows for the same image-backed disp (NET_PIC family: 13, 125–130, 173–175); later rows overwrite earlier ones — docs/disp-usage.md. Multipart framing (Content-Length / boundary / manual construction): see watchface_upload_file description and resources/skill-quick-reference.md.", inputSchema: { type: "object", properties: { @@ -321,7 +321,7 @@ const tools: Tool[] = [ { name: "watchface_create_local_clock", description: - "POST /create_local_clock (multipart) — Device/CreateLocalClock. metadata.DialAssets accepts 'auto' (default; sniffs gzip magic on the file part), 'image' (single JPEG/WebP backdrop), or 'bundle' (clock_bg.tar.gz). Legacy UseDialAssetBundle (0=image, non-0=bundle) is honored when DialAssets is omitted. Backdrop is JPEG/WebP only; element slots inside the tarball accept JPEG/WebP/PNG (firmware wf_validate_bundle_slot_image_file). Each ItemList[i] needs disp/font/x/y/w/h/size/alig numbers and color_1/color_2/item_id non-empty strings; ItemIdList must be a parallel non-empty string array. alig: 3=center, 4=left, 5=right. Multipart framing (Content-Length per part): see watchface_upload_file description.", + "POST /create_local_clock (multipart) — Device/CreateLocalClock. metadata.DialAssets accepts 'auto' (default; sniffs gzip magic on the file part), 'image' (single JPEG/WebP backdrop), or 'bundle' (clock_bg.tar.gz). Legacy UseDialAssetBundle (0=image, non-0=bundle) is honored when DialAssets is omitted. Backdrop is JPEG/WebP only; element slots inside the tarball accept JPEG/WebP/PNG (firmware wf_validate_bundle_slot_image_file). Each ItemList[i] needs disp/font/x/y/w/h/size/alig numbers and color_1/color_2/item_id non-empty strings; ItemIdList must be a parallel non-empty string array. alig: 3=center, 4=left, 5=right. For analog pointer disps (131 HOUR_POINT_IMAGE, 132 MIN_POINT_IMAGE, 233 SECOND_POINT_IMAGE): matching square w=h, image pixels = w×h, pivot center, hand toward 12 o'clock — never full-screen 800×1280 hand layers. transp: MUST use 100 for visible layers — LLMs often emit 0 → invisible on device. hier: ONLY 0=auto, 1=bottom, 2=top (no tier 3+). See docs/tool-examples.md §5b. Uniqueness: do not duplicate the same image-backed disp in one dial — later rows overwrite earlier ones; especially NET_PIC family (disp 13, 125–130, 173–175, DIVOOM_CLOCK_DISP_SUPPORT_NET*_PIC); see docs/disp-usage.md. Multipart framing (Content-Length per part): see watchface_upload_file description.", inputSchema: { type: "object", properties: { From 8874b644ff9f9c56917a8fe6fe9fc2ea028a7e00 Mon Sep 17 00:00:00 2001 From: Divoom Date: Mon, 18 May 2026 14:57:12 +0800 Subject: [PATCH 2/4] docs(mcp): pointer macros 131/132/233, square bbox, center rotation Document DIVOOM_CLOCK_DISP_SUPPORT_*_POINT_IMAGE names; require shared square w=h and ClockId 60012-style cfg. Co-authored-by: Cursor --- docs/disp-usage.md | 35 ++++++++++++++++--------- docs/reference/guide-key-points.en.md | 9 +++++++ docs/reference/guide-key-points.zh.md | 7 +++++ docs/tool-examples.md | 26 +++++++++++------- resources/guide-quick-reference.md | 7 +++-- resources/skill-quick-reference.md | 6 ++--- scripts/gen_ocean_analog_dial_assets.py | 15 +++++++---- src/index.ts | 4 +-- 8 files changed, 71 insertions(+), 38 deletions(-) diff --git a/docs/disp-usage.md b/docs/disp-usage.md index bbea3aa..f11900d 100644 --- a/docs/disp-usage.md +++ b/docs/disp-usage.md @@ -90,19 +90,28 @@ ItemList[i] is asset-driven (hints.likelyUsesRasterOrAssetLayer) ## 模拟指针类 `disp`(131 / 132 / 233) -**`HOUR_POINT_IMAGE`(131)、`MIN_POINT_IMAGE`(132)、`SECOND_POINT_IMAGE`(233)** -用于「_RESOURCE 指针图」驱动模拟时钟,而不是数字时间字符串。 - -- **`ItemList[i].image_addr`**(或 PATCH 时的 **`bundle_image`**)指向 tar 里的叶子;每张图 - 的尺寸必须等于该条的 **`w`×`h`**。 -- **旋转枢轴**在图层矩形 **`(x,y,w,h)`** 的几何中心一侧(设备按指针类型旋转);因此三根针 - 应共用同一 **`x,y,w,h`**,且素材以**正方形**最为稳妥。 -- 指针美术:**枢轴在画布中心**,针尖指向 **12 点(屏幕上方)**;禁止把指针画在 **`800×1280`** - 全屏图的任意偏移位置来代替「方形图层」——会出现三根针围绕不同中心旋转的现象。 -- **`transp`**:**需要能看见的元素必须显式 `transp: 100`**。模型生成 JSON 时常默认为 **`0`**,设备上会 **整块透明看不见**(误以为坐标错了)。 -- **`hier`**(叠层,仅 **`0` / `1` / `2`**):**`0`** = 自动;**`1`** = **底层**;**`2`** = **顶层**。秒针通常 **`2`**,时针常 **`1`**,分针可用 **`0`**,按真机微调。 -- 参考排版统计:`watchface_disp_catalog` 中上述 `disp` 的 **`typography.box.p50`**。 -- 实操示例:`docs/tool-examples.md` 第 **5b** 节。 +固件常量名(与 C 枚举一致,便于对照工程代码): + +| `disp` | 编辑器符号 | 固件常量 | +|---:|---|---| +| 131 | `HOUR_POINT_IMAGE`(时针指针) | `DIVOOM_CLOCK_DISP_SUPPORT_HOUR_POINT_IMAGE` | +| 132 | `MIN_POINT_IMAGE`(分针指针) | `DIVOOM_CLOCK_DISP_SUPPORT_MIN_POINT_IMAGE` | +| 233 | `SECOND_POINT_IMAGE`(秒针指针) | `DIVOOM_CLOCK_DISP_SUPPORT_SECOND_POINT_IMAGE` | + +**硬性约定(不按此做容易错位、「三根针各绕各的轴」):** + +1. **三根指针必须使用同一组图层矩形**:**完全相同**的 **`x` / `y` / `w` / `h`**。 +2. **必须使用正方形**:**`w = h`**。不要用三根互不相同的细长矩形框(例如时针 `60×240`、分针 `40×280`)——即便盒子中心碰巧对齐,仍不如正方形稳妥且易与素材不匹配。 +3. **中心旋转**:每张指针图的像素尺寸为 **`w`×`w`**(与正方形边长相等);指针枢轴在**正方形画布的正中央**,针默认指向 **12 点(屏幕上方)**,固件绕该**图层几何中心**旋转。 +4. **禁止**用 **`800×1280`** 整屏图只在某一角落画一根针来代替上述正方形图层——旋转锚点会错。 +5. **参考配置**:设备导出表 **`ClockId 60012`**(示例文件名如 **`clock60012.cfg`**)中,三根指针条目 **`disp` 131 / 132 / 233** 共用同一正方形 `x,y,w,h`,即为推荐写法。 + +**其它:** + +- **`ItemList[i].image_addr`**(或 PATCH **`bundle_image`**)指向 tar 叶子;解码后尺寸须等于 **`w`×`h`**。 +- **`transp`**:须 **`100`** 才能看见(勿默认 **`0`**)。**`hier`**:仅 **`0`** 自动 / **`1`** 底 / **`2`** 顶。 +- 参考排版统计:`watchface_disp_catalog` 里上述 `disp` 的 **`typography.box.p50`**。 +- 实操示例:`docs/tool-examples.md` 第 **5b** 节、`scripts/gen_ocean_analog_dial_assets.py`。 ## `typography` 排版统计(v0.1.4+) diff --git a/docs/reference/guide-key-points.en.md b/docs/reference/guide-key-points.en.md index a45647d..7a73986 100644 --- a/docs/reference/guide-key-points.en.md +++ b/docs/reference/guide-key-points.en.md @@ -128,6 +128,15 @@ and is conventionally the same value as the matching `item_id` - **`transp`:** Treat as **on-screen visibility / opacity**. Anything that must **appear** needs **`transp: 100`**. **Many AI-generated payloads omit the field or set `0`, which makes the layer invisible on-device** — looks like broken coordinates when it is only transparency. - **`hier`:** Only three levels — **`0`** automatic ordering, **`1`** **bottom** (painted first), **`2`** **top** (painted last, above others). **There is no `hier: 3`.** Typical analog clock: hour **`1`**, minute **`0`**, second **`2`**; tune on hardware. +### Analog pointer images (`disp` 131 / 132 / 233) + +Firmware constants: **`DIVOOM_CLOCK_DISP_SUPPORT_HOUR_POINT_IMAGE` (131)**, +**`DIVOOM_CLOCK_DISP_SUPPORT_MIN_POINT_IMAGE` (132)**, +**`DIVOOM_CLOCK_DISP_SUPPORT_SECOND_POINT_IMAGE` (233)**. + +- **Required:** all three rows share the **same square bbox** (**`w == h`**) and the **same** **`x,y,w,h`**; each bitmap is **`w`×`w`** with the **hand pivot at the square center** and the hand pointing to **12 o'clock** (center rotation). Do **not** use three different skinny rectangles or a full **`800×1280`** hand sprite. +- Reference export: **`ClockId 60012`** (e.g. `clock60012.cfg`). See **`docs/disp-usage.md`** and **`docs/tool-examples.md` §5b**. + ### Image / net-gallery slot uniqueness In a single dial **`ItemList`**, keep **at most one row per image-backed `disp`**. diff --git a/docs/reference/guide-key-points.zh.md b/docs/reference/guide-key-points.zh.md index 16071a3..a097b91 100644 --- a/docs/reference/guide-key-points.zh.md +++ b/docs/reference/guide-key-points.zh.md @@ -122,6 +122,13 @@ weather.gif - **`hier`**:**仅有三档** — **`0`**:自动叠层顺序;**`1`**:**底层**(先绘制);**`2`**:**顶层**(后绘制,压住下层)。 **不存在 `3`、`4`…**。模拟指针常见组合:时针 **`hier: 1`**、秒针 **`hier: 2`**、分针 **`hier: 0`**(自动),以真机为准微调。 +### 时针 / 分针 / 秒针指针图(131 / 132 / 233) + +固件常量:**`DIVOOM_CLOCK_DISP_SUPPORT_HOUR_POINT_IMAGE`(131)**、**`DIVOOM_CLOCK_DISP_SUPPORT_MIN_POINT_IMAGE`(132)**、**`DIVOOM_CLOCK_DISP_SUPPORT_SECOND_POINT_IMAGE`(233)**。 + +- **必须**三根共用**同一正方形**图层 **`w = h`** 及相同 **`x,y,w,h`**;素材 **`w`×`w`**,**绕正方形中心旋转**,针朝 **12 点**。勿用互不相同的细长矩形框或 **`800×1280`** 全屏单笔图。 +- 正确布局可参考 **`ClockId 60012`** 导出配置(示例 **`clock60012.cfg`**)。详见 **`docs/disp-usage.md`** 与 **`docs/tool-examples.md` §5b**。 + ### 图像槽位与网络图库唯一性 同一表盘的 **`ItemList` 中,每种图像相关 `disp` 原则上只保留一行**。若同一 **`disp`** diff --git a/docs/tool-examples.md b/docs/tool-examples.md index c5ba1a8..8fd0d00 100644 --- a/docs/tool-examples.md +++ b/docs/tool-examples.md @@ -176,16 +176,22 @@ weather_pack.bin ## 5b) 模拟指针(`HOUR_POINT_IMAGE` / `MIN_POINT_IMAGE` / `SECOND_POINT_IMAGE`) -对应 `disp`:**131**(时针)、**132**(分针)、**233**(秒针)。这类槽位走 **`image_addr` + -`clock_bg.tar.gz` 元素图**,但布局和「整屏贴图」完全不同: - -1. **图层几何**:三根指针应使用**完全相同**的 **`x` / `y` / `w` / `h`**,把方块中心对准表盘 - 物理轴心(太阳 / 圆心)。固件在该矩形中心附近旋转指针;常见取值是 **正方形** - `w = h`,约在 **180~600** 区间(编辑器模板的中位数多在 **200~310**,见 - `watchface_disp_catalog` 里对应 `typography.box`)。 -2. **素材像素**:每张指针 PNG(或 WebP/JPEG)的分辨率必须与 **`w`×`h` 一致**。指针画在 - **图像正中心**为枢轴,默认朝向 **12 点方向(竖直向上)**;不要用 **`800×1280`** - 的全屏画布画一根针——否则旋转中心会偏离视觉圆心,三根针的「轴帽」会散落在画面各处。 +固件侧常量(与设备代码一致): + +- **`DIVOOM_CLOCK_DISP_SUPPORT_HOUR_POINT_IMAGE`** = **131**(时针指针) +- **`DIVOOM_CLOCK_DISP_SUPPORT_MIN_POINT_IMAGE`** = **132**(分针指针) +- **`DIVOOM_CLOCK_DISP_SUPPORT_SECOND_POINT_IMAGE`** = **233**(秒针指针) + +对应 `disp`:**131** / **132** / **233**。这类槽位走 **`image_addr` + `clock_bg.tar.gz` 元素图**,但布局和「整屏贴图」完全不同: + +0. **必须先满足(否则易出现错位)**:三根条目使用**完全相同**的 **`x`、`y`、`w`、`h`**,且 **`w = h`(正方形)**; + 三张指针素材均为 **`w`×`w` 像素**,在各自画布**正中央**枢轴、朝 **12 点**方向绘制,由固件绕**图层中心**旋转。 + **不要用三根互不相同的细长矩形 `w×h`**,也不要用 **`800×1280`** 全屏笔画一根针。 + 正确示例可参考设备导出 **`ClockId 60012`**(如 **`d:\debug\clock60012.cfg`**):三根指针共用同一正方形图层参数。 + +1. **图层几何**:把该正方形中心对准表盘物理轴心(圆心)。常见边长在 **180~600**(编辑器模板中位数见 `watchface_disp_catalog` → `typography.box`)。 + +2. **素材像素**:每张指针 PNG(或 WebP/JPEG)分辨率 **必须等于** **`w`×`h`**(即 **`w`×`w`**)。 3. **叠放顺序 `hier`(仅三档)**:**`0`** = 自动排序;**`1`** = **底层**(先画);**`2`** = **顶层**(后画,压住下层)。没有 `3`、`4`… 等扩展档位。模拟表盘常见写法:时针 **`hier: 1`**(底层)、 秒针 **`hier: 2`**(顶层盖住时针分针)、分针 **`hier: 0`**(交给自动顺序);最终以真机为准微调。 diff --git a/resources/guide-quick-reference.md b/resources/guide-quick-reference.md index 0f9c5e1..9d3a542 100644 --- a/resources/guide-quick-reference.md +++ b/resources/guide-quick-reference.md @@ -167,10 +167,9 @@ Validation lives in `wf_unpack_disp_items` (firmware Other recognized keys: `sep`, `image_id`, `image_addr`, `animation`, `angle`, `hier`, `transp`, and (bundle only) `bundle_image`. -**Analog pointer slots (`disp` 131 / 132 / 233 — `*_POINT_IMAGE`):** use one **square** -layer (`w == h`) shared by hour/minute/second; each `image_addr` bitmap must match **`w`×`h`** with the -hand pivot at the **image center** and the hand painted pointing to **12 o'clock**. Do **not** use a -full **`800×1280`** hand sprite — rotation will not share the dial center. +**Analog pointer slots (`DIVOOM_CLOCK_DISP_SUPPORT_*_POINT_IMAGE` — **`disp` 131 / 132 / 233**, +editor **`HOUR_POINT_IMAGE` / `MIN_POINT_IMAGE` / `SECOND_POINT_IMAGE`):** **Mandatory:** one shared **square** +layer (**`w == h`**) for hour/minute/second — identical **`x,y,w,h`** on all three rows — center pivot rotation (hand painted at bitmap center, toward **12 o'clock**). Bitmap size **`w`×`w`**. Do **not** use mismatched skinny rectangles or a full **`800×1280`** hand sprite. Reference export **`ClockId 60012`** (`clock60012.cfg`). See **`docs/disp-usage.md`** / **`docs/tool-examples.md` §5b**. **`transp`:** Use **`100`** for every layer that must be visible. **AI generators often emit `0`, which makes the layer invisible on-device** (looks like bad coordinates). diff --git a/resources/skill-quick-reference.md b/resources/skill-quick-reference.md index 037cee2..1eb8a5e 100644 --- a/resources/skill-quick-reference.md +++ b/resources/skill-quick-reference.md @@ -122,11 +122,9 @@ In bundle mode, leaf basenames inside the tar must exactly match the - **`transp`:** Treat as **opacity for visibility**. For anything that should **show on screen**, set **`transp: 100`**. **Many LLMs default missing/`transp: 0`, which renders fully invisible on device** — users think layout is broken when it is only transparency. - **`hier`:** Only **`0` = auto**, **`1` = bottom layer**, **`2` = top layer**. There is **no `hier: 3`**. Stack analog hands e.g. hour **`1`**, minute **`0`**, second **`2`**. -## Analog pointer slots (`disp` 131 / 132 / 233) +## Analog pointer slots (`DIVOOM_CLOCK_DISP_SUPPORT_*_POINT_IMAGE`: **131** / **132** / **233**) -Square **`w = h`** layer shared by hour/minute/second; bitmap **`w`×`h`** with pivot at **center**, -hand toward **12 o'clock**. Avoid **`800×1280`** hand sprites. Use **`transp: 100`**; use **`hier`** -**`0`/`1`/`2`** so the second hand can paint on top (**`2`**). Details: `docs/tool-examples.md` §5b. +**Required:** shared **square** bbox (**`w = h`**, **same `x,y,w,h`** on all three `ItemList` rows); each PNG/WebP/JPEG is **`w`×`w`**, **pivot at square center**, hand toward **12 o'clock** (firmware rotates around layer center). **Never** three different thin rectangles or **`800×1280`** full-screen needles. Good reference: device export **`ClockId 60012`** (`clock60012.cfg`). **`transp: 100`**; **`hier`** **`0`/`1`/`2`** with second often **`2`**. Details: `docs/tool-examples.md` §5b, `docs/disp-usage.md`. ## Image elements / net-gallery uniqueness diff --git a/scripts/gen_ocean_analog_dial_assets.py b/scripts/gen_ocean_analog_dial_assets.py index 254416c..2ab0759 100644 --- a/scripts/gen_ocean_analog_dial_assets.py +++ b/scripts/gen_ocean_analog_dial_assets.py @@ -1,10 +1,15 @@ """ -Generate ocean-themed dial backdrop + square PNG hands for Divoom POINT_IMAGE slots. +Generate ocean-themed dial backdrop + square PNG hands for Divoom pointer slots. -Firmware rotates analog hands around the center of the layer box (x,y,w,h). Each -hand image must be exactly w×h with the pivot at the image center and the hand -drawn pointing toward 12 o'clock (up, -Y). Do not use full-screen 800×1280 hand -sprites — see docs/tool-examples.md. +Uses firmware pointer types (same square bbox for all three rows): + DIVOOM_CLOCK_DISP_SUPPORT_HOUR_POINT_IMAGE = 131 + DIVOOM_CLOCK_DISP_SUPPORT_MIN_POINT_IMAGE = 132 + DIVOOM_CLOCK_DISP_SUPPORT_SECOND_POINT_IMAGE = 233 + +Rotation is around the center of the layer box (x,y,w,h). Each hand bitmap must +match w×h with w==h (square), pivot at image center, hand toward 12 o'clock. +Do not use full-screen 800×1280 hand sprites or mismatched skinny rectangles — +see docs/tool-examples.md §5b and a good export example ClockId 60012 (e.g. clock60012.cfg). """ from __future__ import annotations diff --git a/src/index.ts b/src/index.ts index d23f026..06e5eca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -141,7 +141,7 @@ const tools: Tool[] = [ { name: "watchface_patch_local", description: - "Patch local dial via Device/PatchLocalClockInfo with precheck. Defaults to POST /divoom_api (JSON only) for pure metadata edits. Prefer ItemPatchList (per-index field diff) — DO NOT include item_id inside patch.* unless the user explicitly asks to rename a slot, since the firmware will overwrite the device-side item_id and break menu/config bindings. When dialAssetsPath is set, switches to multipart POST /patch_local_clock: first JSON part (Device/PatchLocalClockInfo, optional DialAssets), second part single JPEG/WebP dial backdrop or clock_bg.tar.gz bundle. Element slots inside the tarball must be JPEG, WebP, or PNG (validated by firmware wf_validate_bundle_slot_image_file). Use ItemPatchList[].patch.bundle_image= to bind a tar leaf to that slot's img_addr; supplying ItemList alone is a full-table replace and should be avoided unless the row count actually changes. For analog pointer fixes (disp 131/132/233): patch shared square x/y/w/h, bundle_image leaves matching w×h PNGs; set transp 100 for visibility (AI often uses 0 → invisible); hier only 0=auto, 1=bottom, 2=top — see docs/tool-examples.md §5b. Avoid duplicate rows for the same image-backed disp (NET_PIC family: 13, 125–130, 173–175); later rows overwrite earlier ones — docs/disp-usage.md. Multipart framing (Content-Length / boundary / manual construction): see watchface_upload_file description and resources/skill-quick-reference.md.", + "Patch local dial via Device/PatchLocalClockInfo with precheck. Defaults to POST /divoom_api (JSON only) for pure metadata edits. Prefer ItemPatchList (per-index field diff) — DO NOT include item_id inside patch.* unless the user explicitly asks to rename a slot, since the firmware will overwrite the device-side item_id and break menu/config bindings. When dialAssetsPath is set, switches to multipart POST /patch_local_clock: first JSON part (Device/PatchLocalClockInfo, optional DialAssets), second part single JPEG/WebP dial backdrop or clock_bg.tar.gz bundle. Element slots inside the tarball must be JPEG, WebP, or PNG (validated by firmware wf_validate_bundle_slot_image_file). Use ItemPatchList[].patch.bundle_image= to bind a tar leaf to that slot's img_addr; supplying ItemList alone is a full-table replace and should be avoided unless the row count actually changes. Pointer fixes (131/132/233 = DIVOOM_CLOCK_DISP_SUPPORT_*_POINT_IMAGE): shared square x/y/w/h, w×w PNGs, center rotation; ref ClockId 60012; transp 100; hier 0/1/2 only — docs/tool-examples.md §5b. Avoid duplicate image-backed disp rows (NET_PIC family); docs/disp-usage.md. Multipart framing: watchface_upload_file description and resources/skill-quick-reference.md.", inputSchema: { type: "object", properties: { @@ -321,7 +321,7 @@ const tools: Tool[] = [ { name: "watchface_create_local_clock", description: - "POST /create_local_clock (multipart) — Device/CreateLocalClock. metadata.DialAssets accepts 'auto' (default; sniffs gzip magic on the file part), 'image' (single JPEG/WebP backdrop), or 'bundle' (clock_bg.tar.gz). Legacy UseDialAssetBundle (0=image, non-0=bundle) is honored when DialAssets is omitted. Backdrop is JPEG/WebP only; element slots inside the tarball accept JPEG/WebP/PNG (firmware wf_validate_bundle_slot_image_file). Each ItemList[i] needs disp/font/x/y/w/h/size/alig numbers and color_1/color_2/item_id non-empty strings; ItemIdList must be a parallel non-empty string array. alig: 3=center, 4=left, 5=right. For analog pointer disps (131 HOUR_POINT_IMAGE, 132 MIN_POINT_IMAGE, 233 SECOND_POINT_IMAGE): matching square w=h, image pixels = w×h, pivot center, hand toward 12 o'clock — never full-screen 800×1280 hand layers. transp: MUST use 100 for visible layers — LLMs often emit 0 → invisible on device. hier: ONLY 0=auto, 1=bottom, 2=top (no tier 3+). See docs/tool-examples.md §5b. Uniqueness: do not duplicate the same image-backed disp in one dial — later rows overwrite earlier ones; especially NET_PIC family (disp 13, 125–130, 173–175, DIVOOM_CLOCK_DISP_SUPPORT_NET*_PIC); see docs/disp-usage.md. Multipart framing (Content-Length per part): see watchface_upload_file description.", + "POST /create_local_clock (multipart) — Device/CreateLocalClock. metadata.DialAssets accepts 'auto' (default; sniffs gzip magic on the file part), 'image' (single JPEG/WebP backdrop), or 'bundle' (clock_bg.tar.gz). Legacy UseDialAssetBundle (0=image, non-0=bundle) is honored when DialAssets is omitted. Backdrop is JPEG/WebP only; element slots inside the tarball accept JPEG/WebP/PNG (firmware wf_validate_bundle_slot_image_file). Each ItemList[i] needs disp/font/x/y/w/h/size/alig numbers and color_1/color_2/item_id non-empty strings; ItemIdList must be a parallel non-empty string array. alig: 3=center, 4=left, 5=right. Pointer slots DIVOOM_CLOCK_DISP_SUPPORT_HOUR_POINT_IMAGE=131, MIN_POINT_IMAGE=132, SECOND_POINT_IMAGE=233: mandatory shared square w=h and identical x,y,w,h on all three rows; w×w bitmaps; center pivot, hand toward 12 o'clock — never full-screen 800×1280 layers or mismatched thin rects; reference ClockId 60012 export. transp: MUST use 100 for visible layers — LLMs often emit 0 → invisible on device. hier: ONLY 0=auto, 1=bottom, 2=top (no tier 3+). See docs/tool-examples.md §5b. Uniqueness: do not duplicate the same image-backed disp in one dial — later rows overwrite earlier ones; especially NET_PIC family (disp 13, 125–130, 173–175, DIVOOM_CLOCK_DISP_SUPPORT_NET*_PIC); see docs/disp-usage.md. Multipart framing (Content-Length per part): see watchface_upload_file description.", inputSchema: { type: "object", properties: { From 8f448764181f3b4625668debef1d6068afbb1d1d Mon Sep 17 00:00:00 2001 From: Divoom Date: Mon, 18 May 2026 18:21:11 +0800 Subject: [PATCH 3/4] docs(mcp): sync submission guides and add platform update playbooks - Refresh Baidu/Bailian, Coze, MCP SO, and Smithery submission-ready docs - Extend html-visual-editor documentation - Add zh-CN notes for anthropics/skills updates and Smithery MCP publishing Co-authored-by: Cursor --- BAILIAN_MCP_SUBMISSION_READY.md | 57 ++++++----- COZE_SUBMISSION_READY.md | 44 +++++---- MCP_SO_SUBMISSION_READY.md | 35 +++---- SMITHERY_SUBMISSION_READY.md | 5 +- ...hropic-skills\346\233\264\346\226\260.txt" | 99 +++++++++++++++++++ docs/html-visual-editor.md | 18 +++- git-agent-status.txt | 18 ++++ "smithery.ai\346\233\264\346\226\260.TXT" | 52 ++++++++++ 8 files changed, 256 insertions(+), 72 deletions(-) create mode 100644 "anthropic-skills\346\233\264\346\226\260.txt" create mode 100644 git-agent-status.txt create mode 100644 "smithery.ai\346\233\264\346\226\260.TXT" diff --git a/BAILIAN_MCP_SUBMISSION_READY.md b/BAILIAN_MCP_SUBMISSION_READY.md index 0ebdc27..8a3cb31 100644 --- a/BAILIAN_MCP_SUBMISSION_READY.md +++ b/BAILIAN_MCP_SUBMISSION_READY.md @@ -1,21 +1,23 @@ # 阿里云百炼(Model Studio)— MCP 接入说明 -百炼的 MCP 能力分散在 **MCP 广场**(挑选官方/已上架服务)与 **MCP 管理**(自定义部署)两处。以下为 **`mcp-divoom-lan`** 的推荐配置与文档索引。 +百炼的 MCP 能力分散在 **MCP 广场**(挑选官方/已上架服务)与 **MCP 管理**(自定义部署)两处。以下为 `**mcp-divoom-lan`** 的推荐配置与文档索引。 ## 官方文档(建议收藏) -| 主题 | 链接 | -|------|------| -| MCP 简介与计费 | [模型上下文协议(MCP)](https://help.aliyun.com/zh/model-studio/mcp-introduction) | -| 官方 MCP 开通与智能体配置 | [官方 MCP 服务](https://help.aliyun.com/zh/model-studio/official-and-third-party-mcp) | -| **自定义 MCP(脚本 npx / uvx、AI 网关、OpenAPI)** | [**自定义 MCP 服务**](https://help.aliyun.com/zh/model-studio/custom-mcp) | -| 快速开始(广场开通示例) | [官方 MCP 服务快速入门](https://help.aliyun.com/zh/model-studio/mcp-quickstart) | -| 外部调用 | [外部调用 MCP 服务](https://help.aliyun.com/zh/model-studio/mcp-external-calls) | + +| 主题 | 链接 | +| --------------------------------------- | --------------------------------------------------------------------------------- | +| MCP 简介与计费 | [模型上下文协议(MCP)](https://help.aliyun.com/zh/model-studio/mcp-introduction) | +| 官方 MCP 开通与智能体配置 | [官方 MCP 服务](https://help.aliyun.com/zh/model-studio/official-and-third-party-mcp) | +| **自定义 MCP(脚本 npx / uvx、AI 网关、OpenAPI)** | **[自定义 MCP 服务](https://help.aliyun.com/zh/model-studio/custom-mcp)** | +| 快速开始(广场开通示例) | [官方 MCP 服务快速入门](https://help.aliyun.com/zh/model-studio/mcp-quickstart) | +| 外部调用 | [外部调用 MCP 服务](https://help.aliyun.com/zh/model-studio/mcp-external-calls) | + ## 控制台入口 - **MCP 广场(市场)**:[百炼控制台 MCP](https://bailian.console.aliyun.com/?tab=mcp#/mcp-market) -- **MCP 管理(创建自定义服务)**:[MCP 管理](https://bailian.console.aliyun.com/?tab=app#/mcp-manage) +- **MCP 管理(创建自定义服务)**:[MCP 管理](https://bailian.console.aliyun.com/?tab=app#/mcp-manage) 说明:帮助中心写的是「在 **MCP 市场** 搜寻服务,以**自定义 MCP 服务**形式部署」——即多数第三方开源服务是 **你自己在 MCP 管理里创建**,而非一定有「对外开放上架到广场」的自助入口。若要把服务做成 **广场首页可检索的公开条目**,通常需 **阿里云运营/合作渠道**,以最新控制台与协议为准。 @@ -23,20 +25,22 @@ ## 在百炼中创建「自定义 MCP」(脚本部署 · npx) -适用于已发布到 npm、支持 **`npx -y <包名>`** 的 Node MCP(本仓库 **0.1.2+** 已配置 `bin`)。 +适用于已发布到 npm、支持 `**npx -y <包名>`** 的 Node MCP(本仓库 **0.1.2+** 已配置 `bin`)。 ### 1. 进入创建流程 -1. 打开 [MCP 管理](https://bailian.console.aliyun.com/?tab=app#/mcp-manage)。 -2. **创建 MCP 服务** → 选择 **使用脚本部署** → **部署服务**。 +1. 打开 [MCP 管理](https://bailian.console.aliyun.com/?tab=app#/mcp-manage)。 +2. **创建 MCP 服务** → 选择 **使用脚本部署** → **部署服务**。 ### 2. 表单建议填写 -| 配置项 | 建议值 | -|--------|--------| + +| 配置项 | 建议值 | +| -------- | ----------------------------------------- | | **服务名称** | `Divoom LAN Watchface` 或 `mcp-divoom-lan` | -| **描述** | 见下 **「详细描述(控制台 ≤500 字)」**,可直接粘贴。 | -| **安装方式** | **npx** | +| **描述** | 见下 **「详细描述(控制台 ≤500 字)」**,可直接粘贴。 | +| **安装方式** | **npx** | + #### 详细描述(控制台 ≤500 字) @@ -49,7 +53,7 @@ mcp-divoom-lan 是基于模型上下文协议(MCP)的 Divoom 局域网工具 ### 3. MCP 服务配置(JSON) -百炼控制台里 **NPX 配置** 常与 **高德等官方卡片** 相同:仅 `command` / `args` / `env`,**不带 `type`**。帮助中心 [自定义 MCP](https://help.aliyun.com/zh/model-studio/custom-mcp) 文档模板里则写有 **`"type": "stdio"`**。若一种格式校验失败,可换另一种。 +百炼控制台里 **NPX 配置** 常与 **高德等官方卡片** 相同:仅 `command` / `args` / `env`,**不带 `type`**。帮助中心 [自定义 MCP](https://help.aliyun.com/zh/model-studio/custom-mcp) 文档模板里则写有 `**"type": "stdio"**`。若一种格式校验失败,可换另一种。 **A. 与控制台高德示例一致(推荐先试)** @@ -111,7 +115,7 @@ mcp-divoom-lan 是基于模型上下文协议(MCP)的 Divoom 局域网工具 - 百炼 **脚本部署**会将 MCP 跑在 **函数计算 FC** 等云端环境,**默认无法访问你家里路由器后面的 IP**。 - **因此:** 若无 **专线 / VPN / 内网穿透 / 设备公网可达地址**,云端智能体**无法**稳定驱动实机;更适合的场景是: - **本地开发机 / 同局域网主机**上跑 MCP(Cursor、Claude 等),或 - - 自行在 **可访问设备网段**的环境部署 **Streamable HTTP** MCP,再在百炼用 **`type: sse/streamableHttp`** 填 URL(需你先有远端 MCP 网关;本仓库默认 **stdio**)。 + - 自行在 **可访问设备网段**的环境部署 **Streamable HTTP** MCP,再在百炼用 `**type: sse/streamableHttp`** 填 URL(需你先有远端 MCP 网关;本仓库默认 **stdio**)。 若仅希望在百炼里 **演示协议与工具列表**,可将 `DIVOOM_DEVICE_HOST` 指向测试中可达的地址;真实用户需理解 **网络可达性** 是前提。 @@ -123,10 +127,12 @@ mcp-divoom-lan 是基于模型上下文协议(MCP)的 Divoom 局域网工具 **这是预期现象。** -| 位置 | 含义 | -|------|------| -| **[MCP 管理](https://bailian.console.aliyun.com/?tab=app#/mcp-manage) → 自定义服务** | 当前账号下**自行创建/部署**的 MCP(如脚本 npx),状态「已部署」表示**本账号内**可用的自定义服务。 | -| **[MCP 广场](https://bailian.console.aliyun.com/?tab=mcp#/mcp-market)** | 平台**对外展示、可检索**的服务目录,多为**官方预置或与阿里云合作上架**;**不会**因为你在管理里部署成功就自动出现在广场。 | + +| 位置 | 含义 | +| ----------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| **[MCP 管理](https://bailian.console.aliyun.com/?tab=app#/mcp-manage) → 自定义服务** | 当前账号下**自行创建/部署**的 MCP(如脚本 npx),状态「已部署」表示**本账号内**可用的自定义服务。 | +| **[MCP 广场](https://bailian.console.aliyun.com/?tab=mcp#/mcp-market)** | 平台**对外展示、可检索**的服务目录,多为**官方预置或与阿里云合作上架**;**不会**因为你在管理里部署成功就自动出现在广场。 | + **使用方式:** 在 **应用管理** 里给智能体/工作流 **添加 MCP** 时,从 **自定义服务** 列表里选用你部署的服务(例如 `mcp-divoom`),无需在广场搜同名条目。 @@ -135,11 +141,12 @@ mcp-divoom-lan 是基于模型上下文协议(MCP)的 Divoom 局域网工具 ### 其它说明 - **广场**:浏览已收录服务,点卡片可复制 **NPX** 等示例。 -- **`mcp-divoom-lan`** 未上广场时,仍用 **MCP 管理 → 自定义 MCP(脚本部署)** 即可。 +- `**mcp-divoom-lan`** 未上广场时,仍用 **MCP 管理 → 自定义 MCP(脚本部署)** 即可。 - 若贵司推动 **入驻广场**,请以控制台与阿里云最新政策为准。 ## 相关(本仓库) - npm:`mcp-divoom-lan`(推荐 0.1.2+,`npx -y mcp-divoom-lan`) -- 源码: -- 其他目录:`MCP_SO_SUBMISSION_READY.md`、`VOLCENGINE_SUBMISSION_READY.md` +- 源码:[https://github.com/DivoomDevelop/mcp-divoom-lan](https://github.com/DivoomDevelop/mcp-divoom-lan) +- 其他目录:`MCP_SO_SUBMISSION_READY.md`、`VOLCENGINE_SUBMISSION_READY.md` + diff --git a/COZE_SUBMISSION_READY.md b/COZE_SUBMISSION_READY.md index 1c9b2b1..0d3155a 100644 --- a/COZE_SUBMISSION_READY.md +++ b/COZE_SUBMISSION_READY.md @@ -1,14 +1,16 @@ # 扣子(Coze)— 插件发布与上架说明 -扣子生态以 **插件(Plugin)** 为核心:每个插件包含一个或多个 **工具(Tool)**,通过 **HTTP API**(REST)与外部服务通信。这与 **`mcp-divoom-lan`** 使用的 **stdio / MCP** 不是同一套协议;要「在扣子上可用」,通常需要 **HTTP 封装**或 **平台当前支持的 MCP 扩展能力**(以控制台为准)。 +扣子生态以 **插件(Plugin)** 为核心:每个插件包含一个或多个 **工具(Tool)**,通过 **HTTP API**(REST)与外部服务通信。这与 `**mcp-divoom-lan`** 使用的 **stdio / MCP** 不是同一套协议;要「在扣子上可用」,通常需要 **HTTP 封装**或 **平台当前支持的 MCP 扩展能力**(以控制台为准)。 ## 官方文档入口 -| 主题 | 链接 | -|------|------| + +| 主题 | 链接 | +| --------------------------------- | -------------------------------------------------------------------------------------- | | 从 API 搭建插件全流程(创建 → 添加工具 → **发布**) | [快速搭建智能体列表查询插件](https://www.coze.cn/api/open/docs/tutorial/build_a_agent_query_plugin) | -| 鉴权与访问令牌 | [鉴权方式概述](https://docs.coze.cn/api/open/docs/developer_guides/authentication) | -| 扣子编程 / 资源库 | [扣子编程](https://code.coze.cn/home) | +| 鉴权与访问令牌 | [鉴权方式概述](https://docs.coze.cn/api/open/docs/developer_guides/authentication) | +| 扣子编程 / 资源库 | [扣子编程](https://code.coze.cn/home) | + 教程摘要:**资源库** → **+ 资源 → 插件** → **云侧插件 - 基于已有服务创建** → 填写插件 URL、Header、授权方式 → **创建工具**(路径、方法、入参/出参、试运行)→ 点击 **发布**(版本号、是否收集个人信息)。 @@ -16,11 +18,11 @@ ## 一、在自己空间里「发布」插件(必做) -1. 登录 **[扣子编程](https://code.coze.cn/home)**,选好 **工作空间**。 -2. 左侧 **资源库** → 右上角 **+ 资源** → **插件**。 -3. 填写名称、描述;**插件工具创建方式** 选 **基于已有服务创建**;填写你的 **API 根 URL**(需 **HTTPS** 公网可达,除非产品说明支持 HTTP/私网)。 -4. **创建工具**:为每个 API 配置路径、Method、入参、出参;**试运行** 成功。 -5. 点击 **发布**:填写版本说明,选择是否涉及用户个人信息收集,确认发布。 +1. 登录 **[扣子编程](https://code.coze.cn/home)**,选好 **工作空间**。 +2. 左侧 **资源库** → 右上角 **+ 资源** → **插件**。 +3. 填写名称、描述;**插件工具创建方式** 选 **基于已有服务创建**;填写你的 **API 根 URL**(需 **HTTPS** 公网可达,除非产品说明支持 HTTP/私网)。 +4. **创建工具**:为每个 API 配置路径、Method、入参、出参;**试运行** 成功。 +5. 点击 **发布**:填写版本说明,选择是否涉及用户个人信息收集,确认发布。 发布后,插件可被本空间 **智能体(Bot)** 在 **插件 / 工具** 中添加使用(官方教程:**插件只有发布后,才可以被智能体使用**)。 @@ -30,7 +32,7 @@ 常见流程分两层: -1. **发布**:见上一节,插件处于「已发布」,本账号/本空间可用。 +1. **发布**:见上一节,插件处于「已发布」,本账号/本空间可用。 2. **上架到商店**:在扣子/扣子编程控制台查找 **上架插件、提交审核、插件商店** 等入口(**文案随版本变化**),填写类目、介绍、是否收费/授权,**提交审核**;通过后其他用户可在商店发现。 若控制台无「上架」项,可能受 **空间类型、企业认证、地区或灰度** 限制,需查阅 **帮助中心** 或 **工单**。 @@ -39,16 +41,18 @@ ## 三、和 `mcp-divoom-lan` 的关系(重要) -| 能力 | mcp-divoom-lan | 扣子插件(默认) | -|------|----------------|------------------| -| 协议 | MCP over **stdio** | **HTTP/HTTPS** 工具定义 | -| 典型部署 | 本地 / MCP 客户端主机 | 公网 API 服务地址 | -| Divoom 设备 | 多为 **局域网 IP** | Bot 调插件时,你的服务须 **能访问设备**(内网穿透、边缘网关、同局域服务器等) | + +| 能力 | mcp-divoom-lan | 扣子插件(默认) | +| --------- | ------------------ | ------------------------------------------- | +| 协议 | MCP over **stdio** | **HTTP/HTTPS** 工具定义 | +| 典型部署 | 本地 / MCP 客户端主机 | 公网 API 服务地址 | +| Divoom 设备 | 多为 **局域网 IP** | Bot 调插件时,你的服务须 **能访问设备**(内网穿透、边缘网关、同局域服务器等) | + **可选路线:** -1. **自研 HTTP 桥**:用 Node/Python 等把 Divoom LAN HTTP 包一层 REST,部署在可达设备的机器上,再按扣子教程建插件。 -2. **产品若支持 MCP 扩展**:以扣子 **当前** 控制台与文档为准;**不能**想当然把本地 `node dist/index.js` 当作「插件 URL」。 +1. **自研 HTTP 桥**:用 Node/Python 等把 Divoom LAN HTTP 包一层 REST,部署在可达设备的机器上,再按扣子教程建插件。 +2. **产品若支持 MCP 扩展**:以扣子 **当前** 控制台与文档为准;**不能**想当然把本地 `node dist/index.js` 当作「插件 URL」。 3. **内部使用**:仅团队 Bot 使用,也要满足控制台对 URL 与安全的要求。 --- @@ -57,10 +61,10 @@ - 插件说明、边界与是否收集个人信息 - 稳定 API 基地址、鉴权说明 -- 各工具的请求/响应示例 +- 各工具的请求/响应示例 --- ## 五、本仓库其它目录 -- `BAILIAN_MCP_SUBMISSION_READY.md`、`VOLCENGINE_SUBMISSION_READY.md`、`MCP_SO_SUBMISSION_READY.md`、`GLAMA_SUBMISSION_READY.md` +- `BAILIAN_MCP_SUBMISSION_READY.md`、`VOLCENGINE_SUBMISSION_READY.md`、`MCP_SO_SUBMISSION_READY.md`、`GLAMA_SUBMISSION_READY.md` \ No newline at end of file diff --git a/MCP_SO_SUBMISSION_READY.md b/MCP_SO_SUBMISSION_READY.md index 8f580b3..1f1802b 100644 --- a/MCP_SO_SUBMISSION_READY.md +++ b/MCP_SO_SUBMISSION_READY.md @@ -4,29 +4,13 @@ Submit at: [mcp.so/submit](https://mcp.so/submit) (sign in if required). ## Form fields (copy-paste) -| Field | Value | -| ----- | ----- | -| **Type** | MCP Server | -| **Name** | `mcp-divoom-lan` | -| **URL** | `https://github.com/DivoomDevelop/mcp-divoom-lan` | -| **Description** | See §Description (full paragraph) below — copy into multi-line “Description” if the form has one. | -| **Tags** | `MCP,Model Context Protocol,Divoom,watchface,clock face,customization,LAN,local network,IoT,smart clock,gadget,Node.js,stdio,HTTP API,multipart upload` | -## Description (for “Description” textarea) +| Field | Value | +| -------- | ------------------------------------------------- | +| **Type** | MCP Server | +| **Name** | `mcp-divoom-lan` | +| **URL** | `https://github.com/DivoomDevelop/mcp-divoom-lan` | -**mcp-divoom-lan** is an open-source Model Context Protocol (MCP) server that exposes Divoom watchface customization over **local LAN HTTP**. AI assistants can read and patch the current on-device watchface JSON, manage fonts and store market lists, switch the active dial, adjust brightness, and run **multipart** flows for background replacement, file upload, and creating a local clock—while following a **read-before-write** workflow and explicit warnings for destructive actions (e.g. reset-from-cloud). Requires network access to the Divoom device; configure `DIVOOM_DEVICE_HOST` (and optionally port/timeout). MIT license; Node.js 20+; stdio transport. - -**One-liner (if the form has a strict length limit):** MCP server for **Divoom** devices: customize watchfaces over **LAN** (read/patch config, dial/brightness, multipart uploads). Node 20+, stdio—set `DIVOOM_DEVICE_HOST` on your network. - -## Tags (comma-separated) - -Shorter list: - -`MCP,Divoom,watchface,LAN,IoT,smart-device,Node.js,stdio,home-automation` - -Longer list (more discoverability): - -`MCP,Model Context Protocol,Divoom,watchface,clock face,customization,LAN,local network,IoT,smart clock,gadget,Node.js,stdio,HTTP API,multipart upload` ## Server Config (paste into the form) @@ -52,7 +36,7 @@ Use **one** of the following JSON blocks (mcp.so may expect the full `mcpServers } ``` -> **npm:** From **0.1.2** on, the published package includes `bin` and a `dist/index.js` shebang so **`npx -y mcp-divoom-lan`** works. **Variant A** is the default recommendation for MCP.so and end users. Use **Variant B** / **C** for contributors (local `node_modules`, git clone, or pinned paths). +> **Note:** The `npx -y mcp-divoom-lan` entry requires a published npm version that includes the `bin` field in `package.json` (present in this repo from the `bin` + shebang change). Until you publish that version to npm, use **Variant B** or **C** below on the form. ### Variant B — `node` + local `node_modules` path @@ -92,7 +76,11 @@ After `npm install mcp-divoom-lan` in a project directory: } ``` -## Long description (optional extra field) +## Short description (if the site has a free-text field) + +MCP server for Divoom LAN watchface customization: read/patch local clock config, multipart background upload, brightness and dial selection — with read-before-write safety notes. + +## Long description (optional) `mcp-divoom-lan` wraps Divoom device HTTP/LAN APIs as MCP tools (`watchface_get_local`, `watchface_patch_local`, multipart dial/upload helpers, etc.) and ships markdown resources for protocol constraints. Requires LAN access to the watch; set `DIVOOM_DEVICE_HOST` (or pass `target.host` per call). See repository `README.md` and `SECURITY.md` (`developer@divoom.com`). @@ -101,3 +89,4 @@ After `npm install mcp-divoom-lan` in a project directory: - Directory template: `MCP_DIRECTORY_LISTING_TEMPLATE.md` - npm package: `mcp-divoom-lan` - MCP Registry id: `io.github.DivoomDevelop/mcp-divoom-lan` + diff --git a/SMITHERY_SUBMISSION_READY.md b/SMITHERY_SUBMISSION_READY.md index ffc2a28..8fc8c6e 100644 --- a/SMITHERY_SUBMISSION_READY.md +++ b/SMITHERY_SUBMISSION_READY.md @@ -28,8 +28,9 @@ Core workflows: ## Install / Command -- Command: `node` -- Args: `["/ABSOLUTE/PATH/to/mcp-divoom-lan/dist/index.js"]` +- **npm:** `npx -y mcp-divoom-lan` with env `DIVOOM_DEVICE_HOST`, optional `DIVOOM_DEVICE_PORT` (9000), `DIVOOM_TIMEOUT_MS` (45000). +- **MCPB:** Build with `npm run mcpb:pack` in [mcp-divoom-lan](https://github.com/DivoomDevelop/mcp-divoom-lan); install `mcp-divoom-lan.mcpb` in a host that supports MCP Bundles; publish the same file to registries (e.g. Smithery) that accept stdio bundles. +- **From source:** `node dist/index.js` after `npm run build`. ## Environment diff --git "a/anthropic-skills\346\233\264\346\226\260.txt" "b/anthropic-skills\346\233\264\346\226\260.txt" new file mode 100644 index 0000000..14ce018 --- /dev/null +++ "b/anthropic-skills\346\233\264\346\226\260.txt" @@ -0,0 +1,99 @@ +下面是一份 anthropics/skills(Divoom skill)后续如何更新 的操作说明,默认你已经在 GitHub 上 fork 了 anthropics/skills(例如 DivoomDevelop/skills),并在本机有 tmp-anthropic-skills 这种克隆目录;若没有,可从「首次准备」开始。 + +一、仓库关系(心里要有数) +远程 含义 +https://github.com/anthropics/skills.git +上游官方,只读同步用 +https://github.com/DivoomDevelop/skills.git(示例) +你的 fork,你的分支往这儿 push +PR +从 fork 某分支 → anthropics/skills 的 main(你们当时是 PR #1121) +Skill 相关文件主要是: + +skills/divoom-watchface-mcp/SKILL.md +.claude-plugin/marketplace.json(example-skills 里要有 ./skills/divoom-watchface-mcp) +二、首次准备(只做一次) +Fork +浏览器打开 https://github.com/anthropics/skills → Fork 到你的账号/组织(如 DivoomDevelop)。 + +克隆自己的 fork,并加 upstream + +git clone https://github.com/DivoomDevelop/skills.git tmp-anthropic-skills +cd tmp-anthropic-skills +git remote add upstream https://github.com/anthropics/skills.git +git fetch upstream +开发与 PR 用分支(可与现有一致) + +git checkout -b add-divoom-watchface-mcp-skill +git push -u origin add-divoom-watchface-mcp-skill +在 GitHub 提 PR +DivoomDevelop/skills:add-divoom-watchface-mcp-skill → anthropics/skills:main。 +合并前,以后所有更新都 往同一分支推,PR 会自动刷新。 + +三、每次要更新 Skill 时的推荐流程 +1. 切到 skill 分支并同步上游(减少冲突) +cd Z:\...\tmp-anthropic-skills # 你的实际路径 +git fetch upstream +git checkout add-divoom-watchface-mcp-skill +git merge upstream/main +# 若有冲突:解决后 git add / git commit +说明: + +用 merge 或 rebase 均可;团队习惯 merge 更省事。 +若官方大改 marketplace.json,冲突多在你改的 example-skills 列表,保留两边的 skill 路径合并即可。 +2. 编辑内容(与 mcp-divoom-lan 对齐) +建议每次发 MCP 新版本时检查并更新 SKILL.md 中例如: + +npm 包名、版本或文档链接(如有硬编码) +工具列表是否与 mcp-divoom-lan 的 watchface_* 一致 +安全规则(空 ItemList、禁止隐式建表盘等) +v2 编辑器 GitHub / Pages URL +mcpName / Registry 若你在 SKILL 里写了 +marketplace.json 通常 不用动;只有当你 改名、换路径、或 example-skills 结构调整 时才改。 + +3. 提交并推到 fork +git add skills/divoom-watchface-mcp/SKILL.md +# 若改了 marketplace: +git add .claude-plugin/marketplace.json +git commit -m "docs(skills): sync divoom-watchface-mcp with mcp-divoom-lan v0.1.x" +git push origin add-divoom-watchface-mcp-skill +4. PR 侧 +打开已有 PR(例如 **https://github.com/anthropics/skills/pull/1121**),确认 Commits / Files changed 已更新。 +若 Review 有评论,在该分支继续改 → commit → push,不要关 PR 重开(除非维护者要求)。 +5. 合并之后 +anthropics/skills main 里会有你的 SKILL;用户拉官方仓库或通过 Claude 插件市场配置 anthropics/skills 时才能 公开检索到(具体展示方式随产品而变)。 +之后若再更新:仍建议 fetch upstream + merge → 改 SKILL → push 同分支**;若 PR 已合并,应 **从最新 main` 建新分支再提新 PR(见下文)。 +四、PR 已经合并以后,再想改 Skill +从 最新官方 main 拉分支(不要在很旧的分支上堆新 PR): +git fetch upstream +git checkout main +git pull upstream main +git push origin main # 同步你的 fork 的 main(可选但推荐) +git checkout -b update-divoom-skill-2026-xx +改 SKILL.md(及必要的 marketplace.json)。 +git push -u origin update-divoom-skill-2026-xx +在 GitHub 新开 PR → anthropics/skills。 +五、和 mcp-divoom-lan 发版怎么对上 +建议 顺序(与之前 MCP 流程一致): + +mcp-divoom-lan:改代码 → 升版本 → npm publish → (可选)Registry / Smithery / README。 +再改 SKILL.md:把「用户如何安装、工具名、链接、版本说明」写成与当前文档一致。 +push anthropics/skills PR,描述里可写:Aligns with mcp-divoom-lan vX.Y.Z on npm。 +这样审稿人能看到 MCP 与 Skill 一致。 + +六、常见问题 +情况 处理 +merge upstream/main 冲突 +重点看 .claude-plugin/marketplace.json,保留 divoom-watchface-mcp 条目,其余按上游合并。 +只想改 SKILL,不想动别的 +只 add SKILL.md,避免误提交整个克隆里无关文件。 +fork 很久没同步 +先 merge upstream/main 再改,减少 PR 里无关 diff。 +七、最短「检查清单」(复制用) +[ ] git fetch upstream && git checkout add-divoom-watchface-mcp-skill && git merge upstream/main +[ ] 更新 skills/divoom-watchface-mcp/SKILL.md(与 mcp-divoom-lan 一致) +[ ] (按需)更新 .claude-plugin/marketplace.json +[ ] git commit / git push origin add-divoom-watchface-mcp-skill +[ ] 看 PR #1121(或新 PR)是否已更新 +[ ] (若 PR 已合并过)从 main 拉新分支再提 PR +如果你愿意,可以把 本机 tmp-anthropic-skills 的实际路径 和 当前用的是否仍是 PR #1121 固定写进 mcp-divoom-lan 的 CONTRIBUTING.md 或 docs/,以后团队只盯仓库文档即可。 \ No newline at end of file diff --git a/docs/html-visual-editor.md b/docs/html-visual-editor.md index d719ef4..ce5cd83 100644 --- a/docs/html-visual-editor.md +++ b/docs/html-visual-editor.md @@ -36,8 +36,22 @@ - 先读后写:不要盲写未知 `index` - 尽量使用 `itemPatchList` / `itemPatchByRoleList`,避免整表覆盖 -- 图片操作遵循规格:`800x1280`、`JPEG/WebP` -- 危险命令(重置、切换)应有明确用户意图 +- `patch.*` 不要发 `item_id`(编辑器实现里也是这样,避免覆盖设备元数据) +- 底图必须 JPEG/WebP,800×1280,≤ 500 KiB;tar.gz 元素允许 JPEG/WebP/PNG +- `alig`:`3`=居中、`4`=左、`5`=右(与固件一致;编辑器在导入时把旧 `1/2` + 归一化) +- 危险命令(`watchface_set_clock_select` / `watchface_reset_local_then_cloud`) + 必须有明确用户意图 + +## 编辑器侧的 LAN UX 行为(与 MCP 保持一致) + +为了和 MCP 的安全约束同步,HTML 编辑器内置以下行为: + +- 未选择 LAN 设备时,「创建表盘」/「应用配置」/「显示表盘」三个按钮置灰禁用 +- LAN 操作的成功/错误反馈用居中 `` 弹出,不再用浏览器 `alert()` +- 模板表盘预览自动按 `preview-stage` 容器尺寸缩放,不出滚动条 +- 本地元素图按文件**魔数**(不是扩展名)识别 JPEG/WebP/GIF/PNG/BMP, + 非设备支持格式(GIF/BMP/TIFF)会被透明转码成 JPEG 后再打入 tar.gz ## 可视化能力范围建议 diff --git a/git-agent-status.txt b/git-agent-status.txt new file mode 100644 index 0000000..07dfb00 --- /dev/null +++ b/git-agent-status.txt @@ -0,0 +1,18 @@ +On branch cursor/docs-watchface-transp-hier-net-gallery +Your branch is up to date with 'origin/cursor/docs-watchface-transp-hier-net-gallery'. + +Changes not staged for commit: + (use "git add ..." to update what will be committed) + (use "git restore ..." to discard changes in working directory) + modified: BAILIAN_MCP_SUBMISSION_READY.md + modified: COZE_SUBMISSION_READY.md + modified: MCP_SO_SUBMISSION_READY.md + modified: SMITHERY_SUBMISSION_READY.md + modified: docs/html-visual-editor.md + +Untracked files: + (use "git add ..." to include in what will be committed) + "anthropic-skills\346\233\264\346\226\260.txt" + "smithery.ai\346\233\264\346\226\260.TXT" + +no changes added to commit (use "git add" and/or "git commit -a") diff --git "a/smithery.ai\346\233\264\346\226\260.TXT" "b/smithery.ai\346\233\264\346\226\260.TXT" new file mode 100644 index 0000000..d18fd65 --- /dev/null +++ "b/smithery.ai\346\233\264\346\226\260.TXT" @@ -0,0 +1,52 @@ +更新smithery.ai的MCP发布文档 + +下面是一份 以后改了 mcp-divoom-lan 建议按顺序做的事(按你是否需要「对外发布」裁剪步骤)。 + +1. 本地开发与自检 +在 tools/mcp-divoom-lan(或你克隆的仓库根)改代码。 +运行:npm run check(或至少 npm run build)。 +本地连设备做一次冒烟(任选):npm start / Cursor 里调几个 watchface_*。 +2. 版本号(只要对外发版就做) +编辑 package.json 的 version(semver,例如 0.1.1 → 0.1.2)。 +同步这些文件里的版本(若存在且需要一致): +CHANGELOG.md(新增一节) +server.json:version 与 packages[0].version +mcpb/manifest.json 里的 version(可选用脚本从 package.json 读,你们当前是 build-mcpb.mjs 会覆盖 manifest 里的 version,模板里可保留占位或手动对齐) +3. 发 npm(用户用 npx / 安装包) +cd +npm run release:check +npm publish --access public +确认:npm view mcp-divoom-lan version 与预期一致。 + +4. 更新 MCP Registry(元数据) +# 需已安装 mcp-publisher,且 server.json 与 npm 版本、mcpName 一致 +mcp-publisher validate -f .\server.json +mcp-publisher login github # 若 token 过期再登录 +mcp-publisher publish +5. 打 MCPB 并更新 Smithery +npm run mcpb:pack +# 得到 mcp-divoom-lan.mcpb 后 +smithery auth login # 若需要 +smithery namespace use aijun # 或你的 namespace +smithery mcp publish .\mcp-divoom-lan.mcpb -n aijun/mcp-divoom-lan +网络盘若很慢,staging 里 npm install 可能十几二十分钟,属正常。 + +6. Git 提交推送(DivoomDevelop/mcp-divoom-lan) +提交:源码与配置(src/、package.json、CHANGELOG.md、server.json、mcpb/manifest.json、scripts/、README.md 等)。 + +不要提交: node_modules/、dist/(若你策略是不提交构建产物)、mcpb/staging/、*.mcpb、*.tgz。 + +若 dist 应随仓库发布:在你们的流程里可改为提交 dist 或由 CI 构建。 + +7. 其它(按需) +目标 动作 +Anthropic skill +改 tmp-anthropic-skills / PR 里 SKILL.md,推送分支更新 PR。 +可视化编辑器 v2 +在 divoom-watchface-visual-editor_v2 仓库单独发版/部署 Pages。 +文档里的链接 +README 中 npm、GitHub、Smithery、Pages 是否与当前版本一致。 +一句话流程 +改代码 → 升版本 + 写 CHANGELOG → npm publish → mcp-publisher publish → npm run mcpb:pack → smithery mcp publish → git push。 + +若某次只是 私有调试、不给别人用:做到 第 1 步 + 本地跑 即可;npm / Registry / Smithery 都可以跳过。 \ No newline at end of file From 594a064f0f11094c709671983ccf3925bfe384ca Mon Sep 17 00:00:00 2001 From: Divoom Date: Mon, 18 May 2026 18:21:23 +0800 Subject: [PATCH 4/4] chore: remove accidental git-agent-status.txt from repo Co-authored-by: Cursor --- git-agent-status.txt | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 git-agent-status.txt diff --git a/git-agent-status.txt b/git-agent-status.txt deleted file mode 100644 index 07dfb00..0000000 --- a/git-agent-status.txt +++ /dev/null @@ -1,18 +0,0 @@ -On branch cursor/docs-watchface-transp-hier-net-gallery -Your branch is up to date with 'origin/cursor/docs-watchface-transp-hier-net-gallery'. - -Changes not staged for commit: - (use "git add ..." to update what will be committed) - (use "git restore ..." to discard changes in working directory) - modified: BAILIAN_MCP_SUBMISSION_READY.md - modified: COZE_SUBMISSION_READY.md - modified: MCP_SO_SUBMISSION_READY.md - modified: SMITHERY_SUBMISSION_READY.md - modified: docs/html-visual-editor.md - -Untracked files: - (use "git add ..." to include in what will be committed) - "anthropic-skills\346\233\264\346\226\260.txt" - "smithery.ai\346\233\264\346\226\260.TXT" - -no changes added to commit (use "git add" and/or "git commit -a")