From 3986b75b085830311a00a73827a0c40ea144dca3 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 2 Sep 2021 14:16:05 +0200 Subject: [PATCH] Improve target charging and refactor loadpoint and site apis (#1470) --- charger/easee.go | 10 ++--- charger/eebus.go | 10 ++--- cmd/setup.go | 5 ++- core/loadpoint.go | 37 ++++++++-------- core/loadpoint/api.go | 43 ++++++++++++++++++ core/{ => loadpoint}/remote.go | 2 +- core/loadpoint_adapter.go | 24 ++++------ core/loadpoint_api.go | 38 ++-------------- core/site.go | 5 ++- core/site/api.go | 10 +++++ core/site_api.go | 8 +--- core/site_test.go | 4 -- core/soc/adapter.go | 5 ++- core/soc/timer.go | 81 +++++++++++++++++++--------------- hems/ocpp/ocpp.go | 8 ++-- hems/semp/semp.go | 17 +++---- server/http.go | 61 ++++++++++++------------- server/influxdb.go | 4 +- server/mqtt.go | 7 +-- server/uds.go | 5 ++- 20 files changed, 204 insertions(+), 180 deletions(-) create mode 100644 core/loadpoint/api.go rename core/{ => loadpoint}/remote.go (96%) create mode 100644 core/site/api.go diff --git a/charger/easee.go b/charger/easee.go index 58c65c6e2..790cd8d3a 100644 --- a/charger/easee.go +++ b/charger/easee.go @@ -24,7 +24,7 @@ import ( "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/charger/easee" - "github.com/evcc-io/evcc/core" + "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/util/request" "github.com/evcc-io/evcc/util/sponsor" @@ -40,7 +40,7 @@ type Easee struct { status easee.ChargerStatus updated time.Time cache time.Duration - lp core.LoadPointAPI + lp loadpoint.API //lastSmartCharging bool //lastChargeMode api.ChargeMode log *util.Logger @@ -348,9 +348,9 @@ func (c *Easee) Currents() (float64, float64, float64, error) { err } -var _ core.LoadpointController = (*Easee)(nil) +var _ loadpoint.Controller = (*Easee)(nil) -// LoadpointControl implements core.LoadpointController -func (c *Easee) LoadpointControl(lp core.LoadPointAPI) { +// LoadpointControl implements loadpoint.Controller +func (c *Easee) LoadpointControl(lp loadpoint.API) { c.lp = lp } diff --git a/charger/eebus.go b/charger/eebus.go index 8d54eb32c..2da829099 100644 --- a/charger/eebus.go +++ b/charger/eebus.go @@ -11,7 +11,7 @@ import ( "github.com/evcc-io/eebus/ship" "github.com/evcc-io/eebus/spine" "github.com/evcc-io/evcc/api" - "github.com/evcc-io/evcc/core" + "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/server" "github.com/evcc-io/evcc/util" ) @@ -19,7 +19,7 @@ import ( type EEBus struct { log *util.Logger cc *communication.ConnectionController - lp core.LoadPointAPI + lp loadpoint.API forcePVLimits bool communicationStandard communication.EVCommunicationStandardEnumType @@ -517,10 +517,10 @@ func (c *EEBus) SoC() (float64, error) { return data.EVData.Measurements.SoC, nil } -var _ core.LoadpointController = (*EEBus)(nil) +var _ loadpoint.Controller = (*EEBus)(nil) -// LoadpointControl implements core.LoadpointController -func (c *EEBus) LoadpointControl(lp core.LoadPointAPI) { +// LoadpointControl implements loadpoint.Controller +func (c *EEBus) LoadpointControl(lp loadpoint.API) { c.lp = lp // set current known min, max current limits diff --git a/cmd/setup.go b/cmd/setup.go index 36ec616d7..e3393631e 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -12,6 +12,7 @@ import ( "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/api/proto/pb" "github.com/evcc-io/evcc/core" + "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/hems" "github.com/evcc-io/evcc/provider/javascript" "github.com/evcc-io/evcc/provider/mqtt" @@ -94,8 +95,8 @@ func configureSponsorship(token string) error { return err } -// setup influx databases -func configureDatabase(conf server.InfluxConfig, loadPoints []core.LoadPointAPI, in <-chan util.Param) { +// setup influx database +func configureDatabase(conf server.InfluxConfig, loadPoints []loadpoint.API, in <-chan util.Param) { influx := server.NewInfluxClient( conf.URL, conf.Token, diff --git a/core/loadpoint.go b/core/loadpoint.go index 724fba956..c2694b1c3 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -10,6 +10,7 @@ import ( "time" "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/core/soc" "github.com/evcc-io/evcc/core/wrapper" "github.com/evcc-io/evcc/provider" @@ -120,13 +121,13 @@ type LoadPoint struct { socTimer *soc.Timer // cached state - status api.ChargeStatus // Charger status - remoteDemand RemoteDemand // External status demand - chargePower float64 // Charging power - chargeCurrents []float64 // Phase currents - connectedTime time.Time // Time when vehicle was connected - pvTimer time.Time // PV enabled/disable timer - phaseTimer time.Time // 1p3p switch timer + status api.ChargeStatus // Charger status + remoteDemand loadpoint.RemoteDemand // External status demand + chargePower float64 // Charging power + chargeCurrents []float64 // Phase currents + connectedTime time.Time // Time when vehicle was connected + pvTimer time.Time // PV enabled/disable timer + phaseTimer time.Time // 1p3p switch timer vehicleSoc float64 // Vehicle SoC chargedEnergy float64 // Charged energy while connected in Wh @@ -201,7 +202,7 @@ func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[strin lp.configureChargerType(lp.charger) // allow target charge handler to access loadpoint - lp.socTimer = soc.NewTimer(lp.log, lp.adapter(), lp.MaxCurrent) + lp.socTimer = soc.NewTimer(lp.log, &adapter{LoadPoint: lp}) if lp.Enable.Threshold > lp.Disable.Threshold { log.WARN.Printf("PV mode enable threshold (%.0fW) is larger than disable threshold (%.0fW)", lp.Enable.Threshold, lp.Disable.Threshold) } else if lp.Enable.Threshold > 0 { @@ -454,7 +455,7 @@ func (lp *LoadPoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even } // allow charger to access loadpoint - if ctrl, ok := lp.charger.(LoadpointController); ok { + if ctrl, ok := lp.charger.(loadpoint.Controller); ok { ctrl.LoadpointControl(lp) } } @@ -602,7 +603,7 @@ func (lp *LoadPoint) climateActive() bool { } // remoteControlled returns true if remote control status is active -func (lp *LoadPoint) remoteControlled(demand RemoteDemand) bool { +func (lp *LoadPoint) remoteControlled(demand loadpoint.RemoteDemand) bool { lp.Lock() defer lp.Unlock() @@ -1245,7 +1246,7 @@ func (lp *LoadPoint) Update(sitePower float64, cheap bool) { var err error // track if remote disabled is actually active - remoteDisabled := RemoteEnable + remoteDisabled := loadpoint.RemoteEnable // execute loading strategy switch { @@ -1265,8 +1266,8 @@ func (lp *LoadPoint) Update(sitePower float64, cheap bool) { lp.socTimer.Reset() // once SoC is reached, the target charge request is removed // OCPP has priority over target charging - case lp.remoteControlled(RemoteHardDisable): - remoteDisabled = RemoteHardDisable + case lp.remoteControlled(loadpoint.RemoteHardDisable): + remoteDisabled = loadpoint.RemoteHardDisable fallthrough case mode == api.ModeOff: @@ -1286,9 +1287,9 @@ func (lp *LoadPoint) Update(sitePower float64, cheap bool) { } // target charging - case lp.socTimer.StartRequired(): + case lp.socTimer.DemandActive(): targetCurrent := lp.socTimer.Handle() - err = lp.setLimit(targetCurrent, false) + err = lp.setLimit(targetCurrent, true) case mode == api.ModeMinPV || mode == api.ModePV: targetCurrent := lp.pvMaxCurrent(mode, sitePower) @@ -1308,8 +1309,8 @@ func (lp *LoadPoint) Update(sitePower float64, cheap bool) { } // Sunny Home Manager - if lp.remoteControlled(RemoteSoftDisable) { - remoteDisabled = RemoteSoftDisable + if lp.remoteControlled(loadpoint.RemoteSoftDisable) { + remoteDisabled = loadpoint.RemoteSoftDisable targetCurrent = 0 required = true } @@ -1318,7 +1319,7 @@ func (lp *LoadPoint) Update(sitePower float64, cheap bool) { } // effective disabled status - if remoteDisabled != RemoteEnable { + if remoteDisabled != loadpoint.RemoteEnable { lp.publish("remoteDisabled", remoteDisabled) } diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go new file mode 100644 index 000000000..8a9293f27 --- /dev/null +++ b/core/loadpoint/api.go @@ -0,0 +1,43 @@ +package loadpoint + +import ( + "time" + + "github.com/evcc-io/evcc/api" +) + +// Controller gives access to loadpoint +type Controller interface { + LoadpointControl(API) +} + +// API is the external loadpoint API +type API interface { + Name() string + HasChargeMeter() bool + + // status + GetStatus() api.ChargeStatus + + // settings + GetMode() api.ChargeMode + SetMode(api.ChargeMode) + GetTargetSoC() int + SetTargetSoC(int) error + GetMinSoC() int + SetMinSoC(int) error + GetPhases() int + SetPhases(int) error + + SetTargetCharge(time.Time, int) + RemoteControl(string, RemoteDemand) + + // energy + GetChargePower() float64 + GetMinCurrent() float64 + SetMinCurrent(float64) + GetMaxCurrent() float64 + SetMaxCurrent(float64) + GetMinPower() float64 + GetMaxPower() float64 +} diff --git a/core/remote.go b/core/loadpoint/remote.go similarity index 96% rename from core/remote.go rename to core/loadpoint/remote.go index d84c89bbd..03c3e8704 100644 --- a/core/remote.go +++ b/core/loadpoint/remote.go @@ -1,4 +1,4 @@ -package core +package loadpoint import "strings" diff --git a/core/loadpoint_adapter.go b/core/loadpoint_adapter.go index 9f18e7653..0c5ee7d14 100644 --- a/core/loadpoint_adapter.go +++ b/core/loadpoint_adapter.go @@ -1,27 +1,19 @@ package core -import "github.com/evcc-io/evcc/core/soc" +import ( + "github.com/evcc-io/evcc/core/soc" +) + +var _ soc.Adapter = (*adapter)(nil) type adapter struct { - lp *LoadPoint -} - -func (lp *LoadPoint) adapter() soc.Adapter { - return &adapter{lp: lp} + *LoadPoint } func (a *adapter) Publish(key string, val interface{}) { - a.lp.publish(key, val) + a.LoadPoint.publish(key, val) } func (a *adapter) SocEstimator() *soc.Estimator { - return a.lp.socEstimator -} - -func (a *adapter) ActivePhases() int { - return a.lp.activePhases -} - -func (a *adapter) Voltage() float64 { - return Voltage + return a.LoadPoint.socEstimator } diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go index 3a06023d7..155d807ed 100644 --- a/core/loadpoint_api.go +++ b/core/loadpoint_api.go @@ -4,43 +4,11 @@ import ( "time" "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/core/wrapper" ) -// LoadpointController gives access to loadpoint -type LoadpointController interface { - LoadpointControl(LoadPointAPI) -} - -// LoadPointAPI is the external loadpoint API -type LoadPointAPI interface { - Name() string - HasChargeMeter() bool - - // status - GetStatus() api.ChargeStatus - - // settings - GetMode() api.ChargeMode - SetMode(api.ChargeMode) - GetTargetSoC() int - SetTargetSoC(int) error - GetMinSoC() int - SetMinSoC(int) error - GetPhases() int - SetPhases(int) error - SetTargetCharge(time.Time, int) - RemoteControl(string, RemoteDemand) - - // energy - GetChargePower() float64 - GetMinCurrent() float64 - SetMinCurrent(float64) - GetMaxCurrent() float64 - SetMaxCurrent(float64) - GetMinPower() float64 - GetMaxPower() float64 -} +var _ loadpoint.API = (*LoadPoint)(nil) // GetStatus returns the charging status func (lp *LoadPoint) GetStatus() api.ChargeStatus { @@ -163,7 +131,7 @@ func (lp *LoadPoint) SetTargetCharge(finishAt time.Time, targetSoC int) { } // RemoteControl sets remote status demand -func (lp *LoadPoint) RemoteControl(source string, demand RemoteDemand) { +func (lp *LoadPoint) RemoteControl(source string, demand loadpoint.RemoteDemand) { lp.Lock() defer lp.Unlock() diff --git a/core/site.go b/core/site.go index de6452e9e..067f5f280 100644 --- a/core/site.go +++ b/core/site.go @@ -9,6 +9,7 @@ import ( "github.com/avast/retry-go/v3" "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/push" "github.com/evcc-io/evcc/util" ) @@ -105,8 +106,8 @@ func NewSite() *Site { } // LoadPoints returns the array of associated loadpoints -func (site *Site) LoadPoints() []LoadPointAPI { - res := make([]LoadPointAPI, len(site.loadpoints)) +func (site *Site) LoadPoints() []loadpoint.API { + res := make([]loadpoint.API, len(site.loadpoints)) for id, lp := range site.loadpoints { res[id] = lp } diff --git a/core/site/api.go b/core/site/api.go new file mode 100644 index 000000000..83962dcd6 --- /dev/null +++ b/core/site/api.go @@ -0,0 +1,10 @@ +package site + +import "github.com/evcc-io/evcc/core/loadpoint" + +// API is the external site API +type API interface { + Healthy() bool + LoadPoints() []loadpoint.API + SetPrioritySoC(float64) error +} diff --git a/core/site_api.go b/core/site_api.go index 32d6eb0f0..f8976aab4 100644 --- a/core/site_api.go +++ b/core/site_api.go @@ -4,14 +4,10 @@ import ( "errors" "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/core/site" ) -// SiteAPI is the external site API -type SiteAPI interface { - Healthy() bool - LoadPoints() []LoadPointAPI - SetPrioritySoC(float64) error -} +var _ site.API = (*Site)(nil) // GetPrioritySoC returns the PrioritySoC func (site *Site) GetPrioritySoC() float64 { diff --git a/core/site_test.go b/core/site_test.go index 60469828b..bbbda9556 100644 --- a/core/site_test.go +++ b/core/site_test.go @@ -4,10 +4,6 @@ import ( "testing" ) -func TestSiteApi(t *testing.T) { - var _ SiteAPI = NewSite() -} - func TestSitePower(t *testing.T) { tc := []struct { grid, battery, site float64 diff --git a/core/soc/adapter.go b/core/soc/adapter.go index 58ec2fdf7..6605a2977 100644 --- a/core/soc/adapter.go +++ b/core/soc/adapter.go @@ -1,9 +1,10 @@ package soc +import "github.com/evcc-io/evcc/core/loadpoint" + // Adapter provides the required methods for interacting with the loadpoint type Adapter interface { + loadpoint.API Publish(key string, val interface{}) SocEstimator() *Estimator - ActivePhases() int - Voltage() float64 } diff --git a/core/soc/timer.go b/core/soc/timer.go index 143cc3db8..6b7fd34b4 100644 --- a/core/soc/timer.go +++ b/core/soc/timer.go @@ -4,6 +4,7 @@ import ( "math" "time" + "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/util" ) @@ -14,21 +15,19 @@ const ( // Timer is the target charging handler type Timer struct { Adapter - log *util.Logger - maxCurrent float64 - current float64 - SoC int - Time time.Time - finishAt time.Time - chargeRequired bool + log *util.Logger + current float64 + SoC int + Time time.Time + finishAt time.Time + active bool } // NewTimer creates a Timer -func NewTimer(log *util.Logger, adapter Adapter, maxCurrent float64) *Timer { +func NewTimer(log *util.Logger, api Adapter) *Timer { lp := &Timer{ - log: log, - Adapter: adapter, - maxCurrent: maxCurrent, + log: log, + Adapter: api, } return lp @@ -40,62 +39,74 @@ func (lp *Timer) Reset() { return } - lp.current = float64(lp.maxCurrent) + lp.current = float64(lp.GetMaxCurrent()) lp.Time = time.Time{} lp.SoC = 0 } -// StartRequired calculates remaining charge duration and returns true if charge start is required to achieve target soc in time -func (lp *Timer) StartRequired() bool { +// DemandActive calculates remaining charge duration and returns true if charge start is required to achieve target soc in time +func (lp *Timer) DemandActive() bool { if lp == nil { return false } se := lp.SocEstimator() - if !lp.active() || se == nil { + if se == nil { + lp.log.WARN.Printf("target charging: not possible") return false } - power := float64(lp.ActivePhases()) * lp.maxCurrent * lp.Voltage() + defer func() { + lp.Publish("timerSet", lp.Time.After(time.Now())) + lp.Publish("timerActive", lp.active) + lp.Publish("timerProjectedEnd", lp.finishAt) + }() + + // power + power := lp.GetMaxPower() + if lp.active { + power *= lp.current / lp.GetMaxCurrent() + } // time remainingDuration := se.RemainingChargeDuration(power, lp.SoC) lp.finishAt = time.Now().Add(remainingDuration).Round(time.Minute) - lp.log.DEBUG.Printf("target charging active for %v: projected %v (%v remaining)", lp.Time, lp.finishAt, remainingDuration.Round(time.Minute)) - lp.chargeRequired = lp.finishAt.After(lp.Time) - lp.Publish("timerActive", lp.chargeRequired) + // timer charging is already active- only deactivate once charging has stopped + if lp.active { + if time.Now().After(lp.Time) && lp.GetStatus() != api.StatusC { + lp.log.TRACE.Printf("target charging: deactivating") + lp.active = false + } - return lp.chargeRequired -} - -// active returns true if there is an active target charging request -func (lp *Timer) active() bool { - inactive := lp.Time.IsZero() || lp.Time.Before(time.Now()) - lp.Publish("timerSet", !inactive) - - // reset active - if inactive && lp.chargeRequired { - lp.chargeRequired = false - lp.Publish("timerActive", lp.chargeRequired) + return lp.active } - return !inactive + // check if charging need be activated + if lp.active = lp.finishAt.After(lp.Time); lp.active { + lp.current = lp.GetMaxCurrent() + lp.log.DEBUG.Printf("target charging active for %v: projected %v (%v remaining)", lp.Time, lp.finishAt, remainingDuration.Round(time.Minute)) + } + + return lp.active } // Handle adjusts current up/down to achieve desired target time taking. func (lp *Timer) Handle() float64 { + action := "steady" + switch { case lp.finishAt.Before(lp.Time.Add(-deviation)): lp.current-- - lp.log.DEBUG.Printf("target charging: slowdown") + action = "slowdown" case lp.finishAt.After(lp.Time): lp.current++ - lp.log.DEBUG.Printf("target charging: speedup") + action = "speedup" } - lp.current = math.Max(math.Min(lp.current, float64(lp.maxCurrent)), 0) + lp.current = math.Max(math.Min(lp.current, lp.GetMaxCurrent()), lp.GetMinCurrent()) + lp.log.DEBUG.Printf("target charging: %s (%.3gA)", action, lp.current) return lp.current } diff --git a/hems/ocpp/ocpp.go b/hems/ocpp/ocpp.go index 5a52eb34e..c8745ab69 100644 --- a/hems/ocpp/ocpp.go +++ b/hems/ocpp/ocpp.go @@ -6,12 +6,12 @@ import ( "strings" "time" - "github.com/denisbrodbeck/machineid" "github.com/evcc-io/evcc/api" - "github.com/evcc-io/evcc/core" + "github.com/evcc-io/evcc/core/site" "github.com/evcc-io/evcc/hems/ocpp/profile" "github.com/evcc-io/evcc/util" + "github.com/denisbrodbeck/machineid" ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" ocppcore "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" "github.com/lorenzodonini/ocpp-go/ws" @@ -21,14 +21,14 @@ import ( type OCPP struct { log *util.Logger cache *util.Cache - site core.SiteAPI + site site.API cp ocpp16.ChargePoint } const retryTimeout = 5 * time.Second // New generates OCPP chargepoint client -func New(conf map[string]interface{}, site core.SiteAPI, cache *util.Cache) (*OCPP, error) { +func New(conf map[string]interface{}, site site.API, cache *util.Cache) (*OCPP, error) { cc := struct { URI string StationID string diff --git a/hems/semp/semp.go b/hems/semp/semp.go index 711a783cd..bd7716b08 100644 --- a/hems/semp/semp.go +++ b/hems/semp/semp.go @@ -14,7 +14,8 @@ import ( "github.com/denisbrodbeck/machineid" "github.com/evcc-io/evcc/api" - "github.com/evcc-io/evcc/core" + "github.com/evcc-io/evcc/core/loadpoint" + "github.com/evcc-io/evcc/core/site" "github.com/evcc-io/evcc/server" "github.com/evcc-io/evcc/util" "github.com/google/uuid" @@ -49,11 +50,11 @@ type SEMP struct { uid string hostURI string port int - site core.SiteAPI + site site.API } // New generates SEMP Gateway listening at /semp endpoint -func New(conf map[string]interface{}, site core.SiteAPI, cache *util.Cache, httpd *server.HTTPd) (*SEMP, error) { +func New(conf map[string]interface{}, site site.API, cache *util.Cache, httpd *server.HTTPd) (*SEMP, error) { cc := struct { VendorID string DeviceID string @@ -363,7 +364,7 @@ func (s *SEMP) deviceID(id int) string { return fmt.Sprintf(sempDeviceId, s.vid, ^uint64(0xffff<<48)&(binary.BigEndian.Uint64(did)+uint64(id))) } -func (s *SEMP) deviceInfo(id int, lp core.LoadPointAPI) DeviceInfo { +func (s *SEMP) deviceInfo(id int, lp loadpoint.API) DeviceInfo { method := MethodEstimation if lp.HasChargeMeter() { method = MethodMeasurement @@ -399,7 +400,7 @@ func (s *SEMP) allDeviceInfo() (res []DeviceInfo) { return res } -func (s *SEMP) deviceStatus(id int, lp core.LoadPointAPI) DeviceStatus { +func (s *SEMP) deviceStatus(id int, lp loadpoint.API) DeviceStatus { chargePower := lp.GetChargePower() mode := lp.GetMode() @@ -438,7 +439,7 @@ func (s *SEMP) allDeviceStatus() (res []DeviceStatus) { // TODO remove GetChecked function -func (s *SEMP) planningRequest(id int, lp core.LoadPointAPI) (res PlanningRequest) { +func (s *SEMP) planningRequest(id int, lp loadpoint.API) (res PlanningRequest) { mode := lp.GetMode() charging := lp.GetStatus() == api.StatusC connected := charging || lp.GetStatus() == api.StatusB @@ -531,9 +532,9 @@ func (s *SEMP) deviceControlHandler(w http.ResponseWriter, r *http.Request) { return } - demand := core.RemoteSoftDisable + demand := loadpoint.RemoteSoftDisable if dev.On { - demand = core.RemoteEnable + demand = loadpoint.RemoteEnable } lp.RemoteControl(sempController, demand) diff --git a/server/http.go b/server/http.go index a6559be63..6ac4e37f6 100644 --- a/server/http.go +++ b/server/http.go @@ -11,7 +11,8 @@ import ( "time" "github.com/evcc-io/evcc/api" - "github.com/evcc-io/evcc/core" + "github.com/evcc-io/evcc/core/loadpoint" + "github.com/evcc-io/evcc/core/site" "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/util/test" "github.com/gorilla/handlers" @@ -61,7 +62,7 @@ func routeLogger(inner http.Handler) http.HandlerFunc { } } -func indexHandler(site core.SiteAPI) http.HandlerFunc { +func indexHandler(site site.API) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=UTF-8") @@ -102,7 +103,7 @@ func jsonResponse(w http.ResponseWriter, r *http.Request, content interface{}) { } // HealthHandler returns current charge mode -func HealthHandler(site core.SiteAPI) http.HandlerFunc { +func HealthHandler(site site.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if !site.Healthy() { w.WriteHeader(http.StatusInternalServerError) @@ -155,15 +156,15 @@ func StateHandler(cache *util.Cache) http.HandlerFunc { } // CurrentChargeModeHandler returns current charge mode -func CurrentChargeModeHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func CurrentChargeModeHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - res := chargeModeJSON{Mode: loadpoint.GetMode()} + res := chargeModeJSON{Mode: lp.GetMode()} jsonResponse(w, r, res) } } // ChargeModeHandler updates charge mode -func ChargeModeHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func ChargeModeHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -176,23 +177,23 @@ func ChargeModeHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { return } - loadpoint.SetMode(mode) + lp.SetMode(mode) - res := chargeModeJSON{Mode: loadpoint.GetMode()} + res := chargeModeJSON{Mode: lp.GetMode()} jsonResponse(w, r, res) } } // CurrentTargetSoCHandler returns current target soc -func CurrentTargetSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func CurrentTargetSoCHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - res := targetSoCJSON{TargetSoC: loadpoint.GetTargetSoC()} + res := targetSoCJSON{TargetSoC: lp.GetTargetSoC()} jsonResponse(w, r, res) } } // TargetSoCHandler updates target soc -func TargetSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func TargetSoCHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -200,7 +201,7 @@ func TargetSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { soc, err := strconv.ParseInt(socS, 10, 32) if err == nil { - err = loadpoint.SetTargetSoC(int(soc)) + err = lp.SetTargetSoC(int(soc)) } if err != nil { @@ -209,21 +210,21 @@ func TargetSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { return } - res := targetSoCJSON{TargetSoC: loadpoint.GetTargetSoC()} + res := targetSoCJSON{TargetSoC: lp.GetTargetSoC()} jsonResponse(w, r, res) } } // CurrentMinSoCHandler returns current minimum soc -func CurrentMinSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func CurrentMinSoCHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - res := minSoCJSON{MinSoC: loadpoint.GetMinSoC()} + res := minSoCJSON{MinSoC: lp.GetMinSoC()} jsonResponse(w, r, res) } } // MinSoCHandler updates minimum soc -func MinSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func MinSoCHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -231,7 +232,7 @@ func MinSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { soc, err := strconv.ParseInt(socS, 10, 32) if err == nil { - err = loadpoint.SetMinSoC(int(soc)) + err = lp.SetMinSoC(int(soc)) } if err != nil { @@ -240,21 +241,21 @@ func MinSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { return } - res := minSoCJSON{MinSoC: loadpoint.GetMinSoC()} + res := minSoCJSON{MinSoC: lp.GetMinSoC()} jsonResponse(w, r, res) } } // CurrentPhasesHandler returns current minimum soc -func CurrentPhasesHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func CurrentPhasesHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - res := phasesJSON{Phases: loadpoint.GetPhases()} + res := phasesJSON{Phases: lp.GetPhases()} jsonResponse(w, r, res) } } // PhasesHandler updates minimum soc -func PhasesHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func PhasesHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -262,7 +263,7 @@ func PhasesHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { phases, err := strconv.ParseInt(phasesS, 10, 32) if err == nil { - err = loadpoint.SetPhases(int(phases)) + err = lp.SetPhases(int(phases)) } if err != nil { @@ -271,13 +272,13 @@ func PhasesHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { return } - res := phasesJSON{Phases: loadpoint.GetPhases()} + res := phasesJSON{Phases: lp.GetPhases()} jsonResponse(w, r, res) } } // RemoteDemandHandler updates minimum soc -func RemoteDemandHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func RemoteDemandHandler(lp loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -288,7 +289,7 @@ func RemoteDemandHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { source, ok = vars["source"] } - demand, err := core.RemoteDemandString(demandS) + demand, err := loadpoint.RemoteDemandString(demandS) if !ok || err != nil { w.WriteHeader(http.StatusBadRequest) @@ -296,11 +297,11 @@ func RemoteDemandHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { return } - loadpoint.RemoteControl(source, demand) + lp.RemoteControl(source, demand) res := struct { - Demand core.RemoteDemand `json:"demand"` - Source string `json:"source"` + Demand loadpoint.RemoteDemand `json:"demand"` + Source string `json:"source"` }{ Source: source, Demand: demand, @@ -321,7 +322,7 @@ func timezone() *time.Location { } // TargetChargeHandler updates target soc -func TargetChargeHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { +func TargetChargeHandler(loadpoint loadpoint.API) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -369,7 +370,7 @@ type HTTPd struct { } // NewHTTPd creates HTTP server with configured routes for loadpoint -func NewHTTPd(url string, site core.SiteAPI, hub *SocketHub, cache *util.Cache) *HTTPd { +func NewHTTPd(url string, site site.API, hub *SocketHub, cache *util.Cache) *HTTPd { routes := map[string]route{ "health": {[]string{"GET"}, "/health", HealthHandler(site)}, "state": {[]string{"GET"}, "/state", StateHandler(cache)}, diff --git a/server/influxdb.go b/server/influxdb.go index a86d86867..f28ba9feb 100644 --- a/server/influxdb.go +++ b/server/influxdb.go @@ -5,7 +5,7 @@ import ( "sync" "time" - "github.com/evcc-io/evcc/core" + "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/util" influxdb2 "github.com/influxdata/influxdb-client-go/v2" influxlog "github.com/influxdata/influxdb-client-go/v2/log" @@ -73,7 +73,7 @@ func (m *Influx) supportedType(p util.Param) bool { } // Run Influx publisher -func (m *Influx) Run(loadPoints []core.LoadPointAPI, in <-chan util.Param) { +func (m *Influx) Run(loadPoints []loadpoint.API, in <-chan util.Param) { writer := m.client.WriteAPI(m.org, m.database) // log errors diff --git a/server/mqtt.go b/server/mqtt.go index 83ded260a..2be4529f7 100644 --- a/server/mqtt.go +++ b/server/mqtt.go @@ -6,7 +6,8 @@ import ( "time" "github.com/evcc-io/evcc/api" - "github.com/evcc-io/evcc/core" + "github.com/evcc-io/evcc/core/loadpoint" + "github.com/evcc-io/evcc/core/site" "github.com/evcc-io/evcc/provider/mqtt" "github.com/evcc-io/evcc/util" ) @@ -64,7 +65,7 @@ func (m *MQTT) publish(topic string, retained bool, payload interface{}) { m.publishSingleValue(topic, retained, payload) } -func (m *MQTT) listenSetters(topic string, apiHandler core.LoadPointAPI) { +func (m *MQTT) listenSetters(topic string, apiHandler loadpoint.API) { m.Handler.Listen(topic+"/mode/set", func(payload string) { apiHandler.SetMode(api.ChargeMode(payload)) }) @@ -89,7 +90,7 @@ 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) { +func (m *MQTT) Run(site site.API, in <-chan util.Param) { // alive topic := fmt.Sprintf("%s/status", m.root) m.publish(topic, true, "online") diff --git a/server/uds.go b/server/uds.go index 0c41f5b12..5d50c97d9 100644 --- a/server/uds.go +++ b/server/uds.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package server @@ -7,7 +8,7 @@ import ( "net/http" "os" - "github.com/evcc-io/evcc/core" + "github.com/evcc-io/evcc/core/site" ) // SocketPath is the unix domain socket path @@ -26,7 +27,7 @@ func remoteIfExists(file string) { } // HealthListener attaches listener to unix domain socket and runs listener -func HealthListener(site core.SiteAPI) { +func HealthListener(site site.API) { remoteIfExists(SocketPath) l, err := net.Listen("unix", SocketPath)