@@ -81,11 +81,15 @@ async def async_update_options(
8181async def async_migrate_integration (hass : HomeAssistant ) -> None :
8282 """Migrate integration entry structure."""
8383
84- entries = hass .config_entries .async_entries (DOMAIN )
84+ # Make sure we get enabled config entries first
85+ entries = sorted (
86+ hass .config_entries .async_entries (DOMAIN ),
87+ key = lambda e : e .disabled_by is not None ,
88+ )
8589 if not any (entry .version == 1 for entry in entries ):
8690 return
8791
88- api_keys_entries : dict [str , ConfigEntry ] = {}
92+ api_keys_entries : dict [str , tuple [ ConfigEntry , bool ] ] = {}
8993 entity_registry = er .async_get (hass )
9094 device_registry = dr .async_get (hass )
9195
@@ -99,30 +103,61 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
99103 )
100104 if entry .data [CONF_API_KEY ] not in api_keys_entries :
101105 use_existing = True
102- api_keys_entries [entry .data [CONF_API_KEY ]] = entry
106+ all_disabled = all (
107+ e .disabled_by is not None
108+ for e in entries
109+ if e .data [CONF_API_KEY ] == entry .data [CONF_API_KEY ]
110+ )
111+ api_keys_entries [entry .data [CONF_API_KEY ]] = (entry , all_disabled )
103112
104- parent_entry = api_keys_entries [entry .data [CONF_API_KEY ]]
113+ parent_entry , all_disabled = api_keys_entries [entry .data [CONF_API_KEY ]]
105114
106115 hass .config_entries .async_add_subentry (parent_entry , subentry )
107- conversation_entity = entity_registry .async_get_entity_id (
116+ conversation_entity_id = entity_registry .async_get_entity_id (
108117 "conversation" ,
109118 DOMAIN ,
110119 entry .entry_id ,
111120 )
112- if conversation_entity is not None :
121+ device = device_registry .async_get_device (
122+ identifiers = {(DOMAIN , entry .entry_id )}
123+ )
124+
125+ if conversation_entity_id is not None :
126+ conversation_entity_entry = entity_registry .entities [conversation_entity_id ]
127+ entity_disabled_by = conversation_entity_entry .disabled_by
128+ if (
129+ entity_disabled_by is er .RegistryEntryDisabler .CONFIG_ENTRY
130+ and not all_disabled
131+ ):
132+ # Device and entity registries don't update the disabled_by flag
133+ # when moving a device or entity from one config entry to another,
134+ # so we need to do it manually.
135+ entity_disabled_by = (
136+ er .RegistryEntryDisabler .DEVICE
137+ if device
138+ else er .RegistryEntryDisabler .USER
139+ )
113140 entity_registry .async_update_entity (
114- conversation_entity ,
141+ conversation_entity_id ,
115142 config_entry_id = parent_entry .entry_id ,
116143 config_subentry_id = subentry .subentry_id ,
144+ disabled_by = entity_disabled_by ,
117145 new_unique_id = subentry .subentry_id ,
118146 )
119147
120- device = device_registry .async_get_device (
121- identifiers = {(DOMAIN , entry .entry_id )}
122- )
123148 if device is not None :
149+ # Device and entity registries don't update the disabled_by flag when
150+ # moving a device or entity from one config entry to another, so we
151+ # need to do it manually.
152+ device_disabled_by = device .disabled_by
153+ if (
154+ device .disabled_by is dr .DeviceEntryDisabler .CONFIG_ENTRY
155+ and not all_disabled
156+ ):
157+ device_disabled_by = dr .DeviceEntryDisabler .USER
124158 device_registry .async_update_device (
125159 device .id ,
160+ disabled_by = device_disabled_by ,
126161 new_identifiers = {(DOMAIN , subentry .subentry_id )},
127162 add_config_subentry_id = subentry .subentry_id ,
128163 add_config_entry_id = parent_entry .entry_id ,
@@ -147,7 +182,7 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
147182 title = DEFAULT_CONVERSATION_NAME ,
148183 options = {},
149184 version = 2 ,
150- minor_version = 2 ,
185+ minor_version = 3 ,
151186 )
152187
153188
@@ -173,6 +208,38 @@ async def async_migrate_entry(hass: HomeAssistant, entry: AnthropicConfigEntry)
173208
174209 hass .config_entries .async_update_entry (entry , minor_version = 2 )
175210
211+ if entry .version == 2 and entry .minor_version == 2 :
212+ # Fix migration where the disabled_by flag was not set correctly.
213+ # We can currently only correct this for enabled config entries,
214+ # because migration does not run for disabled config entries. This
215+ # is asserted in tests, and if that behavior is changed, we should
216+ # correct also disabled config entries.
217+ device_registry = dr .async_get (hass )
218+ entity_registry = er .async_get (hass )
219+ devices = dr .async_entries_for_config_entry (device_registry , entry .entry_id )
220+ entity_entries = er .async_entries_for_config_entry (
221+ entity_registry , entry .entry_id
222+ )
223+ if entry .disabled_by is None :
224+ # If the config entry is not disabled, we need to set the disabled_by
225+ # flag on devices to USER, and on entities to DEVICE, if they are set
226+ # to CONFIG_ENTRY.
227+ for device in devices :
228+ if device .disabled_by is not dr .DeviceEntryDisabler .CONFIG_ENTRY :
229+ continue
230+ device_registry .async_update_device (
231+ device .id ,
232+ disabled_by = dr .DeviceEntryDisabler .USER ,
233+ )
234+ for entity in entity_entries :
235+ if entity .disabled_by is not er .RegistryEntryDisabler .CONFIG_ENTRY :
236+ continue
237+ entity_registry .async_update_entity (
238+ entity .entity_id ,
239+ disabled_by = er .RegistryEntryDisabler .DEVICE ,
240+ )
241+ hass .config_entries .async_update_entry (entry , minor_version = 3 )
242+
176243 LOGGER .debug (
177244 "Migration to version %s:%s successful" , entry .version , entry .minor_version
178245 )
0 commit comments