Files
odroid-power-mate/example/powermate-console-frontend/api.go
YoungSoo Shin a7ed7f9df2 WIP
Signed-off-by: YoungSoo Shin <shinys000114@gmail.com>
(cherry picked from commit 61db696a03)
2025-12-16 06:08:22 +09:00

178 lines
5.0 KiB
Go

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)
}
}