[evcc] Adjust to evcc version 0.123.1 (#16114)

* updated url of setTargetEnergy and setTargetSoC to match evcc version 0.123.1
* removed minSoc from Loadpoint (since evcc 0.123.0 part of vehicle)
* renamed from targetEnergy to limitEnergy to match new evcc version
* renamed from targetSoC to limitSoC to match new evcc version
* plementation of vehicle object to match new evcc version 0.123.1 -> new implementation of minSoC and plans (served by new api)

Signed-off-by: Luca Arnecke <luca@arnecke.name>
Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
Signed-off-by: Michael Weger <weger.michael@gmx.net>
Co-authored-by: Florian Hotze <florianh_dev@icloud.com>
Co-authored-by: Michael Weger <weger.michael@gmx.net>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
lucaarn 2024-02-05 20:49:57 +01:00 committed by Ciprian Pascu
parent bd97a633f9
commit 1521e25ab9
10 changed files with 847 additions and 334 deletions

View File

@ -1,7 +1,7 @@
# evcc Binding
This binding integrates [evcc - electric vehicle charging control](https://evcc.io), a project that provides a control center for electric vehicle charging.
The binding requires evcc [version 0.117.0](https://github.com/evcc-io/evcc/releases/tag/0.117.0) or newer and is tested with this version.
The binding requires evcc [version 0.123.1](https://github.com/evcc-io/evcc/releases/tag/0.123.1) or newer and is tested with this version.
You can easily install and upgrade evcc on openHABian using `sudo openhabian-config`.
@ -38,50 +38,66 @@ Default value for _refreshInterval_ is 60 seconds.
Those channels exist only once.
Please note that some of them are only available when evcc is properly configured.
| Channel | Type | Read/Write | Description |
|----------------------------|----------------------|------------|--------------------------------------------------------------------------------------------------------------|
| general#batteryCapacity | Number:Energy | R | Capacity of (home) battery. |
| general#batteryPower | Number:Power | R | Current power from battery. |
| general#batterySoC | Number:Dimensionless | R | Current State of Charge of battery. |
| general#batteryPrioritySoC | Number:Dimensionless | RW | State of State of Charge for which the battery has priority over charging the ev when charging mode is "pv". |
| general#gridPower | Number:Power | R | Current power from grid (negative means feed-in) |
| general#homePower | Number:Power | R | Current power taken by home. |
| general#pvPower | Number:Power | R | Current power from photovoltaik. |
| Channel | Type | Read/Write | Description |
|---------------------------------|----------------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| general#batteryCapacity | Number:Energy | R | Capacity of (home) battery |
| general#batteryPower | Number:Power | R | Current power from battery |
| general#batterySoC | Number:Dimensionless | R | Current State of Charge of battery |
| general#batteryDischargeControl | Switch | RW | Enable or disable battery discharge control |
| general#batteryMode | String | R | Current battery mode |
| general#prioritySoC | Number:Dimensionless | RW | State of State of Charge for which the battery has priority over charging the ev when charging mode is "pv" |
| general#bufferSoC | Number:Dimensionless | RW | Until this State of Charge the discharging of a house battery is allowed in "pv" mode, when there is insufficient solar surplus (below the minimum charging power) |
| general#bufferStartSoC | Number:Dimensionless | RW | State of Charge for which a charging session in "pv" mode is started, even if there is insufficient solar surplus |
| general#residualPower | Number:Power | RW | Target operating point of the surplus regulation at the grid connection (grid meter) |
| general#gridPower | Number:Power | R | Current power from grid (negative means feed-in) |
| general#homePower | Number:Power | R | Current power taken by home |
| general#pvPower | Number:Power | R | Current power from photovoltaik |
### Loadpoint channels
Those channels exist per configured loadpoint.
Please note that you have to replace _N_ with your loadpoint number.
| Channel | Type | Read/Write | Description |
|-------------------------------------|------------------------|------------|-----------------------------------------------------------------------------------------------------|
| loadpointN#activePhases | Number | R | Current number of active phases while charging |
| loadpointN#chargeCurrent | Number:ElectricCurrent | R | Current amperage per connected phase while charging |
| loadpointN#chargeDuration | Number:Time | R | Charging duration |
| loadpointN#chargeRemainingDuration | Number:Time | R | Remaining duration until target SoC is reached |
| loadpointN#chargeRemainingEnergy | Number:Energy | R | Remaining energy until target SoC is reached |
| loadpointN#chargePower | Number:Power | R | Current power of charging |
| loadpointN#chargedEnergy | Number:Energy | R | Energy charged since plugged-in |
| loadpointN#charging | Switch | R | Loadpoint is currently charging |
| loadpointN#enabled | Switch | R | Charging enabled (mode is not "off") |
| loadpointN#maxCurrent | Number:ElectricCurrent | RW | Maximum amperage per connected phase with which the car should be charged |
| loadpointN#minCurrent | Number:ElectricCurrent | RW | Minimum amperage per connected phase with which the car should be charged |
| loadpointN#minSoC | Number:Dimensionless | RW | Charge immediately with maximum power up to the defined SoC, if the charge mode is not set to "off" |
| loadpointN#mode | String | RW | Charging mode: "off", "now", "minpv", "pv" |
| loadpointN#phases | Number | RW | The maximum number of phases which can be used |
| loadpointN#targetEnergy | Number:Energy | RW | Amount of energy to charge the vehicle with |
| loadpointN#targetSoC | Number:Dimensionless | RW | Until which state of charge (SoC) should the vehicle be charged |
| loadpointN#targetTime | DateTime | RW | When the target SoC should be reached |
| loadpointN#targetTimeEnabled | Switch | RW | Target time for charging enabled |
| loadpointN#title | String | R | Title of loadpoint |
| loadpointN#vehicleConnected | Switch | R | Whether vehicle is connected to loadpoint |
| loadpointN#vehicleConnectedDuration | Number:Time | R | Duration the vehicle is connected to loadpoint |
| loadpointN#vehicleCapacity | Number:Energy | R | Capacity of EV battery |
| loadpointN#vehicleOdometer | Number:Length | R | Total distance travelled by EV |
| loadpointN#vehiclePresent | Switch | R | Whether evcc is able to get data from vehicle |
| loadpointN#vehicleRange | Number:Length | R | Battery range for EV |
| loadpointN#vehicleSoC | Number:Dimensionless | R | Current State of Charge of EV |
| loadpointN#vehicleTitle | String | R | Name of EV |
| Channel | Type | Read/Write | Description |
|-------------------------------------|------------------------|------------|-------------------------------------------------------------------------------------------------------------------|
| loadpointN#activePhases | Number | R | Current number of active phases while charging |
| loadpointN#chargeCurrent | Number:ElectricCurrent | R | Current amperage per connected phase while charging |
| loadpointN#chargeDuration | Number:Time | R | Charging duration |
| loadpointN#chargeRemainingDuration | Number:Time | R | Remaining duration until limit SoC is reached |
| loadpointN#chargeRemainingEnergy | Number:Energy | R | Remaining energy until limit SoC is reached |
| loadpointN#chargePower | Number:Power | R | Current power of charging |
| loadpointN#chargedEnergy | Number:Energy | R | Energy charged since plugged-in |
| loadpointN#charging | Switch | R | Loadpoint is currently charging |
| loadpointN#enabled | Switch | R | Charging enabled (mode is not "off") |
| loadpointN#maxCurrent | Number:ElectricCurrent | RW | Maximum amperage per connected phase with which the car should be charged |
| loadpointN#minCurrent | Number:ElectricCurrent | RW | Minimum amperage per connected phase with which the car should be charged |
| loadpointN#mode | String | RW | Charging mode: "off", "now", "minpv", "pv" |
| loadpointN#phases | Number | RW | The maximum number of phases which can be used |
| loadpointN#limitEnergy | Number:Energy | RW | Amount of energy to charge the vehicle with |
| loadpointN#limitSoC | Number:Dimensionless | RW | Until which state of charge (SoC) should the vehicle be charged |
| loadpointN#title | String | R | Title of loadpoint |
| loadpointN#vehicleConnected | Switch | R | Whether vehicle is connected to loadpoint |
| loadpointN#vehicleConnectedDuration | Number:Time | R | Duration the vehicle is connected to loadpoint |
| loadpointN#vehicleCapacity | Number:Energy | R | Capacity of EV battery |
| loadpointN#vehicleOdometer | Number:Length | R | Total distance travelled by EV |
| loadpointN#vehiclePresent | Switch | R | Whether evcc is able to get data from vehicle |
| loadpointN#vehicleRange | Number:Length | R | Battery range for EV |
| loadpointN#vehicleSoC | Number:Dimensionless | R | Current State of Charge of EV |
| loadpointN#vehicleName | String | R | The unique identifier of the EV used in the evcc configuration (containing no whitespaces nor special characters) |
### Vehicle channels
Those channels exist per configured vehicle.
Please note that you have to replace _ID_ with your vehicle id/name.
| Channel | Type | Read/Write | Description |
|------------------------------|----------------------|------------|--------------------------------------------------------------------------|
| vehicleID#vehicleTitle | String | R | Title of vehicle |
| vehicleID#vehicleMinSoC | Number:Dimensionless | RW | Minimum state of charge (SoC) a vehicle should have |
| vehicleID#vehicleLimitSoC | Number:Dimensionless | RW | Until which state of charge (SoC) should the specific vehicle be charged |
| vehicleID#vehiclePlanEnabled | Switch | RW | Plan for charging enabled |
| vehicleID#vehiclePlanSoC | Number:Dimensionless | RW | Until which state of charge (SoC) should vehicle be charged in plan |
| vehicleID#vehiclePlanTime | DateTime | RW | When the plan SoC should be reached |
## Full Example
@ -95,43 +111,53 @@ Thing evcc:device:demo "evcc Demo" [url="https://demo.evcc.io", refreshInterval=
```java
// General
Number:General evcc_batteryCapacity "Battery Capacity [%.0f kWH]" <energy> {channel="evcc:device:demo:general#batteryCapacity"}
Number:Power evcc_batteryPower "Battery Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#batteryPower"}
Number:Dimensionless evcc_batterySoC "Battery SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:general#batterySoC"}
Number:Dimensionless evcc_batteryPrioritySoC "Battery Priority SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:general#batteryPrioritySoC"}
Number:Power evcc_gridPower "Grid Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#gridPower"}
Number:Power evcc_homePower "Home Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#homePower"}
Number:Power evcc_pvPower "PV Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#pvPower"}
Number:Energy evcc_batteryCapacity "Battery Capacity [%.0f kWh]" <energy> {channel="evcc:device:demo:general#batteryCapacity"}
Number:Power evcc_batteryPower "Battery Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#batteryPower"}
Number:Dimensionless evcc_batterySoC "Battery SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:general#batterySoC"}
Switch evcc_batteryDischargeControl "Battery Discharge Control [%s]" <switch> {channel="evcc:device:demo:general#batteryDischargeControl"}
String evcc_batteryMode "Battery Mode [%s]" <battery> {channel="evcc:device:demo:general#batteryMode"}
Number:Dimensionless evcc_prioritySoC "Battery Priority SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:general#prioritySoC"}
Number:Dimensionless evcc_bufferSoC "Battery Buffer SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:general#bufferSoC"}
Number:Dimensionless evcc_bufferStartSoC "Battery Buffer Start SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:general#bufferStartSoC"}
Number:Power evcc_residualPower "Grid Residual Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#residualPower"}
Number:Power evcc_gridPower "Grid Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#gridPower"}
Number:Power evcc_homePower "Home Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#homePower"}
Number:Power evcc_pvPower "PV Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#pvPower"}
// Loadpoint
Number evcc_loadpoint0_activePhases "Active Phases [%d]" {channel="evcc:device:demo:loadpoint0#activePhases"}
Number:ElectricCurrent evcc_loadpoint0_chargeCurrent "Charging current [%.0f A]" <energy> {channel="evcc:device:demo:loadpoint0#chargeCurrent"}
Number:Time evcc_loadpoint0_chargeDuration "Charging duration [%1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0#chargeDuration"}
Number:Time evcc_loadpoint0_chargeRemainingDuration "Charging remaining duration [%1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0#chargeRemainingDuration"}
Number:Energy evcc_loadpoint0_chargeRemainingEnergy "Charging remaining energy [%.1f kWh]" <energy> {channel="evcc:device:demo:loadpoint0#chargeRemainingEnergy"}
Number:Power evcc_loadpoint0_chargePower "Charging power [%.1f kW]" <energy> {channel="evcc:device:demo:loadpoint0#chargePower"}
Number:Energy evcc_loadpoint0_chargedEnergy "Charged energy [%.1f kWh]" <energy> {channel="evcc:device:demo:loadpoint0#chargedEnergy"}
Switch evcc_loadpoint0_charging "Currently charging [%s]" <battery> {channel="evcc:device:demo:loadpoint0#charging"}
Switch evcc_loadpoint0_enabled "Charging enabled [%s]" <switch> {channel="evcc:device:demo:loadpoint0#enabled"}
Number:ElectricCurrent evcc_loadpoint0_maxCurrent "Maximum current [%.0f A]" <energy> {channel="evcc:device:demo:loadpoint0#maxCurrent"}
Number:ElectricCurrent evcc_loadpoint0_minCurrent "Minimum current [%.0f A]" <energy> {channel="evcc:device:demo:loadpoint0#minCurrent"}
Number:Dimensionless evcc_loadpoint0_minSoC "Minimum SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0#minSoC"}
String evcc_loadpoint0_mode "Mode [%s]" {channel="evcc:device:demo:loadpoint0#mode"}
Number evcc_loadpoint0_phases "Enabled phases [%d]" {channel="evcc:device:demo:loadpoint0#phases"}
Number:Energy evcc_loadpoint0_targetEnergy "Target energy [%.1f kWh]" <batterylevel> {channel="evcc:device:demo:loadpoint0#targetEnergy"}
Number:Dimensionless evcc_loadpoint0_targetSoC "Target SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0#targetSoC"}
DateTime evcc_loadpoint0_targetTime "Target time [%1$td.%1$tm.%1$tY, %1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0#targetTime"}
Switch evcc_loadpoint0_targetTimeEnabled "Target time enabled [%s]" <switch> {channel="evcc:device:demo:loadpoint0#targetTimeEnabled"}
String evcc_loadpoint0_title "Loadpoint title [%s]" <text> {channel="evcc:device:demo:loadpoint0#title"}
Number evcc_loadpoint0_activePhases "Active Phases [%d]" {channel="evcc:device:demo:loadpoint0#activePhases"}
Number:ElectricCurrent evcc_loadpoint0_chargeCurrent "Charging current [%.0f A]" <energy> {channel="evcc:device:demo:loadpoint0#chargeCurrent"}
Number:Time evcc_loadpoint0_chargeDuration "Charging duration [%1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0#chargeDuration"}
Number:Time evcc_loadpoint0_chargeRemainingDuration "Charging remaining duration [%1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0#chargeRemainingDuration"}
Number:Energy evcc_loadpoint0_chargeRemainingEnergy "Charging remaining energy [%.1f kWh]" <energy> {channel="evcc:device:demo:loadpoint0#chargeRemainingEnergy"}
Number:Power evcc_loadpoint0_chargePower "Charging power [%.1f kW]" <energy> {channel="evcc:device:demo:loadpoint0#chargePower"}
Number:Energy evcc_loadpoint0_chargedEnergy "Charged energy [%.1f kWh]" <energy> {channel="evcc:device:demo:loadpoint0#chargedEnergy"}
Switch evcc_loadpoint0_charging "Currently charging [%s]" <battery> {channel="evcc:device:demo:loadpoint0#charging"}
Switch evcc_loadpoint0_enabled "Charging enabled [%s]" <switch> {channel="evcc:device:demo:loadpoint0#enabled"}
Number:ElectricCurrent evcc_loadpoint0_maxCurrent "Maximum current [%.0f A]" <energy> {channel="evcc:device:demo:loadpoint0#maxCurrent"}
Number:ElectricCurrent evcc_loadpoint0_minCurrent "Minimum current [%.0f A]" <energy> {channel="evcc:device:demo:loadpoint0#minCurrent"}
String evcc_loadpoint0_mode "Mode [%s]" {channel="evcc:device:demo:loadpoint0#mode"}
Number evcc_loadpoint0_phases "Enabled phases [%d]" {channel="evcc:device:demo:loadpoint0#phases"}
Number:Energy evcc_loadpoint0_limitEnergy "Limit energy [%.1f kWh]" <batterylevel> {channel="evcc:device:demo:loadpoint0#limitEnergy"}
Number:Dimensionless evcc_loadpoint0_limitSoC "Limit SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0#limitSoC"}
String evcc_loadpoint0_title "Loadpoint title [%s]" <text> {channel="evcc:device:demo:loadpoint0#title"}
// Vehicle on loadpoint
Switch evcc_loadpoint0_vehicleConnected "Vehicle connected [%s]" <switch> {channel="evcc:device:demo:loadpoint0#vehicleConnected"}
Number:Time evcc_loadpoint0_vehicleConnectedDuration "Vehicle connected duration [%.1f h]" <time> {channel="evcc:device:demo:loadpoint0#vehicleConnectedDuration"}
Number:Energy evcc_loadpoint0_vehicleCapacity "Vehicle capacity [%.0f kWH]" <batterylevel> {channel="evcc:device:demo:loadpoint0#vehicleCapacity"}
Number:Length evcc_loadpoint0_vehicleOdometer "Vehicle odometer [%.1f km]" {channel="evcc:device:demo:loadpoint0#vehicleOdometer"}
Switch evcc_loadpoint0_vehiclePresent "Vehicle present [%s]" <switch> {channel="evcc:device:demo:loadpoint0#vehiclePresent"}
Number:Length evcc_loadpoint0_vehicleRange "Vehicle Range [%.0f km]" {channel="evcc:device:demo:loadpoint0#vehicleRange"}
Number:Dimensionless evcc_loadpoint0_vehicleSoC "Vehicle SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0#vehicleSoC"}
String evcc_loadpoint0_vehicleName "Vehicle name [%s]" <text> {channel="evcc:device:demo:loadpoint0#vehicleTitle"}
Switch evcc_loadpoint0_vehicleConnected "Vehicle connected [%s]" <switch> {channel="evcc:device:demo:loadpoint0#vehicleConnected"}
Number:Time evcc_loadpoint0_vehicleConnectedDuration "Vehicle connected duration [%.1f h]" <time> {channel="evcc:device:demo:loadpoint0#vehicleConnectedDuration"}
Number:Energy evcc_loadpoint0_vehicleCapacity "Vehicle capacity [%.0f kWh]" <batterylevel> {channel="evcc:device:demo:loadpoint0#vehicleCapacity"}
Number:Length evcc_loadpoint0_vehicleOdometer "Vehicle odometer [%.1f km]" {channel="evcc:device:demo:loadpoint0#vehicleOdometer"}
Switch evcc_loadpoint0_vehiclePresent "Vehicle present [%s]" <switch> {channel="evcc:device:demo:loadpoint0#vehiclePresent"}
Number:Length evcc_loadpoint0_vehicleRange "Vehicle Range [%.0f km]" {channel="evcc:device:demo:loadpoint0#vehicleRange"}
Number:Dimensionless evcc_loadpoint0_vehicleSoC "Vehicle SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0#vehicleSoC"}
String evcc_loadpoint0_VehicleName "Vehicle name [%s]" <text> {channel="evcc:device:demo:loadpoint0#vehicleName"}
// Vehicle
String evcc_vehicle0_vehicleTitle "Vehicle title [%s]" <text> {channel="evcc:device:demo:vehicle0#vehicleTitle"}
Number:Dimensionless evcc_vehicle0_vehicleMinSoC "Vehicle minimum SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:vehicle0#vehicleMinSoC"}
Number:Dimensionless evcc_vehicle0_vehicleLimitSoC "Vehicle limit SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:vehicle0#vehicleLimitSoC"}
Switch evcc_vehicle0_vehiclePlanEnabled "Vehicle plan enabled [%s]" <switch> {channel="evcc:device:demo:vehicle0#vehiclePlanEnabled"}
Number:Dimensionless evcc_vehicle0_vehiclePlanSoC "Vehicle plan SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:vehicle0#vehiclePlanSoC"}
DateTime evcc_vehicle0_vehiclePlanTime "Vehicle plan time [%1$td.%1$tm.%1$tY, %1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0#targetTime"}
```
### Sitemap
@ -158,8 +184,8 @@ sitemap evcc label="evcc Demo" {
}
Switch item=evcc_loadpoint0_mode mappings=["off"="Stop","now"="Now","minpv"="Min + PV", "pv"="Only PV"]
Text label="Charging settings" icon="settings" {
Setpoint item=evcc_loadpoint0_targetEnergy minValue=5 maxValue=100 step=5
Setpoint item=evcc_loadpoint0_targetSoC minValue=5 maxValue=100 step=5
Setpoint item=evcc_loadpoint0_limitEnergy minValue=5 maxValue=100 step=5
Setpoint item=evcc_loadpoint0_limitSoC minValue=5 maxValue=100 step=5
Setpoint item=evcc_loadpoint0_minCurrent minValue=6 maxValue=96 step=2
Setpoint item=evcc_loadpoint0_maxCurrent minValue=6 maxValue=96 step=2
Setpoint item=evcc_loadpoint0_minSoC minValue=0 maxValue=100 step=5
@ -170,6 +196,12 @@ sitemap evcc label="evcc Demo" {
Text item=evcc_loadpoint0_vehicleOdometer
Text item=evcc_loadpoint0_vehicleRange
Text item=evcc_loadpoint0_vehicleSoC
Text item=evcc_vehicle0_vehicleTitle
Setpoint item=evcc_vehicle0_vehicleMinSoC minValue=0 maxValue=100 step=5
Setpoint item=evcc_vehicle0_vehicleLimitSoC minValue=5 maxValue=100 step=5
Switch item=evcc_vehicle0_vehiclePlanEnabled
Setpoint item=evcc_vehicle0_vehiclePlanSoC minValue=5 maxValue=100 step=5
Input item=evcc_vehicle0_vehiclePlanTime
}
}
}

View File

@ -21,6 +21,7 @@ import org.openhab.core.thing.type.ChannelTypeUID;
* used across the whole binding.
*
* @author Florian Hotze - Initial contribution
* @author Luca Arnecke - Update to evcc version 0.123.1
*/
@NonNullByDefault
public class EvccBindingConstants {
@ -28,12 +29,19 @@ public class EvccBindingConstants {
private static final String BINDING_ID = "evcc";
public static final String CHANNEL_GROUP_ID_GENERAL = "general";
public static final String CHANNEL_GROUP_ID_LOADPOINT = "loadpoint";
public static final String CHANNEL_GROUP_ID_VEHICLE = "vehicle";
// List of all Channel ids
public static final String CHANNEL_BATTERY_CAPACITY = "batteryCapacity";
public static final String CHANNEL_BATTERY_POWER = "batteryPower";
public static final String CHANNEL_BATTERY_SOC = "batterySoC";
public static final String CHANNEL_BATTERY_PRIORITY_SOC = "batteryPrioritySoC";
public static final String CHANNEL_BATTERY_DISCHARGE_CONTROL = "batteryDischargeControl";
public static final String CHANNEL_BATTERY_MODE = "batteryMode";
public static final String CHANNEL_PRIORITY_SOC = "prioritySoC";
public static final String CHANNEL_BUFFER_SOC = "bufferSoC";
public static final String CHANNEL_BUFFER_START_SOC = "bufferStartSoC";
public static final String CHANNEL_RESIDUAL_POWER = "residualPower";
public static final String CHANNEL_GRID_POWER = "gridPower";
public static final String CHANNEL_HOME_POWER = "homePower";
public static final String CHANNEL_PV_POWER = "pvPower";
@ -47,27 +55,26 @@ public class EvccBindingConstants {
public static final String CHANNEL_LOADPOINT_CHARGING = "charging";
public static final String CHANNEL_LOADPOINT_CONNECTED = "vehicleConnected";
public static final String CHANNEL_LOADPOINT_CONNECTED_DURATION = "vehicleConnectedDuration";
public static final String CHANNEL_LOADPOINT_HAS_VEHICLE = "hasVehicle";
public static final String CHANNEL_LOADPOINT_ENABLED = "enabled";
public static final String CHANNEL_LOADPOINT_MAX_CURRENT = "maxCurrent";
public static final String CHANNEL_LOADPOINT_MIN_CURRENT = "minCurrent";
public static final String CHANNEL_LOADPOINT_MIN_SOC = "minSoC";
public static final String CHANNEL_LOADPOINT_MODE = "mode";
public static final String CHANNEL_LOADPOINT_PHASES = "phases";
public static final String CHANNEL_LOADPOINT_TARGET_ENERGY = "targetEnergy";
public static final String CHANNEL_LOADPOINT_TARGET_SOC = "targetSoC";
public static final String CHANNEL_LOADPOINT_TARGET_TIME = "targetTime";
/**
* Whether a target time is set on loadpoint.
*/
public static final String CHANNEL_LOADPOINT_TARGET_TIME_ENABLED = "targetTimeEnabled";
public static final String CHANNEL_LOADPOINT_LIMIT_ENERGY = "limitEnergy";
public static final String CHANNEL_LOADPOINT_LIMIT_SOC = "limitSoC";
public static final String CHANNEL_LOADPOINT_TITLE = "title";
public static final String CHANNEL_LOADPOINT_VEHICLE_CAPACITY = "vehicleCapacity";
public static final String CHANNEL_LOADPOINT_VEHICLE_ODOMETER = "vehicleOdometer";
public static final String CHANNEL_LOADPOINT_VEHICLE_PRESENT = "vehiclePresent";
public static final String CHANNEL_LOADPOINT_VEHICLE_RANGE = "vehicleRange";
public static final String CHANNEL_LOADPOINT_VEHICLE_SOC = "vehicleSoC";
public static final String CHANNEL_LOADPOINT_VEHICLE_TITLE = "vehicleTitle";
public static final String CHANNEL_LOADPOINT_VEHICLE_NAME = "vehicleName";
public static final String CHANNEL_VEHICLE_TITLE = "vehicleTitle";
public static final String CHANNEL_VEHICLE_MIN_SOC = "vehicleMinSoC";
public static final String CHANNEL_VEHICLE_LIMIT_SOC = "vehicleLimitSoC";
public static final String CHANNEL_VEHICLE_PLAN_ENABLED = "vehiclePlanEnabled";
public static final String CHANNEL_VEHICLE_PLAN_SOC = "vehiclePlanSoC";
public static final String CHANNEL_VEHICLE_PLAN_TIME = "vehiclePlanTime";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "device");
@ -79,8 +86,17 @@ public class EvccBindingConstants {
CHANNEL_BATTERY_POWER);
public static final ChannelTypeUID CHANNEL_TYPE_UID_BATTERY_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_BATTERY_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_BATTERY_PRIORITY_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_BATTERY_PRIORITY_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_BATTERY_DISCHARGE_CONTROL = new ChannelTypeUID(BINDING_ID,
CHANNEL_BATTERY_DISCHARGE_CONTROL);
public static final ChannelTypeUID CHANNEL_TYPE_UID_BATTERY_MODE = new ChannelTypeUID(BINDING_ID,
CHANNEL_BATTERY_MODE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_PRIORITY_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_PRIORITY_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_BUFFER_SOC = new ChannelTypeUID(BINDING_ID, CHANNEL_BUFFER_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_BUFFER_START_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_BUFFER_START_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_RESIDUAL_POWER = new ChannelTypeUID(BINDING_ID,
CHANNEL_RESIDUAL_POWER);
public static final ChannelTypeUID CHANNEL_TYPE_UID_GRID_POWER = new ChannelTypeUID(BINDING_ID, CHANNEL_GRID_POWER);
public static final ChannelTypeUID CHANNEL_TYPE_UID_HOME_POWER = new ChannelTypeUID(BINDING_ID, CHANNEL_HOME_POWER);
public static final ChannelTypeUID CHANNEL_TYPE_UID_PV_POWER = new ChannelTypeUID(BINDING_ID, CHANNEL_PV_POWER);
@ -110,20 +126,14 @@ public class EvccBindingConstants {
CHANNEL_LOADPOINT_MAX_CURRENT);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_MIN_CURRENT = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_MIN_CURRENT);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_MIN_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_MIN_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_MODE = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_MODE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_PHASES = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_PHASES);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_TARGET_ENERGY = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_TARGET_ENERGY);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_TARGET_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_TARGET_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_TARGET_TIME = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_TARGET_TIME);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_TARGET_TIME_ENABLED = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_TARGET_TIME_ENABLED);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_LIMIT_ENERGY = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_LIMIT_ENERGY);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_LIMIT_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_LIMIT_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_TITLE = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_TITLE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_CAPACITY = new ChannelTypeUID(BINDING_ID,
@ -136,8 +146,20 @@ public class EvccBindingConstants {
CHANNEL_LOADPOINT_VEHICLE_RANGE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_VEHICLE_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_TITLE = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_VEHICLE_TITLE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_NAME = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_VEHICLE_NAME);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VEHICLE_TITLE = new ChannelTypeUID(BINDING_ID,
CHANNEL_VEHICLE_TITLE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VEHICLE_MIN_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_VEHICLE_MIN_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VEHICLE_LIMIT_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_VEHICLE_LIMIT_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VEHICLE_PLAN_ENABLED = new ChannelTypeUID(BINDING_ID,
CHANNEL_VEHICLE_PLAN_ENABLED);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VEHICLE_PLAN_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_VEHICLE_PLAN_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VEHICLE_PLAN_TIME = new ChannelTypeUID(BINDING_ID,
CHANNEL_VEHICLE_PLAN_TIME);
public static final int CONNECTION_TIMEOUT_MILLISEC = 5000;
public static final int LONG_CONNECTION_TIMEOUT_MILLISEC = 60000;

View File

@ -16,6 +16,8 @@ import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -24,7 +26,9 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.evcc.internal.api.EvccAPI;
import org.openhab.binding.evcc.internal.api.EvccApiException;
import org.openhab.binding.evcc.internal.api.dto.Loadpoint;
import org.openhab.binding.evcc.internal.api.dto.Plan;
import org.openhab.binding.evcc.internal.api.dto.Result;
import org.openhab.binding.evcc.internal.api.dto.Vehicle;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
@ -52,6 +56,7 @@ import org.slf4j.LoggerFactory;
* sent to one of the channels.
*
* @author Florian Hotze - Initial contribution
* @author Luca Arnecke - Update to evcc version 0.123.1
*/
@NonNullByDefault
public class EvccHandler extends BaseThingHandler {
@ -64,9 +69,7 @@ public class EvccHandler extends BaseThingHandler {
private boolean batteryConfigured = false;
private boolean gridConfigured = false;
private boolean pvConfigured = false;
private boolean targetTimeEnabled = false;
private ZonedDateTime targetTimeZDT = ZonedDateTime.now().plusHours(12);
Map<String, Triple<Boolean, Float, ZonedDateTime>> vehiclePlans = new HashMap<>();
public EvccHandler(Thing thing) {
super(thing);
@ -82,25 +85,64 @@ public class EvccHandler extends BaseThingHandler {
if (groupId == null) {
return;
}
String channelGroupId = channelUID.getGroupId();
String channelIdWithoutGroup = channelUID.getIdWithoutGroup();
EvccAPI evccAPI = this.evccAPI;
if (evccAPI == null) {
return;
}
try {
if (channelGroupId.equals(CHANNEL_GROUP_ID_GENERAL)) {
if (!channelIdWithoutGroup.equals(CHANNEL_BATTERY_PRIORITY_SOC)) {
return;
if (groupId.equals(CHANNEL_GROUP_ID_GENERAL)) {
switch (channelIdWithoutGroup) {
case CHANNEL_PRIORITY_SOC -> {
if (command instanceof QuantityType<?> qt) {
evccAPI.setPrioritySoC(qt.toUnit(Units.PERCENT).intValue());
} else if (command instanceof DecimalType dt) {
evccAPI.setPrioritySoC(dt.intValue());
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
}
case CHANNEL_BUFFER_SOC -> {
if (command instanceof QuantityType<?> qt) {
evccAPI.setBufferSoC(qt.toUnit(Units.PERCENT).intValue());
} else if (command instanceof DecimalType dt) {
evccAPI.setBufferSoC(dt.intValue());
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
}
case CHANNEL_BUFFER_START_SOC -> {
if (command instanceof QuantityType<?> qt) {
evccAPI.setBufferStartSoC(qt.toUnit(Units.PERCENT).intValue());
} else if (command instanceof DecimalType dt) {
evccAPI.setBufferStartSoC(dt.intValue());
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
}
case CHANNEL_RESIDUAL_POWER -> {
if (command instanceof QuantityType<?> qt) {
evccAPI.setResidualPower(qt.toUnit(Units.WATT).intValue());
} else if (command instanceof DecimalType dt) {
evccAPI.setResidualPower(dt.intValue());
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
}
case CHANNEL_BATTERY_DISCHARGE_CONTROL -> {
if (command == OnOffType.ON) {
evccAPI.setBatteryDischargeControl(true);
} else if (command == OnOffType.OFF) {
evccAPI.setBatteryDischargeControl(false);
} else {
logger.debug("Command has wrong type, OnOffType required!");
}
}
default -> {
return;
}
}
if (command instanceof QuantityType<?> qt) {
evccAPI.setBatteryPrioritySoC(qt.toUnit(Units.PERCENT).intValue());
} else if (command instanceof DecimalType dt) {
evccAPI.setBatteryPrioritySoC(dt.intValue());
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
} else {
} else if (groupId.startsWith(CHANNEL_GROUP_ID_LOADPOINT)) {
int loadpoint = Integer.parseInt(groupId.substring(9)) + 1;
switch (channelIdWithoutGroup) {
case CHANNEL_LOADPOINT_MODE -> {
@ -110,59 +152,25 @@ public class EvccHandler extends BaseThingHandler {
logger.debug("Command has wrong type, StringType required!");
}
}
case CHANNEL_LOADPOINT_MIN_SOC -> {
case CHANNEL_LOADPOINT_LIMIT_ENERGY -> {
if (command instanceof QuantityType<?> qt) {
evccAPI.setMinSoC(loadpoint, qt.toUnit(Units.PERCENT).intValue());
evccAPI.setLimitEnergy(loadpoint, qt.toUnit(Units.WATT_HOUR).floatValue());
} else if (command instanceof DecimalType dt) {
evccAPI.setMinSoC(loadpoint, dt.intValue());
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
}
case CHANNEL_LOADPOINT_TARGET_ENERGY -> {
if (command instanceof QuantityType<?> qt) {
evccAPI.setTargetEnergy(loadpoint, qt.toUnit(Units.WATT_HOUR).floatValue());
// DecimalType commands are interpreted as 'kWh'
evccAPI.setLimitEnergy(loadpoint, dt.intValue() * 1000);
} else {
logger.debug("Command has wrong type, QuantityType required!");
}
}
case CHANNEL_LOADPOINT_TARGET_SOC -> {
case CHANNEL_LOADPOINT_LIMIT_SOC -> {
if (command instanceof QuantityType<?> qt) {
evccAPI.setTargetSoC(loadpoint, qt.toUnit(Units.PERCENT).intValue());
evccAPI.setLimitSoC(loadpoint, qt.toUnit(Units.PERCENT).intValue());
} else if (command instanceof DecimalType dt) {
evccAPI.setTargetSoC(loadpoint, dt.intValue());
evccAPI.setLimitSoC(loadpoint, dt.intValue());
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
}
case CHANNEL_LOADPOINT_TARGET_TIME -> {
if (command instanceof DateTimeType dtt) {
targetTimeZDT = dtt.getZonedDateTime();
ChannelUID channel = new ChannelUID(getThing().getUID(), "loadpoint" + loadpoint,
CHANNEL_LOADPOINT_TARGET_TIME);
updateState(channel, new DateTimeType(targetTimeZDT));
if (targetTimeEnabled) {
try {
evccAPI.setTargetTime(loadpoint, targetTimeZDT);
} catch (DateTimeParseException e) {
logger.debug("Failed to set target charge: ", e);
}
}
} else {
logger.debug("Command has wrong type, DateTimeType required!");
}
}
case CHANNEL_LOADPOINT_TARGET_TIME_ENABLED -> {
if (command == OnOffType.ON) {
evccAPI.setTargetTime(loadpoint, targetTimeZDT);
targetTimeEnabled = true;
} else if (command == OnOffType.OFF) {
evccAPI.removeTargetTime(loadpoint);
targetTimeEnabled = false;
} else {
logger.debug("Command has wrong type, OnOffType required!");
}
}
case CHANNEL_LOADPOINT_PHASES -> {
if (command instanceof DecimalType dt) {
evccAPI.setPhases(loadpoint, dt.intValue());
@ -192,6 +200,82 @@ public class EvccHandler extends BaseThingHandler {
return;
}
}
} else if (groupId.startsWith(CHANNEL_GROUP_ID_VEHICLE)) {
String vehicleName = groupId.substring(7);
switch (channelIdWithoutGroup) {
case CHANNEL_VEHICLE_MIN_SOC -> {
if (command instanceof QuantityType<?> qt) {
evccAPI.setVehicleMinSoC(vehicleName, qt.toUnit(Units.PERCENT).intValue());
} else if (command instanceof DecimalType dt) {
evccAPI.setVehicleMinSoC(vehicleName, dt.intValue());
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
}
case CHANNEL_VEHICLE_LIMIT_SOC -> {
if (command instanceof QuantityType<?> qt) {
evccAPI.setVehicleLimitSoC(vehicleName, qt.toUnit(Units.PERCENT).intValue());
} else if (command instanceof DecimalType dt) {
evccAPI.setVehicleLimitSoC(vehicleName, dt.intValue());
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
}
case CHANNEL_VEHICLE_PLAN_ENABLED -> {
Triple<Boolean, Float, ZonedDateTime> planValues = vehiclePlans.get(vehicleName);
if (command == OnOffType.ON) {
evccAPI.setVehiclePlan(vehicleName, planValues.getMiddle().intValue(),
planValues.getRight());
vehiclePlans.put(vehicleName,
new Triple<>(true, planValues.getMiddle(), planValues.getRight()));
} else if (command == OnOffType.OFF) {
evccAPI.removeVehiclePlan(vehicleName);
vehiclePlans.put(vehicleName,
new Triple<>(false, planValues.getMiddle(), planValues.getRight()));
} else {
logger.debug("Command has wrong type, OnOffType required!");
}
}
case CHANNEL_VEHICLE_PLAN_SOC -> {
Triple<Boolean, Float, ZonedDateTime> planValues = vehiclePlans.get(vehicleName);
if (command instanceof QuantityType<?> qt) {
vehiclePlans.put(vehicleName, new Triple<>(planValues.getLeft(),
qt.toUnit(Units.PERCENT).floatValue(), planValues.getRight()));
if (planValues.getLeft()) {
evccAPI.setVehiclePlan(vehicleName, qt.toUnit(Units.PERCENT).intValue(),
planValues.getRight());
}
} else if (command instanceof DecimalType dt) {
vehiclePlans.put(vehicleName,
new Triple<>(planValues.getLeft(), dt.floatValue(), planValues.getRight()));
if (planValues.getLeft()) {
evccAPI.setVehiclePlan(vehicleName, dt.intValue(), planValues.getRight());
}
} else {
logger.debug("Command has wrong type, QuantityType or DecimalType required!");
}
}
case CHANNEL_VEHICLE_PLAN_TIME -> {
Triple<Boolean, Float, ZonedDateTime> planValues = vehiclePlans.get(vehicleName);
if (command instanceof DateTimeType dtt) {
vehiclePlans.put(vehicleName, new Triple<>(planValues.getLeft(), planValues.getMiddle(),
dtt.getZonedDateTime()));
if (planValues.getLeft()) {
try {
evccAPI.setVehiclePlan(vehicleName, planValues.getMiddle().intValue(),
dtt.getZonedDateTime());
} catch (DateTimeParseException e) {
logger.debug("Failed to set vehicle plan time: ", e);
}
}
} else {
logger.debug("Command has wrong type, DateTimeType required!");
}
}
default -> {
return;
}
}
}
} catch (EvccApiException e) {
Throwable cause = e.getCause();
@ -248,6 +332,8 @@ public class EvccHandler extends BaseThingHandler {
String sitename = result.getSiteTitle();
int numberOfLoadpoints = result.getLoadpoints().length;
logger.debug("Found {} loadpoints on site {}.", numberOfLoadpoints, sitename);
Map<String, Vehicle> vehicles = result.getVehicles();
logger.debug("Found {} vehicles on site {}.", vehicles.size(), sitename);
updateStatus(ThingStatus.ONLINE);
batteryConfigured = result.getBatteryConfigured();
gridConfigured = result.getGridConfigured();
@ -258,6 +344,10 @@ public class EvccHandler extends BaseThingHandler {
createChannelsLoadpoint(i);
updateChannelsLoadpoint(i);
}
for (String vehicleName : vehicles.keySet()) {
createChannelsVehicle(vehicleName);
updateChannelsVehicle(vehicleName);
}
}
}
@ -279,8 +369,17 @@ public class EvccHandler extends BaseThingHandler {
"Number:Power");
createChannel(CHANNEL_BATTERY_SOC, CHANNEL_GROUP_ID_GENERAL, CHANNEL_TYPE_UID_BATTERY_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_BATTERY_PRIORITY_SOC, CHANNEL_GROUP_ID_GENERAL, CHANNEL_TYPE_UID_BATTERY_PRIORITY_SOC,
createChannel(CHANNEL_BATTERY_DISCHARGE_CONTROL, CHANNEL_GROUP_ID_GENERAL,
CHANNEL_TYPE_UID_BATTERY_DISCHARGE_CONTROL, "Switch");
createChannel(CHANNEL_BATTERY_MODE, CHANNEL_GROUP_ID_GENERAL, CHANNEL_TYPE_UID_BATTERY_MODE, "String");
createChannel(CHANNEL_PRIORITY_SOC, CHANNEL_GROUP_ID_GENERAL, CHANNEL_TYPE_UID_PRIORITY_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_BUFFER_SOC, CHANNEL_GROUP_ID_GENERAL, CHANNEL_TYPE_UID_BUFFER_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_BUFFER_START_SOC, CHANNEL_GROUP_ID_GENERAL, CHANNEL_TYPE_UID_BUFFER_START_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_RESIDUAL_POWER, CHANNEL_GROUP_ID_GENERAL, CHANNEL_TYPE_UID_RESIDUAL_POWER,
"Number:Power");
}
if (gridConfigured) {
createChannel(CHANNEL_GRID_POWER, CHANNEL_GROUP_ID_GENERAL, CHANNEL_TYPE_UID_GRID_POWER, "Number:Power");
@ -289,10 +388,11 @@ public class EvccHandler extends BaseThingHandler {
if (pvConfigured) {
createChannel(CHANNEL_PV_POWER, CHANNEL_GROUP_ID_GENERAL, CHANNEL_TYPE_UID_PV_POWER, "Number:Power");
}
removeChannel("batteryPrioritySoC", CHANNEL_GROUP_ID_GENERAL);
}
private void createChannelsLoadpoint(int loadpointId) {
final String channelGroup = "loadpoint" + loadpointId;
final String channelGroup = CHANNEL_GROUP_ID_LOADPOINT + loadpointId;
createChannel(CHANNEL_LOADPOINT_ACTIVE_PHASES, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_ACTIVE_PHASES,
CoreItemFactory.NUMBER);
createChannel(CHANNEL_LOADPOINT_CHARGE_CURRENT, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_CHARGE_CURRENT,
@ -319,19 +419,13 @@ public class EvccHandler extends BaseThingHandler {
"Number:ElectricCurrent");
createChannel(CHANNEL_LOADPOINT_MIN_CURRENT, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_MIN_CURRENT,
"Number:ElectricCurrent");
createChannel(CHANNEL_LOADPOINT_MIN_SOC, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_MIN_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_LOADPOINT_MODE, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_MODE, CoreItemFactory.STRING);
createChannel(CHANNEL_LOADPOINT_PHASES, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_PHASES,
CoreItemFactory.NUMBER);
createChannel(CHANNEL_LOADPOINT_TARGET_ENERGY, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_TARGET_ENERGY,
createChannel(CHANNEL_LOADPOINT_LIMIT_ENERGY, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_LIMIT_ENERGY,
"Number:Energy");
createChannel(CHANNEL_LOADPOINT_TARGET_SOC, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_TARGET_SOC,
createChannel(CHANNEL_LOADPOINT_LIMIT_SOC, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_LIMIT_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_LOADPOINT_TARGET_TIME, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_TARGET_TIME,
CoreItemFactory.DATETIME);
createChannel(CHANNEL_LOADPOINT_TARGET_TIME_ENABLED, channelGroup,
CHANNEL_TYPE_UID_LOADPOINT_TARGET_TIME_ENABLED, CoreItemFactory.SWITCH);
createChannel(CHANNEL_LOADPOINT_TITLE, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_TITLE, CoreItemFactory.STRING);
createChannel(CHANNEL_LOADPOINT_VEHICLE_CAPACITY, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_CAPACITY,
"Number:Energy");
@ -343,10 +437,33 @@ public class EvccHandler extends BaseThingHandler {
"Number:Length");
createChannel(CHANNEL_LOADPOINT_VEHICLE_SOC, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_LOADPOINT_VEHICLE_TITLE, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_TITLE,
createChannel(CHANNEL_LOADPOINT_VEHICLE_NAME, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_NAME,
CoreItemFactory.STRING);
removeChannel(CHANNEL_LOADPOINT_HAS_VEHICLE, channelGroup);
removeChannel("hasVehicle", channelGroup);
removeChannel("minSoC", channelGroup);
removeChannel("targetEnergy", channelGroup);
removeChannel("targetSoC", channelGroup);
removeChannel("targetTime", channelGroup);
removeChannel("targetTimeEnabled", channelGroup);
}
private void createChannelsVehicle(String vehicleName) {
final String channelGroup = CHANNEL_GROUP_ID_VEHICLE + vehicleName;
createChannel(CHANNEL_VEHICLE_TITLE, channelGroup, CHANNEL_TYPE_UID_VEHICLE_TITLE, CoreItemFactory.STRING);
createChannel(CHANNEL_VEHICLE_MIN_SOC, channelGroup, CHANNEL_TYPE_UID_VEHICLE_MIN_SOC, "Number:Dimensionless");
createChannel(CHANNEL_VEHICLE_LIMIT_SOC, channelGroup, CHANNEL_TYPE_UID_VEHICLE_LIMIT_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_VEHICLE_PLAN_SOC, channelGroup, CHANNEL_TYPE_UID_VEHICLE_PLAN_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_VEHICLE_PLAN_TIME, channelGroup, CHANNEL_TYPE_UID_VEHICLE_PLAN_TIME,
CoreItemFactory.DATETIME);
createChannel(CHANNEL_VEHICLE_PLAN_ENABLED, channelGroup, CHANNEL_TYPE_UID_VEHICLE_PLAN_ENABLED,
CoreItemFactory.SWITCH);
createChannel(CHANNEL_VEHICLE_PLAN_SOC, channelGroup, CHANNEL_TYPE_UID_VEHICLE_PLAN_SOC,
"Number:Dimensionless");
createChannel(CHANNEL_VEHICLE_PLAN_TIME, channelGroup, CHANNEL_TYPE_UID_VEHICLE_PLAN_TIME,
CoreItemFactory.DATETIME);
}
// Units and description for vars: https://docs.evcc.io/docs/reference/configuration/messaging/#msg
@ -371,9 +488,29 @@ public class EvccHandler extends BaseThingHandler {
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_GENERAL, CHANNEL_BATTERY_SOC);
updateState(channel, new QuantityType<>(batterySoC, Units.PERCENT));
float batteryPrioritySoC = result.getBatteryPrioritySoC();
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_GENERAL, CHANNEL_BATTERY_PRIORITY_SOC);
updateState(channel, new QuantityType<>(batteryPrioritySoC, Units.PERCENT));
boolean batteryDischargeControl = result.getBatteryDischargeControl();
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_GENERAL, CHANNEL_BATTERY_DISCHARGE_CONTROL);
updateState(channel, OnOffType.from(batteryDischargeControl));
String batteryMode = result.getBatteryMode();
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_GENERAL, CHANNEL_BATTERY_MODE);
updateState(channel, new StringType(batteryMode));
float prioritySoC = result.getPrioritySoC();
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_GENERAL, CHANNEL_PRIORITY_SOC);
updateState(channel, new QuantityType<>(prioritySoC, Units.PERCENT));
float bufferSoC = result.getBufferSoC();
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_GENERAL, CHANNEL_BUFFER_SOC);
updateState(channel, new QuantityType<>(bufferSoC, Units.PERCENT));
float bufferStartSoC = result.getBufferStartSoC();
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_GENERAL, CHANNEL_BUFFER_START_SOC);
updateState(channel, new QuantityType<>(bufferStartSoC, Units.PERCENT));
float residualPower = result.getResidualPower();
channel = new ChannelUID(uid, CHANNEL_GROUP_ID_GENERAL, CHANNEL_RESIDUAL_POWER);
updateState(channel, new QuantityType<>(residualPower, Units.WATT));
}
boolean gridConfigured = this.gridConfigured;
if (gridConfigured) {
@ -398,123 +535,147 @@ public class EvccHandler extends BaseThingHandler {
return;
}
final ThingUID uid = getThing().getUID();
final String loadpointName = "loadpoint" + loadpointId;
final String channelGroup = CHANNEL_GROUP_ID_LOADPOINT + loadpointId;
ChannelUID channel;
Loadpoint loadpoint = result.getLoadpoints()[loadpointId];
int activePhases = loadpoint.getActivePhases();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_ACTIVE_PHASES);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_ACTIVE_PHASES);
updateState(channel, new DecimalType(activePhases));
float chargeCurrent = loadpoint.getChargeCurrent();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_CHARGE_CURRENT);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_CHARGE_CURRENT);
updateState(channel, new QuantityType<>(chargeCurrent, Units.AMPERE));
long chargeDuration = loadpoint.getChargeDuration();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_CHARGE_DURATION);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_CHARGE_DURATION);
updateState(channel, new QuantityType<>(chargeDuration, MetricPrefix.NANO(Units.SECOND)));
float chargePower = loadpoint.getChargePower();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_CHARGE_POWER);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_CHARGE_POWER);
updateState(channel, new QuantityType<>(chargePower, Units.WATT));
long chargeRemainingDuration = loadpoint.getChargeRemainingDuration();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_CHARGE_REMAINING_DURATION);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_CHARGE_REMAINING_DURATION);
updateState(channel, new QuantityType<>(chargeRemainingDuration, MetricPrefix.NANO(Units.SECOND)));
float chargeRemainingEnergy = loadpoint.getChargeRemainingEnergy();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_CHARGE_REMAINING_ENERGY);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_CHARGE_REMAINING_ENERGY);
updateState(channel, new QuantityType<>(chargeRemainingEnergy, Units.WATT_HOUR));
float chargedEnergy = loadpoint.getChargedEnergy();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_CHARGED_ENERGY);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_CHARGED_ENERGY);
updateState(channel, new QuantityType<>(chargedEnergy, Units.WATT_HOUR));
boolean charging = loadpoint.getCharging();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_CHARGING);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_CHARGING);
updateState(channel, OnOffType.from(charging));
boolean connected = loadpoint.getConnected();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_CONNECTED);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_CONNECTED);
updateState(channel, OnOffType.from(connected));
long connectedDuration = loadpoint.getConnectedDuration();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_CONNECTED_DURATION);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_CONNECTED_DURATION);
updateState(channel, new QuantityType<>(connectedDuration, MetricPrefix.NANO(Units.SECOND)));
boolean enabled = loadpoint.getEnabled();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_ENABLED);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_ENABLED);
updateState(channel, OnOffType.from(enabled));
float maxCurrent = loadpoint.getMaxCurrent();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_MAX_CURRENT);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_MAX_CURRENT);
updateState(channel, new QuantityType<>(maxCurrent, Units.AMPERE));
float minCurrent = loadpoint.getMinCurrent();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_MIN_CURRENT);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_MIN_CURRENT);
updateState(channel, new QuantityType<>(minCurrent, Units.AMPERE));
float minSoC = loadpoint.getMinSoC();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_MIN_SOC);
updateState(channel, new QuantityType<>(minSoC, Units.PERCENT));
String mode = loadpoint.getMode();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_MODE);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_MODE);
updateState(channel, new StringType(mode));
int phases = loadpoint.getPhases();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_PHASES);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_PHASES);
updateState(channel, new DecimalType(phases));
float targetEnergy = loadpoint.getTargetEnergy();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_TARGET_ENERGY);
updateState(channel, new QuantityType<>(targetEnergy, Units.WATT_HOUR));
float limitEnergy = loadpoint.getLimitEnergy();
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_LIMIT_ENERGY);
updateState(channel, new QuantityType<>(limitEnergy, Units.WATT_HOUR));
float targetSoC = loadpoint.getTargetSoC();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_TARGET_SOC);
updateState(channel, new QuantityType<>(targetSoC, Units.PERCENT));
String targetTime = loadpoint.getTargetTime();
if (targetTime == null || "0001-01-01T00:00:00Z".equals(targetTime)) {
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_TARGET_TIME_ENABLED);
updateState(channel, OnOffType.OFF);
targetTimeEnabled = false;
} else {
this.targetTimeZDT = ZonedDateTime.parse(targetTime);
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_TARGET_TIME);
updateState(channel, new DateTimeType(targetTimeZDT));
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_TARGET_TIME_ENABLED);
updateState(channel, OnOffType.ON);
targetTimeEnabled = true;
}
float limitSoC = loadpoint.getLimitSoC();
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_LIMIT_SOC);
updateState(channel, new QuantityType<>(limitSoC, Units.PERCENT));
String title = loadpoint.getTitle();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_TITLE);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_TITLE);
updateState(channel, new StringType(title));
float vehicleCapacity = loadpoint.getVehicleCapacity();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_VEHICLE_CAPACITY);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_VEHICLE_CAPACITY);
updateState(channel, new QuantityType<>(vehicleCapacity, Units.KILOWATT_HOUR));
float vehicleOdometer = loadpoint.getVehicleOdometer();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_VEHICLE_ODOMETER);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_VEHICLE_ODOMETER);
updateState(channel, new QuantityType<>(vehicleOdometer, MetricPrefix.KILO(SIUnits.METRE)));
boolean vehiclePresent = loadpoint.getVehiclePresent();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_VEHICLE_PRESENT);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_VEHICLE_PRESENT);
updateState(channel, OnOffType.from(vehiclePresent));
float vehicleRange = loadpoint.getVehicleRange();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_VEHICLE_RANGE);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_VEHICLE_RANGE);
updateState(channel, new QuantityType<>(vehicleRange, MetricPrefix.KILO(SIUnits.METRE)));
float vehicleSoC = loadpoint.getVehicleSoC();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_VEHICLE_SOC);
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_VEHICLE_SOC);
updateState(channel, new QuantityType<>(vehicleSoC, Units.PERCENT));
String vehicleTitle = loadpoint.getVehicleTitle();
channel = new ChannelUID(uid, loadpointName, CHANNEL_LOADPOINT_VEHICLE_TITLE);
updateState(channel, new StringType(vehicleTitle));
String vehicleName = loadpoint.getVehicleName();
channel = new ChannelUID(uid, channelGroup, CHANNEL_LOADPOINT_VEHICLE_NAME);
updateState(channel, new StringType(vehicleName));
}
private void updateChannelsVehicle(String vehicleName) {
final Result result = this.result;
if (result == null) {
return;
}
final ThingUID uid = getThing().getUID();
final String channelGroup = CHANNEL_GROUP_ID_VEHICLE + vehicleName;
ChannelUID channel;
Vehicle vehicle = result.getVehicles().get(vehicleName);
String title = vehicle.getTitle();
channel = new ChannelUID(uid, channelGroup, CHANNEL_VEHICLE_TITLE);
updateState(channel, new StringType(title));
float minSoC = vehicle.getMinSoC();
channel = new ChannelUID(uid, channelGroup, CHANNEL_VEHICLE_MIN_SOC);
updateState(channel, new QuantityType<>(minSoC, Units.PERCENT));
float limitSoC = vehicle.getLimitSoC();
channel = new ChannelUID(uid, channelGroup, CHANNEL_VEHICLE_LIMIT_SOC);
updateState(channel, new QuantityType<>(limitSoC, Units.PERCENT));
Plan plan = vehicle.getPlan();
if (plan == null && vehiclePlans.get(vehicleName) == null) {
vehiclePlans.put(vehicleName, new Triple<>(false, 100f, ZonedDateTime.now().plusHours(12)));
} else if (plan != null) {
vehiclePlans.put(vehicleName, new Triple<>(true, plan.getSoC(), ZonedDateTime.parse(plan.getTime())));
}
updateVehiclePlanChannel(vehicleName, uid, channelGroup);
}
private void updateVehiclePlanChannel(String vehicleName, ThingUID uid, String channelGroup) {
Triple<Boolean, Float, ZonedDateTime> planValues = vehiclePlans.get(vehicleName);
ChannelUID channel = new ChannelUID(uid, channelGroup, CHANNEL_VEHICLE_PLAN_ENABLED);
updateState(channel, planValues.getLeft() ? OnOffType.ON : OnOffType.OFF);
channel = new ChannelUID(uid, channelGroup, CHANNEL_VEHICLE_PLAN_SOC);
updateState(channel, new QuantityType<>(planValues.getMiddle(), Units.PERCENT));
channel = new ChannelUID(uid, channelGroup, CHANNEL_VEHICLE_PLAN_TIME);
updateState(channel, new DateTimeType(planValues.getRight()));
}
private void createChannel(String channel, String channelGroupId, ChannelTypeUID channelTypeUID, String itemType) {
@ -531,4 +692,28 @@ public class EvccHandler extends BaseThingHandler {
updateThing(editThing().withoutChannel(channelUid).build());
}
}
private class Triple<L, M, R> {
private final L left;
private final M middle;
private final R right;
private Triple(L left, M middle, R right) {
this.left = left;
this.middle = middle;
this.right = right;
}
private L getLeft() {
return left;
}
private M getMiddle() {
return middle;
}
private R getRight() {
return right;
}
}
}

View File

@ -31,9 +31,10 @@ import com.google.gson.JsonSyntaxException;
/**
* The {@link EvccAPI} is responsible for API calls to evcc.
* API requests were written for evcc version 0.117.0
* API requests were written for evcc version 0.123.1
*
* @author Florian Hotze - Initial contribution
* @author Luca Arnecke - Update to evcc version 0.123.1
*/
@NonNullByDefault
public class EvccAPI {
@ -86,26 +87,38 @@ public class EvccAPI {
}
// Site API calls.
public String setBatteryPrioritySoC(int prioritySoc) throws EvccApiException {
public String setPrioritySoC(int prioritySoc) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "prioritysoc/" + prioritySoc, "POST");
}
public String setBufferSoC(int bufferSoC) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "buffersoc/" + bufferSoC, "POST");
}
public String setBufferStartSoC(int bufferStartSoC) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "bufferstartsoc/" + bufferStartSoC, "POST");
}
public String setResidualPower(int residualPower) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "residualpower/" + residualPower, "POST");
}
public String setBatteryDischargeControl(boolean batteryDischargeControl) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "batterydischargecontrol/" + batteryDischargeControl, "POST");
}
// Loadpoint specific API calls.
public String setMode(int loadpoint, String mode) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/mode/" + mode, "POST");
}
public String setMinSoC(int loadpoint, int minSoC) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/minsoc/" + minSoC, "POST");
}
public String setTargetEnergy(int loadpoint, float targetEnergy) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/target/energy/" + targetEnergy,
public String setLimitEnergy(int loadpoint, float limitEnergy) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/limitenergy/" + limitEnergy,
"POST");
}
public String setTargetSoC(int loadpoint, int targetSoC) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/target/soc/" + targetSoC, "POST");
public String setLimitSoC(int loadpoint, int limitSoC) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/limitsoc/" + limitSoC, "POST");
}
public String setPhases(int loadpoint, int phases) throws EvccApiException {
@ -120,12 +133,21 @@ public class EvccAPI {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/maxcurrent/" + maxCurrent, "POST");
}
public String setTargetTime(int loadpoint, ZonedDateTime targetTime) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/target/time/"
+ targetTime.toLocalDateTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + "Z", "POST");
// Vehicle specific API calls.
public String setVehicleMinSoC(String vehicleName, int minSoC) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "vehicles/" + vehicleName + "/minsoc/" + minSoC, "POST");
}
public String removeTargetTime(int loadpoint) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/target/time", "DELETE");
public String setVehicleLimitSoC(String vehicleName, int limitSoC) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "vehicles/" + vehicleName + "/limitsoc/" + limitSoC, "POST");
}
public String setVehiclePlan(String vehicleName, int planSoC, ZonedDateTime planTime) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "vehicles/" + vehicleName + "/plan/soc/" + planSoC + "/"
+ planTime.toLocalDateTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + "Z", "POST");
}
public String removeVehiclePlan(String vehicleName) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "vehicles/" + vehicleName + "/plan/soc", "DELETE");
}
}

View File

@ -16,9 +16,10 @@ import com.google.gson.annotations.SerializedName;
/**
* This class represents a loadpoint object of the status response (/api/state).
* This DTO was written for evcc version 0.117.0
* This DTO was written for evcc version 0.123.1
*
* @author Florian Hotze - Initial contribution
* @author Luca Arnecke - Update to evcc version 0.123.1
*/
public class Loadpoint {
// Data types from https://github.com/evcc-io/evcc/blob/master/api/api.go
@ -63,23 +64,17 @@ public class Loadpoint {
@SerializedName("minCurrent")
private float minCurrent;
@SerializedName("minSoc")
private float minSoC;
@SerializedName("mode")
private String mode;
@SerializedName("phasesEnabled")
private int phases;
@SerializedName("planActive")
private boolean planActive;
@SerializedName("limitEnergy")
private float limitEnergy;
@SerializedName("targetEnergy")
private float targetEnergy;
@SerializedName("targetSoc")
private float targetSoC;
@SerializedName("limitSoc")
private float limitSoC;
@SerializedName("targetTime")
private String targetTime;
@ -102,8 +97,8 @@ public class Loadpoint {
@SerializedName("vehicleSoc")
private float vehicleSoC;
@SerializedName("vehicleTitle")
private String vehicleTitle;
@SerializedName("vehicleName")
private String vehicleName;
/**
* @return number of active phases
@ -196,13 +191,6 @@ public class Loadpoint {
return minCurrent;
}
/**
* @return minimum state of charge
*/
public float getMinSoC() {
return minSoC;
}
/**
* @return charging mode: off, now, minpv, pv
*/
@ -218,24 +206,17 @@ public class Loadpoint {
}
/**
* @return whether charging plan is active
* @return limit energy
*/
public boolean getPlanActive() {
return planActive;
public float getLimitEnergy() {
return limitEnergy;
}
/**
* @return target energy
* @return limit state of charge (SoC)
*/
public float getTargetEnergy() {
return targetEnergy;
}
/**
* @return target state of charge (SoC)
*/
public float getTargetSoC() {
return targetSoC;
public float getLimitSoC() {
return limitSoC;
}
/**
@ -290,7 +271,7 @@ public class Loadpoint {
/**
* @return vehicle's title/name
*/
public String getVehicleTitle() {
return vehicleTitle;
public String getVehicleName() {
return vehicleName;
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.evcc.internal.api.dto;
import com.google.gson.annotations.SerializedName;
/**
* This class represents a plan object of the status response (/api/state).
* This DTO was written for evcc version 0.123.1
*
* @author Luca Arnecke - Initial contribution
*/
public class Plan {
// Data types from https://github.com/evcc-io/evcc/blob/master/api/api.go
// and from https://docs.evcc.io/docs/reference/configuration/messaging/#msg
@SerializedName("soc")
private float soc;
@SerializedName("time")
private String time;
/**
* @return state of charge
*/
public float getSoC() {
return soc;
}
/**
* @return time
*/
public String getTime() {
return time;
}
}

View File

@ -12,13 +12,16 @@
*/
package org.openhab.binding.evcc.internal.api.dto;
import java.util.Map;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the result object of the status response (/api/state).
* This DTO was written for evcc version 0.117.0
* This DTO was written for evcc version 0.123.1
*
* @author Florian Hotze - Initial contribution
* @author Luca Arnecke - update to evcc version 0.123.1
*/
public class Result {
// Data types from https://github.com/evcc-io/evcc/blob/master/api/api.go
@ -38,6 +41,12 @@ public class Result {
@SerializedName("batterySoc")
private float batterySoC;
@SerializedName("batteryDischargeControl")
private boolean batteryDischargeControl;
@SerializedName("batteryMode")
private String batteryMode;
@SerializedName("gridConfigured")
private boolean gridConfigured;
@ -51,7 +60,16 @@ public class Result {
private Loadpoint[] loadpoints;
@SerializedName("prioritySoc")
private float batteryPrioritySoC;
private float prioritySoC;
@SerializedName("bufferSoc")
private float bufferSoC;
@SerializedName("bufferStartSoc")
private float bufferStartSoC;
@SerializedName("residualPower")
private float residualPower;
@SerializedName("pvConfigured")
private boolean pvConfigured;
@ -62,6 +80,9 @@ public class Result {
@SerializedName("siteTitle")
private String siteTitle;
@SerializedName("vehicles")
private Map<String, Vehicle> vehicles;
/**
* @return battery's capacity
*/
@ -86,8 +107,29 @@ public class Result {
/**
* @return battery's priority state of charge
*/
public float getBatteryPrioritySoC() {
return batteryPrioritySoC;
public float getPrioritySoC() {
return prioritySoC;
}
/**
* @return Battery Buffer SoC
*/
public float getBufferSoC() {
return bufferSoC;
}
/**
* @return Battery Buffer Start SoC
*/
public float getBufferStartSoC() {
return bufferStartSoC;
}
/**
* @return Grid Residual Power
*/
public float getResidualPower() {
return residualPower;
}
/**
@ -97,6 +139,20 @@ public class Result {
return batterySoC;
}
/**
* @return battery discharge control
*/
public boolean getBatteryDischargeControl() {
return batteryDischargeControl;
}
/**
* @return battery mode
*/
public String getBatteryMode() {
return batteryMode;
}
/**
* @return whether grid is configured
*/
@ -145,4 +201,8 @@ public class Result {
public String getSiteTitle() {
return siteTitle;
}
public Map<String, Vehicle> getVehicles() {
return vehicles;
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.evcc.internal.api.dto;
import com.google.gson.annotations.SerializedName;
/**
* This class represents a vehicle object of the status response (/api/state).
* This DTO was written for evcc version 0.123.1
*
* @author Luca Arnecke - Initial contribution
*/
public class Vehicle {
// Data types from https://github.com/evcc-io/evcc/blob/master/api/api.go
// and from https://docs.evcc.io/docs/reference/configuration/messaging/#msg
@SerializedName("title")
private String title;
@SerializedName("minSoc")
private float minSoC;
@SerializedName("limitSoc")
private float limitSoC;
@SerializedName("plans")
private Plan[] plans;
/**
* @return vehicle name
*/
public String getTitle() {
return title;
}
/**
* @return minimum state of charge
*/
public float getMinSoC() {
return minSoC;
}
/**
* @return limit state of charge
*/
public float getLimitSoC() {
return limitSoC;
}
/**
* @return current plan for vehicle
*/
public Plan getPlan() {
if (plans != null) {
return plans[0];
} else {
return null;
}
}
}

View File

@ -26,12 +26,20 @@ channel-type.evcc.activePhases.label = Charging Active Phases
channel-type.evcc.activePhases.description = Current number of active phases while charging
channel-type.evcc.batteryCapacity.label = Battery Capacity
channel-type.evcc.batteryCapacity.description = Capacity of (home) battery
channel-type.evcc.batteryDischargeControl.label = Battery Discharge Control
channel-type.evcc.batteryDischargeControl.description = Enable or disable battery discharge control
channel-type.evcc.batteryDischargeControl.state.option.ON = Enabled
channel-type.evcc.batteryDischargeControl.state.option.OFF = Disabled
channel-type.evcc.batteryMode.label = Battery Mode
channel-type.evcc.batteryMode.description = Current Battery Mode
channel-type.evcc.batteryPower.label = Battery Power
channel-type.evcc.batteryPower.description = Current power from battery
channel-type.evcc.batteryPrioritySoC.label = Battery Priority SoC
channel-type.evcc.batteryPrioritySoC.description = State of Charge for which the battery has priority over charging the ev when charging mode is "pv".
channel-type.evcc.batterySoC.label = Battery SoC
channel-type.evcc.batterySoC.description = Current State of Charge of battery
channel-type.evcc.bufferSoC.label = Battery Buffer SoC
channel-type.evcc.bufferSoC.description = Until this State of Charge the discharging of a house battery is allowed in "pv" mode, when there is insufficient solar surplus (below the minimum charging power)
channel-type.evcc.bufferStartSoC.label = Battery Buffer Start SoC
channel-type.evcc.bufferStartSoC.description = State of Charge for which a charging session in "pv" mode is started, even if there is insufficient solar surplus
channel-type.evcc.chargeCurrent.label = Charging Current
channel-type.evcc.chargeCurrent.description = Current amperage per connected phase while charging
channel-type.evcc.chargeDuration.label = Charging Duration
@ -39,9 +47,9 @@ channel-type.evcc.chargeDuration.description = Charging duration
channel-type.evcc.chargePower.label = Charging Power
channel-type.evcc.chargePower.description = Current power of charging
channel-type.evcc.chargeRemainingDuration.label = Charging Remaining Duration
channel-type.evcc.chargeRemainingDuration.description = Remaining duration until target SoC is reached
channel-type.evcc.chargeRemainingDuration.description = Remaining duration until limit SoC is reached
channel-type.evcc.chargeRemainingEnergy.label = Charging Remaining Energy
channel-type.evcc.chargeRemainingEnergy.description = Remaining energy until target SoC is reached
channel-type.evcc.chargeRemainingEnergy.description = Remaining energy until limit SoC is reached
channel-type.evcc.chargedEnergy.label = Charged Energy
channel-type.evcc.chargedEnergy.description = Energy charged since plugged-in
channel-type.evcc.charging.label = Charging State
@ -56,12 +64,14 @@ channel-type.evcc.gridPower.label = Grid Power
channel-type.evcc.gridPower.description = Current power from grid (negative means feed-in)
channel-type.evcc.homePower.label = Home Power
channel-type.evcc.homePower.description = Current power taken by home
channel-type.evcc.limitEnergy.label = Charging Limit Energy
channel-type.evcc.limitEnergy.description = Amount of energy to charge the vehicle with
channel-type.evcc.limitSoC.label = Charging Limit SoC
channel-type.evcc.limitSoC.description = Until which state of charge (SoC) should the vehicle be charged
channel-type.evcc.maxCurrent.label = Charging max Current
channel-type.evcc.maxCurrent.description = Maximum amperage per connected phase with which the car should be charged
channel-type.evcc.minCurrent.label = Charging min Current
channel-type.evcc.minCurrent.description = Minimum amperage per connected phase with which the car should be charged
channel-type.evcc.minSoC.label = Charging min SoC
channel-type.evcc.minSoC.description = Charge immediately with maximum power up to the defined SoC, if the charge mode is not set to "off"
channel-type.evcc.mode.label = Charging Mode
channel-type.evcc.mode.description = Charging mode: "off", "now", "minpv", "pv"
channel-type.evcc.mode.state.option.off = Off
@ -70,18 +80,12 @@ channel-type.evcc.mode.state.option.minpv = Min + PV
channel-type.evcc.mode.state.option.pv = Only PV
channel-type.evcc.phases.label = Charging Enabled Phases
channel-type.evcc.phases.description = The maximum number of phases which can be used
channel-type.evcc.prioritySoC.label = Battery Priority SoC
channel-type.evcc.prioritySoC.description = State of Charge for which the battery has priority over charging the EV when charging mode is "pv"
channel-type.evcc.pvPower.label = PV Power
channel-type.evcc.pvPower.description = Current power from photovoltaik
channel-type.evcc.targetEnergy.label = Charging Target Energy
channel-type.evcc.targetEnergy.description = Amount of energy to charge the vehicle with
channel-type.evcc.targetSoC.label = Charging Target SoC
channel-type.evcc.targetSoC.description = Until which state of charge (SoC) should the vehicle be charged
channel-type.evcc.targetTime.label = Charging Target Time
channel-type.evcc.targetTime.description = When the target SoC should be reached
channel-type.evcc.targetTimeEnabled.label = Charging Target Time Enabled
channel-type.evcc.targetTimeEnabled.description = Target time for charging enabled
channel-type.evcc.targetTimeEnabled.state.option.ON = Enabled
channel-type.evcc.targetTimeEnabled.state.option.OFF = Disabled
channel-type.evcc.residualPower.label = Grid Residual Power
channel-type.evcc.residualPower.description = Target operating point of the surplus regulation at the grid connection (grid meter)
channel-type.evcc.title.label = Loadpoint Title
channel-type.evcc.title.description = Title of loadpoint
channel-type.evcc.vehicleCapacity.label = Vehicle Capacity
@ -92,8 +96,22 @@ channel-type.evcc.vehicleConnected.state.option.ON = Connected
channel-type.evcc.vehicleConnected.state.option.OFF = Not connected
channel-type.evcc.vehicleConnectedDuration.label = Vehicle Connected Duration
channel-type.evcc.vehicleConnectedDuration.description = Duration the vehicle is connected to loadpoint
channel-type.evcc.vehicleLimitSoC.label = Vehicle Charging Limit SoC
channel-type.evcc.vehicleLimitSoC.description = Until which state of charge (SoC) should the specific vehicle be charged
channel-type.evcc.vehicleMinSoC.label = Vehicle Min SoC
channel-type.evcc.vehicleMinSoC.description = Minimum state of charge (SoC) a vehicle should have
channel-type.evcc.vehicleName.label = Vehicle Name
channel-type.evcc.vehicleName.description = The unique identifier of the EV used in the evcc configuration (containing no whitespaces nor special characters)
channel-type.evcc.vehicleOdometer.label = Vehicle Odometer
channel-type.evcc.vehicleOdometer.description = Total distance travelled by EV
channel-type.evcc.vehiclePlanEnabled.label = Vehicle Plan Enabled
channel-type.evcc.vehiclePlanEnabled.description = Plan for charging enabled
channel-type.evcc.vehiclePlanEnabled.state.option.ON = Enabled
channel-type.evcc.vehiclePlanEnabled.state.option.OFF = Disabled
channel-type.evcc.vehiclePlanSoC.label = Vehicle Plan SoC
channel-type.evcc.vehiclePlanSoC.description = Until which state of charge (SoC) should vehicle be charged in plan
channel-type.evcc.vehiclePlanTime.label = Vehicle Plan Time
channel-type.evcc.vehiclePlanTime.description = When the plan SoC should be reached
channel-type.evcc.vehiclePresent.label = Vehicle Data Access
channel-type.evcc.vehiclePresent.description = Whether evcc is able to get data from vehicle
channel-type.evcc.vehiclePresent.state.option.ON = Data access
@ -103,7 +121,12 @@ channel-type.evcc.vehicleRange.description = Battery range for EV
channel-type.evcc.vehicleSoC.label = Vehicle SoC
channel-type.evcc.vehicleSoC.description = Current State of Charge of EV
channel-type.evcc.vehicleTitle.label = Vehicle Title
channel-type.evcc.vehicleTitle.description = Name of EV
channel-type.evcc.vehicleTitle.description = Title of vehicle
# channel types
channel-type.evcc.batteryPrioritySoC.label = Battery Priority SoC
channel-type.evcc.batteryPrioritySoC.description = State of Charge for which the battery has priority over charging the ev when charging mode is "pv".
# thing status description

View File

@ -67,13 +67,59 @@
<category>BatteryLevel</category>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="batteryPrioritySoC">
<channel-type id="batteryDischargeControl">
<item-type>Switch</item-type>
<label>Battery Discharge Control</label>
<description>Enable or disable battery discharge control</description>
<category>Switch</category>
<state readOnly="false">
<options>
<option value="ON">Enabled</option>
<option value="OFF">Disabled</option>
</options>
</state>
</channel-type>
<channel-type id="batteryMode">
<item-type>String</item-type>
<label>Battery Mode</label>
<description>Current Battery Mode</description>
<category>Battery</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="prioritySoC">
<item-type>Number:Dimensionless</item-type>
<label>Battery Priority SoC</label>
<description>State of Charge for which the battery has priority over charging the ev when charging mode is "pv".</description>
<description>State of Charge for which the battery has priority over charging the EV when charging mode is "pv"
</description>
<category>BatteryLevel</category>
<state min="0" step="0.1" max="100" pattern="%.0f %unit%" readOnly="false"/>
</channel-type>
<channel-type id="bufferSoC">
<item-type>Number:Dimensionless</item-type>
<label>Battery Buffer SoC</label>
<description>Until this State of Charge the discharging of a house battery is allowed in "pv" mode, when there is
insufficient solar surplus (below the minimum charging power)
</description>
<category>BatteryLevel</category>
<state min="0" step="0.1" max="100" pattern="%.0f %unit%" readOnly="false"/>
</channel-type>
<channel-type id="bufferStartSoC">
<item-type>Number:Dimensionless</item-type>
<label>Battery Buffer Start SoC</label>
<description>State of Charge for which a charging session in "pv" mode is started, even if there is insufficient solar
surplus
</description>
<category>BatteryLevel</category>
<state min="0" step="0.1" max="100" pattern="%.0f %unit%" readOnly="false"/>
</channel-type>
<channel-type id="residualPower">
<item-type>Number:Power</item-type>
<label>Grid Residual Power</label>
<description>Target operating point of the surplus regulation at the grid connection (grid meter)
</description>
<category>Energy</category>
<state min="0" step="1" pattern="%.0f %unit%" readOnly="false"/>
</channel-type>
<channel-type id="gridPower">
<item-type>Number:Power</item-type>
<label>Grid Power</label>
@ -96,7 +142,7 @@
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<!-- Channels for loadpoints -->
<!-- Channel Types for loadpoints -->
<channel-type id="activePhases">
<item-type>Number</item-type>
<label>Charging Active Phases</label>
@ -128,14 +174,14 @@
<channel-type id="chargeRemainingDuration">
<item-type>Number:Time</item-type>
<label>Charging Remaining Duration</label>
<description>Remaining duration until target SoC is reached</description>
<description>Remaining duration until limit SoC is reached</description>
<category>Time</category>
<state pattern="%.1f min" readOnly="true"/>
</channel-type>
<channel-type id="chargeRemainingEnergy">
<item-type>Number:Energy</item-type>
<label>Charging Remaining Energy</label>
<description>Remaining energy until target SoC is reached</description>
<description>Remaining energy until limit SoC is reached</description>
<category>Energy</category>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
@ -186,19 +232,11 @@
<state min="0" step="1" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="minSoC">
<item-type>Number:Dimensionless</item-type>
<label>Charging min SoC</label>
<description>Charge immediately with maximum power up to the defined SoC, if the charge mode is not set to "off"</description>
<category>BatteryLevel</category>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="mode">
<item-type>String</item-type>
<label>Charging Mode</label>
<description>Charging mode: "off", "now", "minpv", "pv"</description>
<category>String</category>
<category>Heating</category>
<state readOnly="false">
<options>
<option value="off">Off</option>
@ -217,48 +255,27 @@
<state min="0" step="1" max="3" pattern="%d" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="targetEnergy">
<channel-type id="limitEnergy">
<item-type>Number:Energy</item-type>
<label>Charging Target Energy</label>
<label>Charging Limit Energy</label>
<description>Amount of energy to charge the vehicle with</description>
<category>BatteryLevel</category>
<state min="0" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="targetSoC">
<channel-type id="limitSoC">
<item-type>Number:Dimensionless</item-type>
<label>Charging Target SoC</label>
<label>Charging Limit SoC</label>
<description>Until which state of charge (SoC) should the vehicle be charged</description>
<category>BatteryLevel</category>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="targetTime">
<item-type>DateTime</item-type>
<label>Charging Target Time</label>
<description>When the target SoC should be reached</description>
<category>Time</category>
<state readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="targetTimeEnabled">
<item-type>Switch</item-type>
<label>Charging Target Time Enabled</label>
<description>Target time for charging enabled</description>
<category>Switch</category>
<state readOnly="false">
<options>
<option value="ON">Enabled</option>
<option value="OFF">Disabled</option>
</options>
</state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="title">
<item-type>String</item-type>
<label>Loadpoint Title</label>
<description>Title of loadpoint</description>
<category>Text</category>
<category>PowerOutlet</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="vehicleConnected">
@ -291,7 +308,7 @@
<item-type>Number:Length</item-type>
<label>Vehicle Odometer</label>
<description>Total distance travelled by EV</description>
<category></category>
<category>MoveControl</category>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="vehiclePresent">
@ -320,11 +337,66 @@
<category>BatteryLevel</category>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="vehicleName">
<item-type>String</item-type>
<label>Vehicle Name</label>
<description>The unique identifier of the EV used in the evcc configuration (containing no whitespaces nor special
characters)</description>
<category>Settings</category>
<state readOnly="true"/>
</channel-type>
<!-- Channel Types for vehicles -->
<channel-type id="vehicleTitle">
<item-type>String</item-type>
<label>Vehicle Title</label>
<description>Name of EV</description>
<category>Text</category>
<description>Title of vehicle</description>
<category>GarageDoor</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="vehicleMinSoC">
<item-type>Number:Dimensionless</item-type>
<label>Vehicle Min SoC</label>
<description>Minimum state of charge (SoC) a vehicle should have</description>
<category>BatteryLevel</category>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicleLimitSoC">
<item-type>Number:Dimensionless</item-type>
<label>Vehicle Charging Limit SoC</label>
<description>Until which state of charge (SoC) should the specific vehicle be charged</description>
<category>BatteryLevel</category>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehiclePlanEnabled">
<item-type>Switch</item-type>
<label>Vehicle Plan Enabled</label>
<description>Plan for charging enabled</description>
<category>Switch</category>
<state readOnly="false">
<options>
<option value="ON">Enabled</option>
<option value="OFF">Disabled</option>
</options>
</state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehiclePlanSoC">
<item-type>Number:Dimensionless</item-type>
<label>Vehicle Plan SoC</label>
<description>Until which state of charge (SoC) should vehicle be charged in plan</description>
<category>BatteryLevel</category>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehiclePlanTime">
<item-type>DateTime</item-type>
<label>Vehicle Plan Time</label>
<description>When the plan SoC should be reached</description>
<category>Time</category>
<state readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
</thing:thing-descriptions>