Add port to config flow of P1 Monitor integration (#128324)

This commit is contained in:
Klaas Schoute 2024-10-16 19:40:20 +02:00 committed by GitHub
parent 5497697cf2
commit a0637a6ff8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 129 additions and 19 deletions

View File

@ -3,11 +3,11 @@
from __future__ import annotations from __future__ import annotations
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import CONF_HOST, CONF_PORT, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from .const import DOMAIN from .const import DOMAIN, LOGGER
from .coordinator import P1MonitorDataUpdateCoordinator from .coordinator import P1MonitorDataUpdateCoordinator
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
@ -30,6 +30,29 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True return True
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Migrate old entry."""
LOGGER.debug("Migrating from version %s", config_entry.version)
if config_entry.version == 1:
# Migrate to split host and port
host = config_entry.data[CONF_HOST]
if ":" in host:
host, port = host.split(":")
else:
port = 80
new_data = {
**config_entry.data,
CONF_HOST: host,
CONF_PORT: int(port),
}
hass.config_entries.async_update_entry(config_entry, data=new_data, version=2)
LOGGER.debug("Migration to version %s successful", config_entry.version)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload P1 Monitor config entry.""" """Unload P1 Monitor config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@ -8,7 +8,7 @@ from p1monitor import P1Monitor, P1MonitorError
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import TextSelector from homeassistant.helpers.selector import TextSelector
@ -18,7 +18,7 @@ from .const import DOMAIN
class P1MonitorFlowHandler(ConfigFlow, domain=DOMAIN): class P1MonitorFlowHandler(ConfigFlow, domain=DOMAIN):
"""Config flow for P1 Monitor.""" """Config flow for P1 Monitor."""
VERSION = 1 VERSION = 2
async def async_step_user( async def async_step_user(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
@ -31,7 +31,9 @@ class P1MonitorFlowHandler(ConfigFlow, domain=DOMAIN):
session = async_get_clientsession(self.hass) session = async_get_clientsession(self.hass)
try: try:
async with P1Monitor( async with P1Monitor(
host=user_input[CONF_HOST], session=session host=user_input[CONF_HOST],
port=user_input[CONF_PORT],
session=session,
) as client: ) as client:
await client.smartmeter() await client.smartmeter()
except P1MonitorError: except P1MonitorError:
@ -41,6 +43,7 @@ class P1MonitorFlowHandler(ConfigFlow, domain=DOMAIN):
title="P1 Monitor", title="P1 Monitor",
data={ data={
CONF_HOST: user_input[CONF_HOST], CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
}, },
) )
@ -49,6 +52,7 @@ class P1MonitorFlowHandler(ConfigFlow, domain=DOMAIN):
data_schema=vol.Schema( data_schema=vol.Schema(
{ {
vol.Required(CONF_HOST): TextSelector(), vol.Required(CONF_HOST): TextSelector(),
vol.Required(CONF_PORT, default=80): int,
} }
), ),
errors=errors, errors=errors,

View File

@ -15,7 +15,7 @@ from p1monitor import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST, CONF_PORT
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 homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
@ -59,7 +59,9 @@ class P1MonitorDataUpdateCoordinator(DataUpdateCoordinator[P1MonitorData]):
) )
self.p1monitor = P1Monitor( self.p1monitor = P1Monitor(
self.config_entry.data[CONF_HOST], session=async_get_clientsession(hass) host=self.config_entry.data[CONF_HOST],
port=self.config_entry.data[CONF_PORT],
session=async_get_clientsession(hass),
) )
async def _async_update_data(self) -> P1MonitorData: async def _async_update_data(self) -> P1MonitorData:

View File

@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any, cast
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import ( from .const import (
@ -22,9 +22,7 @@ from .coordinator import P1MonitorDataUpdateCoordinator
if TYPE_CHECKING: if TYPE_CHECKING:
from _typeshed import DataclassInstance from _typeshed import DataclassInstance
TO_REDACT = { TO_REDACT = {CONF_HOST, CONF_PORT}
CONF_HOST,
}
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(

View File

@ -4,10 +4,12 @@
"user": { "user": {
"description": "Set up P1 Monitor to integrate with Home Assistant.", "description": "Set up P1 Monitor to integrate with Home Assistant.",
"data": { "data": {
"host": "[%key:common::config_flow::data::host%]" "host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
}, },
"data_description": { "data_description": {
"host": "The IP address or hostname of your P1 Monitor installation." "host": "The IP address or hostname of your P1 Monitor installation.",
"port": "The port of your P1 Monitor installation."
} }
} }
}, },

View File

@ -7,7 +7,7 @@ from p1monitor import Phases, Settings, SmartMeter, WaterMeter
import pytest import pytest
from homeassistant.components.p1_monitor.const import DOMAIN from homeassistant.components.p1_monitor.const import DOMAIN
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry, load_fixture from tests.common import MockConfigEntry, load_fixture
@ -19,8 +19,9 @@ def mock_config_entry() -> MockConfigEntry:
return MockConfigEntry( return MockConfigEntry(
title="monitor", title="monitor",
domain=DOMAIN, domain=DOMAIN,
data={CONF_HOST: "example"}, data={CONF_HOST: "example", CONF_PORT: 80},
unique_id="unique_thingy", unique_id="unique_thingy",
version=2,
) )

View File

@ -0,0 +1,45 @@
# serializer version: 1
# name: test_migration
ConfigEntrySnapshot({
'data': dict({
'host': 'example',
'port': 80,
}),
'disabled_by': None,
'discovery_keys': dict({
}),
'domain': 'p1_monitor',
'entry_id': <ANY>,
'minor_version': 1,
'options': dict({
}),
'pref_disable_new_entities': False,
'pref_disable_polling': False,
'source': 'user',
'title': 'Mock Title',
'unique_id': 'unique_thingy',
'version': 2,
})
# ---
# name: test_port_migration
ConfigEntrySnapshot({
'data': dict({
'host': 'example',
'port': 80,
}),
'disabled_by': None,
'discovery_keys': dict({
}),
'domain': 'p1_monitor',
'entry_id': <ANY>,
'minor_version': 1,
'options': dict({
}),
'pref_disable_new_entities': False,
'pref_disable_polling': False,
'source': 'user',
'title': 'Mock Title',
'unique_id': 'unique_thingy',
'version': 2,
})
# ---

View File

@ -6,7 +6,7 @@ from p1monitor import P1MonitorError
from homeassistant.components.p1_monitor.const import DOMAIN from homeassistant.components.p1_monitor.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
@ -30,12 +30,12 @@ async def test_full_user_flow(hass: HomeAssistant) -> None:
): ):
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={CONF_HOST: "example.com"}, user_input={CONF_HOST: "example.com", CONF_PORT: 80},
) )
assert result2.get("type") is FlowResultType.CREATE_ENTRY assert result2.get("type") is FlowResultType.CREATE_ENTRY
assert result2.get("title") == "P1 Monitor" assert result2.get("title") == "P1 Monitor"
assert result2.get("data") == {CONF_HOST: "example.com"} assert result2.get("data") == {CONF_HOST: "example.com", CONF_PORT: 80}
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
assert len(mock_p1monitor.mock_calls) == 1 assert len(mock_p1monitor.mock_calls) == 1
@ -50,7 +50,7 @@ async def test_api_error(hass: HomeAssistant) -> None:
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": SOURCE_USER}, context={"source": SOURCE_USER},
data={CONF_HOST: "example.com"}, data={CONF_HOST: "example.com", CONF_PORT: 80},
) )
assert result.get("type") is FlowResultType.FORM assert result.get("type") is FlowResultType.FORM

View File

@ -21,6 +21,7 @@ async def test_diagnostics(
"title": "monitor", "title": "monitor",
"data": { "data": {
"host": REDACTED, "host": REDACTED,
"port": REDACTED,
}, },
}, },
"data": { "data": {

View File

@ -3,9 +3,11 @@
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from p1monitor import P1MonitorConnectionError from p1monitor import P1MonitorConnectionError
from syrupy import SnapshotAssertion
from homeassistant.components.p1_monitor.const import DOMAIN from homeassistant.components.p1_monitor.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -44,3 +46,35 @@ async def test_config_entry_not_ready(
assert mock_request.call_count == 1 assert mock_request.call_count == 1
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
async def test_migration(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Test config entry version 1 -> 2 migration."""
mock_config_entry = MockConfigEntry(
unique_id="unique_thingy",
domain=DOMAIN,
data={CONF_HOST: "example"},
version=1,
)
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert hass.config_entries.async_get_entry(mock_config_entry.entry_id) == snapshot
async def test_port_migration(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Test migration of host:port to separate host and port."""
mock_config_entry = MockConfigEntry(
unique_id="unique_thingy",
domain=DOMAIN,
data={CONF_HOST: "example:80"},
version=1,
)
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert hass.config_entries.async_get_entry(mock_config_entry.entry_id) == snapshot