diff --git a/homeassistant/components/samsungtv/manifest.json b/homeassistant/components/samsungtv/manifest.json index 4206aca7213..4ffe940f946 100644 --- a/homeassistant/components/samsungtv/manifest.json +++ b/homeassistant/components/samsungtv/manifest.json @@ -4,7 +4,8 @@ "documentation": "https://www.home-assistant.io/integrations/samsungtv", "requirements": [ "samsungctl[websocket]==0.7.1", - "samsungtvws==1.6.0" + "samsungtvws==1.6.0", + "wakeonlan==2.0.1" ], "ssdp": [ { diff --git a/homeassistant/components/samsungtv/media_player.py b/homeassistant/components/samsungtv/media_player.py index 72e21ed205c..5822bafcc55 100644 --- a/homeassistant/components/samsungtv/media_player.py +++ b/homeassistant/components/samsungtv/media_player.py @@ -3,6 +3,7 @@ import asyncio from datetime import timedelta import voluptuous as vol +from wakeonlan import send_magic_packet from homeassistant.components.media_player import DEVICE_CLASS_TV, MediaPlayerEntity from homeassistant.components.media_player.const import ( @@ -71,6 +72,7 @@ class SamsungTVDevice(MediaPlayerEntity): def __init__(self, bridge, config_entry, on_script): """Initialize the Samsung device.""" self._config_entry = config_entry + self._host = config_entry.data[CONF_HOST] self._mac = config_entry.data.get(CONF_MAC) self._manufacturer = config_entry.data.get(CONF_MANUFACTURER) self._model = config_entry.data.get(CONF_MODEL) @@ -146,7 +148,7 @@ class SamsungTVDevice(MediaPlayerEntity): """Return the availability of the device.""" if self._auth_failed: return False - return self._state == STATE_ON or self._on_script + return self._state == STATE_ON or self._on_script or self._mac @property def device_info(self): @@ -174,7 +176,7 @@ class SamsungTVDevice(MediaPlayerEntity): @property def supported_features(self): """Flag media player features that are supported.""" - if self._on_script: + if self._on_script or self._mac: return SUPPORT_SAMSUNGTV | SUPPORT_TURN_ON return SUPPORT_SAMSUNGTV @@ -246,10 +248,19 @@ class SamsungTVDevice(MediaPlayerEntity): await asyncio.sleep(KEY_PRESS_TIMEOUT, self.hass.loop) await self.hass.async_add_executor_job(self.send_key, "KEY_ENTER") + def _wake_on_lan(self): + """Wake the device via wake on lan.""" + send_magic_packet(self._mac, ip_address=self._host) + # If the ip address changed since we last saw the device + # broadcast a packet as well + send_magic_packet(self._mac) + async def async_turn_on(self): """Turn the media player on.""" if self._on_script: await self._on_script.async_run(context=self._context) + elif self._mac: + await self.hass.async_add_executor_job(self._wake_on_lan) def select_source(self, source): """Select input source.""" diff --git a/requirements_all.txt b/requirements_all.txt index 656a4f7da8d..18365ca14cb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2325,6 +2325,7 @@ vtjp==0.1.14 # homeassistant.components.vultr vultr==0.1.2 +# homeassistant.components.samsungtv # homeassistant.components.wake_on_lan wakeonlan==2.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2688ffabed1..e90799028e8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1249,6 +1249,7 @@ vsure==1.7.3 # homeassistant.components.vultr vultr==0.1.2 +# homeassistant.components.samsungtv # homeassistant.components.wake_on_lan wakeonlan==2.0.1 diff --git a/tests/components/samsungtv/test_media_player.py b/tests/components/samsungtv/test_media_player.py index 0cf54e32807..02eceeaacb7 100644 --- a/tests/components/samsungtv/test_media_player.py +++ b/tests/components/samsungtv/test_media_player.py @@ -35,6 +35,7 @@ from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, CONF_HOST, CONF_IP_ADDRESS, + CONF_MAC, CONF_METHOD, CONF_NAME, CONF_PORT, @@ -98,6 +99,17 @@ MOCK_ENTRY_WS = { CONF_TOKEN: "123456789", } + +MOCK_ENTRY_WS_WITH_MAC = { + CONF_IP_ADDRESS: "test", + CONF_HOST: "fake_host", + CONF_METHOD: "websocket", + CONF_MAC: "aa:bb:cc:dd:ee:ff", + CONF_NAME: "fake", + CONF_PORT: 8002, + CONF_TOKEN: "123456789", +} + ENTITY_ID_NOTURNON = f"{DOMAIN}.fake_noturnon" MOCK_CONFIG_NOTURNON = { SAMSUNGTV_DOMAIN: [ @@ -593,6 +605,26 @@ async def test_turn_on_with_turnon(hass, remote, delay): assert delay.call_count == 1 +async def test_turn_on_wol(hass, remotews): + """Test turn on.""" + entry = MockConfigEntry( + domain=SAMSUNGTV_DOMAIN, + data=MOCK_ENTRY_WS_WITH_MAC, + unique_id="any", + ) + entry.add_to_hass(hass) + assert await async_setup_component(hass, SAMSUNGTV_DOMAIN, {}) + await hass.async_block_till_done() + with patch( + "homeassistant.components.samsungtv.media_player.send_magic_packet" + ) as mock_send_magic_packet: + assert await hass.services.async_call( + DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, True + ) + await hass.async_block_till_done() + assert mock_send_magic_packet.called + + async def test_turn_on_without_turnon(hass, remote): """Test turn on.""" await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON)