SolarEdge: fix battery control (#10906)
This commit is contained in:
parent
2ea1c55948
commit
ea2fe72040
6 changed files with 189 additions and 16 deletions
|
|
@ -12,8 +12,10 @@ const (
|
|||
flagHeaders = "log-headers"
|
||||
flagHeadersDescription = "Log headers"
|
||||
|
||||
flagBatteryMode = "battery-mode"
|
||||
flagBatteryModeDescription = "Set battery mode (normal, hold, charge)"
|
||||
flagBatteryMode = "battery-mode"
|
||||
flagBatteryModeDescription = "Set battery mode (normal, hold, charge)"
|
||||
flagBatteryModeWait = "battery-mode-wait"
|
||||
flagBatteryModeWaitDescription = "Wait given duration during which potential watchdogs are active"
|
||||
|
||||
flagCurrent = "current"
|
||||
flagCurrentDescription = "Set maximum current"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/util/config"
|
||||
"github.com/spf13/cobra"
|
||||
|
|
@ -17,6 +19,7 @@ var meterCmd = &cobra.Command{
|
|||
func init() {
|
||||
rootCmd.AddCommand(meterCmd)
|
||||
meterCmd.Flags().StringP(flagBatteryMode, "b", "", flagBatteryModeDescription)
|
||||
meterCmd.Flags().DurationP(flagBatteryModeWait, "w", 0, flagBatteryModeWaitDescription)
|
||||
}
|
||||
|
||||
func runMeter(cmd *cobra.Command, args []string) {
|
||||
|
|
@ -58,6 +61,11 @@ func runMeter(cmd *cobra.Command, args []string) {
|
|||
log.FATAL.Fatalln("set battery mode:", err)
|
||||
}
|
||||
}
|
||||
|
||||
if d, err := cmd.Flags().GetDuration(flagBatteryModeWait); d > 0 && err == nil {
|
||||
log.FATAL.Println("waiting for:", d)
|
||||
time.Sleep(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
130
provider/watchdog.go
Normal file
130
provider/watchdog.go
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
)
|
||||
|
||||
type watchdogProvider struct {
|
||||
mu sync.Mutex
|
||||
log *util.Logger
|
||||
reset *string
|
||||
set Config
|
||||
timeout time.Duration
|
||||
cancel func()
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Add("watchdog", NewWatchDogFromConfig)
|
||||
}
|
||||
|
||||
// NewWatchDogFromConfig creates watchDog provider
|
||||
func NewWatchDogFromConfig(other map[string]interface{}) (Provider, error) {
|
||||
var cc struct {
|
||||
Reset *string
|
||||
Set Config
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o := &watchdogProvider{
|
||||
log: util.NewLogger("watchdog"),
|
||||
reset: cc.Reset,
|
||||
set: cc.Set,
|
||||
timeout: cc.Timeout,
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func (o *watchdogProvider) wdt(ctx context.Context, set func() error) {
|
||||
tick := time.NewTicker(o.timeout / 2)
|
||||
for range tick.C {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
tick.Stop()
|
||||
return
|
||||
default:
|
||||
if err := set(); err != nil {
|
||||
o.log.ERROR.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var _ SetIntProvider = (*watchdogProvider)(nil)
|
||||
|
||||
func (o *watchdogProvider) IntSetter(param string) (func(int64) error, error) {
|
||||
set, err := NewIntSetterFromConfig(param, o.set)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var reset *int64
|
||||
if o.reset != nil {
|
||||
val, err := strconv.ParseInt(*o.reset, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reset = &val
|
||||
}
|
||||
|
||||
return func(val int64) error {
|
||||
o.mu.Lock()
|
||||
|
||||
// stop wdt on new write
|
||||
if o.cancel != nil {
|
||||
o.cancel()
|
||||
o.cancel = nil
|
||||
}
|
||||
|
||||
// start wdt on non-reset value
|
||||
if reset == nil || val != *reset {
|
||||
var ctx context.Context
|
||||
ctx, o.cancel = context.WithCancel(context.Background())
|
||||
|
||||
go o.wdt(ctx, func() error {
|
||||
return set(val)
|
||||
})
|
||||
}
|
||||
|
||||
o.mu.Unlock()
|
||||
|
||||
return set(val)
|
||||
}, err
|
||||
}
|
||||
|
||||
// var _ SetFloatProvider = (*watchdogProvider)(nil)
|
||||
|
||||
// func (o *watchdogProvider) FloatSetter(param string) (func(float64) error, error) {
|
||||
// set, err := NewFloatSetterFromConfig(param, o.set)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// val, err := strconv.ParseFloat(o.str, 64)
|
||||
// return func(_ float64) error {
|
||||
// return set(val)
|
||||
// }, err
|
||||
// }
|
||||
|
||||
// var _ SetBoolProvider = (*watchdogProvider)(nil)
|
||||
|
||||
// func (o *watchdogProvider) BoolSetter(param string) (func(bool) error, error) {
|
||||
// set, err := NewBoolSetterFromConfig(param, o.set)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// val, err := strconv.ParseBool(o.str)
|
||||
// return func(_ bool) error {
|
||||
// return set(val)
|
||||
// }, err
|
||||
// }
|
||||
|
|
@ -5,8 +5,12 @@ products:
|
|||
generic: Hybrid Inverter
|
||||
requirements:
|
||||
description:
|
||||
de: Nur ein System kann und darf auf den Wechselrichter zugreifen!
|
||||
en: Only one system may access the inverter!
|
||||
de: |
|
||||
Nur ein System kann und darf auf den Wechselrichter zugreifen!
|
||||
Für die optionale Batteriesteuerung muss StorageConf_CtrlMode (0xE004) auf 4 "Remote" stehen.
|
||||
en: |
|
||||
Only one system may access the inverter!
|
||||
For optional battery control, StorageConf_CtrlMode (0xE004) must be at 4 "Remote".
|
||||
params:
|
||||
- name: usage
|
||||
choice: ["grid", "pv", "battery"]
|
||||
|
|
@ -18,6 +22,10 @@ params:
|
|||
- name: timeout
|
||||
- name: capacity
|
||||
advanced: true
|
||||
- name: watchdog
|
||||
type: duration
|
||||
default: 60s
|
||||
advanced: true
|
||||
render: |
|
||||
type: custom
|
||||
{{- if eq .usage "grid" }}
|
||||
|
|
@ -66,19 +74,34 @@ render: |
|
|||
type: holding
|
||||
decode: float32s
|
||||
batterymode:
|
||||
source: map
|
||||
values:
|
||||
1: 7 # normal
|
||||
2: 1 # hold
|
||||
3: 3 # charge
|
||||
source: sequence
|
||||
set:
|
||||
source: modbus
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
timeout: {{ .timeout }}
|
||||
register:
|
||||
address: 0xE00A # Battery mode
|
||||
type: writesingle
|
||||
decode: uint16
|
||||
- source: const
|
||||
value: {{ (toDuration .watchdog).Seconds }}
|
||||
set:
|
||||
source: watchdog
|
||||
timeout: {{ .watchdog }} # re-write at timeout/2
|
||||
set:
|
||||
source: modbus
|
||||
{{- include "modbus" . | indent 8 }}
|
||||
timeout: {{ .timeout }}
|
||||
register:
|
||||
address: 0xE00B # StorageRemoteCtrl_CommandTimeout
|
||||
type: writesingle
|
||||
decode: uint16
|
||||
- source: map
|
||||
values:
|
||||
1: 7 # normal
|
||||
2: 1 # hold
|
||||
3: 3 # charge
|
||||
set:
|
||||
source: modbus
|
||||
{{- include "modbus" . | indent 6 }}
|
||||
timeout: {{ .timeout }}
|
||||
register:
|
||||
address: 0xE00D # StorageRemoteCtrl_CommandMode
|
||||
type: writesingle
|
||||
decode: uint16
|
||||
{{- if .capacity }}
|
||||
capacity: {{ .capacity }} # kWh
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package templates
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
|
@ -34,6 +35,7 @@ func RenderInstance(class Class, other map[string]interface{}) (*Instance, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println(string(b))
|
||||
var instance Instance
|
||||
if err = yaml.Unmarshal(b, &instance); err == nil && instance.Type == "" {
|
||||
err = errors.New("empty instance type- check for missing usage")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/url"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
|
@ -59,6 +60,13 @@ func FuncMap(tmpl *template.Template) *template.Template {
|
|||
"urlEncode": func(v string) string {
|
||||
return url.QueryEscape(v)
|
||||
},
|
||||
"toDuration": func(v string) time.Duration {
|
||||
d, err := time.ParseDuration(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return d
|
||||
},
|
||||
}
|
||||
|
||||
return tmpl.Funcs(sprig.TxtFuncMap()).Funcs(funcMap)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue