mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 18:27:09 +00:00
Add Kostal plenticore Installer login support (#133773)
* feat: Add Installer login, Add ManualCharge Switch * remove unnecessary field * replace strings with consts * change to CONF and camel_case * Improve existing code * Add translation string * format code * add service code test * format code * format code * remove manual charge switch * add reconfigure config flow * fix flow * add return type * add reconfigure strings * adjust tests * change string * simlify tests * add reconfigure test * add more tests * Fix --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
e413e9b93b
commit
67b9904740
@ -12,7 +12,7 @@ from homeassistant.const import CONF_BASE, CONF_HOST, CONF_PASSWORD
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import CONF_SERVICE_CODE, DOMAIN
|
||||||
from .helper import get_hostname_id
|
from .helper import get_hostname_id
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -21,6 +21,7 @@ DATA_SCHEMA = vol.Schema(
|
|||||||
{
|
{
|
||||||
vol.Required(CONF_HOST): str,
|
vol.Required(CONF_HOST): str,
|
||||||
vol.Required(CONF_PASSWORD): str,
|
vol.Required(CONF_PASSWORD): str,
|
||||||
|
vol.Optional(CONF_SERVICE_CODE): str,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,8 +33,10 @@ async def test_connection(hass: HomeAssistant, data) -> str:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
session = async_get_clientsession(hass)
|
session = async_get_clientsession(hass)
|
||||||
async with ApiClient(session, data["host"]) as client:
|
async with ApiClient(session, data[CONF_HOST]) as client:
|
||||||
await client.login(data["password"])
|
await client.login(
|
||||||
|
data[CONF_PASSWORD], service_code=data.get(CONF_SERVICE_CODE)
|
||||||
|
)
|
||||||
hostname_id = await get_hostname_id(client)
|
hostname_id = await get_hostname_id(client)
|
||||||
values = await client.get_setting_values("scb:network", hostname_id)
|
values = await client.get_setting_values("scb:network", hostname_id)
|
||||||
|
|
||||||
@ -70,3 +73,30 @@ class KostalPlenticoreConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user", data_schema=DATA_SCHEMA, errors=errors
|
step_id="user", data_schema=DATA_SCHEMA, errors=errors
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_reconfigure(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Add reconfigure step to allow to reconfigure a config entry."""
|
||||||
|
errors = {}
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
|
||||||
|
try:
|
||||||
|
hostname = await test_connection(self.hass, user_input)
|
||||||
|
except AuthenticationException as ex:
|
||||||
|
errors[CONF_PASSWORD] = "invalid_auth"
|
||||||
|
_LOGGER.error("Error response: %s", ex)
|
||||||
|
except (ClientError, TimeoutError):
|
||||||
|
errors[CONF_HOST] = "cannot_connect"
|
||||||
|
except Exception:
|
||||||
|
_LOGGER.exception("Unexpected exception")
|
||||||
|
errors[CONF_BASE] = "unknown"
|
||||||
|
else:
|
||||||
|
return self.async_update_reload_and_abort(
|
||||||
|
entry=self._get_reconfigure_entry(), title=hostname, data=user_input
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="reconfigure", data_schema=DATA_SCHEMA, errors=errors
|
||||||
|
)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
"""Constants for the Kostal Plenticore Solar Inverter integration."""
|
"""Constants for the Kostal Plenticore Solar Inverter integration."""
|
||||||
|
|
||||||
DOMAIN = "kostal_plenticore"
|
DOMAIN = "kostal_plenticore"
|
||||||
|
CONF_SERVICE_CODE = "service_code"
|
||||||
|
@ -25,7 +25,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
|
|||||||
from homeassistant.helpers.event import async_call_later
|
from homeassistant.helpers.event import async_call_later
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import CONF_SERVICE_CODE, DOMAIN
|
||||||
from .helper import get_hostname_id
|
from .helper import get_hostname_id
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -60,7 +60,10 @@ class Plenticore:
|
|||||||
async_get_clientsession(self.hass), host=self.host
|
async_get_clientsession(self.hass), host=self.host
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
await self._client.login(self.config_entry.data[CONF_PASSWORD])
|
await self._client.login(
|
||||||
|
self.config_entry.data[CONF_PASSWORD],
|
||||||
|
service_code=self.config_entry.data.get(CONF_SERVICE_CODE),
|
||||||
|
)
|
||||||
except AuthenticationException as err:
|
except AuthenticationException as err:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Authentication exception connecting to %s: %s", self.host, err
|
"Authentication exception connecting to %s: %s", self.host, err
|
||||||
|
@ -4,7 +4,15 @@
|
|||||||
"user": {
|
"user": {
|
||||||
"data": {
|
"data": {
|
||||||
"host": "[%key:common::config_flow::data::host%]",
|
"host": "[%key:common::config_flow::data::host%]",
|
||||||
"password": "[%key:common::config_flow::data::password%]"
|
"password": "[%key:common::config_flow::data::password%]",
|
||||||
|
"service_code": "Service code"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reconfigure": {
|
||||||
|
"data": {
|
||||||
|
"host": "[%key:common::config_flow::data::host%]",
|
||||||
|
"password": "[%key:common::config_flow::data::password%]",
|
||||||
|
"service_code": "[%key:component::kostal_plenticore::config::step::user::data::service_code%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -14,7 +22,8 @@
|
|||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
|
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.kostal_plenticore.const import DOMAIN
|
from homeassistant.components.kostal_plenticore.const import DOMAIN
|
||||||
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ async def test_form_g1(
|
|||||||
return_value={"scb:network": {"Hostname": "scb"}}
|
return_value={"scb:network": {"Hostname": "scb"}}
|
||||||
)
|
)
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
@ -86,15 +87,15 @@ async def test_form_g1(
|
|||||||
mock_apiclient_class.assert_called_once_with(ANY, "1.1.1.1")
|
mock_apiclient_class.assert_called_once_with(ANY, "1.1.1.1")
|
||||||
mock_apiclient.__aenter__.assert_called_once()
|
mock_apiclient.__aenter__.assert_called_once()
|
||||||
mock_apiclient.__aexit__.assert_called_once()
|
mock_apiclient.__aexit__.assert_called_once()
|
||||||
mock_apiclient.login.assert_called_once_with("test-password")
|
mock_apiclient.login.assert_called_once_with("test-password", service_code=None)
|
||||||
mock_apiclient.get_settings.assert_called_once()
|
mock_apiclient.get_settings.assert_called_once()
|
||||||
mock_apiclient.get_setting_values.assert_called_once_with(
|
mock_apiclient.get_setting_values.assert_called_once_with(
|
||||||
"scb:network", "Hostname"
|
"scb:network", "Hostname"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert result2["title"] == "scb"
|
assert result["title"] == "scb"
|
||||||
assert result2["data"] == {
|
assert result["data"] == {
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
"password": "test-password",
|
"password": "test-password",
|
||||||
}
|
}
|
||||||
@ -140,7 +141,7 @@ async def test_form_g2(
|
|||||||
return_value={"scb:network": {"Network:Hostname": "scb"}}
|
return_value={"scb:network": {"Network:Hostname": "scb"}}
|
||||||
)
|
)
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
@ -152,21 +153,91 @@ async def test_form_g2(
|
|||||||
mock_apiclient_class.assert_called_once_with(ANY, "1.1.1.1")
|
mock_apiclient_class.assert_called_once_with(ANY, "1.1.1.1")
|
||||||
mock_apiclient.__aenter__.assert_called_once()
|
mock_apiclient.__aenter__.assert_called_once()
|
||||||
mock_apiclient.__aexit__.assert_called_once()
|
mock_apiclient.__aexit__.assert_called_once()
|
||||||
mock_apiclient.login.assert_called_once_with("test-password")
|
mock_apiclient.login.assert_called_once_with("test-password", service_code=None)
|
||||||
mock_apiclient.get_settings.assert_called_once()
|
mock_apiclient.get_settings.assert_called_once()
|
||||||
mock_apiclient.get_setting_values.assert_called_once_with(
|
mock_apiclient.get_setting_values.assert_called_once_with(
|
||||||
"scb:network", "Network:Hostname"
|
"scb:network", "Network:Hostname"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert result2["title"] == "scb"
|
assert result["title"] == "scb"
|
||||||
assert result2["data"] == {
|
assert result["data"] == {
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
"password": "test-password",
|
"password": "test-password",
|
||||||
}
|
}
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_g2_with_service_code(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_apiclient_class: type[ApiClient],
|
||||||
|
mock_apiclient: ApiClient,
|
||||||
|
) -> None:
|
||||||
|
"""Test the config flow for G2 models with a Service Code."""
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.kostal_plenticore.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
# mock of the context manager instance
|
||||||
|
mock_apiclient.login = AsyncMock()
|
||||||
|
mock_apiclient.get_settings = AsyncMock(
|
||||||
|
return_value={
|
||||||
|
"scb:network": [
|
||||||
|
SettingsData(
|
||||||
|
min="1",
|
||||||
|
max="63",
|
||||||
|
default=None,
|
||||||
|
access="readwrite",
|
||||||
|
unit=None,
|
||||||
|
id="Network:Hostname",
|
||||||
|
type="string",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
mock_apiclient.get_setting_values = AsyncMock(
|
||||||
|
# G1 model has the entry id "Hostname"
|
||||||
|
return_value={"scb:network": {"Network:Hostname": "scb"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"host": "1.1.1.1",
|
||||||
|
"password": "test-password",
|
||||||
|
"service_code": "test-service-code",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mock_apiclient_class.assert_called_once_with(ANY, "1.1.1.1")
|
||||||
|
mock_apiclient.__aenter__.assert_called_once()
|
||||||
|
mock_apiclient.__aexit__.assert_called_once()
|
||||||
|
mock_apiclient.login.assert_called_once_with(
|
||||||
|
"test-password", service_code="test-service-code"
|
||||||
|
)
|
||||||
|
mock_apiclient.get_settings.assert_called_once()
|
||||||
|
mock_apiclient.get_setting_values.assert_called_once_with(
|
||||||
|
"scb:network", "Network:Hostname"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == "scb"
|
||||||
|
assert result["data"] == {
|
||||||
|
"host": "1.1.1.1",
|
||||||
|
"password": "test-password",
|
||||||
|
"service_code": "test-service-code",
|
||||||
|
}
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_form_invalid_auth(hass: HomeAssistant) -> None:
|
async def test_form_invalid_auth(hass: HomeAssistant) -> None:
|
||||||
"""Test we handle invalid auth."""
|
"""Test we handle invalid auth."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
@ -189,7 +260,7 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
mock_api_class.return_value = mock_api
|
mock_api_class.return_value = mock_api
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
@ -197,8 +268,8 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result2["errors"] == {"password": "invalid_auth"}
|
assert result["errors"] == {"password": "invalid_auth"}
|
||||||
|
|
||||||
|
|
||||||
async def test_form_cannot_connect(hass: HomeAssistant) -> None:
|
async def test_form_cannot_connect(hass: HomeAssistant) -> None:
|
||||||
@ -223,7 +294,7 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
mock_api_class.return_value = mock_api
|
mock_api_class.return_value = mock_api
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
@ -231,8 +302,8 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result2["errors"] == {"host": "cannot_connect"}
|
assert result["errors"] == {"host": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
async def test_form_unexpected_error(hass: HomeAssistant) -> None:
|
async def test_form_unexpected_error(hass: HomeAssistant) -> None:
|
||||||
@ -257,7 +328,7 @@ async def test_form_unexpected_error(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
mock_api_class.return_value = mock_api
|
mock_api_class.return_value = mock_api
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
@ -265,8 +336,8 @@ async def test_form_unexpected_error(hass: HomeAssistant) -> None:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result2["errors"] == {"base": "unknown"}
|
assert result["errors"] == {"base": "unknown"}
|
||||||
|
|
||||||
|
|
||||||
async def test_already_configured(hass: HomeAssistant) -> None:
|
async def test_already_configured(hass: HomeAssistant) -> None:
|
||||||
@ -281,7 +352,7 @@ async def test_already_configured(hass: HomeAssistant) -> None:
|
|||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
@ -289,5 +360,197 @@ async def test_already_configured(hass: HomeAssistant) -> None:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["type"] is FlowResultType.ABORT
|
assert result["type"] is FlowResultType.ABORT
|
||||||
assert result2["reason"] == "already_configured"
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reconfigure(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_apiclient_class: type[ApiClient],
|
||||||
|
mock_apiclient: ApiClient,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test the config flow for G1 models."""
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "reconfigure"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
# mock of the context manager instance
|
||||||
|
mock_apiclient.login = AsyncMock()
|
||||||
|
mock_apiclient.get_settings = AsyncMock(
|
||||||
|
return_value={
|
||||||
|
"scb:network": [
|
||||||
|
SettingsData(
|
||||||
|
min="1",
|
||||||
|
max="63",
|
||||||
|
default=None,
|
||||||
|
access="readwrite",
|
||||||
|
unit=None,
|
||||||
|
id="Hostname",
|
||||||
|
type="string",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
mock_apiclient.get_setting_values = AsyncMock(
|
||||||
|
# G1 model has the entry id "Hostname"
|
||||||
|
return_value={"scb:network": {"Hostname": "scb"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"host": "1.1.1.1",
|
||||||
|
"password": "test-password",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mock_apiclient_class.assert_called_once_with(ANY, "1.1.1.1")
|
||||||
|
mock_apiclient.__aenter__.assert_called_once()
|
||||||
|
mock_apiclient.__aexit__.assert_called_once()
|
||||||
|
mock_apiclient.login.assert_called_once_with("test-password", service_code=None)
|
||||||
|
mock_apiclient.get_settings.assert_called_once()
|
||||||
|
mock_apiclient.get_setting_values.assert_called_once_with("scb:network", "Hostname")
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "reconfigure_successful"
|
||||||
|
|
||||||
|
# changed entry
|
||||||
|
assert mock_config_entry.data[CONF_HOST] == "1.1.1.1"
|
||||||
|
assert mock_config_entry.data[CONF_PASSWORD] == "test-password"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reconfigure_invalid_auth(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test we handle invalid auth while reconfiguring."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.kostal_plenticore.config_flow.ApiClient"
|
||||||
|
) as mock_api_class:
|
||||||
|
# mock of the context manager instance
|
||||||
|
mock_api_ctx = MagicMock()
|
||||||
|
mock_api_ctx.login = AsyncMock(
|
||||||
|
side_effect=AuthenticationException(404, "invalid user"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# mock of the return instance of ApiClient
|
||||||
|
mock_api = MagicMock()
|
||||||
|
mock_api.__aenter__.return_value = mock_api_ctx
|
||||||
|
mock_api.__aexit__.return_value = None
|
||||||
|
|
||||||
|
mock_api_class.return_value = mock_api
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"host": "1.1.1.1",
|
||||||
|
"password": "test-password",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["errors"] == {"password": "invalid_auth"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reconfigure_cannot_connect(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test we handle cannot connect error."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.kostal_plenticore.config_flow.ApiClient"
|
||||||
|
) as mock_api_class:
|
||||||
|
# mock of the context manager instance
|
||||||
|
mock_api_ctx = MagicMock()
|
||||||
|
mock_api_ctx.login = AsyncMock(
|
||||||
|
side_effect=TimeoutError(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# mock of the return instance of ApiClient
|
||||||
|
mock_api = MagicMock()
|
||||||
|
mock_api.__aenter__.return_value = mock_api_ctx
|
||||||
|
mock_api.__aexit__.return_value = None
|
||||||
|
|
||||||
|
mock_api_class.return_value = mock_api
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"host": "1.1.1.1",
|
||||||
|
"password": "test-password",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["errors"] == {"host": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reconfigure_unexpected_error(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test we handle unexpected error."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.kostal_plenticore.config_flow.ApiClient"
|
||||||
|
) as mock_api_class:
|
||||||
|
# mock of the context manager instance
|
||||||
|
mock_api_ctx = MagicMock()
|
||||||
|
mock_api_ctx.login = AsyncMock(
|
||||||
|
side_effect=Exception(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# mock of the return instance of ApiClient
|
||||||
|
mock_api = MagicMock()
|
||||||
|
mock_api.__aenter__.return_value = mock_api_ctx
|
||||||
|
mock_api.__aexit__.return_value = None
|
||||||
|
|
||||||
|
mock_api_class.return_value = mock_api
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"host": "1.1.1.1",
|
||||||
|
"password": "test-password",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["errors"] == {"base": "unknown"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reconfigure_already_configured(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test we handle already configured error."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
MockConfigEntry(
|
||||||
|
domain="kostal_plenticore",
|
||||||
|
data={CONF_HOST: "1.1.1.1", CONF_PASSWORD: "foobar"},
|
||||||
|
unique_id="112233445566",
|
||||||
|
).add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_HOST: "1.1.1.1",
|
||||||
|
CONF_PASSWORD: "test-password",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user