Skip to content

Instantly share code, notes, and snippets.

@du-song
Last active October 13, 2022 14:12
Show Gist options
  • Save du-song/a9a2f6b155a74dc81e19c704ffacceeb to your computer and use it in GitHub Desktop.
Save du-song/a9a2f6b155a74dc81e19c704ffacceeb to your computer and use it in GitHub Desktop.
Software KVM Switch. Tested with Logi keyboard/mouse + Dell monitor. Inspired by https://github.com/pwr-Solaar/Solaar

Requirement

on macOS

brew install hidapi
brew install ddcctl

on Linux

modprobe i2c-dev
pip install monitorcontrol

on both

pip install hid
#!/usr/bin/env python
import sys
import hid
import os
host = int(sys.argv[1]) - 1
monitor_input_sources = [17, 18, 27] # HDMI 1, HDMI 2, USB-C
hid_devices = [
{
'name': 'Logi Bolt Receiver',
'receivers': hid.enumerate(0x046D, 0xC548),
'command': [ # MX Keys Mini as first device on the receiver
bytes([0x11, 0x01, 0x09, 0x1A, 0x00] + [0] * 15), # change host to 1
bytes([0x11, 0x01, 0x09, 0x1F, 0x01] + [0] * 15), # change host to 2
bytes([0x11, 0x01, 0x09, 0x1D, 0x02] + [0] * 15)] # change host to 3
}, {
'name': 'Logitech Unified Receiver',
'receivers': hid.enumerate(0x046D, 0xC52B),
'command': [ # MX Anywhere 3 as first device on the receiver
bytes([0x11, 0x01, 0x0A, 0x1A, 0x00] + [0] * 15), # change host to 1
bytes([0x11, 0x01, 0x0A, 0x1E, 0x01] + [0] * 15), # change host to 2
bytes([0x11, 0x01, 0x0A, 0x19, 0x02] + [0] * 15)] # change host to 3
}, {
'name': 'MX Keys Mini Bluetooth',
'receivers': hid.enumerate(0x046D, 0xB36E),
'command': [
bytes([0x11, 0xFF, 0x09, 0x1E, 0x00] + [0] * 15), # change host to 1
bytes([0x11, 0x01, 0x09, 0x1F, 0x01] + [0] * 15), # change host to 2
bytes([0x11, 0xFF, 0x09, 0x1D, 0x02] + [0] * 15)] # change host to 3
}, {
'name': 'MX Anywhere 3 Bluetooth',
'receivers': hid.enumerate(0x046D, 0xB025),
'command': [
bytes([0x11, 0xFF, 0x0A, 0x1A, 0x00] + [0] * 15), # change host to 1
bytes([0x11, 0x01, 0x0A, 0x1E, 0x01] + [0] * 15), # change host to 2
bytes([0x11, 0xFF, 0x0A, 0x19, 0x02] + [0] * 15)] # change host to 3
}
]
for d in hid_devices:
for r in d['receivers']:
if r['usage_page'] >= 0xFF00: # and r['usage'] == 1 and
print('found:', d['name'], 'path:', r['path'])
try:
receiver = hid.Device(path=r['path'])
receiver.nonblocking = 1
data = d['command'][host]
print("sending:", data)
receiver.write(data)
receiver.close()
except hid.HIDException as err:
print(err)
if sys.platform == 'darwin':
os.system(f'/usr/local/bin/ddcctl -d 1 -i {monitor_input_sources[host]}')
else:
import monitorcontrol
try:
m = monitorcontrol.get_monitors()[0]
i = monitor_input_sources[host]
print('display input source:', i)
with m:
m.set_input_source(i)
except Exception as e:
print(e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment