diff --git a/.coveragerc b/.coveragerc index 14bd1c68cb3..2fdaa9dff5f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -94,6 +94,7 @@ omit = homeassistant/components/media_player/plex.py homeassistant/components/media_player/sonos.py homeassistant/components/media_player/squeezebox.py + homeassistant/components/media_player/samsungtv.py homeassistant/components/notify/free_mobile.py homeassistant/components/notify/instapush.py homeassistant/components/notify/nma.py diff --git a/homeassistant/components/media_player/samsungtv.py b/homeassistant/components/media_player/samsungtv.py new file mode 100644 index 00000000000..1d2703b0e2e --- /dev/null +++ b/homeassistant/components/media_player/samsungtv.py @@ -0,0 +1,170 @@ +""" +homeassistant.components.media_player.denon +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Provides an interface to Samsung TV with a Laninterface. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/media_player.samsungtv/ +""" +import logging +import socket + +from homeassistant.components.media_player import ( + MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_VOLUME_STEP, + SUPPORT_VOLUME_MUTE, SUPPORT_PREVIOUS_TRACK, + SUPPORT_NEXT_TRACK, SUPPORT_TURN_OFF, + DOMAIN) +from homeassistant.const import ( + CONF_HOST, CONF_NAME, STATE_OFF, + STATE_ON, STATE_UNKNOWN) + +from homeassistant.helpers import validate_config + +CONF_PORT = "port" +CONF_TIMEOUT = "timeout" + +_LOGGER = logging.getLogger(__name__) + +REQUIREMENTS = ['samsungctl==0.5.1'] + +SUPPORT_SAMSUNGTV = SUPPORT_PAUSE | SUPPORT_VOLUME_STEP | \ + SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | \ + SUPPORT_NEXT_TRACK | SUPPORT_TURN_OFF + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Samsung TV platform. """ + + # Validate that all required config options are given + if not validate_config({DOMAIN: config}, {DOMAIN: [CONF_HOST]}, _LOGGER): + return False + + # Default the entity_name to 'Samsung TV Remote' + name = config.get(CONF_NAME, 'Samsung TV Remote') + + # Generate a config for the Samsung lib + remote_config = { + "name": "HomeAssistant", + "description": config.get(CONF_NAME, ''), + "id": "ha.component.samsung", + "port": config.get(CONF_PORT, 55000), + "host": config.get(CONF_HOST), + "timeout": config.get(CONF_TIMEOUT, 0), + } + + add_devices([SamsungTVDevice(name, remote_config)]) + + +# pylint: disable=abstract-method +class SamsungTVDevice(MediaPlayerDevice): + """ Represents a Samsung TV. """ + + # pylint: disable=too-many-public-methods + def __init__(self, name, config): + from samsungctl import Remote + # Save a reference to the imported class + self._remote_class = Remote + self._name = name + # Assume that the TV is not muted + self._muted = False + # Assume that the TV is in Play mode + self._playing = True + self._state = STATE_UNKNOWN + self._remote = None + self._config = config + + def update(self): + # Send an empty key to see if we are still connected + return self.send_key('KEY_POWER') + + def get_remote(self): + """ Creates or Returns a remote control instance """ + + if self._remote is None: + # We need to create a new instance to reconnect. + self._remote = self._remote_class(self._config) + + return self._remote + + def send_key(self, key): + """ Sends a key to the tv and handles exceptions """ + try: + self.get_remote().control(key) + self._state = STATE_ON + except (self._remote_class.UnhandledResponse, + self._remote_class.AccessDenied, BrokenPipeError): + # We got a response so it's on. + # BrokenPipe can occur when the commands is sent to fast + self._state = STATE_ON + self._remote = None + return False + except (self._remote_class.ConnectionClosed, socket.timeout, + TimeoutError, OSError): + self._state = STATE_OFF + self._remote = None + return False + + return True + + @property + def name(self): + """ Returns the name of the device. """ + return self._name + + @property + def state(self): + return self._state + + @property + def is_volume_muted(self): + """ Boolean if volume is currently muted. """ + return self._muted + + @property + def supported_media_commands(self): + """ Flags of media commands that are supported. """ + return SUPPORT_SAMSUNGTV + + def turn_off(self): + """ turn_off media player. """ + self.send_key("KEY_POWEROFF") + + def volume_up(self): + """ volume_up media player. """ + self.send_key("KEY_VOLUP") + + def volume_down(self): + """ volume_down media player. """ + self.send_key("KEY_VOLDOWN") + + def mute_volume(self, mute): + self.send_key("KEY_MUTE") + + def media_play_pause(self): + """ Simulate play pause media player. """ + if self._playing: + self.media_pause() + else: + self.media_play() + + def media_play(self): + """ media_play media player. """ + self._playing = True + self.send_key("KEY_PLAY") + + def media_pause(self): + """ media_pause media player. """ + self._playing = False + self.send_key("KEY_PAUSE") + + def media_next_track(self): + """ Send next track command. """ + self.send_key("KEY_FF") + + def media_previous_track(self): + self.send_key("KEY_REWIND") + + def turn_on(self): + """ turn the media player on. """ + self.send_key("KEY_POWERON") diff --git a/requirements_all.txt b/requirements_all.txt index 295518f367b..9e9f33188ec 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -92,6 +92,9 @@ python-mpd2==0.5.4 # homeassistant.components.media_player.plex plexapi==1.1.0 +# homeassistant.components.media_player.samsungtv +samsungctl==0.5.1 + # homeassistant.components.media_player.sonos SoCo==0.11.1