Drop Sofar meter sensors a model denies from the registry (#182633)

This commit is contained in:
darkrain-nl
2026-09-24 21:50:07 +02:00
committed by GitHub
parent c52809cec7
commit 4dfd74e0e4
4 changed files with 64 additions and 8 deletions
+24 -1
View File
@@ -15,7 +15,7 @@ from homeassistant.components.sensor import (
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryError
from homeassistant.helpers import (
config_validation as cv,
@@ -29,6 +29,7 @@ from .const import (
BATTERY_COMPONENTS,
CONF_UNIT_ID,
DOMAIN,
METER_ENERGY,
SCAN_INTERVAL,
SETTINGS_SCAN_INTERVAL,
)
@@ -51,6 +52,7 @@ _IDENTITY_ATTEMPTS = 3
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
@callback
def _async_remove_stale_waiting_time(hass: HomeAssistant, serial: str) -> None:
"""Drop the removed waiting-time entity so it doesn't linger unavailable."""
registry = er.async_get(hass)
@@ -61,6 +63,24 @@ def _async_remove_stale_waiting_time(hass: HomeAssistant, serial: str) -> None:
registry.async_remove(entity_id)
@callback
def _async_remove_denied_meter_energy(
hass: HomeAssistant, serial: str, served: frozenset[str]
) -> None:
"""Drop meter sensors a model denies, so they don't linger unavailable."""
if METER_ENERGY in served:
return
registry = er.async_get(hass)
for description in SENSOR_DESCRIPTIONS:
if description.component != METER_ENERGY:
continue
entity_id = registry.async_get_entity_id(
SENSOR_DOMAIN, DOMAIN, f"{serial}_{description.key}"
)
if entity_id is not None:
registry.async_remove(entity_id)
async def _async_read_identity(entry: SofarConfigEntry, device: SofarInverter) -> None:
"""Read identity once, retrying a few times against a transient blip."""
for attempt in range(_IDENTITY_ATTEMPTS):
@@ -156,6 +176,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: SofarConfigEntry) -> boo
config_entry_id=entry.entry_id, **readings.device_info
)
entry.runtime_data = SofarRuntimeData(readings, settings, inverter.id)
_async_remove_denied_meter_energy(
hass, serial, entry.runtime_data.served_components
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
+2
View File
@@ -11,6 +11,8 @@ SETTINGS_SCAN_INTERVAL = 60
CONF_UNIT_ID = "unit_id"
METER_ENERGY = "meter_energy"
BATTERY_COMPONENTS = {
n: "battery_1_2" if n <= 2 else "battery_3_8" for n in range(1, 9)
}
+7 -7
View File
@@ -31,7 +31,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import BATTERY_COMPONENTS
from .const import BATTERY_COMPONENTS, METER_ENERGY
from .coordinator import SofarConfigEntry
from .entity import SofarEntity, SofarEntityDescription
@@ -1153,7 +1153,7 @@ SENSOR_DESCRIPTIONS: tuple[SofarSensorDescription, ...] = (
),
SofarSensorDescription(
key="load_consumption_today",
component="meter_energy",
component=METER_ENERGY,
translation_key="load_consumption_today",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
@@ -1163,7 +1163,7 @@ SENSOR_DESCRIPTIONS: tuple[SofarSensorDescription, ...] = (
),
SofarSensorDescription(
key="load_consumption_total",
component="meter_energy",
component=METER_ENERGY,
translation_key="load_consumption_total",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
@@ -1172,7 +1172,7 @@ SENSOR_DESCRIPTIONS: tuple[SofarSensorDescription, ...] = (
),
SofarSensorDescription(
key="import_energy_today",
component="meter_energy",
component=METER_ENERGY,
translation_key="import_energy_today",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
@@ -1182,7 +1182,7 @@ SENSOR_DESCRIPTIONS: tuple[SofarSensorDescription, ...] = (
),
SofarSensorDescription(
key="import_energy_total",
component="meter_energy",
component=METER_ENERGY,
translation_key="import_energy_total",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
@@ -1191,7 +1191,7 @@ SENSOR_DESCRIPTIONS: tuple[SofarSensorDescription, ...] = (
),
SofarSensorDescription(
key="export_energy_today",
component="meter_energy",
component=METER_ENERGY,
translation_key="export_energy_today",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
@@ -1201,7 +1201,7 @@ SENSOR_DESCRIPTIONS: tuple[SofarSensorDescription, ...] = (
),
SofarSensorDescription(
key="export_energy_total",
component="meter_energy",
component=METER_ENERGY,
translation_key="export_energy_total",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
+31
View File
@@ -255,6 +255,37 @@ async def test_setup_keeps_meter_sensors_a_model_serves(
assert hass.states.get(entity_id).state == "1000.0"
async def test_setup_removes_meter_sensors_a_model_denies(
hass: HomeAssistant,
mock_connection: MockModbusConnection,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test an upgrade drops meter sensors the model turns out to deny."""
mock_config_entry.add_to_hass(hass)
deny_meter_energy(mock_connection.for_unit(1))
stale = [
entity_registry.async_get_or_create(
SENSOR_DOMAIN,
DOMAIN,
f"{MOCK_SERIAL}_{key}",
config_entry=mock_config_entry,
).entity_id
for key in METER_ENERGY_KEYS
]
with patch(
"homeassistant.components.sofar.async_get_unit",
side_effect=lambda hass, entry, params, unit_id: mock_connection.for_unit(
unit_id
),
):
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done(wait_background_tasks=True)
assert all(entity_registry.async_get(entity_id) is None for entity_id in stale)
async def test_setup_keeps_meter_sensors_when_no_mask_is_published(
hass: HomeAssistant,
mock_connection: MockModbusConnection,