1# Copyright 2024 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"""Class to hold the GATT service/characteristic/descriptor object."""
15
16import uuid
17
18
19class Service:
20    """Class represents Bluetooth GATT service."""
21
22    def __init__(self,
23                 instance_id=None,
24                 service_type=None,
25                 uuid=None,
26                 characteristics=None,
27                 included_services=None,
28                 value=None):
29        self.instance_id = instance_id
30        self.service_type = service_type
31        self.uuid = uuid
32        self.characteristics = characteristics
33        self.included_services = included_services
34        self.value = value
35
36    def to_dict(self):
37        """Converts service object to dictionary.
38
39        Returns:
40            GATT service as dict.
41        """
42        return {
43            'instance_id': self.instance_id,
44            'service_type': self.service_type,
45            'uuid': self.uuid,
46            'included_services': [service.to_dict() for service in self.included_services],
47            'characteristics': [characteristic.to_dict() for characteristic in self.characteristics],
48            'value': self.value
49        }
50
51
52class Characteristic:
53    """Class represents Bluetooth GATT characteristic."""
54
55    def __init__(self,
56                 instance_id=None,
57                 permissions=None,
58                 write_type=None,
59                 descriptors=None,
60                 uuid=None,
61                 key_size=None,
62                 properties=None,
63                 value=None):
64        self.instance_id = instance_id
65        self.permissions = permissions
66        self.write_type = write_type
67        self.descriptors = descriptors
68        self.uuid = uuid
69        self.key_size = key_size
70        self.properties = properties
71        self.value = value
72
73    def to_dict(self):
74        """Converts characteristic object to dictionary.
75
76        Returns:
77            GATT characteristic as dict.
78        """
79        return {
80            'properties': self.properties,
81            'permissions': self.permissions,
82            'uuid': self.uuid,
83            'instance_id': self.instance_id,
84            'descriptors': [descriptor.to_dict() for descriptor in self.descriptors],
85            'key_size': self.key_size,
86            'write_type': self.write_type,
87            'value': self.value
88        }
89
90
91class Descriptor:
92    """Class represents Bluetooth GATT descriptor."""
93
94    def __init__(self, permissions=None, uuid=None, instance_id=None, value=None):
95        self.permissions = permissions
96        self.uuid = uuid
97        self.instance_id = instance_id
98        self.value = value
99
100    def to_dict(self):
101        """Converts descriptor object to dictionary.
102
103        Returns:
104            GATT descriptor as dict.
105        """
106        return {
107            'instance_id': self.instance_id,
108            'permissions': self.permissions,
109            'uuid': self.uuid,
110            'value': self.value
111        }
112
113
114def create_gatt_service(service):
115    """Creates GATT service from a dictionary.
116
117    Args:
118        service: Bluetooth GATT service as a dictionary.
119
120    Returns:
121        Bluetooth GATT service object.
122    """
123    return Service(
124        instance_id=service['instance_id'],
125        service_type=service['service_type'],
126        uuid=str(uuid.UUID(bytes=bytes(service['uuid']))).upper(),
127        included_services=[create_gatt_service(included_service) for included_service in service['included_services']],
128        characteristics=[create_gatt_characteristic(characteristic) for characteristic in service['characteristics']],
129        value=service.get('value'))
130
131
132def create_gatt_characteristic(characteristic):
133    """Creates GATT characteristic from a dictionary.
134
135    Args:
136        characteristic: Bluetooth GATT characteristic as a dictionary.
137
138    Returns:
139        Bluetooth GATT characteristic object.
140    """
141    return Characteristic(
142        properties=characteristic['properties'],
143        permissions=characteristic['permissions'],
144        uuid=str(uuid.UUID(bytes=bytes(characteristic['uuid']))).upper(),
145        instance_id=characteristic['instance_id'],
146        descriptors=[create_gatt_characteristic_descriptor(descriptor) for descriptor in characteristic['descriptors']],
147        key_size=characteristic['key_size'],
148        write_type=characteristic['write_type'],
149        value=characteristic.get('value'))
150
151
152def create_gatt_characteristic_descriptor(descriptor):
153    """Creates GATT descriptor from a dictionary.
154
155    Args:
156        descriptor: Bluetooth GATT descriptor as a dictionary.
157
158    Returns:
159        Bluetooth GATT descriptor object.
160    """
161    return Descriptor(instance_id=descriptor['instance_id'],
162                      permissions=descriptor['permissions'],
163                      uuid=str(uuid.UUID(bytes=bytes(descriptor['uuid']))).upper(),
164                      value=descriptor.get('value'))
165
166
167def convert_object_to_dict(obj):
168    """Coverts object to dictionary.
169
170    Args:
171        obj: Service/Characteristic/Descriptor object.
172
173    Returns:
174        A dictionary represents the object.
175    """
176    if isinstance(obj, (Descriptor, Characteristic, Service)):
177        return obj.to_dict()
178    elif isinstance(obj, list):
179        return [convert_object_to_dict(item) for item in obj]
180    else:
181        return obj
182