Skip to content

Commit d325f67

Browse files
abmantisCopilot
andauthored
Deprecate TargetSelectorData in favor of TargetSelection (home-assistant#158734)
Co-authored-by: Copilot <[email protected]>
1 parent f786ec1 commit d325f67

File tree

11 files changed

+74
-59
lines changed

11 files changed

+74
-59
lines changed

homeassistant/components/homeassistant/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
from homeassistant.helpers.signal import KEY_HA_STOP
5050
from homeassistant.helpers.system_info import async_get_system_info
5151
from homeassistant.helpers.target import (
52-
TargetSelectorData,
52+
TargetSelection,
5353
async_extract_referenced_entity_ids,
5454
)
5555
from homeassistant.helpers.template import async_load_custom_templates
@@ -115,7 +115,7 @@ async def async_save_persistent_states(service: ServiceCall) -> None:
115115
async def async_handle_turn_service(service: ServiceCall) -> None:
116116
"""Handle calls to homeassistant.turn_on/off."""
117117
referenced = async_extract_referenced_entity_ids(
118-
hass, TargetSelectorData(service.data)
118+
hass, TargetSelection(service.data)
119119
)
120120
all_referenced = referenced.referenced | referenced.indirectly_referenced
121121

homeassistant/components/homekit/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
from homeassistant.helpers.service import async_register_admin_service
7979
from homeassistant.helpers.start import async_at_started
8080
from homeassistant.helpers.target import (
81-
TargetSelectorData,
81+
TargetSelection,
8282
async_extract_referenced_entity_ids,
8383
)
8484
from homeassistant.helpers.typing import ConfigType
@@ -483,7 +483,7 @@ async def async_handle_homekit_reset_accessory(service: ServiceCall) -> None:
483483
async def async_handle_homekit_unpair(service: ServiceCall) -> None:
484484
"""Handle unpair HomeKit service call."""
485485
referenced = async_extract_referenced_entity_ids(
486-
hass, TargetSelectorData(service.data)
486+
hass, TargetSelection(service.data)
487487
)
488488
dev_reg = dr.async_get(hass)
489489
for device_id in referenced.referenced_devices:

homeassistant/components/lifx/manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from homeassistant.core import HomeAssistant, ServiceCall, callback
3030
from homeassistant.helpers import config_validation as cv
3131
from homeassistant.helpers.target import (
32-
TargetSelectorData,
32+
TargetSelection,
3333
async_extract_referenced_entity_ids,
3434
)
3535

@@ -272,7 +272,7 @@ def async_setup(self) -> None:
272272
async def service_handler(service: ServiceCall) -> None:
273273
"""Apply a service, i.e. start an effect."""
274274
referenced = async_extract_referenced_entity_ids(
275-
self.hass, TargetSelectorData(service.data)
275+
self.hass, TargetSelection(service.data)
276276
)
277277
all_referenced = referenced.referenced | referenced.indirectly_referenced
278278
if all_referenced:

homeassistant/components/light/condition.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ def check_all_match_state(states: list[str]) -> bool:
8181
@trace_condition_function
8282
def test_state(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
8383
"""Test state condition."""
84-
selector_data = target.TargetSelectorData(self._target)
84+
target_selection = target.TargetSelection(self._target)
8585
targeted_entities = target.async_extract_referenced_entity_ids(
86-
hass, selector_data, expand_group=False
86+
hass, target_selection, expand_group=False
8787
)
8888
referenced_entity_ids = targeted_entities.referenced.union(
8989
targeted_entities.indirectly_referenced

homeassistant/components/unifiprotect/services.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
entity_registry as er,
2929
)
3030
from homeassistant.helpers.target import (
31-
TargetSelectorData,
31+
TargetSelection,
3232
async_extract_referenced_entity_ids,
3333
)
3434
from homeassistant.util.json import JsonValueType
@@ -117,7 +117,7 @@ def _async_get_ufp_instance(hass: HomeAssistant, device_id: str) -> ProtectApiCl
117117

118118
@callback
119119
def _async_get_ufp_camera(call: ServiceCall) -> Camera:
120-
ref = async_extract_referenced_entity_ids(call.hass, TargetSelectorData(call.data))
120+
ref = async_extract_referenced_entity_ids(call.hass, TargetSelection(call.data))
121121
entity_registry = er.async_get(call.hass)
122122

123123
entity_id = ref.indirectly_referenced.pop()
@@ -135,7 +135,7 @@ def _async_get_protect_from_call(call: ServiceCall) -> set[ProtectApiClient]:
135135
return {
136136
_async_get_ufp_instance(call.hass, device_id)
137137
for device_id in async_extract_referenced_entity_ids(
138-
call.hass, TargetSelectorData(call.data)
138+
call.hass, TargetSelection(call.data)
139139
).referenced_devices
140140
}
141141

@@ -207,7 +207,7 @@ def _async_unique_id_to_mac(unique_id: str) -> str:
207207

208208
async def set_chime_paired_doorbells(call: ServiceCall) -> None:
209209
"""Set paired doorbells on chime."""
210-
ref = async_extract_referenced_entity_ids(call.hass, TargetSelectorData(call.data))
210+
ref = async_extract_referenced_entity_ids(call.hass, TargetSelection(call.data))
211211
entity_registry = er.async_get(call.hass)
212212

213213
entity_id = ref.indirectly_referenced.pop()
@@ -223,7 +223,7 @@ async def set_chime_paired_doorbells(call: ServiceCall) -> None:
223223

224224
call.data = ReadOnlyDict(call.data.get("doorbells") or {})
225225
doorbell_refs = async_extract_referenced_entity_ids(
226-
call.hass, TargetSelectorData(call.data)
226+
call.hass, TargetSelection(call.data)
227227
)
228228
doorbell_ids: set[str] = set()
229229
for camera_id in doorbell_refs.referenced | doorbell_refs.indirectly_referenced:

homeassistant/components/websocket_api/automation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def _async_get_automation_components_for_target(
150150
"""
151151
extracted = target_helpers.async_extract_referenced_entity_ids(
152152
hass,
153-
target_helpers.TargetSelectorData(target_selection),
153+
target_helpers.TargetSelection(target_selection),
154154
expand_group=expand_group,
155155
)
156156
_LOGGER.debug("Extracted entities for lookup: %s", extracted)

homeassistant/components/websocket_api/commands.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -865,9 +865,9 @@ def handle_extract_from_target(
865865
) -> None:
866866
"""Handle extract from target command."""
867867

868-
selector_data = target_helpers.TargetSelectorData(msg["target"])
868+
target_selection = target_helpers.TargetSelection(msg["target"])
869869
extracted = target_helpers.async_extract_referenced_entity_ids(
870-
hass, selector_data, expand_group=msg["expand_group"]
870+
hass, target_selection, expand_group=msg["expand_group"]
871871
)
872872

873873
extracted_dict = {

homeassistant/helpers/service.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,10 @@ class ServiceParams(TypedDict):
223223

224224

225225
@deprecated_class(
226-
"homeassistant.helpers.target.TargetSelectorData",
226+
"homeassistant.helpers.target.TargetSelection",
227227
breaks_in_ha_version="2026.8",
228228
)
229-
class ServiceTargetSelector(target_helpers.TargetSelectorData):
229+
class ServiceTargetSelector(target_helpers.TargetSelection):
230230
"""Class to hold a target selector for a service."""
231231

232232
def __init__(self, service_call: ServiceCall) -> None:
@@ -406,9 +406,9 @@ async def async_extract_entities[_EntityT: Entity](
406406
if data_ent_id == ENTITY_MATCH_ALL:
407407
return [entity for entity in entities if entity.available]
408408

409-
selector_data = target_helpers.TargetSelectorData(service_call.data)
409+
target_selection = target_helpers.TargetSelection(service_call.data)
410410
referenced = target_helpers.async_extract_referenced_entity_ids(
411-
service_call.hass, selector_data, expand_group
411+
service_call.hass, target_selection, expand_group
412412
)
413413
combined = referenced.referenced | referenced.indirectly_referenced
414414

@@ -438,9 +438,9 @@ async def async_extract_entity_ids(
438438
439439
Will convert group entity ids to the entity ids it represents.
440440
"""
441-
selector_data = target_helpers.TargetSelectorData(service_call.data)
441+
target_selection = target_helpers.TargetSelection(service_call.data)
442442
referenced = target_helpers.async_extract_referenced_entity_ids(
443-
service_call.hass, selector_data, expand_group
443+
service_call.hass, target_selection, expand_group
444444
)
445445
return referenced.referenced | referenced.indirectly_referenced
446446

@@ -454,9 +454,9 @@ def async_extract_referenced_entity_ids(
454454
hass: HomeAssistant, service_call: ServiceCall, expand_group: bool = True
455455
) -> SelectedEntities:
456456
"""Extract referenced entity IDs from a service call."""
457-
selector_data = target_helpers.TargetSelectorData(service_call.data)
457+
target_selection = target_helpers.TargetSelection(service_call.data)
458458
selected = target_helpers.async_extract_referenced_entity_ids(
459-
hass, selector_data, expand_group
459+
hass, target_selection, expand_group
460460
)
461461
return SelectedEntities(**dataclasses.asdict(selected))
462462

@@ -466,9 +466,9 @@ async def async_extract_config_entry_ids(
466466
service_call: ServiceCall, expand_group: bool = True
467467
) -> set[str]:
468468
"""Extract referenced config entry ids from a service call."""
469-
selector_data = target_helpers.TargetSelectorData(service_call.data)
469+
target_selection = target_helpers.TargetSelection(service_call.data)
470470
referenced = target_helpers.async_extract_referenced_entity_ids(
471-
service_call.hass, selector_data, expand_group
471+
service_call.hass, target_selection, expand_group
472472
)
473473
ent_reg = entity_registry.async_get(service_call.hass)
474474
dev_reg = device_registry.async_get(service_call.hass)
@@ -752,9 +752,9 @@ async def entity_service_call(
752752
all_referenced: set[str] | None = None
753753
else:
754754
# A set of entities we're trying to target.
755-
selector_data = target_helpers.TargetSelectorData(call.data)
755+
target_selection = target_helpers.TargetSelection(call.data)
756756
referenced = target_helpers.async_extract_referenced_entity_ids(
757-
hass, selector_data, True
757+
hass, target_selection, True
758758
)
759759
all_referenced = referenced.referenced | referenced.indirectly_referenced
760760

homeassistant/helpers/target.py

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
group,
3535
label_registry as lr,
3636
)
37+
from .deprecation import deprecated_class
3738
from .event import async_track_state_change_event
3839
from .typing import ConfigType
3940

@@ -53,8 +54,8 @@ def _has_match(ids: str | list[str] | None) -> TypeGuard[str | list[str]]:
5354
return ids not in (None, ENTITY_MATCH_NONE)
5455

5556

56-
class TargetSelectorData:
57-
"""Class to hold data of target selector."""
57+
class TargetSelection:
58+
"""Class to represent target selection."""
5859

5960
__slots__ = ("area_ids", "device_ids", "entity_ids", "floor_ids", "label_ids")
6061

@@ -81,8 +82,8 @@ def __init__(self, config: ConfigType) -> None:
8182
)
8283

8384
@property
84-
def has_any_selector(self) -> bool:
85-
"""Determine if any selectors are present."""
85+
def has_any_target(self) -> bool:
86+
"""Determine if any target is present."""
8687
return bool(
8788
self.entity_ids
8889
or self.device_ids
@@ -92,6 +93,16 @@ def has_any_selector(self) -> bool:
9293
)
9394

9495

96+
@deprecated_class("TargetSelection", breaks_in_ha_version="2026.12.0")
97+
class TargetSelectorData(TargetSelection):
98+
"""Class to represent target selector data."""
99+
100+
@property
101+
def has_any_selector(self) -> bool:
102+
"""Determine if any selectors are present."""
103+
return super().has_any_target
104+
105+
95106
@dataclasses.dataclass(slots=True)
96107
class SelectedEntities:
97108
"""Class to hold the selected entities."""
@@ -135,49 +146,49 @@ def log_missing(self, missing_entities: set[str], logger: Logger) -> None:
135146

136147

137148
def async_extract_referenced_entity_ids(
138-
hass: HomeAssistant, selector_data: TargetSelectorData, expand_group: bool = True
149+
hass: HomeAssistant, target_selection: TargetSelection, expand_group: bool = True
139150
) -> SelectedEntities:
140-
"""Extract referenced entity IDs from a target selector."""
151+
"""Extract referenced entity IDs from a target selection."""
141152
selected = SelectedEntities()
142153

143-
if not selector_data.has_any_selector:
154+
if not target_selection.has_any_target:
144155
return selected
145156

146-
entity_ids: set[str] | list[str] = selector_data.entity_ids
157+
entity_ids: set[str] | list[str] = target_selection.entity_ids
147158
if expand_group:
148159
entity_ids = group.expand_entity_ids(hass, entity_ids)
149160

150161
selected.referenced.update(entity_ids)
151162

152163
if (
153-
not selector_data.device_ids
154-
and not selector_data.area_ids
155-
and not selector_data.floor_ids
156-
and not selector_data.label_ids
164+
not target_selection.device_ids
165+
and not target_selection.area_ids
166+
and not target_selection.floor_ids
167+
and not target_selection.label_ids
157168
):
158169
return selected
159170

160171
entities = er.async_get(hass).entities
161172
dev_reg = dr.async_get(hass)
162173
area_reg = ar.async_get(hass)
163174

164-
if selector_data.floor_ids:
175+
if target_selection.floor_ids:
165176
floor_reg = fr.async_get(hass)
166-
for floor_id in selector_data.floor_ids:
177+
for floor_id in target_selection.floor_ids:
167178
if floor_id not in floor_reg.floors:
168179
selected.missing_floors.add(floor_id)
169180

170-
for area_id in selector_data.area_ids:
181+
for area_id in target_selection.area_ids:
171182
if area_id not in area_reg.areas:
172183
selected.missing_areas.add(area_id)
173184

174-
for device_id in selector_data.device_ids:
185+
for device_id in target_selection.device_ids:
175186
if device_id not in dev_reg.devices:
176187
selected.missing_devices.add(device_id)
177188

178-
if selector_data.label_ids:
189+
if target_selection.label_ids:
179190
label_reg = lr.async_get(hass)
180-
for label_id in selector_data.label_ids:
191+
for label_id in target_selection.label_ids:
181192
if label_id not in label_reg.labels:
182193
selected.missing_labels.add(label_id)
183194

@@ -192,15 +203,15 @@ def async_extract_referenced_entity_ids(
192203
selected.referenced_areas.add(area_entry.id)
193204

194205
# Find areas for targeted floors
195-
if selector_data.floor_ids:
206+
if target_selection.floor_ids:
196207
selected.referenced_areas.update(
197208
area_entry.id
198-
for floor_id in selector_data.floor_ids
209+
for floor_id in target_selection.floor_ids
199210
for area_entry in area_reg.areas.get_areas_for_floor(floor_id)
200211
)
201212

202-
selected.referenced_areas.update(selector_data.area_ids)
203-
selected.referenced_devices.update(selector_data.device_ids)
213+
selected.referenced_areas.update(target_selection.area_ids)
214+
selected.referenced_devices.update(target_selection.device_ids)
204215

205216
if not selected.referenced_areas and not selected.referenced_devices:
206217
return selected
@@ -263,13 +274,13 @@ class TargetStateChangeTracker:
263274
def __init__(
264275
self,
265276
hass: HomeAssistant,
266-
selector_data: TargetSelectorData,
277+
target_selection: TargetSelection,
267278
action: Callable[[TargetStateChangedData], Any],
268279
entity_filter: Callable[[set[str]], set[str]],
269280
) -> None:
270281
"""Initialize the state change tracker."""
271282
self._hass = hass
272-
self._selector_data = selector_data
283+
self._target_selection = target_selection
273284
self._action = action
274285
self._entity_filter = entity_filter
275286

@@ -285,7 +296,7 @@ def async_setup(self) -> Callable[[], None]:
285296
def _track_entities_state_change(self) -> None:
286297
"""Set up state change tracking for currently selected entities."""
287298
selected = async_extract_referenced_entity_ids(
288-
self._hass, self._selector_data, expand_group=False
299+
self._hass, self._target_selection, expand_group=False
289300
)
290301

291302
tracked_entities = self._entity_filter(
@@ -352,10 +363,10 @@ def async_track_target_selector_state_change_event(
352363
entity_filter: Callable[[set[str]], set[str]] = lambda x: x,
353364
) -> CALLBACK_TYPE:
354365
"""Track state changes for entities referenced directly or indirectly in a target selector."""
355-
selector_data = TargetSelectorData(target_selector_config)
356-
if not selector_data.has_any_selector:
366+
target_selection = TargetSelection(target_selector_config)
367+
if not target_selection.has_any_target:
357368
raise HomeAssistantError(
358369
f"Target selector {target_selector_config} does not have any selectors defined"
359370
)
360-
tracker = TargetStateChangeTracker(hass, selector_data, action, entity_filter)
371+
tracker = TargetStateChangeTracker(hass, target_selection, action, entity_filter)
361372
return tracker.async_setup()

tests/helpers/test_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2699,7 +2699,7 @@ async def test_deprecated_service_target_selector_class(hass: HomeAssistant) ->
26992699
assert selector.device_ids == {"device1", "device2"}
27002700
assert selector.floor_ids == {"first_floor"}
27012701
assert selector.label_ids == {"label1", "label2"}
2702-
assert selector.has_any_selector is True
2702+
assert selector.has_any_target is True
27032703

27042704

27052705
async def test_deprecated_selected_entities_class(

0 commit comments

Comments
 (0)