Skip to content

Commit 7b74812

Browse files
committed
External Generator: own sub-device with dependency-driven availability
The 33 generator entities were gated by the Dry Contact Box option, which is a different feature - in the SolaX app the external generator is its own tree. They move to a "<hub> External Generator" device behind a new "External Generator Support" option (read_gen, off by default, translated in all shipped languages), and each entity only exists while its parent in that tree is active: External Generator (0xC7): Disabled | ATS Control | Dry Contact Start Gen Method (0xE3): only with Dry Contact Switch On/Off SoC (0xE4/0xE5): only with Reference SOC Max Run Time (0xE6), Max Rest Time (0xE7), Hold Min Power (0x10E) Allow Work Start/Stop Time (0xE8/0xE9), Max Charge (0xC8) Charge Battery from Generator (0x109 on Gen4, 0x10C on Gen5/Gen6) Charge Battery To (0x10A on Gen4, 0x10D on Gen5/Gen6) Schedule 2 (0x104 on Gen4, 0x107 on Gen5/Gen6): unlocks the second Charge Period and Allowed Discharge Period time pair Names follow the cloud app now that the device provides the context, with keys and entity ids unchanged: Generator Max Charge -> Max Charge (in kW, 0.01 scale, 0-300 kW instead of a raw 10 W register), Generator Charge -> Charge Battery from Generator, Generator Min Power -> Hold Min Power, Generator Time 2 -> Schedule 2, Generator Charge SOC -> Charge Battery To. Verified live on an X3-Hybrid-G4 15kW by changing each setting in the SolaX app and tracking the readbacks. Stacked on the sub-device infrastructure PR.
1 parent 27c579d commit 7b74812

13 files changed

Lines changed: 210 additions & 111 deletions

File tree

custom_components/solax_modbus/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@
105105
from .const import (
106106
CONF_READ_EPS as CONF_READ_EPS,
107107
)
108+
from .const import (
109+
CONF_READ_GEN as CONF_READ_GEN,
110+
)
108111
from .const import (
109112
CONF_READ_PM as CONF_READ_PM,
110113
)
@@ -129,6 +132,9 @@
129132
from .const import (
130133
DEFAULT_READ_EPS as DEFAULT_READ_EPS,
131134
)
135+
from .const import (
136+
DEFAULT_READ_GEN as DEFAULT_READ_GEN,
137+
)
132138
from .const import (
133139
DEFAULT_READ_PM as DEFAULT_READ_PM,
134140
)
@@ -445,11 +451,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
445451
# Device groups that a config option can switch off, and the option controlling each.
446452
# Display names for sub-devices, where a plain title-case of the group key would read badly.
447453
DEVICE_GROUP_NAMES: dict[str, str] = {
454+
"external_generator": "External Generator",
448455
"eps": "EPS",
449456
"pm": "Parallel",
450457
}
451458

452459
GATED_DEVICE_GROUPS: dict[str, tuple[str, bool]] = {
460+
"external_generator": (CONF_READ_GEN, DEFAULT_READ_GEN),
453461
"eps": (CONF_READ_EPS, DEFAULT_READ_EPS),
454462
"pm": (CONF_READ_PM, DEFAULT_READ_PM),
455463
"ENERGY_DASHBOARD": (CONF_ENERGY_DASHBOARD_DEVICE, DEFAULT_ENERGY_DASHBOARD_DEVICE),

custom_components/solax_modbus/config_flow.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
CONF_READ_BATTERY,
4747
CONF_READ_DCB,
4848
CONF_READ_EPS,
49+
CONF_READ_GEN,
4950
CONF_READ_PM,
5051
CONF_SCAN_INTERVAL_FAST,
5152
CONF_SCAN_INTERVAL_MEDIUM,
@@ -64,6 +65,7 @@
6465
DEFAULT_READ_BATTERY,
6566
DEFAULT_READ_DCB,
6667
DEFAULT_READ_EPS,
68+
DEFAULT_READ_GEN,
6769
DEFAULT_READ_PM,
6870
DEFAULT_SCAN_INTERVAL,
6971
DEFAULT_SERIAL_PORT,
@@ -155,6 +157,7 @@ def _configured_hub_names(handler: SchemaCommonFlowHandler) -> set[str]:
155157
vol.Optional(CONF_ENERGY_DASHBOARD_DEVICE, default=DEFAULT_ENERGY_DASHBOARD_DEVICE): bool,
156158
vol.Optional(CONF_READ_EPS, default=DEFAULT_READ_EPS): bool,
157159
vol.Optional(CONF_READ_DCB, default=DEFAULT_READ_DCB): bool,
160+
vol.Optional(CONF_READ_GEN, default=DEFAULT_READ_GEN): bool,
158161
vol.Optional(CONF_READ_PM, default=DEFAULT_READ_PM): bool,
159162
vol.Optional(CONF_TIME_OUT, default=DEFAULT_TIME_OUT): int,
160163
}
@@ -177,6 +180,7 @@ def _configured_hub_names(handler: SchemaCommonFlowHandler) -> set[str]:
177180
vol.Optional(CONF_ENERGY_DASHBOARD_DEVICE, default=DEFAULT_ENERGY_DASHBOARD_DEVICE): bool,
178181
vol.Optional(CONF_READ_EPS, default=DEFAULT_READ_EPS): bool,
179182
vol.Optional(CONF_READ_DCB, default=DEFAULT_READ_DCB): bool,
183+
vol.Optional(CONF_READ_GEN, default=DEFAULT_READ_GEN): bool,
180184
vol.Optional(CONF_READ_PM, default=DEFAULT_READ_PM): bool,
181185
vol.Optional(CONF_TIME_OUT, default=DEFAULT_TIME_OUT): int,
182186
}
@@ -376,6 +380,8 @@ async def _option_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
376380
"""Options schema without the feature switches the selected plugin does not implement."""
377381
plugin_name = handler.options.get(CONF_PLUGIN)
378382
hidden: set[str] = set()
383+
if not _plugin_supports_device_group(plugin_name, "external_generator"):
384+
hidden.add(CONF_READ_GEN)
379385
if not _plugin_supports_energy_dashboard(plugin_name):
380386
hidden.add(CONF_ENERGY_DASHBOARD_DEVICE)
381387
for option, flag_name in ((CONF_READ_EPS, "EPS"), (CONF_READ_PM, "PM")):

custom_components/solax_modbus/const.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class UnitOfReactivePower(StrEnum): # type: ignore[no-redef]
5050
CONF_INVERTER_POWER_KW = "inverter_power_kw"
5151
CONF_READ_EPS = "read_eps"
5252
CONF_READ_DCB = "read_dcb"
53+
CONF_READ_GEN = "read_gen"
5354
CONF_READ_PM = "read_pm"
5455
CONF_MODBUS_ADDR = "read_modbus_addr"
5556
CONF_INTERFACE = "interface"
@@ -66,6 +67,7 @@ class UnitOfReactivePower(StrEnum): # type: ignore[no-redef]
6667
DEFAULT_SERIAL_PORT = "/dev/ttyUSB0"
6768
DEFAULT_READ_EPS = False
6869
DEFAULT_READ_DCB = False
70+
DEFAULT_READ_GEN = False
6971
DEFAULT_READ_PM = False
7072
DEFAULT_BAUDRATE = "19200"
7173
DEFAULT_PLUGIN = "solax"

0 commit comments

Comments
 (0)