1# Copyright 2019 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#     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"""Controller class for a Bluetooth Device.
15
16This controller will instantiate derived classes from BtDevice and the
17module/class specified via strings in configs dictionary.
18
19The idea is to allow vendors to run blueberry tests with their controller class
20through this controller module, eliminating the need to edit the test classes
21themselves.
22"""
23
24from __future__ import absolute_import
25from __future__ import division
26from __future__ import print_function
27
28import importlib
29import logging
30from typing import Any, Dict, List, Sequence
31
32import yaml
33
34
35MOBLY_CONTROLLER_CONFIG_NAME = 'DerivedBtDevice'
36MOBLY_CONTROLLER_CONFIG_MODULE_KEY = 'ModuleName'
37MOBLY_CONTROLLER_CONFIG_CLASS_KEY = 'ClassName'
38MOBLY_CONTROLLER_CONFIG_PARAMS_KEY = 'Params'
39
40
41def create(configs: List[Dict[str, Any]]) -> List[Any]:
42  """Creates DerivedBtDevice controller objects.
43
44  For each config dict in configs:
45    Import desired controller class from config, compose DerivedBtDevice class
46    from that class and BtDevice, instantiate with params from config.
47
48  Args:
49    configs (list): A list of dicts, each representing a configuration for a
50        Bluetooth device. Each dict should be of the format:
51          {"ModuleName": <name of module in blueberry.controllers>,
52           "ClassName": <name of class to derive controller from>,
53           "Params": <kwargs in dict form to instantiate class with>}
54
55  Returns:
56    A list with DerivedBtDevice objects.
57  """
58  return [_create_bt_device_class(config) for config in configs]
59
60
61def _create_bt_device_class(config: Dict[str, Any]) -> Any:
62  """Created new device class from associated device controller from config."""
63  module = importlib.import_module(
64      'blueberry.controllers.%s' %
65      config[MOBLY_CONTROLLER_CONFIG_MODULE_KEY])
66  logging.info('Creating DerivedBtDevice from %r', config)
67  cls = getattr(module, config[MOBLY_CONTROLLER_CONFIG_CLASS_KEY])
68  params = yaml.safe_load('%s' %
69                          config.get(MOBLY_CONTROLLER_CONFIG_PARAMS_KEY, {}))
70  new_class = type(MOBLY_CONTROLLER_CONFIG_NAME, (cls, BtDevice), params)
71  return new_class(**params)
72
73
74def destroy(derived_bt_devices: Sequence[Any])-> None:
75  """Cleans up DerivedBtDevice objects."""
76  for device in derived_bt_devices:
77    # Execute cleanup if the controller class has the method "clean_up".
78    if hasattr(device, 'clean_up'):
79      device.clean_up()
80  del derived_bt_devices
81
82
83class BtDevice(object):
84  """Base class for all Bluetooth Devices.
85
86  Provides additional necessary functionality for use within blueberry.
87  """
88
89  def __init__(self) -> None:
90    """Initializes a derived bt base class."""
91    self._user_params = {}
92
93  def setup(self) -> None:
94    """For devices that need extra setup."""
95
96  def set_user_params(self, params: Dict[str, str]) -> None:
97    """Intended for passing mobly user_params into a derived device class.
98
99    Args:
100      params: Mobly user params.
101    """
102    self._user_params = params
103
104  def get_user_params(self) -> Dict[str, str]:
105    """Return saved user_params.
106
107    Returns:
108      user_params.
109    """
110    return self._user_params
111
112  def factory_reset_bluetooth(self) -> None:
113    """Factory resets Bluetooth on an BT Device."""
114    raise NotImplementedError
115
116  def activate_pairing_mode(self) -> None:
117    """Activates pairing mode on an AndroidDevice."""
118    raise NotImplementedError
119
120  def get_bluetooth_mac_address(self) -> None:
121    """Get bluetooth mac address of an BT Device."""
122    pass
123