def __init__(self, host, port, name, timeout, mac, uuid, update_method,
                 update_custom_ping_url, source_list, app_list):
        """Initialize the Samsung device."""

        # Save a reference to the imported classes
        self._host = host
        self._name = name
        self._timeout = timeout
        self._mac = mac
        self._update_method = update_method
        self._update_custom_ping_url = update_custom_ping_url
        self._source_list = json.loads(source_list)
        self._app_list = json.loads(app_list) if app_list is not None else None
        self._uuid = uuid
        self._is_ws_connection = True if port in (8001, 8002) else False
        # Assume that the TV is not muted and volume is 0
        self._muted = False
        # Assume that the TV is in Play mode
        self._playing = True
        self._state = None
        # Mark the end of a shutdown command (need to wait 15 seconds before
        # sending the next command to avoid turning the TV back ON).
        self._end_of_power_off = None
        self._token_file = None

        # Generate token file only for WS + SSL + Token connection
        if port == 8002:
            self._gen_token_file()

        self._remote = SamsungTVWS(name=name,
                                   host=host,
                                   port=port,
                                   timeout=self._timeout,
                                   key_press_delay=KEY_PRESS_TIMEOUT,
                                   token_file=self._token_file)
 def _get_remote(self):
     """Create or return a remote control instance."""
     if self._remote is None:
         # We need to create a new instance to reconnect.
         LOGGER.debug("Create SamsungTVWS")
         self._remote = SamsungTVWS(
             host=self.config["host"],
             port=self.config["port"],
             token=self.config["token"],
             timeout=self.config["timeout"],
             name=self.config["name"],
         )
         self._remote.open()
     return self._remote
    def _try_connect_samsungtvws(self, port):
        if self._port is None or port == self._port:
            token_file = None
            if port == 8002:
                token_file = _get_token_file(self._host)
            config = {
                "name": "HomeAssistant",
                "description": "HomeAssistant",
                "host": self._host,
                "method": "websocket",
                "port": port,
                # We need this high timeout because waiting for auth popup is just an open socket
                "timeout": 31,
                "token_file": token_file,
            }
            try:
                LOGGER.debug("Try config: %s", config)
                # with SamsungTVWS(
                #     host=self._host,
                #     port=port,
                #     token_file=token_file,
                #     timeout=config["timeout"],
                #     name=config["name"],
                # ) as remote:
                remote = SamsungTVWS(
                    host=self._host,
                    port=port,
                    token_file=token_file,
                    timeout=config["timeout"],
                    name=config["name"],
                )
                remote.open()
                LOGGER.debug("Working config: %s", config)
                self._method = "websocket"
                self._port = port
                self._token_file = token_file
                return RESULT_SUCCESS
            except AccessDenied:
                LOGGER.debug("Working but denied config: %s", config)
                return RESULT_AUTH_MISSING
            except UnhandledResponse:
                LOGGER.debug("Working but unsupported config: %s", config)
                return RESULT_NOT_SUPPORTED
            except OSError as err:
                LOGGER.debug("Failing config: %s, error: %s", config, err)
            except AttributeError as err:
                LOGGER.debug("Failing config: %s, error: %s", config, err)

        return RESULT_NOT_SUCCESSFUL
Beispiel #4
0
    def try_connect(self):
        """Try to connect to the Websocket TV."""
        for self.port in (8002, 8001):
            config = {
                CONF_NAME: VALUE_CONF_NAME,
                CONF_HOST: self.host,
                CONF_METHOD: self.method,
                CONF_PORT: self.port,
                # We need this high timeout because waiting for auth popup is just an open socket
                CONF_TIMEOUT: 31,
                CONF_TOKEN: self.token,
            }
            try:
                LOGGER.debug("Try config: %s", _hide_token(config))
                with SamsungTVWS(
                    host=self.host,
                    port=self.port,
                    token=self.token,
                    timeout=config[CONF_TIMEOUT],
                    name=config[CONF_NAME],
                ) as remote:
                    remote.open()
                    self.token = remote.token
                LOGGER.debug("Working config: %s", _hide_token(config))
                return RESULT_SUCCESS
            except (WebSocketException, ConnectionFailure):
                LOGGER.debug("Working but unsupported config: %s", _hide_token(config))
                return RESULT_NOT_SUPPORTED
            except OSError as err:
                LOGGER.debug("Failing config: %s, error: %s", _hide_token(config), err)

        return RESULT_NOT_SUCCESSFUL
    def try_connect(self, port):
        """Try to connect to the Websocket TV."""
        for self.port in (8001, 8002):
            if port is not None and port != self.port:
                continue
            config = {
                "name": "HomeAssistant",
                "description": "HomeAssistant",
                "host": self.host,
                "method": self.method,
                "port": self.port,
                # We need this high timeout because waiting for auth popup is just an open socket
                "timeout": 31,
                "token": self.token,
            }
            try:
                LOGGER.debug("Try config: %s", config)
                with SamsungTVWS(
                        host=self.host,
                        port=self.port,
                        token=self.token,
                        timeout=config["timeout"],
                        name=config["name"],
                ) as remote:
                    remote.open()
                LOGGER.debug("Working config: %s", config)
                LOGGER.debug("Token: %s", self.token)
                return RESULT_SUCCESS
            except WebSocketException:
                LOGGER.debug("Working but unsupported config: %s", config)
                return RESULT_NOT_SUPPORTED
            except (OSError, ConnectionFailure) as err:
                LOGGER.debug("Failing config: %s, error: %s", config, err)

        return RESULT_NOT_SUCCESSFUL
Beispiel #6
0
 def _get_remote(self):
     """Create or return a remote control instance."""
     if self._remote is None:
         # We need to create a new instance to reconnect.
         try:
             LOGGER.debug("Create SamsungTVWS")
             self._remote = SamsungTVWS(
                 host=self.config[CONF_HOST],
                 port=self.config[CONF_PORT],
                 token=self.config[CONF_TOKEN],
                 timeout=self.config[CONF_TIMEOUT],
                 name=self.config[CONF_NAME],
             )
             self._remote.open()
         # This is only happening when the auth was switched to DENY
         # A removed auth will lead to socket timeout because waiting for auth popup is just an open socket
         except ConnectionFailure:
             self._notify_callback()
             raise
     return self._remote
Beispiel #7
0
 def _get_remote(self):
     """Create or return a remote control instance."""
     if self._remote is None:
         # We need to create a new instance to reconnect.
         try:
             LOGGER.debug("Create SamsungTVWS")
             self._remote = SamsungTVWS(
                 host=self.host,
                 port=self.port,
                 token=self.token,
                 timeout=1,
                 name=VALUE_CONF_NAME,
             )
             self._remote.open()
         # This is only happening when the auth was switched to DENY
         # A removed auth will lead to socket timeout because waiting for auth popup is just an open socket
         except ConnectionFailure:
             self._notify_callback()
             raise
         except WebSocketException:
             self._remote = None
     return self._remote
Beispiel #8
0
    def connect(self,
                host: Optional[str] = None,
                port: Optional[int] = None) -> SamsungTVWS:
        host, port = self._get_host_and_port(host, port)
        if (host, port) not in self._connections:
            self._connections[(host,
                               port)] = SamsungTVWS(host=host,
                                                    port=port,
                                                    token_file=self.token_file,
                                                    timeout=self.timeout,
                                                    name=self.name)

        return self._connections[(host, port)]
    def __init__(self, host, port, name, timeout, mac, uuid, update_method, update_custom_ping_url, source_list, app_list):
        """Initialize the Samsung device."""

        # Save a reference to the imported classes
        self._name = name
        self._host = host
        self._mac = mac
        self._update_method = update_method
        self._update_custom_ping_url = update_custom_ping_url
        self._source_list = json.loads(source_list)
        self._app_list = json.loads(app_list) if app_list is not None else None
        self._uuid = uuid
        self._is_ws_connection = True if port in (8001, 8002) else False
        # Assume that the TV is not muted and volume is 0
        self._muted = False
        # Assume that the TV is in Play mode
        self._playing = True
        self._state = None
        # Mark the end of a shutdown command (need to wait 15 seconds before
        # sending the next command to avoid turning the TV back ON).
        self._end_of_power_off = None

        token_file = None
        if port == 8002:
            token_file = os.path.dirname(os.path.realpath(__file__)) + '/token-' + host + '.txt'

            # For correct set of auth token
            if os.path.isfile(token_file) is False:
                timeout = 30

        self._remote = SamsungTVWS(
            name=name,
            host=host,
            port=port,
            timeout=timeout,
            key_press_delay=KEY_PRESS_TIMEOUT,
            token_file=token_file
        )
Beispiel #10
0
 def _get_remote(self, avoid_open: bool = False):
     """Create or return a remote control instance."""
     if self._remote is None:
         # We need to create a new instance to reconnect.
         try:
             LOGGER.debug("Create SamsungTVWSBridge for %s (%s)", CONF_NAME,
                          self.host)
             self._remote = SamsungTVWS(
                 host=self.host,
                 port=self.port,
                 token=self.token,
                 timeout=TIMEOUT_WEBSOCKET,
                 name=VALUE_CONF_NAME,
             )
             if not avoid_open:
                 self._remote.open()
         # This is only happening when the auth was switched to DENY
         # A removed auth will lead to socket timeout because waiting for auth popup is just an open socket
         except ConnectionFailure:
             self._notify_callback()
         except (WebSocketException, OSError):
             self._remote = None
     return self._remote
Beispiel #11
0
    def try_connect(self):
        """Try to connect to the Websocket TV."""
        for self.port in WEBSOCKET_PORTS:
            config = {
                CONF_NAME: VALUE_CONF_NAME,
                CONF_HOST: self.host,
                CONF_METHOD: self.method,
                CONF_PORT: self.port,
                # We need this high timeout because waiting for auth popup is just an open socket
                CONF_TIMEOUT: TIMEOUT_REQUEST,
            }

            result = None
            try:
                LOGGER.debug("Try config: %s", config)
                with SamsungTVWS(
                        host=self.host,
                        port=self.port,
                        token=self.token,
                        timeout=config[CONF_TIMEOUT],
                        name=config[CONF_NAME],
                ) as remote:
                    remote.open()
                    self.token = remote.token
                    if self.token:
                        config[CONF_TOKEN] = "*****"
                LOGGER.debug("Working config: %s", config)
                return RESULT_SUCCESS
            except WebSocketException as err:
                LOGGER.debug("Working but unsupported config: %s, error: %s",
                             config, err)
                result = RESULT_NOT_SUPPORTED
            except (OSError, ConnectionFailure) as err:
                LOGGER.debug("Failing config: %s, error: %s", config, err)
        # pylint: disable=useless-else-on-loop
        else:
            if result:
                return result

        return RESULT_CANNOT_CONNECT
class SamsungTVDevice(MediaPlayerDevice):
    """Representation of a Samsung TV."""
    def __init__(self, host, port, name, timeout, mac, uuid, update_method,
                 update_custom_ping_url, source_list, app_list):
        """Initialize the Samsung device."""

        # Save a reference to the imported classes
        self._host = host
        self._name = name
        self._timeout = timeout
        self._mac = mac
        self._update_method = update_method
        self._update_custom_ping_url = update_custom_ping_url
        self._source_list = json.loads(source_list)
        self._app_list = json.loads(app_list) if app_list is not None else None
        self._uuid = uuid
        self._is_ws_connection = True if port in (8001, 8002) else False
        # Assume that the TV is not muted and volume is 0
        self._muted = False
        # Assume that the TV is in Play mode
        self._playing = True
        self._state = None
        # Mark the end of a shutdown command (need to wait 15 seconds before
        # sending the next command to avoid turning the TV back ON).
        self._end_of_power_off = None
        self._token_file = None

        # Generate token file only for WS + SSL + Token connection
        if port == 8002:
            self._gen_token_file()

        self._remote = SamsungTVWS(name=name,
                                   host=host,
                                   port=port,
                                   timeout=self._timeout,
                                   key_press_delay=KEY_PRESS_TIMEOUT,
                                   token_file=self._token_file)

    def _gen_token_file(self):
        self._token_file = os.path.dirname(
            os.path.realpath(__file__)) + '/token-' + self._host + '.txt'

        if os.path.isfile(self._token_file) is False:
            # For correct auth
            self._timeout = 45

            # Create token file for catch possible errors
            try:
                handle = open(self._token_file, "w+")
                handle.close()
            except:
                _LOGGER.error("Samsung TV - Error creating token file: %s",
                              self._token_file)

    def _power_off_in_progress(self):
        return (self._end_of_power_off is not None
                and self._end_of_power_off > dt_util.utcnow())

    def _ping_device(self):
        # HTTP ping
        if self._is_ws_connection and self._update_method == "ping":
            try:
                ping_url = "http://{}:8001/api/v2/".format(self._host)
                if self._update_custom_ping_url is not None:
                    ping_url = self._update_custom_ping_url

                requests.get(ping_url, timeout=UPDATE_PING_TIMEOUT)
                self._state = STATE_ON
            except:
                self._state = STATE_OFF

        # WS ping
        else:
            self.send_command("KEY", 1, 0)

    def _gen_installed_app_list(self):
        if self._state == STATE_OFF:
            _LOGGER.info(
                "Samsung TV is OFF, _gen_installed_app_list not executed")
            self._app_list = {}

        app_list = self._remote.app_list()

        # app_list is a list of dict
        clean_app_list = {}
        for i in range(len(app_list)):
            try:
                app = app_list[i]
                clean_app_list[app.get('name')] = app.get('appId')
            except Exception:
                pass

        self._app_list = clean_app_list
        _LOGGER.debug("Gen installed app_list %s", clean_app_list)

    @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
    def update(self):
        """Update state of device."""
        self._ping_device()

    def send_command(self, payload, command_type="send_key", retry_count=1):
        """Send a key to the tv and handles exceptions."""
        if self._power_off_in_progress() and payload not in ("KEY_POWER",
                                                             "KEY_POWEROFF"):
            _LOGGER.info("TV is powering off, not sending command: %s",
                         payload)
            return False

        try:
            # recreate connection if connection was dead
            for _ in range(retry_count + 1):
                try:
                    if command_type == "run_app":
                        #run_app(self, app_id, app_type='DEEP_LINK', meta_tag='')
                        self._remote.run_app(payload)
                    else:
                        times = 1

                        # fix KEY_HDMI ws error
                        if payload == "KEY_HDMI_FIX":
                            payload = "KEY_HDMI"
                            times = 2

                        self._remote.send_key(payload, times, 1)

                    break
                except (ConnectionResetError, AttributeError, BrokenPipeError):
                    self._remote.close()
                    _LOGGER.debug(
                        "Error in send_command() -> ConnectionResetError/AttributeError/BrokenPipeError"
                    )

            self._state = STATE_ON
        except websocket._exceptions.WebSocketTimeoutException:
            # We got a response so it's on.
            self._state = STATE_ON
            self._remote.close()
            _LOGGER.debug("Failed sending payload %s command_type %s",
                          payload,
                          command_type,
                          exc_info=True)

        except OSError:
            self._state = STATE_OFF
            self._remote.close()
            _LOGGER.debug("Error in send_command() -> OSError")

        if self._power_off_in_progress():
            self._state = STATE_OFF

        return True

    @property
    def unique_id(self) -> str:
        """Return the unique ID of the device."""
        return self._uuid

    @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 is_volume_muted(self):
        """Boolean if volume is currently muted."""
        return self._muted

    @property
    def source_list(self):
        """List of available input sources."""
        if self._app_list is None:
            self._gen_installed_app_list()

        source_list = []
        source_list.extend(list(self._source_list))
        source_list.extend(list(self._app_list))

        return source_list

    @property
    def supported_features(self):
        """Flag media player features that are supported."""
        return SUPPORT_SAMSUNGTV | SUPPORT_TURN_ON

    def turn_on(self):
        """Turn the media player on."""
        if self._mac:
            if self._power_off_in_progress():
                _LOGGER.info("TV is powering off, not sending WOL")
                return

            wakeonlan.send_magic_packet(self._mac)
            time.sleep(2)
            self._ping_device()
        else:
            self.send_command("KEY_POWERON")

    def turn_off(self):
        """Turn off media player."""
        # In my tests if _end_of_power_off < 15 WS ping method randomly fail!!!
        self._end_of_power_off = dt_util.utcnow() + timedelta(seconds=15)

        if self._is_ws_connection:
            self.send_command("KEY_POWER")
        else:
            self.send_command("KEY_POWEROFF")

        # Force closing of remote session to provide instant UI feedback
        try:
            self._remote.close()
        except OSError:
            _LOGGER.debug("Could not establish connection.")

    def volume_up(self):
        """Volume up the media player."""
        self.send_command("KEY_VOLUP")

    def volume_down(self):
        """Volume down media player."""
        self.send_command("KEY_VOLDOWN")

    def mute_volume(self, mute):
        """Send mute command."""
        self.send_command("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):
        """Send play command."""
        self._playing = True
        self.send_command("KEY_PLAY")

    def media_pause(self):
        """Send media pause command to media player."""
        self._playing = False
        self.send_command("KEY_PAUSE")

    def media_next_track(self):
        """Send next track command."""
        self.send_command("KEY_FF")

    def media_previous_track(self):
        """Send the previous track command."""
        self.send_command("KEY_REWIND")

    async def async_play_media(self, media_type, media_id, **kwargs):
        """Support changing a channel."""

        # Type channel
        if media_type == MEDIA_TYPE_CHANNEL:
            try:
                cv.positive_int(media_id.replace("-", "", 1))
                # Hyphen must be between numbers
                if media_id.startswith("-") or media_id.endswith("-"):
                    raise vol.Invalid("")
            except vol.Invalid:
                _LOGGER.error(
                    "Media ID must be channel with optional sub-channel, separated by a hyphen (ex. 20-2)"
                )
                return

            for digit in media_id:
                await self.hass.async_add_job(
                    self.send_command,
                    "KEY_PLUS100" if digit == "-" else "KEY_" + digit)

            await self.hass.async_add_job(self.send_command, "KEY_ENTER")

        # Launch an app
        elif media_type == MEDIA_TYPE_APP:
            await self.hass.async_add_job(self.send_command, media_id,
                                          "run_app")

        # Send custom key
        elif media_type == MEDIA_TYPE_KEY:
            try:
                cv.string(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be a string (ex: "KEY_HOME"')
                return

            await self.hass.async_add_job(self.send_command, media_id)

        else:
            _LOGGER.error("Unsupported media type")
            return

    async def async_select_source(self, source):
        """Select input source."""
        if source in self._source_list:
            await self.hass.async_add_job(self.send_command,
                                          self._source_list[source])
        elif source in self._app_list:
            await self.hass.async_add_job(self.send_command,
                                          self._app_list[source], "run_app")
        else:
            _LOGGER.error("Unsupported source")
Beispiel #13
0
#
import os
import wakeonlan
from time import sleep
from random import randrange

TV_IP_ADDR = '192.168.0.38'
TV_MAC_ADDR = '00:7C:2D:06:89:40'
NETFLIX_APP_ID = '11101200001'

from samsungtvws import SamsungTVWS, SamsungTVShortcuts

token_file = os.path.dirname(os.path.realpath(__file__)) + '/token.txt'
tv = SamsungTVWS(host=TV_IP_ADDR, port=8002, token_file=token_file)
remote_ctrl: SamsungTVShortcuts = tv.shortcuts()

# Wake up TV Set
wakeonlan.send_magic_packet(TV_MAC_ADDR)

# check is Netfix app running and close it
tv.rest_app_close(NETFLIX_APP_ID)
while True:
    app = tv.rest_app_status(NETFLIX_APP_ID)
    if not app.get("running"):
        break
    sleep(1)

# run netflix
tv.run_app(NETFLIX_APP_ID)
sleep(10)
Beispiel #14
0
class SamsungTVDevice(MediaPlayerEntity):
    """Representation of a Samsung TV."""
    def __init__(self, host, port, name, timeout, mac, uuid, update_method,
                 update_custom_ping_url, source_list, app_list, api_key,
                 device_id, expand_sources):
        """Initialize the Samsung device."""

        # Save a reference to the imported classes
        self._host = host
        self._name = name
        self._timeout = timeout
        self._mac = mac
        self._update_method = update_method
        self._update_custom_ping_url = update_custom_ping_url
        self._source = None
        self._source_list = json.loads(source_list)
        self._app_list = json.loads(app_list) if app_list is not None else None
        self._uuid = uuid
        self._is_ws_connection = True if port in (8001, 8002) else False
        # Assume that the TV is not muted and volume is 0
        self._muted = False
        self._volume = 0
        # Assume that the TV is in Play mode
        self._playing = True
        self._state = None
        # Mark the end of a shutdown command (need to wait 15 seconds before
        # sending the next command to avoid turning the TV back ON).
        self._end_of_power_off = None
        self._token_file = None

        # Credentials for smartthings integration
        self._api_key = api_key
        self._device_id = device_id
        self._expand_sources = expand_sources

        # Generate token file only for WS + SSL + Token connection
        if port == 8002:
            self._gen_token_file()

        self._remote = SamsungTVWS(name=name,
                                   host=host,
                                   port=port,
                                   timeout=self._timeout,
                                   key_press_delay=KEY_PRESS_TIMEOUT,
                                   token_file=self._token_file)

        self._upnp = SamsungTVUpnp(host=host, app_list=self._app_list)

        # Smartthing intégration
        if self._api_key is not None:
            self._smarttv = SmartthingsTV(api_key=api_key, device_id=device_id)
        else:
            self._smarttv = None

    def _gen_token_file(self):
        self._token_file = os.path.dirname(
            os.path.realpath(__file__)) + '/token-' + self._host + '.txt'

        if os.path.isfile(self._token_file) is False:
            # For correct auth
            self.timeout = 30

            # Create token file for catch possible errors
            try:
                handle = open(self._token_file, "w+")
                handle.close()
            except:
                _LOGGER.error("Samsung TV - Error creating token file: %s",
                              self._token_file)

    def _power_off_in_progress(self):
        return (self._end_of_power_off is not None
                and self._end_of_power_off > dt_util.utcnow())

    def _ping_device(self):
        # HTTP ping
        if self._is_ws_connection and self._update_method == "ping":
            try:
                ping_url = "http://{}:8001/api/v2/".format(self._host)
                if self._update_custom_ping_url is not None:
                    ping_url = self._update_custom_ping_url

                requests.get(ping_url, timeout=UPDATE_PING_TIMEOUT)
                self._state = STATE_ON
            except:
                self._state = STATE_OFF
        elif self._update_method == "dmr":
            try:
                r = requests.get("http://{}:9197/dmr".format(
                    self._config['host']),
                                 timeout=0.2)
                self._state = STATE_ON
            except:
                self._state = STATE_OFF
        # Smartthings ping
        elif self._update_method == "smartthings":
            self._smarttv.device_update()
            if self._smarttv._state == "on":
                self._state = STATE_ON
            else:
                self._state = STATE_OFF

        # WS ping
        else:
            self.send_command("KEY")

    def _gen_installed_app_list(self):
        app_list = self._remote.app_list()

        # app_list is a list of dict
        clean_app_list = {}
        for i in range(len(app_list)):
            try:
                app = app_list[i]
                clean_app_list[app.get('name')] = app.get('appId')
            except Exception:
                pass

        self._app_list = clean_app_list
        _LOGGER.debug("Gen installed app_list %s", clean_app_list)

    @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
    async def async_update(self):
        """Update state of device."""
        await self.hass.async_add_job(self._ping_device)

        self._muted = await self.hass.async_add_job(self._upnp.get_mute)

        volume = await self.hass.async_add_job(self._upnp.get_volume)
        self._volume = int(volume) / 100

        if self._state != STATE_OFF and self._smarttv is not None:
            running_app = await self.hass.async_add_job(
                self._upnp.get_running_app)
            if running_app != None:
                self._source = running_app
            else:
                if self._expand_sources:
                    if len(self._smarttv._source) > 0:
                        self._source = self._smarttv._source_list[
                            self._smarttv._source_list.index(
                                self._smarttv._source) + 1]
                    else:
                        self._source = self._smarttv._source
                else:
                    self._source = self._smarttv._source
        else:
            self._source = None

    def send_command(self, payload, command_type="send_key", retry_count=1):
        """Send a key to the tv and handles exceptions."""
        if self._power_off_in_progress() and payload not in ("KEY_POWER",
                                                             "KEY_POWEROFF"):
            _LOGGER.info("TV is powering off, not sending command: %s",
                         payload)
            return False

        try:
            # recreate connection if connection was dead
            for _ in range(retry_count + 1):
                try:
                    if command_type == "run_app":
                        #run_app(self, app_id, app_type='DEEP_LINK', meta_tag='')
                        self._remote.run_app(payload)
                    else:
                        self._remote.send_key(payload)

                    break
                except (ConnectionResetError, AttributeError, BrokenPipeError):
                    self._remote.close()
                    _LOGGER.debug(
                        "Error in send_command() -> ConnectionResetError/AttributeError/BrokenPipeError"
                    )

            self._state = STATE_ON
        except websocket._exceptions.WebSocketTimeoutException:
            # We got a response so it's on.
            self._state = STATE_ON
            self._remote.close()
            _LOGGER.debug("Failed sending payload %s command_type %s",
                          payload,
                          command_type,
                          exc_info=True)

        except OSError:
            self._state = STATE_OFF
            self._remote.close()
            _LOGGER.debug("Error in send_command() -> OSError")

        if self._power_off_in_progress():
            self._state = STATE_OFF

        return True

    @property
    def unique_id(self) -> str:
        """Return the unique ID of the device."""
        return self._uuid

    @property
    def name(self):
        """Return the name of the device."""
        return self._name

    @property
    def media_title(self):
        """Title of current playing media."""
        self._media_title = self._source
        return self._media_title

    @property
    def state(self):
        """Return the state of the device."""
        return self._state

    @property
    def is_volume_muted(self):
        """Boolean if volume is currently muted."""
        return self._muted

    @property
    def source_list(self):
        """List of available input sources."""
        if self._app_list is None:
            self._gen_installed_app_list()

        if self._api_key is not None:
            if self._expand_sources:
                self._source_list = [
                    self._smarttv._source_list[i]
                    for i in range(len(self._smarttv._source_list)) if i % 2
                ]
            else:
                self._source_list = self._smarttvtv._source_list

        source_list = []
        source_list.extend(list(self._source_list))
        source_list.extend(list(self._app_list))

        return source_list

    @property
    def volume_level(self):
        """Volume level of the media player (0..1)."""
        return self._volume

    @property
    def source(self):
        """Return the current input source."""
        return self._source

    @property
    def sound_mode(self):
        """Current sound mode."""
        if self._api_key is None:
            self._sound_mode = None
        else:
            self._sound_mode = self._smarttv._sound_mode
        return self._sound_mode

    @property
    def sound_mode_list(self):
        """Available sound modes."""
        if self._api_key is None:
            self._sound_mode_list = None
        else:
            self._sound_mode_list = self._smarttv._supported_sound_modes
        return self._sound_mode_list

    @property
    def supported_features(self):
        """Flag media player features that are supported."""
        return SUPPORT_SAMSUNGTV | SUPPORT_TURN_ON

    @property
    def device_class(self):
        """Set the device class to TV."""
        return DEVICE_CLASS_TV

    def turn_on(self):
        """Turn the media player on."""
        if self._mac:
            if self._power_off_in_progress():
                _LOGGER.info("TV is powering off, not sending WOL")
                return

            wakeonlan.send_magic_packet(self._mac)
            time.sleep(2)
            self._ping_device()
        else:
            self.send_command("KEY_POWERON")

    def turn_off(self):
        """Turn off media player."""
        # In my tests if _end_of_power_off < 15 WS ping method randomly fail!!!
        self._end_of_power_off = dt_util.utcnow() + timedelta(seconds=15)

        if self._is_ws_connection:
            self.send_command("KEY_POWER")
        else:
            self.send_command("KEY_POWEROFF")

        # Force closing of remote session to provide instant UI feedback
        try:
            self._remote.close()
        except OSError:
            _LOGGER.debug("Could not establish connection.")

    def volume_up(self):
        """Volume up the media player."""
        self.send_command("KEY_VOLUP")

    def volume_down(self):
        """Volume down media player."""
        self.send_command("KEY_VOLDOWN")

    def mute_volume(self, mute):
        """Send mute command."""
        self.send_command("KEY_MUTE")

    async def set_volume_level(self, volume):
        """Set volume level, range 0..1."""
        await self.hass.async_add_job(self._upnp.set_volume, int(volume * 100))

    def media_play_pause(self):
        """Simulate play pause media player."""
        if self._playing:
            self.media_pause()
        else:
            self.media_play()

    def media_play(self):
        """Send play command."""
        self._playing = True
        self.send_command("KEY_PLAY")

    def media_pause(self):
        """Send media pause command to media player."""
        self._playing = False
        self.send_command("KEY_PAUSE")

    def media_next_track(self):
        """Send next track command."""
        self.send_command("KEY_FF")

    def media_previous_track(self):
        """Send the previous track command."""
        self.send_command("KEY_REWIND")

    async def async_play_media(self, media_type, media_id, **kwargs):
        """Support changing a channel."""

        # Type channel
        if media_type == MEDIA_TYPE_CHANNEL:
            try:
                cv.positive_int(media_id)
            except vol.Invalid:
                _LOGGER.error("Media ID must be positive integer")
                return

            for digit in media_id:
                await self.hass.async_add_job(self.send_command,
                                              "KEY_" + digit)

            await self.hass.async_add_job(self.send_command, "KEY_ENTER")

        # Launch an app
        elif media_type == MEDIA_TYPE_APP:
            await self.hass.async_add_job(self.send_command, media_id,
                                          "run_app")

        # Send custom key
        elif media_type == MEDIA_TYPE_KEY:
            try:
                cv.string(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be a string (ex: "KEY_HOME"')
                return

            await self.hass.async_add_job(self.send_command, media_id)

        # Play media
        elif media_type == MEDIA_TYPE_URL:
            try:
                cv.url(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be an url (ex: "http://"')
                return

            await self.hass.async_add_job(self._upnp.set_current_media,
                                          media_id)
            self._playing = True

        # Trying to make stream component work on TV
        elif media_type == "application/vnd.apple.mpegurl":
            await self.hass.async_add_job(self._upnp.set_current_media,
                                          media_id)
            self._playing = True

        else:
            _LOGGER.error("Unsupported media type: " + media_type)
            return

    async def async_select_source(self, source):
        """Select input source."""
        if source in self._source_list:
            source_key = self._source_list[source]
            if "+" in source_key:
                all_source_keys = source_key.split("+")
                for this_key in all_source_keys:
                    if this_key.isdigit():
                        time.sleep(int(this_key) / 1000)
                    else:
                        await self.hass.async_add_job(self.send_command,
                                                      this_key)
            else:
                await self.hass.async_add_job(self.send_command,
                                              self._source_list[source])
        elif source in self._app_list:
            source_key = self._app_list[source]
            await self.hass.async_add_job(self.send_command, source_key,
                                          "run_app")
        else:
            _LOGGER.error("Unsupported source")

    async def async_select_source(self, source):
        """Select input source."""
        if source in self._source_list:
            if self._api_key is None:
                source_key = self._source_list[source]
                if "+" in source_key:
                    all_source_keys = source_key.split("+")
                    for this_key in all_source_keys:
                        if this_key.isdigit():
                            time.sleep(int(this_key) / 1000)
                        else:
                            await self.hass.async_add_job(
                                self.send_command, this_key)
                else:
                    await self.hass.async_add_job(self.send_command,
                                                  self._source_list[source])
            else:
                # Use smartthings to change source
                await self.hass.async_add_job(self._smarttv.select_source,
                                              source)
        elif source in self._app_list:
            source_key = self._app_list[source]
            await self.hass.async_add_job(self.send_command, source_key,
                                          "run_app")
        else:
            _LOGGER.error("Unsupported source")
class SamsungTVWSBridge(SamsungTVBridge):
    """The Bridge for WebSocket TVs."""
    def __init__(self, config):
        """Initialize Bridge."""
        super().__init__(config)
        self.config = config

    def try_connect(self, port):
        """Try to connect to the Websocket TV."""
        for self.port in (8001, 8002):
            if port is not None and port != self.port:
                continue
            config = {
                "name": "HomeAssistant",
                "description": "HomeAssistant",
                "host": self.host,
                "method": self.method,
                "port": self.port,
                # We need this high timeout because waiting for auth popup is just an open socket
                "timeout": 31,
                "token": self.token,
            }
            try:
                LOGGER.debug("Try config: %s", config)
                with SamsungTVWS(
                        host=self.host,
                        port=self.port,
                        token=self.token,
                        timeout=config["timeout"],
                        name=config["name"],
                ) as remote:
                    remote.open()
                LOGGER.debug("Working config: %s", config)
                LOGGER.debug("Token: %s", self.token)
                return RESULT_SUCCESS
            except WebSocketException:
                LOGGER.debug("Working but unsupported config: %s", config)
                return RESULT_NOT_SUPPORTED
            except (OSError, ConnectionFailure) as err:
                LOGGER.debug("Failing config: %s, error: %s", config, err)

        return RESULT_NOT_SUCCESSFUL

    def is_on(self):
        """Get TV state."""
        try:
            ping_url = f"http://{self.host}:8001/api/v2/"

            requests.get(ping_url, timeout=1)
            return True
        except RequestException:
            return False

    def _send_key(self, key):
        """Send the key using websocket protocol."""
        if key == "KEY_POWEROFF":
            key = "KEY_POWER"
        self._get_remote().send_key(key)

    def _get_remote(self):
        """Create or return a remote control instance."""
        if self._remote is None:
            # We need to create a new instance to reconnect.
            LOGGER.debug("Create SamsungTVWS")
            self._remote = SamsungTVWS(
                host=self.config["host"],
                port=self.config["port"],
                token=self.config["token"],
                timeout=self.config["timeout"],
                name=self.config["name"],
            )
            self._remote.open()
        return self._remote
Beispiel #16
0
import sys
import os
import wakeonlan

sys.path.append('../')

from samsungtvws import SamsungTVWS

# Normal constructor
tv = SamsungTVWS('192.168.xxx.xxx')

# Autosave token to file 
token_file = os.path.dirname(os.path.realpath(__file__)) + '/tv-token.txt'
tv = SamsungTVWS(host='192.168.xxx.xxx', port=8002, token_file=token_file)

# Toggle power
tv.shortcuts().power()

# Power On
wakeonlan.send_magic_packet('CC:6E:A4:xx:xx:xx')

# Open web in browser
tv.open_browser('https://duckduckgo.com/')

# View installed apps (Spotify)
tv.app_list()

# Open apps (Spotify)
tv.run_app('3201606009684')
Beispiel #17
0
SAT = "sat"
CD = "cd"
FM = "fm"
AM = "am"
NET = "net"
PC = "game"
USB = "usb"
BT = "bluetooth"

cube_on = False
second_slide = False
pause_on = False
last_volume = 80
sys.path.append('../')
token_file = os.path.dirname(os.path.realpath(__file__)) + '/tv-token'
tvr = SamsungTVWS(host=TVHOST, port=TVPORT, token_file=token_file)
vsxr = eiscp.eISCP(VSXHOST)
dr = 'http://' + DHOST + ':8080/control/rcu'


def bd_send(command):
    bdr = telnetlib.Telnet(BDHOST, BDPORT)
    bdr.write(command)
    bdr.close()


def bd_channel_up():
    bd_send(NEXT)


def bd_channel_down():
Beispiel #18
0
class SamsungTVWSBridge(SamsungTVBridge):
    """The Bridge for WebSocket TVs."""
    def __init__(self, method, host, port, token=None):
        """Initialize Bridge."""
        super().__init__(method, host, port)
        self.token = token

    def mac_from_device(self):
        """Try to fetch the mac address of the TV."""
        info = self.device_info()
        return mac_from_device_info(info) if info else None

    def try_connect(self):
        """Try to connect to the Websocket TV."""
        for self.port in WEBSOCKET_PORTS:
            config = {
                CONF_NAME: VALUE_CONF_NAME,
                CONF_HOST: self.host,
                CONF_METHOD: self.method,
                CONF_PORT: self.port,
                # We need this high timeout because waiting for auth popup is just an open socket
                CONF_TIMEOUT: TIMEOUT_REQUEST,
            }

            result = None
            try:
                LOGGER.debug("Try config: %s", config)
                with SamsungTVWS(
                        host=self.host,
                        port=self.port,
                        token=self.token,
                        timeout=config[CONF_TIMEOUT],
                        name=config[CONF_NAME],
                ) as remote:
                    remote.open()
                    self.token = remote.token
                    if self.token:
                        config[CONF_TOKEN] = "*****"
                LOGGER.debug("Working config: %s", config)
                return RESULT_SUCCESS
            except WebSocketException:
                LOGGER.debug("Working but unsupported config: %s", config)
                result = RESULT_NOT_SUPPORTED
            except (OSError, ConnectionFailure) as err:
                LOGGER.debug("Failing config: %s, error: %s", config, err)
        # pylint: disable=useless-else-on-loop
        else:
            if result:
                return result

        return RESULT_CANNOT_CONNECT

    def device_info(self):
        """Try to gather infos of this TV."""
        remote = self._get_remote(avoid_open=True)
        if not remote:
            return None
        with contextlib.suppress(HttpApiError):
            return remote.rest_device_info()

    def _send_key(self, key):
        """Send the key using websocket protocol."""
        if key == "KEY_POWEROFF":
            key = "KEY_POWER"
        self._get_remote().send_key(key)

    def _get_remote(self, avoid_open: bool = False):
        """Create or return a remote control instance."""
        if self._remote is None:
            # We need to create a new instance to reconnect.
            try:
                LOGGER.debug("Create SamsungTVWSBridge for %s (%s)", CONF_NAME,
                             self.host)
                self._remote = SamsungTVWS(
                    host=self.host,
                    port=self.port,
                    token=self.token,
                    timeout=TIMEOUT_WEBSOCKET,
                    name=VALUE_CONF_NAME,
                )
                if not avoid_open:
                    self._remote.open()
            # This is only happening when the auth was switched to DENY
            # A removed auth will lead to socket timeout because waiting for auth popup is just an open socket
            except ConnectionFailure:
                self._notify_callback()
            except (WebSocketException, OSError):
                self._remote = None
        return self._remote

    def stop(self):
        """Stop Bridge."""
        LOGGER.debug("Stopping SamsungTVWSBridge")
        self.close_remote()
Beispiel #19
0
import sys
import os
import logging
import wakeonlan

sys.path.append('../')

from samsungtvws import SamsungTVWS

# Increase debug level
logging.basicConfig(level=logging.INFO)

# Normal constructor
tv = SamsungTVWS('192.168.xxx.xxx')

# Autosave token to file
token_file = os.path.dirname(os.path.realpath(__file__)) + '/tv-token.txt'
tv = SamsungTVWS(host='192.168.xxx.xxx', port=8002, token_file=token_file)

# Toggle power
tv.shortcuts().power()

# Power On
wakeonlan.send_magic_packet('CC:6E:A4:xx:xx:xx')

# Open web in browser
tv.open_browser('https://duckduckgo.com/')

# View installed apps
apps = tv.app_list()
logging.info(apps)
Beispiel #20
0
import logging
import os
import sys

import wakeonlan

sys.path.append("../")

from samsungtvws import SamsungTVWS  # noqa: E402

# Increase debug level
logging.basicConfig(level=logging.INFO)

# Normal constructor
tv = SamsungTVWS("192.168.xxx.xxx")

# Autosave token to file
token_file = os.path.dirname(os.path.realpath(__file__)) + "/tv-token.txt"
tv = SamsungTVWS(host="192.168.xxx.xxx", port=8002, token_file=token_file)

# Toggle power
tv.shortcuts().power()

# Power On
wakeonlan.send_magic_packet("CC:6E:A4:xx:xx:xx")

# Open web in browser
tv.open_browser("https://duckduckgo.com/")

# View installed apps
apps = tv.app_list()
Beispiel #21
0
class SamsungTVWSBridge(SamsungTVBridge):
    """The Bridge for WebSocket TVs."""

    def __init__(self, method, host, port, token=None):
        """Initialize Bridge."""
        super().__init__(method, host, port, token)

    def try_connect(self):
        """Try to connect to the Websocket TV."""
        for self.port in (8002, 8001):
            config = {
                CONF_NAME: VALUE_CONF_NAME,
                CONF_HOST: self.host,
                CONF_METHOD: self.method,
                CONF_PORT: self.port,
                # We need this high timeout because waiting for auth popup is just an open socket
                CONF_TIMEOUT: 31,
                CONF_TOKEN: self.token,
            }
            try:
                LOGGER.debug("Try config: %s", _hide_token(config))
                with SamsungTVWS(
                    host=self.host,
                    port=self.port,
                    token=self.token,
                    timeout=config[CONF_TIMEOUT],
                    name=config[CONF_NAME],
                ) as remote:
                    remote.open()
                    self.token = remote.token
                LOGGER.debug("Working config: %s", _hide_token(config))
                return RESULT_SUCCESS
            except (WebSocketException, ConnectionFailure):
                LOGGER.debug("Working but unsupported config: %s", _hide_token(config))
                return RESULT_NOT_SUPPORTED
            except OSError as err:
                LOGGER.debug("Failing config: %s, error: %s", _hide_token(config), err)

        return RESULT_NOT_SUCCESSFUL

    def _send_key(self, key):
        """Send the key using websocket protocol."""
        if key == "KEY_POWEROFF":
            key = "KEY_POWER"
        self._get_remote().send_key(key)

    def _get_remote(self):
        """Create or return a remote control instance."""
        if self._remote is None:
            # We need to create a new instance to reconnect.
            try:
                LOGGER.debug("Create SamsungTVWS")
                self._remote = SamsungTVWS(
                    host=self.config[CONF_HOST],
                    port=self.config[CONF_PORT],
                    token=self.config[CONF_TOKEN],
                    timeout=self.config[CONF_TIMEOUT],
                    name=self.config[CONF_NAME],
                )
                self._remote.open()
            # This is only happening when the auth was switched to DENY
            # A removed auth will lead to socket timeout because waiting for auth popup is just an open socket
            except ConnectionFailure:
                self._notify_callback()
                raise
        return self._remote