diff --git a/homeassistant/components/media_player/device_trigger.py b/homeassistant/components/media_player/device_trigger.py index 1bdbb62248f..1493e28a350 100644 --- a/homeassistant/components/media_player/device_trigger.py +++ b/homeassistant/components/media_player/device_trigger.py @@ -9,7 +9,10 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation import ( + DEVICE_TRIGGER_BASE_SCHEMA, + entity, +) from homeassistant.components.homeassistant.triggers import state as state_trigger from homeassistant.const import ( CONF_DEVICE_ID, @@ -32,7 +35,7 @@ from .const import DOMAIN TRIGGER_TYPES = {"turned_on", "turned_off", "idle", "paused", "playing"} -TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( +MEDIA_PLAYER_TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( { vol.Required(CONF_ENTITY_ID): cv.entity_id, vol.Required(CONF_TYPE): vol.In(TRIGGER_TYPES), @@ -40,13 +43,21 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( } ) +TRIGGER_SCHEMA = vol.All( + vol.Any( + MEDIA_PLAYER_TRIGGER_SCHEMA, + entity.TRIGGER_SCHEMA, + ), + vol.Schema({vol.Required(CONF_DOMAIN): DOMAIN}, extra=vol.ALLOW_EXTRA), +) + async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Media player entities.""" registry = await entity_registry.async_get_registry(hass) - triggers = [] + triggers = await entity.async_get_triggers(hass, device_id, DOMAIN) # Get all the integration entities for this device for entry in entity_registry.async_entries_for_device(registry, device_id): @@ -72,6 +83,8 @@ async def async_get_trigger_capabilities( hass: HomeAssistant, config: ConfigType ) -> dict[str, vol.Schema]: """List trigger capabilities.""" + if config[CONF_TYPE] not in TRIGGER_TYPES: + return await entity.async_get_trigger_capabilities(hass, config) return { "extra_fields": vol.Schema( {vol.Optional(CONF_FOR): cv.positive_time_period_dict} @@ -86,6 +99,8 @@ async def async_attach_trigger( automation_info: AutomationTriggerInfo, ) -> CALLBACK_TYPE: """Attach a trigger.""" + if config[CONF_TYPE] not in TRIGGER_TYPES: + return await entity.async_attach_trigger(hass, config, action, automation_info) if config[CONF_TYPE] == "turned_on": to_state = STATE_ON elif config[CONF_TYPE] == "turned_off": diff --git a/homeassistant/components/media_player/strings.json b/homeassistant/components/media_player/strings.json index 64841413f12..ea773195380 100644 --- a/homeassistant/components/media_player/strings.json +++ b/homeassistant/components/media_player/strings.json @@ -13,7 +13,8 @@ "turned_off": "{entity_name} turned off", "idle": "{entity_name} becomes idle", "paused": "{entity_name} is paused", - "playing": "{entity_name} starts playing" + "playing": "{entity_name} starts playing", + "changed_states": "{entity_name} changed states" } }, "state": { diff --git a/tests/components/media_player/test_device_trigger.py b/tests/components/media_player/test_device_trigger.py index 842184d62ef..0e58395319e 100644 --- a/tests/components/media_player/test_device_trigger.py +++ b/tests/components/media_player/test_device_trigger.py @@ -58,7 +58,14 @@ async def test_get_triggers(hass, device_reg, entity_reg): ) entity_reg.async_get_or_create(DOMAIN, "test", "5678", device_id=device_entry.id) - trigger_types = {"turned_on", "turned_off", "idle", "paused", "playing"} + trigger_types = { + "turned_on", + "turned_off", + "idle", + "paused", + "playing", + "changed_states", + } expected_triggers = [ { "platform": "device", @@ -88,7 +95,7 @@ async def test_get_trigger_capabilities(hass, device_reg, entity_reg): triggers = await async_get_device_automations( hass, DeviceAutomationType.TRIGGER, device_entry.id ) - assert len(triggers) == 5 + assert len(triggers) == 6 for trigger in triggers: capabilities = await async_get_device_automation_capabilities( hass, DeviceAutomationType.TRIGGER, trigger @@ -109,7 +116,14 @@ async def test_if_fires_on_state_change(hass, calls): "{{{{ trigger.entity_id}}}} - {{{{ trigger.from_state.state}}}} - " "{{{{ trigger.to_state.state}}}} - {{{{ trigger.for }}}}" ) - trigger_types = {"turned_on", "turned_off", "idle", "paused", "playing"} + trigger_types = { + "turned_on", + "turned_off", + "idle", + "paused", + "playing", + "changed_states", + } assert await async_setup_component( hass, @@ -137,47 +151,47 @@ async def test_if_fires_on_state_change(hass, calls): # Fake that the entity is turning on. hass.states.async_set("media_player.entity", STATE_ON) await hass.async_block_till_done() - assert len(calls) == 1 - assert ( - calls[0].data["some"] - == "turned_on - device - media_player.entity - off - on - None" - ) + assert len(calls) == 2 + assert {calls[0].data["some"], calls[1].data["some"]} == { + "turned_on - device - media_player.entity - off - on - None", + "changed_states - device - media_player.entity - off - on - None", + } # Fake that the entity is turning off. hass.states.async_set("media_player.entity", STATE_OFF) await hass.async_block_till_done() - assert len(calls) == 2 - assert ( - calls[1].data["some"] - == "turned_off - device - media_player.entity - on - off - None" - ) + assert len(calls) == 4 + assert {calls[2].data["some"], calls[3].data["some"]} == { + "turned_off - device - media_player.entity - on - off - None", + "changed_states - device - media_player.entity - on - off - None", + } # Fake that the entity becomes idle. hass.states.async_set("media_player.entity", STATE_IDLE) await hass.async_block_till_done() - assert len(calls) == 3 - assert ( - calls[2].data["some"] - == "idle - device - media_player.entity - off - idle - None" - ) + assert len(calls) == 6 + assert {calls[4].data["some"], calls[5].data["some"]} == { + "idle - device - media_player.entity - off - idle - None", + "changed_states - device - media_player.entity - off - idle - None", + } # Fake that the entity starts playing. hass.states.async_set("media_player.entity", STATE_PLAYING) await hass.async_block_till_done() - assert len(calls) == 4 - assert ( - calls[3].data["some"] - == "playing - device - media_player.entity - idle - playing - None" - ) + assert len(calls) == 8 + assert {calls[6].data["some"], calls[7].data["some"]} == { + "playing - device - media_player.entity - idle - playing - None", + "changed_states - device - media_player.entity - idle - playing - None", + } # Fake that the entity is paused. hass.states.async_set("media_player.entity", STATE_PAUSED) await hass.async_block_till_done() - assert len(calls) == 5 - assert ( - calls[4].data["some"] - == "paused - device - media_player.entity - playing - paused - None" - ) + assert len(calls) == 10 + assert {calls[8].data["some"], calls[9].data["some"]} == { + "paused - device - media_player.entity - playing - paused - None", + "changed_states - device - media_player.entity - playing - paused - None", + } async def test_if_fires_on_state_change_with_for(hass, calls):