mirror of
https://github.com/home-assistant/core.git
synced 2025-06-25 01:21:51 +02:00
Add sensors for detailed Enphase inverter readings (#146916)
* Add extra details to Enphase inverters * Bump pyenphase version to 2.1.0 * Add new inverter sensors and translations * Add new endpoint * Start updating tests * Remove duplicate class * Add `max_reported` sensor * Move translation strings to correct location * Update fixtures and snapshots * Update unit tests * Fix linting * Apply suggestions from code review Co-authored-by: Arie Catsman <120491684+catsmanac@users.noreply.github.com> * Fix Telegram bot parsing of inline keyboard (#146376) * bug fix for inline keyboard * update inline keyboard test * Update tests/components/telegram_bot/test_telegram_bot.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * revert last_message_id and updated tests * removed TypeError test --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Handle the new JSON payload from traccar clients (#147254) * Set `entity_id` * Update unit tests * Bump aioamazondevices to 3.1.14 (#147257) * Bump pyseventeentrack to 1.1.1 (#147253) Update pyseventeentrack requirement to version 1.1.1 * Bump uiprotect to version 7.14.1 (#147280) * Fix `state_class`es for energy production * Make `max_reported` `name` more descriptive * Update snapshots * Reuse some translations * Remove unnecessary translation keys * Update unit tests * Update homeassistant/components/enphase_envoy/strings.json * Update homeassistant/components/enphase_envoy/strings.json * Fix --------- Co-authored-by: Arie Catsman <120491684+catsmanac@users.noreply.github.com> Co-authored-by: hanwg <han.wuguang@gmail.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com> Co-authored-by: Joakim Sørensen <joasoe@proton.me> Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com> Co-authored-by: Shai Ungar <shai.ungar@riskified.com> Co-authored-by: Raphael Hehl <7577984+RaHehl@users.noreply.github.com> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
@ -65,6 +65,7 @@ async def _get_fixture_collection(envoy: Envoy, serial: str) -> dict[str, Any]:
|
||||
"/ivp/ensemble/generator",
|
||||
"/ivp/meters",
|
||||
"/ivp/meters/readings",
|
||||
"/ivp/pdm/device_data",
|
||||
"/home",
|
||||
]
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pyenphase"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["pyenphase==2.0.1"],
|
||||
"requirements": ["pyenphase==2.1.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_enphase-envoy._tcp.local."
|
||||
|
@ -45,6 +45,7 @@ from homeassistant.const import (
|
||||
UnitOfFrequency,
|
||||
UnitOfPower,
|
||||
UnitOfTemperature,
|
||||
UnitOfTime,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@ -80,6 +81,114 @@ INVERTER_SENSORS = (
|
||||
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,
|
||||
|
@ -380,6 +380,33 @@
|
||||
},
|
||||
"aggregated_soc": {
|
||||
"name": "Aggregated battery soc"
|
||||
},
|
||||
"dc_voltage": {
|
||||
"name": "DC voltage"
|
||||
},
|
||||
"dc_current": {
|
||||
"name": "DC current"
|
||||
},
|
||||
"ac_voltage": {
|
||||
"name": "AC voltage"
|
||||
},
|
||||
"ac_current": {
|
||||
"name": "AC current"
|
||||
},
|
||||
"lifetime_energy": {
|
||||
"name": "Lifetime energy produced"
|
||||
},
|
||||
"energy_today": {
|
||||
"name": "Energy produced today"
|
||||
},
|
||||
"energy_produced": {
|
||||
"name": "Energy produced since previous report"
|
||||
},
|
||||
"max_reported": {
|
||||
"name": "Lifetime maximum power"
|
||||
},
|
||||
"last_report_duration": {
|
||||
"name": "Last report duration"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@ -1962,7 +1962,7 @@ pyeiscp==0.0.7
|
||||
pyemoncms==0.1.1
|
||||
|
||||
# homeassistant.components.enphase_envoy
|
||||
pyenphase==2.0.1
|
||||
pyenphase==2.1.0
|
||||
|
||||
# homeassistant.components.envisalink
|
||||
pyenvisalink==4.7
|
||||
|
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@ -1634,7 +1634,7 @@ pyeiscp==0.0.7
|
||||
pyemoncms==0.1.1
|
||||
|
||||
# homeassistant.components.enphase_envoy
|
||||
pyenphase==2.0.1
|
||||
pyenphase==2.1.0
|
||||
|
||||
# homeassistant.components.everlights
|
||||
pyeverlights==0.1.0
|
||||
|
@ -38,9 +38,19 @@
|
||||
"inverters": {
|
||||
"1": {
|
||||
"serial_number": "1",
|
||||
"last_report_date": 1,
|
||||
"last_report_watts": 1,
|
||||
"max_report_watts": 1
|
||||
"last_report_date": 1750460765,
|
||||
"last_report_watts": 116,
|
||||
"max_report_watts": 325,
|
||||
"dc_voltage": 33.793,
|
||||
"dc_current": 3.668,
|
||||
"ac_voltage": 243.438,
|
||||
"ac_current": 0.504,
|
||||
"ac_frequency": 50.01,
|
||||
"temperature": 23,
|
||||
"energy_produced": 32.254,
|
||||
"energy_today": 134,
|
||||
"lifetime_energy": 130209,
|
||||
"last_report_duration": 903
|
||||
}
|
||||
},
|
||||
"tariff": null,
|
||||
|
@ -78,7 +78,17 @@
|
||||
"serial_number": "1",
|
||||
"last_report_date": 1,
|
||||
"last_report_watts": 1,
|
||||
"max_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": {
|
||||
|
@ -220,7 +220,17 @@
|
||||
"serial_number": "1",
|
||||
"last_report_date": 1,
|
||||
"last_report_watts": 1,
|
||||
"max_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": {
|
||||
|
@ -208,7 +208,17 @@
|
||||
"serial_number": "1",
|
||||
"last_report_date": 1,
|
||||
"last_report_watts": 1,
|
||||
"max_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": {
|
||||
|
@ -412,7 +412,17 @@
|
||||
"serial_number": "1",
|
||||
"last_report_date": 1,
|
||||
"last_report_watts": 1,
|
||||
"max_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": {
|
||||
|
@ -227,7 +227,17 @@
|
||||
"serial_number": "1",
|
||||
"last_report_date": 1,
|
||||
"last_report_watts": 1,
|
||||
"max_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": {
|
||||
|
@ -73,7 +73,17 @@
|
||||
"serial_number": "1",
|
||||
"last_report_date": 1,
|
||||
"last_report_watts": 1,
|
||||
"max_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": {
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -54,7 +54,7 @@ async def test_with_pre_v7_firmware(
|
||||
await setup_integration(hass, config_entry)
|
||||
|
||||
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||
assert entity_state.state == "1"
|
||||
assert entity_state.state == "116"
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2024-07-23 00:00:00+00:00")
|
||||
@ -85,7 +85,7 @@ async def test_token_in_config_file(
|
||||
await setup_integration(hass, entry)
|
||||
|
||||
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||
assert entity_state.state == "1"
|
||||
assert entity_state.state == "116"
|
||||
|
||||
|
||||
@respx.mock
|
||||
@ -128,7 +128,7 @@ async def test_expired_token_in_config(
|
||||
await setup_integration(hass, entry)
|
||||
|
||||
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||
assert entity_state.state == "1"
|
||||
assert entity_state.state == "116"
|
||||
|
||||
|
||||
async def test_coordinator_update_error(
|
||||
@ -226,7 +226,7 @@ async def test_coordinator_token_refresh_error(
|
||||
await setup_integration(hass, entry)
|
||||
|
||||
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||
assert entity_state.state == "1"
|
||||
assert entity_state.state == "116"
|
||||
|
||||
|
||||
async def test_config_no_unique_id(
|
||||
|
@ -772,6 +772,70 @@ async def test_sensor_inverter_data(
|
||||
) == dt_util.utc_from_timestamp(inverter.last_report_date)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("mock_envoy"),
|
||||
[
|
||||
"envoy",
|
||||
],
|
||||
indirect=["mock_envoy"],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_sensor_inverter_detailed_data(
|
||||
hass: HomeAssistant,
|
||||
mock_envoy: AsyncMock,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test enphase_envoy inverter detailed entities values."""
|
||||
with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
|
||||
await setup_integration(hass, config_entry)
|
||||
|
||||
entity_base = f"{Platform.SENSOR}.inverter"
|
||||
|
||||
for sn, inverter in mock_envoy.data.inverters.items():
|
||||
assert (dc_voltage := hass.states.get(f"{entity_base}_{sn}_dc_voltage"))
|
||||
assert float(dc_voltage.state) == (inverter.dc_voltage)
|
||||
assert (dc_current := hass.states.get(f"{entity_base}_{sn}_dc_current"))
|
||||
assert float(dc_current.state) == (inverter.dc_current)
|
||||
assert (ac_voltage := hass.states.get(f"{entity_base}_{sn}_ac_voltage"))
|
||||
assert float(ac_voltage.state) == (inverter.ac_voltage)
|
||||
assert (ac_current := hass.states.get(f"{entity_base}_{sn}_ac_current"))
|
||||
assert float(ac_current.state) == (inverter.ac_current)
|
||||
assert (frequency := hass.states.get(f"{entity_base}_{sn}_frequency"))
|
||||
assert float(frequency.state) == (inverter.ac_frequency)
|
||||
assert (temperature := hass.states.get(f"{entity_base}_{sn}_temperature"))
|
||||
assert int(temperature.state) == (inverter.temperature)
|
||||
assert (
|
||||
lifetime_energy := hass.states.get(
|
||||
f"{entity_base}_{sn}_lifetime_energy_produced"
|
||||
)
|
||||
)
|
||||
assert float(lifetime_energy.state) == (inverter.lifetime_energy / 1000.0)
|
||||
assert (
|
||||
energy_produced_today := hass.states.get(
|
||||
f"{entity_base}_{sn}_energy_produced_today"
|
||||
)
|
||||
)
|
||||
assert int(energy_produced_today.state) == (inverter.energy_today)
|
||||
assert (
|
||||
last_report_duration := hass.states.get(
|
||||
f"{entity_base}_{sn}_last_report_duration"
|
||||
)
|
||||
)
|
||||
assert int(last_report_duration.state) == (inverter.last_report_duration)
|
||||
assert (
|
||||
energy_produced := hass.states.get(
|
||||
f"{entity_base}_{sn}_energy_produced_since_previous_report"
|
||||
)
|
||||
)
|
||||
assert float(energy_produced.state) == (inverter.energy_produced)
|
||||
assert (
|
||||
lifetime_maximum_power := hass.states.get(
|
||||
f"{entity_base}_{sn}_lifetime_maximum_power"
|
||||
)
|
||||
)
|
||||
assert int(lifetime_maximum_power.state) == (inverter.max_report_watts)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("mock_envoy"),
|
||||
[
|
||||
@ -797,9 +861,23 @@ async def test_sensor_inverter_disabled_by_integration(
|
||||
INVERTER_BASE = f"{Platform.SENSOR}.inverter"
|
||||
|
||||
assert all(
|
||||
f"{INVERTER_BASE}_{sn}_last_reported"
|
||||
f"{INVERTER_BASE}_{sn}_{key}"
|
||||
in integration_disabled_entities(entity_registry, config_entry)
|
||||
for sn in mock_envoy.data.inverters
|
||||
for key in (
|
||||
"dc_voltage",
|
||||
"dc_current",
|
||||
"ac_voltage",
|
||||
"ac_current",
|
||||
"frequency",
|
||||
"temperature",
|
||||
"lifetime_energy_produced",
|
||||
"energy_produced_today",
|
||||
"last_report_duration",
|
||||
"energy_produced_since_previous_report",
|
||||
"last_reported",
|
||||
"lifetime_maximum_power",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user