From b4898583d761b702fc29de8533d32449dc11e732 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 5 Sep 2026 10:43:31 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BF=D0=BE=D0=B4=D1=81=D0=BA=D0=B0?= =?UTF-8?q?=D0=B7=D0=BA=D0=B0=20=D0=BA=20=D0=BD=D0=B0=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=B9=D0=BA=D0=B5=20=D1=81=D0=B2=D1=8F=D0=B7=D0=B5=D0=B9?= =?UTF-8?q?=20Zigbee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-Visible: yes Issue: #459 --- docs/CHANGELOG.md | 8 +++ docs/CHANGELOG.ru.md | 8 +++ docs/images/screenshots.json | 26 +++----- scripts/mutation-gate.mjs | 26 ++++++++ src/hp-zigbee-topology-settings.ts | 35 ++++++++-- src/i18n/topology.ts | 15 +++++ src/i18n/topology/de.json | 2 + src/i18n/topology/en.json | 2 + src/i18n/topology/fr.json | 2 + src/i18n/topology/ru.json | 2 + test/zigbee-topology.test.mjs | 103 +++++++++++++++++++++++++++++ 11 files changed, 210 insertions(+), 19 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4bdf51a4..b2191927 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,14 @@ incoming arrows identify devices behind a router, while a short bubble truthfully ends the path when the next node is in another space or is not placed ([#457](https://github.com/Matysh/houseplan-card/issues/457)). + +- The «Zigbee links» setting now carries contextual help: it explains the + feature in one breath, spells out the legend — line colour as link quality, + a dashed line as unreported quality, arrows as the way to the coordinator — + and states the limits: data comes from the last load and goes stale, and + links are shown with a mouse only + ([#459](https://github.com/Matysh/houseplan-card/issues/459)). + - Space settings can now create a room-free copy of the current space under a new name, preserving walls, openings, columns, Background objects, image transform, scale and display settings while leaving device placement behind; diff --git a/docs/CHANGELOG.ru.md b/docs/CHANGELOG.ru.md index c9251ff3..0a9426dc 100755 --- a/docs/CHANGELOG.ru.md +++ b/docs/CHANGELOG.ru.md @@ -12,6 +12,14 @@ координатора; входящие стрелки показывают устройства за роутером, а если следующий узел находится на другом плане или не размещён, короткая подпись честно завершает путь ([#457](https://github.com/Matysh/houseplan-card/issues/457)). + +- У настройки «Связи Zigbee» появилась контекстная подсказка: она коротко + объясняет функцию, разбирает легенду — цвет линии как качество связи, пунктир + как несообщённое качество, стрелки как путь к координатору — и называет + ограничения: данные из последней загрузки устаревают, а связи видны только + при работе мышью + ([#459](https://github.com/Matysh/houseplan-card/issues/459)). + - В настройках пространства теперь можно создать под новым именем копию без комнат: стены, проёмы, колонны, элементы подложки, положение фонового плана, масштаб и настройки отображения сохраняются, а размещение устройств не diff --git a/docs/images/screenshots.json b/docs/images/screenshots.json index 1c0d962e..8a15126c 100644 --- a/docs/images/screenshots.json +++ b/docs/images/screenshots.json @@ -3,7 +3,7 @@ "fixture": "synthetic-only", "chromium": "151.0.7922.34", "oxipng": "oxipng 10.2.0", - "sourceFingerprint": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceFingerprint": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "captureScriptSha256": "cadb8e1bcab9f1dcdd7d75b3b90ddcbaaeb2b8c2a098f575a21f39ff70f5c59c", "command": "npm run build && node demo/docs/capture.mjs", "scenarios": { @@ -15,7 +15,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "80a70361dc18dd0461568df332062e6482c633af5d280954f8b675701418a76d" }, "view-touch": { @@ -26,7 +26,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "4106cc28847047505f46921ff95765d8abdf5b382d9d17d4c5e4ad129dd8f6be" }, "space-create": { @@ -37,7 +37,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "617b51b3648498787b5039980c9f3eceb75ba56ed63a1a20e616bc05bc304362" }, "room-contour-close": { @@ -48,7 +48,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "1dab6cc3b9d1bf7d8c40f0e5137f8c688683c9b7eabc5167da99d41ecfdd5b79" }, "plan-context-tray": { @@ -59,7 +59,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "94ef50762753c0ac6ddc84d2521c4232a3f9ecd89843810d2df61c517592bed7" }, "device-editor": { @@ -70,7 +70,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "7a601769de38aa19c2e280f6ff4cf3695b854b1d1c6d1f8de4e6b5e8f7eb1559" }, "device-display-preview": { @@ -81,7 +81,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "0939875f8631694f4de0ef4fd01032010ede8c7d49fb7c0a857612d4b3afff93" }, "background-editor": { @@ -92,7 +92,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "054170fd9ef45762b602b4d5c9c3b9ea9724858be61af137970c246f485c13bb" }, "room-card": { @@ -103,7 +103,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "2ae4a58853d98e10d12456b2078ec2f6a0b597722c8310d7b016abd2bc42561e" }, "device-info": { @@ -114,12 +114,8 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "997a6ed2829850e0bd02ffbe6aaef8fdfc3a1e22b8828d7cbe207b426a694b3b", + "sourceSha256": "baf74cdb584726b1925079b3ee326edc1afc8b10f362bffb11be205cdab379d5", "imageSha256": "cb37f7eefd936f98ef44969b21dfe8e40ee27d1f558abe8d53c0223f6488b8a6" } - }, - "acceptedOn": "linux", - "acceptance": { - "lastWriteWasFingerprintOnly": true } } diff --git a/scripts/mutation-gate.mjs b/scripts/mutation-gate.mjs index 7bb9efe4..5b69f39d 100644 --- a/scripts/mutation-gate.mjs +++ b/scripts/mutation-gate.mjs @@ -5793,6 +5793,32 @@ const MUTANT_DEFINITIONS = [ replace: ' if (candidates.length >= 1) placements.set(node.key, {', }], }, + { + id: 'topology-help-renders-without-aria', + guard: 'node --test --test-name-pattern="кружок справки не рисуется|вызывается из блока" ' + + 'test/zigbee-topology.test.mjs', + because: 'a help circle that opens without an accessible name is worse than no circle, and ' + + 'topologyT answers a missing key with the key itself — so absence has to be asked of the ' + + 'dictionary, not of the resolved string (#459 AC2)', + patches: [{ + file: 'src/i18n/topology.ts', + find: ' const value = DICTIONARIES[lang]?.[key] ?? en[key];', + replace: ' const value = DICTIONARIES[lang]?.[key] ?? en[key] ?? key;', + }], + }, + { + id: 'topology-help-drops-arrow-legend', + guard: 'node --test --test-name-pattern="все шесть пунктов легенды|не путь пакета" ' + + 'test/zigbee-topology.test.mjs', + because: 'a hint that names only colour and dashes passes «the help exists» while answering ' + + 'none of the questions the arrows raise — the very content this task waited for #457 to ' + + 'write (#459 AC3, AC3b)', + patches: [{ + file: 'src/i18n/topology/ru.json', + find: ' Стрелка ведёт к следующему устройству по пути к координатору: исходящая одна, входящие — те, кто ходит через это устройство. Линия без стрелки — запасной сосед. Это дерево маршрутов, которое строит House Plan, а не путь пакета в эту секунду: подпись на конце стрелки значит, что цель не на этом плане, а отсутствие стрелки — что путь неизвестен.', + replace: '', + }], + }, { id: 'zigbee-topology-z2m-camelcase-node-rejected', guard: 'node --test --test-name-pattern="real anonymized camelCase" test/zigbee-topology.test.mjs', diff --git a/src/hp-zigbee-topology-settings.ts b/src/hp-zigbee-topology-settings.ts index 9f23ec7c..ddfe05f1 100644 --- a/src/hp-zigbee-topology-settings.ts +++ b/src/hp-zigbee-topology-settings.ts @@ -1,6 +1,7 @@ -import { LitElement, css, html, nothing, type PropertyValues } from 'lit'; +import { LitElement, css, html, nothing, type PropertyValues, type TemplateResult } from 'lit'; import { langOf } from './i18n'; -import { topologyT, type TopologyI18nKey } from './i18n/topology'; +import { hasTopologyTranslation, topologyT, type TopologyI18nKey } from './i18n/topology'; +import './hp-help'; import { normalizeZ2mBaseTopic, type ZigbeeTopologySettings, } from './zigbee-topology-settings'; @@ -35,7 +36,13 @@ export class HpZigbeeTopologySettings extends LitElement { static styles = css` :host { display: block; color: inherit; font: inherit; } - .section { margin-top: 18px; font-weight: 700; } + .section { + margin-top: 18px; + font-weight: 700; + display: flex; + align-items: center; + gap: 2px; + } .toggle { display: flex; align-items: center; gap: 10px; margin-top: 10px; } .hint, .status { color: var(--secondary-text-color, #9aa0aa); font-size: 12px; line-height: 1.45; } .hint { margin-top: 6px; } @@ -86,6 +93,26 @@ export class HpZigbeeTopologySettings extends LitElement { return topologyT(langOf(this.hass), key, vars); } + /** + * Contextual help for the feature, not for the checkbox (#459). + * + * `_help()` of the editor runtime is typed against the MAIN dictionary + * (`I18nKey`), and this component owns its own `topology` namespace — so the + * affordance is rebuilt here rather than imported. The fail-closed rule is + * copied deliberately: a missing text or accessible name renders nothing at + * all, because a help circle that opens an empty surface is worse than no + * circle. Absence is asked of the dictionaries (`hasTopologyTranslation`), + * not of the resolved string: `topologyT` answers a missing key with the key + * itself, and «help» is a perfectly non-empty string. + */ + private _help(): TemplateResult | typeof nothing { + const lang = langOf(this.hass); + if (!hasTopologyTranslation(lang, 'help') + || !hasTopologyTranslation(lang, 'help_aria')) return nothing; + return html``; + } + private async _ensureRuntime(): Promise { if (!this._runtime) { this._runtime = await import('./zigbee-topology-runtime'); @@ -157,7 +184,7 @@ export class HpZigbeeTopologySettings extends LitElement { const mayLoad = admin && this.savedEnabled; const topics = this._topics(); return html` -
${this._t('title')}
+
${this._t('title')}${this._help()}