package webswitch import ( "fmt" "io" "net/http" "strconv" "strings" "time" ) type Client struct { host string httpClient *http.Client } type Status struct { Relays []RelayState `json:"relays"` Connected bool `json:"connected"` } type RelayState struct { Number int `json:"number"` State bool `json:"state"` } func New(host string) *Client { return &Client{ host: host, httpClient: &http.Client{ Timeout: 5 * time.Second, }, } } func (c *Client) SetRelay(relay int, state bool) error { if relay < 1 || relay > 5 { return fmt.Errorf("relay number must be between 1 and 5") } action := "off" if state { action = "on" } url := fmt.Sprintf("http://%s/relaycontrol/%s/%d", c.host, action, relay) resp, err := c.httpClient.Get(url) if err != nil { return fmt.Errorf("failed to control relay: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body)) } return nil } func (c *Client) TurnOn(relay int) error { return c.SetRelay(relay, true) } func (c *Client) TurnOff(relay int) error { return c.SetRelay(relay, false) } func (c *Client) AllOn() error { // Sequence for ALL ON: // 1. Turn on relays 1, 2, 3, 5 immediately // 2. Wait 5 seconds // 3. Turn on relay 4 (Flex Radio Start) // Turn on relays 1, 2, 3, 5 for _, relay := range []int{1, 2, 3, 5} { if err := c.TurnOn(relay); err != nil { return fmt.Errorf("failed to turn on relay %d: %w", relay, err) } } // Wait 5 seconds for power supply to stabilize time.Sleep(5 * time.Second) // Turn on relay 4 (Flex Radio) if err := c.TurnOn(4); err != nil { return fmt.Errorf("failed to turn on relay 4: %w", err) } return nil } func (c *Client) AllOff() error { // Sequence for ALL OFF: // 1. Turn off relay 4 (Flex Radio) immediately // 2. Turn off relays 2, 3, 5 immediately // 3. Wait 35 seconds for Flex Radio to shut down // 4. Turn off relay 1 (Power Supply) // Turn off relay 4 (Flex Radio) if err := c.TurnOff(4); err != nil { return fmt.Errorf("failed to turn off relay 4: %w", err) } // Turn off relays 2, 3, 5 for _, relay := range []int{2, 3, 5} { if err := c.TurnOff(relay); err != nil { return fmt.Errorf("failed to turn off relay %d: %w", relay, err) } } // Wait 35 seconds for Flex Radio to shut down properly time.Sleep(35 * time.Second) // Turn off relay 1 (Power Supply) if err := c.TurnOff(1); err != nil { return fmt.Errorf("failed to turn off relay 1: %w", err) } return nil } // GetStatus queries the actual state of all relays func (c *Client) GetStatus() (*Status, error) { url := fmt.Sprintf("http://%s/relaystate/get2/1$2$3$4$5$", c.host) resp, err := c.httpClient.Get(url) if err != nil { return nil, fmt.Errorf("failed to get relay status: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode) } body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } // Parse response format: "1,1\n2,1\n3,1\n4,1\n5,0\n" status := &Status{ Relays: make([]RelayState, 0, 5), Connected: true, } lines := strings.Split(strings.TrimSpace(string(body)), "\n") for _, line := range lines { parts := strings.Split(strings.TrimSpace(line), ",") if len(parts) != 2 { continue } relayNum, err := strconv.Atoi(parts[0]) if err != nil { continue } relayState, err := strconv.Atoi(parts[1]) if err != nil { continue } status.Relays = append(status.Relays, RelayState{ Number: relayNum, State: relayState == 1, }) } return status, nil } // Ping checks if the device is reachable func (c *Client) Ping() error { url := fmt.Sprintf("http://%s/", c.host) resp, err := c.httpClient.Get(url) if err != nil { return err } defer resp.Body.Close() return nil }