Skip to content

Commit

Permalink
Merge branch 'evcc-io:master' into enphase-update
Browse files Browse the repository at this point in the history
  • Loading branch information
ivoks authored Dec 29, 2024
2 parents 25d3889 + 20dbe42 commit aee224e
Show file tree
Hide file tree
Showing 14 changed files with 438 additions and 73 deletions.
2 changes: 1 addition & 1 deletion api/tariff.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package api

//go:generate enumer -type TariffType -trimprefix TariffType -transform=lower
//go:generate enumer -type TariffType -trimprefix TariffType -transform=lower -text

type TariffType int

Expand Down
14 changes: 13 additions & 1 deletion api/tarifftype_enumer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

224 changes: 224 additions & 0 deletions charger/vaillant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package charger

// LICENSE

// Copyright (c) 2024 andig

// This module is NOT covered by the MIT license. All rights reserved.

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import (
"context"
"errors"
"fmt"
"reflect"
"strings"

"github.com/WulfgarW/sensonet"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"golang.org/x/oauth2"
)

func init() {
registry.AddCtx("vaillant", NewVaillantFromConfig)
}

type Vaillant struct {
*SgReady
log *util.Logger
conn *sensonet.Connection
systemId string
}

//go:generate decorate -f decorateVaillant -b *Vaillant -r api.Charger -t "api.Battery,Soc,func() (float64, error)"

// NewVaillantFromConfig creates an Vaillant configurable charger from generic config
func NewVaillantFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
cc := struct {
embed `mapstructure:",squash"`
User, Password string
Realm string
HeatingZone int
HeatingSetpoint float32
Phases int
}{
embed: embed{
Icon_: "heatpump",
Features_: []api.Feature{api.Heating, api.IntegratedDevice},
},
Realm: sensonet.REALM_GERMANY,
Phases: 1,
}

if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}

if cc.User == "" || cc.Password == "" {
return nil, api.ErrMissingCredentials
}

log := util.NewLogger("vaillant").Redact(cc.User, cc.Password)
client := request.NewClient(log)
clientCtx := context.WithValue(ctx, oauth2.HTTPClient, client)

oc := sensonet.Oauth2ConfigForRealm(cc.Realm)
token, err := oc.PasswordCredentialsToken(clientCtx, cc.User, cc.Password)
if err != nil {
return nil, err
}

conn, err := sensonet.NewConnection(oc.TokenSource(clientCtx, token), sensonet.WithHttpClient(client), sensonet.WithLogger(log.TRACE))
if err != nil {
return nil, err
}

homes, err := conn.GetHomes()
if err != nil {
return nil, err
}

systemId := homes[0].SystemID

system, err := conn.GetSystem(systemId)
if err != nil {
return nil, err
}

var strategy int
hotWater := len(system.State.Dhw)+len(system.State.DomesticHotWater) > 0

switch {
case hotWater && cc.HeatingSetpoint != 0:
strategy = sensonet.STRATEGY_HOTWATER_THEN_HEATING
case hotWater:
strategy = sensonet.STRATEGY_HOTWATER
case cc.HeatingSetpoint != 0:
strategy = sensonet.STRATEGY_HEATING
default:
return nil, errors.New("could not determine boost strategy, need either hot water or heating zone and setpoint")
}

heatingPar := sensonet.HeatingParStruct{
ZoneIndex: cc.HeatingZone,
VetoSetpoint: cc.HeatingSetpoint,
VetoDuration: -1, // negative value means: use default
}
hotwaterPar := sensonet.HotwaterParStruct{
Index: -1, // negative value means: use default
}

set := func(mode int64) error {
switch mode {
case Normal:
_, err := conn.StopStrategybased(systemId, &heatingPar, &hotwaterPar)
return err
case Boost:
_, err := conn.StartStrategybased(systemId, strategy, &heatingPar, &hotwaterPar)
return err
default:
return api.ErrNotAvailable
}
}

sgr, err := NewSgReady(ctx, &cc.embed, set, nil, nil, cc.Phases)
if err != nil {
return nil, err
}

res := &Vaillant{
log: log,
conn: conn,
systemId: systemId,
SgReady: sgr,
}

var temp func() (float64, error)
if hotWater {
temp = func() (float64, error) {
system, err := conn.GetSystem(systemId)
if err != nil {
return 0, err
}

switch {
case len(system.State.Dhw) > 0:
return system.State.Dhw[0].CurrentDhwTemperature, nil
case len(system.State.DomesticHotWater) > 0:
return system.State.DomesticHotWater[0].CurrentDomesticHotWaterTemperature, nil
default:
return 0, api.ErrNotAvailable
}
}
}

return decorateVaillant(res, temp), nil
}

func (v *Vaillant) print(chapter int, prefix string, zz ...any) {
var i int
for _, z := range zz {
rt := reflect.TypeOf(z)
rv := reflect.ValueOf(z)

if rt.Kind() == reflect.Slice && rv.Len() == 0 {
continue
}
i++

typ := strings.TrimPrefix(strings.TrimPrefix(fmt.Sprintf("%T", z), "[]sensonet."), prefix)

fmt.Println()
fmt.Printf("%d.%d. %s\n", chapter, i+1, typ)

if rt.Kind() == reflect.Slice {
for j := 0; j < rv.Len(); j++ {
fmt.Printf("%d.%d.%d. %s %d\n", chapter, i+1, j+1, typ, j)
fmt.Printf("%+v\n", rv.Index(j))
}
} else {
fmt.Printf("%+v\n", z)
}
}
}

func (v *Vaillant) Diagnose() {
sys, err := v.conn.GetSystem(v.systemId)
if err != nil {
v.log.ERROR.Println(err)
return
}

fmt.Println()
fmt.Println("1. State")
fmt.Println()
fmt.Println("1.1. System")
fmt.Printf("%+v\n", sys.State.System)
v.print(1, "State", sys.State.Zones, sys.State.Circuits, sys.State.Dhw, sys.State.DomesticHotWater)

fmt.Println()
fmt.Println("2. Properties")
fmt.Println()
fmt.Println("2.1. System")
fmt.Printf("%+v\n", sys.Properties.System)
v.print(2, "Properties", sys.Properties.Zones, sys.Properties.Circuits, sys.Properties.Dhw, sys.Properties.DomesticHotWater)

fmt.Println()
fmt.Println("3. Configuration")
fmt.Println()
fmt.Println("3.1. System")
fmt.Printf("%+v\n", sys.Configuration.System)
v.print(3, "Configuration", sys.Configuration.Zones, sys.Configuration.Circuits, sys.Configuration.Dhw, sys.Configuration.DomesticHotWater)
}
35 changes: 35 additions & 0 deletions charger/vaillant_decorators.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/BurntSushi/toml v1.4.0
github.com/Masterminds/sprig/v3 v3.3.0
github.com/PuerkitoBio/goquery v1.10.0
github.com/WulfgarW/sensonet v0.0.2-0.20241228113605-f5304e7a183c
github.com/andig/go-powerwall v0.2.1-0.20230808194509-dd70cdb6e140
github.com/andig/gosunspec v0.0.0-20240918203654-860ce51d602b
github.com/andig/mbserver v0.0.0-20230310211055-1d29cbb5820e
Expand Down Expand Up @@ -94,13 +95,13 @@ require (
github.com/writeas/go-strip-markdown/v2 v2.1.1
gitlab.com/bboehmke/sunny v0.16.0
go.uber.org/mock v0.5.0
golang.org/x/crypto v0.29.0
golang.org/x/crypto v0.31.0
golang.org/x/crypto/x509roots/fallback v0.0.0-20241127184453-8c4e668694cc
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
golang.org/x/net v0.31.0
golang.org/x/oauth2 v0.24.0
golang.org/x/sync v0.9.0
golang.org/x/text v0.20.0
golang.org/x/sync v0.10.0
golang.org/x/text v0.21.0
golang.org/x/tools v0.27.0
google.golang.org/grpc v1.68.0
google.golang.org/protobuf v1.35.2
Expand Down Expand Up @@ -191,8 +192,8 @@ require (
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/term v0.26.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
gopkg.in/go-playground/validator.v9 v9.31.0 // indirect
gopkg.in/sourcemap.v1 v1.0.5 // indirect
Expand Down
22 changes: 12 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/WulfgarW/sensonet v0.0.2-0.20241228113605-f5304e7a183c h1:3VCEbidjIb4Luhh9dszkYKrvyvgsfI/Eb6CW2tkzEdY=
github.com/WulfgarW/sensonet v0.0.2-0.20241228113605-f5304e7a183c/go.mod h1:QZodMch+UskApzlhhGa5ydSY0v5knlF4uQ5WO78xrEs=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE=
github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U=
Expand Down Expand Up @@ -714,8 +716,8 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto/x509roots/fallback v0.0.0-20241127184453-8c4e668694cc h1:N2DXFnxni8U4KZ7CcK6kicRat3uowReUjOJxwYTxQv8=
golang.org/x/crypto/x509roots/fallback v0.0.0-20241127184453-8c4e668694cc/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down Expand Up @@ -777,8 +779,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -820,14 +822,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand All @@ -836,8 +838,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
Loading

0 comments on commit aee224e

Please sign in to comment.