mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
bump pyenphase to 4.0 and add required None handling and tests (#179673)
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pyenphase"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["pyenphase==3.2.1"],
|
||||
"requirements": ["pyenphase==4.0.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_enphase-envoy._tcp.local."
|
||||
|
||||
@@ -21,7 +21,7 @@ from pyenphase import (
|
||||
EnvoySystemConsumption,
|
||||
EnvoySystemProduction,
|
||||
)
|
||||
from pyenphase.const import PHASENAMES
|
||||
from pyenphase.const import PHASENAMES, SupportedFeatures
|
||||
from pyenphase.models.acb import ACBChargeStatus, ACBSleepState
|
||||
from pyenphase.models.meters import (
|
||||
CtMeterStatus,
|
||||
@@ -383,7 +383,7 @@ class EnvoyCTSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes an Envoy CT sensor entity."""
|
||||
|
||||
value_fn: Callable[
|
||||
[EnvoyMeterData],
|
||||
[EnvoyMeterData | None],
|
||||
int | float | str | CtType | CtMeterStatus | CtStatusFlags | CtState | None,
|
||||
]
|
||||
on_phase: str | None = None
|
||||
@@ -586,7 +586,9 @@ CT_SENSORS = (
|
||||
translation_key=(translation_key if translation_key != "" else key),
|
||||
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),
|
||||
value_fn=lambda ct: (
|
||||
0 if ct is None or ct.status_flags is None else len(ct.status_flags)
|
||||
),
|
||||
cttype=cttype,
|
||||
)
|
||||
for cttype, key, translation_key in (
|
||||
@@ -1020,7 +1022,9 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up envoy sensor platform."""
|
||||
coordinator = config_entry.runtime_data
|
||||
envoy_data = coordinator.envoy.data
|
||||
envoy = coordinator.envoy
|
||||
assert envoy is not None
|
||||
envoy_data = envoy.data
|
||||
assert envoy_data is not None
|
||||
_LOGGER.debug("Envoy data: %s", envoy_data)
|
||||
|
||||
@@ -1028,39 +1032,57 @@ async def async_setup_entry(
|
||||
EnvoyProductionEntity(coordinator, description)
|
||||
for description in PRODUCTION_SENSORS
|
||||
]
|
||||
if envoy_data.system_consumption:
|
||||
# add unconditionally if TOTAL_CONSUMPTION is available to overcome
|
||||
# None value at startup caused by envoy fw issues
|
||||
if envoy.supported_features & SupportedFeatures.TOTAL_CONSUMPTION:
|
||||
entities.extend(
|
||||
EnvoyConsumptionEntity(coordinator, description)
|
||||
for description in CONSUMPTION_SENSORS
|
||||
)
|
||||
if envoy_data.system_net_consumption:
|
||||
# add unconditionally if NET_CONSUMPTION is available to overcome
|
||||
# None value at startup caused by envoy fw issues
|
||||
if envoy.supported_features & SupportedFeatures.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:
|
||||
# if PRODUCTION is available and phases detected even if None
|
||||
# to overcome None value at startup caused by envoy fw issues
|
||||
if envoy.active_phase_count and (
|
||||
envoy.supported_features & SupportedFeatures.PRODUCTION
|
||||
):
|
||||
entities.extend(
|
||||
EnvoyProductionPhaseEntity(coordinator, description)
|
||||
for use_phase, phase in envoy_data.system_production_phases.items()
|
||||
for index, use_phase in enumerate(PHASENAMES)
|
||||
for description in PRODUCTION_PHASE_SENSORS[use_phase]
|
||||
if phase is not None
|
||||
if index < (envoy.phase_count if envoy.phase_count > 1 else 0)
|
||||
)
|
||||
# For each consumption phase reported add consumption entities
|
||||
if envoy_data.system_consumption_phases:
|
||||
# if TOTAL_CONSUMPTION is available and phases detected even if None
|
||||
# to overcome None value at startup caused by envoy fw issues
|
||||
if (
|
||||
envoy.active_phase_count
|
||||
and envoy.phase_count > 1
|
||||
and (envoy.supported_features & SupportedFeatures.TOTAL_CONSUMPTION)
|
||||
):
|
||||
entities.extend(
|
||||
EnvoyConsumptionPhaseEntity(coordinator, description)
|
||||
for use_phase, phase in envoy_data.system_consumption_phases.items()
|
||||
for index, use_phase in enumerate(PHASENAMES)
|
||||
for description in CONSUMPTION_PHASE_SENSORS[use_phase]
|
||||
if phase is not None
|
||||
if index < (envoy.phase_count if envoy.phase_count > 1 else 0)
|
||||
)
|
||||
# For each net_consumption phase reported add consumption entities
|
||||
if envoy_data.system_net_consumption_phases:
|
||||
# if NET_CONSUMPTION is available and phases detected even if None
|
||||
# to overcome None value at startup caused by envoy fw issues
|
||||
if envoy.active_phase_count and (
|
||||
envoy.supported_features & SupportedFeatures.NET_CONSUMPTION
|
||||
):
|
||||
entities.extend(
|
||||
EnvoyNetConsumptionPhaseEntity(coordinator, description)
|
||||
for use_phase, phase in envoy_data.system_net_consumption_phases.items()
|
||||
for index, use_phase in enumerate(PHASENAMES)
|
||||
for description in NET_CONSUMPTION_PHASE_SENSORS[use_phase]
|
||||
if phase is not None
|
||||
if index < (envoy.phase_count if envoy.phase_count > 1 else 0)
|
||||
)
|
||||
# Add Current Transformer entities
|
||||
if envoy_data.ctmeters:
|
||||
@@ -1181,8 +1203,8 @@ class EnvoyProductionEntity(EnvoySystemSensorEntity):
|
||||
@override
|
||||
def native_value(self) -> int | None:
|
||||
"""Return the state of the sensor."""
|
||||
system_production = self.data.system_production
|
||||
assert system_production is not None
|
||||
if (system_production := self.data.system_production) is None:
|
||||
return None
|
||||
return self.entity_description.value_fn(system_production)
|
||||
|
||||
|
||||
@@ -1195,8 +1217,8 @@ class EnvoyConsumptionEntity(EnvoySystemSensorEntity):
|
||||
@override
|
||||
def native_value(self) -> int | None:
|
||||
"""Return the state of the sensor."""
|
||||
system_consumption = self.data.system_consumption
|
||||
assert system_consumption is not None
|
||||
if (system_consumption := self.data.system_consumption) is None:
|
||||
return None
|
||||
return self.entity_description.value_fn(system_consumption)
|
||||
|
||||
|
||||
@@ -1209,8 +1231,8 @@ class EnvoyNetConsumptionEntity(EnvoySystemSensorEntity):
|
||||
@override
|
||||
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
|
||||
if (system_net_consumption := self.data.system_net_consumption) is None:
|
||||
return None
|
||||
return self.entity_description.value_fn(system_net_consumption)
|
||||
|
||||
|
||||
@@ -1225,8 +1247,11 @@ class EnvoyProductionPhaseEntity(EnvoySystemSensorEntity):
|
||||
"""Return the state of the sensor."""
|
||||
if TYPE_CHECKING:
|
||||
assert self.entity_description.on_phase
|
||||
assert self.data.system_production_phases
|
||||
|
||||
if self.data.system_production_phases is None:
|
||||
return None
|
||||
if self.entity_description.on_phase not in self.data.system_production_phases:
|
||||
return None
|
||||
if (
|
||||
system_production := self.data.system_production_phases[
|
||||
self.entity_description.on_phase
|
||||
@@ -1247,8 +1272,11 @@ class EnvoyConsumptionPhaseEntity(EnvoySystemSensorEntity):
|
||||
"""Return the state of the sensor."""
|
||||
if TYPE_CHECKING:
|
||||
assert self.entity_description.on_phase
|
||||
assert self.data.system_consumption_phases
|
||||
|
||||
if self.data.system_consumption_phases is None:
|
||||
return None
|
||||
if self.entity_description.on_phase not in self.data.system_consumption_phases:
|
||||
return None
|
||||
if (
|
||||
system_consumption := self.data.system_consumption_phases[
|
||||
self.entity_description.on_phase
|
||||
@@ -1269,8 +1297,14 @@ class EnvoyNetConsumptionPhaseEntity(EnvoySystemSensorEntity):
|
||||
"""Return the state of the sensor."""
|
||||
if TYPE_CHECKING:
|
||||
assert self.entity_description.on_phase
|
||||
assert self.data.system_net_consumption_phases
|
||||
|
||||
if self.data.system_net_consumption_phases is None:
|
||||
return None
|
||||
if (
|
||||
self.entity_description.on_phase
|
||||
not in self.data.system_net_consumption_phases
|
||||
):
|
||||
return None
|
||||
if (
|
||||
system_net_consumption := self.data.system_net_consumption_phases[
|
||||
self.entity_description.on_phase
|
||||
@@ -1293,6 +1327,8 @@ class EnvoyCTEntity(EnvoySystemSensorEntity):
|
||||
"""Return the state of the CT sensor."""
|
||||
if (cttype := self.entity_description.cttype) not in self.data.ctmeters:
|
||||
return None
|
||||
if self.data.ctmeters[cttype] is None:
|
||||
return None
|
||||
return self.entity_description.value_fn(self.data.ctmeters[cttype])
|
||||
|
||||
|
||||
@@ -1315,6 +1351,8 @@ class EnvoyCTPhaseEntity(EnvoySystemSensorEntity):
|
||||
cttype
|
||||
]:
|
||||
return None
|
||||
if self.data.ctmeters_phases[cttype][phase] is None:
|
||||
return None
|
||||
return self.entity_description.value_fn(
|
||||
self.data.ctmeters_phases[cttype][phase]
|
||||
)
|
||||
|
||||
Generated
+1
-1
@@ -2171,7 +2171,7 @@ pyegps==0.2.5
|
||||
pyemoncms==0.1.3
|
||||
|
||||
# homeassistant.components.enphase_envoy
|
||||
pyenphase==3.2.1
|
||||
pyenphase==4.0.0
|
||||
|
||||
# homeassistant.components.envertech_evt800
|
||||
pyenvertechevt800==0.2.4
|
||||
|
||||
@@ -206,8 +206,8 @@ def _load_json_2_production_data(
|
||||
if item := json_fixture["data"].get("system_consumption_phases"):
|
||||
mocked_data.system_consumption_phases = {}
|
||||
for sub_item, item_data in item.items():
|
||||
mocked_data.system_consumption_phases[sub_item] = EnvoySystemConsumption(
|
||||
**item_data
|
||||
mocked_data.system_consumption_phases[sub_item] = (
|
||||
None if not item_data else EnvoySystemConsumption(**item_data)
|
||||
)
|
||||
if item := json_fixture["data"].get("system_net_consumption_phases"):
|
||||
mocked_data.system_net_consumption_phases = {}
|
||||
@@ -218,8 +218,8 @@ def _load_json_2_production_data(
|
||||
if item := json_fixture["data"].get("system_production_phases"):
|
||||
mocked_data.system_production_phases = {}
|
||||
for sub_item, item_data in item.items():
|
||||
mocked_data.system_production_phases[sub_item] = EnvoySystemProduction(
|
||||
**item_data
|
||||
mocked_data.system_production_phases[sub_item] = (
|
||||
None if not item_data else EnvoySystemProduction(**item_data)
|
||||
)
|
||||
if item := json_fixture["data"].get("acb_power"):
|
||||
mocked_data.acb_power = EnvoyACBPower(**item)
|
||||
@@ -232,15 +232,19 @@ def _load_json_2_meter_data(
|
||||
if meters := json_fixture["data"].get("ctmeters"):
|
||||
mocked_data.ctmeters = {}
|
||||
[
|
||||
mocked_data.ctmeters.update({meter: EnvoyMeterData(**meter_data)})
|
||||
mocked_data.ctmeters.update(
|
||||
{meter: None if not meter_data else EnvoyMeterData(**meter_data)}
|
||||
)
|
||||
for meter, meter_data in meters.items()
|
||||
]
|
||||
if meters := json_fixture["data"].get("ctmeters_phases"):
|
||||
mocked_data.ctmeters_phases = {}
|
||||
for meter, meter_data in meters.items():
|
||||
meter_phase_data: dict[str, EnvoyMeterData] = {}
|
||||
meter_phase_data: dict[str, EnvoyMeterData | None] = {}
|
||||
[
|
||||
meter_phase_data.update({phase: EnvoyMeterData(**phase_data)})
|
||||
meter_phase_data.update(
|
||||
{phase: None if not phase_data else EnvoyMeterData(**phase_data)}
|
||||
)
|
||||
for phase, phase_data in meter_data.items()
|
||||
]
|
||||
mocked_data.ctmeters_phases.update({meter: meter_phase_data})
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"firmware": "7.1.2",
|
||||
"part_number": "123456789",
|
||||
"envoy_model": "Envoy, phases: 3, phase mode: split, net-consumption CT, production CT, storage CT",
|
||||
"supported_features": 1659,
|
||||
"supported_features": 1663,
|
||||
"phase_mode": "three",
|
||||
"phase_count": 3,
|
||||
"active_phase_count": 3,
|
||||
|
||||
@@ -0,0 +1,639 @@
|
||||
{
|
||||
"serial_number": "1234",
|
||||
"firmware": "7.1.2",
|
||||
"part_number": "123456789",
|
||||
"envoy_model": "Envoy, phases: 3, phase mode: split, net-consumption CT, production CT, storage CT",
|
||||
"supported_features": 1663,
|
||||
"phase_mode": "three",
|
||||
"phase_count": 3,
|
||||
"active_phase_count": 3,
|
||||
"ct_meter_count": 2,
|
||||
"consumption_meter_type": "net-consumption",
|
||||
"production_meter_type": "production",
|
||||
"storage_meter_type": "storage",
|
||||
"data": {
|
||||
"encharge_inventory": {
|
||||
"123456": {
|
||||
"admin_state": 6,
|
||||
"admin_state_str": "ENCHG_STATE_READY",
|
||||
"bmu_firmware_version": "2.1.34",
|
||||
"comm_level_2_4_ghz": 4,
|
||||
"comm_level_sub_ghz": 4,
|
||||
"communicating": true,
|
||||
"dc_switch_off": false,
|
||||
"encharge_capacity": 3500,
|
||||
"encharge_revision": 2,
|
||||
"firmware_loaded_date": 1695330323,
|
||||
"firmware_version": "2.6.5973_rel/22.11",
|
||||
"installed_date": 1695330323,
|
||||
"last_report_date": 1695769447,
|
||||
"led_status": 17,
|
||||
"max_cell_temp": 30,
|
||||
"operating": true,
|
||||
"part_number": "830-01760-r37",
|
||||
"percent_full": 15,
|
||||
"serial_number": "123456",
|
||||
"temperature": 29,
|
||||
"temperature_unit": "C",
|
||||
"zigbee_dongle_fw_version": "100F"
|
||||
}
|
||||
},
|
||||
"encharge_power": {
|
||||
"123456": {
|
||||
"apparent_power_mva": 0,
|
||||
"real_power_mw": 0,
|
||||
"soc": 15
|
||||
}
|
||||
},
|
||||
"encharge_aggregate": {
|
||||
"available_energy": 525,
|
||||
"backup_reserve": 526,
|
||||
"state_of_charge": 15,
|
||||
"reserve_state_of_charge": 15,
|
||||
"configured_reserve_state_of_charge": 15,
|
||||
"max_available_capacity": 3500
|
||||
},
|
||||
"enpower": {
|
||||
"grid_mode": "multimode-ongrid",
|
||||
"admin_state": 24,
|
||||
"admin_state_str": "ENPWR_STATE_OPER_CLOSED",
|
||||
"comm_level_2_4_ghz": 5,
|
||||
"comm_level_sub_ghz": 5,
|
||||
"communicating": true,
|
||||
"firmware_loaded_date": 1695330323,
|
||||
"firmware_version": "1.2.2064_release/20.34",
|
||||
"installed_date": 1695330323,
|
||||
"last_report_date": 1695769447,
|
||||
"mains_admin_state": "closed",
|
||||
"mains_oper_state": "closed",
|
||||
"operating": true,
|
||||
"part_number": "830-01760-r37",
|
||||
"serial_number": "654321",
|
||||
"temperature": 79,
|
||||
"temperature_unit": "F",
|
||||
"zigbee_dongle_fw_version": "1009"
|
||||
},
|
||||
"system_consumption": null,
|
||||
"system_net_consumption": {
|
||||
"watt_hours_lifetime": 4321,
|
||||
"watt_hours_last_7_days": -1,
|
||||
"watt_hours_today": -1,
|
||||
"watts_now": 2341
|
||||
},
|
||||
"system_production": null,
|
||||
"system_consumption_phases": {
|
||||
"L1": null,
|
||||
"L2": null,
|
||||
"L3": null
|
||||
},
|
||||
"system_net_consumption_phases": {
|
||||
"L1": {
|
||||
"watt_hours_lifetime": 1321,
|
||||
"watt_hours_last_7_days": -1,
|
||||
"watt_hours_today": -1,
|
||||
"watts_now": 12341
|
||||
},
|
||||
"L2": {
|
||||
"watt_hours_lifetime": 2321,
|
||||
"watt_hours_last_7_days": -1,
|
||||
"watt_hours_today": -1,
|
||||
"watts_now": 22341
|
||||
},
|
||||
"L3": {
|
||||
"watt_hours_lifetime": 3321,
|
||||
"watt_hours_last_7_days": -1,
|
||||
"watt_hours_today": -1,
|
||||
"watts_now": 32341
|
||||
}
|
||||
},
|
||||
"system_production_phases": {
|
||||
"L1": null,
|
||||
"L2": null,
|
||||
"L3": null
|
||||
},
|
||||
"ctmeters": {
|
||||
"production": {
|
||||
"eid": "100000010",
|
||||
"timestamp": 1708006110,
|
||||
"energy_delivered": 11234,
|
||||
"energy_received": 12345,
|
||||
"active_power": 100,
|
||||
"power_factor": 0.11,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance", "power-on-unused-phase"]
|
||||
},
|
||||
"net-consumption": {
|
||||
"eid": "100000020",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 21234,
|
||||
"energy_received": 22345,
|
||||
"active_power": 101,
|
||||
"power_factor": 0.21,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"storage": null,
|
||||
"backfeed": null,
|
||||
"load": {
|
||||
"eid": "100000050",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 51234,
|
||||
"energy_received": 52345,
|
||||
"active_power": 105,
|
||||
"power_factor": 0.25,
|
||||
"voltage": 115,
|
||||
"current": 0.6,
|
||||
"frequency": 50.6,
|
||||
"state": "enabled",
|
||||
"measurement_type": "load",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"evse": {
|
||||
"eid": "100000060",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 61234,
|
||||
"energy_received": 62345,
|
||||
"active_power": 106,
|
||||
"power_factor": 0.26,
|
||||
"voltage": 116,
|
||||
"current": 0.7,
|
||||
"frequency": 50.7,
|
||||
"state": "enabled",
|
||||
"measurement_type": "evse",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"pv3p": {
|
||||
"eid": "100000070",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 71234,
|
||||
"energy_received": 72345,
|
||||
"active_power": 107,
|
||||
"power_factor": 0.27,
|
||||
"voltage": 117,
|
||||
"current": 0.8,
|
||||
"frequency": 50.8,
|
||||
"state": "enabled",
|
||||
"measurement_type": "pv3p",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"ctmeters_phases": {
|
||||
"production": {
|
||||
"L1": {
|
||||
"eid": "100000011",
|
||||
"timestamp": 1708006111,
|
||||
"energy_delivered": 112341,
|
||||
"energy_received": 123451,
|
||||
"active_power": 20,
|
||||
"power_factor": 0.12,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance"]
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000012",
|
||||
"timestamp": 1708006112,
|
||||
"energy_delivered": 112342,
|
||||
"energy_received": 123452,
|
||||
"active_power": 30,
|
||||
"power_factor": 0.13,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["power-on-unused-phase"]
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000013",
|
||||
"timestamp": 1708006113,
|
||||
"energy_delivered": 112343,
|
||||
"energy_received": 123453,
|
||||
"active_power": 50,
|
||||
"power_factor": 0.14,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"net-consumption": {
|
||||
"L1": {
|
||||
"eid": "100000021",
|
||||
"timestamp": 1708006121,
|
||||
"energy_delivered": 212341,
|
||||
"energy_received": 223451,
|
||||
"active_power": 21,
|
||||
"power_factor": 0.22,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000022",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 212342,
|
||||
"energy_received": 223452,
|
||||
"active_power": 31,
|
||||
"power_factor": 0.23,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000023",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 212343,
|
||||
"energy_received": 223453,
|
||||
"active_power": 51,
|
||||
"power_factor": 0.24,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
"L1": null,
|
||||
"L2": {
|
||||
"eid": "100000032",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 312342,
|
||||
"energy_received": 323452,
|
||||
"active_power": 33,
|
||||
"power_factor": 0.23,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "storage",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000033",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 312343,
|
||||
"energy_received": 323453,
|
||||
"active_power": 53,
|
||||
"power_factor": 0.24,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "storage",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"backfeed": {
|
||||
"L1": null,
|
||||
"L2": null,
|
||||
"L3": null
|
||||
},
|
||||
"load": {
|
||||
"L1": {
|
||||
"eid": "100000051",
|
||||
"timestamp": 1708006121,
|
||||
"energy_delivered": 512341,
|
||||
"energy_received": 523451,
|
||||
"active_power": 115,
|
||||
"power_factor": 0.25,
|
||||
"voltage": 115,
|
||||
"current": 5.1,
|
||||
"frequency": 50.6,
|
||||
"state": "enabled",
|
||||
"measurement_type": "load",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000052",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 512342,
|
||||
"energy_received": 523452,
|
||||
"active_power": 125,
|
||||
"power_factor": 0.25,
|
||||
"voltage": 115,
|
||||
"current": 5.2,
|
||||
"frequency": 50.6,
|
||||
"state": "enabled",
|
||||
"measurement_type": "load",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000052",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 512343,
|
||||
"energy_received": 523453,
|
||||
"active_power": 135,
|
||||
"power_factor": 0.25,
|
||||
"voltage": 115,
|
||||
"current": 5.3,
|
||||
"frequency": 50.6,
|
||||
"state": "enabled",
|
||||
"measurement_type": "load",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"evse": {
|
||||
"L1": {
|
||||
"eid": "100000061",
|
||||
"timestamp": 1708006121,
|
||||
"energy_delivered": 612341,
|
||||
"energy_received": 623451,
|
||||
"active_power": 116,
|
||||
"power_factor": 0.26,
|
||||
"voltage": 116,
|
||||
"current": 6.1,
|
||||
"frequency": 50.6,
|
||||
"state": "enabled",
|
||||
"measurement_type": "evse",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000062",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 612342,
|
||||
"energy_received": 623452,
|
||||
"active_power": 126,
|
||||
"power_factor": 0.26,
|
||||
"voltage": 116,
|
||||
"current": 6.2,
|
||||
"frequency": 50.6,
|
||||
"state": "enabled",
|
||||
"measurement_type": "evse",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000063",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 612343,
|
||||
"energy_received": 623453,
|
||||
"active_power": 136,
|
||||
"power_factor": 0.26,
|
||||
"voltage": 116,
|
||||
"current": 6.3,
|
||||
"frequency": 50.6,
|
||||
"state": "enabled",
|
||||
"measurement_type": "evse",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"pv3p": {
|
||||
"L1": {
|
||||
"eid": "100000071",
|
||||
"timestamp": 1708006127,
|
||||
"energy_delivered": 712341,
|
||||
"energy_received": 723451,
|
||||
"active_power": 117,
|
||||
"power_factor": 0.27,
|
||||
"voltage": 117,
|
||||
"current": 7.1,
|
||||
"frequency": 50.7,
|
||||
"state": "enabled",
|
||||
"measurement_type": "pv3p",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000072",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 712342,
|
||||
"energy_received": 723452,
|
||||
"active_power": 127,
|
||||
"power_factor": 0.27,
|
||||
"voltage": 117,
|
||||
"current": 7.2,
|
||||
"frequency": 50.7,
|
||||
"state": "enabled",
|
||||
"measurement_type": "pv3p",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000073",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 712343,
|
||||
"energy_received": 723453,
|
||||
"active_power": 137,
|
||||
"power_factor": 0.27,
|
||||
"voltage": 117,
|
||||
"current": 7.3,
|
||||
"frequency": 50.7,
|
||||
"state": "enabled",
|
||||
"measurement_type": "pv3p",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"dry_contact_status": {
|
||||
"NC1": {
|
||||
"id": "NC1",
|
||||
"status": "open"
|
||||
},
|
||||
"NC2": {
|
||||
"id": "NC2",
|
||||
"status": "closed"
|
||||
},
|
||||
"NC3": {
|
||||
"id": "NC3",
|
||||
"status": "open"
|
||||
}
|
||||
},
|
||||
"dry_contact_settings": {
|
||||
"NC1": {
|
||||
"id": "NC1",
|
||||
"black_start": 5.0,
|
||||
"essential_end_time": 32400.0,
|
||||
"essential_start_time": 57600.0,
|
||||
"generator_action": "shed",
|
||||
"grid_action": "shed",
|
||||
"load_name": "NC1 Fixture",
|
||||
"manual_override": true,
|
||||
"micro_grid_action": "shed",
|
||||
"mode": "manual",
|
||||
"override": true,
|
||||
"priority": 1.0,
|
||||
"pv_serial_nb": [],
|
||||
"soc_high": 70.0,
|
||||
"soc_low": 25.0,
|
||||
"type": "LOAD"
|
||||
},
|
||||
"NC2": {
|
||||
"id": "NC2",
|
||||
"black_start": 5.0,
|
||||
"essential_end_time": 57600.0,
|
||||
"essential_start_time": 32400.0,
|
||||
"generator_action": "shed",
|
||||
"grid_action": "apply",
|
||||
"load_name": "NC2 Fixture",
|
||||
"manual_override": true,
|
||||
"micro_grid_action": "shed",
|
||||
"mode": "manual",
|
||||
"override": true,
|
||||
"priority": 2.0,
|
||||
"pv_serial_nb": [],
|
||||
"soc_high": 70.0,
|
||||
"soc_low": 30.0,
|
||||
"type": "LOAD"
|
||||
},
|
||||
"NC3": {
|
||||
"id": "NC3",
|
||||
"black_start": 5.0,
|
||||
"essential_end_time": 57600.0,
|
||||
"essential_start_time": 32400.0,
|
||||
"generator_action": "apply",
|
||||
"grid_action": "shed",
|
||||
"load_name": "NC3 Fixture",
|
||||
"manual_override": true,
|
||||
"micro_grid_action": "apply",
|
||||
"mode": "manual",
|
||||
"override": true,
|
||||
"priority": 3.0,
|
||||
"pv_serial_nb": [],
|
||||
"soc_high": 70.0,
|
||||
"soc_low": 30.0,
|
||||
"type": "NONE"
|
||||
}
|
||||
},
|
||||
"collar": {
|
||||
"admin_state": 88,
|
||||
"admin_state_str": "ENCMN_MDE_ON_GRID",
|
||||
"firmware_loaded_date": 1752939759,
|
||||
"firmware_version": "3.0.6-D0",
|
||||
"installed_date": 1752939759,
|
||||
"last_report_date": 1752939759,
|
||||
"communicating": true,
|
||||
"mid_state": "close",
|
||||
"grid_state": "on_grid",
|
||||
"part_number": "865-00400-r22",
|
||||
"serial_number": "482520020939",
|
||||
"temperature": 42,
|
||||
"temperature_unit": "C",
|
||||
"control_error": 0,
|
||||
"collar_state": "Installed"
|
||||
},
|
||||
"c6cc": {
|
||||
"admin_state": 82,
|
||||
"admin_state_str": "ENCMN_C6_CC_READY",
|
||||
"firmware_loaded_date": 1752945451,
|
||||
"firmware_version": "0.1.20-D1",
|
||||
"installed_date": 1752945451,
|
||||
"last_report_date": 1752945451,
|
||||
"communicating": true,
|
||||
"part_number": "800-02403-r08",
|
||||
"serial_number": "482523040549",
|
||||
"dmir_version": "0.1.20-D1"
|
||||
},
|
||||
"inverters": {
|
||||
"1": {
|
||||
"serial_number": "1",
|
||||
"last_report_date": 1,
|
||||
"last_report_watts": 1,
|
||||
"max_report_watts": 1,
|
||||
"dc_voltage": null,
|
||||
"dc_current": null,
|
||||
"ac_voltage": null,
|
||||
"ac_current": null,
|
||||
"ac_frequency": null,
|
||||
"temperature": null,
|
||||
"energy_produced": null,
|
||||
"energy_today": null,
|
||||
"lifetime_energy": null,
|
||||
"last_report_duration": null
|
||||
}
|
||||
},
|
||||
"tariff": {
|
||||
"currency": {
|
||||
"code": "EUR"
|
||||
},
|
||||
"logger": "mylogger",
|
||||
"date": "1695744220",
|
||||
"storage_settings": {
|
||||
"mode": "self-consumption",
|
||||
"operation_mode_sub_type": "",
|
||||
"reserved_soc": 15.0,
|
||||
"very_low_soc": 5,
|
||||
"charge_from_grid": true,
|
||||
"date": "1695598084",
|
||||
"opt_schedules": true
|
||||
},
|
||||
"single_rate": {
|
||||
"rate": 0.0,
|
||||
"sell": 0.0
|
||||
},
|
||||
"seasons": [
|
||||
{
|
||||
"id": "season_1",
|
||||
"start": "1/1",
|
||||
"days": [
|
||||
{
|
||||
"id": "all_days",
|
||||
"days": "Mon,Tue,Wed,Thu,Fri,Sat,Sun",
|
||||
"must_charge_start": 444,
|
||||
"must_charge_duration": 35,
|
||||
"must_charge_mode": "CG",
|
||||
"enable_discharge_to_grid": true,
|
||||
"periods": [
|
||||
{
|
||||
"id": "period_1",
|
||||
"start": 480,
|
||||
"rate": 0.1898
|
||||
},
|
||||
{
|
||||
"id": "filler",
|
||||
"start": 1320,
|
||||
"rate": 0.1034
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tiers": []
|
||||
}
|
||||
],
|
||||
"seasons_sell": []
|
||||
},
|
||||
"raw": {
|
||||
"varies_by": "firmware_version"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"firmware": "7.6.175",
|
||||
"part_number": "123456789",
|
||||
"envoy_model": "Envoy, phases: 1, phase mode: three, total-consumption CT, production CT",
|
||||
"supported_features": 1217,
|
||||
"supported_features": 1231,
|
||||
"phase_mode": "three",
|
||||
"phase_count": 1,
|
||||
"active_phase_count": 0,
|
||||
|
||||
@@ -19923,6 +19923,7 @@
|
||||
'supported_features': list([
|
||||
'INVERTERS',
|
||||
'METERING',
|
||||
'TOTAL_CONSUMPTION',
|
||||
'NET_CONSUMPTION',
|
||||
'ENCHARGE',
|
||||
'ENPOWER',
|
||||
|
||||
@@ -43540,6 +43540,67 @@
|
||||
'state': '2.341',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_current_power_consumption-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.envoy_1234_current_power_consumption',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Current power consumption',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 3,
|
||||
}),
|
||||
'sensor.private': dict({
|
||||
'suggested_unit_of_measurement': <UnitOfPower.KILO_WATT: 'kW'>,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Current power consumption',
|
||||
'platform': 'enphase_envoy',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'current_power_consumption',
|
||||
'unique_id': '1234_consumption',
|
||||
'unit_of_measurement': <UnitOfPower.KILO_WATT: 'kW'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_current_power_consumption-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'power',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Envoy 1234 Current power consumption',
|
||||
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPower.KILO_WATT: 'kW'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.envoy_1234_current_power_consumption',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_current_power_production-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
@@ -43601,6 +43662,125 @@
|
||||
'state': '1.234',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_energy_consumption_last_seven_days-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.envoy_1234_energy_consumption_last_seven_days',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Energy consumption last seven days',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 1,
|
||||
}),
|
||||
'sensor.private': dict({
|
||||
'suggested_unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Energy consumption last seven days',
|
||||
'platform': 'enphase_envoy',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'seven_days_consumption',
|
||||
'unique_id': '1234_seven_days_consumption',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_energy_consumption_last_seven_days-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'energy',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Envoy 1234 Energy consumption last seven days',
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.envoy_1234_energy_consumption_last_seven_days',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_energy_consumption_today-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.envoy_1234_energy_consumption_today',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Energy consumption today',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 2,
|
||||
}),
|
||||
'sensor.private': dict({
|
||||
'suggested_unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Energy consumption today',
|
||||
'platform': 'enphase_envoy',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'daily_consumption',
|
||||
'unique_id': '1234_daily_consumption',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_energy_consumption_today-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'energy',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Envoy 1234 Energy consumption today',
|
||||
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.envoy_1234_energy_consumption_today',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_energy_production_last_seven_days-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
@@ -43897,6 +44077,67 @@
|
||||
'state': '4.321',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_lifetime_energy_consumption-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.envoy_1234_lifetime_energy_consumption',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Lifetime energy consumption',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 3,
|
||||
}),
|
||||
'sensor.private': dict({
|
||||
'suggested_unit_of_measurement': <UnitOfEnergy.MEGA_WATT_HOUR: 'MWh'>,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Lifetime energy consumption',
|
||||
'platform': 'enphase_envoy',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'lifetime_consumption',
|
||||
'unique_id': '1234_lifetime_consumption',
|
||||
'unit_of_measurement': <UnitOfEnergy.MEGA_WATT_HOUR: 'MWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_lifetime_energy_consumption-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'energy',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Envoy 1234 Lifetime energy consumption',
|
||||
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfEnergy.MEGA_WATT_HOUR: 'MWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.envoy_1234_lifetime_energy_consumption',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[envoy_tot_cons_metered][sensor.envoy_1234_lifetime_energy_production-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
|
||||
@@ -6,13 +6,14 @@ from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pyenphase import EnvoyData
|
||||
from pyenphase.const import PHASENAMES, PhaseNames
|
||||
from pyenphase.models.acb import ACBChargeStatus, EnvoyACB
|
||||
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.const import DOMAIN, Platform
|
||||
from homeassistant.components.enphase_envoy.coordinator import SCAN_INTERVAL
|
||||
from homeassistant.components.enphase_envoy.sensor import aggregate_acb_sleep_state
|
||||
from homeassistant.components.sensor import SensorStateClass
|
||||
@@ -23,8 +24,14 @@ from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.unit_conversion import TemperatureConverter
|
||||
|
||||
from . import setup_integration
|
||||
from .conftest import _load_json_2_meter_data, _load_json_2_production_data
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
async_fire_time_changed,
|
||||
load_json_object_fixture,
|
||||
snapshot_platform,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -1413,6 +1420,264 @@ async def test_sensor_missing_data(
|
||||
assert entity_state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
def reference_fixture(fixture: str) -> EnvoyData:
|
||||
"""Load reference fixture in envoy data model."""
|
||||
reference_data = EnvoyData()
|
||||
json_fixture: dict[str, Any] = load_json_object_fixture(f"{fixture}.json", DOMAIN)
|
||||
_load_json_2_production_data(reference_data, json_fixture)
|
||||
_load_json_2_meter_data(reference_data, json_fixture)
|
||||
return reference_data
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("mock_envoy", "ref_fixture"),
|
||||
[
|
||||
(
|
||||
"envoy_metered_batt_relay_none",
|
||||
"envoy_metered_batt_relay",
|
||||
)
|
||||
],
|
||||
indirect=["mock_envoy"],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_sensor_load_none_data(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_envoy: AsyncMock,
|
||||
ref_fixture: str,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test enphase_envoy sensor platform load None 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}"
|
||||
|
||||
# these have None data and should show up as unknown
|
||||
for entity in (
|
||||
"lifetime_energy_production",
|
||||
"lifetime_energy_consumption",
|
||||
"current_battery_discharge",
|
||||
"backfeed_ct_energy_delivered",
|
||||
"lifetime_energy_production_l1",
|
||||
"lifetime_energy_consumption_l1",
|
||||
"backfeed_ct_energy_delivered_l1",
|
||||
"current_battery_discharge_l1",
|
||||
):
|
||||
assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{entity}"))
|
||||
assert entity_state.state == STATE_UNKNOWN
|
||||
|
||||
# restore None data to operational state
|
||||
|
||||
reference_data = reference_fixture(ref_fixture)
|
||||
mock_envoy.data.system_production = reference_data.system_production
|
||||
mock_envoy.data.system_consumption = reference_data.system_consumption
|
||||
mock_envoy.data.ctmeters[CtType.BACKFEED] = reference_data.ctmeters[CtType.BACKFEED]
|
||||
mock_envoy.data.ctmeters[CtType.STORAGE] = reference_data.ctmeters[CtType.STORAGE]
|
||||
|
||||
mock_envoy.data.system_production_phases = reference_data.system_production_phases
|
||||
mock_envoy.data.system_consumption_phases = reference_data.system_consumption_phases
|
||||
mock_envoy.data.ctmeters_phases[CtType.BACKFEED] = reference_data.ctmeters_phases[
|
||||
CtType.BACKFEED
|
||||
]
|
||||
mock_envoy.data.ctmeters_phases[CtType.STORAGE][PhaseNames.PHASE_1] = (
|
||||
reference_data.ctmeters_phases[CtType.STORAGE][PhaseNames.PHASE_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 no longer be in unknown state
|
||||
for entity in (
|
||||
"lifetime_energy_production",
|
||||
"lifetime_energy_consumption",
|
||||
"current_battery_discharge",
|
||||
"backfeed_ct_energy_delivered",
|
||||
"lifetime_energy_production_l1",
|
||||
"lifetime_energy_consumption_l1",
|
||||
"backfeed_ct_energy_delivered_l1",
|
||||
"current_battery_discharge_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_sensor_none_data(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_envoy: AsyncMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test enphase_envoy sensor platform None 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}"
|
||||
|
||||
for entity in (
|
||||
"lifetime_energy_production",
|
||||
"lifetime_energy_consumption",
|
||||
"lifetime_balanced_net_energy_consumption",
|
||||
"backfeed_ct_energy_delivered",
|
||||
"lifetime_energy_production_l1",
|
||||
"lifetime_energy_consumption_l1",
|
||||
"lifetime_balanced_net_energy_consumption_l1",
|
||||
):
|
||||
assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{entity}"))
|
||||
|
||||
# force None data to test 'if == none' code sections
|
||||
mock_envoy.data.system_production = None
|
||||
mock_envoy.data.system_consumption = None
|
||||
mock_envoy.data.system_net_consumption = None
|
||||
mock_envoy.data.ctmeters[CtType.BACKFEED] = None
|
||||
|
||||
mock_envoy.data.system_production_phases = None
|
||||
mock_envoy.data.system_consumption_phases = None
|
||||
mock_envoy.data.system_net_consumption_phases = None
|
||||
|
||||
# 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",
|
||||
"lifetime_energy_consumption",
|
||||
"lifetime_balanced_net_energy_consumption",
|
||||
"backfeed_ct_energy_delivered",
|
||||
"lifetime_energy_production_l1",
|
||||
"lifetime_energy_consumption_l1",
|
||||
"lifetime_balanced_net_energy_consumption_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_sensor_phase_values_none_data(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_envoy: AsyncMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test enphase_envoy sensor platform phase None 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}"
|
||||
|
||||
for entity in (
|
||||
"lifetime_energy_production_l1",
|
||||
"lifetime_energy_consumption_l1",
|
||||
"lifetime_balanced_net_energy_consumption_l1",
|
||||
"backfeed_ct_energy_delivered_l1",
|
||||
):
|
||||
assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{entity}"))
|
||||
|
||||
# force None data to test 'if == none' code sections
|
||||
mock_envoy.data.system_production_phases[PhaseNames.PHASE_1] = None
|
||||
mock_envoy.data.system_consumption_phases[PhaseNames.PHASE_1] = None
|
||||
mock_envoy.data.system_net_consumption_phases[PhaseNames.PHASE_1] = None
|
||||
mock_envoy.data.ctmeters_phases[CtType.BACKFEED][PhaseNames.PHASE_1] = None
|
||||
|
||||
# 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_l1",
|
||||
"lifetime_energy_consumption_l1",
|
||||
"lifetime_balanced_net_energy_consumption_l1",
|
||||
"backfeed_ct_energy_delivered_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_sensor_phase_values_missing_data(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_envoy: AsyncMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test enphase_envoy sensor platform missing phase 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}"
|
||||
|
||||
for entity in (
|
||||
"lifetime_energy_production_l1",
|
||||
"lifetime_energy_consumption_l1",
|
||||
"lifetime_balanced_net_energy_consumption_l1",
|
||||
"backfeed_ct_energy_delivered_l1",
|
||||
):
|
||||
assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{entity}"))
|
||||
|
||||
# test handling of missing phase data
|
||||
del mock_envoy.data.system_production_phases[PhaseNames.PHASE_1]
|
||||
del mock_envoy.data.system_consumption_phases[PhaseNames.PHASE_1]
|
||||
del mock_envoy.data.system_net_consumption_phases[PhaseNames.PHASE_1]
|
||||
del mock_envoy.data.ctmeters_phases[CtType.BACKFEED][PhaseNames.PHASE_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_l1",
|
||||
"lifetime_energy_consumption_l1",
|
||||
"lifetime_balanced_net_energy_consumption_l1",
|
||||
"backfeed_ct_energy_delivered_l1",
|
||||
):
|
||||
assert (entity_state := hass.states.get(f"{ENTITY_BASE}_{entity}"))
|
||||
assert entity_state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("mock_envoy"),
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user