Loadpoint: add battery boost (experimental) (#16599)
This commit is contained in:
parent
52a9082c90
commit
16c467a65d
11 changed files with 165 additions and 27 deletions
|
|
@ -14,6 +14,7 @@ const (
|
|||
DisableThreshold = "disableThreshold"
|
||||
EnableDelay = "enableDelay"
|
||||
DisableDelay = "disableDelay"
|
||||
BatteryBoost = "batteryBoost"
|
||||
|
||||
PhasesConfigured = "phasesConfigured" // configured phases (1/3, 0 for auto on 1p3p chargers, nil for plain chargers)
|
||||
PhasesEnabled = "phasesEnabled" // enabled phases (1/3)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ const (
|
|||
|
||||
chargerSwitchDuration = 60 * time.Second // allow out of sync during this timespan
|
||||
phaseSwitchDuration = 60 * time.Second // allow out of sync and do not measure phases during this timespan
|
||||
|
||||
// battery boost states
|
||||
boostDisabled = 0
|
||||
boostStart = 1
|
||||
boostContinue = 2
|
||||
)
|
||||
|
||||
// elapsed is the time an expired timer will be set to
|
||||
|
|
@ -127,6 +132,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
|
||||
batteryBoost int // battery boost state
|
||||
|
||||
mode api.ChargeMode
|
||||
enabled bool // Charger enabled state
|
||||
|
|
@ -531,6 +537,11 @@ func (lp *Loadpoint) evVehicleDisconnectHandler() {
|
|||
// soc update reset
|
||||
lp.socUpdated = time.Time{}
|
||||
|
||||
// boost
|
||||
if err := lp.SetBatteryBoost(false); err != nil {
|
||||
lp.log.ERROR.Printf("battery boost: %v", err)
|
||||
}
|
||||
|
||||
// reset session
|
||||
lp.SetLimitSoc(0)
|
||||
lp.SetLimitEnergy(0)
|
||||
|
|
@ -800,10 +811,16 @@ func (lp *Loadpoint) syncCharger() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// coarseCurrent returns true if charger or vehicle require full amp steps
|
||||
func (lp *Loadpoint) coarseCurrent() bool {
|
||||
_, ok := lp.charger.(api.ChargerEx)
|
||||
return !ok || lp.vehicleHasFeature(api.CoarseCurrent)
|
||||
}
|
||||
|
||||
// roundedCurrent rounds current down to full amps if charger or vehicle require it
|
||||
func (lp *Loadpoint) roundedCurrent(chargeCurrent float64) float64 {
|
||||
// full amps only?
|
||||
if _, ok := lp.charger.(api.ChargerEx); !ok || lp.vehicleHasFeature(api.CoarseCurrent) {
|
||||
if lp.coarseCurrent() {
|
||||
chargeCurrent = math.Trunc(chargeCurrent)
|
||||
}
|
||||
return chargeCurrent
|
||||
|
|
@ -1273,12 +1290,44 @@ func (lp *Loadpoint) publishTimer(name string, delay time.Duration, action strin
|
|||
}
|
||||
}
|
||||
|
||||
// boostPower returns the additional power that the loadpoint should draw from the battery
|
||||
func (lp *Loadpoint) boostPower(batteryBoostPower float64, batteryBuffered bool) float64 {
|
||||
boost := lp.getBatteryBoost()
|
||||
if boost == boostDisabled || !batteryBuffered {
|
||||
return 0
|
||||
}
|
||||
|
||||
// push demand to drain battery
|
||||
delta := lp.effectiveStepPower()
|
||||
|
||||
// start boosting by setting maximum power
|
||||
if boost == boostStart {
|
||||
delta = lp.EffectiveMaxPower()
|
||||
|
||||
// expire timers
|
||||
lp.phaseTimer = elapsed
|
||||
lp.pvTimer = elapsed
|
||||
|
||||
if lp.charging() {
|
||||
lp.setBatteryBoost(boostContinue)
|
||||
}
|
||||
}
|
||||
|
||||
res := batteryBoostPower + delta
|
||||
lp.log.DEBUG.Printf("pv charge battery boost: %.0fW = -%.0fW battery - %.0fW boost", -res, batteryBoostPower, delta)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// pvMaxCurrent calculates the maximum target current for PV mode
|
||||
func (lp *Loadpoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64, batteryBuffered, batteryStart bool) float64 {
|
||||
func (lp *Loadpoint) pvMaxCurrent(mode api.ChargeMode, sitePower, batteryBoostPower float64, batteryBuffered, batteryStart bool) float64 {
|
||||
// read only once to simplify testing
|
||||
minCurrent := lp.effectiveMinCurrent()
|
||||
maxCurrent := lp.effectiveMaxCurrent()
|
||||
|
||||
// push demand to drain battery
|
||||
sitePower -= lp.boostPower(batteryBoostPower, batteryBuffered)
|
||||
|
||||
// switch phases up/down
|
||||
var scaledTo int
|
||||
if lp.hasPhaseSwitching() && lp.phaseSwitchCompleted() {
|
||||
|
|
@ -1656,7 +1705,7 @@ func (lp *Loadpoint) phaseSwitchCompleted() bool {
|
|||
}
|
||||
|
||||
// Update is the main control function. It reevaluates meters and charger state
|
||||
func (lp *Loadpoint) Update(sitePower float64, rates api.Rates, batteryBuffered, batteryStart bool, greenShare float64, effPrice, effCo2 *float64) {
|
||||
func (lp *Loadpoint) Update(sitePower, batteryBoostPower float64, rates api.Rates, batteryBuffered, batteryStart bool, greenShare float64, effPrice, effCo2 *float64) {
|
||||
// smart cost
|
||||
smartCostActive := lp.smartCostActive(rates)
|
||||
lp.publish(keys.SmartCostActive, smartCostActive)
|
||||
|
|
@ -1781,7 +1830,7 @@ func (lp *Loadpoint) Update(sitePower float64, rates api.Rates, batteryBuffered,
|
|||
break
|
||||
}
|
||||
|
||||
targetCurrent := lp.pvMaxCurrent(mode, sitePower, batteryBuffered, batteryStart)
|
||||
targetCurrent := lp.pvMaxCurrent(mode, sitePower, batteryBoostPower, batteryBuffered, batteryStart)
|
||||
|
||||
if targetCurrent == 0 && lp.vehicleClimateActive() {
|
||||
targetCurrent = lp.effectiveMinCurrent()
|
||||
|
|
|
|||
|
|
@ -111,6 +111,11 @@ type API interface {
|
|||
// SetDisableDelay sets loadpoint disable delay
|
||||
SetDisableDelay(delay time.Duration)
|
||||
|
||||
// GetBatteryBoost returns the battery boost
|
||||
GetBatteryBoost() bool
|
||||
// SetBatteryBoost sets the battery boost
|
||||
SetBatteryBoost(enable bool) error
|
||||
|
||||
// RemoteControl sets remote status demand
|
||||
RemoteControl(string, RemoteDemand)
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,20 @@ func (mr *MockAPIMockRecorder) EffectivePriority() *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EffectivePriority", reflect.TypeOf((*MockAPI)(nil).EffectivePriority))
|
||||
}
|
||||
|
||||
// GetBatteryBoost mocks base method.
|
||||
func (m *MockAPI) GetBatteryBoost() bool {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBatteryBoost")
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetBatteryBoost indicates an expected call of GetBatteryBoost.
|
||||
func (mr *MockAPIMockRecorder) GetBatteryBoost() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBatteryBoost", reflect.TypeOf((*MockAPI)(nil).GetBatteryBoost))
|
||||
}
|
||||
|
||||
// GetChargePower mocks base method.
|
||||
func (m *MockAPI) GetChargePower() float64 {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
@ -501,6 +515,20 @@ func (mr *MockAPIMockRecorder) RemoteControl(arg0, arg1 any) *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteControl", reflect.TypeOf((*MockAPI)(nil).RemoteControl), arg0, arg1)
|
||||
}
|
||||
|
||||
// SetBatteryBoost mocks base method.
|
||||
func (m *MockAPI) SetBatteryBoost(arg0 bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetBatteryBoost", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetBatteryBoost indicates an expected call of SetBatteryBoost.
|
||||
func (mr *MockAPIMockRecorder) SetBatteryBoost(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBatteryBoost", reflect.TypeOf((*MockAPI)(nil).SetBatteryBoost), arg0)
|
||||
}
|
||||
|
||||
// SetDisableDelay mocks base method.
|
||||
func (m *MockAPI) SetDisableDelay(arg0 time.Duration) {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ func (lp *Loadpoint) SetMode(mode api.ChargeMode) {
|
|||
if lp.mode != mode {
|
||||
lp.setMode(mode)
|
||||
|
||||
lp.batteryBoost = 0
|
||||
|
||||
// reset timers
|
||||
switch mode {
|
||||
case api.ModeNow, api.ModeOff:
|
||||
|
|
@ -316,6 +318,49 @@ func (lp *Loadpoint) SetDisableDelay(delay time.Duration) {
|
|||
}
|
||||
}
|
||||
|
||||
// getBatteryBoost returns the battery boost
|
||||
func (lp *Loadpoint) getBatteryBoost() int {
|
||||
lp.RLock()
|
||||
defer lp.RUnlock()
|
||||
return lp.batteryBoost
|
||||
}
|
||||
|
||||
// GetBatteryBoost returns the battery boost
|
||||
func (lp *Loadpoint) GetBatteryBoost() bool {
|
||||
return lp.getBatteryBoost() > 0
|
||||
}
|
||||
|
||||
// setBatteryBoost returns the battery boost
|
||||
func (lp *Loadpoint) setBatteryBoost(boost int) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
lp.batteryBoost = boost
|
||||
}
|
||||
|
||||
// SetBatteryBoost sets the battery boost
|
||||
func (lp *Loadpoint) SetBatteryBoost(enable bool) error {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
if enable && lp.mode != api.ModePV && lp.mode != api.ModeMinPV {
|
||||
return errors.New("battery boost is only available in PV modes")
|
||||
}
|
||||
|
||||
lp.log.DEBUG.Println("set battery boost:", enable)
|
||||
|
||||
if enable != (lp.batteryBoost != boostDisabled) {
|
||||
lp.publish(keys.BatteryBoost, enable)
|
||||
|
||||
lp.batteryBoost = boostDisabled
|
||||
if enable {
|
||||
lp.batteryBoost = boostStart
|
||||
lp.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoteControl sets remote status demand
|
||||
func (lp *Loadpoint) RemoteControl(source string, demand loadpoint.RemoteDemand) {
|
||||
lp.Lock()
|
||||
|
|
|
|||
|
|
@ -124,6 +124,11 @@ func (lp *Loadpoint) effectiveLimitSoc() int {
|
|||
return 100
|
||||
}
|
||||
|
||||
// effectiveStepPower returns the effective step power for the currently active phases
|
||||
func (lp *Loadpoint) effectiveStepPower() float64 {
|
||||
return Voltage * float64(lp.ActivePhases())
|
||||
}
|
||||
|
||||
// EffectiveMinPower returns the effective min power for the minimum active phases
|
||||
func (lp *Loadpoint) EffectiveMinPower() float64 {
|
||||
return Voltage * lp.effectiveMinCurrent() * float64(lp.minActivePhases())
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ func TestUpdatePowerZero(t *testing.T) {
|
|||
}
|
||||
|
||||
lp.mode = tc.mode
|
||||
lp.Update(0, nil, false, false, 0, nil, nil) // false,sitePower false,0
|
||||
lp.Update(0, 0, nil, false, false, 0, nil, nil) // false,sitePower false,0
|
||||
|
||||
ctrl.Finish()
|
||||
}
|
||||
|
|
@ -338,7 +338,7 @@ func TestPVHysteresis(t *testing.T) {
|
|||
// charger.EXPECT().Enabled().Return(tc.enabled, nil)
|
||||
|
||||
lp.enabled = tc.enabled
|
||||
current := lp.pvMaxCurrent(api.ModePV, se.site, false, false)
|
||||
current := lp.pvMaxCurrent(api.ModePV, se.site, 0, false, false)
|
||||
|
||||
if current != se.current {
|
||||
t.Errorf("step %d: wanted %.1f, got %.1f", step, se.current, current)
|
||||
|
|
@ -371,7 +371,7 @@ func TestPVHysteresisForStatusOtherThanC(t *testing.T) {
|
|||
|
||||
// maxCurrent will read enabled state in PV mode
|
||||
sitePower := -float64(phases)*minA*Voltage + 1 // 1W below min power
|
||||
current := lp.pvMaxCurrent(api.ModePV, sitePower, false, false)
|
||||
current := lp.pvMaxCurrent(api.ModePV, sitePower, 0, false, false)
|
||||
|
||||
if current != 0 {
|
||||
t.Errorf("PV mode could not disable charger as expected. Expected 0, got %.f", current)
|
||||
|
|
@ -428,7 +428,7 @@ func TestDisableAndEnableAtTargetSoc(t *testing.T) {
|
|||
charger.EXPECT().Status().Return(api.StatusC, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().MaxCurrent(int64(maxA)).Return(nil)
|
||||
lp.Update(500, nil, false, false, 0, nil, nil)
|
||||
lp.Update(500, 0, nil, false, false, 0, nil, nil)
|
||||
ctrl.Finish()
|
||||
|
||||
t.Log("charging above target - soc deactivates charger")
|
||||
|
|
@ -437,7 +437,7 @@ func TestDisableAndEnableAtTargetSoc(t *testing.T) {
|
|||
charger.EXPECT().Status().Return(api.StatusC, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Enable(false).Return(nil)
|
||||
lp.Update(500, nil, false, false, 0, nil, nil)
|
||||
lp.Update(500, 0, nil, false, false, 0, nil, nil)
|
||||
ctrl.Finish()
|
||||
|
||||
t.Log("deactivated charger changes status to B")
|
||||
|
|
@ -445,14 +445,14 @@ func TestDisableAndEnableAtTargetSoc(t *testing.T) {
|
|||
vehicle.EXPECT().Soc().Return(95.0, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusB, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
lp.Update(-500, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-500, 0, nil, false, false, 0, nil, nil)
|
||||
ctrl.Finish()
|
||||
|
||||
t.Log("soc has risen below target - soc update prevented by timer")
|
||||
clock.Add(5 * time.Minute)
|
||||
charger.EXPECT().Status().Return(api.StatusB, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
lp.Update(-500, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-500, 0, nil, false, false, 0, nil, nil)
|
||||
ctrl.Finish()
|
||||
|
||||
t.Log("soc has fallen below target - soc update timer expired")
|
||||
|
|
@ -462,7 +462,7 @@ func TestDisableAndEnableAtTargetSoc(t *testing.T) {
|
|||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().MaxCurrent(int64(maxA)).Return(nil)
|
||||
charger.EXPECT().Enable(true).Return(nil)
|
||||
lp.Update(-500, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-500, 0, nil, false, false, 0, nil, nil)
|
||||
ctrl.Finish()
|
||||
}
|
||||
|
||||
|
|
@ -497,14 +497,14 @@ func TestSetModeAndSocAtDisconnect(t *testing.T) {
|
|||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusC, nil)
|
||||
charger.EXPECT().MaxCurrent(int64(maxA)).Return(nil)
|
||||
lp.Update(500, nil, false, false, 0, nil, nil)
|
||||
lp.Update(500, 0, nil, false, false, 0, nil, nil)
|
||||
|
||||
t.Log("switch off when disconnected")
|
||||
clock.Add(5 * time.Minute)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusA, nil)
|
||||
charger.EXPECT().Enable(false).Return(nil)
|
||||
lp.Update(-300, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-300, 0, nil, false, false, 0, nil, nil)
|
||||
|
||||
if mode := lp.GetMode(); mode != api.ModeOff {
|
||||
t.Error("unexpected mode", mode)
|
||||
|
|
@ -567,14 +567,14 @@ func TestChargedEnergyAtDisconnect(t *testing.T) {
|
|||
rater.EXPECT().ChargedEnergy().Return(0.0, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusC, nil)
|
||||
lp.Update(-1, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-1, 0, nil, false, false, 0, nil, nil)
|
||||
|
||||
t.Log("at 1:00h charging at 5 kWh")
|
||||
clock.Add(time.Hour)
|
||||
rater.EXPECT().ChargedEnergy().Return(5.0, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusC, nil)
|
||||
lp.Update(-1, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-1, 0, nil, false, false, 0, nil, nil)
|
||||
expectCache("chargedEnergy", 5000.0)
|
||||
|
||||
t.Log("at 1:00h stop charging at 5 kWh")
|
||||
|
|
@ -582,7 +582,7 @@ func TestChargedEnergyAtDisconnect(t *testing.T) {
|
|||
rater.EXPECT().ChargedEnergy().Return(5.0, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusB, nil)
|
||||
lp.Update(-1, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-1, 0, nil, false, false, 0, nil, nil)
|
||||
expectCache("chargedEnergy", 5000.0)
|
||||
|
||||
t.Log("at 1:00h restart charging at 5 kWh")
|
||||
|
|
@ -590,7 +590,7 @@ func TestChargedEnergyAtDisconnect(t *testing.T) {
|
|||
rater.EXPECT().ChargedEnergy().Return(5.0, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusC, nil)
|
||||
lp.Update(-1, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-1, 0, nil, false, false, 0, nil, nil)
|
||||
expectCache("chargedEnergy", 5000.0)
|
||||
|
||||
t.Log("at 1:30h continue charging at 7.5 kWh")
|
||||
|
|
@ -598,7 +598,7 @@ func TestChargedEnergyAtDisconnect(t *testing.T) {
|
|||
rater.EXPECT().ChargedEnergy().Return(7.5, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusC, nil)
|
||||
lp.Update(-1, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-1, 0, nil, false, false, 0, nil, nil)
|
||||
expectCache("chargedEnergy", 7500.0)
|
||||
|
||||
t.Log("at 2:00h stop charging at 10 kWh")
|
||||
|
|
@ -606,7 +606,7 @@ func TestChargedEnergyAtDisconnect(t *testing.T) {
|
|||
rater.EXPECT().ChargedEnergy().Return(10.0, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusB, nil)
|
||||
lp.Update(-1, nil, false, false, 0, nil, nil)
|
||||
lp.Update(-1, 0, nil, false, false, 0, nil, nil)
|
||||
expectCache("chargedEnergy", 10000.0)
|
||||
|
||||
ctrl.Finish()
|
||||
|
|
@ -758,7 +758,7 @@ func TestPVHysteresisAfterPhaseSwitch(t *testing.T) {
|
|||
|
||||
for step, se := range tc.series {
|
||||
clck.Set(start.Add(se.delay))
|
||||
assert.Equal(t, se.current, lp.pvMaxCurrent(api.ModePV, se.site, false, false), step)
|
||||
assert.Equal(t, se.current, lp.pvMaxCurrent(api.ModePV, se.site, 0, false, false), step)
|
||||
}
|
||||
|
||||
ctrl.Finish()
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ func TestReconnectVehicle(t *testing.T) {
|
|||
// vehicle not updated yet
|
||||
vehicle.MockChargeState.EXPECT().Status().Return(api.StatusA, nil)
|
||||
|
||||
lp.Update(0, nil, false, false, 0, nil, nil)
|
||||
lp.Update(0, 0, nil, false, false, 0, nil, nil)
|
||||
ctrl.Finish()
|
||||
|
||||
// detection started
|
||||
|
|
@ -284,7 +284,7 @@ func TestReconnectVehicle(t *testing.T) {
|
|||
// vehicle not updated yet
|
||||
vehicle.MockChargeState.EXPECT().Status().Return(api.StatusB, nil)
|
||||
|
||||
lp.Update(0, nil, false, false, 0, nil, nil)
|
||||
lp.Update(0, 0, nil, false, false, 0, nil, nil)
|
||||
ctrl.Finish()
|
||||
|
||||
// vehicle detected
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ const standbyPower = 10 // consider less than 10W as charger in standby
|
|||
// updater abstracts the Loadpoint implementation for testing
|
||||
type updater interface {
|
||||
loadpoint.API
|
||||
Update(availablePower float64, rates api.Rates, batteryBuffered, batteryStart bool, greenShare float64, effectivePrice, effectiveCo2 *float64)
|
||||
Update(sitePower, batteryBoostPower float64, rates api.Rates, batteryBuffered, batteryStart bool, greenShare float64, effectivePrice, effectiveCo2 *float64)
|
||||
}
|
||||
|
||||
// meterMeasurement is used as slice element for publishing structured data
|
||||
|
|
@ -942,7 +942,10 @@ func (site *Site) update(lp updater) {
|
|||
greenShareHome := site.greenShare(0, homePower)
|
||||
greenShareLoadpoints := site.greenShare(nonChargePower, nonChargePower+totalChargePower)
|
||||
|
||||
lp.Update(sitePower, rates, batteryBuffered, batteryStart, greenShareLoadpoints, site.effectivePrice(greenShareLoadpoints), site.effectiveCo2(greenShareLoadpoints))
|
||||
lp.Update(
|
||||
sitePower, max(0, site.batteryPower), rates, batteryBuffered, batteryStart,
|
||||
greenShareLoadpoints, site.effectivePrice(greenShareLoadpoints), site.effectiveCo2(greenShareLoadpoints),
|
||||
)
|
||||
|
||||
site.Health.Update()
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, valueChan chan<- util.Param)
|
|||
"health": {"GET", "/health", healthHandler(site)},
|
||||
"buffersoc": {"POST", "/buffersoc/{value:[0-9.]+}", floatHandler(site.SetBufferSoc, site.GetBufferSoc)},
|
||||
"bufferstartsoc": {"POST", "/bufferstartsoc/{value:[0-9.]+}", floatHandler(site.SetBufferStartSoc, site.GetBufferStartSoc)},
|
||||
"batterydischargecontrol": {"POST", "/batterydischargecontrol/{value:[a-z]+}", boolHandler(site.SetBatteryDischargeControl, site.GetBatteryDischargeControl)},
|
||||
"batterydischargecontrol": {"POST", "/batterydischargecontrol/{value:[01truefalse]+}", boolHandler(site.SetBatteryDischargeControl, site.GetBatteryDischargeControl)},
|
||||
"batterygridcharge": {"POST", "/batterygridchargelimit/{value:-?[0-9.]+}", floatPtrHandler(pass(site.SetBatteryGridChargeLimit), site.GetBatteryGridChargeLimit)},
|
||||
"batterygridchargedelete": {"DELETE", "/batterygridchargelimit", floatPtrHandler(pass(site.SetBatteryGridChargeLimit), site.GetBatteryGridChargeLimit)},
|
||||
"maxgridsupply": {"POST", "/maxgridsupply/{value:[0-9.]+}", floatHandler(site.SetMaxGridSupplyWhileBatteryCharging, site.GetMaxGridSupplyWhileBatteryCharging)},
|
||||
|
|
@ -117,7 +117,7 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, valueChan chan<- util.Param)
|
|||
"updatesession": {"PUT", "/session/{id:[0-9]+}", updateSessionHandler},
|
||||
"deletesession": {"DELETE", "/session/{id:[0-9]+}", deleteSessionHandler},
|
||||
"telemetry": {"GET", "/settings/telemetry", boolGetHandler(telemetry.Enabled)},
|
||||
"telemetry2": {"POST", "/settings/telemetry/{value:[a-z]+}", boolHandler(telemetry.Enable, telemetry.Enabled)},
|
||||
"telemetry2": {"POST", "/settings/telemetry/{value:[01truefalse]+}", boolHandler(telemetry.Enable, telemetry.Enabled)},
|
||||
}
|
||||
|
||||
for _, r := range routes {
|
||||
|
|
@ -181,6 +181,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]+}", intHandler(pass(lp.SetPriority), lp.GetPriority)},
|
||||
"batteryBoost": {"POST", "/batteryboost/{value:[01truefalse]}", boolHandler(lp.SetBatteryBoost, lp.GetBatteryBoost)},
|
||||
}
|
||||
|
||||
for _, r := range routes {
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ func (m *MQTT) listenLoadpointSetters(topic string, site site.API, lp loadpoint.
|
|||
{"/enableDelay", durationSetter(pass(lp.SetEnableDelay))},
|
||||
{"/disableDelay", durationSetter(pass(lp.SetDisableDelay))},
|
||||
{"/smartCostLimit", floatPtrSetter(pass(lp.SetSmartCostLimit))},
|
||||
{"/batteryBoost", boolSetter(lp.SetBatteryBoost)},
|
||||
{"/planEnergy", func(payload string) error {
|
||||
var plan struct {
|
||||
Time time.Time `json:"time"`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue