This commit is contained in:
andig 2026-02-24 11:48:57 +01:00 • committed by GitHub
parent 1e509e4f6e
commit b816ae43c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 27 additions and 90 deletions

View file

@ -20,7 +20,7 @@ import (
"github.com/evcc-io/evcc/charger/warp"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/icholy/digest"
"github.com/jpfielding/go-http-digest/pkg/digest"
)
type WarpWS struct {
@ -117,10 +117,7 @@ func NewWarpWSFromConfig(ctx context.Context, other map[string]any) (api.Charger
}
wb.pmHelper = request.NewHelper(wb.log)
if cc.EnergyManagerUser != "" {
wb.pmHelper.Client.Transport = &digest.Transport{
Username: cc.EnergyManagerUser,
Password: cc.EnergyManagerPassword,
}
wb.pmHelper.Client.Transport = digest.NewTransport(cc.EnergyManagerUser, cc.EnergyManagerPassword, wb.pmHelper.Client.Transport)
}
}
@ -159,10 +156,7 @@ func NewWarpWS(ctx context.Context, uri, user, password string, meterIndex uint)
client := request.NewHelper(log)
if user != "" {
client.Client.Transport = &digest.Transport{
Username: user,
Password: password,
}
client.Client.Transport = digest.NewTransport(user, password, client.Client.Transport)
}
w := &WarpWS{
@ -174,87 +168,37 @@ func NewWarpWS(ctx context.Context, uri, user, password string, meterIndex uint)
metersValuesTopic: fmt.Sprintf("meters/%d/values", meterIndex),
}
uri, err := parseURI(w.uri, true)
if err != nil {
w.log.DEBUG.Println(err)
if uri == "" {
return nil, err
}
}
go w.run(ctx, uri, user, password)
go w.run(ctx)
return w, nil
}
func (w *WarpWS) run(ctx context.Context, uri, user, pass string) {
bo := backoff.NewExponentialBackOff(
backoff.WithMaxInterval(30*time.Second),
backoff.WithMaxElapsedTime(0))
for ctx.Err() == nil {
w.log.DEBUG.Printf("ws connecting to %s", uri)
conn, resp, err := dialWebsocket(ctx, uri, user, pass)
if err != nil {
if resp != nil {
resp.Body.Close()
}
if conn != nil {
conn.Close(websocket.StatusInternalError, "dial failed")
}
w.log.ERROR.Printf("ws dial to %s failed: %v", uri, err)
} else {
w.log.DEBUG.Printf("ws connected to %s", uri)
bo.Reset()
if err := w.handleConnection(ctx, conn); err != nil {
w.log.ERROR.Println(err)
}
}
select {
case <-ctx.Done():
return
case <-time.After(bo.NextBackOff()):
}
}
}
func dialWebsocket(ctx context.Context, wsURL, user, pass string) (*websocket.Conn, *http.Response, error) {
conn, resp, err := websocket.Dial(ctx, wsURL, nil)
if err == nil {
return conn, resp, err
}
// Extract challeng from response
if resp == nil {
return nil, nil, fmt.Errorf("no response on websocket dial")
}
www := resp.Header.Get("WWW-Authenticate")
chal, err := digest.ParseChallenge(www)
func (w *WarpWS) run(ctx context.Context) {
uri, err := parseURI(w.uri, true)
if err != nil {
return nil, resp, fmt.Errorf("digest parse error: %w", err)
w.log.DEBUG.Println(err)
if uri == "" {
return
}
}
w.log.TRACE.Printf("connecting to %s …", uri)
cred, _ := digest.Digest(chal, digest.Options{
Username: user,
Password: pass,
Method: "GET",
URI: wsURL,
Count: 1,
})
resp.Body.Close()
bo := backoff.NewExponentialBackOff(backoff.WithMaxElapsedTime(0))
for ctx.Err() == nil {
conn, _, err := websocket.Dial(ctx, uri, nil)
if err != nil {
if ctx.Err() != nil {
return
}
time.Sleep(bo.NextBackOff())
continue
}
// Dial with Digest Auth
dialer := websocket.DialOptions{
HTTPHeader: http.Header{
"Authorization": []string{cred.String()},
},
bo.Reset()
if err := w.handleConnection(ctx, conn); err != nil {
w.log.ERROR.Println(err)
}
}
return websocket.Dial(ctx, wsURL, &dialer)
}
func parseURI(uri string, toWS bool) (string, error) {
@ -294,9 +238,9 @@ func (w *WarpWS) handleConnection(ctx context.Context, conn *websocket.Conn) err
return err
}
w.log.TRACE.Printf("%s ws event %s: %s", w.uri, event.Topic, event.Payload)
w.log.TRACE.Printf("ws event %s: %s", event.Topic, event.Payload)
if err := w.handleEvent(event.Topic, event.Payload); err != nil {
w.log.ERROR.Printf("bad payload from %s for topic %s: %v", w.uri, event.Topic, err)
w.log.ERROR.Printf("bad payload for topic %s: %v", event.Topic, err)
}
}
}
@ -387,7 +331,6 @@ func (w *WarpWS) hasFeature(feature string) bool {
var f []string
if err := w.GetJSON(uri, &f); err == nil {
w.log.DEBUG.Printf("%s features response: %s", w.uri, f)
w.mu.Lock()
w.features = f
w.mu.Unlock()
@ -495,7 +438,6 @@ func (w *WarpWS) disablePhaseAutoSwitch() error {
uri := fmt.Sprintf("%s/evse/phase_auto_switch", w.uri)
req, _ := request.New(http.MethodPost, uri, request.MarshalJSON(map[string]bool{"enabled": false}), request.JSONEncoding)
_, err := w.Do(req)
w.log.DEBUG.Println("disable phase auto switch was successful")
return err
}

1
go.mod
View file

@ -55,7 +55,6 @@ require (
github.com/hashicorp/go-version v1.8.0
github.com/hasura/go-graphql-client v0.15.1
github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83
github.com/icholy/digest v1.1.0
github.com/influxdata/influxdb-client-go/v2 v2.14.0
github.com/insomniacslk/tapo v1.0.2
github.com/itchyny/gojq v0.12.18

4
go.sum
View file

@ -399,8 +399,6 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/icholy/digest v1.1.0 h1:HfGg9Irj7i+IX1o1QAmPfIBNu/Q5A5Tu3n/MED9k9H4=
github.com/icholy/digest v1.1.0/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@ -1068,8 +1066,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=