
Protocolo IPC do agente Logi Options+ com engenharia reversa. Alterne dispositivos Logitech multi-host programaticamente via socket Unix (macOS) ou pipe nomeado (Windows).
Documentação de engenharia reversa do protocolo IPC do agente Logi Options+. Permite o controle programático de dispositivos multi-host Logitech (alternância de host, consultas de dispositivos) sem acesso HID bruto, tanto no macOS quanto no Windows.
Este protocolo não foi documentado publicamente antes deste projeto.
O macOS bloqueia o acesso HID bruto a dispositivos de entrada Bluetooth no nível do kernel. Nenhuma permissão, autorização ou hack contorna isso. O agente Logi Options+ possui autorizações assinadas pela Apple (com.apple.security.device.bluetooth) que concedem acesso HID Bluetooth. Este projeto se comunica com o agente através de seu canal IPC.
| Arquivo | Descrição |
|---|---|
logi-options-ipc-reverse-engineering.md | Crônica completa da engenharia reversa |
software-kvm-setup.md | Guia de configuração de KVM bidirecional via software (Windows + Mac) |
switch_to_windows.py | Script no lado Mac que alterna dispositivos Logitech e entrada de monitor via IPC de socket Unix |
api-reference.md | Referência da API do agente: endpoints funcionais, tipos protobuf, capacidades dos dispositivos |
kvm.ahk | Script AutoHotkey v2: teclas de atalho Win+1/2/3 que chamam kvm_daemon_windows.py --switch (executa na bandeja do sistema) |
kvm_daemon_windows.py | Alternância de dispositivos no Windows via pipe nomeado, alternância de monitor via DDC/CI |
kvm_config.ini | Configuração do Windows (teclas de atalho, entradas de monitor) |
query_feature_index.py | Descobre o índice da funcionalidade HID++ ChangeHost para dispositivos Logitech (Windows) |
query_agent_windows.py | Consulta o agente no Windows via pipe nomeado |
config.ini | Configuração legada do UnifiedSwitch (substituída por kvm_daemon_windows.py) |
python3 switch_to_windows.py 0 # Alternar para o host 0 (DisplayPort)
python3 switch_to_windows.py 1 # Alternar para o host 1 (HDMI)
python3 switch_to_windows.py --dry-run 0 # Mostrar o que aconteceria
Requer o Logi Options+ em execução e o m1ddc instalado (brew install m1ddc).
# Iniciar o ouvinte de teclas de atalho AHK (executa na bandeja do sistema, sem console)
# Requer AutoHotkey v2: winget install AutoHotkey.AutoHotkey
start kvm.ahk
# Ou alternar diretamente pela linha de comando
python kvm_daemon_windows.py --switch 1
# Mostrar dispositivos descobertos e teclas de atalho configuradas sem alternar
python kvm_daemon_windows.py --dry-run
Requer o Logi Options+ em execução. Instale as dependências: pip install pywin32.
O script AHK escuta Win+1/2/3 e chama kvm_daemon_windows.py --switch N para cada uma. O script Python descobre os dispositivos automaticamente a partir do agente (sem IDs de dispositivos ou caminhos HID fixos). Edite kvm_config.ini para configurar teclas de atalho e valores de entrada DDC/CI do monitor.
O agente escuta em:
/tmp/logitech_kiros_agent-<hash>\\.\pipe\logitech_kiros_agent-<hash>Mesmo protocolo de transmissão em ambas as plataformas. Formato do frame binário:
LE32(total_len) + BE32(proto_name_len) + "json" + BE32(msg_len) + JSON_message
Alternar um dispositivo para um host diferente:
{
"msg_id": "1",
"verb": "SET",
"path": "/change_host/<device_id>/host",
"payload": {
"@type": "type.googleapis.com/logi.protocol.devices.ChangeHost",
"host": 0
}
}
O payload é um campo google.protobuf.Any serializado como JSON inline com uma anotação @type. O agente usa um parser JSON estrito de protobuf; campos desconhecidos causam INVALID_MESSAGE_RECEIVED.
As requisições usam msg_id (snake_case). As respostas usam msgId (camelCase). Verbos são strings: "GET", "SET", "SUBSCRIBE", "BROADCAST".
Consulte logi-options-ipc-reverse-engineering.md para a documentação completa do protocolo.
Para automação de longa duração, reconectar em caso de BrokenPipeError e redescobrir o caminho do socket/pipe.
Estas se aplicam ao enviar comandos HID++ diretamente, não através do agente.
kvm_daemon_windows.pyevita todas elas usando o pipe nomeado do agente.
A coleção HID++ varia por dispositivo. O MX Master 3S expõe HID++ na COL02. O MX Keys S usa COL05. Ambos usam a página de uso FF43:0202. Verifique com:
Get-PnpDeviceProperty -InstanceId "<instance_id>" -KeyName DEVPKEY_Device_HardwareIds
# Procure por UP:FF43_U:0202
Índices de funcionalidade diferem por dispositivo. ChangeHost (0x1814) está no índice 0x0A no MX Keys S, mas 0x09 no MX Mechanical. Consulte em tempo de execução via IRoot::GetFeature:
Enviar: {0x11, 0x00, 0x00, 0x0D, 0x18, 0x14, ...} (20 bytes)
Ler: byte 4 da resposta = índice da funcionalidade
O reemparelhamento do dispositivo altera os caminhos HID. Alternar do receptor Bolt para BT LE direto altera o caminho completamente. Execute query_agent_windows.py para obter os caminhos atuais do agente.
A coleção de fornecedor GATT BT LE fica "Desconhecido". O Windows ocasionalmente falha ao inicializar o serviço GATT HID++. O dispositivo funciona normalmente, mas o canal de comando do fornecedor está morto. Correção: alternar Bluetooth desligado/ligado nas Configurações do Windows. Isso é um problema do Windows/firmware.
| Versão | Status |
|---|---|
| Logi Options+ 2.0.840907 | Funcionando (macOS Tahoe, Windows 11) |
O protocolo de transmissão e os caminhos principais da API (/devices/list, /change_host/<id>/host) permaneceram estáveis. O reemparelhamento do dispositivo quebrou caminhos HID e números de coleção, mas o protocolo IPC em si não foi afetado.
Este projeto é apenas para fins educacionais e de pesquisa. Ele documenta um protocolo não documentado e não suportado que a Logitech pode alterar ou remover a qualquer momento. Os autores não são responsáveis por qualquer dano, perda de dados, dispositivos danificados ou funcionalidades quebradas resultantes do uso deste código ou documentação. Use por sua conta e risco.
Este projeto não é afiliado ou endossado pela Logitech.
| Cenário | O que acontece | Detecção |
|---|
| Agente não em execução | Socket/pipe não existe | connect() levanta FileNotFoundError ou ConnectionRefusedError |
| Agente reinicia no meio da sessão | Conexão é quebrada | send() levanta BrokenPipeError; recv() retorna vazio |
| Dispositivo em outro host | NO_SUCH_PATH | Verificar result.code |
| Dispositivo inacessível | TIMEOUT após ~3s | Verificar result.code |
| Payload malformado | INVALID_MESSAGE_RECEIVED | Faltando @type ou campos desconhecidos |
| Hash do socket muda | Caminho antigo desaparece | Sempre descobrir dinamicamente, nunca fixar |
| Socket obsoleto após reinicialização | ConnectionRefusedError | Tentar novamente após um breve atraso |
| Clientes concorrentes | Funciona normalmente | Agente lida com múltiplas conexões |