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

Add solax inverter model selector in config_flow to bypass discovery #115129

Closed
Closed
Show file tree
Hide file tree
Changes from 5 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
31 changes: 28 additions & 3 deletions homeassistant/components/solax/__init__.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,51 @@
"""The solax component."""

from solax import real_time_api
import asyncio
from importlib.metadata import entry_points
import logging

import solax
from solax import RealTimeAPI

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady

from .const import DOMAIN
from .const import CONF_SOLAX_INVERTER, DOMAIN, SOLAX_ENTRY_POINT_GROUP

_LOGGER = logging.getLogger(__name__)

PLATFORMS = [Platform.SENSOR]

INVERTERS_ENTRY_POINTS = {
ep.name: ep.load() for ep in entry_points(group=SOLAX_ENTRY_POINT_GROUP)
}


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up the sensors from a ConfigEntry."""

invset = set()
if (CONF_SOLAX_INVERTER in entry.data) and entry.data.get(
CONF_SOLAX_INVERTER
) is not None:
invset.add(INVERTERS_ENTRY_POINTS.get(entry.data[CONF_SOLAX_INVERTER]))
else:
for ep in INVERTERS_ENTRY_POINTS.values():
invset.add(ep)

_LOGGER.debug("solax inverter set %s", invset)

try:
api = await real_time_api(
inverter = await solax.discover(
entry.data[CONF_IP_ADDRESS],
entry.data[CONF_PORT],
entry.data[CONF_PASSWORD],
inverters=invset,
frenck marked this conversation as resolved.
Show resolved Hide resolved
return_when=asyncio.FIRST_COMPLETED,
)
api = RealTimeAPI(inverter)
await api.get_data()
except Exception as err:
raise ConfigEntryNotReady from err
Expand Down
44 changes: 38 additions & 6 deletions homeassistant/components/solax/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,70 @@

from __future__ import annotations

import asyncio
from importlib.metadata import entry_points
import logging
from typing import Any

from solax import real_time_api
from solax import discover
from solax.discovery import DiscoveryError
import voluptuous as vol

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT
from homeassistant.helpers import selector
import homeassistant.helpers.config_validation as cv

from .const import DOMAIN
from .const import CONF_SOLAX_INVERTER, DOMAIN, SOLAX_ENTRY_POINT_GROUP

_LOGGER = logging.getLogger(__name__)

DEFAULT_PORT = 80
DEFAULT_PASSWORD = ""
DEFAULT_INVERTER = ""

INVERTERS_ENTRY_POINTS = {
ep.name: ep.load() for ep in entry_points(group=SOLAX_ENTRY_POINT_GROUP)
}

STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_IP_ADDRESS): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_PASSWORD, default=DEFAULT_PASSWORD): cv.string,
vol.Optional(CONF_SOLAX_INVERTER): selector.SelectSelector(
selector.SelectSelectorConfig(
options=list(INVERTERS_ENTRY_POINTS.keys()),
mode=selector.SelectSelectorMode.DROPDOWN,
multiple=False,
)
),
}
)


async def validate_api(data) -> str:
"""Validate the credentials."""

api = await real_time_api(
data[CONF_IP_ADDRESS], data[CONF_PORT], data[CONF_PASSWORD]
_LOGGER.debug("CONF_SOLAX_INVERTER entry: %s", data.get(CONF_SOLAX_INVERTER))

invset = set()
if CONF_SOLAX_INVERTER in data:
invset.add(INVERTERS_ENTRY_POINTS.get(data.get(CONF_SOLAX_INVERTER)))
else:
for ep in INVERTERS_ENTRY_POINTS.values():
invset.add(ep)

inverter = await discover(
data[CONF_IP_ADDRESS],
data[CONF_PORT],
data[CONF_PASSWORD],
inverters=invset,
return_when=asyncio.FIRST_COMPLETED,
)
response = await api.get_data()

response = await inverter.get_data()
_LOGGER.debug("Solax serial number %s", response.serial_number)
return response.serial_number


Expand All @@ -54,8 +84,10 @@ async def async_step_user(

try:
serial_number = await validate_api(user_input)
except (ConnectionError, DiscoveryError):
except ConnectionError:
errors["base"] = "cannot_connect"
except DiscoveryError:
errors["base"] = "inverter discovery error"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/solax/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
DOMAIN = "solax"

MANUFACTURER = "SolaX Power"

CONF_SOLAX_INVERTER = "solax.inverters"

SOLAX_ENTRY_POINT_GROUP = "solax.inverter"
37 changes: 25 additions & 12 deletions tests/components/solax/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

from unittest.mock import patch

from solax import RealTimeAPI
from solax.inverter import InverterResponse
from solax.inverters import X1MiniV34

from homeassistant import config_entries
from homeassistant.components.solax.const import DOMAIN
from homeassistant.components.solax.const import CONF_SOLAX_INVERTER, DOMAIN
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType


def __mock_real_time_api_success():
return RealTimeAPI(X1MiniV34)
def __mock_discover_success():
return X1MiniV34


def __mock_get_data():
Expand All @@ -37,18 +36,23 @@ async def test_form_success(hass: HomeAssistant) -> None:

with (
patch(
"homeassistant.components.solax.config_flow.real_time_api",
return_value=__mock_real_time_api_success(),
"homeassistant.components.solax.config_flow.discover",
return_value=__mock_discover_success(),
),
patch("solax.RealTimeAPI.get_data", return_value=__mock_get_data()),
patch("solax.Inverter.get_data", return_value=__mock_get_data()),
patch(
"homeassistant.components.solax.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
entry_result = await hass.config_entries.flow.async_configure(
flow["flow_id"],
{CONF_IP_ADDRESS: "192.168.1.87", CONF_PORT: 80, CONF_PASSWORD: "password"},
{
CONF_IP_ADDRESS: "192.168.1.87",
CONF_PORT: 80,
CONF_PASSWORD: "password",
CONF_SOLAX_INVERTER: "x1_mini_v34",
},
)
await hass.async_block_till_done()

Expand All @@ -58,6 +62,7 @@ async def test_form_success(hass: HomeAssistant) -> None:
CONF_IP_ADDRESS: "192.168.1.87",
CONF_PORT: 80,
CONF_PASSWORD: "password",
CONF_SOLAX_INVERTER: "x1_mini_v34",
}
assert len(mock_setup_entry.mock_calls) == 1

Expand All @@ -71,12 +76,16 @@ async def test_form_connect_error(hass: HomeAssistant) -> None:
assert flow["errors"] == {}

with patch(
"homeassistant.components.solax.config_flow.real_time_api",
"homeassistant.components.solax.config_flow.discover",
side_effect=ConnectionError,
):
entry_result = await hass.config_entries.flow.async_configure(
flow["flow_id"],
{CONF_IP_ADDRESS: "192.168.1.87", CONF_PORT: 80, CONF_PASSWORD: "password"},
{
CONF_IP_ADDRESS: "192.168.1.87",
CONF_PORT: 80,
CONF_PASSWORD: "password",
},
)

assert entry_result["type"] is FlowResultType.FORM
Expand All @@ -92,12 +101,16 @@ async def test_form_unknown_error(hass: HomeAssistant) -> None:
assert flow["errors"] == {}

with patch(
"homeassistant.components.solax.config_flow.real_time_api",
"homeassistant.components.solax.config_flow.discover",
side_effect=Exception,
):
entry_result = await hass.config_entries.flow.async_configure(
flow["flow_id"],
{CONF_IP_ADDRESS: "192.168.1.87", CONF_PORT: 80, CONF_PASSWORD: "password"},
{
CONF_IP_ADDRESS: "192.168.1.87",
CONF_PORT: 80,
CONF_PASSWORD: "password",
},
)

assert entry_result["type"] is FlowResultType.FORM
Expand Down