Add media_player device triggers (#45430)

* Add media player device triggers

* Update tests
This commit is contained in:
Erik Montnemery 2021-02-08 09:36:14 +01:00 committed by GitHub
parent ca87bf49b6
commit 28ef3f68f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 260 additions and 6 deletions

View File

@ -0,0 +1,90 @@
"""Provides device automations for Media player."""
from typing import List
import voluptuous as vol
from homeassistant.components.automation import AutomationActionType
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
from homeassistant.components.homeassistant.triggers import state as state_trigger
from homeassistant.const import (
CONF_DEVICE_ID,
CONF_DOMAIN,
CONF_ENTITY_ID,
CONF_PLATFORM,
CONF_TYPE,
STATE_IDLE,
STATE_OFF,
STATE_ON,
STATE_PAUSED,
STATE_PLAYING,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.typing import ConfigType
from . import DOMAIN
TRIGGER_TYPES = {"turned_on", "turned_off", "idle", "paused", "playing"}
TRIGGER_SCHEMA = TRIGGER_BASE_SCHEMA.extend(
{
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_TYPE): vol.In(TRIGGER_TYPES),
}
)
async def async_get_triggers(hass: HomeAssistant, device_id: str) -> List[dict]:
"""List device triggers for Media player entities."""
registry = await entity_registry.async_get_registry(hass)
triggers = []
# Get all the integration entities for this device
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue
# Add triggers for each entity that belongs to this integration
triggers += [
{
CONF_PLATFORM: "device",
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: trigger,
}
for trigger in TRIGGER_TYPES
]
return triggers
async def async_attach_trigger(
hass: HomeAssistant,
config: ConfigType,
action: AutomationActionType,
automation_info: dict,
) -> CALLBACK_TYPE:
"""Attach a trigger."""
config = TRIGGER_SCHEMA(config)
if config[CONF_TYPE] == "turned_on":
to_state = STATE_ON
elif config[CONF_TYPE] == "turned_off":
to_state = STATE_OFF
elif config[CONF_TYPE] == "idle":
to_state = STATE_IDLE
elif config[CONF_TYPE] == "paused":
to_state = STATE_PAUSED
else:
to_state = STATE_PLAYING
state_config = {
CONF_PLATFORM: "state",
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
state_trigger.CONF_TO: to_state,
}
state_config = state_trigger.TRIGGER_SCHEMA(state_config)
return await state_trigger.async_attach_trigger(
hass, state_config, action, automation_info, platform_type="device"
)

View File

@ -7,6 +7,13 @@
"is_idle": "{entity_name} is idle",
"is_paused": "{entity_name} is paused",
"is_playing": "{entity_name} is playing"
},
"trigger_type": {
"turned_on": "{entity_name} turned on",
"turned_off": "{entity_name} turned off",
"idle": "{entity_name} becomes idle",
"paused": "{entity_name} is paused",
"playing": "{entity_name} starts playing"
}
},
"state": {

View File

@ -7,7 +7,6 @@ from homeassistant.setup import async_setup_component
from tests.common import (
MockConfigEntry,
assert_lists_same,
async_get_device_automations,
async_mock_service,
mock_device_registry,
@ -55,7 +54,13 @@ async def test_get_triggers(hass, device_reg, entity_reg):
},
]
triggers = await async_get_device_automations(hass, "trigger", device_entry.id)
assert_lists_same(triggers, expected_triggers)
# Test triggers are either arcam_fmj specific or media_player entity triggers
triggers = await async_get_device_automations(hass, "trigger", device_entry.id)
for expected_trigger in expected_triggers:
assert expected_trigger in triggers
for trigger in triggers:
assert trigger in expected_triggers or trigger["domain"] == "media_player"
async def test_if_fires_on_turn_on_request(hass, calls, player_setup, state):

View File

@ -63,6 +63,7 @@ async def test_lg_tv(hass):
assert device.sw_version == "04.71.04"
assert device.via_device_id is None
# A TV doesn't have any triggers
# A TV has media player device triggers
triggers = await async_get_device_automations(hass, "trigger", device.id)
assert triggers == []
for trigger in triggers:
assert trigger["domain"] == "media_player"

View File

@ -10,7 +10,6 @@ from . import init_integration
from tests.common import (
MockConfigEntry,
assert_lists_same,
async_get_device_automations,
async_mock_service,
mock_device_registry,
@ -69,8 +68,13 @@ async def test_get_triggers(hass, device_reg, entity_reg):
"entity_id": f"{MP_DOMAIN}.kodi_5678",
},
]
# Test triggers are either kodi specific triggers or media_player entity triggers
triggers = await async_get_device_automations(hass, "trigger", device_entry.id)
assert_lists_same(triggers, expected_triggers)
for expected_trigger in expected_triggers:
assert expected_trigger in triggers
for trigger in triggers:
assert trigger in expected_triggers or trigger["domain"] == "media_player"
async def test_if_fires_on_state_change(hass, calls, kodi_media_player):

View File

@ -0,0 +1,147 @@
"""The tests for Media player device triggers."""
import pytest
import homeassistant.components.automation as automation
from homeassistant.components.media_player import DOMAIN
from homeassistant.const import (
STATE_IDLE,
STATE_OFF,
STATE_ON,
STATE_PAUSED,
STATE_PLAYING,
)
from homeassistant.helpers import device_registry
from homeassistant.setup import async_setup_component
from tests.common import (
MockConfigEntry,
assert_lists_same,
async_get_device_automations,
async_mock_service,
mock_device_registry,
mock_registry,
)
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture
def device_reg(hass):
"""Return an empty, loaded, registry."""
return mock_device_registry(hass)
@pytest.fixture
def entity_reg(hass):
"""Return an empty, loaded, registry."""
return mock_registry(hass)
@pytest.fixture
def calls(hass):
"""Track calls to a mock service."""
return async_mock_service(hass, "test", "automation")
async def test_get_triggers(hass, device_reg, entity_reg):
"""Test we get the expected triggers from a media player."""
config_entry = MockConfigEntry(domain="test", data={})
config_entry.add_to_hass(hass)
device_entry = device_reg.async_get_or_create(
config_entry_id=config_entry.entry_id,
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
)
entity_reg.async_get_or_create(DOMAIN, "test", "5678", device_id=device_entry.id)
trigger_types = {"turned_on", "turned_off", "idle", "paused", "playing"}
expected_triggers = [
{
"platform": "device",
"domain": DOMAIN,
"type": trigger,
"device_id": device_entry.id,
"entity_id": f"{DOMAIN}.test_5678",
}
for trigger in trigger_types
]
triggers = await async_get_device_automations(hass, "trigger", device_entry.id)
assert_lists_same(triggers, expected_triggers)
async def test_if_fires_on_state_change(hass, calls):
"""Test triggers firing."""
hass.states.async_set("media_player.entity", STATE_OFF)
data_template = (
"{label} - {{{{ trigger.platform}}}} - "
"{{{{ trigger.entity_id}}}} - {{{{ trigger.from_state.state}}}} - "
"{{{{ trigger.to_state.state}}}} - {{{{ trigger.for }}}}"
)
trigger_types = {"turned_on", "turned_off", "idle", "paused", "playing"}
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: [
{
"trigger": {
"platform": "device",
"domain": DOMAIN,
"device_id": "",
"entity_id": "media_player.entity",
"type": trigger,
},
"action": {
"service": "test.automation",
"data_template": {"some": data_template.format(label=trigger)},
},
}
for trigger in trigger_types
]
},
)
# 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"
)
# 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"
)
# 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"
)
# 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"
)
# 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"
)