diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index 323fda37a02..ee0225d2a76 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -185,3 +185,22 @@ sonos_restore: entity_id: description: Name(s) of entites that will be restored. Platform dependent. example: 'media_player.living_room_sonos' + +sonos_set_sleep_timer: + description: Set a Sonos timer + + fields: + entity_id: + description: Name(s) of entites that will have a timer set. + example: 'media_player.living_room_sonos' + sleep_time: + description: Number of seconds to set the timer + example: '900' + +sonos_clear_sleep_timer: + description: Clear a Sonos timer + + fields: + entity_id: + description: Name(s) of entites that will have the timer cleared. + example: 'media_player.living_room_sonos' diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index dd57a61f230..3bc6778ce39 100755 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -42,14 +42,24 @@ SERVICE_GROUP_PLAYERS = 'sonos_group_players' SERVICE_UNJOIN = 'sonos_unjoin' SERVICE_SNAPSHOT = 'sonos_snapshot' SERVICE_RESTORE = 'sonos_restore' +SERVICE_SET_TIMER = 'sonos_set_sleep_timer' +SERVICE_CLEAR_TIMER = 'sonos_clear_sleep_timer' SUPPORT_SOURCE_LINEIN = 'Line-in' SUPPORT_SOURCE_TV = 'TV' +# Service call validation schemas +ATTR_SLEEP_TIME = 'sleep_time' + SONOS_SCHEMA = vol.Schema({ ATTR_ENTITY_ID: cv.entity_ids, }) +SONOS_SET_TIMER_SCHEMA = SONOS_SCHEMA.extend({ + vol.Required(ATTR_SLEEP_TIME): vol.All(vol.Coerce(int), + vol.Range(min=0, max=86399)) +}) + # List of devices that have been registered DEVICES = [] @@ -126,6 +136,16 @@ def register_services(hass): descriptions.get(SERVICE_RESTORE), schema=SONOS_SCHEMA) + hass.services.register(DOMAIN, SERVICE_SET_TIMER, + _set_sleep_timer_service, + descriptions.get(SERVICE_SET_TIMER), + schema=SONOS_SET_TIMER_SCHEMA) + + hass.services.register(DOMAIN, SERVICE_CLEAR_TIMER, + _clear_sleep_timer_service, + descriptions.get(SERVICE_CLEAR_TIMER), + schema=SONOS_SCHEMA) + def _apply_service(service, service_func, *service_func_args): """Internal func for applying a service.""" @@ -162,6 +182,19 @@ def _restore_service(service): _apply_service(service, SonosDevice.restore) +def _set_sleep_timer_service(service): + """Set a timer.""" + _apply_service(service, + SonosDevice.set_sleep_timer, + service.data[ATTR_SLEEP_TIME]) + + +def _clear_sleep_timer_service(service): + """Set a timer.""" + _apply_service(service, + SonosDevice.clear_sleep_timer) + + def only_if_coordinator(func): """Decorator for coordinator. @@ -553,6 +586,16 @@ class SonosDevice(MediaPlayerDevice): """Restore snapshot for the player.""" self.soco_snapshot.restore(True) + @only_if_coordinator + def set_sleep_timer(self, sleep_time): + """Set the timer on the player.""" + self._player.set_sleep_timer(sleep_time) + + @only_if_coordinator + def clear_sleep_timer(self): + """Clear the timer on the player.""" + self._player.set_sleep_timer(None) + @property def available(self): """Return True if player is reachable, False otherwise.""" diff --git a/tests/components/media_player/test_sonos.py b/tests/components/media_player/test_sonos.py index d1fb87ef44a..42f39ca5572 100755 --- a/tests/components/media_player/test_sonos.py +++ b/tests/components/media_player/test_sonos.py @@ -38,6 +38,10 @@ class SoCoMock(): self.is_visible = True self.avTransport = AvTransportMock() + def clear_sleep_timer(self): + """Clear the sleep timer.""" + return + def get_speaker_info(self): """Return a dict with various data points about the speaker.""" return {'serial_number': 'B8-E9-37-BO-OC-BA:2', @@ -74,6 +78,10 @@ class SoCoMock(): """Cause the speaker to join all other speakers in the network.""" return + def set_sleep_timer(self, sleep_time_seconds): + """Set the sleep timer.""" + return + def unjoin(self): """Cause the speaker to separate itself from other speakers.""" return @@ -154,6 +162,24 @@ class TestSonosMediaPlayer(unittest.TestCase): self.assertEqual(unjoinMock.call_count, 1) self.assertEqual(unjoinMock.call_args, mock.call()) + @mock.patch('soco.SoCo', new=SoCoMock) + @mock.patch.object(SoCoMock, 'set_sleep_timer') + def test_sonos_set_sleep_timer(self, set_sleep_timerMock): + """Ensuring soco methods called for sonos_set_sleep_timer service.""" + sonos.setup_platform(self.hass, {}, mock.MagicMock(), '192.0.2.1') + device = sonos.DEVICES[-1] + device.set_sleep_timer(30) + set_sleep_timerMock.assert_called_once_with(30) + + @mock.patch('soco.SoCo', new=SoCoMock) + @mock.patch.object(SoCoMock, 'set_sleep_timer') + def test_sonos_clear_sleep_timer(self, set_sleep_timerMock): + """Ensuring soco methods called for sonos_clear_sleep_timer service.""" + sonos.setup_platform(self.hass, {}, mock.MagicMock(), '192.0.2.1') + device = sonos.DEVICES[-1] + device.set_sleep_timer(None) + set_sleep_timerMock.assert_called_once_with(None) + @mock.patch('soco.SoCo', new=SoCoMock) @mock.patch.object(soco.snapshot.Snapshot, 'snapshot') def test_sonos_snapshot(self, snapshotMock):