Control newer Samsung TVs

Hey, thanks for the development on this!

I’ve installed it and it’s dependencies, but it’s still failing on load. Maybe I messed something up?

2018-02-03 13:02:14 ERROR (MainThread) [homeassistant.loader] Error loading custom_components.media_player.samsungtv. Make sure all dependencies are installed

Traceback (most recent call last):
File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/loader.py”, line 142, in get_component
module = importlib.import_module(path)
File “/usr/lib/python3.5/importlib/init.py”, line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File “”, line 986, in _gcd_import
File “”, line 969, in _find_and_load
File “”, line 958, in _find_and_load_unlocked
File “”, line 673, in _load_unlocked
File “”, line 673, in exec_module
File “”, line 222, in _call_with_frames_removed
File “/home/homeassistant/.homeassistant/custom_components/media_player/samsungtv.py”, line 9, in
from bs4 import BeautifulSoup
ImportError: No module named ‘bs4’

The power status is also broken for me in 0.62 and the play/pause feature only seems to work 1 in 5 times.

I’ve just rolled back to v 0.60 for the Samsung component and copied it into custom components so it isn’t overwritten next update.

So I have the latest build of HomeAssistant(0.63.2) and the following configuration for my Samsung Q6F (2017 model):

  • platform: samsungtv
    host: 192.168.1.XX
    port: 8001
    name: TV
    timeout: 5
    mac: cc:XX:XX:XX:XX:XX

`
I can only see the status (ON or OFF) and the only interaction working it’s Turn ON the TV, the remaning options doesn’t work, like: Turn OFF, volume UP/DOWN, change channel.

Any suggestion?

What model do you have? Mine will not work because of this statement listed under the Samsung componet page.

"None of the 2014 (H) and 2015 (J) model series (e.g. J5200) will work, since Samsung have used a different (encrypted) type of interface for these."

I don’t know if the newer ones work.

2016 or 2017 models, working fine on 0.60 so I’m just going to use that version until this stabilises

Does anyone run this on hassio?

From what I can see, this is already being reported here: https://github.com/home-assistant/home-assistant/issues/12302

Ive got the same any issue, have you had any progression? @Ola_Svensson

I’m running HA on Raspberry Pi 3 B+ in a virtual environment. My TV is a UE46ES8000 (2012). My configuration:

- platform: samsungtv
  host: 192.168.0.4
  mac: 'XX:XX:XX:XX:XX:XX'
  name: Samsung UE46ES8000

I’m getting wakeonlan error. I’ve installed wakeonlan 0.2.2 but the error persists. What may be wrong?

It detects samsung tv also if i have discovery : disabled?

Is everything still working for you with your Q7F? I have the 2018 model and I can’t for the life of me get it to respond to commands. Are you using the OP custom script, or the original samsung component that is provided by HA?

Yes, it works fine and I’m using the original HA component for Samsung TV.

1 Like

Thanks to MrUart232 custom samsungtv.py I can control my Samsung H6400 TV.
Now I can: mute / unmute, set volume, increase / decrease volume and change the source.
It would have been great if I could turn it on or off but at least i have some control of the tv.

I have a 2018 Q7F. @Kent may have an earlier model. I can turn on the TV if the TV has been off for a short amount of time (like 10s, you can hear a click in the control box). I can’t get it to do anything else.

I tried samsungctl and it can only control the TV if I manually confirm a popup “grant permission?” screen, so it kinda nulls out any automation value…

I bought the TV 2018 and I doubt that Samsung has changed the design for Q7F, I also have this delay when I turn of the TV about 5-10 sec. until I can turn it on again via Hassio, I have set the grant permission for external devices so it only prompts the first time Hassio connects to the TV, it’s under General settings, external devices.

1 Like

I’m trying with my UN55LS003 (Frame) TV. Discovers it, but I’m unable to do much. It does not ask for permission.

How does one install BeautifulSoap and wakeonlan if running Hassio OS? I’m interested in trying this as I have an H series Samsung TV, but I am extremely new to Home Assistant.

Hi,
try this version out, it puts the requirements on the top of the code. Then it should work automatically.

"""
Support for interface with an Samsung TV.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.samsungtv/
"""
import logging
import socket

REQUIREMENTS = ['wakeonlan==0.2.2', 'beautifulsoup4==4.6.0']

from bs4 import BeautifulSoup

import voluptuous as vol

from homeassistant.components.media_player import (
    SUPPORT_SELECT_SOURCE, SUPPORT_TURN_OFF, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
    SUPPORT_VOLUME_STEP, MediaPlayerDevice, PLATFORM_SCHEMA, SUPPORT_TURN_ON)
from homeassistant.const import (
    CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN, CONF_PORT,
    CONF_MAC)
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

CONF_TIMEOUT = 'timeout'

DEFAULT_NAME = 'Samsung TV Remote'
DEFAULT_PORT = 55000
DEFAULT_TIMEOUT = 0

KNOWN_DEVICES_KEY = 'samsungtv_known_devices'

SUPPORT_SAMSUNGTV = SUPPORT_SELECT_SOURCE | SUPPORT_VOLUME_SET | \
    SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | SUPPORT_TURN_OFF

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_HOST): cv.string,
    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
    vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
})


# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the Samsung TV platform."""
    known_devices = hass.data.get(KNOWN_DEVICES_KEY)
    if known_devices is None:
        known_devices = set()
        hass.data[KNOWN_DEVICES_KEY] = known_devices

    # Is this a manual configuration?
    if config.get(CONF_HOST) is not None:
        host = config.get(CONF_HOST)
        port = config.get(CONF_PORT)
        name = config.get(CONF_NAME)
        mac = config.get(CONF_MAC)
        timeout = config.get(CONF_TIMEOUT)
    elif discovery_info is not None:
        tv_name = discovery_info.get('name')
        model = discovery_info.get('model_name')
        host = discovery_info.get('host')
        name = "{} ({})".format(tv_name, model)
        port = DEFAULT_PORT
        timeout = DEFAULT_TIMEOUT
        mac = None
    else:
        _LOGGER.warning("Cannot determine device")
        return

    # Only add a device once, so discovered devices do not override manual
    # config.
    ip_addr = socket.gethostbyname(host)
    if ip_addr not in known_devices:
        known_devices.add(ip_addr)
        add_devices([SamsungTVDevice(host, port, name, timeout, mac)])
        _LOGGER.info("Samsung TV %s:%d added as '%s'", host, port, name)
    else:
        _LOGGER.info("Ignoring duplicate Samsung TV %s:%d", host, port)


class SamsungTVDevice(MediaPlayerDevice):
    """Representation of a Samsung TV."""

    def __init__(self, host, port, name, timeout, mac):
        """Initialize the Samsung device."""
        from wakeonlan import wol
        # Save a reference to the imported classes
        self._name = name
        self._mac = mac
        self._wol = wol
        # Assume that the TV is not muted
        self._muted = False
        self._volume = 0
        self._state = STATE_OFF
        # Generate a configuration for the Samsung library
        self._config = {
            'name': 'HomeAssistant',
            'description': name,
            'id': 'ha.component.samsung',
            'port': 7676,
            'host': host,
            'timeout': timeout,
        }
        self._selected_source = ''
        self._source_names = self.SendSOAP('smp_4_', 'urn:samsung.com:service:MainTVAgent2:1', 'GetSourceList', '', 'sourcetype')
        if self._source_names:
            del self._source_names[0]
            self._source_ids = self.SendSOAP('smp_4_', 'urn:samsung.com:service:MainTVAgent2:1', 'GetSourceList', '', 'id')
            self._sources = dict(zip(self._source_names, self._source_ids))
        else:
            self._source_names = {}
            self._source_ids = {}
            self._sources = {}        

    def update(self):
        """Retrieve the latest data."""
        currentvolume = self.SendSOAP('smp_17_', 'urn:schemas-upnp-org:service:RenderingControl:1', 'GetVolume', '<InstanceID>0</InstanceID><Channel>Master</Channel>','currentvolume')
        if currentvolume:
            self._volume = int(currentvolume) / 100
            currentmute = self.SendSOAP('smp_17_', 'urn:schemas-upnp-org:service:RenderingControl:1', 'GetMute', '<InstanceID>0</InstanceID><Channel>Master</Channel>','currentmute')
            if currentmute == '1':
                self._muted = True
            else:
                self._muted = False
            source = self.SendSOAP('smp_4_', 'urn:samsung.com:service:MainTVAgent2:1', 'GetCurrentExternalSource', '','currentexternalsource')
            self._selected_source = source
            self._state = STATE_ON
            return True
        else:
            self._state = STATE_OFF
            return False

    def SendSOAP(self,path,urn,service,body,XMLTag):
        CRLF = "\r\n"
        xmlBody = "";
        xmlBody += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
        xmlBody += '<s:Body>'
        xmlBody += '<u:{service} xmlns:u="{urn}">{body}</u:{service}>'
        xmlBody += '</s:Body>'
        xmlBody += '</s:Envelope>'
        xmlBody = xmlBody.format(urn = urn, service = service, body = body)
    
        soapRequest  = "POST /{path} HTTP/1.0%s" % (CRLF)
        soapRequest += "HOST: {host}:{port}%s" % (CRLF)
        soapRequest += "CONTENT-TYPE: text/xml;charset=\"utf-8\"%s" % (CRLF)
        soapRequest += "SOAPACTION: \"{urn}#{service}\"%s" % (CRLF)
        soapRequest += "%s" % (CRLF)
        soapRequest += "{xml}%s" % (CRLF)
        soapRequest = soapRequest.format(host = self._config['host'], port = self._config['port'], xml = xmlBody, path = path, urn = urn, service = service)
    
        
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.settimeout(0.5)
        client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        dataBuffer = ''
        response_xml = ''
        _LOGGER.info("Samsung TV sending: %s", soapRequest)
    
        try:
            client.connect( (self._config['host'], self._config['port']) )
            client.send(bytes(soapRequest, 'utf-8'))
            while True:
                dataBuffer = client.recv(4096)
                if not dataBuffer: break
                response_xml += str(dataBuffer)
        except socket.error as e:
            return
        
        response_xml = bytes(response_xml, 'utf-8')
        response_xml = response_xml.decode(encoding="utf-8")
        response_xml = response_xml.replace("&lt;","<")
        response_xml = response_xml.replace("&gt;",">")
        response_xml = response_xml.replace("&quot;","\"")
        _LOGGER.info("Samsung TV received: %s", response_xml)
        if XMLTag:
            soup = BeautifulSoup(str(response_xml), 'html.parser')
            xmlValues = soup.find_all(XMLTag)
            xmlValues_names = [xmlValue.string for xmlValue in xmlValues]
            if len(xmlValues_names)== 1: 
                return xmlValues_names[0]
            else:
                return xmlValues_names
        else:
            return response_xml[response_xml.find('<s:Envelope'):]

    @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 volume_level(self):
        """Volume level of the media player (0..1)."""
        return self._volume

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

    def source(self):
        """Return the current input source."""
        return self._selected_source

    @property
    def source_list(self):
        """List of available input sources."""
        return self._source_names

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

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

    def select_source(self, source):
        """Select input source."""
        self.SendSOAP('smp_4_', 'urn:samsung.com:service:MainTVAgent2:1', 'SetMainTVSource', '<Source>'+source+'</Source><ID>' + self._sources[source] + '</ID><UiID>0</UiID>','')

    def turn_off(self):
        """Turn off media player."""

    def set_volume_level(self, volume):
        """Volume up the media player."""
        volset = str(round(volume * 100))
        self.SendSOAP('smp_17_', 'urn:schemas-upnp-org:service:RenderingControl:1', 'SetVolume', '<InstanceID>0</InstanceID><DesiredVolume>' + volset + '</DesiredVolume><Channel>Master</Channel>','')

    def volume_up(self):
        """Volume up the media player."""
        volume = self._volume + 0.01
        self.set_volume_level(volume)

    def volume_down(self):
        """Volume down media player."""
        volume = self._volume - 0.01
        self.set_volume_level(volume)

    def mute_volume(self, mute):
        """Send mute command."""
        if self._muted == True:
            doMute = '0'
        else:
            doMute = '1'
        self.SendSOAP('smp_17_', 'urn:schemas-upnp-org:service:RenderingControl:1', 'SetMute', '<InstanceID>0</InstanceID><DesiredMute>' + doMute + '</DesiredMute><Channel>Master</Channel>','')

    def turn_on(self):
        """Turn the media player on."""
        if self._mac:
            self._wol.send_magic_packet(self._mac)
1 Like

Tried this in hassio and get…

Error while setting up platform samsungtv
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity_platform.py", line 129, in _async_setup_platform
    SLOW_SETUP_MAX_WAIT, loop=hass.loop)
  File "/usr/local/lib/python3.6/asyncio/tasks.py", line 358, in wait_for
    return fut.result()
  File "/usr/local/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/media_player/samsungtv.py", line 74, in setup_platform
    ip_addr = socket.gethostbyname(host)
socket.gaierror: [Errno -2] Name does not resolve