evcc-io/core/api.go
2020-05-14 15:19:50 +02:00

164 lines
4 KiB
Go

package core
import (
"time"
"github.com/andig/evcc/api"
"github.com/andig/evcc/core/wrapper"
)
// nilVal implements Stringer for nil values
type nilVal int
func (n *nilVal) String() string {
return "—"
}
// Configuration is the loadpoint feature structure
type Configuration struct {
Mode string `json:"mode"`
Phases int64 `json:"phases"`
MinCurrent int64 `json:"minCurrent"`
MaxCurrent int64 `json:"maxCurrent"`
GridMeter bool `json:"gridMeter"`
PVMeter bool `json:"pvMeter"`
ChargeMeter bool `json:"chargeMeter"`
SoC bool `json:"soc"`
SoCCapacity int64 `json:"socCapacity"`
SoCTitle string `json:"socTitle"`
}
// Configuration returns meter configuration
func (lp *LoadPoint) Configuration() Configuration {
c := Configuration{
Mode: string(lp.GetMode()),
Phases: lp.Phases,
MinCurrent: lp.MinCurrent,
MaxCurrent: lp.MaxCurrent,
GridMeter: lp.gridMeter != nil,
PVMeter: lp.pvMeter != nil,
ChargeMeter: lp.hasChargeMeter(),
}
if lp.vehicle != nil {
c.SoC = true
c.SoCCapacity = lp.vehicle.Capacity()
c.SoCTitle = lp.vehicle.Title()
}
return c
}
func (lp *LoadPoint) hasChargeMeter() bool {
_, isWrapped := lp.chargeMeter.(*wrapper.ChargeMeter)
return lp.chargeMeter != nil && !isWrapped
}
// Dump loadpoint configuration
func (lp *LoadPoint) Dump() {
log.INFO.Printf("%s loadpoint config: vehicle %s grid %s pv %s battery %s charge %s", lp.Name,
presence[lp.vehicle != nil],
presence[lp.gridMeter != nil],
presence[lp.pvMeter != nil],
presence[lp.batteryMeter != nil],
presence[lp.hasChargeMeter()],
)
_, power := lp.charger.(api.Meter)
_, energy := lp.charger.(api.ChargeRater)
_, timer := lp.charger.(api.ChargeTimer)
log.INFO.Printf("%s charger config: power %s energy %s timer %s", lp.Name,
presence[power],
presence[energy],
presence[timer],
)
log.INFO.Printf("%s charge mode: %s", lp.Name, lp.GetMode())
}
// Update triggers loadpoint to run main control loop and push messages to UI socket
func (lp *LoadPoint) Update() {
select {
case lp.triggerChan <- struct{}{}: // non-blocking send
default:
log.WARN.Printf("%s update blocked", lp.Name)
}
}
// GetMode returns loadpoint charge mode
func (lp *LoadPoint) GetMode() api.ChargeMode {
lp.Lock()
defer lp.Unlock()
return lp.Mode
}
// SetMode sets loadpoint charge mode
func (lp *LoadPoint) SetMode(mode api.ChargeMode) {
lp.Lock()
defer lp.Unlock()
log.INFO.Printf("%s set charge mode: %s", lp.Name, string(mode))
// apply immediately
if lp.Mode != mode {
lp.resetGuard()
lp.Mode = mode
}
lp.Update()
}
// chargeDuration returns for how long the charge cycle has been running
func (lp *LoadPoint) chargeDuration() time.Duration {
d, err := lp.chargeTimer.ChargingTime()
if err != nil {
log.ERROR.Printf("%s charge timer error: %v", lp.Name, err)
}
return d
}
// chargedEnergy returns energy consumption since charge start in kWh
func (lp *LoadPoint) chargedEnergy() float64 {
f, err := lp.chargeRater.ChargedEnergy()
if err != nil {
log.ERROR.Printf("%s charge rater error: %v", lp.Name, err)
}
return f
}
// remainingChargeDuration returns the remaining charge time
func (lp *LoadPoint) remainingChargeDuration(chargePercent float64) time.Duration {
if !lp.charging {
return -1
}
if lp.chargePower > 0 && lp.vehicle != nil {
whRemaining := (1 - chargePercent/100.0) * 1e3 * float64(lp.vehicle.Capacity())
return time.Duration(float64(time.Hour) * whRemaining / lp.chargePower)
}
return -1
}
// publish state of charge and remaining charge duration
func (lp *LoadPoint) publishSoC() {
if lp.vehicle == nil {
return
}
if lp.connected() {
f, err := lp.vehicle.ChargeState()
if err == nil {
log.DEBUG.Printf("%s vehicle soc: %.1f%%", lp.Name, f)
lp.publish("socCharge", f)
lp.publish("chargeEstimate", lp.remainingChargeDuration(f))
return
}
log.ERROR.Printf("%s vehicle error: %v", lp.Name, err)
}
var n *nilVal
lp.publish("socCharge", n)
lp.publish("chargeEstimate", -1)
}