Files
docker-voltronic-homeassistant/sources/inverter-mqtt/mqtt-push-parallel.sh
T
Pi Developer 078544381e
Build Docker Image for Raspberry Pi / build-and-push (push) Has been cancelled
refactor: Rimuove discovery, usa QPGS diretto in cascade mode
- mqtt-push-parallel.sh: riscritto da 659 a 230 righe
  * Rimosse tutte le meccaniche di serial discovery (VALID_SERIALS,
    VALID_QPGS, PARALLEL_DISCOVERY, DIRECT_SERIALS, ecc.)
  * Ora query direte QPGS0→inv1, QPGS1→inv2 senza discovery preliminare
  * Numero inverter configurabile via CASCADE_COUNT env var (default 2)
  * Fix PV_in_watts: quando DATA[25]=0 (batteria carica) e SCC_OK e
    no AC grid (line_loss=1), usa Load_watt come proxy di produzione PV
  * Aggiunto publish di PV_in_watthour e Load_watthour
  * Aggiunto parsing corretto dei status flags dal byte di stato QPGS

- entrypoint.sh: riscritto da 240 a 131 righe
  * Rimossa intera logica di auto-discovery buffer sizes
  * Rimossi FORCE_DISCOVERY, SKIP_DISCOVERY, DISCOVERY_FLAG
  * Rimossi run_discovery() e update_config_with_discovery()
  * Startup immediato senza attese/tentativi di discovery
  * CASCADE_COUNT env var propagata agli script

- healthcheck: corretto per processi effettivi del container
  * Prima: cercava mqtt-subscriber + mosquitto_sub + watch (3 proc)
  * watch non viene mai avviato → sempre unhealthy
  * Ora: controlla mosquitto_sub (subscriber) + sleep (push loop cicla)
2026-02-22 15:02:22 +01:00

231 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# MQTT Push for Parallel/Cascade Inverters
# Queries QPGS0 (inv1) and QPGS1 (inv2) directly - no serial discovery.
# Intended for inverters connected in cascade on the same RS232 bus.
# Detect environment (container vs development)
if [ -f "/etc/inverter/mqtt.json" ] && [ -x "/opt/inverter-cli/bin/inverter_poller" ]; then
MQTT_CONFIG="/etc/inverter/mqtt.json"
INVERTER_BIN="/opt/inverter-cli/bin/inverter_poller"
INVERTER_CONF="/etc/inverter/inverter.conf"
else
MQTT_CONFIG="/home/pi/Progetti/config/mqtt.json"
INVERTER_BIN="/home/pi/Progetti/sources/inverter-cli/bin/inverter_poller"
INVERTER_CONF="/home/pi/Progetti/config/inverter.conf"
fi
if ! command -v jq &> /dev/null; then
echo "ERROR: jq is not installed. Install with: sudo apt-get install jq"
exit 1
fi
MQTT_SERVER=`cat $MQTT_CONFIG | jq '.server' -r`
MQTT_PORT=`cat $MQTT_CONFIG | jq '.port' -r`
MQTT_TOPIC=`cat $MQTT_CONFIG | jq '.topic' -r`
MQTT_DEVICENAME=`cat $MQTT_CONFIG | jq '.devicename' -r`
MQTT_USERNAME=`cat $MQTT_CONFIG | jq '.username' -r`
MQTT_PASSWORD=`cat $MQTT_CONFIG | jq '.password' -r`
MQTT_CLIENTID=`cat $MQTT_CONFIG | jq '.clientid' -r`
INFLUX_ENABLED=`cat $MQTT_CONFIG | jq '.influx.enabled' -r`
# Number of cascade inverters (default 2, override with CASCADE_COUNT env var)
CASCADE_COUNT="${CASCADE_COUNT:-2}"
pushMQTTData () {
# $1 = inverter_id (1, 2, or "system"), $2 = metric, $3 = value
local inverter_id=$1
local metric=$2
local value=$3
mosquitto_pub \
-h $MQTT_SERVER \
-p $MQTT_PORT \
-u "$MQTT_USERNAME" \
-P "$MQTT_PASSWORD" \
-i $MQTT_CLIENTID \
-r \
-t "$MQTT_TOPIC/sensor/${MQTT_DEVICENAME}_inv${inverter_id}_${metric}" \
-m "$value"
if [[ $INFLUX_ENABLED == "true" ]]; then
pushInfluxData $inverter_id $metric $value
fi
}
pushInfluxData () {
INFLUX_HOST=`cat $MQTT_CONFIG | jq '.influx.host' -r`
INFLUX_USERNAME=`cat $MQTT_CONFIG | jq '.influx.username' -r`
INFLUX_PASSWORD=`cat $MQTT_CONFIG | jq '.influx.password' -r`
INFLUX_DEVICE=`cat $MQTT_CONFIG | jq '.influx.device' -r`
INFLUX_PREFIX=`cat $MQTT_CONFIG | jq '.influx.prefix' -r`
INFLUX_DATABASE=`cat $MQTT_CONFIG | jq '.influx.database' -r`
INFLUX_MEASUREMENT_NAME=`cat $MQTT_CONFIG | jq '.influx.namingMap.'$2'' -r`
curl -i -XPOST "$INFLUX_HOST/write?db=$INFLUX_DATABASE&precision=s" \
-u "$INFLUX_USERNAME:$INFLUX_PASSWORD" \
--data-binary "$INFLUX_PREFIX,device=${INFLUX_DEVICE}_inv${1} $INFLUX_MEASUREMENT_NAME=$3" \
> /dev/null 2>&1
}
# Process QPGS data and publish to MQTT for one inverter.
# Arguments: $1=inv_id, $2=QPGS_RAW string
# Returns 0 on success, 1 on failure.
processInverter () {
local inv_id=$1
local QPGS_RAW=$2
if [ -z "$QPGS_RAW" ] || [ "$QPGS_RAW" = "NAK" ]; then
echo " ✗ inv$inv_id: no data (NAK or empty)"
return 1
fi
IFS=' ' read -ra DATA <<< "$QPGS_RAW"
# QPGS field mapping (per protocol HS_MS_MSX_RS232_Protocol):
# 0=Exists 1=Serial 2=Mode 3=Fault
# 4=GridV 5=GridF 6=OutV 7=OutF
# 8=OutVA 9=OutW 10=LoadPct
# 11=BattV 12=BattChgA 13=BattCap
# 14=PVInputV 15=TotalChgA 16=TotalOutVA 17=TotalOutW 18=TotalOutPct
# 19=StatusByte(b7b6b5b4b3b2b1b0) 20=OutMode 21=ChgSourcePriority
# 22=MaxChgA 23=MaxChgRange 24=MaxAcChgA
# 25=PV_in_current 26=Batt_discharge_current
[ "${DATA[2]}" ] && pushMQTTData "$inv_id" "Inverter_mode" "${DATA[2]}"
[ "${DATA[4]}" ] && pushMQTTData "$inv_id" "AC_grid_voltage" "${DATA[4]}"
[ "${DATA[5]}" ] && pushMQTTData "$inv_id" "AC_grid_frequency" "${DATA[5]}"
[ "${DATA[6]}" ] && pushMQTTData "$inv_id" "AC_out_voltage" "${DATA[6]}"
[ "${DATA[7]}" ] && pushMQTTData "$inv_id" "AC_out_frequency" "${DATA[7]}"
[ "${DATA[8]}" ] && pushMQTTData "$inv_id" "Load_va" "${DATA[8]}"
[ "${DATA[9]}" ] && pushMQTTData "$inv_id" "Load_watt" "${DATA[9]}"
[ "${DATA[10]}" ] && pushMQTTData "$inv_id" "Load_pct" "${DATA[10]}"
[ "${DATA[11]}" ] && pushMQTTData "$inv_id" "Battery_voltage" "${DATA[11]}"
[ "${DATA[12]}" ] && pushMQTTData "$inv_id" "Battery_charge_current" "${DATA[12]}"
[ "${DATA[13]}" ] && pushMQTTData "$inv_id" "Battery_capacity" "${DATA[13]}"
[ "${DATA[14]}" ] && pushMQTTData "$inv_id" "PV_in_voltage" "${DATA[14]}"
[ "${DATA[25]}" ] && pushMQTTData "$inv_id" "PV_in_current" "${DATA[25]}"
[ "${DATA[26]}" ] && pushMQTTData "$inv_id" "Battery_discharge_current" "${DATA[26]}"
# ─── PV_in_watts calculation ──────────────────────────────────────────────
# DATA[25] = SCC output current at battery-bus voltage (not at PV panel V).
# Source: original QPIGS comment "current going out to battery at battery voltage".
# Formula: Battery_voltage × DATA[25] = power delivered by SCC to battery.
#
# Edge case - battery fully charged (DATA[25] = 0):
# If STATUS b7=1 (SCC_OK) and STATUS b2=1 (line_loss = no AC grid),
# solar is feeding the load directly. Use Load_watt as proxy.
# If AC grid is present (line_loss=0), solar just floats battery → 0W is correct.
local BATT_V="${DATA[11]:-0}"
local SCC_A="${DATA[25]:-0}"
local LOAD_W="${DATA[9]:-0}"
local STATUS="${DATA[19]:-00000000}"
# STATUS string b7b6b5b4b3b2b1b0 - each char is one bit:
# index 0=b7=SCC_OK, index 1=b6=AC_charge, index 2=b5=SCC_charge,
# index 5=b2=line_loss (1=no AC grid), index 6=b1=load_on
local SCC_OK="${STATUS:0:1}"
local LINE_LOSS="${STATUS:5:1}"
local PV_WATTS
if awk -v v="$SCC_A" 'BEGIN{exit !(v+0 > 0)}' 2>/dev/null; then
# SCC delivering current to battery
PV_WATTS=$(echo "$BATT_V $SCC_A" | awk '{printf "%.1f", $1 * $2}')
elif [ "$SCC_OK" = "1" ] && [ "$LINE_LOSS" = "1" ]; then
# SCC OK, battery full, no AC grid → solar feeds load
PV_WATTS="$LOAD_W"
else
PV_WATTS="0.0"
fi
pushMQTTData "$inv_id" "PV_in_watts" "$PV_WATTS"
# Watt-hours (approximated from polling interval = 30s = 1/120 hour)
local PV_WH=$(echo "$PV_WATTS" | awk '{printf "%.4f", $1 / 120}')
pushMQTTData "$inv_id" "PV_in_watthour" "$PV_WH"
local LOAD_WH=$(echo "$LOAD_W" | awk '{printf "%.4f", $1 / 120}')
pushMQTTData "$inv_id" "Load_watthour" "$LOAD_WH"
# Status flags from STATUS byte
if [ ${#STATUS} -ge 8 ]; then
pushMQTTData "$inv_id" "SCC_charge_on" "${STATUS:2:1}" # b5
pushMQTTData "$inv_id" "AC_charge_on" "${STATUS:1:1}" # b6
pushMQTTData "$inv_id" "Load_status_on" "${STATUS:6:1}" # b1
fi
echo " ✓ inv$inv_id: OK (PV_watts=${PV_WATTS}W, Load=${LOAD_W}W)"
return 0
}
# ─── Main ─────────────────────────────────────────────────────────────────────
SUDO_CMD=""
if [ "$EUID" -ne 0 ]; then
SUDO_CMD="sudo"
fi
echo "=== Cascade Inverter MQTT Push ==="
echo "Inverter count: $CASCADE_COUNT"
# ── Step 1: QPIRI (shared config, same for all cascade inverters) ─────────────
QPIRI_RAW=""
for attempt in 1 2 3; do
QPIRI_RAW=`$SUDO_CMD "$INVERTER_BIN" -r "QPIRI" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs`
[ ! -z "$QPIRI_RAW" ] && [ "$QPIRI_RAW" != "NAK" ] && break
sleep 1
done
BATT_RECHARGE="" BATT_UNDER="" BATT_BULK="" BATT_FLOAT=""
MAX_CHARGE_CURRENT="" MAX_GRID_CHARGE="" OUT_SOURCE_PRIORITY=""
CHARGER_SOURCE_PRIORITY="" BATT_REDISCHARGE=""
if [ ! -z "$QPIRI_RAW" ] && [ "$QPIRI_RAW" != "NAK" ]; then
echo "✓ QPIRI retrieved"
IFS=' ' read -ra QPIRI <<< "$QPIRI_RAW"
BATT_RECHARGE="${QPIRI[8]}"
BATT_UNDER="${QPIRI[9]}"
BATT_BULK="${QPIRI[10]}"
BATT_FLOAT="${QPIRI[11]}"
MAX_CHARGE_CURRENT="${QPIRI[13]}"
MAX_GRID_CHARGE="${QPIRI[14]}"
OUT_SOURCE_PRIORITY="${QPIRI[15]}"
CHARGER_SOURCE_PRIORITY="${QPIRI[16]}"
BATT_REDISCHARGE="${QPIRI[22]}"
else
echo "⚠ QPIRI failed - config parameters unavailable"
fi
# ── Step 2: QPGS per each inverter ───────────────────────────────────────────
SUCCESS_IDS=()
for inv_id in $(seq 1 $CASCADE_COUNT); do
qpgs_idx=$((inv_id - 1))
echo "Querying QPGS${qpgs_idx} → inverter #${inv_id}..."
QPGS_RAW=""
for attempt in 1 2 3; do
QPGS_RAW=`$SUDO_CMD "$INVERTER_BIN" -r "QPGS${qpgs_idx}" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs`
[ ! -z "$QPGS_RAW" ] && [ "$QPGS_RAW" != "NAK" ] && break
sleep 0.5
done
if processInverter "$inv_id" "$QPGS_RAW"; then
# Publish shared QPIRI config for this inverter
[ ! -z "$BATT_RECHARGE" ] && pushMQTTData "$inv_id" "Battery_recharge_voltage" "$BATT_RECHARGE"
[ ! -z "$BATT_UNDER" ] && pushMQTTData "$inv_id" "Battery_under_voltage" "$BATT_UNDER"
[ ! -z "$BATT_BULK" ] && pushMQTTData "$inv_id" "Battery_bulk_voltage" "$BATT_BULK"
[ ! -z "$BATT_FLOAT" ] && pushMQTTData "$inv_id" "Battery_float_voltage" "$BATT_FLOAT"
[ ! -z "$MAX_CHARGE_CURRENT" ] && pushMQTTData "$inv_id" "Max_charge_current" "$MAX_CHARGE_CURRENT"
[ ! -z "$MAX_GRID_CHARGE" ] && pushMQTTData "$inv_id" "Max_grid_charge_current" "$MAX_GRID_CHARGE"
[ ! -z "$OUT_SOURCE_PRIORITY" ] && pushMQTTData "$inv_id" "Out_source_priority" "$OUT_SOURCE_PRIORITY"
[ ! -z "$CHARGER_SOURCE_PRIORITY" ] && pushMQTTData "$inv_id" "Charger_source_priority" "$CHARGER_SOURCE_PRIORITY"
[ ! -z "$BATT_REDISCHARGE" ] && pushMQTTData "$inv_id" "Battery_redischarge_voltage" "$BATT_REDISCHARGE"
SUCCESS_IDS+=("$inv_id")
fi
done
# Publish overall active inverter count
pushMQTTData "system" "parallel_count" "${#SUCCESS_IDS[@]}"
echo "Push completed: ${#SUCCESS_IDS[@]}/$CASCADE_COUNT inverters OK"