Skip to content

Commit 5bb96f7

Browse files
authored
Adjust device disabled_by flag when changing config entry (home-assistant#151155)
1 parent dfbe42f commit 5bb96f7

File tree

10 files changed

+390
-56
lines changed

10 files changed

+390
-56
lines changed

homeassistant/components/anthropic/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
129129
entity_disabled_by is er.RegistryEntryDisabler.CONFIG_ENTRY
130130
and not all_disabled
131131
):
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.
132+
# Device and entity registries will set the disabled_by flag to None
133+
# when moving a device or entity disabled by CONFIG_ENTRY to an enabled
134+
# config entry, but we want to set it to DEVICE or USER instead,
135135
entity_disabled_by = (
136136
er.RegistryEntryDisabler.DEVICE
137137
if device
@@ -146,9 +146,9 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
146146
)
147147

148148
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.
149+
# Device and entity registries will set the disabled_by flag to None
150+
# when moving a device or entity disabled by CONFIG_ENTRY to an enabled
151+
# config entry, but we want to set it to USER instead,
152152
device_disabled_by = device.disabled_by
153153
if (
154154
device.disabled_by is dr.DeviceEntryDisabler.CONFIG_ENTRY

homeassistant/components/google_generative_ai_conversation/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,9 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
260260
entity_disabled_by is er.RegistryEntryDisabler.CONFIG_ENTRY
261261
and not all_disabled
262262
):
263-
# Device and entity registries don't update the disabled_by flag
264-
# when moving a device or entity from one config entry to another,
265-
# so we need to do it manually.
263+
# Device and entity registries will set the disabled_by flag to None
264+
# when moving a device or entity disabled by CONFIG_ENTRY to an enabled
265+
# config entry, but we want to set it to DEVICE or USER instead,
266266
entity_disabled_by = (
267267
er.RegistryEntryDisabler.DEVICE
268268
if device
@@ -277,9 +277,9 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
277277
)
278278

279279
if device is not None:
280-
# Device and entity registries don't update the disabled_by flag when
281-
# moving a device or entity from one config entry to another, so we
282-
# need to do it manually.
280+
# Device and entity registries will set the disabled_by flag to None
281+
# when moving a device or entity disabled by CONFIG_ENTRY to an enabled
282+
# config entry, but we want to set it to USER instead,
283283
device_disabled_by = device.disabled_by
284284
if (
285285
device.disabled_by is dr.DeviceEntryDisabler.CONFIG_ENTRY

homeassistant/components/ollama/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
145145
entity_disabled_by is er.RegistryEntryDisabler.CONFIG_ENTRY
146146
and not all_disabled
147147
):
148-
# Device and entity registries don't update the disabled_by flag
149-
# when moving a device or entity from one config entry to another,
150-
# so we need to do it manually.
148+
# Device and entity registries will set the disabled_by flag to None
149+
# when moving a device or entity disabled by CONFIG_ENTRY to an enabled
150+
# config entry, but we want to set it to DEVICE or USER instead,
151151
entity_disabled_by = (
152152
er.RegistryEntryDisabler.DEVICE
153153
if device
@@ -162,9 +162,9 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
162162
)
163163

164164
if device is not None:
165-
# Device and entity registries don't update the disabled_by flag when
166-
# moving a device or entity from one config entry to another, so we
167-
# need to do it manually.
165+
# Device and entity registries will set the disabled_by flag to None
166+
# when moving a device or entity disabled by CONFIG_ENTRY to an enabled
167+
# config entry, but we want to set it to USER instead,
168168
device_disabled_by = device.disabled_by
169169
if (
170170
device.disabled_by is dr.DeviceEntryDisabler.CONFIG_ENTRY

homeassistant/components/openai_conversation/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,9 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
320320
entity_disabled_by is er.RegistryEntryDisabler.CONFIG_ENTRY
321321
and not all_disabled
322322
):
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.
323+
# Device and entity registries will set the disabled_by flag to None
324+
# when moving a device or entity disabled by CONFIG_ENTRY to an enabled
325+
# config entry, but we want to set it to DEVICE or USER instead,
326326
entity_disabled_by = (
327327
er.RegistryEntryDisabler.DEVICE
328328
if device
@@ -337,9 +337,9 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
337337
)
338338

339339
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.
340+
# Device and entity registries will set the disabled_by flag to None
341+
# when moving a device or entity disabled by CONFIG_ENTRY to an enabled
342+
# config entry, but we want to set it to USER instead,
343343
device_disabled_by = device.disabled_by
344344
if (
345345
device.disabled_by is dr.DeviceEntryDisabler.CONFIG_ENTRY

homeassistant/helpers/device_registry.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,16 @@ def async_update_device( # noqa: C901
11151115
config_entries_subentries = old.config_entries_subentries | {
11161116
add_config_entry_id: {add_config_subentry_id}
11171117
}
1118+
# Enable the device if it was disabled by config entry and we're adding
1119+
# a non disabled config entry
1120+
if (
1121+
# mypy says add_config_entry can be None. That's impossible, because we
1122+
# raise above if that happens
1123+
not add_config_entry.disabled_by # type: ignore[union-attr]
1124+
and old.disabled_by is DeviceEntryDisabler.CONFIG_ENTRY
1125+
):
1126+
new_values["disabled_by"] = None
1127+
old_values["disabled_by"] = old.disabled_by
11181128
elif (
11191129
add_config_subentry_id
11201130
not in old.config_entries_subentries[add_config_entry_id]
@@ -1157,6 +1167,22 @@ def async_update_device( # noqa: C901
11571167

11581168
config_entries = config_entries - {remove_config_entry_id}
11591169

1170+
# Disable the device if it is enabled and all remaining config entries
1171+
# are disabled
1172+
has_enabled_config_entries = any(
1173+
config_entry.disabled_by is None
1174+
for config_entry_id in config_entries
1175+
if (
1176+
config_entry := self.hass.config_entries.async_get_entry(
1177+
config_entry_id
1178+
)
1179+
)
1180+
is not None
1181+
)
1182+
if not has_enabled_config_entries and old.disabled_by is None:
1183+
new_values["disabled_by"] = DeviceEntryDisabler.CONFIG_ENTRY
1184+
old_values["disabled_by"] = old.disabled_by
1185+
11601186
if config_entries != old.config_entries:
11611187
new_values["config_entries"] = config_entries
11621188
old_values["config_entries"] = old.config_entries

tests/components/anthropic/test_init.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,17 @@ async def test_migration_from_v1_to_v2(
156156
@pytest.mark.parametrize(
157157
(
158158
"config_entry_disabled_by",
159+
"device_disabled_by",
160+
"entity_disabled_by",
159161
"merged_config_entry_disabled_by",
160162
"conversation_subentry_data",
161163
"main_config_entry",
162164
),
163165
[
164166
(
165167
[ConfigEntryDisabler.USER, None],
168+
[DeviceEntryDisabler.CONFIG_ENTRY, None],
169+
[RegistryEntryDisabler.CONFIG_ENTRY, None],
166170
None,
167171
[
168172
{
@@ -182,25 +186,29 @@ async def test_migration_from_v1_to_v2(
182186
),
183187
(
184188
[None, ConfigEntryDisabler.USER],
189+
[None, DeviceEntryDisabler.CONFIG_ENTRY],
190+
[None, RegistryEntryDisabler.CONFIG_ENTRY],
185191
None,
186192
[
187193
{
188194
"conversation_entity_id": "conversation.claude",
189-
"device_disabled_by": DeviceEntryDisabler.USER,
190-
"entity_disabled_by": RegistryEntryDisabler.DEVICE,
195+
"device_disabled_by": None,
196+
"entity_disabled_by": None,
191197
"device": 0,
192198
},
193199
{
194200
"conversation_entity_id": "conversation.claude_2",
195-
"device_disabled_by": None,
196-
"entity_disabled_by": None,
201+
"device_disabled_by": DeviceEntryDisabler.USER,
202+
"entity_disabled_by": RegistryEntryDisabler.DEVICE,
197203
"device": 1,
198204
},
199205
],
200206
0,
201207
),
202208
(
203209
[ConfigEntryDisabler.USER, ConfigEntryDisabler.USER],
210+
[DeviceEntryDisabler.CONFIG_ENTRY, DeviceEntryDisabler.CONFIG_ENTRY],
211+
[RegistryEntryDisabler.CONFIG_ENTRY, RegistryEntryDisabler.CONFIG_ENTRY],
204212
ConfigEntryDisabler.USER,
205213
[
206214
{
@@ -211,8 +219,8 @@ async def test_migration_from_v1_to_v2(
211219
},
212220
{
213221
"conversation_entity_id": "conversation.claude_2",
214-
"device_disabled_by": None,
215-
"entity_disabled_by": None,
222+
"device_disabled_by": DeviceEntryDisabler.CONFIG_ENTRY,
223+
"entity_disabled_by": RegistryEntryDisabler.CONFIG_ENTRY,
216224
"device": 1,
217225
},
218226
],
@@ -225,6 +233,8 @@ async def test_migration_from_v1_disabled(
225233
device_registry: dr.DeviceRegistry,
226234
entity_registry: er.EntityRegistry,
227235
config_entry_disabled_by: list[ConfigEntryDisabler | None],
236+
device_disabled_by: list[DeviceEntryDisabler | None],
237+
entity_disabled_by: list[RegistryEntryDisabler | None],
228238
merged_config_entry_disabled_by: ConfigEntryDisabler | None,
229239
conversation_subentry_data: list[dict[str, Any]],
230240
main_config_entry: int,
@@ -264,7 +274,7 @@ async def test_migration_from_v1_disabled(
264274
manufacturer="Anthropic",
265275
model="Claude",
266276
entry_type=dr.DeviceEntryType.SERVICE,
267-
disabled_by=DeviceEntryDisabler.CONFIG_ENTRY,
277+
disabled_by=device_disabled_by[0],
268278
)
269279
entity_registry.async_get_or_create(
270280
"conversation",
@@ -273,7 +283,7 @@ async def test_migration_from_v1_disabled(
273283
config_entry=mock_config_entry,
274284
device_id=device_1.id,
275285
suggested_object_id="claude",
276-
disabled_by=RegistryEntryDisabler.CONFIG_ENTRY,
286+
disabled_by=entity_disabled_by[0],
277287
)
278288

279289
device_2 = device_registry.async_get_or_create(
@@ -283,6 +293,7 @@ async def test_migration_from_v1_disabled(
283293
manufacturer="Anthropic",
284294
model="Claude",
285295
entry_type=dr.DeviceEntryType.SERVICE,
296+
disabled_by=device_disabled_by[1],
286297
)
287298
entity_registry.async_get_or_create(
288299
"conversation",
@@ -291,6 +302,7 @@ async def test_migration_from_v1_disabled(
291302
config_entry=mock_config_entry_2,
292303
device_id=device_2.id,
293304
suggested_object_id="claude",
305+
disabled_by=entity_disabled_by[1],
294306
)
295307

296308
devices = [device_1, device_2]

tests/components/google_generative_ai_conversation/test_init.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -576,13 +576,17 @@ async def test_migration_from_v1(
576576
@pytest.mark.parametrize(
577577
(
578578
"config_entry_disabled_by",
579+
"device_disabled_by",
580+
"entity_disabled_by",
579581
"merged_config_entry_disabled_by",
580582
"conversation_subentry_data",
581583
"main_config_entry",
582584
),
583585
[
584586
(
585587
[ConfigEntryDisabler.USER, None],
588+
[DeviceEntryDisabler.CONFIG_ENTRY, None],
589+
[RegistryEntryDisabler.CONFIG_ENTRY, None],
586590
None,
587591
[
588592
{
@@ -602,25 +606,29 @@ async def test_migration_from_v1(
602606
),
603607
(
604608
[None, ConfigEntryDisabler.USER],
609+
[None, DeviceEntryDisabler.CONFIG_ENTRY],
610+
[None, RegistryEntryDisabler.CONFIG_ENTRY],
605611
None,
606612
[
607613
{
608614
"conversation_entity_id": "conversation.google_generative_ai_conversation",
609-
"device_disabled_by": DeviceEntryDisabler.USER,
610-
"entity_disabled_by": RegistryEntryDisabler.DEVICE,
615+
"device_disabled_by": None,
616+
"entity_disabled_by": None,
611617
"device": 0,
612618
},
613619
{
614620
"conversation_entity_id": "conversation.google_generative_ai_conversation_2",
615-
"device_disabled_by": None,
616-
"entity_disabled_by": None,
621+
"device_disabled_by": DeviceEntryDisabler.USER,
622+
"entity_disabled_by": RegistryEntryDisabler.DEVICE,
617623
"device": 1,
618624
},
619625
],
620626
0,
621627
),
622628
(
623629
[ConfigEntryDisabler.USER, ConfigEntryDisabler.USER],
630+
[DeviceEntryDisabler.CONFIG_ENTRY, DeviceEntryDisabler.CONFIG_ENTRY],
631+
[RegistryEntryDisabler.CONFIG_ENTRY, RegistryEntryDisabler.CONFIG_ENTRY],
624632
ConfigEntryDisabler.USER,
625633
[
626634
{
@@ -631,8 +639,8 @@ async def test_migration_from_v1(
631639
},
632640
{
633641
"conversation_entity_id": "conversation.google_generative_ai_conversation_2",
634-
"device_disabled_by": None,
635-
"entity_disabled_by": None,
642+
"device_disabled_by": DeviceEntryDisabler.CONFIG_ENTRY,
643+
"entity_disabled_by": RegistryEntryDisabler.CONFIG_ENTRY,
636644
"device": 1,
637645
},
638646
],
@@ -645,6 +653,8 @@ async def test_migration_from_v1_disabled(
645653
device_registry: dr.DeviceRegistry,
646654
entity_registry: er.EntityRegistry,
647655
config_entry_disabled_by: list[ConfigEntryDisabler | None],
656+
device_disabled_by: list[DeviceEntryDisabler | None],
657+
entity_disabled_by: list[RegistryEntryDisabler | None],
648658
merged_config_entry_disabled_by: ConfigEntryDisabler | None,
649659
conversation_subentry_data: list[dict[str, Any]],
650660
main_config_entry: int,
@@ -684,7 +694,7 @@ async def test_migration_from_v1_disabled(
684694
manufacturer="Google",
685695
model="Generative AI",
686696
entry_type=dr.DeviceEntryType.SERVICE,
687-
disabled_by=DeviceEntryDisabler.CONFIG_ENTRY,
697+
disabled_by=device_disabled_by[0],
688698
)
689699
entity_registry.async_get_or_create(
690700
"conversation",
@@ -693,7 +703,7 @@ async def test_migration_from_v1_disabled(
693703
config_entry=mock_config_entry,
694704
device_id=device_1.id,
695705
suggested_object_id="google_generative_ai_conversation",
696-
disabled_by=RegistryEntryDisabler.CONFIG_ENTRY,
706+
disabled_by=entity_disabled_by[0],
697707
)
698708

699709
device_2 = device_registry.async_get_or_create(
@@ -703,6 +713,7 @@ async def test_migration_from_v1_disabled(
703713
manufacturer="Google",
704714
model="Generative AI",
705715
entry_type=dr.DeviceEntryType.SERVICE,
716+
disabled_by=device_disabled_by[1],
706717
)
707718
entity_registry.async_get_or_create(
708719
"conversation",
@@ -711,6 +722,7 @@ async def test_migration_from_v1_disabled(
711722
config_entry=mock_config_entry_2,
712723
device_id=device_2.id,
713724
suggested_object_id="google_generative_ai_conversation_2",
725+
disabled_by=entity_disabled_by[1],
714726
)
715727

716728
devices = [device_1, device_2]

0 commit comments

Comments
 (0)