Skip to content

Commit 7480d59

Browse files
authored
Normalize input for Droplet pairing code (home-assistant#157361)
1 parent 4c8d9ed commit 7480d59

File tree

2 files changed

+45
-12
lines changed

2 files changed

+45
-12
lines changed

homeassistant/components/droplet/config_flow.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
from .const import DOMAIN
1616

1717

18+
def normalize_pairing_code(code: str) -> str:
19+
"""Normalize pairing code by removing spaces and capitalizing."""
20+
return code.replace(" ", "").upper()
21+
22+
1823
class DropletConfigFlow(ConfigFlow, domain=DOMAIN):
1924
"""Handle Droplet config flow."""
2025

@@ -52,14 +57,13 @@ async def async_step_confirm(
5257
if user_input is not None:
5358
# Test if we can connect before returning
5459
session = async_get_clientsession(self.hass)
55-
if await self._droplet_discovery.try_connect(
56-
session, user_input[CONF_CODE]
57-
):
60+
code = normalize_pairing_code(user_input[CONF_CODE])
61+
if await self._droplet_discovery.try_connect(session, code):
5862
device_data = {
5963
CONF_IP_ADDRESS: self._droplet_discovery.host,
6064
CONF_PORT: self._droplet_discovery.port,
6165
CONF_DEVICE_ID: device_id,
62-
CONF_CODE: user_input[CONF_CODE],
66+
CONF_CODE: code,
6367
}
6468

6569
return self.async_create_entry(
@@ -90,14 +94,15 @@ async def async_step_user(
9094
user_input[CONF_IP_ADDRESS], DropletConnection.DEFAULT_PORT, ""
9195
)
9296
session = async_get_clientsession(self.hass)
93-
if await self._droplet_discovery.try_connect(
94-
session, user_input[CONF_CODE]
95-
) and (device_id := await self._droplet_discovery.get_device_id()):
97+
code = normalize_pairing_code(user_input[CONF_CODE])
98+
if await self._droplet_discovery.try_connect(session, code) and (
99+
device_id := await self._droplet_discovery.get_device_id()
100+
):
96101
device_data = {
97102
CONF_IP_ADDRESS: self._droplet_discovery.host,
98103
CONF_PORT: self._droplet_discovery.port,
99104
CONF_DEVICE_ID: device_id,
100-
CONF_CODE: user_input[CONF_CODE],
105+
CONF_CODE: code,
101106
}
102107
await self.async_set_unique_id(device_id, raise_on_progress=False)
103108
self._abort_if_unique_id_configured(

tests/components/droplet/test_config_flow.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,22 @@
2323
from tests.common import MockConfigEntry
2424

2525

26+
@pytest.mark.parametrize(
27+
("pre_normalized_code", "normalized_code"),
28+
[
29+
(
30+
"abc 123",
31+
"ABC123",
32+
),
33+
(" 123456 ", "123456"),
34+
("123ABC", "123ABC"),
35+
],
36+
ids=["alphanumeric_lower_space", "numeric_space", "alphanumeric_no_space"],
37+
)
2638
async def test_user_setup(
2739
hass: HomeAssistant,
40+
pre_normalized_code: str,
41+
normalized_code: str,
2842
mock_droplet_discovery: AsyncMock,
2943
mock_droplet_connection: AsyncMock,
3044
mock_droplet: AsyncMock,
@@ -39,12 +53,12 @@ async def test_user_setup(
3953

4054
result = await hass.config_entries.flow.async_configure(
4155
result["flow_id"],
42-
user_input={CONF_CODE: MOCK_CODE, CONF_IP_ADDRESS: "192.168.1.2"},
56+
user_input={CONF_CODE: pre_normalized_code, CONF_IP_ADDRESS: "192.168.1.2"},
4357
)
4458
assert result is not None
4559
assert result.get("type") is FlowResultType.CREATE_ENTRY
4660
assert result.get("data") == {
47-
CONF_CODE: MOCK_CODE,
61+
CONF_CODE: normalized_code,
4862
CONF_DEVICE_ID: MOCK_DEVICE_ID,
4963
CONF_IP_ADDRESS: MOCK_HOST,
5064
CONF_PORT: MOCK_PORT,
@@ -133,8 +147,22 @@ async def test_user_setup_already_configured(
133147
assert result.get("reason") == "already_configured"
134148

135149

150+
@pytest.mark.parametrize(
151+
("pre_normalized_code", "normalized_code"),
152+
[
153+
(
154+
"abc 123",
155+
"ABC123",
156+
),
157+
(" 123456 ", "123456"),
158+
("123ABC", "123ABC"),
159+
],
160+
ids=["alphanumeric_lower_space", "numeric_space", "alphanumeric_no_space"],
161+
)
136162
async def test_zeroconf_setup(
137163
hass: HomeAssistant,
164+
pre_normalized_code: str,
165+
normalized_code: str,
138166
mock_droplet_discovery: AsyncMock,
139167
mock_droplet: AsyncMock,
140168
mock_droplet_connection: AsyncMock,
@@ -159,15 +187,15 @@ async def test_zeroconf_setup(
159187
assert result.get("step_id") == "confirm"
160188

161189
result = await hass.config_entries.flow.async_configure(
162-
result["flow_id"], user_input={CONF_CODE: MOCK_CODE}
190+
result["flow_id"], user_input={CONF_CODE: pre_normalized_code}
163191
)
164192
assert result is not None
165193
assert result.get("type") is FlowResultType.CREATE_ENTRY
166194
assert result.get("data") == {
167195
CONF_DEVICE_ID: MOCK_DEVICE_ID,
168196
CONF_IP_ADDRESS: MOCK_HOST,
169197
CONF_PORT: MOCK_PORT,
170-
CONF_CODE: MOCK_CODE,
198+
CONF_CODE: normalized_code,
171199
}
172200
assert result.get("context") is not None
173201
assert result.get("context", {}).get("unique_id") == MOCK_DEVICE_ID

0 commit comments

Comments
 (0)