Skip to content

Solis fixes for mode control, GECloud EMS auto config fix#3666

Open
springfall2008 wants to merge 2 commits intomainfrom
solis4
Open

Solis fixes for mode control, GECloud EMS auto config fix#3666
springfall2008 wants to merge 2 commits intomainfrom
solis4

Conversation

@springfall2008
Copy link
Owner

No description provided.

Copilot AI review requested due to automatic review settings March 26, 2026 11:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates Predbat’s Solis and GECloud integrations to improve Solis mode-control correctness (bitwise mode handling) and fix GECloud EMS auto-configuration sensor wiring.

Changes:

  • Solis: replace numeric “storage mode” mapping with bitwise decode/encode helpers; update entity publishing and event handling accordingly.
  • Tests: update/add Solis tests for the new mode encoding/decoding and switch-bit behavior.
  • GECloud: update EMS auto-config to use *_total energy sensors instead of *_today; bump version.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
apps/predbat/solis.py Introduces bit-based Solis storage-mode model, updates UI publish + mode write logic, and adjusts TOU V2 current clamping behavior.
apps/predbat/tests/test_solis.py Updates existing Solis tests and adds new tests for mode enum/value roundtrips and corrected bit positions.
apps/predbat/gecloud.py Fixes EMS auto-config to point to *_total sensors for energy totals.
apps/predbat/tests/test_ge_cloud.py Updates GECloud EMS auto-config expectations to match new *_total sensor mappings.
apps/predbat/predbat.py Version bump to v8.34.14.

Comment on lines 1894 to 1901
# Use dynamic storage modes if available, otherwise static
mode_options = list(self.storage_modes.get(inverter_sn, SOLIS_STORAGE_MODES).keys())
mode_options = list(SOLIS_STORAGE_MODES.keys())
self.dashboard_item(
entity_id,
state=SOLIS_STORAGE_MODES.get(mode_value, mode_value) if mode_value is not None else None,
state=mode_str,
attributes={
"friendly_name": f"Solis {inverter_name} Storage Mode",
"options": mode_options,
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including "Other" in the selectable options is problematic because selecting it results in no change (compute_solis_mode_value(ENUM_OTHER, old) returns old), so the UI will immediately revert to the decoded mode. Consider excluding "Other" from the selector options and using it only as a display state for unknown/unsupported bit patterns.

Copilot uses AI. Check for mistakes.
from solis import SOLIS_CID_POWER_LIMIT, SOLIS_BIT_BACKUP_MODE
from solis import get_solis_mode_enum, compute_solis_mode_value
from solis import ENUM_OTHER, ENUM_SELF_USE, ENUM_SELF_USE_NO_GRID_CHARGING, ENUM_FEED_IN_PRIORITY, ENUM_FEED_IN_PRIORITY_NO_GRID_CHARGING
from solis import SOLIS_BIT_SELF_USE, SOLIS_BIT_FEED_IN_PRIORITY, SOLIS_BIT_GRID_CHARGING, SOLIS_BIT_OFF_GRID
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SOLIS_BIT_GRID_CHARGING is imported twice (it’s already imported earlier on line 16). Remove the duplicate import to keep the import list clean and avoid confusion.

Suggested change
from solis import SOLIS_BIT_SELF_USE, SOLIS_BIT_FEED_IN_PRIORITY, SOLIS_BIT_GRID_CHARGING, SOLIS_BIT_OFF_GRID
from solis import SOLIS_BIT_SELF_USE, SOLIS_BIT_FEED_IN_PRIORITY, SOLIS_BIT_OFF_GRID

Copilot uses AI. Check for mistakes.
Comment on lines +2140 to +2145
value = self.cached_values.get(inverter_sn, {}).get(SOLIS_CID_STORAGE_MODE, 1<<SOLIS_BIT_SELF_USE)
try:
return int(value)
except (ValueError, TypeError):
self.log(f"Warn: Failed to parse storage mode value for {inverter_sn}: {value}")
return 1<<SOLIS_BIT_SELF_USE
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_current_solis_mode_value() falls back to 1 << SOLIS_BIT_SELF_USE when the cached value is missing or unparsable. That implicitly reports the inverter as being in self-use mode even though the real state is unknown, and it also forces related UI switches (reserve/grid-charging/TOU) to render as off rather than unknown. Consider defaulting to 0 (so get_solis_mode_enum shows "Other") or returning None and handling the unknown state explicitly in publish_entities.

Suggested change
value = self.cached_values.get(inverter_sn, {}).get(SOLIS_CID_STORAGE_MODE, 1<<SOLIS_BIT_SELF_USE)
try:
return int(value)
except (ValueError, TypeError):
self.log(f"Warn: Failed to parse storage mode value for {inverter_sn}: {value}")
return 1<<SOLIS_BIT_SELF_USE
value = self.cached_values.get(inverter_sn, {}).get(SOLIS_CID_STORAGE_MODE, 0)
try:
return int(value)
except (ValueError, TypeError):
self.log(f"Warn: Failed to parse storage mode value for {inverter_sn}: {value}")
return 0

Copilot uses AI. Check for mistakes.
Comment on lines +137 to +138
"Feed-in priority": ENUM_FEED_IN_PRIORITY,
"Feed-in priority - No Timed Charge/Discharge": ENUM_FEED_IN_PRIORITY_NO_GRID_CHARGING
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SOLIS_STORAGE_MODES no longer contains mode strings that are still used elsewhere in this module (e.g. "Self-Use - No Timed Charge/Discharge" in write_time_windows_if_changed). As-is, set_storage_mode_if_needed will treat those as unknown and skip the write, breaking mode control in V1 TOU mode. Add the missing mode(s) back to SOLIS_STORAGE_MODES (and support them in get_solis_mode_enum/compute_solis_mode_value), or update the call sites to only use supported options.

Suggested change
"Feed-in priority": ENUM_FEED_IN_PRIORITY,
"Feed-in priority - No Timed Charge/Discharge": ENUM_FEED_IN_PRIORITY_NO_GRID_CHARGING
# Backwards-compatible alias used by some call sites (e.g. V1 TOU logic)
"Self-Use - No Timed Charge/Discharge": ENUM_SELF_USE_NO_GRID_CHARGING,
"Feed-in priority": ENUM_FEED_IN_PRIORITY,
"Feed-in priority - No Timed Charge/Discharge": ENUM_FEED_IN_PRIORITY_NO_GRID_CHARGING,
# Symmetric alias for completeness; maps to the same enum as the "No Timed" label
"Feed-in priority - No Grid Charging": ENUM_FEED_IN_PRIORITY_NO_GRID_CHARGING,

Copilot uses AI. Check for mistakes.
"Self-Use": ENUM_SELF_USE,
"Self-Use - No Grid Charging": ENUM_SELF_USE_NO_GRID_CHARGING,
"Feed-in priority": ENUM_FEED_IN_PRIORITY,
"Feed-in priority - No Timed Charge/Discharge": ENUM_FEED_IN_PRIORITY_NO_GRID_CHARGING
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enum name ENUM_FEED_IN_PRIORITY_NO_GRID_CHARGING conflicts with the user-facing label "Feed-in priority - No Timed Charge/Discharge". This mismatch makes it unclear which behavior this mode represents and risks inconsistent UI/config strings. Align the enum name and the label (and keep labels consistent with existing config strings elsewhere in the repo, e.g. "Feed-in priority - No Grid Charging" in config.py).

Suggested change
"Feed-in priority - No Timed Charge/Discharge": ENUM_FEED_IN_PRIORITY_NO_GRID_CHARGING
"Feed-in priority - No Grid Charging": ENUM_FEED_IN_PRIORITY_NO_GRID_CHARGING

Copilot uses AI. Check for mistakes.
current_cid = SOLIS_CID_DISCHARGE_CURRENT[slot - 1]
max_discharge_current_amps = min(self.cached_infos.get(inverter_sn, {}).get(current_cid, {}).get('sysCommand', {}).get('max', max_discharge_current_amps), max_discharge_current_amps)

self.log("Solis API: Max charge current amps determined to be {} and max discharge current amps determined to be {} for {}".format(max_charge_current_amps, max_discharge_current_amps, inverter_sn))
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This log line will run every time write_time_windows_if_changed() executes in TOU V2 mode (currently every 60s per inverter). That can add a lot of noise to logs in normal operation; consider gating it behind a debug flag or only logging when the computed max values change.

Suggested change
self.log("Solis API: Max charge current amps determined to be {} and max discharge current amps determined to be {} for {}".format(max_charge_current_amps, max_discharge_current_amps, inverter_sn))
# Log only when the determined max charge/discharge currents change for this inverter to reduce log noise
last_max_currents_by_inverter = getattr(self, "_last_max_currents_by_inverter", None)
if last_max_currents_by_inverter is None:
last_max_currents_by_inverter = {}
setattr(self, "_last_max_currents_by_inverter", last_max_currents_by_inverter)
last_max_charge, last_max_discharge = last_max_currents_by_inverter.get(inverter_sn, (None, None))
if max_charge_current_amps != last_max_charge or max_discharge_current_amps != last_max_discharge:
self.log("Solis API: Max charge current amps determined to be {} and max discharge current amps determined to be {} for {}".format(max_charge_current_amps, max_discharge_current_amps, inverter_sn))
last_max_currents_by_inverter[inverter_sn] = (max_charge_current_amps, max_discharge_current_amps)

Copilot uses AI. Check for mistakes.
except (ValueError, TypeError):
self.log("Warn: Failed to convert storage mode to int for {}: {}".format(inverter_sn, mode_value)) # Debug log
storage_mode_value = self.get_current_solis_mode_value(inverter_sn)
mode_enum, mode_str = get_solis_mode_enum(storage_mode_value)
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mode_enum is assigned but never used. Use _ instead (or remove the assignment) to avoid dead locals and keep intent clear.

Suggested change
mode_enum, mode_str = get_solis_mode_enum(storage_mode_value)
_, mode_str = get_solis_mode_enum(storage_mode_value)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants