A tool to control EZCOO KVM switches via the serial interface.
Tested Devices: EZCOO EZ-SW41HA-KVMU3L with firmware version 2.03 (should be equal to EZ-SW41HA-KVMU3P)
Install using uv:
uv add ezcoo-cliInstall from the Arch User Repository:
yay -S ezcoo-cli
# or
paru -S ezcoo-cliAUR package: https://aur.archlinux.org/packages/ezcoo-cli
git clone https://github.com/Luminger/ezcoo-cli
cd ezcoo-cli
uv syncThe CLI provides commands to control your EZCOO KVM switch through a serial connection.
Switch between inputs:
# Switch to input 2
ezcoo-cli input switch 2
# Switch to input 3 (output 1 is implicit)
ezcoo-cli input switch 3 --output 1Check current status:
# View system information
ezcoo-cli status
# Check which input is currently active
ezcoo-cli output routing
# Check stream status
ezcoo-cli output streamGet device information:
# View available commands
ezcoo-cli help
# Get raw device response (useful for debugging)
ezcoo-cli help --format raw
ezcoo-cli status --format rawMost query commands support multiple output formats to suit different use cases. You can specify the format using the --format (or -f) flag:
pretty- Human-readable formatted output (default)json- Machine-readable JSON output for scripting and automationraw- Raw device response as received from the KVM
For example, to get system status as JSON:
ezcoo-cli status --format json
# or using the short form
ezcoo-cli status -f jsonNote
Breaking Change in v0.2.0: Version 0.1.0 always printed raw output. Starting from v0.2.0, commands default to pretty-formatted output. Use --format raw to get the previous behavior.
By default, the tool connects to /dev/ttyUSB0 at address 0 (single device mode). You can specify a different device and address:
# Use a different serial device
ezcoo-cli -d /dev/ttyUSB1 input switch 2
# Communicate with device at address 5
ezcoo-cli --address 5 status
# Short form
ezcoo-cli -a 5 statusEZCOO devices support address-based multi-device setups where multiple KVM switches can share a single serial connection. Each device needs a unique address (0-99), with 0 being the default for single-device setups.
Discovering devices on the serial port:
# Scan all addresses (0-99)
ezcoo-cli system discover
# Scan specific range
ezcoo-cli system discover --start 0 --end 10Changing device addresses:
# Change device at address 0 to address 5
ezcoo-cli system set-address 5
# Change device at address 5 to address 10
ezcoo-cli --address 5 system set-address 10Warning
After changing a device's address, you must use the --address option to communicate with it at its new address.
You can use ezcoo-cli as a library in your Python projects. There are two interfaces available:
The high-level interface provides type-safe, structured access to KVM functionality:
from pathlib import Path
from ezcoo_cli.kvm import KVM
# Create KVM instance (default address 0)
kvm = KVM(Path("/dev/ttyUSB0"))
# Get system information
status = kvm.get_system_status()
print(f"Firmware: {status.firmware_version}")
print(f"Address: {status.system_address}")
# Switch inputs
kvm.switch_input(2) # Switch to input 2
# Get current routing
routing = kvm.get_output_routing()
print(f"Output {routing.output} -> Input {routing.input}")
# Get stream status
stream = kvm.get_stream_status()
print(f"Stream enabled: {stream.enabled}")
# Get help information
help_info = kvm.get_help()
print(f"Available commands: {help_info.total_commands}")
# Working with devices at specific addresses
kvm_at_5 = KVM(Path("/dev/ttyUSB0"), address=5)
status = kvm_at_5.get_system_status()
# Change device address
kvm.set_device_address(5) # Change from 0 to 5
kvm.address = 5 # Update instance to use new address
# Access raw response for any command
print(status.raw_response) # Raw device output
print(status.command) # Command that was sentFor direct command access, use the Device class:
from pathlib import Path
from ezcoo_cli.device import Device
# Basic usage
with Device(Path("/dev/ttyUSB0")) as device:
# Switch input 2 to output 1
device.write("EZS OUT1 VS IN2")
# Get help
device.write("EZH")
for line in device.readlines():
print(line, end="")This project uses uv for dependency management and ruff for linting.
# Install development dependencies
uv sync --dev
# Run linting
uv run ruff check
# Run formatting
uv run ruff formatThe test suite uses pytest-reserial to record and replay serial device interactions, allowing tests to run without physical hardware.
Replay Mode (no hardware needed):
./scripts/test-replay.sh
# or: uv run pytest tests/ --replay -vHardware Mode (with real device):
./scripts/test-with-hardware.sh
# or: uv run pytest tests/ -vRecord Mode (capture new traffic):
./scripts/test-record.sh
# or: uv run pytest tests/ --record -vpytest-reserial automatically records serial traffic in the tests/ directory, with one recording file per test module.
Important: Commit these recording files to version control so others can run tests without hardware.
- EZCOO device connected to
/dev/ttyUSB0 - User has permissions to access serial device:
sudo usermod -a -G dialout $USER # Log out and back in for changes to take effect
Based on testing with EZCOO EZ-SW41HA-KVMU3L devices running firmware 2.03:
| Command | Description | Response |
|---|---|---|
EZSTA |
Get system status | System info with address, firmware, serial config |
EZH |
Get help | Complete command list |
EZG OUTx VS |
Get output routing | Current input routing |
EZG OUT1 STREAM |
Get stream status | Stream on/off status |
| Command | Description | Response | CLI Command |
|---|---|---|---|
EZS OUTx VS INy |
Switch input | No response (SET command) | ezcoo-cli input switch <input_num> |
EZS ADDR xx |
Set system address | No response (SET command) | ezcoo-cli system set-address <new_address> |
Query commands that don't return data (Firmware 2.03):
These commands have been tested and confirmed to return no data on firmware 2.03. They are not exposed by the project as they don't work on this firmware version.
| Command | Description | Test Result |
|---|---|---|
EZG INx SIG STA |
Get input signal status | No response from device |
EZG INx EDID |
Get EDID information | No response from device |
EZG ADDR |
Get system address | No response from device |
EZG AUTO MODE |
Get auto switch mode status | No response from device |
EZG CAS |
Get cascade mode status | No response from device |
EZG STA |
Get system status (alternative) | No response from device (use EZSTA instead) |
SET commands with unknown/unclear effect (not implemented in this tool):
These SET commands have been tested and the device accepts them without errors (no response, which is normal for SET commands). However, their actual effect is unclear - either no observable changes occurred or the expected behavior was not seen.
| Command | Description | Test Result | Reason Not Implemented |
|---|---|---|---|
EZS CAS EN/DIS |
Set cascade mode enable/disable | Accepted by device, no observable effect | Effect unclear |
EZS OUTx VIDEOy |
Set output video mode (BYPASS/4K->2K) | Accepted by device, no observable effect | Effect unclear |
EZS INx EDID y |
Set input EDID | Accepted by device, no observable effect | Effect unclear |
EZS RST |
Reset to factory defaults | Accepted by device, but address was NOT reset (still at 01 after reset) | Effect unclear - may not work or may only reset some settings |
This project is licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later).
See the LICENSE file for details.