diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py index c043ea65ee5..4ee46939f2d 100644 --- a/homeassistant/components/knx/config_flow.py +++ b/homeassistant/components/knx/config_flow.py @@ -313,7 +313,6 @@ class KNXCommonFlow(ABC, FlowHandler): if user_input is not None: self.new_entry_data |= KNXConfigEntryData( - connection_type=CONF_KNX_TUNNELING_TCP_SECURE, device_authentication=user_input[CONF_KNX_SECURE_DEVICE_AUTHENTICATION], user_id=user_input[CONF_KNX_SECURE_USER_ID], user_password=user_input[CONF_KNX_SECURE_USER_PASSWORD], @@ -428,10 +427,13 @@ class KNXCommonFlow(ABC, FlowHandler): if not errors: self.new_entry_data |= KNXConfigEntryData( - backbone_key=None, - sync_latency_tolerance=None, knxkeys_filename=storage_key, knxkeys_password=user_input[CONF_KNX_KNXKEY_PASSWORD], + backbone_key=None, + sync_latency_tolerance=None, + device_authentication=None, + user_id=None, + user_password=None, ) if ( self.new_entry_data[CONF_KNX_CONNECTION_TYPE] @@ -442,10 +444,11 @@ class KNXCommonFlow(ABC, FlowHandler): title = f"Secure Tunneling @ {self.new_entry_data[CONF_HOST]}" return self.finish_flow(title=title) + if _default_filename := self.initial_data.get(CONF_KNX_KNXKEY_FILENAME): + _default_filename = _default_filename.lstrip(CONST_KNX_STORAGE_KEY) fields = { vol.Required( - CONF_KNX_KNXKEY_FILENAME, - default=self.initial_data.get(CONF_KNX_KNXKEY_FILENAME), + CONF_KNX_KNXKEY_FILENAME, default=_default_filename ): selector.TextSelector(), vol.Required( CONF_KNX_KNXKEY_PASSWORD, diff --git a/homeassistant/components/knx/const.py b/homeassistant/components/knx/const.py index 4a8b113516c..f98d42fbd9e 100644 --- a/homeassistant/components/knx/const.py +++ b/homeassistant/components/knx/const.py @@ -90,9 +90,9 @@ class KNXConfigEntryData(TypedDict, total=False): host: str port: int - user_id: int - user_password: str - device_authentication: str + user_id: int | None + user_password: str | None + device_authentication: str | None knxkeys_filename: str knxkeys_password: str backbone_key: str | None diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index eb593d20924..c5e26561b92 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -383,6 +383,9 @@ async def test_routing_secure_keyfile( CONF_KNX_KNXKEY_PASSWORD: "password", CONF_KNX_ROUTING_BACKBONE_KEY: None, CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, + CONF_KNX_SECURE_USER_ID: None, + CONF_KNX_SECURE_USER_PASSWORD: None, CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", } assert len(mock_setup_entry.mock_calls) == 1 @@ -876,6 +879,9 @@ async def test_configure_secure_knxkeys(hass: HomeAssistant): CONF_KNX_KNXKEY_PASSWORD: "password", CONF_KNX_ROUTING_BACKBONE_KEY: None, CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, + CONF_KNX_SECURE_USER_ID: None, + CONF_KNX_SECURE_USER_PASSWORD: None, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", @@ -999,6 +1005,100 @@ async def test_options_flow_connection_type( } +async def test_options_flow_secure_manual_to_keyfile(hass: HomeAssistant) -> None: + """Test options flow changing secure credential source.""" + mock_config_entry = MockConfigEntry( + title="KNX", + domain="knx", + data={ + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, + CONF_KNX_SECURE_USER_ID: 2, + CONF_KNX_SECURE_USER_PASSWORD: "password", + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: "device_auth", + CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "invalid_password", + CONF_HOST: "192.168.0.1", + CONF_PORT: 3675, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", + CONF_KNX_ROUTE_BACK: False, + CONF_KNX_LOCAL_IP: None, + }, + ) + mock_config_entry.add_to_hass(hass) + gateway = _gateway_descriptor( + "192.168.0.1", + 3675, + supports_tunnelling_tcp=True, + requires_secure=True, + ) + + menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) + with patch( + "homeassistant.components.knx.config_flow.GatewayScanner" + ) as gateway_scanner_mock: + gateway_scanner_mock.return_value = GatewayScannerMock([gateway]) + result = await hass.config_entries.options.async_configure( + menu_step["flow_id"], + {"next_step_id": "connection_type"}, + ) + assert result.get("type") == FlowResultType.FORM + assert result.get("step_id") == "connection_type" + + result2 = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, + }, + ) + assert result2["type"] == FlowResultType.FORM + assert result2["step_id"] == "tunnel" + assert not result2["errors"] + + result3 = await hass.config_entries.options.async_configure( + result2["flow_id"], + {CONF_KNX_GATEWAY: str(gateway)}, + ) + assert result3["type"] == FlowResultType.MENU + assert result3["step_id"] == "secure_key_source" + + result4 = await hass.config_entries.options.async_configure( + result3["flow_id"], + {"next_step_id": "secure_knxkeys"}, + ) + assert result4["type"] == FlowResultType.FORM + assert result4["step_id"] == "secure_knxkeys" + assert not result4["errors"] + + with patch( + "homeassistant.components.knx.config_flow.load_keyring", return_value=True + ): + secure_knxkeys = await hass.config_entries.options.async_configure( + result4["flow_id"], + { + CONF_KNX_KNXKEY_FILENAME: "testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + }, + ) + assert secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY + assert mock_config_entry.data == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, + CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, + CONF_KNX_SECURE_USER_ID: None, + CONF_KNX_SECURE_USER_PASSWORD: None, + CONF_KNX_ROUTING_BACKBONE_KEY: None, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3675, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", + CONF_KNX_ROUTE_BACK: False, + CONF_KNX_LOCAL_IP: None, + } + + async def test_options_communication_settings( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: