Skip to content

Conversation

SpotlightForBugs
Copy link

@SpotlightForBugs SpotlightForBugs commented Feb 15, 2025

This pull request to pwnagotchi includes significant changes to improve the CLI wizard and add Bluetooth functionality. The most important changes include adding DBus imports, refactoring the wizard setup, and adding new helper functions for user input validation and Bluetooth setup.

Enhancements to CLI wizard:

  • pwnagotchi/cli.py: Added DBus imports to support Bluetooth functionality.
  • pwnagotchi/cli.py: Refactored the wizard setup to include new helper functions for input validation (ask_yes_no, ask_non_empty, ask_positive_int) and Bluetooth setup (setup_bluetooth_dbus).
  • pwnagotchi/cli.py: Added a new function run_wizard to streamline the interactive configuration process, including Bluetooth pairing and display settings.

Minor code improvements:

Motivation and Context

The wizard breaks every time something wrong was entered and the wizard is only semi-automatic; the bluetooth pairing and trusting is manual.

How Has This Been Tested?

This has been tested from start to finish on Raspberrypi 0wh.

The bluetooth pairing was also tested on other Raspberry Pi devices (4B 8GB) using the following python script:

Important

This script needs to be ran as root (sudo python3.11 test_auto_bt_dbus.py)
 

python script
#!/usr/bin/env python3

import dbus
import dbus.mainloop.glib
import time

def main():
    # Initialize the main loop so DBus calls work
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    # Get the system bus
    system_bus = dbus.SystemBus()

    # Obtain the BlueZ ObjectManager
    manager = dbus.Interface(
        system_bus.get_object("org.bluez", "/"),
        "org.freedesktop.DBus.ObjectManager"
    )

    # Find the first Bluetooth adapter (usually /org/bluez/hci0)
    objects = manager.GetManagedObjects()
    adapter_path = None
    for path, interfaces in objects.items():
        if "org.bluez.Adapter1" in interfaces:
            adapter_path = path
            break

    if not adapter_path:
        print("No Bluetooth adapter found. Exiting.")
        return

    # Get interfaces for the adapter
    adapter_props = dbus.Interface(
        system_bus.get_object("org.bluez", adapter_path),
        "org.freedesktop.DBus.Properties"
    )
    adapter_iface = dbus.Interface(
        system_bus.get_object("org.bluez", adapter_path),
        "org.bluez.Adapter1"
    )

    # Power on the adapter and make it discoverable/pairable
    print("Turning on Bluetooth adapter, making discoverable/pairable...")
    adapter_props.Set("org.bluez.Adapter1", "Powered", dbus.Boolean(True))
    adapter_props.Set("org.bluez.Adapter1", "Discoverable", dbus.Boolean(True))
    adapter_props.Set("org.bluez.Adapter1", "Pairable", dbus.Boolean(True))

    # Start Discovery
    print("Starting discovery for ~10 seconds. Make sure your phone is in discoverable mode...")
    adapter_iface.StartDiscovery()
    time.sleep(10)
    adapter_iface.StopDiscovery()

    # Re-check discovered objects
    objects = manager.GetManagedObjects()
    device_list = []  # to store (MAC, Name, DBus path)

    for path, interfaces in objects.items():
        if "org.bluez.Device1" in interfaces:
            dev_props = interfaces["org.bluez.Device1"]
            mac_addr = dev_props.get("Address")
            name = dev_props.get("Name", "Unknown")
            device_list.append((mac_addr, name, path))

    if not device_list:
        print("No devices discovered. Make sure your phone is discoverable and retry.")
        return

    # Show discovered devices
    print("\nDiscovered devices:")
    for idx, (mac_addr, name, _) in enumerate(device_list):
        print(f"  [{idx}]  {mac_addr}  '{name}'")

    # Let user pick a device by index or MAC
    choice = input("\nEnter the index of your phone (or a MAC address): ").strip()

    if choice.isdigit():
        choice_idx = int(choice)
        if choice_idx < 0 or choice_idx >= len(device_list):
            print("Invalid index. Exiting.")
            return
        chosen_mac = device_list[choice_idx][0]
        device_path = device_list[choice_idx][2]
    else:
        # Assume user typed a MAC
        found_dev = [(m, p) for (m, _, p) in device_list if m.lower() == choice.lower()]
        if not found_dev:
            print("No matching discovered device for that MAC. Exiting.")
            return
        chosen_mac, device_path = found_dev[0]

    print(f"\nAttempting to pair with {chosen_mac}...")
    dev_obj = system_bus.get_object("org.bluez", device_path)
    dev_iface = dbus.Interface(dev_obj, "org.bluez.Device1")

    try:
        dev_iface.Pair()
        print("Pairing initiated. If your phone prompts, confirm the pairing code.")
    except dbus.DBusException as e:
        print(f"Pairing failed: {e}")
        print("You might need to confirm on the phone or try again.")
        # continue anyway to see if partial pairing is recognized

    # Trust the device
    dev_props_iface = dbus.Interface(dev_obj, "org.freedesktop.DBus.Properties")
    try:
        dev_props_iface.Set("org.bluez.Device1", "Trusted", dbus.Boolean(True))
        print("Successfully set device as Trusted!")
    except Exception as e:
        print(f"Trust step encountered an error: {e}")

    print("\nDone. You can exit now or verify from your phone's Bluetooth settings.\n")


if __name__ == '__main__':
    main()

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation. (The wiki would require an update)
  • I have updated the documentation accordingly.
  • I've read the CONTRIBUTION guide
  • I have signed-off my commits with git commit -s

Signed-off-by: Johannes Häusler <mail@spotlightforbugs.eu>
Signed-off-by: Johannes Häusler <mail@spotlightforbugs.eu>
Signed-off-by: Johannes Häusler <mail@spotlightforbugs.eu>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant