399 lines
10 KiB
Go
399 lines
10 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"git.rouggy.com/rouggy/ShackMaster/internal/config"
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
type Server struct {
|
|
deviceManager *DeviceManager
|
|
hub *Hub
|
|
config *config.Config
|
|
upgrader websocket.Upgrader
|
|
}
|
|
|
|
func NewServer(dm *DeviceManager, hub *Hub, cfg *config.Config) *Server {
|
|
return &Server{
|
|
deviceManager: dm,
|
|
hub: hub,
|
|
config: cfg,
|
|
upgrader: websocket.Upgrader{
|
|
ReadBufferSize: 1024,
|
|
WriteBufferSize: 1024,
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
return true // Allow all origins for now
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (s *Server) SetupRoutes() *http.ServeMux {
|
|
mux := http.NewServeMux()
|
|
|
|
// WebSocket endpoint
|
|
mux.HandleFunc("/ws", s.handleWebSocket)
|
|
|
|
// REST API endpoints
|
|
mux.HandleFunc("/api/status", s.handleGetStatus)
|
|
mux.HandleFunc("/api/config", s.handleGetConfig)
|
|
|
|
// WebSwitch endpoints
|
|
mux.HandleFunc("/api/webswitch/relay/on", s.handleWebSwitchRelayOn)
|
|
mux.HandleFunc("/api/webswitch/relay/off", s.handleWebSwitchRelayOff)
|
|
mux.HandleFunc("/api/webswitch/all/on", s.handleWebSwitchAllOn)
|
|
mux.HandleFunc("/api/webswitch/all/off", s.handleWebSwitchAllOff)
|
|
|
|
// Rotator endpoints
|
|
mux.HandleFunc("/api/rotator/heading", s.handleRotatorHeading)
|
|
mux.HandleFunc("/api/rotator/cw", s.handleRotatorCW)
|
|
mux.HandleFunc("/api/rotator/ccw", s.handleRotatorCCW)
|
|
mux.HandleFunc("/api/rotator/stop", s.handleRotatorStop)
|
|
|
|
// Tuner endpoints
|
|
mux.HandleFunc("/api/tuner/operate", s.handleTunerOperate)
|
|
mux.HandleFunc("/api/tuner/bypass", s.handleTunerBypass)
|
|
mux.HandleFunc("/api/tuner/autotune", s.handleTunerAutoTune)
|
|
|
|
// Antenna Genius endpoints
|
|
mux.HandleFunc("/api/antenna/select", s.handleAntennaSelect)
|
|
mux.HandleFunc("/api/antenna/reboot", s.handleAntennaReboot)
|
|
|
|
// Power Genius endpoints
|
|
mux.HandleFunc("/api/power/fanmode", s.handlePowerFanMode)
|
|
mux.HandleFunc("/api/power/operate", s.handlePowerOperate)
|
|
|
|
// Note: Static files are now served from embedded FS in main.go
|
|
|
|
return mux
|
|
}
|
|
|
|
func (s *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
|
conn, err := s.upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
log.Printf("WebSocket upgrade error: %v", err)
|
|
return
|
|
}
|
|
|
|
ServeWs(s.hub, conn)
|
|
}
|
|
|
|
func (s *Server) handleGetStatus(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
status := s.deviceManager.GetStatus()
|
|
s.sendJSON(w, status)
|
|
}
|
|
|
|
func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
// Only send public config info (not API keys)
|
|
configInfo := map[string]interface{}{
|
|
"callsign": s.config.Location.Callsign,
|
|
"location": map[string]float64{
|
|
"latitude": s.config.Location.Latitude,
|
|
"longitude": s.config.Location.Longitude,
|
|
},
|
|
}
|
|
|
|
s.sendJSON(w, configInfo)
|
|
}
|
|
|
|
// WebSwitch handlers
|
|
func (s *Server) handleWebSwitchRelayOn(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
relay, err := strconv.Atoi(r.URL.Query().Get("relay"))
|
|
if err != nil || relay < 1 || relay > 5 {
|
|
http.Error(w, "Invalid relay number", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.WebSwitch().TurnOn(relay); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handleWebSwitchRelayOff(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
relay, err := strconv.Atoi(r.URL.Query().Get("relay"))
|
|
if err != nil || relay < 1 || relay > 5 {
|
|
http.Error(w, "Invalid relay number", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.WebSwitch().TurnOff(relay); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handleWebSwitchAllOn(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.WebSwitch().AllOn(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handleWebSwitchAllOff(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.WebSwitch().AllOff(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
// Rotator handlers
|
|
func (s *Server) handleRotatorHeading(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Heading int `json:"heading"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.RotatorGenius().SetHeading(req.Heading); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handleRotatorCW(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.RotatorGenius().RotateCW(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handleRotatorCCW(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.RotatorGenius().RotateCCW(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handleRotatorStop(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.RotatorGenius().Stop(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
// Tuner handlers
|
|
func (s *Server) handleTunerOperate(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Value int `json:"value"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.TunerGenius().SetOperate(req.Value); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handleTunerBypass(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Value int `json:"value"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.TunerGenius().SetBypass(req.Value); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handleTunerAutoTune(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.TunerGenius().AutoTune(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
// Antenna Genius handlers
|
|
func (s *Server) handleAntennaSelect(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Port int `json:"port"`
|
|
Antenna int `json:"antenna"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.AntennaGenius().SetAntenna(req.Port, req.Antenna); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handleAntennaReboot(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.AntennaGenius().Reboot(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
// Power Genius handlers
|
|
func (s *Server) handlePowerFanMode(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Mode string `json:"mode"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.PowerGenius().SetFanMode(req.Mode); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) handlePowerOperate(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Value int `json:"value"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.PowerGenius().SetOperate(req.Value); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func (s *Server) sendJSON(w http.ResponseWriter, data interface{}) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(data)
|
|
}
|