""" modules/bt_devices.py — Phase 4: Bluetooth disconnect by device name (best-effort). Windows Bluetooth pairing/unpairing is not stable without additional tooling. We provide a real "disconnect" approximation by disabling the matching Bluetooth PnP device. This often requires admin. """ from __future__ import annotations import subprocess from dataclasses import dataclass @dataclass(frozen=True, slots=True) class BTResult: ok: bool message: str def _ps(cmd: str) -> subprocess.CompletedProcess[str]: return subprocess.run( ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", cmd], capture_output=True, text=True, shell=False, ) def disconnect(name_substring: str) -> BTResult: s = (name_substring or "").strip() if not s: return BTResult(False, "Missing device name.") cmd = ( "try { " f"$d = Get-PnpDevice -Class Bluetooth -ErrorAction SilentlyContinue | Where-Object {{$_.FriendlyName -like '*{s}*'}} | Select-Object -First 1; " "if($null -eq $d){ 'NOTFOUND' } else { Disable-PnpDevice -InstanceId $d.InstanceId -Confirm:$false -ErrorAction Stop; 'OK' }" "} catch { 'FAIL:' + $_.Exception.Message }" ) r = _ps(cmd) out = (r.stdout or "").strip() if out == "OK": return BTResult(True, "Bluetooth device disconnected (disabled).") if out == "NOTFOUND": return BTResult(False, "Bluetooth device not found.") if "access is denied" in out.lower(): return BTResult(False, "Bluetooth disconnect needs admin permission.") return BTResult(False, f"Bluetooth disconnect failed: {out[:160]}")