
Обратно спроектированный протокол IPC агента Logi Options+. Переключайте многохостные устройства Logitech программно через Unix-сокет (macOS) или именованный канал (Windows).
Документация, полученная в результате реверс-инжиниринга IPC-протокола агента Logi Options+. Обеспечивает программное управление мульти-хостовыми устройствами Logitech (переключение между хостами, запросы информации об устройстве) без прямого доступа к HID, как на macOS, так и на Windows.
Данный протокол ранее не был публично задокументирован.
macOS блокирует прямой HID-доступ к Bluetooth-устройствам ввода на уровне ядра. Никакие разрешения, антлиты или хаки это не обходят. Агент Logi Options+ имеет подписанные Apple антлиты (com.apple.security.device.bluetooth), предоставляющие ему Bluetooth HID-доступ. Этот проект взаимодействует с агентом через его IPC-канал.
| Файл | Описание |
|---|---|
logi-options-ipc-reverse-engineering.md | Полная хроника реверс-инжиниринга |
software-kvm-setup.md | Руководство по настройке двустороннего программного KVM (Windows + Mac) |
switch_to_windows.py | Скрипт для Mac, который переключает устройства Logitech и вход монитора через Unix-сокет IPC |
api-reference.md | Справочник API агента: работающие эндпоинты, типы protobuf, возможности устройств |
kvm.ahk | AutoHotkey v2 скрипт: горячие клавиши Win+1/2/3, вызывающие kvm_daemon_windows.py --switch (работает в системном трее) |
kvm_daemon_windows.py | Переключение устройств Windows через именованный канал, переключение мониторов через DDC/CI |
kvm_config.ini | Конфигурация Windows (горячие клавиши, входы монитора) |
query_feature_index.py | Определяет индекс функции HID++ ChangeHost для устройств Logitech (Windows) |
query_agent_windows.py | Запросы к агенту на Windows через именованный канал |
config.ini | Устаревшая конфигурация UnifiedSwitch (заменена kvm_daemon_windows.py) |
python3 switch_to_windows.py 0 # Переключение на хост 0 (DisplayPort)
python3 switch_to_windows.py 1 # Переключение на хост 1 (HDMI)
python3 switch_to_windows.py --dry-run 0 # Показать, что произойдёт
Требуется запущенный Logi Options+ и установленный m1ddc (brew install m1ddc).
# Start the AHK hotkey listener (runs in system tray, no console)
# Requires AutoHotkey v2: winget install AutoHotkey.AutoHotkey
start kvm.ahk
# Or switch directly from the command line
python kvm_daemon_windows.py --switch 1
# Show discovered devices and configured hotkeys without switching
python kvm_daemon_windows.py --dry-run
Требуется запущенный Logi Options+. Установите зависимости: pip install pywin32.
Скрипт AHK слушает Win+1/2/3 и вызывает kvm_daemon_windows.py --switch N для каждой. Python-скрипт автоматически обнаруживает устройства через агента (без жёстко заданных идентификаторов устройств или HID-путей). Отредактируйте kvm_config.ini для настройки горячих клавиш и значений DDC/CI для входа монитора.
Агент слушает на:
/tmp/logitech_kiros_agent-<hash>\\.\pipe\logitech_kiros_agent-<hash>Одинаковый проводной протокол на обеих платформах. Формат двоичного фрейма:
LE32(total_len) + BE32(proto_name_len) + "json" + BE32(msg_len) + JSON_message
Переключение устройства на другой хост:
{
"msg_id": "1",
"verb": "SET",
"path": "/change_host/<device_id>/host",
"payload": {
"@type": "type.googleapis.com/logi.protocol.devices.ChangeHost",
"host": 0
}
}
Полезная нагрузка — это поле google.protobuf.Any, сериализованное как встроенный JSON с аннотацией @type. Агент использует строгий синтаксический анализатор JSON для protobuf; неизвестные поля вызывают INVALID_MESSAGE_RECEIVED.
В запросах используется msg_id (snake_case). В ответах — msgId (camelCase). Глаголы — строки: "GET", "SET", "SUBSCRIBE", "BROADCAST".
См. logi-options-ipc-reverse-engineering.md для полной документации протокола.
Для длительной автоматизации повторно подключайтесь при BrokenPipeError и заново обнаруживайте путь к сокету/каналу.
Это применимо при отправке команд HID++ напрямую, а не через агента.
kvm_daemon_windows.pyизбегает всех этих проблем, работая через именованный канал агента.
Коллекция HID++ различается для каждого устройства. MX Master 3S использует HID++ на COL02. MX Keys S — на COL05. Оба используют страницу использования FF43:0202. Проверьте с помощью:
Get-PnpDeviceProperty -InstanceId "<instance_id>" -KeyName DEVPKEY_Device_HardwareIds
# Ищите UP:FF43_U:0202
Индексы функций различаются для каждого устройства. ChangeHost (0x1814) находится по индексу 0x0A на MX Keys S, но 0x09 на MX Mechanical. Запрашивайте во время выполнения через IRoot::GetFeature:
Send: {0x11, 0x00, 0x00, 0x0D, 0x18, 0x14, ...} (20 байт)
Read: ответ, байт 4 = индекс функции
Переспаривание устройства изменяет HID-пути. Переключение с приёмника Bolt на прямое BT LE полностью меняет путь. Запустите query_agent_windows.py, чтобы получить текущие пути от агента.
GATT-коллекция поставщика BT LE показывает "Неизвестно". Windows иногда не может инициализировать службу HID++ GATT. Устройство работает нормально, но канал команд поставщика мёртв. Решение: выключите и включите Bluetooth в настройках Windows. Это проблема Windows/прошивки.
| Версия | Статус |
|---|---|
| Logi Options+ 2.0.840907 | Работает (macOS Tahoe, Windows 11) |
Проводной протокол и основные пути API (/devices/list, /change_host/<id>/host) остаются стабильными. Переспаривание устройств нарушало HID-пути и номера коллекций, но сам IPC-протокол не затрагивался.
Этот проект предназначен только для образовательных и исследовательских целей. Он документирует незадокументированный, неподдерживаемый протокол, который Logitech может изменить или удалить в любое время. Авторы не несут ответственности за любой ущерб, потерю данных, повреждение устройств или нарушение функциональности в результате использования этого кода или документации. Используйте на свой страх и риск.
Этот проект не связан с Logitech и не одобрен ею.
| Сценарий | Что происходит | Обнаружение |
|---|
| Агент не запущен | Сокет/канал не существует | connect() вызывает FileNotFoundError или ConnectionRefusedError |
| Перезапуск агента во время сессии | Соединение разрывается | send() вызывает BrokenPipeError; recv() возвращает пустое значение |
| Устройство на другом хосте | NO_SUCH_PATH | Проверьте result.code |
| Устройство недоступно | TIMEOUT через ~3 сек | Проверьте result.code |
| Некорректная полезная нагрузка | INVALID_MESSAGE_RECEIVED | Отсутствует @type или неизвестные поля |
| Изменение хэша сокета | Старый путь исчез | Всегда динамически обнаруживайте, никогда не задавайте жёстко |
| Устаревший сокет после перезапуска | ConnectionRefusedError | Повторите попытку после короткой задержки |
| Одновременные клиенты | Работает нормально | Агент обрабатывает несколько соединений |