1#!/usr/bin/env python3

2#

3# This file is part of Checkbox.

4#

5# Copyright 2016 Canonical Ltd.

6#

7# Authors:

8# Po-Hsu Lin <po-hsu.lin@canonical.com>

9# Yung Shen <yung.shen@canonical.com>

10#

11# Checkbox is free software: you can redistribute it and/or modify

12# it under the terms of the GNU General Public License version 3,

13# as published by the Free Software Foundation.

14#

15# Checkbox is distributed in the hope that it will be useful,

16# but WITHOUT ANY WARRANTY; without even the implied warranty of

17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

18# GNU General Public License for more details.

19#

20# You should have received a copy of the GNU General Public License

21# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.

22

23 import sys

24 import time

25

26 import checkbox_support.bt_helper as bt_helper

27

28 from argparse import ArgumentParser

29

30

31 def unpair_all(devices, manager):

32 """Unpairing paired devices and scanning again for rerun jobs."""

33 for dev in devices:

34 try:

35 print("INFO: Unpairing", dev)

36 dev.unpair()

37 except bt_helper.BtException as exc:

38 print("Warning: Unpairing failed", exc)

39 else:

40 # print(flush=True) to bypass plainbox output buffer,

41 # see LP: #1569808 for more details.

42 print(

43 "Please reset the device to pairing mode in 13 seconds", flush=True

44 )

45 time.sleep(13)

46 print("INFO: Re-scaning for devices in pairing mode", flush=True)

47 manager.scan()

48

49

50 def main():

51 """Add argument parser here and do most of the job."""

52 parser = ArgumentParser(

53 description=(

54 "Bluetooth auto paring and connect. " "Please select one option."

55 )

56 )

57 group = parser.add_mutually_exclusive_group(required=True)

58 group.add_argument(

59 "--mac", type=str, help="Pair with a given MAC, not using scan result,"

60 )

61 group.add_argument(

62 "--mouse",

63 action="store_const",

64 const="input-mouse",

65 dest="target",

66 help="List and pair with mouse devices",

67 )

68 group.add_argument(

69 "--keyboard",

70 action="store_const",

71 const="input-keyboard",

72 dest="target",

73 help="List and pair with keyboard devices",

74 )

75 args = parser.parse_args()

76

77 manager = bt_helper.BtManager()

78 # Power on bluetooth adapter and scanning devices in advance.

79 manager.ensure_adapters_powered()

80 manager.scan()

81

82 if args.mac:

83 # TODO check MAC format

84 print("INFO: Trying to pair with {}".format(args.mac))

85 device = list(manager.get_bt_devices(filters={"Address": args.mac}))

86 paired_device = list(

87 manager.get_bt_devices(

88 filters={"Address": args.mac, "Paired": True}

89 )

90 )

91 if not device:

92 print("ERROR: No pairable device found, terminating")

93 return 1

94

95 unpair_all(paired_device, manager)

96

97 for dev in device:

98 try:

99 dev.pair()

100 except bt_helper.BtException as exc:

101 print("ERROR: Unable to pair: ", exc)

102 return 1

103 else:

104 print("INFO: Device paired")

105 return 0

106 else:

107 print("INFO: Listing targeting devices")

108 # Listing device based on RSSI

109 paired_targets = list(

110 manager.get_bt_devices(

111 category=bt_helper.BT_ANY,

112 filters={"Paired": True, "Icon": args.target},

113 )

114 )

115 if not paired_targets:

116 print("INFO: No paired targeting devices found")

117 manager.scan()

118 else:

119 unpair_all(paired_targets, manager)

120

121 target_devices = sorted(

122 manager.get_bt_devices(

123 category=bt_helper.BT_ANY,

124 filters={"Paired": False, "Icon": args.target},

125 ),

126 key=lambda x: int(x.rssi or -255),

127 reverse=True,

128 )

129 if not target_devices:

130 print("ERROR: No target devices found, terminating")

131 return 1

132 print("INFO: Detected devices (sorted by RSSI; highest first).")

133 # let's assing numbers to devices

134 devices = dict(enumerate(target_devices, 1))

135 for num, dev in devices.items():

136 print("{}. {} (RSSI: {})".format(num, dev, dev.rssi))

137 chosen = False

138 while not chosen:

139 print("Which one would you like to connect to? (0 to exit)")

140 num = input()

141 # TODO: enter as default to 1st device

142 if num == "0":

143 return 1

144 chosen = num.isnumeric() and int(num) in devices.keys()

145 print("INFO: {} chosen.".format(devices[int(num)]))

146 print("INFO: Pairing selected device..")

147 try:

148 devices[int(num)].pair()

149 except bt_helper.BtException as exc:

150 print("ERROR: something wrong: ", exc)

151 return 1

152 else:

153 print("Paired successfully.")

154 return 0

155 # capture all other silence failures

156 return 1

157

158

159 if __name__ == "__main__":

160 sys.exit(main())