1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 21:06:19 +00:00
Files
core/homeassistant/components/imeon_inverter/config_flow.py
Imeon-Energy b51bb668c6 Add imeon inverter integration (#130958)
* Initial commit prototype with empty inverters

* Use modern methods and global variable for character strings

* Platform that get the value of the meter in an entity

* Add check if inverter already configured

* Add tests for config_flow

* Update "imeon_inverter_api" in manifest.json

* Update "imeon_inverter_api" in requirements_all.txt

* Remove async_setup, clean comments, use of const PLATFORM

* Use of global variable and remove configuration of device name

* Use of entry.data instead of user_input variable

* Remove services.yaml

* No quality scale

* Use of common string

* Add sensors, use of EntityDescription and '_attr_device_info'

* Remove name from config_flow tests

* Use sentence case and change integration from hub to device

* Check connection before add platform in config_flow

* Use of _async_setup and minor changes

* Improve sensor description

* Add quality_scale.yaml

* Update the quality_scale.json

* Add tests for host invalid, route invalid, exception and invalid auth

* Type more precisely 'DataUpdateCoordinator'

* Don't use 'self.data' directly in coordinator and minor corrections

* Complete full quality_scale.yaml

* Use of fixtures in the tests

* Add snapshot tests for sensors

* Refactor the try except and use serial as unique id

* Change API version

* Add test for sensor

* Mock the api to generate the snapshot

* New type for async_add_entries

* Except timeout error for get_serial

* Add test for get_serial timeout error

* Move store data out of the try

* Use sentence case

* Use of fixtures

* Use separates fixtures

* Mock the api

* Put sensors fake data in json fixture file

* Use of a const interval, remove except timeout, enhance lisibility

* Try to use same fixture in test_config_flow

* Try use same fixture for all mock of inverter

* Modify the fixture in the context manager, correct the tests

* Fixture return mock.__aenter__ directly

* Adjust code clarity

* Bring all tests to either ABORT or CREATE_ENTRY

* Make the try except more concise

* Synthetize exception tests into one

* Add code clarity

* Nitpick with the tests

* Use unique id sensor

* Log an error on unknown error

* Remove useless comments, disable always_update and better use of timeout

* Adjust units, set the model and software version

* Set full name for Battery SOC and use ip instead of url

* Use of host instead of IP

* Fix the unit of economy factor

* Reduce mornitoring data display precision and update snapshots

* Remove unused variable HUBs

* Fix device info

* Set address label 'Host or IP'

* Fix the config_flow tests

* Re evaluate the quality_scale

* Use of 'host' instead of 'address'

* Make inverter discoverable by ssdp

* Add test ssdp configuration already exist

* Add exemption in quality scale

* Test abort ssdp if serial is unknown

* Handle update error

* Raise other exceptions

* Handle ClientError and ValueError from the api

* Update homeassistant/components/imeon_inverter/quality_scale.yaml

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-04-10 08:25:35 +02:00

115 lines
3.6 KiB
Python

"""Config flow for Imeon integration."""
import logging
from typing import Any
from urllib.parse import urlparse
from imeon_inverter_api.inverter import Inverter
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers.service_info.ssdp import (
ATTR_UPNP_MODEL_NUMBER,
ATTR_UPNP_SERIAL,
SsdpServiceInfo,
)
from homeassistant.helpers.typing import VolDictType
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class ImeonInverterConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle the initial setup flow for Imeon Inverters."""
_host: str | None = None
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the user step for creating a new configuration entry."""
errors: dict[str, str] = {}
if user_input is not None:
# User have to provide the hostname if device is not discovered
host = self._host or user_input[CONF_HOST]
async with Inverter(host) as client:
try:
# Check connection
if await client.login(
user_input[CONF_USERNAME], user_input[CONF_PASSWORD]
):
serial = await client.get_serial()
else:
errors["base"] = "invalid_auth"
except TimeoutError:
errors["base"] = "cannot_connect"
except ValueError as e:
if "Host invalid" in str(e):
errors["base"] = "invalid_host"
elif "Route invalid" in str(e):
errors["base"] = "invalid_route"
else:
errors["base"] = "unknown"
_LOGGER.exception(
"Unexpected error occurred while connecting to the Imeon"
)
if not errors:
# Check if entry already exists
await self.async_set_unique_id(serial, raise_on_progress=False)
self._abort_if_unique_id_configured()
# Create a new configuration entry if login succeeds
return self.async_create_entry(
title=f"Imeon {serial}", data={CONF_HOST: host, **user_input}
)
host_schema: VolDictType = (
{vol.Required(CONF_HOST): str} if not self._host else {}
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
**host_schema,
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
}
),
errors=errors,
)
async def async_step_ssdp(
self, discovery_info: SsdpServiceInfo
) -> ConfigFlowResult:
"""Handle a SSDP discovery."""
host = str(urlparse(discovery_info.ssdp_location).hostname)
serial = discovery_info.upnp.get(ATTR_UPNP_SERIAL, "")
if not serial:
return self.async_abort(reason="cannot_connect")
await self.async_set_unique_id(serial)
self._abort_if_unique_id_configured(updates={CONF_HOST: host})
self._host = host
self.context["title_placeholders"] = {
"model": discovery_info.upnp.get(ATTR_UPNP_MODEL_NUMBER, ""),
"serial": serial,
}
return await self.async_step_user()