
Reverse-engineeriertes Logi Options+ Agenten IPC-Protokoll. Schalten Sie Logitech Multi-Host-Geräte programmgesteuert über Unix-Socket (macOS) oder Named Pipe (Windows) um.
Dokumentation des per Reverse Engineering ermittelten IPC-Protokolls des Logi Options+ Agenten. Ermöglicht die programmatische Steuerung von Logitech-Multi-Host-Geräten (Host-Wechsel, Geräteabfragen) ohne direkten HID-Zugriff, sowohl unter macOS als auch Windows.
Das Protokoll wurde vor diesem Projekt nicht öffentlich dokumentiert.
macOS blockiert den direkten HID-Zugriff auf Bluetooth-Eingabegeräte auf Kernel-Ebene. Keine Berechtigungen, Berechtigungsnachweise oder Hacks umgehen dies. Der Logi Options+ Agent verfügt über von Apple signierte Berechtigungsnachweise (com.apple.security.device.bluetooth), die ihm Bluetooth-HID-Zugriff gewähren. Dieses Projekt kommuniziert stattdessen über den IPC-Kanal mit dem Agenten.
| Datei | Beschreibung |
|---|---|
logi-options-ipc-reverse-engineering.md | Ausführliche Reverse-Engineering-Chronik |
software-kvm-setup.md | Anleitung zur Einrichtung einer bidirektionalen Software-KVM (Windows + Mac) |
switch_to_windows.py | Mac-seitiges Skript, das Logitech-Geräte und Monitor-Eingang über Unix-Socket-IPC umschaltet |
api-reference.md | Agent-API-Referenz: funktionierende Endpunkte, Protobuf-Typen, Gerätefunktionen |
kvm.ahk | AutoHotkey v2-Skript: Win+1/2/3-Hotkeys, die kvm_daemon_windows.py --switch aufrufen (läuft in der Taskleiste) |
kvm_daemon_windows.py | Windows-Geräteumschaltung per Named Pipe, Monitorumschaltung per DDC/CI |
kvm_config.ini | Windows-Konfiguration (Hotkeys, Monitor-Eingänge) |
query_feature_index.py | Ermittelt den HID++ ChangeHost-Feature-Index für Logitech-Geräte (Windows) |
query_agent_windows.py | Fragt den Agenten unter Windows per Named Pipe ab |
config.ini | Legacy-UnifiedSwitch-Konfiguration (durch kvm_daemon_windows.py ersetzt) |
python3 switch_to_windows.py 0 # Zu Host 0 wechseln (DisplayPort)
python3 switch_to_windows.py 1 # Zu Host 1 wechseln (HDMI)
python3 switch_to_windows.py --dry-run 0 # Anzeigen, was passieren würde
Erfordert, dass Logi Options+ läuft und m1ddc installiert ist (brew install m1ddc).
# Den AHK-Hotkey-Listener starten (läuft in der Taskleiste, keine Konsole)
# Erfordert AutoHotkey v2: winget install AutoHotkey.AutoHotkey
start kvm.ahk
# Oder direkt von der Kommandozeile umschalten
python kvm_daemon_windows.py --switch 1
# Gefundene Geräte und konfigurierte Hotkeys anzeigen, ohne umzuschalten
python kvm_daemon_windows.py --dry-run
Erfordert, dass Logi Options+ läuft. Abhängigkeiten installieren: pip install pywin32.
Das AHK-Skript lauscht auf Win+1/2/3 und ruft für jede Taste kvm_daemon_windows.py --switch N auf. Das Python-Skript ermittelt Geräte automatisch vom Agenten (keine fest codierten Geräte-IDs oder HID-Pfade). Bearbeiten Sie kvm_config.ini, um Hotkeys und DDC/CI-Eingabewerte für den Monitor zu konfigurieren.
Der Agent lauscht auf:
/tmp/logitech_kiros_agent-<hash>\\.\pipe\logitech_kiros_agent-<hash>Gleiches Drahtprotokoll auf beiden Plattformen. Binäres Frame-Format:
LE32(total_len) + BE32(proto_name_len) + "json" + BE32(msg_len) + JSON_message
Ein Gerät auf einen anderen Host umschalten:
{
"msg_id": "1",
"verb": "SET",
"path": "/change_host/<device_id>/host",
"payload": {
"@type": "type.googleapis.com/logi.protocol.devices.ChangeHost",
"host": 0
}
}
Das Payload ist ein google.protobuf.Any-Feld, das als Inline-JSON mit einer @type-Annotation serialisiert wird. Der Agent verwendet einen strengen Protobuf-JSON-Parser; unbekannte Felder führen zu INVALID_MESSAGE_RECEIVED.
Anfragen verwenden msg_id (snake_case). Antworten verwenden msgId (camelCase). Verben sind Zeichenketten: "GET", "SET", "SUBSCRIBE", "BROADCAST".
Siehe logi-options-ipc-reverse-engineering.md für die vollständige Protokolldokumentation.
Bei langlebiger Automatisierung nach BrokenPipeError erneut verbinden und den Socket/Pipe-Pfad neu ermitteln.
Diese gelten beim direkten Senden von HID++-Befehlen, nicht über den Agenten.
kvm_daemon_windows.pyvermeidet sie alle, indem es die Named Pipe des Agenten verwendet.
HID++-Collection variiert pro Gerät. Der MX Master 3S stellt HID++ auf COL02 bereit. Die MX Keys S verwendet COL05. Beide verwenden die Usage Page FF43:0202. Überprüfen mit:
Get-PnpDeviceProperty -InstanceId "<instance_id>" -KeyName DEVPKEY_Device_HardwareIds
# Look for UP:FF43_U:0202
Feature-Indizes unterscheiden sich pro Gerät. ChangeHost (0x1814) befindet sich auf der MX Keys S bei Index 0x0A, aber auf der MX Mechanical bei 0x09. Zur Laufzeit über IRoot::GetFeature abfragen:
Send: {0x11, 0x00, 0x00, 0x0D, 0x18, 0x14, ...} (20 bytes)
Read: response byte 4 = feature index
Geräte-Neukopplung ändert HID-Pfade. Das Umschalten vom Bolt-Empfänger auf direktes BT LE ändert den Pfad vollständig. Führen Sie query_agent_windows.py aus, um die aktuellen Pfade vom Agenten zu erhalten.
BT LE GATT-Vendor-Collection wird "Unbekannt". Windows initialisiert gelegentlich den HID++ GATT-Dienst nicht. Das Gerät funktioniert normal, aber der Vendor-Befehlskanal ist tot. Behebung: Bluetooth in den Windows-Einstellungen aus- und wieder einschalten. Dies ist ein Windows-/Firmware-Problem.
| Version | Status |
|---|---|
| Logi Options+ 2.0.840907 | Funktioniert (macOS Tahoe, Windows 11) |
Das Drahtprotokoll und die Kern-API-Pfade (/devices/list, /change_host/<id>/host) waren stabil. Das Neukoppeln von Geräten hat HID-Pfade und Collection-Nummern beeinträchtigt, aber das IPC-Protokoll selbst war nicht betroffen.
Dieses Projekt dient ausschließlich Ausbildungs- und Forschungszwecken. Es dokumentiert ein undokumentiertes, nicht unterstütztes Protokoll, das Logitech jederzeit ändern oder entfernen kann. Die Autoren übernehmen keine Verantwortung für Schäden, Datenverlust, unbrauchbare Geräte oder beeinträchtigte Funktionalität, die aus der Nutzung dieses Codes oder der Dokumentation resultieren. Nutzung auf eigene Gefahr.
Dieses Projekt steht in keiner Verbindung zu Logitech und wird nicht von Logitech unterstützt.
| Szenario | Was passiert | Erkennung |
|---|
| Agent läuft nicht | Socket/Pipe existiert nicht | connect() wirft FileNotFoundError oder ConnectionRefusedError |
| Agent startet während der Sitzung neu | Verbindung wird unterbrochen | send() wirft BrokenPipeError; recv() gibt Leeres zurück |
| Gerät auf einem anderen Host | NO_SUCH_PATH | result.code prüfen |
| Gerät nicht erreichbar | TIMEOUT nach ca. 3s | result.code prüfen |
| Fehlerhaftes Payload | INVALID_MESSAGE_RECEIVED | Fehlendes @type oder unbekannte Felder |
| Socket-Hash ändert sich | Alter Pfad verschwunden | Immer dynamisch ermitteln, niemals fest codieren |
| Veralteter Socket nach Neustart | ConnectionRefusedError | Nach kurzer Verzögerung erneut versuchen |
| Gleichzeitige Clients | Funktioniert einwandfrei | Agent behandelt mehrere Verbindungen |