A local, security-focused service for exposing UniPi 1.1 I/O through MQTT and Home Assistant MQTT Discovery. It is designed for Raspberry Pi hosts, but its configuration and deployment model are intended to be reusable for other UniPi 1.1 installations.
unipi-service is not tied to one room, one Home Assistant instance, or one deployment. Installation-specific settings belong in an ignored local YAML file; the tracked example configuration contains safe defaults.
- Event-driven UniPi digital inputs (
DI1–DI12) using libgpiod v2 edge events. - Raspberry Pi internal pull-ups for disconnected active-low inputs, so their default MQTT state is
OFF. - Debounced, retained MQTT input state and Home Assistant MQTT Discovery binary sensors.
- MQTT-controlled UniPi relay outputs (
RO1–RO8) using the MCP23008 I²C expander. - Safe relay initialization and recovery: every startup or detected UniPi power reset sets all relay channels to
OFF; previousONstates are never restored automatically. - Retained hardware availability on
<topic_prefix>/availabilityand<topic_prefix>/status. - A configurable Home Assistant device display name, without changing stable MQTT topics or entity identifiers.
- A local-only Mosquitto broker and a hardened, non-privileged Docker runtime.
ADC and analogue output support are deliberately out of scope for now. Their YAML features remain disabled unless separately designed, implemented, and validated.
- Raspberry Pi with Raspberry Pi OS or another Linux system exposing
/dev/gpiochip0. - A UniPi 1.1 board connected to the Raspberry Pi I²C bus (
/dev/i2c-1) when relay support is enabled. - Docker Engine and the Docker Compose plugin.
- Home Assistant with the MQTT integration configured, if Home Assistant Discovery is desired.
The service runs Mosquitto on 127.0.0.1:1883 only. Home Assistant should run on the same host or have another deliberately designed and secured broker connection. Do not expose this broker to a LAN or the Internet without authentication, ACLs, TLS, and a separate security review.
Create the deployment-specific configuration and environment files:
cp config/config.example.yaml config/local.yaml
cp .env.example .envconfig/local.yaml is ignored by Git. Enable only the hardware capabilities you actually need:
mqtt:
# Presentation only: does not change topics or Home Assistant identifiers.
device_name: UniPi Board
# Choose a unique root for this installation before its first HA discovery.
topic_prefix: unipi/utility-room
hardware:
digital_inputs:
enabled: true
debounce_ms: 50
relays:
enabled: trueThe UniPi 1.1 DI and relay mappings are fixed in the application and cannot be changed through YAML. This prevents a configuration typo from targeting arbitrary Raspberry Pi GPIO lines or MCP23008 bits.
Determine the required host group IDs:
stat -c '%g' /dev/gpiochip0
stat -c '%g' /dev/i2c-1Set the resulting numeric values as GPIO_GID and I2C_GID in .env. Keep .env out of Git.
The container uses UID/GID 10001 for the service user, so make the local configuration readable but not world-readable:
sudo chown root:10001 config/local.yaml
sudo chmod 640 config/local.yamlThe runtime services are behind an explicit Compose profile. Nothing starts with a plain docker compose up.
docker compose --profile runtime up -d --build
docker compose --profile runtime psTo inspect the service:
docker compose --profile runtime logs -f unipi-serviceThe CLI is built with Click. It can validate configuration without opening MQTT or hardware:
unipi-service --config config/local.yaml validate-configA dry run performs no MQTT connection and never writes to relays:
unipi-service --config config/local.yaml --disable-adc run --dry-runThe configured mqtt.topic_prefix is the stable integration boundary. With the default example prefix:
unipi/utility-room/availability
unipi/utility-room/status
unipi/utility-room/input/DI<n>/state
unipi/utility-room/relay/RO<n>/state
unipi/utility-room/relay/RO<n>/set
Home Assistant MQTT Discovery is published below homeassistant/. Device display names are controlled by mqtt.device_name; do not change MQTT root topics or discovery identifiers casually after Home Assistant has discovered the entities, or duplicate/orphaned entities may result.
- Digital inputs and relays are disabled by default.
- The runtime is not
privileged; it has a read-only root filesystem, no Linux capabilities,no-new-privileges, and only the required GPIO/I²C devices. - Docker cannot restrict a GPIO character device to individual lines, nor I²C to one address. The application therefore uses a fixed approved UniPi mapping. This limits accidental misuse but is not a complete containment boundary against a compromised container.
- Relay startup and recovery always write
OFFbefore enabling output pins. A temporary UniPi power loss publishesoffline; recovery safely publishes every relay state asOFFbefore returning toonline. - Do not commit passwords, MQTT credentials, API tokens, private keys,
.env, orconfig/local.yaml.
See docs/ARCHITECTURE.md for the implementation architecture and operational rationale.
This project is licensed under the MIT License.