Support for Bravia Smart TV (2013+)

How about supporting sony Bravia TVs?
There are some python 2 samples but they are admitedly not very intuitive…


I would like to write a component myself but i am struggling with the samples and my wife can’t let me work on something 5 minutes traight :frowning:

1 Like

Would love this feature as well…some kind of remote would be best.Found another link as well-

Hm, power off, volume up, volume down and mute is working so far… I have a hard time to get how this is supposed to be embeded. I figure the power on over wifi needs to be either enabled on my tv or the command is wrong…

We’ll see when i get to to it again…
It was only found when i placed it in the mdiea_player folder and not when in custom_components (huh?)

here is the initial toy version of bravia.py based on the Samsung one:

“”"
Support for interface with an Bravia TV.

For more details about this platform, please refer to the documentation at
wait, noo… not yet
“”"
import logging
import json
import requests

from homeassistant.components.media_player import (
DOMAIN, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK,
SUPPORT_TURN_OFF, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP,
MediaPlayerDevice)
from homeassistant.const import (
CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN)
from homeassistant.helpers import validate_config

CONF_PORT = “port”
CONF_TIMEOUT = “timeout”

_LOGGER = logging.getLogger(name)

SUPPORT_BRAVIA = SUPPORT_PAUSE | SUPPORT_VOLUME_STEP |
SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK |
SUPPORT_NEXT_TRACK | SUPPORT_TURN_OFF

pylint: disable=unused-argument

def setup_platform(hass, config, add_devices, discovery_info=None):
“”“Setup the Bravia TV platform.”""
# Validate that all required config options are given
if not validate_config({DOMAIN: config}, {DOMAIN: [CONF_HOST]}, _LOGGER):
return False

# Default the entity_name to 'Samsung TV Remote'
name = config.get(CONF_NAME, 'Samsung TV Remote')

# Generate a config for the Samsung lib
remote_config = {
    "name": "HomeAssistant",
    "description": config.get(CONF_NAME, ''),
    "id": "ha.component.samsung",
    "port": config.get(CONF_PORT, 55000),
    "host": config.get(CONF_HOST),
    "timeout": config.get(CONF_TIMEOUT, 0),
}

add_devices([BraviaTVDevice(name, remote_config)])

pylint: disable=abstract-method

class BraviaTVDevice(MediaPlayerDevice):
“”“Representation of a Samsung TV.”""

# pylint: disable=too-many-public-methods
def __init__(self, name, config):
    """Initialize the Sony device."""
    #from samsungctl import Remote
    # Save a reference to the imported class
    #self._remote_class = Remote
    self._name = name
    # Assume that the TV is not muted
    self._muted = False
    # Assume that the TV is in Play mode
    self._playing = True
    self._state = STATE_UNKNOWN
    self._remote = None
    self._config = config

@classmethod
def do_ircc(self, data):
    sony_xml = \
"""<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:X_SendIRCC xmlns:u="urn:schemas-sony-com:service:IRCC:1">
      <IRCCCode>%s</IRCCCode>
    </u:X_SendIRCC>
  </s:Body>
</s:Envelope>"""
    req = requests.request('POST', 'http://192.168.0.26/sony/IRCC/',
                           headers={'SOAPAction': "urn:schemas-sony-com:service:IRCC:1#X_SendIRCC"},
                           data=sony_xml % data)
    return req
    
def update(self):
    """Retrieve the latest data."""
    # Send an empty key to see if we are still connected
    return self.send_key('KEY_POWER')
"""
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.
        self._remote = self._remote_class(self._config)
    
    return self._remote
"""
def send_key(self, key):
    """Send a key to the tv and handles exceptions.
    try:
        self.get_remote().control(key)
        self._state = STATE_ON
    except (self._remote_class.UnhandledResponse,
            self._remote_class.AccessDenied, BrokenPipeError):
        # We got a response so it's on.
        # BrokenPipe can occur when the commands is sent to fast
        self._state = STATE_ON
        self._remote = None
        return False
    except (self._remote_class.ConnectionClosed, socket.timeout,
            TimeoutError, OSError):
        self._state = STATE_OFF
        self._remote = None
        return False
    """
    
    return True

@property
def name(self):
    """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 supported_media_commands(self):
    """Flag of media commands that are supported."""
    return SUPPORT_BRAVIA

def turn_off(self):
    """Turn off media player."""
    self.do_ircc('AAAAAQAAAAEAAAAvAw==') #power off

def volume_up(self):
    """Volume up the media player."""
    self.do_ircc('AAAAAQAAAAEAAAASAw==') #VolumeUp

def volume_down(self):
    """Volume down media player."""
    self.do_ircc('AAAAAQAAAAEAAAATAw==') #VolumeDown

def mute_volume(self, mute):
    """Send mute command."""
    self.do_ircc('AAAAAQAAAAEAAAAUAw==') #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_key("KEY_PLAY")

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

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

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

def turn_on(self):
    """Turn the media player on."""
    self.do_ircc('AAAAAQAAAAEAAAAVAw==') #PowerOn i guess

Sorry, not that good with Python, how would i go about trying to set this up in my HA?

Actually, I believe the bravia series use WOL to turn on.

Everything else can be controlled via the following values.

            {
                "name": "PowerOff",
                "value": "AAAAAQAAAAEAAAAvAw=="
            },
            {
                "name": "Input",
                "value": "AAAAAQAAAAEAAAAlAw=="
            },
            {
                "name": "GGuide",
                "value": "AAAAAQAAAAEAAAAOAw=="
            },
            {
                "name": "EPG",
                "value": "AAAAAgAAAKQAAABbAw=="
            },
            {
                "name": "Favorites",
                "value": "AAAAAgAAAHcAAAB2Aw=="
            },
            {
                "name": "Display",
                "value": "AAAAAQAAAAEAAAA6Aw=="
            },
            {
                "name": "Home",
                "value": "AAAAAQAAAAEAAABgAw=="
            },
            {
                "name": "Options",
                "value": "AAAAAgAAAJcAAAA2Aw=="
            },
            {
                "name": "Return",
                "value": "AAAAAgAAAJcAAAAjAw=="
            },
            {
                "name": "Up",
                "value": "AAAAAQAAAAEAAAB0Aw=="
            },
            {
                "name": "Down",
                "value": "AAAAAQAAAAEAAAB1Aw=="
            },
            {
                "name": "Right",
                "value": "AAAAAQAAAAEAAAAzAw=="
            },
            {
                "name": "Left",
                "value": "AAAAAQAAAAEAAAA0Aw=="
            },
            {
                "name": "Confirm",
                "value": "AAAAAQAAAAEAAABlAw=="
            },
            {
                "name": "Red",
                "value": "AAAAAgAAAJcAAAAlAw=="
            },
            {
                "name": "Green",
                "value": "AAAAAgAAAJcAAAAmAw=="
            },
            {
                "name": "Yellow",
                "value": "AAAAAgAAAJcAAAAnAw=="
            },
            {
                "name": "Blue",
                "value": "AAAAAgAAAJcAAAAkAw=="
            },
            {
                "name": "Num1",
                "value": "AAAAAQAAAAEAAAAAAw=="
            },
            {
                "name": "Num2",
                "value": "AAAAAQAAAAEAAAABAw=="
            },
            {
                "name": "Num3",
                "value": "AAAAAQAAAAEAAAACAw=="
            },
            {
                "name": "Num4",
                "value": "AAAAAQAAAAEAAAADAw=="
            },
            {
                "name": "Num5",
                "value": "AAAAAQAAAAEAAAAEAw=="
            },
            {
                "name": "Num6",
                "value": "AAAAAQAAAAEAAAAFAw=="
            },
            {
                "name": "Num7",
                "value": "AAAAAQAAAAEAAAAGAw=="
            },
            {
                "name": "Num8",
                "value": "AAAAAQAAAAEAAAAHAw=="
            },
            {
                "name": "Num9",
                "value": "AAAAAQAAAAEAAAAIAw=="
            },
            {
                "name": "Num0",
                "value": "AAAAAQAAAAEAAAAJAw=="
            },
            {
                "name": "Num11",
                "value": "AAAAAQAAAAEAAAAKAw=="
            },
            {
                "name": "Num12",
                "value": "AAAAAQAAAAEAAAALAw=="
            },
            {
                "name": "VolumeUp",
                "value": "AAAAAQAAAAEAAAASAw=="
            },
            {
                "name": "VolumeDown",
                "value": "AAAAAQAAAAEAAAATAw=="
            },
            {
                "name": "Mute",
                "value": "AAAAAQAAAAEAAAAUAw=="
            },
            {
                "name": "ChannelUp",
                "value": "AAAAAQAAAAEAAAAQAw=="
            },
            {
                "name": "ChannelDown",
                "value": "AAAAAQAAAAEAAAARAw=="
            },
            {
                "name": "SubTitle",
                "value": "AAAAAgAAAJcAAAAoAw=="
            },
            {
                "name": "ClosedCaption",
                "value": "AAAAAgAAAKQAAAAQAw=="
            },
            {
                "name": "Enter",
                "value": "AAAAAQAAAAEAAAALAw=="
            },
            {
                "name": "DOT",
                "value": "AAAAAgAAAJcAAAAdAw=="
            },
            {
                "name": "Analog",
                "value": "AAAAAgAAAHcAAAANAw=="
            },
            {
                "name": "Teletext",
                "value": "AAAAAQAAAAEAAAA/Aw=="
            },
            {
                "name": "Exit",
                "value": "AAAAAQAAAAEAAABjAw=="
            },
            {
                "name": "Analog2",
                "value": "AAAAAQAAAAEAAAA4Aw=="
            },
            {
                "name": "*AD",
                "value": "AAAAAgAAABoAAAA7Aw=="
            },
            {
                "name": "Digital",
                "value": "AAAAAgAAAJcAAAAyAw=="
            },
            {
                "name": "Analog?",
                "value": "AAAAAgAAAJcAAAAuAw=="
            },
            {
                "name": "BS",
                "value": "AAAAAgAAAJcAAAAsAw=="
            },
            {
                "name": "CS",
                "value": "AAAAAgAAAJcAAAArAw=="
            },
            {
                "name": "BSCS",
                "value": "AAAAAgAAAJcAAAAQAw=="
            },
            {
                "name": "Ddata",
                "value": "AAAAAgAAAJcAAAAVAw=="
            },
            {
                "name": "PicOff",
                "value": "AAAAAQAAAAEAAAA+Aw=="
            },
            {
                "name": "Tv_Radio",
                "value": "AAAAAgAAABoAAABXAw=="
            },
            {
                "name": "Theater",
                "value": "AAAAAgAAAHcAAABgAw=="
            },
            {
                "name": "SEN",
                "value": "AAAAAgAAABoAAAB9Aw=="
            },
            {
                "name": "InternetWidgets",
                "value": "AAAAAgAAABoAAAB6Aw=="
            },
            {
                "name": "InternetVideo",
                "value": "AAAAAgAAABoAAAB5Aw=="
            },
            {
                "name": "Netflix",
                "value": "AAAAAgAAABoAAAB8Aw=="
            },
            {
                "name": "SceneSelect",
                "value": "AAAAAgAAABoAAAB4Aw=="
            },
            {
                "name": "Mode3D",
                "value": "AAAAAgAAAHcAAABNAw=="
            },
            {
                "name": "iManual",
                "value": "AAAAAgAAABoAAAB7Aw=="
            },
            {
                "name": "Audio",
                "value": "AAAAAQAAAAEAAAAXAw=="
            },
            {
                "name": "Wide",
                "value": "AAAAAgAAAKQAAAA9Aw=="
            },
            {
                "name": "Jump",
                "value": "AAAAAQAAAAEAAAA7Aw=="
            },
            {
                "name": "PAP",
                "value": "AAAAAgAAAKQAAAB3Aw=="
            },
            {
                "name": "MyEPG",
                "value": "AAAAAgAAAHcAAABrAw=="
            },
            {
                "name": "ProgramDescription",
                "value": "AAAAAgAAAJcAAAAWAw=="
            },
            {
                "name": "WriteChapter",
                "value": "AAAAAgAAAHcAAABsAw=="
            },
            {
                "name": "TrackID",
                "value": "AAAAAgAAABoAAAB+Aw=="
            },
            {
                "name": "TenKey",
                "value": "AAAAAgAAAJcAAAAMAw=="
            },
            {
                "name": "AppliCast",
                "value": "AAAAAgAAABoAAABvAw=="
            },
            {
                "name": "acTVila",
                "value": "AAAAAgAAABoAAAByAw=="
            },
            {
                "name": "DeleteVideo",
                "value": "AAAAAgAAAHcAAAAfAw=="
            },
            {
                "name": "PhotoFrame",
                "value": "AAAAAgAAABoAAABVAw=="
            },
            {
                "name": "TvPause",
                "value": "AAAAAgAAABoAAABnAw=="
            },
            {
                "name": "KeyPad",
                "value": "AAAAAgAAABoAAAB1Aw=="
            },
            {
                "name": "Media",
                "value": "AAAAAgAAAJcAAAA4Aw=="
            },
            {
                "name": "SyncMenu",
                "value": "AAAAAgAAABoAAABYAw=="
            },
            {
                "name": "Forward",
                "value": "AAAAAgAAAJcAAAAcAw=="
            },
            {
                "name": "Play",
                "value": "AAAAAgAAAJcAAAAaAw=="
            },
            {
                "name": "Rewind",
                "value": "AAAAAgAAAJcAAAAbAw=="
            },
            {
                "name": "Prev",
                "value": "AAAAAgAAAJcAAAA8Aw=="
            },
            {
                "name": "Stop",
                "value": "AAAAAgAAAJcAAAAYAw=="
            },
            {
                "name": "Next",
                "value": "AAAAAgAAAJcAAAA9Aw=="
            },
            {
                "name": "Rec",
                "value": "AAAAAgAAAJcAAAAgAw=="
            },
            {
                "name": "Pause",
                "value": "AAAAAgAAAJcAAAAZAw=="
            },
            {
                "name": "Eject",
                "value": "AAAAAgAAAJcAAABIAw=="
            },
            {
                "name": "FlashPlus",
                "value": "AAAAAgAAAJcAAAB4Aw=="
            },
            {
                "name": "FlashMinus",
                "value": "AAAAAgAAAJcAAAB5Aw=="
            },
            {
                "name": "TopMenu",
                "value": "AAAAAgAAABoAAABgAw=="
            },
            {
                "name": "PopUpMenu",
                "value": "AAAAAgAAABoAAABhAw=="
            },
            {
                "name": "RakurakuStart",
                "value": "AAAAAgAAAHcAAABqAw=="
            },
            {
                "name": "OneTouchTimeRec",
                "value": "AAAAAgAAABoAAABkAw=="
            },
            {
                "name": "OneTouchView",
                "value": "AAAAAgAAABoAAABlAw=="
            },
            {
                "name": "OneTouchRec",
                "value": "AAAAAgAAABoAAABiAw=="
            },
            {
                "name": "OneTouchStop",
                "value": "AAAAAgAAABoAAABjAw=="
            },
            {
                "name": "DUX",
                "value": "AAAAAgAAABoAAABzAw=="
            },
            {
                "name": "FootballMode",
                "value": "AAAAAgAAABoAAAB2Aw=="
            },
            {
                "name": "Social",
                "value": "AAAAAgAAABoAAAB0Aw=="
            }

Um, does it do WoL over wifi?
Will add it when i get to it.

@vswraith

place the file next to samsungtv.py (change the ip address, hardcoded for now) and add something like

media_player 3:
platform: bravia
name: MyBraviaTV

to your configuration.yaml

WoL works fine via wifi

I would love, love, love to see this working as well.

um, you need to do ugly stuff because i am beeing dense but

please note, you still have to edit the ip and mac address in the code :-/
Seems to work for me.

If it helps someone to make dominique’s script works i had to add ‘X-Auth-PSK’: ‘1111’ to the header of the request like this:

headers={‘X-Auth-PSK’: ‘1111’, ‘SOAPAction’: ‘urn:schemas-sony-com:service:IRCC:1#X_SendIRCC’},
(note that 1111 is a preshared key that i had to set in the tv network options)

I also found other commands that works:
‘HDMI1’: ‘AAAAAgAAABoAAABaAw==’,
‘HDMI2’: ‘AAAAAgAAABoAAABbAw==’,
‘HDMI3’: ‘AAAAAgAAABoAAABcAw==’,
‘HDMI4’: ‘AAAAAgAAABoAAABdAw==’,

You can find more here if you are interested:

Thanks. Yeah depending on the model one needs to either get a key (tricky to set up) or set a preshared key. I think we should go with the preshared key. Maybe the default port of HA would be good? :wink:

Would love to use that to get more functionality like read the current show/channel etc.

I can’t make sense out of the exmples to find out how to make the tv a swtchable component so one can switch it on with all other devices in a group.

@alvise so it kinda works for you?

Yes it works!
it also power on correctly with wol and power off with the request.
(All other commands works nicely too.)

Good idea with the HA port as the preshared key, now i was also trying to get some status/info from the tv.

Ok, i updated it, now you can set the channel :slight_smile:
V. 0.2 is on github

also toggling works so if you place the tv in a group it gets switched on/off with the group :slight_smile: wee.

Maybe we need to turn off the logging for -
"updating state of the bravia tv"
I keep getting this in the logs.

Yeah it still has some redundant messages and what not.

I saw the code has option to send channel keys, but i dont see anything in the GUI.Am i missing a step? Mine is an android TV so would be cool to automate the apps as well.

Actually, i was wondering if its possible to design a full blown remote card with GUI? How do the buttons and icons map to the actual command X/Y coordinates etc?

There is no such UI in home assistant. See the example for a input and a automation rule. The trick is to have a list with the channels and the begin needs to be the channel number. The method takes the part before the first string and then sends character by character (number actually) as a remote call. see the readme.

How exactly do you get this working? I modified the bravia.py with my MAC and IP information, updated my configuration.yaml as instructed, but where does the bravia.py file go? I assumed that it would go in the home assistant root directory, with all the other python code (eg. /var/opt/homeassistant/lib) however this was not the case, and my ha log says that it is unable to find the brava component on start.

Any advice would be appreciated.