"""Support for Enphase Envoy solar energy monitor."""

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass, replace
import datetime
import logging
from operator import attrgetter
from typing import TYPE_CHECKING

from pyenphase import (
    EnvoyACBPower,
    EnvoyBatteryAggregate,
    EnvoyC6CC,
    EnvoyCollar,
    EnvoyEncharge,
    EnvoyEnchargeAggregate,
    EnvoyEnchargePower,
    EnvoyEnpower,
    EnvoyInverter,
    EnvoySystemConsumption,
    EnvoySystemProduction,
)
from pyenphase.const import PHASENAMES
from pyenphase.models.meters import (
    CtMeterStatus,
    CtState,
    CtStatusFlags,
    CtType,
    EnvoyMeterData,
)

from homeassistant.components.sensor import (
    SensorDeviceClass,
    SensorEntity,
    SensorEntityDescription,
    SensorStateClass,
)
from homeassistant.const import (
    PERCENTAGE,
    EntityCategory,
    UnitOfApparentPower,
    UnitOfElectricCurrent,
    UnitOfElectricPotential,
    UnitOfEnergy,
    UnitOfFrequency,
    UnitOfPower,
    UnitOfTemperature,
    UnitOfTime,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import dt as dt_util

from .const import DOMAIN
from .coordinator import EnphaseConfigEntry, EnphaseUpdateCoordinator
from .entity import EnvoyBaseEntity

_LOGGER = logging.getLogger(__name__)

INVERTERS_KEY = "inverters"
LAST_REPORTED_KEY = "last_reported"

PARALLEL_UPDATES = 0


@dataclass(frozen=True, kw_only=True)
class EnvoyInverterSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy inverter sensor entity."""

    value_fn: Callable[[EnvoyInverter], datetime.datetime | float]


INVERTER_SENSORS = (
    EnvoyInverterSensorEntityDescription(
        key=INVERTERS_KEY,
        name=None,
        native_unit_of_measurement=UnitOfPower.WATT,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.POWER,
        value_fn=attrgetter("last_report_watts"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="dc_voltage",
        translation_key="dc_voltage",
        native_unit_of_measurement=UnitOfElectricPotential.VOLT,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.VOLTAGE,
        suggested_display_precision=3,
        entity_registry_enabled_default=False,
        value_fn=attrgetter("dc_voltage"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="dc_current",
        translation_key="dc_current",
        native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.CURRENT,
        suggested_display_precision=3,
        entity_registry_enabled_default=False,
        value_fn=attrgetter("dc_current"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="ac_voltage",
        translation_key="ac_voltage",
        native_unit_of_measurement=UnitOfElectricPotential.VOLT,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.VOLTAGE,
        suggested_display_precision=3,
        entity_registry_enabled_default=False,
        value_fn=attrgetter("ac_voltage"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="ac_current",
        translation_key="ac_current",
        native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.CURRENT,
        suggested_display_precision=3,
        entity_registry_enabled_default=False,
        value_fn=attrgetter("ac_current"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="ac_frequency",
        native_unit_of_measurement=UnitOfFrequency.HERTZ,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.FREQUENCY,
        suggested_display_precision=3,
        entity_registry_enabled_default=False,
        value_fn=attrgetter("ac_frequency"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="temperature",
        native_unit_of_measurement=UnitOfTemperature.CELSIUS,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.TEMPERATURE,
        suggested_display_precision=3,
        entity_registry_enabled_default=False,
        entity_category=EntityCategory.DIAGNOSTIC,
        value_fn=attrgetter("temperature"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="lifetime_energy",
        translation_key="lifetime_energy",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
        state_class=SensorStateClass.TOTAL_INCREASING,
        device_class=SensorDeviceClass.ENERGY,
        entity_registry_enabled_default=False,
        value_fn=attrgetter("lifetime_energy"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="energy_today",
        translation_key="energy_today",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        state_class=SensorStateClass.TOTAL_INCREASING,
        device_class=SensorDeviceClass.ENERGY,
        entity_registry_enabled_default=False,
        value_fn=attrgetter("energy_today"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="last_report_duration",
        translation_key="last_report_duration",
        native_unit_of_measurement=UnitOfTime.SECONDS,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.DURATION,
        entity_registry_enabled_default=False,
        entity_category=EntityCategory.DIAGNOSTIC,
        value_fn=attrgetter("last_report_duration"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="energy_produced",
        translation_key="energy_produced",
        native_unit_of_measurement=UnitOfEnergy.MILLIWATT_HOUR,
        state_class=SensorStateClass.TOTAL,
        device_class=SensorDeviceClass.ENERGY,
        suggested_display_precision=3,
        entity_registry_enabled_default=False,
        value_fn=attrgetter("energy_produced"),
    ),
    EnvoyInverterSensorEntityDescription(
        key="max_reported",
        translation_key="max_reported",
        native_unit_of_measurement=UnitOfPower.WATT,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.POWER,
        entity_registry_enabled_default=False,
        entity_category=EntityCategory.DIAGNOSTIC,
        value_fn=attrgetter("max_report_watts"),
    ),
    EnvoyInverterSensorEntityDescription(
        key=LAST_REPORTED_KEY,
        translation_key=LAST_REPORTED_KEY,
        device_class=SensorDeviceClass.TIMESTAMP,
        entity_registry_enabled_default=False,
        value_fn=lambda inverter: dt_util.utc_from_timestamp(inverter.last_report_date),
    ),
)


@dataclass(frozen=True, kw_only=True)
class EnvoyProductionSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy production sensor entity."""

    value_fn: Callable[[EnvoySystemProduction], int]
    on_phase: str | None


PRODUCTION_SENSORS = (
    EnvoyProductionSensorEntityDescription(
        key="production",
        translation_key="current_power_production",
        native_unit_of_measurement=UnitOfPower.WATT,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.POWER,
        suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
        suggested_display_precision=3,
        value_fn=attrgetter("watts_now"),
        on_phase=None,
    ),
    EnvoyProductionSensorEntityDescription(
        key="daily_production",
        translation_key="daily_production",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        state_class=SensorStateClass.TOTAL_INCREASING,
        device_class=SensorDeviceClass.ENERGY,
        suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
        suggested_display_precision=2,
        value_fn=attrgetter("watt_hours_today"),
        on_phase=None,
    ),
    EnvoyProductionSensorEntityDescription(
        key="seven_days_production",
        translation_key="seven_days_production",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        device_class=SensorDeviceClass.ENERGY,
        suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
        suggested_display_precision=1,
        value_fn=attrgetter("watt_hours_last_7_days"),
        on_phase=None,
    ),
    EnvoyProductionSensorEntityDescription(
        key="lifetime_production",
        translation_key="lifetime_production",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        state_class=SensorStateClass.TOTAL_INCREASING,
        device_class=SensorDeviceClass.ENERGY,
        suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR,
        suggested_display_precision=3,
        value_fn=attrgetter("watt_hours_lifetime"),
        on_phase=None,
    ),
)


PRODUCTION_PHASE_SENSORS = {
    (on_phase := PHASENAMES[phase]): [
        replace(
            sensor,
            key=f"{sensor.key}_l{phase + 1}",
            translation_key=f"{sensor.translation_key}_phase",
            entity_registry_enabled_default=False,
            on_phase=on_phase,
            translation_placeholders={"phase_name": f"l{phase + 1}"},
        )
        for sensor in list(PRODUCTION_SENSORS)
    ]
    for phase in range(3)
}


@dataclass(frozen=True, kw_only=True)
class EnvoyConsumptionSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy consumption sensor entity."""

    value_fn: Callable[[EnvoySystemConsumption], int]
    on_phase: str | None


CONSUMPTION_SENSORS = (
    EnvoyConsumptionSensorEntityDescription(
        key="consumption",
        translation_key="current_power_consumption",
        native_unit_of_measurement=UnitOfPower.WATT,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.POWER,
        suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
        suggested_display_precision=3,
        value_fn=attrgetter("watts_now"),
        on_phase=None,
    ),
    EnvoyConsumptionSensorEntityDescription(
        key="daily_consumption",
        translation_key="daily_consumption",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        state_class=SensorStateClass.TOTAL_INCREASING,
        device_class=SensorDeviceClass.ENERGY,
        suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
        suggested_display_precision=2,
        value_fn=attrgetter("watt_hours_today"),
        on_phase=None,
    ),
    EnvoyConsumptionSensorEntityDescription(
        key="seven_days_consumption",
        translation_key="seven_days_consumption",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        device_class=SensorDeviceClass.ENERGY,
        suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
        suggested_display_precision=1,
        value_fn=attrgetter("watt_hours_last_7_days"),
        on_phase=None,
    ),
    EnvoyConsumptionSensorEntityDescription(
        key="lifetime_consumption",
        translation_key="lifetime_consumption",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        state_class=SensorStateClass.TOTAL_INCREASING,
        device_class=SensorDeviceClass.ENERGY,
        suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR,
        suggested_display_precision=3,
        value_fn=attrgetter("watt_hours_lifetime"),
        on_phase=None,
    ),
)


CONSUMPTION_PHASE_SENSORS = {
    (on_phase := PHASENAMES[phase]): [
        replace(
            sensor,
            key=f"{sensor.key}_l{phase + 1}",
            translation_key=f"{sensor.translation_key}_phase",
            entity_registry_enabled_default=False,
            on_phase=on_phase,
            translation_placeholders={"phase_name": f"l{phase + 1}"},
        )
        for sensor in list(CONSUMPTION_SENSORS)
    ]
    for phase in range(3)
}


NET_CONSUMPTION_SENSORS = (
    EnvoyConsumptionSensorEntityDescription(
        key="balanced_net_consumption",
        translation_key="balanced_net_consumption",
        entity_registry_enabled_default=False,
        native_unit_of_measurement=UnitOfPower.WATT,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.POWER,
        suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
        suggested_display_precision=3,
        value_fn=attrgetter("watts_now"),
        on_phase=None,
    ),
    EnvoyConsumptionSensorEntityDescription(
        key="lifetime_balanced_net_consumption",
        translation_key="lifetime_balanced_net_consumption",
        entity_registry_enabled_default=False,
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        state_class=SensorStateClass.TOTAL,
        device_class=SensorDeviceClass.ENERGY,
        suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
        suggested_display_precision=3,
        value_fn=attrgetter("watt_hours_lifetime"),
        on_phase=None,
    ),
)


NET_CONSUMPTION_PHASE_SENSORS = {
    (on_phase := PHASENAMES[phase]): [
        replace(
            sensor,
            key=f"{sensor.key}_l{phase + 1}",
            translation_key=f"{sensor.translation_key}_phase",
            entity_registry_enabled_default=False,
            on_phase=on_phase,
            translation_placeholders={"phase_name": f"l{phase + 1}"},
        )
        for sensor in list(NET_CONSUMPTION_SENSORS)
    ]
    for phase in range(3)
}


@dataclass(frozen=True, kw_only=True)
class EnvoyCTSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy CT sensor entity."""

    value_fn: Callable[
        [EnvoyMeterData],
        int | float | str | CtType | CtMeterStatus | CtStatusFlags | CtState | None,
    ]
    on_phase: str | None
    cttype: str | None = None


# All ct types unified in common setup
CT_SENSORS = (
    [
        EnvoyCTSensorEntityDescription(
            key=key,
            translation_key=key,
            native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
            state_class=SensorStateClass.TOTAL_INCREASING,
            device_class=SensorDeviceClass.ENERGY,
            suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR,
            suggested_display_precision=3,
            value_fn=attrgetter("energy_delivered"),
            on_phase=None,
            cttype=cttype,
        )
        for cttype, key in (
            (CtType.NET_CONSUMPTION, "lifetime_net_consumption"),
            # Production CT energy_delivered is not used
            (CtType.STORAGE, "lifetime_battery_discharged"),
        )
    ]
    + [
        EnvoyCTSensorEntityDescription(
            key=key,
            translation_key=key,
            native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
            state_class=SensorStateClass.TOTAL_INCREASING,
            device_class=SensorDeviceClass.ENERGY,
            suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR,
            suggested_display_precision=3,
            value_fn=attrgetter("energy_received"),
            on_phase=None,
            cttype=cttype,
        )
        for cttype, key in (
            (CtType.NET_CONSUMPTION, "lifetime_net_production"),
            # Production CT energy_received is not used
            (CtType.STORAGE, "lifetime_battery_charged"),
        )
    ]
    + [
        EnvoyCTSensorEntityDescription(
            key=key,
            translation_key=key,
            native_unit_of_measurement=UnitOfPower.WATT,
            state_class=SensorStateClass.MEASUREMENT,
            device_class=SensorDeviceClass.POWER,
            suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
            suggested_display_precision=3,
            value_fn=attrgetter("active_power"),
            on_phase=None,
            cttype=cttype,
        )
        for cttype, key in (
            (CtType.NET_CONSUMPTION, "net_consumption"),
            # Production CT active_power is not used
            (CtType.STORAGE, "battery_discharge"),
        )
    ]
    + [
        EnvoyCTSensorEntityDescription(
            key=key,
            translation_key=(translation_key if translation_key != "" else key),
            native_unit_of_measurement=UnitOfFrequency.HERTZ,
            state_class=SensorStateClass.MEASUREMENT,
            device_class=SensorDeviceClass.FREQUENCY,
            suggested_display_precision=1,
            entity_registry_enabled_default=False,
            value_fn=attrgetter("frequency"),
            on_phase=None,
            cttype=cttype,
        )
        for cttype, key, translation_key in (
            (CtType.NET_CONSUMPTION, "frequency", "net_ct_frequency"),
            (CtType.PRODUCTION, "production_ct_frequency", ""),
            (CtType.STORAGE, "storage_ct_frequency", ""),
        )
    ]
    + [
        EnvoyCTSensorEntityDescription(
            key=key,
            translation_key=(translation_key if translation_key != "" else key),
            native_unit_of_measurement=UnitOfElectricPotential.VOLT,
            state_class=SensorStateClass.MEASUREMENT,
            device_class=SensorDeviceClass.VOLTAGE,
            suggested_unit_of_measurement=UnitOfElectricPotential.VOLT,
            suggested_display_precision=1,
            entity_registry_enabled_default=False,
            value_fn=attrgetter("voltage"),
            on_phase=None,
            cttype=cttype,
        )
        for cttype, key, translation_key in (
            (CtType.NET_CONSUMPTION, "voltage", "net_ct_voltage"),
            (CtType.PRODUCTION, "production_ct_voltage", ""),
            (CtType.STORAGE, "storage_voltage", "storage_ct_voltage"),
        )
    ]
    + [
        EnvoyCTSensorEntityDescription(
            key=key,
            translation_key=key,
            native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
            state_class=SensorStateClass.MEASUREMENT,
            device_class=SensorDeviceClass.CURRENT,
            suggested_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
            suggested_display_precision=3,
            entity_registry_enabled_default=False,
            value_fn=attrgetter("current"),
            on_phase=None,
            cttype=cttype,
        )
        for cttype, key in (
            (CtType.NET_CONSUMPTION, "net_ct_current"),
            (CtType.PRODUCTION, "production_ct_current"),
            (CtType.STORAGE, "storage_ct_current"),
        )
    ]
    + [
        EnvoyCTSensorEntityDescription(
            key=key,
            translation_key=key,
            device_class=SensorDeviceClass.POWER_FACTOR,
            state_class=SensorStateClass.MEASUREMENT,
            suggested_display_precision=2,
            entity_registry_enabled_default=False,
            value_fn=attrgetter("power_factor"),
            on_phase=None,
            cttype=cttype,
        )
        for cttype, key in (
            (CtType.NET_CONSUMPTION, "net_ct_powerfactor"),
            (CtType.PRODUCTION, "production_ct_powerfactor"),
            (CtType.STORAGE, "storage_ct_powerfactor"),
        )
    ]
    + [
        EnvoyCTSensorEntityDescription(
            key=key,
            translation_key=(translation_key if translation_key != "" else key),
            device_class=SensorDeviceClass.ENUM,
            entity_category=EntityCategory.DIAGNOSTIC,
            options=list(CtMeterStatus),
            entity_registry_enabled_default=False,
            value_fn=attrgetter("metering_status"),
            on_phase=None,
            cttype=cttype,
        )
        for cttype, key, translation_key in (
            (
                CtType.NET_CONSUMPTION,
                "net_consumption_ct_metering_status",
                "net_ct_metering_status",
            ),
            (CtType.PRODUCTION, "production_ct_metering_status", ""),
            (CtType.STORAGE, "storage_ct_metering_status", ""),
        )
    ]
    + [
        EnvoyCTSensorEntityDescription(
            key=key,
            translation_key=(translation_key if translation_key != "" else key),
            state_class=None,
            entity_category=EntityCategory.DIAGNOSTIC,
            entity_registry_enabled_default=False,
            value_fn=lambda ct: 0 if ct.status_flags is None else len(ct.status_flags),
            on_phase=None,
            cttype=cttype,
        )
        for cttype, key, translation_key in (
            (
                CtType.NET_CONSUMPTION,
                "net_consumption_ct_status_flags",
                "net_ct_status_flags",
            ),
            (CtType.PRODUCTION, "production_ct_status_flags", ""),
            (CtType.STORAGE, "storage_ct_status_flags", ""),
        )
    ]
)


CT_PHASE_SENSORS = {
    (on_phase := PHASENAMES[phase]): [
        replace(
            sensor,
            key=f"{sensor.key}_l{phase + 1}",
            translation_key=f"{sensor.translation_key}_phase",
            entity_registry_enabled_default=False,
            on_phase=on_phase,
            translation_placeholders={"phase_name": f"l{phase + 1}"},
        )
        for sensor in list(CT_SENSORS)
    ]
    for phase in range(3)
}


@dataclass(frozen=True, kw_only=True)
class EnvoyEnchargeSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy Encharge sensor entity."""

    value_fn: Callable[[EnvoyEncharge], datetime.datetime | int | float]


@dataclass(frozen=True)
class EnvoyEnchargePowerRequiredKeysMixin:
    """Mixin for required keys."""


@dataclass(frozen=True, kw_only=True)
class EnvoyEnchargePowerSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy Encharge sensor entity."""

    value_fn: Callable[[EnvoyEnchargePower], int | float]


ENCHARGE_INVENTORY_SENSORS = (
    EnvoyEnchargeSensorEntityDescription(
        key="temperature",
        native_unit_of_measurement=UnitOfTemperature.CELSIUS,
        device_class=SensorDeviceClass.TEMPERATURE,
        value_fn=attrgetter("temperature"),
    ),
    EnvoyEnchargeSensorEntityDescription(
        key=LAST_REPORTED_KEY,
        translation_key=LAST_REPORTED_KEY,
        native_unit_of_measurement=None,
        device_class=SensorDeviceClass.TIMESTAMP,
        value_fn=lambda encharge: dt_util.utc_from_timestamp(encharge.last_report_date),
    ),
)
ENCHARGE_POWER_SENSORS = (
    EnvoyEnchargePowerSensorEntityDescription(
        key="soc",
        native_unit_of_measurement=PERCENTAGE,
        device_class=SensorDeviceClass.BATTERY,
        value_fn=attrgetter("soc"),
    ),
    EnvoyEnchargePowerSensorEntityDescription(
        key="apparent_power_mva",
        native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
        device_class=SensorDeviceClass.APPARENT_POWER,
        value_fn=lambda encharge: encharge.apparent_power_mva * 0.001,
    ),
    EnvoyEnchargePowerSensorEntityDescription(
        key="real_power_mw",
        native_unit_of_measurement=UnitOfPower.WATT,
        device_class=SensorDeviceClass.POWER,
        value_fn=lambda encharge: encharge.real_power_mw * 0.001,
    ),
)


@dataclass(frozen=True, kw_only=True)
class EnvoyEnpowerSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy Encharge sensor entity."""

    value_fn: Callable[[EnvoyEnpower], datetime.datetime | int | float]


ENPOWER_SENSORS = (
    EnvoyEnpowerSensorEntityDescription(
        key="temperature",
        native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
        device_class=SensorDeviceClass.TEMPERATURE,
        value_fn=attrgetter("temperature"),
    ),
    EnvoyEnpowerSensorEntityDescription(
        key=LAST_REPORTED_KEY,
        translation_key=LAST_REPORTED_KEY,
        device_class=SensorDeviceClass.TIMESTAMP,
        value_fn=lambda enpower: dt_util.utc_from_timestamp(enpower.last_report_date),
    ),
)


@dataclass(frozen=True, kw_only=True)
class EnvoyCollarSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy Collar sensor entity."""

    value_fn: Callable[[EnvoyCollar], datetime.datetime | int | float | str]


# translations don't accept uppercase
ADMIN_STATE_MAP = {
    "ENCMN_MDE_ON_GRID": "on_grid",
    "ENCMN_MDE_OFF_GRID": "off_grid",
}

COLLAR_SENSORS = (
    EnvoyCollarSensorEntityDescription(
        key="temperature",
        native_unit_of_measurement=UnitOfTemperature.CELSIUS,
        device_class=SensorDeviceClass.TEMPERATURE,
        value_fn=attrgetter("temperature"),
    ),
    EnvoyCollarSensorEntityDescription(
        key=LAST_REPORTED_KEY,
        translation_key=LAST_REPORTED_KEY,
        native_unit_of_measurement=None,
        device_class=SensorDeviceClass.TIMESTAMP,
        value_fn=lambda collar: dt_util.utc_from_timestamp(collar.last_report_date),
    ),
    # grid_state does not seem to change when off-grid, but rather admin_state_str
    EnvoyCollarSensorEntityDescription(
        key="grid_state",
        translation_key="grid_status",
        value_fn=lambda collar: collar.grid_state,
    ),
    # grid_status off-grid shows in admin_state rather than in grid_state
    # map values as translations don't accept uppercase which these are
    EnvoyCollarSensorEntityDescription(
        key="admin_state_str",
        translation_key="admin_state",
        value_fn=lambda collar: ADMIN_STATE_MAP.get(
            collar.admin_state_str, collar.admin_state_str
        ),
    ),
    EnvoyCollarSensorEntityDescription(
        key="mid_state",
        translation_key="mid_state",
        value_fn=lambda collar: collar.mid_state,
    ),
)


@dataclass(frozen=True, kw_only=True)
class EnvoyC6CCSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy C6 Combiner controller sensor entity."""

    value_fn: Callable[[EnvoyC6CC], datetime.datetime]


C6CC_SENSORS = (
    EnvoyC6CCSensorEntityDescription(
        key=LAST_REPORTED_KEY,
        translation_key=LAST_REPORTED_KEY,
        native_unit_of_measurement=None,
        device_class=SensorDeviceClass.TIMESTAMP,
        value_fn=lambda c6cc: dt_util.utc_from_timestamp(c6cc.last_report_date),
    ),
)


@dataclass(frozen=True)
class EnvoyEnchargeAggregateRequiredKeysMixin:
    """Mixin for required keys."""


@dataclass(frozen=True, kw_only=True)
class EnvoyEnchargeAggregateSensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy Encharge sensor entity."""

    value_fn: Callable[[EnvoyEnchargeAggregate], int]


ENCHARGE_AGGREGATE_SENSORS = (
    EnvoyEnchargeAggregateSensorEntityDescription(
        key="battery_level",
        native_unit_of_measurement=PERCENTAGE,
        device_class=SensorDeviceClass.BATTERY,
        value_fn=attrgetter("state_of_charge"),
    ),
    EnvoyEnchargeAggregateSensorEntityDescription(
        key="reserve_soc",
        translation_key="reserve_soc",
        native_unit_of_measurement=PERCENTAGE,
        device_class=SensorDeviceClass.BATTERY,
        value_fn=attrgetter("reserve_state_of_charge"),
    ),
    EnvoyEnchargeAggregateSensorEntityDescription(
        key="available_energy",
        translation_key="available_energy",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        device_class=SensorDeviceClass.ENERGY,
        value_fn=attrgetter("available_energy"),
    ),
    EnvoyEnchargeAggregateSensorEntityDescription(
        key="reserve_energy",
        translation_key="reserve_energy",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        device_class=SensorDeviceClass.ENERGY,
        value_fn=attrgetter("backup_reserve"),
    ),
    EnvoyEnchargeAggregateSensorEntityDescription(
        key="max_capacity",
        translation_key="max_capacity",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        device_class=SensorDeviceClass.ENERGY,
        value_fn=attrgetter("max_available_capacity"),
    ),
)


@dataclass(frozen=True, kw_only=True)
class EnvoyAcbBatterySensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy ACB Battery sensor entity."""

    value_fn: Callable[[EnvoyACBPower], int | str]


ACB_BATTERY_POWER_SENSORS = (
    EnvoyAcbBatterySensorEntityDescription(
        key="acb_power",
        native_unit_of_measurement=UnitOfPower.WATT,
        device_class=SensorDeviceClass.POWER,
        value_fn=attrgetter("power"),
    ),
    EnvoyAcbBatterySensorEntityDescription(
        key="acb_soc",
        native_unit_of_measurement=PERCENTAGE,
        device_class=SensorDeviceClass.BATTERY,
        value_fn=attrgetter("state_of_charge"),
    ),
    EnvoyAcbBatterySensorEntityDescription(
        key="acb_battery_state",
        translation_key="acb_battery_state",
        device_class=SensorDeviceClass.ENUM,
        options=["discharging", "idle", "charging", "full"],
        value_fn=attrgetter("state"),
    ),
)

ACB_BATTERY_ENERGY_SENSORS = (
    EnvoyAcbBatterySensorEntityDescription(
        key="acb_available_energy",
        translation_key="acb_available_energy",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        device_class=SensorDeviceClass.ENERGY_STORAGE,
        value_fn=attrgetter("charge_wh"),
    ),
)


@dataclass(frozen=True, kw_only=True)
class EnvoyAggregateBatterySensorEntityDescription(SensorEntityDescription):
    """Describes an Envoy aggregate Ensemble and ACB Battery sensor entity."""

    value_fn: Callable[[EnvoyBatteryAggregate], int]


AGGREGATE_BATTERY_SENSORS = (
    EnvoyAggregateBatterySensorEntityDescription(
        key="aggregated_soc",
        translation_key="aggregated_soc",
        native_unit_of_measurement=PERCENTAGE,
        device_class=SensorDeviceClass.BATTERY,
        value_fn=attrgetter("state_of_charge"),
    ),
    EnvoyAggregateBatterySensorEntityDescription(
        key="aggregated_available_energy",
        translation_key="aggregated_available_energy",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        device_class=SensorDeviceClass.ENERGY_STORAGE,
        value_fn=attrgetter("available_energy"),
    ),
    EnvoyAggregateBatterySensorEntityDescription(
        key="aggregated_max_battery_capacity",
        translation_key="aggregated_max_capacity",
        native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
        device_class=SensorDeviceClass.ENERGY_STORAGE,
        value_fn=attrgetter("max_available_capacity"),
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: EnphaseConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up envoy sensor platform."""
    coordinator = config_entry.runtime_data
    envoy_data = coordinator.envoy.data
    assert envoy_data is not None
    _LOGGER.debug("Envoy data: %s", envoy_data)

    entities: list[Entity] = [
        EnvoyProductionEntity(coordinator, description)
        for description in PRODUCTION_SENSORS
    ]
    if envoy_data.system_consumption:
        entities.extend(
            EnvoyConsumptionEntity(coordinator, description)
            for description in CONSUMPTION_SENSORS
        )
    if envoy_data.system_net_consumption:
        entities.extend(
            EnvoyNetConsumptionEntity(coordinator, description)
            for description in NET_CONSUMPTION_SENSORS
        )
    # For each production phase reported add production entities
    if envoy_data.system_production_phases:
        entities.extend(
            EnvoyProductionPhaseEntity(coordinator, description)
            for use_phase, phase in envoy_data.system_production_phases.items()
            for description in PRODUCTION_PHASE_SENSORS[use_phase]
            if phase is not None
        )
    # For each consumption phase reported add consumption entities
    if envoy_data.system_consumption_phases:
        entities.extend(
            EnvoyConsumptionPhaseEntity(coordinator, description)
            for use_phase, phase in envoy_data.system_consumption_phases.items()
            for description in CONSUMPTION_PHASE_SENSORS[use_phase]
            if phase is not None
        )
    # For each net_consumption phase reported add consumption entities
    if envoy_data.system_net_consumption_phases:
        entities.extend(
            EnvoyNetConsumptionPhaseEntity(coordinator, description)
            for use_phase, phase in envoy_data.system_net_consumption_phases.items()
            for description in NET_CONSUMPTION_PHASE_SENSORS[use_phase]
            if phase is not None
        )
    # Add Current Transformer entities
    if envoy_data.ctmeters:
        entities.extend(
            EnvoyCTEntity(coordinator, description)
            for description in CT_SENSORS
            if description.cttype in envoy_data.ctmeters
        )
    # Add Current Transformer phase entities
    if ctmeters_phases := envoy_data.ctmeters_phases:
        entities.extend(
            EnvoyCTPhaseEntity(coordinator, description)
            for phase, descriptions in CT_PHASE_SENSORS.items()
            for description in descriptions
            if (cttype := description.cttype) in ctmeters_phases
            and phase in ctmeters_phases[cttype]
        )

    if envoy_data.inverters:
        entities.extend(
            EnvoyInverterEntity(coordinator, description, inverter)
            for description in INVERTER_SENSORS
            for inverter in envoy_data.inverters
        )

    if envoy_data.encharge_inventory:
        entities.extend(
            EnvoyEnchargeInventoryEntity(coordinator, description, encharge)
            for description in ENCHARGE_INVENTORY_SENSORS
            for encharge in envoy_data.encharge_inventory
        )
    if envoy_data.encharge_power:
        entities.extend(
            EnvoyEnchargePowerEntity(coordinator, description, encharge)
            for description in ENCHARGE_POWER_SENSORS
            for encharge in envoy_data.encharge_power
        )
    if envoy_data.encharge_aggregate:
        entities.extend(
            EnvoyEnchargeAggregateEntity(coordinator, description)
            for description in ENCHARGE_AGGREGATE_SENSORS
        )
    if envoy_data.enpower:
        entities.extend(
            EnvoyEnpowerEntity(coordinator, description)
            for description in ENPOWER_SENSORS
        )
    if envoy_data.acb_power:
        entities.extend(
            EnvoyAcbBatteryPowerEntity(coordinator, description)
            for description in ACB_BATTERY_POWER_SENSORS
        )
        entities.extend(
            EnvoyAcbBatteryEnergyEntity(coordinator, description)
            for description in ACB_BATTERY_ENERGY_SENSORS
        )
    if envoy_data.battery_aggregate:
        entities.extend(
            AggregateBatteryEntity(coordinator, description)
            for description in AGGREGATE_BATTERY_SENSORS
        )
    if envoy_data.collar:
        entities.extend(
            EnvoyCollarEntity(coordinator, description)
            for description in COLLAR_SENSORS
        )
    if envoy_data.c6cc:
        entities.extend(
            EnvoyC6CCEntity(coordinator, description) for description in C6CC_SENSORS
        )

    async_add_entities(entities)


class EnvoySensorBaseEntity(EnvoyBaseEntity, SensorEntity):
    """Defines a base envoy entity."""


class EnvoySystemSensorEntity(EnvoySensorBaseEntity):
    """Envoy system base entity."""

    def __init__(
        self,
        coordinator: EnphaseUpdateCoordinator,
        description: SensorEntityDescription,
    ) -> None:
        """Initialize Envoy entity."""
        super().__init__(coordinator, description)
        self._attr_unique_id = f"{self.envoy_serial_num}_{description.key}"
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, self.envoy_serial_num)},
            manufacturer="Enphase",
            model=coordinator.envoy.envoy_model,
            name=coordinator.name,
            sw_version=str(coordinator.envoy.firmware),
            hw_version=coordinator.envoy.part_number,
            serial_number=self.envoy_serial_num,
        )


class EnvoyProductionEntity(EnvoySystemSensorEntity):
    """Envoy production entity."""

    entity_description: EnvoyProductionSensorEntityDescription

    @property
    def native_value(self) -> int | None:
        """Return the state of the sensor."""
        system_production = self.data.system_production
        assert system_production is not None
        return self.entity_description.value_fn(system_production)


class EnvoyConsumptionEntity(EnvoySystemSensorEntity):
    """Envoy consumption entity."""

    entity_description: EnvoyConsumptionSensorEntityDescription

    @property
    def native_value(self) -> int | None:
        """Return the state of the sensor."""
        system_consumption = self.data.system_consumption
        assert system_consumption is not None
        return self.entity_description.value_fn(system_consumption)


class EnvoyNetConsumptionEntity(EnvoySystemSensorEntity):
    """Envoy consumption entity."""

    entity_description: EnvoyConsumptionSensorEntityDescription

    @property
    def native_value(self) -> int | None:
        """Return the state of the sensor."""
        system_net_consumption = self.data.system_net_consumption
        assert system_net_consumption is not None
        return self.entity_description.value_fn(system_net_consumption)


class EnvoyProductionPhaseEntity(EnvoySystemSensorEntity):
    """Envoy phase production entity."""

    entity_description: EnvoyProductionSensorEntityDescription

    @property
    def native_value(self) -> int | None:
        """Return the state of the sensor."""
        if TYPE_CHECKING:
            assert self.entity_description.on_phase
            assert self.data.system_production_phases

        if (
            system_production := self.data.system_production_phases[
                self.entity_description.on_phase
            ]
        ) is None:
            return None
        return self.entity_description.value_fn(system_production)


class EnvoyConsumptionPhaseEntity(EnvoySystemSensorEntity):
    """Envoy phase consumption entity."""

    entity_description: EnvoyConsumptionSensorEntityDescription

    @property
    def native_value(self) -> int | None:
        """Return the state of the sensor."""
        if TYPE_CHECKING:
            assert self.entity_description.on_phase
            assert self.data.system_consumption_phases

        if (
            system_consumption := self.data.system_consumption_phases[
                self.entity_description.on_phase
            ]
        ) is None:
            return None
        return self.entity_description.value_fn(system_consumption)


class EnvoyNetConsumptionPhaseEntity(EnvoySystemSensorEntity):
    """Envoy phase consumption entity."""

    entity_description: EnvoyConsumptionSensorEntityDescription

    @property
    def native_value(self) -> int | None:
        """Return the state of the sensor."""
        if TYPE_CHECKING:
            assert self.entity_description.on_phase
            assert self.data.system_net_consumption_phases

        if (
            system_net_consumption := self.data.system_net_consumption_phases[
                self.entity_description.on_phase
            ]
        ) is None:
            return None
        return self.entity_description.value_fn(system_net_consumption)


class EnvoyCTEntity(EnvoySystemSensorEntity):
    """Envoy CT entity."""

    entity_description: EnvoyCTSensorEntityDescription

    @property
    def native_value(
        self,
    ) -> int | float | str | CtType | CtMeterStatus | CtStatusFlags | None:
        """Return the state of the CT sensor."""
        if (cttype := self.entity_description.cttype) not in self.data.ctmeters:
            return None
        return self.entity_description.value_fn(self.data.ctmeters[cttype])


class EnvoyCTPhaseEntity(EnvoySystemSensorEntity):
    """Envoy CT phase entity."""

    entity_description: EnvoyCTSensorEntityDescription

    @property
    def native_value(
        self,
    ) -> int | float | str | CtType | CtMeterStatus | CtStatusFlags | None:
        """Return the state of the CT phase sensor."""
        if TYPE_CHECKING:
            assert self.entity_description.on_phase
        if (cttype := self.entity_description.cttype) not in self.data.ctmeters_phases:
            return None
        if (phase := self.entity_description.on_phase) not in self.data.ctmeters_phases[
            cttype
        ]:
            return None
        return self.entity_description.value_fn(
            self.data.ctmeters_phases[cttype][phase]
        )


class EnvoyInverterEntity(EnvoySensorBaseEntity):
    """Envoy inverter entity."""

    entity_description: EnvoyInverterSensorEntityDescription

    def __init__(
        self,
        coordinator: EnphaseUpdateCoordinator,
        description: EnvoyInverterSensorEntityDescription,
        serial_number: str,
    ) -> None:
        """Initialize Envoy inverter entity."""
        super().__init__(coordinator, description)
        self._serial_number = serial_number
        key = description.key
        if key == INVERTERS_KEY:
            # Originally there was only one inverter sensor, so we don't want to
            # break existing installations by changing the unique_id.
            self._attr_unique_id = serial_number
        else:
            # Additional sensors have a unique_id that includes the
            # sensor key.
            self._attr_unique_id = f"{serial_number}_{key}"
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, serial_number)},
            name=f"Inverter {serial_number}",
            manufacturer="Enphase",
            model="Inverter",
            via_device=(DOMAIN, self.envoy_serial_num),
            serial_number=serial_number,
        )

    @property
    def native_value(self) -> datetime.datetime | float | None:
        """Return the state of the sensor."""
        inverters = self.data.inverters
        assert inverters is not None
        # Some envoy fw versions return an empty inverter array every 4 hours when
        # no production is taking place. Prevent collection failure due to this
        # as other data seems fine. Inverters will show unknown during this cycle.
        if self._serial_number not in inverters:
            _LOGGER.debug(
                "Inverter %s not in returned inverters array (size: %s)",
                self._serial_number,
                len(inverters),
            )
            return None
        return self.entity_description.value_fn(inverters[self._serial_number])


class EnvoyEnchargeEntity(EnvoySensorBaseEntity):
    """Envoy Encharge sensor entity."""

    def __init__(
        self,
        coordinator: EnphaseUpdateCoordinator,
        description: EnvoyEnchargeSensorEntityDescription
        | EnvoyEnchargePowerSensorEntityDescription,
        serial_number: str,
    ) -> None:
        """Initialize Encharge entity."""
        super().__init__(coordinator, description)
        self._serial_number = serial_number
        self._attr_unique_id = f"{serial_number}_{description.key}"
        encharge_inventory = self.data.encharge_inventory
        assert encharge_inventory is not None
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, serial_number)},
            manufacturer="Enphase",
            model="Encharge",
            name=f"Encharge {serial_number}",
            sw_version=str(encharge_inventory[self._serial_number].firmware_version),
            via_device=(DOMAIN, self.envoy_serial_num),
            serial_number=serial_number,
        )


class EnvoyEnchargeInventoryEntity(EnvoyEnchargeEntity):
    """Envoy Encharge inventory entity."""

    entity_description: EnvoyEnchargeSensorEntityDescription

    @property
    def native_value(self) -> int | float | datetime.datetime | None:
        """Return the state of the inventory sensors."""
        encharge_inventory = self.data.encharge_inventory
        assert encharge_inventory is not None
        return self.entity_description.value_fn(encharge_inventory[self._serial_number])


class EnvoyEnchargePowerEntity(EnvoyEnchargeEntity):
    """Envoy Encharge power entity."""

    entity_description: EnvoyEnchargePowerSensorEntityDescription

    @property
    def native_value(self) -> int | float | None:
        """Return the state of the power sensors."""
        encharge_power = self.data.encharge_power
        assert encharge_power is not None
        return self.entity_description.value_fn(encharge_power[self._serial_number])


class EnvoyEnchargeAggregateEntity(EnvoySystemSensorEntity):
    """Envoy Encharge Aggregate sensor entity."""

    entity_description: EnvoyEnchargeAggregateSensorEntityDescription

    @property
    def native_value(self) -> int:
        """Return the state of the aggregate sensors."""
        encharge_aggregate = self.data.encharge_aggregate
        assert encharge_aggregate is not None
        return self.entity_description.value_fn(encharge_aggregate)


class EnvoyEnpowerEntity(EnvoySensorBaseEntity):
    """Envoy Enpower sensor entity."""

    entity_description: EnvoyEnpowerSensorEntityDescription

    def __init__(
        self,
        coordinator: EnphaseUpdateCoordinator,
        description: EnvoyEnpowerSensorEntityDescription,
    ) -> None:
        """Initialize Enpower entity."""
        super().__init__(coordinator, description)
        enpower_data = self.data.enpower
        assert enpower_data is not None
        self._attr_unique_id = f"{enpower_data.serial_number}_{description.key}"
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, enpower_data.serial_number)},
            manufacturer="Enphase",
            model="Enpower",
            name=f"Enpower {enpower_data.serial_number}",
            sw_version=str(enpower_data.firmware_version),
            via_device=(DOMAIN, self.envoy_serial_num),
            serial_number=enpower_data.serial_number,
        )

    @property
    def native_value(self) -> datetime.datetime | int | float | None:
        """Return the state of the power sensors."""
        enpower = self.data.enpower
        assert enpower is not None
        return self.entity_description.value_fn(enpower)


class EnvoyAcbBatteryPowerEntity(EnvoySensorBaseEntity):
    """Envoy ACB Battery power sensor entity."""

    entity_description: EnvoyAcbBatterySensorEntityDescription

    def __init__(
        self,
        coordinator: EnphaseUpdateCoordinator,
        description: EnvoyAcbBatterySensorEntityDescription,
    ) -> None:
        """Initialize ACB Battery entity."""
        super().__init__(coordinator, description)
        acb_data = self.data.acb_power
        assert acb_data is not None
        self._attr_unique_id = f"{self.envoy_serial_num}_{description.key}"
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, f"{self.envoy_serial_num}_acb")},
            manufacturer="Enphase",
            model="ACB",
            name=f"ACB {self.envoy_serial_num}",
            via_device=(DOMAIN, self.envoy_serial_num),
        )

    @property
    def native_value(self) -> int | str | None:
        """Return the state of the ACB Battery power sensors."""
        acb = self.data.acb_power
        assert acb is not None
        return self.entity_description.value_fn(acb)


class EnvoyAcbBatteryEnergyEntity(EnvoySystemSensorEntity):
    """Envoy combined ACB and Ensemble Battery Aggregate energy sensor entity."""

    entity_description: EnvoyAcbBatterySensorEntityDescription

    @property
    def native_value(self) -> int | str:
        """Return the state of the aggregate energy sensors."""
        acb = self.data.acb_power
        assert acb is not None
        return self.entity_description.value_fn(acb)


class AggregateBatteryEntity(EnvoySystemSensorEntity):
    """Envoy combined ACB and Ensemble Battery Aggregate sensor entity."""

    entity_description: EnvoyAggregateBatterySensorEntityDescription

    @property
    def native_value(self) -> int:
        """Return the state of the aggregate sensors."""
        battery_aggregate = self.data.battery_aggregate
        assert battery_aggregate is not None
        return self.entity_description.value_fn(battery_aggregate)


class EnvoyCollarEntity(EnvoySensorBaseEntity):
    """Envoy Collar sensor entity."""

    entity_description: EnvoyCollarSensorEntityDescription

    def __init__(
        self,
        coordinator: EnphaseUpdateCoordinator,
        description: EnvoyCollarSensorEntityDescription,
    ) -> None:
        """Initialize Collar entity."""
        super().__init__(coordinator, description)
        collar_data = self.data.collar
        assert collar_data is not None
        self._serial_number = collar_data.serial_number
        self._attr_unique_id = f"{collar_data.serial_number}_{description.key}"
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, collar_data.serial_number)},
            manufacturer="Enphase",
            model="IQ Meter Collar",
            name=f"Collar {collar_data.serial_number}",
            sw_version=str(collar_data.firmware_version),
            via_device=(DOMAIN, self.envoy_serial_num),
            serial_number=collar_data.serial_number,
        )

    @property
    def native_value(self) -> datetime.datetime | int | float | str:
        """Return the state of the collar sensors."""
        collar_data = self.data.collar
        assert collar_data is not None
        return self.entity_description.value_fn(collar_data)


class EnvoyC6CCEntity(EnvoySensorBaseEntity):
    """Envoy C6CC sensor entity."""

    entity_description: EnvoyC6CCSensorEntityDescription

    def __init__(
        self,
        coordinator: EnphaseUpdateCoordinator,
        description: EnvoyC6CCSensorEntityDescription,
    ) -> None:
        """Initialize Encharge entity."""
        super().__init__(coordinator, description)
        c6cc_data = self.data.c6cc
        assert c6cc_data is not None
        self._attr_unique_id = f"{c6cc_data.serial_number}_{description.key}"
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, c6cc_data.serial_number)},
            manufacturer="Enphase",
            model="C6 COMBINER CONTROLLER",
            name=f"C6 Combiner {c6cc_data.serial_number}",
            sw_version=str(c6cc_data.firmware_version),
            via_device=(DOMAIN, self.envoy_serial_num),
            serial_number=c6cc_data.serial_number,
        )

    @property
    def native_value(self) -> datetime.datetime:
        """Return the state of the c6cc inventory sensors."""
        c6cc_data = self.data.c6cc
        assert c6cc_data is not None
        return self.entity_description.value_fn(c6cc_data)
