Merge lp:~kaxing/checkbox/bt4-bluez5 into lp:checkbox
- bt4-bluez5
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Sylvain Pineau |
Approved revision: | 4279 |
Merged at revision: | 4320 |
Proposed branch: | lp:~kaxing/checkbox/bt4-bluez5 |
Merge into: | lp:checkbox |
Diff against target: |
498 lines (+477/-0) 3 files modified
providers/plainbox-provider-checkbox/bin/bt_connect (+135/-0) providers/plainbox-provider-checkbox/bin/bt_helper.py (+300/-0) providers/plainbox-provider-checkbox/jobs/bluetooth.txt.in (+42/-0) |
To merge this branch: | bzr merge lp:~kaxing/checkbox/bt4-bluez5 |
Related bugs: | |
Related blueprints: |
Add test cases for Bluetooth 4.x
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sylvain Pineau (community) | Approve | ||
Yung Shen (community) | Needs Resubmitting | ||
Review via email: mp+290716@code.launchpad.net |
Commit message
Description of the change
context inherited from: https:/
Add a manifest request for asking tester about BT 4.x capability.
Add a BT 4.x specific jos for HOGP devices (keyboard/mouse).
Based on bt_helper for Bluez 5.x support
known issue:
Unable to get the input() in plainbox, looking for alternative ways.
Maciej Kisielewski (kissiel) wrote : | # |
Yung Shen (kaxing) wrote : | # |
@kissiel Thanks for the review, will update base on your feedbacks. But for naming about unpairing() still needs a bit of advising. Please check.
Po-Hsu Lin (cypressyew) wrote : | # |
One small note for the break in the for loop
Yung Shen (kaxing) wrote : | # |
Update few in-line comment replies.
Maciej Kisielewski (kissiel) wrote : | # |
Following up the discussion in the inline comments
Yung Shen (kaxing) wrote : | # |
replies updating!
Yung Shen (kaxing) wrote : | # |
Updating everything based on previous in-line comments.
For print(flush=True) I've file a new bug for further discussing.(if anyone interested)
https:/
Yung Shen (kaxing) wrote : | # |
Verified on a targeting system(bt4, xenial/bluez5), everything works as expected.
Only one kind of failure the script won't able to capture:
the host system/bluez thinks it's paired but the device still blinking.
but I think this is left to tester's judgment as they are user-interact-
Sylvain Pineau (sylvain-pineau) wrote : | # |
I've proposed a workaround to the print(flush=True), see my inline suggestion.
Sylvain Pineau (sylvain-pineau) wrote : | # |
idem for the mouse job
Yung Shen (kaxing) wrote : | # |
I've removed the part that requires instant flush, so the bt_connect now is not effected to the buffered issue. those print() within flush=True is working as expected.
Po-Hsu Lin (cypressyew) wrote : | # |
Hi Yung,
from the revision history, it seems that the branch was not re-submitted successfully, the latest one is still rev 4276, Apr. 13
Yung Shen (kaxing) wrote : | # |
Hey @cypressyew, that's about right, I didn't made any changes since I've already removed the "buffered" part that mentioned in previous comments in revno 4276.
Sylvain Pineau (sylvain-pineau) wrote : | # |
I tried to run the new tests using:
plainbox run -i 2013.com.
I didn't work for two reasons:
1. Manifest entries does not support the requires statement
2. The two new jobs needs the following line to use manifest properly:
imports: from 2013.com.
Yung Shen (kaxing) wrote : | # |
Thanks for the hint, fixed manifest and add bug number in comment for buffered print().
Sylvain Pineau (sylvain-pineau) wrote : | # |
One minor fix and it should be good to go, see below
Po-Hsu Lin (cypressyew) wrote : | # |
And I think we could remove the TODO part in the comment, as this is quite mature now. (Convert print to logging might be the last thing)
Yung Shen (kaxing) wrote : | # |
So I tried logging with level=logging.info it appears there are a lot informations going on, And I think we will need to discuss this, will keep my fake logging outputs at the moment so the cli work as expected.
Yung Shen (kaxing) : | # |
Sylvain Pineau (sylvain-pineau) wrote : | # |
Those logging info could indeed be very useful. Let's merge this version and iterate.
A big +1 to all contributors. Thanks
Preview Diff
1 | === added file 'providers/plainbox-provider-checkbox/bin/bt_connect' |
2 | --- providers/plainbox-provider-checkbox/bin/bt_connect 1970-01-01 00:00:00 +0000 |
3 | +++ providers/plainbox-provider-checkbox/bin/bt_connect 2016-04-21 10:03:47 +0000 |
4 | @@ -0,0 +1,135 @@ |
5 | +#!/usr/bin/env python3 |
6 | +# |
7 | +# This file is part of Checkbox. |
8 | +# |
9 | +# Copyright 2016 Canonical Ltd. |
10 | +# |
11 | +# Authors: |
12 | +# Po-Hsu Lin <po-hsu.lin@canonical.com> |
13 | +# Yung Shen <yung.shen@canonical.com> |
14 | +# |
15 | +# Checkbox is free software: you can redistribute it and/or modify |
16 | +# it under the terms of the GNU General Public License version 3, |
17 | +# as published by the Free Software Foundation. |
18 | +# |
19 | +# Checkbox is distributed in the hope that it will be useful, |
20 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | +# GNU General Public License for more details. |
23 | +# |
24 | +# You should have received a copy of the GNU General Public License |
25 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
26 | + |
27 | +import sys |
28 | +import time |
29 | + |
30 | +import bt_helper |
31 | + |
32 | +from argparse import ArgumentParser |
33 | + |
34 | + |
35 | +def unpair_all(devices, manager): |
36 | + """ Unpairing paired devices and scanning again for rerun jobs.""" |
37 | + for dev in devices: |
38 | + try: |
39 | + print("INFO: Unpairing", dev) |
40 | + dev.unpair() |
41 | + except bt_helper.BtException as exc: |
42 | + print("Warning: Unpairing failed", exc) |
43 | + else: |
44 | + # print(flush=True) to bypass plainbox output buffer, |
45 | + # see LP: #1569808 for more details. |
46 | + print("Please reset the device to pairing mode in 13 seconds", |
47 | + flush=True) |
48 | + time.sleep(13) |
49 | + print("INFO: Re-scaning for devices in pairing mode", flush=True) |
50 | + manager.scan() |
51 | + |
52 | + |
53 | +def main(): |
54 | + """Add argument parser here and do most of the job.""" |
55 | + parser = ArgumentParser(description=("Bluetooth auto paring and connect. " |
56 | + "Please select one option.")) |
57 | + group = parser.add_mutually_exclusive_group(required=True) |
58 | + group.add_argument("--mac", type=str, |
59 | + help="Pair with a given MAC, not using scan result,") |
60 | + group.add_argument("--mouse", action="store_const", |
61 | + const="input-mouse", dest="target", |
62 | + help="List and pair with mouse devices") |
63 | + group.add_argument("--keyboard", action="store_const", |
64 | + const="input-keyboard", dest="target", |
65 | + help="List and pair with keyboard devices") |
66 | + args = parser.parse_args() |
67 | + |
68 | + manager = bt_helper.BtManager() |
69 | + # Power on bluetooth adapter and scanning devices in advance. |
70 | + manager.ensure_adapters_powered() |
71 | + manager.scan() |
72 | + |
73 | + if args.mac: |
74 | + # TODO check MAC format |
75 | + print("INFO: Trying to pair with {}".format(args.mac)) |
76 | + device = list(manager.get_bt_devices(filters={'Address': args.mac})) |
77 | + paired_device = list(manager.get_bt_devices( |
78 | + filters={'Address': args.mac, 'Paired': True})) |
79 | + if not device: |
80 | + print("ERROR: No pairable device found, terminating") |
81 | + return 1 |
82 | + |
83 | + unpair_all(paired_device, manager) |
84 | + |
85 | + for dev in device: |
86 | + try: |
87 | + dev.pair() |
88 | + except bt_helper.BtException as exc: |
89 | + print("ERROR: Unable to pair: ", exc) |
90 | + return 1 |
91 | + else: |
92 | + print("INFO: Device paired") |
93 | + return 0 |
94 | + else: |
95 | + print("INFO: Listing targeting devices") |
96 | + # Listing device based on RSSI |
97 | + paired_targets = list(manager.get_bt_devices(category=bt_helper.BT_ANY, |
98 | + filters={'Paired': True, 'Icon': args.target})) |
99 | + if not paired_targets: |
100 | + print("INFO: No paired targeting devices found") |
101 | + manager.scan() |
102 | + else: |
103 | + unpair_all(paired_targets, manager) |
104 | + |
105 | + target_devices = sorted(manager.get_bt_devices( |
106 | + category=bt_helper.BT_ANY, filters={ |
107 | + 'Paired': False, 'Icon': args.target}), |
108 | + key=lambda x: int(x.rssi or -255), reverse=True) |
109 | + if not target_devices: |
110 | + print("ERROR: No target devices found, terminating") |
111 | + return 1 |
112 | + print("INFO: Detected devices (sorted by RSSI; highest first).") |
113 | + # let's assing numbers to devices |
114 | + devices = dict(enumerate(target_devices, 1)) |
115 | + for num, dev in devices.items(): |
116 | + print("{}. {} (RSSI: {})".format(num, dev, dev.rssi)) |
117 | + chosen = False |
118 | + while not chosen: |
119 | + print("Which one would you like to connect to? (0 to exit)") |
120 | + num = input() |
121 | + # TODO: enter as default to 1st device |
122 | + if num == '0': |
123 | + return 1 |
124 | + chosen = num.isnumeric() and int(num) in devices.keys() |
125 | + print("INFO: {} chosen.".format(devices[int(num)])) |
126 | + print("INFO: Pairing selected device..") |
127 | + try: |
128 | + devices[int(num)].pair() |
129 | + except bt_helper.BtException as exc: |
130 | + print("ERROR: something wrong: ", exc) |
131 | + return 1 |
132 | + else: |
133 | + print("Paired successfully.") |
134 | + return 0 |
135 | + # capture all other silence failures |
136 | + return 1 |
137 | + |
138 | +if __name__ == "__main__": |
139 | + sys.exit(main()) |
140 | |
141 | === added file 'providers/plainbox-provider-checkbox/bin/bt_helper.py' |
142 | --- providers/plainbox-provider-checkbox/bin/bt_helper.py 1970-01-01 00:00:00 +0000 |
143 | +++ providers/plainbox-provider-checkbox/bin/bt_helper.py 2016-04-21 10:03:47 +0000 |
144 | @@ -0,0 +1,300 @@ |
145 | +# Copyright 2016 Canonical Ltd. |
146 | +# Written by: |
147 | +# Maciej Kisielewski <maciej.kisielewski@canonical.com> |
148 | +# |
149 | +# This is free software: you can redistribute it and/or modify |
150 | +# it under the terms of the GNU General Public License version 3, |
151 | +# as published by the Free Software Foundation. |
152 | +# |
153 | +# This file is distributed in the hope that it will be useful, |
154 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
155 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
156 | +# GNU General Public License for more details. |
157 | +# |
158 | +# You should have received a copy of the GNU General Public License |
159 | +# along with this file. If not, see <http://www.gnu.org/licenses/>. |
160 | +""" |
161 | +This module provides a set of abstractions to ease the process of automating |
162 | +typical Bluetooth task like scanning for devices and pairing with them. |
163 | + |
164 | +It talks with BlueZ stack using dbus. |
165 | +""" |
166 | +import logging |
167 | + |
168 | +import dbus |
169 | +import dbus.service |
170 | +import dbus.mainloop.glib |
171 | +from gi.repository import GObject |
172 | + |
173 | +logger = logging.getLogger(__file__) |
174 | +logger.addHandler(logging.StreamHandler()) |
175 | + |
176 | +IFACE = 'org.bluez.Adapter1' |
177 | +ADAPTER_IFACE = 'org.bluez.Adapter1' |
178 | +DEVICE_IFACE = 'org.bluez.Device1' |
179 | +AGENT_IFACE = 'org.bluez.Agent1' |
180 | + |
181 | +dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |
182 | + |
183 | +# To get additional Bluetoot CoDs, check |
184 | +# https://www.bluetooth.com/specifications/assigned-numbers/baseband |
185 | +BT_ANY = 0 |
186 | +BT_KEYBOARD = int('0x2540', 16) |
187 | + |
188 | + |
189 | +class BtException(Exception): |
190 | + pass |
191 | + |
192 | + |
193 | +class BtManager: |
194 | + """ Main point of contact with dbus factoring bt objects. """ |
195 | + def __init__(self, verbose=False): |
196 | + if verbose: |
197 | + logger.setLevel(logging.DEBUG) |
198 | + self._bus = dbus.SystemBus() |
199 | + self._bt_root = self._bus.get_object('org.bluez', '/') |
200 | + self._manager = dbus.Interface( |
201 | + self._bt_root, 'org.freedesktop.DBus.ObjectManager') |
202 | + self._main_loop = GObject.MainLoop() |
203 | + self._register_agent() |
204 | + |
205 | + def _register_agent(self): |
206 | + path = "/bt_helper/agent" |
207 | + BtAgent(self._bus, path) |
208 | + obj = self._bus.get_object('org.bluez', "/org/bluez") |
209 | + agent_manager = dbus.Interface(obj, "org.bluez.AgentManager1") |
210 | + agent_manager.RegisterAgent(path, 'NoInputNoOutput') |
211 | + logger.info("Agent registered") |
212 | + |
213 | + def _get_objects_by_iface(self, iface_name): |
214 | + for path, ifaces in self._manager.GetManagedObjects().items(): |
215 | + if ifaces.get(iface_name): |
216 | + yield self._bus.get_object('org.bluez', path) |
217 | + |
218 | + def get_bt_adapters(self): |
219 | + """Yield BtAdapter objects for each BT adapter found.""" |
220 | + for adapter in self._get_objects_by_iface(ADAPTER_IFACE): |
221 | + yield BtAdapter(dbus.Interface(adapter, ADAPTER_IFACE), self) |
222 | + |
223 | + def get_bt_devices(self, category=BT_ANY, filters={}): |
224 | + """Yields BtDevice objects currently known to the system. |
225 | + |
226 | + filters - specifies the characteristics of that a BT device must have |
227 | + to be yielded. The keys of filters dictionary represent names of |
228 | + parameters (as specified by the bluetooth DBus Api and represented by |
229 | + DBus proxy object), and its values must match proxy values. |
230 | + I.e. {'Paired': False}. For a full list of Parameters see: |
231 | + http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt |
232 | + |
233 | + Note that this function returns objects corresponding to BT devices |
234 | + that were seen last time scanning was done.""" |
235 | + for device in self._get_objects_by_iface(DEVICE_IFACE): |
236 | + obj = self.get_object_by_path(device.object_path)[DEVICE_IFACE] |
237 | + try: |
238 | + if category != BT_ANY: |
239 | + if obj['Class'] != category: |
240 | + continue |
241 | + rejected = False |
242 | + for filter in filters: |
243 | + if obj[filter] != filters[filter]: |
244 | + rejected = True |
245 | + break |
246 | + if rejected: |
247 | + continue |
248 | + yield BtDevice(dbus.Interface(device, DEVICE_IFACE), self) |
249 | + except KeyError as exc: |
250 | + logger.info('Property %s not found on device %s', |
251 | + exc, device.object_path) |
252 | + continue |
253 | + |
254 | + def get_prop_iface(self, obj): |
255 | + return dbus.Interface(self._bus.get_object( |
256 | + 'org.bluez', obj.object_path), 'org.freedesktop.DBus.Properties') |
257 | + |
258 | + def get_object_by_path(self, path): |
259 | + return self._manager.GetManagedObjects()[path] |
260 | + |
261 | + def get_proxy_by_path(self, path): |
262 | + return self._bus.get_object('org.bluez', path) |
263 | + |
264 | + def wait(self): |
265 | + self._main_loop.run() |
266 | + |
267 | + def quit_loop(self): |
268 | + self._main_loop.quit() |
269 | + |
270 | + def ensure_adapters_powered(self): |
271 | + for adapter in self.get_bt_adapters(): |
272 | + adapter.ensure_powered() |
273 | + |
274 | + def scan(self, timeout=10): |
275 | + """Scan for BT devices visible to all adapters.'""" |
276 | + self._bus.add_signal_receiver( |
277 | + interfaces_added, |
278 | + dbus_interface="org.freedesktop.DBus.ObjectManager", |
279 | + signal_name="InterfacesAdded") |
280 | + self._bus.add_signal_receiver( |
281 | + properties_changed, |
282 | + dbus_interface="org.freedesktop.DBus.Properties", |
283 | + signal_name="PropertiesChanged", |
284 | + arg0="org.bluez.Device1", |
285 | + path_keyword="path") |
286 | + for adapter in self._get_objects_by_iface(ADAPTER_IFACE): |
287 | + try: |
288 | + dbus.Interface(adapter, ADAPTER_IFACE).StopDiscovery() |
289 | + except dbus.exceptions.DBusException: |
290 | + pass |
291 | + dbus.Interface(adapter, ADAPTER_IFACE).StartDiscovery() |
292 | + GObject.timeout_add_seconds(timeout, self._scan_timeout) |
293 | + self._main_loop.run() |
294 | + |
295 | + def get_devices(self, timeout=10, rescan=True): |
296 | + """Scan for and list all devices visible to all adapters.""" |
297 | + if rescan: |
298 | + self.scan(timeout) |
299 | + return list(self.get_bt_devices()) |
300 | + |
301 | + def _scan_timeout(self): |
302 | + for adapter in self._get_objects_by_iface(ADAPTER_IFACE): |
303 | + dbus.Interface(adapter, ADAPTER_IFACE).StopDiscovery() |
304 | + self._main_loop.quit() |
305 | + |
306 | + |
307 | +class BtAdapter: |
308 | + def __init__(self, dbus_iface, bt_mgr): |
309 | + self._if = dbus_iface |
310 | + self._bt_mgr = bt_mgr |
311 | + self._prop_if = bt_mgr.get_prop_iface(dbus_iface) |
312 | + |
313 | + def set_bool_prop(self, prop_name, value): |
314 | + self._prop_if.Set(IFACE, prop_name, dbus.Boolean(value)) |
315 | + |
316 | + def ensure_powered(self): |
317 | + """Turn the adapter on, and do nothing if already on.""" |
318 | + powered = self._prop_if.Get(IFACE, 'Powered') |
319 | + logger.info('Powering on {}'.format( |
320 | + self._if.object_path.split('/')[-1])) |
321 | + if powered: |
322 | + logger.info('Device already powered') |
323 | + return |
324 | + try: |
325 | + self.set_bool_prop('Powered', True) |
326 | + logger.info('Powered on') |
327 | + except Exception as exc: |
328 | + logging.error('Failed to power on - {}'.format( |
329 | + exc.get_dbus_message())) |
330 | + |
331 | + |
332 | +class BtDevice: |
333 | + def __init__(self, dbus_iface, bt_mgr): |
334 | + self._if = dbus_iface |
335 | + self._obj = bt_mgr.get_object_by_path( |
336 | + self._if.object_path)[DEVICE_IFACE] |
337 | + self._bt_mgr = bt_mgr |
338 | + self._prop_if = bt_mgr.get_prop_iface(dbus_iface) |
339 | + self._pair_outcome = None |
340 | + |
341 | + def __str__(self): |
342 | + return "{} ({})".format(self.name, self.address) |
343 | + |
344 | + def __repr__(self): |
345 | + return "<BtDevice name:{}, address:{}>".format(self.name, self.address) |
346 | + |
347 | + def pair(self): |
348 | + """Pair the device. |
349 | + |
350 | + This function will try pairing with the device and block until device |
351 | + is paired, error occured or default timeout elapsed (whichever comes |
352 | + first). |
353 | + """ |
354 | + self._prop_if.Set(DEVICE_IFACE, 'Trusted', True) |
355 | + self._if.Pair( |
356 | + reply_handler=self._pair_ok, error_handler=self._pair_error) |
357 | + self._bt_mgr.wait() |
358 | + if self._pair_outcome: |
359 | + raise BtException(self._pair_outcome) |
360 | + try: |
361 | + self._if.Connect() |
362 | + except dbus.exceptions.DBusException as exc: |
363 | + logging.error('Failed to connect - {}'.format( |
364 | + exc.get_dbus_message())) |
365 | + |
366 | + def unpair(self): |
367 | + self._if.Disconnect() |
368 | + adapter = self._bt_mgr.get_proxy_by_path(self._obj['Adapter']) |
369 | + dbus.Interface(adapter, ADAPTER_IFACE).RemoveDevice(self._if) |
370 | + |
371 | + @property |
372 | + def name(self): |
373 | + return self._obj.get('Name', '<Unnamed>') |
374 | + |
375 | + @property |
376 | + def address(self): |
377 | + return self._obj['Address'] |
378 | + |
379 | + @property |
380 | + def rssi(self): |
381 | + return self._obj.get('RSSI', None) |
382 | + |
383 | + def _pair_ok(self): |
384 | + logger.info('%s successfully paired', self.name) |
385 | + self._pair_outcome = None |
386 | + self._bt_mgr.quit_loop() |
387 | + |
388 | + def _pair_error(self, error): |
389 | + logger.warning('Pairing of %s device failed. %s', self.name, error) |
390 | + self._pair_outcome = error |
391 | + self._bt_mgr.quit_loop() |
392 | + |
393 | + |
394 | +class Rejected(dbus.DBusException): |
395 | + _dbus_error_name = "org.bluez.Error.Rejected" |
396 | + |
397 | + |
398 | +class BtAgent(dbus.service.Object): |
399 | + """Agent authenticating everything that is possible.""" |
400 | + @dbus.service.method(AGENT_IFACE, in_signature="os", out_signature="") |
401 | + def AuthorizeService(self, device, uuid): |
402 | + logger.info("AuthorizeService (%s, %s)", device, uuid) |
403 | + |
404 | + @dbus.service.method(AGENT_IFACE, in_signature="o", out_signature="u") |
405 | + def RequestPasskey(self, device): |
406 | + logger.info("RequestPasskey (%s)", device) |
407 | + passkey = input("Enter passkey: ") |
408 | + return dbus.UInt32(passkey) |
409 | + |
410 | + @dbus.service.method(AGENT_IFACE, in_signature="o", out_signature="s") |
411 | + def RequestPinCode(self, device): |
412 | + logger.info("RequestPinCode (%s)", device) |
413 | + return input("Enter PIN Code: ") |
414 | + |
415 | + @dbus.service.method(AGENT_IFACE, in_signature="ouq", out_signature="") |
416 | + def DisplayPasskey(self, device, passkey, entered): |
417 | + print("DisplayPasskey (%s, %06u entered %u)" % |
418 | + (device, passkey, entered), flush=True) |
419 | + |
420 | + @dbus.service.method(AGENT_IFACE, in_signature="os", out_signature="") |
421 | + def DisplayPinCode(self, device, pincode): |
422 | + logger.info("DisplayPinCode (%s, %s)", device, pincode) |
423 | + print('Type following pin on your device: {}'.format(pincode), |
424 | + flush=True) |
425 | + |
426 | + @dbus.service.method(AGENT_IFACE, in_signature="ou", out_signature="") |
427 | + def RequestConfirmation(self, device, passkey): |
428 | + logger.info("RequestConfirmation (%s, %06d)", device, passkey) |
429 | + |
430 | + @dbus.service.method(AGENT_IFACE, in_signature="o", out_signature="") |
431 | + def RequestAuthorization(self, device): |
432 | + logger.info("RequestAuthorization (%s)", device) |
433 | + |
434 | + @dbus.service.method(AGENT_IFACE, in_signature="", out_signature="") |
435 | + def Cancel(self): |
436 | + logger.info("Cancelled") |
437 | + |
438 | + |
439 | +def properties_changed(interface, changed, invalidated, path): |
440 | + logger.info('Property changed for device @ %s. Change: %s', path, changed) |
441 | + |
442 | + |
443 | +def interfaces_added(path, interfaces): |
444 | + logger.info('Added new bt interfaces: %s @ %s', interfaces, path) |
445 | |
446 | === modified file 'providers/plainbox-provider-checkbox/jobs/bluetooth.txt.in' |
447 | --- providers/plainbox-provider-checkbox/jobs/bluetooth.txt.in 2016-01-12 10:28:35 +0000 |
448 | +++ providers/plainbox-provider-checkbox/jobs/bluetooth.txt.in 2016-04-21 10:03:47 +0000 |
449 | @@ -1,3 +1,8 @@ |
450 | +unit: manifest entry |
451 | +id: has_bt_smart |
452 | +_name: Bluetooth Smart (4.0 or later) Support |
453 | +value-type: bool |
454 | + |
455 | plugin: shell |
456 | category_id: 2013.com.canonical.plainbox::bluetooth |
457 | id: bluetooth/detect-output |
458 | @@ -157,3 +162,40 @@ |
459 | retrieves it again using Bluetooth and verifies the checksum to ensure the |
460 | transfer didn't corrupt the file. |
461 | |
462 | +plugin: user-interact-verify |
463 | +category_id: 2013.com.canonical.plainbox::bluetooth |
464 | +id: bluetooth4/HOGP-mouse |
465 | +depends: bluetooth/detect-output |
466 | +imports: from 2013.com.canonical.plainbox import manifest |
467 | +requires: |
468 | + manifest.has_bt_smart == 'True' |
469 | + package.name == 'bluez' and package.version >= '5.37' |
470 | +estimated_duration: 30.0 |
471 | +command: bt_connect-weak-logging --mouse |
472 | +_purpose: |
473 | + This test will check that you can use a HID Over GATT Profile (HOGP) with your Bluetooth Smart mouse. |
474 | +_steps: |
475 | + 1. Enable a Bluetooth smart mouse, and put it into paring mode. |
476 | + 2. Commence the test to do the auto-pairing, you will be asked to select targeting mouse from the list. |
477 | + 3. After it's paired and connected, perform actions such as moving the pointer, right and left button clicks and double clicks. |
478 | +_verification: |
479 | + Did the Bluetooth Smart mouse work as expected? |
480 | + |
481 | +plugin: user-interact-verify |
482 | +category_id: 2013.com.canonical.plainbox::bluetooth |
483 | +id: bluetooth4/HOGP-keyboard |
484 | +depends: bluetooth/detect-output |
485 | +imports: from 2013.com.canonical.plainbox import manifest |
486 | +requires: |
487 | + manifest.has_bt_smart == 'True' |
488 | + package.name == 'bluez' and package.version >= '5.37' |
489 | +estimated_duration: 30.0 |
490 | +command: bt_connect --keyboard |
491 | +_purpose: |
492 | + This test will check that you can use a HID Over GATT Profile (HOGP) with your Bluetooth Smart keyboard. |
493 | +_steps: |
494 | + 1. Enable a Bluetooth Smart keyboard, and put it into paring mode. |
495 | + 2. Commence the test to do the auto-pairing, you will be asked to select targeting keyboard from the list. |
496 | + 3. After it's paired and connected, enter some text with your keyboard and close the small input test tool. |
497 | +_verification: |
498 | + Did the Bluetooth Smart keyboard work as expected? |
I proposed a few improvements over tiny details inline.