Fix: Wallbox-Read resilient gegen IllegalAddress, Relais-Matrix ergänzt
- wallbox_client: Bei Reg-Fehler Range überspringen statt komplett abbrechen → Meter-Daten kommen weiter wenn EVSE-Block (0x0060) nicht verfügbar - wallbox_client: set_current() schreibt jetzt auch EMS_RELAIS_REG (0x00A1) mit korrekter Phasen-Bitmaske (1→0x0001, 2→0x0003, 3→0x0007) - ems_controller: phases wird an set_current() und Zwangsladen übergeben Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -99,7 +99,7 @@ class EmsController:
|
|||||||
self._no_pv_since = None
|
self._no_pv_since = None
|
||||||
self._forced_charging = False
|
self._forced_charging = False
|
||||||
ma = self._surplus_to_ma(pv_surplus_w)
|
ma = self._surplus_to_ma(pv_surplus_w)
|
||||||
wallbox_reader.set_current(ma)
|
wallbox_reader.set_current(ma, self.phases)
|
||||||
return f"PV-Laden {ma // 1000:.1f}A ({pv_surplus_w:.0f}W)"
|
return f"PV-Laden {ma // 1000:.1f}A ({pv_surplus_w:.0f}W)"
|
||||||
|
|
||||||
# Kein PV
|
# Kein PV
|
||||||
@@ -113,7 +113,7 @@ class EmsController:
|
|||||||
target = self._next_target()
|
target = self._next_target()
|
||||||
mins_left = (target - datetime.now()).total_seconds() / 60
|
mins_left = (target - datetime.now()).total_seconds() / 60
|
||||||
if not self._forced_charging:
|
if not self._forced_charging:
|
||||||
wallbox_reader.set_current(32000)
|
wallbox_reader.set_current(32000, self.phases)
|
||||||
self._forced_charging = True
|
self._forced_charging = True
|
||||||
log.info("[EMS] Zwangsladen gestartet — %dmin bis %02d:00", int(mins_left), self.target_hour)
|
log.info("[EMS] Zwangsladen gestartet — %dmin bis %02d:00", int(mins_left), self.target_hour)
|
||||||
return f"Zwangsladen ({no_pv_h:.1f}h kein PV, {int(mins_left)}min bis {self.target_hour:02d}:00)"
|
return f"Zwangsladen ({no_pv_h:.1f}h kein PV, {int(mins_left)}min bis {self.target_hour:02d}:00)"
|
||||||
|
|||||||
@@ -12,11 +12,14 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
SLAVE = 0 # Kathrein: Unit-ID 0 (Broadcast)
|
SLAVE = 0 # Kathrein: Unit-ID 0 (Broadcast)
|
||||||
EMS_CTRL_REG = 0x00A0
|
EMS_CTRL_REG = 0x00A0
|
||||||
|
EMS_RELAIS_REG = 0x00A1
|
||||||
EMS_CURRENT_REG = 0x00A2
|
EMS_CURRENT_REG = 0x00A2
|
||||||
EMS_ENABLE = 0x8000
|
EMS_ENABLE = 0x8000
|
||||||
MIN_CURRENT_MA = 6000
|
MIN_CURRENT_MA = 6000
|
||||||
MAX_CURRENT_MA = 32000
|
MAX_CURRENT_MA = 32000
|
||||||
|
|
||||||
|
_PHASE_MASK = {1: 0x0001, 2: 0x0003, 3: 0x0007}
|
||||||
|
|
||||||
|
|
||||||
class WallboxReader:
|
class WallboxReader:
|
||||||
def __init__(self, host: str, port: int = 502, timeout: float = 10.0):
|
def __init__(self, host: str, port: int = 502, timeout: float = 10.0):
|
||||||
@@ -45,21 +48,22 @@ class WallboxReader:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
reg_cache: Dict[int, int] = {}
|
reg_cache: Dict[int, int] = {}
|
||||||
|
any_ok = False
|
||||||
for start, length in inverter.read_ranges:
|
for start, length in inverter.read_ranges:
|
||||||
try:
|
try:
|
||||||
result = self._client.read_holding_registers(start, count=length, slave=SLAVE)
|
result = self._client.read_holding_registers(start, count=length, slave=SLAVE)
|
||||||
if result.isError():
|
if result.isError():
|
||||||
log.error("[Wallbox] FC03 Fehler Reg 0x%04X: %s", start, result)
|
log.warning("[Wallbox] FC03 Reg 0x%04X nicht verfügbar: %s", start, result)
|
||||||
self._disconnect()
|
continue
|
||||||
return None
|
|
||||||
for i, val in enumerate(result.registers):
|
for i, val in enumerate(result.registers):
|
||||||
reg_cache[start + i] = val
|
reg_cache[start + i] = val
|
||||||
|
any_ok = True
|
||||||
except ModbusException as e:
|
except ModbusException as e:
|
||||||
log.error("[Wallbox] Modbus Ausnahme: %s", e)
|
log.error("[Wallbox] Modbus Ausnahme: %s", e)
|
||||||
self._disconnect()
|
self._disconnect()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return _extract(inverter.sensors, reg_cache)
|
return _extract(inverter.sensors, reg_cache) if any_ok else None
|
||||||
|
|
||||||
def enable_ems(self) -> bool:
|
def enable_ems(self) -> bool:
|
||||||
if not self._connect():
|
if not self._connect():
|
||||||
@@ -71,16 +75,20 @@ class WallboxReader:
|
|||||||
log.error("[Wallbox] EMS enable Fehler: %s", e)
|
log.error("[Wallbox] EMS enable Fehler: %s", e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def set_current(self, ma: int) -> bool:
|
def set_current(self, ma: int, phases: int = 3) -> bool:
|
||||||
"""Ladestrom setzen. ma=0 → Pause, ma=0xFFFF → Abbruch."""
|
"""Ladestrom setzen. ma=0 → Pause, ma=0xFFFF → Abbruch."""
|
||||||
if not self._connect():
|
if not self._connect():
|
||||||
return False
|
return False
|
||||||
val = 0 if ma == 0 else max(MIN_CURRENT_MA, min(MAX_CURRENT_MA, ma))
|
val = 0 if ma == 0 else max(MIN_CURRENT_MA, min(MAX_CURRENT_MA, ma))
|
||||||
|
relais = _PHASE_MASK.get(phases, 0x0007)
|
||||||
try:
|
try:
|
||||||
|
r = self._client.write_register(EMS_RELAIS_REG, relais, slave=SLAVE)
|
||||||
|
if r.isError():
|
||||||
|
log.warning("[Wallbox] Relais-Matrix Fehler: %s", r)
|
||||||
r = self._client.write_register(EMS_CURRENT_REG, val, slave=SLAVE)
|
r = self._client.write_register(EMS_CURRENT_REG, val, slave=SLAVE)
|
||||||
ok = not r.isError()
|
ok = not r.isError()
|
||||||
if ok:
|
if ok:
|
||||||
log.info("[Wallbox] Strom gesetzt: %d mA", val)
|
log.info("[Wallbox] Strom gesetzt: %d mA (%d Phase(n))", val, phases)
|
||||||
return ok
|
return ok
|
||||||
except ModbusException as e:
|
except ModbusException as e:
|
||||||
log.error("[Wallbox] set_current Fehler: %s", e)
|
log.error("[Wallbox] set_current Fehler: %s", e)
|
||||||
|
|||||||
Reference in New Issue
Block a user