From faf8bbcf13c42de7972ff81f7ef17fc5d3c9c137 Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Mon, 27 Feb 2017 22:55:34 -0500 Subject: [PATCH] Fix toggle and media_play_pause post async (#6291) --- .../components/media_player/__init__.py | 22 ++-- .../media_player/test_async_helpers.py | 104 ++++++++++++++++++ 2 files changed, 112 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index f4b828a0289..a603cb9c3e3 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -757,18 +757,15 @@ class MediaPlayerDevice(Entity): """Boolean if clear playlist command supported.""" return bool(self.supported_features & SUPPORT_CLEAR_PLAYLIST) - def toggle(self): - """Toggle the power on the media player.""" - if self.state in [STATE_OFF, STATE_IDLE]: - self.turn_on() - else: - self.turn_off() - def async_toggle(self): """Toggle the power on the media player. This method must be run in the event loop and returns a coroutine. """ + if hasattr(self, 'toggle'): + # pylint: disable=no-member + return self.hass.loop.run_in_executor(None, self.toggle) + if self.state in [STATE_OFF, STATE_IDLE]: return self.async_turn_on() else: @@ -804,18 +801,15 @@ class MediaPlayerDevice(Entity): yield from self.async_set_volume_level( max(0, self.volume_level - .1)) - def media_play_pause(self): - """Play or pause the media player.""" - if self.state == STATE_PLAYING: - self.media_pause() - else: - self.media_play() - def async_media_play_pause(self): """Play or pause the media player. This method must be run in the event loop and returns a coroutine. """ + if hasattr(self, 'media_play_pause'): + # pylint: disable=no-member + return self.hass.loop.run_in_executor(None, self.media_play_pause) + if self.state == STATE_PLAYING: return self.async_media_pause() else: diff --git a/tests/components/media_player/test_async_helpers.py b/tests/components/media_player/test_async_helpers.py index 784c54f6d62..6acbf5c2db3 100644 --- a/tests/components/media_player/test_async_helpers.py +++ b/tests/components/media_player/test_async_helpers.py @@ -3,6 +3,8 @@ import unittest import asyncio import homeassistant.components.media_player as mp +from homeassistant.const import ( + STATE_PLAYING, STATE_PAUSED, STATE_ON, STATE_OFF, STATE_IDLE) from homeassistant.util.async import run_coroutine_threadsafe from tests.common import get_test_home_assistant @@ -15,6 +17,12 @@ class AsyncMediaPlayer(mp.MediaPlayerDevice): """Initialize the test media player.""" self.hass = hass self._volume = 0 + self._state = STATE_OFF + + @property + def state(self): + """State of the player.""" + return self._state @property def volume_level(self): @@ -26,6 +34,26 @@ class AsyncMediaPlayer(mp.MediaPlayerDevice): """Set volume level, range 0..1.""" self._volume = volume + @asyncio.coroutine + def async_media_play(self): + """Send play command.""" + self._state = STATE_PLAYING + + @asyncio.coroutine + def async_media_pause(self): + """Send pause command.""" + self._state = STATE_PAUSED + + @asyncio.coroutine + def async_turn_on(self): + """Turn the media player on.""" + self._state = STATE_ON + + @asyncio.coroutine + def async_turn_off(self): + """Turn the media player off.""" + self._state = STATE_OFF + class SyncMediaPlayer(mp.MediaPlayerDevice): """Sync media player test class.""" @@ -34,6 +62,12 @@ class SyncMediaPlayer(mp.MediaPlayerDevice): """Initialize the test media player.""" self.hass = hass self._volume = 0 + self._state = STATE_OFF + + @property + def state(self): + """State of the player.""" + return self._state @property def volume_level(self): @@ -54,6 +88,36 @@ class SyncMediaPlayer(mp.MediaPlayerDevice): if self.volume_level > 0: self.set_volume_level(max(0, self.volume_level - .2)) + def media_play_pause(self): + """Play or pause the media player.""" + if self._state == STATE_PLAYING: + self._state = STATE_PAUSED + else: + self._state = STATE_PLAYING + + def toggle(self): + """Toggle the power on the media player.""" + if self._state in [STATE_OFF, STATE_IDLE]: + self._state = STATE_ON + else: + self._state = STATE_OFF + + @asyncio.coroutine + def async_media_play_pause(self): + """Create a coroutine to wrap the future returned by ABC. + + This allows the run_coroutine_threadsafe helper to be used. + """ + yield from super().async_media_play_pause() + + @asyncio.coroutine + def async_toggle(self): + """Create a coroutine to wrap the future returned by ABC. + + This allows the run_coroutine_threadsafe helper to be used. + """ + yield from super().async_toggle() + class TestAsyncMediaPlayer(unittest.TestCase): """Test the media_player module.""" @@ -87,6 +151,26 @@ class TestAsyncMediaPlayer(unittest.TestCase): self.player.async_volume_down(), self.hass.loop).result() self.assertEqual(self.player.volume_level, 0.4) + def test_media_play_pause(self): + """Test the media_play_pause helper function.""" + self.assertEqual(self.player.state, STATE_OFF) + run_coroutine_threadsafe( + self.player.async_media_play_pause(), self.hass.loop).result() + self.assertEqual(self.player.state, STATE_PLAYING) + run_coroutine_threadsafe( + self.player.async_media_play_pause(), self.hass.loop).result() + self.assertEqual(self.player.state, STATE_PAUSED) + + def test_toggle(self): + """Test the toggle helper function.""" + self.assertEqual(self.player.state, STATE_OFF) + run_coroutine_threadsafe( + self.player.async_toggle(), self.hass.loop).result() + self.assertEqual(self.player.state, STATE_ON) + run_coroutine_threadsafe( + self.player.async_toggle(), self.hass.loop).result() + self.assertEqual(self.player.state, STATE_OFF) + class TestSyncMediaPlayer(unittest.TestCase): """Test the media_player module.""" @@ -117,3 +201,23 @@ class TestSyncMediaPlayer(unittest.TestCase): run_coroutine_threadsafe( self.player.async_volume_down(), self.hass.loop).result() self.assertEqual(self.player.volume_level, 0.3) + + def test_media_play_pause(self): + """Test the media_play_pause helper function.""" + self.assertEqual(self.player.state, STATE_OFF) + run_coroutine_threadsafe( + self.player.async_media_play_pause(), self.hass.loop).result() + self.assertEqual(self.player.state, STATE_PLAYING) + run_coroutine_threadsafe( + self.player.async_media_play_pause(), self.hass.loop).result() + self.assertEqual(self.player.state, STATE_PAUSED) + + def test_toggle(self): + """Test the toggle helper function.""" + self.assertEqual(self.player.state, STATE_OFF) + run_coroutine_threadsafe( + self.player.async_toggle(), self.hass.loop).result() + self.assertEqual(self.player.state, STATE_ON) + run_coroutine_threadsafe( + self.player.async_toggle(), self.hass.loop).result() + self.assertEqual(self.player.state, STATE_OFF)