Skip to content

Commit 017344a

Browse files
committed
fix: EVC register segmentation, firmware-version gating, doc-aligned entities
SolaX support (ticket 703158) confirmed the EVC holding registers are internally segmented: 0x600-0x650 is one segment, 0x660-0x66F another (used for master-slave parallel operation). A function 0x03 read that spans the boundary silently returns 0 for second-segment registers. Reproduced on an X3-EVC-11kW Gen1 (ARM V1.18). Fixed by starting a new read block at 0x668 and restoring the default block_size 100 (the block_size=32 workaround predating this knowledge is no longer needed). The plugin now publishes its firmware version (input 0x25, e.g. V1.18 -> 118) as hub.modbus_protocol_version, enabling modbus_min/ modbus_max gating for EV chargers. Entities on registers dated by the protocol document's revision history are gated accordingly, and every register pair that changed location across firmware versions is exposed as a single entity identity whose backing register follows the firmware: Charge Power 0x08-0x0A -> 0x100-0x102, Run Mode 0x1D -> 0x106, Charge Phase 0x625 -> 0x63B, Charge Mode (0x104 sensor on V1.12-13, 0x641 select from V1.14, 0x669 select on GEN2). The Alt-suffixed duplicate entities are gone. Support suggested using the documented 0x64F (limit_current_allmode) for the maximum current. Live testing shows it is the persistent device limit rather than the setpoint (SolaXCloud itself writes 0x668; external writes to 0x64F are acknowledged but ignored), so Max Charge Current stays on 0x668 and a new optional max_key field on number descriptions caps the Max/Min Charge Current sliders dynamically from the 0x64F readback. New doc-based entities (disabled by default unless noted): Unbalanced Power 0x63C and Unbalanced Switch 0x63D (X3), Mode Button 0x63E, Min Charge Current 0x63F, Datahub Charge Power 0x643, Green 30s Delay 0x644 (switch), Parallel Support 0x107, WiFi S/N 0x607, Customized S/N 0x629. Alignments: Overload Limit renamed Overvoltage Limit (doc OverVoltSet), Charger Type name unified across GEN1/GEN2, RSSI renamed WiFi RSSI, per-phase Charge Current sensors consistently disabled by default, Datahub Charge Current disabled by default, charge_added_cum GEN2 scope narrowed from GEN2|GEN3|GEN4. Input 0x2D/0x2E are marked in code as conflicting between the Gen1 V3.5 and GEN2 documents (clarification requested from SolaX). Register 0x642 Parallel_enable is deliberately not exposed: the device returns values outside the documented 0/1 range and SolaXCloud's parallel toggle does not use it.
1 parent 27875b3 commit 017344a

4 files changed

Lines changed: 351 additions & 33 deletions

File tree

custom_components/solax_modbus/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ class BaseModbusNumberEntityDescription(NumberEntityDescription):
390390
prevent_update: bool = False # if set to True, value will not be re-read/updated with each polling cycle;
391391
# update only when read value changes
392392
sensor_key: str | None = None # only specify this if corresponding sensor has a different key name
393+
max_key: str | None = None # key of a sensor whose value dynamically overrides native_max_value
393394
depends_on: list[str] | None = None # list of modbus register keys that must be read
394395
display_as_box: bool = True # display numbers as an input box (default); set False for a slider.
395396
suggested_display_precision: int | None = None

custom_components/solax_modbus/number.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,15 @@ def name(self) -> str:
184184
def unique_id(self) -> str | None:
185185
return f"{self._platform_name}_{self._key}"
186186

187+
@property
188+
def native_max_value(self) -> float:
189+
max_key = self.entity_description.max_key
190+
if max_key:
191+
value = self._hub.data.get(max_key)
192+
if value:
193+
return float(value)
194+
return self._attr_native_max_value
195+
187196
@property
188197
def native_value(self) -> float | None:
189198
descr = self.entity_description
@@ -267,6 +276,10 @@ async def async_set_native_value(self, value: float) -> None:
267276
f"writing {self._platform_name} {self._key} number register {self._register} value {pl} after div by readscale {self.entity_description.read_scale} scale {self._attr_scale} with mode {self._write_method}"
268277
)
269278
await self._hub.async_write_registers_multi(unit=self._modbus_addr, address=self._register, payload=pl)
279+
if self._write_method in (WRITE_MULTISINGLE_MODBUS, WRITE_SINGLE_MODBUS, WRITE_MULTI_MODBUS):
280+
# Publish the written value locally: the readback register only catches up on a
281+
# later poll, so the entity would otherwise keep reporting the previous value.
282+
self._hub.data[self._key] = value
270283
elif self._write_method == WRITE_DATA_LOCAL:
271284
_LOGGER.info(f"*** local data written {self._key}: {payload}")
272285
# corresponding_sensor = self._hub.preventSensors.get(self.entity_description.key, None)

0 commit comments

Comments
 (0)