153 lines
2.6 KiB
Go
153 lines
2.6 KiB
Go
package fnn
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/evcc-io/evcc/api"
|
|
"github.com/evcc-io/evcc/core/site"
|
|
"github.com/evcc-io/evcc/hems/smartgrid"
|
|
"github.com/evcc-io/evcc/plugin"
|
|
"github.com/evcc-io/evcc/util"
|
|
)
|
|
|
|
type Fnn3 struct {
|
|
mu sync.Mutex
|
|
log *util.Logger
|
|
|
|
root api.Circuit
|
|
s1, s2, w3 func() (bool, error)
|
|
|
|
smartgridID uint
|
|
limit *float64
|
|
maxPower float64
|
|
interval time.Duration
|
|
}
|
|
|
|
// NewFromConfig creates an Fnn3 HEMS from generic config
|
|
func NewFromConfig(ctx context.Context, other map[string]any, site site.API) (*Fnn3, error) {
|
|
cc := struct {
|
|
MaxPower float64
|
|
W3 plugin.Config
|
|
S1 *plugin.Config
|
|
S2 *plugin.Config
|
|
Interval time.Duration
|
|
}{
|
|
Interval: 10 * time.Second,
|
|
}
|
|
|
|
if err := util.DecodeOther(other, &cc); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// setup grid control circuit
|
|
gridcontrol, err := smartgrid.SetupCircuit()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
site.SetCircuit(gridcontrol)
|
|
|
|
// s1 getter
|
|
s1G, err := cc.S1.BoolGetter(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s2G, err := cc.S2.BoolGetter(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
w3G, err := cc.W3.BoolGetter(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return NewFnn3(gridcontrol, s1G, s2G, w3G, cc.MaxPower, cc.Interval)
|
|
}
|
|
|
|
// NewFnn3 creates Fnn3 HEMS
|
|
func NewFnn3(root api.Circuit, s1, s2, w3 func() (bool, error), maxPower float64, interval time.Duration) (*Fnn3, error) {
|
|
c := &Fnn3{
|
|
log: util.NewLogger("fnn3"),
|
|
root: root,
|
|
maxPower: maxPower,
|
|
s1: s1,
|
|
s2: s2,
|
|
w3: w3,
|
|
interval: interval,
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
func (c *Fnn3) Run() {
|
|
for range time.Tick(c.interval) {
|
|
if err := c.run(); err != nil {
|
|
c.log.ERROR.Println(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Fnn3) run() error {
|
|
w3, err := c.w3()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if w3 {
|
|
// 0%
|
|
return c.curtail(0.0)
|
|
}
|
|
|
|
if c.s2 != nil {
|
|
s2, err := c.s2()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if s2 {
|
|
// 30%
|
|
return c.curtail(0.3)
|
|
}
|
|
}
|
|
|
|
if c.s1 != nil {
|
|
s1, err := c.s1()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if s1 {
|
|
// 60%
|
|
return c.curtail(0.6)
|
|
}
|
|
}
|
|
|
|
// 100%
|
|
return c.curtail(1.0)
|
|
}
|
|
|
|
func (c *Fnn3) curtail(frac float64) error {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
active := frac < 1.0
|
|
|
|
c.limit = nil
|
|
if active {
|
|
c.limit = new(c.maxPower * frac)
|
|
}
|
|
|
|
c.root.Curtail(active)
|
|
// TODO make ProductionNominalMax configurable (Site kWp)
|
|
// c.root.SetMaxPower(c.maxPower*frac)
|
|
|
|
if err := smartgrid.UpdateSession(&c.smartgridID, smartgrid.Curtail, c.root.GetChargePower(), c.maxPower*frac, active); err != nil {
|
|
c.log.ERROR.Printf("smartgrid session: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|