fixed battery SoC and various other regs
This commit is contained in:
@@ -338,23 +338,28 @@ def decode_eg4_modbus_regs(regs: list[int], expose_raw: bool = False) -> dict[st
|
||||
out["cell_lowest"] = cells_v.index(vmin) + 1
|
||||
out["cell_highest"] = cells_v.index(vmax) + 1
|
||||
|
||||
# --- temperatures (regs 18-21 = Temp_01..04, reg 24 = Temp_PCB) ---
|
||||
# --- temperatures (regs 18-20 = Temp_01..03) ---
|
||||
# Regs 21 and 24 were previously labeled temperature_04 / temperature_pcb
|
||||
# (carryover from a misread of the lv_host.app schema) but are actually
|
||||
# the BMS's SoC tracker — see the SoC block below. The pack only exposes
|
||||
# three real cell temps.
|
||||
out["temperature_01"] = regs[18]
|
||||
out["temperature_02"] = regs[19]
|
||||
out["temperature_03"] = regs[20]
|
||||
out["temperature_04"] = regs[21]
|
||||
out["temperature_pcb"] = regs[24]
|
||||
|
||||
# --- SoC / SoH (regs 22, 23) ---
|
||||
out["soc"] = regs[22]
|
||||
out["soh"] = regs[23]
|
||||
|
||||
# --- heater / status (regs 25-30) ---
|
||||
# reg 30 has been observed = 1 on a healthy pack; treat as binary
|
||||
out["heater"] = "on" if regs[30] & 0x01 else "off"
|
||||
|
||||
# --- max charge/discharge current limit (reg 31), A ---
|
||||
out["max_current_limit"] = round(regs[31] / 100.0, 2)
|
||||
# --- SoC (regs 21 / 24) ---
|
||||
# Identified 2026-05-16 by comparing a week of register snapshots against
|
||||
# the bank's known SoC drift (95% → ~40%): only regs 21 and 24 moved in
|
||||
# lockstep with the physical state change. regs 22/23 are pegged at 100
|
||||
# across all observed conditions — they are emitted below as raw fields
|
||||
# for visibility but do NOT name them soc/soh until we know what they are.
|
||||
# r24 stays within ±1 of r21 (probably a re-snapshotted copy from later
|
||||
# in the BMS poll loop); kept as `soc_alt` (diagnostic) for now so we
|
||||
# can watch whether the relationship holds.
|
||||
out["soc"] = regs[21]
|
||||
out["soc_alt"] = regs[24]
|
||||
out["bms_field_22"] = regs[22] # was misnamed `soc` — purpose unknown
|
||||
out["bms_field_23"] = regs[23] # was misnamed `soh` — purpose unknown
|
||||
|
||||
# --- error / alarm word (reg 35) ---
|
||||
# reg 33/34 used to decode as warning_/protection_ bitfields but were
|
||||
@@ -362,12 +367,28 @@ def decode_eg4_modbus_regs(regs: list[int], expose_raw: bool = False) -> dict[st
|
||||
# reg 35 is the canonical alarm word; 0 on healthy packs.
|
||||
out["error_code"] = regs[35]
|
||||
|
||||
# --- static-ish (regs 36, 37) ---
|
||||
# --- static-ish (regs 36, 37, 38) ---
|
||||
out["cell_count"] = regs[36]
|
||||
out["capacity_ah"] = round(regs[37] / 10.0, 1)
|
||||
out["remaining_ah"] = round(regs[38] / 100.0, 2)
|
||||
out["cycle_count"] = regs[39]
|
||||
out["battery_mode"] = regs[40]
|
||||
# Several other regs in this block were previously decoded with names that
|
||||
# turned out not to match reality. Removed 2026-05-16, pending re-derivation:
|
||||
# reg 30 → was "heater" via bit-0 test; bit 0 is always 0 across all
|
||||
# 6 packs in all observed states, so the heater claim is
|
||||
# unprovable. The reg itself has values 6 / 38 / 42 that
|
||||
# vary by pack batch (older packs ~38-42, newer ~6) and
|
||||
# look like a status-flag bitfield, not heater on/off.
|
||||
# reg 31 → was "max_current_limit" in A; value 5493 → 54.93 sits
|
||||
# squarely in the float-voltage range (54-55 V) and is
|
||||
# identical across all packs regardless of charge state.
|
||||
# Almost certainly V*100 of a config threshold, not amps.
|
||||
# reg 39 → was "cycle_count"; pegged at 0 across all packs after
|
||||
# weeks of cycling. Not the cycle counter.
|
||||
# reg 40 → was "battery_mode"; pegged at 7 across all packs in all
|
||||
# states. Possibly a battery-type code or fixed mode flag,
|
||||
# but value never changes so naming it "mode" was misleading.
|
||||
# All four entities + their HA discovery configs were retracted at deploy.
|
||||
# Use --expose-raw / register_NN entities to investigate further.
|
||||
|
||||
# regs 41-42: u16 version codes — superseded by `firmware_version` ASCII
|
||||
# decode below; available via expose_raw if needed
|
||||
@@ -703,14 +724,12 @@ _FIELD_META.update({
|
||||
"temperature_01": ("°C", "temperature", "measurement", "mdi:thermometer"),
|
||||
"temperature_02": ("°C", "temperature", "measurement", "mdi:thermometer"),
|
||||
"temperature_03": ("°C", "temperature", "measurement", "mdi:thermometer"),
|
||||
"temperature_04": ("°C", "temperature", "measurement", "mdi:thermometer"),
|
||||
"temperature_pcb": ("°C", "temperature", "measurement", "mdi:chip"),
|
||||
"heater": (None, None, None, "mdi:heating-coil"),
|
||||
"max_current_limit": ("A", "current", "measurement", "mdi:current-dc"),
|
||||
"soc_alt": ("%", "battery", "measurement", "mdi:battery-50"),
|
||||
"bms_field_22": (None, None, "measurement", "mdi:numeric"),
|
||||
"bms_field_23": (None, None, "measurement", "mdi:numeric"),
|
||||
"error_code": (None, None, None, "mdi:alert-octagon"),
|
||||
"cell_count": (None, None, "measurement", "mdi:numeric"),
|
||||
"remaining_ah": ("Ah", None, "measurement", "mdi:battery-clock"),
|
||||
"battery_mode": (None, None, None, "mdi:state-machine"),
|
||||
"model": (None, None, None, "mdi:battery-outline"),
|
||||
"firmware_version": (None, None, None, "mdi:chip"),
|
||||
"firmware_date": (None, None, None, "mdi:calendar"),
|
||||
@@ -726,9 +745,10 @@ def field_meta(key: str) -> tuple[str | None, str | None, str | None, str | None
|
||||
_FIELD_PRECISION: dict[str, int] = {
|
||||
"pack_voltage": 2,
|
||||
"pack_current": 2,
|
||||
"max_current_limit": 2,
|
||||
"soc": 0,
|
||||
"soh": 0,
|
||||
"soc_alt": 0,
|
||||
"bms_field_22": 0,
|
||||
"bms_field_23": 0,
|
||||
"cycle_count": 0,
|
||||
"cell_voltage_min": 3,
|
||||
"cell_voltage_max": 3,
|
||||
@@ -739,13 +759,11 @@ _FIELD_PRECISION: dict[str, int] = {
|
||||
"capacity_ah": 1,
|
||||
"remaining_ah": 1,
|
||||
"error_code": 0,
|
||||
"battery_mode": 0,
|
||||
}
|
||||
for _i in range(1, 17):
|
||||
_FIELD_PRECISION[f"cell_{_i:02d}_voltage"] = 3
|
||||
for _i in range(1, 7):
|
||||
_FIELD_PRECISION[f"temperature_{_i}"] = 0
|
||||
_FIELD_PRECISION["temperature_pcb"] = 0
|
||||
|
||||
|
||||
def field_precision(key: str) -> int | None:
|
||||
|
||||
Reference in New Issue
Block a user