Files
ShackMaster/internal/devices/webswitch/webswitch.go
2026-01-09 11:56:40 +01:00

147 lines
2.8 KiB
Go

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
}