package main import ( "encoding/json" "net/http" "time" ) type hostAPIResponse struct { Transport string `json:"transport"` Endpoint string `json:"endpoint"` Connected bool `json:"connected"` BleName string `json:"ble_name,omitempty"` Error string `json:"error,omitempty"` } type hostConnectRequest struct { Transport string `json:"transport"` // uart|ble Port string `json:"port,omitempty"` Address string `json:"address,omitempty"` Name string `json:"name,omitempty"` Baud int `json:"baud,omitempty"` } type bleScanRequest struct { TimeoutS float64 `json:"timeout_s"` NameFilter string `json:"name_filter"` } type bleScanResponse struct { Devices []bleScanDevice `json:"devices"` Error string `json:"error,omitempty"` } type serialPortsResponse struct { Ports []string `json:"ports"` Error string `json:"error,omitempty"` } func mountHostAPI(mux *http.ServeMux, link *managedHost) { mux.HandleFunc("/api/host", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } st := link.HostStatus() writeJSON(w, http.StatusOK, hostAPIResponse{ Transport: st["transport"].(string), Endpoint: st["endpoint"].(string), Connected: st["connected"].(bool), BleName: st["ble_name"].(string), }) }) mux.HandleFunc("/api/host/connect", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } var body hostConnectRequest if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeJSON(w, http.StatusBadRequest, hostAPIResponse{Error: "invalid JSON"}) return } var err error switch body.Transport { case hostTransportUART: err = link.ConnectUART(body.Port, body.Baud) case hostTransportBLE: err = link.ConnectBLE(body.Address, body.Name) default: writeJSON(w, http.StatusBadRequest, hostAPIResponse{Error: "transport must be uart or ble"}) return } st := link.HostStatus() resp := hostAPIResponse{ Transport: st["transport"].(string), Endpoint: st["endpoint"].(string), Connected: st["connected"].(bool), BleName: st["ble_name"].(string), } if err != nil { resp.Error = err.Error() resp.Connected = false writeJSON(w, http.StatusBadRequest, resp) return } writeJSON(w, http.StatusOK, resp) }) mux.HandleFunc("/api/host/disconnect", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } _ = link.Disconnect() st := link.HostStatus() writeJSON(w, http.StatusOK, hostAPIResponse{ Transport: st["transport"].(string), Endpoint: st["endpoint"].(string), Connected: false, }) }) mux.HandleFunc("/api/serial/ports", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } ports, err := listSerialPorts() if err != nil { writeJSON(w, http.StatusOK, serialPortsResponse{Ports: []string{}, Error: err.Error()}) return } writeJSON(w, http.StatusOK, serialPortsResponse{Ports: ports}) }) mux.HandleFunc("/api/ble/scan", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } var body bleScanRequest _ = json.NewDecoder(r.Body).Decode(&body) timeout := time.Duration(body.TimeoutS * float64(time.Second)) if timeout <= 0 { timeout = 5 * time.Second } // Disconnect active BLE before scan (BlueZ adapter contention). if link.Transport() == hostTransportBLE { _ = link.Disconnect() } devs, err := scanBLEDevices(timeout, body.NameFilter) if err != nil { writeJSON(w, http.StatusOK, bleScanResponse{Devices: []bleScanDevice{}, Error: err.Error()}) return } writeJSON(w, http.StatusOK, bleScanResponse{Devices: devs}) }) }