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"` } 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 { for i := 1; i <= 5; i++ { if err := c.TurnOn(i); err != nil { return fmt.Errorf("failed to turn on relay %d: %w", i, err) } } return nil } func (c *Client) AllOff() error { for i := 1; i <= 5; i++ { if err := c.TurnOff(i); err != nil { return fmt.Errorf("failed to turn off relay %d: %w", i, 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), } 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 }