Skip to content

Commit 3ac32c0

Browse files
SmartNightlyclaude
andcommitted
feat: multi-arch container for taptap + taptap-mqtt (v0.2.6), CI to GHCR, ioBroker + Grafana integrations
Extracted from the private iobroker-hecho monorepo (projects/tigo-taptap), sanitized: no site-specific serials, addresses or credentials. Runs as uid 1000, verifies the taptap release checksum, HEALTHCHECK on the taptap-mqtt heartbeat file. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
0 parents  commit 3ac32c0

12 files changed

Lines changed: 1329 additions & 0 deletions

File tree

‎.dockerignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!Dockerfile

‎.github/dependabot.yml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: github-actions
4+
directory: /
5+
schedule:
6+
interval: monthly
7+
- package-ecosystem: docker
8+
directory: /
9+
schedule:
10+
interval: monthly

‎.github/workflows/docker.yml‎

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
name: Build and publish container image
2+
3+
on:
4+
push:
5+
branches: [main]
6+
tags: ['v*.*.*']
7+
paths-ignore:
8+
- '**.md'
9+
- 'integrations/**'
10+
- 'docs/**'
11+
pull_request:
12+
paths-ignore:
13+
- '**.md'
14+
- 'integrations/**'
15+
- 'docs/**'
16+
workflow_dispatch:
17+
inputs:
18+
taptap_version:
19+
description: 'taptap release tag (litinoveweedle/taptap)'
20+
default: 'v0.2.6'
21+
required: true
22+
taptap_mqtt_version:
23+
description: 'taptap-mqtt release tag (litinoveweedle/taptap-mqtt)'
24+
default: 'v0.2.6'
25+
required: true
26+
27+
env:
28+
IMAGE: ghcr.io/${{ github.repository_owner }}/taptap-mqtt
29+
PLATFORMS: linux/amd64,linux/arm64,linux/arm/v7
30+
TAPTAP_VERSION: ${{ inputs.taptap_version || 'v0.2.6' }}
31+
TAPTAP_MQTT_VERSION: ${{ inputs.taptap_mqtt_version || 'v0.2.6' }}
32+
33+
permissions:
34+
contents: read
35+
packages: write
36+
37+
jobs:
38+
build:
39+
name: Build (multi-arch) and push
40+
runs-on: ubuntu-latest
41+
steps:
42+
- name: Checkout
43+
uses: actions/checkout@v4
44+
45+
- name: Set up QEMU (arm emulation for cross builds)
46+
uses: docker/setup-qemu-action@v3
47+
48+
- name: Set up Docker Buildx
49+
uses: docker/setup-buildx-action@v3
50+
51+
# Smoke test on the native platform before anything is pushed:
52+
# the taptap binary must run, and taptap-mqtt must start and reject a missing config cleanly.
53+
- name: Build amd64 for smoke test
54+
uses: docker/build-push-action@v6
55+
with:
56+
context: .
57+
platforms: linux/amd64
58+
load: true
59+
tags: taptap-mqtt:smoke
60+
build-args: |
61+
TAPTAP_VERSION=${{ env.TAPTAP_VERSION }}
62+
TAPTAP_MQTT_VERSION=${{ env.TAPTAP_MQTT_VERSION }}
63+
cache-from: type=gha
64+
cache-to: type=gha,mode=max
65+
66+
- name: Smoke test
67+
run: |
68+
set -eux
69+
docker run --rm --entrypoint taptap taptap-mqtt:smoke --version
70+
# No config mounted → taptap-mqtt must exit 1 with a config error, not crash otherwise.
71+
set +e
72+
out=$(docker run --rm taptap-mqtt:smoke 2>&1); rc=$?
73+
set -e
74+
echo "$out"
75+
test "$rc" -eq 1
76+
echo "$out" | grep -qi "config"
77+
docker run --rm --entrypoint id taptap-mqtt:smoke | grep -q 'uid=1000(taptap)'
78+
79+
- name: Log in to GHCR
80+
if: github.event_name != 'pull_request'
81+
uses: docker/login-action@v3
82+
with:
83+
registry: ghcr.io
84+
username: ${{ github.actor }}
85+
password: ${{ secrets.GITHUB_TOKEN }}
86+
87+
- name: Image metadata (tags, labels)
88+
id: meta
89+
uses: docker/metadata-action@v5
90+
with:
91+
images: ${{ env.IMAGE }}
92+
tags: |
93+
type=semver,pattern={{version}}
94+
type=semver,pattern={{major}}.{{minor}}
95+
type=semver,pattern={{major}}
96+
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
97+
type=raw,value=edge,enable=${{ github.ref == 'refs/heads/main' }}
98+
type=sha
99+
labels: |
100+
org.opencontainers.image.title=taptap-mqtt
101+
org.opencontainers.image.description=Tigo TAP/CCA optimizer data to MQTT (taptap ${{ env.TAPTAP_VERSION }} + taptap-mqtt ${{ env.TAPTAP_MQTT_VERSION }})
102+
103+
- name: Build and push (${{ env.PLATFORMS }})
104+
uses: docker/build-push-action@v6
105+
with:
106+
context: .
107+
platforms: ${{ env.PLATFORMS }}
108+
push: ${{ github.event_name != 'pull_request' }}
109+
tags: ${{ steps.meta.outputs.tags }}
110+
labels: ${{ steps.meta.outputs.labels }}
111+
build-args: |
112+
TAPTAP_VERSION=${{ env.TAPTAP_VERSION }}
113+
TAPTAP_MQTT_VERSION=${{ env.TAPTAP_MQTT_VERSION }}
114+
cache-from: type=gha
115+
cache-to: type=gha,mode=max
116+
provenance: false
117+
118+
- name: Show published manifest
119+
if: github.event_name != 'pull_request'
120+
run: |
121+
first=$(echo "${{ steps.meta.outputs.tags }}" | head -n1)
122+
docker buildx imagetools inspect "$first"

‎.gitignore‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# live configuration with broker credentials and your optimizer serials
2+
config/config.ini
3+
# runtime data (persisted topology)
4+
data/
5+
.DS_Store

‎CHANGELOG.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Changelog
2+
3+
All notable changes to this repository are documented here. The image version is the git tag;
4+
the bundled upstream versions are recorded per release.
5+
6+
## [1.0.0] — 2026-09-25
7+
8+
First public release, extracted from a private home-automation monorepo where the image has been
9+
running in production since May 2026 (31 optimizers on two strings, RS-485-to-Ethernet converter).
10+
11+
- Multi-arch image: `linux/amd64`, `linux/arm64`, `linux/arm/v7` (Raspberry Pi 3/4/5, Synology, x86 NAS).
12+
- Bundles **taptap v0.2.6** (litinoveweedle fork, release binary with SHA-256 verification) and
13+
**taptap-mqtt v0.2.6** (source tarball of the tag, no git in the image).
14+
- Runs unprivileged (uid 1000), `HEALTHCHECK` on the taptap-mqtt heartbeat file.
15+
- GitHub Actions: smoke test on amd64, then multi-arch build and push to GHCR on tags and `main`.
16+
- Integrations: ioBroker JSON parser script (with a setState rate-limit guard) and a Grafana dashboard.
17+
- Documented config pitfalls of taptap-mqtt v0.2.6 (`DISCOVERY_PREFIX` must not be empty,
18+
`DISCOVERY_LEGACY` is required, `MODULES` triplet format).

‎Dockerfile‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# syntax=docker/dockerfile:1.7
2+
#
3+
# taptap-mqtt — Tigo TAP/CCA optimizer data → MQTT, packaged as a multi-arch container.
4+
#
5+
# Bundles two upstream projects at pinned versions:
6+
# - taptap (Rust, MIT) https://github.com/litinoveweedle/taptap — reads the Tigo RS-485 bus
7+
# - taptap-mqtt (Python, GPL) https://github.com/litinoveweedle/taptap-mqtt — publishes to MQTT (HA discovery)
8+
#
9+
# Build args let you bump either version without touching the file:
10+
# docker build --build-arg TAPTAP_VERSION=v0.2.6 --build-arg TAPTAP_MQTT_VERSION=v0.2.6 .
11+
#
12+
# Supported platforms (mapped to the upstream musl release assets):
13+
# linux/amd64 → x86_64, linux/arm64 → arm64, linux/arm/v7 → armv7, linux/arm/v6 → arm, linux/386 → i686
14+
15+
ARG PYTHON_VERSION=3.12
16+
17+
# ---------------------------------------------------------------- stage 1: fetch upstream artefacts
18+
FROM --platform=$BUILDPLATFORM debian:bookworm-slim AS fetch
19+
20+
ARG TAPTAP_VERSION=v0.2.6
21+
ARG TAPTAP_MQTT_VERSION=v0.2.6
22+
ARG TARGETARCH
23+
ARG TARGETVARIANT
24+
25+
RUN apt-get update \
26+
&& apt-get install -y --no-install-recommends ca-certificates curl \
27+
&& rm -rf /var/lib/apt/lists/*
28+
29+
# taptap: pick the release asset for the *target* platform, verify the published SHA-256, extract the binary.
30+
RUN set -eu; \
31+
case "${TARGETARCH}/${TARGETVARIANT:-}" in \
32+
amd64/*) asset=taptap-Linux-musl-x86_64 ;; \
33+
arm64/*) asset=taptap-Linux-musl-arm64 ;; \
34+
arm/v7) asset=taptap-Linux-musleabihf-armv7 ;; \
35+
arm/v6) asset=taptap-Linux-musleabihf-arm ;; \
36+
386/*) asset=taptap-Linux-musl-i686 ;; \
37+
*) echo "unsupported platform ${TARGETARCH}/${TARGETVARIANT:-}" >&2; exit 1 ;; \
38+
esac; \
39+
base="https://github.com/litinoveweedle/taptap/releases/download/${TAPTAP_VERSION}"; \
40+
mkdir -p /out/taptap && cd /out/taptap; \
41+
curl -fsSLo "${asset}.tar.gz" "${base}/${asset}.tar.gz"; \
42+
curl -fsSLo "${asset}.tar.gz.sha256" "${base}/${asset}.tar.gz.sha256"; \
43+
sha256sum -c "${asset}.tar.gz.sha256"; \
44+
tar -xzf "${asset}.tar.gz" taptap; \
45+
rm -f "${asset}.tar.gz" "${asset}.tar.gz.sha256"; \
46+
chmod 0755 taptap
47+
48+
# taptap-mqtt: source tarball of the tagged release (no git needed in the image).
49+
RUN set -eu; \
50+
mkdir -p /out/taptap-mqtt; \
51+
curl -fsSL "https://github.com/litinoveweedle/taptap-mqtt/archive/refs/tags/${TAPTAP_MQTT_VERSION}.tar.gz" \
52+
| tar -xz --strip-components=1 -C /out/taptap-mqtt
53+
54+
# ---------------------------------------------------------------- stage 2: runtime image
55+
FROM python:${PYTHON_VERSION}-slim
56+
57+
ARG TAPTAP_VERSION=v0.2.6
58+
ARG TAPTAP_MQTT_VERSION=v0.2.6
59+
60+
LABEL org.opencontainers.image.title="taptap-mqtt" \
61+
org.opencontainers.image.description="Tigo TAP/CCA optimizer data to MQTT (taptap + taptap-mqtt), multi-arch" \
62+
org.opencontainers.image.source="https://github.com/SmartNightly/taptap-mqtt-docker" \
63+
org.opencontainers.image.licenses="MIT AND GPL-3.0-only" \
64+
taptap.version="${TAPTAP_VERSION}" \
65+
taptap-mqtt.version="${TAPTAP_MQTT_VERSION}"
66+
67+
# Python deps of taptap-mqtt (paho-mqtt, python-dateutil, uptime)
68+
COPY --from=fetch /out/taptap-mqtt/requirements.txt /tmp/requirements.txt
69+
RUN pip install --no-cache-dir -r /tmp/requirements.txt && rm -f /tmp/requirements.txt
70+
71+
COPY --from=fetch /out/taptap/taptap /usr/local/bin/taptap
72+
COPY --from=fetch /out/taptap-mqtt/taptap-mqtt.py /opt/taptap-mqtt/taptap-mqtt.py
73+
COPY --from=fetch /out/taptap-mqtt/LICENSE /opt/taptap-mqtt/LICENSE
74+
COPY --from=fetch /out/taptap-mqtt/README.md /opt/taptap-mqtt/README.md
75+
76+
# Unprivileged runtime user. /config holds config.ini (read-only bind mount),
77+
# /data the persisted optimizer topology (STATE_FILE), /run/taptap the RUN_FILE heartbeat.
78+
RUN groupadd --gid 1000 taptap \
79+
&& useradd --uid 1000 --gid taptap --home-dir /opt/taptap-mqtt --no-create-home --shell /usr/sbin/nologin taptap \
80+
&& mkdir -p /config /data /run/taptap \
81+
&& chown -R taptap:taptap /data /run/taptap \
82+
&& /usr/local/bin/taptap --version
83+
84+
USER taptap
85+
WORKDIR /opt/taptap-mqtt
86+
VOLUME ["/data"]
87+
88+
# taptap-mqtt touches RUN_FILE while it runs and removes it on shutdown → cheap liveness probe.
89+
HEALTHCHECK --interval=60s --timeout=5s --start-period=90s --retries=3 \
90+
CMD test -f /run/taptap/taptap.run || exit 1
91+
92+
ENTRYPOINT ["python3", "/opt/taptap-mqtt/taptap-mqtt.py"]
93+
CMD ["/config/config.ini"]

‎LICENSE‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Dominic Erni (SmartNightly)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+
23+
---
24+
25+
This license covers the files of this repository (Dockerfile, CI workflow,
26+
documentation, example configuration, integration scripts). The container
27+
image built from it bundles third-party software under its own licenses:
28+
29+
- taptap https://github.com/litinoveweedle/taptap MIT License
30+
- taptap-mqtt https://github.com/litinoveweedle/taptap-mqtt GNU GPL v3.0
31+
32+
Their license texts are included in the image under /opt/taptap-mqtt/LICENSE
33+
(taptap-mqtt) and are available in the upstream repositories.

0 commit comments

Comments
 (0)