From 2eb74a36259032824265e97806c030350b1e6ec1 Mon Sep 17 00:00:00 2001 From: fboundy Date: Wed, 13 Aug 2025 13:30:50 +0100 Subject: [PATCH 01/11] Fix bug in password entry --- custom_components/solis/config_flow.py | 57 +++++++++++++++----------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/custom_components/solis/config_flow.py b/custom_components/solis/config_flow.py index 7e2fd6c..8c5d299 100644 --- a/custom_components/solis/config_flow.py +++ b/custom_components/solis/config_flow.py @@ -53,19 +53,26 @@ async def async_step_init(self, user_input: dict[str, Any] | None = None) -> Flo errors = {} if user_input is not None: - updated_config = {} - for key in self.config_entry.data.keys(): - updated_config[key] = self.config_entry.data[key] - updated_config[CONF_CONTROL] = False - for key in (CONF_CONTROL, CONF_PASSWORD, CONF_REFRESH_OK, CONF_REFRESH_NOK): - if key in user_input: - updated_config[key] = user_input[key] + updated_config = dict(self.config_entry.data) + + control_section = user_input.get("Control") or {} + new_pw = control_section.get(CONF_PASSWORD) + + if new_pw: # only overwrite if user actually typed something + updated_config[CONF_PASSWORD] = new_pw + + updated_config[CONF_CONTROL] = control_section.get(CONF_CONTROL, updated_config.get(CONF_CONTROL, False)) + updated_config[CONF_REFRESH_OK] = user_input.get(CONF_REFRESH_OK, updated_config.get(CONF_REFRESH_OK, 300)) + updated_config[CONF_REFRESH_NOK] = user_input.get( + CONF_REFRESH_NOK, updated_config.get(CONF_REFRESH_NOK, 60) + ) self.hass.config_entries.async_update_entry( self.config_entry, data=updated_config, - title=user_input.get(CONF_NAME), + title=self.config_entry.title, ) + data_schema = { vol.Required(CONF_REFRESH_OK, default=300): cv.positive_int, vol.Required(CONF_REFRESH_NOK, default=60): cv.positive_int, @@ -115,12 +122,12 @@ async def async_step_user(self, user_input=None): errors: dict[str, str] = {} if user_input is not None: - self._data = user_input + self._data = dict(user_input) if user_input.get(CONF_PORTAL_VERSION) is None: - user_input[CONF_PORTAL_VERSION] = PLATFORMV2 - if user_input.get(CONF_PORTAL_VERSION) == PLATFORMV2: - return await self.async_step_credentials_password(user_input) - return await self.async_step_credentials_secret(user_input) + self._data[CONF_PORTAL_VERSION] = PLATFORMV2 + if self._data[CONF_PORTAL_VERSION] == PLATFORMV2: + return await self.async_step_credentials_password() # no arg + return await self.async_step_credentials_secret() # no arg data_schema = { vol.Required(CONF_NAME, default=SENSOR_PREFIX): cv.string, @@ -160,7 +167,7 @@ async def async_step_credentials_password(self, user_input=None): data_schema = { vol.Required(CONF_USERNAME, default=None): cv.string, - vol.Required(CONF_PASSWORD, default=""): cv.string, + vol.Required(CONF_PASSWORD): cv.string, vol.Required(CONF_PLANT_ID, default=None): cv.positive_int, } @@ -171,27 +178,32 @@ async def async_step_credentials_password(self, user_input=None): ) async def async_step_credentials_secret(self, user_input=None): - """Handle key_id/secret based credential settings.""" errors: dict[str, str] = {} if user_input is not None: + control_section = user_input.get("Control") or {} + password = control_section.get(CONF_PASSWORD) or user_input.get(CONF_PASSWORD) + url = self._data.get(CONF_PORTAL_DOMAIN) plant_id = user_input.get(CONF_PLANT_ID) username = user_input.get(CONF_USERNAME) - password = user_input.get(CONF_PASSWORD) key_id = user_input.get(CONF_KEY_ID) - secret: bytes = bytes("", "utf-8") - schedule_ok = user_input.get(CONF_REFRESH_OK) - schedule_nok = user_input.get(CONF_REFRESH_NOK) + + # SECRET comes from top-level field try: secret = bytes(user_input.get(CONF_SECRET), "utf-8") except TypeError: - pass + secret = b"" + if url[:8] != "https://": errors["base"] = "invalid_path" else: if username and key_id and secret and plant_id: - self._data.update(user_input) + # Merge nested section keys into _data so CONF_PASSWORD is stored + merged = dict(user_input) + merged.update(control_section) # brings CONF_PASSWORD, CONF_CONTROL to top + self._data.update(merged) + config = SoliscloudConfig(url, username, key_id, secret, plant_id, password) api = SoliscloudAPI(config) if await api.login(async_get_clientsession(self.hass)): @@ -210,10 +222,9 @@ async def async_step_credentials_secret(self, user_input=None): vol.Schema( { vol.Required(CONF_CONTROL, default=False): bool, - vol.Optional(CONF_PASSWORD, default=""): cv.string, + vol.Optional(CONF_PASSWORD): cv.string, } ), - # Whether or not the section is initially collapsed (default = False) {"collapsed": False}, ), } From 62d8be4bedb79f1240c35af40560bd1cfab659b2 Mon Sep 17 00:00:00 2001 From: fboundy Date: Wed, 13 Aug 2025 17:34:47 +0100 Subject: [PATCH 02/11] Fix incorrect HMI firmware threshold --- custom_components/solis/soliscloud_api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/custom_components/solis/soliscloud_api.py b/custom_components/solis/soliscloud_api.py index b9525f6..490fec4 100644 --- a/custom_components/solis/soliscloud_api.py +++ b/custom_components/solis/soliscloud_api.py @@ -391,7 +391,7 @@ async def fetch_inverter_data(self, inverter_serial: str, controls=True) -> Ginl self._collect_inverter_data(payload) if inverter_serial not in self._hmi_fb00: hmi_flag = self._data[HMI_VERSION_ALL] - self._hmi_fb00[inverter_serial] = int(hmi_flag, 16) >= int("4200", 16) + self._hmi_fb00[inverter_serial] = int(hmi_flag, 16) >= int("4b00", 16) if self._hmi_fb00[inverter_serial]: _LOGGER.debug( f"HMI firmware version ({hmi_flag}) >=4200 for Inverter SN {inverter_serial} " @@ -792,8 +792,6 @@ async def _fetch_token(self, username: str, password: str) -> str: return "" async def write_control_data(self, device_serial: str, cid: str, value: str): - _LOGGER.debug(f">>> Writing value {value} for cid {cid} to inverter {device_serial}") - params = {"inverterSn": str(device_serial), "cid": str(cid), "value": value} result = await self._post_data_json(CONTROL, params, csrf=True) From 01d86ecfaef408b42204f1bf43181aa7f6ce752d Mon Sep 17 00:00:00 2001 From: fboundy Date: Wed, 13 Aug 2025 17:45:10 +0100 Subject: [PATCH 03/11] Update logging of firmware threshold --- custom_components/solis/soliscloud_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/solis/soliscloud_api.py b/custom_components/solis/soliscloud_api.py index 490fec4..deb5659 100644 --- a/custom_components/solis/soliscloud_api.py +++ b/custom_components/solis/soliscloud_api.py @@ -394,11 +394,11 @@ async def fetch_inverter_data(self, inverter_serial: str, controls=True) -> Ginl self._hmi_fb00[inverter_serial] = int(hmi_flag, 16) >= int("4b00", 16) if self._hmi_fb00[inverter_serial]: _LOGGER.debug( - f"HMI firmware version ({hmi_flag}) >=4200 for Inverter SN {inverter_serial} " + f"HMI firmware version ({hmi_flag}) >=4B00 for Inverter SN {inverter_serial} " ) else: _LOGGER.debug( - f"HMI firmware version ({hmi_flag}) <4200 for Inverter SN {inverter_serial} " + f"HMI firmware version ({hmi_flag}) <4B00 for Inverter SN {inverter_serial} " ) if (self._token != "") and controls: From 56da71564d6f34c021a288bb6914fd00782e4f94 Mon Sep 17 00:00:00 2001 From: fboundy Date: Wed, 13 Aug 2025 17:47:26 +0100 Subject: [PATCH 04/11] Revert debug logging --- custom_components/solis/soliscloud_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/solis/soliscloud_api.py b/custom_components/solis/soliscloud_api.py index deb5659..ab1c0d1 100644 --- a/custom_components/solis/soliscloud_api.py +++ b/custom_components/solis/soliscloud_api.py @@ -792,6 +792,7 @@ async def _fetch_token(self, username: str, password: str) -> str: return "" async def write_control_data(self, device_serial: str, cid: str, value: str): + _LOGGER.debug(f"Writing value {value} for cid {cid} to inverter {device_serial}") params = {"inverterSn": str(device_serial), "cid": str(cid), "value": value} result = await self._post_data_json(CONTROL, params, csrf=True) From fcd419e73c72bf4fc071e3cf7dd68129c1c2e7ab Mon Sep 17 00:00:00 2001 From: fboundy Date: Wed, 13 Aug 2025 17:53:47 +0100 Subject: [PATCH 05/11] Add .vscode --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1dd07b4..9080399 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ custom_components/solis/__pycache__/* +.vscode/* From 64240e544406c0e9b14f7c5e7643b4476549d397 Mon Sep 17 00:00:00 2001 From: fboundy Date: Fri, 15 Aug 2025 13:07:42 +0100 Subject: [PATCH 06/11] Change control entitty uniquie_ids to include inverter_sn - should help with multipl inverter support --- custom_components/solis/button.py | 5 +- custom_components/solis/control_const.py | 80 ++++++++++++------------ custom_components/solis/number.py | 6 +- custom_components/solis/select.py | 4 +- custom_components/solis/sensor.py | 7 ++- custom_components/solis/time.py | 5 +- 6 files changed, 59 insertions(+), 48 deletions(-) diff --git a/custom_components/solis/button.py b/custom_components/solis/button.py index e71a958..45bf58f 100644 --- a/custom_components/solis/button.py +++ b/custom_components/solis/button.py @@ -17,7 +17,6 @@ from .service import InverterService, ServiceSubscriber _LOGGER = logging.getLogger(__name__) -RETRIES = 100 async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities): @@ -29,13 +28,15 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn service = hass.data[DOMAIN][config_entry.entry_id] _LOGGER.info(f"Waiting for discovery of controls for plant {plant_id}") - await asyncio.sleep(8) + await asyncio.sleep(RETRY_WAIT) attempts = 0 while (attempts < RETRIES) and (not service.has_controls): _LOGGER.debug(f" Attempt {attempts} failed") await asyncio.sleep(RETRY_WAIT) attempts += 1 + _LOGGER.debug(f" Attempt {attempts} succeeded") + if service.has_controls: entities = [] _LOGGER.debug(f"Plant ID {plant_id} has controls:") diff --git a/custom_components/solis/control_const.py b/custom_components/solis/control_const.py index 217a2b1..fdc6f6e 100644 --- a/custom_components/solis/control_const.py +++ b/custom_components/solis/control_const.py @@ -12,13 +12,15 @@ from .const import API_NAME, DOMAIN, EMPTY_ATTR, SERIAL -RETRIES = 1000 +RETRIES = 100 RETRY_WAIT = 10 _LOGGER = logging.getLogger(__name__) class SolisBaseControlEntity: + _attr_entity_registry_enabled_default = True + def __init__(self, service, config_name, inverter_sn, cid, info): self._measured: datetime | None = None self._entity_type = "control" @@ -37,7 +39,7 @@ def __init__(self, service, config_name, inverter_sn, cid, info): @property def unique_id(self) -> str: - return f"{self._platform_name}_{self._key}" + return f"{self._platform_name}_{self._inverter_sn}_{self._key}" @property def name(self) -> str: @@ -58,10 +60,10 @@ def device_info(self) -> DeviceInfo | None: identifiers={ ( DOMAIN, - f"{self._attributes[SERIAL]}_{DOMAIN, self._attributes[API_NAME]}", + f"{self._attributes[SERIAL]}_{self._attributes[API_NAME]}", ) }, - manufacturer=f"Solis {self._attributes[API_NAME]}", + manufacturer=f"Solis", name=f"Solis_Inverter_{self._attributes[SERIAL]}", ) @@ -261,18 +263,18 @@ class SolisButtonEntityDescription(ButtonEntityDescription): # key="update_timed_charge_discharge", # ), # ], - "15": [ - SolisNumberEntityDescription( - name="Power limit setting", - key="power_limit_setting", - native_unit_of_measurement=PERCENTAGE, - device_class=NumberDeviceClass.POWER_FACTOR, - icon="mdi:transmission-tower-export", - native_min_value=0, - native_max_value=110, - native_step=1, - ) - ], + # "15": [ + # SolisNumberEntityDescription( + # name="Power limit setting", + # key="power_limit_setting", + # native_unit_of_measurement=PERCENTAGE, + # device_class=NumberDeviceClass.POWER_FACTOR, + # icon="mdi:transmission-tower-export", + # native_min_value=0, + # native_max_value=110, + # native_step=1, + # ) + # ], "157": [ SolisNumberEntityDescription( name="Backup SOC", @@ -309,18 +311,18 @@ class SolisButtonEntityDescription(ButtonEntityDescription): native_step=1, ) ], - "230": [ - SolisNumberEntityDescription( - name="System Export Power Limit Value", - key="sytem_export_power_limit_value", - native_unit_of_measurement=UnitOfPower.WATT, - device_class=NumberDeviceClass.POWER, - icon="mdi:transmission-tower-export", - native_min_value=0, - native_max_value=1000000000, # 1 GW - native_step=1, - ) - ], + # "230": [ + # SolisNumberEntityDescription( + # name="System Export Power Limit Value", + # key="sytem_export_power_limit_value", + # native_unit_of_measurement=UnitOfPower.WATT, + # device_class=NumberDeviceClass.POWER, + # icon="mdi:transmission-tower-export", + # native_min_value=0, + # native_max_value=1000000000, # 1 GW + # native_step=1, + # ) + # ], "636": [ SolisSelectEntityDescription( name="Energy Storage Control Switch", @@ -351,17 +353,17 @@ class SolisButtonEntityDescription(ButtonEntityDescription): native_step=100, ) ], - "5161": [ - SolisSelectEntityDescription( - name="Inverter energy export on/off Control Switch", - key="inverter_energy_export_on_off_control_switch", - option_dict={ - "190": "ON", - "222": "OFF", - }, - icon="mdi:dip-switch", - ) - ], + # "5161": [ + # SolisSelectEntityDescription( + # name="Inverter energy export on/off Control Switch", + # key="inverter_energy_export_on_off_control_switch", + # option_dict={ + # "190": "ON", + # "222": "OFF", + # }, + # icon="mdi:dip-switch", + # ) + # ], "5928": [ SolisNumberEntityDescription( name="Timed Charge SOC 1", diff --git a/custom_components/solis/number.py b/custom_components/solis/number.py index d016b73..9398b4c 100644 --- a/custom_components/solis/number.py +++ b/custom_components/solis/number.py @@ -18,7 +18,7 @@ from .service import InverterService, ServiceSubscriber _LOGGER = logging.getLogger(__name__) -RETRIES = 100 +# RETRIES = 100 async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities): @@ -30,13 +30,15 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn service = hass.data[DOMAIN][config_entry.entry_id] _LOGGER.info(f"Waiting for discovery of controls for plant {plant_id}") - await asyncio.sleep(8) + await asyncio.sleep(RETRY_WAIT) attempts = 0 while (attempts < RETRIES) and (not service.has_controls): _LOGGER.debug(f" Attempt {attempts} failed") await asyncio.sleep(RETRY_WAIT) attempts += 1 + _LOGGER.debug(f" Attempt {attempts} succeeded") + if service.has_controls: entities = [] _LOGGER.debug(f"Plant ID {plant_id} has controls:") diff --git a/custom_components/solis/select.py b/custom_components/solis/select.py index 547504c..c6dc5b4 100644 --- a/custom_components/solis/select.py +++ b/custom_components/solis/select.py @@ -27,13 +27,15 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn service = hass.data[DOMAIN][config_entry.entry_id] _LOGGER.info(f"Waiting for discovery of controls for plant {plant_id}") - await asyncio.sleep(8) + await asyncio.sleep(RETRY_WAIT) attempts = 0 while (attempts < RETRIES) and (not service.has_controls): _LOGGER.debug(f" Attempt {attempts} failed") await asyncio.sleep(RETRY_WAIT) attempts += 1 + _LOGGER.debug(f" Attempt {attempts} succeeded") + if service.has_controls: entities = [] _LOGGER.debug(f"Plant ID {plant_id} has controls:") diff --git a/custom_components/solis/sensor.py b/custom_components/solis/sensor.py index 4818f5c..ae46392 100644 --- a/custom_components/solis/sensor.py +++ b/custom_components/solis/sensor.py @@ -163,6 +163,9 @@ def on_discovered(capabilities, cookie): class SolisSensor(ServiceSubscriber, SensorEntity): """Representation of a Solis sensor.""" + _attr_entity_registry_enabled_default = True + _attr_has_entity_name = True + def __init__( self, ginlong_service: InverterService, @@ -224,10 +227,10 @@ def device_info(self) -> DeviceInfo | None: identifiers={ ( DOMAIN, - f"{self._attributes[SERIAL]}_{DOMAIN, self._attributes[API_NAME]}", + f"{self._attributes[SERIAL]}_{self._attributes[API_NAME]}", ) }, - manufacturer=f"Solis {self._attributes[API_NAME]}", + manufacturer=f"Solis", name=f"Solis_Inverter_{self._attributes[SERIAL]}", # model=config.modelid, # sw_version=config.swversion, diff --git a/custom_components/solis/time.py b/custom_components/solis/time.py index 2fe9e63..e888c53 100644 --- a/custom_components/solis/time.py +++ b/custom_components/solis/time.py @@ -17,7 +17,6 @@ from .service import InverterService, ServiceSubscriber _LOGGER = logging.getLogger(__name__) -RETRIES = 100 YEAR = 2024 MONTH = 1 DAY = 1 @@ -32,13 +31,15 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn service = hass.data[DOMAIN][config_entry.entry_id] _LOGGER.info(f"Waiting for discovery of controls for plant {plant_id}") - await asyncio.sleep(8) + await asyncio.sleep(RETRY_WAIT) attempts = 0 while (attempts < RETRIES) and (not service.has_controls): _LOGGER.debug(f" Attempt {attempts} failed") await asyncio.sleep(RETRY_WAIT) attempts += 1 + _LOGGER.debug(f" Attempt {attempts} succeeded") + if service.has_controls: entities = [] _LOGGER.debug(f"Plant ID {plant_id} has controls:") From 536c34d61196b8549085177eaf91eb1af1585dc5 Mon Sep 17 00:00:00 2001 From: fboundy Date: Fri, 15 Aug 2025 13:11:54 +0100 Subject: [PATCH 07/11] Update keys to Energy Control Storage Switch dict to be str for 3-slot case --- custom_components/solis/control_const.py | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/custom_components/solis/control_const.py b/custom_components/solis/control_const.py index fdc6f6e..f236018 100644 --- a/custom_components/solis/control_const.py +++ b/custom_components/solis/control_const.py @@ -1072,19 +1072,19 @@ class SolisButtonEntityDescription(ButtonEntityDescription): name="Energy Storage Control Switch", key="energy_storage_control_switch", option_dict={ - 1: "Self-Use - No Grid Charging", - 3: "Timed Charge/Discharge - No Grid Charging", - 17: "Backup/Reserve - No Grid Charging", - 33: "Self-Use - No Timed Charge/Discharge", - 35: "Self-Use", - 37: "Off-Grid Mode", - 41: "Battery Awaken", - 43: "Battery Awaken + Timed Charge/Discharge", - 49: "Backup/Reserve - No Timed Charge/Discharge", - 51: "Backup/Reserve", - 64: "Feed-in priority - No Grid Charging", - 96: "Feed-in priority - No Timed Charge/Discharge", - 98: "Feed-in priority", + "1": "Self-Use - No Grid Charging", + "3": "Timed Charge/Discharge - No Grid Charging", + "17": "Backup/Reserve - No Grid Charging", + "33": "Self-Use - No Timed Charge/Discharge", + "35": "Self-Use", + "37": "Off-Grid Mode", + "41": "Battery Awaken", + "43": "Battery Awaken + Timed Charge/Discharge", + "49": "Backup/Reserve - No Timed Charge/Discharge", + "51": "Backup/Reserve", + "64": "Feed-in priority - No Grid Charging", + "96": "Feed-in priority - No Timed Charge/Discharge", + "98": "Feed-in priority", }, icon="mdi:dip-switch", ) From 658da19fb028ecc6d6f614157705efc7e8c56965 Mon Sep 17 00:00:00 2001 From: fboundy Date: Fri, 15 Aug 2025 13:19:16 +0100 Subject: [PATCH 08/11] Revert commented controls --- custom_components/solis/control_const.py | 70 ++++++++++++------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/custom_components/solis/control_const.py b/custom_components/solis/control_const.py index f236018..276cec4 100644 --- a/custom_components/solis/control_const.py +++ b/custom_components/solis/control_const.py @@ -263,18 +263,18 @@ class SolisButtonEntityDescription(ButtonEntityDescription): # key="update_timed_charge_discharge", # ), # ], - # "15": [ - # SolisNumberEntityDescription( - # name="Power limit setting", - # key="power_limit_setting", - # native_unit_of_measurement=PERCENTAGE, - # device_class=NumberDeviceClass.POWER_FACTOR, - # icon="mdi:transmission-tower-export", - # native_min_value=0, - # native_max_value=110, - # native_step=1, - # ) - # ], + "15": [ + SolisNumberEntityDescription( + name="Power limit setting", + key="power_limit_setting", + native_unit_of_measurement=PERCENTAGE, + device_class=NumberDeviceClass.POWER_FACTOR, + icon="mdi:transmission-tower-export", + native_min_value=0, + native_max_value=110, + native_step=1, + ) + ], "157": [ SolisNumberEntityDescription( name="Backup SOC", @@ -311,18 +311,18 @@ class SolisButtonEntityDescription(ButtonEntityDescription): native_step=1, ) ], - # "230": [ - # SolisNumberEntityDescription( - # name="System Export Power Limit Value", - # key="sytem_export_power_limit_value", - # native_unit_of_measurement=UnitOfPower.WATT, - # device_class=NumberDeviceClass.POWER, - # icon="mdi:transmission-tower-export", - # native_min_value=0, - # native_max_value=1000000000, # 1 GW - # native_step=1, - # ) - # ], + "230": [ + SolisNumberEntityDescription( + name="System Export Power Limit Value", + key="sytem_export_power_limit_value", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=NumberDeviceClass.POWER, + icon="mdi:transmission-tower-export", + native_min_value=0, + native_max_value=1000000000, # 1 GW + native_step=1, + ) + ], "636": [ SolisSelectEntityDescription( name="Energy Storage Control Switch", @@ -353,17 +353,17 @@ class SolisButtonEntityDescription(ButtonEntityDescription): native_step=100, ) ], - # "5161": [ - # SolisSelectEntityDescription( - # name="Inverter energy export on/off Control Switch", - # key="inverter_energy_export_on_off_control_switch", - # option_dict={ - # "190": "ON", - # "222": "OFF", - # }, - # icon="mdi:dip-switch", - # ) - # ], + "5161": [ + SolisSelectEntityDescription( + name="Inverter energy export on/off Control Switch", + key="inverter_energy_export_on_off_control_switch", + option_dict={ + "190": "ON", + "222": "OFF", + }, + icon="mdi:dip-switch", + ) + ], "5928": [ SolisNumberEntityDescription( name="Timed Charge SOC 1", From 6c7b093a8e728b9291b9f59f1990a34986dc584d Mon Sep 17 00:00:00 2001 From: Carl Saggs Date: Sun, 17 Aug 2025 14:59:10 +0100 Subject: [PATCH 09/11] Improve the naming of the inverter modes + fix lookup bug --- custom_components/solis/control_const.py | 32 ++++++++++++++---------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/custom_components/solis/control_const.py b/custom_components/solis/control_const.py index 217a2b1..1954dd5 100644 --- a/custom_components/solis/control_const.py +++ b/custom_components/solis/control_const.py @@ -1070,19 +1070,25 @@ class SolisButtonEntityDescription(ButtonEntityDescription): name="Energy Storage Control Switch", key="energy_storage_control_switch", option_dict={ - 1: "Self-Use - No Grid Charging", - 3: "Timed Charge/Discharge - No Grid Charging", - 17: "Backup/Reserve - No Grid Charging", - 33: "Self-Use - No Timed Charge/Discharge", - 35: "Self-Use", - 37: "Off-Grid Mode", - 41: "Battery Awaken", - 43: "Battery Awaken + Timed Charge/Discharge", - 49: "Backup/Reserve - No Timed Charge/Discharge", - 51: "Backup/Reserve", - 64: "Feed-in priority - No Grid Charging", - 96: "Feed-in priority - No Timed Charge/Discharge", - 98: "Feed-in priority", + "3": "Self-Use - Allow Timed Charge/Discharge No Grid Charging", + "35": "Self-Use - Allow Timed Charge/Discharge, Allow Grid Charging", + "1": "Self-Use - No Timed Charge/Discharge, No Grid Charging", + "33": "Self-Use - No Timed Charge/Discharge, Allow Grid Charging", + "17": "Self-Use with Backup - No Timed Charge/Discharge, No Grid Charging", + "49": "Self-Use with Backup - No Timed Charge/Discharge, allow grid charging", + "19": "Self-Use with Backup - Allow Timed Charge/Discharge, No Grid Charging", + "51": "Self-Use with Backup - Allow Timed Charge/Discharge, allow grid charging", + "41": "Battery Awaken - No Timed Charge/Discharge", + "43": "Battery Awaken - Allow Timed Charge/Discharge", + "64": "Feed-in Priority - No Grid Charging, No Timed Charge/Discharge", + "66": "Feed-in Priority - No Grid Charging, Allow Timed Charge/Discharge, + "96": "Feed-in Priority - Allow Grid Charging, No Timed Charge/Discharge", + "98": "Feed-in Priority - Allow Grid Charging, Allow Timed Charge/Discharge", + "80": "Feed-in Priority with Backup - No Grid Charging, No Timed Charge/Discharge", + "82": "Feed-in Priority with Backup - No Grid Charging, Allow Timed Charge/Discharge, + "112": "Feed-in Priority with Backup - Allow Grid Charging, No Timed Charge/Discharge", + "114": "Feed-in Priority with Backup - Allow Grid Charging, Allow Timed Charge/Discharge", + "37": "Off-Grid", }, icon="mdi:dip-switch", ) From c77dcd8e2110af4bcb4b7e5cbe822a503a02620d Mon Sep 17 00:00:00 2001 From: Carl Saggs Date: Sun, 17 Aug 2025 15:04:56 +0100 Subject: [PATCH 10/11] Make order more consistent --- custom_components/solis/control_const.py | 33 ++++++++++++++---------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/custom_components/solis/control_const.py b/custom_components/solis/control_const.py index 1954dd5..63ac540 100644 --- a/custom_components/solis/control_const.py +++ b/custom_components/solis/control_const.py @@ -1070,24 +1070,29 @@ class SolisButtonEntityDescription(ButtonEntityDescription): name="Energy Storage Control Switch", key="energy_storage_control_switch", option_dict={ - "3": "Self-Use - Allow Timed Charge/Discharge No Grid Charging", "35": "Self-Use - Allow Timed Charge/Discharge, Allow Grid Charging", - "1": "Self-Use - No Timed Charge/Discharge, No Grid Charging", + "3": "Self-Use - Allow Timed Charge/Discharge, No Grid Charging", "33": "Self-Use - No Timed Charge/Discharge, Allow Grid Charging", - "17": "Self-Use with Backup - No Timed Charge/Discharge, No Grid Charging", - "49": "Self-Use with Backup - No Timed Charge/Discharge, allow grid charging", + "1": "Self-Use - No Timed Charge/Discharge, No Grid Charging", + + "51": "Self-Use with Backup - Allow Timed Charge/Discharge, Allow Grid Charging", "19": "Self-Use with Backup - Allow Timed Charge/Discharge, No Grid Charging", - "51": "Self-Use with Backup - Allow Timed Charge/Discharge, allow grid charging", - "41": "Battery Awaken - No Timed Charge/Discharge", + "49": "Self-Use with Backup - No Timed Charge/Discharge, Allow grid Charging", + "17": "Self-Use with Backup - No Timed Charge/Discharge, No Grid Charging", + "43": "Battery Awaken - Allow Timed Charge/Discharge", - "64": "Feed-in Priority - No Grid Charging, No Timed Charge/Discharge", - "66": "Feed-in Priority - No Grid Charging, Allow Timed Charge/Discharge, - "96": "Feed-in Priority - Allow Grid Charging, No Timed Charge/Discharge", - "98": "Feed-in Priority - Allow Grid Charging, Allow Timed Charge/Discharge", - "80": "Feed-in Priority with Backup - No Grid Charging, No Timed Charge/Discharge", - "82": "Feed-in Priority with Backup - No Grid Charging, Allow Timed Charge/Discharge, - "112": "Feed-in Priority with Backup - Allow Grid Charging, No Timed Charge/Discharge", - "114": "Feed-in Priority with Backup - Allow Grid Charging, Allow Timed Charge/Discharge", + "41": "Battery Awaken - No Timed Charge/Discharge", + + "98": "Feed-in Priority - Allow Timed Charge/Discharge, Allow Grid Charging", + "66": "Feed-in Priority - Allow Timed Charge/Discharge, No Grid Charging", + "96": "Feed-in Priority - No Timed Charge/Discharge, Allow Grid Charging, ", + "64": "Feed-in Priority - No Timed Charge/Discharge, No Grid Charging", + + "114": "Feed-in Priority with Backup - Allow Timed Charge/Discharge, Allow Grid Charging", + "82": "Feed-in Priority with Backup - Allow Timed Charge/Discharge, No Grid Charging", + "112": "Feed-in Priority with Backup - No Timed Charge/Discharge, Allow Grid Charging", + "80": "Feed-in Priority with Backup - No Timed Charge/Discharge, No Grid Charging", + "37": "Off-Grid", }, icon="mdi:dip-switch", From a6ee795b18b3880ccc6fc1560afdcd76a4532185 Mon Sep 17 00:00:00 2001 From: Carl Saggs Date: Sun, 17 Aug 2025 15:18:22 +0100 Subject: [PATCH 11/11] Fix caseing --- custom_components/solis/control_const.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/solis/control_const.py b/custom_components/solis/control_const.py index 63ac540..ea58b58 100644 --- a/custom_components/solis/control_const.py +++ b/custom_components/solis/control_const.py @@ -1077,7 +1077,7 @@ class SolisButtonEntityDescription(ButtonEntityDescription): "51": "Self-Use with Backup - Allow Timed Charge/Discharge, Allow Grid Charging", "19": "Self-Use with Backup - Allow Timed Charge/Discharge, No Grid Charging", - "49": "Self-Use with Backup - No Timed Charge/Discharge, Allow grid Charging", + "49": "Self-Use with Backup - No Timed Charge/Discharge, Allow Grid Charging", "17": "Self-Use with Backup - No Timed Charge/Discharge, No Grid Charging", "43": "Battery Awaken - Allow Timed Charge/Discharge", @@ -1085,7 +1085,7 @@ class SolisButtonEntityDescription(ButtonEntityDescription): "98": "Feed-in Priority - Allow Timed Charge/Discharge, Allow Grid Charging", "66": "Feed-in Priority - Allow Timed Charge/Discharge, No Grid Charging", - "96": "Feed-in Priority - No Timed Charge/Discharge, Allow Grid Charging, ", + "96": "Feed-in Priority - No Timed Charge/Discharge, Allow Grid Charging", "64": "Feed-in Priority - No Timed Charge/Discharge, No Grid Charging", "114": "Feed-in Priority with Backup - Allow Timed Charge/Discharge, Allow Grid Charging",