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 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ RUN \
|
||||
curl \
|
||||
jq \
|
||||
libexpat1 \
|
||||
mosquitto-clients \
|
||||
netcat-openbsd && \
|
||||
echo "**** install foldingathome ****" && \
|
||||
if [ "${BUILD_ARCH}" = "aarch64" ]; then \
|
||||
|
||||
@@ -13,6 +13,8 @@ ports_description:
|
||||
7396/tcp: Folding@home Web Interface
|
||||
map:
|
||||
- config:rw
|
||||
services:
|
||||
- mqtt:want
|
||||
options:
|
||||
user: ""
|
||||
team: "247478"
|
||||
|
||||
99
root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/run
Executable file
99
root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/run
Executable file
@@ -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
|
||||
1
root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/type
Normal file
1
root/etc/s6-overlay/s6-rc.d/svc-foldingathome-mqtt/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
Reference in New Issue
Block a user