matter-python-client 1.4.1a11.dev20261007__py3-none-any.whl → 1.5.0a0.dev20261013__py3-none-any.whl
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.
- chip/clusters/Objects.py +1 -0
- chip/clusters/cluster_defs/AmbientContextSensing.py +20 -18
- chip/clusters/cluster_defs/Globals.py +0 -2
- chip/clusters/cluster_defs/Groupcast.py +20 -20
- chip/clusters/cluster_defs/IcdManagement.py +2 -2
- chip/clusters/cluster_defs/IkeaValveCalibrationCluster.py +168 -0
- chip/clusters/cluster_defs/IlluminanceMeasurement.py +4 -4
- chip/clusters/cluster_defs/Thermostat.py +43 -32
- chip/clusters/cluster_defs/WebRtcTransportDefinitions.py +2 -2
- chip/clusters/cluster_defs/__init__.py +2 -0
- {matter_python_client-1.4.1a11.dev20261007.dist-info → matter_python_client-1.5.0a0.dev20261013.dist-info}/METADATA +1 -1
- {matter_python_client-1.4.1a11.dev20261007.dist-info → matter_python_client-1.5.0a0.dev20261013.dist-info}/RECORD +19 -18
- matter_server/client/client.py +9 -1
- matter_server/client/models/device_types.py +1 -0
- matter_server/client/models/node.py +245 -33
- matter_server/common/custom_clusters.py +3 -0
- matter_server/common/models.py +4 -0
- {matter_python_client-1.4.1a11.dev20261007.dist-info → matter_python_client-1.5.0a0.dev20261013.dist-info}/WHEEL +0 -0
- {matter_python_client-1.4.1a11.dev20261007.dist-info → matter_python_client-1.5.0a0.dev20261013.dist-info}/top_level.txt +0 -0
chip/clusters/Objects.py
CHANGED
|
@@ -12,7 +12,7 @@ from ...tlv import float32, uint
|
|
|
12
12
|
from ..ClusterObjects import (Cluster, ClusterAttributeDescriptor, ClusterCommand, ClusterEvent, ClusterObject,
|
|
13
13
|
ClusterObjectDescriptor, ClusterObjectFieldDescriptor)
|
|
14
14
|
from ..Types import Nullable, NullValue
|
|
15
|
-
from .
|
|
15
|
+
from .Globals import Globals
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@dataclass
|
|
@@ -27,8 +27,8 @@ class AmbientContextSensing(Cluster):
|
|
|
27
27
|
ClusterObjectFieldDescriptor(Label="objectIdentified", Tag=0x00000001, Type=typing.Optional[bool]),
|
|
28
28
|
ClusterObjectFieldDescriptor(Label="audioContextDetected", Tag=0x00000002, Type=typing.Optional[bool]),
|
|
29
29
|
ClusterObjectFieldDescriptor(Label="ambientContextType", Tag=0x00000003, Type=typing.Optional[typing.List[AmbientContextSensing.Structs.AmbientContextTypeStruct]]),
|
|
30
|
-
ClusterObjectFieldDescriptor(Label="ambientContextTypeSupported", Tag=0x00000004, Type=typing.Optional[typing.List[
|
|
31
|
-
ClusterObjectFieldDescriptor(Label="
|
|
30
|
+
ClusterObjectFieldDescriptor(Label="ambientContextTypeSupported", Tag=0x00000004, Type=typing.Optional[typing.List[Globals.Structs.semtag]]),
|
|
31
|
+
ClusterObjectFieldDescriptor(Label="objectCountThresholdReached", Tag=0x00000005, Type=typing.Optional[bool]),
|
|
32
32
|
ClusterObjectFieldDescriptor(Label="objectCountConfig", Tag=0x00000006, Type=typing.Optional[AmbientContextSensing.Structs.ObjectCountConfigStruct]),
|
|
33
33
|
ClusterObjectFieldDescriptor(Label="objectCount", Tag=0x00000007, Type=typing.Optional[uint]),
|
|
34
34
|
ClusterObjectFieldDescriptor(Label="simultaneousDetectionLimit", Tag=0x00000008, Type=typing.Optional[uint]),
|
|
@@ -46,8 +46,8 @@ class AmbientContextSensing(Cluster):
|
|
|
46
46
|
objectIdentified: typing.Optional[bool] = None
|
|
47
47
|
audioContextDetected: typing.Optional[bool] = None
|
|
48
48
|
ambientContextType: typing.Optional[typing.List[AmbientContextSensing.Structs.AmbientContextTypeStruct]] = None
|
|
49
|
-
ambientContextTypeSupported: typing.Optional[typing.List[
|
|
50
|
-
|
|
49
|
+
ambientContextTypeSupported: typing.Optional[typing.List[Globals.Structs.semtag]] = None
|
|
50
|
+
objectCountThresholdReached: typing.Optional[bool] = None
|
|
51
51
|
objectCountConfig: typing.Optional[AmbientContextSensing.Structs.ObjectCountConfigStruct] = None
|
|
52
52
|
objectCount: typing.Optional[uint] = None
|
|
53
53
|
simultaneousDetectionLimit: typing.Optional[uint] = None
|
|
@@ -90,10 +90,10 @@ class AmbientContextSensing(Cluster):
|
|
|
90
90
|
def descriptor(cls) -> ClusterObjectDescriptor:
|
|
91
91
|
return ClusterObjectDescriptor(
|
|
92
92
|
Fields=[
|
|
93
|
-
ClusterObjectFieldDescriptor(Label="ambientContextSensed", Tag=0, Type=typing.Optional[typing.List[
|
|
93
|
+
ClusterObjectFieldDescriptor(Label="ambientContextSensed", Tag=0, Type=typing.Optional[typing.List[Globals.Structs.semtag]]),
|
|
94
94
|
])
|
|
95
95
|
|
|
96
|
-
ambientContextSensed: typing.Optional[typing.List[
|
|
96
|
+
ambientContextSensed: typing.Optional[typing.List[Globals.Structs.semtag]] = None
|
|
97
97
|
|
|
98
98
|
@dataclass
|
|
99
99
|
class ObjectCountConfigStruct(ClusterObject):
|
|
@@ -101,11 +101,11 @@ class AmbientContextSensing(Cluster):
|
|
|
101
101
|
def descriptor(cls) -> ClusterObjectDescriptor:
|
|
102
102
|
return ClusterObjectDescriptor(
|
|
103
103
|
Fields=[
|
|
104
|
-
ClusterObjectFieldDescriptor(Label="countingObject", Tag=0, Type=typing.Optional[
|
|
104
|
+
ClusterObjectFieldDescriptor(Label="countingObject", Tag=0, Type=typing.Optional[Globals.Structs.semtag]),
|
|
105
105
|
ClusterObjectFieldDescriptor(Label="objectCountThreshold", Tag=1, Type=typing.Optional[uint]),
|
|
106
106
|
])
|
|
107
107
|
|
|
108
|
-
countingObject: typing.Optional[
|
|
108
|
+
countingObject: typing.Optional[Globals.Structs.semtag] = None
|
|
109
109
|
objectCountThreshold: typing.Optional[uint] = None
|
|
110
110
|
|
|
111
111
|
@dataclass
|
|
@@ -116,7 +116,7 @@ class AmbientContextSensing(Cluster):
|
|
|
116
116
|
Fields=[
|
|
117
117
|
ClusterObjectFieldDescriptor(Label="startTimestamp", Tag=0, Type=typing.Optional[uint]),
|
|
118
118
|
ClusterObjectFieldDescriptor(Label="endTimestamp", Tag=1, Type=typing.Optional[uint]),
|
|
119
|
-
ClusterObjectFieldDescriptor(Label="ambientContextType", Tag=2, Type=typing.Optional[typing.List[
|
|
119
|
+
ClusterObjectFieldDescriptor(Label="ambientContextType", Tag=2, Type=typing.Optional[typing.List[Globals.Structs.semtag]]),
|
|
120
120
|
ClusterObjectFieldDescriptor(Label="crowdDetected", Tag=3, Type=typing.Optional[bool]),
|
|
121
121
|
ClusterObjectFieldDescriptor(Label="crowdCount", Tag=4, Type=typing.Optional[uint]),
|
|
122
122
|
ClusterObjectFieldDescriptor(Label="confidence", Tag=5, Type=typing.Optional[uint]),
|
|
@@ -124,7 +124,7 @@ class AmbientContextSensing(Cluster):
|
|
|
124
124
|
|
|
125
125
|
startTimestamp: typing.Optional[uint] = None
|
|
126
126
|
endTimestamp: typing.Optional[uint] = None
|
|
127
|
-
ambientContextType: typing.Optional[typing.List[
|
|
127
|
+
ambientContextType: typing.Optional[typing.List[Globals.Structs.semtag]] = None
|
|
128
128
|
crowdDetected: typing.Optional[bool] = None
|
|
129
129
|
crowdCount: typing.Optional[uint] = None
|
|
130
130
|
confidence: typing.Optional[uint] = None
|
|
@@ -206,12 +206,12 @@ class AmbientContextSensing(Cluster):
|
|
|
206
206
|
|
|
207
207
|
@ChipUtility.classproperty
|
|
208
208
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
209
|
-
return ClusterObjectFieldDescriptor(Type=typing.Optional[typing.List[
|
|
209
|
+
return ClusterObjectFieldDescriptor(Type=typing.Optional[typing.List[Globals.Structs.semtag]])
|
|
210
210
|
|
|
211
|
-
value: typing.Optional[typing.List[
|
|
211
|
+
value: typing.Optional[typing.List[Globals.Structs.semtag]] = None
|
|
212
212
|
|
|
213
213
|
@dataclass
|
|
214
|
-
class
|
|
214
|
+
class ObjectCountThresholdReached(ClusterAttributeDescriptor):
|
|
215
215
|
@ChipUtility.classproperty
|
|
216
216
|
def cluster_id(cls) -> int:
|
|
217
217
|
return 0x00000431
|
|
@@ -418,12 +418,12 @@ class AmbientContextSensing(Cluster):
|
|
|
418
418
|
return ClusterObjectDescriptor(
|
|
419
419
|
Fields=[
|
|
420
420
|
ClusterObjectFieldDescriptor(Label="ambientContextDetected", Tag=0, Type=typing.Optional[AmbientContextSensing.Structs.AmbientContextTypeStruct]),
|
|
421
|
-
ClusterObjectFieldDescriptor(Label="
|
|
421
|
+
ClusterObjectFieldDescriptor(Label="objectCountThresholdReached", Tag=1, Type=typing.Optional[bool]),
|
|
422
422
|
ClusterObjectFieldDescriptor(Label="objectCount", Tag=2, Type=typing.Optional[uint]),
|
|
423
423
|
])
|
|
424
424
|
|
|
425
425
|
ambientContextDetected: typing.Optional[AmbientContextSensing.Structs.AmbientContextTypeStruct] = None
|
|
426
|
-
|
|
426
|
+
objectCountThresholdReached: typing.Optional[bool] = None
|
|
427
427
|
objectCount: typing.Optional[uint] = None
|
|
428
428
|
|
|
429
429
|
@dataclass
|
|
@@ -440,7 +440,9 @@ class AmbientContextSensing(Cluster):
|
|
|
440
440
|
def descriptor(cls) -> ClusterObjectDescriptor:
|
|
441
441
|
return ClusterObjectDescriptor(
|
|
442
442
|
Fields=[
|
|
443
|
-
ClusterObjectFieldDescriptor(Label="
|
|
443
|
+
ClusterObjectFieldDescriptor(Label="eventStartTimePos", Tag=0, Type=typing.Optional[uint]),
|
|
444
|
+
ClusterObjectFieldDescriptor(Label="eventStartTimeSys", Tag=1, Type=typing.Optional[uint]),
|
|
444
445
|
])
|
|
445
446
|
|
|
446
|
-
|
|
447
|
+
eventStartTimePos: typing.Optional[uint] = None
|
|
448
|
+
eventStartTimeSys: typing.Optional[uint] = None
|
|
@@ -198,7 +198,6 @@ class Globals:
|
|
|
198
198
|
kUnsupportedWrite = 0x88
|
|
199
199
|
kResourceExhausted = 0x89
|
|
200
200
|
kNotFound = 0x8B
|
|
201
|
-
kUnreportableAttribute = 0x8C
|
|
202
201
|
kInvalidDataType = 0x8D
|
|
203
202
|
kUnsupportedRead = 0x8F
|
|
204
203
|
kDataVersionMismatch = 0x92
|
|
@@ -207,7 +206,6 @@ class Globals:
|
|
|
207
206
|
kBusy = 0x9C
|
|
208
207
|
kAccessRestricted = 0x9D
|
|
209
208
|
kUnsupportedCluster = 0xC3
|
|
210
|
-
kNoUpstreamSubscription = 0xC5
|
|
211
209
|
kNeedsTimedInteraction = 0xC6
|
|
212
210
|
kUnsupportedEvent = 0xC7
|
|
213
211
|
kPathsExhausted = 0xC8
|
|
@@ -22,11 +22,11 @@ class Groupcast(Cluster):
|
|
|
22
22
|
def descriptor(cls) -> ClusterObjectDescriptor:
|
|
23
23
|
return ClusterObjectDescriptor(
|
|
24
24
|
Fields=[
|
|
25
|
-
ClusterObjectFieldDescriptor(Label="membership", Tag=0x00000000, Type=typing.
|
|
26
|
-
ClusterObjectFieldDescriptor(Label="maxMembershipCount", Tag=0x00000001, Type=
|
|
27
|
-
ClusterObjectFieldDescriptor(Label="maxMcastAddrCount", Tag=0x00000002, Type=
|
|
28
|
-
ClusterObjectFieldDescriptor(Label="usedMcastAddrCount", Tag=0x00000003, Type=
|
|
29
|
-
ClusterObjectFieldDescriptor(Label="fabricUnderTest", Tag=0x00000004, Type=
|
|
25
|
+
ClusterObjectFieldDescriptor(Label="membership", Tag=0x00000000, Type=typing.List[Groupcast.Structs.MembershipStruct]),
|
|
26
|
+
ClusterObjectFieldDescriptor(Label="maxMembershipCount", Tag=0x00000001, Type=uint),
|
|
27
|
+
ClusterObjectFieldDescriptor(Label="maxMcastAddrCount", Tag=0x00000002, Type=uint),
|
|
28
|
+
ClusterObjectFieldDescriptor(Label="usedMcastAddrCount", Tag=0x00000003, Type=uint),
|
|
29
|
+
ClusterObjectFieldDescriptor(Label="fabricUnderTest", Tag=0x00000004, Type=uint),
|
|
30
30
|
ClusterObjectFieldDescriptor(Label="generatedCommandList", Tag=0x0000FFF8, Type=typing.List[uint]),
|
|
31
31
|
ClusterObjectFieldDescriptor(Label="acceptedCommandList", Tag=0x0000FFF9, Type=typing.List[uint]),
|
|
32
32
|
ClusterObjectFieldDescriptor(Label="attributeList", Tag=0x0000FFFB, Type=typing.List[uint]),
|
|
@@ -34,11 +34,11 @@ class Groupcast(Cluster):
|
|
|
34
34
|
ClusterObjectFieldDescriptor(Label="clusterRevision", Tag=0x0000FFFD, Type=uint),
|
|
35
35
|
])
|
|
36
36
|
|
|
37
|
-
membership: typing.
|
|
38
|
-
maxMembershipCount:
|
|
39
|
-
maxMcastAddrCount:
|
|
40
|
-
usedMcastAddrCount:
|
|
41
|
-
fabricUnderTest:
|
|
37
|
+
membership: typing.List[Groupcast.Structs.MembershipStruct] = field(default_factory=lambda: [])
|
|
38
|
+
maxMembershipCount: uint = 0
|
|
39
|
+
maxMcastAddrCount: uint = 0
|
|
40
|
+
usedMcastAddrCount: uint = 0
|
|
41
|
+
fabricUnderTest: uint = 0
|
|
42
42
|
generatedCommandList: typing.List[uint] = field(default_factory=lambda: [])
|
|
43
43
|
acceptedCommandList: typing.List[uint] = field(default_factory=lambda: [])
|
|
44
44
|
attributeList: typing.List[uint] = field(default_factory=lambda: [])
|
|
@@ -240,9 +240,9 @@ class Groupcast(Cluster):
|
|
|
240
240
|
|
|
241
241
|
@ChipUtility.classproperty
|
|
242
242
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
243
|
-
return ClusterObjectFieldDescriptor(Type=typing.
|
|
243
|
+
return ClusterObjectFieldDescriptor(Type=typing.List[Groupcast.Structs.MembershipStruct])
|
|
244
244
|
|
|
245
|
-
value: typing.
|
|
245
|
+
value: typing.List[Groupcast.Structs.MembershipStruct] = field(default_factory=lambda: [])
|
|
246
246
|
|
|
247
247
|
@dataclass
|
|
248
248
|
class MaxMembershipCount(ClusterAttributeDescriptor):
|
|
@@ -256,9 +256,9 @@ class Groupcast(Cluster):
|
|
|
256
256
|
|
|
257
257
|
@ChipUtility.classproperty
|
|
258
258
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
259
|
-
return ClusterObjectFieldDescriptor(Type=
|
|
259
|
+
return ClusterObjectFieldDescriptor(Type=uint)
|
|
260
260
|
|
|
261
|
-
value:
|
|
261
|
+
value: uint = 0
|
|
262
262
|
|
|
263
263
|
@dataclass
|
|
264
264
|
class MaxMcastAddrCount(ClusterAttributeDescriptor):
|
|
@@ -272,9 +272,9 @@ class Groupcast(Cluster):
|
|
|
272
272
|
|
|
273
273
|
@ChipUtility.classproperty
|
|
274
274
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
275
|
-
return ClusterObjectFieldDescriptor(Type=
|
|
275
|
+
return ClusterObjectFieldDescriptor(Type=uint)
|
|
276
276
|
|
|
277
|
-
value:
|
|
277
|
+
value: uint = 0
|
|
278
278
|
|
|
279
279
|
@dataclass
|
|
280
280
|
class UsedMcastAddrCount(ClusterAttributeDescriptor):
|
|
@@ -288,9 +288,9 @@ class Groupcast(Cluster):
|
|
|
288
288
|
|
|
289
289
|
@ChipUtility.classproperty
|
|
290
290
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
291
|
-
return ClusterObjectFieldDescriptor(Type=
|
|
291
|
+
return ClusterObjectFieldDescriptor(Type=uint)
|
|
292
292
|
|
|
293
|
-
value:
|
|
293
|
+
value: uint = 0
|
|
294
294
|
|
|
295
295
|
@dataclass
|
|
296
296
|
class FabricUnderTest(ClusterAttributeDescriptor):
|
|
@@ -304,9 +304,9 @@ class Groupcast(Cluster):
|
|
|
304
304
|
|
|
305
305
|
@ChipUtility.classproperty
|
|
306
306
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
307
|
-
return ClusterObjectFieldDescriptor(Type=
|
|
307
|
+
return ClusterObjectFieldDescriptor(Type=uint)
|
|
308
308
|
|
|
309
|
-
value:
|
|
309
|
+
value: uint = 0
|
|
310
310
|
|
|
311
311
|
@dataclass
|
|
312
312
|
class GeneratedCommandList(ClusterAttributeDescriptor):
|
|
@@ -109,14 +109,14 @@ class IcdManagement(Cluster):
|
|
|
109
109
|
Fields=[
|
|
110
110
|
ClusterObjectFieldDescriptor(Label="checkInNodeID", Tag=1, Type=uint),
|
|
111
111
|
ClusterObjectFieldDescriptor(Label="monitoredSubject", Tag=2, Type=uint),
|
|
112
|
-
ClusterObjectFieldDescriptor(Label="key", Tag=3, Type=typing.Optional[
|
|
112
|
+
ClusterObjectFieldDescriptor(Label="key", Tag=3, Type=typing.Optional[bytes]),
|
|
113
113
|
ClusterObjectFieldDescriptor(Label="clientType", Tag=4, Type=IcdManagement.Enums.ClientTypeEnum),
|
|
114
114
|
ClusterObjectFieldDescriptor(Label="fabricIndex", Tag=254, Type=uint),
|
|
115
115
|
])
|
|
116
116
|
|
|
117
117
|
checkInNodeID: uint = 0
|
|
118
118
|
monitoredSubject: uint = 0
|
|
119
|
-
key: typing.Optional[
|
|
119
|
+
key: typing.Optional[bytes] = None
|
|
120
120
|
clientType: IcdManagement.Enums.ClientTypeEnum = 0
|
|
121
121
|
fabricIndex: uint = 0
|
|
122
122
|
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""IkeaValveCalibrationCluster cluster definition (auto-generated, DO NOT edit)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typing
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from enum import IntFlag
|
|
8
|
+
|
|
9
|
+
from ... import ChipUtility
|
|
10
|
+
from ...clusters.enum import MatterIntEnum
|
|
11
|
+
from ...tlv import float32, uint
|
|
12
|
+
from ..ClusterObjects import (Cluster, ClusterAttributeDescriptor, ClusterCommand, ClusterEvent, ClusterObject,
|
|
13
|
+
ClusterObjectDescriptor, ClusterObjectFieldDescriptor)
|
|
14
|
+
from ..Types import Nullable, NullValue
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class IkeaValveCalibrationCluster(Cluster):
|
|
19
|
+
id: typing.ClassVar[int] = 0x117CFC01
|
|
20
|
+
|
|
21
|
+
@ChipUtility.classproperty
|
|
22
|
+
def descriptor(cls) -> ClusterObjectDescriptor:
|
|
23
|
+
return ClusterObjectDescriptor(
|
|
24
|
+
Fields=[
|
|
25
|
+
ClusterObjectFieldDescriptor(Label="calibrationStatus", Tag=0x00000000, Type=typing.Optional[uint]),
|
|
26
|
+
ClusterObjectFieldDescriptor(Label="lastCalibrationError", Tag=0x00000001, Type=typing.Optional[uint]),
|
|
27
|
+
ClusterObjectFieldDescriptor(Label="generatedCommandList", Tag=0x0000FFF8, Type=typing.List[uint]),
|
|
28
|
+
ClusterObjectFieldDescriptor(Label="acceptedCommandList", Tag=0x0000FFF9, Type=typing.List[uint]),
|
|
29
|
+
ClusterObjectFieldDescriptor(Label="attributeList", Tag=0x0000FFFB, Type=typing.List[uint]),
|
|
30
|
+
ClusterObjectFieldDescriptor(Label="featureMap", Tag=0x0000FFFC, Type=uint),
|
|
31
|
+
ClusterObjectFieldDescriptor(Label="clusterRevision", Tag=0x0000FFFD, Type=uint),
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
calibrationStatus: typing.Optional[uint] = None
|
|
35
|
+
lastCalibrationError: typing.Optional[uint] = None
|
|
36
|
+
generatedCommandList: typing.List[uint] = field(default_factory=lambda: [])
|
|
37
|
+
acceptedCommandList: typing.List[uint] = field(default_factory=lambda: [])
|
|
38
|
+
attributeList: typing.List[uint] = field(default_factory=lambda: [])
|
|
39
|
+
featureMap: uint = 0
|
|
40
|
+
clusterRevision: uint = 0
|
|
41
|
+
|
|
42
|
+
class Commands:
|
|
43
|
+
@dataclass
|
|
44
|
+
class TriggerCalibration(ClusterCommand):
|
|
45
|
+
cluster_id: typing.ClassVar[int] = 0x117CFC01
|
|
46
|
+
command_id: typing.ClassVar[int] = 0x00000000
|
|
47
|
+
is_client: typing.ClassVar[bool] = True
|
|
48
|
+
response_type: typing.ClassVar[typing.Optional[str]] = None
|
|
49
|
+
|
|
50
|
+
@ChipUtility.classproperty
|
|
51
|
+
def descriptor(cls) -> ClusterObjectDescriptor:
|
|
52
|
+
return ClusterObjectDescriptor(
|
|
53
|
+
Fields=[
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Attributes:
|
|
58
|
+
@dataclass
|
|
59
|
+
class CalibrationStatus(ClusterAttributeDescriptor):
|
|
60
|
+
@ChipUtility.classproperty
|
|
61
|
+
def cluster_id(cls) -> int:
|
|
62
|
+
return 0x117CFC01
|
|
63
|
+
|
|
64
|
+
@ChipUtility.classproperty
|
|
65
|
+
def attribute_id(cls) -> int:
|
|
66
|
+
return 0x00000000
|
|
67
|
+
|
|
68
|
+
@ChipUtility.classproperty
|
|
69
|
+
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
70
|
+
return ClusterObjectFieldDescriptor(Type=typing.Optional[uint])
|
|
71
|
+
|
|
72
|
+
value: typing.Optional[uint] = None
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class LastCalibrationError(ClusterAttributeDescriptor):
|
|
76
|
+
@ChipUtility.classproperty
|
|
77
|
+
def cluster_id(cls) -> int:
|
|
78
|
+
return 0x117CFC01
|
|
79
|
+
|
|
80
|
+
@ChipUtility.classproperty
|
|
81
|
+
def attribute_id(cls) -> int:
|
|
82
|
+
return 0x00000001
|
|
83
|
+
|
|
84
|
+
@ChipUtility.classproperty
|
|
85
|
+
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
86
|
+
return ClusterObjectFieldDescriptor(Type=typing.Optional[uint])
|
|
87
|
+
|
|
88
|
+
value: typing.Optional[uint] = None
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class GeneratedCommandList(ClusterAttributeDescriptor):
|
|
92
|
+
@ChipUtility.classproperty
|
|
93
|
+
def cluster_id(cls) -> int:
|
|
94
|
+
return 0x117CFC01
|
|
95
|
+
|
|
96
|
+
@ChipUtility.classproperty
|
|
97
|
+
def attribute_id(cls) -> int:
|
|
98
|
+
return 0x0000FFF8
|
|
99
|
+
|
|
100
|
+
@ChipUtility.classproperty
|
|
101
|
+
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
102
|
+
return ClusterObjectFieldDescriptor(Type=typing.List[uint])
|
|
103
|
+
|
|
104
|
+
value: typing.List[uint] = field(default_factory=lambda: [])
|
|
105
|
+
|
|
106
|
+
@dataclass
|
|
107
|
+
class AcceptedCommandList(ClusterAttributeDescriptor):
|
|
108
|
+
@ChipUtility.classproperty
|
|
109
|
+
def cluster_id(cls) -> int:
|
|
110
|
+
return 0x117CFC01
|
|
111
|
+
|
|
112
|
+
@ChipUtility.classproperty
|
|
113
|
+
def attribute_id(cls) -> int:
|
|
114
|
+
return 0x0000FFF9
|
|
115
|
+
|
|
116
|
+
@ChipUtility.classproperty
|
|
117
|
+
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
118
|
+
return ClusterObjectFieldDescriptor(Type=typing.List[uint])
|
|
119
|
+
|
|
120
|
+
value: typing.List[uint] = field(default_factory=lambda: [])
|
|
121
|
+
|
|
122
|
+
@dataclass
|
|
123
|
+
class AttributeList(ClusterAttributeDescriptor):
|
|
124
|
+
@ChipUtility.classproperty
|
|
125
|
+
def cluster_id(cls) -> int:
|
|
126
|
+
return 0x117CFC01
|
|
127
|
+
|
|
128
|
+
@ChipUtility.classproperty
|
|
129
|
+
def attribute_id(cls) -> int:
|
|
130
|
+
return 0x0000FFFB
|
|
131
|
+
|
|
132
|
+
@ChipUtility.classproperty
|
|
133
|
+
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
134
|
+
return ClusterObjectFieldDescriptor(Type=typing.List[uint])
|
|
135
|
+
|
|
136
|
+
value: typing.List[uint] = field(default_factory=lambda: [])
|
|
137
|
+
|
|
138
|
+
@dataclass
|
|
139
|
+
class FeatureMap(ClusterAttributeDescriptor):
|
|
140
|
+
@ChipUtility.classproperty
|
|
141
|
+
def cluster_id(cls) -> int:
|
|
142
|
+
return 0x117CFC01
|
|
143
|
+
|
|
144
|
+
@ChipUtility.classproperty
|
|
145
|
+
def attribute_id(cls) -> int:
|
|
146
|
+
return 0x0000FFFC
|
|
147
|
+
|
|
148
|
+
@ChipUtility.classproperty
|
|
149
|
+
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
150
|
+
return ClusterObjectFieldDescriptor(Type=uint)
|
|
151
|
+
|
|
152
|
+
value: uint = 0
|
|
153
|
+
|
|
154
|
+
@dataclass
|
|
155
|
+
class ClusterRevision(ClusterAttributeDescriptor):
|
|
156
|
+
@ChipUtility.classproperty
|
|
157
|
+
def cluster_id(cls) -> int:
|
|
158
|
+
return 0x117CFC01
|
|
159
|
+
|
|
160
|
+
@ChipUtility.classproperty
|
|
161
|
+
def attribute_id(cls) -> int:
|
|
162
|
+
return 0x0000FFFD
|
|
163
|
+
|
|
164
|
+
@ChipUtility.classproperty
|
|
165
|
+
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
166
|
+
return ClusterObjectFieldDescriptor(Type=uint)
|
|
167
|
+
|
|
168
|
+
value: uint = 0
|
|
@@ -26,7 +26,7 @@ class IlluminanceMeasurement(Cluster):
|
|
|
26
26
|
ClusterObjectFieldDescriptor(Label="minMeasuredValue", Tag=0x00000001, Type=typing.Union[Nullable, uint]),
|
|
27
27
|
ClusterObjectFieldDescriptor(Label="maxMeasuredValue", Tag=0x00000002, Type=typing.Union[Nullable, uint]),
|
|
28
28
|
ClusterObjectFieldDescriptor(Label="tolerance", Tag=0x00000003, Type=typing.Optional[uint]),
|
|
29
|
-
ClusterObjectFieldDescriptor(Label="lightSensorType", Tag=0x00000004, Type=typing.Union[None, Nullable,
|
|
29
|
+
ClusterObjectFieldDescriptor(Label="lightSensorType", Tag=0x00000004, Type=typing.Union[None, Nullable, IlluminanceMeasurement.Enums.LightSensorTypeEnum]),
|
|
30
30
|
ClusterObjectFieldDescriptor(Label="generatedCommandList", Tag=0x0000FFF8, Type=typing.List[uint]),
|
|
31
31
|
ClusterObjectFieldDescriptor(Label="acceptedCommandList", Tag=0x0000FFF9, Type=typing.List[uint]),
|
|
32
32
|
ClusterObjectFieldDescriptor(Label="attributeList", Tag=0x0000FFFB, Type=typing.List[uint]),
|
|
@@ -38,7 +38,7 @@ class IlluminanceMeasurement(Cluster):
|
|
|
38
38
|
minMeasuredValue: typing.Union[Nullable, uint] = NullValue
|
|
39
39
|
maxMeasuredValue: typing.Union[Nullable, uint] = NullValue
|
|
40
40
|
tolerance: typing.Optional[uint] = None
|
|
41
|
-
lightSensorType: typing.Union[None, Nullable,
|
|
41
|
+
lightSensorType: typing.Union[None, Nullable, IlluminanceMeasurement.Enums.LightSensorTypeEnum] = None
|
|
42
42
|
generatedCommandList: typing.List[uint] = field(default_factory=lambda: [])
|
|
43
43
|
acceptedCommandList: typing.List[uint] = field(default_factory=lambda: [])
|
|
44
44
|
attributeList: typing.List[uint] = field(default_factory=lambda: [])
|
|
@@ -132,9 +132,9 @@ class IlluminanceMeasurement(Cluster):
|
|
|
132
132
|
|
|
133
133
|
@ChipUtility.classproperty
|
|
134
134
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
135
|
-
return ClusterObjectFieldDescriptor(Type=typing.Union[None, Nullable,
|
|
135
|
+
return ClusterObjectFieldDescriptor(Type=typing.Union[None, Nullable, IlluminanceMeasurement.Enums.LightSensorTypeEnum])
|
|
136
136
|
|
|
137
|
-
value: typing.Union[None, Nullable,
|
|
137
|
+
value: typing.Union[None, Nullable, IlluminanceMeasurement.Enums.LightSensorTypeEnum] = None
|
|
138
138
|
|
|
139
139
|
@dataclass
|
|
140
140
|
class GeneratedCommandList(ClusterAttributeDescriptor):
|
|
@@ -32,7 +32,7 @@ class Thermostat(Cluster):
|
|
|
32
32
|
ClusterObjectFieldDescriptor(Label="absMaxCoolSetpointLimit", Tag=0x00000006, Type=typing.Optional[int]),
|
|
33
33
|
ClusterObjectFieldDescriptor(Label="PICoolingDemand", Tag=0x00000007, Type=typing.Optional[uint]),
|
|
34
34
|
ClusterObjectFieldDescriptor(Label="PIHeatingDemand", Tag=0x00000008, Type=typing.Optional[uint]),
|
|
35
|
-
ClusterObjectFieldDescriptor(Label="HVACSystemTypeConfiguration", Tag=0x00000009, Type=typing.Optional[
|
|
35
|
+
ClusterObjectFieldDescriptor(Label="HVACSystemTypeConfiguration", Tag=0x00000009, Type=typing.Optional[Thermostat.Bitmaps.HVACSystemTypeBitmap]),
|
|
36
36
|
ClusterObjectFieldDescriptor(Label="localTemperatureCalibration", Tag=0x00000010, Type=typing.Optional[int]),
|
|
37
37
|
ClusterObjectFieldDescriptor(Label="occupiedCoolingSetpoint", Tag=0x00000011, Type=typing.Optional[int]),
|
|
38
38
|
ClusterObjectFieldDescriptor(Label="occupiedHeatingSetpoint", Tag=0x00000012, Type=typing.Optional[int]),
|
|
@@ -49,17 +49,17 @@ class Thermostat(Cluster):
|
|
|
49
49
|
ClusterObjectFieldDescriptor(Label="thermostatRunningMode", Tag=0x0000001E, Type=typing.Optional[Thermostat.Enums.ThermostatRunningModeEnum]),
|
|
50
50
|
ClusterObjectFieldDescriptor(Label="temperatureSetpointHold", Tag=0x00000023, Type=typing.Optional[Thermostat.Enums.TemperatureSetpointHoldEnum]),
|
|
51
51
|
ClusterObjectFieldDescriptor(Label="temperatureSetpointHoldDuration", Tag=0x00000024, Type=typing.Union[None, Nullable, uint]),
|
|
52
|
-
ClusterObjectFieldDescriptor(Label="thermostatProgrammingOperationMode", Tag=0x00000025, Type=typing.Optional[
|
|
52
|
+
ClusterObjectFieldDescriptor(Label="thermostatProgrammingOperationMode", Tag=0x00000025, Type=typing.Optional[Thermostat.Bitmaps.ProgrammingOperationModeBitmap]),
|
|
53
53
|
ClusterObjectFieldDescriptor(Label="thermostatRunningState", Tag=0x00000029, Type=typing.Optional[Thermostat.Bitmaps.RelayStateBitmap]),
|
|
54
54
|
ClusterObjectFieldDescriptor(Label="setpointChangeSource", Tag=0x00000030, Type=typing.Optional[Thermostat.Enums.SetpointChangeSourceEnum]),
|
|
55
55
|
ClusterObjectFieldDescriptor(Label="setpointChangeAmount", Tag=0x00000031, Type=typing.Union[None, Nullable, int]),
|
|
56
56
|
ClusterObjectFieldDescriptor(Label="setpointChangeSourceTimestamp", Tag=0x00000032, Type=typing.Optional[uint]),
|
|
57
|
-
ClusterObjectFieldDescriptor(Label="occupiedSetback", Tag=0x00000034, Type=typing.
|
|
58
|
-
ClusterObjectFieldDescriptor(Label="occupiedSetbackMin", Tag=0x00000035, Type=typing.
|
|
59
|
-
ClusterObjectFieldDescriptor(Label="occupiedSetbackMax", Tag=0x00000036, Type=typing.
|
|
60
|
-
ClusterObjectFieldDescriptor(Label="unoccupiedSetback", Tag=0x00000037, Type=typing.
|
|
61
|
-
ClusterObjectFieldDescriptor(Label="unoccupiedSetbackMin", Tag=0x00000038, Type=typing.
|
|
62
|
-
ClusterObjectFieldDescriptor(Label="unoccupiedSetbackMax", Tag=0x00000039, Type=typing.
|
|
57
|
+
ClusterObjectFieldDescriptor(Label="occupiedSetback", Tag=0x00000034, Type=typing.Union[None, Nullable, uint]),
|
|
58
|
+
ClusterObjectFieldDescriptor(Label="occupiedSetbackMin", Tag=0x00000035, Type=typing.Union[None, Nullable, uint]),
|
|
59
|
+
ClusterObjectFieldDescriptor(Label="occupiedSetbackMax", Tag=0x00000036, Type=typing.Union[None, Nullable, uint]),
|
|
60
|
+
ClusterObjectFieldDescriptor(Label="unoccupiedSetback", Tag=0x00000037, Type=typing.Union[None, Nullable, uint]),
|
|
61
|
+
ClusterObjectFieldDescriptor(Label="unoccupiedSetbackMin", Tag=0x00000038, Type=typing.Union[None, Nullable, uint]),
|
|
62
|
+
ClusterObjectFieldDescriptor(Label="unoccupiedSetbackMax", Tag=0x00000039, Type=typing.Union[None, Nullable, uint]),
|
|
63
63
|
ClusterObjectFieldDescriptor(Label="emergencyHeatDelta", Tag=0x0000003A, Type=typing.Optional[uint]),
|
|
64
64
|
ClusterObjectFieldDescriptor(Label="ACType", Tag=0x00000040, Type=typing.Optional[Thermostat.Enums.ACTypeEnum]),
|
|
65
65
|
ClusterObjectFieldDescriptor(Label="ACCapacity", Tag=0x00000041, Type=typing.Optional[uint]),
|
|
@@ -100,7 +100,7 @@ class Thermostat(Cluster):
|
|
|
100
100
|
absMaxCoolSetpointLimit: typing.Optional[int] = None
|
|
101
101
|
PICoolingDemand: typing.Optional[uint] = None
|
|
102
102
|
PIHeatingDemand: typing.Optional[uint] = None
|
|
103
|
-
HVACSystemTypeConfiguration: typing.Optional[
|
|
103
|
+
HVACSystemTypeConfiguration: typing.Optional[Thermostat.Bitmaps.HVACSystemTypeBitmap] = None
|
|
104
104
|
localTemperatureCalibration: typing.Optional[int] = None
|
|
105
105
|
occupiedCoolingSetpoint: typing.Optional[int] = None
|
|
106
106
|
occupiedHeatingSetpoint: typing.Optional[int] = None
|
|
@@ -117,17 +117,17 @@ class Thermostat(Cluster):
|
|
|
117
117
|
thermostatRunningMode: typing.Optional[Thermostat.Enums.ThermostatRunningModeEnum] = None
|
|
118
118
|
temperatureSetpointHold: typing.Optional[Thermostat.Enums.TemperatureSetpointHoldEnum] = None
|
|
119
119
|
temperatureSetpointHoldDuration: typing.Union[None, Nullable, uint] = None
|
|
120
|
-
thermostatProgrammingOperationMode: typing.Optional[
|
|
120
|
+
thermostatProgrammingOperationMode: typing.Optional[Thermostat.Bitmaps.ProgrammingOperationModeBitmap] = None
|
|
121
121
|
thermostatRunningState: typing.Optional[Thermostat.Bitmaps.RelayStateBitmap] = None
|
|
122
122
|
setpointChangeSource: typing.Optional[Thermostat.Enums.SetpointChangeSourceEnum] = None
|
|
123
123
|
setpointChangeAmount: typing.Union[None, Nullable, int] = None
|
|
124
124
|
setpointChangeSourceTimestamp: typing.Optional[uint] = None
|
|
125
|
-
occupiedSetback: typing.
|
|
126
|
-
occupiedSetbackMin: typing.
|
|
127
|
-
occupiedSetbackMax: typing.
|
|
128
|
-
unoccupiedSetback: typing.
|
|
129
|
-
unoccupiedSetbackMin: typing.
|
|
130
|
-
unoccupiedSetbackMax: typing.
|
|
125
|
+
occupiedSetback: typing.Union[None, Nullable, uint] = None
|
|
126
|
+
occupiedSetbackMin: typing.Union[None, Nullable, uint] = None
|
|
127
|
+
occupiedSetbackMax: typing.Union[None, Nullable, uint] = None
|
|
128
|
+
unoccupiedSetback: typing.Union[None, Nullable, uint] = None
|
|
129
|
+
unoccupiedSetbackMin: typing.Union[None, Nullable, uint] = None
|
|
130
|
+
unoccupiedSetbackMax: typing.Union[None, Nullable, uint] = None
|
|
131
131
|
emergencyHeatDelta: typing.Optional[uint] = None
|
|
132
132
|
ACType: typing.Optional[Thermostat.Enums.ACTypeEnum] = None
|
|
133
133
|
ACCapacity: typing.Optional[uint] = None
|
|
@@ -380,6 +380,17 @@ class Thermostat(Cluster):
|
|
|
380
380
|
kPreCoolingOrPreHeating = 0x40
|
|
381
381
|
kConflictingSuggestions = 0x80
|
|
382
382
|
|
|
383
|
+
class HVACSystemTypeBitmap(IntFlag):
|
|
384
|
+
kCoolingStage = 0x3
|
|
385
|
+
kHeatingStage = 0xC
|
|
386
|
+
kHeatingIsHeatPump = 0x10
|
|
387
|
+
kHeatingUsesFuel = 0x20
|
|
388
|
+
|
|
389
|
+
class ProgrammingOperationModeBitmap(IntFlag):
|
|
390
|
+
kScheduleActive = 0x1
|
|
391
|
+
kAutoRecovery = 0x2
|
|
392
|
+
kEconomy = 0x4
|
|
393
|
+
|
|
383
394
|
class Structs:
|
|
384
395
|
@dataclass
|
|
385
396
|
class PresetStruct(ClusterObject):
|
|
@@ -806,9 +817,9 @@ class Thermostat(Cluster):
|
|
|
806
817
|
|
|
807
818
|
@ChipUtility.classproperty
|
|
808
819
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
809
|
-
return ClusterObjectFieldDescriptor(Type=typing.Optional[
|
|
820
|
+
return ClusterObjectFieldDescriptor(Type=typing.Optional[Thermostat.Bitmaps.HVACSystemTypeBitmap])
|
|
810
821
|
|
|
811
|
-
value: typing.Optional[
|
|
822
|
+
value: typing.Optional[Thermostat.Bitmaps.HVACSystemTypeBitmap] = None
|
|
812
823
|
|
|
813
824
|
@dataclass
|
|
814
825
|
class LocalTemperatureCalibration(ClusterAttributeDescriptor):
|
|
@@ -1078,9 +1089,9 @@ class Thermostat(Cluster):
|
|
|
1078
1089
|
|
|
1079
1090
|
@ChipUtility.classproperty
|
|
1080
1091
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
1081
|
-
return ClusterObjectFieldDescriptor(Type=typing.Optional[
|
|
1092
|
+
return ClusterObjectFieldDescriptor(Type=typing.Optional[Thermostat.Bitmaps.ProgrammingOperationModeBitmap])
|
|
1082
1093
|
|
|
1083
|
-
value: typing.Optional[
|
|
1094
|
+
value: typing.Optional[Thermostat.Bitmaps.ProgrammingOperationModeBitmap] = None
|
|
1084
1095
|
|
|
1085
1096
|
@dataclass
|
|
1086
1097
|
class ThermostatRunningState(ClusterAttributeDescriptor):
|
|
@@ -1158,9 +1169,9 @@ class Thermostat(Cluster):
|
|
|
1158
1169
|
|
|
1159
1170
|
@ChipUtility.classproperty
|
|
1160
1171
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
1161
|
-
return ClusterObjectFieldDescriptor(Type=typing.
|
|
1172
|
+
return ClusterObjectFieldDescriptor(Type=typing.Union[None, Nullable, uint])
|
|
1162
1173
|
|
|
1163
|
-
value: typing.
|
|
1174
|
+
value: typing.Union[None, Nullable, uint] = None
|
|
1164
1175
|
|
|
1165
1176
|
@dataclass
|
|
1166
1177
|
class OccupiedSetbackMin(ClusterAttributeDescriptor):
|
|
@@ -1174,9 +1185,9 @@ class Thermostat(Cluster):
|
|
|
1174
1185
|
|
|
1175
1186
|
@ChipUtility.classproperty
|
|
1176
1187
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
1177
|
-
return ClusterObjectFieldDescriptor(Type=typing.
|
|
1188
|
+
return ClusterObjectFieldDescriptor(Type=typing.Union[None, Nullable, uint])
|
|
1178
1189
|
|
|
1179
|
-
value: typing.
|
|
1190
|
+
value: typing.Union[None, Nullable, uint] = None
|
|
1180
1191
|
|
|
1181
1192
|
@dataclass
|
|
1182
1193
|
class OccupiedSetbackMax(ClusterAttributeDescriptor):
|
|
@@ -1190,9 +1201,9 @@ class Thermostat(Cluster):
|
|
|
1190
1201
|
|
|
1191
1202
|
@ChipUtility.classproperty
|
|
1192
1203
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
1193
|
-
return ClusterObjectFieldDescriptor(Type=typing.
|
|
1204
|
+
return ClusterObjectFieldDescriptor(Type=typing.Union[None, Nullable, uint])
|
|
1194
1205
|
|
|
1195
|
-
value: typing.
|
|
1206
|
+
value: typing.Union[None, Nullable, uint] = None
|
|
1196
1207
|
|
|
1197
1208
|
@dataclass
|
|
1198
1209
|
class UnoccupiedSetback(ClusterAttributeDescriptor):
|
|
@@ -1206,9 +1217,9 @@ class Thermostat(Cluster):
|
|
|
1206
1217
|
|
|
1207
1218
|
@ChipUtility.classproperty
|
|
1208
1219
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
1209
|
-
return ClusterObjectFieldDescriptor(Type=typing.
|
|
1220
|
+
return ClusterObjectFieldDescriptor(Type=typing.Union[None, Nullable, uint])
|
|
1210
1221
|
|
|
1211
|
-
value: typing.
|
|
1222
|
+
value: typing.Union[None, Nullable, uint] = None
|
|
1212
1223
|
|
|
1213
1224
|
@dataclass
|
|
1214
1225
|
class UnoccupiedSetbackMin(ClusterAttributeDescriptor):
|
|
@@ -1222,9 +1233,9 @@ class Thermostat(Cluster):
|
|
|
1222
1233
|
|
|
1223
1234
|
@ChipUtility.classproperty
|
|
1224
1235
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
1225
|
-
return ClusterObjectFieldDescriptor(Type=typing.
|
|
1236
|
+
return ClusterObjectFieldDescriptor(Type=typing.Union[None, Nullable, uint])
|
|
1226
1237
|
|
|
1227
|
-
value: typing.
|
|
1238
|
+
value: typing.Union[None, Nullable, uint] = None
|
|
1228
1239
|
|
|
1229
1240
|
@dataclass
|
|
1230
1241
|
class UnoccupiedSetbackMax(ClusterAttributeDescriptor):
|
|
@@ -1238,9 +1249,9 @@ class Thermostat(Cluster):
|
|
|
1238
1249
|
|
|
1239
1250
|
@ChipUtility.classproperty
|
|
1240
1251
|
def attribute_type(cls) -> ClusterObjectFieldDescriptor:
|
|
1241
|
-
return ClusterObjectFieldDescriptor(Type=typing.
|
|
1252
|
+
return ClusterObjectFieldDescriptor(Type=typing.Union[None, Nullable, uint])
|
|
1242
1253
|
|
|
1243
|
-
value: typing.
|
|
1254
|
+
value: typing.Union[None, Nullable, uint] = None
|
|
1244
1255
|
|
|
1245
1256
|
@dataclass
|
|
1246
1257
|
class EmergencyHeatDelta(ClusterAttributeDescriptor):
|
|
@@ -44,13 +44,13 @@ class WebRtcTransportDefinitions:
|
|
|
44
44
|
def descriptor(cls) -> ClusterObjectDescriptor:
|
|
45
45
|
return ClusterObjectDescriptor(
|
|
46
46
|
Fields=[
|
|
47
|
-
ClusterObjectFieldDescriptor(Label="
|
|
47
|
+
ClusterObjectFieldDescriptor(Label="URLs", Tag=0, Type=typing.List[str]),
|
|
48
48
|
ClusterObjectFieldDescriptor(Label="username", Tag=1, Type=typing.Optional[str]),
|
|
49
49
|
ClusterObjectFieldDescriptor(Label="credential", Tag=2, Type=typing.Optional[str]),
|
|
50
50
|
ClusterObjectFieldDescriptor(Label="caid", Tag=3, Type=typing.Optional[uint]),
|
|
51
51
|
])
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
URLs: typing.List[str] = field(default_factory=lambda: [])
|
|
54
54
|
username: typing.Optional[str] = None
|
|
55
55
|
credential: typing.Optional[str] = None
|
|
56
56
|
caid: typing.Optional[uint] = None
|
|
@@ -65,6 +65,7 @@ from .HeimanCluster import HeimanCluster
|
|
|
65
65
|
from .HepaFilterMonitoring import HepaFilterMonitoring
|
|
66
66
|
from .IcdManagement import IcdManagement
|
|
67
67
|
from .Identify import Identify
|
|
68
|
+
from .IkeaValveCalibrationCluster import IkeaValveCalibrationCluster
|
|
68
69
|
from .IlluminanceMeasurement import IlluminanceMeasurement
|
|
69
70
|
from .InovelliCluster import InovelliCluster
|
|
70
71
|
from .JointFabricAdministrator import JointFabricAdministrator
|
|
@@ -215,6 +216,7 @@ __all__ = [
|
|
|
215
216
|
"HepaFilterMonitoring",
|
|
216
217
|
"IcdManagement",
|
|
217
218
|
"Identify",
|
|
219
|
+
"IkeaValveCalibrationCluster",
|
|
218
220
|
"IlluminanceMeasurement",
|
|
219
221
|
"InovelliCluster",
|
|
220
222
|
"JointFabricAdministrator",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
chip/ChipUtility.py,sha256=pD_krwMaHGww1OPAVJWGli5Sc9yjlVxrKE2N-3Yeerc,279
|
|
2
2
|
chip/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
chip/clusters/ClusterObjects.py,sha256=6NDOEUl_zu5KjN-_32_xIrMl7y1M5X1XDZ4eYvVCiSo,11655
|
|
4
|
-
chip/clusters/Objects.py,sha256=
|
|
4
|
+
chip/clusters/Objects.py,sha256=GRhlj14gA09nxGAsL4D7nEJXgzc1ENDAasJO1YS1veY,4953
|
|
5
5
|
chip/clusters/Types.py,sha256=jWwhYCFH5S1JV78wkrXSAoOrDZs29miTL8heZq3QHhU,759
|
|
6
6
|
chip/clusters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
chip/clusters/enum.py,sha256=ZldcWGjqzfVvWFWOFr_nCgtiCVyQxD6MKiptle_i0O4,1660
|
|
@@ -11,7 +11,7 @@ chip/clusters/cluster_defs/Actions.py,sha256=M4HX7L-pWu2a64AxPMayYHksVLC8NRhHerp
|
|
|
11
11
|
chip/clusters/cluster_defs/ActivatedCarbonFilterMonitoring.py,sha256=RTf_nI8lVTwSyIGUMiaS5Mrk5YjlG4ATr2jOYdYEBaA,12240
|
|
12
12
|
chip/clusters/cluster_defs/AdministratorCommissioning.py,sha256=ymeGLlOeEAFjeONxBHG1V2qInYkXS1i87vPUmSfM6Z8,10556
|
|
13
13
|
chip/clusters/cluster_defs/AirQuality.py,sha256=8tEIDVOkJFfOSi_sq5n_tV-Azft9L_X0BFFnl9tizXQ,5830
|
|
14
|
-
chip/clusters/cluster_defs/AmbientContextSensing.py,sha256=
|
|
14
|
+
chip/clusters/cluster_defs/AmbientContextSensing.py,sha256=Vq94BPgCRtkkYdvauDYua90_XQ8E8veL5sqNDrlx-0E,19612
|
|
15
15
|
chip/clusters/cluster_defs/ApplicationBasic.py,sha256=s9u_hOIlZOG09AAwu7Pfy5IUXkpXSrwJf-pPAmKWJwY,11261
|
|
16
16
|
chip/clusters/cluster_defs/ApplicationLauncher.py,sha256=jaaVtlpvEiRIGXKZ6UqbVWz7QiZfGyvvBjvtT1SAp1M,11277
|
|
17
17
|
chip/clusters/cluster_defs/AqaraAmbientSensingConfigurationCluster.py,sha256=MB4f_eh1HNlu_bJibmW_-lMnzI9bT_wGKl0fo1sxPaA,30123
|
|
@@ -62,15 +62,16 @@ chip/clusters/cluster_defs/FlowMeasurement.py,sha256=Jvzf_fPK4OkVOiKLv1CPEZro-sl
|
|
|
62
62
|
chip/clusters/cluster_defs/FormaldehydeConcentrationMeasurement.py,sha256=oJcn3RJgAaiIxHRQWmyGPOab7YUU5adrEiT7BRELP7M,15078
|
|
63
63
|
chip/clusters/cluster_defs/GeneralCommissioning.py,sha256=-wzrr7lasuH6cRJfq48jb2y22uCH8gHaT8qXl3dX1lk,23513
|
|
64
64
|
chip/clusters/cluster_defs/GeneralDiagnostics.py,sha256=-hH3fIn4AI_UVQLDIBbK3QLwX2ncMVO-Nnrg0VoVkGo,26405
|
|
65
|
-
chip/clusters/cluster_defs/Globals.py,sha256=
|
|
65
|
+
chip/clusters/cluster_defs/Globals.py,sha256=dSoc8yRrEg_-KHPC0uaQxgbl4MkqtibIhrfBWPu36Qs,18254
|
|
66
66
|
chip/clusters/cluster_defs/GroupKeyManagement.py,sha256=RYwByuqdwdk-J0PrS-M1ej6K68vIgxgWXcn_k4i7-54,18040
|
|
67
|
-
chip/clusters/cluster_defs/Groupcast.py,sha256=
|
|
67
|
+
chip/clusters/cluster_defs/Groupcast.py,sha256=KOtUlFNWmdWs67KQblP95lidkckjwUPdcWRG-JITo4U,18576
|
|
68
68
|
chip/clusters/cluster_defs/Groups.py,sha256=h3cOEjMXNZ8gRDpygOul58OlHRdDd14nF3ZMf8dFdBs,12613
|
|
69
69
|
chip/clusters/cluster_defs/HeimanCluster.py,sha256=LNWZT3VZ5KwMDzXZP1i7bgp0XXTAk2Y8j9UqP-PzxC4,9940
|
|
70
70
|
chip/clusters/cluster_defs/HepaFilterMonitoring.py,sha256=vT9GneKzd9izTSH3tl6IMjPbyVVrzBY9FKZza6Jb7NI,12064
|
|
71
|
-
chip/clusters/cluster_defs/IcdManagement.py,sha256=
|
|
71
|
+
chip/clusters/cluster_defs/IcdManagement.py,sha256=aMylsvfRVy1sS6jey3Y22x9mzFz18U5_1R_DsPTF9Ko,18833
|
|
72
72
|
chip/clusters/cluster_defs/Identify.py,sha256=XMFOZpvtU6m3bOaZSuL9G0fdrAUfvvdSHIW8tCFlLqM,8829
|
|
73
|
-
chip/clusters/cluster_defs/
|
|
73
|
+
chip/clusters/cluster_defs/IkeaValveCalibrationCluster.py,sha256=9P3k8dxYNKGEF_L4hozqA4BAgw7E0UldjHVD79t22Vo,6338
|
|
74
|
+
chip/clusters/cluster_defs/IlluminanceMeasurement.py,sha256=y5PRkKKjCjP5vw7xoUAM1ojPx381QJV3Hm7zHWuehFI,8704
|
|
74
75
|
chip/clusters/cluster_defs/InovelliCluster.py,sha256=coQSGJPJW6zlr_WJjw3aP6vU9YODEd7f35vqP9FgYcA,6587
|
|
75
76
|
chip/clusters/cluster_defs/JointFabricAdministrator.py,sha256=SuCVMyrT_7VAXTic1QqLKeP5u7_t6Mb32Fu7_AfZHzo,13258
|
|
76
77
|
chip/clusters/cluster_defs/JointFabricDatastore.py,sha256=DOyq40rnUhJdsKF0PD0l6thKXEUvzKqpXXmXrkwWUsQ,50708
|
|
@@ -127,7 +128,7 @@ chip/clusters/cluster_defs/TclDehumidifierCluster.py,sha256=Hy0Rq5B0-DEejiilz3gF
|
|
|
127
128
|
chip/clusters/cluster_defs/TemperatureAlarm.py,sha256=gKNDRarQlSyH58tDlWaIeqV1Wtc7YkeQLzlAcxgdx_A,17059
|
|
128
129
|
chip/clusters/cluster_defs/TemperatureControl.py,sha256=dTnpNOrQOWCqrF-ckzlEWZOWdayPsNvBgVxil04uy4o,9724
|
|
129
130
|
chip/clusters/cluster_defs/TemperatureMeasurement.py,sha256=zhUXDFDk1jzSzVkv8KL08Wyz-i_vFhNAUVdQo19P0RY,7282
|
|
130
|
-
chip/clusters/cluster_defs/Thermostat.py,sha256=
|
|
131
|
+
chip/clusters/cluster_defs/Thermostat.py,sha256=VpCO3AS8ecsHqSTNN9V0gBkDNAWC9UNephkKy_yaYvU,83431
|
|
131
132
|
chip/clusters/cluster_defs/ThermostatUserInterfaceConfiguration.py,sha256=Vb8fzuE2X1uv7SlTTixaMsEwrL2MC6nNm2mo76NJ-Cc,8755
|
|
132
133
|
chip/clusters/cluster_defs/ThirdRealityMeteringCluster.py,sha256=k22CR0g42A3UIQuUQV-23z0a8zYo6d-tq8DmFueXshA,7207
|
|
133
134
|
chip/clusters/cluster_defs/ThreadBorderRouterManagement.py,sha256=z6j_zRjEAe-211JqQzDcB6BX-q3f3rp7W6OHOeJQK5I,11964
|
|
@@ -146,35 +147,35 @@ chip/clusters/cluster_defs/WakeOnLan.py,sha256=yvXBZXs6c14fBqSNzHiFIPQCSRZKbw3Ta
|
|
|
146
147
|
chip/clusters/cluster_defs/WaterHeaterManagement.py,sha256=d4VKVKZYbiVFAjpszZek1EFKi1OZwmfJ2AiSoA8gDpU,13619
|
|
147
148
|
chip/clusters/cluster_defs/WaterHeaterMode.py,sha256=kEaoxPvKn1qIeZ8f60qYxGXrwfRBGVB5U5rp99es45g,11532
|
|
148
149
|
chip/clusters/cluster_defs/WaterTankLevelMonitoring.py,sha256=q6CCsmYzPlkbRyC6Hbl2Qn00PSicpsCxl_LrUBo9izE,12128
|
|
149
|
-
chip/clusters/cluster_defs/WebRtcTransportDefinitions.py,sha256=
|
|
150
|
+
chip/clusters/cluster_defs/WebRtcTransportDefinitions.py,sha256=nJdqnE95A8yp2OgDNh6NOKCDxujwaQUdWgbhTEJMo84,4904
|
|
150
151
|
chip/clusters/cluster_defs/WebRtcTransportProvider.py,sha256=4Wf-bEBMuQ7L2l1seO6EMKWltR5-weC9jewnEmQMSao,15122
|
|
151
152
|
chip/clusters/cluster_defs/WebRtcTransportRequestor.py,sha256=4w-83xo38CJgNDMzq0edlIdp87czjNpKwuj8Z6owI9o,9113
|
|
152
153
|
chip/clusters/cluster_defs/WiFiNetworkDiagnostics.py,sha256=X6fWBeCXDk3t1Y_95gkNDiD9lVNxx4IyuOYDoGjvCM8,19473
|
|
153
154
|
chip/clusters/cluster_defs/WiFiNetworkManagement.py,sha256=eBmysZ7TEuUCxPsrsNc22dGxnNXE0SHS4Oh43Y5fTdE,7041
|
|
154
155
|
chip/clusters/cluster_defs/WindowCovering.py,sha256=zMD343RPMoUBnw1bQ2brRNNZ9gsMukjEp1EslcyxEBc,24267
|
|
155
156
|
chip/clusters/cluster_defs/ZoneManagement.py,sha256=d-ZCgnpaGCB74KNUW9Q7wu23kaBFZr3C5oK_VSAIXLE,21789
|
|
156
|
-
chip/clusters/cluster_defs/__init__.py,sha256=
|
|
157
|
+
chip/clusters/cluster_defs/__init__.py,sha256=5In7gjC5jk0b_nsdpy0NPofRhNLyS7Pnr6O7h8SwC5w,11962
|
|
157
158
|
chip/tlv/__init__.py,sha256=24xl0LIaL_f_ciqdaLwZCtDxkJzMgpRAx1QbHCw4Kic,13487
|
|
158
159
|
chip/tlv/tlvlist.py,sha256=ZpT6Hr6t3qDHiUZWkY4WnioVP2icv5i5LIKNUIgOOps,2469
|
|
159
160
|
matter_server/__init__.py,sha256=lJ6xyVLSMGtiHvq36VPSDb75ksxfNKYmKEEE-ZwJdhQ,47
|
|
160
161
|
matter_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
161
162
|
matter_server/client/__init__.py,sha256=pZzjJp-7C5BmYGZJjIo5SpyjGLU0FcqBa2nJ558y-Is,97
|
|
162
|
-
matter_server/client/client.py,sha256=
|
|
163
|
+
matter_server/client/client.py,sha256=HgBEEix4or5y9ktrEiDV-aQCJ2DEOXJcUZ7e-JF6oj0,40822
|
|
163
164
|
matter_server/client/connection.py,sha256=Fu_9xShWBcUUaKUAf7XWfi3Mm3AUs4-ohIXHpgObL4I,9245
|
|
164
165
|
matter_server/client/exceptions.py,sha256=741JkGMBQFJYZ70zX80B-Dn_NtgHffb6jDHC83tCL2U,1901
|
|
165
166
|
matter_server/client/models/__init__.py,sha256=pT16UC2evZVw2trjun2xJZpaOR55ky7i_8znlA3Mreo,63
|
|
166
|
-
matter_server/client/models/device_types.py,sha256=
|
|
167
|
-
matter_server/client/models/node.py,sha256=
|
|
167
|
+
matter_server/client/models/device_types.py,sha256=SU82O_aC_HTV6KEaO00rXbGBy0cUImD8aR7tq692Mw0,26612
|
|
168
|
+
matter_server/client/models/node.py,sha256=RCLPsumxVw433nPO_Ii7vB_Ak8sAbfZ26U-94KXiRGU,25680
|
|
168
169
|
matter_server/common/__init__.py,sha256=y4EeCH05sUYifS2b624oweA6omnY5k-rLNHHv4t4p7Q,50
|
|
169
170
|
matter_server/common/const.py,sha256=hTu0siAgpQvAz6oYavTGuqgkXaQH2glQxUQuGyPnn1o,256
|
|
170
|
-
matter_server/common/custom_clusters.py,sha256
|
|
171
|
+
matter_server/common/custom_clusters.py,sha256=-B8chVS7kKxRrQ_g_qOaWTjOJ7YPYR3IBzf73VwNeIM,2197
|
|
171
172
|
matter_server/common/errors.py,sha256=h225mChQDHZ_CpkKRkg_F1_RF0qmXFcCP0ERBVMQkJ8,3770
|
|
172
|
-
matter_server/common/models.py,sha256=
|
|
173
|
+
matter_server/common/models.py,sha256=j9fLNRREVD4cCB_KVmdiOEwj4DV7H9aURClpq84cseI,12641
|
|
173
174
|
matter_server/common/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
174
175
|
matter_server/common/helpers/api.py,sha256=9maToXRnef6SLGnecaTSBlWU7wDSG3elil-HtffQ2TA,1993
|
|
175
176
|
matter_server/common/helpers/json.py,sha256=FktcqarAhmdI0AdiBwo7gCdJp6rA-uVdeNUglQv6HhA,1307
|
|
176
177
|
matter_server/common/helpers/util.py,sha256=fPYh89ii65JqSKyR4db-L1QkEXBqnsOlFCiDzTC2oT4,14827
|
|
177
|
-
matter_python_client-1.
|
|
178
|
-
matter_python_client-1.
|
|
179
|
-
matter_python_client-1.
|
|
180
|
-
matter_python_client-1.
|
|
178
|
+
matter_python_client-1.5.0a0.dev20261013.dist-info/METADATA,sha256=CdUn7nvWgm7KPE5VaTcGBE-op1rd7dXnMlrPqdqRh8k,5494
|
|
179
|
+
matter_python_client-1.5.0a0.dev20261013.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
180
|
+
matter_python_client-1.5.0a0.dev20261013.dist-info/top_level.txt,sha256=qqEVT8dTHhSn15gf6otxpB5dYM48Sqsucdx9W2Lq-So,19
|
|
181
|
+
matter_python_client-1.5.0a0.dev20261013.dist-info/RECORD,,
|
matter_server/client/client.py
CHANGED
|
@@ -908,7 +908,7 @@ class MatterClient:
|
|
|
908
908
|
self._signal_event(EventType.ENDPOINT_REMOVED, data=msg.data, node_id=node_id)
|
|
909
909
|
# cleanup endpoint only after signalling subscribers
|
|
910
910
|
if node := self._nodes.get(node_id):
|
|
911
|
-
node.
|
|
911
|
+
node._remove_endpoint(endpoint_id)
|
|
912
912
|
return
|
|
913
913
|
if msg.event == EventType.ATTRIBUTE_UPDATED:
|
|
914
914
|
# data is tuple[node_id, attribute_path, new_value]
|
|
@@ -932,6 +932,14 @@ class MatterClient:
|
|
|
932
932
|
node_id = msg.data["node_id"]
|
|
933
933
|
endpoint_id = msg.data["endpoint_id"]
|
|
934
934
|
self.logger.debug("Endpoint added: %s/%s", node_id, endpoint_id)
|
|
935
|
+
# subscribers look the endpoint up; the node snapshot that carries it reaches them later
|
|
936
|
+
if (node := self._nodes.get(node_id)) is None or endpoint_id not in node.endpoints:
|
|
937
|
+
self.logger.debug(
|
|
938
|
+
"Not signalling endpoint %s/%s that the node model does not contain",
|
|
939
|
+
node_id,
|
|
940
|
+
endpoint_id,
|
|
941
|
+
)
|
|
942
|
+
return
|
|
935
943
|
if msg.event == EventType.NODE_EVENT:
|
|
936
944
|
if self.logger.isEnabledFor(logging.DEBUG):
|
|
937
945
|
self.logger.debug(
|
|
@@ -795,6 +795,7 @@ class RootNode(DeviceType):
|
|
|
795
795
|
all_clusters.GeneralCommissioning,
|
|
796
796
|
all_clusters.GeneralDiagnostics,
|
|
797
797
|
all_clusters.GroupKeyManagement,
|
|
798
|
+
all_clusters.Groupcast,
|
|
798
799
|
all_clusters.IcdManagement,
|
|
799
800
|
all_clusters.LocalizationConfiguration,
|
|
800
801
|
all_clusters.NetworkCommissioning,
|
|
@@ -24,10 +24,15 @@ from .device_types import (
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Mapping
|
|
28
|
+
|
|
27
29
|
from matter_server.common.models import MatterNodeData
|
|
28
30
|
|
|
29
31
|
LOGGER = logging.getLogger(__name__)
|
|
30
32
|
|
|
33
|
+
# the Matter specification places the Root Node on endpoint 0 of every node
|
|
34
|
+
ROOT_ENDPOINT_ID = 0
|
|
35
|
+
|
|
31
36
|
# pylint: disable=invalid-name
|
|
32
37
|
_CLUSTER_T = TypeVar("_CLUSTER_T", bound=Clusters.Cluster)
|
|
33
38
|
_ATTRIBUTE_T = TypeVar("_ATTRIBUTE_T", bound=Clusters.ClusterAttributeDescriptor)
|
|
@@ -44,6 +49,18 @@ def get_object_params(
|
|
|
44
49
|
raise KeyError(f"No descriptor found for object {object_id}")
|
|
45
50
|
|
|
46
51
|
|
|
52
|
+
def _reaches(start_id: int, target_id: int, parents: Mapping[int, int]) -> bool:
|
|
53
|
+
"""Return whether walking up the parent chain from an endpoint arrives at another."""
|
|
54
|
+
seen: set[int] = set()
|
|
55
|
+
current: int | None = start_id
|
|
56
|
+
while current is not None and current not in seen:
|
|
57
|
+
if current == target_id:
|
|
58
|
+
return True
|
|
59
|
+
seen.add(current)
|
|
60
|
+
current = parents.get(current)
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
|
|
47
64
|
@dataclass
|
|
48
65
|
class MatterFabricData:
|
|
49
66
|
"""Data about a Matter fabric."""
|
|
@@ -84,18 +101,25 @@ class MatterEndpoint:
|
|
|
84
101
|
@property
|
|
85
102
|
def device_info(
|
|
86
103
|
self,
|
|
87
|
-
) -> Clusters.BasicInformation | Clusters.BridgedDeviceBasicInformation:
|
|
104
|
+
) -> Clusters.BasicInformation | Clusters.BridgedDeviceBasicInformation | None:
|
|
88
105
|
"""
|
|
89
106
|
Return device info.
|
|
90
107
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
Returns the BridgedDeviceBasicInformation of the Bridged Node this endpoint is or is a part
|
|
109
|
+
of, else the BasicInformation of the Node itself. It is None when that Bridged Node reports
|
|
110
|
+
no BridgedDeviceBasicInformation, because the info of the bridge or of the Aggregator above
|
|
111
|
+
it belongs to a different device.
|
|
94
112
|
"""
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
113
|
+
endpoint = self
|
|
114
|
+
seen: set[int] = set()
|
|
115
|
+
while endpoint.endpoint_id not in seen:
|
|
116
|
+
seen.add(endpoint.endpoint_id)
|
|
117
|
+
if endpoint.is_bridged_device:
|
|
118
|
+
return endpoint.get_cluster(Clusters.BridgedDeviceBasicInformation)
|
|
119
|
+
parent = endpoint.node.get_closest_compose_parent(endpoint.endpoint_id)
|
|
120
|
+
if parent is None:
|
|
121
|
+
break
|
|
122
|
+
endpoint = parent
|
|
99
123
|
return self.node.device_info
|
|
100
124
|
|
|
101
125
|
def has_cluster(self, cluster: type[_CLUSTER_T] | int) -> bool:
|
|
@@ -217,11 +241,14 @@ class MatterEndpoint:
|
|
|
217
241
|
|
|
218
242
|
def update(self, attributes_data: dict[str, Any]) -> None:
|
|
219
243
|
"""Update MatterEndpoint from (endpoint-specific) raw Attributes data."""
|
|
244
|
+
# a snapshot carries the complete state of the endpoint, never a delta
|
|
245
|
+
self.clusters = {}
|
|
220
246
|
# unwrap cluster and clusterattributes from raw node data attributes
|
|
221
247
|
for attribute_path, attribute_value in attributes_data.items():
|
|
222
248
|
self.set_attribute_value(attribute_path, attribute_value)
|
|
223
|
-
#
|
|
224
|
-
if cluster := self.get_cluster(Clusters.Descriptor):
|
|
249
|
+
# a snapshot without the DeviceTypeList says nothing about the device types, so the previous ones stay
|
|
250
|
+
if f"{self.endpoint_id}/29/0" in attributes_data and (cluster := self.get_cluster(Clusters.Descriptor)):
|
|
251
|
+
self.device_types = set()
|
|
225
252
|
for dev_info in cluster.deviceTypeList:
|
|
226
253
|
device_type = DEVICE_TYPES.get(dev_info.deviceType)
|
|
227
254
|
if device_type is None:
|
|
@@ -240,9 +267,10 @@ class MatterNode:
|
|
|
240
267
|
def __init__(self, node_data: MatterNodeData) -> None:
|
|
241
268
|
"""Initialize MatterNode from MatterNodeData."""
|
|
242
269
|
self.endpoints: dict[int, MatterEndpoint] = {}
|
|
243
|
-
# composed devices reference to other endpoints through the partsList attribute
|
|
244
|
-
# create a mapping table
|
|
245
270
|
self._composed_endpoints: dict[int, int] = {}
|
|
271
|
+
self._bridge_parents: dict[int, int] = {}
|
|
272
|
+
self._snapshot_endpoint_ids: set[int] = set()
|
|
273
|
+
self._reported_missing_bridged_info: set[int] = set()
|
|
246
274
|
self.update(node_data)
|
|
247
275
|
|
|
248
276
|
@property
|
|
@@ -263,11 +291,12 @@ class MatterNode:
|
|
|
263
291
|
return self.node_data.available
|
|
264
292
|
|
|
265
293
|
@property
|
|
266
|
-
def device_info(self) -> Clusters.BasicInformation:
|
|
294
|
+
def device_info(self) -> Clusters.BasicInformation | None:
|
|
267
295
|
"""
|
|
268
296
|
Return device info for this Node.
|
|
269
297
|
|
|
270
|
-
Returns BasicInformation from the Node itself (endpoint 0)
|
|
298
|
+
Returns BasicInformation from the Node itself (endpoint 0), or None while the node has not
|
|
299
|
+
reported it.
|
|
271
300
|
"""
|
|
272
301
|
return self.get_cluster(0, Clusters.BasicInformation)
|
|
273
302
|
|
|
@@ -304,17 +333,49 @@ class MatterNode:
|
|
|
304
333
|
|
|
305
334
|
Returns None is the Cluster is not present on the node.
|
|
306
335
|
"""
|
|
307
|
-
|
|
336
|
+
if (endpoint_obj := self.endpoints.get(endpoint)) is None:
|
|
337
|
+
return None
|
|
338
|
+
return endpoint_obj.get_cluster(cluster)
|
|
308
339
|
|
|
309
340
|
def get_compose_parent(self, endpoint_id: int) -> MatterEndpoint | None:
|
|
310
|
-
"""Return endpoint of
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
341
|
+
"""Return the endpoint of the composed device the endpoint is a part of, at any depth.
|
|
342
|
+
|
|
343
|
+
Consumers derive the device of a part from this endpoint, so a part nested below another
|
|
344
|
+
part resolves to the top of the composition, not to its closest parent.
|
|
345
|
+
"""
|
|
346
|
+
if (parent_id := self._composed_endpoints.get(endpoint_id)) is None:
|
|
347
|
+
return None
|
|
348
|
+
seen = {endpoint_id, parent_id}
|
|
349
|
+
while (next_id := self._composed_endpoints.get(parent_id)) is not None and next_id not in seen:
|
|
350
|
+
seen.add(next_id)
|
|
351
|
+
parent_id = next_id
|
|
352
|
+
return self.endpoints.get(parent_id)
|
|
353
|
+
|
|
354
|
+
def get_closest_compose_parent(self, endpoint_id: int) -> MatterEndpoint | None:
|
|
355
|
+
"""Return the closest endpoint the endpoint is a part of."""
|
|
356
|
+
if (parent_id := self._composed_endpoints.get(endpoint_id)) is None:
|
|
357
|
+
return None
|
|
358
|
+
return self.endpoints.get(parent_id)
|
|
359
|
+
|
|
360
|
+
def get_compose_child_ids(self, endpoint_id: int) -> tuple[int, ...]:
|
|
361
|
+
"""Return endpoint IDs of the parts, at any depth, of the composed device the endpoint is the top of."""
|
|
362
|
+
return tuple(
|
|
363
|
+
sorted(
|
|
364
|
+
child_id
|
|
365
|
+
for child_id in self._composed_endpoints
|
|
366
|
+
if (parent := self.get_compose_parent(child_id)) is not None and parent.endpoint_id == endpoint_id
|
|
367
|
+
)
|
|
368
|
+
)
|
|
314
369
|
|
|
315
|
-
def
|
|
316
|
-
"""Return
|
|
317
|
-
|
|
370
|
+
def get_bridge_parent(self, endpoint_id: int) -> MatterEndpoint | None:
|
|
371
|
+
"""Return the Aggregator endpoint that bridges the given endpoint, if any."""
|
|
372
|
+
if (parent_id := self._bridge_parents.get(endpoint_id)) is None:
|
|
373
|
+
return None
|
|
374
|
+
return self.endpoints.get(parent_id)
|
|
375
|
+
|
|
376
|
+
def get_bridge_child_ids(self, endpoint_id: int) -> tuple[int, ...]:
|
|
377
|
+
"""Return endpoint IDs of the devices bridged by the given Aggregator endpoint."""
|
|
378
|
+
return tuple(sorted(x for x, y in self._bridge_parents.items() if y == endpoint_id))
|
|
318
379
|
|
|
319
380
|
def update(self, node_data: MatterNodeData) -> None:
|
|
320
381
|
"""Update MatterNode from MatterNodeData."""
|
|
@@ -326,6 +387,7 @@ class MatterNode:
|
|
|
326
387
|
if endpoint_id not in endpoint_data:
|
|
327
388
|
endpoint_data[endpoint_id] = {}
|
|
328
389
|
endpoint_data[endpoint_id][attribute_path] = attribute_data
|
|
390
|
+
self._snapshot_endpoint_ids = set(endpoint_data)
|
|
329
391
|
for endpoint_id, attributes_data in endpoint_data.items():
|
|
330
392
|
if endpoint_id in self.endpoints:
|
|
331
393
|
self.endpoints[endpoint_id].update(attributes_data)
|
|
@@ -333,15 +395,64 @@ class MatterNode:
|
|
|
333
395
|
self.endpoints[endpoint_id] = MatterEndpoint(
|
|
334
396
|
endpoint_id=endpoint_id, attributes_data=attributes_data, node=self
|
|
335
397
|
)
|
|
336
|
-
|
|
337
|
-
|
|
398
|
+
self._map_endpoint_parents()
|
|
399
|
+
|
|
400
|
+
def _remove_endpoint(self, endpoint_id: int) -> None:
|
|
401
|
+
"""Remove an endpoint together with its parts, and every parent relation they are in.
|
|
402
|
+
|
|
403
|
+
This is the only way an endpoint leaves `endpoints`: a snapshot that no longer reports an
|
|
404
|
+
endpoint keeps its object until the server announces the removal, because consumers look
|
|
405
|
+
the endpoint up when handling that announcement. The parts of the endpoint, at any depth,
|
|
406
|
+
leave with it whether or not a snapshot still reports them: their device is gone, and left
|
|
407
|
+
behind they would resolve to no device when their own removal arrives. A device the
|
|
408
|
+
endpoint bridged stays, without its bridge parent, until its own removal arrives.
|
|
409
|
+
|
|
410
|
+
May only be called by logic that received data from the server.
|
|
411
|
+
"""
|
|
412
|
+
if endpoint_id not in self.endpoints:
|
|
413
|
+
return
|
|
414
|
+
removed = {endpoint_id}
|
|
415
|
+
pending = [endpoint_id]
|
|
416
|
+
while pending:
|
|
417
|
+
parent_id = pending.pop()
|
|
418
|
+
for child_id, child_parent_id in self._composed_endpoints.items():
|
|
419
|
+
if child_parent_id == parent_id and child_id not in removed:
|
|
420
|
+
removed.add(child_id)
|
|
421
|
+
pending.append(child_id)
|
|
422
|
+
for removed_id in removed:
|
|
423
|
+
del self.endpoints[removed_id]
|
|
424
|
+
self._reported_missing_bridged_info.discard(removed_id)
|
|
425
|
+
self._composed_endpoints = {
|
|
426
|
+
child_id: parent_id for child_id, parent_id in self._composed_endpoints.items() if child_id not in removed
|
|
427
|
+
}
|
|
428
|
+
self._bridge_parents = {
|
|
429
|
+
child_id: parent_id
|
|
430
|
+
for child_id, parent_id in self._bridge_parents.items()
|
|
431
|
+
if child_id not in removed and parent_id not in removed
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
def _map_endpoint_parents(self) -> None:
|
|
435
|
+
"""Resolve each endpoint to its closest parent endpoint, split by parent kind.
|
|
436
|
+
|
|
437
|
+
An endpoint's partsList may enumerate its whole family - every descendant, not just
|
|
438
|
+
the direct children - which is the pattern a bridge uses. Of the endpoints listing a
|
|
439
|
+
given endpoint, the closest one is therefore the endpoint that lists none of the
|
|
440
|
+
others - on contradictory lists the smaller family wins, then the lower endpoint
|
|
441
|
+
number - and a parent is only accepted while the relations stay a tree.
|
|
442
|
+
|
|
443
|
+
Only the endpoints of the last snapshot take part. A snapshot replaces the relation of an
|
|
444
|
+
endpoint only where it names a parent for it: one that bridges or composes it, or the
|
|
445
|
+
parent it already had, whose classification then wins. Where it names none - the endpoint
|
|
446
|
+
unlisted or unreported, its parent without a Descriptor or device types, or listed only by
|
|
447
|
+
endpoints that make it neither bridged nor a part - the endpoint keeps its relation until
|
|
448
|
+
its own removal or that of the parent is announced, so a consumer handling the
|
|
449
|
+
announcement still finds the device the endpoint is a part of. A kept relation that would
|
|
450
|
+
close a cycle with a named one is dropped.
|
|
451
|
+
"""
|
|
452
|
+
snapshot_ids = self._snapshot_endpoint_ids & self.endpoints.keys()
|
|
453
|
+
families: dict[int, set[int]] = {}
|
|
338
454
|
for endpoint in self.endpoints.values():
|
|
339
|
-
if
|
|
340
|
-
# ignore root endpoint
|
|
341
|
-
continue
|
|
342
|
-
if Aggregator in endpoint.device_types:
|
|
343
|
-
# ignore Bridge endpoint
|
|
344
|
-
# (as that will also use partsList to indicate its child's)
|
|
455
|
+
if endpoint.endpoint_id not in snapshot_ids:
|
|
345
456
|
continue
|
|
346
457
|
descriptor = endpoint.get_cluster(Clusters.Descriptor)
|
|
347
458
|
if descriptor is None:
|
|
@@ -351,9 +462,110 @@ class MatterNode:
|
|
|
351
462
|
endpoint.endpoint_id,
|
|
352
463
|
)
|
|
353
464
|
continue
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
465
|
+
families[endpoint.endpoint_id] = {
|
|
466
|
+
child_id
|
|
467
|
+
for child_id in descriptor.partsList or ()
|
|
468
|
+
if child_id in snapshot_ids and child_id != endpoint.endpoint_id
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
candidates: dict[int, set[int]] = {}
|
|
472
|
+
for parent_id, family in families.items():
|
|
473
|
+
for child_id in family:
|
|
474
|
+
candidates.setdefault(child_id, set()).add(parent_id)
|
|
475
|
+
|
|
476
|
+
parents: dict[int, int] = {}
|
|
477
|
+
for child_id in sorted(candidates):
|
|
478
|
+
parent_ids = candidates[child_id]
|
|
479
|
+
for parent_id in sorted(
|
|
480
|
+
parent_ids,
|
|
481
|
+
key=lambda pid: (
|
|
482
|
+
len((parent_ids - {pid}) & families[pid]),
|
|
483
|
+
len(families[pid]),
|
|
484
|
+
pid,
|
|
485
|
+
),
|
|
486
|
+
):
|
|
487
|
+
if _reaches(parent_id, child_id, parents):
|
|
488
|
+
LOGGER.warning(
|
|
489
|
+
"Ignoring cyclic partsList relation: Node %s, endpoint %s below %s",
|
|
490
|
+
self.node_id,
|
|
491
|
+
child_id,
|
|
492
|
+
parent_id,
|
|
493
|
+
)
|
|
494
|
+
continue
|
|
495
|
+
parents[child_id] = parent_id
|
|
496
|
+
break
|
|
497
|
+
|
|
498
|
+
previous_composed = self._composed_endpoints
|
|
499
|
+
previous_bridge = self._bridge_parents
|
|
500
|
+
self._composed_endpoints = {}
|
|
501
|
+
self._bridge_parents = {}
|
|
502
|
+
decided = {
|
|
503
|
+
child_id for child_id, parent_id in parents.items() if self._map_endpoint_parent(child_id, parent_id)
|
|
504
|
+
}
|
|
505
|
+
related = self._composed_endpoints | self._bridge_parents
|
|
506
|
+
for relations, previous in (
|
|
507
|
+
(self._composed_endpoints, previous_composed),
|
|
508
|
+
(self._bridge_parents, previous_bridge),
|
|
509
|
+
):
|
|
510
|
+
for child_id, parent_id in sorted(previous.items()):
|
|
511
|
+
if child_id in related or (child_id in decided and parents[child_id] == parent_id):
|
|
512
|
+
continue
|
|
513
|
+
if _reaches(parent_id, child_id, related):
|
|
514
|
+
LOGGER.warning(
|
|
515
|
+
"Dropping parent relation that a snapshot made cyclic: Node %s, endpoint %s below %s",
|
|
516
|
+
self.node_id,
|
|
517
|
+
child_id,
|
|
518
|
+
parent_id,
|
|
519
|
+
)
|
|
520
|
+
continue
|
|
521
|
+
relations[child_id] = parent_id
|
|
522
|
+
related[child_id] = parent_id
|
|
523
|
+
|
|
524
|
+
self._report_bridged_devices_without_info()
|
|
525
|
+
|
|
526
|
+
def _report_bridged_devices_without_info(self) -> None:
|
|
527
|
+
"""Warn about bridged devices the bridge does not describe, once per endpoint.
|
|
528
|
+
|
|
529
|
+
Such an endpoint has no device info of its own, and the info of the bridge describes a
|
|
530
|
+
different device, so it stays unnamed until the bridge reports it.
|
|
531
|
+
"""
|
|
532
|
+
for endpoint_id, endpoint in self.endpoints.items():
|
|
533
|
+
if endpoint_id in self._reported_missing_bridged_info:
|
|
534
|
+
continue
|
|
535
|
+
if not endpoint.is_bridged_device:
|
|
536
|
+
continue
|
|
537
|
+
if endpoint.get_cluster(Clusters.BridgedDeviceBasicInformation) is not None:
|
|
538
|
+
continue
|
|
539
|
+
self._reported_missing_bridged_info.add(endpoint_id)
|
|
540
|
+
LOGGER.warning(
|
|
541
|
+
"Bridged device without BridgedDeviceBasicInformation: Node %s, endpoint %s",
|
|
542
|
+
self.node_id,
|
|
543
|
+
endpoint_id,
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
def _map_endpoint_parent(self, child_id: int, parent_id: int) -> bool:
|
|
547
|
+
"""Record what the parent endpoint makes of the endpoint below it, return whether it decides.
|
|
548
|
+
|
|
549
|
+
A child of the root endpoint is top level. Below an Aggregator, a Bridged Node is a
|
|
550
|
+
bridged device; any other child stands alone, whether or not the Aggregator is itself a
|
|
551
|
+
Bridged Node (so ids derived from the compose parent match older clients). Below any other endpoint, a child
|
|
552
|
+
is a part of it - also when the device types of that endpoint are unknown to this client,
|
|
553
|
+
so only a parent that reports no device type at all decides nothing.
|
|
554
|
+
"""
|
|
555
|
+
if parent_id == ROOT_ENDPOINT_ID:
|
|
556
|
+
return True
|
|
557
|
+
parent = self.endpoints[parent_id]
|
|
558
|
+
descriptor = parent.get_cluster(Clusters.Descriptor)
|
|
559
|
+
assert descriptor is not None # only endpoints with a Descriptor are parents
|
|
560
|
+
if not descriptor.deviceTypeList:
|
|
561
|
+
return False
|
|
562
|
+
if RootNode in parent.device_types:
|
|
563
|
+
return True
|
|
564
|
+
if Aggregator not in parent.device_types:
|
|
565
|
+
self._composed_endpoints[child_id] = parent_id
|
|
566
|
+
elif BridgedNode in self.endpoints[child_id].device_types:
|
|
567
|
+
self._bridge_parents[child_id] = parent_id
|
|
568
|
+
return True
|
|
357
569
|
|
|
358
570
|
def update_attribute(self, attribute_path: str, new_value: Any) -> None:
|
|
359
571
|
"""Handle Attribute value update."""
|
|
@@ -6,6 +6,7 @@ from chip.clusters.cluster_defs.AqaraRadarSensingUnionCluster import AqaraRadarS
|
|
|
6
6
|
from chip.clusters.cluster_defs.DraftElectricalMeasurementCluster import DraftElectricalMeasurementCluster
|
|
7
7
|
from chip.clusters.cluster_defs.EveCluster import EveCluster
|
|
8
8
|
from chip.clusters.cluster_defs.HeimanCluster import HeimanCluster
|
|
9
|
+
from chip.clusters.cluster_defs.IkeaValveCalibrationCluster import IkeaValveCalibrationCluster
|
|
9
10
|
from chip.clusters.cluster_defs.InovelliCluster import InovelliCluster
|
|
10
11
|
from chip.clusters.cluster_defs.NeoCluster import NeoCluster
|
|
11
12
|
from chip.clusters.cluster_defs.TclDehumidifierCluster import TclDehumidifierCluster
|
|
@@ -19,6 +20,7 @@ ALL_CUSTOM_CLUSTERS: dict = {
|
|
|
19
20
|
DraftElectricalMeasurementCluster.id: DraftElectricalMeasurementCluster,
|
|
20
21
|
EveCluster.id: EveCluster,
|
|
21
22
|
HeimanCluster.id: HeimanCluster,
|
|
23
|
+
IkeaValveCalibrationCluster.id: IkeaValveCalibrationCluster,
|
|
22
24
|
InovelliCluster.id: InovelliCluster,
|
|
23
25
|
NeoCluster.id: NeoCluster,
|
|
24
26
|
TclDehumidifierCluster.id: TclDehumidifierCluster,
|
|
@@ -34,6 +36,7 @@ __all__ = [
|
|
|
34
36
|
"DraftElectricalMeasurementCluster",
|
|
35
37
|
"EveCluster",
|
|
36
38
|
"HeimanCluster",
|
|
39
|
+
"IkeaValveCalibrationCluster",
|
|
37
40
|
"InovelliCluster",
|
|
38
41
|
"NeoCluster",
|
|
39
42
|
"TclDehumidifierCluster",
|
matter_server/common/models.py
CHANGED
|
@@ -258,6 +258,10 @@ class CommissioningParameters:
|
|
|
258
258
|
setup_pin_code: int
|
|
259
259
|
setup_manual_code: str
|
|
260
260
|
setup_qr_code: str
|
|
261
|
+
discriminator: int | None = None
|
|
262
|
+
vendor_id: int | None = None
|
|
263
|
+
product_id: int | None = None
|
|
264
|
+
commissioning_timeout: int | None = None
|
|
261
265
|
|
|
262
266
|
|
|
263
267
|
@dataclass
|
|
File without changes
|