Skip to content

Commit

Permalink
Add balanced grid import/export to enphase_envoy (#123154)
Browse files Browse the repository at this point in the history
* Add balanced grid import/export to enphase_envoy

* rebuild sensor snapshot after dev merge

* Cleanup snapshot file
  • Loading branch information
catsmanac authored Sep 8, 2024
1 parent c0492d4 commit 74b7830
Show file tree
Hide file tree
Showing 10 changed files with 1,678 additions and 255 deletions.
91 changes: 91 additions & 0 deletions homeassistant/components/enphase_envoy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,50 @@ class EnvoyConsumptionSensorEntityDescription(SensorEntityDescription):
}


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."""
Expand Down Expand Up @@ -697,6 +741,11 @@ async def async_setup_entry(
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(
Expand All @@ -713,6 +762,14 @@ async def async_setup_entry(
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 net consumption CT entities
if ctmeter := envoy_data.ctmeter_consumption:
entities.extend(
Expand Down Expand Up @@ -846,6 +903,19 @@ def native_value(self) -> int | 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."""

Expand Down Expand Up @@ -888,6 +958,27 @@ def native_value(self) -> int | 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 EnvoyConsumptionCTEntity(EnvoySystemSensorEntity):
"""Envoy net consumption CT entity."""

Expand Down
12 changes: 12 additions & 0 deletions homeassistant/components/enphase_envoy/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,18 @@
"lifetime_consumption_phase": {
"name": "Lifetime energy consumption {phase_name}"
},
"balanced_net_consumption": {
"name": "balanced net power consumption"
},
"lifetime_balanced_net_consumption": {
"name": "Lifetime balanced net energy consumption"
},
"balanced_net_consumption_phase": {
"name": "balanced net power consumption {phase_name}"
},
"lifetime_balanced_net_consumption_phase": {
"name": "Lifetime balanced net energy consumption {phase_name}"
},
"lifetime_net_consumption": {
"name": "Lifetime net energy consumption"
},
Expand Down
8 changes: 8 additions & 0 deletions tests/components/enphase_envoy/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ def _load_json_2_production_data(
"""Fill envoy production data from fixture."""
if item := json_fixture["data"].get("system_consumption"):
mocked_data.system_consumption = EnvoySystemConsumption(**item)
if item := json_fixture["data"].get("system_net_consumption"):
mocked_data.system_net_consumption = EnvoySystemConsumption(**item)
if item := json_fixture["data"].get("system_production"):
mocked_data.system_production = EnvoySystemProduction(**item)
if item := json_fixture["data"].get("system_consumption_phases"):
Expand All @@ -158,6 +160,12 @@ def _load_json_2_production_data(
mocked_data.system_consumption_phases[sub_item] = EnvoySystemConsumption(
**item_data
)
if item := json_fixture["data"].get("system_net_consumption_phases"):
mocked_data.system_net_consumption_phases = {}
for sub_item, item_data in item.items():
mocked_data.system_net_consumption_phases[sub_item] = (
EnvoySystemConsumption(**item_data)
)
if item := json_fixture["data"].get("system_production_phases"):
mocked_data.system_production_phases = {}
for sub_item, item_data in item.items():
Expand Down
2 changes: 2 additions & 0 deletions tests/components/enphase_envoy/fixtures/envoy.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
"encharge_aggregate": null,
"enpower": null,
"system_consumption": null,
"system_net_consumption": null,
"system_production": {
"watt_hours_lifetime": 1234,
"watt_hours_last_7_days": 1234,
"watt_hours_today": 1234,
"watts_now": 1234
},
"system_consumption_phases": null,
"system_net_consumption_phases": null,
"system_production_phases": null,
"ctmeter_production": null,
"ctmeter_consumption": null,
Expand Down
7 changes: 7 additions & 0 deletions tests/components/enphase_envoy/fixtures/envoy_1p_metered.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@
"watt_hours_today": 1234,
"watts_now": 1234
},
"system_net_consumption": {
"watt_hours_lifetime": 4321,
"watt_hours_last_7_days": -1,
"watt_hours_today": -1,
"watts_now": 2341
},
"system_production": {
"watt_hours_lifetime": 1234,
"watt_hours_last_7_days": 1234,
"watt_hours_today": 1234,
"watts_now": 1234
},
"system_consumption_phases": null,
"system_net_consumption_phases": null,
"system_production_phases": null,
"ctmeter_production": {
"eid": "100000010",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@
"watt_hours_today": 1234,
"watts_now": 1234
},
"system_net_consumption": {
"watt_hours_lifetime": 4321,
"watt_hours_last_7_days": -1,
"watt_hours_today": -1,
"watts_now": 2341
},
"system_production": {
"watt_hours_lifetime": 1234,
"watt_hours_last_7_days": 1234,
Expand All @@ -105,6 +111,26 @@
"watts_now": 3324
}
},
"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": {
"watt_hours_lifetime": 1232,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
"watt_hours_today": 1234,
"watts_now": 1234
},
"system_net_consumption": {
"watt_hours_lifetime": 4321,
"watt_hours_last_7_days": -1,
"watt_hours_today": -1,
"watts_now": 2341
},
"system_production": {
"watt_hours_lifetime": 1234,
"watt_hours_last_7_days": 1234,
Expand All @@ -48,6 +54,26 @@
"watts_now": 3324
}
},
"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": {
"watt_hours_lifetime": 1232,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,20 @@
"encharge_aggregate": null,
"enpower": null,
"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": {
"watt_hours_lifetime": 1234,
"watt_hours_last_7_days": 1234,
"watt_hours_today": 1234,
"watts_now": 1234
},
"system_consumption_phases": null,
"system_net_consumption_phases": null,
"system_production_phases": null,
"ctmeter_production": {
"eid": "100000010",
Expand Down
Loading

0 comments on commit 74b7830

Please sign in to comment.