mirror of
https://github.com/home-assistant/core.git
synced 2026-09-10 23:51:49 +01:00
Seed Sofar total sensors' high-water marks before the first poll (#180624)
This commit is contained in:
@@ -7,13 +7,23 @@ from modbus_connection import ModbusError, ModbusTcpParams
|
||||
from sofar_modbus.modern.device import SofarInverter, identify
|
||||
|
||||
from homeassistant.components.modbus import async_get_unit
|
||||
from homeassistant.components.sensor import (
|
||||
DOMAIN as SENSOR_DOMAIN,
|
||||
SensorExtraStoredData,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryError
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers import (
|
||||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
restore_state,
|
||||
)
|
||||
|
||||
from .const import CONF_UNIT_ID, DOMAIN, SCAN_INTERVAL, SETTINGS_SCAN_INTERVAL
|
||||
from .coordinator import SofarConfigEntry, SofarDataUpdateCoordinator, SofarRuntimeData
|
||||
from .sensor import SENSOR_DESCRIPTIONS
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -34,6 +44,30 @@ async def _async_read_identity(entry: SofarConfigEntry, device: SofarInverter) -
|
||||
return
|
||||
|
||||
|
||||
def _async_seed_high_water_marks(
|
||||
hass: HomeAssistant, serial: str, device: SofarInverter
|
||||
) -> None:
|
||||
"""Prime high-water marks before the first poll has nothing to compare."""
|
||||
registry = er.async_get(hass)
|
||||
last_states = restore_state.async_get(hass).last_states
|
||||
for description in SENSOR_DESCRIPTIONS:
|
||||
if description.state_class is not SensorStateClass.TOTAL_INCREASING:
|
||||
continue
|
||||
entity_id = registry.async_get_entity_id(
|
||||
SENSOR_DOMAIN, DOMAIN, f"{serial}_{description.key}"
|
||||
)
|
||||
if entity_id is None or (stored := last_states.get(entity_id)) is None:
|
||||
continue
|
||||
if stored.extra_data is None:
|
||||
continue
|
||||
extra = SensorExtraStoredData.from_dict(stored.extra_data.as_dict())
|
||||
if extra is None or not isinstance(extra.native_value, (int, float)):
|
||||
continue
|
||||
getattr(device, description.component).seed_high_water(
|
||||
description.key, float(extra.native_value)
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SofarConfigEntry) -> bool:
|
||||
"""Set up Sofar Inverter Modbus from a config entry."""
|
||||
serial = entry.unique_id
|
||||
@@ -59,6 +93,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: SofarConfigEntry) -> boo
|
||||
model=model,
|
||||
inverter_type=inverter_type,
|
||||
)
|
||||
_async_seed_high_water_marks(hass, serial, device)
|
||||
|
||||
readings = SofarDataUpdateCoordinator(
|
||||
hass,
|
||||
|
||||
@@ -473,6 +473,45 @@ async def test_only_wired_battery_packs_become_devices(
|
||||
assert entity_registry.async_get(total_id).device_id == inverter.id
|
||||
|
||||
|
||||
async def test_total_survives_a_torn_first_poll_after_reload(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_connection: MockModbusConnection,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test a reload's first poll is protected by the pre-reload total."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
unit = mock_connection.for_unit(1)
|
||||
unit.holding[0x068A] = 0
|
||||
unit.holding[0x068B] = 10000 # load_consumption_total -> 1000.0 kWh
|
||||
|
||||
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)
|
||||
|
||||
entity_id = entity_registry.async_get_entity_id(
|
||||
SENSOR_DOMAIN, DOMAIN, f"{MOCK_SERIAL}_load_consumption_total"
|
||||
)
|
||||
assert entity_id is not None
|
||||
assert hass.states.get(entity_id).state == "1000.0"
|
||||
|
||||
await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# A torn read on the reload's first poll, inside the 1% dip band.
|
||||
unit.holding[0x068B] = 9995
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
assert hass.states.get(entity_id).state == "1000.0"
|
||||
|
||||
|
||||
async def test_battery_pack_appears_once_its_block_answers(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
|
||||
Reference in New Issue
Block a user