1""" 2Support for Bluetooth (using BlueZ in Linux). 3 4The following packages are required packages for this module: 5 6 bluez >= 5.7 7 bluez-libs >= 5.7 8 bluez-utils >= 5.7 9 pybluez >= 0.18 10""" 11import shlex 12 13import salt.utils.validate.net 14from salt.exceptions import CommandExecutionError 15 16HAS_PYBLUEZ = False 17try: 18 import bluetooth # pylint: disable=import-error 19 20 HAS_PYBLUEZ = True 21except ImportError: 22 pass 23 24__func_alias__ = {"address_": "address"} 25 26# Define the module's virtual name 27__virtualname__ = "bluetooth" 28 29 30def __virtual__(): 31 """ 32 Only load the module if bluetooth is installed 33 """ 34 if HAS_PYBLUEZ: 35 return __virtualname__ 36 return ( 37 False, 38 "The bluetooth execution module cannot be loaded: bluetooth not installed.", 39 ) 40 41 42def version(): 43 """ 44 Return Bluez version from bluetoothd -v 45 46 CLI Example: 47 48 .. code-block:: bash 49 50 salt '*' bluetoothd.version 51 """ 52 cmd = "bluetoothctl -v" 53 out = __salt__["cmd.run"](cmd).splitlines() 54 bluez_version = out[0] 55 pybluez_version = "<= 0.18 (Unknown, but installed)" 56 try: 57 pybluez_version = bluetooth.__version__ 58 except Exception as exc: # pylint: disable=broad-except 59 pass 60 return {"Bluez": bluez_version, "PyBluez": pybluez_version} 61 62 63def address_(): 64 """ 65 Get the many addresses of the Bluetooth adapter 66 67 CLI Example: 68 69 .. code-block:: bash 70 71 salt '*' bluetooth.address 72 """ 73 ret = {} 74 cmd = "hciconfig" 75 out = __salt__["cmd.run"](cmd).splitlines() 76 dev = "" 77 for line in out: 78 if line.startswith("hci"): 79 comps = line.split(":") 80 dev = comps[0] 81 ret[dev] = { 82 "device": dev, 83 "path": "/sys/class/bluetooth/{}".format(dev), 84 } 85 if "BD Address" in line: 86 comps = line.split() 87 ret[dev]["address"] = comps[2] 88 if "DOWN" in line: 89 ret[dev]["power"] = "off" 90 if "UP RUNNING" in line: 91 ret[dev]["power"] = "on" 92 return ret 93 94 95def power(dev, mode): 96 """ 97 Power a bluetooth device on or off 98 99 CLI Examples: 100 101 .. code-block:: bash 102 103 salt '*' bluetooth.power hci0 on 104 salt '*' bluetooth.power hci0 off 105 """ 106 if dev not in address_(): 107 raise CommandExecutionError("Invalid dev passed to bluetooth.power") 108 109 if mode == "on" or mode is True: 110 state = "up" 111 mode = "on" 112 else: 113 state = "down" 114 mode = "off" 115 cmd = "hciconfig {} {}".format(dev, state) 116 __salt__["cmd.run"](cmd).splitlines() 117 info = address_() 118 if info[dev]["power"] == mode: 119 return True 120 return False 121 122 123def discoverable(dev): 124 """ 125 Enable this bluetooth device to be discoverable. 126 127 CLI Example: 128 129 .. code-block:: bash 130 131 salt '*' bluetooth.discoverable hci0 132 """ 133 if dev not in address_(): 134 raise CommandExecutionError("Invalid dev passed to bluetooth.discoverable") 135 136 cmd = "hciconfig {} iscan".format(dev) 137 __salt__["cmd.run"](cmd).splitlines() 138 cmd = "hciconfig {}".format(dev) 139 out = __salt__["cmd.run"](cmd) 140 if "UP RUNNING ISCAN" in out: 141 return True 142 return False 143 144 145def noscan(dev): 146 """ 147 Turn off scanning modes on this device. 148 149 CLI Example: 150 151 .. code-block:: bash 152 153 salt '*' bluetooth.noscan hci0 154 """ 155 if dev not in address_(): 156 raise CommandExecutionError("Invalid dev passed to bluetooth.noscan") 157 158 cmd = "hciconfig {} noscan".format(dev) 159 __salt__["cmd.run"](cmd).splitlines() 160 cmd = "hciconfig {}".format(dev) 161 out = __salt__["cmd.run"](cmd) 162 if "SCAN" in out: 163 return False 164 return True 165 166 167def scan(): 168 """ 169 Scan for bluetooth devices in the area 170 171 CLI Example: 172 173 .. code-block:: bash 174 175 salt '*' bluetooth.scan 176 """ 177 ret = [] 178 devices = bluetooth.discover_devices(lookup_names=True) 179 for device in devices: 180 ret.append({device[0]: device[1]}) 181 return ret 182 183 184def block(bdaddr): 185 """ 186 Block a specific bluetooth device by BD Address 187 188 CLI Example: 189 190 .. code-block:: bash 191 192 salt '*' bluetooth.block DE:AD:BE:EF:CA:FE 193 """ 194 if not salt.utils.validate.net.mac(bdaddr): 195 raise CommandExecutionError("Invalid BD address passed to bluetooth.block") 196 197 cmd = "hciconfig {} block".format(bdaddr) 198 __salt__["cmd.run"](cmd).splitlines() 199 200 201def unblock(bdaddr): 202 """ 203 Unblock a specific bluetooth device by BD Address 204 205 CLI Example: 206 207 .. code-block:: bash 208 209 salt '*' bluetooth.unblock DE:AD:BE:EF:CA:FE 210 """ 211 if not salt.utils.validate.net.mac(bdaddr): 212 raise CommandExecutionError("Invalid BD address passed to bluetooth.unblock") 213 214 cmd = "hciconfig {} unblock".format(bdaddr) 215 __salt__["cmd.run"](cmd).splitlines() 216 217 218def pair(address, key): 219 """ 220 Pair the bluetooth adapter with a device 221 222 CLI Example: 223 224 .. code-block:: bash 225 226 salt '*' bluetooth.pair DE:AD:BE:EF:CA:FE 1234 227 228 Where DE:AD:BE:EF:CA:FE is the address of the device to pair with, and 1234 229 is the passphrase. 230 231 TODO: This function is currently broken, as the bluez-simple-agent program 232 no longer ships with BlueZ >= 5.0. It needs to be refactored. 233 """ 234 if not salt.utils.validate.net.mac(address): 235 raise CommandExecutionError("Invalid BD address passed to bluetooth.pair") 236 237 try: 238 int(key) 239 except Exception: # pylint: disable=broad-except 240 raise CommandExecutionError( 241 "bluetooth.pair requires a numerical key to be used" 242 ) 243 244 addy = address_() 245 cmd = "echo {} | bluez-simple-agent {} {}".format( 246 shlex.quote(addy["device"]), shlex.quote(address), shlex.quote(key) 247 ) 248 out = __salt__["cmd.run"](cmd, python_shell=True).splitlines() 249 return out 250 251 252def unpair(address): 253 """ 254 Unpair the bluetooth adapter from a device 255 256 CLI Example: 257 258 .. code-block:: bash 259 260 salt '*' bluetooth.unpair DE:AD:BE:EF:CA:FE 261 262 Where DE:AD:BE:EF:CA:FE is the address of the device to unpair. 263 264 TODO: This function is currently broken, as the bluez-simple-agent program 265 no longer ships with BlueZ >= 5.0. It needs to be refactored. 266 """ 267 if not salt.utils.validate.net.mac(address): 268 raise CommandExecutionError("Invalid BD address passed to bluetooth.unpair") 269 270 cmd = "bluez-test-device remove {}".format(address) 271 out = __salt__["cmd.run"](cmd).splitlines() 272 return out 273 274 275def start(): 276 """ 277 Start the bluetooth service. 278 279 CLI Example: 280 281 .. code-block:: bash 282 283 salt '*' bluetooth.start 284 """ 285 out = __salt__["service.start"]("bluetooth") 286 return out 287 288 289def stop(): 290 """ 291 Stop the bluetooth service. 292 293 CLI Example: 294 295 .. code-block:: bash 296 297 salt '*' bluetooth.stop 298 """ 299 out = __salt__["service.stop"]("bluetooth") 300 return out 301