22
33from __future__ import annotations
44
5- from abc import ABC , abstractmethod
65from collections .abc import AsyncGenerator
76from typing import Any , Final , Literal
87
2019from xknx .secure .keyring import Keyring , XMLInterface
2120
2221from homeassistant .config_entries import (
22+ SOURCE_RECONFIGURE ,
2323 ConfigEntry ,
24- ConfigEntryBaseFlow ,
2524 ConfigFlow ,
2625 ConfigFlowResult ,
2726 OptionsFlow ,
103102)
104103
105104
106- class KNXCommonFlow (ABC , ConfigEntryBaseFlow ):
107- """Base class for KNX flows."""
105+ class KNXConfigFlow (ConfigFlow , domain = DOMAIN ):
106+ """Handle a KNX config flow."""
107+
108+ VERSION = 1
108109
109- def __init__ (self , initial_data : KNXConfigEntryData ) -> None :
110- """Initialize KNXCommonFlow ."""
111- self .initial_data = initial_data
110+ def __init__ (self ) -> None :
111+ """Initialize KNX config flow ."""
112+ self .initial_data = DEFAULT_ENTRY_DATA
112113 self .new_entry_data = KNXConfigEntryData ()
113114 self .new_title : str | None = None
114115
@@ -121,19 +122,21 @@ def __init__(self, initial_data: KNXConfigEntryData) -> None:
121122 self ._gatewayscanner : GatewayScanner | None = None
122123 self ._async_scan_gen : AsyncGenerator [GatewayDescriptor ] | None = None
123124
125+ @staticmethod
126+ @callback
127+ def async_get_options_flow (config_entry : ConfigEntry ) -> KNXOptionsFlow :
128+ """Get the options flow for this handler."""
129+ return KNXOptionsFlow (config_entry )
130+
124131 @property
125132 def _xknx (self ) -> XKNX :
126133 """Return XKNX instance."""
127- if isinstance (self , OptionsFlow ) and (
134+ if (self . source == SOURCE_RECONFIGURE ) and (
128135 knx_module := self .hass .data .get (KNX_MODULE_KEY )
129136 ):
130137 return knx_module .xknx
131138 return XKNX ()
132139
133- @abstractmethod
134- def finish_flow (self ) -> ConfigFlowResult :
135- """Finish the flow."""
136-
137140 @property
138141 def connection_type (self ) -> str :
139142 """Return the configured connection type."""
@@ -150,6 +153,61 @@ def tunnel_endpoint_ia(self) -> str | None:
150153 self .initial_data .get (CONF_KNX_TUNNEL_ENDPOINT_IA ),
151154 )
152155
156+ @callback
157+ def finish_flow (self ) -> ConfigFlowResult :
158+ """Create or update the ConfigEntry."""
159+ if self .source == SOURCE_RECONFIGURE :
160+ entry = self ._get_reconfigure_entry ()
161+ _tunnel_endpoint_str = self .initial_data .get (
162+ CONF_KNX_TUNNEL_ENDPOINT_IA , "Tunneling"
163+ )
164+ if self .new_title and not entry .title .startswith (
165+ # Overwrite standard titles, but not user defined ones
166+ (
167+ f"KNX { self .initial_data [CONF_KNX_CONNECTION_TYPE ]} " ,
168+ CONF_KNX_AUTOMATIC .capitalize (),
169+ "Tunneling @ " ,
170+ f"{ _tunnel_endpoint_str } @" ,
171+ "Tunneling UDP @ " ,
172+ "Tunneling TCP @ " ,
173+ "Secure Tunneling" ,
174+ "Routing as " ,
175+ "Secure Routing as " ,
176+ )
177+ ):
178+ self .new_title = None
179+ return self .async_update_reload_and_abort (
180+ self ._get_reconfigure_entry (),
181+ data_updates = self .new_entry_data ,
182+ title = self .new_title or UNDEFINED ,
183+ )
184+
185+ title = self .new_title or f"KNX { self .new_entry_data [CONF_KNX_CONNECTION_TYPE ]} "
186+ return self .async_create_entry (
187+ title = title ,
188+ data = DEFAULT_ENTRY_DATA | self .new_entry_data ,
189+ )
190+
191+ async def async_step_user (
192+ self , user_input : dict [str , Any ] | None = None
193+ ) -> ConfigFlowResult :
194+ """Handle a flow initialized by the user."""
195+ return await self .async_step_connection_type ()
196+
197+ async def async_step_reconfigure (
198+ self , user_input : dict [str , Any ] | None = None
199+ ) -> ConfigFlowResult :
200+ """Handle reconfiguration of existing entry."""
201+ entry = self ._get_reconfigure_entry ()
202+ self .initial_data = dict (entry .data ) # type: ignore[assignment]
203+ return self .async_show_menu (
204+ step_id = "reconfigure" ,
205+ menu_options = [
206+ "connection_type" ,
207+ "secure_knxkeys" ,
208+ ],
209+ )
210+
153211 async def async_step_connection_type (
154212 self , user_input : dict | None = None
155213 ) -> ConfigFlowResult :
@@ -441,7 +499,7 @@ async def async_step_manual_tunnel(
441499 )
442500 ip_address : str | None
443501 if ( # initial attempt on ConfigFlow or coming from automatic / routing
444- ( isinstance ( self , ConfigFlow ) or not _reconfiguring_existing_tunnel )
502+ not _reconfiguring_existing_tunnel
445503 and not user_input
446504 and self ._selected_tunnel is not None
447505 ): # default to first found tunnel
@@ -841,79 +899,41 @@ async def async_step_secure_key_source_menu_routing(
841899 )
842900
843901
844- class KNXConfigFlow (KNXCommonFlow , ConfigFlow , domain = DOMAIN ):
845- """Handle a KNX config flow."""
846-
847- VERSION = 1
848-
849- def __init__ (self ) -> None :
850- """Initialize KNX options flow."""
851- super ().__init__ (initial_data = DEFAULT_ENTRY_DATA )
852-
853- @staticmethod
854- @callback
855- def async_get_options_flow (config_entry : ConfigEntry ) -> KNXOptionsFlow :
856- """Get the options flow for this handler."""
857- return KNXOptionsFlow (config_entry )
858-
859- @callback
860- def finish_flow (self ) -> ConfigFlowResult :
861- """Create the ConfigEntry."""
862- title = self .new_title or f"KNX { self .new_entry_data [CONF_KNX_CONNECTION_TYPE ]} "
863- return self .async_create_entry (
864- title = title ,
865- data = DEFAULT_ENTRY_DATA | self .new_entry_data ,
866- )
867-
868- async def async_step_user (self , user_input : dict | None = None ) -> ConfigFlowResult :
869- """Handle a flow initialized by the user."""
870- return await self .async_step_connection_type ()
871-
872-
873- class KNXOptionsFlow (KNXCommonFlow , OptionsFlow ):
902+ class KNXOptionsFlow (OptionsFlow ):
874903 """Handle KNX options."""
875904
876- general_settings : dict
877-
878905 def __init__ (self , config_entry : ConfigEntry ) -> None :
879906 """Initialize KNX options flow."""
880- super (). __init__ ( initial_data = config_entry .data ) # type: ignore[arg-type]
907+ self . initial_data = dict ( config_entry .data )
881908
882909 @callback
883- def finish_flow (self ) -> ConfigFlowResult :
910+ def finish_flow (self , new_entry_data : KNXConfigEntryData ) -> ConfigFlowResult :
884911 """Update the ConfigEntry and finish the flow."""
885- new_data = DEFAULT_ENTRY_DATA | self .initial_data | self . new_entry_data
912+ new_data = self .initial_data | new_entry_data
886913 self .hass .config_entries .async_update_entry (
887914 self .config_entry ,
888915 data = new_data ,
889- title = self .new_title or UNDEFINED ,
890916 )
891917 return self .async_create_entry (title = "" , data = {})
892918
893919 async def async_step_init (
894920 self , user_input : dict [str , Any ] | None = None
895921 ) -> ConfigFlowResult :
896922 """Manage KNX options."""
897- return self .async_show_menu (
898- step_id = "init" ,
899- menu_options = [
900- "connection_type" ,
901- "communication_settings" ,
902- "secure_knxkeys" ,
903- ],
904- )
923+ return await self .async_step_communication_settings ()
905924
906925 async def async_step_communication_settings (
907926 self , user_input : dict [str , Any ] | None = None
908927 ) -> ConfigFlowResult :
909928 """Manage KNX communication settings."""
910929 if user_input is not None :
911- self .new_entry_data = KNXConfigEntryData (
912- state_updater = user_input [CONF_KNX_STATE_UPDATER ],
913- rate_limit = user_input [CONF_KNX_RATE_LIMIT ],
914- telegram_log_size = user_input [CONF_KNX_TELEGRAM_LOG_SIZE ],
930+ return self .finish_flow (
931+ KNXConfigEntryData (
932+ state_updater = user_input [CONF_KNX_STATE_UPDATER ],
933+ rate_limit = user_input [CONF_KNX_RATE_LIMIT ],
934+ telegram_log_size = user_input [CONF_KNX_TELEGRAM_LOG_SIZE ],
935+ )
915936 )
916- return self .finish_flow ()
917937
918938 data_schema = {
919939 vol .Required (
0 commit comments