Sonnenbatterie 5/6: add battery control (#21152)
This commit is contained in:
parent
d14c9ee49b
commit
34cf433b1b
3 changed files with 149 additions and 18 deletions
61
plugin/sleep.go
Normal file
61
plugin/sleep.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
)
|
||||
|
||||
type sleepPlugin struct {
|
||||
duration time.Duration
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.AddCtx("sleep", NewSleepFromConfig)
|
||||
}
|
||||
|
||||
// NewSleepFromConfig creates sleep provider
|
||||
func NewSleepFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
|
||||
var cc struct {
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o := &sleepPlugin{
|
||||
duration: cc.Duration,
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// sleeper is the generic sleeper function for sleepPlugin
|
||||
// it is currently not possible to write this as a method
|
||||
func sleeper[T comparable](o *sleepPlugin) func(T) error {
|
||||
return func(val T) error {
|
||||
<-time.After(o.duration)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var _ IntSetter = (*sleepPlugin)(nil)
|
||||
|
||||
func (o *sleepPlugin) IntSetter(param string) (func(int64) error, error) {
|
||||
return sleeper[int64](o), nil
|
||||
}
|
||||
|
||||
var _ FloatSetter = (*sleepPlugin)(nil)
|
||||
|
||||
func (o *sleepPlugin) FloatSetter(param string) (func(float64) error, error) {
|
||||
return sleeper[float64](o), nil
|
||||
}
|
||||
|
||||
var _ BoolSetter = (*sleepPlugin)(nil)
|
||||
|
||||
func (o *sleepPlugin) BoolSetter(param string) (func(bool) error, error) {
|
||||
return sleeper[bool](o), nil
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package plugin
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -13,7 +15,7 @@ type watchdogPlugin struct {
|
|||
mu sync.Mutex
|
||||
ctx context.Context
|
||||
log *util.Logger
|
||||
reset *string
|
||||
reset []string
|
||||
initial *string
|
||||
set Config
|
||||
timeout time.Duration
|
||||
|
|
@ -27,7 +29,7 @@ func init() {
|
|||
// NewWatchDogFromConfig creates watchDog provider
|
||||
func NewWatchDogFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) {
|
||||
var cc struct {
|
||||
Reset *string
|
||||
Reset []string
|
||||
Initial *string
|
||||
Set Config
|
||||
Timeout time.Duration
|
||||
|
|
@ -64,7 +66,7 @@ func (o *watchdogPlugin) wdt(ctx context.Context, set func() error) {
|
|||
|
||||
// setter is the generic setter function for watchdogPlugin
|
||||
// it is currently not possible to write this as a method
|
||||
func setter[T comparable](o *watchdogPlugin, set func(T) error, reset *T) func(T) error {
|
||||
func setter[T comparable](o *watchdogPlugin, set func(T) error, reset []T) func(T) error {
|
||||
return func(val T) error {
|
||||
o.mu.Lock()
|
||||
|
||||
|
|
@ -75,7 +77,7 @@ func setter[T comparable](o *watchdogPlugin, set func(T) error, reset *T) func(T
|
|||
}
|
||||
|
||||
// start wdt on non-reset value
|
||||
if reset == nil || val != *reset {
|
||||
if !slices.Contains(reset, val) {
|
||||
var ctx context.Context
|
||||
ctx, o.cancel = context.WithCancel(context.Background())
|
||||
|
||||
|
|
@ -98,13 +100,15 @@ func (o *watchdogPlugin) IntSetter(param string) (func(int64) error, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var reset *int64
|
||||
var reset []int64
|
||||
if o.reset != nil {
|
||||
val, err := strconv.ParseInt(*o.reset, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, v := range o.reset {
|
||||
val, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reset = append(reset, val)
|
||||
}
|
||||
reset = &val
|
||||
}
|
||||
|
||||
res := setter(o, set, reset)
|
||||
|
|
@ -130,13 +134,15 @@ func (o *watchdogPlugin) FloatSetter(param string) (func(float64) error, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var reset *float64
|
||||
var reset []float64
|
||||
if o.reset != nil {
|
||||
val, err := strconv.ParseFloat(*o.reset, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, v := range o.reset {
|
||||
val, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reset = append(reset, val)
|
||||
}
|
||||
reset = &val
|
||||
}
|
||||
|
||||
res := setter(o, set, reset)
|
||||
|
|
@ -162,13 +168,15 @@ func (o *watchdogPlugin) BoolSetter(param string) (func(bool) error, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var reset *bool
|
||||
if o.reset != nil {
|
||||
val, err := strconv.ParseBool(*o.reset)
|
||||
var reset []bool
|
||||
if len(o.reset) > 1 {
|
||||
return nil, fmt.Errorf("more than one boolean reset value")
|
||||
} else if len(o.reset) == 1 {
|
||||
val, err := strconv.ParseBool(o.reset[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reset = &val
|
||||
reset = append(reset, val)
|
||||
}
|
||||
|
||||
res := setter(o, set, reset)
|
||||
|
|
|
|||
|
|
@ -11,42 +11,104 @@ params:
|
|||
default: 7979
|
||||
- name: capacity
|
||||
advanced: true
|
||||
- name: maxchargepower
|
||||
advanced: true
|
||||
- name: cache
|
||||
advanced: true
|
||||
default: 5s
|
||||
render: |
|
||||
type: custom
|
||||
{{- if eq .usage "grid" }}
|
||||
power:
|
||||
source: http
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery
|
||||
cache: {{ .cache }}
|
||||
jq: .M39 - .M38 # current purchase - current feed-in at the interconnection point
|
||||
energy:
|
||||
source: http
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery
|
||||
cache: {{ .cache }}
|
||||
jq: .M41 # cumulated purchase since installation
|
||||
{{- end }}
|
||||
{{- if eq .usage "pv" }}
|
||||
power:
|
||||
source: http
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery
|
||||
cache: {{ .cache }}
|
||||
jq: .M03 # current pv power
|
||||
energy:
|
||||
source: http
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery
|
||||
cache: {{ .cache }}
|
||||
jq: .M37 # cumulated pv production since installation of Sonnenbatterie
|
||||
{{- end }}
|
||||
{{- if eq .usage "battery" }}
|
||||
power:
|
||||
source: http
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery
|
||||
cache: {{ .cache }}
|
||||
# M34 current discharging power, S65 max inverter power
|
||||
# M35 current charging power, S65 max inverter power
|
||||
jq: (if .M34 <= .S65 then .M34 else 0 end) - (if .M35 <= .S65 then .M35 else 0 end)
|
||||
energy:
|
||||
source: http
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery
|
||||
cache: {{ .cache }}
|
||||
jq: .M31 # total stored energy over lifetime
|
||||
soc:
|
||||
source: http
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery
|
||||
cache: {{ .cache }}
|
||||
jq: .M30 # SOC relative to usable capacity (.M05 # display SOC)
|
||||
batterymode:
|
||||
# use a sequence to propagate resets to the watchdog
|
||||
source: sequence
|
||||
set:
|
||||
- source: switch
|
||||
switch:
|
||||
- case: 1 # normal
|
||||
set:
|
||||
source: http
|
||||
method: PUT
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery/C06
|
||||
body: '10' # Automatic
|
||||
- case: 2 # hold
|
||||
set:
|
||||
source: http
|
||||
method: PUT
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery/C06
|
||||
body: '20' # Standby
|
||||
- case: 3 # charge
|
||||
set:
|
||||
source: http
|
||||
method: PUT
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery/C06
|
||||
body: '55' # slave mode
|
||||
# run the watchdog only on the charging power request to avoid making unnecessary mode changes
|
||||
- source: watchdog
|
||||
timeout: 30s # 3 minutes without setting a value will stop all charging, 30s was chosen to account for api instability
|
||||
reset: [1,2]
|
||||
set:
|
||||
source: switch
|
||||
switch:
|
||||
- case: 1 # normal
|
||||
set:
|
||||
source: sleep
|
||||
duration: 0s
|
||||
- case: 2 # hold
|
||||
set:
|
||||
source: sleep
|
||||
duration: 0s
|
||||
# only charging requires repeated requests
|
||||
- case: 3 # charge
|
||||
set:
|
||||
source: sequence
|
||||
set:
|
||||
- source: sleep
|
||||
duration: 1s
|
||||
- source: http
|
||||
method: PUT
|
||||
uri: http://{{ .host }}:{{ .port }}/rest/devices/battery/C24
|
||||
body: {{ if .maxchargepower }}{{ .maxchargepower }}{{ else }}99000{{ end }}
|
||||
capacity: {{ .capacity }} # kWh
|
||||
{{- end }}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue