* Handle Matter accessory command (triggers user handlers) * This is for UI/external control that should invoke plugin handlers * Checks both external servers and bridge server
(uuid: string, cluster: string, attributes: Record<string, unknown>, partId?: string)
| 77 | * Checks both external servers and bridge server |
| 78 | */ |
| 79 | async handleTriggerCommand(uuid: string, cluster: string, attributes: Record<string, unknown>, partId?: string): Promise<void> { |
| 80 | // Map attributes to command using centralized mapper |
| 81 | const commandMapping = mapAttributesToCommand(cluster, attributes) |
| 82 | |
| 83 | // Debug logging — pass objects as %j format args so JSON.stringify only |
| 84 | // runs when debug is enabled (Logger.log short-circuits on level first). |
| 85 | log.debug(`handleTriggerCommand: uuid=${uuid}, cluster=${cluster}, partId=${partId}`) |
| 86 | log.debug('Attributes: %j', attributes) |
| 87 | if (commandMapping) { |
| 88 | log.debug('Command mapping: %j', commandMapping) |
| 89 | } else { |
| 90 | log.debug('Command mapping: null (state-only update)') |
| 91 | } |
| 92 | log.debug(`External servers count: ${this.externalMatterServers.size}`) |
| 93 | if (this.externalMatterServers.size > 0) { |
| 94 | // Pass the keys iterator as a %j arg so the spread+join only runs once |
| 95 | // util.format is reached (i.e. when debug is enabled). |
| 96 | log.debug('External server UUIDs: %j', [...this.externalMatterServers.keys()]) |
| 97 | } |
| 98 | |
| 99 | // Check if this is an external accessory first |
| 100 | const externalServer = this.externalMatterServers.get(uuid) |
| 101 | if (externalServer) { |
| 102 | log.debug(`Found external server for ${uuid}`) |
| 103 | if (commandMapping) { |
| 104 | // Explicit command invocation |
| 105 | await externalServer.triggerCommand(uuid, cluster, commandMapping.command, commandMapping.args, partId) |
| 106 | // After a command, read back the current state and notify so the UI updates |
| 107 | this.notifyCurrentState(externalServer, uuid, cluster, partId) |
| 108 | } else { |
| 109 | // State-only update (triggers change handlers automatically) |
| 110 | await externalServer.updateAccessoryState(uuid, cluster, attributes, partId) |
| 111 | } |
| 112 | return |
| 113 | } |
| 114 | |
| 115 | // Otherwise, try the bridge Matter server. If this bridge doesn't own the |
| 116 | // UUID, throw the routing sentinel rather than letting the StateManager |
| 117 | // throw a plain MatterDeviceError("Accessory ... not found or not |
| 118 | // registered") — otherwise control broadcasts from the UI emit a real |
| 119 | // error from every non-owner matter-enabled child bridge. |
| 120 | if (!this.matterServer || !this.matterServer.getAccessoryInfo(uuid)) { |
| 121 | log.debug(`Bridge does not own ${uuid}; signalling routing sentinel`) |
| 122 | throw new MatterAccessoryNotOnBridgeError(uuid) |
| 123 | } |
| 124 | |
| 125 | log.debug(`Trying matterServer for ${uuid}`) |
| 126 | if (commandMapping) { |
| 127 | // Explicit command invocation |
| 128 | await this.matterServer.triggerCommand(uuid, cluster, commandMapping.command, commandMapping.args, partId) |
| 129 | // After a command, read back the current state and notify so the UI updates |
| 130 | this.notifyCurrentState(this.matterServer, uuid, cluster, partId) |
| 131 | } else { |
| 132 | // State-only update (triggers change handlers automatically) |
| 133 | await this.matterServer.updateAccessoryState(uuid, cluster, attributes, partId) |
| 134 | } |
| 135 | } |
| 136 |
nothing calls this directly
no test coverage detected