package main import ( "fmt" "io" "log" "net" "net/http" "os" "strings" "time" "github.com/beanzilla/glom" "github.com/beego/x2j" ) // https://zetcode.com/golang/getpostrequest/ post_req_form.go func Post(where string) *http.Response { resp, err := http.PostForm(where, nil) if err != nil { log.Printf("POST got non 200 code, %v", err) } return resp } func ListenToRequests(ln *net.UDPConn, data *string) { done := false for !done { buffer := make([]byte, 1024) // 1k ln.SetReadDeadline(time.Now().Add(3 * time.Second)) for !done { b, err := ln.Read(buffer) if b > 0 { str := string(buffer[0:]) *data += str } if err != nil { if _, ok := err.(net.Error); !ok { log.Print(err) done = true } } } } } func PostFind() error { file, err := os.ReadFile("finder.txt") if err != nil { return err } addr, err := net.ResolveUDPAddr("udp", "239.255.255.250:1900") if err != nil { return err } conn, err := net.DialUDP("udp", nil, addr) if err != nil { return err } defer conn.Close() conn.Write(file) return nil } func GetFind() []string { var data string addr, err := net.ResolveUDPAddr("udp", ":1900") if err != nil { log.Printf("GET Find failed getting port 1900, %v", err) return nil } ln, err := net.ListenUDP("udp", addr) if err != nil { log.Printf("GET Find started listener for 1900, %v", err) return nil } go ListenToRequests(ln, &data) err = PostFind() if err != nil { log.Printf("POST Find returned %v", err) } var list []string data_list := strings.Split(data, "\n") for _, line := range data_list { if strings.Contains(line, "Location") { str := strings.ReplaceAll(strings.ToLower(line), "location: ", "") str = strings.ReplaceAll(str, "http://", "") str = strings.ReplaceAll(str, ":8060/", "") // Location: http://192.168.1.134:8060/ list = append(list, str) } } return list } func Get(where string) *http.Response { resp, err := http.Get(where) if err != nil { log.Printf("GET got non 200 code, %v", err) return nil } else { return resp } } // https://developer.roku.com/docs/developer-program/debugging/external-control-api.md#keypress-key-values codes func KeyPress(ip, command string) string { return fmt.Sprintf("http://%s:8060/keypress/%s", ip, command) } // https://developer.roku.com/docs/developer-program/debugging/external-control-api.md#general-ecp-commands func Query(ip, what string) string { return fmt.Sprintf("http://%s:8060/query/%s", ip, what) } func PerformKey(ip, command string) { r := Post(KeyPress(ip, command)) defer r.Body.Close() } func GetCurrentPlay(ip string) map[string]interface{} { resp := Get(Query(ip, "media-player")) if resp == nil { return nil } body, err := io.ReadAll(resp.Body) if err != nil { log.Printf("Reading from body got error, %v", err) } r := make(map[string]interface{}) err = x2j.Unmarshal(body, &r) if err != nil { log.Printf("Got a error parsing XML, %v", err) } defer resp.Body.Close() return r } func GetQuery(ip, command string) map[string]interface{} { resp := Get(Query(ip, command)) if resp == nil { return nil } body, err := io.ReadAll(resp.Body) if err != nil { log.Printf("Reading from body got error, %v", err) } r := make(map[string]interface{}) err = x2j.Unmarshal(body, &r) if err != nil { log.Printf("Got a error parsing XML, %v", err) } defer resp.Body.Close() return r } func GetDevices() []string { str := GetFind() //fmt.Print(str) return str } func DebugInfo(ip string) string { // Query the current playing thing, and get the tv channels r := GetQuery(ip, "media-player") if r == nil { return "" } name, _ := glom.Glom(r, "player.plugin.-name") id, _ := glom.Glom(r, "player.plugin.-id") /*r1 := GetQuery(ip, "tv-channels") chan_name, _ := glom.Glom(r1, "tv-channels.channel.name") chan_phy_id, _ := glom.Glom(r1, "tv-channels.channel.physical-channel") chan_number, _ := glom.Glom(r1, "tv-channels.channel.number")*/ // Debug print fmt.Printf("Name: %v (%v)\r\n", name, id) //fmt.Printf("Channel: %v (%v / %v)\r\n", chan_name, chan_phy_id, chan_number) return fmt.Sprintf("%v (%v)", name, id) } func examplePost() { keys := GetKeys() _ = keys // Keep alive PerformKey("192.168.254.75", keys["mute"]) //Post("http://192.168.254.75:8060/launch/74519") //PerformKey("192.168.254.75", keys["home"]) /* //resp := Post("http://192.168.254.75:8060/keypress/volumeMute") //resp := Post(KeyPress("192.168.254.75", "volumeMute")) //resp := Get(Query("192.168.254.75", "device-info")) //resp := GetQuery("192.168.254.75", "tv-active-channel") resp := GetQuery("192.168.254.75", "tv-channels") //result, err := glom.Glom(resp, "tv-channels.channel") _, err := glom.Glom(resp, "*") if err != nil { log.Println(err) } else { //fmt.Println(result) fmt.Println(glom.GetPossible(result)) } */ // Query the current playing thing, and get the tv channels r := GetQuery("192.168.254.75", "media-player") name, _ := glom.Glom(r, "player.plugin.-name") id, _ := glom.Glom(r, "player.plugin.-id") r1 := GetQuery("192.168.254.75", "tv-channels") chan_name, _ := glom.Glom(r1, "tv-channels.channel.name") chan_phy_id, _ := glom.Glom(r1, "tv-channels.channel.physical-channel") chan_number, _ := glom.Glom(r1, "tv-channels.channel.number") // Debug print fmt.Printf("Name: %v (%v)\r\n", name, id) fmt.Printf("Channel: %v (%v / %v)\r\n", chan_name, chan_phy_id, chan_number) }