Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 22 additions & 57 deletions homeassistant/components/solaredge/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,70 +347,35 @@ async def async_update_data(self) -> None:
"""Update the data from the SolarEdge Monitoring API."""
now = dt_util.now()
start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0)
data = await self.api.get_storage_data(
self.site_id,
start_of_day,
now,
)
storage_data = data.get("storageData")
if storage_data is None:
raise UpdateFailed("Storage data not available from API")

batteries = storage_data.get("batteries")
if batteries is None:
raise UpdateFailed("Battery data not available from API")
try:
storage = await self.api.get_parsed_storage_data(
self.site_id,
start_of_day,
now,
)
except KeyError as ex:
raise UpdateFailed("Storage data not available from API") from ex

self.data = {}
self.attributes = {}

if not batteries:
if not storage.batteries:
LOGGER.debug("No batteries found in storage data")
return

# Aggregate totals across all batteries
total_charge_energy = 0.0
total_discharge_energy = 0.0

for battery in batteries:
serial = battery.get("serialNumber")
if not serial:
LOGGER.debug("Skipping battery without serialNumber")
continue

telemetries = battery.get("telemetries", [])

if not telemetries:
continue

latest = telemetries[-1]

# Per-battery current values
self.data[f"{serial}_state_of_charge"] = latest.get(
"batteryPercentageState"
)
self.data[f"{serial}_power"] = latest.get("power")

# Compute daily charge/discharge delta from lifetime counters
if len(telemetries) >= 2:
first = telemetries[0]
charge_energy = latest.get("lifeTimeEnergyCharged", 0.0) - first.get(
"lifeTimeEnergyCharged", 0.0
)
discharge_energy = latest.get(
"lifeTimeEnergyDischarged", 0.0
) - first.get("lifeTimeEnergyDischarged", 0.0)
else:
charge_energy = 0.0
discharge_energy = 0.0

total_charge_energy += charge_energy
total_discharge_energy += discharge_energy

self.data[f"{serial}_charge_energy"] = charge_energy
self.data[f"{serial}_discharge_energy"] = discharge_energy

self.data["charge_energy"] = total_charge_energy
self.data["discharge_energy"] = total_discharge_energy
# aiosolaredge derives the charged/discharged energy by integrating the
# power telemetry, because SolarEdge frequently leaves
# lifeTimeEnergyCharged/Discharged at 0 even while the battery is
# cycling. See issue #169964.
for battery in storage.batteries:
serial = battery.serial_number
self.data[f"{serial}_state_of_charge"] = battery.state_of_charge
self.data[f"{serial}_power"] = battery.power
self.data[f"{serial}_charge_energy"] = battery.charge_energy
self.data[f"{serial}_discharge_energy"] = battery.discharge_energy

self.data["charge_energy"] = storage.total_charge_energy
self.data["discharge_energy"] = storage.total_discharge_energy

LOGGER.debug("Updated SolarEdge storage data: %s", self.data)

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/solaredge/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
"integration_type": "device",
"iot_class": "cloud_polling",
"loggers": ["aiosolaredge", "solaredge_web"],
"requirements": ["aiosolaredge==1.0.2", "solaredge-web==0.0.1"]
"requirements": ["aiosolaredge==1.1.0", "solaredge-web==0.0.1"]
}
2 changes: 1 addition & 1 deletion requirements_all.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions tests/components/solaredge/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from collections.abc import Generator
from unittest.mock import AsyncMock, Mock, patch

from aiosolaredge import StorageData
import pytest

from homeassistant.components.solaredge.const import CONF_SITE_ID, DOMAIN
Expand Down Expand Up @@ -71,8 +72,10 @@ def mock_solaredge_api_fixture() -> Generator[Mock]:
api.get_energy_details = AsyncMock(
return_value=load_json_object_fixture("energy_details.json", DOMAIN)
)
api.get_storage_data = AsyncMock(
return_value=load_json_object_fixture("storage_data.json", DOMAIN)
api.get_parsed_storage_data = AsyncMock(
return_value=StorageData.from_response(
load_json_object_fixture("storage_data.json", DOMAIN)
)
)
with (
patch(
Expand Down
30 changes: 22 additions & 8 deletions tests/components/solaredge/fixtures/storage_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,31 @@
"telemetries": [
{
"timeStamp": "2025-01-01 00:00:00",
"lifeTimeEnergyCharged": 1000.0,
"lifeTimeEnergyDischarged": 500.0,
"lifeTimeEnergyCharged": 0,
"lifeTimeEnergyDischarged": 0,
"batteryPercentageState": 50.0,
"power": 100.0
"power": 0.0
},
{
"timeStamp": "2025-01-01 12:00:00",
"lifeTimeEnergyCharged": 1500.0,
"lifeTimeEnergyDischarged": 800.0,
"batteryPercentageState": 75.0,
"power": 200.0
"timeStamp": "2025-01-01 01:00:00",
"lifeTimeEnergyCharged": 0,
"lifeTimeEnergyDischarged": 0,
"batteryPercentageState": 80.0,
"power": 2000.0
},
{
"timeStamp": "2025-01-01 02:00:00",
"lifeTimeEnergyCharged": 0,
"lifeTimeEnergyDischarged": 0,
"batteryPercentageState": 90.0,
"power": 0.0
},
{
"timeStamp": "2025-01-01 03:00:00",
"lifeTimeEnergyCharged": 0,
"lifeTimeEnergyDischarged": 0,
"batteryPercentageState": 70.0,
"power": -1000.0
}
]
}
Expand Down
12 changes: 6 additions & 6 deletions tests/components/solaredge/snapshots/test_sensor.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '500.0',
'state': '2000.0',
})
# ---
# name: test_all_entities[sensor.battery_bat001_discharge_energy_today-entry]
Expand Down Expand Up @@ -112,7 +112,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '300.0',
'state': '500.0',
})
# ---
# name: test_all_entities[sensor.battery_bat001_power-entry]
Expand Down Expand Up @@ -170,7 +170,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '200.0',
'state': '-1000.0',
})
# ---
# name: test_all_entities[sensor.battery_bat001_state_of_charge-entry]
Expand Down Expand Up @@ -225,7 +225,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '75.0',
'state': '70.0',
})
# ---
# name: test_all_entities[sensor.solaredge_batteries-entry]
Expand Down Expand Up @@ -1462,7 +1462,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '500.0',
'state': '2000.0',
})
# ---
# name: test_all_entities[sensor.solaredge_storage_discharge_energy_today-entry]
Expand Down Expand Up @@ -1520,7 +1520,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '300.0',
'state': '500.0',
})
# ---
# name: test_all_entities[sensor.solaredge_storage_flow_direction-entry]
Expand Down
Loading
Loading