From ca988fcf2b22d355cd782f2ee753924996de0df0 Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 2 Feb 2021 16:53:38 +0100 Subject: [PATCH] Add prioritySoC api (mqtt only) (#633) --- README.md | 1 + core/site.go | 15 +++++++++------ core/site_api.go | 36 ++++++++++++++++++++++++++++++++++++ server/mqtt.go | 10 ++++++++++ 4 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 core/site_api.go diff --git a/README.md b/README.md index b415d5c93..732b42379 100644 --- a/README.md +++ b/README.md @@ -501,6 +501,7 @@ The MQTT API follows the REST API's structure, with loadpoint ids starting at `0 - `evcc`: root topic - `evcc/updated`: timestamp of last update - `evcc/site`: site dynamic state +- `evcc/site/prioritySoC`: battery priority SoC (writable) - `evcc/loadpoints`: number of available loadpoints - `evcc/loadpoints/`: loadpoint dynamic state - `evcc/loadpoints//mode`: loadpoint charge mode (writable) diff --git a/core/site.go b/core/site.go index 0b264a308..556f8415c 100644 --- a/core/site.go +++ b/core/site.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math" + "sync" "time" "github.com/andig/evcc/api" @@ -26,6 +27,7 @@ type Site struct { *Health + sync.Mutex log *util.Logger // configuration @@ -55,12 +57,6 @@ type MetersConfig struct { BatteryMeterRef string `mapstructure:"battery"` // Battery charging meter reference } -// SiteAPI is the external site API -type SiteAPI interface { - Healthy() bool - LoadPoints() []LoadPointAPI -} - // NewSiteFromConfig creates a new site func NewSiteFromConfig( log *util.Logger, @@ -158,6 +154,10 @@ func (site *Site) DumpConfig() { meterCapabilities("battery", site.batteryMeter), fmt.Sprintf("soc %s", presence[ok]), ) + + if ok { + site.publish("prioritySoC", site.PrioritySoC) + } } for i, lp := range site.loadpoints { @@ -287,6 +287,9 @@ func (site *Site) sitePower() (float64, error) { site.log.DEBUG.Printf("battery soc: %.0f%%", soc) site.publish("batterySoC", math.Trunc(soc)) + site.Lock() + defer site.Unlock() + // if battery is charging give it priority if soc < site.PrioritySoC && batteryPower < 0 { site.log.DEBUG.Printf("giving priority to battery at soc: %.0f", soc) diff --git a/core/site_api.go b/core/site_api.go new file mode 100644 index 000000000..c13cbfe17 --- /dev/null +++ b/core/site_api.go @@ -0,0 +1,36 @@ +package core + +import ( + "errors" + + "github.com/andig/evcc/api" +) + +// SiteAPI is the external site API +type SiteAPI interface { + Healthy() bool + LoadPoints() []LoadPointAPI + SetPrioritySoC(float64) error +} + +// GetPrioritySoC returns the PrioritySoC +func (site *Site) GetPrioritySoC() float64 { + site.Lock() + defer site.Unlock() + return site.PrioritySoC +} + +// SetPrioritySoC sets the PrioritySoC +func (site *Site) SetPrioritySoC(soc float64) error { + site.Lock() + defer site.Unlock() + + if _, ok := site.batteryMeter.(api.Battery); !ok { + return errors.New("battery not configured") + } + + site.PrioritySoC = soc + site.publish("prioritySoC", site.PrioritySoC) + + return nil +} diff --git a/server/mqtt.go b/server/mqtt.go index 0ae0a3bb5..ea3ff7184 100644 --- a/server/mqtt.go +++ b/server/mqtt.go @@ -88,10 +88,19 @@ func (m *MQTT) listenSetters(topic string, apiHandler core.LoadPointAPI) { // Run starts the MQTT publisher for the MQTT API func (m *MQTT) Run(site core.SiteAPI, in <-chan util.Param) { + // site setters + m.Handler.Listen(fmt.Sprintf("%s/site/prioritySoC/set", m.root), func(payload string) { + soc, err := strconv.Atoi(payload) + if err == nil { + _ = site.SetPrioritySoC(float64(soc)) + } + }) + // number of loadpoints topic := fmt.Sprintf("%s/loadpoints", m.root) m.publish(topic, true, len(site.LoadPoints())) + // loadpoint setters for id, lp := range site.LoadPoints() { topic := fmt.Sprintf("%s/loadpoints/%d", m.root, id+1) m.listenSetters(topic, lp) @@ -101,6 +110,7 @@ func (m *MQTT) Run(site core.SiteAPI, in <-chan util.Param) { updated := time.Now().Unix() m.publish(fmt.Sprintf("%s/updated", m.root), true, updated) + // publish for p := range in { topic := fmt.Sprintf("%s/site", m.root) if p.LoadPoint != nil {