0
# TLV Data Handling
1
2
TLV (Tag-Length-Value) encoding and decoding for Matter protocol data structures, enabling low-level protocol operations and custom data handling.
3
4
## Capabilities
5
6
### TLV Writer
7
8
Encode data into TLV format for Matter protocol transmission.
9
10
```python { .api }
11
class TLVWriter:
12
"""TLV encoder for Matter protocol data structures."""
13
14
def __init__(self):
15
"""Initialize TLV writer."""
16
...
17
18
def put(self, tag: int, value) -> bool:
19
"""
20
Write a tagged value to the TLV stream.
21
22
Parameters:
23
- tag: TLV tag identifier
24
- value: Value to encode (int, float, str, bytes, bool, list, dict)
25
26
Returns:
27
True if write successful
28
"""
29
...
30
31
def put_null(self, tag: int) -> bool:
32
"""
33
Write a null value.
34
35
Parameters:
36
- tag: TLV tag identifier
37
38
Returns:
39
True if write successful
40
"""
41
...
42
43
def put_bool(self, tag: int, value: bool) -> bool:
44
"""
45
Write a boolean value.
46
47
Parameters:
48
- tag: TLV tag identifier
49
- value: Boolean value
50
51
Returns:
52
True if write successful
53
"""
54
...
55
56
def put_int(self, tag: int, value: int) -> bool:
57
"""
58
Write a signed integer value.
59
60
Parameters:
61
- tag: TLV tag identifier
62
- value: Integer value
63
64
Returns:
65
True if write successful
66
"""
67
...
68
69
def put_uint(self, tag: int, value: int) -> bool:
70
"""
71
Write an unsigned integer value.
72
73
Parameters:
74
- tag: TLV tag identifier
75
- value: Unsigned integer value
76
77
Returns:
78
True if write successful
79
"""
80
...
81
82
def put_float(self, tag: int, value: float) -> bool:
83
"""
84
Write a floating-point value.
85
86
Parameters:
87
- tag: TLV tag identifier
88
- value: Float value (32-bit or 64-bit)
89
90
Returns:
91
True if write successful
92
"""
93
...
94
95
def put_string(self, tag: int, value: str) -> bool:
96
"""
97
Write a UTF-8 string value.
98
99
Parameters:
100
- tag: TLV tag identifier
101
- value: String value
102
103
Returns:
104
True if write successful
105
"""
106
...
107
108
def put_bytes(self, tag: int, value: bytes) -> bool:
109
"""
110
Write a byte string value.
111
112
Parameters:
113
- tag: TLV tag identifier
114
- value: Byte string value
115
116
Returns:
117
True if write successful
118
"""
119
...
120
121
def start_container(self, tag: int, container_type: int) -> bool:
122
"""
123
Start a TLV container (structure, array, or list).
124
125
Parameters:
126
- tag: TLV tag identifier
127
- container_type: Container type (0=Structure, 1=Array, 2=List)
128
129
Returns:
130
True if container started successfully
131
"""
132
...
133
134
def end_container(self) -> bool:
135
"""
136
End the current TLV container.
137
138
Returns:
139
True if container ended successfully
140
"""
141
...
142
143
def finalize(self) -> bytes:
144
"""
145
Finalize the TLV encoding and return the encoded data.
146
147
Returns:
148
Encoded TLV data as bytes
149
"""
150
...
151
152
def get_length(self) -> int:
153
"""
154
Get the length of encoded data so far.
155
156
Returns:
157
Number of encoded bytes
158
"""
159
...
160
161
def reset(self):
162
"""Reset the writer to start encoding new data."""
163
...
164
```
165
166
### TLV Reader
167
168
Decode TLV formatted data from Matter protocol messages.
169
170
```python { .api }
171
class TLVReader:
172
"""TLV decoder for Matter protocol data structures."""
173
174
def __init__(self, data: bytes):
175
"""
176
Initialize TLV reader with encoded data.
177
178
Parameters:
179
- data: TLV encoded data bytes
180
"""
181
...
182
183
def get(self, tag: int):
184
"""
185
Get value by tag.
186
187
Parameters:
188
- tag: TLV tag identifier to find
189
190
Returns:
191
Decoded value or None if tag not found
192
"""
193
...
194
195
def get_bool(self, tag: int) -> bool:
196
"""
197
Get boolean value by tag.
198
199
Parameters:
200
- tag: TLV tag identifier
201
202
Returns:
203
Boolean value or None if tag not found
204
205
Raises:
206
ValueError: If tag exists but is not a boolean
207
"""
208
...
209
210
def get_int(self, tag: int) -> int:
211
"""
212
Get signed integer value by tag.
213
214
Parameters:
215
- tag: TLV tag identifier
216
217
Returns:
218
Integer value or None if tag not found
219
220
Raises:
221
ValueError: If tag exists but is not an integer
222
"""
223
...
224
225
def get_uint(self, tag: int) -> int:
226
"""
227
Get unsigned integer value by tag.
228
229
Parameters:
230
- tag: TLV tag identifier
231
232
Returns:
233
Unsigned integer value or None if tag not found
234
235
Raises:
236
ValueError: If tag exists but is not an unsigned integer
237
"""
238
...
239
240
def get_float(self, tag: int) -> float:
241
"""
242
Get floating-point value by tag.
243
244
Parameters:
245
- tag: TLV tag identifier
246
247
Returns:
248
Float value or None if tag not found
249
250
Raises:
251
ValueError: If tag exists but is not a float
252
"""
253
...
254
255
def get_string(self, tag: int) -> str:
256
"""
257
Get UTF-8 string value by tag.
258
259
Parameters:
260
- tag: TLV tag identifier
261
262
Returns:
263
String value or None if tag not found
264
265
Raises:
266
ValueError: If tag exists but is not a string
267
"""
268
...
269
270
def get_bytes(self, tag: int) -> bytes:
271
"""
272
Get byte string value by tag.
273
274
Parameters:
275
- tag: TLV tag identifier
276
277
Returns:
278
Byte string value or None if tag not found
279
280
Raises:
281
ValueError: If tag exists but is not a byte string
282
"""
283
...
284
285
def get_all(self) -> dict:
286
"""
287
Get all tag-value pairs.
288
289
Returns:
290
Dictionary mapping tags to decoded values
291
"""
292
...
293
294
def get_tags(self) -> list:
295
"""
296
Get all tags in the TLV data.
297
298
Returns:
299
List of tag identifiers
300
"""
301
...
302
303
def has_tag(self, tag: int) -> bool:
304
"""
305
Check if a tag exists in the TLV data.
306
307
Parameters:
308
- tag: TLV tag identifier
309
310
Returns:
311
True if tag exists
312
"""
313
...
314
315
def get_container(self, tag: int) -> 'TLVReader':
316
"""
317
Get a container (structure, array, or list) as a new TLV reader.
318
319
Parameters:
320
- tag: TLV tag identifier for the container
321
322
Returns:
323
New TLVReader for the container contents or None if not found
324
325
Raises:
326
ValueError: If tag exists but is not a container
327
"""
328
...
329
330
def is_container(self, tag: int) -> bool:
331
"""
332
Check if a tag contains a container.
333
334
Parameters:
335
- tag: TLV tag identifier
336
337
Returns:
338
True if tag contains a container
339
"""
340
...
341
342
def get_container_type(self, tag: int) -> int:
343
"""
344
Get the type of a container.
345
346
Parameters:
347
- tag: TLV tag identifier for the container
348
349
Returns:
350
Container type (0=Structure, 1=Array, 2=List) or None if not a container
351
"""
352
...
353
```
354
355
### TLV Utility Functions
356
357
Utility functions for common TLV operations and data type handling.
358
359
```python { .api }
360
class TLVUtils:
361
"""Utility functions for TLV operations."""
362
363
@staticmethod
364
def encode_tag(profile_id: int, tag_num: int, is_context_specific: bool = False) -> int:
365
"""
366
Encode a TLV tag from profile ID and tag number.
367
368
Parameters:
369
- profile_id: Profile identifier (0 for anonymous)
370
- tag_num: Tag number within the profile
371
- is_context_specific: Whether this is a context-specific tag
372
373
Returns:
374
Encoded tag value
375
"""
376
...
377
378
@staticmethod
379
def decode_tag(tag: int) -> dict:
380
"""
381
Decode a TLV tag into its components.
382
383
Parameters:
384
- tag: Encoded tag value
385
386
Returns:
387
Dictionary with 'profile_id', 'tag_num', and 'is_context_specific'
388
"""
389
...
390
391
@staticmethod
392
def python_to_tlv(data) -> bytes:
393
"""
394
Convert Python data structure to TLV bytes.
395
396
Parameters:
397
- data: Python data (dict, list, primitive types)
398
399
Returns:
400
TLV encoded bytes
401
"""
402
...
403
404
@staticmethod
405
def tlv_to_python(data: bytes) -> dict:
406
"""
407
Convert TLV bytes to Python data structure.
408
409
Parameters:
410
- data: TLV encoded bytes
411
412
Returns:
413
Python data structure (dict with tag keys)
414
"""
415
...
416
417
@staticmethod
418
def validate_tlv(data: bytes) -> bool:
419
"""
420
Validate TLV data structure.
421
422
Parameters:
423
- data: TLV encoded bytes to validate
424
425
Returns:
426
True if TLV data is well-formed
427
"""
428
...
429
430
@staticmethod
431
def pretty_print_tlv(data: bytes) -> str:
432
"""
433
Create a human-readable representation of TLV data.
434
435
Parameters:
436
- data: TLV encoded bytes
437
438
Returns:
439
Formatted string representation
440
"""
441
...
442
443
@staticmethod
444
def get_tlv_size(data: bytes) -> int:
445
"""
446
Get the total size of TLV data.
447
448
Parameters:
449
- data: TLV encoded bytes
450
451
Returns:
452
Size in bytes
453
"""
454
...
455
```
456
457
### Matter-Specific TLV Types
458
459
TLV handling for Matter-specific data types and structures.
460
461
```python { .api }
462
class float32:
463
"""32-bit floating point TLV type."""
464
465
def __init__(self, value: float):
466
"""
467
Initialize 32-bit float.
468
469
Parameters:
470
- value: Float value
471
"""
472
...
473
474
@property
475
def value(self) -> float:
476
"""Get the float value."""
477
...
478
479
class uint:
480
"""Unsigned integer TLV type with size specification."""
481
482
def __init__(self, value: int, size: int = None):
483
"""
484
Initialize unsigned integer.
485
486
Parameters:
487
- value: Integer value
488
- size: Size in bytes (1, 2, 4, or 8), auto-detected if None
489
"""
490
...
491
492
@property
493
def value(self) -> int:
494
"""Get the integer value."""
495
...
496
497
@property
498
def size(self) -> int:
499
"""Get the size in bytes."""
500
...
501
502
class int:
503
"""Signed integer TLV type with size specification."""
504
505
def __init__(self, value: int, size: int = None):
506
"""
507
Initialize signed integer.
508
509
Parameters:
510
- value: Integer value
511
- size: Size in bytes (1, 2, 4, or 8), auto-detected if None
512
"""
513
...
514
515
@property
516
def value(self) -> int:
517
"""Get the integer value."""
518
...
519
520
@property
521
def size(self) -> int:
522
"""Get the size in bytes."""
523
...
524
```
525
526
## Usage Examples
527
528
### Basic TLV Encoding and Decoding
529
530
```python
531
from chip.tlv import TLVWriter, TLVReader
532
533
# Create a TLV writer
534
writer = TLVWriter()
535
536
# Encode various data types
537
writer.put_int(1, 42) # Tag 1: integer 42
538
writer.put_string(2, "Hello, Matter!") # Tag 2: string
539
writer.put_bool(3, True) # Tag 3: boolean true
540
writer.put_float(4, 3.14159) # Tag 4: float
541
writer.put_bytes(5, b"binary_data") # Tag 5: byte string
542
543
# Get the encoded TLV data
544
tlv_data = writer.finalize()
545
print(f"Encoded TLV data: {len(tlv_data)} bytes")
546
547
# Create a TLV reader to decode the data
548
reader = TLVReader(tlv_data)
549
550
# Read values by tag
551
int_value = reader.get_int(1)
552
string_value = reader.get_string(2)
553
bool_value = reader.get_bool(3)
554
float_value = reader.get_float(4)
555
bytes_value = reader.get_bytes(5)
556
557
print(f"Integer: {int_value}")
558
print(f"String: {string_value}")
559
print(f"Boolean: {bool_value}")
560
print(f"Float: {float_value}")
561
print(f"Bytes: {bytes_value}")
562
563
# Get all values at once
564
all_values = reader.get_all()
565
print(f"All values: {all_values}")
566
```
567
568
### TLV Containers (Structures and Arrays)
569
570
```python
571
from chip.tlv import TLVWriter, TLVReader
572
573
# Create writer for structured data
574
writer = TLVWriter()
575
576
# Start a structure container
577
writer.start_container(1, 0) # Tag 1, type 0 (Structure)
578
579
# Add fields to the structure
580
writer.put_string(1, "Device Name") # Device name
581
writer.put_int(2, 0x1234) # Vendor ID
582
writer.put_int(3, 0x5678) # Product ID
583
584
# Start an array of endpoints
585
writer.start_container(4, 1) # Tag 4, type 1 (Array)
586
writer.put_int(0, 0) # Endpoint 0
587
writer.put_int(1, 1) # Endpoint 1
588
writer.put_int(2, 2) # Endpoint 2
589
writer.end_container() # End endpoints array
590
591
# End the main structure
592
writer.end_container()
593
594
# Get encoded data
595
tlv_data = writer.finalize()
596
print(f"Structured TLV data: {len(tlv_data)} bytes")
597
598
# Read the structured data
599
reader = TLVReader(tlv_data)
600
601
# Get the main structure
602
main_struct = reader.get_container(1)
603
if main_struct:
604
device_name = main_struct.get_string(1)
605
vendor_id = main_struct.get_int(2)
606
product_id = main_struct.get_int(3)
607
608
print(f"Device: {device_name}")
609
print(f"Vendor ID: 0x{vendor_id:04X}")
610
print(f"Product ID: 0x{product_id:04X}")
611
612
# Get the endpoints array
613
endpoints_array = main_struct.get_container(4)
614
if endpoints_array:
615
all_endpoints = endpoints_array.get_all()
616
endpoints = [all_endpoints[tag] for tag in sorted(all_endpoints.keys())]
617
print(f"Endpoints: {endpoints}")
618
```
619
620
### Matter Cluster Data Encoding
621
622
```python
623
from chip.tlv import TLVWriter, TLVReader
624
import chip.clusters as Clusters
625
626
# Encode OnOff cluster command
627
writer = TLVWriter()
628
629
# OnOff On command (no parameters)
630
writer.start_container(1, 0) # Command structure
631
writer.end_container()
632
633
on_command_tlv = writer.finalize()
634
635
# Encode LevelControl MoveToLevel command with parameters
636
writer = TLVWriter()
637
writer.start_container(1, 0) # Command structure
638
writer.put_int(0, 128) # Level (0-254)
639
writer.put_int(1, 10) # Transition time (1/10 seconds)
640
writer.put_int(2, 0) # Options mask
641
writer.put_int(3, 0) # Options override
642
writer.end_container()
643
644
level_command_tlv = writer.finalize()
645
646
print(f"OnOff command TLV: {len(on_command_tlv)} bytes")
647
print(f"LevelControl command TLV: {len(level_command_tlv)} bytes")
648
649
# Decode level control command
650
reader = TLVReader(level_command_tlv)
651
command_struct = reader.get_container(1)
652
if command_struct:
653
level = command_struct.get_int(0)
654
transition_time = command_struct.get_int(1)
655
options_mask = command_struct.get_int(2)
656
options_override = command_struct.get_int(3)
657
658
print(f"Decoded MoveToLevel command:")
659
print(f" Level: {level}")
660
print(f" Transition time: {transition_time/10.0}s")
661
print(f" Options mask: {options_mask}")
662
print(f" Options override: {options_override}")
663
```
664
665
### TLV Utilities and Type Handling
666
667
```python
668
from chip.tlv import TLVUtils, float32, uint, int
669
670
# Convert Python data to TLV
671
python_data = {
672
"device_info": {
673
"name": "Smart Light",
674
"endpoints": [0, 1],
675
"active": True,
676
"temperature": 23.5
677
},
678
"settings": {
679
"brightness": 75,
680
"color_temp": 2700
681
}
682
}
683
684
# Convert to TLV
685
tlv_bytes = TLVUtils.python_to_tlv(python_data)
686
print(f"Python to TLV: {len(tlv_bytes)} bytes")
687
688
# Convert back to Python
689
decoded_data = TLVUtils.tlv_to_python(tlv_bytes)
690
print(f"TLV to Python: {decoded_data}")
691
692
# Validate TLV data
693
is_valid = TLVUtils.validate_tlv(tlv_bytes)
694
print(f"TLV is valid: {is_valid}")
695
696
# Pretty print TLV structure
697
pretty_output = TLVUtils.pretty_print_tlv(tlv_bytes)
698
print("TLV Structure:")
699
print(pretty_output)
700
701
# Use specific TLV types
702
writer = TLVWriter()
703
704
# Use 32-bit float specifically
705
writer.put(1, float32(3.14159))
706
707
# Use unsigned integer with specific size
708
writer.put(2, uint(65535, size=2)) # 16-bit unsigned int
709
710
# Use signed integer with specific size
711
writer.put(3, int(-32768, size=2)) # 16-bit signed int
712
713
typed_tlv_data = writer.finalize()
714
715
# Read with type information preserved
716
reader = TLVReader(typed_tlv_data)
717
float_val = reader.get_float(1)
718
uint_val = reader.get_uint(2)
719
int_val = reader.get_int(3)
720
721
print(f"32-bit float: {float_val}")
722
print(f"16-bit uint: {uint_val}")
723
print(f"16-bit int: {int_val}")
724
```
725
726
### Custom TLV Tag Encoding
727
728
```python
729
from chip.tlv import TLVWriter, TLVReader, TLVUtils
730
731
# Create custom tags using profile IDs
732
writer = TLVWriter()
733
734
# Standard Matter tags (profile 0)
735
standard_tag = TLVUtils.encode_tag(0, 1) # Profile 0, tag 1
736
writer.put_string(standard_tag, "Standard tag")
737
738
# Vendor-specific tags (custom profile)
739
vendor_profile = 0x1234
740
vendor_tag = TLVUtils.encode_tag(vendor_profile, 100)
741
writer.put_int(vendor_tag, 42)
742
743
# Context-specific tags
744
context_tag = TLVUtils.encode_tag(0, 1, is_context_specific=True)
745
writer.put_bool(context_tag, True)
746
747
custom_tlv_data = writer.finalize()
748
749
# Read and decode tag information
750
reader = TLVReader(custom_tlv_data)
751
all_data = reader.get_all()
752
753
for tag, value in all_data.items():
754
tag_info = TLVUtils.decode_tag(tag)
755
print(f"Tag {tag}: Profile {tag_info['profile_id']}, "
756
f"Number {tag_info['tag_num']}, "
757
f"Context-specific: {tag_info['is_context_specific']}, "
758
f"Value: {value}")
759
```
760
761
### Integration with Matter Device Communication
762
763
```python
764
from chip.tlv import TLVWriter, TLVReader
765
from chip.ChipDeviceCtrl import ChipDeviceController
766
import chip.clusters as Clusters
767
768
# Initialize controller
769
controller = ChipDeviceController(controllerNodeId=12345)
770
771
# Example: Create custom attribute write using TLV
772
writer = TLVWriter()
773
774
# Encode attribute write request
775
writer.start_container(1, 0) # AttributeWriteRequest structure
776
777
# Attribute path
778
writer.start_container(1, 0) # AttributePath
779
writer.put_int(1, 1) # Endpoint
780
writer.put_int(2, 6) # Cluster ID (OnOff)
781
writer.put_int(3, 0) # Attribute ID (OnOff)
782
writer.end_container()
783
784
# Attribute value
785
writer.put_bool(2, True) # Turn on
786
787
writer.end_container()
788
789
write_request_tlv = writer.finalize()
790
791
# In practice, this TLV would be sent as part of a Matter message
792
print(f"Custom attribute write TLV: {len(write_request_tlv)} bytes")
793
794
# Example: Parse attribute read response TLV
795
# (This would typically come from a device response)
796
response_tlv = b"..." # Simulated response data
797
798
try:
799
reader = TLVReader(response_tlv)
800
801
# Parse attribute read response structure
802
if reader.has_tag(1): # Success response
803
attr_data = reader.get_container(1)
804
if attr_data:
805
# Extract attribute path and value
806
path = attr_data.get_container(1)
807
value = attr_data.get(2)
808
809
if path:
810
endpoint = path.get_int(1)
811
cluster = path.get_int(2)
812
attribute = path.get_int(3)
813
814
print(f"Attribute read response:")
815
print(f" Endpoint: {endpoint}")
816
print(f" Cluster: {cluster}")
817
print(f" Attribute: {attribute}")
818
print(f" Value: {value}")
819
820
elif reader.has_tag(2): # Error response
821
error_code = reader.get_int(2)
822
print(f"Attribute read failed with error: {error_code}")
823
824
except Exception as e:
825
print(f"Failed to parse TLV response: {e}")
826
827
# Clean up
828
controller.Shutdown()
829
```