Hey Patrick,
of cause I can share my rules. But keep in mind, that I’m also kinda a beginner with OpenHAB and not a professional programmer. So the code definitely isn’t perfect 
So maybe some considerations first:
- I’m getting my photovoltaic production values via Modbus from my SMA inverter. I need to know how much power is produced and how much is needed else where in the household
- I’m using an GOe-charger, because it’s somewhat cheap and has a quite open API. The only downside is, that it’s not possible to change the amount of phases used for charging via the API. My current solution is to have two plugs for it, one for 1 phase and one for 3 phases. So I have to change the amount manually. Also the amperage can only be changed by 1A steps, beginning with 6A up to 32A (16A for Tesla). This results in roughly 1400W to 3700W for 1 phase operation and 4100W to 11000W for 3 phase operation.
- I have an electrical boiler controlled via a smart plug for warm usage water. This is somewhat also considered in the rules.
The rules are divided into 3 parts:
- Check every 20 seconds what power is available for the charger
- Define the amperage for the charger depending on the check or user input
- Wake the Tesla if charging could start
The user inputs are the following (sorry for german variable/item names, the rules weren’t meant to publish
):
- ChargerWunschleistungUser: The power the charger should output. -2 means off always, -1 the electrical boiler has priority, 0: Auto, >0 the define the power.
- ChargerPVEigenverbrauch: How much percentage of the power the charger outputs should be from my own production. Because I pay less during the night, I have to produce at least 40% on my own that it’s worth charging the car. Anyway I have it most of the time on 70% or 90%.
Now to the code with a basic explanation. Please simply ask if you need further descriptions.
Checking each 20 seconds for the available power
First I check if the user input is above -1 which means it’s not turned off. Then I check if a car is connected to the charger. The variable “powerPurchaseAverage” gives the available power. For example if I produce 1000W and 200W is used, it’s 800W. The “Verlustfaktor” I use to somewhat take the power loss due heat generated in the cables. The target of the rule is to define the variable “ChargerSollLeistung” which will then be used in the next rule.
var double verlustFaktor
rule "Elektroauto Sollleistung einstellen"
when
Time cron "8/20 * * * * ? *" // alle 20 Sekunden mit 8 Sekunden Offset
then
val int chargerWunschleistungUser = (ChargerWunschleistungUser.state as DecimalType).intValue
if (chargerWunschleistungUser >= -1) { // Wenn -2 dann immer aus, -1 => Boiler Prio, 0 => Auto, >0 => Manuelle Leistung
val int chargerCar = (ChargerCar.state as DecimalType).intValue
if (chargerCar == 1) { // Kein Auto verbunden
} else {
if (chargerWunschleistungUser <= 0) {
val int powerPurchaseAverage = (PowerPurchase.state as DecimalType).intValue
// Verlustfaktor berechnen
verlustFaktor = 1 + (0.035 / Math.pow(16, 2) * Math.pow((ChargerAmpere.state as DecimalType).doubleValue, 2)) // Annahme 3.5% bei 16A, 0% bei 0A. Quadratischer Zusammenhang
val int chargerLeistung = ((ChargerLeistung.state as DecimalType) * 10 * verlustFaktor).intValue
var int availableLeistung = powerPurchaseAverage + chargerLeistung
if (chargerWunschleistungUser == -1 && !Boiler_UserInput.state.toString.contains("OFF")) { // Bei Boiler Prio wird die verfügbare Leistung reduziert, falls sie höher ist als der Boiler Grenzwert.
if (OnOff_Boiler.state == OFF) {
var int grenzwertBoiler = (BoilerGrenzwertAuto.state as DecimalType).intValue
if (grenzwertBoiler < 11000) {
if ((Boiler_UserInputGrenzwert.state as DecimalType).intValue != 0) {
// User hat einen manuellen Grenzwert gesetzt
grenzwertBoiler = (Boiler_UserInputGrenzwert.state as DecimalType).intValue
}
if (availableLeistung > grenzwertBoiler) {
availableLeistung = availableLeistung - 4400
}
}
}
}
if (availableLeistung <= 0) {
if (ChargerSollLeistung.state != 0) {
ChargerSollLeistung.postUpdate(0)
}
if (AllowCharging.state == 1) {
sendHttpGetRequest("http://192.168.0.20/mqtt?payload=alw=0")
}
} else {
ChargerSollLeistung.postUpdate(availableLeistung)
}
}
}
} else {
if (AllowCharging.state == 1) {
sendHttpGetRequest("http://192.168.0.20/mqtt?payload=alw=0")
}
}
end
Define the amperage for the charger
Well this may be overly complex, but it’s grown like this and I’m to lazy to optimize it 
So each time the “ChargerSollLeistung” or user input “ChargerWunschleistungUser” is changed, this rule will fire. Basically first it checks how much voltage is connected to the charger, which is used to calculate the power related to the amperage and secondly is used to determine how much phases are connected to the charger. If the power requested is higher than a threshold, it will send me a notification to my phone that I should change the amount of phases (different plug).
Then the code is divided into two scenarios:
- I’m below the minimum of 6A
- I’m above the 6A
If it’s below the 6A the percentage needed to start the car charging is modified by 10%. So if it’s set to 60%, the charging will start when I produce 70% and stop when I produce 50%. The intention is to prevent to much starting and stopping of the charging.
If it’s above 6A then I divide the requested power by the floor amperage. The rest of the division (for example if 7 is divided by 3 the result is 2.33. The 0.33 will be the rest) is compared to the percentage the user has inputted. If it’s below the user input, then the lower amperage is used, if it’s upper, the upper amperage is used.
At the end the results are send to the charger via HTTP.
rule "Stromstärke am Laderegler Elektroauto eistellen"
when
Item ChargerSollLeistung changed or
Item ChargerWunschleistungUser changed
then
val int spannungL1 = (ChargerSpannungL1.state as DecimalType).intValue
val int spannungL2 = (ChargerSpannungL2.state as DecimalType).intValue
val int spannungL3 = (ChargerSpannungL3.state as DecimalType).intValue
if (verlustFaktor == null) {
verlustFaktor = 1
}
val int summeSpannungen = ((spannungL1 + spannungL2 + spannungL3) * verlustFaktor).intValue
var int chargerAmpere = (ChargerAmpere.state as DecimalType).intValue
var double wunschleistung = 0
if (ChargerWunschleistungUser.state > 0) {
wunschleistung = (ChargerWunschleistungUser.state as DecimalType).doubleValue
} else {
wunschleistung = (ChargerSollLeistung.state as DecimalType).doubleValue
}
val double notwendigeAmpere = wunschleistung / summeSpannungen
var double userEigenverbrauch = (ChargerPVEigenverbrauch.state as DecimalType).doubleValue / 100
var double istEigenverbrauch = 0
// Checken wie viele Phasen angeschlossen sind und ob etwas geändert werden sollte
if (userEigenverbrauch >= 0) {
val Number letzteBenachrichtigung = (LetzteBenachrichtigung.state as DecimalType)
val Number minutenSeitLetzterBenachrichtigung = (now.millis / 60000) - letzteBenachrichtigung
if (minutenSeitLetzterBenachrichtigung >= 30) { // Nur alle 30 Minuten benachrichtigen
if (summeSpannungen > 600) { // 3 Phasen sind angeschlossen
val double einPhasenAmpere = 2 * userEigenverbrauch // 2 Ampere dreiphasig entsprechen 6 Ampere einphasig
if (notwendigeAmpere < 5.5 && notwendigeAmpere >= einPhasenAmpere) {
Benachrichtigung.postUpdate("Anzahl Phasen auf 1 reduzieren")
LetzteBenachrichtigung.postUpdate(now.millis / 60000)
}
} else { // 1 Phase ist angeschlossen
if (notwendigeAmpere >= 19) {
Benachrichtigung.postUpdate("Anzahl Phasen auf 3 erhöhen")
LetzteBenachrichtigung.postUpdate(now.millis / 60000)
}
}
}
}
if (notwendigeAmpere == 0) {
chargerAmpere = 0
} else if (notwendigeAmpere <= 6) {
if (ChargerWunschleistungUser.state <= 0) {
if (ChargerCar.state != 2) { // Das Auto wird aktuell nicht geladen.
userEigenverbrauch = userEigenverbrauch + 0.1
if (userEigenverbrauch > 1.1) {
userEigenverbrauch = 1.1
}
} else { // Das Auto wird geladen
userEigenverbrauch = userEigenverbrauch - 0.1
if (userEigenverbrauch <= 0) {
userEigenverbrauch = 0.005
}
}
istEigenverbrauch = wunschleistung / (6 * summeSpannungen)
if (istEigenverbrauch < userEigenverbrauch) {
chargerAmpere = 0
} else {
chargerAmpere = 6
}
} else {
chargerAmpere = 6
}
} else if (notwendigeAmpere < 16) {
val double restAmpere = notwendigeAmpere - Math.floor(notwendigeAmpere)
if (restAmpere < userEigenverbrauch) {
chargerAmpere = (Math.floor(notwendigeAmpere)).intValue
} else {
chargerAmpere = (Math.ceil(notwendigeAmpere)).intValue
}
} else {
chargerAmpere = 16
}
if (chargerAmpere < 6) {
if (AllowCharging.state != 0) {
sendHttpGetRequest("http://192.168.0.20/mqtt?payload=alw=0")
}
if (ChargerAmpere.state != 6) {
sendHttpGetRequest("http://192.168.0.20/mqtt?payload=amp=6")
}
} else {
if (AllowCharging.state != 1) {
sendHttpGetRequest("http://192.168.0.20/mqtt?payload=alw=1")
}
if (ChargerAmpere.state != chargerAmpere) {
sendHttpGetRequest("http://192.168.0.20/mqtt?payload=amp=" + String.valueOf(chargerAmpere))
}
}
end
Wake the tesla
Should be pretty straight forward.
rule "Tesla bei Bedarf aufwecken"
when
Item AllowCharging changed or
Item TeslaState changed or
Item TeslaChargeLimit changed or
Item TeslaChargingState changed
then
if (TeslaState != "online" && AllowCharging.state == 1 && TeslaChargingState.state != "Disconnected") {
if ((TeslaChargeLimit.state as DecimalType).intValue > (TeslaBatteryLevel.state as DecimalType).intValue) {
TeslaWakeup.sendCommand(ON)
}
}
end
rule "Tesla aufwecken deaktivieren"
when
Item TeslaState changed or
Item TeslaWakeup changed
then
if (TeslaWakeup.state == ON) {
if (TeslaState.state == "online") {
TeslaWakeup.sendCommand(OFF)
}
}
end
Remarks
I only own the Tesla for 2 month which where winter time. So I couldn’t test it if I have a lot of power left. Most of the time it was to cloudy to really fully charge the car. But in the times I had some power, it worked like a charm.
The other thing is that the adjustments of the amperage a bit delayed. I plan to add a trend to the script knows a bit better how the power of the photovoltaic will be change to better set the amperage. This should be quite simple by using averageSince and calculate a linear trend. But I hadn’t the time for it yet.
I hope this helps. If any questions, please ask.