Samsung Smart TV - No control?

I changed the directory according to the changes in the update description to:
/custom_component/samsungtv_custom/media_player.py. (simply renamed the old samsungtv_custom.py to media_player.py)

Changed is only Line 11 → here block 9 - 16

import voluptuous as vol

import homeassistant.components.samsungtv.media_player as stv
from homeassistant.components.media_player import (DOMAIN)
from homeassistant.const import (ATTR_ENTITY_ID)
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(name)

Did the folder change as well, by py file looks like this (line 7-20):

import logging
import socket
from bs4 import BeautifulSoup

import homeassistant.components.samsungtv.media_player as stv
#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

Can you link / post the entire file?

as i mentioned the folder structure changed, so you have to rename both the folder where the file is in to samsungtv_custom and the file itself to media_player.py.
my media_player.py in /custom_components/samsungtv_custom/ looks like the original only line changed is 11:

"""
Custom component to allow overriding or adding features to the samsungtv media_player component.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.samsungtv/
"""

import logging

import voluptuous as vol

import homeassistant.components.samsungtv.media_player as stv
from homeassistant.components.media_player import (DOMAIN)
from homeassistant.const import (ATTR_ENTITY_ID)
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

SAMSUNG_TV_CUSTOM_DATA = 'samsungtv_custom'

SERVICE_KEY = 'send_key'

# Service call validation schemas
ATTR_KEY = 'key_code'

SAMSUNG_TV_CUSTOM_KEY_SCHEMA = vol.Schema({
    vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
    vol.Required(ATTR_KEY): cv.string,
})

def setup_platform(hass, config, add_devices, discovery_info=None):
    if SAMSUNG_TV_CUSTOM_DATA not in hass.data:
        hass.data[SAMSUNG_TV_CUSTOM_DATA] = []

    # Use this to get my hands on the SamsungTVDevices that get added
    def add_devices_custom(devices):
        add_devices(devices)
        for device in devices:
            hass.data[SAMSUNG_TV_CUSTOM_DATA].append(device)

    # pass in my add_devices_custom function
    stv.setup_platform(hass, config, add_devices_custom, discovery_info)
    _LOGGER.debug("hass.data[SAMSUNG_TV_CUSTOM_DATA] = %s.", hass.data[SAMSUNG_TV_CUSTOM_DATA])

    def service_handle(service):
        _LOGGER.debug("service_handle called for %s with %s", service.service, service.data)
        entity_ids = service.data.get('entity_id')
        devices = hass.data[SAMSUNG_TV_CUSTOM_DATA]

        for device in devices:
            if device.entity_id in entity_ids:
                if service.service == SERVICE_KEY:
                    device.send_key(service.data.get(ATTR_KEY))

                    device.schedule_update_ha_state(True)

    hass.services.register(
        DOMAIN, SERVICE_KEY, service_handle,
        schema=SAMSUNG_TV_CUSTOM_KEY_SCHEMA)

I already did that, so I hope I’m on the way.
Hmm your file doesn’t seem to be that long. I just checked the original file and that is also way longer:

Here is mine:

"""
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
from bs4 import BeautifulSoup

import homeassistant.components.samsungtv.media_player as stv
#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

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

_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)

Sorry I just can’t see it.

dunni, can post it again, but its a 58 Rows long py file. perhaps im not using the newest version, got mine out of this forum

You can try pm it to me. Yours is working, so that’s all I need as well :slight_smile:

EDIT: I tried yours with thr 58 lines of code. Kinda works - I can see when my Samsung is On and Off, but I can’t control anything at all :frowning:

in lovelace ui i use buttons like this one…

              - type: horizontal-stack
                cards:
                  - color_type: card
                    entity: script.samsung_progdown
                    icon: 'mdi:chevron-down'
                    service:
                      data:
                        entity_id: media_player.samsung_tv
                        key_code: KEY_CHDOWN
                      domain: media_player
                      action: send_key
                    color_off: var(--primary-color)
                    type: 'custom:button-card'
                    action: service
                    style:
                      - border-radius: 20px

and in media_players i have:

  • platform: samsungtv_custom
    name: Samsung TV
    host: “192.168.178.26”
    mac: “D0:66:XX:XX:XX:XX”

i also struggled hard to get this thing working, im hoping for a better integration at all, also what doesn’t work is jumping to a specified hdmi channel like hdmi1 or hdmi2 only switching through all works… but i think with more time there would be a way over hdmi-cec to solve this issue.

I hope you are right. I will try later this afternoon. Previously I was on openhab and the Samsung Integration worked very well for me, also the part where you can switch to a specific Hdmi channel.

Hmm, if I try this on my configutation.yaml file, it just spins when I test for erros and I get this error in my log:

Error handling request

Here’s my config:

# Samsung TV
media_player:
  - platform: samsungtv_custom
    name: '[Stue] Samsung TV'
    host: xxx.xxx.xxx.75
#    port: 8001
#    timeout: 30
    mac: "50:56:XX:XX:XX:XX"

I’d be happy if I knew the state! I just get unavailable…

I dud have a script which reported state and turned it on but didn’t turn it off. That worked on 86.4 but diesbt on 91.0.

What version are you on mate?

What file is exactly working,
Please provide,’would like to try and see whether it works with my Samsungtv.

i really don’t have a clue, why it isn’t working - also get to my solution by accident, thought by myself why don’t change the line like i changed the folder logic? and that does the trick - never have done any custom components or something bevor, only wanted to share this so the creator of the component can integrate this fix…

so again, my working solution is posted in this thread by Drywcyfyr on post 10. --> can’t get links to work properly and can’t repost the code due to the forum rules.

do you have the normal samsungtv component enabled? Drywcyfyr mentioned that this could be a problem.

I’m on the same version 91.0.

Well if you see my config file then it shows the samsungtv platform:

media_player:
  - platform: samsungtv
    name: '[Living Rom] Samsung TV'
    host: xxx.xxx.xxx.75
    port: 8001
    timeout: 30
    mac: "50:XX:XX:XX:XX:XX"

But if I try to change the platform to: samsungtv_custom
Then HA won’t accept it.

I did change the folder structure so I now have:
www/custom_components/samsungtv_custom/media_player.py

EDIT: Seems that some of the caracters got messes up when copy pasting, but now another error shows up:

File “/config/custom_components/samsungtv_custom/media_player.py”, line 41, in <module> stv.setup_platform(hass, config, add_devices_custom, discovery_info) NameError: name ‘hass’ is not defined

huh?

I’m having the exact same issue on my MU6070. Created the folder structure under the /config folder but when I rename the component like below, the check config just spins and HA wont restart.

media_player:

  • platform: samsungtv_custom
    host: 192.168.1.3
    port: 8001
    name: Living Room TV
    mac: REDACTED

I was able to get it to power on but not power off or do anything else. When i use the standard component samsungtv I get an Invalid OPCODE error which seems to have changed recently. I’m at the latest vers of HA at 91.3

I’ve gotten most of everything else in HA to work but really struggling having it control this TV. Any suggestions or help would be much appreciated.

Anyone getting:

“Integration samsungtv_custom not found when trying to verify its media_player platform.”

… after upgrading to the latest 0.92.0?

I’ve only been able to turn it on with the WoL. I was able to setup SmartThings integration so I can now sometimes turn it off as well but its a separate switch. It also has sensors for Volume, Channel, and Source, but they only update if you go into the app and refresh the settings there. Then it updates in HA.

I just put in an order for a Broadlink RM Mini3 that Im going to install in my livingroom to take care of this. its pretty crappy that Samsung nerfed the webhooks with the firmware update. But im tired of waiting for them to make any changes to get it back. Plus this way I can control any other IR based devices as well ultimately via voice through Google Mini’s.

Love to hear about anyone else’s setup or if you were able to get true TCP/IP control of your Samsung TV through the samsungctl component. So far all I get is " Invalid OpCode" or nothing at all.

I have a Samsung 6 Series 58Inch un58mu6070

1 Like

I have exactly the same issue after upgrading to .92