evcc-io/core/loadpoint_api.go

938 lines
22 KiB
Go

package core
import (
"errors"
"fmt"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/core/keys"
"github.com/evcc-io/evcc/core/loadpoint"
"github.com/evcc-io/evcc/core/settings"
"github.com/evcc-io/evcc/core/wrapper"
)
var _ loadpoint.API = (*Loadpoint)(nil)
func (lp *Loadpoint) isConfigurable() bool {
_, ok := lp.settings.(*settings.ConfigSettings)
return ok
}
// GetChargerRef returns the loadpoint charger
func (lp *Loadpoint) GetChargerRef() string {
lp.RLock()
defer lp.RUnlock()
return lp.ChargerRef
}
// SetChargerRef sets the loadpoint charger
func (lp *Loadpoint) SetChargerRef(ref string) {
if !lp.isConfigurable() {
lp.log.ERROR.Println("cannot set charger ref: not configurable")
return
}
lp.Lock()
defer lp.Unlock()
lp.ChargerRef = ref
lp.settings.SetString(keys.Charger, ref)
}
// GetMeter returns the loadpoint meter
func (lp *Loadpoint) GetMeterRef() string {
lp.RLock()
defer lp.RUnlock()
return lp.MeterRef
}
// SetMeter sets the loadpoint meter
func (lp *Loadpoint) SetMeterRef(ref string) {
if !lp.isConfigurable() {
lp.log.ERROR.Println("cannot set meter ref: not configurable")
return
}
lp.Lock()
defer lp.Unlock()
lp.MeterRef = ref
lp.settings.SetString(keys.Meter, ref)
}
// GetCircuitName returns the loadpoint circuit
func (lp *Loadpoint) GetCircuitRef() string {
lp.RLock()
defer lp.RUnlock()
return lp.CircuitRef
}
// SetCircuitRef sets the loadpoint circuit
func (lp *Loadpoint) SetCircuitRef(ref string) {
if !lp.isConfigurable() {
lp.log.ERROR.Println("cannot set circuit ref: not configurable")
return
}
lp.log.DEBUG.Println("set circuit ref:", ref)
lp.Lock()
defer lp.Unlock()
lp.CircuitRef = ref
lp.settings.SetString(keys.Circuit, ref)
}
// GetDefaultVehicleRef returns the loadpoint default vehicle
func (lp *Loadpoint) GetDefaultVehicleRef() string {
lp.RLock()
defer lp.RUnlock()
return lp.VehicleRef
}
// SetDefaultVehicleRef returns the loadpoint default vehicle
func (lp *Loadpoint) SetDefaultVehicleRef(ref string) {
if !lp.isConfigurable() {
lp.log.ERROR.Println("cannot set default vehicle ref: not configurable")
return
}
lp.log.DEBUG.Println("set default vehicle ref:", ref)
lp.Lock()
defer lp.Unlock()
lp.VehicleRef = ref
lp.settings.SetString(keys.DefaultVehicle, ref)
}
// GetTitle returns the loadpoint title
func (lp *Loadpoint) GetTitle() string {
lp.RLock()
defer lp.RUnlock()
return lp.title
}
// SetTitle sets the loadpoint title
func (lp *Loadpoint) SetTitle(title string) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set title:", title)
if title != lp.title {
lp.setTitle(title)
}
}
// setTitle sets the loadpoint title (no mutex)
func (lp *Loadpoint) setTitle(title string) {
lp.title = title
lp.publish(keys.Title, lp.title)
lp.settings.SetString(keys.Title, lp.title)
if lp.chargeEnergy != nil {
if err := lp.chargeEnergy.UpdateTitle(title); err != nil {
lp.log.ERROR.Printf("update title: %v", err)
}
}
}
// GetStatus returns the charging status
func (lp *Loadpoint) GetStatus() api.ChargeStatus {
lp.RLock()
defer lp.RUnlock()
return lp.status
}
// IsEnabled returns the charger enabled state
func (lp *Loadpoint) IsEnabled() bool {
lp.RLock()
defer lp.RUnlock()
return lp.enabled
}
// GetMode returns loadpoint charge mode
func (lp *Loadpoint) GetMode() api.ChargeMode {
lp.RLock()
defer lp.RUnlock()
return lp.mode
}
// setMode sets loadpoint charge mode (no mutex)
func (lp *Loadpoint) setMode(mode api.ChargeMode) {
lp.mode = mode
lp.publish(keys.Mode, mode)
lp.settings.SetString(keys.Mode, string(mode))
}
// SetMode sets loadpoint charge mode
func (lp *Loadpoint) SetMode(mode api.ChargeMode) {
lp.Lock()
defer lp.Unlock()
if _, err := api.ChargeModeString(mode.String()); err != nil {
lp.log.ERROR.Printf("invalid charge mode: %s", string(mode))
return
}
lp.log.DEBUG.Printf("set charge mode: %s", string(mode))
// apply immediately
if lp.mode != mode {
lp.setMode(mode)
lp.batteryBoost = boostDisabled
lp.publish(keys.BatteryBoost, false)
// reset timers
switch mode {
case api.ModeNow, api.ModeOff:
lp.resetPhaseTimer()
lp.resetPVTimer()
lp.setPlanActive(false)
case api.ModeMinPV:
lp.resetPVTimer()
}
lp.requestUpdate()
}
}
// GetDefaultMode returns the default charge mode
func (lp *Loadpoint) GetDefaultMode() api.ChargeMode {
lp.RLock()
defer lp.RUnlock()
return lp.DefaultMode
}
// SetDefaultMode sets the default charge mode
func (lp *Loadpoint) SetDefaultMode(mode api.ChargeMode) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set default mode:", mode)
if lp.DefaultMode != mode {
lp.DefaultMode = mode
lp.settings.SetString(keys.DefaultMode, string(mode))
}
}
// GetChargedEnergy returns session charge energy in Wh
func (lp *Loadpoint) GetChargedEnergy() float64 {
lp.RLock()
defer lp.RUnlock()
return lp.getChargedEnergy()
}
// getChargedEnergy returns session charge energy in Wh
func (lp *Loadpoint) getChargedEnergy() float64 {
return lp.energyMetrics.TotalWh()
}
// GetPriority returns the loadpoint priority
func (lp *Loadpoint) GetPriority() int {
lp.RLock()
defer lp.RUnlock()
return lp.priority
}
// setPriority sets the loadpoint priority (no mutex)
func (lp *Loadpoint) setPriority(prio int) {
lp.priority = prio
lp.publish(keys.Priority, lp.priority)
lp.settings.SetInt(keys.Priority, int64(lp.priority))
}
// SetPriority sets the loadpoint priority
func (lp *Loadpoint) SetPriority(prio int) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set priority:", prio)
if lp.priority != prio {
lp.setPriority(prio)
}
}
// GetPhases returns the enabled phases
func (lp *Loadpoint) GetPhases() int {
lp.RLock()
defer lp.RUnlock()
return lp.phases
}
// GetPhasesConfigured returns the configured phases
func (lp *Loadpoint) GetPhasesConfigured() int {
lp.RLock()
defer lp.RUnlock()
return lp.phasesConfigured
}
// SetPhasesConfigured sets the configured phases
func (lp *Loadpoint) SetPhasesConfigured(phases int) error {
// limit auto mode (phases=0) to scalable charger
if !lp.hasPhaseSwitching() && phases == 0 {
return fmt.Errorf("charger does not support phase switching")
}
if phases != 0 && phases != 1 && phases != 3 {
return fmt.Errorf("invalid number of phases: %d", phases)
}
if physical := lp.getChargerPhysicalPhases(); physical != 0 && phases > physical {
return fmt.Errorf("cannot configure more phases than physically connected: %d > %d", phases, physical)
}
// set new default
lp.log.DEBUG.Println("set phases:", phases)
lp.Lock()
lp.setPhasesConfigured(phases)
lp.Unlock()
lp.requestUpdate()
return nil
}
// GetLimitSoc returns the session limit soc
func (lp *Loadpoint) GetLimitSoc() int {
lp.RLock()
defer lp.RUnlock()
return lp.limitSoc
}
// setLimitSoc sets the session limit soc (no mutex)
func (lp *Loadpoint) setLimitSoc(soc int) {
lp.limitSoc = soc
lp.publish(keys.LimitSoc, soc)
lp.settings.SetInt(keys.LimitSoc, int64(soc))
}
// SetLimitSoc sets the session soc limit
func (lp *Loadpoint) SetLimitSoc(soc int) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set session soc limit:", soc)
// apply immediately
if lp.limitSoc != soc {
lp.setLimitSoc(soc)
lp.requestUpdate()
}
}
// GetLimitEnergy returns the session limit energy
func (lp *Loadpoint) GetLimitEnergy() float64 {
lp.RLock()
defer lp.RUnlock()
return lp.getLimitEnergy()
}
// getLimitEnergy returns the session limit energy
func (lp *Loadpoint) getLimitEnergy() float64 {
return lp.limitEnergy
}
// setLimitEnergy sets the session limit energy (no mutex)
func (lp *Loadpoint) setLimitEnergy(energy float64) {
lp.limitEnergy = energy
lp.publish(keys.LimitEnergy, energy)
lp.settings.SetFloat(keys.LimitEnergy, energy)
}
// SetLimitEnergy sets the session energy limit
func (lp *Loadpoint) SetLimitEnergy(energy float64) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set session energy limit:", energy)
// apply immediately
if lp.limitEnergy != energy {
lp.setLimitEnergy(energy)
lp.requestUpdate()
}
}
// GetPlanEnergy returns plan target energy
func (lp *Loadpoint) GetPlanEnergy() (time.Time, float64) {
lp.RLock()
defer lp.RUnlock()
return lp.getPlanEnergy()
}
// getPlanEnergy returns plan target energy
func (lp *Loadpoint) getPlanEnergy() (time.Time, float64) {
return lp.planTime, lp.planEnergy
}
// setPlanEnergy sets plan target energy (no mutex)
func (lp *Loadpoint) setPlanEnergy(finishAt time.Time, energy float64) {
// clear locked goal when energy plan changes
lp.clearPlanLock()
lp.planEnergy = energy
lp.publish(keys.PlanEnergy, energy)
lp.settings.SetFloat(keys.PlanEnergy, energy)
// remove plan
if energy == 0 {
finishAt = time.Time{}
}
lp.planTime = finishAt
lp.planEnergyOffset = lp.getChargedEnergy() / 1e3
lp.publish(keys.PlanTime, finishAt)
lp.settings.SetTime(keys.PlanTime, finishAt)
if finishAt.IsZero() {
lp.setPlanActive(false)
}
}
// SetPlanEnergy sets plan target energy
func (lp *Loadpoint) SetPlanEnergy(finishAt time.Time, energy float64) error {
lp.Lock()
defer lp.Unlock()
if !finishAt.IsZero() && finishAt.Before(lp.clock.Now()) {
return errors.New("timestamp is in the past")
}
lp.log.DEBUG.Printf("set plan energy: %.3gkWh @ %v", energy, finishAt.Round(time.Second).Local())
// apply immediately
if lp.planEnergy != energy || !lp.planTime.Equal(finishAt) {
lp.setPlanEnergy(finishAt, energy)
lp.requestUpdate()
}
return nil
}
// setPlanStrategy sets the plan strategy (no mutex)
func (lp *Loadpoint) setPlanStrategy(strategy api.PlanStrategy) error {
if err := lp.settings.SetJson(keys.PlanStrategy, strategy); err != nil {
return err
}
lp.planStrategy = strategy
lp.publish(keys.PlanStrategy, strategy)
lp.publish(keys.EffectivePlanStrategy, lp.getEffectivePlanStrategy())
lp.requestUpdate()
return nil
}
// SetPlanStrategy sets the plan strategy
func (lp *Loadpoint) SetPlanStrategy(strategy api.PlanStrategy) error {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Printf("set plan strategy: continuous=%v, precondition=%v", strategy.Continuous, strategy.Precondition)
return lp.setPlanStrategy(strategy)
}
// getPlanStrategy returns the plan strategy (no mutex)
func (lp *Loadpoint) getPlanStrategy() api.PlanStrategy {
return lp.planStrategy
}
// GetPlanStrategy returns the plan strategy
func (lp *Loadpoint) GetPlanStrategy() api.PlanStrategy {
lp.RLock()
defer lp.RUnlock()
return lp.getPlanStrategy()
}
// GetSoc returns the PV mode threshold settings
func (lp *Loadpoint) GetSocConfig() loadpoint.SocConfig {
lp.RLock()
defer lp.RUnlock()
return lp.Soc
}
func (lp *Loadpoint) setSocConfig(soc loadpoint.SocConfig) {
lp.Soc = soc
lp.settings.SetJson(keys.Soc, soc)
lp.requestUpdate()
}
// SetSoc sets the PV mode threshold settings
func (lp *Loadpoint) SetSocConfig(soc loadpoint.SocConfig) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Printf("set soc config: %+v", soc)
// apply immediately
lp.setSocConfig(soc)
}
// GetUI returns the display-only ui settings
func (lp *Loadpoint) GetUI() loadpoint.UIConfig {
lp.RLock()
defer lp.RUnlock()
return lp.ui
}
func (lp *Loadpoint) setUI(ui loadpoint.UIConfig) {
lp.ui = ui
lp.publish(keys.UI, ui)
lp.settings.SetJson(keys.UI, ui)
}
// SetUI sets the display-only ui settings. Not used in control logic.
func (lp *Loadpoint) SetUI(ui loadpoint.UIConfig) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Printf("set ui config: %+v", ui)
lp.setUI(ui)
}
// GetThresholds returns the PV mode threshold settings
func (lp *Loadpoint) GetThresholds() loadpoint.ThresholdsConfig {
lp.RLock()
defer lp.RUnlock()
return loadpoint.ThresholdsConfig{
Enable: lp.Enable,
Disable: lp.Disable,
}
}
func (lp *Loadpoint) setThresholds(thresholds loadpoint.ThresholdsConfig) {
lp.Enable = thresholds.Enable
lp.Disable = thresholds.Disable
lp.publish(keys.EnableThreshold, lp.Enable.Threshold)
lp.publish(keys.DisableThreshold, lp.Disable.Threshold)
lp.settings.SetJson(keys.Thresholds, thresholds)
lp.requestUpdate()
}
// SetThresholds sets the PV mode threshold settings
func (lp *Loadpoint) SetThresholds(thresholds loadpoint.ThresholdsConfig) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Printf("set thresholds: %+v", thresholds)
// apply immediately
lp.setThresholds(thresholds)
}
// GetEnableThreshold gets the loadpoint enable threshold
func (lp *Loadpoint) GetEnableThreshold() float64 {
lp.RLock()
defer lp.RUnlock()
return lp.Enable.Threshold
}
// SetEnableThreshold sets loadpoint enable threshold
func (lp *Loadpoint) SetEnableThreshold(threshold float64) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set enable threshold:", threshold)
if lp.Enable.Threshold != threshold {
lp.Enable.Threshold = threshold
// TODO reduce APIs
lp.setThresholds(loadpoint.ThresholdsConfig{
Enable: lp.Enable,
Disable: lp.Disable,
})
}
}
// GetDisableThreshold gets the loadpoint enable threshold
func (lp *Loadpoint) GetDisableThreshold() float64 {
lp.RLock()
defer lp.RUnlock()
return lp.Disable.Threshold
}
// SetDisableThreshold sets loadpoint disable threshold
func (lp *Loadpoint) SetDisableThreshold(threshold float64) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set disable threshold:", threshold)
if lp.Disable.Threshold != threshold {
lp.Disable.Threshold = threshold
// TODO reduce APIs
lp.setThresholds(loadpoint.ThresholdsConfig{
Enable: lp.Enable,
Disable: lp.Disable,
})
}
}
// GetEnableDelay gets the loadpoint enable delay
func (lp *Loadpoint) GetEnableDelay() time.Duration {
lp.RLock()
defer lp.RUnlock()
return lp.Enable.Delay
}
// SetEnableDelay sets loadpoint enable delay
func (lp *Loadpoint) SetEnableDelay(delay time.Duration) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set enable delay:", delay)
if lp.Enable.Delay != delay {
lp.Enable.Delay = delay
lp.publish(keys.EnableDelay, delay)
}
}
// GetDisableDelay gets the loadpoint enable delay
func (lp *Loadpoint) GetDisableDelay() time.Duration {
lp.RLock()
defer lp.RUnlock()
return lp.Disable.Delay
}
// SetDisableDelay sets loadpoint disable delay
func (lp *Loadpoint) SetDisableDelay(delay time.Duration) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set disable delay:", delay)
if lp.Disable.Delay != delay {
lp.Disable.Delay = delay
lp.publish(keys.DisableDelay, delay)
}
}
// GetBatteryBoost returns the battery boost
func (lp *Loadpoint) GetBatteryBoost() int {
lp.RLock()
defer lp.RUnlock()
return lp.batteryBoost
}
// 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
}
// GetBatteryBoostLimit returns the battery boost soc limit
func (lp *Loadpoint) GetBatteryBoostLimit() int {
lp.RLock()
defer lp.RUnlock()
return lp.batteryBoostLimit
}
// SetBatteryBoostLimit sets the battery boost soc limit
func (lp *Loadpoint) SetBatteryBoostLimit(limit int) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set battery boost limit:", limit)
if lp.batteryBoostLimit != limit {
lp.batteryBoostLimit = limit
lp.settings.SetInt(keys.BatteryBoostLimit, int64(limit))
lp.publish(keys.BatteryBoostLimit, limit)
}
}
// HasChargeMeter determines if a physical charge meter is attached
func (lp *Loadpoint) HasChargeMeter() bool {
_, isWrapped := lp.chargeMeter.(*wrapper.ChargeMeter)
return lp.chargeMeter != nil && !isWrapped
}
// GetChargePower returns the current charge power
func (lp *Loadpoint) GetChargePower() float64 {
lp.RLock()
defer lp.RUnlock()
return lp.chargePower
}
// GetChargePowerFlexibility returns the flexible amount of current charging power
func (lp *Loadpoint) GetChargePowerFlexibility(rates api.Rates) float64 {
mode := lp.GetMode()
if mode == api.ModeNow || !lp.charging() || lp.minSocNotReached() ||
lp.planActive || lp.smartLimitActive(lp.GetSmartCostLimit(), rates, true) {
return 0
}
if mode == api.ModePV {
return lp.GetChargePower()
}
// MinPV mode: a charger without current control (switch socket or heatpump) cannot release power.
if lp.chargerHasFeature(api.SwitchDevice) || lp.chargerHasFeature(api.Continuous) {
return 0
}
return max(0, lp.GetChargePower()-lp.EffectiveMinPower())
}
// GetMaxPhaseCurrent returns the maximum charge current per phase or- if not available-
// the offered current from either charger or charge meter
func (lp *Loadpoint) GetMaxPhaseCurrent() float64 {
lp.RLock()
defer lp.RUnlock()
if lp.chargeCurrents == nil {
return lp.offeredCurrent
}
return max(lp.chargeCurrents[0], lp.chargeCurrents[1], lp.chargeCurrents[2])
}
// GetMinCurrent returns the min loadpoint current
func (lp *Loadpoint) GetMinCurrent() float64 {
lp.RLock()
defer lp.RUnlock()
return lp.getMinCurrent()
}
// getMinCurrent returns the max loadpoint current
func (lp *Loadpoint) getMinCurrent() float64 {
return lp.minCurrent
}
// setMinCurrent sets the min loadpoint current (no mutex)
func (lp *Loadpoint) setMinCurrent(current float64) {
lp.minCurrent = current
lp.publish(keys.MinCurrent, lp.minCurrent)
lp.settings.SetFloat(keys.MinCurrent, lp.minCurrent)
}
// SetMinCurrent sets the min loadpoint current
func (lp *Loadpoint) SetMinCurrent(current float64) error {
lp.Lock()
defer lp.Unlock()
if current > lp.maxCurrent {
return errors.New("min current must be smaller or equal than max current")
}
lp.log.DEBUG.Println("set min current:", current)
if current != lp.minCurrent {
lp.setMinCurrent(current)
}
return nil
}
// GetMaxCurrent returns the max loadpoint current
func (lp *Loadpoint) GetMaxCurrent() float64 {
lp.RLock()
defer lp.RUnlock()
return lp.getMaxCurrent()
}
// getMaxCurrent returns the max loadpoint current
func (lp *Loadpoint) getMaxCurrent() float64 {
return lp.maxCurrent
}
// setMaxCurrent sets the max loadpoint current
func (lp *Loadpoint) setMaxCurrent(current float64) {
lp.maxCurrent = current
lp.publish(keys.MaxCurrent, lp.maxCurrent)
lp.settings.SetFloat(keys.MaxCurrent, lp.maxCurrent)
}
// SetMaxCurrent sets the max loadpoint current
func (lp *Loadpoint) SetMaxCurrent(current float64) error {
lp.Lock()
defer lp.Unlock()
if current < lp.minCurrent {
return errors.New("max current must be greater or equal than min current")
}
lp.log.DEBUG.Println("set max current:", current)
if current != lp.maxCurrent {
lp.setMaxCurrent(current)
}
return nil
}
// IsFastChargingActive indicates if fast charging with maximum power is active
func (lp *Loadpoint) IsFastChargingActive() bool {
lp.RLock()
defer lp.RUnlock()
return lp.mode == api.ModeNow || lp.planActive || lp.minSocNotReached()
}
// GetRemainingDuration is the estimated remaining charging duration
func (lp *Loadpoint) GetRemainingDuration() time.Duration {
lp.Lock()
defer lp.Unlock()
return lp.chargeRemainingDuration
}
// SetRemainingDuration sets the estimated remaining charging duration
func (lp *Loadpoint) SetRemainingDuration(chargeRemainingDuration time.Duration) {
lp.Lock()
defer lp.Unlock()
lp.setRemainingDuration(chargeRemainingDuration)
}
// setRemainingDuration sets the estimated remaining charging duration (no mutex)
func (lp *Loadpoint) setRemainingDuration(remainingDuration time.Duration) {
if lp.chargeRemainingDuration != remainingDuration {
lp.chargeRemainingDuration = remainingDuration
lp.publish(keys.ChargeRemainingDuration, remainingDuration)
}
}
// GetRemainingEnergy is the remaining charge energy in kWh
func (lp *Loadpoint) GetRemainingEnergy() float64 {
lp.RLock()
defer lp.RUnlock()
return lp.chargeRemainingEnergy
}
// SetRemainingEnergy sets the remaining charge energy in kWh
func (lp *Loadpoint) SetRemainingEnergy(chargeRemainingEnergy float64) {
lp.Lock()
defer lp.Unlock()
lp.setRemainingEnergy(chargeRemainingEnergy)
}
// setRemainingEnergy sets the remaining charge energy in kWh (no mutex)
func (lp *Loadpoint) setRemainingEnergy(chargeRemainingEnergy float64) {
if lp.chargeRemainingEnergy != chargeRemainingEnergy {
lp.chargeRemainingEnergy = chargeRemainingEnergy
lp.publish(keys.ChargeRemainingEnergy, chargeRemainingEnergy*1e3)
}
}
// GetVehicle gets the active vehicle
func (lp *Loadpoint) GetVehicle() api.Vehicle {
lp.vmu.RLock()
defer lp.vmu.RUnlock()
return lp.vehicle
}
// SetVehicle sets the active vehicle
func (lp *Loadpoint) SetVehicle(vehicle api.Vehicle) {
// set desired vehicle (protected by lock, no locking here)
lp.setActiveVehicle(vehicle)
lp.vmu.Lock()
defer lp.vmu.Unlock()
// disable auto-detect
lp.stopVehicleDetection()
}
// GetSoc returns the estimated vehicle soc in %
func (lp *Loadpoint) GetSoc() float64 {
lp.vmu.RLock()
defer lp.vmu.RUnlock()
return lp.vehicleSoc
}
// StartVehicleDetection allows triggering vehicle detection for debugging purposes
func (lp *Loadpoint) StartVehicleDetection() {
// reset vehicle
lp.setActiveVehicle(nil)
lp.vmu.Lock()
defer lp.vmu.Unlock()
// start auto-detect
lp.startVehicleDetection()
}
// GetSmartCostLimit gets the smart cost limit
func (lp *Loadpoint) GetSmartCostLimit() *float64 {
lp.RLock()
defer lp.RUnlock()
return lp.smartCostLimit
}
// SetSmartCostLimit sets the smart cost limit
func (lp *Loadpoint) SetSmartCostLimit(val *float64) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set smart cost limit:", printPtr("%.1f", val))
if !ptrValueEqual(lp.smartCostLimit, val) {
lp.smartCostLimit = val
lp.settings.SetFloatPtr(keys.SmartCostLimit, val)
lp.publish(keys.SmartCostLimit, val)
}
}
// GetSmartFeedInPriorityLimit gets the smart feed-in limit
func (lp *Loadpoint) GetSmartFeedInPriorityLimit() *float64 {
lp.RLock()
defer lp.RUnlock()
return lp.smartFeedInPriorityLimit
}
// SetSmartFeedInPriorityLimit sets the smart cost feed-in
func (lp *Loadpoint) SetSmartFeedInPriorityLimit(val *float64) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Println("set smart feed-in limit:", printPtr("%.1f", val))
if !ptrValueEqual(lp.smartFeedInPriorityLimit, val) {
lp.smartFeedInPriorityLimit = val
lp.settings.SetFloatPtr(keys.SmartFeedInPriorityLimit, val)
lp.publish(keys.SmartFeedInPriorityLimit, val)
}
}
// GetCircuit returns the assigned circuit
func (lp *Loadpoint) GetCircuit() api.Circuit {
lp.RLock()
defer lp.RUnlock()
// return untyped nil
if lp.circuit == nil {
return nil
}
return lp.circuit
}