Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SolarEdge: fix battery control #10906

Merged
merged 11 commits into from
Nov 26, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 number of seconds during which watchdog is active"
andig marked this conversation as resolved.
Show resolved Hide resolved

flagCurrent = "current"
flagCurrentDescription = "Set maximum current"
Expand Down
8 changes: 8 additions & 0 deletions cmd/meter.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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) {
Expand Down Expand Up @@ -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)
}
}
}

Expand Down
130 changes: 130 additions & 0 deletions provider/watchdog.go
Original file line number Diff line number Diff line change
@@ -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
// }
54 changes: 40 additions & 14 deletions templates/definition/meter/solaredge-hybrid.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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" }}
Expand Down Expand Up @@ -66,19 +74,37 @@ 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: 1
andig marked this conversation as resolved.
Show resolved Hide resolved
set:
source: watchdog
timeout: 60s # 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: watchdog
timeout: 1m
set:
source: modbus
{{- include "modbus" . | indent 8 }}
timeout: {{ .timeout }}
register:
address: 0xE00D # StorageRemoteCtrl_CommandMode
type: writesingle
decode: uint16
andig marked this conversation as resolved.
Show resolved Hide resolved
{{- if .capacity }}
capacity: {{ .capacity }} # kWh
{{- end }}
Expand Down