Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

unipi-service

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.

What it provides

  • 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; previous ON states are never restored automatically.
  • Retained hardware availability on <topic_prefix>/availability and <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.

Requirements

  • 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.

Configuration

Create the deployment-specific configuration and environment files:

cp config/config.example.yaml config/local.yaml
cp .env.example .env

config/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: true

The 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-1

Set 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.yaml

Run with Docker Compose

The 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 ps

To inspect the service:

docker compose --profile runtime logs -f unipi-service

CLI

The CLI is built with Click. It can validate configuration without opening MQTT or hardware:

unipi-service --config config/local.yaml validate-config

A dry run performs no MQTT connection and never writes to relays:

unipi-service --config config/local.yaml --disable-adc run --dry-run

MQTT contract

The 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.

Safety model

  • 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 OFF before enabling output pins. A temporary UniPi power loss publishes offline; recovery safely publishes every relay state as OFF before returning to online.
  • Do not commit passwords, MQTT credentials, API tokens, private keys, .env, or config/local.yaml.

See docs/ARCHITECTURE.md for the implementation architecture and operational rationale.

License

This project is licensed under the MIT License.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages