From 5a77dcdd3d07d563b633454912182105431ede88 Mon Sep 17 00:00:00 2001 From: escoand Date: Fri, 3 Jan 2020 09:49:38 +0100 Subject: [PATCH] add turn on action (dry without testing) --- .../components/samsungtv/__init__.py | 16 +-- .../components/samsungtv/config_flow.py | 20 ++-- homeassistant/components/samsungtv/const.py | 1 + .../components/samsungtv/manifest.json | 3 +- .../components/samsungtv/media_player.py | 52 ++++----- requirements_all.txt | 1 - requirements_test_all.txt | 1 - tests/components/samsungtv/test_init.py | 12 +- .../components/samsungtv/test_media_player.py | 105 ++++++------------ 9 files changed, 72 insertions(+), 139 deletions(-) diff --git a/homeassistant/components/samsungtv/__init__.py b/homeassistant/components/samsungtv/__init__.py index bcb9d094ecd..261bdd01a85 100644 --- a/homeassistant/components/samsungtv/__init__.py +++ b/homeassistant/components/samsungtv/__init__.py @@ -1,17 +1,10 @@ """The Samsung TV integration.""" import voluptuous as vol -from wakeonlan import BROADCAST_IP -from homeassistant.const import ( - CONF_BROADCAST_ADDRESS, - CONF_HOST, - CONF_MAC, - CONF_NAME, - CONF_PORT, -) +from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT import homeassistant.helpers.config_validation as cv -from .const import DEFAULT_NAME, DOMAIN +from .const import CONF_ON_ACTION, DEFAULT_NAME, DOMAIN CONFIG_SCHEMA = vol.Schema( { @@ -22,10 +15,7 @@ CONFIG_SCHEMA = vol.Schema( vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PORT): cv.port, - vol.Optional(CONF_MAC): cv.string, - vol.Optional( - CONF_BROADCAST_ADDRESS, default=BROADCAST_IP - ): cv.string, + vol.Optional(CONF_ON_ACTION): cv.SCRIPT_SCHEMA, } ) ] diff --git a/homeassistant/components/samsungtv/config_flow.py b/homeassistant/components/samsungtv/config_flow.py index da221cd1241..9a16c852764 100644 --- a/homeassistant/components/samsungtv/config_flow.py +++ b/homeassistant/components/samsungtv/config_flow.py @@ -14,16 +14,21 @@ from homeassistant.components.ssdp import ( ATTR_UPNP_UDN, ) from homeassistant.const import ( - CONF_BROADCAST_ADDRESS, CONF_HOST, CONF_ID, CONF_IP_ADDRESS, - CONF_MAC, CONF_NAME, CONF_PORT, ) -from .const import CONF_MANUFACTURER, CONF_MODEL, DOMAIN, LOGGER, METHODS +from .const import ( + CONF_MANUFACTURER, + CONF_MODEL, + CONF_ON_ACTION, + DOMAIN, + LOGGER, + METHODS, +) DATA_SCHEMA = vol.Schema({vol.Required(CONF_HOST): str, vol.Required(CONF_NAME): str}) @@ -90,13 +95,12 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def __init__(self): """Initialize flow.""" - self._broadcast = None self._host = None self._ip = None - self._mac = None self._manufacturer = None self._model = None self._name = None + self._on_script = None self._port = None self._title = None self._uuid = None @@ -105,14 +109,13 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry( title=self._title, data={ - CONF_BROADCAST_ADDRESS: self._broadcast, CONF_HOST: self._host, CONF_ID: self._uuid, CONF_IP_ADDRESS: self._ip, - CONF_MAC: self._mac, CONF_MANUFACTURER: self._manufacturer, CONF_MODEL: self._model, CONF_NAME: self._name, + CONF_ON_ACTION: self._on_script, CONF_PORT: self._port, }, ) @@ -134,10 +137,9 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if _is_already_configured(self.hass, ip_address): return self.async_abort(reason="already_configured") - self._broadcast = user_input.get(CONF_BROADCAST_ADDRESS) self._host = user_input.get(CONF_HOST) self._ip = self.context[CONF_IP_ADDRESS] = ip_address - self._mac = user_input.get(CONF_MAC) + self._on_script = user_input.get(CONF_ON_ACTION) self._port = user_input.get(CONF_PORT) self._title = user_input.get(CONF_NAME) diff --git a/homeassistant/components/samsungtv/const.py b/homeassistant/components/samsungtv/const.py index 6717b59f4f2..7cf71e406cb 100644 --- a/homeassistant/components/samsungtv/const.py +++ b/homeassistant/components/samsungtv/const.py @@ -8,5 +8,6 @@ DEFAULT_NAME = "Samsung TV Remote" CONF_MANUFACTURER = "manufacturer" CONF_MODEL = "model" +CONF_ON_ACTION = "turn_on_action" METHODS = ("websocket", "legacy") diff --git a/homeassistant/components/samsungtv/manifest.json b/homeassistant/components/samsungtv/manifest.json index bbb2f33828c..7fb31d57980 100644 --- a/homeassistant/components/samsungtv/manifest.json +++ b/homeassistant/components/samsungtv/manifest.json @@ -3,8 +3,7 @@ "name": "Samsung TV", "documentation": "https://www.home-assistant.io/integrations/samsungtv", "requirements": [ - "samsungctl[websocket]==0.7.1", - "wakeonlan==1.1.6" + "samsungctl[websocket]==0.7.1" ], "ssdp": [ { diff --git a/homeassistant/components/samsungtv/media_player.py b/homeassistant/components/samsungtv/media_player.py index 24752558213..bc8122a1be6 100644 --- a/homeassistant/components/samsungtv/media_player.py +++ b/homeassistant/components/samsungtv/media_player.py @@ -4,7 +4,6 @@ from datetime import timedelta from samsungctl import Remote as SamsungRemote, exceptions as samsung_exceptions import voluptuous as vol -import wakeonlan from websocket import WebSocketException from homeassistant.components.media_player import DEVICE_CLASS_TV, MediaPlayerDevice @@ -21,19 +20,19 @@ from homeassistant.components.media_player.const import ( SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP, ) -from homeassistant.const import ( - CONF_BROADCAST_ADDRESS, - CONF_HOST, - CONF_ID, - CONF_MAC, - CONF_PORT, - STATE_OFF, - STATE_ON, -) +from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT, STATE_OFF, STATE_ON import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.script import Script from homeassistant.util import dt as dt_util -from .const import CONF_MANUFACTURER, CONF_MODEL, DOMAIN, LOGGER, METHODS +from .const import ( + CONF_MANUFACTURER, + CONF_MODEL, + CONF_ON_ACTION, + DOMAIN, + LOGGER, + METHODS, +) KEY_PRESS_TIMEOUT = 1.2 SOURCES = {"TV": "KEY_TV", "HDMI": "KEY_HDMI"} @@ -61,20 +60,16 @@ async def async_setup_platform( async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the Samsung TV from a config entry.""" host = config_entry.data[CONF_HOST] - mac = config_entry.data.get(CONF_MAC) - broadcast = config_entry.data.get(CONF_BROADCAST_ADDRESS) or wakeonlan.BROADCAST_IP manufacturer = config_entry.data.get(CONF_MANUFACTURER) model = config_entry.data.get(CONF_MODEL) name = config_entry.title port = config_entry.data.get(CONF_PORT) uuid = config_entry.data.get(CONF_ID) + turn_on_action = config_entry.data.get(CONF_ON_ACTION) + on_script = Script(hass, turn_on_action) if turn_on_action else None async_add_entities( - [ - SamsungTVDevice( - host, port, name, mac, broadcast, uuid, manufacturer, model - ) - ] + [SamsungTVDevice(host, port, name, uuid, manufacturer, model, on_script)] ) @@ -82,24 +77,15 @@ class SamsungTVDevice(MediaPlayerDevice): """Representation of a Samsung TV.""" def __init__( - self, - host, - port, - name, - mac, - broadcast, - uuid, - manufacturer=None, - model=None, + self, host, port, name, uuid, manufacturer=None, model=None, on_script=None, ): """Initialize the Samsung device.""" self._name = name - self._mac = mac - self._broadcast = broadcast self._uuid = uuid self._manufacturer = manufacturer self._model = model + self._on_script = on_script # Assume that the TV is not muted self._muted = False # Assume that the TV is in Play mode @@ -250,7 +236,7 @@ class SamsungTVDevice(MediaPlayerDevice): @property def supported_features(self): """Flag media player features that are supported.""" - if self._mac: + if self._on_script: return SUPPORT_SAMSUNGTV | SUPPORT_TURN_ON return SUPPORT_SAMSUNGTV @@ -329,10 +315,10 @@ class SamsungTVDevice(MediaPlayerDevice): await asyncio.sleep(KEY_PRESS_TIMEOUT, self.hass.loop) await self.hass.async_add_job(self.send_key, "KEY_ENTER") - def turn_on(self): + async def async_turn_on(self): """Turn the media player on.""" - if self._mac: - wakeonlan.send_magic_packet(self._mac, ip_address=self._broadcast) + if self._on_script: + await self.hass.async_add_job(self._on_script.async_run()) else: self.send_key("KEY_POWERON") diff --git a/requirements_all.txt b/requirements_all.txt index d2ed4e0e1e2..ac28e84a872 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2016,7 +2016,6 @@ vtjp==0.1.14 vultr==0.1.2 # homeassistant.components.panasonic_viera -# homeassistant.components.samsungtv # homeassistant.components.wake_on_lan wakeonlan==1.1.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ff1ded4434c..d8680636da2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -629,7 +629,6 @@ vsure==1.5.4 vultr==0.1.2 # homeassistant.components.panasonic_viera -# homeassistant.components.samsungtv # homeassistant.components.wake_on_lan wakeonlan==1.1.6 diff --git a/tests/components/samsungtv/test_init.py b/tests/components/samsungtv/test_init.py index fe7ef6cc336..0b4219e55e7 100644 --- a/tests/components/samsungtv/test_init.py +++ b/tests/components/samsungtv/test_init.py @@ -5,14 +5,15 @@ import pytest from homeassistant.components import samsungtv from homeassistant.components.media_player.const import DOMAIN, SUPPORT_TURN_ON -from homeassistant.components.samsungtv.const import DOMAIN as SAMSUNGTV_DOMAIN +from homeassistant.components.samsungtv.const import ( + CONF_ON_ACTION, + DOMAIN as SAMSUNGTV_DOMAIN, +) from homeassistant.components.samsungtv.media_player import SUPPORT_SAMSUNGTV from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, - CONF_BROADCAST_ADDRESS, CONF_HOST, - CONF_MAC, CONF_NAME, CONF_PORT, SERVICE_VOLUME_UP, @@ -22,11 +23,10 @@ ENTITY_ID = f"{DOMAIN}.fake_name" MOCK_CONFIG = { SAMSUNGTV_DOMAIN: [ { - CONF_BROADCAST_ADDRESS: "fake_broadcast", CONF_HOST: "fake_host", - CONF_MAC: "fake_mac", CONF_NAME: "fake_name", CONF_PORT: 1234, + CONF_ON_ACTION: None, } ] } @@ -56,7 +56,7 @@ async def test_setup(hass, remote): await hass.async_block_till_done() state = hass.states.get(ENTITY_ID) - # test name and mac + # test name and turn_on assert state assert state.name == "fake_name" assert ( diff --git a/tests/components/samsungtv/test_media_player.py b/tests/components/samsungtv/test_media_player.py index a3d201a411d..4e8f2426048 100644 --- a/tests/components/samsungtv/test_media_player.py +++ b/tests/components/samsungtv/test_media_player.py @@ -7,7 +7,6 @@ from unittest.mock import call, patch from asynctest import mock import pytest from samsungctl import exceptions -import wakeonlan from websocket import WebSocketException from homeassistant.components.media_player import DEVICE_CLASS_TV @@ -23,16 +22,17 @@ from homeassistant.components.media_player.const import ( SERVICE_SELECT_SOURCE, SUPPORT_TURN_ON, ) -from homeassistant.components.samsungtv.const import DOMAIN as SAMSUNGTV_DOMAIN +from homeassistant.components.samsungtv.const import ( + CONF_ON_ACTION, + DOMAIN as SAMSUNGTV_DOMAIN, +) from homeassistant.components.samsungtv.media_player import SUPPORT_SAMSUNGTV from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, ATTR_SUPPORTED_FEATURES, - CONF_BROADCAST_ADDRESS, CONF_HOST, - CONF_MAC, CONF_NAME, CONF_PORT, SERVICE_MEDIA_NEXT_TRACK, @@ -60,32 +60,15 @@ MOCK_CONFIG = { CONF_HOST: "fake", CONF_NAME: "fake", CONF_PORT: 8001, - CONF_MAC: "38:f9:d3:82:b4:f1", + CONF_ON_ACTION: [{"delay": "00:00:01"}], } ] } -ENTITY_ID_BROADCAST = f"{DOMAIN}.fake_broadcast" -MOCK_CONFIG_BROADCAST = { +ENTITY_ID_NOTURNON = f"{DOMAIN}.fake_noturnon" +MOCK_CONFIG_NOTURNON = { SAMSUNGTV_DOMAIN: [ - { - CONF_HOST: "fake_broadcast", - CONF_NAME: "fake_broadcast", - CONF_PORT: 8001, - CONF_MAC: "38:f9:d3:82:b4:f1", - CONF_BROADCAST_ADDRESS: "192.168.5.255", - } - ] -} - -ENTITY_ID_NOMAC = f"{DOMAIN}.fake_nomac" -MOCK_CONFIG_NOMAC = { - SAMSUNGTV_DOMAIN: [ - { - CONF_HOST: "fake_nomac", - CONF_NAME: "fake_nomac", - CONF_PORT: 55000, - } + {CONF_HOST: "fake_noturnon", CONF_NAME: "fake_noturnon", CONF_PORT: 55000} ] } @@ -151,16 +134,6 @@ def remote_fixture(): yield remote -@pytest.fixture(name="wakeonlan") -def wakeonlan_fixture(): - """Patch the wakeonlan Remote.""" - with patch( - "homeassistant.components.samsungtv.media_player.wakeonlan" - ) as wakeonlan_module: - wakeonlan_module.BROADCAST_IP = wakeonlan.BROADCAST_IP - yield wakeonlan_module - - @pytest.fixture def mock_now(): """Fixture for dtutil.now.""" @@ -173,7 +146,7 @@ async def setup_samsungtv(hass, config): await hass.async_block_till_done() -async def test_setup_with_mac(hass, remote): +async def test_setup_with_turnon(hass, remote): """Test setup of platform.""" await setup_samsungtv(hass, MOCK_CONFIG) assert hass.states.get(ENTITY_ID) @@ -192,10 +165,10 @@ async def test_setup_duplicate(hass, remote, caplog): assert len(hass.states.async_all()) == 1 -async def test_setup_without_mac(hass, remote): +async def test_setup_without_turnon(hass, remote): """Test setup of platform.""" - await setup_samsungtv(hass, MOCK_CONFIG_NOMAC) - assert hass.states.get(ENTITY_ID_NOMAC) + await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON) + assert hass.states.get(ENTITY_ID_NOTURNON) async def test_update_on(hass, remote, mock_now): @@ -225,7 +198,7 @@ async def test_update_off(hass, remote, mock_now): assert state.state == STATE_OFF -async def test_send_key(hass, remote, wakeonlan): +async def test_send_key(hass, remote): """Test for send key.""" await setup_samsungtv(hass, MOCK_CONFIG) assert await hass.services.async_call( @@ -394,7 +367,7 @@ async def test_name(hass, remote): assert state.attributes[ATTR_FRIENDLY_NAME] == "fake" -async def test_state_with_mac(hass, remote, wakeonlan): +async def test_state_with_turnon(hass, remote): """Test for state property.""" await setup_samsungtv(hass, MOCK_CONFIG) assert await hass.services.async_call( @@ -409,22 +382,22 @@ async def test_state_with_mac(hass, remote, wakeonlan): assert state.state == STATE_OFF -async def test_state_without_mac(hass, remote): +async def test_state_without_turnon(hass, remote): """Test for state property.""" - await setup_samsungtv(hass, MOCK_CONFIG_NOMAC) + await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON) assert await hass.services.async_call( - DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID_NOMAC}, True + DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True ) - state = hass.states.get(ENTITY_ID_NOMAC) + state = hass.states.get(ENTITY_ID_NOTURNON) assert state.state == STATE_ON assert await hass.services.async_call( - DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID_NOMAC}, True + DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True ) - state = hass.states.get(ENTITY_ID_NOMAC) + state = hass.states.get(ENTITY_ID_NOTURNON) assert state.state == STATE_OFF -async def test_supported_features_with_mac(hass, remote): +async def test_supported_features_with_turnon(hass, remote): """Test for supported_features property.""" await setup_samsungtv(hass, MOCK_CONFIG) state = hass.states.get(ENTITY_ID) @@ -433,10 +406,10 @@ async def test_supported_features_with_mac(hass, remote): ) -async def test_supported_features_without_mac(hass, remote): +async def test_supported_features_without_turnon(hass, remote): """Test for supported_features property.""" - await setup_samsungtv(hass, MOCK_CONFIG_NOMAC) - state = hass.states.get(ENTITY_ID_NOMAC) + await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON) + state = hass.states.get(ENTITY_ID_NOTURNON) assert state.attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_SAMSUNGTV @@ -460,9 +433,9 @@ async def test_turn_off_websocket(hass, remote): async def test_turn_off_legacy(hass, remote): """Test for turn_off.""" - await setup_samsungtv(hass, MOCK_CONFIG_NOMAC) + await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON) assert await hass.services.async_call( - DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID_NOMAC}, True + DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True ) # key called assert remote.control.call_count == 1 @@ -560,37 +533,21 @@ async def test_media_previous_track(hass, remote): assert remote.control.call_args_list == [call("KEY_CHDOWN"), call("KEY")] -async def test_turn_on_with_mac(hass, remote, wakeonlan): +async def test_turn_on_with_turnon(hass, remote): """Test turn on.""" await setup_samsungtv(hass, MOCK_CONFIG) assert await hass.services.async_call( DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, True ) # key and update called - assert wakeonlan.send_magic_packet.call_count == 1 - assert wakeonlan.send_magic_packet.call_args_list == [ - call("38:f9:d3:82:b4:f1", ip_address="255.255.255.255") - ] + assert False # TODO -async def test_turn_on_with_mac_and_broadcast(hass, remote, wakeonlan): +async def test_turn_on_without_turnon(hass, remote): """Test turn on.""" - await setup_samsungtv(hass, MOCK_CONFIG_BROADCAST) + await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON) assert await hass.services.async_call( - DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID_BROADCAST}, True - ) - # key and update called - assert wakeonlan.send_magic_packet.call_count == 1 - assert wakeonlan.send_magic_packet.call_args_list == [ - call("38:f9:d3:82:b4:f1", ip_address="192.168.5.255") - ] - - -async def test_turn_on_without_mac(hass, remote): - """Test turn on.""" - await setup_samsungtv(hass, MOCK_CONFIG_NOMAC) - assert await hass.services.async_call( - DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID_NOMAC}, True + DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True ) # nothing called as not supported feature assert remote.control.call_count == 0