Add Sofar settings sensors for the paired-register controls (#181197)

This commit is contained in:
darkrain-nl
2026-09-03 20:16:43 +02:00
committed by GitHub
parent 38df1695f2
commit f5809f5b87
4 changed files with 555 additions and 2 deletions
+72
View File
@@ -7,6 +7,7 @@ from enum import IntEnum
from typing import cast, override
from sofar_modbus.modern.device import SofarInverter
from sofar_modbus.modern.enums import FeedinLimitationMode, PassiveModeTimeoutAction
from homeassistant.components.sensor import (
RestoreSensor,
@@ -1440,6 +1441,77 @@ SENSOR_DESCRIPTIONS: tuple[SofarSensorDescription, ...] = (
],
entity_category=EntityCategory.DIAGNOSTIC,
),
SofarSensorDescription(
key="feedin_limitation_mode",
component="feed_in",
translation_key="feedin_limitation_mode",
device_class=SensorDeviceClass.ENUM,
options=[mode.name.lower() for mode in FeedinLimitationMode],
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SofarSensorDescription(
key="feedin_max_power",
component="feed_in",
translation_key="feedin_max_power",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SofarSensorDescription(
key="active_power_export_limit",
component="active_power_control",
translation_key="active_power_export_limit",
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SofarSensorDescription(
key="passive_mode_timeout",
component="passive",
translation_key="passive_mode_timeout",
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SofarSensorDescription(
key="passive_mode_timeout_action",
component="passive",
translation_key="passive_mode_timeout_action",
device_class=SensorDeviceClass.ENUM,
options=[action.name.lower() for action in PassiveModeTimeoutAction],
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SofarSensorDescription(
key="passive_mode_grid_power",
component="passive",
translation_key="passive_mode_grid_power",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SofarSensorDescription(
key="passive_mode_battery_power_min",
component="passive",
translation_key="passive_mode_battery_power_min",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SofarSensorDescription(
key="passive_mode_battery_power_max",
component="passive",
translation_key="passive_mode_battery_power_max",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
)
SENSOR_DESCRIPTIONS += _part_sensors(
@@ -131,6 +131,9 @@
}
},
"sensor": {
"active_power_export_limit": {
"name": "Active power limit"
},
"active_power_load_sys": {
"name": "Active power load system"
},
@@ -317,6 +320,17 @@
"export_energy_total": {
"name": "Export energy total"
},
"feedin_limitation_mode": {
"name": "Feed-in limitation mode",
"state": {
"disabled": "[%key:component::sofar::selector::feedin_limitation_mode::options::disabled%]",
"enabled_3_phase_limit": "[%key:component::sofar::selector::feedin_limitation_mode::options::enabled_3_phase_limit%]",
"enabled_feed_in_limitation": "[%key:component::sofar::selector::feedin_limitation_mode::options::enabled_feed_in_limitation%]"
}
},
"feedin_max_power": {
"name": "Feed-in maximum power"
},
"grid_frequency": {
"name": "Grid frequency"
},
@@ -446,6 +460,25 @@
"passive_eps_wait_time": {
"name": "EPS wait time"
},
"passive_mode_battery_power_max": {
"name": "Passive mode maximum battery power"
},
"passive_mode_battery_power_min": {
"name": "Passive mode minimum battery power"
},
"passive_mode_grid_power": {
"name": "Passive mode grid power"
},
"passive_mode_timeout": {
"name": "Passive mode timeout"
},
"passive_mode_timeout_action": {
"name": "Passive mode timeout action",
"state": {
"force_standby": "[%key:component::sofar::selector::passive_mode_timeout_action::options::force_standby%]",
"return_to_previous_mode": "[%key:component::sofar::selector::passive_mode_timeout_action::options::return_to_previous_mode%]"
}
},
"power": {
"name": "Power"
},
@@ -769,6 +769,57 @@
'state': '51.5',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_active_power_limit-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.hydxxktl_3p_active_power_limit',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Active power limit',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Active power limit',
'platform': 'sofar',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'active_power_export_limit',
'unique_id': 'SP1XXES100XX_active_power_export_limit',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_active_power_limit-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'HYDxxKTL-3P Active power limit',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.hydxxktl_3p_active_power_limit',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0.0',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_active_power_load_system-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
@@ -3747,6 +3798,123 @@
'state': '0.0',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_feed_in_limitation_mode-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'disabled',
'enabled_feed_in_limitation',
'enabled_3_phase_limit',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.hydxxktl_3p_feed_in_limitation_mode',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Feed-in limitation mode',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Feed-in limitation mode',
'platform': 'sofar',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'feedin_limitation_mode',
'unique_id': 'SP1XXES100XX_feedin_limitation_mode',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_feed_in_limitation_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'enum',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'HYDxxKTL-3P Feed-in limitation mode',
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'disabled',
'enabled_feed_in_limitation',
'enabled_3_phase_limit',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.hydxxktl_3p_feed_in_limitation_mode',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'disabled',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_feed_in_maximum_power-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.hydxxktl_3p_feed_in_maximum_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Feed-in maximum power',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Feed-in maximum power',
'platform': 'sofar',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'feedin_max_power',
'unique_id': 'SP1XXES100XX_feedin_max_power',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_feed_in_maximum_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'power',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'HYDxxKTL-3P Feed-in maximum power',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.hydxxktl_3p_feed_in_maximum_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_grid_frequency-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
@@ -5823,6 +5991,286 @@
'state': '0.0',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_grid_power-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_grid_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Passive mode grid power',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Passive mode grid power',
'platform': 'sofar',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'passive_mode_grid_power',
'unique_id': 'SP1XXES100XX_passive_mode_grid_power',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_grid_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'power',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'HYDxxKTL-3P Passive mode grid power',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_grid_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_maximum_battery_power-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_maximum_battery_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Passive mode maximum battery power',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Passive mode maximum battery power',
'platform': 'sofar',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'passive_mode_battery_power_max',
'unique_id': 'SP1XXES100XX_passive_mode_battery_power_max',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_maximum_battery_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'power',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'HYDxxKTL-3P Passive mode maximum battery power',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_maximum_battery_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_minimum_battery_power-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_minimum_battery_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Passive mode minimum battery power',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Passive mode minimum battery power',
'platform': 'sofar',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'passive_mode_battery_power_min',
'unique_id': 'SP1XXES100XX_passive_mode_battery_power_min',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_minimum_battery_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'power',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'HYDxxKTL-3P Passive mode minimum battery power',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_minimum_battery_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_timeout-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_timeout',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Passive mode timeout',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Passive mode timeout',
'platform': 'sofar',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'passive_mode_timeout',
'unique_id': 'SP1XXES100XX_passive_mode_timeout',
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_timeout-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'HYDxxKTL-3P Passive mode timeout',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_timeout',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_timeout_action-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'force_standby',
'return_to_previous_mode',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_timeout_action',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Passive mode timeout action',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Passive mode timeout action',
'platform': 'sofar',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'passive_mode_timeout_action',
'unique_id': 'SP1XXES100XX_passive_mode_timeout_action',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_passive_mode_timeout_action-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'enum',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'HYDxxKTL-3P Passive mode timeout action',
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'force_standby',
'return_to_previous_mode',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.hydxxktl_3p_passive_mode_timeout_action',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'force_standby',
})
# ---
# name: test_all_entities[sensor.hydxxktl_3p_power_factor_output_l1-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
+2 -2
View File
@@ -102,12 +102,12 @@ async def test_sensor_entities_created_and_state(
@pytest.mark.parametrize(
("serial", "model", "seed", "created", "enabled"),
[
pytest.param(MOCK_SERIAL, MOCK_MODEL, seed_pv_inverter, 71, 21, id="pv"),
pytest.param(MOCK_SERIAL, MOCK_MODEL, seed_pv_inverter, 74, 21, id="pv"),
pytest.param(
MOCK_HYBRID_SERIAL,
MOCK_HYBRID_MODEL,
seed_hybrid_inverter,
137,
145,
44,
id="hybrid",
),