pvMaxCurrent calculates the maximum target current for PV mode
(mode api.ChargeMode, sitePower, batteryBoostPower float64, batteryBuffered, batteryStart bool)
| 1529 | |
| 1530 | // pvMaxCurrent calculates the maximum target current for PV mode |
| 1531 | func (lp *Loadpoint) pvMaxCurrent(mode api.ChargeMode, sitePower, batteryBoostPower float64, batteryBuffered, batteryStart bool) float64 { |
| 1532 | // read only once to simplify testing |
| 1533 | minCurrent := lp.effectiveMinCurrent() |
| 1534 | maxCurrent := lp.effectiveMaxCurrent() |
| 1535 | |
| 1536 | // push demand to drain battery |
| 1537 | sitePower -= lp.boostPower(batteryBoostPower) |
| 1538 | |
| 1539 | // switch phases up/down |
| 1540 | var scaledTo int |
| 1541 | if lp.hasPhaseSwitching() && lp.phaseSwitchCompleted() { |
| 1542 | scaledTo = lp.pvScalePhases(sitePower, minCurrent, maxCurrent) |
| 1543 | } |
| 1544 | |
| 1545 | // calculate target charge current from delta power and actual current |
| 1546 | activePhases := lp.ActivePhases() |
| 1547 | effectiveCurrent := lp.effectiveCurrent() |
| 1548 | if scaledTo == 3 { |
| 1549 | // if we did scale, adjust the effective current to the new phase count |
| 1550 | effectiveCurrent /= float64(lp.maxActivePhases()) |
| 1551 | } |
| 1552 | if lp.chargerHasFeature(api.IntegratedDevice) { |
| 1553 | // for slow-acting heating devices, only take actually consumed power into account |
| 1554 | effectiveCurrent = powerToCurrent(lp.chargePower, activePhases) |
| 1555 | } |
| 1556 | deltaCurrent := powerToCurrent(-sitePower, activePhases) |
| 1557 | targetCurrent := max(effectiveCurrent+deltaCurrent, 0) |
| 1558 | |
| 1559 | // in MinPV mode or under special conditions return at least minCurrent |
| 1560 | if battery := batteryStart || batteryBuffered && lp.charging(); (mode == api.ModeMinPV || battery) && targetCurrent < minCurrent { |
| 1561 | lp.log.DEBUG.Printf("pv charge current: min %.3gA > %.3gA (%.0fW @ %dp, battery: %t)", minCurrent, targetCurrent, sitePower, activePhases, battery) |
| 1562 | return minCurrent |
| 1563 | } |
| 1564 | |
| 1565 | lp.log.DEBUG.Printf("pv charge current: %.3gA = %.3gA + %.3gA (%.0fW @ %dp)", targetCurrent, effectiveCurrent, deltaCurrent, sitePower, activePhases) |
| 1566 | |
| 1567 | if mode == api.ModePV && lp.enabled && targetCurrent < minCurrent { |
| 1568 | projectedSitePower := sitePower |
| 1569 | if lp.hasPhaseSwitching() && !lp.phaseTimer.IsZero() { |
| 1570 | // calculate site power after a phase switch from activePhases phases -> 1 phase |
| 1571 | // notes: activePhases can be 1, 2 or 3 and phaseTimer can only be active if lp current is already at minCurrent |
| 1572 | projectedSitePower -= Voltage * minCurrent * float64(activePhases-1) |
| 1573 | } |
| 1574 | // kick off disable sequence, unless climater keep-alive is holding |
| 1575 | // charging at minCurrent — otherwise the "pausing soon" badge would |
| 1576 | // flash on/off forever while climater is active (issue #29834). |
| 1577 | if projectedSitePower >= lp.Disable.Threshold && !lp.vehicleClimateActive() { |
| 1578 | lp.log.DEBUG.Printf("projected site power %.0fW >= %.0fW disable threshold", projectedSitePower, lp.Disable.Threshold) |
| 1579 | |
| 1580 | if lp.pvTimer.IsZero() { |
| 1581 | lp.log.DEBUG.Printf("pv disable timer start: %v", lp.GetDisableDelay()) |
| 1582 | lp.pvTimer = lp.clock.Now() |
| 1583 | } |
| 1584 | |
| 1585 | lp.publishTimer(pvTimer, lp.GetDisableDelay(), pvDisable) |
| 1586 | |
| 1587 | elapsed := lp.clock.Since(lp.pvTimer) |
| 1588 | if elapsed >= lp.GetDisableDelay() { |