evcc-io/charger/semp/connection.go

93 lines
2.1 KiB
Go

package semp
import (
"net/http"
"strings"
"sync"
"time"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
)
// Connection represents a SEMP HTTP connection with helper methods
type Connection struct {
*request.Helper
uri string
mu sync.Mutex
updated time.Time
}
// NewConnection creates a new SEMP client
func NewConnection(log *util.Logger, uri string) *Connection {
// Ensure URI ends with exactly one trailing slash
uri = strings.TrimRight(uri, "/") + "/"
return &Connection{
Helper: request.NewHelper(log),
uri: uri,
}
}
// GetDeviceXML retrieves the complete SEMP document from the base URL
func (c *Connection) GetDeviceXML() (Device2EM, error) {
var response Device2EM
if err := c.GetXML(c.uri, &response); err != nil {
return Device2EM{}, err
}
return response, nil
}
// GetParametersXML retrieves device parameters from the /Parameters endpoint
func (c *Connection) GetParametersXML() ([]Parameter, error) {
var response Device2EM
uri := c.uri + "Parameters"
if err := c.GetXML(uri, &response); err != nil {
return nil, err
}
if response.Parameters == nil {
return []Parameter{}, nil
}
return response.Parameters.Parameter, nil
}
// SendDeviceControl sends a control message to the SEMP device
// power is optional - if nil, RecommendedPowerConsumption will be omitted
func (c *Connection) SendDeviceControl(deviceId string, power int) error {
control := DeviceControl{
DeviceID: deviceId,
On: power > 0,
Timestamp: int(time.Now().Unix()),
}
if power > 0 {
control.RecommendedPowerConsumption = &power
}
message := EM2Device{
Xmlns: "http://www.sma.de/communication/schema/SEMP/v1",
DeviceControl: []DeviceControl{control},
}
req, err := request.New(http.MethodPost, c.uri, request.MarshalXML(message), request.XMLEncoding)
if err != nil {
return err
}
_, err = c.DoBody(req)
if err == nil {
c.mu.Lock()
c.updated = time.Now()
c.mu.Unlock()
}
return err
}
// Updated returns the last successful device control update time
func (c *Connection) Updated() time.Time {
c.mu.Lock()
defer c.mu.Unlock()
return c.updated
}