Skip to content

Instantly share code, notes, and snippets.

@freman
Last active January 12, 2023 04:51
Show Gist options
  • Save freman/8d98742de09d476c4d3d9e5d55f9db63 to your computer and use it in GitHub Desktop.
Save freman/8d98742de09d476c4d3d9e5d55f9db63 to your computer and use it in GitHub Desktop.
Log into grafana on a samsung tv
package main
// NB our grafana has keycloak in front, probably tweak a little (remove a click or two if you're just logging into grafana)
import (
"crypto/tls"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"strconv"
"strings"
"time"
"github.com/fatih/color"
"github.com/gorilla/websocket"
)
const (
username = "user.name"
password = "some password"
// not sure why samsung escapes these slashes, but I'll copy them
grafanahome = `https:\/\/grafana.example.com`
grafanadashboard = `https:\/\/grafnana.example.com\/dashboard`
tv = "10.1.1.5"
)
type chansock struct {
*websocket.Conn
rCh chan interface{}
}
type msgwrap struct {
m []byte
t int
}
func wrapSock(c *websocket.Conn) *chansock {
cs := &chansock{
Conn: c,
rCh: make(chan interface{}),
}
go cs.readPump()
return cs
}
// Can't just use readdeadline because it corrups the socket apparently
func (c *chansock) readPump() {
c.Conn.SetReadLimit(2048)
//c.c.SetpingHandler()
for {
t, msg, err := c.Conn.ReadMessage()
if err != nil {
c.rCh <- err
return
}
c.rCh <- msgwrap{m: msg, t: t}
}
}
func (c *chansock) ReadMessage() (messageType int, p []byte, err error) {
r := <-c.rCh
if err, ok := r.(error); ok && err != nil {
return 0, nil, err
}
msg := r.(msgwrap)
return msg.t, msg.m, nil
}
func (c *chansock) waitFor(event string) {
fmt.Println("Waiting for ", event)
for {
_, msg, err := c.ReadMessage()
if err != nil {
panic("TV broke: " + err.Error())
}
color.HiCyan("TV said: %s", string(msg))
in := struct {
Event string `event`
}{}
if err := json.Unmarshal(msg, &in); err != nil {
panic("JSON broke: " + err.Error())
}
if strings.EqualFold(in.Event, event) {
return
}
}
}
func (c *chansock) nbSleep(t time.Duration) {
deadline := time.Now().Add(t)
for {
select {
case <-time.After(deadline.Sub(time.Now())):
return
case r := <-c.rCh:
if err, ok := r.(error); ok {
panic(err)
}
msg := r.(msgwrap)
color.Cyan("TV said: %s", string(msg.m))
}
}
}
func (c *chansock) send(msg string) {
fmt.Println("I said: ", msg)
c.WriteMessage(websocket.TextMessage, []byte(msg))
}
func ts() string {
return strconv.FormatInt(time.Now().UnixNano()/1000/1000, 10)
}
func main() {
websocket.DefaultDialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
cs, _, err := websocket.DefaultDialer.Dial("wss://"+tv+":8002/api/v2/channels/samsung.remote.control", nil)
if err != nil {
log.Fatal("dial:", err)
}
defer cs.Close()
c := wrapSock(cs)
c.waitFor("ms.channel.connect")
c.send(`{"method":"ms.channel.emit","params":{"event": "ed.apps.launch", "to":"host", "data":{"appId":"org.tizen.browser","action_type":"NATIVE_LAUNCH","metaTag":"` + grafanahome + `"}}}`)
c.waitFor("ed.apps.launch")
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":0,"y":0,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
c.nbSleep(50 * time.Millisecond)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":-1000,"y":-1000,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
c.nbSleep(50 * time.Millisecond)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":-1000,"y":-1000,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
c.nbSleep(50 * time.Millisecond)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":550,"y":330,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
// Allow the page to finish loading and moving
c.nbSleep(8 * time.Second)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"LeftClick","TypeOfRemote":"ProcessMouseDevice"}}`)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":0,"y":0,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
c.nbSleep(50 * time.Millisecond)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":0,"y":100,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
c.nbSleep(50 * time.Millisecond)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":0,"y":140,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
c.nbSleep(3 * time.Second)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"LeftClick","TypeOfRemote":"ProcessMouseDevice"}}`)
c.waitFor("ms.remote.imeStart")
c.waitFor("ms.remote.imeUpdate")
c.waitFor("ms.remote.touchEnable")
c.nbSleep(100 * time.Millisecond)
s := base64.StdEncoding.EncodeToString([]byte(username))
c.send(`{"method":"ms.remote.control","params":{"Cmd":"` + s + `","TypeOfRemote":"SendInputString","DataOfCmd":"base64"}}`)
c.nbSleep(800 * time.Millisecond)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Click","DataOfCmd":"KEY_RETURN","Option":"false","TypeOfRemote":"SendRemoteKey"}}`)
c.waitFor("ms.remote.imeEnd")
c.waitFor("ms.remote.touchEnable")
c.nbSleep(300 * time.Millisecond)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":0,"y":0,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
c.nbSleep(50 * time.Millisecond)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":0,"y":0,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
c.nbSleep(50 * time.Millisecond)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"Move","Position":{"x":0,"y":170,"Time":"` + ts() + `"},"TypeOfRemote":"ProcessMouseDevice"}}`)
c.send(`{"method":"ms.remote.control","params":{"Cmd":"LeftClick","TypeOfRemote":"ProcessMouseDevice"}}`)
c.waitFor("ms.remote.imeStart")
c.waitFor("ms.remote.imeUpdate")
c.waitFor("ms.remote.touchEnable")
c.nbSleep(300 * time.Millisecond)
s = base64.StdEncoding.EncodeToString([]byte(password))
c.send(`{"method":"ms.remote.control","params":{"Cmd":"` + s + `","TypeOfRemote":"SendInputString","DataOfCmd":"base64"}}`)
c.send(`{"method":"ms.remote.control","params":{"TypeOfRemote":"SendInputEnd"}}`)
c.waitFor("ms.remote.imeEnd")
c.waitFor("ms.remote.touchEnable")
c.nbSleep(1 * time.Second)
c.send(`{"method":"ms.channel.emit","params":{"event": "ed.apps.launch", "to":"host", "data":{"appId":"org.tizen.browser","action_type":"NATIVE_LAUNCH","metaTag":"` + grafanadashboard + `"}}}`)
}
@nadgobpx01
Copy link

Hi , any hint on how to use this code?

@cHicHillo
Copy link

Hi!, do you know where I can find documentation on the available methods (ms.channel.emit) and events (ed.apps.launch)?

thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment