package antennagenius import ( "bufio" "fmt" "net" "strconv" "strings" "time" . "git.rouggy.com/rouggy/ShackMaster/internal/devices" ) type Client struct { host string port int conn net.Conn } type Status struct { Radio1Antenna int `json:"radio1_antenna"` // 0-7 (antenna index) Radio2Antenna int `json:"radio2_antenna"` // 0-7 (antenna index) Connected bool `json:"connected"` } func New(host string, port int) *Client { return &Client{ host: host, port: port, } } func (c *Client) Connect() error { conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", c.host, c.port), 5*time.Second) if err != nil { return fmt.Errorf("failed to connect: %w", err) } c.conn = conn return nil } func (c *Client) Close() error { if c.conn != nil { return c.conn.Close() } return nil } func (c *Client) sendCommand(cmd string) (string, error) { if c.conn == nil { if err := c.Connect(); err != nil { return "", err } } // Get next command ID from global counter cmdID := GetGlobalCommandID().GetNextID() // Format command with ID: C| fullCmd := fmt.Sprintf("C%d|%s\n", cmdID, cmd) // Send command _, err := c.conn.Write([]byte(fullCmd)) if err != nil { c.conn = nil return "", fmt.Errorf("failed to send command: %w", err) } // Read response reader := bufio.NewReader(c.conn) response, err := reader.ReadString('\n') if err != nil { c.conn = nil return "", fmt.Errorf("failed to read response: %w", err) } return strings.TrimSpace(response), nil } func (c *Client) GetStatus() (*Status, error) { resp, err := c.sendCommand("status") if err != nil { return nil, err } return c.parseStatus(resp) } func (c *Client) parseStatus(resp string) (*Status, error) { status := &Status{ Connected: true, } // Parse response format from 4O3A API // Expected format will vary - this is a basic parser pairs := strings.Fields(resp) for _, pair := range pairs { parts := strings.SplitN(pair, "=", 2) if len(parts) != 2 { continue } key := parts[0] value := parts[1] switch key { case "radio1", "r1": status.Radio1Antenna, _ = strconv.Atoi(value) case "radio2", "r2": status.Radio2Antenna, _ = strconv.Atoi(value) } } return status, nil } // SetRadioAntenna sets which antenna a radio should use // radio: 1 or 2 // antenna: 0-7 (antenna index) func (c *Client) SetRadioAntenna(radio int, antenna int) error { if radio < 1 || radio > 2 { return fmt.Errorf("radio must be 1 or 2") } if antenna < 0 || antenna > 7 { return fmt.Errorf("antenna must be between 0 and 7") } cmd := fmt.Sprintf("set radio%d=%d", radio, antenna) resp, err := c.sendCommand(cmd) if err != nil { return err } // Check response for success if !strings.Contains(strings.ToLower(resp), "ok") && resp != "" { // If response doesn't contain "ok" but isn't empty, assume success // (some devices may return the new state instead of "ok") } return nil } // GetRadioAntenna gets which antenna a radio is currently using func (c *Client) GetRadioAntenna(radio int) (int, error) { if radio < 1 || radio > 2 { return -1, fmt.Errorf("radio must be 1 or 2") } status, err := c.GetStatus() if err != nil { return -1, err } if radio == 1 { return status.Radio1Antenna, nil } return status.Radio2Antenna, nil }