#!/bin/bash # MQTT Push for Parallel Inverters # Discovers parallel inverters and publishes data for each one separately # Detect environment (container vs development) if [ -f "/etc/inverter/mqtt.json" ] && [ -x "/opt/inverter-cli/bin/inverter_poller" ]; then # Container mode MQTT_CONFIG="/etc/inverter/mqtt.json" INVERTER_BIN="/opt/inverter-cli/bin/inverter_poller" MQTT_FALLBACK="/opt/inverter-mqtt/mqtt-push.sh" INVERTER_CONF="/etc/inverter/inverter.conf" CONTAINER_MODE=true else # Development mode MQTT_CONFIG="/home/pi/Progetti/config/mqtt.json" INVERTER_BIN="/home/pi/Progetti/sources/inverter-cli/bin/inverter_poller" MQTT_FALLBACK="/home/pi/Progetti/sources/inverter-mqtt/mqtt-push.sh" INVERTER_CONF="/home/pi/Progetti/config/inverter.conf" CONTAINER_MODE=false fi echo "Mode: $([ "$CONTAINER_MODE" = true ] && echo "Container" || echo "Development")" echo "Using binary: $INVERTER_BIN" # Check if jq is installed if ! command -v jq &> /dev/null; then echo "ERROR: jq is not installed. Install it 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` pushMQTTData () { # $1 = inverter_id, $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 } # Extract all 33 parameters from JSON (like original mqtt-push.sh) extractAndPublishAllData () { local inv_id=$1 local json_data=$2 # Extract all 33 parameters exactly like original script Inverter_mode=`echo "$json_data" | jq '.Inverter_mode' -r` [ ! -z "$Inverter_mode" ] && [ "$Inverter_mode" != "null" ] && pushMQTTData "$inv_id" "Inverter_mode" "$Inverter_mode" AC_grid_voltage=`echo "$json_data" | jq '.AC_grid_voltage' -r` [ ! -z "$AC_grid_voltage" ] && [ "$AC_grid_voltage" != "null" ] && pushMQTTData "$inv_id" "AC_grid_voltage" "$AC_grid_voltage" AC_grid_frequency=`echo "$json_data" | jq '.AC_grid_frequency' -r` [ ! -z "$AC_grid_frequency" ] && [ "$AC_grid_frequency" != "null" ] && pushMQTTData "$inv_id" "AC_grid_frequency" "$AC_grid_frequency" AC_out_voltage=`echo "$json_data" | jq '.AC_out_voltage' -r` [ ! -z "$AC_out_voltage" ] && [ "$AC_out_voltage" != "null" ] && pushMQTTData "$inv_id" "AC_out_voltage" "$AC_out_voltage" AC_out_frequency=`echo "$json_data" | jq '.AC_out_frequency' -r` [ ! -z "$AC_out_frequency" ] && [ "$AC_out_frequency" != "null" ] && pushMQTTData "$inv_id" "AC_out_frequency" "$AC_out_frequency" PV_in_voltage=`echo "$json_data" | jq '.PV_in_voltage' -r` [ ! -z "$PV_in_voltage" ] && [ "$PV_in_voltage" != "null" ] && pushMQTTData "$inv_id" "PV_in_voltage" "$PV_in_voltage" PV_in_current=`echo "$json_data" | jq '.PV_in_current' -r` [ ! -z "$PV_in_current" ] && [ "$PV_in_current" != "null" ] && pushMQTTData "$inv_id" "PV_in_current" "$PV_in_current" PV_in_watts=`echo "$json_data" | jq '.PV_in_watts' -r` [ ! -z "$PV_in_watts" ] && [ "$PV_in_watts" != "null" ] && pushMQTTData "$inv_id" "PV_in_watts" "$PV_in_watts" PV_in_watthour=`echo "$json_data" | jq '.PV_in_watthour' -r` [ ! -z "$PV_in_watthour" ] && [ "$PV_in_watthour" != "null" ] && pushMQTTData "$inv_id" "PV_in_watthour" "$PV_in_watthour" SCC_voltage=`echo "$json_data" | jq '.SCC_voltage' -r` [ ! -z "$SCC_voltage" ] && [ "$SCC_voltage" != "null" ] && pushMQTTData "$inv_id" "SCC_voltage" "$SCC_voltage" Load_pct=`echo "$json_data" | jq '.Load_pct' -r` [ ! -z "$Load_pct" ] && [ "$Load_pct" != "null" ] && pushMQTTData "$inv_id" "Load_pct" "$Load_pct" Load_watt=`echo "$json_data" | jq '.Load_watt' -r` [ ! -z "$Load_watt" ] && [ "$Load_watt" != "null" ] && pushMQTTData "$inv_id" "Load_watt" "$Load_watt" Load_watthour=`echo "$json_data" | jq '.Load_watthour' -r` [ ! -z "$Load_watthour" ] && [ "$Load_watthour" != "null" ] && pushMQTTData "$inv_id" "Load_watthour" "$Load_watthour" Load_va=`echo "$json_data" | jq '.Load_va' -r` [ ! -z "$Load_va" ] && [ "$Load_va" != "null" ] && pushMQTTData "$inv_id" "Load_va" "$Load_va" Bus_voltage=`echo "$json_data" | jq '.Bus_voltage' -r` [ ! -z "$Bus_voltage" ] && [ "$Bus_voltage" != "null" ] && pushMQTTData "$inv_id" "Bus_voltage" "$Bus_voltage" Heatsink_temperature=`echo "$json_data" | jq '.Heatsink_temperature' -r` [ ! -z "$Heatsink_temperature" ] && [ "$Heatsink_temperature" != "null" ] && pushMQTTData "$inv_id" "Heatsink_temperature" "$Heatsink_temperature" Battery_capacity=`echo "$json_data" | jq '.Battery_capacity' -r` [ ! -z "$Battery_capacity" ] && [ "$Battery_capacity" != "null" ] && pushMQTTData "$inv_id" "Battery_capacity" "$Battery_capacity" Battery_voltage=`echo "$json_data" | jq '.Battery_voltage' -r` [ ! -z "$Battery_voltage" ] && [ "$Battery_voltage" != "null" ] && pushMQTTData "$inv_id" "Battery_voltage" "$Battery_voltage" Battery_charge_current=`echo "$json_data" | jq '.Battery_charge_current' -r` [ ! -z "$Battery_charge_current" ] && [ "$Battery_charge_current" != "null" ] && pushMQTTData "$inv_id" "Battery_charge_current" "$Battery_charge_current" Battery_discharge_current=`echo "$json_data" | jq '.Battery_discharge_current' -r` [ ! -z "$Battery_discharge_current" ] && [ "$Battery_discharge_current" != "null" ] && pushMQTTData "$inv_id" "Battery_discharge_current" "$Battery_discharge_current" Load_status_on=`echo "$json_data" | jq '.Load_status_on' -r` [ ! -z "$Load_status_on" ] && [ "$Load_status_on" != "null" ] && pushMQTTData "$inv_id" "Load_status_on" "$Load_status_on" SCC_charge_on=`echo "$json_data" | jq '.SCC_charge_on' -r` [ ! -z "$SCC_charge_on" ] && [ "$SCC_charge_on" != "null" ] && pushMQTTData "$inv_id" "SCC_charge_on" "$SCC_charge_on" AC_charge_on=`echo "$json_data" | jq '.AC_charge_on' -r` [ ! -z "$AC_charge_on" ] && [ "$AC_charge_on" != "null" ] && pushMQTTData "$inv_id" "AC_charge_on" "$AC_charge_on" Battery_recharge_voltage=`echo "$json_data" | jq '.Battery_recharge_voltage' -r` [ ! -z "$Battery_recharge_voltage" ] && [ "$Battery_recharge_voltage" != "null" ] && pushMQTTData "$inv_id" "Battery_recharge_voltage" "$Battery_recharge_voltage" Battery_under_voltage=`echo "$json_data" | jq '.Battery_under_voltage' -r` [ ! -z "$Battery_under_voltage" ] && [ "$Battery_under_voltage" != "null" ] && pushMQTTData "$inv_id" "Battery_under_voltage" "$Battery_under_voltage" Battery_bulk_voltage=`echo "$json_data" | jq '.Battery_bulk_voltage' -r` [ ! -z "$Battery_bulk_voltage" ] && [ "$Battery_bulk_voltage" != "null" ] && pushMQTTData "$inv_id" "Battery_bulk_voltage" "$Battery_bulk_voltage" Battery_float_voltage=`echo "$json_data" | jq '.Battery_float_voltage' -r` [ ! -z "$Battery_float_voltage" ] && [ "$Battery_float_voltage" != "null" ] && pushMQTTData "$inv_id" "Battery_float_voltage" "$Battery_float_voltage" Max_grid_charge_current=`echo "$json_data" | jq '.Max_grid_charge_current' -r` [ ! -z "$Max_grid_charge_current" ] && [ "$Max_grid_charge_current" != "null" ] && pushMQTTData "$inv_id" "Max_grid_charge_current" "$Max_grid_charge_current" Max_charge_current=`echo "$json_data" | jq '.Max_charge_current' -r` [ ! -z "$Max_charge_current" ] && [ "$Max_charge_current" != "null" ] && pushMQTTData "$inv_id" "Max_charge_current" "$Max_charge_current" Out_source_priority=`echo "$json_data" | jq '.Out_source_priority' -r` [ ! -z "$Out_source_priority" ] && [ "$Out_source_priority" != "null" ] && pushMQTTData "$inv_id" "Out_source_priority" "$Out_source_priority" Charger_source_priority=`echo "$json_data" | jq '.Charger_source_priority' -r` [ ! -z "$Charger_source_priority" ] && [ "$Charger_source_priority" != "null" ] && pushMQTTData "$inv_id" "Charger_source_priority" "$Charger_source_priority" Battery_redischarge_voltage=`echo "$json_data" | jq '.Battery_redischarge_voltage' -r` [ ! -z "$Battery_redischarge_voltage" ] && [ "$Battery_redischarge_voltage" != "null" ] && pushMQTTData "$inv_id" "Battery_redischarge_voltage" "$Battery_redischarge_voltage" Warnings=`echo "$json_data" | jq '.Warnings' -r` [ ! -z "$Warnings" ] && [ "$Warnings" != "null" ] && pushMQTTData "$inv_id" "Warnings" "$Warnings" } # Discover parallel inverters with retry logic SUDO_CMD="" if [ "$EUID" -ne 0 ] && [ -c "/dev/ttyUSB0" ]; then SUDO_CMD="sudo" fi # Multi-device mode: if INVERTER_DEVICES is set, poll each device separately if [ -n "$INVERTER_DEVICES" ]; then IFS=':' read -ra DEVICE_ARRAY <<< "$INVERTER_DEVICES" if [ ${#DEVICE_ARRAY[@]} -gt 0 ]; then echo "Multi-device mode enabled (${#DEVICE_ARRAY[@]} devices)" for idx in "${!DEVICE_ARRAY[@]}"; do inv_id=$((idx + 1)) device_path="${DEVICE_ARRAY[$idx]}" temp_dir=$(mktemp -d) cp "$INVERTER_CONF" "$temp_dir/inverter.conf" 2>/dev/null sed -i "s|^device=.*|device=$device_path|g" "$temp_dir/inverter.conf" INVERTER_DATA=$(cd "$temp_dir" && $SUDO_CMD "$INVERTER_BIN" -1 2>/dev/null | tr -d '\r') rm -rf "$temp_dir" if [ -z "$INVERTER_DATA" ] || ! echo "$INVERTER_DATA" | jq -e . >/dev/null 2>&1; then echo "⚠ No valid JSON from $device_path (inv$inv_id)" continue fi extractAndPublishAllData "$inv_id" "$INVERTER_DATA" done echo "Multi-device MQTT push completed" exit 0 fi fi # Try parallel discovery with retry MAX_RETRIES=3 RETRY_DELAY=2 PARALLEL_COUNT=0 for attempt in $(seq 1 $MAX_RETRIES); do echo "Parallel discovery attempt $attempt/$MAX_RETRIES..." PARALLEL_DISCOVERY=`$SUDO_CMD "$INVERTER_BIN" -p 2>&1` PARALLEL_COUNT=`echo "$PARALLEL_DISCOVERY" | grep "PARALLEL_COUNT=" | cut -d= -f2` if [ ! -z "$PARALLEL_COUNT" ] && [ "$PARALLEL_COUNT" -gt 0 ]; then echo "✓ Found $PARALLEL_COUNT inverters" break else echo "⚠ Discovery failed (count=$PARALLEL_COUNT)" if [ $attempt -lt $MAX_RETRIES ]; then echo " Waiting ${RETRY_DELAY}s before retry..." sleep $RETRY_DELAY fi fi done # If still no count or discovery failed, try direct QPGS commands if [ -z "$PARALLEL_COUNT" ] || [ "$PARALLEL_COUNT" -eq 0 ]; then echo "⚠ Discovery reports 0 inverters, trying direct QPGS0/QPGS1 commands..." for attempt in 1 2 3; do # Test QPGS0 to see if parallel mode is active TEST_QPGS0=`$SUDO_CMD "$INVERTER_BIN" -r "QPGS0" 2>&1 | grep "Reply:"` TEST_QPGS1=`$SUDO_CMD "$INVERTER_BIN" -r "QPGS1" 2>&1 | grep "Reply:"` if [ ! -z "$TEST_QPGS0" ] && [[ "$TEST_QPGS0" != *"NAK"* ]]; then echo "✓ QPGS0 responds, assuming 2 inverters in cascade" PARALLEL_COUNT=2 # Build fake discovery output PARALLEL_DISCOVERY="INVERTER_1_SERIAL=unknown INVERTER_1_QPGS=0 INVERTER_2_SERIAL=unknown INVERTER_2_QPGS=1" break elif [ ! -z "$TEST_QPGS1" ] && [[ "$TEST_QPGS1" != *"NAK"* ]]; then echo "✓ QPGS1 responds, assuming 2 inverters in cascade" PARALLEL_COUNT=2 # Build fake discovery output PARALLEL_DISCOVERY="INVERTER_1_SERIAL=unknown INVERTER_1_QPGS=0 INVERTER_2_SERIAL=unknown INVERTER_2_QPGS=1" break else echo "⚠ QPGS commands failed (attempt $attempt/3)" sleep 1 fi done if [ "$PARALLEL_COUNT" -eq 0 ]; then echo "✗ QPGS commands also failed" fi fi DISCOVERY_SERIALS=() DISCOVERY_QPGS=() MAX_QPGS_IDX=-1 for i in $(seq 1 $PARALLEL_COUNT); do serial=$(echo "$PARALLEL_DISCOVERY" | grep "INVERTER_${i}_SERIAL=" | cut -d= -f2) qpgs_idx=$(echo "$PARALLEL_DISCOVERY" | grep "INVERTER_${i}_QPGS=" | cut -d= -f2) if [ -z "$serial" ] || [ "$serial" = "0.0" ]; then continue fi if echo "$serial" | grep -qE '^0+$'; then continue fi if [ -z "$qpgs_idx" ]; then continue fi DISCOVERY_SERIALS+=("$serial") DISCOVERY_QPGS+=("$qpgs_idx") if [ "$qpgs_idx" -gt "$MAX_QPGS_IDX" ]; then MAX_QPGS_IDX=$qpgs_idx fi done # Get QPIRI data once (shared configuration for all inverters in cascade) echo "" echo "Getting shared configuration (QPIRI)..." QPIRI_RAW="" for attempt in 1 2 3 4 5; do QPIRI_RAW=`$SUDO_CMD "$INVERTER_BIN" -r "QPIRI" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs` if [ ! -z "$QPIRI_RAW" ] && [ "$QPIRI_RAW" != "NAK" ]; then break fi sleep 1 done QPIRI_SUCCESS=false if [ ! -z "$QPIRI_RAW" ] && [ "$QPIRI_RAW" != "NAK" ]; then echo "✓ QPIRI retrieved successfully" QPIRI_SUCCESS=true # Parse QPIRI: Grid rating, Grid rating, AC output rating, AC output frequency, # AC output current, AC output apparent power, AC output active power, # Battery rating, Battery recharge, Battery under, Battery bulk, Battery float, # Battery type, Max charge current, Max AC charge current, Input voltage range, # Output source priority, Charger source priority, Parallel max num, Machine type, # Topology, Output mode, Battery redischarge, PV OK, PV power balance IFS=' ' read -ra QPIRI <<< "$QPIRI_RAW" # Extract configuration values (same for all inverters) 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, configuration parameters unavailable" fi # Extract runtime data for each inverter using QPGS (single session) PARALLEL_SUCCESS=false PARALLEL_QPGS_COUNT=$PARALLEL_COUNT if [ "$PARALLEL_QPGS_COUNT" -lt 2 ]; then PARALLEL_QPGS_COUNT=2 fi if [ "$MAX_QPGS_IDX" -ge 0 ] && [ $((MAX_QPGS_IDX + 1)) -gt "$PARALLEL_QPGS_COUNT" ]; then PARALLEL_QPGS_COUNT=$((MAX_QPGS_IDX + 1)) fi PARALLEL_QPGS_OUTPUT=`$SUDO_CMD "$INVERTER_BIN" -P "$PARALLEL_QPGS_COUNT" 2>/dev/null` HAS_PARALLEL_QPGS=false if echo "$PARALLEL_QPGS_OUTPUT" | grep -q "^QPGS[0-9]_REPLY="; then HAS_PARALLEL_QPGS=true PARALLEL_QPGS_OUTPUT=`$SUDO_CMD "$INVERTER_BIN" -P "$PARALLEL_QPGS_COUNT" 2>/dev/null` fi VALID_SERIALS=() VALID_QPGS=() DIRECT_SERIALS=() DIRECT_QPGS=() if [ "$PARALLEL_COUNT" -lt 2 ]; then for idx in 0 1; do reply_value=`$SUDO_CMD "$INVERTER_BIN" -r "QPGS$idx" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs` if [ -z "$reply_value" ]; then continue fi serial_from_reply=`echo "$reply_value" | awk '{print $2}'` if [ -z "$serial_from_reply" ] || [ "$serial_from_reply" = "0.0" ]; then continue fi if echo "$serial_from_reply" | grep -qE '^0+$'; then continue fi if ! echo "$serial_from_reply" | grep -qE '^[0-9]{10,}$'; then continue fi duplicate=false for existing in "${DIRECT_SERIALS[@]}"; do if [ "$existing" = "$serial_from_reply" ]; then duplicate=true break fi done if [ "$duplicate" = true ]; then continue fi DIRECT_SERIALS+=("$serial_from_reply") DIRECT_QPGS+=("$idx") done fi if [ ${#DIRECT_SERIALS[@]} -gt 0 ]; then VALID_SERIALS=("${DIRECT_SERIALS[@]}") VALID_QPGS=("${DIRECT_QPGS[@]}") fi if [ ${#VALID_SERIALS[@]} -eq 0 ] && [ "$HAS_PARALLEL_QPGS" = true ]; then for attempt in 1 2 3; do VALID_SERIALS=() VALID_QPGS=() for idx in $(seq 0 $((PARALLEL_QPGS_COUNT - 1))); do reply_line=`echo "$PARALLEL_QPGS_OUTPUT" | grep "^QPGS${idx}_REPLY="` reply_value=`echo "$reply_line" | cut -d= -f2- | xargs` if [ -z "$reply_value" ]; then continue fi serial_from_reply=`echo "$reply_value" | awk '{print $2}'` if [ -z "$serial_from_reply" ] || [ "$serial_from_reply" = "0.0" ]; then continue fi if echo "$serial_from_reply" | grep -qE '^0+$'; then continue fi if ! echo "$serial_from_reply" | grep -qE '^[0-9]{10,}$'; then continue fi duplicate=false for existing in "${VALID_SERIALS[@]}"; do if [ "$existing" = "$serial_from_reply" ]; then duplicate=true break fi done if [ "$duplicate" = true ]; then continue fi VALID_SERIALS+=("$serial_from_reply") VALID_QPGS+=("$idx") done if [ ${#VALID_SERIALS[@]} -ge 2 ]; then break fi PARALLEL_QPGS_OUTPUT=`$SUDO_CMD "$INVERTER_BIN" -P "$PARALLEL_QPGS_COUNT" 2>/dev/null` sleep 1 done fi if [ ${#VALID_SERIALS[@]} -eq 0 ]; then VALID_SERIALS=("${DISCOVERY_SERIALS[@]}") VALID_QPGS=("${DISCOVERY_QPGS[@]}") fi VALID_COUNT=${#VALID_SERIALS[@]} SUCCESS_INV_IDS=() if [ "$VALID_COUNT" -eq 0 ]; then echo "⚠ No valid inverter serials found (excluding 0.0/all-zero)" else echo "Processing $VALID_COUNT valid parallel inverters" fi # Publish discovery info (valid inverters only) pushMQTTData "system" "parallel_count" "$VALID_COUNT" for idx in "${!VALID_SERIALS[@]}"; do inv_id=$((idx + 1)) QPGS_IDX="${VALID_QPGS[$idx]}" SERIAL="${VALID_SERIALS[$idx]}" echo "" echo "Processing Inverter #$inv_id (Serial: $SERIAL, QPGS$QPGS_IDX)" # Get QPGS data from single-session output, fallback to direct query if [ "$HAS_PARALLEL_QPGS" = true ]; then QPGS_RAW=`echo "$PARALLEL_QPGS_OUTPUT" | grep "^QPGS${QPGS_IDX}_REPLY=" | cut -d= -f2- | xargs` fi if [ -z "$QPGS_RAW" ]; then QPGS_RAW=`$SUDO_CMD "$INVERTER_BIN" -r "QPGS$QPGS_IDX" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs` fi if [ ! -z "$QPGS_RAW" ] && [ "$QPGS_RAW" != "NAK" ]; then echo " ✓ QPGS$QPGS_IDX successful" PARALLEL_SUCCESS=true # Parse QPGS response IFS=' ' read -ra DATA <<< "$QPGS_RAW" # Field mapping (verified from test output): # 0=Exists, 1=Serial, 2=Mode, 3=Status, 4=GridV, 5=GridF, 6=OutV, 7=OutF # 8=VA, 9=W, 10=PCT, 11=BattV, 12=ChgA, 13=Cap, 14=PVV, 15=PVA # 16=?, 17=?, 18=?, 19-26=additional fields # Prefer serial from QPGS payload if valid serial_from_data="${DATA[1]}" if echo "$serial_from_data" | grep -qE '^[0-9]{10,}$'; then SERIAL="$serial_from_data" fi # Publish serial pushMQTTData "$inv_id" "serial" "$SERIAL" # Runtime data from QPGS (19 parameters) [ "${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[15]}" ] && pushMQTTData "$inv_id" "PV_in_current" "${DATA[15]}" # Calculate PV watts (V * A) if [ ! -z "${DATA[14]}" ] && [ ! -z "${DATA[15]}" ]; then PV_WATTS=`echo "${DATA[14]} ${DATA[15]}" | awk '{printf "%.1f", $1 * $2}'` pushMQTTData "$inv_id" "PV_in_watts" "$PV_WATTS" fi # Status flags (parse from field 18 bitmap if available) [ "${DATA[18]}" ] && { BITMAP="${DATA[18]}" if [[ "$BITMAP" =~ ^[0-9]+$ ]]; then BITMAP=$((10#$BITMAP)) # Binary flags: bit0=Load_on, bit1=SCC_on, bit2=AC_charge_on LOAD_ON=$((($BITMAP & 1) ? 1 : 0)) SCC_ON=$((($BITMAP & 2) ? 1 : 0)) AC_CHG_ON=$((($BITMAP & 4) ? 1 : 0)) pushMQTTData "$inv_id" "Load_status_on" "$LOAD_ON" pushMQTTData "$inv_id" "SCC_charge_on" "$SCC_ON" pushMQTTData "$inv_id" "AC_charge_on" "$AC_CHG_ON" fi } echo " ✓ Published 17 runtime parameters for inverter #$inv_id" echo " (QPGS data + calculated PV_watts + status flags)" echo " ⚠ Missing 5 params: PV_watthour, Load_watthour, Bus_voltage, Heatsink_temp, Warnings" SUCCESS_INV_IDS+=("$inv_id") else echo " ✗ QPGS$QPGS_IDX failed (NAK or empty)" fi done # Publish shared configuration parameters for ALL inverters (replicate QPIRI data) if [ "$QPIRI_SUCCESS" = true ] && [ ${#SUCCESS_INV_IDS[@]} -gt 0 ]; then echo "" echo "Publishing shared configuration to all inverters..." for inv_id in "${SUCCESS_INV_IDS[@]}"; do echo " Replicating QPIRI config to inverter #$inv_id..." [ ! -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" echo " ✓ Published 9 shared config parameters to inv$inv_id" done fi # Retry shared config once after QPGS if needed if [ "$QPIRI_SUCCESS" = false ] && [ ${#SUCCESS_INV_IDS[@]} -gt 0 ]; then echo "" echo "Retrying shared configuration (QPIRI) after QPGS..." for attempt in 1 2 3; do QPIRI_RAW=`$SUDO_CMD "$INVERTER_BIN" -r "QPIRI" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs` if [ ! -z "$QPIRI_RAW" ] && [ "$QPIRI_RAW" != "NAK" ]; then QPIRI_SUCCESS=true break fi sleep 1 done if [ "$QPIRI_SUCCESS" = true ]; then 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]}" echo "Publishing shared configuration to all inverters..." for inv_id in "${SUCCESS_INV_IDS[@]}"; do echo " Replicating QPIRI config to inverter #$inv_id..." [ ! -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" echo " ✓ Published 9 shared config parameters to inv$inv_id" done else echo "⚠ QPIRI retry failed, shared configuration not published" fi fi # Fallback: use standard mode with full JSON output if [ "$PARALLEL_SUCCESS" = false ]; then echo "" echo "⚠ Parallel mode failed completely (QPGS not responding)" echo " Possible reasons:" echo " - Inverters not configured in parallel mode in firmware" echo " - RS232 connection issue" echo " - Inverters in standby or fault mode" echo "" echo " Falling back to standard single-inverter mode (QPIGS)..." echo " Will publish data as 'inv1' (master inverter or aggregated data)" # Use standard inverter_poller -1 (QPIGS+QPIRI+QMOD+QPIWS) WITHOUT debug flag INVERTER_DATA=`timeout 10 $SUDO_CMD "$INVERTER_BIN" -1 2>/dev/null` if [ ! -z "$INVERTER_DATA" ]; then # Check if it's valid JSON echo "$INVERTER_DATA" | jq . > /dev/null 2>&1 if [ $? -eq 0 ]; then echo " ✓ Standard mode data retrieved successfully" extractAndPublishAllData "1" "$INVERTER_DATA" echo " ✓ Published all 33 parameters for inverter #1" echo "" echo " NOTE: If you have 2 inverters in cascade:" echo " - Enable parallel mode in inverter firmware settings" echo " - OR connect second inverter to separate USB port" echo " - Check manual for 'Machine in Parallel' configuration" else echo " ✗ Invalid JSON data from inverter_poller" echo " Raw output: $INVERTER_DATA" fi else echo " ✗ No data from inverter (timeout or device error)" fi fi echo "Parallel MQTT push completed"