Add turn_on trigger to Samsung TV (#89018)

* Add turn_on trigger to Samsung TV

* Add tests

* Apply suggestions from code review

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Remove assert

* Cleanup mock_send_magic_packet

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
epenet
2023-03-15 12:43:53 +01:00
committed by GitHub
co-authored by Martin Hjelmare
parent cd23caff58
commit 6270776fbb
10 changed files with 675 additions and 2 deletions
+9
View File
@@ -20,10 +20,13 @@ from samsungtvws.exceptions import ResponseError
from samsungtvws.remote import ChannelEmitCommand
from homeassistant.components.samsungtv.const import WEBSOCKET_SSL_PORT
from homeassistant.core import HomeAssistant, ServiceCall
import homeassistant.util.dt as dt_util
from .const import SAMPLE_DEVICE_INFO_UE48JU6400, SAMPLE_DEVICE_INFO_WIFI
from tests.common import async_mock_service
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
@@ -307,3 +310,9 @@ def mac_address_fixture() -> Mock:
"""Patch getmac.get_mac_address."""
with patch("getmac.get_mac_address", return_value=None) as mac:
yield mac
@pytest.fixture
def calls(hass: HomeAssistant) -> list[ServiceCall]:
"""Track calls to a mock service."""
return async_mock_service(hass, "test", "automation")
@@ -0,0 +1,149 @@
"""The tests for Samsung TV device triggers."""
from unittest.mock import patch
import pytest
from homeassistant.components import automation
from homeassistant.components.device_automation import DeviceAutomationType
from homeassistant.components.device_automation.exceptions import (
InvalidDeviceAutomationConfig,
)
from homeassistant.components.samsungtv import DOMAIN, device_trigger
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import async_get as get_dev_reg
from homeassistant.setup import async_setup_component
from . import setup_samsungtv_entry
from .test_media_player import ENTITY_ID, MOCK_ENTRYDATA_ENCRYPTED_WS
from tests.common import MockConfigEntry, async_get_device_automations
@pytest.mark.usefixtures("remoteencws", "rest_api")
async def test_get_triggers(hass: HomeAssistant) -> None:
"""Test we get the expected triggers."""
await setup_samsungtv_entry(hass, MOCK_ENTRYDATA_ENCRYPTED_WS)
device_reg = get_dev_reg(hass)
device = device_reg.async_get_device(identifiers={(DOMAIN, "any")})
turn_on_trigger = {
"platform": "device",
"domain": DOMAIN,
"type": "samsungtv.turn_on",
"device_id": device.id,
"metadata": {},
}
triggers = await async_get_device_automations(
hass, DeviceAutomationType.TRIGGER, device.id
)
assert turn_on_trigger in triggers
@pytest.mark.usefixtures("remoteencws", "rest_api")
async def test_if_fires_on_turn_on_request(
hass: HomeAssistant, calls: list[ServiceCall]
) -> None:
"""Test for turn_on and turn_off triggers firing."""
await setup_samsungtv_entry(hass, MOCK_ENTRYDATA_ENCRYPTED_WS)
device_reg = get_dev_reg(hass)
device = device_reg.async_get_device(identifiers={(DOMAIN, "any")})
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: [
{
"trigger": {
"platform": "device",
"domain": DOMAIN,
"device_id": device.id,
"type": "samsungtv.turn_on",
},
"action": {
"service": "test.automation",
"data_template": {
"some": "{{ trigger.device_id }}",
"id": "{{ trigger.id }}",
},
},
},
{
"trigger": {
"platform": "samsungtv.turn_on",
"entity_id": ENTITY_ID,
},
"action": {
"service": "test.automation",
"data_template": {
"some": ENTITY_ID,
"id": "{{ trigger.id }}",
},
},
},
],
},
)
with patch("homeassistant.components.samsungtv.media_player.send_magic_packet"):
await hass.services.async_call(
"media_player",
"turn_on",
{"entity_id": ENTITY_ID},
blocking=True,
)
await hass.async_block_till_done()
assert len(calls) == 2
assert calls[0].data["some"] == device.id
assert calls[0].data["id"] == 0
assert calls[1].data["some"] == ENTITY_ID
assert calls[1].data["id"] == 0
@pytest.mark.usefixtures("remoteencws", "rest_api")
async def test_failure_scenarios(hass: HomeAssistant) -> None:
"""Test failure scenarios."""
await setup_samsungtv_entry(hass, MOCK_ENTRYDATA_ENCRYPTED_WS)
# Test wrong trigger platform type
with pytest.raises(HomeAssistantError):
await device_trigger.async_attach_trigger(
hass, {"type": "wrong.type", "device_id": "invalid_device_id"}, None, {}
)
# Test invalid device id
with pytest.raises(InvalidDeviceAutomationConfig):
await device_trigger.async_validate_trigger_config(
hass,
{
"platform": "device",
"domain": DOMAIN,
"type": "samsungtv.turn_on",
"device_id": "invalid_device_id",
},
)
entry = MockConfigEntry(domain="fake", state=ConfigEntryState.LOADED, data={})
entry.add_to_hass(hass)
device_reg = get_dev_reg(hass)
device = device_reg.async_get_or_create(
config_entry_id=entry.entry_id, identifiers={("fake", "fake")}
)
config = {
"platform": "device",
"domain": DOMAIN,
"device_id": device.id,
"type": "samsungtv.turn_on",
}
# Test that device id from non samsungtv domain raises exception
with pytest.raises(InvalidDeviceAutomationConfig):
await device_trigger.async_validate_trigger_config(hass, config)
+196
View File
@@ -0,0 +1,196 @@
"""The tests for WebOS TV automation triggers."""
from unittest.mock import patch
import pytest
from homeassistant.components import automation
from homeassistant.components.samsungtv import DOMAIN
from homeassistant.const import SERVICE_RELOAD
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import device_registry as dr
from homeassistant.setup import async_setup_component
from . import setup_samsungtv_entry
from .test_media_player import ENTITY_ID, MOCK_ENTRYDATA_ENCRYPTED_WS
from tests.common import MockEntity, MockEntityPlatform
@pytest.mark.usefixtures("remoteencws", "rest_api")
async def test_turn_on_trigger_device_id(
hass: HomeAssistant, calls: list[ServiceCall], device_registry: dr.DeviceRegistry
) -> None:
"""Test for turn_on triggers by device_id firing."""
await setup_samsungtv_entry(hass, MOCK_ENTRYDATA_ENCRYPTED_WS)
device = device_registry.async_get_device(identifiers={(DOMAIN, "any")})
assert device, repr(device_registry.devices)
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: [
{
"trigger": {
"platform": "samsungtv.turn_on",
"device_id": device.id,
},
"action": {
"service": "test.automation",
"data_template": {
"some": device.id,
"id": "{{ trigger.id }}",
},
},
},
],
},
)
with patch("homeassistant.components.samsungtv.media_player.send_magic_packet"):
await hass.services.async_call(
"media_player",
"turn_on",
{"entity_id": ENTITY_ID},
blocking=True,
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["some"] == device.id
assert calls[0].data["id"] == 0
with patch("homeassistant.config.load_yaml", return_value={}):
await hass.services.async_call(automation.DOMAIN, SERVICE_RELOAD, blocking=True)
calls.clear()
with patch("homeassistant.components.samsungtv.media_player.send_magic_packet"):
await hass.services.async_call(
"media_player",
"turn_on",
{"entity_id": ENTITY_ID},
blocking=True,
)
await hass.async_block_till_done()
assert len(calls) == 0
@pytest.mark.usefixtures("remoteencws", "rest_api")
async def test_turn_on_trigger_entity_id(
hass: HomeAssistant, calls: list[ServiceCall]
) -> None:
"""Test for turn_on triggers by entity_id firing."""
await setup_samsungtv_entry(hass, MOCK_ENTRYDATA_ENCRYPTED_WS)
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: [
{
"trigger": {
"platform": "samsungtv.turn_on",
"entity_id": ENTITY_ID,
},
"action": {
"service": "test.automation",
"data_template": {
"some": ENTITY_ID,
"id": "{{ trigger.id }}",
},
},
},
],
},
)
with patch("homeassistant.components.samsungtv.media_player.send_magic_packet"):
await hass.services.async_call(
"media_player",
"turn_on",
{"entity_id": ENTITY_ID},
blocking=True,
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["some"] == ENTITY_ID
assert calls[0].data["id"] == 0
@pytest.mark.usefixtures("remoteencws", "rest_api")
async def test_wrong_trigger_platform_type(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test wrong trigger platform type."""
await setup_samsungtv_entry(hass, MOCK_ENTRYDATA_ENCRYPTED_WS)
await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: [
{
"trigger": {
"platform": "samsungtv.wrong_type",
"entity_id": ENTITY_ID,
},
"action": {
"service": "test.automation",
"data_template": {
"some": ENTITY_ID,
"id": "{{ trigger.id }}",
},
},
},
],
},
)
assert (
"ValueError: Unknown Samsung TV trigger platform samsungtv.wrong_type"
in caplog.text
)
@pytest.mark.usefixtures("remoteencws", "rest_api")
async def test_trigger_invalid_entity_id(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test turn on trigger using invalid entity_id."""
await setup_samsungtv_entry(hass, MOCK_ENTRYDATA_ENCRYPTED_WS)
platform = MockEntityPlatform(hass)
invalid_entity = f"{DOMAIN}.invalid"
await platform.async_add_entities([MockEntity(name=invalid_entity)])
await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: [
{
"trigger": {
"platform": "samsungtv.turn_on",
"entity_id": invalid_entity,
},
"action": {
"service": "test.automation",
"data_template": {
"some": ENTITY_ID,
"id": "{{ trigger.id }}",
},
},
},
],
},
)
assert (
f"ValueError: Entity {invalid_entity} is not a valid samsungtv entity"
in caplog.text
)