Vaillant: make hysteresis and reboost interval configurable (#31379)
This commit is contained in:
parent
2039cffb39
commit
f11cf690dc
2 changed files with 83 additions and 13 deletions
|
|
@ -19,6 +19,7 @@ package charger
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
|
@ -27,6 +28,7 @@ import (
|
|||
"github.com/WulfgarW/sensonet"
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/api/implement"
|
||||
"github.com/evcc-io/evcc/core/loadpoint"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"github.com/samber/lo"
|
||||
|
|
@ -41,6 +43,7 @@ type Vaillant struct {
|
|||
*SgReady
|
||||
log *util.Logger
|
||||
conn *sensonet.Connection
|
||||
lp loadpoint.API
|
||||
systemId string
|
||||
}
|
||||
|
||||
|
|
@ -53,14 +56,17 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charg
|
|||
System string
|
||||
HeatingZone int
|
||||
HeatingSetpoint float32
|
||||
Hysteresis float64
|
||||
Reboost time.Duration
|
||||
Cache time.Duration
|
||||
}{
|
||||
embed: embed{
|
||||
Icon_: "heatpump",
|
||||
Features_: []api.Feature{api.Continuous, api.Heating, api.IntegratedDevice, api.SwitchDevice},
|
||||
},
|
||||
Realm: sensonet.REALM_GERMANY,
|
||||
Cache: time.Minute,
|
||||
Realm: sensonet.REALM_GERMANY,
|
||||
Reboost: 15 * time.Minute,
|
||||
Cache: time.Minute,
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
|
|
@ -99,6 +105,45 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charg
|
|||
|
||||
wwCancel := func() {}
|
||||
|
||||
res := &Vaillant{
|
||||
log: log,
|
||||
conn: conn,
|
||||
systemId: systemId,
|
||||
}
|
||||
|
||||
// skipBoost reports whether the (re-)boost can be skipped because the measured
|
||||
// temperature is already within the hysteresis band below the loadpoint limit
|
||||
skipBoost := func() bool {
|
||||
if res.lp == nil || cc.Hysteresis <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
bat, ok := api.Cap[api.Battery](res)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
temp, err := bat.Soc()
|
||||
if err != nil {
|
||||
if !errors.Is(err, api.ErrNotAvailable) {
|
||||
log.ERROR.Printf("temp: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
limit := res.lp.GetLimitSoc()
|
||||
if limit <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if hysteresis := float64(limit) - cc.Hysteresis; temp >= hysteresis {
|
||||
log.DEBUG.Printf("temp: %.1f >= %.1f hysteresis", temp, hysteresis)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
set := func(mode int64) error {
|
||||
switch mode {
|
||||
case Normal:
|
||||
|
|
@ -114,8 +159,10 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charg
|
|||
return conn.StartZoneQuickVeto(systemId, cc.HeatingZone, cc.HeatingSetpoint, 4) // hours
|
||||
}
|
||||
|
||||
if err := conn.StartHotWaterBoost(systemId, sensonet.HOTWATERINDEX_DEFAULT); err != nil {
|
||||
return err
|
||||
if !skipBoost() {
|
||||
if err := conn.StartHotWaterBoost(systemId, sensonet.HOTWATERINDEX_DEFAULT); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var wwCtx context.Context
|
||||
|
|
@ -127,7 +174,11 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charg
|
|||
select {
|
||||
case <-wwCtx.Done():
|
||||
return
|
||||
case <-time.After(15 * time.Minute):
|
||||
case <-time.After(cc.Reboost):
|
||||
if skipBoost() {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := conn.StartHotWaterBoost(systemId, sensonet.HOTWATERINDEX_DEFAULT); err != nil {
|
||||
log.ERROR.Println("hot water boost:", err)
|
||||
}
|
||||
|
|
@ -142,18 +193,11 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charg
|
|||
}
|
||||
}
|
||||
|
||||
sgr, err := NewSgReady(ctx, &cc.embed, set, nil, nil)
|
||||
res.SgReady, err = NewSgReady(ctx, &cc.embed, set, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &Vaillant{
|
||||
log: log,
|
||||
conn: conn,
|
||||
systemId: systemId,
|
||||
SgReady: sgr,
|
||||
}
|
||||
|
||||
if devices, _ := conn.GetMpcData(systemId); len(devices) > 0 {
|
||||
implement.Has(res, implement.Meter(util.Cached(func() (float64, error) {
|
||||
res, err := conn.GetMpcData(systemId)
|
||||
|
|
@ -205,6 +249,13 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charg
|
|||
return res, nil
|
||||
}
|
||||
|
||||
var _ loadpoint.Controller = (*Vaillant)(nil)
|
||||
|
||||
// LoadpointControl implements loadpoint.Controller
|
||||
func (wb *Vaillant) LoadpointControl(lp loadpoint.API) {
|
||||
wb.lp = lp
|
||||
}
|
||||
|
||||
func (v *Vaillant) print(chapter int, prefix string, zz ...any) {
|
||||
var i int
|
||||
for _, z := range zz {
|
||||
|
|
|
|||
|
|
@ -78,6 +78,23 @@ params:
|
|||
help:
|
||||
de: Notwendig falls mehrere Anlagen im Account vorhanden sind
|
||||
en: Required if multiple systems are present in the account
|
||||
- name: reboost
|
||||
type: duration
|
||||
default: 15m
|
||||
advanced: true
|
||||
description:
|
||||
de: Reboost-Intervall
|
||||
en: Reboost interval
|
||||
help:
|
||||
de: Wartezeit, nach der Boost erneut gestartet wird.
|
||||
en: Wait time after which Boost is started again.
|
||||
- name: hysteresis
|
||||
type: float
|
||||
unit: °C
|
||||
advanced: true
|
||||
description:
|
||||
de: Reboost Hysterese
|
||||
en: Reboost Hysteresis
|
||||
- name: phases
|
||||
deprecated: true
|
||||
render: |
|
||||
|
|
@ -90,3 +107,5 @@ render: |
|
|||
{{- end }}
|
||||
heatingzone: {{ .zone }}
|
||||
heatingsetpoint: {{ .setpoint }}
|
||||
reboost: {{ .reboost }}
|
||||
hysteresis: {{ .hysteresis }}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue