summaryrefslogtreecommitdiff
path: root/tools/websocket.go
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2026-03-25 21:37:20 +0100
committerxengineering <me@xengineering.eu>2026-03-25 21:37:20 +0100
commit4bc67b734dc8c90dd4679877e8825da32e67b7eb (patch)
treefc4b97bdb6b91caff22b771bb9d8f5ca64791772 /tools/websocket.go
parent7afbc98e6d715eef8809beb9793ccf5096104e26 (diff)
parent6001997a66c4c4b12e9d8b0853fef0fc0ff14768 (diff)
downloadsia-server-4bc67b734dc8c90dd4679877e8825da32e67b7eb.tar
sia-server-4bc67b734dc8c90dd4679877e8825da32e67b7eb.tar.zst
sia-server-4bc67b734dc8c90dd4679877e8825da32e67b7eb.zip
Merge branch 'shelly'
This adds basic support for Shelly 2PM Gen3 devices.
Diffstat (limited to 'tools/websocket.go')
-rw-r--r--tools/websocket.go158
1 files changed, 158 insertions, 0 deletions
diff --git a/tools/websocket.go b/tools/websocket.go
new file mode 100644
index 0000000..af23959
--- /dev/null
+++ b/tools/websocket.go
@@ -0,0 +1,158 @@
+// Websocket debug tool
+//
+// Usage: ./websocket-linux-amd64 ws://<shelly-ip>/rpc
+//
+// This tools is intended to support development of the Websocket-based
+// application programming interface (API) of the Shelly Internet of Things
+// (IoT) devices.
+
+package main
+
+import (
+ "encoding/json"
+ "log"
+ "net/url"
+ "os"
+ "os/signal"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+func main() {
+ log.SetFlags(0)
+
+ interrupt := make(chan os.Signal, 1)
+ signal.Notify(interrupt, os.Interrupt)
+
+ var u url.URL = getURL()
+ log.Printf("connecting to %s", u.String())
+
+ c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer c.Close()
+
+ go rx(c)
+
+ getConfig(c)
+ coverClose(c)
+ time.Sleep(1 * time.Second)
+ coverOpen(c)
+
+ Await(syscall.SIGTERM, syscall.SIGINT)
+}
+
+func getURL() url.URL {
+ if len(os.Args) != 2 {
+ log.Fatalf("Exactly one argument expected but got %d.", len(os.Args) - 1)
+ }
+
+ maybeURL, err := url.Parse(os.Args[1])
+ if err != nil {
+ log.Fatalf("Cannot parse given URL: %s", os.Args[1])
+ }
+
+ return *maybeURL
+}
+
+func Await(signals ...os.Signal) {
+ listener := make(chan os.Signal, 1)
+ signal.Notify(listener, signals...)
+ defer signal.Stop(listener)
+
+ sig := <-listener
+ log.Printf("Received OS signal '%v'\n", sig)
+}
+
+func getConfig(c *websocket.Conn) {
+ tx(c, `
+{
+ "jsonrpc":"2.0",
+ "id": 1,
+ "src":"user_1",
+ "method":"Sys.GetConfig",
+ "params": {
+ "id":2
+ }
+}
+`)
+}
+
+func coverClose(c *websocket.Conn) {
+ tx(c, `
+{
+ "jsonrpc":"2.0",
+ "id": 1,
+ "src":"user_1",
+ "method":"Cover.Close",
+ "params": {
+ "id":0
+ }
+}
+`)
+}
+
+func coverOpen(c *websocket.Conn) {
+ tx(c, `
+{
+ "jsonrpc":"2.0",
+ "id": 1,
+ "src":"user_1",
+ "method":"Cover.Open",
+ "params": {
+ "id":0
+ }
+}
+`)
+}
+
+func rx(c *websocket.Conn) {
+ for {
+ _, message, err := c.ReadMessage()
+ if err != nil {
+ log.Println("read:", err)
+ return
+ }
+ log.Println("")
+ log.Println(quote(prettify(string(message)), "< "))
+ }
+}
+
+func tx(c *websocket.Conn, d string) {
+ log.Println(quote(prettify(d), "> "))
+
+ err := c.WriteMessage(websocket.TextMessage, []byte(d))
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func prettify(input string) string {
+ var parsed any
+
+ err := json.Unmarshal([]byte(input), &parsed)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ pretty, err := json.MarshalIndent(parsed, "", " ")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ return string(pretty)
+}
+
+func quote(input string, quotation string) string {
+ lines := strings.Split(input, "\n")
+
+ for i, line := range lines {
+ lines[i] = quotation + line
+ }
+
+ return strings.Join(lines, "\n")
+}