mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 02:07:09 +00:00
Get kostal plenticore hostname id from get_settings (#93008)
* Get hostname id from get_settings * Add try except in get_hostname_id function * Update tests after adding get_hostname_id function * Revert "Update tests after adding get_hostname_id function" This reverts commit 5fa4e533cb18e8d141dbb1b7aed021f80f4312a2. * Add test for G2 models in config flow. * Add test for helper module. * Fix test for numbers. * Revert "Add try except in get_hostname_id function" This reverts commit 059f5bd9b413ca060300e09a6a1f0dffb4420f56. * Update variable name with known hostname ids to be private --------- Co-authored-by: Stefan Gmeiner <stefangm42@gmail.com>
This commit is contained in:
parent
1dccb8a9a9
commit
2721874f13
@ -12,6 +12,7 @@ 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 DOMAIN
|
||||||
|
from .helper import get_hostname_id
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -32,9 +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["host"]) as client:
|
||||||
await client.login(data["password"])
|
await client.login(data["password"])
|
||||||
values = await client.get_setting_values("scb:network", "Hostname")
|
hostname_id = await get_hostname_id(client)
|
||||||
|
values = await client.get_setting_values("scb:network", hostname_id)
|
||||||
|
|
||||||
return values["scb:network"]["Hostname"]
|
return values["scb:network"][hostname_id]
|
||||||
|
|
||||||
|
|
||||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
@ -23,6 +23,7 @@ from .const import DOMAIN
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_DataT = TypeVar("_DataT")
|
_DataT = TypeVar("_DataT")
|
||||||
|
_KNOWN_HOSTNAME_IDS = ("Network:Hostname", "Hostname")
|
||||||
|
|
||||||
|
|
||||||
class Plenticore:
|
class Plenticore:
|
||||||
@ -69,6 +70,7 @@ class Plenticore:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# get some device meta data
|
# get some device meta data
|
||||||
|
hostname_id = await get_hostname_id(self._client)
|
||||||
settings = await self._client.get_setting_values(
|
settings = await self._client.get_setting_values(
|
||||||
{
|
{
|
||||||
"devices:local": [
|
"devices:local": [
|
||||||
@ -78,7 +80,7 @@ class Plenticore:
|
|||||||
"Properties:VersionIOC",
|
"Properties:VersionIOC",
|
||||||
"Properties:VersionMC",
|
"Properties:VersionMC",
|
||||||
],
|
],
|
||||||
"scb:network": ["Hostname"],
|
"scb:network": [hostname_id],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -91,7 +93,7 @@ class Plenticore:
|
|||||||
identifiers={(DOMAIN, device_local["Properties:SerialNo"])},
|
identifiers={(DOMAIN, device_local["Properties:SerialNo"])},
|
||||||
manufacturer="Kostal",
|
manufacturer="Kostal",
|
||||||
model=f"{prod1} {prod2}",
|
model=f"{prod1} {prod2}",
|
||||||
name=settings["scb:network"]["Hostname"],
|
name=settings["scb:network"][hostname_id],
|
||||||
sw_version=f'IOC: {device_local["Properties:VersionIOC"]}'
|
sw_version=f'IOC: {device_local["Properties:VersionIOC"]}'
|
||||||
+ f' MC: {device_local["Properties:VersionMC"]}',
|
+ f' MC: {device_local["Properties:VersionMC"]}',
|
||||||
)
|
)
|
||||||
@ -403,3 +405,12 @@ class PlenticoreDataFormatter:
|
|||||||
return state
|
return state
|
||||||
|
|
||||||
return PlenticoreDataFormatter.EM_STATES.get(value)
|
return PlenticoreDataFormatter.EM_STATES.get(value)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_hostname_id(client: ApiClient) -> str:
|
||||||
|
"""Check for known existing hostname ids."""
|
||||||
|
all_settings = await client.get_settings()
|
||||||
|
for entry in all_settings["scb:network"]:
|
||||||
|
if entry.id in _KNOWN_HOSTNAME_IDS:
|
||||||
|
return entry.id
|
||||||
|
raise ApiException("Hostname identifier not found in KNOWN_HOSTNAME_IDS")
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""Test the Kostal Plenticore Solar Inverter config flow."""
|
"""Test the Kostal Plenticore Solar Inverter config flow."""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from collections.abc import Generator
|
||||||
from unittest.mock import ANY, AsyncMock, MagicMock, patch
|
from unittest.mock import ANY, AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
from pykoplenti import AuthenticationException
|
from pykoplenti import ApiClient, AuthenticationException, SettingsData
|
||||||
|
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
|
||||||
@ -11,8 +13,33 @@ from homeassistant.core import HomeAssistant
|
|||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
async def test_formx(hass: HomeAssistant) -> None:
|
@pytest.fixture
|
||||||
"""Test we get the form."""
|
def mock_apiclient() -> ApiClient:
|
||||||
|
"""Return a mocked ApiClient instance."""
|
||||||
|
apiclient = MagicMock(spec=ApiClient)
|
||||||
|
apiclient.__aenter__.return_value = apiclient
|
||||||
|
apiclient.__aexit__ = AsyncMock()
|
||||||
|
|
||||||
|
return apiclient
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_apiclient_class(mock_apiclient) -> Generator[type[ApiClient], None, None]:
|
||||||
|
"""Return a mocked ApiClient class."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.kostal_plenticore.config_flow.ApiClient",
|
||||||
|
autospec=True,
|
||||||
|
) as mock_api_class:
|
||||||
|
mock_api_class.return_value = mock_apiclient
|
||||||
|
yield mock_api_class
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_g1(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_apiclient_class: type[ApiClient],
|
||||||
|
mock_apiclient: ApiClient,
|
||||||
|
) -> None:
|
||||||
|
"""Test the config flow for G1 models."""
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
@ -21,25 +48,19 @@ async def test_formx(hass: HomeAssistant) -> None:
|
|||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.kostal_plenticore.config_flow.ApiClient"
|
|
||||||
) as mock_api_class, patch(
|
|
||||||
"homeassistant.components.kostal_plenticore.async_setup_entry",
|
"homeassistant.components.kostal_plenticore.async_setup_entry",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
) as mock_setup_entry:
|
) as mock_setup_entry:
|
||||||
# mock of the context manager instance
|
# mock of the context manager instance
|
||||||
mock_api_ctx = MagicMock()
|
mock_apiclient.login = AsyncMock()
|
||||||
mock_api_ctx.login = AsyncMock()
|
mock_apiclient.get_settings = AsyncMock(
|
||||||
mock_api_ctx.get_setting_values = AsyncMock(
|
return_value={"scb:network": [SettingsData({"id": "Hostname"})]}
|
||||||
|
)
|
||||||
|
mock_apiclient.get_setting_values = AsyncMock(
|
||||||
|
# G1 model has the entry id "Hostname"
|
||||||
return_value={"scb:network": {"Hostname": "scb"}}
|
return_value={"scb:network": {"Hostname": "scb"}}
|
||||||
)
|
)
|
||||||
|
|
||||||
# mock of the return instance of ApiClient
|
|
||||||
mock_api = MagicMock()
|
|
||||||
mock_api.__aenter__.return_value = mock_api_ctx
|
|
||||||
mock_api.__aexit__ = AsyncMock()
|
|
||||||
|
|
||||||
mock_api_class.return_value = mock_api
|
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
@ -49,11 +70,68 @@ async def test_formx(hass: HomeAssistant) -> None:
|
|||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
mock_api_class.assert_called_once_with(ANY, "1.1.1.1")
|
mock_apiclient_class.assert_called_once_with(ANY, "1.1.1.1")
|
||||||
mock_api.__aenter__.assert_called_once()
|
mock_apiclient.__aenter__.assert_called_once()
|
||||||
mock_api.__aexit__.assert_called_once()
|
mock_apiclient.__aexit__.assert_called_once()
|
||||||
mock_api_ctx.login.assert_called_once_with("test-password")
|
mock_apiclient.login.assert_called_once_with("test-password")
|
||||||
mock_api_ctx.get_setting_values.assert_called_once()
|
mock_apiclient.get_settings.assert_called_once()
|
||||||
|
mock_apiclient.get_setting_values.assert_called_once_with(
|
||||||
|
"scb:network", "Hostname"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] == "create_entry"
|
||||||
|
assert result2["title"] == "scb"
|
||||||
|
assert result2["data"] == {
|
||||||
|
"host": "1.1.1.1",
|
||||||
|
"password": "test-password",
|
||||||
|
}
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_g2(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_apiclient_class: type[ApiClient],
|
||||||
|
mock_apiclient: ApiClient,
|
||||||
|
) -> None:
|
||||||
|
"""Test the config flow for G2 models."""
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == "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({"id": "Network:Hostname"})]}
|
||||||
|
)
|
||||||
|
mock_apiclient.get_setting_values = AsyncMock(
|
||||||
|
# G1 model has the entry id "Hostname"
|
||||||
|
return_value={"scb:network": {"Network:Hostname": "scb"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
result2 = 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")
|
||||||
|
mock_apiclient.get_settings.assert_called_once()
|
||||||
|
mock_apiclient.get_setting_values.assert_called_once_with(
|
||||||
|
"scb:network", "Network:Hostname"
|
||||||
|
)
|
||||||
|
|
||||||
assert result2["type"] == "create_entry"
|
assert result2["type"] == "create_entry"
|
||||||
assert result2["title"] == "scb"
|
assert result2["title"] == "scb"
|
||||||
|
107
tests/components/kostal_plenticore/test_helper.py
Normal file
107
tests/components/kostal_plenticore/test_helper.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
"""Test Kostal Plenticore helper."""
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
|
from pykoplenti import ApiClient, SettingsData
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.kostal_plenticore.const import DOMAIN
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_apiclient() -> Generator[ApiClient, None, None]:
|
||||||
|
"""Return a mocked ApiClient class."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.kostal_plenticore.helper.ApiClient",
|
||||||
|
autospec=True,
|
||||||
|
) as mock_api_class:
|
||||||
|
apiclient = MagicMock(spec=ApiClient)
|
||||||
|
apiclient.__aenter__.return_value = apiclient
|
||||||
|
apiclient.__aexit__ = AsyncMock()
|
||||||
|
mock_api_class.return_value = apiclient
|
||||||
|
yield apiclient
|
||||||
|
|
||||||
|
|
||||||
|
async def test_plenticore_async_setup_g1(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_apiclient: ApiClient,
|
||||||
|
) -> None:
|
||||||
|
"""Tests the async_setup() method of the Plenticore class for G1 models."""
|
||||||
|
mock_apiclient.get_settings = AsyncMock(
|
||||||
|
return_value={"scb:network": [SettingsData({"id": "Hostname"})]}
|
||||||
|
)
|
||||||
|
mock_apiclient.get_setting_values = AsyncMock(
|
||||||
|
# G1 model has the entry id "Hostname"
|
||||||
|
return_value={
|
||||||
|
"devices:local": {
|
||||||
|
"Properties:SerialNo": "12345",
|
||||||
|
"Branding:ProductName1": "PLENTICORE",
|
||||||
|
"Branding:ProductName2": "plus 10",
|
||||||
|
"Properties:VersionIOC": "01.45",
|
||||||
|
"Properties:VersionMC": "01.46",
|
||||||
|
},
|
||||||
|
"scb:network": {"Hostname": "scb"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
plenticore = hass.data[DOMAIN][mock_config_entry.entry_id]
|
||||||
|
|
||||||
|
assert plenticore.device_info == DeviceInfo(
|
||||||
|
configuration_url="http://192.168.1.2",
|
||||||
|
identifiers={(DOMAIN, "12345")},
|
||||||
|
manufacturer="Kostal",
|
||||||
|
model="PLENTICORE plus 10",
|
||||||
|
name="scb",
|
||||||
|
sw_version="IOC: 01.45 MC: 01.46",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_plenticore_async_setup_g2(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_apiclient: ApiClient,
|
||||||
|
) -> None:
|
||||||
|
"""Tests the async_setup() method of the Plenticore class for G2 models."""
|
||||||
|
mock_apiclient.get_settings = AsyncMock(
|
||||||
|
return_value={"scb:network": [SettingsData({"id": "Network:Hostname"})]}
|
||||||
|
)
|
||||||
|
mock_apiclient.get_setting_values = AsyncMock(
|
||||||
|
# G1 model has the entry id "Hostname"
|
||||||
|
return_value={
|
||||||
|
"devices:local": {
|
||||||
|
"Properties:SerialNo": "12345",
|
||||||
|
"Branding:ProductName1": "PLENTICORE",
|
||||||
|
"Branding:ProductName2": "plus 10",
|
||||||
|
"Properties:VersionIOC": "01.45",
|
||||||
|
"Properties:VersionMC": "01.46",
|
||||||
|
},
|
||||||
|
"scb:network": {"Network:Hostname": "scb"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
plenticore = hass.data[DOMAIN][mock_config_entry.entry_id]
|
||||||
|
|
||||||
|
assert plenticore.device_info == DeviceInfo(
|
||||||
|
configuration_url="http://192.168.1.2",
|
||||||
|
identifiers={(DOMAIN, "12345")},
|
||||||
|
manufacturer="Kostal",
|
||||||
|
model="PLENTICORE plus 10",
|
||||||
|
name="scb",
|
||||||
|
sw_version="IOC: 01.45 MC: 01.46",
|
||||||
|
)
|
@ -62,7 +62,20 @@ def mock_get_setting_values(mock_plenticore_client: ApiClient) -> list:
|
|||||||
"id": "Battery:MinHomeComsumption",
|
"id": "Battery:MinHomeComsumption",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
]
|
],
|
||||||
|
"scb:network": [
|
||||||
|
SettingsData(
|
||||||
|
{
|
||||||
|
"min": "1",
|
||||||
|
"default": None,
|
||||||
|
"access": "readwrite",
|
||||||
|
"unit": None,
|
||||||
|
"id": "Hostname",
|
||||||
|
"type": "string",
|
||||||
|
"max": "63",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
# this values are always retrieved by the integration on startup
|
# this values are always retrieved by the integration on startup
|
||||||
@ -112,7 +125,22 @@ async def test_setup_no_entries(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test that no entries are setup if Plenticore does not provide data."""
|
"""Test that no entries are setup if Plenticore does not provide data."""
|
||||||
|
|
||||||
mock_plenticore_client.get_settings.return_value = []
|
# remove all settings except hostname which is used during setup
|
||||||
|
mock_plenticore_client.get_settings.return_value = {
|
||||||
|
"scb:network": [
|
||||||
|
SettingsData(
|
||||||
|
{
|
||||||
|
"min": "1",
|
||||||
|
"default": None,
|
||||||
|
"access": "readwrite",
|
||||||
|
"unit": None,
|
||||||
|
"id": "Hostname",
|
||||||
|
"type": "string",
|
||||||
|
"max": "63",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
mock_config_entry.add_to_hass(hass)
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user