Skip to content

Releases: dgprivate/kronoterm2mqtt

0.1.20

Choose a tag to compare

@dgprivate dgprivate released this 22 Aug 21:12

Two additions, and both were checked rather than asserted.

services:
  kronoterm2mqtt:
    image: hausbit/kronoterm2mqtt:0.1.20

Home Assistant add-on

Home Assistant OS and Supervised installations can install this from an add-on repository instead of running a container by hand: Settings → Add-ons → Add-on store → ⋮ → Repositories, add https://github.com/dgprivate/kronoterm2mqtt, install kronoterm2mqtt.

  • Every setting is on the Configuration tab — MQTT, TLS, the heat pump, the health endpoint, and all of the custom expander including its lists. A test walks the settings dataclasses and fails if the manifest misses one, and a second fails if it offers something the app does not know.
  • The broker fills itself in. use_supervisor takes the address and credentials from the Mosquitto add-on, because Home Assistant already knows them.
  • Still unprivileged. The entrypoint starts as root to write /data, then drops to UID 65532 before starting the app.
  • Watched. The Supervisor polls the same health endpoint the container health check uses, and restarts the add-on when it stops answering.

Documentation for every option is in ha-addon/DOCS.md.

Fuzzing

Three Atheris targets over the input the project does not control — the health report read off a socket, the settings file edited by hand, and the bytes a Modbus gateway sends — run per pull request and weekly, with the corpus cached so coverage accumulates. See fuzz/README.md.

It found two defects in the health command in its first two runs, both fixed here with the inputs pinned as tests:

  • a report missing the keys the command expects — an older container, a truncated answer, or something else on that port — ended in a KeyError traceback instead of a status
  • a report value containing square brackets, which a Modbus error such as Invalid response [0x10] is, was read by rich as markup and ended in a MarkupError

Also

  • The repository's main branch is protected: pull requests, passing tests on 3.12/3.13/3.14, and resolved conversations before a merge.
  • Renovate leaves the add-on's own image tag alone, since the release moves it together with the version.

Vulnerabilities

No publicly known run-time vulnerabilities were fixed in this release.

0.1.19

Choose a tag to compare

@dgprivate dgprivate released this 21 Aug 14:46

Everything in this release is about being able to check what the project claims, rather than take it on trust.

services:
  kronoterm2mqtt:
    image: hausbit/kronoterm2mqtt:0.1.19

Tests and correctness

  • 200 tests, coverage 83% → 96%, and the build now fails below 95%
  • Writing them turned up three real defects, all fixed: the loop mode callback compared an enum member against a string so expedited heating never switched itself off after five hours; an unknown MQTT payload escaped the callback that was written to catch it; and ranges() was annotated as returning a list while being a generator
  • Two pieces of dead code removed: a get_definitions() copied onto a class that lacks the field it reads, and the tomli fallback that cannot run on Python 3.12

Documentation

  • REFERENCE.md — every command, setting, Modbus register source, MQTT topic and health response
  • CONTRIBUTING.md — what a change has to pass before it can be merged
  • SECURITY.md and CODE_OF_CONDUCT.md — how to report something, privately
  • README now leads with the published image and says where to send a bug report

Supply chain

  • security/README.md maps the CIS Docker Benchmark controls to the command that verifies each one, and states what is deliberately not claimed: FIPS, STIG, or any patching service level
  • OpenVEX statements record findings that cannot be fixed and do not apply here, in a form grype --vex and trivy --vex read
  • Renovate keeps the base image digest, syft, uv, the actions and uv.lock moving; the image is rebuilt weekly
  • Every GitHub Action is pinned by commit SHA, uv is copied from a digest-pinned image instead of installed with pip, CodeQL runs on every push, and OpenSSF Scorecard grades the result
  • This release carries its SBOM as an asset, with a Sigstore bundle beside it:
cosign verify-blob --bundle kronoterm2mqtt-v0.1.19.sbom.cdx.json.sigstore.json \
  --certificate-identity-regexp '^https://github.com/.+/.github/workflows/publish-image.yml@.+' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  kronoterm2mqtt-v0.1.19.sbom.cdx.json

No change to how the service behaves: same Modbus handling, same MQTT entities, same health endpoint as 0.1.18.

0.1.18

Choose a tag to compare

@dgprivate dgprivate released this 19 Aug 15:01

First release published as a container image.

services:
  kronoterm2mqtt:
    image: hausbit/kronoterm2mqtt:0.1.18

Built for linux/amd64 and linux/arm64, so a server and a Raspberry Pi take the same tag. 26 MB compressed.

What is in it

  • Modbus resilience: retries with reconnect, tolerance for gateways that greet a new connection with their MAC address, and an undefined register value no longer takes the publish loop down
  • a health endpoint on 127.0.0.1:8099 with docker compose exec kronoterm2mqtt health, and a watchdog that ends the process after a long outage so the restart policy takes over
  • hardened image: Alpine, unprivileged UID 65532, all capabilities dropped, read-only root filesystem, no package managers, base pinned by digest
  • Python 3.14 with every dependency at its current release
  • an SBOM at /usr/share/kronoterm2mqtt/sbom.cdx.json, registry SBOM and provenance attestations, and a keyless Sigstore signature
  • 139 tests, 83% coverage

Verify what you pulled

cosign verify hausbit/kronoterm2mqtt:0.1.18 \
  --certificate-identity-regexp '^https://github.com/.+/.github/workflows/publish-image.yml@.+' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

See security/README.md for what is and is not claimed.