"""Test Enphase Envoy sensors."""

from itertools import chain
import logging
from typing import Any
from unittest.mock import AsyncMock, patch

from freezegun.api import FrozenDateTimeFactory
from pyenphase.const import PHASENAMES, PhaseNames
from pyenphase.models.meters import CtType
import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.enphase_envoy.const import Platform
from homeassistant.components.enphase_envoy.coordinator import SCAN_INTERVAL
from homeassistant.components.sensor import SensorStateClass
from homeassistant.const import STATE_UNKNOWN, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.util import dt as dt_util
from homeassistant.util.unit_conversion import TemperatureConverter

from . import setup_integration

from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy",
        "envoy_1p_metered",
        "envoy_eu_batt",
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
        "envoy_tot_cons_metered",
        "envoy_acb_batt",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor(
    hass: HomeAssistant,
    snapshot: SnapshotAssertion,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
    entity_registry: er.EntityRegistry,
) -> None:
    """Test sensor platform entities against snapshot."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)
    await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)


PRODUCTION_NAMES: tuple[str, ...] = (
    "current_power_production",
    "energy_production_today",
    "energy_production_last_seven_days",
    "lifetime_energy_production",
)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy",
        "envoy_1p_metered",
        "envoy_eu_batt",
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
        "envoy_tot_cons_metered",
        "envoy_acb_batt",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_production_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test production entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    data = mock_envoy.data.system_production
    PRODUCTION_TARGETS: tuple[float, ...] = (
        data.watts_now / 1000.0,
        data.watt_hours_today / 1000.0,
        data.watt_hours_last_7_days / 1000.0,
        data.watt_hours_lifetime / 1000000.0,
    )

    for name, target in list(zip(PRODUCTION_NAMES, PRODUCTION_TARGETS, strict=False)):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target


PRODUCTION_PHASE_NAMES: list[str] = [
    f"{name}_{phase.lower()}" for phase in PHASENAMES for name in PRODUCTION_NAMES
]


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_production_phase_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test production phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    PRODUCTION_PHASE_TARGET = chain(
        *[
            (
                phase_data.watts_now / 1000.0,
                phase_data.watt_hours_today / 1000.0,
                phase_data.watt_hours_last_7_days / 1000.0,
                phase_data.watt_hours_lifetime / 1000000.0,
            )
            for phase_data in mock_envoy.data.system_production_phases.values()
        ]
    )

    for name, target in list(
        zip(PRODUCTION_PHASE_NAMES, PRODUCTION_PHASE_TARGET, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target


CONSUMPTION_NAMES: tuple[str, ...] = (
    "current_power_consumption",
    "energy_consumption_today",
    "energy_consumption_last_seven_days",
    "lifetime_energy_consumption",
)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_1p_metered",
        "envoy_eu_batt",
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
        "envoy_acb_batt",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_consumption_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test consumption entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    data = mock_envoy.data.system_consumption
    CONSUMPTION_TARGETS = (
        data.watts_now / 1000.0,
        data.watt_hours_today / 1000.0,
        data.watt_hours_last_7_days / 1000.0,
        data.watt_hours_lifetime / 1000000.0,
    )

    for name, target in list(zip(CONSUMPTION_NAMES, CONSUMPTION_TARGETS, strict=False)):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target


NET_CONSUMPTION_NAMES: tuple[str, ...] = (
    "balanced_net_power_consumption",
    "lifetime_balanced_net_energy_consumption",
)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_1p_metered",
        "envoy_eu_batt",
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
        "envoy_tot_cons_metered",
        "envoy_acb_batt",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_net_consumption_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test net consumption entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    data = mock_envoy.data.system_net_consumption
    NET_CONSUMPTION_TARGETS = (
        data.watts_now / 1000.0,
        data.watt_hours_lifetime / 1000.0,
    )
    for name, target in list(
        zip(NET_CONSUMPTION_NAMES, NET_CONSUMPTION_TARGETS, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target


CONSUMPTION_PHASE_NAMES: list[str] = [
    f"{name}_{phase.lower()}" for phase in PHASENAMES for name in CONSUMPTION_NAMES
]


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_consumption_phase_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test consumption phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    CONSUMPTION_PHASE_TARGET = chain(
        *[
            (
                phase_data.watts_now / 1000.0,
                phase_data.watt_hours_today / 1000.0,
                phase_data.watt_hours_last_7_days / 1000.0,
                phase_data.watt_hours_lifetime / 1000000.0,
            )
            for phase_data in mock_envoy.data.system_consumption_phases.values()
        ]
    )

    for name, target in list(
        zip(CONSUMPTION_PHASE_NAMES, CONSUMPTION_PHASE_TARGET, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target


NET_CONSUMPTION_PHASE_NAMES: list[str] = [
    f"{name}_{phase.lower()}" for phase in PHASENAMES for name in NET_CONSUMPTION_NAMES
]


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_net_consumption_phase_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test consumption phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    NET_CONSUMPTION_PHASE_TARGET = chain(
        *[
            (
                phase_data.watts_now / 1000.0,
                phase_data.watt_hours_lifetime / 1000.0,
            )
            for phase_data in mock_envoy.data.system_net_consumption_phases.values()
        ]
    )
    for name, target in list(
        zip(NET_CONSUMPTION_PHASE_NAMES, NET_CONSUMPTION_PHASE_TARGET, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target


CT_PRODUCTION_NAMES_INT = ("meter_status_flags_active_production_ct",)
CT_PRODUCTION_NAMES_STR = ("metering_status_production_ct",)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_production_ct_data(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    mock_envoy: AsyncMock,
) -> None:
    """Test production CT phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    data = mock_envoy.data.ctmeter_production

    CT_PRODUCTION_TARGETS_INT = (len(data.status_flags),)
    for name, target in list(
        zip(CT_PRODUCTION_NAMES_INT, CT_PRODUCTION_TARGETS_INT, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target

    CT_PRODUCTION_TARGETS_STR = (data.metering_status,)
    for name, target in list(
        zip(CT_PRODUCTION_NAMES_STR, CT_PRODUCTION_TARGETS_STR, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert entity_state.state == target


CT_PRODUCTION_NAMES_FLOAT_PHASE = [
    f"{name}_{phase.lower()}"
    for phase in PHASENAMES
    for name in CT_PRODUCTION_NAMES_INT
]

CT_PRODUCTION_NAMES_STR_PHASE = [
    f"{name}_{phase.lower()}"
    for phase in PHASENAMES
    for name in CT_PRODUCTION_NAMES_STR
]


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_production_ct_phase_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test production ct phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    CT_PRODUCTION_NAMES_FLOAT_TARGET = [
        len(phase_data.status_flags)
        for phase_data in mock_envoy.data.ctmeter_production_phases.values()
    ]

    for name, target in list(
        zip(
            CT_PRODUCTION_NAMES_FLOAT_PHASE,
            CT_PRODUCTION_NAMES_FLOAT_TARGET,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target

    CT_PRODUCTION_NAMES_STR_TARGET = [
        phase_data.metering_status
        for phase_data in mock_envoy.data.ctmeter_production_phases.values()
    ]

    for name, target in list(
        zip(
            CT_PRODUCTION_NAMES_STR_PHASE,
            CT_PRODUCTION_NAMES_STR_TARGET,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert entity_state.state == target


CT_CONSUMPTION_NAMES_FLOAT: tuple[str, ...] = (
    "lifetime_net_energy_consumption",
    "lifetime_net_energy_production",
    "current_net_power_consumption",
    "frequency_net_consumption_ct",
    "voltage_net_consumption_ct",
    "meter_status_flags_active_net_consumption_ct",
)

CT_CONSUMPTION_NAMES_STR: tuple[str, ...] = ("metering_status_net_consumption_ct",)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_consumption_ct_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test consumption CT phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    data = mock_envoy.data.ctmeter_consumption

    CT_CONSUMPTION_TARGETS_FLOAT = (
        data.energy_delivered / 1000000.0,
        data.energy_received / 1000000.0,
        data.active_power / 1000.0,
        data.frequency,
        data.voltage,
        len(data.status_flags),
    )
    for name, target in list(
        zip(CT_CONSUMPTION_NAMES_FLOAT, CT_CONSUMPTION_TARGETS_FLOAT, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target

    CT_CONSUMPTION_TARGETS_STR = (data.metering_status,)
    for name, target in list(
        zip(CT_CONSUMPTION_NAMES_STR, CT_CONSUMPTION_TARGETS_STR, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert entity_state.state == target


CT_CONSUMPTION_NAMES_FLOAT_PHASE = [
    f"{name}_{phase.lower()}"
    for phase in PHASENAMES
    for name in CT_CONSUMPTION_NAMES_FLOAT
]

CT_CONSUMPTION_NAMES_STR_PHASE = [
    f"{name}_{phase.lower()}"
    for phase in PHASENAMES
    for name in CT_CONSUMPTION_NAMES_STR
]


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_consumption_ct_phase_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test consumption ct phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    CT_CONSUMPTION_NAMES_FLOAT_PHASE_TARGET = chain(
        *[
            (
                phase_data.energy_delivered / 1000000.0,
                phase_data.energy_received / 1000000.0,
                phase_data.active_power / 1000.0,
                phase_data.frequency,
                phase_data.voltage,
                len(phase_data.status_flags),
            )
            for phase_data in mock_envoy.data.ctmeter_consumption_phases.values()
        ]
    )

    for name, target in list(
        zip(
            CT_CONSUMPTION_NAMES_FLOAT_PHASE,
            CT_CONSUMPTION_NAMES_FLOAT_PHASE_TARGET,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target

    CT_CONSUMPTION_NAMES_STR_PHASE_TARGET = [
        phase_data.metering_status
        for phase_data in mock_envoy.data.ctmeter_consumption_phases.values()
    ]

    for name, target in list(
        zip(
            CT_CONSUMPTION_NAMES_STR_PHASE,
            CT_CONSUMPTION_NAMES_STR_PHASE_TARGET,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert entity_state.state == target


CT_STORAGE_NAMES_FLOAT = (
    "lifetime_battery_energy_discharged",
    "lifetime_battery_energy_charged",
    "current_battery_discharge",
    "voltage_storage_ct",
    "meter_status_flags_active_storage_ct",
)
CT_STORAGE_NAMES_STR = ("metering_status_storage_ct",)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_storage_ct_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test storage phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    data = mock_envoy.data.ctmeter_storage

    CT_STORAGE_TARGETS_FLOAT = (
        data.energy_delivered / 1000000.0,
        data.energy_received / 1000000.0,
        data.active_power / 1000.0,
        data.voltage,
        len(data.status_flags),
    )
    for name, target in list(
        zip(CT_STORAGE_NAMES_FLOAT, CT_STORAGE_TARGETS_FLOAT, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target

    CT_STORAGE_TARGETS_STR = (data.metering_status,)
    for name, target in list(
        zip(CT_STORAGE_NAMES_STR, CT_STORAGE_TARGETS_STR, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert entity_state.state == target


CT_STORAGE_NAMES_FLOAT_PHASE = [
    f"{name}_{phase.lower()}"
    for phase in PHASENAMES
    for name in (CT_STORAGE_NAMES_FLOAT)
]

CT_STORAGE_NAMES_STR_PHASE = [
    f"{name}_{phase.lower()}" for phase in PHASENAMES for name in (CT_STORAGE_NAMES_STR)
]


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_storage_ct_phase_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test storage ct phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    CT_STORAGE_NAMES_FLOAT_PHASE_TARGET = chain(
        *[
            (
                phase_data.energy_delivered / 1000000.0,
                phase_data.energy_received / 1000000.0,
                phase_data.active_power / 1000.0,
                phase_data.voltage,
                len(phase_data.status_flags),
            )
            for phase_data in mock_envoy.data.ctmeter_storage_phases.values()
        ]
    )

    for name, target in list(
        zip(
            CT_STORAGE_NAMES_FLOAT_PHASE,
            CT_STORAGE_NAMES_FLOAT_PHASE_TARGET,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target

    CT_STORAGE_NAMES_STR_PHASE_TARGET = [
        phase_data.metering_status
        for phase_data in mock_envoy.data.ctmeter_storage_phases.values()
    ]

    for name, target in list(
        zip(
            CT_STORAGE_NAMES_STR_PHASE,
            CT_STORAGE_NAMES_STR_PHASE_TARGET,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert entity_state.state == target


CT_NAMES_FLOAT = (
    "<cttype>_ct_energy_delivered",
    "<cttype>_ct_energy_received",
    "<cttype>_ct_power",
    "frequency_<cttype>_ct",
    "voltage_<cttype>_ct",
    "<cttype>_ct_current",
    "power_factor_<cttype>_ct",
    "meter_status_flags_active_<cttype>_ct",
)
CT_NAMES_STR = ("metering_status_<cttype>_ct",)


@pytest.mark.parametrize(
    ("cttype", "mock_envoy"),
    [
        (CtType.PRODUCTION, "envoy_metered_batt_relay"),
        (CtType.TOTAL_CONSUMPTION, "envoy_tot_cons_metered"),
        (CtType.BACKFEED, "envoy_metered_batt_relay"),
        (CtType.LOAD, "envoy_metered_batt_relay"),
        (CtType.EVSE, "envoy_metered_batt_relay"),
        (CtType.PV3P, "envoy_metered_batt_relay"),
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_ct_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
    cttype: CtType,
) -> None:
    """Test ct entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    data = mock_envoy.data.ctmeters[cttype]

    CT_TARGETS_FLOAT = (
        data.energy_delivered / 1000000.0,
        data.energy_received / 1000000.0,
        data.active_power / 1000.0,
        data.frequency,
        data.voltage,
        data.current,
        data.power_factor,
        len(data.status_flags),
    )
    count_names: int = 0

    for name, target in list(
        zip(
            [
                entity.replace("<cttype>", cttype).replace("-", "_")
                for entity in CT_NAMES_FLOAT
            ],
            CT_TARGETS_FLOAT,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target
        count_names += 1

    CT_TARGETS_STR = (data.metering_status,)
    for name, target in list(
        zip(
            [
                entity.replace("<cttype>", cttype).replace("-", "_")
                for entity in CT_NAMES_STR
            ],
            CT_TARGETS_STR,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert entity_state.state == target
        count_names += 1

    # verify we're testing them all
    assert len(CT_NAMES_FLOAT) + len(CT_NAMES_STR) == count_names


CT_NAMES_FLOAT_PHASE = [
    f"{name}_{phase.lower()}" for phase in PHASENAMES for name in (CT_NAMES_FLOAT)
]

CT_NAMES_STR_PHASE = [
    f"{name}_{phase.lower()}" for phase in PHASENAMES for name in (CT_NAMES_STR)
]


@pytest.mark.parametrize(
    "cttype",
    [
        CtType.PRODUCTION,
        CtType.BACKFEED,
        CtType.LOAD,
        CtType.EVSE,
        CtType.PV3P,
    ],
)
@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_ct_phase_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
    cttype: CtType,
) -> None:
    """Test ct phase entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    CT_NAMES_FLOAT_PHASE_TARGET = chain(
        *[
            (
                phase_data.energy_delivered / 1000000.0,
                phase_data.energy_received / 1000000.0,
                phase_data.active_power / 1000.0,
                phase_data.frequency,
                phase_data.voltage,
                phase_data.current,
                phase_data.power_factor,
                len(phase_data.status_flags),
            )
            for phase_data in mock_envoy.data.ctmeters_phases[cttype].values()
        ]
    )

    count_names: int = 0
    for name, target in list(
        zip(
            [
                entity.replace("<cttype>", cttype).replace("-", "_")
                for entity in CT_NAMES_FLOAT_PHASE
            ],
            CT_NAMES_FLOAT_PHASE_TARGET,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert float(entity_state.state) == target
        count_names += 1

    CT_NAMES_STR_PHASE_TARGET = [
        phase_data.metering_status
        for phase_data in mock_envoy.data.ctmeters_phases[cttype].values()
    ]

    for name, target in list(
        zip(
            [
                entity.replace("<cttype>", cttype).replace("-", "_")
                for entity in CT_NAMES_STR_PHASE
            ],
            CT_NAMES_STR_PHASE_TARGET,
            strict=False,
        )
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert entity_state.state == target
        count_names += 1

    # verify we're testing them all
    assert len(CT_NAMES_FLOAT_PHASE) + len(CT_NAMES_STR_PHASE) == count_names


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
    ],
    indirect=["mock_envoy"],
)
async def test_sensor_all_phase_entities_disabled_by_integration(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
    entity_registry: er.EntityRegistry,
) -> None:
    """Test all phase entities are disabled by integration."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    assert all(
        f"{ENTITY_BASE}_{entity}"
        in (integration_disabled_entities(entity_registry, config_entry))
        for entity in (
            PRODUCTION_PHASE_NAMES
            + CONSUMPTION_PHASE_NAMES
            + CT_PRODUCTION_NAMES_FLOAT_PHASE
            + CT_PRODUCTION_NAMES_STR_PHASE
            + CT_CONSUMPTION_NAMES_FLOAT_PHASE
            + CT_CONSUMPTION_NAMES_STR_PHASE
        )
    )


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
    ],
    indirect=["mock_envoy"],
)
async def test_sensor_storage_phase_disabled_by_integration(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    entity_registry: er.EntityRegistry,
    mock_envoy: AsyncMock,
) -> None:
    """Test all storage CT phase entities are disabled by integration."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    assert all(
        f"{ENTITY_BASE}_{entity}"
        in integration_disabled_entities(entity_registry, config_entry)
        for entity in (CT_STORAGE_NAMES_FLOAT_PHASE + CT_STORAGE_NAMES_STR_PHASE)
    )


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy",
        "envoy_1p_metered",
        "envoy_eu_batt",
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
        "envoy_tot_cons_metered",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_inverter_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test enphase_envoy inverter entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    entity_base = f"{Platform.SENSOR}.inverter"

    for sn, inverter in mock_envoy.data.inverters.items():
        assert (entity_state := hass.states.get(f"{entity_base}_{sn}"))
        assert float(entity_state.state) == (inverter.last_report_watts)
        assert (last_reported := hass.states.get(f"{entity_base}_{sn}_last_reported"))
        assert dt_util.parse_datetime(
            last_reported.state
        ) == dt_util.utc_from_timestamp(inverter.last_report_date)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_inverter_detailed_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test enphase_envoy inverter detailed entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    entity_base = f"{Platform.SENSOR}.inverter"

    for sn, inverter in mock_envoy.data.inverters.items():
        assert (dc_voltage := hass.states.get(f"{entity_base}_{sn}_dc_voltage"))
        assert float(dc_voltage.state) == (inverter.dc_voltage)
        assert (dc_current := hass.states.get(f"{entity_base}_{sn}_dc_current"))
        assert float(dc_current.state) == (inverter.dc_current)
        assert (ac_voltage := hass.states.get(f"{entity_base}_{sn}_ac_voltage"))
        assert float(ac_voltage.state) == (inverter.ac_voltage)
        assert (ac_current := hass.states.get(f"{entity_base}_{sn}_ac_current"))
        assert float(ac_current.state) == (inverter.ac_current)
        assert (frequency := hass.states.get(f"{entity_base}_{sn}_frequency"))
        assert float(frequency.state) == (inverter.ac_frequency)
        assert (temperature := hass.states.get(f"{entity_base}_{sn}_temperature"))
        assert int(temperature.state) == (inverter.temperature)
        assert (
            lifetime_energy := hass.states.get(
                f"{entity_base}_{sn}_lifetime_energy_production"
            )
        )
        assert float(lifetime_energy.state) == (inverter.lifetime_energy / 1000.0)
        assert (
            energy_produced_today := hass.states.get(
                f"{entity_base}_{sn}_energy_production_today"
            )
        )
        assert int(energy_produced_today.state) == (inverter.energy_today)
        assert (
            last_report_duration := hass.states.get(
                f"{entity_base}_{sn}_last_report_duration"
            )
        )
        assert int(last_report_duration.state) == (inverter.last_report_duration)
        assert (
            energy_produced := hass.states.get(
                f"{entity_base}_{sn}_energy_production_since_previous_report"
            )
        )
        assert float(energy_produced.state) == (inverter.energy_produced)
        assert (
            lifetime_maximum_power := hass.states.get(
                f"{entity_base}_{sn}_lifetime_maximum_power"
            )
        )
        assert int(lifetime_maximum_power.state) == (inverter.max_report_watts)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy",
        "envoy_1p_metered",
        "envoy_eu_batt",
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
        "envoy_tot_cons_metered",
    ],
    indirect=["mock_envoy"],
)
async def test_sensor_inverter_disabled_by_integration(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
    entity_registry: er.EntityRegistry,
) -> None:
    """Test enphase_envoy inverter disabled by integration entities."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    INVERTER_BASE = f"{Platform.SENSOR}.inverter"

    assert all(
        f"{INVERTER_BASE}_{sn}_{key}"
        in integration_disabled_entities(entity_registry, config_entry)
        for sn in mock_envoy.data.inverters
        for key in (
            "dc_voltage",
            "dc_current",
            "ac_voltage",
            "ac_current",
            "frequency",
            "temperature",
            "lifetime_energy_production",
            "energy_production_today",
            "last_report_duration",
            "energy_production_since_previous_report",
            "last_reported",
            "lifetime_maximum_power",
        )
    )


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_acb_batt",
    ],
    indirect=["mock_envoy"],
)
async def test_sensor_encharge_aggregate_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test enphase_envoy encharge aggregate entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE = f"{Platform.SENSOR}.envoy_{sn}"

    data = mock_envoy.data.encharge_aggregate

    for target in (
        ("battery", data.state_of_charge, SensorStateClass.MEASUREMENT),
        (
            "reserve_battery_level",
            data.reserve_state_of_charge,
            SensorStateClass.MEASUREMENT,
        ),
        (
            "available_battery_energy",
            data.available_energy,
            SensorStateClass.MEASUREMENT,
        ),
        ("reserve_battery_energy", data.backup_reserve, SensorStateClass.MEASUREMENT),
        ("battery_capacity", data.max_available_capacity, None),
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{target[0]}"))
        assert float(entity_state.state) == target[1]
        assert entity_state.attributes.get("state_class") == target[2]


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
    ],
    indirect=["mock_envoy"],
)
async def test_sensor_encharge_enpower_data(
    hass: HomeAssistant,
    mock_envoy: AsyncMock,
    config_entry: MockConfigEntry,
) -> None:
    """Test enphase_envoy encharge enpower entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.data.enpower.serial_number
    ENTITY_BASE = f"{Platform.SENSOR}.enpower"

    assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{sn}_temperature"))
    assert (
        round(
            TemperatureConverter.convert(
                float(entity_state.state),
                hass.config.units.temperature_unit,
                UnitOfTemperature.FAHRENHEIT
                if mock_envoy.data.enpower.temperature_unit == "F"
                else UnitOfTemperature.CELSIUS,
            )
        )
        == mock_envoy.data.enpower.temperature
    )
    assert entity_state.attributes["state_class"] == SensorStateClass.MEASUREMENT
    assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{sn}_last_reported"))
    assert dt_util.parse_datetime(entity_state.state) == dt_util.utc_from_timestamp(
        mock_envoy.data.enpower.last_report_date
    )
    assert entity_state.attributes.get("state_class") is None


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
        "envoy_acb_batt",
    ],
    indirect=["mock_envoy"],
)
async def test_sensor_encharge_power_data(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    mock_envoy: AsyncMock,
) -> None:
    """Test enphase_envoy encharge_power entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    ENTITY_BASE = f"{Platform.SENSOR}.encharge"

    ENCHARGE_POWER_NAMES = (
        "battery",
        "apparent_power",
        "power",
    )

    ENCHARGE_POWER_TARGETS = [
        (
            sn,
            (
                encharge_power.soc,
                encharge_power.apparent_power_mva / 1000.0,
                encharge_power.real_power_mw / 1000.0,
            ),
        )
        for sn, encharge_power in mock_envoy.data.encharge_power.items()
    ]

    for sn, sn_target in ENCHARGE_POWER_TARGETS:
        for name, target in list(zip(ENCHARGE_POWER_NAMES, sn_target, strict=False)):
            assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{sn}_{name}"))
            assert float(entity_state.state) == target
            assert (
                entity_state.attributes["state_class"] == SensorStateClass.MEASUREMENT
            )

    for sn, encharge_inventory in mock_envoy.data.encharge_inventory.items():
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{sn}_temperature"))
        assert (
            round(
                TemperatureConverter.convert(
                    float(entity_state.state),
                    hass.config.units.temperature_unit,
                    UnitOfTemperature.FAHRENHEIT
                    if encharge_inventory.temperature_unit == "F"
                    else UnitOfTemperature.CELSIUS,
                )
            )
            == encharge_inventory.temperature
        )
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{sn}_last_reported"))
        assert dt_util.parse_datetime(entity_state.state) == dt_util.utc_from_timestamp(
            encharge_inventory.last_report_date
        )
        assert entity_state.attributes.get("state_class") is None


ACB_POWER_INT_NAMES: tuple[str, ...] = (
    "power",
    "battery",
)
ACB_POWER_STR_NAMES: tuple[str, ...] = ("battery_state",)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_acb_batt",
    ],
    indirect=["mock_envoy"],
)
async def test_sensor_acb_power_data(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    mock_envoy: AsyncMock,
) -> None:
    """Test enphase_envoy acb battery power entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.acb_{sn}"

    data = mock_envoy.data.acb_power
    ACB_POWER_INT_TARGETS: tuple[int, ...] = (
        data.power,
        data.state_of_charge,
    )
    ACB_POWER_STR_TARGETS: tuple[int, ...] = (data.state,)

    for name, target in list(
        zip(ACB_POWER_INT_NAMES, ACB_POWER_INT_TARGETS, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert int(entity_state.state) == target
        assert entity_state.attributes["state_class"] == SensorStateClass.MEASUREMENT

    for name, target in list(
        zip(ACB_POWER_STR_NAMES, ACB_POWER_STR_TARGETS, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert entity_state.state == target
        assert entity_state.attributes.get("state_class") is None


AGGREGATED_BATTERY_NAMES: tuple[str, ...] = (
    "aggregated_battery_soc",
    "aggregated_available_battery_energy",
    "aggregated_battery_capacity",
)
AGGREGATED_ACB_BATTERY_NAMES: tuple[str, ...] = ("available_acb_battery_energy",)


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_acb_batt",
    ],
    indirect=["mock_envoy"],
)
async def test_sensor_aggegated_battery_data(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    mock_envoy: AsyncMock,
) -> None:
    """Test enphase_envoy aggregated batteries entities values."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    sn = mock_envoy.serial_number
    ENTITY_BASE: str = f"{Platform.SENSOR}.envoy_{sn}"

    data = mock_envoy.data.battery_aggregate
    AGGREGATED_TARGETS: tuple[tuple[Any, ...], ...] = (
        (data.state_of_charge, SensorStateClass.MEASUREMENT),
        (data.available_energy, SensorStateClass.MEASUREMENT),
        (data.max_available_capacity, None),
    )

    for name, target in list(
        zip(AGGREGATED_BATTERY_NAMES, AGGREGATED_TARGETS, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert int(entity_state.state) == target[0]
        assert entity_state.attributes.get("state_class") == target[1]

    data = mock_envoy.data.acb_power
    AGGREGATED_ACB_TARGETS: tuple[int, ...] = (data.charge_wh,)
    for name, target in list(
        zip(AGGREGATED_ACB_BATTERY_NAMES, AGGREGATED_ACB_TARGETS, strict=False)
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{name}"))
        assert int(entity_state.state) == target
        assert (
            entity_state.attributes.get("state_class") == SensorStateClass.MEASUREMENT
        )


def integration_disabled_entities(
    entity_registry: er.EntityRegistry, config_entry: MockConfigEntry
) -> list[str]:
    """Return list of entity ids marked as disabled by integration."""
    return [
        entity_entry.entity_id
        for entity_entry in er.async_entries_for_config_entry(
            entity_registry, config_entry.entry_id
        )
        if entity_entry.disabled_by == er.RegistryEntryDisabler.INTEGRATION
    ]


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_missing_data(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    mock_envoy: AsyncMock,
    entity_registry: er.EntityRegistry,
    freezer: FrozenDateTimeFactory,
) -> None:
    """Test enphase_envoy sensor platform missing data handling."""
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    ENTITY_BASE = f"{Platform.SENSOR}.envoy_{mock_envoy.serial_number}"

    # force missing data to test 'if == none' code sections
    mock_envoy.data.system_production_phases["L2"] = None
    mock_envoy.data.system_consumption_phases["L2"] = None
    mock_envoy.data.system_net_consumption_phases["L2"] = None
    mock_envoy.data.ctmeter_production = None
    mock_envoy.data.ctmeter_consumption = None
    mock_envoy.data.ctmeter_storage = None
    mock_envoy.data.ctmeter_production_phases = None
    mock_envoy.data.ctmeter_consumption_phases = None
    mock_envoy.data.ctmeter_storage_phases = None
    del mock_envoy.data.ctmeters[CtType.NET_CONSUMPTION]
    del mock_envoy.data.ctmeters_phases[CtType.NET_CONSUMPTION][PhaseNames.PHASE_2]
    del mock_envoy.data.ctmeters[CtType.PRODUCTION]
    del mock_envoy.data.ctmeters_phases[CtType.PRODUCTION][PhaseNames.PHASE_2]
    del mock_envoy.data.ctmeters[CtType.STORAGE]
    del mock_envoy.data.ctmeters_phases[CtType.STORAGE][PhaseNames.PHASE_2]

    # use different inverter serial to test 'expected inverter missing' code
    mock_envoy.data.inverters["2"] = mock_envoy.data.inverters.pop("1")

    # force HA to detect changed data by changing raw
    mock_envoy.data.raw = {"I": "am changed"}

    # MOve time to next update
    freezer.tick(SCAN_INTERVAL)
    async_fire_time_changed(hass)
    await hass.async_block_till_done(wait_background_tasks=True)

    # all these should now be in unknown state
    for entity in (
        "lifetime_energy_production_l2",
        "lifetime_energy_consumption_l2",
        "metering_status_production_ct",
        "metering_status_net_consumption_ct",
        "metering_status_storage_ct",
        "metering_status_production_ct_l2",
        "metering_status_net_consumption_ct_l2",
        "metering_status_storage_ct_l2",
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{entity}"))
        assert entity_state.state == STATE_UNKNOWN

    # test the original inverter is now unknown
    assert (entity_state := hass.states.get("sensor.inverter_1"))
    assert entity_state.state == STATE_UNKNOWN

    del mock_envoy.data.ctmeters_phases[CtType.PRODUCTION]
    del mock_envoy.data.ctmeters_phases[CtType.STORAGE]
    # force HA to detect changed data by changing raw
    mock_envoy.data.raw = {"I": "am changed again"}

    # Move time to next update
    freezer.tick(SCAN_INTERVAL)
    async_fire_time_changed(hass)
    await hass.async_block_till_done(wait_background_tasks=True)

    for entity in (
        "metering_status_production_ct",
        "metering_status_production_ct_l1",
        "metering_status_storage_ct",
        "metering_status_storage_ct_l1",
    ):
        assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{entity}"))
        assert entity_state.state == STATE_UNKNOWN


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy_metered_batt_relay",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_fw_update(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    mock_envoy: AsyncMock,
    entity_registry: er.EntityRegistry,
    freezer: FrozenDateTimeFactory,
    caplog: pytest.LogCaptureFixture,
) -> None:
    """Test enphase_envoy sensor update over fw update."""
    logging.getLogger("homeassistant.components.enphase_envoy").setLevel(logging.DEBUG)
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    # force HA to detect changed data by changing raw
    mock_envoy.firmware = "0.0.0"

    # Move time to next update
    freezer.tick(SCAN_INTERVAL)
    async_fire_time_changed(hass)
    await hass.async_block_till_done(wait_background_tasks=True)

    assert "firmware changed from: " in caplog.text
    assert "to: 0.0.0, reloading enphase envoy integration" in caplog.text


@pytest.mark.parametrize(
    ("mock_envoy"),
    [
        "envoy",
        "envoy_1p_metered",
        "envoy_eu_batt",
        "envoy_metered_batt_relay",
        "envoy_nobatt_metered_3p",
        "envoy_tot_cons_metered",
        "envoy_acb_batt",
    ],
    indirect=["mock_envoy"],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_no_state_class_warnings(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    mock_envoy: AsyncMock,
    entity_registry: er.EntityRegistry,
    caplog: pytest.LogCaptureFixture,
) -> None:
    """Test sensor creation has no deviceclass/state_class warnings."""
    logging.getLogger("homeassistant.components.enphase_envoy").setLevel(logging.DEBUG)
    with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
        await setup_integration(hass, config_entry)

    # Verify no sensor device class / state class mismatch
    # warning is reported
    #
    # assert "which is impossible considering" not in caplog.text
    assert "create a bug report at" not in caplog.text
