@homebridge-plugins/homebridge-matter 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/MATTER_API.md +1189 -182
  3. package/README.md +1 -1
  4. package/dist/devices/section-12-robotic/robotic-vacuum-cleaner.d.ts +9 -53
  5. package/dist/devices/section-12-robotic/robotic-vacuum-cleaner.js +12 -62
  6. package/dist/devices/section-12-robotic/robotic-vacuum-cleaner.js.map +1 -1
  7. package/dist/devices/section-4-lighting/color-temperature-light.d.ts +8 -1
  8. package/dist/devices/section-4-lighting/color-temperature-light.js +27 -23
  9. package/dist/devices/section-4-lighting/color-temperature-light.js.map +1 -1
  10. package/dist/devices/section-4-lighting/dimmable-light.d.ts +5 -22
  11. package/dist/devices/section-4-lighting/dimmable-light.js +40 -319
  12. package/dist/devices/section-4-lighting/dimmable-light.js.map +1 -1
  13. package/dist/devices/section-4-lighting/extended-color-light.d.ts +6 -25
  14. package/dist/devices/section-4-lighting/extended-color-light.js +33 -399
  15. package/dist/devices/section-4-lighting/extended-color-light.js.map +1 -1
  16. package/dist/devices/section-4-lighting/on-off-light.d.ts +8 -28
  17. package/dist/devices/section-4-lighting/on-off-light.js +64 -434
  18. package/dist/devices/section-4-lighting/on-off-light.js.map +1 -1
  19. package/dist/devices/section-5-smart-plugs/dimmable-plug-in-unit.d.ts +6 -0
  20. package/dist/devices/section-5-smart-plugs/dimmable-plug-in-unit.js +13 -3
  21. package/dist/devices/section-5-smart-plugs/dimmable-plug-in-unit.js.map +1 -1
  22. package/dist/devices/section-5-smart-plugs/on-off-plug-in-unit.d.ts +5 -0
  23. package/dist/devices/section-5-smart-plugs/on-off-plug-in-unit.js +9 -2
  24. package/dist/devices/section-5-smart-plugs/on-off-plug-in-unit.js.map +1 -1
  25. package/dist/devices/section-6-switches/on-off-light-switch.d.ts +5 -0
  26. package/dist/devices/section-6-switches/on-off-light-switch.js +19 -5
  27. package/dist/devices/section-6-switches/on-off-light-switch.js.map +1 -1
  28. package/dist/devices/section-7-sensors/contact-sensor.d.ts +5 -0
  29. package/dist/devices/section-7-sensors/contact-sensor.js +5 -0
  30. package/dist/devices/section-7-sensors/contact-sensor.js.map +1 -1
  31. package/dist/devices/section-7-sensors/humidity-sensor.d.ts +5 -0
  32. package/dist/devices/section-7-sensors/humidity-sensor.js +5 -0
  33. package/dist/devices/section-7-sensors/humidity-sensor.js.map +1 -1
  34. package/dist/devices/section-7-sensors/light-sensor.d.ts +5 -0
  35. package/dist/devices/section-7-sensors/light-sensor.js +5 -0
  36. package/dist/devices/section-7-sensors/light-sensor.js.map +1 -1
  37. package/dist/devices/section-7-sensors/occupancy-sensor.d.ts +6 -0
  38. package/dist/devices/section-7-sensors/occupancy-sensor.js +6 -0
  39. package/dist/devices/section-7-sensors/occupancy-sensor.js.map +1 -1
  40. package/dist/devices/section-7-sensors/smoke-co-alarm.d.ts +6 -0
  41. package/dist/devices/section-7-sensors/smoke-co-alarm.js +6 -0
  42. package/dist/devices/section-7-sensors/smoke-co-alarm.js.map +1 -1
  43. package/dist/devices/section-7-sensors/temperature-sensor.d.ts +5 -0
  44. package/dist/devices/section-7-sensors/temperature-sensor.js +5 -0
  45. package/dist/devices/section-7-sensors/temperature-sensor.js.map +1 -1
  46. package/dist/devices/section-7-sensors/water-leak-detector.d.ts +5 -0
  47. package/dist/devices/section-7-sensors/water-leak-detector.js +5 -0
  48. package/dist/devices/section-7-sensors/water-leak-detector.js.map +1 -1
  49. package/dist/devices/section-8-closure/door-lock.d.ts +6 -0
  50. package/dist/devices/section-8-closure/door-lock.js +12 -27
  51. package/dist/devices/section-8-closure/door-lock.js.map +1 -1
  52. package/dist/devices/section-8-closure/window-covering.d.ts +7 -0
  53. package/dist/devices/section-8-closure/window-covering.js +27 -43
  54. package/dist/devices/section-8-closure/window-covering.js.map +1 -1
  55. package/dist/devices/section-9-hvac/fan.d.ts +7 -0
  56. package/dist/devices/section-9-hvac/fan.js +17 -23
  57. package/dist/devices/section-9-hvac/fan.js.map +1 -1
  58. package/dist/devices/section-9-hvac/thermostat.d.ts +7 -0
  59. package/dist/devices/section-9-hvac/thermostat.js +21 -25
  60. package/dist/devices/section-9-hvac/thermostat.js.map +1 -1
  61. package/package.json +3 -3
package/MATTER_API.md CHANGED
@@ -13,7 +13,8 @@ This document serves as a comprehensive guide for integrating Matter devices int
13
13
  5. [Monitoring External Changes](#monitoring-external-changes)
14
14
  6. [Using Matter Types](#using-matter-types)
15
15
  7. [Best Practices](#best-practices)
16
- 8. [Device Reference](#device-reference)
16
+ 8. [API Reference](#api-reference)
17
+ 9. [Device Reference](#device-reference)
17
18
 
18
19
  ---
19
20
 
@@ -137,6 +138,85 @@ handlers: {
137
138
 
138
139
  The command-based approach is more explicit about the user's intent and can provide additional context (like transition time for smooth dimming).
139
140
 
141
+ #### Type-Safe Handler Arguments with MatterRequests
142
+
143
+ For commands with parameters, import `MatterRequests` for TypeScript autocomplete and type checking:
144
+
145
+ ```typescript
146
+ import type { MatterRequests } from 'homebridge'
147
+
148
+ handlers: {
149
+ levelControl: {
150
+ moveToLevelWithOnOff: async (request: MatterRequests.MoveToLevel) => {
151
+ const { level, transitionTime } = request // Fully typed!
152
+ await device.setBrightness(level)
153
+ },
154
+ },
155
+ }
156
+ ```
157
+
158
+ **Quick Examples:**
159
+ - `MatterRequests.MoveToLevel` - Brightness control
160
+ - `MatterRequests.MoveToHueAndSaturation` - Color control
161
+ - `MatterRequests.LockDoor` - Door lock with optional PIN
162
+ - `MatterRequests.SetpointRaiseLower` - Thermostat temperature
163
+
164
+ **Why use this?**
165
+ ✅ Autocomplete shows available properties
166
+ ✅ Compile-time errors for typos
167
+ ✅ Hover to see parameter types
168
+
169
+ **Note:** TypeScript types only - not available on `api.matter` at runtime.
170
+
171
+ <details>
172
+ <summary><strong>Click to see all MatterRequests types</strong></summary>
173
+
174
+ #### Level Control
175
+ ```typescript
176
+ MatterRequests.MoveToLevel // { level, transitionTime?, optionsMask?, optionsOverride? }
177
+ MatterRequests.Move // { moveMode, rate?, optionsMask?, optionsOverride? }
178
+ MatterRequests.Step // { stepMode, stepSize, transitionTime?, ... }
179
+ MatterRequests.Stop // { optionsMask?, optionsOverride? }
180
+ ```
181
+
182
+ #### Color Control
183
+ ```typescript
184
+ MatterRequests.MoveToHue // { hue, direction, transitionTime?, ... }
185
+ MatterRequests.MoveToSaturation // { saturation, transitionTime?, ... }
186
+ MatterRequests.MoveToHueAndSaturation // { hue, saturation, transitionTime?, ... }
187
+ MatterRequests.MoveToColorTemperature // { colorTemperatureMireds, transitionTime?, ... }
188
+ MatterRequests.MoveHue // { moveMode, rate?, ... }
189
+ MatterRequests.MoveSaturation // { moveMode, rate?, ... }
190
+ MatterRequests.MoveColorTemperature // { moveMode, rate?, ... }
191
+ MatterRequests.StepHue // { stepMode, stepSize, transitionTime?, ... }
192
+ MatterRequests.StepSaturation // { stepMode, stepSize, transitionTime?, ... }
193
+ MatterRequests.StepColorTemperature // { stepMode, stepSize, transitionTime?, ... }
194
+ ```
195
+
196
+ #### Door Lock
197
+ ```typescript
198
+ MatterRequests.LockDoor // { pinCode? }
199
+ MatterRequests.UnlockDoor // { pinCode? }
200
+ ```
201
+
202
+ #### Window Covering
203
+ ```typescript
204
+ MatterRequests.GoToLiftPercentage // { liftPercent100thsValue }
205
+ MatterRequests.GoToTiltPercentage // { tiltPercent100thsValue }
206
+ ```
207
+
208
+ #### Thermostat
209
+ ```typescript
210
+ MatterRequests.SetpointRaiseLower // { mode, amount }
211
+ ```
212
+
213
+ #### Fan Control
214
+ ```typescript
215
+ MatterRequests.FanStep // { direction, wrap?, lowestOff? }
216
+ ```
217
+
218
+ </details>
219
+
140
220
  ### Endpoints vs Clusters (HAP: Accessories vs Services)
141
221
 
142
222
  Understanding the relationship between endpoints and clusters is fundamental to Matter architecture.
@@ -197,7 +277,7 @@ const accessory = {
197
277
 
198
278
  // These are CLUSTERS within the endpoint
199
279
  clusters: {
200
- onOff: { onOff: false }, // OnOff cluster
280
+ onOff: { onOff: false }, // OnOff cluster
201
281
  levelControl: { currentLevel: 127 }, // LevelControl cluster
202
282
  },
203
283
  }
@@ -208,7 +288,9 @@ const accessory = {
208
288
  - The `clusters` object defines which clusters (capabilities) that endpoint has
209
289
  - The device type determines which clusters are required/optional for that endpoint
210
290
 
211
- ### HomeKit vs Matter
291
+ #### HAP vs Matter: Detailed Comparison
292
+
293
+ For HAP developers, here's how common patterns translate:
212
294
 
213
295
  | Aspect | HAP | Matter |
214
296
  |---------------------------|-------------------------------------------------------|----------------------------------------------------------|
@@ -298,6 +380,8 @@ Your physical device is **not** a Matter device—it's a regular IoT device (HTT
298
380
 
299
381
  ## Implementing Matter Devices
300
382
 
383
+ Now that you understand the core concepts and two-way flow architecture, let's implement a Matter device.
384
+
301
385
  ### Basic Structure
302
386
 
303
387
  Every Matter device registration follows this pattern:
@@ -486,6 +570,8 @@ clusters: {
486
570
 
487
571
  ## Reading and Updating State
488
572
 
573
+ After implementing your device, you'll need to read and update its state for both Flow A (handlers) and Flow B (external changes).
574
+
489
575
  ### Reading Current State
490
576
 
491
577
  There are two ways to read cluster state:
@@ -496,15 +582,15 @@ Access cluster attributes directly from the accessory object:
496
582
 
497
583
  ```typescript
498
584
  // Read power state
499
- const isOn = accessory.clusters.onOff.onOff // boolean
585
+ const isOn = accessory.clusters.onOff.onOff // boolean
500
586
 
501
587
  // Read brightness
502
- const level = accessory.clusters.levelControl.currentLevel // 1-254
503
- const percent = Math.round((level / 254) * 100) // Convert to percentage
588
+ const level = accessory.clusters.levelControl.currentLevel // 1-254
589
+ const percent = Math.round((level / 254) * 100) // Convert to percentage
504
590
 
505
591
  // Read color
506
- const hue = accessory.clusters.colorControl.currentHue // 0-254
507
- const saturation = accessory.clusters.colorControl.currentSaturation // 0-254
592
+ const hue = accessory.clusters.colorControl.currentHue // 0-254
593
+ const saturation = accessory.clusters.colorControl.currentSaturation // 0-254
508
594
  ```
509
595
 
510
596
  **When to use**: This is the recommended approach in most cases when you have a reference to the accessory object.
@@ -523,13 +609,13 @@ Use the API method when you don't have a reference to the accessory object:
523
609
  // Read state by UUID
524
610
  const state = api.matter.getAccessoryState(uuid, api.matter.clusterNames.OnOff)
525
611
  if (state) {
526
- const isOn = state.onOff // boolean
612
+ const isOn = state.onOff // boolean
527
613
  }
528
614
 
529
615
  // Read brightness
530
616
  const levelState = api.matter.getAccessoryState(uuid, api.matter.clusterNames.LevelControl)
531
617
  if (levelState) {
532
- const level = levelState.currentLevel // 1-254
618
+ const level = levelState.currentLevel // 1-254
533
619
  }
534
620
  ```
535
621
 
@@ -547,9 +633,9 @@ Use `updateAccessoryState()` to manually update cluster attributes:
547
633
 
548
634
  ```typescript
549
635
  api.matter.updateAccessoryState(
550
- accessory.uuid, // UUID of the accessory
551
- api.matter.clusterNames.OnOff, // Cluster name (use constants!)
552
- { onOff: true } // New attribute values
636
+ accessory.uuid, // UUID of the accessory
637
+ api.matter.clusterNames.OnOff, // Cluster name (use constants!)
638
+ { onOff: true } // New attribute values
553
639
  )
554
640
  ```
555
641
 
@@ -690,7 +776,9 @@ api.matter.updateAccessoryState(
690
776
 
691
777
  ## Monitoring External Changes
692
778
 
693
- You must monitor your physical device to detect external changes (Flow B). There are two approaches:
779
+ To implement Flow B (physical device → Home app), you must monitor your physical device and call `updateAccessoryState()` when it changes externally.
780
+
781
+ There are two approaches:
694
782
 
695
783
  ### Recommended: Event-Based Updates
696
784
 
@@ -823,7 +911,7 @@ setInterval(async () => {
823
911
  } catch (error) {
824
912
  log.error(`Error polling device: ${error}`)
825
913
  }
826
- }, 5000) // Poll every 5 seconds
914
+ }, 5000) // Poll every 5 seconds
827
915
  ```
828
916
 
829
917
  ---
@@ -884,7 +972,7 @@ api.matter.types.TemperatureMeasurement
884
972
 
885
973
  ---
886
974
 
887
- ## Best Practices - WE ARE UP TO HERE!
975
+ ## Best Practices
888
976
 
889
977
  ### 1. Always Compare Before Updating
890
978
 
@@ -901,27 +989,39 @@ if (newState !== currentState) {
901
989
 
902
990
  Whenever possible, use event-based updates (MQTT, WebSocket, webhooks) instead of polling for better performance and user experience.
903
991
 
904
- ### 3. Never Update in Handlers (Flow A)
992
+ ### 3. Don't Update the Same Attribute in Handlers
905
993
 
906
- Handlers automatically update Matter state. Only use `updateAccessoryState()` for external changes (Flow B).
994
+ Handlers automatically update their own attribute. Only manually update OTHER attributes (side effects) or for external changes (Flow B).
907
995
 
908
996
  ```typescript
909
- // ❌ WRONG
997
+ // ❌ WRONG - Redundant update
910
998
  handlers: {
911
999
  onOff: {
912
1000
  on: async () => {
913
1001
  await myDevice.turnOn()
914
- api.matter.updateAccessoryState(...) // Don't do this!
1002
+ api.matter.updateAccessoryState(uuid, api.matter.clusterNames.OnOff, { onOff: true })
1003
+ // Redundant! onOff is already updated automatically
915
1004
  }
916
1005
  }
917
1006
  }
918
1007
 
919
- // ✅ CORRECT
1008
+ // ✅ CORRECT - No manual update needed
1009
+ handlers: {
1010
+ onOff: {
1011
+ on: async () => {
1012
+ await myDevice.turnOn()
1013
+ // State automatically updated
1014
+ }
1015
+ }
1016
+ }
1017
+
1018
+ // ✅ ALSO CORRECT - Update different attribute as side effect
920
1019
  handlers: {
921
1020
  onOff: {
922
1021
  on: async () => {
923
1022
  await myDevice.turnOn()
924
- // State is automatically updated
1023
+ // Light resets to 100% when turned on, so update brightness too
1024
+ api.matter.updateAccessoryState(uuid, api.matter.clusterNames.LevelControl, { currentLevel: 254 })
925
1025
  }
926
1026
  }
927
1027
  }
@@ -929,29 +1029,55 @@ handlers: {
929
1029
 
930
1030
  ### 4. Handle Errors Gracefully
931
1031
 
932
- Always wrap device communication in try-catch blocks:
1032
+ Always throw errors from handlers so the Home app can be notified of failures:
933
1033
 
934
1034
  ```typescript
935
- try {
936
- await myDevice.turnOn()
937
- } catch (error) {
938
- log.error(`Failed to control device: ${error}`)
939
- throw error // Let Homebridge handle the error
1035
+ handlers: {
1036
+ onOff: {
1037
+ on: async () => {
1038
+ try {
1039
+ // Control your physical device
1040
+ await myDevice.turnOn()
1041
+ log.info('Successfully turned on device')
1042
+ } catch (error) {
1043
+ // Log the error for debugging
1044
+ log.error(`Failed to turn on device: ${error}`)
1045
+
1046
+ // ✅ IMPORTANT: Re-throw the error
1047
+ // This propagates the error to the Matter protocol, which:
1048
+ // 1. Notifies the Home app that the command failed
1049
+ // 2. Prevents state from updating incorrectly
1050
+ // 3. Shows an error message to the user
1051
+ throw error
1052
+ }
1053
+ },
1054
+
1055
+ off: async () => {
1056
+ try {
1057
+ await myDevice.turnOff()
1058
+ log.info('Successfully turned off device')
1059
+ } catch (error) {
1060
+ log.error(`Failed to turn off device: ${error}`)
1061
+ throw error // Always re-throw!
1062
+ }
1063
+ },
1064
+ },
940
1065
  }
941
1066
  ```
942
1067
 
943
- ### 5. Reconnect on Connection Loss
1068
+ **Why throw errors?**
944
1069
 
945
- For event-based monitoring, implement automatic reconnection:
1070
+ Without throwing:
1071
+ - ❌ Home app thinks command succeeded
1072
+ - ❌ State updates incorrectly (shows "on" when device is actually off)
1073
+ - ❌ User has no feedback that something went wrong
946
1074
 
947
- ```typescript
948
- ws.on('close', () => {
949
- log.warn('Connection lost, reconnecting...')
950
- setTimeout(() => reconnect(), 5000)
951
- })
952
- ```
1075
+ With throwing:
1076
+ - Home app displays error to user
1077
+ - State remains unchanged (accurate)
1078
+ - User knows to try again or investigate the issue
953
1079
 
954
- ### 6. Log Clearly
1080
+ ### 5. Log Clearly
955
1081
 
956
1082
  Distinguish between the two flows in your logs:
957
1083
 
@@ -960,7 +1086,7 @@ log.info('[Device] Home app → Physical device: Turning ON')
960
1086
  log.info('[Device] Physical device → Home app: State changed to ON')
961
1087
  ```
962
1088
 
963
- ### 7. Use Cluster Name Constants
1089
+ ### 6. Use Cluster Name Constants
964
1090
 
965
1091
  Always use `api.matter.clusterNames.*` constants instead of strings:
966
1092
 
@@ -972,225 +1098,1106 @@ api.matter.updateAccessoryState(uuid, api.matter.clusterNames.OnOff, {...})
972
1098
  api.matter.updateAccessoryState(uuid, 'onOff', {...})
973
1099
  ```
974
1100
 
975
- ### 8. Store Connection Objects for Cleanup
1101
+ ---
976
1102
 
977
- Keep references to connections for proper cleanup when plugin stops:
1103
+ ## API Reference
978
1104
 
979
- ```typescript
980
- let mqttClient: mqtt.MqttClient
981
- let wsConnection: WebSocket
1105
+ Complete reference for all Matter API methods and properties available in Homebridge.
982
1106
 
983
- // Clean up on plugin shutdown
984
- context.api.on('shutdown', () => {
985
- mqttClient?.end()
986
- wsConnection?.close()
987
- })
1107
+ ### Platform API Methods
1108
+
1109
+ #### `api.isMatterAvailable(): boolean`
1110
+
1111
+ Check if Matter is available in the current version of Homebridge.
1112
+
1113
+ **Returns:** `true` if Homebridge version is >= 2.0.0-alpha.0
1114
+
1115
+ **Usage:**
1116
+ ```typescript
1117
+ if (api.isMatterAvailable()) {
1118
+ log.info('Matter is available in this Homebridge version')
1119
+ } else {
1120
+ log.warn('Matter requires Homebridge >= 2.0.0-alpha.0')
1121
+ }
988
1122
  ```
989
1123
 
1124
+ **When to use:**
1125
+ - Plugin compatibility checks
1126
+ - Conditional feature loading
1127
+ - Version-specific functionality
1128
+
990
1129
  ---
991
1130
 
992
- ## Device Reference
1131
+ #### `api.isMatterEnabled(): boolean`
993
1132
 
994
- This section documents all available Matter device types with their clusters, attributes, handlers, and usage examples.
1133
+ Check if Matter is enabled for this bridge instance.
995
1134
 
996
- ### On/Off Light
1135
+ **Returns:** `true` if Matter is enabled in the bridge configuration
997
1136
 
998
- **Device Type**: `api.matter.deviceTypes.OnOffLight`
1137
+ **Configuration:**
1138
+ - For main bridge: Set `bridge.matter = true` in config.json
1139
+ - For child bridge: Set `_bridge.matter = true` in platform config
999
1140
 
1000
- **Description**: A lighting device capable of being switched on or off.
1141
+ **Usage:**
1142
+ ```typescript
1143
+ if (api.isMatterEnabled()) {
1144
+ // Register Matter accessories
1145
+ api.matter.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessories)
1146
+ } else {
1147
+ log.info('Matter is not enabled for this bridge')
1148
+ }
1149
+ ```
1001
1150
 
1002
- **Matter Specification**: § 4.1
1151
+ **When to use:**
1152
+ - Runtime checks before registering Matter accessories
1153
+ - Conditional accessory registration
1154
+ - User feedback about Matter status
1003
1155
 
1004
- #### Required Clusters
1156
+ ---
1005
1157
 
1006
- ##### OnOff Cluster
1158
+ ### Matter API Properties
1007
1159
 
1008
- Controls the power state of the light.
1160
+ All properties are accessed via `api.matter.*`
1009
1161
 
1010
- **Attributes**:
1162
+ #### `api.matter.uuid`
1011
1163
 
1012
- | Attribute | Type | Range/Values | Description |
1013
- |-----------|---------|-----------------|--------------------------------|
1014
- | `onOff` | boolean | `true`, `false` | Power state (true=on, false=off) |
1164
+ UUID generator for creating unique accessory identifiers (alias of `api.hap.uuid`).
1165
+
1166
+ **Type:** `HAP['uuid']`
1015
1167
 
1016
- **Handlers**:
1168
+ **Methods:**
1169
+ - `generate(data: string): string` - Generate deterministic UUID from string
1170
+ - `isValid(uuid: string): boolean` - Validate UUID format
1017
1171
 
1172
+ **Usage:**
1018
1173
  ```typescript
1019
- handlers: {
1020
- onOff: {
1021
- /**
1022
- * Called when user turns light ON via Home app
1023
- */
1024
- on: async () => {
1025
- // Control your physical device
1026
- await myLightAPI.turnOn()
1027
- // State automatically updated by Homebridge
1028
- },
1174
+ const uuid = api.matter.uuid.generate('my-light-123')
1175
+ // Output: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
1029
1176
 
1030
- /**
1031
- * Called when user turns light OFF via Home app
1032
- */
1033
- off: async () => {
1034
- // Control your physical device
1035
- await myLightAPI.turnOff()
1036
- // State automatically updated by Homebridge
1037
- },
1038
- },
1177
+ if (api.matter.uuid.isValid(uuid)) {
1178
+ // UUID is valid
1039
1179
  }
1040
1180
  ```
1041
1181
 
1042
- #### Optional Clusters
1182
+ **Important:**
1183
+ - UUIDs must be deterministic (same input = same output)
1184
+ - Use unique identifiers (device ID, MAC address, etc.)
1185
+ - UUIDs persist across restarts for state restoration
1043
1186
 
1044
- - **LevelControl**: Adds brightness control (see Dimmable Light)
1045
- - **ScenesManagement**: Enables scene support
1046
- - **Groups**: Enables grouping with other devices
1047
- - **OccupancySensing**: Can respond to occupancy sensors
1187
+ ---
1048
1188
 
1049
- #### Complete Example
1189
+ #### `api.matter.deviceTypes`
1050
1190
 
1051
- ```typescript
1052
- const accessory = {
1053
- uuid: api.matter.uuid.generate('onoff-light-001'),
1054
- displayName: 'On/Off Light',
1055
- deviceType: api.matter.deviceTypes.OnOffLight,
1056
- serialNumber: 'LIGHT-001',
1057
- manufacturer: 'My Company',
1058
- model: 'OnOffLight v1',
1191
+ Available Matter device types for creating accessories.
1059
1192
 
1060
- clusters: {
1061
- onOff: {
1062
- onOff: false, // Initial state: off
1063
- },
1064
- },
1193
+ **Type:** `typeof deviceTypes` (from Matter.js)
1065
1194
 
1066
- handlers: {
1067
- onOff: {
1068
- on: async () => {
1069
- log.info('[Light] Turning ON')
1070
- await myLightAPI.turnOn()
1071
- },
1072
- off: async () => {
1073
- log.info('[Light] Turning OFF')
1074
- await myLightAPI.turnOff()
1075
- },
1076
- },
1077
- },
1195
+ **Common Device Types:**
1196
+ - Lighting: `OnOffLight`, `DimmableLight`, `ColorTemperatureLight`, `ExtendedColorLight`
1197
+ - Switches & Outlets: `OnOffSwitch`, `OnOffOutlet`, `DimmableOutlet`
1198
+ - Sensors: `ContactSensor`, `TemperatureSensor`, `HumiditySensor`, `OccupancySensor`, etc.
1199
+ - HVAC: `Thermostat`, `Fan`
1200
+ - Closure: `DoorLock`, `WindowCovering`
1201
+ - Robotic: `RoboticVacuumCleaner`
1202
+
1203
+ **Usage:**
1204
+ ```typescript
1205
+ const accessory = {
1206
+ uuid: api.matter.uuid.generate('my-light'),
1207
+ displayName: 'Living Room Light',
1208
+ deviceType: api.matter.deviceTypes.DimmableLight,
1209
+ // ...
1078
1210
  }
1211
+ ```
1079
1212
 
1080
- // Monitor external changes (Flow B)
1081
- mqttClient.on('message', (topic, message) => {
1082
- const { state } = JSON.parse(message.toString())
1083
- const deviceIsOn = state === 'ON'
1213
+ **See:** [Available Device Types](#available-device-types) for complete list
1084
1214
 
1085
- if (deviceIsOn !== accessory.clusters.onOff.onOff) {
1086
- api.matter.updateAccessoryState(
1087
- accessory.uuid,
1088
- api.matter.clusterNames.OnOff,
1089
- { onOff: deviceIsOn }
1090
- )
1091
- }
1092
- })
1093
- ```
1215
+ ---
1094
1216
 
1095
- #### Cluster Reference
1217
+ #### `api.matter.clusters`
1096
1218
 
1097
- Access cluster attributes programmatically:
1219
+ Direct access to Matter.js cluster definitions for advanced use cases.
1098
1220
 
1099
- ```typescript
1100
- // Reading state
1101
- const isOn = accessory.clusters.onOff.onOff
1221
+ **Type:** `typeof clusters` (from Matter.js)
1102
1222
 
1103
- // All OnOff cluster attributes via api.matter
1223
+ **Usage:**
1224
+ ```typescript
1225
+ // Access cluster attributes programmatically
1104
1226
  const onOffAttrs = api.matter.clusters.OnOffCluster.attributes
1105
1227
  console.log(Object.keys(onOffAttrs))
1106
1228
  // Output: ['onOff', 'clusterRevision', 'featureMap', ...]
1229
+
1230
+ // Check if cluster supports specific features
1231
+ const levelControlFeatures = api.matter.clusters.LevelControlCluster.features
1107
1232
  ```
1108
1233
 
1234
+ **When to use:**
1235
+ - Advanced cluster introspection
1236
+ - Dynamic attribute discovery
1237
+ - Custom cluster implementations
1238
+
1239
+ **Note:** Most plugins should use the higher-level APIs instead.
1240
+
1109
1241
  ---
1110
1242
 
1111
- ## Additional Resources
1243
+ #### `api.matter.clusterNames`
1112
1244
 
1113
- - **Matter.js Documentation**: https://github.com/project-chip/matter.js
1114
- - **Matter Specification**: https://csa-iot.org/developer-resource/specifications-download-request/
1115
- - **Homebridge Documentation**: https://developers.homebridge.io
1116
- - **Example Devices**: See the `src/devices/` directory for complete working examples
1245
+ Cluster name constants for type safety and autocomplete with state methods.
1117
1246
 
1118
- ---
1247
+ **Type:** `typeof clusterNames`
1119
1248
 
1120
- ## Appendix: Common Attribute Value Types
1249
+ **Available Names:**
1250
+ - `OnOff`, `LevelControl`, `ColorControl`
1251
+ - `DoorLock`, `WindowCovering`
1252
+ - `Thermostat`, `FanControl`
1253
+ - `TemperatureMeasurement`, `RelativeHumidityMeasurement`
1254
+ - And many more...
1121
1255
 
1122
- | Type | Range/Format | Common Uses | Notes |
1123
- |-------------|---------------------------|--------------------------------------|------------------------------------------|
1124
- | Boolean | `true`, `false` | Power states, binary sensors | Simple on/off values |
1125
- | Uint8 | 0-254 | Brightness, hue, saturation | 0 often reserved, 254 = 100% |
1126
- | Uint16 | 0-65535 | Color XY values, extended ranges | Full 16-bit range |
1127
- | Enum | Varies | Modes, states | Use `api.matter.types` for type safety |
1128
- | Temperature | Hundredths of degrees C | Thermostat, temperature sensors | 2500 = 25.00°C |
1129
- | Percentage | 0-100 or 0-254 | Sensors (0-100), Controls (0-254) | Check device spec for range |
1130
- | Mireds | 147-454 | Color temperature | Reciprocal megakelvin: 1000000 / kelvin |
1256
+ **Usage:**
1257
+ ```typescript
1258
+ // Type-safe cluster references
1259
+ api.matter.updateAccessoryState(
1260
+ uuid,
1261
+ api.matter.clusterNames.OnOff, // Autocomplete available!
1262
+ { onOff: true }
1263
+ )
1264
+
1265
+ const state = api.matter.getAccessoryState(
1266
+ uuid,
1267
+ api.matter.clusterNames.LevelControl
1268
+ )
1269
+ ```
1270
+
1271
+ **Benefits:**
1272
+ - Autocomplete in IDEs
1273
+ - Compile-time error checking
1274
+ - Prevents typos in cluster names
1131
1275
 
1132
1276
  ---
1133
1277
 
1134
- ## Appendix: Value Conversion Formulas
1278
+ #### `api.matter.types`
1135
1279
 
1136
- ### Brightness (Matter Percentage)
1280
+ Type-safe enum values for cluster attributes (modes, states, etc.).
1281
+
1282
+ **Type:** `typeof MatterTypes` (from Homebridge)
1283
+
1284
+ **Common Types:**
1285
+ - `DoorLock.LockState` - Lock states (Locked, Unlocked, etc.)
1286
+ - `DoorLock.LockType` - Lock types (DeadBolt, Magnetic, etc.)
1287
+ - `FanControl.FanMode` - Fan modes (Off, Low, Medium, High, Auto, etc.)
1288
+ - `FanControl.FanModeSequence` - Supported mode sequences
1289
+ - `Thermostat.SystemMode` - HVAC modes (Off, Heat, Cool, Auto, etc.)
1290
+ - `ColorControl.ColorMode` - Color modes (HS, XY, ColorTemperature)
1291
+ - `RvcRunMode.ModeTag` - Vacuum run mode tags (Idle, Cleaning, Mapping)
1292
+ - `RvcCleanMode.ModeTag` - Vacuum clean mode tags (Vacuum, Mop)
1293
+ - `RvcOperationalState.OperationalState` - Vacuum states (Stopped, Running, Docked, etc.)
1137
1294
 
1295
+ **Usage:**
1138
1296
  ```typescript
1139
- // To Matter (1-254)
1140
- const matterLevel = Math.max(1, Math.round((percent / 100) * 254))
1297
+ // Door lock states
1298
+ clusters: {
1299
+ doorLock: {
1300
+ lockState: api.matter.types.DoorLock.LockState.Unlocked,
1301
+ lockType: api.matter.types.DoorLock.LockType.DeadBolt
1302
+ }
1303
+ }
1141
1304
 
1142
- // From Matter (to 0-100%)
1143
- const percent = Math.round((matterLevel / 254) * 100)
1305
+ // Fan modes
1306
+ clusters: {
1307
+ fanControl: {
1308
+ fanMode: api.matter.types.FanControl.FanMode.Auto,
1309
+ fanModeSequence: api.matter.types.FanControl.FanModeSequence.OffLowMedHigh
1310
+ }
1311
+ }
1312
+
1313
+ // Color modes
1314
+ clusters: {
1315
+ colorControl: {
1316
+ colorMode: api.matter.types.ColorControl.ColorMode.ColorTemperatureMireds
1317
+ }
1318
+ }
1144
1319
  ```
1145
1320
 
1146
- ### Hue (Matter ↔ Degrees)
1321
+ **Benefits:**
1322
+ - Type safety prevents invalid values
1323
+ - IDE autocomplete shows available options
1324
+ - Self-documenting code
1325
+ - Compile-time validation
1147
1326
 
1148
- ```typescript
1149
- // To Matter (0-254)
1150
- const matterHue = Math.round((degrees / 360) * 254)
1327
+ **See:** [Using Matter Types](#using-matter-types) for detailed examples
1151
1328
 
1152
- // From Matter (to 0-360°)
1153
- const degrees = Math.round((matterHue / 254) * 360)
1329
+ ---
1330
+
1331
+ ### Matter API Methods
1332
+
1333
+ #### `api.matter.registerPlatformAccessories()`
1334
+
1335
+ Register Matter accessories with the platform (standard registration method).
1336
+
1337
+ **Signature:**
1338
+ ```typescript
1339
+ registerPlatformAccessories(
1340
+ pluginIdentifier: string,
1341
+ platformName: string,
1342
+ accessories: MatterAccessory[]
1343
+ ): void
1154
1344
  ```
1155
1345
 
1156
- ### Saturation (Matter ↔ Percentage)
1346
+ **Parameters:**
1347
+ - `pluginIdentifier` - Plugin identifier (e.g., `'homebridge-example'`)
1348
+ - `platformName` - Platform name (e.g., `'ExamplePlatform'`)
1349
+ - `accessories` - Array of Matter accessories to register
1157
1350
 
1351
+ **Usage:**
1158
1352
  ```typescript
1159
- // To Matter (0-254)
1160
- const matterSat = Math.round((percent / 100) * 254)
1353
+ const PLUGIN_NAME = 'homebridge-example'
1354
+ const PLATFORM_NAME = 'ExamplePlatform'
1161
1355
 
1162
- // From Matter (to 0-100%)
1163
- const percent = Math.round((matterSat / 254) * 100)
1356
+ const accessories = [
1357
+ {
1358
+ uuid: api.matter.uuid.generate('my-light'),
1359
+ displayName: 'Living Room Light',
1360
+ deviceType: api.matter.deviceTypes.OnOffLight,
1361
+ // ...
1362
+ }
1363
+ ]
1364
+
1365
+ api.matter.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessories)
1164
1366
  ```
1165
1367
 
1166
- ### Color Temperature (Mireds ↔ Kelvin)
1368
+ **When to use:**
1369
+ - Standard accessory registration
1370
+ - Multiple accessories on shared bridge
1371
+ - Most common use case
1167
1372
 
1168
- ```typescript
1169
- // To Mireds
1170
- const mireds = Math.round(1000000 / kelvin)
1373
+ **See also:** `publishExternalAccessories()` for isolated accessories
1171
1374
 
1172
- // From Mireds
1173
- const kelvin = Math.round(1000000 / mireds)
1375
+ ---
1376
+
1377
+ #### `api.matter.unregisterPlatformAccessories()`
1378
+
1379
+ Unregister Matter accessories by UUID.
1380
+
1381
+ **Signature:**
1382
+ ```typescript
1383
+ unregisterPlatformAccessories(
1384
+ pluginIdentifier: string,
1385
+ platformName: string,
1386
+ accessories: MatterAccessory[]
1387
+ ): void
1174
1388
  ```
1175
1389
 
1176
- ### XY Color (Matter ↔ Float)
1390
+ **Parameters:**
1391
+ - `pluginIdentifier` - Plugin identifier
1392
+ - `platformName` - Platform name
1393
+ - `accessories` - Array of accessories to unregister (only `uuid` is required)
1177
1394
 
1395
+ **Usage:**
1178
1396
  ```typescript
1179
- // To Matter (0-65535)
1180
- const matterX = Math.round(floatX * 65535)
1181
- const matterY = Math.round(floatY * 65535)
1397
+ // Unregister accessories
1398
+ const accessoriesToRemove = [
1399
+ { uuid: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' }
1400
+ ]
1401
+
1402
+ api.matter.unregisterPlatformAccessories(
1403
+ PLUGIN_NAME,
1404
+ PLATFORM_NAME,
1405
+ accessoriesToRemove
1406
+ )
1407
+ ```
1182
1408
 
1183
- // From Matter (to 0.0-1.0)
1184
- const floatX = matterX / 65535
1185
- const floatY = matterY / 65535
1409
+ **When to use:**
1410
+ - Removing accessories from Homebridge
1411
+ - Cleanup during plugin shutdown
1412
+ - User-initiated accessory removal
1413
+
1414
+ ---
1415
+
1416
+ #### `api.matter.publishExternalAccessories()`
1417
+
1418
+ Publish accessories on dedicated Matter bridges (isolated from other accessories).
1419
+
1420
+ **Signature:**
1421
+ ```typescript
1422
+ publishExternalAccessories(
1423
+ pluginIdentifier: string,
1424
+ accessories: MatterAccessory[]
1425
+ ): void
1186
1426
  ```
1187
1427
 
1188
- ### Temperature (Matter ↔ Celsius)
1428
+ **Parameters:**
1429
+ - `pluginIdentifier` - Plugin identifier
1430
+ - `accessories` - Array of accessories to publish externally
1189
1431
 
1432
+ **Usage:**
1190
1433
  ```typescript
1191
- // To Matter (hundredths)
1192
- const matterTemp = Math.round(celsius * 100)
1434
+ const accessories = [
1435
+ {
1436
+ uuid: api.matter.uuid.generate('robot-vacuum'),
1437
+ displayName: 'Robot Vacuum',
1438
+ deviceType: api.matter.deviceTypes.RoboticVacuumCleaner,
1439
+ // ...
1440
+ }
1441
+ ]
1193
1442
 
1194
- // From Matter
1195
- const celsius = matterTemp / 100
1443
+ // Publish on dedicated bridge
1444
+ api.matter.publishExternalAccessories(PLUGIN_NAME, accessories)
1445
+ ```
1446
+
1447
+ **When to use:**
1448
+ - Robotic Vacuum Cleaners (required by Apple Home)
1449
+ - Cameras and video doorbells
1450
+ - Devices requiring isolation
1451
+ - Testing single accessories
1452
+
1453
+ **Behavior:**
1454
+ - Each accessory gets its own Matter server instance
1455
+ - Separate port allocation (e.g., 5541, 5542, etc.)
1456
+ - Independent QR codes for commissioning
1457
+ - Complete isolation from other accessories
1458
+
1459
+ **Similar to:** HAP's `api.publishExternalAccessories()`
1460
+
1461
+ ---
1462
+
1463
+ #### `api.matter.updateAccessoryState()`
1464
+
1465
+ Update accessory cluster state when device changes externally (Flow B).
1466
+
1467
+ **Signature:**
1468
+ ```typescript
1469
+ updateAccessoryState(
1470
+ uuid: string,
1471
+ cluster: string,
1472
+ attributes: Record<string, any>
1473
+ ): void
1196
1474
  ```
1475
+
1476
+ **Parameters:**
1477
+ - `uuid` - Accessory UUID
1478
+ - `cluster` - Cluster name (use `api.matter.clusterNames.*`)
1479
+ - `attributes` - Attributes to update (key-value pairs)
1480
+
1481
+ **Usage:**
1482
+ ```typescript
1483
+ // Device turned on via native app
1484
+ api.matter.updateAccessoryState(
1485
+ uuid,
1486
+ api.matter.clusterNames.OnOff,
1487
+ { onOff: true }
1488
+ )
1489
+
1490
+ // Brightness changed via physical button
1491
+ api.matter.updateAccessoryState(
1492
+ uuid,
1493
+ api.matter.clusterNames.LevelControl,
1494
+ { currentLevel: 200 }
1495
+ )
1496
+
1497
+ // Update multiple attributes at once
1498
+ api.matter.updateAccessoryState(
1499
+ uuid,
1500
+ api.matter.clusterNames.ColorControl,
1501
+ {
1502
+ colorMode: api.matter.types.ColorControl.ColorMode.ColorTemperatureMireds,
1503
+ colorTemperatureMireds: 250
1504
+ }
1505
+ )
1506
+ ```
1507
+
1508
+ **IMPORTANT:**
1509
+ - ❌ **DO NOT** use inside handlers (state updates automatically)
1510
+ - ✅ **DO** use for external changes (webhooks, polling, events)
1511
+
1512
+ **When to use:**
1513
+ - Native app controls
1514
+ - Physical button presses
1515
+ - Webhook notifications
1516
+ - Polling results
1517
+ - MQTT/WebSocket messages
1518
+
1519
+ **See:** [Flow B: Physical Device → Home App](#flow-b-physical-device--home-app-manual)
1520
+
1521
+ ---
1522
+
1523
+ #### `api.matter.getAccessoryState()`
1524
+
1525
+ Get current cluster state from a Matter accessory.
1526
+
1527
+ **Signature:**
1528
+ ```typescript
1529
+ getAccessoryState(
1530
+ uuid: string,
1531
+ cluster: string
1532
+ ): Record<string, any> | undefined
1533
+ ```
1534
+
1535
+ **Parameters:**
1536
+ - `uuid` - Accessory UUID
1537
+ - `cluster` - Cluster name (use `api.matter.clusterNames.*`)
1538
+
1539
+ **Returns:**
1540
+ - Object with current attribute values, or `undefined` if not found
1541
+
1542
+ **Usage:**
1543
+ ```typescript
1544
+ // Read OnOff state
1545
+ const state = api.matter.getAccessoryState(uuid, api.matter.clusterNames.OnOff)
1546
+ if (state?.onOff) {
1547
+ log.info('Light is currently on')
1548
+ }
1549
+
1550
+ // Read level control state
1551
+ const levelState = api.matter.getAccessoryState(
1552
+ uuid,
1553
+ api.matter.clusterNames.LevelControl
1554
+ )
1555
+ log.info(`Current brightness: ${levelState?.currentLevel}`)
1556
+
1557
+ // Check color mode
1558
+ const colorState = api.matter.getAccessoryState(
1559
+ uuid,
1560
+ api.matter.clusterNames.ColorControl
1561
+ )
1562
+ if (colorState?.colorMode === api.matter.types.ColorControl.ColorMode.ColorTemperatureMireds) {
1563
+ log.info(`Color temp: ${colorState.colorTemperatureMireds} mireds`)
1564
+ }
1565
+ ```
1566
+
1567
+ **When to use:**
1568
+ - Reading state after plugin restart
1569
+ - Verifying current state before changes
1570
+ - Debugging and logging
1571
+ - Conditional logic based on state
1572
+
1573
+ **Note:** State is persisted across restarts automatically.
1574
+
1575
+ ---
1576
+
1577
+ ## Additional Resources
1578
+
1579
+ - **Matter.js Documentation**: https://github.com/project-chip/matter.js
1580
+ - **Matter Specification**: https://csa-iot.org/developer-resource/specifications-download-request/
1581
+ - **Homebridge Documentation**: https://developers.homebridge.io
1582
+ - **Example Devices**: See the `src/devices/` directory for complete working examples
1583
+
1584
+ ---
1585
+
1586
+ ## Appendix: Common Attribute Value Types
1587
+
1588
+ | Type | Range/Format | Common Uses | Notes |
1589
+ |-------------|---------------------------|--------------------------------------|------------------------------------------|
1590
+ | Boolean | `true`, `false` | Power states, binary sensors | Simple on/off values |
1591
+ | Uint8 | 0-254 | Brightness, hue, saturation | 0 often reserved, 254 = 100% |
1592
+ | Uint16 | 0-65535 | Color XY values, extended ranges | Full 16-bit range |
1593
+ | Enum | Varies | Modes, states | Use `api.matter.types` for type safety |
1594
+ | Temperature | Hundredths of degrees C | Thermostat, temperature sensors | 2500 = 25.00°C |
1595
+ | Percentage | 0-100 or 0-254 | Sensors (0-100), Controls (0-254) | Check device spec for range |
1596
+ | Mireds | 147-454 | Color temperature | Reciprocal megakelvin: 1000000 / kelvin |
1597
+
1598
+ ---
1599
+
1600
+ ## Appendix: Value Conversion Formulas
1601
+
1602
+ ### Brightness (Matter ↔ Percentage)
1603
+
1604
+ ```typescript
1605
+ // To Matter (1-254)
1606
+ const matterLevel = Math.max(1, Math.round((percent / 100) * 254))
1607
+
1608
+ // From Matter (to 0-100%)
1609
+ const percent = Math.round((matterLevel / 254) * 100)
1610
+ ```
1611
+
1612
+ ### Hue (Matter ↔ Degrees)
1613
+
1614
+ ```typescript
1615
+ // To Matter (0-254)
1616
+ const matterHue = Math.round((degrees / 360) * 254)
1617
+
1618
+ // From Matter (to 0-360°)
1619
+ const degrees = Math.round((matterHue / 254) * 360)
1620
+ ```
1621
+
1622
+ ### Saturation (Matter ↔ Percentage)
1623
+
1624
+ ```typescript
1625
+ // To Matter (0-254)
1626
+ const matterSat = Math.round((percent / 100) * 254)
1627
+
1628
+ // From Matter (to 0-100%)
1629
+ const percent = Math.round((matterSat / 254) * 100)
1630
+ ```
1631
+
1632
+ ### Color Temperature (Mireds ↔ Kelvin)
1633
+
1634
+ ```typescript
1635
+ // To Mireds
1636
+ const mireds = Math.round(1000000 / kelvin)
1637
+
1638
+ // From Mireds
1639
+ const kelvin = Math.round(1000000 / mireds)
1640
+ ```
1641
+
1642
+ ### XY Color (Matter ↔ Float)
1643
+
1644
+ ```typescript
1645
+ // To Matter (0-65535)
1646
+ const matterX = Math.round(floatX * 65535)
1647
+ const matterY = Math.round(floatY * 65535)
1648
+
1649
+ // From Matter (to 0.0-1.0)
1650
+ const floatX = matterX / 65535
1651
+ const floatY = matterY / 65535
1652
+ ```
1653
+
1654
+ ### Temperature (Matter ↔ Celsius)
1655
+
1656
+ ```typescript
1657
+ // To Matter (hundredths)
1658
+ const matterTemp = Math.round(celsius * 100)
1659
+
1660
+ // From Matter
1661
+ const celsius = matterTemp / 100
1662
+ ```
1663
+
1664
+ ---
1665
+
1666
+ ## Device Reference
1667
+
1668
+ This section documents all available Matter device types with their clusters, attributes, handlers, and usage examples.
1669
+
1670
+ ### On/Off Light
1671
+
1672
+ | Property | Value |
1673
+ |--------------------------|--------------------------------------------------------|
1674
+ | **Device Type** | `api.matter.deviceTypes.OnOffLight` |
1675
+ | **Description** | A lighting device capable of being switched on or off. |
1676
+ | **Matter Specification** | § 4.1 |
1677
+
1678
+ #### Required Clusters
1679
+
1680
+ ###### `OnOff` Cluster
1681
+
1682
+ Controls the power state of the light.
1683
+
1684
+ **Attributes**:
1685
+
1686
+ ```typescript
1687
+ // All OnOff cluster attributes via api.matter
1688
+ const onOffAttrs = api.matter.clusters.OnOffCluster.attributes
1689
+ console.log(Object.keys(onOffAttrs))
1690
+ // Output: ['onOff', 'clusterRevision', 'featureMap', ...]
1691
+ ```
1692
+
1693
+ | Attribute | Type | Range/Values | Description |
1694
+ |-----------|---------|-----------------|----------------------------------|
1695
+ | `onOff` | boolean | `true`, `false` | Power state (true=on, false=off) |
1696
+
1697
+ **Reading State**:
1698
+
1699
+ ```typescript
1700
+ const isOn = accessory.clusters.onOff.onOff
1701
+ ```
1702
+
1703
+ <details>
1704
+ <summary><strong>Handlers</strong></summary>
1705
+
1706
+ ```typescript
1707
+ handlers: {
1708
+ onOff: {
1709
+ /**
1710
+ * Called when user turns light ON via Home app
1711
+ */
1712
+ on: async () => {
1713
+ // Control your physical device
1714
+ await myLightAPI.turnOn()
1715
+ // State automatically updated by Homebridge
1716
+ },
1717
+
1718
+ /**
1719
+ * Called when user turns light OFF via Home app
1720
+ */
1721
+ off: async () => {
1722
+ // Control your physical device
1723
+ await myLightAPI.turnOff()
1724
+ // State automatically updated by Homebridge
1725
+ },
1726
+ },
1727
+ }
1728
+ ```
1729
+
1730
+ </details>
1731
+
1732
+ ### Dimmable Light
1733
+
1734
+ | Property | Value |
1735
+ |--------------------------|--------------------------------------------------------|
1736
+ | **Device Type** | `api.matter.deviceTypes.DimmableLight` |
1737
+ | **Description** | A lighting device with on/off and brightness control. |
1738
+ | **Matter Specification** | § 4.2 |
1739
+
1740
+ #### Required Clusters
1741
+
1742
+ ###### `OnOff` Cluster
1743
+
1744
+ Controls the power state of the light.
1745
+
1746
+ **Attributes**:
1747
+
1748
+ | Attribute | Type | Range/Values | Description |
1749
+ |-----------|---------|-----------------|----------------------------------|
1750
+ | `onOff` | boolean | `true`, `false` | Power state (true=on, false=off) |
1751
+
1752
+ **Reading State**:
1753
+
1754
+ ```typescript
1755
+ const isOn = accessory.clusters.onOff.onOff
1756
+ ```
1757
+
1758
+ ###### `LevelControl` Cluster
1759
+
1760
+ Controls the brightness level of the light.
1761
+
1762
+ **Attributes**:
1763
+
1764
+ ```typescript
1765
+ // All LevelControl cluster attributes via api.matter
1766
+ const levelAttrs = api.matter.clusters.LevelControlCluster.attributes
1767
+ console.log(Object.keys(levelAttrs))
1768
+ // Output: ['currentLevel', 'minLevel', 'maxLevel', 'onLevel', 'options', ...]
1769
+ ```
1770
+
1771
+ | Attribute | Type | Range/Values | Description |
1772
+ |----------------|--------|--------------|--------------------------------------------------|
1773
+ | `currentLevel` | number | 1-254 | Current brightness (1 = 0.4%, 254 = 100%) |
1774
+ | `minLevel` | number | 1-254 | Minimum brightness level |
1775
+ | `maxLevel` | number | 1-254 | Maximum brightness level |
1776
+ | `onLevel` | number | 0-254 | Brightness when turned on (0 = restore previous) |
1777
+
1778
+ **Reading State**:
1779
+
1780
+ ```typescript
1781
+ const level = accessory.clusters.levelControl.currentLevel
1782
+ const brightnessPercent = Math.round((level / 254) * 100)
1783
+ ```
1784
+
1785
+ <details>
1786
+ <summary><strong>Handlers</strong></summary>
1787
+
1788
+ ```typescript
1789
+ handlers: {
1790
+ onOff: {
1791
+ on: async () => {
1792
+ log.info('[Dimmable Light] Turning ON')
1793
+ await myLightAPI.turnOn()
1794
+ },
1795
+
1796
+ off: async () => {
1797
+ log.info('[Dimmable Light] Turning OFF')
1798
+ await myLightAPI.turnOff()
1799
+ },
1800
+ },
1801
+
1802
+ levelControl: {
1803
+ /**
1804
+ * Called when user adjusts brightness via Home app
1805
+ * Also called when turning on with specific brightness
1806
+ */
1807
+ moveToLevelWithOnOff: async (request: MatterRequests.MoveToLevel) => {
1808
+ const { level, transitionTime } = request
1809
+ const brightnessPercent = Math.round((level / 254) * 100)
1810
+
1811
+ log.info(`[Dimmable Light] Setting brightness to ${brightnessPercent}%`)
1812
+ await myLightAPI.setBrightness(brightnessPercent, transitionTime)
1813
+ },
1814
+ },
1815
+ }
1816
+ ```
1817
+
1818
+ </details>
1819
+
1820
+ ---
1821
+
1822
+ ### Color Temperature Light
1823
+
1824
+ | Property | Value |
1825
+ |--------------------------|---------------------------------------------------------------------------|
1826
+ | **Device Type** | `api.matter.deviceTypes.ColorTemperatureLight` |
1827
+ | **Description** | A lighting device with on/off, brightness, and color temperature control. |
1828
+ | **Matter Specification** | § 4.3 |
1829
+
1830
+ #### Required Clusters
1831
+
1832
+ ###### `OnOff` Cluster
1833
+
1834
+ Controls the power state of the light.
1835
+
1836
+ **Attributes**:
1837
+
1838
+ | Attribute | Type | Range/Values | Description |
1839
+ |-----------|---------|-----------------|----------------------------------|
1840
+ | `onOff` | boolean | `true`, `false` | Power state (true=on, false=off) |
1841
+
1842
+ ###### `LevelControl` Cluster
1843
+
1844
+ Controls the brightness level of the light.
1845
+
1846
+ **Attributes**:
1847
+
1848
+ | Attribute | Type | Range/Values | Description |
1849
+ |----------------|--------|--------------|--------------------------------------------------|
1850
+ | `currentLevel` | number | 1-254 | Current brightness (1 = 0.4%, 254 = 100%) |
1851
+ | `minLevel` | number | 1-254 | Minimum brightness level |
1852
+ | `maxLevel` | number | 1-254 | Maximum brightness level |
1853
+
1854
+ ###### `ColorControl` Cluster
1855
+
1856
+ Controls the color temperature of the light.
1857
+
1858
+ **Attributes**:
1859
+
1860
+ ```typescript
1861
+ // All ColorControl cluster attributes via api.matter
1862
+ const colorAttrs = api.matter.clusters.ColorControlCluster.attributes
1863
+ console.log(Object.keys(colorAttrs))
1864
+ ```
1865
+
1866
+ | Attribute | Type | Range/Values | Description |
1867
+ |---------------------------------|--------|--------------|-------------------------------------------------------|
1868
+ | `colorMode` | number | 0-2 | Current color mode (2 = Color Temperature) |
1869
+ | `colorTemperatureMireds` | number | 147-454 | Color temp in mireds (reciprocal megakelvin) |
1870
+ | `colorTempPhysicalMinMireds` | number | 147-500 | Coolest temperature supported (e.g., 147 = ~6800K) |
1871
+ | `colorTempPhysicalMaxMireds` | number | 147-500 | Warmest temperature supported (e.g., 454 = ~2200K) |
1872
+
1873
+ **Reading State**:
1874
+
1875
+ ```typescript
1876
+ const mireds = accessory.clusters.colorControl.colorTemperatureMireds
1877
+ const kelvin = Math.round(1000000 / mireds)
1878
+ ```
1879
+
1880
+ **Value Conversions**:
1881
+
1882
+ ```typescript
1883
+ // Kelvin to Mireds
1884
+ const mireds = Math.round(1000000 / kelvin)
1885
+
1886
+ // Mireds to Kelvin
1887
+ const kelvin = Math.round(1000000 / mireds)
1888
+ ```
1889
+
1890
+ <details>
1891
+ <summary><strong>Handlers</strong></summary>
1892
+
1893
+ ```typescript
1894
+ handlers: {
1895
+ onOff: {
1896
+ on: async () => {
1897
+ log.info('[CCT Light] Turning ON')
1898
+ await myLightAPI.turnOn()
1899
+ },
1900
+
1901
+ off: async () => {
1902
+ log.info('[CCT Light] Turning OFF')
1903
+ await myLightAPI.turnOff()
1904
+ },
1905
+ },
1906
+
1907
+ levelControl: {
1908
+ moveToLevelWithOnOff: async (request: MatterRequests.MoveToLevel) => {
1909
+ const { level } = request
1910
+ const brightnessPercent = Math.round((level / 254) * 100)
1911
+
1912
+ log.info(`[CCT Light] Setting brightness to ${brightnessPercent}%`)
1913
+ await myLightAPI.setBrightness(brightnessPercent)
1914
+ },
1915
+ },
1916
+
1917
+ colorControl: {
1918
+ /**
1919
+ * Called when user adjusts color temperature via Home app
1920
+ */
1921
+ moveToColorTemperatureLogic: async (request: { targetMireds: number, transitionTime: number }) => {
1922
+ const { targetMireds, transitionTime } = request
1923
+ const kelvin = Math.round(1000000 / targetMireds)
1924
+
1925
+ log.info(`[CCT Light] Setting color temp to ${kelvin}K (${targetMireds} mireds)`)
1926
+ await myLightAPI.setColorTemperature(kelvin, transitionTime)
1927
+ },
1928
+ },
1929
+ }
1930
+ ```
1931
+
1932
+ </details>
1933
+
1934
+ ---
1935
+
1936
+ ### Color Light
1937
+
1938
+ | Property | Value |
1939
+ |--------------------------|--------------------------------------------------------------------------------|
1940
+ | **Device Type** | `api.matter.deviceTypes.ExtendedColorLight` |
1941
+ | **Description** | A lighting device with on/off, brightness, and color (Hue/Saturation) control. |
1942
+ | **Matter Specification** | § 4.4 |
1943
+
1944
+ #### Required Clusters
1945
+
1946
+ ###### `OnOff` Cluster
1947
+
1948
+ Controls the power state of the light.
1949
+
1950
+ **Attributes**:
1951
+
1952
+ | Attribute | Type | Range/Values | Description |
1953
+ |-----------|---------|-----------------|----------------------------------|
1954
+ | `onOff` | boolean | `true`, `false` | Power state (true=on, false=off) |
1955
+
1956
+ ###### `LevelControl` Cluster
1957
+
1958
+ Controls the brightness level of the light.
1959
+
1960
+ **Attributes**:
1961
+
1962
+ | Attribute | Type | Range/Values | Description |
1963
+ |----------------|--------|--------------|--------------------------------------------------|
1964
+ | `currentLevel` | number | 1-254 | Current brightness (1 = 0.4%, 254 = 100%) |
1965
+ | `minLevel` | number | 1-254 | Minimum brightness level |
1966
+ | `maxLevel` | number | 1-254 | Maximum brightness level |
1967
+
1968
+ ###### `ColorControl` Cluster
1969
+
1970
+ Controls the color (Hue/Saturation or XY) of the light.
1971
+
1972
+ **Attributes**:
1973
+
1974
+ | Attribute | Type | Range/Values | Description |
1975
+ |--------------------|--------|--------------|-------------------------------------------------------|
1976
+ | `colorMode` | number | 0-2 | Current color mode (0 = HS, 1 = XY) |
1977
+ | `currentHue` | number | 0-254 | Current hue (maps to 0-360 degrees) |
1978
+ | `currentSaturation`| number | 0-254 | Current saturation (maps to 0-100%) |
1979
+ | `currentX` | number | 0-65535 | CIE 1931 x coordinate |
1980
+ | `currentY` | number | 0-65535 | CIE 1931 y coordinate |
1981
+
1982
+ **Reading State**:
1983
+
1984
+ ```typescript
1985
+ const hue = accessory.clusters.colorControl.currentHue
1986
+ const saturation = accessory.clusters.colorControl.currentSaturation
1987
+
1988
+ // Convert to degrees/percentage
1989
+ const hueDegrees = Math.round((hue / 254) * 360)
1990
+ const saturationPercent = Math.round((saturation / 254) * 100)
1991
+ ```
1992
+
1993
+ **Value Conversions**:
1994
+
1995
+ ```typescript
1996
+ // Hue: Degrees (0-360) to Matter (0-254)
1997
+ const matterHue = Math.round((degrees / 360) * 254)
1998
+ const degrees = Math.round((matterHue / 254) * 360)
1999
+
2000
+ // Saturation: Percent (0-100) to Matter (0-254)
2001
+ const matterSat = Math.round((percent / 100) * 254)
2002
+ const percent = Math.round((matterSat / 254) * 100)
2003
+
2004
+ // XY: Float (0.0-1.0) to Matter (0-65535)
2005
+ const matterX = Math.round(floatX * 65535)
2006
+ const floatX = matterX / 65535
2007
+ ```
2008
+
2009
+ <details>
2010
+ <summary><strong>Handlers</strong></summary>
2011
+
2012
+ ```typescript
2013
+ handlers: {
2014
+ onOff: {
2015
+ on: async () => {
2016
+ log.info('[Color Light] Turning ON')
2017
+ await myLightAPI.turnOn()
2018
+ },
2019
+
2020
+ off: async () => {
2021
+ log.info('[Color Light] Turning OFF')
2022
+ await myLightAPI.turnOff()
2023
+ },
2024
+ },
2025
+
2026
+ levelControl: {
2027
+ moveToLevelWithOnOff: async (request: MatterRequests.MoveToLevel) => {
2028
+ const { level } = request
2029
+ const brightnessPercent = Math.round((level / 254) * 100)
2030
+
2031
+ log.info(`[Color Light] Setting brightness to ${brightnessPercent}%`)
2032
+ await myLightAPI.setBrightness(brightnessPercent)
2033
+ },
2034
+ },
2035
+
2036
+ colorControl: {
2037
+ /**
2038
+ * Called when user adjusts color via XY coordinates in Home app
2039
+ */
2040
+ moveToColorLogic: async (request: { targetX: number, targetY: number, transitionTime: number }) => {
2041
+ const { targetX, targetY, transitionTime } = request
2042
+ const xFloat = (targetX / 65535).toFixed(4)
2043
+ const yFloat = (targetY / 65535).toFixed(4)
2044
+
2045
+ log.info(`[Color Light] Setting XY color to (${xFloat}, ${yFloat})`)
2046
+ await myLightAPI.setColorXY(xFloat, yFloat, transitionTime)
2047
+ },
2048
+
2049
+ /**
2050
+ * Called when user adjusts color via Hue/Saturation in Home app
2051
+ */
2052
+ moveToHueAndSaturationLogic: async (request: { targetHue: number, targetSaturation: number, transitionTime: number }) => {
2053
+ const { targetHue, targetSaturation, transitionTime } = request
2054
+ const hueDegrees = Math.round((targetHue / 254) * 360)
2055
+ const saturationPercent = Math.round((targetSaturation / 254) * 100)
2056
+
2057
+ log.info(`[Color Light] Setting color to ${hueDegrees}°, ${saturationPercent}%`)
2058
+ await myLightAPI.setColorHS(hueDegrees, saturationPercent, transitionTime)
2059
+ },
2060
+ },
2061
+ }
2062
+ ```
2063
+
2064
+ </details>
2065
+
2066
+ ---
2067
+
2068
+ ### Extended Color Light
2069
+
2070
+ | Property | Value |
2071
+ |--------------------------|---------------------------------------------------------------------------------------------------|
2072
+ | **Device Type** | `api.matter.deviceTypes.ExtendedColorLight` |
2073
+ | **Description** | A lighting device with on/off, brightness, color (Hue/Saturation), and color temperature control. |
2074
+ | **Matter Specification** | § 4.4 |
2075
+
2076
+ #### Required Clusters
2077
+
2078
+ ###### `OnOff` Cluster
2079
+
2080
+ Controls the power state of the light.
2081
+
2082
+ **Attributes**:
2083
+
2084
+ | Attribute | Type | Range/Values | Description |
2085
+ |-----------|---------|-----------------|----------------------------------|
2086
+ | `onOff` | boolean | `true`, `false` | Power state (true=on, false=off) |
2087
+
2088
+ ###### `LevelControl` Cluster
2089
+
2090
+ Controls the brightness level of the light.
2091
+
2092
+ **Attributes**:
2093
+
2094
+ | Attribute | Type | Range/Values | Description |
2095
+ |----------------|--------|--------------|--------------------------------------------------|
2096
+ | `currentLevel` | number | 1-254 | Current brightness (1 = 0.4%, 254 = 100%) |
2097
+ | `minLevel` | number | 1-254 | Minimum brightness level |
2098
+ | `maxLevel` | number | 1-254 | Maximum brightness level |
2099
+
2100
+ ###### `ColorControl` Cluster
2101
+
2102
+ Controls both color (Hue/Saturation or XY) and color temperature of the light.
2103
+
2104
+ When updating state for Extended Color Light (Flow B), always update the `colorMode` attribute along with the color/temperature values to indicate which mode is active.
2105
+
2106
+ **Attributes**:
2107
+
2108
+ | Attribute | Type | Range/Values | Description |
2109
+ |---------------------------------|--------|--------------|-------------------------------------------------------|
2110
+ | `colorMode` | number | 0-2 | Current color mode (0 = HS, 1 = XY, 2 = ColorTemp) |
2111
+ | `currentHue` | number | 0-254 | Current hue (maps to 0-360 degrees) |
2112
+ | `currentSaturation` | number | 0-254 | Current saturation (maps to 0-100%) |
2113
+ | `currentX` | number | 0-65535 | CIE 1931 x coordinate |
2114
+ | `currentY` | number | 0-65535 | CIE 1931 y coordinate |
2115
+ | `colorTemperatureMireds` | number | 147-454 | Color temp in mireds (when in ColorTemp mode) |
2116
+ | `colorTempPhysicalMinMireds` | number | 147-500 | Coolest temperature supported |
2117
+ | `colorTempPhysicalMaxMireds` | number | 147-500 | Warmest temperature supported |
2118
+
2119
+ **Reading State**:
2120
+
2121
+ ```typescript
2122
+ // Check current mode
2123
+ const mode = accessory.clusters.colorControl.colorMode
2124
+ const ColorMode = api.matter.types.ColorControl.ColorMode
2125
+
2126
+ if (mode === ColorMode.CurrentHueAndCurrentSaturation || mode === ColorMode.CurrentXAndCurrentY) {
2127
+ // Light is in color mode
2128
+ const hue = accessory.clusters.colorControl.currentHue
2129
+ const sat = accessory.clusters.colorControl.currentSaturation
2130
+ } else if (mode === ColorMode.ColorTemperatureMireds) {
2131
+ // Light is in white/CCT mode
2132
+ const mireds = accessory.clusters.colorControl.colorTemperatureMireds
2133
+ const kelvin = Math.round(1000000 / mireds)
2134
+ }
2135
+ ```
2136
+
2137
+ <details>
2138
+ <summary><strong>Handlers</strong></summary>
2139
+
2140
+ ```typescript
2141
+ handlers: {
2142
+ onOff: {
2143
+ on: async () => {
2144
+ log.info('[Extended Color Light] Turning ON')
2145
+ await myLightAPI.turnOn()
2146
+ },
2147
+
2148
+ off: async () => {
2149
+ log.info('[Extended Color Light] Turning OFF')
2150
+ await myLightAPI.turnOff()
2151
+ },
2152
+ },
2153
+
2154
+ levelControl: {
2155
+ moveToLevelWithOnOff: async (request: MatterRequests.MoveToLevel) => {
2156
+ const { level } = request
2157
+ const brightnessPercent = Math.round((level / 254) * 100)
2158
+
2159
+ log.info(`[Extended Color Light] Setting brightness to ${brightnessPercent}%`)
2160
+ await myLightAPI.setBrightness(brightnessPercent)
2161
+ },
2162
+ },
2163
+
2164
+ colorControl: {
2165
+ /**
2166
+ * Called when user adjusts color via XY coordinates in Home app
2167
+ */
2168
+ moveToColorLogic: async (request: { targetX: number, targetY: number, transitionTime: number }) => {
2169
+ const { targetX, targetY, transitionTime } = request
2170
+ const xFloat = (targetX / 65535).toFixed(4)
2171
+ const yFloat = (targetY / 65535).toFixed(4)
2172
+
2173
+ log.info(`[Extended Color Light] Setting XY color to (${xFloat}, ${yFloat})`)
2174
+ await myLightAPI.setColorXY(xFloat, yFloat, transitionTime)
2175
+ },
2176
+
2177
+ /**
2178
+ * Called when user adjusts color via Hue/Saturation in Home app
2179
+ */
2180
+ moveToHueAndSaturationLogic: async (request: { targetHue: number, targetSaturation: number, transitionTime: number }) => {
2181
+ const { targetHue, targetSaturation, transitionTime } = request
2182
+ const hueDegrees = Math.round((targetHue / 254) * 360)
2183
+ const saturationPercent = Math.round((targetSaturation / 254) * 100)
2184
+
2185
+ log.info(`[Extended Color Light] Setting color to ${hueDegrees}°, ${saturationPercent}%`)
2186
+ await myLightAPI.setColorHS(hueDegrees, saturationPercent, transitionTime)
2187
+ },
2188
+
2189
+ /**
2190
+ * Called when user adjusts color temperature via Home app
2191
+ */
2192
+ moveToColorTemperatureLogic: async (request: { targetMireds: number, transitionTime: number }) => {
2193
+ const { targetMireds, transitionTime } = request
2194
+ const kelvin = Math.round(1000000 / targetMireds)
2195
+
2196
+ log.info(`[Extended Color Light] Setting color temp to ${kelvin}K (${targetMireds} mireds)`)
2197
+ await myLightAPI.setColorTemperature(kelvin, transitionTime)
2198
+ },
2199
+ },
2200
+ }
2201
+ ```
2202
+
2203
+ </details>