From bee80c5b797d8b80a47e193bca879199d34dd1d6 Mon Sep 17 00:00:00 2001 From: GreenTurtwig Date: Sat, 9 Dec 2017 19:01:23 +0000 Subject: [PATCH] Add support for Logitech UE Smart Radios. (#10077) * Add support for Logitech UE Smart Radios. * Removed full stops to please Hound's line limit. * Updated with requested changes. * Fix Pylint Flake8 problem. * Updated with requested changes. --- .coveragerc | 1 + .../components/media_player/ue_smart_radio.py | 207 ++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 homeassistant/components/media_player/ue_smart_radio.py diff --git a/.coveragerc b/.coveragerc index 9db732dfbde..c4b003708d5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -427,6 +427,7 @@ omit = homeassistant/components/media_player/sonos.py homeassistant/components/media_player/spotify.py homeassistant/components/media_player/squeezebox.py + homeassistant/components/media_player/ue_smart_radio.py homeassistant/components/media_player/vizio.py homeassistant/components/media_player/vlc.py homeassistant/components/media_player/volumio.py diff --git a/homeassistant/components/media_player/ue_smart_radio.py b/homeassistant/components/media_player/ue_smart_radio.py new file mode 100644 index 00000000000..2684a819417 --- /dev/null +++ b/homeassistant/components/media_player/ue_smart_radio.py @@ -0,0 +1,207 @@ +""" +Support for Logitech UE Smart Radios. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/media_player.ue_smart_radio/ +""" + +import logging +import voluptuous as vol +import requests + +from homeassistant.components.media_player import ( + MediaPlayerDevice, MEDIA_TYPE_MUSIC, PLATFORM_SCHEMA, + SUPPORT_PLAY, SUPPORT_PAUSE, SUPPORT_STOP, SUPPORT_PREVIOUS_TRACK, + SUPPORT_NEXT_TRACK, SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_VOLUME_SET, + SUPPORT_VOLUME_MUTE) +from homeassistant.const import ( + CONF_USERNAME, CONF_PASSWORD, STATE_OFF, STATE_IDLE, STATE_PLAYING, + STATE_PAUSED) +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +ICON = "mdi:radio" +URL = "http://decibel.logitechmusic.com/jsonrpc.js" + +SUPPORT_UE_SMART_RADIO = SUPPORT_PLAY | SUPPORT_PAUSE | SUPPORT_STOP | \ + SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_TURN_ON | \ + SUPPORT_TURN_OFF | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE + +PLAYBACK_DICT = {"play": STATE_PLAYING, + "pause": STATE_PAUSED, + "stop": STATE_IDLE} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, +}) + + +def send_request(payload, session): + """Send request to radio.""" + try: + request = requests.post(URL, + cookies={"sdi_squeezenetwork_session": + session}, + json=payload, timeout=5) + except requests.exceptions.Timeout: + _LOGGER.error("Timed out when sending request") + except requests.exceptions.ConnectionError: + _LOGGER.error("An error occurred while connecting") + else: + return request.json() + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Logitech UE Smart Radio platform.""" + email = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + + session_request = requests.post("https://www.uesmartradio.com/user/login", + data={"email": email, "password": + password}) + session = session_request.cookies["sdi_squeezenetwork_session"] + + player_request = send_request({"params": ["", ["serverstatus"]]}, session) + player_id = player_request["result"]["players_loop"][0]["playerid"] + player_name = player_request["result"]["players_loop"][0]["name"] + + add_devices([UERadioDevice(session, player_id, player_name)]) + + +class UERadioDevice(MediaPlayerDevice): + """Representation of a Logitech UE Smart Radio device.""" + + def __init__(self, session, player_id, player_name): + """Initialize the Logitech UE Smart Radio device.""" + self._session = session + self._player_id = player_id + self._name = player_name + self._state = None + self._volume = 0 + self._last_volume = 0 + self._media_title = None + self._media_artist = None + self._media_artwork_url = None + + def send_command(self, command): + """Send command to radio.""" + send_request({"method": "slim.request", "params": + [self._player_id, command]}, self._session) + + def update(self): + """Get the latest details from the device.""" + request = send_request({ + "method": "slim.request", "params": + [self._player_id, ["status", "-", 1, + "tags:cgABbehldiqtyrSuoKLN"]]}, self._session) + + if request["error"] is not None: + self._state = None + return + + if request["result"]["power"] == 0: + self._state = STATE_OFF + else: + self._state = PLAYBACK_DICT[request["result"]["mode"]] + + media_info = request["result"]["playlist_loop"][0] + + self._volume = request["result"]["mixer volume"] / 100 + self._media_artwork_url = media_info["artwork_url"] + self._media_title = media_info["title"] + if "artist" in media_info: + self._media_artist = media_info["artist"] + else: + self._media_artist = media_info.get("remote_title") + + @property + def name(self): + """Return the name of the device.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + return self._state + + @property + def icon(self): + """Return the icon to use in the frontend, if any.""" + return ICON + + @property + def is_volume_muted(self): + """Boolean if volume is currently muted.""" + return True if self._volume <= 0 else False + + @property + def volume_level(self): + """Volume level of the media player (0..1).""" + return self._volume + + @property + def supported_features(self): + """Flag of features that are supported.""" + return SUPPORT_UE_SMART_RADIO + + @property + def media_content_type(self): + """Return the media content type.""" + return MEDIA_TYPE_MUSIC + + @property + def media_image_url(self): + """Image URL of current playing media.""" + return self._media_artwork_url + + @property + def media_artist(self): + """Artist of current playing media, music track only.""" + return self._media_artist + + @property + def media_title(self): + """Title of current playing media.""" + return self._media_title + + def turn_on(self): + """Turn on specified media player or all.""" + self.send_command(["power", 1]) + + def turn_off(self): + """Turn off specified media player or all.""" + self.send_command(["power", 0]) + + def media_play(self): + """Send the media player the command for play/pause.""" + self.send_command(["play"]) + + def media_pause(self): + """Send the media player the command for pause.""" + self.send_command(["pause"]) + + def media_stop(self): + """Send the media player the stop command.""" + self.send_command(["stop"]) + + def media_previous_track(self): + """Send the media player the command for prev track.""" + self.send_command(["button", "rew"]) + + def media_next_track(self): + """Send the media player the command for next track.""" + self.send_command(["button", "fwd"]) + + def mute_volume(self, mute): + """Send mute command.""" + if mute: + self._last_volume = self._volume + self.send_command(["mixer", "volume", 0]) + else: + self.send_command(["mixer", "volume", self._last_volume * 100]) + + def set_volume_level(self, volume): + """Set volume level, range 0..1.""" + self.send_command(["mixer", "volume", volume * 100])