Fedora Workstation 44 | „The final workaround“ für die Maus

Einleitung

In diesem Beitrag hatte ich die Microsoft Bluetooth Mouse 3600 unter Fedora 44 wieder zum Laufen gebracht.

Irgendwie halt … und zunächst funktionierte sie auch ganz gut, bis ein System-Update von Fedora 44 notwendig war und dazu GNOME die Version 50.1 erreichte. Danach Schluss mit lustig und ich musste einige Zeit mit einer Cherry Wirless Maus arbeiten, was mir schon wegen der wenigen Ports am Surface Laptop nicht wirklich gefallen hat.

Nach einem Neustart war sie zwar „connected“… aber komplett tot. In den Settings / Bluetooth wurde die Maus meistens mit einer “Connection” angezeigt, wollte sich aber trotzdem nicht zum Arbeiten überreden lassen.

Der entscheidende Test

sudo systemctl restart bluetooth

→ nach wenigen Sekunden funktioniert die Maus wieder. Die Ursache scheint zu sein, dass der Bluetooth Controller beim Start in einen inkonsistenten Zustand kommt. Die Verbindung steht, aber HID-Stack ist nicht aktiv.


Die mögliche Lösung

Der Test unten (restart des Bluetooth-Stacks) brachte die Maus zurück.

bluetoothctl power off
sleep 2
bluetoothctl power on

Damit war die Grundlage für das Script gefunden und der ehemalige Ansatz konnte entsprechend umgebaut werden.


Finales Script – bt-mouse-heal-prod.sh

Damit die Sache nicht zu kompliziert wird, hier die notwendigen Scripte als ZIP-Datei.

Das eigentliche Health-Script, bt-mouse-heal-prod.sh:


#!/usr/bin/env bash
set -Eeuo pipefail

DEVICE_NAME="${DEVICE_NAME:-BluetoothMouse3600}"
DEVICE_PREFIX="${DEVICE_PREFIX:-F1:00:85:}"
MAX_RETRIES="${MAX_RETRIES:-6}"
BTCTL_TIMEOUT="${BTCTL_TIMEOUT:-10}"
RESET_ONCE="${RESET_ONCE:-true}"
DEBUG="${DEBUG:-false}"

RUNDIR="${XDG_RUNTIME_DIR:-/tmp}"
LOCK_FILE="${RUNDIR}/bt-mouse-heal.lock"
RESET_STAMP="${RUNDIR}/bt-mouse-reset.stamp"
TAG="bt-mouse-heal"

log() {
  [[ "$DEBUG" == "true" ]] || return 0
  printf '[%s][DEBUG] %s\n' "$TAG" "$*" >&2
}

btctl() {
  timeout "$BTCTL_TIMEOUT" bluetoothctl "$@" 2>&1 || true
}

is_connected() {
  btctl info "$1" | grep -qi 'Connected: yes'
}

find_mac_by_name() {
  btctl devices Paired | awk -v name="$DEVICE_NAME" -v pfx="$DEVICE_PREFIX" '
    /^Device[[:space:]]+[0-9A-Fa-f:]{17}[[:space:]]+/ {
      mac=$2
      dev=$0
      sub(/^Device[[:space:]]+[0-9A-Fa-f:]{17}[[:space:]]+/, "", dev)
      if (dev==name && toupper(substr(mac,1,length(pfx)))==toupper(pfx)) {
        print mac; exit
      }
    }'
}

ensure_agent() {
  btctl power on >/dev/null
  btctl agent on >/dev/null
  btctl default-agent >/dev/null
}

controller_soft_reset() {
  log "Reset bluetooth controller"
  btctl power off >/dev/null
  sleep 2
  btctl power on >/dev/null
  sleep 3
}

connect_with_retries() {
  local mac="$1"
  ensure_agent
  btctl trust "$mac" >/dev/null

  for i in $(seq 1 "$MAX_RETRIES"); do
    is_connected "$mac" && return 0
    btctl connect "$mac" >/dev/null
    sleep "$i"
    is_connected "$mac" && return 0
  done
  return 1
}

main() {
  exec 9>"$LOCK_FILE"
  flock -n 9 || exit 0

  if [[ "$RESET_ONCE" == "true" ]]; then
    [[ -f "$RESET_STAMP" ]] || {
      controller_soft_reset
      touch "$RESET_STAMP"
    }
  else
    controller_soft_reset
  fi

  mac="$(find_mac_by_name || true)"
  [[ -z "$mac" ]] && exit 2

  connect_with_retries "$mac" && exit 0

  controller_soft_reset
  mac="$(find_mac_by_name || true)"
  connect_with_retries "$mac"
}

main "$@"


systemd Integration

systemd-Service


[Unit]
Description=Heal Microsoft Bluetooth Mouse 3600 connection (RPA-safe)
After=graphical-session.target
Wants=graphical-session.target

[Service]
Type=oneshot
ExecStart=%h/.local/bin/bt-mouse-heal.sh
Environment=DEVICE_PREFIX=F1:00:85:
Environment=DEVICE_NAME_REGEX=BluetoothMouse3600

systemd-Timer


[Unit]
Description=Heal Microsoft Bluetooth Mouse 3600 connection (RPA-safe)
After=graphical-session.target
Wants=graphical-session.target

[Service]
Type=oneshot
ExecStart=%h/.local/bin/bt-mouse-heal.sh
Environment=DEVICE_PREFIX=F1:00:85:
Environment=DEVICE_NAME_REGEX=BluetoothMouse3600

Wichtig:

Die ~/.config/systemd/user/bt-mouse-heal.service und die ~/.config/systemd/user/bt-mouse-heal.timer Dateien erstellt das folgende Installations-Skript automatisch und setzt auch die Rechte passend.


Fazit

Die Sache ist kompliziert, hat aber mit dieser Lösung ein erträgliches Ende gefunden. Falls nicht, muss ich nochmals ran … aber die Hoffnung stirbt bekanntlich zuletzt und wenn man nur zwei USB-Ports am Laptop hat, lohnt sich der Fight allemal.

Enjoy it, b!