feat: Command Response API integration with visual feedback#409
Draft
rusty-art wants to merge 1 commit intoNerivec:mainfrom
Draft
feat: Command Response API integration with visual feedback#409rusty-art wants to merge 1 commit intoNerivec:mainfrom
rusty-art wants to merge 1 commit intoNerivec:mainfrom
Conversation
Integrates the Zigbee2MQTT Command Response API to provide real-time visual feedback when users interact with device controls. - **StatusIndicator**: Visual dot showing command status (pending/confirmed/error) - **SyncRetryButton**: Retry failed commands with one click - **Sleepy device support**: Optimistic UI updates for battery-powered devices that may not respond immediately (queued commands) - `useCommandFeedback` hook: Minimal state machine for command tracking - `FeatureReadingContext`: React context for read operation state - WebSocketManager: Callback registration for transaction-based responses - src/components/editors/useCommandFeedback.ts - src/components/features/FeatureReadingContext.tsx - src/components/features/StatusIndicator.tsx - src/components/features/SyncRetryButton.tsx - Editors: ColorEditor, EnumEditor, RangeEditor, TextEditor - Features: Binary, Feature, FeatureSubFeatures, FeatureWrapper, Gradient, List, Numeric, Text - WebSocketManager: Transaction ID generation and callback system - store.ts: updateDeviceStates action for optimistic updates - types.ts: CommandResponse type definitions - StatusIndicator.test.tsx: Visual state rendering tests - syncRetryButton.logic.test.ts: Retry mechanism tests
5 tasks
Owner
|
Same problems as before, including the fact the PR tries to do far too much at once. The potential for issues/breaks is astronomical. (Same goes for the Z2M PR.) |
Author
|
understood. Will revert to bare-minimum; and replace with the zigbee2mqtt (backend) preferred MQTT topics approach in discussion. Thanks for your patience and guidance. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Integrates the Zigbee2MQTT Command Response API to provide real-time visual feedback when users interact with device controls.
Depends on: Backend PR #30774 (Command Response API)
Related: Backend feature request #30679
Features
StatusIndicator: Visual colored dot showing command status
SyncRetryButton: Retry failed commands with one click
Sleepy device support: Optimistic UI updates for battery-powered devices that may not respond immediately (queued commands)
Changes
New Files
src/components/editors/useCommandFeedback.ts- Minimal hook for command trackingsrc/components/features/FeatureReadingContext.tsx- React context for read statesrc/components/features/StatusIndicator.tsx- Visual status dot componentsrc/components/features/SyncRetryButton.tsx- Retry button componentModified
updateDeviceStatesaction for optimistic updatesTests
StatusIndicator.test.tsx- Visual state rendering tests (26 tests)syncRetryButton.logic.test.ts- Retry mechanism tests (34 tests)Test Plan
Implementation Details & Design Decisions
Frontend Feedback Mechanism - Functional Requirements
Context
The backend Command Response API (v2.0) provides:
zigbee2mqtt/[friendly_name]/response(unified for SET and GET)z2m.request_idechoed back from requestok,error,pending,partialtype: 'set' | 'get'to distinguish operationRelated:
Backend Response Structure
Critical from backend spec:
This confirms: for sleepy devices, we get ONE response with
pendingand then NO follow-up.Production Behavior by Component Type
Upstream (production) has inconsistent behavior across editor types and contexts:
currentValue)currentValue)Summary:
What the Backend Provides (SET operations)
/response){type:"set", status:"ok", z2m:{request_id:"X"}}{type:"set", status:"error", z2m:{request_id:"X"}, error:{...}}{type:"set", status:"partial", z2m:{request_id:"X"}, data:{...}, failed:{...}}{type:"set", status:"pending", z2m:{request_id:"X"}}The Sleepy Device Gap
From backend spec:
What this means:
status: "pending"Why backend can't send follow-up (technical explanation):
Zigbee DOES have a Transaction Sequence Number (TSN) - a 1-byte identifier (0-255) that the device echoes back in its ZCL response. However:
Indirect Transmission: For sleepy devices, the Coordinator sends to the Parent Router, which buffers the command (typically 7 seconds). When the device wakes and polls, it receives the command and sends back a ZCL response with the TSN.
Z2M Architecture: When
sendWhen: 'active'is triggered, thezigbee-herdsmanpromise resolves immediately with "queued" status. Z2M returnspendingwithfinal: trueand closes the transaction. The correlation ID is no longer tracked.When device responds: The ZCL response arrives (potentially minutes/hours later), but Z2M has no mapping from TSN → original
request_id. The state is updated, but there's no way to emit a correlated response.TSN wraparound: The TSN is only 1 byte (256 values), making long-term tracking impractical - it would wrap around on a busy network.
Bottom line: The Zigbee protocol supports tracking, but Z2M deliberately closes the transaction at
pendingto avoid holding resources indefinitely. This is a reasonable architectural decision.Our resolution:
status: pendingwithfinal: true: optimistically update UI, clear indicator after 2 secondsKnown Limitation: Restart While Command Queued
When a command is queued for a sleepy device (
pending + final), the frontend shows the user's requested values via optimistic updates. However, this state is in-memory only. If the frontend or backend restarts while a command is queued:state.db(which has old values)The queued command still executes when the device wakes, but the UI won't reflect the pending change after restart.
This is strictly better than upstream behavior, which showed no feedback at all for sleepy devices.
Design Decision: Decoupled Value and Status
The Two Independent Channels
Zigbee provides two separate sources of information:
Response Channel (
{device}/response) - Command outcome with correlation IDstatus: "ok"- Device accepted the ZCL commandstatus: "error"- Command failed (timeout, no route, ZCL error)status: "pending"- Queued for sleepy devicestatus: "partial"- Some attributes succeeded, some failedState Channel (
{device}) - Device truth (no correlation ID)The Decoupled Model
Instead of trying to detect conflicts, we keep value and status independent:
Why This Works:
Status Indicator Design
Visual: Colored dots (simple, consistent)
Accessibility: Tooltips on hover
Behavior Summary