Fix POP01 encoding bug + harden grid-cal revert verification

Root cause of the grid-calibration auto-revert silently failing: lvx-control
and flash.py encode output_priority solar_battery_utility as "POP01", but PI18
POP is single-digit — the inverter silently rejects "POP01" (raw "POP1" works,
matches the POP_PIRI decoder). Compounded by powermon's adhoc queue wedging,
which dropped commands entirely until a restart. So the monitor logged "revert
done" while the cluster sat in SUB/grid mode for ~1.5h (no harm: battery full,
just running loads on grid).

- lvx-control + flash.py: POP_MAP "01" -> "1" (also patched the live
  /usr/local/bin/lvx-control + restarted; verified it now emits POP1).
- grid-cal-monitor: revert now VERIFIES via behavior (line_power_direction
  leaves 'input'), and on failure restarts powermon and re-sends raw POP1/PCP0,0,
  with a loud manual-fallback message. No more trust-the-publish.

Recovery for the live run: restarted powermon (unstuck adhoc) + raw POP1 + PCP0,0;
confirmed POP=Solar-Battery-Utility, PCP=Solar First, mode=Battery, line_dir=donothing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-25 21:37:16 -04:00
parent 76765a95ed
commit 4bfa021719
3 changed files with 21 additions and 5 deletions

View File

@@ -35,13 +35,29 @@ try:
except:print("")'; }
P(){ echo "sensor.eg4_lifepower4_lifepower4_${1}_lifepower4_${1}_${2}"; }
pub(){ mosquitto_pub -h "$BHOST" -p "$BPORT" -u "$BUSER" -P "$BPASS" -t "solar/control/lvx6048/$1" -m "$2"; }
raw(){ for u in 1 2; do mosquitto_pub -h "$BHOST" -p "$BPORT" -u "$BUSER" -P "$BPASS" -t "powermon/lvx6048_${u}/addcommand" -m "$1"; done; }
# revert VERIFIES it actually took (the friendly path can silently fail — lvx-control
# used to encode POP01 which the inverter rejects, and powermon's adhoc queue can wedge).
# Ground truth = behavior: in SBU with a full bank, line_power_direction leaves 'input'.
reverted_ok(){
local ld pop; ld=$(st sensor.lvx6048_lvx6048_1_line_power_direction)
pop=$(st sensor.lvx6048_lvx6048_1_output_source_priority)
{ [ -n "$ld" ] && [ "$ld" != "input" ]; } || echo "$pop" | grep -q "Battery - Utility"
}
revert(){
[ "$REVERTED" = 1 ] && return 0
log "REVERT: output_priority->solar_battery_utility, charger_priority->solar_first"
pub output_priority solar_battery_utility; sleep 3
pub charger_priority solar_first; sleep 8
log "REVERT done. live POP=$(st sensor.lvx6048_lvx6048_1_output_source_priority)/$(st sensor.lvx6048_lvx6048_2_output_source_priority) PCP=$(st sensor.lvx6048_lvx6048_1_charger_source_priority)/$(st sensor.lvx6048_lvx6048_2_charger_source_priority)"
pub output_priority solar_battery_utility; sleep 3; pub charger_priority solar_first; sleep 12
if reverted_ok; then log "REVERT verified (line_dir=$(st sensor.lvx6048_lvx6048_1_line_power_direction))"; REVERTED=1; return 0; fi
# escalate: powermon adhoc may be wedged and/or friendly encode rejected -> restart + raw
for try in 1 2; do
log "REVERT not effective yet — restart powermon + raw POP1/PCP0,0 (try $try)"
sudo systemctl restart powermon.service powermon2.service 2>/dev/null; sleep 12
raw POP1; sleep 3; raw PCP0,0; sleep 15
if reverted_ok; then log "REVERT verified after escalation"; REVERTED=1; return 0; fi
done
log "REVERT: !!! COULD NOT CONFIRM — still grid-priority. Manually run: raw POP1 to both addcommand topics (POP1, not POP01)."
REVERTED=1
}
trap 'revert; log "exit"' EXIT INT TERM

View File

@@ -44,7 +44,7 @@ ADHOC_TOPICS = (
# Intentionally a strict subset of flash.py's SCHEDULE — only the safe,
# day-to-day knobs. The risky-end calibration setters live in flash.py.
POP_MAP = {"solar_utility_battery": "0", "solar_battery_utility": "01"}
POP_MAP = {"solar_utility_battery": "0", "solar_battery_utility": "1"} # PI18 POP is single-digit; "01" is malformed -> inverter silently rejects (confirmed 2026-06-25, broke a calibration auto-revert)
PCP_MAP = {"solar_first": "0", "solar_and_utility": "1", "solar_only": "2"}
PSP_MAP = {"battery_load_utility_ac": "0", "load_battery_utility": "1"}
ALLOWED_MCHGC = (10, 20, 30, 40, 50, 60, 70, 80)

View File

@@ -79,7 +79,7 @@ KEY_DOCS: dict[str, str] = {
"grid_tie": "enum: enabled | disabled (PEI/PDI)",
}
POP_MAP = {"solar_utility_battery": "0", "solar_battery_utility": "01"}
POP_MAP = {"solar_utility_battery": "0", "solar_battery_utility": "1"} # PI18 POP is single-digit; "01" is malformed -> inverter silently rejects (confirmed 2026-06-25)
PCP_MAP = {"solar_first": "0", "solar_and_utility": "1", "solar_only": "2"}
PSP_MAP = {"battery_load_utility_ac": "0", "load_battery_utility": "1"}
PBT_MAP = {"AGM": "0", "FLOODED": "1", "USER": "2"}