Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions custom_components/solisconnect/sensor_data/hybrid_sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -3443,6 +3443,60 @@
]
## Remove 33132 in next update if this works (43110)


def _apply_control_metadata() -> None:
direct_control_names = {
"Max Charge SOC",
"Overdischarge SOC",
"Max Charge Current",
"Max Discharge Current",
"Force Charge SOC",
"Battery Max Charge Current",
"Battery Max Discharge Current",
}
direct_control_renames = {
"Backup SOC": "Backup Mode SOC",
}

timed_charge_registers: dict[int, tuple[str, int]] = {}
for slot in range(1, 7):
charge_base = 43708 + ((slot - 1) * 7)
timed_charge_registers[charge_base] = ("SOC", slot)
timed_charge_registers[charge_base + 1] = ("Current", slot)
timed_charge_registers[charge_base + 2] = ("Voltage", slot)

discharge_slot = slot + 6
discharge_base = 43750 + ((slot - 1) * 7)
timed_charge_registers[discharge_base] = ("SOC", discharge_slot)
timed_charge_registers[discharge_base + 1] = ("Current", discharge_slot)
timed_charge_registers[discharge_base + 2] = ("Voltage", discharge_slot)

for group in hybrid_sensors:
for entity in group.get("entities", []):
if entity.get("type") == "reserve":
continue

if entity["name"] in direct_control_renames:
entity["name"] = direct_control_renames[entity["name"]]

if entity["name"] in direct_control_names or entity["name"] in direct_control_renames.values():
entity["control"] = True

registers = entity.get("register", [])
if len(registers) != 1:
continue

register = int(registers[0])
timed_control = timed_charge_registers.get(register)
if timed_control:
field, slot = timed_control
entity["name"] = f"Timed Charge {field} {slot}"
entity["control"] = True


_apply_control_metadata()


hybrid_sensors_derived = [
{
"name": "Last Modbus Success",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def get_select_sensors(inverter_config):
{
"register": 43110,
"name": "Work Mode",
"control": True,
"entities": [
# Adheres to RS485_MODBUS ESINV-33000ID Hybrid Inverter V3.2 / Appendix 8
{"bit_position": 0, "name": "Self-Use", "conflicts_with": [0, 6, 11]},
Expand Down
48 changes: 24 additions & 24 deletions custom_components/solisconnect/sensor_data/switch_sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ def get_switch_sensors(inverter_config):
"register": 43110,
"entities": [
# Adheres to RS485_MODBUS ESINV-33000ID Hybrid Inverter V3.2 / Appendix 8
{"bit_position": 0, "name": "Self-Use Mode", "conflicts_with": [2, 6, 11]},
{"bit_position": 1, "name": "Time of Use", "requires_any": [0, 6]},
{"bit_position": 2, "name": "Off-Grid Mode", "conflicts_with": [0, 1, 6, 11]},
{"bit_position": 3, "name": "Battery Wakeup Switch"},
{"bit_position": 4, "name": "Reserve Battery Mode", "conflicts_with": [11]},
{"bit_position": 5, "name": "Allow Grid to Charge the Battery"},
{"bit_position": 6, "name": "Feed-In Priority Mode", "conflicts_with": [0, 11]},
{"bit_position": 7, "name": "Batt OVC"},
{"bit_position": 8, "name": "Battery Forcecharge Peakshaving"},
{"bit_position": 9, "name": "Battery Current Correction"},
{"bit_position": 10, "name": "Battery Healing Mode"},
{"bit_position": 11, "name": "Peak Shaving Mode", "conflicts_with": [0, 4, 6]},
{"bit_position": 0, "name": "Mode - Self Use", "conflicts_with": [2, 6, 11], "control": True},
{"bit_position": 1, "name": "Mode - Timed Charge", "requires_any": [0, 6], "control": True},
{"bit_position": 2, "name": "Mode - Off-Grid", "conflicts_with": [0, 1, 6, 11], "control": True},
{"bit_position": 3, "name": "Mode - Battery Wake Up", "control": True},
{"bit_position": 4, "name": "Mode - Backup", "conflicts_with": [11], "control": True},
{"bit_position": 5, "name": "Mode - Grid Charge", "control": True},
{"bit_position": 6, "name": "Mode - Feed In Priority", "conflicts_with": [0, 11], "control": True},
{"bit_position": 7, "name": "Mode - Night OVD Retain", "control": True},
{"bit_position": 8, "name": "Battery Forcecharge Peakshaving", "enabled": False},
{"bit_position": 9, "name": "Battery Current Correction", "enabled": False},
{"bit_position": 10, "name": "Mode - Battery Condition", "control": True},
{"bit_position": 11, "name": "Mode - Peak Shaving", "conflicts_with": [0, 4, 6], "control": True},
],
},
{
Expand Down Expand Up @@ -146,18 +146,18 @@ def get_switch_sensors(inverter_config):
{
"register": 43707,
"entities": [
{"name": "Grid Time of Use Charging Period 1", "bit_position": 0},
{"name": "Grid Time of Use Charging Period 2", "bit_position": 1},
{"name": "Grid Time of Use Charging Period 3", "bit_position": 2},
{"name": "Grid Time of Use Charging Period 4", "bit_position": 3},
{"name": "Grid Time of Use Charging Period 5", "bit_position": 4},
{"name": "Grid Time of Use Charging Period 6", "bit_position": 5},
{"name": "Grid Time of Use Discharge Period 1", "bit_position": 6},
{"name": "Grid Time of Use Discharge Period 2", "bit_position": 7},
{"name": "Grid Time of Use Discharge Period 3", "bit_position": 8},
{"name": "Grid Time of Use Discharge Period 4", "bit_position": 9},
{"name": "Grid Time of Use Discharge Period 5", "bit_position": 10},
{"name": "Grid Time of Use Discharge Period 6", "bit_position": 11},
{"name": "Timed Charge Enable 1", "bit_position": 0, "control": True},
{"name": "Timed Charge Enable 2", "bit_position": 1, "control": True},
{"name": "Timed Charge Enable 3", "bit_position": 2, "control": True},
{"name": "Timed Charge Enable 4", "bit_position": 3, "control": True},
{"name": "Timed Charge Enable 5", "bit_position": 4, "control": True},
{"name": "Timed Charge Enable 6", "bit_position": 5, "control": True},
{"name": "Timed Charge Enable 7", "bit_position": 6, "control": True},
{"name": "Timed Charge Enable 8", "bit_position": 7, "control": True},
{"name": "Timed Charge Enable 9", "bit_position": 8, "control": True},
{"name": "Timed Charge Enable 10", "bit_position": 9, "control": True},
{"name": "Timed Charge Enable 11", "bit_position": 10, "control": True},
{"name": "Timed Charge Enable 12", "bit_position": 11, "control": True},
],
},
]
Expand Down
48 changes: 24 additions & 24 deletions custom_components/solisconnect/sensor_data/time_sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,30 @@ def get_time_sensors(inverter_config):
if inverter_config.type == InverterType.HYBRID or InverterFeature.V2 in inverter_config.features:
time_definitions.extend(
[
{"name": "Grid Time of Use Charge Start (Slot 1)", "register": 43711, "enabled": True},
{"name": "Grid Time of Use Charge End (Slot 1)", "register": 43713, "enabled": True},
{"name": "Grid Time of Use Discharge Start (Slot 1)", "register": 43753, "enabled": True},
{"name": "Grid Time of Use Discharge End (Slot 1)", "register": 43755, "enabled": True},
{"name": "Grid Time of Use Charge Start (Slot 2)", "register": 43718, "enabled": True},
{"name": "Grid Time of Use Charge End (Slot 2)", "register": 43720, "enabled": True},
{"name": "Grid Time of Use Discharge Start (Slot 2)", "register": 43760, "enabled": True},
{"name": "Grid Time of Use Discharge End (Slot 2)", "register": 43762, "enabled": True},
{"name": "Grid Time of Use Charge Start (Slot 3)", "register": 43725, "enabled": True},
{"name": "Grid Time of Use Charge End (Slot 3)", "register": 43727, "enabled": True},
{"name": "Grid Time of Use Discharge Start (Slot 3)", "register": 43767, "enabled": True},
{"name": "Grid Time of Use Discharge End (Slot 3)", "register": 43769, "enabled": True},
{"name": "Grid Time of Use Charge Start (Slot 4)", "register": 43732, "enabled": True},
{"name": "Grid Time of Use Charge End (Slot 4)", "register": 43734, "enabled": True},
{"name": "Grid Time of Use Discharge Start (Slot 4)", "register": 43774, "enabled": True},
{"name": "Grid Time of Use Discharge End (Slot 4)", "register": 43776, "enabled": True},
{"name": "Grid Time of Use Charge Start (Slot 5)", "register": 43739, "enabled": True},
{"name": "Grid Time of Use Charge End (Slot 5)", "register": 43741, "enabled": True},
{"name": "Grid Time of Use Discharge Start (Slot 5)", "register": 43781, "enabled": True},
{"name": "Grid Time of Use Discharge End (Slot 5)", "register": 43783, "enabled": True},
{"name": "Grid Time of Use Charge Start (Slot 6)", "register": 43746, "enabled": True},
{"name": "Grid Time of Use Charge End (Slot 6)", "register": 43748, "enabled": True},
{"name": "Grid Time of Use Discharge Start (Slot 6)", "register": 43788, "enabled": True},
{"name": "Grid Time of Use Discharge End (Slot 6)", "register": 43790, "enabled": True},
{"name": "Timed Charge Start 1", "register": 43711, "enabled": True, "control": True},
{"name": "Timed Charge End 1", "register": 43713, "enabled": True, "control": True},
{"name": "Timed Charge Start 7", "register": 43753, "enabled": True, "control": True},
{"name": "Timed Charge End 7", "register": 43755, "enabled": True, "control": True},
{"name": "Timed Charge Start 2", "register": 43718, "enabled": True, "control": True},
{"name": "Timed Charge End 2", "register": 43720, "enabled": True, "control": True},
{"name": "Timed Charge Start 8", "register": 43760, "enabled": True, "control": True},
{"name": "Timed Charge End 8", "register": 43762, "enabled": True, "control": True},
{"name": "Timed Charge Start 3", "register": 43725, "enabled": True, "control": True},
{"name": "Timed Charge End 3", "register": 43727, "enabled": True, "control": True},
{"name": "Timed Charge Start 9", "register": 43767, "enabled": True, "control": True},
{"name": "Timed Charge End 9", "register": 43769, "enabled": True, "control": True},
{"name": "Timed Charge Start 4", "register": 43732, "enabled": True, "control": True},
{"name": "Timed Charge End 4", "register": 43734, "enabled": True, "control": True},
{"name": "Timed Charge Start 10", "register": 43774, "enabled": True, "control": True},
{"name": "Timed Charge End 10", "register": 43776, "enabled": True, "control": True},
{"name": "Timed Charge Start 5", "register": 43739, "enabled": True, "control": True},
{"name": "Timed Charge End 5", "register": 43741, "enabled": True, "control": True},
{"name": "Timed Charge Start 11", "register": 43781, "enabled": True, "control": True},
{"name": "Timed Charge End 11", "register": 43783, "enabled": True, "control": True},
{"name": "Timed Charge Start 6", "register": 43746, "enabled": True, "control": True},
{"name": "Timed Charge End 6", "register": 43748, "enabled": True, "control": True},
{"name": "Timed Charge Start 12", "register": 43788, "enabled": True, "control": True},
{"name": "Timed Charge End 12", "register": 43790, "enabled": True, "control": True},
]
)

Expand Down
4 changes: 3 additions & 1 deletion custom_components/solisconnect/sensors/entity_category.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
}


def entity_category_for_sensor_category(category: Category | None) -> EntityCategory | None:
def entity_category_for_sensor_category(category: Category | None, control: bool = False) -> EntityCategory | None:
if control:
return None
if category in SETTING_CATEGORIES:
return EntityCategory.CONFIG
return None
3 changes: 3 additions & 0 deletions custom_components/solisconnect/sensors/solis_base_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def __init__(
poll_speed=PollSpeed.NORMAL,
data_type: str | None = None,
value_format: str | None = None,
control: bool = False,
):
"""
:param name: Sensor name
Expand Down Expand Up @@ -92,6 +93,7 @@ def __init__(
self.category = category
self.identification = identification
self.value_format = value_format
self.control = control

self.dynamic_adjustments()

Expand Down Expand Up @@ -234,6 +236,7 @@ def __init__(self, hass, definition, controller, identification=None):
multiplier=entity.get("multiplier", 1),
data_type=entity.get("data_type", None),
value_format=entity.get("value_format", None),
control=entity.get("control", False),
unique_id=unique_id_generator(controller, entity),
poll_speed=definition.get("poll_speed", PollSpeed.NORMAL),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ def __init__(self, hass, modbus_controller, entity_definition):
self._on_value = entity_definition.get("on_value", None)
self._off_value = entity_definition.get("off_value", None)
self._inverted = entity_definition.get("inverted", None)
self._control = entity_definition.get("control", False)
self._attr_unique_id = unique_id_generator_binary(modbus_controller, self._register, self._bit_position, self._on_value)
self._attr_name = entity_definition["name"]
self._attr_available = False
self._attr_entity_category = EntityCategory.CONFIG
self._attr_entity_category = None if self._control else EntityCategory.CONFIG
self._attr_entity_registry_enabled_default = entity_definition.get("enabled", True)

async def async_added_to_hass(self) -> None:
"""Called when entity is added to HA."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ def __init__(self, hass, sensor: SolisBaseSensor):
self._attr_state_class = sensor.state_class
self._attr_native_unit_of_measurement = sensor.unit_of_measurement
self._attr_available = not sensor.hidden and sensor.enabled
self._attr_entity_category = entity_category_for_sensor_category(getattr(sensor, "category", None))
self._attr_entity_category = entity_category_for_sensor_category(
getattr(sensor, "category", None),
getattr(sensor, "control", False),
)

self._received_values = {}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging

from homeassistant.components.select import SelectEntity
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.restore_state import RestoreEntity

from custom_components.solisconnect.const import DOMAIN, PROTOCOL_CLOUD, PROTOCOL_MODBUS
Expand All @@ -16,7 +15,6 @@ class SolisProtocolSelect(RestoreEntity, SelectEntity):
"""Chooses which protocol actively polls and takes writes (manual dual mode only)."""

_attr_has_entity_name = True
_attr_entity_category = EntityCategory.CONFIG
_attr_icon = "mdi:swap-horizontal"

def __init__(self, hass, hub):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(self, hass, modbus_controller, entity_definition) -> None:
self._register = entity_definition["register"]
self._attr_name = entity_definition["name"]
self._attr_unique_id = unique_id_generator(modbus_controller, entity_definition["register"], "select")
self._attr_entity_category = EntityCategory.CONFIG
self._attr_entity_category = None if entity_definition.get("control", False) else EntityCategory.CONFIG

self._attr_options = [e["name"] for e in entity_definition["entities"]]
self._attr_options_raw = entity_definition["entities"]
Expand Down
5 changes: 4 additions & 1 deletion custom_components/solisconnect/sensors/solis_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ def __init__(self, hass: HomeAssistant, sensor: SolisBaseSensor):
self._attr_native_unit_of_measurement = sensor.unit_of_measurement
self._attr_available = not sensor.hidden and sensor.enabled
self._attr_suggested_display_precision = self.decimal_count(sensor.multiplier)
self._attr_entity_category = entity_category_for_sensor_category(getattr(sensor, "category", None))
self._attr_entity_category = entity_category_for_sensor_category(
getattr(sensor, "category", None),
getattr(sensor, "control", False),
)

self.is_added_to_hass = False
self._state = None
Expand Down
3 changes: 2 additions & 1 deletion custom_components/solisconnect/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def __init__(self, hass, modbus_controller, entity_definition):
self._attr_has_entity_name = True
self._attr_available = True
self._attr_device_class = entity_definition.get("device_class", None)
self._attr_entity_category = EntityCategory.CONFIG
self._attr_entity_category = None if entity_definition.get("control", False) else EntityCategory.CONFIG
self._attr_entity_registry_enabled_default = entity_definition.get("enabled", True)
self._attr_available = True

self._received_values = {}
Expand Down
Loading
Loading