374 lines
9.8 KiB
Go
374 lines
9.8 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/move", s.handleRotatorMove)
|
|
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/tune", s.handleTunerAutoTune)
|
|
mux.HandleFunc("/api/tuner/antenna", s.handleTunerAntenna)
|
|
|
|
// Antenna Genius endpoints
|
|
mux.HandleFunc("/api/antenna/set", s.handleAntennaSet)
|
|
|
|
// Power Genius endpoints
|
|
mux.HandleFunc("/api/power/fanmode", s.handlePowerFanMode)
|
|
|
|
// Static files (will be frontend)
|
|
mux.Handle("/", http.FileServer(http.Dir("./web/dist")))
|
|
|
|
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) handleRotatorMove(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Rotator int `json:"rotator"`
|
|
Azimuth int `json:"azimuth"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.RotatorGenius().MoveToAzimuth(req.Rotator, req.Azimuth); 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
|
|
}
|
|
|
|
rotator, err := strconv.Atoi(r.URL.Query().Get("rotator"))
|
|
if err != nil || rotator < 1 || rotator > 2 {
|
|
http.Error(w, "Invalid rotator number", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.RotatorGenius().RotateCW(rotator); 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
|
|
}
|
|
|
|
rotator, err := strconv.Atoi(r.URL.Query().Get("rotator"))
|
|
if err != nil || rotator < 1 || rotator > 2 {
|
|
http.Error(w, "Invalid rotator number", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := s.deviceManager.RotatorGenius().RotateCCW(rotator); 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 {
|
|
Operate bool `json:"operate"`
|
|
}
|
|
|
|
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.Operate); 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"})
|
|
}
|
|
|
|
func (s *Server) handleTunerAntenna(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
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.TunerGenius().ActivateAntenna(req.Antenna); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
s.sendJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
// Antenna Genius handlers
|
|
func (s *Server) handleAntennaSet(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Radio int `json:"radio"`
|
|
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().SetRadioAntenna(req.Radio, req.Antenna); 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) sendJSON(w http.ResponseWriter, data interface{}) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(data)
|
|
}
|