package main import ( "bytes" "encoding/json" "fmt" "net/http" "time" tea "github.com/charmbracelet/bubbletea" "github.com/gorilla/websocket" "google.golang.org/protobuf/proto" "odroid-tui/pb" ) func scanWifi(d ConnectionDetails) tea.Cmd { return func() tea.Msg { url := fmt.Sprintf("http://%s/api/wifi/scan", d.IP) req, _ := http.NewRequest("GET", url, nil) req.Header.Set("Authorization", "Bearer "+d.Token) client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() var list []WifiAP if err := json.NewDecoder(resp.Body).Decode(&list); err != nil { return err } return WifiScanListMsg(list) } } func fetchSettings(d ConnectionDetails) tea.Cmd { return func() tea.Msg { url := fmt.Sprintf("http://%s/api/setting", d.IP) req, _ := http.NewRequest("GET", url, nil) req.Header.Set("Authorization", "Bearer "+d.Token) client := &http.Client{Timeout: 3 * time.Second} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() var cfg SettingsPayload if err := json.NewDecoder(resp.Body).Decode(&cfg); err != nil { return err } return SettingsFetchedMsg(cfg) } } func saveSettingsMap(d ConnectionDetails, payload interface{}) tea.Cmd { return func() tea.Msg { url := fmt.Sprintf("http://%s/api/setting", d.IP) jsonBytes, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonBytes)) req.Header.Set("Authorization", "Bearer "+d.Token) req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Do(req) if err != nil { return fmt.Errorf("Save Failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("Save Error: %d", resp.StatusCode) } return ActionDoneMsg{} } } func rebootDevice(d ConnectionDetails) tea.Cmd { return func() tea.Msg { url := fmt.Sprintf("http://%s/api/reboot", d.IP) req, _ := http.NewRequest("POST", url, nil) req.Header.Set("Authorization", "Bearer "+d.Token) client := &http.Client{Timeout: 2 * time.Second} client.Do(req) return ActionDoneMsg{} } } func performLogin(d ConnectionDetails) tea.Cmd { return func() tea.Msg { url := fmt.Sprintf("http://%s/login", d.IP) payload := map[string]string{"username": d.Username, "password": d.Password} jsonBytes, _ := json.Marshal(payload) client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Post(url, "application/json", bytes.NewBuffer(jsonBytes)) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("Login Error: %d", resp.StatusCode) } var res map[string]string json.NewDecoder(resp.Body).Decode(&res) return LoginSuccessMsg(res["token"]) } } func fetchInitialState(d ConnectionDetails) tea.Cmd { return func() tea.Msg { url := fmt.Sprintf("http://%s/api/control", d.IP) req, _ := http.NewRequest("GET", url, nil) req.Header.Set("Authorization", "Bearer "+d.Token) client := &http.Client{Timeout: 3 * time.Second} resp, err := client.Do(req) if err != nil { return nil } defer resp.Body.Close() var res InitApiResponse json.NewDecoder(resp.Body).Decode(&res) return InitStateMsg{MainOn: res.Load12vOn, UsbOn: res.Load5vOn} } } func toggleLoad(d ConnectionDetails, key string, state bool) tea.Cmd { return func() tea.Msg { url := fmt.Sprintf("http://%s/api/control", d.IP) payload := map[string]interface{}{key: state} jsonBytes, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonBytes)) req.Header.Set("Authorization", "Bearer "+d.Token) req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 1 * time.Second} client.Do(req) return nil } } func connectWebSocket(d ConnectionDetails) tea.Cmd { return func() tea.Msg { u := fmt.Sprintf("ws://%s/ws?token=%s", d.IP, d.Token) conn, _, err := websocket.DefaultDialer.Dial(u, nil) if err != nil { return err } return waitForWebSocketMessage(conn)() } } func waitForWebSocketMessage(conn *websocket.Conn) tea.Cmd { return func() tea.Msg { _, message, err := conn.ReadMessage() if err != nil { return err } var status pb.StatusMessage if err := proto.Unmarshal(message, &status); err == nil { conv := func(s *pb.SensorChannelData) *PowerMetrics { if s == nil { return nil } return &PowerMetrics{Voltage: float64(s.GetVoltage()), Current: float64(s.GetCurrent()), Power: float64(s.GetPower())} } msg := WsPartialMsg{Conn: conn} if s := status.GetSensorData(); s != nil { msg.Vin = conv(s.GetVin()) msg.Main = conv(s.GetMain()) msg.Usb = conv(s.GetUsb()) } if w := status.GetWifiStatus(); w != nil { msg.Wifi = &WifiInfo{SSID: w.GetSsid(), RSSI: int(w.GetRssi())} } if sw := status.GetSwStatus(); sw != nil { msg.Sw = &SwitchInfo{MainOn: sw.GetMain(), UsbOn: sw.GetUsb()} } return msg } return waitForWebSocketMessage(conn) } }