{"action":"created","issue":{"url":"https://api.github.com/repos/home-assistant/home-assistant/issues/20795","repository_url":"https://api.github.com/repos/home-assistant/home-assistant","labels_url":"https://api.github.com/repos/home-assistant/home-assistant/issues/20795/labels{/name}","comments_url":"https://api.github.com/repos/home-assistant/home-assistant/issues/20795/comments","events_url":"https://api.github.com/repos/home-assistant/home-assistant/issues/20795/events","html_url":"https://github.com/home-assistant/home-assistant/issues/20795","id":407406722,"node_id":"MDU6SXNzdWU0MDc0MDY3MjI=","number":20795,"title":"Samsung TV's not working [Testing fix]","user":{"login":"kdschlosser","id":10932297,"node_id":"MDQ6VXNlcjEwOTMyMjk3","avatar_url":"https://avatars0.githubusercontent.com/u/10932297?v=4","gravatar_id":"","url":"https://api.github.com/users/kdschlosser","html_url":"https://github.com/kdschlosser","followers_url":"https://api.github.com/users/kdschlosser/followers","following_url":"https://api.github.com/users/kdschlosser/following{/other_user}","gists_url":"https://api.github.com/users/kdschlosser/gists{/gist_id}","starred_url":"https://api.github.com/users/kdschlosser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kdschlosser/subscriptions","organizations_url":"https://api.github.com/users/kdschlosser/orgs","repos_url":"https://api.github.com/users/kdschlosser/repos","events_url":"https://api.github.com/users/kdschlosser/events{/privacy}","received_events_url":"https://api.github.com/users/kdschlosser/received_events","type":"User","site_admin":false},"labels":[{"id":543347121,"node_id":"MDU6TGFiZWw1NDMzNDcxMjE=","url":"https://api.github.com/repos/home-assistant/home-assistant/labels/platform:%20media_player.samsungtv","name":"platform: media_player.samsungtv","color":"ededed","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":37,"created_at":"2019-02-06T20:02:27Z","updated_at":"2019-02-08T02:15:50Z","closed_at":null,"author_association":"NONE","body":"I am moving the conversation that is taking place in #17802 as the current conversation really has nothing to do with the issue that was reported.\r\n\r\nIf this is not OK then delete this issue. I am the person that is currently updating the code to samsungctl. It has undergone a lot of additions which I do believe the users of Home Assistant will enjoy being able to use. \r\n\r\nthee library also now supports ALL Samsung TV's that have the capability of being controlled over a network or WiFi Connection from 2008 to present. This includes the encrypted websocket connections (H (2014) and J (2015) TV's) as well as the SSL websocket connection (latest firmware release).\r\n\r\nI have simplified the connection process so that the only things that are needed in thee Home Assistant config file is the host (ip) of the TV. nothing else is needed. the samsungctl library now handles hammering all of the connection specific details. I also added to it a a class that handles loading and saving of config data. I did this to handle the dynamically changing tokens that are used with the encrypted websockets and SSL websockets. the library now also does not stop running if the connection get severed.. (TV gets powered off). I also changed the handling of the power keys to make them function like they should. KEY_POWER = power toggle, KEY_POWERON = power on and KEY_POWEROFF = power off. \r\n\r\nI am currently testing the replacement for /homeassistant/components/media_player/samsungtv.py, because I am not familiar with Home Assistant it is better to hammer these problems out in an issue where they can be tested then to open a PR for non functioning code and end up with a very messy commit history. \r\n\r\n\r\nPLEASE READ\r\n\r\n\r\nThis is a replacement for the /homeassistant/components/media_player/samsungtv.py file\r\nplease do not edit the code and then report an issue. only report problems relating to this code. also do not provide vague errors. like it says config error.. I am willing to bet the error stats more then \"config error\". I need a copy of the error message in it's entirety. not little bits and pieces. because of the nature of Home Assistant you may need to scroll back in the logs and read for a bit to make sure that you have grabbed all of the relevant log messages.\r\n\r\n If you state anything about \"custom_components\" or if i see that in any of the errors I am not going to answer you. this is because you have modified the code. and it is a pretty good chance that your modification is what is causing the problem.\r\n\r\nIf you do not do the above then I have no way of helping to solve any issues.\r\n\r\nDO THIS FIRST\r\n\r\nwe are going to want to clean out any installed versions of the current samsungctl library. you can do this from a command prompt (shell)\r\n```\r\npip uninstall samsungctl\r\n```\r\n\r\nI have updated some of the code in samsungctl and the new code needs to be downloaded. and installed. I really would prefer if you also installed it from the command line using\r\n\r\n```\r\npip install --no-cache-dir \"https://github.com/kdschlosser/samsungctl/archive/develop.zip#samsungctl==0.8.0b\"\r\n```\r\n\r\nthis will make sure it gets installed without using a cached copy of it that may be on your system\r\n\r\n\r\n\r\nthis is going to write the config information to a directory called samsung_tv that is located in the Home Assistant config folder. the config files for the TVs are going to be named using the IP address you supply in the Home Assistant config yaml file.\r\n\r\nHOME ASSISTANT CONFIG FILE\r\nYou will have to have at a minimum the code below added to your Home Assistant config file\r\n\r\n```yaml\r\n- platform: samsungtv\r\n    host: 192.168.2.252\r\n```\r\n\r\nThe next 2 are options \r\n\r\nif you want to specify a name for the TV\r\n\r\n```yaml\r\n- platform: samsungtv\r\n    host: 192.168.2.252\r\n    name: Living Room TV\r\n    description: description of TV\r\n```\r\n\r\noptionally a description (used for legacy TV's)\r\n\r\n```yaml\r\n- platform: samsungtv\r\n    host: 192.168.2.252\r\n    name: Living Room TV\r\n    description: description of TV\r\n```\r\n\r\nONLY IF THERE ARE PROBLEMS\r\nyou will ONLY WANT TO USE THIS if there is a detection problem for the connection type\r\n\r\n```yaml\r\n- platform: samsungtv\r\n    host: 192.168.2.252\r\n    name: Living Room TV\r\n    method: websocket\r\n```\r\n\r\nif you have to specify the connection method this is how they break down.\r\n\r\nTV model number example: UN55D8000\r\n[U] = panel type\r\n[N] = region\r\n[55] = size\r\n[D] = model year\r\n[8000] = series or \"trim\"\r\n\r\nit is the model year we care about,\r\n\r\nH and J model years you need to enter `encrypted` for the method.\r\nK and above you need to enter `websocket`\r\nall others enter `legacy`\r\n\r\nYOU WILL ONLY DO THE ABOVE IF THERE IS AN ISSUE DETECTING THE TV CONNECTION.\r\n\r\n\r\n\r\nHere is the updated code.\r\n\r\nI WILL NOT MAKE CHANGES AND MAKE ANOTHER POST FOR IT. I WILL UPDATE THE CODE BELOW WITH ANY NEW CHANGES AND INFORM YOU THAT THE CODE HAS CHANGED\r\n\r\n\r\n```python\r\n\"\"\"\r\nSupport for interface with an Samsung TV.\r\n\r\nFor more details about this platform, please refer to the documentation at\r\nhttps://home-assistant.io/components/media_player.samsungtv/\r\n\"\"\"\r\nimport asyncio\r\nfrom datetime import timedelta\r\nimport logging\r\nimport socket\r\nimport threading\r\nimport os\r\n\r\nimport voluptuous as vol\r\n\r\nfrom homeassistant.components.media_player import (\r\n    MEDIA_TYPE_CHANNEL, PLATFORM_SCHEMA, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE,\r\n    SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_OFF,\r\n    SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP,\r\n    SUPPORT_VOLUME_SET, SUPPORT_SELECT_SOURCE, MediaPlayerDevice)\r\nfrom homeassistant.const import (\r\n    CONF_HOST,\r\n    CONF_NAME,\r\n    CONF_METHOD,\r\n    STATE_OFF,\r\n    STATE_ON\r\n)\r\nimport homeassistant.helpers.config_validation as cv\r\nfrom homeassistant.util import dt as dt_util\r\n\r\n\r\nREQUIREMENTS = [\r\n    'https://github.com/kdschlosser/'\r\n    'samsungctl/archive/develop.zip#samsungctl==0.8.1b'\r\n]\r\n\r\nSAMSUNG_CONFIG_PATH = 'samsung_tv'\r\n\r\n\r\n_LOGGER = logging.getLogger(__name__)\r\n\r\nDEFAULT_NAME = 'Samsung TV Remote'\r\nDEFAULT_DESCRIPTION = socket.gethostname()\r\n\r\nCONF_DESCRIPTION =  'description'\r\n\r\nKEY_PRESS_TIMEOUT = 1.2\r\nKNOWN_DEVICES_KEY = 'samsungtv_known_devices'\r\n\r\nSUPPORT_SAMSUNGTV = (\r\n    SUPPORT_PAUSE |\r\n    SUPPORT_VOLUME_STEP |\r\n    SUPPORT_VOLUME_MUTE |\r\n    SUPPORT_PREVIOUS_TRACK |\r\n    SUPPORT_NEXT_TRACK |\r\n    SUPPORT_TURN_OFF |\r\n    SUPPORT_PLAY |\r\n    SUPPORT_PLAY_MEDIA |\r\n    SUPPORT_VOLUME_SET\r\n)\r\n\r\nPLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({\r\n    vol.Required(CONF_HOST): cv.string,\r\n    vol.Optional(CONF_METHOD): cv.string,\r\n    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,\r\n    vol.Optional(CONF_DESCRIPTION, default=DEFAULT_DESCRIPTION): cv.string,\r\n})\r\n\r\ndef setup_platform(hass, config, add_entities, discovery_info=None):\r\n    \"\"\"Set up the Samsung TV platform.\"\"\"\r\n    known_devices = hass.data.get(KNOWN_DEVICES_KEY)\r\n    if known_devices is None:\r\n        known_devices = set()\r\n        hass.data[KNOWN_DEVICES_KEY] = known_devices\r\n\r\n    config_path = hass.config.path(SAMSUNG_CONFIG_PATH)\r\n\r\n    if not os.path.exists(config_path):\r\n        os.mkdir(config_path)\r\n\r\n    uuid = None\r\n    # Is this a manual configuration?\r\n    if config.get(CONF_HOST) is not None:\r\n        host = config.get(CONF_HOST)\r\n        name = config.get(CONF_NAME)\r\n        description = config.get(CONF_DESCRIPTION)\r\n        method = config.get(CONF_METHOD)\r\n\r\n    elif discovery_info is not None:\r\n        tv_name = discovery_info.get('name')\r\n        model = discovery_info.get('model_name')\r\n        host = discovery_info.get('host')\r\n        name = \"{} ({})\".format(tv_name, model)\r\n        description = DEFAULT_DESCRIPTION\r\n        method = None\r\n        udn = discovery_info.get('udn')\r\n        if udn and udn.startswith('uuid:'):\r\n            uuid = udn[len('uuid:'):]\r\n    else:\r\n        _LOGGER.warning(\"Cannot determine device\")\r\n        return\r\n\r\n    # Only add a device once, so discovered devices do not override manual\r\n    # config.\r\n    ip_addr = socket.gethostbyname(host)\r\n    if ip_addr not in known_devices:\r\n        known_devices.add(ip_addr)\r\n        add_entities(\r\n            [SamsungTVDevice(host, name, description, method, uuid, config_path)]\r\n        )\r\n        _LOGGER.info(\"Samsung TV %s added as '%s'\", host, config.get(CONF_NAME))\r\n    else:\r\n        _LOGGER.info(\"Ignoring duplicate Samsung TV %s\", host)\r\n\r\n\r\nclass SamsungTVDevice(MediaPlayerDevice):\r\n    \"\"\"Representation of a Samsung TV.\"\"\"\r\n\r\n    def __init__(self, host, name, description, method, uuid, config_path):\r\n        \"\"\"Initialize the Samsung device.\"\"\"\r\n        import samsungctl\r\n        from samsungctl import exceptions\r\n        from samsungctl import Remote\r\n\r\n        _LOGGER.info(\"samsungctl version: \" + samsungctl.__version__)\r\n        _LOGGER.info(\"samsungctl location: \" + os.path.dirname(samsungctl.__file__))\r\n\r\n        # Save a reference to the imported classes\r\n        self._exceptions_class = exceptions\r\n        self._remote_class = Remote\r\n\r\n        self._config = samsungctl.Config.load(config_path)(\r\n            host=host,\r\n            name=name,\r\n            description=description,\r\n            method=method,\r\n        )\r\n\r\n        self._name = self._config.name\r\n        self._mac = self._config.mac\r\n        self._uuid = uuid\r\n        # Assume that the TV is not muted\r\n        self._muted = False\r\n        # Assume that the TV is in Play mode\r\n        self._playing = True\r\n        self._state = None\r\n        self._remote = None\r\n        # Mark the end of a shutdown command (need to wait 15 seconds before\r\n        # sending the next command to avoid turning the TV back ON).\r\n        self._end_of_power_off = None\r\n\r\n        # Mark the end of the TV powering on.need to wait 20 seconds before\r\n        # sending any commands.\r\n        self._end_of_power_on = None\r\n        # Generate a configuration for the Samsung library\r\n\r\n    def update(self):\r\n        \"\"\"Update state of device.\"\"\"\r\n\r\n        if self._power_off_in_progress():\r\n            self._state = STATE_OFF\r\n\r\n        elif self._power_on_in_progress():\r\n            self._state = STATE_ON\r\n\r\n        else:\r\n            power = self.get_remote().power\r\n            if power is True:\r\n                self._state = STATE_ON\r\n            else:\r\n                self._state = STATE_OFF\r\n\r\n    def get_remote(self):\r\n        \"\"\"Create or return a remote control instance.\"\"\"\r\n        if self._remote is None:\r\n            # We need to create a new instance to reconnect.\r\n            self._remote = self._remote_class(self._config)\r\n            self._remote.open()\r\n            self._config.save()\r\n\r\n        return self._remote\r\n\r\n    def send_key(self, key):\r\n        \"\"\"Send a key to the tv and handles exceptions.\"\"\"\r\n        if self._power_off_in_progress():\r\n            _LOGGER.info(\"TV is powering off, not sending command: %s\", key)\r\n            return\r\n\r\n        elif self._power_on_in_progress():\r\n            _LOGGER.info(\"TV is powering on, not sending command: %s\", key)\r\n            return\r\n\r\n        if self._state == STATE_OFF and key not in ('KEY_POWER', 'KEY_POWERON'):\r\n            _LOGGER.info(\"TV is powered off, not sending command: %s\", key)\r\n            return\r\n\r\n        self.get_remote().control(key)\r\n\r\n    def _power_on_in_progress(self):\r\n        return (\r\n            self._end_of_power_on is not None and\r\n            self._end_of_power_on > dt_util.utcnow()\r\n        )\r\n\r\n    def _power_off_in_progress(self):\r\n        return (\r\n            self._end_of_power_off is not None and\r\n            self._end_of_power_off > dt_util.utcnow()\r\n        )\r\n\r\n    @property\r\n    def unique_id(self) -> str:\r\n        \"\"\"Return the unique ID of the device.\"\"\"\r\n        return self._uuid\r\n\r\n    @property\r\n    def name(self):\r\n        \"\"\"Return the name of the device.\"\"\"\r\n        return self._name\r\n\r\n    @property\r\n    def state(self):\r\n        \"\"\"Return the state of the device.\"\"\"\r\n        power = self.get_remote().power\r\n        if power is True:\r\n            self._state = STATE_ON\r\n        else:\r\n            self._state = STATE_OFF\r\n\r\n        return self._state\r\n\r\n    @property\r\n    def supported_features(self):\r\n        \"\"\"Flag media player features that are supported.\"\"\"\r\n        features = SUPPORT_SAMSUNGTV\r\n        if self._config.method != 'legacy':\r\n            features |= SUPPORT_TURN_ON\r\n\r\n        if self._config.method != 'websocket':\r\n            features |= SUPPORT_SELECT_SOURCE\r\n\r\n        return features\r\n\r\n    def turn_off(self):\r\n        \"\"\"Turn off media player.\"\"\"\r\n        self._end_of_power_off = dt_util.utcnow() + timedelta(seconds=15)\r\n        def do():\r\n            self.get_remote().power = False\r\n\r\n        t = threading.Thread(target=do)\r\n        t.daemon = True\r\n        t.start()\r\n\r\n    def select_source(self, source):\r\n        \"\"\"Select input source.\"\"\"\r\n        self.get_remote().source = source\r\n\r\n    @property\r\n    def source(self):\r\n        \"\"\"Name of the current input source.\"\"\"\r\n        source = self.get_remote().source\r\n        if source is None:\r\n            source = ''\r\n        else:\r\n            label = source.label\r\n            name = source.name\r\n\r\n            if name != label:\r\n                name = label + ':' + name\r\n\r\n            source = name\r\n        return source\r\n\r\n    @property\r\n    def source_list(self):\r\n        \"\"\"List of available input sources.\"\"\"\r\n        sources = self.get_remote().sources\r\n\r\n        if sources is None:\r\n            return []\r\n        else:\r\n            result = []\r\n            for source in sources:\r\n                label = source.label\r\n                name = source.name\r\n\r\n                if label != name:\r\n                    name = label + ':' + name\r\n\r\n                result += [name]\r\n\r\n            return result\r\n\r\n    def volume_up(self):\r\n        \"\"\"Volume up the media player.\"\"\"\r\n        self.send_key('KEY_VOLUP')\r\n\r\n    def volume_down(self):\r\n        \"\"\"Volume down media player.\"\"\"\r\n        self.send_key('KEY_VOLDOWN')\r\n\r\n    @property\r\n    def volume_level(self):\r\n        \"\"\"Volume level of the media player scalar volume. 0.0-1.0.\"\"\"\r\n        volume = self.get_remote().volume\r\n        if volume is None:\r\n            volume = 0.0\r\n        else:\r\n            volume /= 100.0\r\n\r\n        return volume\r\n\r\n    def set_volume_level(self, volume):\r\n        \"\"\"Set volume level, convert scalar volume. 0.0-1.0 to percent 0-100\"\"\"\r\n        self.get_remote().volume = int(volume * 100)\r\n\r\n    def mute_volume(self, mute):\r\n        \"\"\"Send mute command.\"\"\"\r\n        self.get_remote().mute = mute\r\n\r\n    @property\r\n    def is_volume_muted(self):\r\n        \"\"\"Boolean if volume is currently muted.\"\"\"\r\n        return False if not self.get_remote().mute else True\r\n\r\n    def media_play_pause(self):\r\n        \"\"\"Simulate play pause media player.\"\"\"\r\n        if self._playing:\r\n            self.media_pause()\r\n        else:\r\n            self.media_play()\r\n\r\n    def media_play(self):\r\n        \"\"\"Send play command.\"\"\"\r\n        self._playing = True\r\n        self.send_key('KEY_PLAY')\r\n\r\n    def media_pause(self):\r\n        \"\"\"Send media pause command to media player.\"\"\"\r\n        self._playing = False\r\n        self.send_key('KEY_PAUSE')\r\n\r\n    def media_next_track(self):\r\n        \"\"\"Send next track command.\"\"\"\r\n        self.send_key('KEY_FF')\r\n\r\n    def media_previous_track(self):\r\n        \"\"\"Send the previous track command.\"\"\"\r\n        self.send_key('KEY_REWIND')\r\n\r\n    async def async_play_media(self, media_type, media_id, **kwargs):\r\n        \"\"\"Support changing a channel.\"\"\"\r\n        if media_type != MEDIA_TYPE_CHANNEL:\r\n            _LOGGER.error('Unsupported media type')\r\n            return\r\n\r\n        # media_id should only be a channel number\r\n        try:\r\n            cv.positive_int(media_id)\r\n        except vol.Invalid:\r\n            _LOGGER.error('Media ID must be positive integer')\r\n            return\r\n\r\n        for digit in media_id:\r\n            await self.hass.async_add_job(self.send_key, 'KEY_' + digit)\r\n            await asyncio.sleep(KEY_PRESS_TIMEOUT, self.hass.loop)\r\n\r\n    def turn_on(self):\r\n        \"\"\"Turn the media player on.\"\"\"\r\n\r\n        if self._config.mac:\r\n            self._end_of_power_on = dt_util.utcnow() + timedelta(seconds=20)\r\n\r\n            def do():\r\n                self.get_remote().power = True\r\n\r\n            t = threading.Thread(target=do)\r\n            t.daemon = True\r\n            t.start()\r\n        elif self._config.method != 'legacy':\r\n            _LOGGER.info(\r\n                \"There was a problem detecting the TV's MAC address, \"\r\n                \"you will have to update the MAC address in the Home \"\r\n                \"Assistant config file manually.\"\r\n            )\r\n\r\n        else:\r\n            _LOGGER.info(\r\n                \"Legacy TV's (2008 - 2013) do not support \"\r\n                \"being powered on remotely.\"\r\n            )\r\n```\r\n\r\n\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/home-assistant/home-assistant/issues/comments/461667267","html_url":"https://github.com/home-assistant/home-assistant/issues/20795#issuecomment-461667267","issue_url":"https://api.github.com/repos/home-assistant/home-assistant/issues/20795","id":461667267,"node_id":"MDEyOklzc3VlQ29tbWVudDQ2MTY2NzI2Nw==","user":{"login":"arsaboo","id":18319734,"node_id":"MDQ6VXNlcjE4MzE5NzM0","avatar_url":"https://avatars1.githubusercontent.com/u/18319734?v=4","gravatar_id":"","url":"https://api.github.com/users/arsaboo","html_url":"https://github.com/arsaboo","followers_url":"https://api.github.com/users/arsaboo/followers","following_url":"https://api.github.com/users/arsaboo/following{/other_user}","gists_url":"https://api.github.com/users/arsaboo/gists{/gist_id}","starred_url":"https://api.github.com/users/arsaboo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arsaboo/subscriptions","organizations_url":"https://api.github.com/users/arsaboo/orgs","repos_url":"https://api.github.com/users/arsaboo/repos","events_url":"https://api.github.com/users/arsaboo/events{/privacy}","received_events_url":"https://api.github.com/users/arsaboo/received_events","type":"User","site_admin":false},"created_at":"2019-02-08T02:15:50Z","updated_at":"2019-02-08T02:15:50Z","author_association":"CONTRIBUTOR","body":"I don't see any PIN on my TV (UN75NU8000). I just see a prompt to allow access and I have done that. So, should not be a pairing issue here. Although not perfect, I am able to communicate with the TV (turn on/off)."}}
{"actor":{"display_login":"arsaboo"}}