Skip to content

Commit e3ff905

Browse files
authored
Merge pull request #2238 from TCWORLD/x1-vast-min-soc
Add Minimum SoC exception for SolaX X1-VAST
2 parents 27875b3 + 3c9ab2f commit e3ff905

4 files changed

Lines changed: 41 additions & 0 deletions

File tree

custom_components/solax_modbus/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ class BaseModbusNumberEntityDescription(NumberEntityDescription):
383383
state: str | None = None
384384
max_exceptions: list[tuple[str, int | float]] | None = None # None or list with structure [ ('U50EC' , 40,) ]
385385
min_exceptions_minus: list[tuple[str, int | float]] | None = None # same structure as max_exceptions, values are applied with a minus
386+
min_exceptions: list[tuple[str, int | float]] | None = None # None or list with structure [ ('U50EC' , 10,) ]
386387
blacklist: list[str] | None = None # None or list of serial number prefixes like
387388
write_method: int = WRITE_SINGLE_MODBUS # WRITE_SINGLE_MOBUS or WRITE_MULTI_MODBUS or WRITE_DATA_LOCAL
388389
initvalue: int | None = None # initial default value for WRITE_DATA_LOCAL entities

custom_components/solax_modbus/number.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ def __init__(
122122
) in number_info.max_exceptions:
123123
if hub.seriesnumber.startswith(prefix):
124124
self._attr_native_max_value = native_value
125+
if number_info.min_exceptions:
126+
for (
127+
prefix,
128+
native_value,
129+
) in number_info.min_exceptions:
130+
if hub.seriesnumber.startswith(prefix):
131+
self._attr_native_min_value = native_value
125132
if number_info.min_exceptions_minus:
126133
for (
127134
prefix,

custom_components/solax_modbus/plugin_solax.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,10 @@ def value_function_bms_2_max_charge(initval: int, descr: Any, datadict: dict[str
18151815
# ('H1E', 1 ), # more specific entry comes last and wins
18161816
]
18171817

1818+
MIN_SOC: list[tuple[str, int | float]] = [
1819+
("10M", 0), # Gen6 X1-VAST allow discharge to 0% per #2187
1820+
]
1821+
18181822
NUMBER_TYPES: Sequence["SolaxModbusNumberEntityDescription"] = [
18191823
###
18201824
#
@@ -1949,6 +1953,7 @@ def value_function_bms_2_max_charge(initval: int, descr: Any, datadict: dict[str
19491953
native_max_value=100,
19501954
native_step=1,
19511955
native_unit_of_measurement=PERCENTAGE,
1956+
min_exceptions=MIN_SOC,
19521957
initvalue=95,
19531958
register_data_type=REGISTER_U16,
19541959
write_method=WRITE_DATA_LOCAL,
@@ -1963,6 +1968,7 @@ def value_function_bms_2_max_charge(initval: int, descr: Any, datadict: dict[str
19631968
native_max_value=100,
19641969
native_step=1,
19651970
native_unit_of_measurement=PERCENTAGE,
1971+
min_exceptions=MIN_SOC,
19661972
initvalue=10,
19671973
register_data_type=REGISTER_U16,
19681974
write_method=WRITE_DATA_LOCAL,
@@ -2214,6 +2220,7 @@ def value_function_bms_2_max_charge(initval: int, descr: Any, datadict: dict[str
22142220
native_max_value=25,
22152221
native_step=1,
22162222
native_unit_of_measurement=PERCENTAGE,
2223+
min_exceptions=MIN_SOC,
22172224
allowedtypes=AC | HYBRID | GEN4 | GEN5 | GEN6 | EPS,
22182225
icon="mdi:battery-charging-low",
22192226
),
@@ -2265,6 +2272,7 @@ def value_function_bms_2_max_charge(initval: int, descr: Any, datadict: dict[str
22652272
native_max_value=100,
22662273
native_step=1,
22672274
native_unit_of_measurement=PERCENTAGE,
2275+
min_exceptions=MIN_SOC,
22682276
allowedtypes=AC | HYBRID | GEN4 | GEN5 | GEN6,
22692277
icon="mdi:battery-charging-low",
22702278
),
@@ -2427,6 +2435,7 @@ def value_function_bms_2_max_charge(initval: int, descr: Any, datadict: dict[str
24272435
native_max_value=100,
24282436
native_step=1,
24292437
native_unit_of_measurement=PERCENTAGE,
2438+
min_exceptions=MIN_SOC,
24302439
allowedtypes=AC | HYBRID | GEN4 | GEN5 | GEN6,
24312440
icon="mdi:battery-charging-low",
24322441
),

tests/unit/test_number_exceptions_type_regressions.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,30 @@ def test_exception_value_used_as_native_max_value() -> None:
137137
assert isinstance(native_max_value, float)
138138

139139

140+
def test_exception_value_used_as_native_min_value() -> None:
141+
"""Test that exception values are used as native_min_value (float).
142+
143+
This documents why the exception values need to support float: they are
144+
directly assigned to native_min_value which is typed as float in number.py.
145+
"""
146+
max_exceptions: list[tuple[str, int | float]] = [
147+
("L30E", 15.0),
148+
("10M", 1.0),
149+
]
150+
151+
serial_number = "10M123456"
152+
native_min_value: float = 10.0 # Default
153+
154+
# Simulate exception lookup and assignment
155+
for prefix, value in max_exceptions:
156+
if serial_number.startswith(prefix):
157+
native_min_value = float(value) # Must be float
158+
break
159+
160+
assert native_min_value == 1.0
161+
assert isinstance(native_min_value, float)
162+
163+
140164
def test_list_invariance_with_mixed_types() -> None:
141165
"""Test that list invariance is satisfied with int | float union type.
142166

0 commit comments

Comments
 (0)