Add basic OCPP support (#395)

This commit is contained in:
andig 2020-11-21 16:49:59 +01:00 • committed by GitHub
parent 9bd8754b85
commit 29ea3c6549
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 618 additions and 130 deletions

View file

@ -86,6 +86,7 @@ type LoadPoint struct {
// cached state
status api.ChargeStatus // Charger status
remoteDemand RemoteDemand // External status demand
charging bool // Charging cycle
chargePower float64 // Charging power
connectedTime time.Time // Time when vehicle was connected
@ -412,6 +413,14 @@ func (lp *LoadPoint) climateActive() bool {
return false
}
// remoteControlled returns true if remote control status is active
func (lp *LoadPoint) remoteControlled(demand RemoteDemand) bool {
lp.Lock()
defer lp.Unlock()
return lp.remoteDemand == demand
}
// setActiveVehicle assigns currently active vehicle and configures soc estimator
func (lp *LoadPoint) setActiveVehicle(vehicle api.Vehicle) {
if lp.vehicle != nil {
@ -703,7 +712,7 @@ func (lp *LoadPoint) publishSoC() {
// Update is the main control function. It reevaluates meters and charger state
func (lp *LoadPoint) Update(sitePower float64) {
mode := lp.GetMode()
lp.publish("mode", string(mode))
lp.publish("mode", mode)
// read and publish meters first
lp.updateChargeMeter()
@ -741,6 +750,9 @@ func (lp *LoadPoint) Update(sitePower float64) {
// check if car connected and ready for charging
var err error
// track if remote disabled is actually active
remoteDisabled := RemoteEnable
// execute loading strategy
switch {
case !lp.connected():
@ -755,6 +767,11 @@ func (lp *LoadPoint) Update(sitePower float64) {
}
err = lp.handler.Ramp(targetCurrent, true)
// OCPP
case lp.remoteControlled(RemoteHardDisable):
remoteDisabled = RemoteHardDisable
fallthrough
case mode == api.ModeOff:
err = lp.handler.Ramp(0, true)
@ -775,9 +792,19 @@ func (lp *LoadPoint) Update(sitePower float64) {
required = true
}
// Sunny Home Manager
if mode == api.ModePV && lp.remoteControlled(RemoteSoftDisable) {
remoteDisabled = RemoteSoftDisable
targetCurrent = 0
required = true
}
err = lp.handler.Ramp(targetCurrent, required)
}
// effective disabled status
lp.publish("remoteDisabled", remoteDisabled)
if err != nil {
lp.log.ERROR.Println(err)
}

View file

@ -21,6 +21,7 @@ type LoadPointSettingsAPI interface {
SetTargetSoC(int) error
GetMinSoC() int
SetMinSoC(int) error
RemoteControl(string, RemoteDemand)
}
// LoadPointEnergyAPI is the external loadpoint API
@ -109,6 +110,24 @@ func (lp *LoadPoint) SetMinSoC(soc int) error {
return nil
}
// RemoteControl sets remote status demand
func (lp *LoadPoint) RemoteControl(source string, demand RemoteDemand) {
lp.Lock()
defer lp.Unlock()
lp.log.INFO.Println("remote demand:", demand)
// apply immediately
if lp.remoteDemand != demand {
lp.remoteDemand = demand
lp.publish("remoteDisabled", demand)
lp.publish("remoteDisabledSource", source)
lp.requestUpdate()
}
}
// HasChargeMeter determines if a physical charge meter is attached
func (lp *LoadPoint) HasChargeMeter() bool {
_, isWrapped := lp.chargeMeter.(*wrapper.ChargeMeter)

11
core/remote.go Normal file
View file

@ -0,0 +1,11 @@
package core
// RemoteDemand defines external status demand
type RemoteDemand string
// remote status demand definition
const (
RemoteEnable RemoteDemand = ""
RemoteHardDisable RemoteDemand = "hard"
RemoteSoftDisable RemoteDemand = "soft"
)

View file

@ -62,3 +62,11 @@ func (site *Site) SetMinSoC(soc int) error {
}
return nil
}
// RemoteControl sets remote status demand
func (site *Site) RemoteControl(source string, demand RemoteDemand) {
site.log.INFO.Println("remote demand:", demand)
for _, lp := range site.loadpoints {
lp.RemoteControl(source, demand)
}
}