docs: add README and Home Assistant integration guide
Build deb packages on release / build-deb (release) Successful in 3m29s

This commit is contained in:
2026-05-24 18:53:37 +03:00
parent caaeef7341
commit e97475ed17
2 changed files with 417 additions and 0 deletions
+268
View File
@@ -0,0 +1,268 @@
# 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.