Petter Holt Juliussen • Mail | Mastodon | GitHub | Letterboxd

for later reference.

Samsung

2019-04-04

http://192.168.1.2:8001/api/v2/ http://192.168.1.2:8001/ws/apps/Netflix

Applications

http://192.168.1.2:8001/api/v2/applications/111299001912

{
    id: "111299001912",
    name: "YouTube",
    running: true,
    version: "2.1.491",
    visible: false
}
wget -x POST http://IP:8001/api/v2/applications/111299001912

https://github.com/shantanugoel/samsung-messagebox/blob/master/samsung-msgbox.py https://github.com/Ape/samsungctl/issues/22 https://gist.github.com/YuukanOO/00b5edefd0446af43f9abfe23fc1a86d https://github.com/Ape/samsungctl/issues/75 https://github.com/tavicu/homebridge-samsung-tizen/blob/master/lib/methods/ws.js

Websocket

import ssl
import json
import time
import base64
import websocket

URL_FORMAT = "wss://{}:{}/api/v2/channels/samsung.remote.control?name={}"
HOST = '192.168.1.2'
PORT = 8002
NAME = 'test'
TIMEOUT = None
KEY_INTERVAL = 0.5
TOKEN = ''

def serialize_string(string):
    if isinstance(string, str):
        string = str.encode(string)
    return base64.b64encode(string).decode("utf-8")

def read_response(con):
    response = json.loads(con.recv())
    print(json.dumps(response, indent=4))

    if response['event'] != 'ms.channel.connect':
        con.close()
        # Unhandled response
    # Access granted
    try:
        if response['data']['token']:
            print(response['data']['token'])
    except:
        pass


def apps(con):
    payload = json.dumps({
        "method": "ms.channel.emit",
        "params": {
            "event": "ed.installedApp.get", 
            "to": "host"
        }
    })

    con.send(payload)
    read_response(con)

def launch(con, app_id, app_type):
    app_types = {4: 'NATIVE_LAUNCH', 2: 'DEEP_LINK'}
    if app_type not in app_types:
        return

    payload = json.dumps({
        "method": "ms.channel.emit",
        "params":{
            "event": "ed.apps.launch", 
            "to": "host", 
            "data": {
                "appId": app_id, 
                "action_type": app_types.get(app_type)
            }
        }
    })

    con.send(payload)
    read_response(con)

def remote(con, key):
    payload = json.dumps({
        "method": "ms.remote.control",
        "params": {
            "Cmd": "Click",
            "DataOfCmd": key,
            "Option": "false",
            "TypeOfRemote": "SendRemoteKey"
        }
    })

    con.send(payload)
    time.sleep(KEY_INTERVAL)


url = URL_FORMAT.format(HOST, PORT, serialize_string(NAME))
url = url + '&token=%s' % (TOKEN) if TOKEN else url
con = websocket.create_connection(url, TIMEOUT, sslopt={"cert_reqs": ssl.CERT_NONE})
read_response(con)

# control(con, 'KEY_MENU')
# apps(con)
# launch(con, 'org.tizen.browser', 4)
launch(con, '111299001912', 2) # YouTube
# launch(con, '111477001221', 2) # NRK TV

# {"method":"ms.channel.emit","params":{"event": "ed.apps.launch", "to":"host", "data":{"appId":"org.tizen.browser","action_type":"NATIVE_LAUNCH","metaTag":"http:\/\/hackaday.com"}}}
# {"method":"ms.remote.control","params":{"Cmd":"$BASE64ENCODEDSTRING$","TypeOfRemote":"SendInputString","DataOfCmd":"base64"}}

Key codes

The list of accepted keys may vary depending on the TV model, but the following list has some common key codes and their descriptions.

Key code Description
KEY_POWEROFF Power off
KEY_UP Up
KEY_DOWN Down
KEY_LEFT Left
KEY_RIGHT Right
KEY_CHUP P Up
KEY_CHDOWN P Down
KEY_ENTER Enter
KEY_RETURN Return
KEY_CH_LIST Channel List
KEY_MENU Menu
KEY_SOURCE Source
KEY_GUIDE Guide
KEY_TOOLS Tools
KEY_INFO Info
KEY_RED A / Red
KEY_GREEN B / Green
KEY_YELLOW C / Yellow
KEY_BLUE D / Blue
KEY_PANNEL_CHDOWN 3D
KEY_VOLUP Volume Up
KEY_VOLDOWN Volume Down
KEY_MUTE Mute
KEY_0 0
KEY_1 1
KEY_2 2
KEY_3 3
KEY_4 4
KEY_5 5
KEY_6 6
KEY_7 7
KEY_8 8
KEY_9 9
KEY_DTV TV Source
KEY_HDMI HDMI Source
KEY_CONTENTS SmartHub

Please note that some codes are different on the 2016+ TVs. For example, KEY_POWEROFF is KEY_POWER on the newer TVs.