• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2021-2022 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15# -----------------------------------------------------------------------------
16# GATT - Generic Attribute Profile
17#
18# See Bluetooth spec @ Vol 3, Part G
19#
20# -----------------------------------------------------------------------------
21
22# -----------------------------------------------------------------------------
23# Imports
24# -----------------------------------------------------------------------------
25import asyncio
26import types
27import logging
28from colors import color
29
30from .core import *
31from .hci import *
32from .att import *
33
34# -----------------------------------------------------------------------------
35# Logging
36# -----------------------------------------------------------------------------
37logger = logging.getLogger(__name__)
38
39# -----------------------------------------------------------------------------
40# Constants
41# -----------------------------------------------------------------------------
42GATT_REQUEST_TIMEOUT = 30  # seconds
43
44GATT_MAX_ATTRIBUTE_VALUE_SIZE = 512
45
46# Services
47GATT_GENERIC_ACCESS_SERVICE                 = UUID.from_16_bits(0x1800, 'Generic Access')
48GATT_GENERIC_ATTRIBUTE_SERVICE              = UUID.from_16_bits(0x1801, 'Generic Attribute')
49GATT_IMMEDIATE_ALERT_SERVICE                = UUID.from_16_bits(0x1802, 'Immediate Alert')
50GATT_LINK_LOSS_SERVICE                      = UUID.from_16_bits(0x1803, 'Link Loss')
51GATT_TX_POWER_SERVICE                       = UUID.from_16_bits(0x1804, 'TX Power')
52GATT_CURRENT_TIME_SERVICE                   = UUID.from_16_bits(0x1805, 'Current Time')
53GATT_REFERENCE_TIME_UPDATE_SERVICE          = UUID.from_16_bits(0x1806, 'Reference Time Update')
54GATT_NEXT_DST_CHANGE_SERVICE                = UUID.from_16_bits(0x1807, 'Next DST Change')
55GATT_GLUCOSE_SERVICE                        = UUID.from_16_bits(0x1808, 'Glucose')
56GATT_HEALTH_THERMOMETER_SERVICE             = UUID.from_16_bits(0x1809, 'Health Thermometer')
57GATT_DEVICE_INFORMATION_SERVICE             = UUID.from_16_bits(0x180A, 'Device Information')
58GATT_HEART_RATE_SERVICE                     = UUID.from_16_bits(0x180D, 'Heart Rate')
59GATT_PHONE_ALERT_STATUS_SERVICE             = UUID.from_16_bits(0x180E, 'Phone Alert Status')
60GATT_BATTERY_SERVICE                        = UUID.from_16_bits(0x180F, 'Battery')
61GATT_BLOOD_PRESSURE_SERVICE                 = UUID.from_16_bits(0x1810, 'Blood Pressure')
62GATT_ALERT_NOTIFICATION_SERVICE             = UUID.from_16_bits(0x1811, 'Alert Notification')
63GATT_HUMAN_INTERFACE_DEVICE_SERVICE         = UUID.from_16_bits(0x1812, 'Human Interface Device')
64GATT_SCAN_PARAMETERS_SERVICE                = UUID.from_16_bits(0x1813, 'Scan Parameters')
65GATT_RUNNING_SPEED_AND_CADENCE_SERVICE      = UUID.from_16_bits(0x1814, 'Running Speed and Cadence')
66GATT_AUTOMATION_IO_SERVICE                  = UUID.from_16_bits(0x1815, 'Automation IO')
67GATT_CYCLING_SPEED_AND_CADENCE_SERVICE      = UUID.from_16_bits(0x1816, 'Cycling Speed and Cadence')
68GATT_CYCLING_POWER_SERVICE                  = UUID.from_16_bits(0x1818, 'Cycling Power')
69GATT_LOCATION_AND_NAVIGATION_SERVICE        = UUID.from_16_bits(0x1819, 'Location and Navigation')
70GATT_ENVIRONMENTAL_SENSING_SERVICE          = UUID.from_16_bits(0x181A, 'Environmental Sensing')
71GATT_BODY_COMPOSITION_SERVICE               = UUID.from_16_bits(0x181B, 'Body Composition')
72GATT_USER_DATA_SERVICE                      = UUID.from_16_bits(0x181C, 'User Data')
73GATT_WEIGHT_SCALE_SERVICE                   = UUID.from_16_bits(0x181D, 'Weight Scale')
74GATT_BOND_MANAGEMENT_SERVICE                = UUID.from_16_bits(0x181E, 'Bond Management')
75GATT_CONTINUOUS_GLUCOSE_MONITORING_SERVICE  = UUID.from_16_bits(0x181F, 'Continuous Glucose Monitoring')
76GATT_INTERNET_PROTOCOL_SUPPORT_SERVICE      = UUID.from_16_bits(0x1820, 'Internet Protocol Support')
77GATT_INDOOR_POSITIONING_SERVICE             = UUID.from_16_bits(0x1821, 'Indoor Positioning')
78GATT_PULSE_OXIMETER_SERVICE                 = UUID.from_16_bits(0x1822, 'Pulse Oximeter')
79GATT_HTTP_PROXY_SERVICE                     = UUID.from_16_bits(0x1823, 'HTTP Proxy')
80GATT_TRANSPORT_DISCOVERY_SERVICE            = UUID.from_16_bits(0x1824, 'Transport Discovery')
81GATT_OBJECT_TRANSFER_SERVICE                = UUID.from_16_bits(0x1825, 'Object Transfer')
82GATT_FITNESS_MACHINE_SERVICE                = UUID.from_16_bits(0x1826, 'Fitness Machine')
83GATT_MESH_PROVISIONING_SERVICE              = UUID.from_16_bits(0x1827, 'Mesh Provisioning')
84GATT_MESH_PROXY_SERVICE                     = UUID.from_16_bits(0x1828, 'Mesh Proxy')
85GATT_RECONNECTION_CONFIGURATION_SERVICE     = UUID.from_16_bits(0x1829, 'Reconnection Configuration')
86GATT_INSULIN_DELIVERY_SERVICE               = UUID.from_16_bits(0x183A, 'Insulin Delivery')
87GATT_BINARY_SENSOR_SERVICE                  = UUID.from_16_bits(0x183B, 'Binary Sensor')
88GATT_EMERGENCY_CONFIGURATION_SERVICE        = UUID.from_16_bits(0x183C, 'Emergency Configuration')
89GATT_PHYSICAL_ACTIVITY_MONITOR_SERVICE      = UUID.from_16_bits(0x183E, 'Physical Activity Monitor')
90GATT_AUDIO_INPUT_CONTROL_SERVICE            = UUID.from_16_bits(0x1843, 'Audio Input Control')
91GATT_VOLUME_CONTROL_SERVICE                 = UUID.from_16_bits(0x1844, 'Volume Control')
92GATT_VOLUME_OFFSET_CONTROL_SERVICE          = UUID.from_16_bits(0x1845, 'Volume Offset Control')
93GATT_COORDINATED_SET_IDENTIFICATION_SERVICE = UUID.from_16_bits(0x1846, 'Coordinated Set Identification Service')
94GATT_DEVICE_TIME_SERVICE                    = UUID.from_16_bits(0x1847, 'Device Time')
95GATT_MEDIA_CONTROL_SERVICE                  = UUID.from_16_bits(0x1848, 'Media Control Service')
96GATT_GENERIC_MEDIA_CONTROL_SERVICE          = UUID.from_16_bits(0x1849, 'Generic Media Control Service')
97GATT_CONSTANT_TONE_EXTENSION_SERVICE        = UUID.from_16_bits(0x184A, 'Constant Tone Extension')
98GATT_TELEPHONE_BEARER_SERVICE               = UUID.from_16_bits(0x184B, 'Telephone Bearer Service')
99GATT_GENERIC_TELEPHONE_BEARER_SERVICE       = UUID.from_16_bits(0x184C, 'Generic Telephone Bearer Service')
100GATT_MICROPHONE_CONTROL_SERVICE             = UUID.from_16_bits(0x184D, 'Microphone Control')
101
102# Types
103GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE   = UUID.from_16_bits(0x2800, 'Primary Service')
104GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2801, 'Secondary Service')
105GATT_INCLUDE_ATTRIBUTE_TYPE           = UUID.from_16_bits(0x2802, 'Include')
106GATT_CHARACTERISTIC_ATTRIBUTE_TYPE    = UUID.from_16_bits(0x2803, 'Characteristic')
107
108# Descriptors
109GATT_CHARACTERISTIC_EXTENDED_PROPERTIES_DESCRIPTOR   = UUID.from_16_bits(0x2900, 'Characteristic Extended Properties')
110GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR      = UUID.from_16_bits(0x2901, 'Characteristic User Description')
111GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR  = UUID.from_16_bits(0x2902, 'Client Characteristic Configuration')
112GATT_SERVER_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR  = UUID.from_16_bits(0x2903, 'Server Characteristic Configuration')
113GATT_CHARACTERISTIC_PRESENTATION_FORMAT_DESCRIPTOR   = UUID.from_16_bits(0x2904, 'Characteristic Format')
114GATT_CHARACTERISTIC_AGGREGATE_FORMAT_DESCRIPTOR      = UUID.from_16_bits(0x2905, 'Characteristic Aggregate Format')
115GATT_VALID_RANGE_DESCRIPTOR                          = UUID.from_16_bits(0x2906, 'Valid Range')
116GATT_EXTERNAL_REPORT_DESCRIPTOR                      = UUID.from_16_bits(0x2907, 'External Report')
117GATT_REPORT_REFERENCE_DESCRIPTOR                     = UUID.from_16_bits(0x2908, 'Report Reference')
118GATT_NUMBER_OF_DIGITALS_DESCRIPTOR                   = UUID.from_16_bits(0x2909, 'Number of Digitals')
119GATT_VALUE_TRIGGER_SETTING_DESCRIPTOR                = UUID.from_16_bits(0x290A, 'Value Trigger Setting')
120GATT_ENVIRONMENTAL_SENSING_CONFIGURATION_DESCRIPTOR  = UUID.from_16_bits(0x290B, 'Environmental Sensing Configuration')
121GATT_ENVIRONMENTAL_SENSING_MEASUREMENT_DESCRIPTOR    = UUID.from_16_bits(0x290C, 'Environmental Sensing Measurement')
122GATT_ENVIRONMENTAL_SENSING_TRIGGER_DESCRIPTOR        = UUID.from_16_bits(0x290D, 'Environmental Sensing Trigger Setting')
123GATT_TIME_TRIGGER_DESCRIPTOR                         = UUID.from_16_bits(0x290E, 'Time Trigger Setting')
124GATT_COMPLETE_BR_EDR_TRANSPORT_BLOCK_DATA_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Complete BR-EDR Transport Block Data')
125
126# Device Information Service
127GATT_SYSTEM_ID_CHARACTERISTIC                          = UUID.from_16_bits(0x2A23, 'System ID')
128GATT_MODEL_NUMBER_STRING_CHARACTERISTIC                = UUID.from_16_bits(0x2A24, 'Model Number String')
129GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC               = UUID.from_16_bits(0x2A25, 'Serial Number String')
130GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A26, 'Firmware Revision String')
131GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A27, 'Hardware Revision String')
132GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A28, 'Software Revision String')
133GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A29, 'Manufacturer Name String')
134GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC = UUID.from_16_bits(0x2A2A, 'IEEE 11073-20601 Regulatory Certification Data List')
135GATT_PNP_ID_CHARACTERISTIC                             = UUID.from_16_bits(0x2A50, 'PnP ID')
136
137# Human Interface Device
138GATT_HID_INFORMATION_CHARACTERISTIC   = UUID.from_16_bits(0x2A4A, 'HID Information')
139GATT_REPORT_MAP_CHARACTERISTIC        = UUID.from_16_bits(0x2A4B, 'Report Map')
140GATT_HID_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2A4C, 'HID Control Point')
141GATT_REPORT_CHARACTERISTIC            = UUID.from_16_bits(0x2A4D, 'Report')
142GATT_PROTOCOL_MODE_CHARACTERISTIC     = UUID.from_16_bits(0x2A4E, 'Protocol Mode')
143
144# Misc
145GATT_DEVICE_NAME_CHARACTERISTIC                                = UUID.from_16_bits(0x2A00, 'Device Name')
146GATT_APPEARANCE_CHARACTERISTIC                                 = UUID.from_16_bits(0x2A01, 'Appearance')
147GATT_PERIPHERAL_PRIVACY_FLAG_CHARACTERISTIC                    = UUID.from_16_bits(0x2A02, 'Peripheral Privacy Flag')
148GATT_RECONNECTION_ADDRESS_CHARACTERISTIC                       = UUID.from_16_bits(0x2A03, 'Reconnection Address')
149GATT_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.from_16_bits(0x2A04, 'Peripheral Preferred Connection Parameters')
150GATT_SERVICE_CHANGED_CHARACTERISTIC                            = UUID.from_16_bits(0x2A05, 'Service Changed')
151GATT_ALERT_LEVEL_CHARACTERISTIC                                = UUID.from_16_bits(0x2A06, 'Alert Level')
152GATT_TX_POWER_LEVEL_CHARACTERISTIC                             = UUID.from_16_bits(0x2A07, 'Tx Power Level')
153GATT_BATTERY_LEVEL_CHARACTERISTIC                              = UUID.from_16_bits(0x2A19, 'Battery Level')
154GATT_BOOT_KEYBOARD_INPUT_REPORT_CHARACTERISTIC                 = UUID.from_16_bits(0x2A22, 'Boot Keyboard Input Report')
155GATT_CURRENT_TIME_CHARACTERISTIC                               = UUID.from_16_bits(0x2A2B, 'Current Time')
156GATT_BOOT_KEYBOARD_OUTPUT_REPORT_CHARACTERISTIC                = UUID.from_16_bits(0x2A32, 'Boot Keyboard Output Report')
157GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC                = UUID.from_16_bits(0x2AA6, 'Central Address Resolution')
158
159
160# -----------------------------------------------------------------------------
161# Utils
162# -----------------------------------------------------------------------------
163
164def show_services(services):
165    for service in services:
166        print(color(str(service), 'cyan'))
167
168        for characteristic in service.characteristics:
169            print(color('  ' + str(characteristic), 'magenta'))
170
171            for descriptor in characteristic.descriptors:
172                print(color('    ' + str(descriptor), 'green'))
173
174
175# -----------------------------------------------------------------------------
176class Service(Attribute):
177    '''
178    See Vol 3, Part G - 3.1 SERVICE DEFINITION
179    '''
180
181    def __init__(self, uuid, characteristics, primary=True):
182        # Convert the uuid to a UUID object if it isn't already
183        if type(uuid) is str:
184            uuid = UUID(uuid)
185
186        super().__init__(
187            GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE if primary else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
188            Attribute.READABLE,
189            uuid.to_pdu_bytes()
190        )
191        self.uuid              = uuid
192        self.included_services = []
193        self.characteristics   = characteristics[:]
194        self.primary           = primary
195
196    def __str__(self):
197        return f'Service(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}){"" if self.primary else "*"}'
198
199
200# -----------------------------------------------------------------------------
201class TemplateService(Service):
202    '''
203    Convenience abstract class that can be used by profile-specific subclasses that want
204    to expose their UUID as a class property
205    '''
206    UUID = None
207
208    def __init__(self, characteristics, primary=True):
209        super().__init__(self.UUID, characteristics, primary)
210
211
212# -----------------------------------------------------------------------------
213class Characteristic(Attribute):
214    '''
215    See Vol 3, Part G - 3.3 CHARACTERISTIC DEFINITION
216    '''
217
218    # Property flags
219    BROADCAST                   = 0x01
220    READ                        = 0x02
221    WRITE_WITHOUT_RESPONSE      = 0x04
222    WRITE                       = 0x08
223    NOTIFY                      = 0x10
224    INDICATE                    = 0X20
225    AUTHENTICATED_SIGNED_WRITES = 0X40
226    EXTENDED_PROPERTIES         = 0X80
227
228    PROPERTY_NAMES = {
229        BROADCAST:                   'BROADCAST',
230        READ:                        'READ',
231        WRITE_WITHOUT_RESPONSE:      'WRITE_WITHOUT_RESPONSE',
232        WRITE:                       'WRITE',
233        NOTIFY:                      'NOTIFY',
234        INDICATE:                    'INDICATE',
235        AUTHENTICATED_SIGNED_WRITES: 'AUTHENTICATED_SIGNED_WRITES',
236        EXTENDED_PROPERTIES:         'EXTENDED_PROPERTIES'
237    }
238
239    @staticmethod
240    def property_name(property):
241        return Characteristic.PROPERTY_NAMES.get(property, '')
242
243    @staticmethod
244    def properties_as_string(properties):
245        return ','.join([
246            Characteristic.property_name(p) for p in Characteristic.PROPERTY_NAMES.keys()
247            if properties & p
248        ])
249
250    def __init__(self, uuid, properties, permissions, value = b'', descriptors = []):
251        super().__init__(uuid, permissions, value)
252        self.uuid        = self.type
253        self.properties  = properties
254        self.descriptors = descriptors
255
256    def get_descriptor(self, descriptor_type):
257        for descriptor in self.descriptors:
258            if descriptor.uuid == descriptor_type:
259                return descriptor
260
261    def __str__(self):
262        return f'Characteristic(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}, properties={Characteristic.properties_as_string(self.properties)})'
263
264
265# -----------------------------------------------------------------------------
266class CharacteristicValue:
267    '''
268    Characteristic value where reading and/or writing is delegated to functions
269    passed as arguments to the constructor.
270    '''
271    def __init__(self, read=None, write=None):
272        self._read = read
273        self._write = write
274
275    def read(self, connection):
276        return self._read(connection) if self._read else b''
277
278    def write(self, connection, value):
279        if self._write:
280            self._write(connection, value)
281
282
283# -----------------------------------------------------------------------------
284class CharacteristicAdapter:
285    '''
286    An adapter that can adapt any object with `read_value` and `write_value`
287    methods (like Characteristic and CharacteristicProxy objects) by wrapping
288    those methods with ones that return/accept encoded/decoded values.
289    Objects with async methods are considered proxies, so the adaptation is one
290    where the return value of `read_value` is decoded and the value passed to
291    `write_value` is encoded. Other objects are considered local characteristics
292    so the adaptation is one where the return value of `read_value` is encoded
293    and the value passed to `write_value` is decoded.
294    If the characteristic has a `subscribe` method, it is wrapped with one where
295    the values are decoded before being passed to the subscriber.
296    '''
297    def __init__(self, characteristic):
298        self.wrapped_characteristic = characteristic
299
300        if (
301            asyncio.iscoroutinefunction(characteristic.read_value) and
302            asyncio.iscoroutinefunction(characteristic.write_value)
303        ):
304            self.read_value  = self.read_decoded_value
305            self.write_value = self.write_decoded_value
306        else:
307            self.read_value  = self.read_encoded_value
308            self.write_value = self.write_encoded_value
309
310        if hasattr(self.wrapped_characteristic, 'subscribe'):
311            self.subscribe = self.wrapped_subscribe
312
313    def __getattr__(self, name):
314        return getattr(self.wrapped_characteristic, name)
315
316    def read_encoded_value(self, connection):
317        return self.encode_value(self.wrapped_characteristic.read_value(connection))
318
319    def write_encoded_value(self, connection, value):
320        return self.wrapped_characteristic.write_value(connection, self.decode_value(value))
321
322    async def read_decoded_value(self):
323        return self.decode_value(await self.wrapped_characteristic.read_value())
324
325    async def write_decoded_value(self, value):
326        return await self.wrapped_characteristic.write_value(self.encode_value(value))
327
328    def encode_value(self, value):
329        return value
330
331    def decode_value(self, value):
332        return value
333
334    def wrapped_subscribe(self, subscriber=None):
335        return self.wrapped_characteristic.subscribe(
336            None if subscriber is None else lambda value: subscriber(self.decode_value(value))
337        )
338
339
340# -----------------------------------------------------------------------------
341class DelegatedCharacteristicAdapter(CharacteristicAdapter):
342    def __init__(self, characteristic, encode, decode):
343        super().__init__(characteristic)
344        self.encode = encode
345        self.decode = decode
346
347    def encode_value(self, value):
348        return self.encode(value)
349
350    def decode_value(self, value):
351        return self.decode(value)
352
353
354# -----------------------------------------------------------------------------
355class PackedCharacteristicAdapter(CharacteristicAdapter):
356    '''
357    Adapter that packs/unpacks characteristic values according to a standard
358    Python `struct` format.
359    For formats with a single value, the adapted `read_value` and `write_value`
360    methods return/accept single values. For formats with multiple values,
361    they return/accept a tuple with the same number of elements as is required for
362    the format.
363    '''
364    def __init__(self, characteristic, format):
365        super().__init__(characteristic)
366        self.struct = struct.Struct(format)
367
368    def pack(self, *values):
369        return self.struct.pack(*values)
370
371    def unpack(self, buffer):
372        return self.struct.unpack(buffer)
373
374    def encode_value(self, value):
375        return self.pack(*value if type(value) is tuple else (value,))
376
377    def decode_value(self, value):
378        unpacked = self.unpack(value)
379        return unpacked[0] if len(unpacked) == 1 else unpacked
380
381
382# -----------------------------------------------------------------------------
383class MappedCharacteristicAdapter(PackedCharacteristicAdapter):
384    '''
385    Adapter that packs/unpacks characteristic values according to a standard
386    Python `struct` format.
387    The adapted `read_value` and `write_value` methods return/accept aa dictionary which
388    is packed/unpacked according to format, with the arguments extracted from the dictionary
389    by key, in the same order as they occur in the `keys` parameter.
390    '''
391    def __init__(self, characteristic, format, keys):
392        super().__init__(characteristic, format)
393        self.keys = keys
394
395    def pack(self, values):
396        return super().pack(*(values[key] for key in self.keys))
397
398    def unpack(self, buffer):
399        return dict(zip(self.keys, super().unpack(buffer)))
400
401
402# -----------------------------------------------------------------------------
403class UTF8CharacteristicAdapter(CharacteristicAdapter):
404    '''
405    Adapter that converts strings to/from bytes using UTF-8 encoding
406    '''
407    def encode_value(self, value):
408        return value.encode('utf-8')
409
410    def decode_value(self, value):
411        return value.decode('utf-8')
412
413
414# -----------------------------------------------------------------------------
415class Descriptor(Attribute):
416    '''
417    See Vol 3, Part G - 3.3.3 Characteristic Descriptor Declarations
418    '''
419
420    def __init__(self, descriptor_type, permissions, value = b''):
421        super().__init__(descriptor_type, permissions, value)
422
423    def __str__(self):
424        return f'Descriptor(handle=0x{self.handle:04X}, type={self.type}, value={self.read_value(None).hex()})'
425