@@ -272,11 +272,15 @@ async def async_update_options(hass: HomeAssistant, entry: OpenAIConfigEntry) ->
272272async def async_migrate_integration (hass : HomeAssistant ) -> None :
273273 """Migrate integration entry structure."""
274274
275- entries = hass .config_entries .async_entries (DOMAIN )
275+ # Make sure we get enabled config entries first
276+ entries = sorted (
277+ hass .config_entries .async_entries (DOMAIN ),
278+ key = lambda e : e .disabled_by is not None ,
279+ )
276280 if not any (entry .version == 1 for entry in entries ):
277281 return
278282
279- api_keys_entries : dict [str , ConfigEntry ] = {}
283+ api_keys_entries : dict [str , tuple [ ConfigEntry , bool ] ] = {}
280284 entity_registry = er .async_get (hass )
281285 device_registry = dr .async_get (hass )
282286
@@ -290,30 +294,61 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
290294 )
291295 if entry .data [CONF_API_KEY ] not in api_keys_entries :
292296 use_existing = True
293- api_keys_entries [entry .data [CONF_API_KEY ]] = entry
297+ all_disabled = all (
298+ e .disabled_by is not None
299+ for e in entries
300+ if e .data [CONF_API_KEY ] == entry .data [CONF_API_KEY ]
301+ )
302+ api_keys_entries [entry .data [CONF_API_KEY ]] = (entry , all_disabled )
294303
295- parent_entry = api_keys_entries [entry .data [CONF_API_KEY ]]
304+ parent_entry , all_disabled = api_keys_entries [entry .data [CONF_API_KEY ]]
296305
297306 hass .config_entries .async_add_subentry (parent_entry , subentry )
298- conversation_entity = entity_registry .async_get_entity_id (
307+ conversation_entity_id = entity_registry .async_get_entity_id (
299308 "conversation" ,
300309 DOMAIN ,
301310 entry .entry_id ,
302311 )
303- if conversation_entity is not None :
312+ device = device_registry .async_get_device (
313+ identifiers = {(DOMAIN , entry .entry_id )}
314+ )
315+
316+ if conversation_entity_id is not None :
317+ conversation_entity_entry = entity_registry .entities [conversation_entity_id ]
318+ entity_disabled_by = conversation_entity_entry .disabled_by
319+ if (
320+ entity_disabled_by is er .RegistryEntryDisabler .CONFIG_ENTRY
321+ and not all_disabled
322+ ):
323+ # Device and entity registries don't update the disabled_by flag
324+ # when moving a device or entity from one config entry to another,
325+ # so we need to do it manually.
326+ entity_disabled_by = (
327+ er .RegistryEntryDisabler .DEVICE
328+ if device
329+ else er .RegistryEntryDisabler .USER
330+ )
304331 entity_registry .async_update_entity (
305- conversation_entity ,
332+ conversation_entity_id ,
306333 config_entry_id = parent_entry .entry_id ,
307334 config_subentry_id = subentry .subentry_id ,
335+ disabled_by = entity_disabled_by ,
308336 new_unique_id = subentry .subentry_id ,
309337 )
310338
311- device = device_registry .async_get_device (
312- identifiers = {(DOMAIN , entry .entry_id )}
313- )
314339 if device is not None :
340+ # Device and entity registries don't update the disabled_by flag when
341+ # moving a device or entity from one config entry to another, so we
342+ # need to do it manually.
343+ device_disabled_by = device .disabled_by
344+ if (
345+ device .disabled_by is dr .DeviceEntryDisabler .CONFIG_ENTRY
346+ and not all_disabled
347+ ):
348+ device_disabled_by = dr .DeviceEntryDisabler .USER
315349 device_registry .async_update_device (
316350 device .id ,
351+ disabled_by = device_disabled_by ,
317352 new_identifiers = {(DOMAIN , subentry .subentry_id )},
318353 add_config_subentry_id = subentry .subentry_id ,
319354 add_config_entry_id = parent_entry .entry_id ,
@@ -333,12 +368,13 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
333368 if not use_existing :
334369 await hass .config_entries .async_remove (entry .entry_id )
335370 else :
371+ _add_ai_task_subentry (hass , entry )
336372 hass .config_entries .async_update_entry (
337373 entry ,
338374 title = DEFAULT_NAME ,
339375 options = {},
340376 version = 2 ,
341- minor_version = 2 ,
377+ minor_version = 4 ,
342378 )
343379
344380
@@ -365,19 +401,56 @@ async def async_migrate_entry(hass: HomeAssistant, entry: OpenAIConfigEntry) ->
365401 hass .config_entries .async_update_entry (entry , minor_version = 2 )
366402
367403 if entry .version == 2 and entry .minor_version == 2 :
368- hass .config_entries .async_add_subentry (
369- entry ,
370- ConfigSubentry (
371- data = MappingProxyType (RECOMMENDED_AI_TASK_OPTIONS ),
372- subentry_type = "ai_task_data" ,
373- title = DEFAULT_AI_TASK_NAME ,
374- unique_id = None ,
375- ),
376- )
404+ _add_ai_task_subentry (hass , entry )
377405 hass .config_entries .async_update_entry (entry , minor_version = 3 )
378406
407+ if entry .version == 2 and entry .minor_version == 3 :
408+ # Fix migration where the disabled_by flag was not set correctly.
409+ # We can currently only correct this for enabled config entries,
410+ # because migration does not run for disabled config entries. This
411+ # is asserted in tests, and if that behavior is changed, we should
412+ # correct also disabled config entries.
413+ device_registry = dr .async_get (hass )
414+ entity_registry = er .async_get (hass )
415+ devices = dr .async_entries_for_config_entry (device_registry , entry .entry_id )
416+ entity_entries = er .async_entries_for_config_entry (
417+ entity_registry , entry .entry_id
418+ )
419+ if entry .disabled_by is None :
420+ # If the config entry is not disabled, we need to set the disabled_by
421+ # flag on devices to USER, and on entities to DEVICE, if they are set
422+ # to CONFIG_ENTRY.
423+ for device in devices :
424+ if device .disabled_by is not dr .DeviceEntryDisabler .CONFIG_ENTRY :
425+ continue
426+ device_registry .async_update_device (
427+ device .id ,
428+ disabled_by = dr .DeviceEntryDisabler .USER ,
429+ )
430+ for entity in entity_entries :
431+ if entity .disabled_by is not er .RegistryEntryDisabler .CONFIG_ENTRY :
432+ continue
433+ entity_registry .async_update_entity (
434+ entity .entity_id ,
435+ disabled_by = er .RegistryEntryDisabler .DEVICE ,
436+ )
437+ hass .config_entries .async_update_entry (entry , minor_version = 4 )
438+
379439 LOGGER .debug (
380440 "Migration to version %s:%s successful" , entry .version , entry .minor_version
381441 )
382442
383443 return True
444+
445+
446+ def _add_ai_task_subentry (hass : HomeAssistant , entry : OpenAIConfigEntry ) -> None :
447+ """Add AI Task subentry to the config entry."""
448+ hass .config_entries .async_add_subentry (
449+ entry ,
450+ ConfigSubentry (
451+ data = MappingProxyType (RECOMMENDED_AI_TASK_OPTIONS ),
452+ subentry_type = "ai_task_data" ,
453+ title = DEFAULT_AI_TASK_NAME ,
454+ unique_id = None ,
455+ ),
456+ )
0 commit comments