@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.
- package/CHANGELOG.md +7 -0
- package/MATTER_API.md +1189 -182
- package/README.md +1 -1
- package/dist/devices/section-12-robotic/robotic-vacuum-cleaner.d.ts +9 -53
- package/dist/devices/section-12-robotic/robotic-vacuum-cleaner.js +12 -62
- package/dist/devices/section-12-robotic/robotic-vacuum-cleaner.js.map +1 -1
- package/dist/devices/section-4-lighting/color-temperature-light.d.ts +8 -1
- package/dist/devices/section-4-lighting/color-temperature-light.js +27 -23
- package/dist/devices/section-4-lighting/color-temperature-light.js.map +1 -1
- package/dist/devices/section-4-lighting/dimmable-light.d.ts +5 -22
- package/dist/devices/section-4-lighting/dimmable-light.js +40 -319
- package/dist/devices/section-4-lighting/dimmable-light.js.map +1 -1
- package/dist/devices/section-4-lighting/extended-color-light.d.ts +6 -25
- package/dist/devices/section-4-lighting/extended-color-light.js +33 -399
- package/dist/devices/section-4-lighting/extended-color-light.js.map +1 -1
- package/dist/devices/section-4-lighting/on-off-light.d.ts +8 -28
- package/dist/devices/section-4-lighting/on-off-light.js +64 -434
- package/dist/devices/section-4-lighting/on-off-light.js.map +1 -1
- package/dist/devices/section-5-smart-plugs/dimmable-plug-in-unit.d.ts +6 -0
- package/dist/devices/section-5-smart-plugs/dimmable-plug-in-unit.js +13 -3
- package/dist/devices/section-5-smart-plugs/dimmable-plug-in-unit.js.map +1 -1
- package/dist/devices/section-5-smart-plugs/on-off-plug-in-unit.d.ts +5 -0
- package/dist/devices/section-5-smart-plugs/on-off-plug-in-unit.js +9 -2
- package/dist/devices/section-5-smart-plugs/on-off-plug-in-unit.js.map +1 -1
- package/dist/devices/section-6-switches/on-off-light-switch.d.ts +5 -0
- package/dist/devices/section-6-switches/on-off-light-switch.js +19 -5
- package/dist/devices/section-6-switches/on-off-light-switch.js.map +1 -1
- package/dist/devices/section-7-sensors/contact-sensor.d.ts +5 -0
- package/dist/devices/section-7-sensors/contact-sensor.js +5 -0
- package/dist/devices/section-7-sensors/contact-sensor.js.map +1 -1
- package/dist/devices/section-7-sensors/humidity-sensor.d.ts +5 -0
- package/dist/devices/section-7-sensors/humidity-sensor.js +5 -0
- package/dist/devices/section-7-sensors/humidity-sensor.js.map +1 -1
- package/dist/devices/section-7-sensors/light-sensor.d.ts +5 -0
- package/dist/devices/section-7-sensors/light-sensor.js +5 -0
- package/dist/devices/section-7-sensors/light-sensor.js.map +1 -1
- package/dist/devices/section-7-sensors/occupancy-sensor.d.ts +6 -0
- package/dist/devices/section-7-sensors/occupancy-sensor.js +6 -0
- package/dist/devices/section-7-sensors/occupancy-sensor.js.map +1 -1
- package/dist/devices/section-7-sensors/smoke-co-alarm.d.ts +6 -0
- package/dist/devices/section-7-sensors/smoke-co-alarm.js +6 -0
- package/dist/devices/section-7-sensors/smoke-co-alarm.js.map +1 -1
- package/dist/devices/section-7-sensors/temperature-sensor.d.ts +5 -0
- package/dist/devices/section-7-sensors/temperature-sensor.js +5 -0
- package/dist/devices/section-7-sensors/temperature-sensor.js.map +1 -1
- package/dist/devices/section-7-sensors/water-leak-detector.d.ts +5 -0
- package/dist/devices/section-7-sensors/water-leak-detector.js +5 -0
- package/dist/devices/section-7-sensors/water-leak-detector.js.map +1 -1
- package/dist/devices/section-8-closure/door-lock.d.ts +6 -0
- package/dist/devices/section-8-closure/door-lock.js +12 -27
- package/dist/devices/section-8-closure/door-lock.js.map +1 -1
- package/dist/devices/section-8-closure/window-covering.d.ts +7 -0
- package/dist/devices/section-8-closure/window-covering.js +27 -43
- package/dist/devices/section-8-closure/window-covering.js.map +1 -1
- package/dist/devices/section-9-hvac/fan.d.ts +7 -0
- package/dist/devices/section-9-hvac/fan.js +17 -23
- package/dist/devices/section-9-hvac/fan.js.map +1 -1
- package/dist/devices/section-9-hvac/thermostat.d.ts +7 -0
- package/dist/devices/section-9-hvac/thermostat.js +21 -25
- package/dist/devices/section-9-hvac/thermostat.js.map +1 -1
- 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. [
|
|
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 },
|
|
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
|
-
|
|
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
|
|
585
|
+
const isOn = accessory.clusters.onOff.onOff // boolean
|
|
500
586
|
|
|
501
587
|
// Read brightness
|
|
502
|
-
const level = accessory.clusters.levelControl.currentLevel
|
|
503
|
-
const percent = Math.round((level / 254) * 100)
|
|
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
|
|
507
|
-
const saturation = accessory.clusters.colorControl.currentSaturation
|
|
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
|
|
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
|
|
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,
|
|
551
|
-
api.matter.clusterNames.OnOff,
|
|
552
|
-
{ onOff: true }
|
|
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
|
-
|
|
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)
|
|
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
|
|
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.
|
|
992
|
+
### 3. Don't Update the Same Attribute in Handlers
|
|
905
993
|
|
|
906
|
-
Handlers automatically update
|
|
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(
|
|
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
|
-
//
|
|
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
|
|
1032
|
+
Always throw errors from handlers so the Home app can be notified of failures:
|
|
933
1033
|
|
|
934
1034
|
```typescript
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
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
|
-
|
|
1068
|
+
**Why throw errors?**
|
|
944
1069
|
|
|
945
|
-
|
|
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
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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
|
-
|
|
1101
|
+
---
|
|
976
1102
|
|
|
977
|
-
|
|
1103
|
+
## API Reference
|
|
978
1104
|
|
|
979
|
-
|
|
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
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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
|
-
|
|
1131
|
+
#### `api.isMatterEnabled(): boolean`
|
|
993
1132
|
|
|
994
|
-
|
|
1133
|
+
Check if Matter is enabled for this bridge instance.
|
|
995
1134
|
|
|
996
|
-
|
|
1135
|
+
**Returns:** `true` if Matter is enabled in the bridge configuration
|
|
997
1136
|
|
|
998
|
-
**
|
|
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
|
-
**
|
|
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
|
-
**
|
|
1151
|
+
**When to use:**
|
|
1152
|
+
- Runtime checks before registering Matter accessories
|
|
1153
|
+
- Conditional accessory registration
|
|
1154
|
+
- User feedback about Matter status
|
|
1003
1155
|
|
|
1004
|
-
|
|
1156
|
+
---
|
|
1005
1157
|
|
|
1006
|
-
|
|
1158
|
+
### Matter API Properties
|
|
1007
1159
|
|
|
1008
|
-
|
|
1160
|
+
All properties are accessed via `api.matter.*`
|
|
1009
1161
|
|
|
1010
|
-
|
|
1162
|
+
#### `api.matter.uuid`
|
|
1011
1163
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1164
|
+
UUID generator for creating unique accessory identifiers (alias of `api.hap.uuid`).
|
|
1165
|
+
|
|
1166
|
+
**Type:** `HAP['uuid']`
|
|
1015
1167
|
|
|
1016
|
-
**
|
|
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
|
-
|
|
1020
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1045
|
-
- **ScenesManagement**: Enables scene support
|
|
1046
|
-
- **Groups**: Enables grouping with other devices
|
|
1047
|
-
- **OccupancySensing**: Can respond to occupancy sensors
|
|
1187
|
+
---
|
|
1048
1188
|
|
|
1049
|
-
####
|
|
1189
|
+
#### `api.matter.deviceTypes`
|
|
1050
1190
|
|
|
1051
|
-
|
|
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
|
-
|
|
1061
|
-
onOff: {
|
|
1062
|
-
onOff: false, // Initial state: off
|
|
1063
|
-
},
|
|
1064
|
-
},
|
|
1193
|
+
**Type:** `typeof deviceTypes` (from Matter.js)
|
|
1065
1194
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1086
|
-
api.matter.updateAccessoryState(
|
|
1087
|
-
accessory.uuid,
|
|
1088
|
-
api.matter.clusterNames.OnOff,
|
|
1089
|
-
{ onOff: deviceIsOn }
|
|
1090
|
-
)
|
|
1091
|
-
}
|
|
1092
|
-
})
|
|
1093
|
-
```
|
|
1215
|
+
---
|
|
1094
1216
|
|
|
1095
|
-
####
|
|
1217
|
+
#### `api.matter.clusters`
|
|
1096
1218
|
|
|
1097
|
-
|
|
1219
|
+
Direct access to Matter.js cluster definitions for advanced use cases.
|
|
1098
1220
|
|
|
1099
|
-
|
|
1100
|
-
// Reading state
|
|
1101
|
-
const isOn = accessory.clusters.onOff.onOff
|
|
1221
|
+
**Type:** `typeof clusters` (from Matter.js)
|
|
1102
1222
|
|
|
1103
|
-
|
|
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
|
-
|
|
1243
|
+
#### `api.matter.clusterNames`
|
|
1112
1244
|
|
|
1113
|
-
|
|
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
|
-
|
|
1249
|
+
**Available Names:**
|
|
1250
|
+
- `OnOff`, `LevelControl`, `ColorControl`
|
|
1251
|
+
- `DoorLock`, `WindowCovering`
|
|
1252
|
+
- `Thermostat`, `FanControl`
|
|
1253
|
+
- `TemperatureMeasurement`, `RelativeHumidityMeasurement`
|
|
1254
|
+
- And many more...
|
|
1121
1255
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
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
|
-
|
|
1278
|
+
#### `api.matter.types`
|
|
1135
1279
|
|
|
1136
|
-
|
|
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
|
-
//
|
|
1140
|
-
|
|
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
|
-
//
|
|
1143
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1160
|
-
const
|
|
1353
|
+
const PLUGIN_NAME = 'homebridge-example'
|
|
1354
|
+
const PLATFORM_NAME = 'ExamplePlatform'
|
|
1161
1355
|
|
|
1162
|
-
|
|
1163
|
-
|
|
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
|
-
|
|
1368
|
+
**When to use:**
|
|
1369
|
+
- Standard accessory registration
|
|
1370
|
+
- Multiple accessories on shared bridge
|
|
1371
|
+
- Most common use case
|
|
1167
1372
|
|
|
1168
|
-
|
|
1169
|
-
// To Mireds
|
|
1170
|
-
const mireds = Math.round(1000000 / kelvin)
|
|
1373
|
+
**See also:** `publishExternalAccessories()` for isolated accessories
|
|
1171
1374
|
|
|
1172
|
-
|
|
1173
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1180
|
-
const
|
|
1181
|
-
|
|
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
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
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
|
-
|
|
1428
|
+
**Parameters:**
|
|
1429
|
+
- `pluginIdentifier` - Plugin identifier
|
|
1430
|
+
- `accessories` - Array of accessories to publish externally
|
|
1189
1431
|
|
|
1432
|
+
**Usage:**
|
|
1190
1433
|
```typescript
|
|
1191
|
-
|
|
1192
|
-
|
|
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
|
-
//
|
|
1195
|
-
|
|
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>
|