Go-eCharger control based on photovoltaik grid feed

This is my rule I currently use for controlling the charging power for a Go-eCharger based on the surplus from photovoltaik energy. It assumes the following items to be defined:

Switch Charging_Solar_Automatic_Override             // a virtual switch in OpenHAB2 to force charging
Number Solar_Meter_Power_CurrentGridConsumed      // the current electric power consumption from the power grid (in my case, this is negative when there is a surplus, i.e. when the car charger should be enabled)
Number:Power GoECharger_PowerL1     // as defined by the default example for the Go-eCharger binding
Number:Power GoECharger_PowerL2     // as defined by the default example for the Go-eCharger binding
Number:Power GoECharger_PowerL3     // as defined by the default example for the Go-eCharger binding
Number:ElectricCurrent GoECharger_MaxCurrent     // as defined by the default example for the Go-eCharger binding
Switch GoECharger_AllowCharging     // as defined by the default example for the Go-eCharger binding

Note that the item values are assumed to be stored in a database that supports averaging over a timeframe (e.g. influxdb).

I currently use a Fronius converter with the respective binding and the GridConsumed item uses channel "fronius:powerinverter:datalogger:inverter:powerflowchannelpgrid". However, any smart meter / inverter will work if it can return the current surplus.

The rule is then defined as

// ATTENTION: Constants to tweak for the local installation
val GridVoltage = 220
val MaxAmperage = 16		// the maximum amperage the charger can use (typically the fuse/cable rating, typically 16A)
val MinAmperage = 6			// the minimum amperage the charger can be set to (e.g. for the Go-eCharger this is 6A)

rule "Set maximum current for car charger based on PV feed (compute every 10 minutes)"
when Time cron "0 */10 * ? * *" or
	 Item Charging_Solar_Automatic_Override changed
then
	// Note: all values averaged over the last 10 minutes to even out spikes in solar generation and other local load
	val averageSince = now.minusMinutes(10)
	val currentChargePower = GoECharger_PowerL1.averageSince(averageSince) +
		GoECharger_PowerL2.averageSince(averageSince) +
		GoECharger_PowerL3.averageSince(averageSince)
	// this is the total power available, i.e. current feeding into grid + what we are currently charging into the car
	// Note: the powerflowchannelpgrid channel for Fronius smart meter is negative when feeding into the grid, positive when pulling,
	// therefore need to invert here
	val totalPower = currentChargePower - Solar_Meter_Power_CurrentGridConsumed.averageSince(averageSince)
	logInfo("Electric power optimization", "Available power for charging: " + (-(Solar_Meter_Power_CurrentGridConsumed.state as Number)) + " (current grid feed, average in last 10 min: " + (-Solar_Meter_Power_CurrentGridConsumed.averageSince(averageSince)) + ") + " + currentChargePower + " (current charge power) = " + totalPower + "W")

	var boolean allow = ON
	var int current = MinAmperage

	// If the override switch is set, allow charging at maximum amperage 
	if (Charging_Solar_Automatic_Override.state == ON) {
		logInfo("Electric power optimization", "Override switch is ON, allowing charging with " + MaxAmperage + "A")
		allow = ON
		current = MaxAmperage
	}
	else if (totalPower < (MinAmperage)*3*GridVoltage) {
		logInfo("Electric power optimization", "Not enough surplus power to charge, switching off")
		allow = OFF
		current = MinAmperage
	}
	else {
		allow = ON
		/*logDebug("Electric power optimization", "totalPower=" + totalPower + " / (GridVoltage*3)= " + (GridVoltage*3) + " = " +
			(totalPower / (GridVoltage*3))) */
		current = Math::floor((totalPower / (GridVoltage*3) as Number).doubleValue).intValue
		if (current > MaxAmperage) {
			logInfo("Electric power optimization", "Power surplus exceeds maximum power for charging, capping to " + current + "A")
			current = MaxAmperage
		}
	}

	// set maxCurrent and allow fields
	logInfo("Electric power optimization", "Setting charger to allow=" + allow + ", maxCurrent=" + current)
	GoECharger_MaxCurrent.sendCommand(current)
	GoECharger_AllowCharging.sendCommand(allow)
end
2 Likes

Hi,

thanks for your rule. I will be expanding my solar system in the coming months from now just 2 kWp to then just over 25 kWp and will be using a go e-Charger and openHAB to use the surplus to charge my car.

I was wondering one thing: You set your GridVoltage to 220 fixed. Does it make sense to not use fixed 220, but rather use the voltages that we get from the go e-Charger binding for each phase?

So instead of multiplying 220*3 I would do something like:

var int voltageSum = (GoeCharger_VoltageL1.state as Number + GoeCharger_VoltageL2.state as Number + GoeCharger_VoltageL3.state as Number).intValue

I am absolutely no expert, but would have guesses that this make the result come closer to the real world? Or is there something I’m missing?

Here’s your rule that I am currently “tweaking” for my system. Unfortunately, I can’t totally test it until we get the bigger solar system :slight_smile:

// ATTENTION: Constants to tweak for the local installation
// val int GridVoltage = 220
val reduceFactor = 0.95 // factor to reduce newPower to be less close to available power, e.g. 0.9 sets newPower to 90% of possible power
val MaxAmperage = 16		// the maximum amperage the charger can use (typically the fuse/cable rating, typically 16A)
val MinAmperage = 6			// the minimum amperage the charger can be set to (e.g. for the Go-eCharger this is 6A)

rule "Set maximum current for car charger based on PV feed (compute every 10 minutes)"
// when Time cron "0 */10 * ? * *" or
// 	 Item Charging_Solar_Automatic_Override changed
when Item WR_Live_Export changed or
     Item WR_Live_Export_Test changed or
 	 Item Charging_Solar_Automatic_Override changed
then
 
	// Note: all values averaged over the last 10 minutes to even out spikes in solar generation and other local load
	//val averageSince = now.minusMinutes(10)
	// val currentChargePower = GoeCharger_PowerL1.averageSince(averageSince) +
	// 	GoeCharger_PowerL2.averageSince(averageSince) +
	// 	GoeCharger_PowerL3.averageSince(averageSince)
    val currentChargePower = (GoeCharger_PowerL1.state as Number).intValue +
		(GoeCharger_PowerL2.state as Number).intValue  +
		(GoeCharger_PowerL3.state as Number).intValue 


	// // this is the total power available, i.e. current feeding into grid + what we are currently charging into the car
	val totalPower = (currentChargePower + ((WR_Live_Export_Test.state as Number)*1000)) * reduceFactor

	var int allow = 0
	var int current = MinAmperage
    var int voltageSum = (GoeCharger_VoltageL1.state as Number + GoeCharger_VoltageL2.state as Number + GoeCharger_VoltageL3.state as Number).intValue


	// If the override switch is set, allow charging at maximum amperage 
	if (Charging_Solar_Automatic_Override.state == ON) {
		logInfo("Electric power optimization", "Override switch is ON, allowing charging with " + MaxAmperage + "A")
		allow = 1
		current = MaxAmperage
	}
	//else if (totalPower < MinAmperage * (GoeCharger_VoltageL1.state as Number + GoeCharger_VoltageL2.state as Number + GoeCharger_VoltageL3.state as Number)) { //else if (totalPower < (MinAmperage)*3*GridVoltage) {
	else if (totalPower < (MinAmperage * voltageSum)) { //
        logInfo("Electric power optimization", "Not enough surplus power to charge, switching off; totalPower: " + totalPower + "; MinAmperage * Voltages: " + (MinAmperage * voltageSum))
		
        allow = 0
		current = MinAmperage
	}
	else {
		allow = 1
		/*logDebug("Electric power optimization", "totalPower=" + totalPower + " / (GridVoltage*3)= " + (GridVoltage*3) + " = " +
			(totalPower / (GridVoltage*3))) */
        current = Math::floor((totalPower / (voltageSum) as Number).doubleValue).intValue

        if (current > MaxAmperage) {
			logInfo("Electric power optimization", "Power surplus exceeds maximum power for charging, capping to " + MaxAmperage + "A")
			current = MaxAmperage
		}
	}

	// set maxCurrent and allow fields
	logInfo("Electric power optimization", "Setting charger to allow=" + allow + ", maxCurrent=" + current)

    // if (allow == 1)
    //     GoeCharger_MaximumCurrent.sendCommand(ON)
    // else 
    //     GoeCharger_MaximumCurrent.sendCommand(OFF)

	// GoeCharger_AllowCharging.sendCommand(allow)
end

That makes sense to me - I will look into that on my rule as well.

Cool.

One more question. When using (a modified version of) your rule, it looks like to takes quite a long time to get down from for example 16 A charging to turning the go-e off. I even changed the rule to not run every 10 but every 1 minute, and my rule went from 16 A down to 15 A after the first minute, then to 14 A after another… to 6 A, and then off. So over 10 minutes. Maybe I messed something up changing the rule though. Is it the same for you, and did you do that on purpose?

I guess it does make sense to not turn off charging right away with every cloud, but maybe it’s better to do it a little more quickly? :slight_smile: