GitOps reconciliation and recovery for Tasmota device configuration
  • Python 88.4%
  • Shell 8.6%
  • Dockerfile 1.9%
  • Just 1.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Andy-Zi 0cd0ffe52f
All checks were successful
Container / publish (pull_request) Has been skipped
Container / validate (pull_request) Successful in 18s
Container / validate (push) Has been skipped
Container / publish (push) Successful in 52s
ci: migrate workflows to KEDA runners
2026-09-02 10:49:55 +02:00
.forgejo ci: migrate workflows to KEDA runners 2026-09-02 10:49:55 +02:00
examples fix(tasmota): retry observations and isolate credentials (#7) 2026-08-05 20:47:43 +00:00
resources/cosign feat: scaffold Tasmota reconciler 2026-07-26 14:56:58 +02:00
src/tasmota_reconciler fix(tasmota): retry observations and isolate credentials (#7) 2026-08-05 20:47:43 +00:00
tests fix(tasmota): retry observations and isolate credentials (#7) 2026-08-05 20:47:43 +00:00
.gitignore feat: scaffold Tasmota reconciler 2026-07-26 14:56:58 +02:00
.python-version feat: scaffold Tasmota reconciler 2026-07-26 14:56:58 +02:00
Containerfile feat: scaffold Tasmota reconciler 2026-07-26 14:56:58 +02:00
justfile feat: scaffold Tasmota reconciler 2026-07-26 14:56:58 +02:00
pyproject.toml fix: fall back to native MQTT commands (#4) 2026-07-27 05:04:38 +00:00
README.md fix(tasmota): retry observations and isolate credentials (#7) 2026-08-05 20:47:43 +00:00
renovate.json feat: scaffold Tasmota reconciler 2026-07-26 14:56:58 +02:00
requirements-dev.lock fix: fall back to native MQTT commands (#4) 2026-07-27 05:04:38 +00:00
requirements.lock fix: fall back to native MQTT commands (#4) 2026-07-27 05:04:38 +00:00
uv.lock fix: fall back to native MQTT commands (#4) 2026-07-27 05:04:38 +00:00

Tasmota reconciler

tasmota-reconciler turns selected, stable Tasmota settings into Git-owned desired state. It uses the upstream decode-config utility for version-aware configuration transport and restore, while this project adds:

  • a multi-device manifest;
  • MQTT credentials from mounted secret files;
  • subset-aware drift detection;
  • explicit plan, apply, and encrypted .dmp backup operations;
  • bounded retries for transient or truncated device observations;
  • reboot-aware post-apply verification;
  • native Tasmota MQTT command fallback when a restore fails to converge; and
  • JSON-line output suitable for Kubernetes Jobs and log monitoring.

Runtime values such as relay state, uptime, signal strength, and accumulated energy are intentionally outside the reconciler unless explicitly present in a device's desired JSON. Secret-like fields are rejected from desired-state files; credentials belong in mounted SOPS-managed secret files.

Configuration

See examples/manifest.json and examples/devices/winterstern.json. Relative desiredFile paths are resolved from the manifest directory.

The broker URL must not contain credentials. If authentication is required, use usernameFile and passwordFile. devicePasswordFile is the password stored in the Tasmota device and is required by decode-config when its MQTT broker identity uses a different password.

Individual device entries may override the global deviceMqtt identity with mqttUsername and mqttPasswordFile. Both fields must be supplied together; this allows each device to have a separately revocable broker credential.

Optional top-level wifi and deviceMqtt blocks inject their passwords only into a short-lived restore file. This lets Git own the SSID and device MQTT identity without putting either password in the manifest, desired JSON, drift output, command arguments, or logs. Password values are intentionally not verified because decode-config hides them when observing a device.

Commands

tasmota-reconciler --manifest /config/manifest.json plan
tasmota-reconciler --manifest /config/manifest.json apply
tasmota-reconciler --manifest /config/manifest.json backup \
  --output-directory /backups

plan returns:

  • 0 when all selected devices are converged;
  • 2 when drift exists; and
  • 1 for configuration or communication errors.

apply reads current state, restores only devices with drift, and retries verification while the device reboots. If the restore errors or persistent drift remains, the reconciler translates the remaining supported fields into native Tasmota commands, submits them with Backlog, and verifies the device a second time. It still fails unless every Git-owned field converges.

The fallback covers the Wi-Fi and MQTT transition fields, energy calibration, resolution, template/module, naming, topic, and telemetry fields used by the managed fleet. It rejects values that cannot be represented safely in a Backlog command instead of sending an ambiguous command.

Backups use Tasmota's encrypted .dmp format. They should be written to a persistent volume and included in the cluster backup policy.

Development

The repository uses the managed Nix Python environment:

direnv exec . uv sync --frozen
direnv exec . uv run ruff check .
direnv exec . uv run pytest

Image publication

Pull requests build the complete image, execute tests and linting inside the build, and inspect the CLI without publishing. Merges and weekly scheduled runs publish signed images with SPDX SBOM and SLSA provenance to:

registry.ancofazi.de/homelab/tasmota-reconciler:<decode-config-version>-<run>

The Andreas/homelab repository owns deployment and pins the resulting image by tag and digest.