Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert solaredge to asyncio with aiosolaredge #115599

Merged
merged 3 commits into from
Apr 23, 2024
Merged
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
4 changes: 2 additions & 2 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -1275,8 +1275,8 @@ build.json @home-assistant/supervisor
/tests/components/snmp/ @nmaggioni
/homeassistant/components/snooz/ @AustinBrunkhorst
/tests/components/snooz/ @AustinBrunkhorst
/homeassistant/components/solaredge/ @frenck
/tests/components/solaredge/ @frenck
/homeassistant/components/solaredge/ @frenck @bdraco
/tests/components/solaredge/ @frenck @bdraco
/homeassistant/components/solaredge_local/ @drobtravels @scheric
/homeassistant/components/solarlog/ @Ernst79
/tests/components/solarlog/ @Ernst79
Expand Down
14 changes: 7 additions & 7 deletions homeassistant/components/solaredge/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

import socket

from requests.exceptions import ConnectTimeout, HTTPError
from solaredge import Solaredge
from aiohttp import ClientError
from aiosolaredge import SolarEdge

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv

from .const import CONF_SITE_ID, DATA_API_CLIENT, DOMAIN, LOGGER
Expand All @@ -22,13 +23,12 @@

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up SolarEdge from a config entry."""
api = Solaredge(entry.data[CONF_API_KEY])
session = async_get_clientsession(hass)
api = SolarEdge(entry.data[CONF_API_KEY], session)

try:
response = await hass.async_add_executor_job(
api.get_details, entry.data[CONF_SITE_ID]
)
except (ConnectTimeout, HTTPError, socket.gaierror) as ex:
response = await api.get_details(entry.data[CONF_SITE_ID])
except (TimeoutError, ClientError, socket.gaierror) as ex:
LOGGER.error("Could not retrieve details from SolarEdge API")
raise ConfigEntryNotReady from ex

Expand Down
19 changes: 10 additions & 9 deletions homeassistant/components/solaredge/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

from __future__ import annotations

import socket
from typing import Any

from requests.exceptions import ConnectTimeout, HTTPError
import solaredge
from aiohttp import ClientError
import aiosolaredge
import voluptuous as vol

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_KEY, CONF_NAME
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util import slugify

from .const import CONF_SITE_ID, DEFAULT_NAME, DOMAIN
Expand Down Expand Up @@ -38,15 +40,16 @@ def _site_in_configuration_exists(self, site_id: str) -> bool:
"""Return True if site_id exists in configuration."""
return site_id in self._async_current_site_ids()

def _check_site(self, site_id: str, api_key: str) -> bool:
async def _async_check_site(self, site_id: str, api_key: str) -> bool:
"""Check if we can connect to the soleredge api service."""
api = solaredge.Solaredge(api_key)
session = async_get_clientsession(self.hass)
api = aiosolaredge.SolarEdge(api_key, session)
try:
response = api.get_details(site_id)
response = await api.get_details(site_id)
if response["details"]["status"].lower() != "active":
self._errors[CONF_SITE_ID] = "site_not_active"
return False
except (ConnectTimeout, HTTPError):
except (TimeoutError, ClientError, socket.gaierror):
self._errors[CONF_SITE_ID] = "could_not_connect"
return False
except KeyError:
Expand All @@ -66,9 +69,7 @@ async def async_step_user(
else:
site = user_input[CONF_SITE_ID]
api = user_input[CONF_API_KEY]
can_connect = await self.hass.async_add_executor_job(
self._check_site, site, api
)
can_connect = await self._async_check_site(site, api)
if can_connect:
return self.async_create_entry(
title=name, data={CONF_SITE_ID: site, CONF_API_KEY: api}
Expand Down
35 changes: 15 additions & 20 deletions homeassistant/components/solaredge/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from datetime import date, datetime, timedelta
from typing import Any

from solaredge import Solaredge
from aiosolaredge import SolarEdge
from stringcase import snakecase

from homeassistant.core import HomeAssistant, callback
Expand All @@ -27,7 +27,7 @@ class SolarEdgeDataService(ABC):

coordinator: DataUpdateCoordinator[None]

def __init__(self, hass: HomeAssistant, api: Solaredge, site_id: str) -> None:
def __init__(self, hass: HomeAssistant, api: SolarEdge, site_id: str) -> None:
"""Initialize the data object."""
self.api = api
self.site_id = site_id
Expand All @@ -54,12 +54,8 @@ def update_interval(self) -> timedelta:
"""Update interval."""

@abstractmethod
def update(self) -> None:
"""Update data in executor."""

async def async_update_data(self) -> None:
"""Update data."""
await self.hass.async_add_executor_job(self.update)


class SolarEdgeOverviewDataService(SolarEdgeDataService):
Expand All @@ -70,10 +66,10 @@ def update_interval(self) -> timedelta:
"""Update interval."""
return OVERVIEW_UPDATE_DELAY

def update(self) -> None:
async def async_update_data(self) -> None:
"""Update the data from the SolarEdge Monitoring API."""
try:
data = self.api.get_overview(self.site_id)
data = await self.api.get_overview(self.site_id)
overview = data["overview"]
except KeyError as ex:
raise UpdateFailed("Missing overview data, skipping update") from ex
Expand Down Expand Up @@ -113,11 +109,11 @@ def update_interval(self) -> timedelta:
"""Update interval."""
return DETAILS_UPDATE_DELAY

def update(self) -> None:
async def async_update_data(self) -> None:
"""Update the data from the SolarEdge Monitoring API."""

try:
data = self.api.get_details(self.site_id)
data = await self.api.get_details(self.site_id)
details = data["details"]
except KeyError as ex:
raise UpdateFailed("Missing details data, skipping update") from ex
Expand Down Expand Up @@ -157,10 +153,10 @@ def update_interval(self) -> timedelta:
"""Update interval."""
return INVENTORY_UPDATE_DELAY

def update(self) -> None:
async def async_update_data(self) -> None:
"""Update the data from the SolarEdge Monitoring API."""
try:
data = self.api.get_inventory(self.site_id)
data = await self.api.get_inventory(self.site_id)
inventory = data["Inventory"]
except KeyError as ex:
raise UpdateFailed("Missing inventory data, skipping update") from ex
Expand All @@ -178,7 +174,7 @@ def update(self) -> None:
class SolarEdgeEnergyDetailsService(SolarEdgeDataService):
"""Get and update the latest power flow data."""

def __init__(self, hass: HomeAssistant, api: Solaredge, site_id: str) -> None:
def __init__(self, hass: HomeAssistant, api: SolarEdge, site_id: str) -> None:
"""Initialize the power flow data service."""
super().__init__(hass, api, site_id)

Expand All @@ -189,17 +185,16 @@ def update_interval(self) -> timedelta:
"""Update interval."""
return ENERGY_DETAILS_DELAY

def update(self) -> None:
async def async_update_data(self) -> None:
"""Update the data from the SolarEdge Monitoring API."""
try:
now = datetime.now()
today = date.today()
midnight = datetime.combine(today, datetime.min.time())
data = self.api.get_energy_details(
data = await self.api.get_energy_details(
self.site_id,
midnight,
now.strftime("%Y-%m-%d %H:%M:%S"),
meters=None,
now,
time_unit="DAY",
)
energy_details = data["energyDetails"]
Expand Down Expand Up @@ -239,7 +234,7 @@ def update(self) -> None:
class SolarEdgePowerFlowDataService(SolarEdgeDataService):
"""Get and update the latest power flow data."""

def __init__(self, hass: HomeAssistant, api: Solaredge, site_id: str) -> None:
def __init__(self, hass: HomeAssistant, api: SolarEdge, site_id: str) -> None:
"""Initialize the power flow data service."""
super().__init__(hass, api, site_id)

Expand All @@ -250,10 +245,10 @@ def update_interval(self) -> timedelta:
"""Update interval."""
return POWER_FLOW_UPDATE_DELAY

def update(self) -> None:
async def async_update_data(self) -> None:
"""Update the data from the SolarEdge Monitoring API."""
try:
data = self.api.get_current_power_flow(self.site_id)
data = await self.api.get_current_power_flow(self.site_id)
power_flow = data["siteCurrentPowerFlow"]
except KeyError as ex:
raise UpdateFailed("Missing power flow data, skipping update") from ex
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/solaredge/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"domain": "solaredge",
"name": "SolarEdge",
"codeowners": ["@frenck"],
"codeowners": ["@frenck", "@bdraco"],
"config_flow": true,
"dhcp": [
{
Expand All @@ -12,6 +12,6 @@
"documentation": "https://www.home-assistant.io/integrations/solaredge",
"integration_type": "device",
"iot_class": "cloud_polling",
"loggers": ["solaredge"],
"requirements": ["solaredge==0.0.2", "stringcase==1.2.0"]
"loggers": ["aiosolaredge"],
"requirements": ["aiosolaredge==0.2.0", "stringcase==1.2.0"]
}
6 changes: 3 additions & 3 deletions homeassistant/components/solaredge/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from dataclasses import dataclass
from typing import Any

from solaredge import Solaredge
from aiosolaredge import SolarEdge

from homeassistant.components.sensor import (
SensorDeviceClass,
Expand Down Expand Up @@ -205,7 +205,7 @@ async def async_setup_entry(
) -> None:
"""Add an solarEdge entry."""
# Add the needed sensors to hass
api: Solaredge = hass.data[DOMAIN][entry.entry_id][DATA_API_CLIENT]
api: SolarEdge = hass.data[DOMAIN][entry.entry_id][DATA_API_CLIENT]

sensor_factory = SolarEdgeSensorFactory(hass, entry.data[CONF_SITE_ID], api)
for service in sensor_factory.all_services:
Expand All @@ -223,7 +223,7 @@ async def async_setup_entry(
class SolarEdgeSensorFactory:
"""Factory which creates sensors based on the sensor_key."""

def __init__(self, hass: HomeAssistant, site_id: str, api: Solaredge) -> None:
def __init__(self, hass: HomeAssistant, site_id: str, api: SolarEdge) -> None:
"""Initialize the factory."""

details = SolarEdgeDetailsDataService(hass, api, site_id)
Expand Down
6 changes: 3 additions & 3 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,9 @@ aioskybell==22.7.0
# homeassistant.components.slimproto
aioslimproto==3.0.0

# homeassistant.components.solaredge
aiosolaredge==0.2.0

# homeassistant.components.steamist
aiosteamist==0.3.2

Expand Down Expand Up @@ -2577,9 +2580,6 @@ soco==0.30.2
# homeassistant.components.solaredge_local
solaredge-local==0.2.3

# homeassistant.components.solaredge
solaredge==0.0.2

# homeassistant.components.solax
solax==3.1.0

Expand Down
6 changes: 3 additions & 3 deletions requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,9 @@ aioskybell==22.7.0
# homeassistant.components.slimproto
aioslimproto==3.0.0

# homeassistant.components.solaredge
aiosolaredge==0.2.0

# homeassistant.components.steamist
aiosteamist==0.3.2

Expand Down Expand Up @@ -1984,9 +1987,6 @@ snapcast==2.3.6
# homeassistant.components.sonos
soco==0.30.2

# homeassistant.components.solaredge
solaredge==0.0.2

# homeassistant.components.solax
solax==3.1.0

Expand Down
15 changes: 9 additions & 6 deletions tests/components/solaredge/test_config_flow.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Tests for the SolarEdge config flow."""

from unittest.mock import Mock, patch
from unittest.mock import AsyncMock, Mock, patch

from aiohttp import ClientError
import pytest
from requests.exceptions import ConnectTimeout, HTTPError

from homeassistant.components.solaredge.const import CONF_SITE_ID, DEFAULT_NAME, DOMAIN
from homeassistant.config_entries import SOURCE_IGNORE, SOURCE_USER
Expand All @@ -22,8 +22,11 @@
def mock_controller():
"""Mock a successful Solaredge API."""
api = Mock()
api.get_details.return_value = {"details": {"status": "active"}}
with patch("solaredge.Solaredge", return_value=api):
api.get_details = AsyncMock(return_value={"details": {"status": "active"}})
with patch(
"homeassistant.components.solaredge.config_flow.aiosolaredge.SolarEdge",
return_value=api,
):
yield api


Expand Down Expand Up @@ -117,7 +120,7 @@ async def test_asserts(hass: HomeAssistant, test_api: Mock) -> None:
assert result.get("errors") == {CONF_SITE_ID: "invalid_api_key"}

# test with ConnectionTimeout
test_api.get_details.side_effect = ConnectTimeout()
test_api.get_details = AsyncMock(side_effect=TimeoutError())
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
Expand All @@ -127,7 +130,7 @@ async def test_asserts(hass: HomeAssistant, test_api: Mock) -> None:
assert result.get("errors") == {CONF_SITE_ID: "could_not_connect"}

# test with HTTPError
test_api.get_details.side_effect = HTTPError()
test_api.get_details = AsyncMock(side_effect=ClientError())
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
Expand Down
Loading