1# Copyright 2018 Espressif Systems (Shanghai) PTE LTD
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#     http://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
16from __future__ import print_function
17
18from . import ble_cli
19from .transport import Transport
20
21
22class Transport_BLE(Transport):
23    def __init__(self, devname, service_uuid, nu_lookup):
24        # Expect service UUID like '0000ffff-0000-1000-8000-00805f9b34fb'
25        for name in nu_lookup.keys():
26            # Calculate characteristic UUID for each endpoint
27            nu_lookup[name] = service_uuid[:4] + '{:02x}'.format(
28                int(nu_lookup[name], 16) & int(service_uuid[4:8], 16)) + service_uuid[8:]
29
30        # Get BLE client module
31        self.cli = ble_cli.get_client()
32
33        # Use client to connect to BLE device and bind to service
34        if not self.cli.connect(devname=devname, iface='hci0',
35                                chrc_names=nu_lookup.keys(),
36                                fallback_srv_uuid=service_uuid):
37            raise RuntimeError('Failed to initialize transport')
38
39        # Irrespective of provided parameters, let the client
40        # generate a lookup table by reading advertisement data
41        # and characteristic user descriptors
42        self.name_uuid_lookup = self.cli.get_nu_lookup()
43
44        # If that doesn't work, use the lookup table provided as parameter
45        if self.name_uuid_lookup is None:
46            self.name_uuid_lookup = nu_lookup
47            # Check if expected characteristics are provided by the service
48            for name in self.name_uuid_lookup.keys():
49                if not self.cli.has_characteristic(self.name_uuid_lookup[name]):
50                    raise RuntimeError("'" + name + "' endpoint not found")
51
52    def __del__(self):
53        # Make sure device is disconnected before application gets closed
54        try:
55            self.disconnect()
56        except Exception:
57            pass
58
59    def disconnect(self):
60        self.cli.disconnect()
61
62    def send_data(self, ep_name, data):
63        # Write (and read) data to characteristic corresponding to the endpoint
64        if ep_name not in self.name_uuid_lookup.keys():
65            raise RuntimeError('Invalid endpoint : ' + ep_name)
66        return self.cli.send_data(self.name_uuid_lookup[ep_name], data)
67