From b48c3868e08a62cf64f61bfabae855e911953756 Mon Sep 17 00:00:00 2001 From: retr0 Date: Tue, 7 Apr 2026 19:50:49 +0200 Subject: [PATCH] feat: add MQTT sensor publishing for Home Assistant Adds a new s6 service (svc-foldingathome-mqtt) that polls the FAH 8 API on port 7396 every 30 seconds and publishes 4 sensors to HA via MQTT autodiscovery: Status, PPD, Work Unit Progress, and Credit estimate. Requires the Mosquitto broker add-on; declared as mqtt:want so the add-on remains functional without it. Co-Authored-By: Claude Sonnet 4.6 --- Dockerfile | 1 + config.yaml | 2 + .../dependencies.d/svc-foldingathome | 0 .../s6-rc.d/svc-foldingathome-mqtt/run | 99 +++++++++++++++++++ .../s6-rc.d/svc-foldingathome-mqtt/type | 1 + .../user/contents.d/svc-foldingathome-mqtt | 0 6 files changed, 103 insertions(+) create mode 100644 root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/dependencies.d/svc-foldingathome create mode 100755 root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/run create mode 100644 root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/type create mode 100644 root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-foldingathome-mqtt diff --git a/Dockerfile b/Dockerfile index ba52460..94005e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,6 +30,7 @@ RUN \ curl \ jq \ libexpat1 \ + mosquitto-clients \ netcat-openbsd && \ echo "**** install foldingathome ****" && \ if [ "${BUILD_ARCH}" = "aarch64" ]; then \ diff --git a/config.yaml b/config.yaml index f119941..ca0463e 100644 --- a/config.yaml +++ b/config.yaml @@ -13,6 +13,8 @@ ports_description: 7396/tcp: Folding@home Web Interface map: - config:rw +services: + - mqtt:want options: user: "" team: "247478" diff --git a/root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/dependencies.d/svc-foldingathome b/root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/dependencies.d/svc-foldingathome new file mode 100644 index 0000000..e69de29 diff --git a/root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/run b/root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/run new file mode 100755 index 0000000..f750d15 --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/run @@ -0,0 +1,99 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash + +FAH_API="http://localhost:7396/api" +POLL_INTERVAL=30 +STATE_TOPIC="foldingathome/state" +DISCOVERY_PREFIX="homeassistant" + +# Abort cleanly if MQTT service is not available +if ! bashio::services.mqtt; then + bashio::log.info "MQTT not available — skipping sensor publishing. Install the Mosquitto broker add-on to enable HA sensors." + exec sleep infinity +fi + +MQTT_HOST=$(bashio::services.mqtt 'host') +MQTT_PORT=$(bashio::services.mqtt 'port') +MQTT_USER=$(bashio::services.mqtt 'username') +MQTT_PASS=$(bashio::services.mqtt 'password') +UNIQUE_ID="foldingathome_$(hostname | tr -cd '[:alnum:]_')" + +mqtt_publish() { + mosquitto_pub \ + -h "$MQTT_HOST" -p "$MQTT_PORT" \ + -u "$MQTT_USER" -P "$MQTT_PASS" \ + -t "$1" -m "$2" -r -q 1 +} + +publish_discovery() { + local device + device=$(jq -n \ + --arg id "$UNIQUE_ID" \ + '{identifiers: [$id], name: "Folding@home", model: "FAH 8", manufacturer: "foldingathome.org"}') + + declare -A sensors + sensors=( + ["status"]='{"name":"Folding@home Status","value_template":"{{ value_json.status }}","icon":"mdi:dna"}' + ["ppd"]='{"name":"Folding@home PPD","value_template":"{{ value_json.ppd }}","unit_of_measurement":"PPD","icon":"mdi:speedometer","state_class":"measurement"}' + ["progress"]='{"name":"Folding@home Progress","value_template":"{{ value_json.progress }}","unit_of_measurement":"%","icon":"mdi:progress-clock","state_class":"measurement"}' + ["credit"]='{"name":"Folding@home Credit","value_template":"{{ value_json.credit }}","unit_of_measurement":"points","icon":"mdi:star","state_class":"total_increasing"}' + ) + + for key in "${!sensors[@]}"; do + local config + config=$(echo "${sensors[$key]}" | jq \ + --arg uid "${UNIQUE_ID}_${key}" \ + --arg topic "$STATE_TOPIC" \ + --argjson device "$device" \ + '. + {unique_id: $uid, state_topic: $topic, device: $device}') + mqtt_publish "${DISCOVERY_PREFIX}/sensor/${UNIQUE_ID}_${key}/config" "$config" + done + + bashio::log.info "HA sensor discovery published (${#sensors[@]} sensors)" +} + +# Wait for FAH API to become ready +bashio::log.info "Waiting for Folding@home API on port 7396..." +until curl -sf "${FAH_API}/info" > /dev/null 2>&1; do + sleep 5 +done +bashio::log.info "Folding@home API ready — starting MQTT sensor publisher" + +publish_discovery + +while true; do + UNITS=$(curl -sf "${FAH_API}/units" 2>/dev/null || echo "[]") + UNIT_COUNT=$(echo "$UNITS" | jq 'length') + + if [ "$UNIT_COUNT" -gt 0 ]; then + RAW_STATE=$(echo "$UNITS" | jq -r '.[0].state // "unknown"') + case "$RAW_STATE" in + RUN) STATUS="Running" ;; + PAUSE) STATUS="Paused" ;; + FINISH) STATUS="Finishing" ;; + ASSIGN) STATUS="Assigning" ;; + DOWNLOAD) STATUS="Downloading" ;; + SEND) STATUS="Sending" ;; + *) STATUS="Idle" ;; + esac + PPD=$(echo "$UNITS" | jq '[.[].ppd // 0] | add // 0 | round') + PROGRESS=$(echo "$UNITS" | jq '.[0].progress // 0 | . * 100 | round') + CREDIT=$(echo "$UNITS" | jq '[.[].credit // 0] | add // 0 | round') + else + STATUS="Idle" + PPD=0 + PROGRESS=0 + CREDIT=0 + fi + + PAYLOAD=$(jq -n \ + --arg status "$STATUS" \ + --argjson ppd "$PPD" \ + --argjson progress "$PROGRESS" \ + --argjson credit "$CREDIT" \ + '{status: $status, ppd: $ppd, progress: $progress, credit: $credit}') + + mqtt_publish "$STATE_TOPIC" "$PAYLOAD" + + sleep "$POLL_INTERVAL" +done diff --git a/root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/type b/root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/type new file mode 100644 index 0000000..5883cff --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/type @@ -0,0 +1 @@ +longrun diff --git a/root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-foldingathome-mqtt b/root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-foldingathome-mqtt new file mode 100644 index 0000000..e69de29