Improve Fronius tests (#132872)

This commit is contained in:
Matthias Alphart 2024-12-15 19:40:51 +01:00 committed by GitHub
parent 544ebcf310
commit be6ed05aa2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 9132 additions and 399 deletions

View File

@ -3,20 +3,16 @@
from __future__ import annotations
from collections.abc import Callable
from datetime import timedelta
import json
from typing import Any
from freezegun.api import FrozenDateTimeFactory
from homeassistant.components.fronius.const import DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.typing import UNDEFINED, UndefinedType
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
from tests.common import MockConfigEntry, load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
MOCK_HOST = "http://fronius"
@ -115,24 +111,3 @@ def mock_responses(
f"{host}/solar_api/v1/GetOhmPilotRealtimeData.cgi?Scope=System",
text=_load(f"{fixture_set}/GetOhmPilotRealtimeData.json", "fronius"),
)
async def enable_all_entities(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
config_entry_id: str,
time_till_next_update: timedelta,
) -> None:
"""Enable all entities for a config entry and fast forward time to receive data."""
registry = er.async_get(hass)
entities = er.async_entries_for_config_entry(registry, config_entry_id)
for entry in [
entry
for entry in entities
if entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
]:
registry.async_update_entity(entry.entity_id, disabled_by=None)
await hass.async_block_till_done()
freezer.tick(time_till_next_update)
async_fire_time_changed(hass)
await hass.async_block_till_done()

File diff suppressed because it is too large Load Diff

View File

@ -44,43 +44,62 @@ MOCK_DHCP_DATA = DhcpServiceInfo(
)
async def test_form_with_logger(hass: HomeAssistant) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert not result["errors"]
with (
patch(
"pyfronius.Fronius.current_logger_info",
return_value=LOGGER_INFO_RETURN_VALUE,
),
patch(
"homeassistant.components.fronius.async_setup_entry",
return_value=True,
) as mock_setup_entry,
async def assert_finish_flow_with_logger(hass: HomeAssistant, flow_id: str) -> None:
"""Assert finishing the flow with a logger device."""
with patch(
"pyfronius.Fronius.current_logger_info",
return_value=LOGGER_INFO_RETURN_VALUE,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
result = await hass.config_entries.flow.async_configure(
flow_id,
{
"host": "10.9.8.1",
},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "SolarNet Datalogger at 10.9.8.1"
assert result2["data"] == {
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "SolarNet Datalogger at 10.9.8.1"
assert result["data"] == {
"host": "10.9.8.1",
"is_logger": True,
}
assert len(mock_setup_entry.mock_calls) == 1
assert result["result"].unique_id == "123.4567"
async def assert_abort_flow_with_logger(
hass: HomeAssistant, flow_id: str, reason: str
) -> config_entries.ConfigFlowResult:
"""Assert the flow was aborted when a logger device responded."""
with patch(
"pyfronius.Fronius.current_logger_info",
return_value=LOGGER_INFO_RETURN_VALUE,
):
result = await hass.config_entries.flow.async_configure(
flow_id,
{
"host": "10.9.8.1",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == reason
return result
async def test_form_with_logger(hass: HomeAssistant) -> None:
"""Test the basic flow with a logger device."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert not result["errors"]
await assert_finish_flow_with_logger(hass, result["flow_id"])
async def test_form_with_inverter(hass: HomeAssistant) -> None:
"""Test we get the form."""
"""Test the basic flow with a Gen24 device."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
@ -96,10 +115,6 @@ async def test_form_with_inverter(hass: HomeAssistant) -> None:
"pyfronius.Fronius.inverter_info",
return_value=INVERTER_INFO_RETURN_VALUE,
),
patch(
"homeassistant.components.fronius.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -115,7 +130,7 @@ async def test_form_with_inverter(hass: HomeAssistant) -> None:
"host": "10.9.1.1",
"is_logger": False,
}
assert len(mock_setup_entry.mock_calls) == 1
assert result2["result"].unique_id == "1234567"
@pytest.mark.parametrize(
@ -154,6 +169,7 @@ async def test_form_cannot_connect(
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "cannot_connect"}
await assert_finish_flow_with_logger(hass, result2["flow_id"])
async def test_form_unexpected(hass: HomeAssistant) -> None:
@ -175,13 +191,14 @@ async def test_form_unexpected(hass: HomeAssistant) -> None:
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "unknown"}
await assert_finish_flow_with_logger(hass, result2["flow_id"])
async def test_form_already_existing(hass: HomeAssistant) -> None:
"""Test existing entry."""
MockConfigEntry(
domain=DOMAIN,
unique_id="123.4567",
unique_id=LOGGER_INFO_RETURN_VALUE["unique_identifier"]["value"],
data={CONF_HOST: "10.9.8.1", "is_logger": True},
).add_to_hass(hass)
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
@ -189,20 +206,9 @@ async def test_form_already_existing(hass: HomeAssistant) -> None:
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"pyfronius.Fronius.current_logger_info",
return_value=LOGGER_INFO_RETURN_VALUE,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"host": "10.9.8.1",
},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.ABORT
assert result2["reason"] == "already_configured"
await assert_abort_flow_with_logger(
hass, result["flow_id"], reason="already_configured"
)
async def test_config_flow_already_configured(
@ -273,6 +279,7 @@ async def test_dhcp(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) ->
"host": MOCK_DHCP_DATA.ip,
"is_logger": True,
}
assert result["result"].unique_id == "123.4567"
async def test_dhcp_already_configured(
@ -345,10 +352,6 @@ async def test_reconfigure(hass: HomeAssistant) -> None:
"pyfronius.Fronius.inverter_info",
return_value=INVERTER_INFO_RETURN_VALUE,
),
patch(
"homeassistant.components.fronius.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -364,14 +367,13 @@ async def test_reconfigure(hass: HomeAssistant) -> None:
"host": new_host,
"is_logger": False,
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_reconfigure_cannot_connect(hass: HomeAssistant) -> None:
"""Test we handle cannot connect error."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="123.4567890",
unique_id=LOGGER_INFO_RETURN_VALUE["unique_identifier"]["value"],
data={
CONF_HOST: "10.1.2.3",
"is_logger": True,
@ -401,12 +403,16 @@ async def test_reconfigure_cannot_connect(hass: HomeAssistant) -> None:
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "cannot_connect"}
await assert_abort_flow_with_logger(
hass, result2["flow_id"], reason="reconfigure_successful"
)
async def test_reconfigure_unexpected(hass: HomeAssistant) -> None:
"""Test we handle unexpected error."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="123.4567890",
unique_id=LOGGER_INFO_RETURN_VALUE["unique_identifier"]["value"],
data={
CONF_HOST: "10.1.2.3",
"is_logger": True,
@ -430,12 +436,16 @@ async def test_reconfigure_unexpected(hass: HomeAssistant) -> None:
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "unknown"}
await assert_abort_flow_with_logger(
hass, result2["flow_id"], reason="reconfigure_successful"
)
async def test_reconfigure_already_configured(hass: HomeAssistant) -> None:
"""Test reconfiguring an entry."""
async def test_reconfigure_to_different_device(hass: HomeAssistant) -> None:
"""Test reconfiguring an entry to a different device."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="123.4567890",
unique_id="999.9999999",
data={
CONF_HOST: "10.1.2.3",
"is_logger": True,
@ -447,68 +457,6 @@ async def test_reconfigure_already_configured(hass: HomeAssistant) -> None:
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure"
with (
patch(
"pyfronius.Fronius.current_logger_info",
return_value=LOGGER_INFO_RETURN_VALUE,
),
patch(
"pyfronius.Fronius.inverter_info",
return_value=INVERTER_INFO_RETURN_VALUE,
),
patch(
"homeassistant.components.fronius.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
"host": "10.1.2.3",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "unique_id_mismatch"
assert len(mock_setup_entry.mock_calls) == 0
async def test_reconfigure_already_existing(hass: HomeAssistant) -> None:
"""Test reconfiguring entry to already existing device."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="123.4567890",
data={
CONF_HOST: "10.1.2.3",
"is_logger": True,
},
await assert_abort_flow_with_logger(
hass, result["flow_id"], reason="unique_id_mismatch"
)
entry.add_to_hass(hass)
entry_2_uid = "222.2222222"
entry_2 = MockConfigEntry(
domain=DOMAIN,
unique_id=entry_2_uid,
data={
CONF_HOST: "10.2.2.2",
"is_logger": True,
},
)
entry_2.add_to_hass(hass)
result = await entry.start_reconfigure_flow(hass)
with patch(
"pyfronius.Fronius.current_logger_info",
return_value={"unique_identifier": {"value": entry_2_uid}},
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"host": "10.1.1.1",
},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.ABORT
assert result2["reason"] == "unique_id_mismatch"

View File

@ -29,7 +29,7 @@ async def test_adaptive_update_interval(
mock_inverter_data.reset_mock()
freezer.tick(FroniusInverterUpdateCoordinator.default_interval)
async_fire_time_changed(hass, None)
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_inverter_data.assert_called_once()
mock_inverter_data.reset_mock()
@ -38,13 +38,13 @@ async def test_adaptive_update_interval(
# first 3 bad requests at default interval - 4th has different interval
for _ in range(3):
freezer.tick(FroniusInverterUpdateCoordinator.default_interval)
async_fire_time_changed(hass, None)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert mock_inverter_data.call_count == 3
mock_inverter_data.reset_mock()
freezer.tick(FroniusInverterUpdateCoordinator.error_interval)
async_fire_time_changed(hass, None)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert mock_inverter_data.call_count == 1
mock_inverter_data.reset_mock()
@ -52,13 +52,13 @@ async def test_adaptive_update_interval(
mock_inverter_data.side_effect = None
# next successful request resets to default interval
freezer.tick(FroniusInverterUpdateCoordinator.error_interval)
async_fire_time_changed(hass, None)
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_inverter_data.assert_called_once()
mock_inverter_data.reset_mock()
freezer.tick(FroniusInverterUpdateCoordinator.default_interval)
async_fire_time_changed(hass, None)
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_inverter_data.assert_called_once()
mock_inverter_data.reset_mock()
@ -68,7 +68,7 @@ async def test_adaptive_update_interval(
# first 3 requests at default interval - 4th has different interval
for _ in range(3):
freezer.tick(FroniusInverterUpdateCoordinator.default_interval)
async_fire_time_changed(hass, None)
async_fire_time_changed(hass)
await hass.async_block_till_done()
# BadStatusError does 3 silent retries for inverter endpoint * 3 request intervals = 9
assert mock_inverter_data.call_count == 9

View File

@ -3,6 +3,7 @@
from datetime import timedelta
from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
from pyfronius import FroniusError
from homeassistant.components.fronius.const import DOMAIN, SOLAR_NET_RESCAN_TIMER
@ -10,7 +11,6 @@ from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
from . import mock_responses, setup_fronius_integration
@ -66,6 +66,7 @@ async def test_inverter_night_rescan(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
aioclient_mock: AiohttpClientMocker,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test dynamic adding of an inverter discovered automatically after a Home Assistant reboot during the night."""
mock_responses(aioclient_mock, fixture_set="igplus_v2", night=True)
@ -78,9 +79,8 @@ async def test_inverter_night_rescan(
# Switch to daytime
mock_responses(aioclient_mock, fixture_set="igplus_v2", night=False)
async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(minutes=SOLAR_NET_RESCAN_TIMER)
)
freezer.tick(timedelta(minutes=SOLAR_NET_RESCAN_TIMER))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# We expect our inverter to be present now
@ -88,9 +88,8 @@ async def test_inverter_night_rescan(
assert inverter_1.manufacturer == "Fronius"
# After another re-scan we still only expect this inverter
async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(minutes=SOLAR_NET_RESCAN_TIMER * 2)
)
freezer.tick(timedelta(minutes=SOLAR_NET_RESCAN_TIMER))
async_fire_time_changed(hass)
await hass.async_block_till_done()
inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "203200")})
assert inverter_1.manufacturer == "Fronius"
@ -100,6 +99,7 @@ async def test_inverter_rescan_interruption(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
aioclient_mock: AiohttpClientMocker,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test interruption of re-scan during runtime to process further."""
mock_responses(aioclient_mock, fixture_set="igplus_v2", night=True)
@ -115,9 +115,8 @@ async def test_inverter_rescan_interruption(
"pyfronius.Fronius.inverter_info",
side_effect=FroniusError,
):
async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(minutes=SOLAR_NET_RESCAN_TIMER)
)
freezer.tick(timedelta(minutes=SOLAR_NET_RESCAN_TIMER))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# No increase of devices expected because of a FroniusError
@ -132,9 +131,8 @@ async def test_inverter_rescan_interruption(
# Next re-scan will pick up the new inverter. Expect 2 devices now.
mock_responses(aioclient_mock, fixture_set="igplus_v2", night=False)
async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(minutes=SOLAR_NET_RESCAN_TIMER * 2)
)
freezer.tick(timedelta(minutes=SOLAR_NET_RESCAN_TIMER))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert (

View File

@ -2,27 +2,29 @@
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.fronius.const import DOMAIN
from homeassistant.components.fronius.coordinator import (
FroniusInverterUpdateCoordinator,
FroniusMeterUpdateCoordinator,
FroniusPowerFlowUpdateCoordinator,
)
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import enable_all_entities, mock_responses, setup_fronius_integration
from . import mock_responses, setup_fronius_integration
from tests.common import async_fire_time_changed
from tests.common import async_fire_time_changed, snapshot_platform
from tests.test_util.aiohttp import AiohttpClientMocker
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_symo_inverter(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion,
) -> None:
"""Test Fronius Symo inverter entities."""
@ -32,15 +34,8 @@ async def test_symo_inverter(
# Init at night
mock_responses(aioclient_mock, night=True)
config_entry = await setup_fronius_integration(hass)
await setup_fronius_integration(hass)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 22
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusInverterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 58
assert_state("sensor.symo_20_dc_current", 0)
assert_state("sensor.symo_20_energy_day", 10828)
@ -54,13 +49,6 @@ async def test_symo_inverter(
freezer.tick(FroniusInverterUpdateCoordinator.default_interval)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 62
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusInverterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 64
# 4 additional AC entities
assert_state("sensor.symo_20_dc_current", 2.19)
@ -104,6 +92,7 @@ async def test_symo_logger(
assert_state("sensor.solarnet_grid_import_tariff", 0.15)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_symo_meter(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
@ -117,15 +106,8 @@ async def test_symo_meter(
assert state.state == str(expected_state)
mock_responses(aioclient_mock)
config_entry = await setup_fronius_integration(hass)
await setup_fronius_integration(hass)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 26
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 64
# states are rounded to 4 decimals
assert_state("sensor.smart_meter_63a_current_phase_1", 7.755)
@ -206,6 +188,7 @@ async def test_symo_meter_forged(
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_symo_power_flow(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
@ -220,15 +203,8 @@ async def test_symo_power_flow(
# First test at night
mock_responses(aioclient_mock, night=True)
config_entry = await setup_fronius_integration(hass)
await setup_fronius_integration(hass)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 22
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusInverterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 58
# states are rounded to 4 decimals
assert_state("sensor.solarnet_energy_day", 10828)
@ -277,10 +253,13 @@ async def test_symo_power_flow(
assert_state("sensor.solarnet_relative_self_consumption", 0)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_gen24(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
freezer: FrozenDateTimeFactory,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test Fronius Gen24 inverter entities."""
@ -292,72 +271,10 @@ async def test_gen24(
mock_responses(aioclient_mock, fixture_set="gen24")
config_entry = await setup_fronius_integration(hass, is_logger=False)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 24
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 58
# inverter 1
assert_state("sensor.inverter_name_ac_current", 0.1589)
assert_state("sensor.inverter_name_dc_current_2", 0.0754)
assert_state("sensor.inverter_name_status_code", 7)
assert_state("sensor.inverter_name_status_message", "running")
assert_state("sensor.inverter_name_dc_current", 0.0783)
assert_state("sensor.inverter_name_dc_voltage_2", 403.4312)
assert_state("sensor.inverter_name_ac_power", 37.3204)
assert_state("sensor.inverter_name_error_code", 0)
assert_state("sensor.inverter_name_dc_voltage", 411.3811)
assert_state("sensor.inverter_name_total_energy", 1530193.42)
assert_state("sensor.inverter_name_inverter_state", "Running")
assert_state("sensor.inverter_name_ac_voltage", 234.9168)
assert_state("sensor.inverter_name_frequency", 49.9917)
# meter
assert_state("sensor.smart_meter_ts_65a_3_real_energy_produced", 3863340.0)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_consumed", 2013105.0)
assert_state("sensor.smart_meter_ts_65a_3_real_power", 653.1)
assert_state("sensor.smart_meter_ts_65a_3_frequency_phase_average", 49.9)
assert_state("sensor.smart_meter_ts_65a_3_meter_location", 0)
assert_state("sensor.smart_meter_ts_65a_3_meter_location_description", "feed_in")
assert_state("sensor.smart_meter_ts_65a_3_power_factor", 0.828)
assert_state("sensor.smart_meter_ts_65a_3_reactive_energy_consumed", 88221.0)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_minus", 3863340.0)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_2", 2.33)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_1", 235.9)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_1_2", 408.7)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_2", 294.9)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_plus", 2013105.0)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_2", 236.1)
assert_state("sensor.smart_meter_ts_65a_3_reactive_energy_produced", 1989125.0)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_3", 236.9)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_1", 0.441)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_2_3", 409.6)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_3", 1.825)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_3", 0.832)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_1", 243.3)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_3_1", 409.4)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_2", 323.4)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_3", 301.2)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_1", 106.8)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_2", 0.934)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_3", 251.3)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_1", -218.6)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_2", -132.8)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_3", -166.0)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power", 868.0)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power", -517.4)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_1", 1.145)
# power_flow
assert_state("sensor.solarnet_power_grid", 658.4)
assert_state("sensor.solarnet_relative_self_consumption", 100.0)
assert_state("sensor.solarnet_power_photovoltaics", 62.9481)
assert_state("sensor.solarnet_power_load", -695.6827)
assert_state("sensor.solarnet_meter_mode", "meter")
assert_state("sensor.solarnet_relative_autonomy", 5.3592)
assert_state("sensor.solarnet_total_energy", 1530193.42)
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
assert_state("sensor.inverter_name_total_energy", 1530193.42)
# Gen24 devices may report 0 for total energy while doing firmware updates.
# This should yield "unknown" state instead of 0.
mock_responses(
@ -375,11 +292,14 @@ async def test_gen24(
assert_state("sensor.inverter_name_total_energy", "unknown")
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_gen24_storage(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
device_registry: dr.DeviceRegistry,
freezer: FrozenDateTimeFactory,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test Fronius Gen24 inverter with BYD battery and Ohmpilot entities."""
@ -393,87 +313,8 @@ async def test_gen24_storage(
hass, is_logger=False, unique_id="12345678"
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 37
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 72
# inverter 1
assert_state("sensor.gen24_storage_dc_current", 0.3952)
assert_state("sensor.gen24_storage_dc_voltage_2", 318.8103)
assert_state("sensor.gen24_storage_dc_current_2", 0.3564)
assert_state("sensor.gen24_storage_ac_current", 1.1087)
assert_state("sensor.gen24_storage_ac_power", 250.9093)
assert_state("sensor.gen24_storage_error_code", 0)
assert_state("sensor.gen24_storage_status_code", 7)
assert_state("sensor.gen24_storage_status_message", "running")
assert_state("sensor.gen24_storage_total_energy", 7512794.0117)
assert_state("sensor.gen24_storage_inverter_state", "Running")
assert_state("sensor.gen24_storage_dc_voltage", 419.1009)
assert_state("sensor.gen24_storage_ac_voltage", 227.354)
assert_state("sensor.gen24_storage_frequency", 49.9816)
# meter
assert_state("sensor.smart_meter_ts_65a_3_real_energy_produced", 1705128.0)
assert_state("sensor.smart_meter_ts_65a_3_real_power", 487.7)
assert_state("sensor.smart_meter_ts_65a_3_power_factor", 0.698)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_consumed", 1247204.0)
assert_state("sensor.smart_meter_ts_65a_3_frequency_phase_average", 49.9)
assert_state("sensor.smart_meter_ts_65a_3_meter_location", 0)
assert_state("sensor.smart_meter_ts_65a_3_meter_location_description", "feed_in")
assert_state("sensor.smart_meter_ts_65a_3_reactive_power", -501.5)
assert_state("sensor.smart_meter_ts_65a_3_reactive_energy_produced", 3266105.0)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_3", 19.6)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_3", 0.645)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_minus", 1705128.0)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_2", 383.9)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_1", 1.701)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_2", 1.832)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_1", 319.5)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_1", 229.4)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_2", 150.0)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_3_1", 394.3)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_2", 225.6)
assert_state("sensor.smart_meter_ts_65a_3_reactive_energy_consumed", 5482.0)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_plus", 1247204.0)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_1", 0.995)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_3", 0.163)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_2", 0.389)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_1", -31.3)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_3", -116.7)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_1_2", 396.0)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_2_3", 393.0)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_2", -353.4)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_1", 317.9)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_3", 228.3)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power", 821.9)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_3", 118.4)
# ohmpilot
assert_state("sensor.ohmpilot_energy_consumed", 1233295.0)
assert_state("sensor.ohmpilot_power", 0.0)
assert_state("sensor.ohmpilot_temperature", 38.9)
assert_state("sensor.ohmpilot_state_code", 0.0)
assert_state("sensor.ohmpilot_state_message", "up_and_running")
# power_flow
assert_state("sensor.solarnet_power_grid", 2274.9)
assert_state("sensor.solarnet_power_battery", 0.1591)
assert_state("sensor.solarnet_power_battery_charge", 0)
assert_state("sensor.solarnet_power_battery_discharge", 0.1591)
assert_state("sensor.solarnet_power_load", -2459.3092)
assert_state("sensor.solarnet_relative_self_consumption", 100.0)
assert_state("sensor.solarnet_power_photovoltaics", 216.4328)
assert_state("sensor.solarnet_relative_autonomy", 7.4984)
assert_state("sensor.solarnet_meter_mode", "bidirectional")
assert_state("sensor.solarnet_total_energy", 7512664.4042)
# storage
assert_state("sensor.byd_battery_box_premium_hv_dc_current", 0.0)
assert_state("sensor.byd_battery_box_premium_hv_state_of_charge", 4.6)
assert_state("sensor.byd_battery_box_premium_hv_maximum_capacity", 16588)
assert_state("sensor.byd_battery_box_premium_hv_temperature", 21.5)
assert_state("sensor.byd_battery_box_premium_hv_designed_capacity", 16588)
assert_state("sensor.byd_battery_box_premium_hv_dc_voltage", 0.0)
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
# Devices
solar_net = device_registry.async_get_device(
@ -507,11 +348,14 @@ async def test_gen24_storage(
assert storage.name == "BYD Battery-Box Premium HV"
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_primo_s0(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
device_registry: dr.DeviceRegistry,
freezer: FrozenDateTimeFactory,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test Fronius Primo dual inverter with S0 meter entities."""
@ -523,64 +367,8 @@ async def test_primo_s0(
mock_responses(aioclient_mock, fixture_set="primo_s0", inverter_ids=[1, 2])
config_entry = await setup_fronius_integration(hass, is_logger=True)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 31
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 47
# logger
assert_state("sensor.solarnet_grid_export_tariff", 1)
assert_state("sensor.solarnet_co2_factor", 0.53)
assert_state("sensor.solarnet_grid_import_tariff", 1)
# inverter 1
assert_state("sensor.primo_5_0_1_total_energy", 17114940)
assert_state("sensor.primo_5_0_1_energy_day", 22504)
assert_state("sensor.primo_5_0_1_dc_voltage", 452.3)
assert_state("sensor.primo_5_0_1_ac_power", 862)
assert_state("sensor.primo_5_0_1_error_code", 0)
assert_state("sensor.primo_5_0_1_dc_current", 4.23)
assert_state("sensor.primo_5_0_1_status_code", 7)
assert_state("sensor.primo_5_0_1_status_message", "running")
assert_state("sensor.primo_5_0_1_energy_year", 7532755.5)
assert_state("sensor.primo_5_0_1_ac_current", 3.85)
assert_state("sensor.primo_5_0_1_ac_voltage", 223.9)
assert_state("sensor.primo_5_0_1_frequency", 60)
assert_state("sensor.primo_5_0_1_led_color", 2)
assert_state("sensor.primo_5_0_1_led_state", 0)
# inverter 2
assert_state("sensor.primo_3_0_1_total_energy", 5796010)
assert_state("sensor.primo_3_0_1_energy_day", 14237)
assert_state("sensor.primo_3_0_1_dc_voltage", 329.5)
assert_state("sensor.primo_3_0_1_ac_power", 296)
assert_state("sensor.primo_3_0_1_error_code", 0)
assert_state("sensor.primo_3_0_1_dc_current", 0.97)
assert_state("sensor.primo_3_0_1_status_code", 7)
assert_state("sensor.primo_3_0_1_status_message", "running")
assert_state("sensor.primo_3_0_1_energy_year", 3596193.25)
assert_state("sensor.primo_3_0_1_ac_current", 1.32)
assert_state("sensor.primo_3_0_1_ac_voltage", 223.6)
assert_state("sensor.primo_3_0_1_frequency", 60.01)
assert_state("sensor.primo_3_0_1_led_color", 2)
assert_state("sensor.primo_3_0_1_led_state", 0)
# meter
assert_state("sensor.s0_meter_at_inverter_1_meter_location", 1)
assert_state(
"sensor.s0_meter_at_inverter_1_meter_location_description", "consumption_path"
)
assert_state("sensor.s0_meter_at_inverter_1_real_power", -2216.7487)
# power_flow
assert_state("sensor.solarnet_power_load", -2218.9349)
assert_state("sensor.solarnet_meter_mode", "vague-meter")
assert_state("sensor.solarnet_power_photovoltaics", 1834)
assert_state("sensor.solarnet_power_grid", 384.9349)
assert_state("sensor.solarnet_relative_self_consumption", 100)
assert_state("sensor.solarnet_relative_autonomy", 82.6523)
assert_state("sensor.solarnet_total_energy", 22910919.5)
assert_state("sensor.solarnet_energy_day", 36724)
assert_state("sensor.solarnet_energy_year", 11128933.25)
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
# Devices
solar_net = device_registry.async_get_device(