refactor: Rimuove discovery, usa QPGS diretto in cascade mode
Build Docker Image for Raspberry Pi / build-and-push (push) Has been cancelled
Build Docker Image for Raspberry Pi / build-and-push (push) Has been cancelled
- 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)
This commit is contained in:
+6
-2
@@ -1,8 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
PROC=`ps cax | grep -E "mqtt-subscriber|mosquitto_sub|watch" | awk '{print $5}' | sort -u | wc -l`
|
# Check that the MQTT subscriber (mosquitto_sub) and the main push loop (sleep 30)
|
||||||
|
# are both running. The subscriber handles incoming commands from Home Assistant,
|
||||||
|
# the sleep 30 indicates the push loop is actively cycling.
|
||||||
|
SUBSCRIBER=`ps ax | grep "mosquitto_sub" | grep -v grep | wc -l`
|
||||||
|
LOOP=`ps ax | grep "sleep 30" | grep -v grep | wc -l`
|
||||||
|
|
||||||
if [ "$PROC" -eq "3" ] ; then
|
if [ "$SUBSCRIBER" -ge "1" ] && [ "$LOOP" -ge "1" ] ; then
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
exit 99
|
exit 99
|
||||||
|
|||||||
@@ -2,20 +2,27 @@
|
|||||||
export TERM=xterm
|
export TERM=xterm
|
||||||
|
|
||||||
echo "=== Voltronic MQTT Bridge Starting ==="
|
echo "=== Voltronic MQTT Bridge Starting ==="
|
||||||
echo "Version: 2.0 with Auto-Discovery"
|
echo "Version: 2.0 - Cascade Mode"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Configuration paths
|
# Configuration paths
|
||||||
CONF_FILE="/etc/inverter/inverter.conf"
|
CONF_FILE="/etc/inverter/inverter.conf"
|
||||||
MQTT_CONF="/etc/inverter/mqtt.json"
|
MQTT_CONF="/etc/inverter/mqtt.json"
|
||||||
DISCOVERY_FLAG="/etc/inverter/.discovery_done"
|
|
||||||
TEMP_CONF="/tmp/inverter_discovered.conf"
|
|
||||||
|
|
||||||
# Ensure config files exist (copy defaults if missing due to empty volume mount)
|
# Environment variables with defaults
|
||||||
|
INVERTER_DEVICE="${INVERTER_DEVICE:-/dev/ttyUSB0}"
|
||||||
|
MQTT_SERVER="${MQTT_SERVER:-192.168.1.37}"
|
||||||
|
MQTT_PORT="${MQTT_PORT:-1883}"
|
||||||
|
MQTT_TOPIC="${MQTT_TOPIC:-homeassistant}"
|
||||||
|
MQTT_DEVICENAME="${MQTT_DEVICENAME:-voltronic}"
|
||||||
|
MQTT_USERNAME="${MQTT_USERNAME:-}"
|
||||||
|
MQTT_PASSWORD="${MQTT_PASSWORD:-}"
|
||||||
|
CASCADE_COUNT="${CASCADE_COUNT:-2}"
|
||||||
|
|
||||||
|
# ── Ensure config files exist ─────────────────────────────────────────────────
|
||||||
if [ ! -f "$CONF_FILE" ]; then
|
if [ ! -f "$CONF_FILE" ]; then
|
||||||
echo "⚠ inverter.conf not found, creating default..."
|
echo "⚠ inverter.conf not found, creating default..."
|
||||||
cat > "$CONF_FILE" << 'EOF'
|
cat > "$CONF_FILE" << 'EOF'
|
||||||
# Basic configuration options for the actual inverter polling process...
|
|
||||||
device=/dev/ttyUSB0
|
device=/dev/ttyUSB0
|
||||||
run_interval=120
|
run_interval=120
|
||||||
amperage_factor=1.0
|
amperage_factor=1.0
|
||||||
@@ -45,42 +52,21 @@ if [ ! -f "$MQTT_CONF" ]; then
|
|||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Environment variables with defaults
|
# ── Apply device path to config ───────────────────────────────────────────────
|
||||||
INVERTER_DEVICE="${INVERTER_DEVICE:-/dev/ttyUSB0}"
|
sed -i "s|^device=.*|device=$INVERTER_DEVICE|g" "$CONF_FILE"
|
||||||
FORCE_DISCOVERY="${FORCE_DISCOVERY:-false}"
|
|
||||||
SKIP_DISCOVERY="${SKIP_DISCOVERY:-false}"
|
|
||||||
MQTT_SERVER="${MQTT_SERVER:-192.168.1.37}"
|
|
||||||
MQTT_PORT="${MQTT_PORT:-1883}"
|
|
||||||
MQTT_TOPIC="${MQTT_TOPIC:-homeassistant}"
|
|
||||||
MQTT_DEVICENAME="${MQTT_DEVICENAME:-voltronic}"
|
|
||||||
MQTT_USERNAME="${MQTT_USERNAME:-}"
|
|
||||||
MQTT_PASSWORD="${MQTT_PASSWORD:-}"
|
|
||||||
|
|
||||||
echo "Configuration:"
|
echo "Configuration:"
|
||||||
echo " Device: $INVERTER_DEVICE"
|
echo " Device: $INVERTER_DEVICE"
|
||||||
echo " Force Discovery: $FORCE_DISCOVERY"
|
echo " Cascade count: $CASCADE_COUNT inverters"
|
||||||
echo " Skip Discovery: $SKIP_DISCOVERY"
|
echo " MQTT Server: $MQTT_SERVER:$MQTT_PORT"
|
||||||
echo " MQTT Server: $MQTT_SERVER:$MQTT_PORT"
|
echo " MQTT Topic: $MQTT_TOPIC"
|
||||||
echo " MQTT Topic: $MQTT_TOPIC"
|
echo " MQTT Device: $MQTT_DEVICENAME"
|
||||||
echo " MQTT Device: $MQTT_DEVICENAME"
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Function to update MQTT configuration
|
# ── Update MQTT config from ENV (only when credentials are provided) ──────────
|
||||||
update_mqtt_config() {
|
if [ ! -z "$MQTT_USERNAME" ] || [ ! -z "$MQTT_PASSWORD" ]; then
|
||||||
local mqtt_conf="/etc/inverter/mqtt.json"
|
|
||||||
|
|
||||||
# Solo aggiorna se le ENV variables sono impostate
|
|
||||||
if [ -z "$MQTT_USERNAME" ] && [ -z "$MQTT_PASSWORD" ]; then
|
|
||||||
echo "ℹ MQTT credentials not provided via ENV, using values from mqtt.json"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Updating MQTT configuration from environment variables..."
|
echo "Updating MQTT configuration from environment variables..."
|
||||||
|
cp $MQTT_CONF ${MQTT_CONF}.backup 2>/dev/null || true
|
||||||
# Backup original
|
|
||||||
cp $mqtt_conf ${mqtt_conf}.backup 2>/dev/null || true
|
|
||||||
|
|
||||||
# Update MQTT settings using jq
|
|
||||||
jq --arg server "$MQTT_SERVER" \
|
jq --arg server "$MQTT_SERVER" \
|
||||||
--arg port "$MQTT_PORT" \
|
--arg port "$MQTT_PORT" \
|
||||||
--arg topic "$MQTT_TOPIC" \
|
--arg topic "$MQTT_TOPIC" \
|
||||||
@@ -88,208 +74,58 @@ update_mqtt_config() {
|
|||||||
--arg username "$MQTT_USERNAME" \
|
--arg username "$MQTT_USERNAME" \
|
||||||
--arg password "$MQTT_PASSWORD" \
|
--arg password "$MQTT_PASSWORD" \
|
||||||
'.server = $server | .port = $port | .topic = $topic | .devicename = $devicename | .username = $username | .password = $password' \
|
'.server = $server | .port = $port | .topic = $topic | .devicename = $devicename | .username = $username | .password = $password' \
|
||||||
$mqtt_conf > ${mqtt_conf}.tmp && mv ${mqtt_conf}.tmp $mqtt_conf
|
$MQTT_CONF > ${MQTT_CONF}.tmp && mv ${MQTT_CONF}.tmp $MQTT_CONF
|
||||||
|
|
||||||
echo "✓ MQTT configuration updated"
|
echo "✓ MQTT configuration updated"
|
||||||
echo " Server: $MQTT_SERVER:$MQTT_PORT"
|
|
||||||
echo " Topic: $MQTT_TOPIC/sensor/$MQTT_DEVICENAME"
|
|
||||||
[ ! -z "$MQTT_USERNAME" ] && echo " Auth: Enabled (username: $MQTT_USERNAME)"
|
|
||||||
|
|
||||||
# Verifica che le credenziali siano state scritte
|
|
||||||
local check_user=$(jq -r '.username' $mqtt_conf)
|
|
||||||
local check_pass=$(jq -r '.password' $mqtt_conf)
|
|
||||||
if [ -z "$check_user" ] || [ -z "$check_pass" ]; then
|
|
||||||
echo "⚠ WARNING: MQTT credentials were not properly written to config file!"
|
|
||||||
fi
|
|
||||||
echo ""
|
echo ""
|
||||||
}
|
|
||||||
|
|
||||||
# Update MQTT config from ENV on startup
|
|
||||||
update_mqtt_config
|
|
||||||
|
|
||||||
# Function to update config file with discovered values
|
|
||||||
update_config_with_discovery() {
|
|
||||||
local qmod=$1
|
|
||||||
local qpigs=$2
|
|
||||||
local qpiri=$3
|
|
||||||
local qpiws=$4
|
|
||||||
|
|
||||||
echo "Updating configuration with discovered values..."
|
|
||||||
|
|
||||||
# Backup original config
|
|
||||||
cp $CONF_FILE ${CONF_FILE}.backup
|
|
||||||
|
|
||||||
# Update device
|
|
||||||
sed -i "s|^device=.*|device=$INVERTER_DEVICE|g" $CONF_FILE
|
|
||||||
|
|
||||||
# Update buffer sizes
|
|
||||||
sed -i "s/^qmod=.*/qmod=$qmod/g" $CONF_FILE
|
|
||||||
sed -i "s/^qpigs=.*/qpigs=$qpigs/g" $CONF_FILE
|
|
||||||
sed -i "s/^qpiri=.*/qpiri=$qpiri/g" $CONF_FILE
|
|
||||||
sed -i "s/^qpiws=.*/qpiws=$qpiws/g" $CONF_FILE
|
|
||||||
|
|
||||||
echo "✓ Configuration updated successfully"
|
|
||||||
echo ""
|
|
||||||
grep -E "^(device|qmod|qpigs|qpiri|qpiws)=" $CONF_FILE
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to run auto-discovery
|
|
||||||
run_discovery() {
|
|
||||||
echo "=== Running Auto-Discovery ==="
|
|
||||||
echo "This will take about 10-15 seconds..."
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Temporarily set device in config for discovery
|
|
||||||
cp $CONF_FILE $TEMP_CONF
|
|
||||||
sed -i "s|^device=.*|device=$INVERTER_DEVICE|g" $TEMP_CONF
|
|
||||||
cp $TEMP_CONF $CONF_FILE
|
|
||||||
|
|
||||||
# Run discovery and capture output
|
|
||||||
DISCOVERY_OUTPUT=$(/opt/inverter-cli/bin/inverter_poller -d -a 2>&1)
|
|
||||||
|
|
||||||
echo "$DISCOVERY_OUTPUT"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Parse discovery output
|
|
||||||
QMOD=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_QMOD=" | cut -d= -f2)
|
|
||||||
QPIGS=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_QPIGS=" | cut -d= -f2)
|
|
||||||
QPIRI=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_QPIRI=" | cut -d= -f2)
|
|
||||||
QPIWS=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_QPIWS=" | cut -d= -f2)
|
|
||||||
SUCCESS=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_SUCCESS=" | cut -d= -f2)
|
|
||||||
|
|
||||||
if [ "$SUCCESS" = "true" ]; then
|
|
||||||
echo "✓ Auto-discovery completed successfully!"
|
|
||||||
update_config_with_discovery $QMOD $QPIGS $QPIRI $QPIWS
|
|
||||||
|
|
||||||
# Mark discovery as done
|
|
||||||
echo "device=$INVERTER_DEVICE" > $DISCOVERY_FLAG
|
|
||||||
echo "qmod=$QMOD" >> $DISCOVERY_FLAG
|
|
||||||
echo "qpigs=$QPIGS" >> $DISCOVERY_FLAG
|
|
||||||
echo "qpiri=$QPIRI" >> $DISCOVERY_FLAG
|
|
||||||
echo "qpiws=$QPIWS" >> $DISCOVERY_FLAG
|
|
||||||
echo "timestamp=$(date -Iseconds)" >> $DISCOVERY_FLAG
|
|
||||||
|
|
||||||
echo "✓ Discovery results saved to $DISCOVERY_FLAG"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "✗ Auto-discovery failed!"
|
|
||||||
echo "Please check:"
|
|
||||||
echo " 1. Inverter is powered on"
|
|
||||||
echo " 2. Cable is properly connected"
|
|
||||||
echo " 3. Device path is correct: $INVERTER_DEVICE"
|
|
||||||
echo ""
|
|
||||||
echo "Falling back to default configuration..."
|
|
||||||
|
|
||||||
# Update device but keep default buffer sizes
|
|
||||||
sed -i "s|^device=.*|device=$INVERTER_DEVICE|g" $CONF_FILE
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if we need to run discovery
|
|
||||||
NEED_DISCOVERY=false
|
|
||||||
|
|
||||||
if [ "$FORCE_DISCOVERY" = "true" ]; then
|
|
||||||
echo "⚠ Force discovery requested via environment variable"
|
|
||||||
rm -f $DISCOVERY_FLAG
|
|
||||||
NEED_DISCOVERY=true
|
|
||||||
elif [ "$SKIP_DISCOVERY" = "true" ]; then
|
|
||||||
echo "⚠ Discovery skipped via environment variable"
|
|
||||||
# Just update device in config
|
|
||||||
sed -i "s|^device=.*|device=$INVERTER_DEVICE|g" $CONF_FILE
|
|
||||||
NEED_DISCOVERY=false
|
|
||||||
elif [ ! -f "$DISCOVERY_FLAG" ]; then
|
|
||||||
echo "ℹ No previous discovery found, will run auto-discovery"
|
|
||||||
NEED_DISCOVERY=true
|
|
||||||
else
|
|
||||||
# Check if device changed
|
|
||||||
SAVED_DEVICE=$(grep "^device=" $DISCOVERY_FLAG 2>/dev/null | cut -d= -f2)
|
|
||||||
if [ "$SAVED_DEVICE" != "$INVERTER_DEVICE" ]; then
|
|
||||||
echo "⚠ Device changed from $SAVED_DEVICE to $INVERTER_DEVICE"
|
|
||||||
echo " Running new discovery..."
|
|
||||||
rm -f $DISCOVERY_FLAG
|
|
||||||
NEED_DISCOVERY=true
|
|
||||||
else
|
|
||||||
echo "✓ Using previous discovery results from $DISCOVERY_FLAG"
|
|
||||||
# Restore saved config
|
|
||||||
while IFS= read -r line; do
|
|
||||||
if [[ $line =~ ^(device|qmod|qpigs|qpiri|qpiws)= ]]; then
|
|
||||||
key=$(echo "$line" | cut -d= -f1)
|
|
||||||
value=$(echo "$line" | cut -d= -f2)
|
|
||||||
sed -i "s|^$key=.*|$key=$value|g" $CONF_FILE
|
|
||||||
fi
|
|
||||||
done < "$DISCOVERY_FLAG"
|
|
||||||
|
|
||||||
echo "Current configuration:"
|
|
||||||
grep -E "^(device|qmod|qpigs|qpiri|qpiws)=" $CONF_FILE
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run discovery if needed
|
# ── Wait for serial device ────────────────────────────────────────────────────
|
||||||
if [ "$NEED_DISCOVERY" = "true" ]; then
|
echo "Waiting for device $INVERTER_DEVICE to be ready..."
|
||||||
if ! run_discovery; then
|
sleep 3
|
||||||
echo "⚠ Continuing with default configuration..."
|
|
||||||
echo " You can manually run discovery later with:"
|
# ── Clean up legacy single-inverter MQTT topics ───────────────────────────────
|
||||||
echo " docker exec -it <container> /opt/inverter-cli/bin/inverter_poller -a"
|
_MQTT_SERVER=$(jq -r '.server' $MQTT_CONF)
|
||||||
fi
|
_MQTT_PORT=$(jq -r '.port' $MQTT_CONF)
|
||||||
fi
|
_MQTT_USER=$(jq -r '.username' $MQTT_CONF)
|
||||||
|
_MQTT_PASS=$(jq -r '.password' $MQTT_CONF)
|
||||||
|
_MQTT_DEVICENAME=$(jq -r '.devicename' $MQTT_CONF)
|
||||||
|
_MQTT_TOPIC=$(jq -r '.topic' $MQTT_CONF)
|
||||||
|
|
||||||
|
mosquitto_pub -h $_MQTT_SERVER -p $_MQTT_PORT -u "$_MQTT_USER" -P "$_MQTT_PASS" \
|
||||||
|
-t "$_MQTT_TOPIC/sensor/$_MQTT_DEVICENAME/config" -n -r > /dev/null 2>&1 || true
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Starting MQTT Bridge Services ==="
|
echo "=== Starting MQTT Bridge Services ==="
|
||||||
if [ -n "$INVERTER_DEVICES" ]; then
|
echo "Cascade mode: $CASCADE_COUNT inverters on $INVERTER_DEVICE"
|
||||||
echo "Using multi-device mode (INVERTER_DEVICES=${INVERTER_DEVICES})"
|
|
||||||
else
|
|
||||||
echo "Using parallel inverter mode (2 inverters)"
|
|
||||||
fi
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Wait a bit for the device to be ready
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Always use parallel scripts
|
|
||||||
MQTT_PUSH_SCRIPT="/opt/inverter-mqtt/mqtt-push-parallel.sh"
|
MQTT_PUSH_SCRIPT="/opt/inverter-mqtt/mqtt-push-parallel.sh"
|
||||||
MQTT_INIT_SCRIPT="/opt/inverter-mqtt/mqtt-init-parallel.sh"
|
MQTT_INIT_SCRIPT="/opt/inverter-mqtt/mqtt-init-parallel.sh"
|
||||||
|
|
||||||
# Remove old single-inverter discovery topics (legacy cleanup)
|
# ── Initialize MQTT discovery topics ─────────────────────────────────────────
|
||||||
echo "Cleaning up legacy MQTT topics..."
|
echo "Initializing MQTT discovery topics..."
|
||||||
MQTT_SERVER=$(jq -r '.server' /etc/inverter/mqtt.json)
|
CASCADE_COUNT="$CASCADE_COUNT" "$MQTT_INIT_SCRIPT"
|
||||||
MQTT_PORT=$(jq -r '.port' /etc/inverter/mqtt.json)
|
|
||||||
MQTT_USERNAME=$(jq -r '.username' /etc/inverter/mqtt.json)
|
|
||||||
MQTT_PASSWORD=$(jq -r '.password' /etc/inverter/mqtt.json)
|
|
||||||
MQTT_DEVICENAME=$(jq -r '.devicename' /etc/inverter/mqtt.json)
|
|
||||||
|
|
||||||
mosquitto_pub -h $MQTT_SERVER -p $MQTT_PORT -u "$MQTT_USERNAME" -P "$MQTT_PASSWORD" \
|
|
||||||
-t "$MQTT_TOPIC/sensor/$MQTT_DEVICENAME/config" -n -r > /dev/null 2>&1
|
|
||||||
|
|
||||||
echo "✓ Legacy topics cleaned"
|
|
||||||
|
|
||||||
# Run MQTT initialization immediately on startup
|
|
||||||
echo "Initializing MQTT discovery topics for both inverters..."
|
|
||||||
"$MQTT_INIT_SCRIPT"
|
|
||||||
echo "✓ MQTT discovery topics initialized"
|
echo "✓ MQTT discovery topics initialized"
|
||||||
|
|
||||||
# Init the mqtt server every 5 minutes (300 seconds)
|
# ── Periodic MQTT init (re-register topics every 5 min for HA restarts) ──────
|
||||||
# This will re-create the auto-created topics in the MQTT server if HA is restarted...
|
|
||||||
echo "Starting MQTT initialization service (every 5 minutes)..."
|
|
||||||
(
|
(
|
||||||
while true; do
|
while true; do
|
||||||
"$MQTT_INIT_SCRIPT" > /dev/null 2>&1
|
|
||||||
sleep 300
|
sleep 300
|
||||||
|
CASCADE_COUNT="$CASCADE_COUNT" "$MQTT_INIT_SCRIPT" > /dev/null 2>&1
|
||||||
done
|
done
|
||||||
) &
|
) &
|
||||||
|
|
||||||
# Run the MQTT Subscriber process in the background (so that way we can change the configuration on the inverter from home assistant)
|
# ── MQTT subscriber (listen for commands from Home Assistant) ─────────────────
|
||||||
echo "Starting MQTT subscriber for commands..."
|
echo "Starting MQTT command subscriber..."
|
||||||
/opt/inverter-mqtt/mqtt-subscriber.sh > /dev/null 2>&1 &
|
/opt/inverter-mqtt/mqtt-subscriber.sh > /dev/null 2>&1 &
|
||||||
|
|
||||||
# execute exactly every 30 seconds...
|
# ── Main data push loop (every 30 seconds) ────────────────────────────────────
|
||||||
echo "Starting MQTT data push service (every 30 seconds)..."
|
echo "Starting data push loop (every 30s)..."
|
||||||
echo ""
|
echo ""
|
||||||
echo "✓ All services started successfully!"
|
echo "✓ All services started. Logs appear below..."
|
||||||
echo " Logs will appear below..."
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
"$MQTT_PUSH_SCRIPT" > /dev/null 2>&1
|
CASCADE_COUNT="$CASCADE_COUNT" "$MQTT_PUSH_SCRIPT" > /dev/null 2>&1
|
||||||
sleep 30
|
sleep 30
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -1,30 +1,21 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# MQTT Push for Parallel Inverters
|
# MQTT Push for Parallel/Cascade Inverters
|
||||||
# Discovers parallel inverters and publishes data for each one separately
|
# 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)
|
# Detect environment (container vs development)
|
||||||
if [ -f "/etc/inverter/mqtt.json" ] && [ -x "/opt/inverter-cli/bin/inverter_poller" ]; then
|
if [ -f "/etc/inverter/mqtt.json" ] && [ -x "/opt/inverter-cli/bin/inverter_poller" ]; then
|
||||||
# Container mode
|
|
||||||
MQTT_CONFIG="/etc/inverter/mqtt.json"
|
MQTT_CONFIG="/etc/inverter/mqtt.json"
|
||||||
INVERTER_BIN="/opt/inverter-cli/bin/inverter_poller"
|
INVERTER_BIN="/opt/inverter-cli/bin/inverter_poller"
|
||||||
MQTT_FALLBACK="/opt/inverter-mqtt/mqtt-push.sh"
|
|
||||||
INVERTER_CONF="/etc/inverter/inverter.conf"
|
INVERTER_CONF="/etc/inverter/inverter.conf"
|
||||||
CONTAINER_MODE=true
|
|
||||||
else
|
else
|
||||||
# Development mode
|
|
||||||
MQTT_CONFIG="/home/pi/Progetti/config/mqtt.json"
|
MQTT_CONFIG="/home/pi/Progetti/config/mqtt.json"
|
||||||
INVERTER_BIN="/home/pi/Progetti/sources/inverter-cli/bin/inverter_poller"
|
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"
|
INVERTER_CONF="/home/pi/Progetti/config/inverter.conf"
|
||||||
CONTAINER_MODE=false
|
|
||||||
fi
|
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
|
if ! command -v jq &> /dev/null; then
|
||||||
echo "ERROR: jq is not installed. Install it with: sudo apt-get install jq"
|
echo "ERROR: jq is not installed. Install with: sudo apt-get install jq"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -37,12 +28,15 @@ MQTT_PASSWORD=`cat $MQTT_CONFIG | jq '.password' -r`
|
|||||||
MQTT_CLIENTID=`cat $MQTT_CONFIG | jq '.clientid' -r`
|
MQTT_CLIENTID=`cat $MQTT_CONFIG | jq '.clientid' -r`
|
||||||
INFLUX_ENABLED=`cat $MQTT_CONFIG | jq '.influx.enabled' -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 () {
|
pushMQTTData () {
|
||||||
# $1 = inverter_id, $2 = metric, $3 = value
|
# $1 = inverter_id (1, 2, or "system"), $2 = metric, $3 = value
|
||||||
local inverter_id=$1
|
local inverter_id=$1
|
||||||
local metric=$2
|
local metric=$2
|
||||||
local value=$3
|
local value=$3
|
||||||
|
|
||||||
mosquitto_pub \
|
mosquitto_pub \
|
||||||
-h $MQTT_SERVER \
|
-h $MQTT_SERVER \
|
||||||
-p $MQTT_PORT \
|
-p $MQTT_PORT \
|
||||||
@@ -52,8 +46,8 @@ pushMQTTData () {
|
|||||||
-r \
|
-r \
|
||||||
-t "$MQTT_TOPIC/sensor/${MQTT_DEVICENAME}_inv${inverter_id}_${metric}" \
|
-t "$MQTT_TOPIC/sensor/${MQTT_DEVICENAME}_inv${inverter_id}_${metric}" \
|
||||||
-m "$value"
|
-m "$value"
|
||||||
|
|
||||||
if [[ $INFLUX_ENABLED == "true" ]] ; then
|
if [[ $INFLUX_ENABLED == "true" ]]; then
|
||||||
pushInfluxData $inverter_id $metric $value
|
pushInfluxData $inverter_id $metric $value
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -66,263 +60,127 @@ pushInfluxData () {
|
|||||||
INFLUX_PREFIX=`cat $MQTT_CONFIG | jq '.influx.prefix' -r`
|
INFLUX_PREFIX=`cat $MQTT_CONFIG | jq '.influx.prefix' -r`
|
||||||
INFLUX_DATABASE=`cat $MQTT_CONFIG | jq '.influx.database' -r`
|
INFLUX_DATABASE=`cat $MQTT_CONFIG | jq '.influx.database' -r`
|
||||||
INFLUX_MEASUREMENT_NAME=`cat $MQTT_CONFIG | jq '.influx.namingMap.'$2'' -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
|
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)
|
# Process QPGS data and publish to MQTT for one inverter.
|
||||||
extractAndPublishAllData () {
|
# Arguments: $1=inv_id, $2=QPGS_RAW string
|
||||||
|
# Returns 0 on success, 1 on failure.
|
||||||
|
processInverter () {
|
||||||
local inv_id=$1
|
local inv_id=$1
|
||||||
local json_data=$2
|
local QPGS_RAW=$2
|
||||||
|
|
||||||
# Extract all 33 parameters exactly like original script
|
if [ -z "$QPGS_RAW" ] || [ "$QPGS_RAW" = "NAK" ]; then
|
||||||
Inverter_mode=`echo "$json_data" | jq '.Inverter_mode' -r`
|
echo " ✗ inv$inv_id: no data (NAK or empty)"
|
||||||
[ ! -z "$Inverter_mode" ] && [ "$Inverter_mode" != "null" ] && pushMQTTData "$inv_id" "Inverter_mode" "$Inverter_mode"
|
return 1
|
||||||
|
fi
|
||||||
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"
|
IFS=' ' read -ra DATA <<< "$QPGS_RAW"
|
||||||
|
|
||||||
AC_grid_frequency=`echo "$json_data" | jq '.AC_grid_frequency' -r`
|
# QPGS field mapping (per protocol HS_MS_MSX_RS232_Protocol):
|
||||||
[ ! -z "$AC_grid_frequency" ] && [ "$AC_grid_frequency" != "null" ] && pushMQTTData "$inv_id" "AC_grid_frequency" "$AC_grid_frequency"
|
# 0=Exists 1=Serial 2=Mode 3=Fault
|
||||||
|
# 4=GridV 5=GridF 6=OutV 7=OutF
|
||||||
AC_out_voltage=`echo "$json_data" | jq '.AC_out_voltage' -r`
|
# 8=OutVA 9=OutW 10=LoadPct
|
||||||
[ ! -z "$AC_out_voltage" ] && [ "$AC_out_voltage" != "null" ] && pushMQTTData "$inv_id" "AC_out_voltage" "$AC_out_voltage"
|
# 11=BattV 12=BattChgA 13=BattCap
|
||||||
|
# 14=PVInputV 15=TotalChgA 16=TotalOutVA 17=TotalOutW 18=TotalOutPct
|
||||||
AC_out_frequency=`echo "$json_data" | jq '.AC_out_frequency' -r`
|
# 19=StatusByte(b7b6b5b4b3b2b1b0) 20=OutMode 21=ChgSourcePriority
|
||||||
[ ! -z "$AC_out_frequency" ] && [ "$AC_out_frequency" != "null" ] && pushMQTTData "$inv_id" "AC_out_frequency" "$AC_out_frequency"
|
# 22=MaxChgA 23=MaxChgRange 24=MaxAcChgA
|
||||||
|
# 25=PV_in_current 26=Batt_discharge_current
|
||||||
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"
|
[ "${DATA[2]}" ] && pushMQTTData "$inv_id" "Inverter_mode" "${DATA[2]}"
|
||||||
|
[ "${DATA[4]}" ] && pushMQTTData "$inv_id" "AC_grid_voltage" "${DATA[4]}"
|
||||||
PV_in_current=`echo "$json_data" | jq '.PV_in_current' -r`
|
[ "${DATA[5]}" ] && pushMQTTData "$inv_id" "AC_grid_frequency" "${DATA[5]}"
|
||||||
[ ! -z "$PV_in_current" ] && [ "$PV_in_current" != "null" ] && pushMQTTData "$inv_id" "PV_in_current" "$PV_in_current"
|
[ "${DATA[6]}" ] && pushMQTTData "$inv_id" "AC_out_voltage" "${DATA[6]}"
|
||||||
|
[ "${DATA[7]}" ] && pushMQTTData "$inv_id" "AC_out_frequency" "${DATA[7]}"
|
||||||
PV_in_watts=`echo "$json_data" | jq '.PV_in_watts' -r`
|
[ "${DATA[8]}" ] && pushMQTTData "$inv_id" "Load_va" "${DATA[8]}"
|
||||||
[ ! -z "$PV_in_watts" ] && [ "$PV_in_watts" != "null" ] && pushMQTTData "$inv_id" "PV_in_watts" "$PV_in_watts"
|
[ "${DATA[9]}" ] && pushMQTTData "$inv_id" "Load_watt" "${DATA[9]}"
|
||||||
|
[ "${DATA[10]}" ] && pushMQTTData "$inv_id" "Load_pct" "${DATA[10]}"
|
||||||
PV_in_watthour=`echo "$json_data" | jq '.PV_in_watthour' -r`
|
[ "${DATA[11]}" ] && pushMQTTData "$inv_id" "Battery_voltage" "${DATA[11]}"
|
||||||
[ ! -z "$PV_in_watthour" ] && [ "$PV_in_watthour" != "null" ] && pushMQTTData "$inv_id" "PV_in_watthour" "$PV_in_watthour"
|
[ "${DATA[12]}" ] && pushMQTTData "$inv_id" "Battery_charge_current" "${DATA[12]}"
|
||||||
|
[ "${DATA[13]}" ] && pushMQTTData "$inv_id" "Battery_capacity" "${DATA[13]}"
|
||||||
SCC_voltage=`echo "$json_data" | jq '.SCC_voltage' -r`
|
[ "${DATA[14]}" ] && pushMQTTData "$inv_id" "PV_in_voltage" "${DATA[14]}"
|
||||||
[ ! -z "$SCC_voltage" ] && [ "$SCC_voltage" != "null" ] && pushMQTTData "$inv_id" "SCC_voltage" "$SCC_voltage"
|
[ "${DATA[25]}" ] && pushMQTTData "$inv_id" "PV_in_current" "${DATA[25]}"
|
||||||
|
[ "${DATA[26]}" ] && pushMQTTData "$inv_id" "Battery_discharge_current" "${DATA[26]}"
|
||||||
Load_pct=`echo "$json_data" | jq '.Load_pct' -r`
|
|
||||||
[ ! -z "$Load_pct" ] && [ "$Load_pct" != "null" ] && pushMQTTData "$inv_id" "Load_pct" "$Load_pct"
|
# ─── PV_in_watts calculation ──────────────────────────────────────────────
|
||||||
|
# DATA[25] = SCC output current at battery-bus voltage (not at PV panel V).
|
||||||
Load_watt=`echo "$json_data" | jq '.Load_watt' -r`
|
# Source: original QPIGS comment "current going out to battery at battery voltage".
|
||||||
[ ! -z "$Load_watt" ] && [ "$Load_watt" != "null" ] && pushMQTTData "$inv_id" "Load_watt" "$Load_watt"
|
# Formula: Battery_voltage × DATA[25] = power delivered by SCC to battery.
|
||||||
|
#
|
||||||
Load_watthour=`echo "$json_data" | jq '.Load_watthour' -r`
|
# Edge case - battery fully charged (DATA[25] = 0):
|
||||||
[ ! -z "$Load_watthour" ] && [ "$Load_watthour" != "null" ] && pushMQTTData "$inv_id" "Load_watthour" "$Load_watthour"
|
# 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.
|
||||||
Load_va=`echo "$json_data" | jq '.Load_va' -r`
|
# If AC grid is present (line_loss=0), solar just floats battery → 0W is correct.
|
||||||
[ ! -z "$Load_va" ] && [ "$Load_va" != "null" ] && pushMQTTData "$inv_id" "Load_va" "$Load_va"
|
local BATT_V="${DATA[11]:-0}"
|
||||||
|
local SCC_A="${DATA[25]:-0}"
|
||||||
Bus_voltage=`echo "$json_data" | jq '.Bus_voltage' -r`
|
local LOAD_W="${DATA[9]:-0}"
|
||||||
[ ! -z "$Bus_voltage" ] && [ "$Bus_voltage" != "null" ] && pushMQTTData "$inv_id" "Bus_voltage" "$Bus_voltage"
|
local STATUS="${DATA[19]:-00000000}"
|
||||||
|
|
||||||
Heatsink_temperature=`echo "$json_data" | jq '.Heatsink_temperature' -r`
|
# STATUS string b7b6b5b4b3b2b1b0 - each char is one bit:
|
||||||
[ ! -z "$Heatsink_temperature" ] && [ "$Heatsink_temperature" != "null" ] && pushMQTTData "$inv_id" "Heatsink_temperature" "$Heatsink_temperature"
|
# 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
|
||||||
Battery_capacity=`echo "$json_data" | jq '.Battery_capacity' -r`
|
local SCC_OK="${STATUS:0:1}"
|
||||||
[ ! -z "$Battery_capacity" ] && [ "$Battery_capacity" != "null" ] && pushMQTTData "$inv_id" "Battery_capacity" "$Battery_capacity"
|
local LINE_LOSS="${STATUS:5:1}"
|
||||||
|
|
||||||
Battery_voltage=`echo "$json_data" | jq '.Battery_voltage' -r`
|
local PV_WATTS
|
||||||
[ ! -z "$Battery_voltage" ] && [ "$Battery_voltage" != "null" ] && pushMQTTData "$inv_id" "Battery_voltage" "$Battery_voltage"
|
if awk -v v="$SCC_A" 'BEGIN{exit !(v+0 > 0)}' 2>/dev/null; then
|
||||||
|
# SCC delivering current to battery
|
||||||
Battery_charge_current=`echo "$json_data" | jq '.Battery_charge_current' -r`
|
PV_WATTS=$(echo "$BATT_V $SCC_A" | awk '{printf "%.1f", $1 * $2}')
|
||||||
[ ! -z "$Battery_charge_current" ] && [ "$Battery_charge_current" != "null" ] && pushMQTTData "$inv_id" "Battery_charge_current" "$Battery_charge_current"
|
elif [ "$SCC_OK" = "1" ] && [ "$LINE_LOSS" = "1" ]; then
|
||||||
|
# SCC OK, battery full, no AC grid → solar feeds load
|
||||||
Battery_discharge_current=`echo "$json_data" | jq '.Battery_discharge_current' -r`
|
PV_WATTS="$LOAD_W"
|
||||||
[ ! -z "$Battery_discharge_current" ] && [ "$Battery_discharge_current" != "null" ] && pushMQTTData "$inv_id" "Battery_discharge_current" "$Battery_discharge_current"
|
else
|
||||||
|
PV_WATTS="0.0"
|
||||||
Load_status_on=`echo "$json_data" | jq '.Load_status_on' -r`
|
fi
|
||||||
[ ! -z "$Load_status_on" ] && [ "$Load_status_on" != "null" ] && pushMQTTData "$inv_id" "Load_status_on" "$Load_status_on"
|
pushMQTTData "$inv_id" "PV_in_watts" "$PV_WATTS"
|
||||||
|
|
||||||
SCC_charge_on=`echo "$json_data" | jq '.SCC_charge_on' -r`
|
# Watt-hours (approximated from polling interval = 30s = 1/120 hour)
|
||||||
[ ! -z "$SCC_charge_on" ] && [ "$SCC_charge_on" != "null" ] && pushMQTTData "$inv_id" "SCC_charge_on" "$SCC_charge_on"
|
local PV_WH=$(echo "$PV_WATTS" | awk '{printf "%.4f", $1 / 120}')
|
||||||
|
pushMQTTData "$inv_id" "PV_in_watthour" "$PV_WH"
|
||||||
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"
|
local LOAD_WH=$(echo "$LOAD_W" | awk '{printf "%.4f", $1 / 120}')
|
||||||
|
pushMQTTData "$inv_id" "Load_watthour" "$LOAD_WH"
|
||||||
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"
|
# Status flags from STATUS byte
|
||||||
|
if [ ${#STATUS} -ge 8 ]; then
|
||||||
Battery_under_voltage=`echo "$json_data" | jq '.Battery_under_voltage' -r`
|
pushMQTTData "$inv_id" "SCC_charge_on" "${STATUS:2:1}" # b5
|
||||||
[ ! -z "$Battery_under_voltage" ] && [ "$Battery_under_voltage" != "null" ] && pushMQTTData "$inv_id" "Battery_under_voltage" "$Battery_under_voltage"
|
pushMQTTData "$inv_id" "AC_charge_on" "${STATUS:1:1}" # b6
|
||||||
|
pushMQTTData "$inv_id" "Load_status_on" "${STATUS:6:1}" # b1
|
||||||
Battery_bulk_voltage=`echo "$json_data" | jq '.Battery_bulk_voltage' -r`
|
fi
|
||||||
[ ! -z "$Battery_bulk_voltage" ] && [ "$Battery_bulk_voltage" != "null" ] && pushMQTTData "$inv_id" "Battery_bulk_voltage" "$Battery_bulk_voltage"
|
|
||||||
|
echo " ✓ inv$inv_id: OK (PV_watts=${PV_WATTS}W, Load=${LOAD_W}W)"
|
||||||
Battery_float_voltage=`echo "$json_data" | jq '.Battery_float_voltage' -r`
|
return 0
|
||||||
[ ! -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
|
# ─── Main ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
SUDO_CMD=""
|
SUDO_CMD=""
|
||||||
if [ "$EUID" -ne 0 ] && [ -c "/dev/ttyUSB0" ]; then
|
if [ "$EUID" -ne 0 ]; then
|
||||||
SUDO_CMD="sudo"
|
SUDO_CMD="sudo"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Multi-device mode: if INVERTER_DEVICES is set, poll each device separately
|
echo "=== Cascade Inverter MQTT Push ==="
|
||||||
if [ -n "$INVERTER_DEVICES" ]; then
|
echo "Inverter count: $CASCADE_COUNT"
|
||||||
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')
|
# ── Step 1: QPIRI (shared config, same for all cascade inverters) ─────────────
|
||||||
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=""
|
QPIRI_RAW=""
|
||||||
for attempt in 1 2 3 4 5; do
|
for attempt in 1 2 3; do
|
||||||
QPIRI_RAW=`$SUDO_CMD "$INVERTER_BIN" -r "QPIRI" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs`
|
QPIRI_RAW=`$SUDO_CMD "$INVERTER_BIN" -r "QPIRI" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs`
|
||||||
if [ ! -z "$QPIRI_RAW" ] && [ "$QPIRI_RAW" != "NAK" ]; then
|
[ ! -z "$QPIRI_RAW" ] && [ "$QPIRI_RAW" != "NAK" ] && break
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
QPIRI_SUCCESS=false
|
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
|
if [ ! -z "$QPIRI_RAW" ] && [ "$QPIRI_RAW" != "NAK" ]; then
|
||||||
echo "✓ QPIRI retrieved successfully"
|
echo "✓ QPIRI retrieved"
|
||||||
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"
|
IFS=' ' read -ra QPIRI <<< "$QPIRI_RAW"
|
||||||
|
|
||||||
# Extract configuration values (same for all inverters)
|
|
||||||
BATT_RECHARGE="${QPIRI[8]}"
|
BATT_RECHARGE="${QPIRI[8]}"
|
||||||
BATT_UNDER="${QPIRI[9]}"
|
BATT_UNDER="${QPIRI[9]}"
|
||||||
BATT_BULK="${QPIRI[10]}"
|
BATT_BULK="${QPIRI[10]}"
|
||||||
@@ -333,326 +191,40 @@ if [ ! -z "$QPIRI_RAW" ] && [ "$QPIRI_RAW" != "NAK" ]; then
|
|||||||
CHARGER_SOURCE_PRIORITY="${QPIRI[16]}"
|
CHARGER_SOURCE_PRIORITY="${QPIRI[16]}"
|
||||||
BATT_REDISCHARGE="${QPIRI[22]}"
|
BATT_REDISCHARGE="${QPIRI[22]}"
|
||||||
else
|
else
|
||||||
echo "⚠ QPIRI failed, configuration parameters unavailable"
|
echo "⚠ QPIRI failed - config parameters unavailable"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract runtime data for each inverter using QPGS (single session)
|
# ── Step 2: QPGS per each inverter ───────────────────────────────────────────
|
||||||
PARALLEL_SUCCESS=false
|
SUCCESS_IDS=()
|
||||||
|
|
||||||
PARALLEL_QPGS_COUNT=$PARALLEL_COUNT
|
for inv_id in $(seq 1 $CASCADE_COUNT); do
|
||||||
if [ "$PARALLEL_QPGS_COUNT" -lt 2 ]; then
|
qpgs_idx=$((inv_id - 1))
|
||||||
PARALLEL_QPGS_COUNT=2
|
echo "Querying QPGS${qpgs_idx} → inverter #${inv_id}..."
|
||||||
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`
|
QPGS_RAW=""
|
||||||
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
|
for attempt in 1 2 3; do
|
||||||
VALID_SERIALS=()
|
QPGS_RAW=`$SUDO_CMD "$INVERTER_BIN" -r "QPGS${qpgs_idx}" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs`
|
||||||
VALID_QPGS=()
|
[ ! -z "$QPGS_RAW" ] && [ "$QPGS_RAW" != "NAK" ] && break
|
||||||
|
sleep 0.5
|
||||||
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
|
done
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ${#VALID_SERIALS[@]} -eq 0 ]; then
|
if processInverter "$inv_id" "$QPGS_RAW"; then
|
||||||
VALID_SERIALS=("${DISCOVERY_SERIALS[@]}")
|
# Publish shared QPIRI config for this inverter
|
||||||
VALID_QPGS=("${DISCOVERY_QPGS[@]}")
|
[ ! -z "$BATT_RECHARGE" ] && pushMQTTData "$inv_id" "Battery_recharge_voltage" "$BATT_RECHARGE"
|
||||||
fi
|
[ ! -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"
|
||||||
|
|
||||||
VALID_COUNT=${#VALID_SERIALS[@]}
|
SUCCESS_IDS+=("$inv_id")
|
||||||
|
|
||||||
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 per protocol (QPGSn):
|
|
||||||
# 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=PVV, 15=TotalChgA, 16=TotalOutVA, 17=TotalOutW, 18=TotalOutPct
|
|
||||||
# 19=StatusByte, 20=OutMode, 21=ChgSourcePriority, 22=MaxChgA
|
|
||||||
# 23=MaxChgRange, 24=MaxAcChgA, 25=PV_in_current, 26=Batt_discharge_current
|
|
||||||
|
|
||||||
# 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[25]}" ] && pushMQTTData "$inv_id" "PV_in_current" "${DATA[25]}"
|
|
||||||
|
|
||||||
# Calculate PV watts using battery voltage (DATA[11]) * SCC output current (DATA[25]).
|
|
||||||
# NOTE: Like QPIGS, DATA[25] ("PV Input Current") is actually the current going to the
|
|
||||||
# battery FROM the solar charge controller at battery voltage (NOT at PV panel voltage).
|
|
||||||
# Using PV voltage (DATA[14]) here would give a value ~4-5x too high.
|
|
||||||
# This mirrors the QPIGS formula: pv_input_watts = scc_voltage * pv_input_current
|
|
||||||
if [ ! -z "${DATA[11]}" ] && [ ! -z "${DATA[25]}" ]; then
|
|
||||||
PV_WATTS=`echo "${DATA[11]} ${DATA[25]}" | awk '{printf "%.1f", $1 * $2}'`
|
|
||||||
pushMQTTData "$inv_id" "PV_in_watts" "$PV_WATTS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ "${DATA[26]}" ] && pushMQTTData "$inv_id" "Battery_discharge_current" "${DATA[26]}"
|
|
||||||
|
|
||||||
# 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
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Publish shared configuration parameters for ALL inverters (replicate QPIRI data)
|
# Publish overall active inverter count
|
||||||
if [ "$QPIRI_SUCCESS" = true ] && [ ${#SUCCESS_INV_IDS[@]} -gt 0 ]; then
|
pushMQTTData "system" "parallel_count" "${#SUCCESS_IDS[@]}"
|
||||||
echo ""
|
|
||||||
echo "Publishing shared configuration to all inverters..."
|
|
||||||
|
|
||||||
for inv_id in "${SUCCESS_INV_IDS[@]}"; do
|
echo "Push completed: ${#SUCCESS_IDS[@]}/$CASCADE_COUNT inverters OK"
|
||||||
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"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user