Loadpoint: add solar share setting (experimental)

This commit is contained in:
andig 2024-10-05 11:51:13 +02:00
parent 62d3e7d151
commit 42eb940fe8
6 changed files with 68 additions and 5 deletions

View file

@ -34,6 +34,7 @@ const (
SmartCostActive = "smartCostActive" // smart cost active
SmartCostLimit = "smartCostLimit" // smart cost limit
SmartCostNextStart = "smartCostNextStart" // smart cost next start
SolarShare = "solarShare" // solar share
// effective values
EffectivePriority = "effectivePriority" // effective priority

View file

@ -127,6 +127,7 @@ type Loadpoint struct {
limitSoc int // Session limit for soc
limitEnergy float64 // Session limit for energy
smartCostLimit *float64 // always charge if cost is below this value
solarShare float64 // solar share for pv mode
mode api.ChargeMode
enabled bool // Charger enabled state
@ -1308,9 +1309,13 @@ func (lp *Loadpoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64, batter
// notes: activePhases can be 1, 2 or 3 and phaseTimer can only be active if lp current is already at minCurrent
projectedSitePower -= Voltage * minCurrent * float64(activePhases-1)
}
// lp.Disable.Threshold
disableThreshold := (lp.GetSolarShare() - 1) * lp.EffectiveMinPower()
// kick off disable sequence
if projectedSitePower >= lp.Disable.Threshold {
lp.log.DEBUG.Printf("projected site power %.0fW >= %.0fW disable threshold", projectedSitePower, lp.Disable.Threshold)
if projectedSitePower >= disableThreshold {
lp.log.DEBUG.Printf("projected site power %.0fW >= %.0fW disable threshold", projectedSitePower, disableThreshold)
if lp.pvTimer.IsZero() {
lp.log.DEBUG.Printf("pv disable timer start: %v", lp.GetDisableDelay())
@ -1339,10 +1344,12 @@ func (lp *Loadpoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64, batter
}
if mode == api.ModePV && !lp.enabled {
// lp.Enable.Threshold
enableThreshold := -lp.GetSolarShare() * lp.EffectiveMinPower()
// kick off enable sequence
if (lp.Enable.Threshold == 0 && targetCurrent >= minCurrent) ||
(lp.Enable.Threshold != 0 && sitePower <= lp.Enable.Threshold) {
lp.log.DEBUG.Printf("site power %.0fW <= %.0fW enable threshold", sitePower, lp.Enable.Threshold)
if sitePower <= enableThreshold {
lp.log.DEBUG.Printf("site power %.0fW <= %.0fW enable threshold", sitePower, enableThreshold)
if lp.pvTimer.IsZero() {
lp.log.DEBUG.Printf("pv enable timer start: %v", lp.GetEnableDelay())

View file

@ -123,6 +123,9 @@ type API interface {
// SetSmartCostLimit sets the smart cost limit
SetSmartCostLimit(limit *float64)
SetSolarShare(float64)
GetSolarShare() float64
//
// power and energy
//

View file

@ -555,6 +555,28 @@ func (lp *Loadpoint) SetSmartCostLimit(val *float64) {
}
}
// GetSolarShare gets the solar share
func (lp *Loadpoint) GetSolarShare() float64 {
lp.RLock()
defer lp.RUnlock()
return lp.solarShare
}
// SetSolarShare sets the solar share
func (lp *Loadpoint) SetSolarShare(val float64) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set solar share:", "%.0f", 100*val)
if lp.solarShare != val {
lp.solarShare = val
lp.settings.SetFloat(keys.SolarShare, val)
lp.publish(keys.SolarShare, val)
}
}
// GetCircuit returns the assigned circuit
func (lp *Loadpoint) GetCircuit() api.Circuit {
lp.RLock()

View file

@ -112,6 +112,7 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, valueChan chan<- util.Param)
"residualpower": {"POST", "/residualpower/{value:-?[0-9.]+}", floatHandler(site.SetResidualPower, site.GetResidualPower)},
"smartcost": {"POST", "/smartcostlimit/{value:-?[0-9.]+}", updateSmartCostLimit(site)},
"smartcostdelete": {"DELETE", "/smartcostlimit", updateSmartCostLimit(site)},
"solar": {"POST", "/solarshare/{value:[0-9]+}", updateSolarShare(site)},
"tariff": {"GET", "/tariff/{tariff:[a-z]+}", tariffHandler(site)},
"sessions": {"GET", "/sessions", sessionHandler},
"updatesession": {"PUT", "/session/{id:[0-9]+}", updateSessionHandler},
@ -181,6 +182,7 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, valueChan chan<- util.Param)
"smartCost": {"POST", "/smartcostlimit/{value:-?[0-9.]+}", floatPtrHandler(pass(lp.SetSmartCostLimit), lp.GetSmartCostLimit)},
"smartCostDelete": {"DELETE", "/smartcostlimit", floatPtrHandler(pass(lp.SetSmartCostLimit), lp.GetSmartCostLimit)},
// "priority": {"POST", "/priority/{value:[0-9.]+}", floatHandler(pass(lp.SetPriority), lp.GetPriority)},
"solar": {"POST", "/solarshare/{value:[0-9]+}", scaledIntHandler(pass(lp.SetSolarShare), lp.GetSolarShare, 100)},
}
for _, r := range routes {

View file

@ -150,6 +150,15 @@ func intHandler(set func(int) error, get func() int) http.HandlerFunc {
return handler(strconv.Atoi, set, get)
}
// scaledIntHandler updates int-param api
func scaledIntHandler(set func(float64) error, get func() float64, scale float64) http.HandlerFunc {
return intHandler(func(v int) error {
return set(float64(v) / scale)
}, func() int {
return int(get() * scale)
})
}
// boolHandler updates bool-param api
func boolHandler(set func(bool) error, get func() bool) http.HandlerFunc {
return handler(strconv.ParseBool, set, get)
@ -191,6 +200,25 @@ func updateSmartCostLimit(site site.API) http.HandlerFunc {
}
}
// updateSolarShare sets the solar share limit globally
func updateSolarShare(site site.API) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
val, err := strconv.Atoi(vars["value"])
if err != nil {
jsonError(w, http.StatusBadRequest, err)
return
}
for _, lp := range site.Loadpoints() {
lp.SetSolarShare(float64(val) / 100)
}
jsonResult(w, val)
}
}
// stateHandler returns the combined state
func stateHandler(cache *util.Cache) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {