Skip to content

feat(dashboard): show client-mode clusters on endpoint view - #952

Open
lboue wants to merge 8 commits into
matter-js:mainfrom
lboue:dashboard/show-client-clusters
Open

feat(dashboard): show client-mode clusters on endpoint view#952
lboue wants to merge 8 commits into
matter-js:mainfrom
lboue:dashboard/show-client-clusters

Conversation

@lboue

@lboue lboue commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Type of change

  • 🐛 Fix — corrects a defect or wrong behavior
  • Feature — adds new functionality or capability

Description

The endpoint cluster list in the dashboard was derived purely from cached attribute keys
(node.attributes), so client-mode clusters (an endpoint binds to a cluster hosted on another
device rather than hosting it itself) never appeared, even though the Descriptor's ClientList
attribute records them. Client clusters have no local attribute storage, so they were silently
invisible with no explicit filter excluding them — it was an emergent effect of the data
collection method.

Update after review: the first version of this PR merged ClientList cluster ids directly
into the Clusters list with a CLIENT badge, but as @Apollon77 pointed out,
those entries weren't clickable and did nothing when tapped. Reworked per that feedback:

  • Client-mode clusters are no longer added to the Clusters list.
  • Instead, matter-endpoint-view.ts shows a "Client Clusters" chip panel near the top of the
    page — but only when the endpoint also has a Binding cluster, since without one there's
    nothing to do with a client cluster anyway.
  • Clusters already covered by an existing binding entry (util/binding.ts's new
    boundClientClusterIds, cross-referencing the Binding cluster's binding attribute against
    the Descriptor's ClientList) are visually marked "Bound" vs. the rest.
  • The cluster detail view (matter-cluster-view.ts) still shows a short notice explaining why a
    client-mode cluster's attribute list is empty, for anyone navigating there directly.

Backing evidence

Not applicable — this is a feature addition, not a fix for a defect.

  • Related issue: #
  • This fix/change is backed by a complete log file — attached here or in the linked issue (not just a few quoted lines).

Checklist

  • Tests added or updated to cover the change
  • npm test passes
  • npm run format-verify and npm run lint pass
  • CHANGELOG updated (if applicable)

Manually verified against a running server: endpoint 38 (Thermostat Controller) has
ServerList = [29, 57, 30] and ClientList = [513] (Thermostat). After this change, the
Clusters list no longer shows the Thermostat client cluster; instead, because the endpoint has
the Binding cluster (30), a "Client Clusters" panel appears showing the Thermostat cluster as a
chip, marked "Bound" once a binding entry targets it.

No new automated tests were added for this UI change; verification was manual (see above) plus
the existing dashboard test suite (153/153 passing).

🤖 Generated with Claude Code

lboue and others added 2 commits July 30, 2026 21:51
Endpoint cluster lists were derived purely from cached attribute keys,
so client-mode clusters (bound to a remote device, no local attribute
storage) never appeared even though the Descriptor's ClientList carries
them. Merge ClientList cluster ids into the endpoint listing with a
CLIENT badge, and note in the cluster detail view why a client-mode
cluster has no attributes to show.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@lboue
lboue marked this pull request as ready for review July 30, 2026 19:59
@Apollon77

Copy link
Copy Markdown
Collaborator

@lboue Hm, honestly I do not really see the benefit of having the Client clusters also in the Cluster list, You can not click on then and basically nothing happens with them.
What about listing them. in the section on top of the page where we also list Tags and features? Kind of a list as "chips" list wit "Client clusters" ... I would also maybe only list them when the EP also has a bindings cluster because if not you can not do anything with them anyway. And if we would like to be very convenient we can read the bindings and mark the Cluster clients that are "Bound" somehow differently then the others.

@Apollon77 Apollon77 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see last comment

lboue added 2 commits July 31, 2026 21:53
Per review feedback on the endpoint view: client-mode clusters were
unclickable dead entries in the Clusters list. Show them instead as
chips in a panel near the top, only when the endpoint has a Binding
cluster, and mark clusters already covered by an existing binding as
"Bound".
@lboue
lboue requested a review from Apollon77 July 31, 2026 20:00
@lboue

lboue commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

@Apollon77 Good points, thanks. Reworked in fd7ce21:

  • Client-mode clusters are no longer merged into the Clusters list (the unclickable CLIENT badge entries are gone).
  • They now show as chips in a new "Client Clusters" panel near the top of the endpoint view, and only when the endpoint also has a Binding cluster — if there's no Binding cluster, the panel doesn't render at all.
  • Clusters already covered by an existing binding entry are marked "Bound" (cross-referencing the Binding cluster's binding attribute against the Descriptor's ClientList, in boundClientClusterIds()).

Verified manually against the same Thermostat Controller endpoint (ServerList = [29, 57, 30], ClientList = [513]): the Thermostat cluster no longer appears in the Clusters list, and shows up as a chip in the Client Clusters panel, marked "Bound" once a binding targets it.

lboue and others added 4 commits July 31, 2026 22:10
…er chip

--md-sys-color-tertiary-container is never defined in public/index.html
(light or dark), so the "Bound" chip rendered with no background/text
color at all. Switch to --md-sys-color-primary-container, which is
defined for both themes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
npm run format now wraps these template expressions differently since
the formatting rules changed upstream (merged from main); re-running
the required format step touched these files with no logic change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Local node_modules had a stale oxfmt 0.59.0 binary that formatted
differently from the ^0.61.0 pinned in package.json, so CI's
format-verify (installing fresh from the lockfile) flagged 31 files
that passed locally. Reinstalling and reformatting syncs the tree
with the pinned version.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Explicit attribute reads are not fabric-filtered, so the cached binding
attribute can hold other fabrics' entries. boundClientClusterIds now filters
on the node's CurrentFabricIndex, mirroring freshOurBindings and the ACL
helpers; without it a second ecosystem's binding — or its cluster-less entry —
marked client clusters Bound as soon as the binding editor's read landed.

A cluster-less entry now unions with the cluster-specific ones instead of
replacing them, and an entry targeting neither Group nor Endpoint no longer
expands to every client cluster.

Drops the client-only notice from the cluster view: nothing links to a
client-mode cluster page, so it was unreachable except by hand-typed URL.
Extracts the duplicated info-panel/chip CSS into shared-styles, adds the
light-theme --md-sys-color-on-secondary-container the chips rely on, widens
the endpoint view's guard dependencies so the cluster list tracks the viewed
endpoint, and states Bound/Not bound as text rather than colour alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 1, 2026 10:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds dashboard visibility for client-mode clusters (Descriptor ClientList) on the endpoint page, and refactors shared “info panel” styling so both endpoint/cluster views can reuse it. This improves UX for controller-style endpoints that only consume clusters via bindings (and therefore have no local attribute cache for those clusters).

Changes:

  • Add boundClientClusterIds() to compute which client clusters are already covered by Binding entries (with new unit tests).
  • Add reusable infoPanelStyles and apply it to cluster/endpoint views.
  • Update endpoint view rendering/guarding and add a “Client Clusters” chip panel (plus related theme variable and changelog entry).

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/dashboard/test/BindingUtilTest.ts Adds unit tests for boundClientClusterIds() behavior across fabric filtering and wildcard bindings.
packages/dashboard/src/util/shared-styles.ts Introduces reusable infoPanelStyles for bordered info panels and chips.
packages/dashboard/src/util/binding.ts Adds BINDING_CLUSTER_ID constant and boundClientClusterIds() helper; refactors list parsing.
packages/dashboard/src/pages/matter-endpoint-view.ts Adds “Client Clusters” panel and refactors cluster list rendering/guarding; uses shared panel styles.
packages/dashboard/src/pages/matter-cluster-view.ts Switches inline panel/chip CSS to shared infoPanelStyles.
packages/dashboard/src/pages/cluster-commands/clusters/binding-commands.ts Uses BINDING_CLUSTER_ID for consistency and tweaks doc comment.
packages/dashboard/public/index.html Adds missing theme token --md-sys-color-on-secondary-container for chip text color in light theme.
CHANGELOG.md Documents the new endpoint “Client Clusters” panel and a claimed cluster list refresh fix.

Comment on lines +71 to +81
export function boundClientClusterIds(node: MatterNode, endpoint: number): Set<number> {
const fabricIndex = nodeFabricIndex(node);
const bindings = readBindings(node, endpoint).filter(
b => fabricIndex === undefined || b.fabricIndex === fabricIndex,
);
const bound = new Set(bindings.map(b => b.cluster).filter((c): c is number => c !== undefined));
if (bindings.some(b => b.cluster === undefined && (b.group !== undefined || b.endpoint !== undefined))) {
for (const cluster of sourceClientClusters(node, endpoint)) bound.add(cluster);
}
return bound;
}
Comment on lines +23 to 26
export const BINDING_CLUSTER_ID = 30;

const BINDING_KEY_RE = /^(\d+)\/30\/0$/;

Comment on lines +128 to 142
${guard(
[nodeId, this.endpoint, this.node.attributes, Object.keys(this.node.attributes).length],
() =>
endpointClusters.map(cluster => {
return html`
<md-list-item type="link" href=${`#node/${nodeId}/${this.endpoint}/${cluster}`}>
<div slot="headline">
${clusters[cluster]?.label ?? "Custom/Unknown Cluster"}
</div>
<div slot="supporting-text">ClusterId ${cluster} (${formatHex(cluster)})</div>
<ha-svg-icon slot="end" .path=${mdiChevronRight}></ha-svg-icon>
</md-list-item>
`;
}),
)}
Comment thread CHANGELOG.md
- Enhancement: Introduces Websocket Schema version 13 (backward compatible)
- (MindFreeze) Adds Websocket command `get_network_topology` and event `network_topology_updated` to expose the Thread and WiFi network details for external visualization
- Feature: (lboue) Dashboard endpoint view shows a "Client Clusters" panel listing the Descriptor's ClientList clusters when the endpoint has a Binding cluster, marking each as "Bound" or "Not bound"
- Fix: Dashboard endpoint view refreshes its cluster list when a cluster appears or disappears on the node, or the viewed endpoint changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants