Browse Source

Added notes on WOL, added parse of device-info

  Added notes on Wake On Lan (WOL) and the ARP cache.

  Added parsing of the device-info query and support adding (ON) or
(OFF) to the main header of the index.html / page.

  Still befuddled by UDP failing to get any responses from roku devices
Apollo 3 years ago
parent
commit
1c9271817e
5 changed files with 113 additions and 53 deletions
  1. 1 1
      CHANNELS_PLUTO.md
  2. 6 0
      NOTES.md
  3. 81 44
      poster.go
  4. 12 6
      templates/index.html
  5. 13 2
      webserver.go

+ 1 - 1
CHANNELS_PLUTO.md

@@ -8,4 +8,4 @@ MST3K - 488
 
 RiffTrax - 489
 
-StarTrel - 150
+StarTrek - 150

+ 6 - 0
NOTES.md

@@ -0,0 +1,6 @@
+
+# Wake On Lan (WOL)
+
+WOL works so long as you don't shut the computer off which is to send the WOL packet. (It must know about the roku device mac-address in order to send the WOL packet to the device)
+
+See `arp -s` for more information

+ 81 - 44
poster.go

@@ -7,7 +7,6 @@ import (
 	"net"
 	"net/http"
 	"os"
-	"strings"
 	"time"
 
 	"github.com/beanzilla/glom"
@@ -23,13 +22,14 @@ func Post(where string) *http.Response {
 	return resp
 }
 
-func ListenToRequests(ln *net.UDPConn, data *string) {
+/*func ListenToRequests(ln *net.UDPConn, data *string) {
 	done := false
+	do_send := true
 	for !done {
 		buffer := make([]byte, 1024) // 1k
 		ln.SetReadDeadline(time.Now().Add(3 * time.Second))
 		for !done {
-			b, err := ln.Read(buffer)
+			b, _, err := ln.ReadFromUDP(buffer)
 			if b > 0 {
 				str := string(buffer[0:])
 				*data += str
@@ -40,61 +40,65 @@ func ListenToRequests(ln *net.UDPConn, data *string) {
 					done = true
 				}
 			}
+
+			if do_send {
+				err = PostFind()
+				if err != nil {
+					log.Printf("POST Find returned %v", err)
+				}
+				do_send = false
+			}
 		}
 	}
-}
+}*/
 
-func PostFind() error {
+func PostFind() {
 	file, err := os.ReadFile("finder.txt")
 	if err != nil {
-		return err
+		log.Print(err)
 	}
 	addr, err := net.ResolveUDPAddr("udp", "239.255.255.250:1900")
 	if err != nil {
-		return err
+		log.Print(err)
 	}
 	conn, err := net.DialUDP("udp", nil, addr)
 	if err != nil {
-		return err
+		log.Print(err)
 	}
 	defer conn.Close()
 	conn.Write(file)
-	return nil
+	log.Println("Posted Request for devices")
 }
 
-func GetFind() []string {
-	var data string
+func GetFind(data *[]string) {
 
 	addr, err := net.ResolveUDPAddr("udp", ":1900")
 	if err != nil {
 		log.Printf("GET Find failed getting port 1900, %v", err)
-		return nil
+		return
 	}
+	fmt.Printf("Listening on %s\r\n", addr.String())
 	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)
+		return
 	}
 
 	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)
+	buffer := make([]byte, 1024) // 1k
+	defer ln.Close()
+	ln.SetReadDeadline(time.Now().Add(3 * time.Second))
+	for {
+		n, adr, err := ln.ReadFromUDP(buffer)
+		str := string(buffer[0:n])
+		fmt.Printf("Got '%s' from %s\r\n", str, adr.String())
+		if err != nil {
+			log.Println(err)
+			break
 		}
+		list = append(list, adr.String())
 	}
-	return list
+	*data = list
 }
 
 func Get(where string) *http.Response {
@@ -122,8 +126,8 @@ func PerformKey(ip, command string) {
 	defer r.Body.Close()
 }
 
-func GetCurrentPlay(ip string) map[string]interface{} {
-	resp := Get(Query(ip, "media-player"))
+func GetQuery(ip, command string) map[string]interface{} {
+	resp := Get(Query(ip, command))
 	if resp == nil {
 		return nil
 	}
@@ -140,28 +144,57 @@ func GetCurrentPlay(ip string) map[string]interface{} {
 	return r
 }
 
-func GetQuery(ip, command string) map[string]interface{} {
-	resp := Get(Query(ip, command))
+func GetCurrentPlay(ip string) map[string]interface{} {
+	resp := GetQuery(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)
+	return resp
+}
+
+func ObtainDeviceInfo(ip string) interface{} {
+	resp := GetQuery(ip, "device-info")
+	if resp == nil {
+		return nil
 	}
-	r := make(map[string]interface{})
-	err = x2j.Unmarshal(body, &r)
+	return resp
+}
+
+func ParseDeviceInfo(dev_info interface{}) map[string]interface{} {
+	dev, err := glom.Glom(dev_info, "device-info.*")
 	if err != nil {
-		log.Printf("Got a error parsing XML, %v", err)
+		log.Print(err)
+		return nil
 	}
-	defer resp.Body.Close()
-	return r
+	data := make(map[string]interface{})
+	keys := glom.GetPossible(dev)
+	for _, key := range keys {
+		val, err := glom.Glom(dev, key)
+		if err != nil {
+			log.Printf("%s -> %v", key, err)
+		} else {
+			data[key] = val
+		}
+	}
+	return data
 }
 
-func GetDevices() []string {
-	str := GetFind()
+// Returns a map of device-infos
+func GetDeviceInfo(ip string) map[string]interface{} {
+	dev := ParseDeviceInfo(ObtainDeviceInfo(ip))
+	/*keys := glom.GetPossible(dev)
+	for _, key := range keys {
+		log.Printf("%s = %v", key, dev[key])
+	}*/
+	return dev
+}
+
+func GetDevices() *[]string {
+	var str []string
+	go GetFind(&str)
+	go PostFind()
 	//fmt.Print(str)
-	return str
+	return &str
 }
 
 func DebugInfo(ip string) string {
@@ -176,6 +209,10 @@ func DebugInfo(ip string) string {
 	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")*/
+	if name == nil && id == nil {
+		name = "TV"
+		id = "No Connection"
+	}
 
 	// Debug print
 	fmt.Printf("Name: %v (%v)\r\n", name, id)

+ 12 - 6
templates/index.html

@@ -10,11 +10,11 @@
     </head>
     <body>
         <div class="grid-x">
-            <h1 class="large-12 medium-12 small-12 cell text-center">goRoku Controller</h1>
+            <h1 id="power" class="large-12 medium-12 small-12 cell text-center">goRoku Controller</h1>
             <div class="large-12 medium-12 small-12 cell text-center">
                 <h3 id="at">TV</h3>
                 <p>
-                    <button type="button" class="alert button" onclick="sendCommandTV('power')">Power</button> <button type="button" class="alert button" onclick="sendCommandTV('display')">Display</button> <button type="button", class="button", onclick="sendTo('/connect')">Connect</button><br>
+                    <button type="button" class="alert button" onclick="sendCommandTV('power')">Power</button> <button type="button" class="alert button" onclick="sendCommandTV('display')">Display</button> <button type="button" class="button" onclick="sendTo('/connect')">Connect</button><br>
                     <button type="button" class="button" onclick="sendCommandTV('back')">Back</button> <button type="button" class="button" onclick="sendCommandTV('home')">Home</button><br>
                     <button type="button" class="button" onclick="sendCommandTV('up')">^</button><br>
                     <button type="button" class="button" onclick="sendCommandTV('left')"><</button> <button type="button" class="success button" onclick="sendCommandTV('ok')">Ok</button> <button type="button" class="button" onclick="sendCommandTV('right')">></button><br>
@@ -42,10 +42,16 @@
         <script>
             function sendCommandTV(cmd) {
                 const xhttp = new XMLHttpRequest();
-                xhttp.onload = function() {}
-                xhttp.onreadystatechange = function() {
-                    if (this.readyState == 4 && this.status == 200 && cmd == "display") {
-                     document.getElementById("at").innerHTML = this.responseText;
+                xhttp.onload = function() {
+                    if (cmd == "display") {
+                        const data = JSON.parse(this.responseText);
+                        console.log(data);
+                        document.getElementById("at").innerHTML = data.at;
+                        if (data.power == true) {
+                            document.getElementById("power").innerHTML = "goRoku Controller (ON)";
+                        } else {
+                            document.getElementById("power").innerHTML = "goRoku Controller (OFF)";
+                        }
                     }
                   };
                 xhttp.open("GET", "tv/"+cmd, true);

+ 13 - 2
webserver.go

@@ -15,6 +15,7 @@ type WebServer struct {
 	Host        string
 	At          string
 	Device_list []string
+	Power       bool
 }
 
 func (w *WebServer) Init(tv, host, port string) {
@@ -56,7 +57,9 @@ func (w *WebServer) connector(c echo.Context) error {
 
 func (w *WebServer) con_refresh(c echo.Context) error {
 	log.Printf("Recieved Connection Refresh")
-	w.Device_list = GetDevices()
+	var list *[]string = GetDevices()
+	//w.Device_list = append(w.Device_list, *list...)
+	w.Device_list = *list
 	log.Printf("Found %d devices", len(w.Device_list))
 	return c.Redirect(http.StatusOK, "/connect")
 }
@@ -99,9 +102,16 @@ func (w *WebServer) tvcmd(c echo.Context) error {
 		PerformKey(w.Tv_ip, "volumemute")
 	case "display":
 		w.At = DebugInfo(w.Tv_ip)
+		device := GetDeviceInfo(w.Tv_ip)
+		if device["power-mode"] == "PowerOn" {
+			w.Power = true
+		} else {
+			w.Power = false
+		}
 		data := make(map[string]interface{})
 		data["at"] = w.At
-		return c.String(http.StatusOK, w.At)
+		data["power"] = w.Power
+		return c.JSON(http.StatusOK, data)
 	case "pluto":
 		Post(fmt.Sprintf("http://%s:8060/launch/%d", w.Tv_ip, 74519))
 	case "pluto-cops":
@@ -124,6 +134,7 @@ func (w *WebServer) tvcmd(c echo.Context) error {
 func main() {
 	web := WebServer{}
 	web.Init("192.168.254.75", "", "8000")
+
 	e := echo.New()
 	e.Use(middleware.Logger())
 	e.Use(middleware.Static("static"))