Files
ezcoo-usb-control/README.md
T
oleksandr e97475ed17
Build deb packages on release / build-deb (release) Successful in 3m29s
docs: add README and Home Assistant integration guide
2026-05-24 18:53:37 +03:00

269 lines
8.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ezcoo-usb-control
> MQTT bridge for the **EZCOO EZ-MX42HAS-ARC** 4×2 HDMI matrix, with Home Assistant auto-discovery.
`ezcoo-usb-control` is a small Go daemon that talks to an EZCOO HDMI matrix
over USB-UART using its ASCII command set and exposes the routing state to
an MQTT broker. It publishes Home Assistant MQTT discovery payloads so the
matrix shows up automatically as two `select` entities — one per output —
ready to be used in dashboards and automations.
Built for, and running on, a Raspberry Pi 4, but the binary is plain Go and
works on any Linux/arm64, Linux/armhf or Linux/amd64 host that can see the
matrix as a serial device.
---
## Table of contents
- [Features](#features)
- [Hardware](#hardware)
- [Quick start](#quick-start)
- [Installation](#installation)
- [Debian / Raspberry Pi OS (.deb)](#debian--raspberry-pi-os-deb)
- [From source](#from-source)
- [Configuration](#configuration)
- [Running](#running)
- [MQTT topics](#mqtt-topics)
- [Home Assistant](#home-assistant)
- [Verifying the serial protocol](#verifying-the-serial-protocol)
- [Troubleshooting](#troubleshooting)
- [Project layout](#project-layout)
- [Development](#development)
- [Contributing](#contributing)
- [License](#license)
- [Acknowledgements](#acknowledgements)
---
## Features
- Two-way control of a 4-in / 2-out EZCOO HDMI matrix over USB-UART
- Home Assistant MQTT auto-discovery — zero manual entity wiring
- Periodic polling so external changes (front panel, IR remote) are reflected
in MQTT
- LWT-based availability (`online` / `offline`)
- Structured logging via `log/slog`, with `--debug` flag
- Hardened `systemd` unit and `.deb` packages for `arm64`, `armhf`, `amd64`
## Hardware
Tested with:
- **EZCOO EZ-MX42HAS-ARC** (4 HDMI inputs, 2 HDMI outputs, ARC) — exposed
as a USB CDC-ACM serial port (typically `/dev/ttyACM0`, 57600 8N1)
- Raspberry Pi 4 (Raspberry Pi OS, 64-bit) as the host
Other EZCOO matrices that share the same `EZG OUT0 VS` / `EZS OUTx VS INy`
ASCII protocol should work; if your unit speaks a slightly different dialect
see [Verifying the serial protocol](#verifying-the-serial-protocol).
## Quick start
```sh
# 1. Build a Raspberry Pi 4 (arm64) .deb on any Linux host with Go installed
make deb-arm64 VERSION=0.1.0
# 2. Copy and install on the Pi
scp dist/ezcoo-usb-control_0.1.0_arm64.deb pi@raspberrypi:~
ssh pi@raspberrypi 'sudo apt install ./ezcoo-usb-control_0.1.0_arm64.deb'
# 3. Configure and start
ssh pi@raspberrypi 'sudoedit /etc/ezcoo-usb-control/config.yaml'
ssh pi@raspberrypi 'sudo systemctl restart ezcoo-usb-control'
ssh pi@raspberrypi 'sudo journalctl -u ezcoo-usb-control -f'
```
## Installation
### Debian / Raspberry Pi OS (.deb)
Pre-built `.deb` packages are produced for each release; download the
matching architecture from the project's releases page and install:
```sh
sudo apt install ./ezcoo-usb-control_<version>_<arch>.deb
```
The package installs:
| Path | Purpose |
|---|---|
| `/usr/bin/ezcoo-usb-control` | Binary |
| `/etc/ezcoo-usb-control/config.yaml` | Default config (marked `conffile`) |
| `/lib/systemd/system/ezcoo-usb-control.service` | systemd unit |
A dedicated `ezcoo` system user is created with `dialout` as a supplementary
group so the daemon can open the serial device without root.
To build a `.deb` yourself for any supported architecture:
```sh
make deb-arm64 VERSION=0.1.0 # Raspberry Pi 4 / 5 (64-bit)
make deb-armhf VERSION=0.1.0 # Raspberry Pi 2/3 / Zero 2 (32-bit)
make deb-amd64 VERSION=0.1.0 # x86_64
make deb-all VERSION=0.1.0 # all three
```
Requires `dpkg-dev` on the build host (`sudo apt install dpkg-dev`).
### From source
```sh
# native build
make build
./build/ezcoo-usb-control --config cmd/ezcoo-usb-control/config.example.yaml
# or directly with go
go build -o ezcoo-usb-control ./cmd/ezcoo-usb-control
```
Requires Go ≥ 1.26 (see `go.mod`).
## Configuration
Copy `cmd/ezcoo-usb-control/config.example.yaml` to `/etc/ezcoo-usb-control/config.yaml`
(the `.deb` package does this for you) and edit:
```yaml
device:
port: /dev/ttyACM0 # serial device exposed by the matrix
baud: 57600 # EZCOO default
poll_interval: 15s # how often to query routing state
mqtt:
broker: tcp://192.168.1.10:1883
username: ""
password: ""
client_id: ezcoo-usb-control
base_topic: ezcoo # all state/set topics live under this
discovery_prefix: homeassistant # Home Assistant discovery root
```
All fields have sensible defaults — the minimum viable config is just
`mqtt.broker`. The config file path is passed via `--config`; if omitted,
defaults are used as-is.
## Running
```sh
ezcoo-usb-control --config /etc/ezcoo-usb-control/config.yaml
```
Flags:
| Flag | Description |
|---|---|
| `--config <path>` | Path to YAML config (optional) |
| `--debug` | Enable debug-level logging |
Under `systemd` the service is started with:
```sh
sudo systemctl enable --now ezcoo-usb-control
sudo journalctl -u ezcoo-usb-control -f
```
To observe what the bridge publishes while running:
```sh
mosquitto_sub -h localhost -t 'homeassistant/#' -v
mosquitto_sub -h localhost -t 'ezcoo/#' -v
```
## MQTT topics
Topic names assume the default `base_topic: ezcoo` and `discovery_prefix: homeassistant`.
| Topic | Direction | Payload | Description |
|---|---|---|---|
| `ezcoo/availability` | publish (retained, LWT) | `online` / `offline` | Bridge liveness |
| `ezcoo/output1/state` | publish (retained) | `IN1`..`IN4` | Current input routed to OUT1 |
| `ezcoo/output2/state` | publish (retained) | `IN1`..`IN4` | Current input routed to OUT2 |
| `ezcoo/output1/set` | subscribe | `IN1`..`IN4` | Switch OUT1 to the given input |
| `ezcoo/output2/set` | subscribe | `IN1`..`IN4` | Switch OUT2 to the given input |
| `homeassistant/device/ezcoo_matrix/config` | publish (retained) | JSON | HA device discovery payload |
## Home Assistant
If Home Assistant is connected to the same MQTT broker, the matrix appears
automatically as a single device with two `select` entities — no YAML
required. See [`docs/home-assistant.md`](docs/home-assistant.md) for
discovery details, example automations (including a "mirror both outputs"
recipe), and a Lovelace card snippet.
## Verifying the serial protocol
Before first deployment, probe the exact command set of your unit:
```sh
picocom -b 57600 /dev/ttyACM0
# then type: EZH (dumps all supported commands)
# then type: EZG OUT0 VS (shows current routing of all outputs)
```
The bridge expects responses in the form `OUT1 VS IN3`. If your unit
replies differently, adjust the `reOutVS` regular expression in
[`pkg/ezcoo/utils.go`](pkg/ezcoo/utils.go).
## Troubleshooting
- **`permission denied: /dev/ttyACM0`** — the `ezcoo` user must be in the
group that owns the device (`dialout` on Debian/RPi OS). The `.deb` adds
this automatically; for custom installs, do it manually.
- **Entities don't appear in Home Assistant** — confirm the discovery prefix
matches HA's MQTT integration setting (default `homeassistant`), and that
the broker shows a retained payload on `homeassistant/device/ezcoo_matrix/config`.
- **State never changes** — run with `--debug` and watch for `state update`
log lines. If polling never returns matches, the regex likely needs
adjusting (see above).
- **Bridge reports `offline` after connecting** — check that the systemd
unit has access to the serial char device; the unit ships with
`DeviceAllow=char-ttyACM rw` and `DeviceAllow=char-ttyUSB rw`.
## Project layout
```
.
├── cmd/ezcoo-usb-control/ # main entrypoint, config loader, example config
├── pkg/
│ ├── bridge/ # MQTT side: connect, subscribe, publish, HA discovery
│ └── ezcoo/ # serial side: device driver, protocol, polling loop
├── packaging/
│ ├── DEBIAN/ # control, postinst, prerm, postrm, conffiles
│ └── systemd/ # hardened service unit
├── .gitea/workflows/ # CI: build .deb packages on release
└── Makefile # build + multi-arch .deb targets
```
## Development
```sh
go build ./...
go vet ./...
go test ./...
```
## Contributing
Issues and pull requests are welcome. For non-trivial changes please open
an issue first so the design can be discussed. When submitting a PR:
1. Run `go vet ./...` and `go test ./...`.
2. Keep changes focused and the commit history clean.
3. Update this README if you add a flag, change a topic, or alter the
config schema.
## License
Released under the [MIT License](LICENSE.md)
## Acknowledgements
- The EZCOO ASCII command set documented in the EZ-MX42HAS-ARC manual.
- [`paho.mqtt.golang`](https://github.com/eclipse/paho.mqtt.golang) for the MQTT client.
- [`go.bug.st/serial`](https://github.com/bugst/go-serial) for cross-platform serial I/O.
- Home Assistant's
[MQTT discovery](https://www.home-assistant.io/integrations/mqtt/#mqtt-discovery)
schema.