Add E3DC native implementation (#13413)
This commit is contained in:
parent
c0bf1ec04b
commit
ce97c635d6
9 changed files with 513 additions and 13 deletions
6
go.mod
6
go.mod
|
|
@ -77,6 +77,7 @@ require (
|
|||
github.com/samber/lo v1.39.0
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||
github.com/smallnest/chanx v1.2.0
|
||||
github.com/spali/go-rscp v0.2.0-beta4
|
||||
github.com/spf13/cast v1.6.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/jwalterweatherman v1.1.0
|
||||
|
|
@ -108,9 +109,11 @@ require (
|
|||
github.com/ahmetb/go-linq/v3 v3.2.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/azihsoyn/rijndael256 v0.0.0-20200316065338-d14eefa2b66b // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bitly/go-simplejson v0.5.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cstockton/go-conv v1.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
|
|
@ -172,8 +175,9 @@ require (
|
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spali/go-slicereader v0.0.0-20201122145524-8e262e1a5127 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/teivah/onecontext v1.3.0 // indirect
|
||||
|
|
|
|||
10
go.sum
10
go.sum
|
|
@ -52,6 +52,8 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
|
|||
github.com/aws/aws-sdk-go v1.51.21 h1:UrT6JC9R9PkYYXDZBV0qDKTualMr+bfK2eboTknMgbs=
|
||||
github.com/aws/aws-sdk-go v1.51.21/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/azihsoyn/rijndael256 v0.0.0-20200316065338-d14eefa2b66b h1:/2dABok/UswXOj5rjbR5bZ411ApGBq1pAEZdy5rvFrY=
|
||||
github.com/azihsoyn/rijndael256 v0.0.0-20200316065338-d14eefa2b66b/go.mod h1:ef+2vMUkiKcy2Tz7HykB01KbgUnkK4gQKq4ZeR4RYVs=
|
||||
github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw=
|
||||
github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw=
|
||||
github.com/basvdlei/gotsmart v0.0.3 h1:7hrI6btBc8dmFzzHRDkr9Xl87PvrrOT43DzbsTFqMk8=
|
||||
|
|
@ -94,6 +96,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
|||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cstockton/go-conv v1.0.0 h1:zj/q/0MpQ/97XfiC9glWiohO8lhgR4TTnHYZifLTv6I=
|
||||
github.com/cstockton/go-conv v1.0.0/go.mod h1:HuiHkkRgOA0IoBNPC7ysG7kNpjDYlgM7Kj62yQPxjy4=
|
||||
github.com/dave/jennifer v1.6.1 h1:T4T/67t6RAA5AIV6+NP8Uk/BIsXgDoqEowgycdQQLuk=
|
||||
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -186,6 +190,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe
|
|||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA=
|
||||
github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
|
|
@ -606,6 +612,10 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
|
|||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spali/go-rscp v0.2.0-beta4 h1:ct9YZTCmTW2IMg74O16nJu0QntGF26dxY5ZejRvl280=
|
||||
github.com/spali/go-rscp v0.2.0-beta4/go.mod h1:yPHx7clunJmpCLFDc60XL04/lp8p/DrrhfeBqM3J8cc=
|
||||
github.com/spali/go-slicereader v0.0.0-20201122145524-8e262e1a5127 h1:YDqvwAH/l3S4ZULmKlUYszPyLBjHq73CLuUPU+2jJeE=
|
||||
github.com/spali/go-slicereader v0.0.0-20201122145524-8e262e1a5127/go.mod h1:nf5bOq6n8UugtmQiD3l0BzkE5VP4NvyngFZVkH3ZzgM=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
|
|
|
|||
301
meter/e3dc.go
Normal file
301
meter/e3dc.go
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"slices"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"github.com/evcc-io/evcc/util/templates"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spali/go-rscp/rscp"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type E3dc struct {
|
||||
capacity float64
|
||||
dischargeLimit uint32
|
||||
usage templates.Usage // TODO check if we really want to depend on templates
|
||||
conn *rscp.Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Add("e3dc-rscp", NewE3dcFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateE3dc -b *E3dc -r api.Meter -t "api.BatteryCapacity,Capacity,func() float64" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error"
|
||||
|
||||
func NewE3dcFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
cc := struct {
|
||||
Usage templates.Usage
|
||||
Uri string
|
||||
User string
|
||||
Password string
|
||||
Key string
|
||||
Battery uint16 // battery id
|
||||
DischargeLimit uint32
|
||||
Timeout time.Duration
|
||||
}{
|
||||
Timeout: request.Timeout,
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
host, port_, err := net.SplitHostPort(util.DefaultPort(cc.Uri, 5033))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port, _ := strconv.Atoi(port_)
|
||||
|
||||
cfg := rscp.ClientConfig{
|
||||
Address: host,
|
||||
Port: uint16(port),
|
||||
Username: cc.User,
|
||||
Password: cc.Password,
|
||||
Key: cc.Key,
|
||||
ConnectionTimeout: cc.Timeout,
|
||||
SendTimeout: cc.Timeout,
|
||||
ReceiveTimeout: cc.Timeout,
|
||||
}
|
||||
|
||||
return NewE3dc(cfg, cc.Usage, cc.Battery, cc.DischargeLimit)
|
||||
}
|
||||
|
||||
var e3dcOnce sync.Once
|
||||
|
||||
func NewE3dc(cfg rscp.ClientConfig, usage templates.Usage, batteryId uint16, dischargeLimit uint32) (api.Meter, error) {
|
||||
e3dcOnce.Do(func() {
|
||||
log := util.NewLogger("e3dc")
|
||||
rscp.Log.SetLevel(logrus.DebugLevel)
|
||||
rscp.Log.SetOutput(log.TRACE.Writer())
|
||||
})
|
||||
|
||||
conn, err := rscp.NewClient(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := &E3dc{
|
||||
usage: usage,
|
||||
conn: conn,
|
||||
dischargeLimit: dischargeLimit,
|
||||
}
|
||||
|
||||
// decorate api.BatterySoc
|
||||
var (
|
||||
batterySoc func() (float64, error)
|
||||
batteryCapacity func() float64
|
||||
batteryMode func(api.BatteryMode) error
|
||||
)
|
||||
|
||||
if usage == templates.UsageBattery {
|
||||
batterySoc = m.batterySoc
|
||||
batteryCapacity = m.batteryCapacity
|
||||
batteryMode = m.setBatteryMode
|
||||
|
||||
res, err := m.conn.Send(rscp.Message{
|
||||
Tag: rscp.BAT_REQ_DATA,
|
||||
DataType: rscp.Container,
|
||||
Value: []rscp.Message{
|
||||
{
|
||||
Tag: rscp.BAT_INDEX,
|
||||
DataType: rscp.UInt16,
|
||||
Value: batteryId,
|
||||
},
|
||||
{
|
||||
Tag: rscp.BAT_REQ_SPECIFICATION,
|
||||
DataType: rscp.None,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
batSpec, err := rscpContains(res, rscp.BAT_SPECIFICATION)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
batCap, err := rscpContains(&batSpec, rscp.BAT_SPECIFIED_CAPACITY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cap, err := rscpValue(batCap, cast.ToFloat64E)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.capacity = cap / 1e3
|
||||
}
|
||||
|
||||
return decorateE3dc(m, batteryCapacity, batterySoc, batteryMode), nil
|
||||
}
|
||||
|
||||
func (m *E3dc) CurrentPower() (float64, error) {
|
||||
switch m.usage {
|
||||
case templates.UsageGrid:
|
||||
res, err := m.conn.Send(*rscp.NewMessage(rscp.EMS_REQ_POWER_GRID, nil))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return rscpValue(*res, cast.ToFloat64E)
|
||||
|
||||
case templates.UsagePV:
|
||||
res, err := m.conn.SendMultiple([]rscp.Message{
|
||||
*rscp.NewMessage(rscp.EMS_REQ_POWER_PV, nil),
|
||||
*rscp.NewMessage(rscp.EMS_REQ_POWER_ADD, nil),
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
values, err := rscpValues(res, cast.ToFloat64E)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return lo.Sum(values), nil
|
||||
|
||||
case templates.UsageBattery:
|
||||
res, err := m.conn.Send(*rscp.NewMessage(rscp.EMS_REQ_POWER_BAT, nil))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
pwr, err := rscpValue(*res, cast.ToFloat64E)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return -pwr, nil
|
||||
|
||||
default:
|
||||
return 0, api.ErrNotAvailable
|
||||
}
|
||||
}
|
||||
|
||||
func (m *E3dc) batteryCapacity() float64 {
|
||||
return m.capacity
|
||||
}
|
||||
|
||||
func (m *E3dc) batterySoc() (float64, error) {
|
||||
res, err := m.conn.Send(*rscp.NewMessage(rscp.EMS_REQ_BAT_SOC, nil))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return rscpValue(*res, cast.ToFloat64E)
|
||||
}
|
||||
|
||||
func (m *E3dc) setBatteryMode(mode api.BatteryMode) error {
|
||||
var (
|
||||
res []rscp.Message
|
||||
err error
|
||||
)
|
||||
|
||||
switch mode {
|
||||
case api.BatteryNormal:
|
||||
res, err = m.conn.SendMultiple([]rscp.Message{
|
||||
e3dcDischargeBatteryLimit(false, 0),
|
||||
e3dcBatteryCharge(0),
|
||||
})
|
||||
|
||||
case api.BatteryHold:
|
||||
res, err = m.conn.SendMultiple([]rscp.Message{
|
||||
e3dcDischargeBatteryLimit(true, m.dischargeLimit),
|
||||
e3dcBatteryCharge(0),
|
||||
})
|
||||
|
||||
case api.BatteryCharge:
|
||||
res, err = m.conn.SendMultiple([]rscp.Message{
|
||||
e3dcDischargeBatteryLimit(false, 0),
|
||||
e3dcBatteryCharge(10000), // 10kWh
|
||||
})
|
||||
|
||||
default:
|
||||
return api.ErrNotAvailable
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = rscpError(res...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func e3dcDischargeBatteryLimit(active bool, limit uint32) rscp.Message {
|
||||
contents := []rscp.Message{
|
||||
*rscp.NewMessage(rscp.EMS_POWER_LIMITS_USED, active),
|
||||
}
|
||||
|
||||
if active {
|
||||
contents = append(contents, *rscp.NewMessage(rscp.EMS_MAX_DISCHARGE_POWER, limit))
|
||||
}
|
||||
|
||||
return *rscp.NewMessage(rscp.EMS_REQ_SET_POWER_SETTINGS, contents)
|
||||
}
|
||||
|
||||
func e3dcBatteryCharge(amount uint32) rscp.Message {
|
||||
return *rscp.NewMessage(rscp.EMS_REQ_START_MANUAL_CHARGE, amount)
|
||||
}
|
||||
|
||||
func rscpError(msg ...rscp.Message) error {
|
||||
var errs []error
|
||||
for _, m := range msg {
|
||||
if m.DataType == rscp.Error {
|
||||
errs = append(errs, errors.New(rscp.RscpError(cast.ToUint32(m.Value)).String()))
|
||||
}
|
||||
}
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func rscpContains(msg *rscp.Message, tag rscp.Tag) (rscp.Message, error) {
|
||||
var zero rscp.Message
|
||||
|
||||
slice, ok := msg.Value.([]rscp.Message)
|
||||
if !ok {
|
||||
return zero, errors.New("not a slice looking for " + tag.String())
|
||||
}
|
||||
|
||||
idx := slices.IndexFunc(slice, func(m rscp.Message) bool {
|
||||
return m.Tag == tag
|
||||
})
|
||||
if idx < 0 {
|
||||
return zero, errors.New("missing " + tag.String())
|
||||
}
|
||||
|
||||
res := slice[idx]
|
||||
return res, rscpError(res)
|
||||
}
|
||||
|
||||
func rscpValue[T any](msg rscp.Message, fun func(any) (T, error)) (T, error) {
|
||||
var zero T
|
||||
if err := rscpError(msg); err != nil {
|
||||
return zero, err
|
||||
}
|
||||
|
||||
return fun(msg.Value)
|
||||
}
|
||||
|
||||
func rscpValues[T any](msg []rscp.Message, fun func(any) (T, error)) ([]T, error) {
|
||||
res := make([]T, 0, len(msg))
|
||||
|
||||
for _, m := range msg {
|
||||
v, err := rscpValue(m, fun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = append(res, v)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
137
meter/e3dc_decorators.go
Normal file
137
meter/e3dc_decorators.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package meter
|
||||
|
||||
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/evcc-io/evcc/api"
|
||||
)
|
||||
|
||||
func decorateE3dc(base *E3dc, batteryCapacity func() float64, battery func() (float64, error), batteryController func(api.BatteryMode) error) api.Meter {
|
||||
switch {
|
||||
case battery == nil && batteryCapacity == nil && batteryController == nil:
|
||||
return base
|
||||
|
||||
case battery == nil && batteryCapacity != nil && batteryController == nil:
|
||||
return &struct {
|
||||
*E3dc
|
||||
api.BatteryCapacity
|
||||
}{
|
||||
E3dc: base,
|
||||
BatteryCapacity: &decorateE3dcBatteryCapacityImpl{
|
||||
batteryCapacity: batteryCapacity,
|
||||
},
|
||||
}
|
||||
|
||||
case battery != nil && batteryCapacity == nil && batteryController == nil:
|
||||
return &struct {
|
||||
*E3dc
|
||||
api.Battery
|
||||
}{
|
||||
E3dc: base,
|
||||
Battery: &decorateE3dcBatteryImpl{
|
||||
battery: battery,
|
||||
},
|
||||
}
|
||||
|
||||
case battery != nil && batteryCapacity != nil && batteryController == nil:
|
||||
return &struct {
|
||||
*E3dc
|
||||
api.Battery
|
||||
api.BatteryCapacity
|
||||
}{
|
||||
E3dc: base,
|
||||
Battery: &decorateE3dcBatteryImpl{
|
||||
battery: battery,
|
||||
},
|
||||
BatteryCapacity: &decorateE3dcBatteryCapacityImpl{
|
||||
batteryCapacity: batteryCapacity,
|
||||
},
|
||||
}
|
||||
|
||||
case battery == nil && batteryCapacity == nil && batteryController != nil:
|
||||
return &struct {
|
||||
*E3dc
|
||||
api.BatteryController
|
||||
}{
|
||||
E3dc: base,
|
||||
BatteryController: &decorateE3dcBatteryControllerImpl{
|
||||
batteryController: batteryController,
|
||||
},
|
||||
}
|
||||
|
||||
case battery == nil && batteryCapacity != nil && batteryController != nil:
|
||||
return &struct {
|
||||
*E3dc
|
||||
api.BatteryCapacity
|
||||
api.BatteryController
|
||||
}{
|
||||
E3dc: base,
|
||||
BatteryCapacity: &decorateE3dcBatteryCapacityImpl{
|
||||
batteryCapacity: batteryCapacity,
|
||||
},
|
||||
BatteryController: &decorateE3dcBatteryControllerImpl{
|
||||
batteryController: batteryController,
|
||||
},
|
||||
}
|
||||
|
||||
case battery != nil && batteryCapacity == nil && batteryController != nil:
|
||||
return &struct {
|
||||
*E3dc
|
||||
api.Battery
|
||||
api.BatteryController
|
||||
}{
|
||||
E3dc: base,
|
||||
Battery: &decorateE3dcBatteryImpl{
|
||||
battery: battery,
|
||||
},
|
||||
BatteryController: &decorateE3dcBatteryControllerImpl{
|
||||
batteryController: batteryController,
|
||||
},
|
||||
}
|
||||
|
||||
case battery != nil && batteryCapacity != nil && batteryController != nil:
|
||||
return &struct {
|
||||
*E3dc
|
||||
api.Battery
|
||||
api.BatteryCapacity
|
||||
api.BatteryController
|
||||
}{
|
||||
E3dc: base,
|
||||
Battery: &decorateE3dcBatteryImpl{
|
||||
battery: battery,
|
||||
},
|
||||
BatteryCapacity: &decorateE3dcBatteryCapacityImpl{
|
||||
batteryCapacity: batteryCapacity,
|
||||
},
|
||||
BatteryController: &decorateE3dcBatteryControllerImpl{
|
||||
batteryController: batteryController,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type decorateE3dcBatteryImpl struct {
|
||||
battery func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateE3dcBatteryImpl) Soc() (float64, error) {
|
||||
return impl.battery()
|
||||
}
|
||||
|
||||
type decorateE3dcBatteryCapacityImpl struct {
|
||||
batteryCapacity func() float64
|
||||
}
|
||||
|
||||
func (impl *decorateE3dcBatteryCapacityImpl) Capacity() float64 {
|
||||
return impl.batteryCapacity()
|
||||
}
|
||||
|
||||
type decorateE3dcBatteryControllerImpl struct {
|
||||
batteryController func(api.BatteryMode) error
|
||||
}
|
||||
|
||||
func (impl *decorateE3dcBatteryControllerImpl) SetBatteryMode(p0 api.BatteryMode) error {
|
||||
return impl.batteryController(p0)
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
template: e3dc
|
||||
deprecated: true
|
||||
products:
|
||||
- brand: E3/DC
|
||||
params:
|
||||
34
templates/definition/meter/e3dc-rscp.yaml
Normal file
34
templates/definition/meter/e3dc-rscp.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
template: e3dc-rscp
|
||||
products:
|
||||
- brand: E3/DC
|
||||
params:
|
||||
- name: usage
|
||||
choice: ["grid", "pv", "battery"]
|
||||
allinone: true
|
||||
- name: host
|
||||
- name: port
|
||||
default: 5033
|
||||
- name: user
|
||||
- name: password
|
||||
- name: key
|
||||
- name: battery
|
||||
type: number
|
||||
advanced: true
|
||||
- name: dischargelimit
|
||||
description:
|
||||
de: Entladelimit in W
|
||||
en: Discharge limit in W
|
||||
help:
|
||||
de: Limitiert die Entladeleistung im 'Halten' Batteriemodus
|
||||
en: Limits discharge power in 'Hold' battery mode
|
||||
type: number
|
||||
advanced: true
|
||||
render: |
|
||||
type: e3dc-rscp
|
||||
usage: {{ .usage }}
|
||||
uri: {{ .host }}:{{ .port }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
key: {{ .key }}
|
||||
battery: {{ .battery }}
|
||||
dischargelimit: {{ .dischargelimit }}
|
||||
|
|
@ -9,17 +9,6 @@ import (
|
|||
"dario.cat/mergo"
|
||||
)
|
||||
|
||||
type Usage int
|
||||
|
||||
//go:generate enumer -type Usage -trimprefix Usage -transform=lower
|
||||
const (
|
||||
UsageGrid Usage = iota
|
||||
UsagePV
|
||||
UsageBattery
|
||||
UsageCharge
|
||||
UsageAux
|
||||
)
|
||||
|
||||
const (
|
||||
ParamUsage = "usage"
|
||||
ParamModbus = "modbus"
|
||||
|
|
|
|||
12
util/templates/usage.go
Normal file
12
util/templates/usage.go
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package templates
|
||||
|
||||
type Usage int
|
||||
|
||||
//go:generate enumer -type Usage -trimprefix Usage -transform=lower -text
|
||||
const (
|
||||
UsageGrid Usage = iota
|
||||
UsagePV
|
||||
UsageBattery
|
||||
UsageCharge
|
||||
UsageAux
|
||||
)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by "enumer -type Usage -trimprefix Usage -transform=lower"; DO NOT EDIT.
|
||||
// Code generated by "enumer -type Usage -trimprefix Usage -transform=lower -text"; DO NOT EDIT.
|
||||
|
||||
package templates
|
||||
|
||||
|
|
@ -88,3 +88,15 @@ func (i Usage) IsAUsage() bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface for Usage
|
||||
func (i Usage) MarshalText() ([]byte, error) {
|
||||
return []byte(i.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface for Usage
|
||||
func (i *Usage) UnmarshalText(text []byte) error {
|
||||
var err error
|
||||
*i, err = UsageString(string(text))
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue