Fix KNX onboarding when there is no yaml config defined yet (#64216)

This commit is contained in:
Matthias Alphart 2022-01-17 05:44:21 +01:00 committed by GitHub
parent 3303ad38fe
commit 456d403303
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 42 deletions

View File

@ -209,7 +209,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return bool(hass.config_entries.async_entries(DOMAIN)) return bool(hass.config_entries.async_entries(DOMAIN))
conf = dict(conf) conf = dict(conf)
hass.data[DATA_KNX_CONFIG] = conf hass.data[DATA_KNX_CONFIG] = conf
# Only import if we haven't before. # Only import if we haven't before.
@ -226,19 +225,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Load a config entry.""" """Load a config entry."""
conf = hass.data.get(DATA_KNX_CONFIG) conf = hass.data.get(DATA_KNX_CONFIG)
# `conf` is None when reloading the integration or no `knx` key in configuration.yaml
# When reloading
if conf is None: if conf is None:
conf = await async_integration_yaml_config(hass, DOMAIN) _conf = await async_integration_yaml_config(hass, DOMAIN)
if not conf or DOMAIN not in conf: if not _conf or DOMAIN not in _conf:
return False _LOGGER.warning(
"No `knx:` key found in configuration.yaml. See "
conf = conf[DOMAIN] "https://www.home-assistant.io/integrations/knx/ "
"for KNX entity configuration documentation"
# If user didn't have configuration.yaml config, generate defaults )
if conf is None: # generate defaults
conf = CONFIG_SCHEMA({DOMAIN: dict(entry.data)})[DOMAIN] conf = CONFIG_SCHEMA({DOMAIN: {}})[DOMAIN]
else:
conf = _conf[DOMAIN]
config = {**conf, **entry.data} config = {**conf, **entry.data}
try: try:
@ -366,7 +365,6 @@ class KNXModule:
self.entry.async_on_unload( self.entry.async_on_unload(
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.stop) self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.stop)
) )
self.entry.async_on_unload(self.entry.add_update_listener(async_update_entry)) self.entry.async_on_unload(self.entry.add_update_listener(async_update_entry))
def init_xknx(self) -> None: def init_xknx(self) -> None:
@ -406,7 +404,6 @@ class KNXModule:
route_back=self.config.get(ConnectionSchema.CONF_KNX_ROUTE_BACK, False), route_back=self.config.get(ConnectionSchema.CONF_KNX_ROUTE_BACK, False),
auto_reconnect=True, auto_reconnect=True,
) )
return ConnectionConfig(auto_reconnect=True) return ConnectionConfig(auto_reconnect=True)
async def connection_state_changed_cb(self, state: XknxConnectionState) -> None: async def connection_state_changed_cb(self, state: XknxConnectionState) -> None:

View File

@ -44,7 +44,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1 VERSION = 1
_tunnels: list _tunnels: list[GatewayDescriptor]
_gateway_ip: str = "" _gateway_ip: str = ""
_gateway_port: int = DEFAULT_MCAST_PORT _gateway_port: int = DEFAULT_MCAST_PORT
@ -64,25 +64,6 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_type(self, user_input: dict | None = None) -> FlowResult: async def async_step_type(self, user_input: dict | None = None) -> FlowResult:
"""Handle connection type configuration.""" """Handle connection type configuration."""
errors: dict = {}
supported_connection_types = CONF_KNX_INITIAL_CONNECTION_TYPES.copy()
fields = {}
if user_input is None:
gateways = await scan_for_gateways()
if gateways:
supported_connection_types.insert(0, CONF_KNX_AUTOMATIC)
self._tunnels = [
gateway for gateway in gateways if gateway.supports_tunnelling
]
fields = {
vol.Required(CONF_KNX_CONNECTION_TYPE): vol.In(
supported_connection_types
)
}
if user_input is not None: if user_input is not None:
connection_type = user_input[CONF_KNX_CONNECTION_TYPE] connection_type = user_input[CONF_KNX_CONNECTION_TYPE]
if connection_type == CONF_KNX_AUTOMATIC: if connection_type == CONF_KNX_AUTOMATIC:
@ -99,6 +80,22 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_manual_tunnel() return await self.async_step_manual_tunnel()
errors: dict = {}
supported_connection_types = CONF_KNX_INITIAL_CONNECTION_TYPES.copy()
fields = {}
gateways = await scan_for_gateways()
if gateways:
# add automatic only if a gateway responded
supported_connection_types.insert(0, CONF_KNX_AUTOMATIC)
self._tunnels = [
gateway for gateway in gateways if gateway.supports_tunnelling
]
fields = {
vol.Required(CONF_KNX_CONNECTION_TYPE): vol.In(supported_connection_types)
}
return self.async_show_form( return self.async_show_form(
step_id="type", data_schema=vol.Schema(fields), errors=errors step_id="type", data_schema=vol.Schema(fields), errors=errors
) )
@ -107,8 +104,6 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self, user_input: dict | None = None self, user_input: dict | None = None
) -> FlowResult: ) -> FlowResult:
"""General setup.""" """General setup."""
errors: dict = {}
if user_input is not None: if user_input is not None:
return self.async_create_entry( return self.async_create_entry(
title=f"{CONF_KNX_TUNNELING.capitalize()} @ {user_input[CONF_HOST]}", title=f"{CONF_KNX_TUNNELING.capitalize()} @ {user_input[CONF_HOST]}",
@ -129,6 +124,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
}, },
) )
errors: dict = {}
fields = { fields = {
vol.Required(CONF_HOST, default=self._gateway_ip): str, vol.Required(CONF_HOST, default=self._gateway_ip): str,
vol.Required(CONF_PORT, default=self._gateway_port): vol.Coerce(int), vol.Required(CONF_PORT, default=self._gateway_port): vol.Coerce(int),
@ -149,8 +145,6 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_tunnel(self, user_input: dict | None = None) -> FlowResult: async def async_step_tunnel(self, user_input: dict | None = None) -> FlowResult:
"""Select a tunnel from a list. Will be skipped if the gateway scan was unsuccessful or if only one gateway was found.""" """Select a tunnel from a list. Will be skipped if the gateway scan was unsuccessful or if only one gateway was found."""
errors: dict = {}
if user_input is not None: if user_input is not None:
gateway: GatewayDescriptor = next( gateway: GatewayDescriptor = next(
gateway gateway
@ -163,6 +157,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_manual_tunnel() return await self.async_step_manual_tunnel()
errors: dict = {}
tunnel_repr = { tunnel_repr = {
str(tunnel) for tunnel in self._tunnels if tunnel.supports_tunnelling str(tunnel) for tunnel in self._tunnels if tunnel.supports_tunnelling
} }
@ -182,8 +177,6 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_routing(self, user_input: dict | None = None) -> FlowResult: async def async_step_routing(self, user_input: dict | None = None) -> FlowResult:
"""Routing setup.""" """Routing setup."""
errors: dict = {}
if user_input is not None: if user_input is not None:
return self.async_create_entry( return self.async_create_entry(
title=CONF_KNX_ROUTING.capitalize(), title=CONF_KNX_ROUTING.capitalize(),
@ -205,6 +198,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
}, },
) )
errors: dict = {}
fields = { fields = {
vol.Required( vol.Required(
CONF_KNX_INDIVIDUAL_ADDRESS, default=XKNX.DEFAULT_ADDRESS CONF_KNX_INDIVIDUAL_ADDRESS, default=XKNX.DEFAULT_ADDRESS
@ -434,7 +428,7 @@ class KNXOptionsFlowHandler(OptionsFlow):
) )
async def scan_for_gateways(stop_on_found: int = 0) -> list: async def scan_for_gateways(stop_on_found: int = 0) -> list[GatewayDescriptor]:
"""Scan for gateways within the network.""" """Scan for gateways within the network."""
xknx = XKNX() xknx = XKNX()
gatewayscanner = GatewayScanner( gatewayscanner = GatewayScanner(