With this set of rules and items, I handle excess charging of a Tesla model Y and a Renault Zoe using a go-e Charger Gemini flex. The script may also work with other wallboxes and cars. The go-e wallbox allows to switch between 1 and 3 phases using the api. If your wallbox does not support this, just leave away the related parts of the code.
You need some source that delivers the current power consumption of your house, e.g. a smart meter. The script tries to keep the power consumption within the configured range.
The script also supports underpowering. If this is activated, it will not stop charging if there is not enough power, it just reduces the power to the minimum supported by the wallbox.
As the Zoe behaves significantly different from the Tesla when being charged, a different set of parameters is applied for it. The script uses the connection state of the Tesla to detect this, as the plug state of the Zoe is not really accurate all the time.
The script writes detailed logs, so you should always see whats going on. This is especially useful to fine-tune the settings
A few items to turn on and off excess charging and underpowering. The names of the items are hopefully self-explanating. As I am German, some of them have german names. I hope they are still understandable.
Ok, let’s go:
Rule to turn on Excess charging:
configuration: {}
triggers:
- id: "1"
configuration:
command: ON
itemName: Ueberschussladen
type: core.ItemCommandTrigger
conditions: []
actions:
- inputs: {}
id: "2"
configuration:
command: "6"
itemName: GoEChargerMaxCurrent
type: core.ItemCommandAction
- inputs: {}
id: "3"
configuration:
itemName: GoeCharger_Force_state
command: "1"
type: core.ItemCommandAction
- inputs: {}
id: "4"
configuration:
itemName: GoEChargerPhases
command: "1"
type: core.ItemCommandAction
- inputs: {}
id: "5"
configuration:
itemName: PhaseSwitchCountdown
command: "6"
type: core.ItemCommandAction
Rule to turn off excess charging:
configuration: {}
triggers:
- id: "1"
configuration:
command: OFF
itemName: Ueberschussladen
type: core.ItemCommandTrigger
conditions: []
actions:
- inputs: {}
id: "2"
configuration:
command: "32"
itemName: GoEChargerMaxCurrent
type: core.ItemCommandAction
- inputs: {}
id: "3"
configuration:
itemName: GoeCharger_Force_state
command: "0"
type: core.ItemCommandAction
- inputs: {}
id: "4"
configuration:
itemName: GoEChargerPhases
command: "3"
type: core.ItemCommandAction
And here the magic happens: The excess charge control rule, running every 10 seconds:
// is excess charging requested?
if (Ueberschussladen.state == OFF) {
return;
}
// is the wallbox available
if (GoEChargerPwmSignal.state == UNDEF) {
logInfo("Charge control","No status from Wallbox")
return;
}
// if there is no car connected, no need to do anything
if (GoEChargerPwmSignal.state == "READY_NO_CAR" || GoEChargerPwmSignal.state == "IDLE" ) {
return;
}
// if openHAB was restarted, some parameters might not be set correctly. Set defaults
if (ExcessUnderpower.state == NULL ) {
logInfo("Charge control","ExcessUnderpower was NULL, was openHAB restarted? Setting defaults")
ExcessUnderpower.sendCommand ( OFF )
ExcessChargeTesla.sendCommand( ON )
ExcessDecreaseCounter.sendCommand(0)
ExcessCurrentMin.sendCommand(6)
return
}
// minimum charging current accepted by cars.
if (ExcessCurrentMin.state == NULL ) {
logInfo("Charge control","ExcessCurrentMin not set")
ExcessCurrentMin.sendCommand(6)
return
}
var Number minChargingCurrent = ExcessCurrentMin.state as DecimalType
val Number phases = GoEChargerPhases.state as DecimalType
// maximum charging current possible for charger
val Number maxChargingCurrent = 16
// this could happen after openhab restart, so as fallback we assume Tesla
if (ExcessMinPower.state == NULL ) {
ExcessChargeTesla.sendCommand( ON )
}
if (ExcessDecreaseCounter.state == NULL) {
ExcessDecreaseCounter.sendCommand(0)
}
// minimum available power needed to start charging
var Number minAvailablePower = ExcessMinPower.state as DecimalType
// the power necessary to increase charging current by one step
var Number powerForIncrease = 270 * phases
// the power necessare to decrease charging current. Negative means we allow some underpowering
var Number powerForDecrease = -20 * phases
// power needed to switch to 3 phases
var Number powerForTPh = 750
// minimum available power needed to start charging -- for 1 phases ~ 6*230
minAvailablePower = 1350
// the number of countdown steps we wait after turning on or switching phases
var Number countdownSteps = 6
// minimum current for 3 phases
var Number minCurrent3P = 6
// Special values for Zoe. If Tesla is disconnected assume Zoe is charging
if (Tessi_Charging_State.state == "Disconnected" ) {
powerForIncrease = 320 * phases
powerForDecrease = -40 * phases
minCurrent3P = 8
powerForTPh = 500
if (phases > 1) {
minChargingCurrent = 8
} else {
minChargingCurrent = 6
}
}
var Number increase = 0
var Number decrease = 0
// available power is the sum of watts off all phases from smartmeter. Negative values mean we are exporting energy
var Number powerP1 = SmP1Watt.state as Number
var Number powerP2 = SmP2Watt.state as Number
var Number powerP3 = SmP3Watt.state as Number
var Number availablePower = ( powerP1 + powerP2 + powerP3 ) * (-1)
var Number current = GoEChargerMaxCurrent.state as QuantityType
logInfo("Charge control","Available power: "+availablePower+", phases: "+phases+", current: "+current+", increase at "+powerForIncrease+", decrease at "+powerForDecrease)
// After charging start or phase switch, cars and wallbox need some time to adapt.
if (PhaseSwitchCountdown.state == NULL) {
PhaseSwitchCountdown.sendCommand(0)
} else {
var Number countdown = PhaseSwitchCountdown.state as DecimalType
if (countdown > 0) {
PhaseSwitchCountdown.sendCommand(countdown - 1)
logInfo("Charge control","Start / phase change running, wait "+countdown+" more cycles")
ExcessDecreaseCounter.sendCommand(0)
return
}
}
// if state is off, we do not charge yet and need the initial power before starting
var Number forcestate = GoeCharger_Force_state.state as DecimalType
if (forcestate == 1 ) {
logInfo("Charge control","Mindestleistung: "+ minAvailablePower )
if (availablePower >= minAvailablePower || ExcessUnderpower.state == ON) {
logInfo("Charge control","Min power reached, start charging with "+minChargingCurrent+" amps")
GoEChargerMaxCurrent.sendCommand(minChargingCurrent)
GoeCharger_Force_state.sendCommand(0)
PhaseSwitchCountdown.sendCommand(countdownSteps)
}
return;
}
// if car is charged, no need to further control power
if (GoEChargerPwmSignal.state == "COMPLETE") {
return
}
// handle power increase and switch from 1 to 3 phases
if (availablePower >= powerForIncrease ) {
if (current < maxChargingCurrent) {
increase = Math.floor(availablePower.doubleValue / powerForIncrease.doubleValue )
if ( (current + increase) > maxChargingCurrent) {
increase = maxChargingCurrent - current
}
logInfo("Charge control","increase amps to "+(current + increase))
GoEChargerMaxCurrent.sendCommand (current + increase)
} else if (phases == 1) {
if ( availablePower > powerForTPh ) {
logInfo("Charge control","switch to 3 phases, "+minCurrent3P+" amps ")
GoEChargerPhases.sendCommand(3)
GoEChargerMaxCurrent.sendCommand (minCurrent3P)
PhaseSwitchCountdown.sendCommand(countdownSteps)
} else {
logInfo("Charge control","not enough power for phase switch")
}
} else {
logInfo("Charge control","reached maximum")
}
ExcessDecreaseCounter.sendCommand(0)
return;
}
// handle power decrease and switch from 3 to one phases
if (availablePower <= powerForDecrease) {
if (current > minChargingCurrent) {
decrease = Math.floor(availablePower.doubleValue / powerForIncrease.doubleValue)*-1
if ((current - decrease) < minChargingCurrent) {
decrease = current - minChargingCurrent
}
logInfo("Charge control","decreasing to "+(current - decrease)+" amps")
GoEChargerMaxCurrent.sendCommand (current - decrease)
} else if (phases > 1) {
var Number decreaseCounter = ExcessDecreaseCounter.state as DecimalType
if (decreaseCounter > 6) {
logInfo("Charge control","switching to 1 phase, 16 amps")
GoEChargerPhases.sendCommand(1)
GoEChargerMaxCurrent.sendCommand (16)
PhaseSwitchCountdown.sendCommand(countdownSteps)
ExcessDecreaseCounter.sendCommand(0)
} else {
ExcessDecreaseCounter.sendCommand(decreaseCounter+1)
logInfo("Charge control","DecreaseCounter: "+decreaseCounter)
}
}
// we already hit the minimum, so we turn off charging completely now
else if (ExcessUnderpower.state == OFF) {
logInfo("Charge control","too little power, stop charging")
GoeCharger_Force_state.sendCommand(1)
}
return;
}
// reset decreaseCounter, just in case
ExcessDecreaseCounter.sendCommand(0)
// we are within the allowed power range, do nothing.
logInfo("Charge control","no action")
In case of questions, just ask