mirror of
https://github.com/home-assistant/core.git
synced 2026-08-28 10:16:02 -05:00
Expand composite devices in tag trigger (#180247)
This commit is contained in:
@@ -4,7 +4,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_PLATFORM
|
||||
from homeassistant.core import CALLBACK_TYPE, Event, HassJob, HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers import config_validation as cv, device_registry as dr
|
||||
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
@@ -31,6 +31,18 @@ async def async_attach_trigger(
|
||||
device_ids: set[str] | None = (
|
||||
set(config[DEVICE_ID]) if DEVICE_ID in config else None
|
||||
)
|
||||
if device_ids is not None:
|
||||
device_registry = dr.async_get(hass)
|
||||
# A pre-migration composite device id no longer refers to a registered device;
|
||||
# a tag scanned event carries the id of one of the devices it was split into.
|
||||
# Expand it to those split device ids so the trigger keeps matching.
|
||||
for device_id in list(device_ids):
|
||||
split_devices = device_registry.async_get_devices_for_composite_device_id(
|
||||
device_id
|
||||
)
|
||||
if split_devices:
|
||||
device_ids.discard(device_id)
|
||||
device_ids.update(split_device.id for split_device in split_devices)
|
||||
|
||||
job = HassJob(action)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
import attr
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import automation
|
||||
@@ -9,8 +10,11 @@ from homeassistant.components.tag import async_scan_tag
|
||||
from homeassistant.components.tag.const import DEVICE_ID, DOMAIN, TAG_ID
|
||||
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tag_setup(hass: HomeAssistant, hass_storage: dict[str, Any]):
|
||||
@@ -151,3 +155,87 @@ async def test_multiple_tags_and_devices_trigger(
|
||||
assert service_calls[1].data["message"] == "service called"
|
||||
assert service_calls[2].data["message"] == "service called"
|
||||
assert service_calls[3].data["message"] == "service called"
|
||||
|
||||
|
||||
COMPOSITE_ID = "composite00000000000000000000ab"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def split_devices(
|
||||
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
||||
) -> tuple[dr.DeviceEntry, dr.DeviceEntry]:
|
||||
"""Create two devices which are splits of a pre-migration composite device."""
|
||||
entry_1 = MockConfigEntry(domain="itg1")
|
||||
entry_1.add_to_hass(hass)
|
||||
entry_2 = MockConfigEntry(domain="itg2")
|
||||
entry_2.add_to_hass(hass)
|
||||
device_1 = device_registry.async_get_or_create(
|
||||
config_entry_id=entry_1.entry_id,
|
||||
identifiers={("itg1", "1")},
|
||||
name="Split device 1",
|
||||
)
|
||||
device_2 = device_registry.async_get_or_create(
|
||||
config_entry_id=entry_2.entry_id,
|
||||
identifiers={("itg2", "1")},
|
||||
name="Split device 2",
|
||||
)
|
||||
device_registry._devices[device_1.id] = attr.evolve(
|
||||
device_1, composite_device_id=COMPOSITE_ID
|
||||
)
|
||||
device_registry._devices[device_2.id] = attr.evolve(
|
||||
device_2, composite_device_id=COMPOSITE_ID
|
||||
)
|
||||
return device_registry._devices[device_1.id], device_registry._devices[device_2.id]
|
||||
|
||||
|
||||
async def test_composite_device_trigger(
|
||||
hass: HomeAssistant,
|
||||
tag_setup,
|
||||
service_calls: list[ServiceCall],
|
||||
split_devices: tuple[dr.DeviceEntry, dr.DeviceEntry],
|
||||
) -> None:
|
||||
"""Test a tag trigger configured with a pre-migration composite device id.
|
||||
|
||||
The composite device id no longer refers to a registered device; it is expanded to
|
||||
the ids of the devices it was split into, so a tag scanned by either split device
|
||||
fires the trigger.
|
||||
"""
|
||||
device_1, device_2 = split_devices
|
||||
assert await tag_setup()
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"trigger": {
|
||||
"platform": DOMAIN,
|
||||
TAG_ID: "abc123",
|
||||
DEVICE_ID: COMPOSITE_ID,
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data": {"message": "service called"},
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# A scan by the composite id or an unrelated device should not fire
|
||||
await async_scan_tag(hass, tag_id="abc123", device_id=COMPOSITE_ID)
|
||||
await async_scan_tag(hass, tag_id="abc123", device_id="other_device")
|
||||
await hass.async_block_till_done()
|
||||
assert len(service_calls) == 0
|
||||
|
||||
# A scan by either split device should fire
|
||||
await async_scan_tag(hass, tag_id="abc123", device_id=device_1.id)
|
||||
await hass.async_block_till_done()
|
||||
await async_scan_tag(hass, tag_id="abc123", device_id=device_2.id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(service_calls) == 2
|
||||
assert service_calls[0].data["message"] == "service called"
|
||||
assert service_calls[1].data["message"] == "service called"
|
||||
|
||||
Reference in New Issue
Block a user