Use SerialPortSelector for DSMR serial port configuration (#171103)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Paulus Schoutsen
2026-06-05 21:35:07 -04:00
committed by GitHub
co-authored by Claude
parent a1e6a6f9a2
commit 266fccf0cf
3 changed files with 3 additions and 133 deletions
+3 -49
View File
@@ -13,7 +13,6 @@ from dsmr_parser.clients.rfxtrx_protocol import (
from dsmr_parser.objects import DSMRObject
import voluptuous as vol
from homeassistant.components import usb
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
@@ -23,6 +22,7 @@ from homeassistant.config_entries import (
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_PROTOCOL, CONF_TYPE
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.selector import SerialPortSelector
from .const import (
CONF_DSMR_VERSION,
@@ -37,8 +37,6 @@ from .const import (
RFXTRX_DSMR_PROTOCOL,
)
CONF_MANUAL_PATH = "Enter Manually"
class DSMRConnection:
"""Test the connection to DSMR and receive telegram to read serial ids."""
@@ -165,8 +163,6 @@ class DSMRFlowHandler(ConfigFlow, domain=DOMAIN):
VERSION = 1
_dsmr_version: str | None = None
@staticmethod
@callback
def async_get_options_flow(
@@ -222,34 +218,13 @@ class DSMRFlowHandler(ConfigFlow, domain=DOMAIN):
"""Step when setting up serial configuration."""
errors: dict[str, str] = {}
if user_input is not None:
user_selection = user_input[CONF_PORT]
if user_selection == CONF_MANUAL_PATH:
self._dsmr_version = user_input[CONF_DSMR_VERSION]
return await self.async_step_setup_serial_manual_path()
dev_path = user_selection
validate_data = {
CONF_PORT: dev_path,
CONF_DSMR_VERSION: user_input[CONF_DSMR_VERSION],
}
data = await self.async_validate_dsmr(validate_data, errors)
data = await self.async_validate_dsmr(user_input, errors)
if not errors:
return self.async_create_entry(title=data[CONF_PORT], data=data)
ports = await usb.async_scan_serial_ports(self.hass)
list_of_ports = {
port.device: f"{port.device} - {port.description or 'n/a'}"
f", s/n: {port.serial_number or 'n/a'}"
+ (f" - {port.manufacturer}" if port.manufacturer else "")
for port in ports
}
list_of_ports[CONF_MANUAL_PATH] = CONF_MANUAL_PATH
schema = vol.Schema(
{
vol.Required(CONF_PORT): vol.In(list_of_ports),
vol.Required(CONF_PORT): SerialPortSelector(),
vol.Required(CONF_DSMR_VERSION): vol.In(DSMR_VERSIONS),
}
)
@@ -259,27 +234,6 @@ class DSMRFlowHandler(ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def async_step_setup_serial_manual_path(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Select path manually."""
if user_input is not None:
validate_data = {
CONF_PORT: user_input[CONF_PORT],
CONF_DSMR_VERSION: self._dsmr_version,
}
errors: dict[str, str] = {}
data = await self.async_validate_dsmr(validate_data, errors)
if not errors:
return self.async_create_entry(title=data[CONF_PORT], data=data)
schema = vol.Schema({vol.Required(CONF_PORT): str})
return self.async_show_form(
step_id="setup_serial_manual_path",
data_schema=schema,
)
async def async_validate_dsmr(
self, input_data: dict[str, Any], errors: dict[str, str]
) -> dict[str, Any]:
@@ -26,12 +26,6 @@
},
"title": "[%key:common::config_flow::data::device%]"
},
"setup_serial_manual_path": {
"data": {
"port": "[%key:common::config_flow::data::usb_path%]"
},
"title": "[%key:common::config_flow::data::path%]"
},
"user": {
"data": {
"type": "Connection type"
-78
View File
@@ -192,12 +192,7 @@ async def test_setup_network_rfxtrx(
),
],
)
@patch(
"homeassistant.components.dsmr.config_flow.usb.async_scan_serial_ports",
return_value=[com_port()],
)
async def test_setup_serial(
com_mock,
hass: HomeAssistant,
dsmr_connection_send_validate_fixture: tuple[MagicMock, MagicMock, MagicMock],
version: str,
@@ -235,12 +230,7 @@ async def test_setup_serial(
assert result["data"] == entry_data
@patch(
"homeassistant.components.dsmr.config_flow.usb.async_scan_serial_ports",
return_value=[com_port()],
)
async def test_setup_serial_rfxtrx(
com_mock,
hass: HomeAssistant,
dsmr_connection_send_validate_fixture: tuple[MagicMock, MagicMock, MagicMock],
rfxtrx_dsmr_connection_send_validate_fixture: tuple[
@@ -291,65 +281,7 @@ async def test_setup_serial_rfxtrx(
assert result["data"] == {**entry_data, **SERIAL_DATA}
@patch(
"homeassistant.components.dsmr.config_flow.usb.async_scan_serial_ports",
return_value=[com_port()],
)
async def test_setup_serial_manual(
com_mock,
hass: HomeAssistant,
dsmr_connection_send_validate_fixture: tuple[MagicMock, MagicMock, MagicMock],
) -> None:
"""Test we can setup serial with manual entry."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] is None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"type": "Serial"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "setup_serial"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"port": "Enter Manually", "dsmr_version": "2.2"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "setup_serial_manual_path"
assert result["errors"] is None
with patch("homeassistant.components.dsmr.async_setup_entry", return_value=True):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {"port": "/dev/ttyUSB0"}
)
await hass.async_block_till_done()
entry_data = {
"port": "/dev/ttyUSB0",
"dsmr_version": "2.2",
"protocol": "dsmr_protocol",
}
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "/dev/ttyUSB0"
assert result["data"] == {**entry_data, **SERIAL_DATA}
@patch(
"homeassistant.components.dsmr.config_flow.usb.async_scan_serial_ports",
return_value=[com_port()],
)
async def test_setup_serial_fail(
com_mock,
hass: HomeAssistant,
dsmr_connection_send_validate_fixture: tuple[MagicMock, MagicMock, MagicMock],
) -> None:
@@ -395,12 +327,7 @@ async def test_setup_serial_fail(
assert result["errors"] == {"base": "cannot_connect"}
@patch(
"homeassistant.components.dsmr.config_flow.usb.async_scan_serial_ports",
return_value=[com_port()],
)
async def test_setup_serial_timeout(
com_mock,
hass: HomeAssistant,
dsmr_connection_send_validate_fixture: tuple[MagicMock, MagicMock, MagicMock],
rfxtrx_dsmr_connection_send_validate_fixture: tuple[
@@ -456,12 +383,7 @@ async def test_setup_serial_timeout(
assert result["errors"] == {"base": "cannot_communicate"}
@patch(
"homeassistant.components.dsmr.config_flow.usb.async_scan_serial_ports",
return_value=[com_port()],
)
async def test_setup_serial_wrong_telegram(
com_mock,
hass: HomeAssistant,
dsmr_connection_send_validate_fixture: tuple[MagicMock, MagicMock, MagicMock],
rfxtrx_dsmr_connection_send_validate_fixture: tuple[