[evcc] Rework the binding, add missing datapoints and make it future ready (#18946)

* First commit of reworked binding

Signed-off-by: Marcel Goerentz <m.goerentz@t-online.de>
This commit is contained in:
Marcel Goerentz
2025-08-13 20:15:32 +02:00
committed by GitHub
parent 0d28575470
commit 24747129e6
46 changed files with 3984 additions and 3480 deletions
+1 -1
View File
@@ -119,7 +119,7 @@
/bundles/org.openhab.binding.ephemeris/ @clinique
/bundles/org.openhab.binding.epsonprojector/ @mlobstein
/bundles/org.openhab.binding.etherrain/ @dfad1469
/bundles/org.openhab.binding.evcc/ @florian-h05
/bundles/org.openhab.binding.evcc/ @florian-h05 @marcelGoerentz
/bundles/org.openhab.binding.evohome/ @Nebula83
/bundles/org.openhab.binding.exec/ @kgoderis
/bundles/org.openhab.binding.feed/ @openhab/add-ons-maintainers
+54 -227
View File
@@ -1,257 +1,84 @@
# evcc Binding
This binding integrates [evcc](https://evcc.io), an extensible **E**lectric **V**ehicle **C**harge **C**ontroller and home energy management system.
The binding is compatible to evcc [version 0.123.1](https://github.com/evcc-io/evcc/releases/tag/0.123.1) or newer and was tested with [version 0.125.0](https://github.com/evcc-io/evcc/releases/tag/0.125.0).
The binding is compatible to evcc [version 0.123.1](https://github.com/evcc-io/evcc/releases/tag/0.123.1) or newer and was tested with [version 0.205.0](https://github.com/evcc-io/evcc/releases/tag/0.205.0).
You can easily install and upgrade evcc on openHABian using `sudo openhabian-config`.
evcc controls your wallbox(es) with multiple charging modes and allows you to charge your ev with your photovoltaik's excess current.
evcc controls your wallbox(es) with multiple charging modes and allows you to charge your ev with your photovoltaic's excess current.
To provide an intelligent charging control, evcc supports over 30 wallboxes and over 20 energy meters/home energy management systems from many manufacturers as well as electric vehicles from over 20 car manufacturers.
Furthermore, evcc calculates your money savings.
This binding enables openHAB to retrieve status data from your evcc installation and to control the charging process.
This binding enables openHAB to retrieve status data from your evcc instance and to control the charging process.
For more advanced features like calculated savings, you have to visit the web UI of evcc.
This binding will create a file in your userdata folder, when it detects new datapoints in the response from evcc.
This will help to add them to the binding and make them available with the next version of the binding.
## Supported Things
- `device`: A running evcc installation.
- `server`: A running evcc instance. It will be used as the bridge for the other things
- `battery`: A battery configured in your evcc instance.
- `heating`: A heating loadpoint configured in your evcc instance.
- `loadpoint`: A loadpoint configured in your evcc instance.
- `pv`: A photovoltaic system configured in your evcc instance.
- `site`: The relevant site data from your evcc instance.
- `vehicle`: A vehicle configured in your evcc instance
## Discovery
No auto discovery supported.
The bridge will discover the things automatically in the background.
## Thing Configuration
## `server` Bridge Configuration
### `device` Thing Configuration
| Parameter | Type | Description | Advanced | Required |
|-----------------|---------|----------------------------------------------------------|----------|----------|
| schema | String | Schema to connect to your instance (http or https) | No | Yes |
| host | String | IP or hostname running your evcc instance | No | Yes |
| port | Integer | Port of your evcc instance | Yes | Yes |
| refreshInterval | Number | Interval the status is polled in seconds (minimum is 15) | Yes | Yes |
| Parameter | Type | Description | Advanced | Required |
|-----------------|--------|----------------------------------------------------------|----------|----------|
| url | String | URL of evcc web UI, e.g. `https://demo.evcc.io` | No | Yes |
| refreshInterval | Number | Interval the status is polled in seconds (minimum is 15) | Yes | No |
Default value for _refreshInterval_ is 30 seconds.
Default value for _refreshInterval_ is 60 seconds.
## Thing(s) Configuration
The Things don't have a configuration. They will be set up automatically when a bridge has been configured.
You can add them via file configuration or in the openHAB UI.
## Channels
### General Channels
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#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 |
| general#version | String | R | Current evcc version |
| general#availableVersion | String | R | Available evcc update version |
### Loadpoint Channels
Those channels exist per configured loadpoint.
Please note that you have to replace _\<N\>_ with your loadpoint id/number.
| Channel | Type | Read/Write | Description |
|-----------------------------------------------|------------------------|------------|-------------------------------------------------------------------------------------------------------------------|
| loadpoint\<N\>#activePhases | Number | R | Current number of active phases while charging |
| loadpoint\<N\>#chargeCurrent | Number:ElectricCurrent | R | Current amperage per connected phase while charging |
| loadpoint\<N\>#chargeDuration | Number:Time | R | Charging duration |
| loadpoint\<N\>#chargeRemainingDuration | Number:Time | R | Remaining duration until limit SoC is reached |
| loadpoint\<N\>#chargeRemainingEnergy | Number:Energy | R | Remaining energy until limit SoC is reached |
| loadpoint\<N\>#chargePower | Number:Power | R | Current power of charging |
| loadpoint\<N\>#chargedEnergy | Number:Energy | R | Energy charged since plugged-in |
| loadpoint\<N\>#charging | Switch | R | Loadpoint is currently charging |
| loadpoint\<N\>#enabled | Switch | R | Charging enabled (mode is not "off") |
| loadpoint\<N\>#maxCurrent | Number:ElectricCurrent | RW | Maximum amperage per connected phase with which the car should be charged |
| loadpoint\<N\>#minCurrent | Number:ElectricCurrent | RW | Minimum amperage per connected phase with which the car should be charged |
| loadpoint\<N\>#mode | String | RW | Charging mode: "off", "now", "minpv", "pv" |
| loadpoint\<N\>#phases | Number | RW | The maximum number of phases which can be used |
| loadpoint\<N\>#limitEnergy | Number:Energy | RW | Amount of energy to charge the vehicle with |
| loadpoint\<N\>#title | String | R | Title of loadpoint |
| loadpoint\<N\>#vehicleConnected | Switch | R | Whether vehicle is connected to loadpoint |
| loadpoint\<N\>#vehicleConnectedDuration | Number:Time | R | Duration the vehicle is connected to loadpoint |
| loadpoint\<N\>#vehicleOdometer | Number:Length | R | Total distance travelled by EV |
| loadpoint\<N\>#vehiclePresent | Switch | R | Whether evcc is able to get data from vehicle |
| loadpoint\<N\>#vehicleRange | Number:Length | R | Battery range for EV |
| loadpoint\<N\>#vehicleName | String | R | The unique identifier of the EV used in the evcc configuration (containing no whitespaces nor special characters) |
| loadpoint\<N\>#chargerFeatureHeating | Switch | R | 'True' for heating device: State of Charge in Degree instead of Percent |
| loadpoint\<N\>#chargerFeatureIntegratedDevice | Switch | R | 'True' for integrated device: Operate without a "vehicle" (e.g. heat pump, eBike) |
#### Loadpoint Channels Specific for Vehicles
| Channel | Type | Read/Write | Description |
|----------------------------------|----------------------|------------|-------------------------------------------------------------------------|
| loadpoint\<N\>#limitSoC | Number:Dimensionless | RW | Until which state of charge (SoC) should the vehicle be charged |
| loadpoint\<N\>#effectiveLimitSoC | Number:Dimensionless | R | Effective state of charge (SoC) until which the vehicle will be charged |
| loadpoint\<N\>#vehicleSoC | Number:Dimensionless | R | Current State of Charge of EV |
#### Loadpoint Channels Specific for Heating Devices
| Channel | Type | Read/Write | Description |
|------------------------------------------|--------------------|------------|----------------------------------------------------------------------|
| loadpoint\<N\>#limitTemperature | Number:Temperature | RW | Until which Temperature should the heating device be charged |
| loadpoint\<N\>#effectiveLimitTemperature | Number:Temperature | R | Effective Temperature until which the heating device will be charged |
| loadpoint\<N\>#vehicleTemperature | Number:Temperature | R | Current Temperature of the heating device |
### Vehicle Channels
Those channels exist:
- 1 per configured loadpoint with `chargerFeatureHeating = false`:
- These channels point to the heating device that is currently active/connected at/to the loadpoint
- Please note that you have to replace _\<N\>_ with your loadpoint id/number
- 1 per configured vehicle:
- Please note that you have to replace _\<ID\>_ with your vehicle id/name
| Channel | Type | Read/Write | Description |
|----------------------------------------------------|----------------------|------------|--------------------------------------------------------------------------|
| [loadpoint\<N\>\|vehicle\<ID\]>#vehicleTitle | String | R | Title of vehicle |
| [loadpoint\<N\>\|vehicle\<ID\]>#vehicleMinSoC | Number:Dimensionless | RW | Minimum state of charge (SoC) a vehicle should have |
| [loadpoint\<N\>\|vehicle\<ID\]>#vehicleLimitSoC | Number:Dimensionless | RW | Until which state of charge (SoC) should the specific vehicle be charged |
| [loadpoint\<N\>\|vehicle\<ID\]>#vehicleCapacity | Number:Energy | R | Capacity of EV battery |
| [loadpoint\<N\>\|vehicle\<ID\]>#vehiclePlanEnabled | Switch | RW | Plan for charging enabled |
| [loadpoint\<N\>\|vehicle\<ID\]>#vehiclePlanSoC | Number:Dimensionless | RW | Until which state of charge (SoC) should vehicle be charged in plan |
| [loadpoint\<N\>\|vehicle\<ID\]>#vehiclePlanTime | DateTime | RW | When the plan SoC should be reached |
### Heating Channels
Those channels exist:
- 1 per configured loadpoint with `chargerFeatureHeating = true`:
- These channels point to the heating device that is currently active/connected at/to the loadpoint
- Please note that you have to replace _\<N\>_ with your loadpoint id/number
- 1 per configured heating device:
- Please note that you have to replace _\<ID\>_ with your heating device id/name
| Channel | Type | Read/Write | Description |
|---------------------------------------------------------|--------------------|------------|-----------------------------------------------------------------------|
| [loadpoint\<N\>\|heating\<ID\]>#heatingTitle | String | R | Title of heating device |
| [loadpoint\<N\>\|heating\<ID\]>#heatingMinTemperature | Number:Temperature | RW | Minimum Temperature a heating device should have |
| [loadpoint\<N\>\|heating\<ID\]>#heatingLimitTemperature | Number:Temperature | RW | Until which Temperature should the specific heating device be charged |
| [loadpoint\<N\>\|heating\<ID\]>#heatingCapacity | Number:Energy | R | Capacity of heating device |
| [loadpoint\<N\>\|heating\<ID\]>#heatingPlanEnabled | Switch | RW | Plan for charging enabled |
| [loadpoint\<N\>\|heating\<ID\]>#heatingPlanTemperature | Number:Temperature | RW | Until which Temperature should heating device be charged in plan |
| [loadpoint\<N\>\|heating\<ID\]>#heatingPlanTime | DateTime | RW | When the plan Temperature should be reached |
evcc Things can have several channels based corresponding on their capabilities.
These channels are dynamically added to the Thing during their initialization; therefore, there is no list of possible channels in this documentation.
## Full Example
### Thing(s)
### `demo.things` Example
```java
Thing evcc:device:demo "evcc Demo" [url="https://demo.evcc.io", refreshInterval=60]
```
### Items
```java
// General
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"}
String evcc_version "Version [%s]" <text> {channel="evcc:device:demo:general#version"}
String evcc_availableVersion "Available Version [%s]" <text> {channel="evcc:device:demo:general#availableVersion"}
// 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"}
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"}
Number:Dimensionless evcc_loadpoint0_effectiveLimitSoC "Effective Limit SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0#effectiveLimitSoC"}
String evcc_loadpoint0_title "Loadpoint title [%s]" <text> {channel="evcc:device:demo:loadpoint0#title"}
Switch evcc_loadpoint0_chargerFeatureHeating "Feature: Heating [%s]" <switch> {channel="evcc:device:demo:loadpoint0#chargerFeatureHeating"}
Switch evcc_loadpoint0_chargerFeatureIntegratedDevice "Feature: Integrated Device [%s]" <switch> {channel="evcc:device:demo:loadpoint0#chargerFeatureIntegratedDevice"}
// Loadpoint vehicle channels
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: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 on loadpoint
String evcc_loadpoint0current_vehicleTitle "Vehicle title [%s]" <text> {channel="evcc:device:demo:loadpoint0current#vehicleTitle"}
Number:Dimensionless evcc_loadpoint0current_vehicleMinSoC "Vehicle minimum SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0current#vehicleMinSoC"}
Number:Dimensionless evcc_loadpoint0current_vehicleLimitSoC "Vehicle limit SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0current#vehicleLimitSoC"}
Number:Energy evcc_loadpoint0current_vehicleCapacity "Vehicle capacity [%.0f kWh]" <batterylevel> {channel="evcc:device:demo:loadpoint0current#vehicleCapacity"}
Switch evcc_loadpoint0current_vehiclePlanEnabled "Vehicle plan enabled [%s]" <switch> {channel="evcc:device:demo:loadpoint0current#vehiclePlanEnabled"}
Number:Dimensionless evcc_loadpoint0current_vehiclePlanSoC "Vehicle plan SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0current#vehiclePlanSoC"}
DateTime evcc_loadpoint0current_vehiclePlanTime "Vehicle plan time [%1$td.%1$tm.%1$tY, %1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0current#vehiclePlanTime"}
```
### Sitemap
```perl
sitemap evcc label="evcc Demo" {
Frame label="General" {
Text item=evcc_batteryPower
Text item=evcc_batterySoC
Text item=evcc_gridPower
Text item=evcc_homePower
Text item=evcc_pvPower
}
Frame label="Loadpoint 0" {
Text item=evcc_loadpoint0_title
Text item=evcc_loadpoint0_enabled label="Charging" {
Text item=evcc_loadpoint0_charging
Text item=evcc_loadpoint0_chargePower
Text item=evcc_loadpoint0_chargeCurrent
Text item=evcc_loadpoint0_activePhases
Text item=evcc_loadpoint0_chargeDuration
Text item=evcc_loadpoint0_chargeRemainingDuration
Text item=evcc_loadpoint0_chargeRemainingEnergy
}
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_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
Setpoint item=evcc_loadpoint0_phases minValue=1 maxValue=3 step=2
}
Text item=evcc_loadpoint0_vehicleName label="Vehicle" {
Text item=evcc_loadpoint0current_vehicleCapacity
Text item=evcc_loadpoint0_vehicleOdometer
Text item=evcc_loadpoint0_vehicleRange
Text item=evcc_loadpoint0_vehicleSoC
Text item=evcc_loadpoint0current_vehicleTitle
Setpoint item=evcc_loadpoint0current_vehicleMinSoC minValue=0 maxValue=100 step=5
Setpoint item=evcc_loadpoint0current_vehicleLimitSoC minValue=5 maxValue=100 step=5
Switch item=evcc_loadpoint0current_vehiclePlanEnabled
Setpoint item=evcc_loadpoint0current_vehiclePlanSoC minValue=5 maxValue=100 step=5
Input item=evcc_loadpoint0current_vehiclePlanTime
}
}
Bridge evcc:server:demo-server "Demo" [scheme="https", url="demo.evcc.io", port=80, refreshInterval=30] {
// This thing will only exist once per evcc instance
Thing site demo-site "Site - evcc Demo"
// You can define as many Battery things as you have batteries configured in your evcc instance
Thing battery demo-battery1 "Battery - evcc Demo Battery 1"
..
// You can define as many PV things as you have photovoltaics configured in your evcc instance
Thing pv demo-pv1 "PV - evcc Demo Photovoltaic 1"
..
// You can define as many Loadpoint things as you have loadpoints configured in your evcc instance
Thing loadpoint demo-loadpoint-carport "Loadpoint - evcc Demo Loadpoint 1"
..
// You can define as many Vehicle things as you have vehicles configured in your evcc instance
Thing vehicle demo-vehicle1 "Vehicle - evcc Demo Vehicle 1"
..
}
```
### `demo.items` Example
```java
Number GridPower "Grid Power" { channel="evcc:site:demo-server:demo-site:site-grid-power" }
```
## Troubleshooting
If you need additional data that can be read out of the API response please reach out to the openHAB community.
@@ -12,206 +12,66 @@
*/
package org.openhab.binding.evcc.internal;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
* The {@link EvccBindingConstants} class defines common constants, which are
* used across the whole binding.
* The {@link EvccBindingConstants} class contains fields mapping thing configuration parameters.
*
* @author Florian Hotze - Initial contribution
* @author Luca Arnecke - Update to evcc version 0.123.1
* @author Marcel Goerentz - Reworked the binding
*/
@NonNullByDefault
public class EvccBindingConstants {
private static final String BINDING_ID = "evcc";
public 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";
public static final String CHANNEL_GROUP_ID_HEATING = "heating";
public static final String CHANNEL_GROUP_ID_CURRENT = "current";
public static final ThingTypeUID THING_TYPE_SERVER = new ThingTypeUID(BINDING_ID, "server");
public static final ThingTypeUID THING_TYPE_SITE = new ThingTypeUID(BINDING_ID, "site");
public static final ThingTypeUID THING_TYPE_LOADPOINT = new ThingTypeUID(BINDING_ID, "loadpoint");
public static final ThingTypeUID THING_TYPE_VEHICLE = new ThingTypeUID(BINDING_ID, "vehicle");
public static final ThingTypeUID THING_TYPE_PV = new ThingTypeUID(BINDING_ID, "pv");
public static final ThingTypeUID THING_TYPE_BATTERY = new ThingTypeUID(BINDING_ID, "battery");
public static final ThingTypeUID THING_TYPE_HEATING = new ThingTypeUID(BINDING_ID, "heating");
public static final ThingTypeUID THING_TYPE_STATISTICS = new ThingTypeUID(BINDING_ID, "statistics");
// 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_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";
public static final String CHANNEL_VERSION = "version";
public static final String CHANNEL_AVAILABLE_VERSION = "availableVersion";
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_SITE, THING_TYPE_VEHICLE,
THING_TYPE_LOADPOINT, THING_TYPE_BATTERY, THING_TYPE_PV, THING_TYPE_HEATING, THING_TYPE_STATISTICS);
public static final String CHANNEL_LOADPOINT_ACTIVE_PHASES = "activePhases";
public static final String CHANNEL_LOADPOINT_CHARGE_CURRENT = "chargeCurrent";
public static final String CHANNEL_LOADPOINT_CHARGE_DURATION = "chargeDuration";
public static final String CHANNEL_LOADPOINT_CHARGE_POWER = "chargePower";
public static final String CHANNEL_LOADPOINT_CHARGE_REMAINING_DURATION = "chargeRemainingDuration";
public static final String CHANNEL_LOADPOINT_CHARGE_REMAINING_ENERGY = "chargeRemainingEnergy";
public static final String CHANNEL_LOADPOINT_CHARGED_ENERGY = "chargedEnergy";
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_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_MODE = "mode";
public static final String CHANNEL_LOADPOINT_PHASES = "phases";
public static final String CHANNEL_LOADPOINT_LIMIT_ENERGY = "limitEnergy";
public static final String CHANNEL_LOADPOINT_LIMIT_SOC = "limitSoC";
public static final String CHANNEL_LOADPOINT_LIMIT_TEMPERATURE = "limitTemperature";
public static final String CHANNEL_LOADPOINT_EFFECTIVE_LIMIT_SOC = "effectiveLimitSoC";
public static final String CHANNEL_LOADPOINT_EFFECTIVE_LIMIT_TEMPERATURE = "effectiveLimitTemperature";
public static final String CHANNEL_LOADPOINT_TITLE = "title";
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_TEMPERATURE = "vehicleTemperature";
public static final String CHANNEL_LOADPOINT_VEHICLE_NAME = "vehicleName";
public static final String CHANNEL_LOADPOINT_CHARGER_FEATURE_HEATING = "chargerFeatureHeating";
public static final String CHANNEL_LOADPOINT_CHARGER_FEATURE_INTEGRATED_DEVICE = "chargerFeatureIntegratedDevice";
public static final String PROPERTY_ID = "id";
public static final String PROPERTY_INDEX = "index";
public static final String PROPERTY_TYPE = "type";
public static final String PROPERTY_TITLE = "title";
public static final String PROPERTY_SITE_TITLE = "siteTitle";
public static final String CHANNEL_VEHICLE_TITLE = "vehicleTitle";
public static final String CHANNEL_HEATING_TITLE = "heatingTitle";
public static final String CHANNEL_VEHICLE_MIN_SOC = "vehicleMinSoC";
public static final String CHANNEL_HEATING_MIN_TEMPERATURE = "heatingMinTemperature";
public static final String CHANNEL_VEHICLE_LIMIT_SOC = "vehicleLimitSoC";
public static final String CHANNEL_HEATING_LIMIT_TEMPERATURE = "heatingLimitTemperature";
public static final String CHANNEL_VEHICLE_CAPACITY = "vehicleCapacity";
public static final String CHANNEL_HEATING_CAPACITY = "heatingCapacity";
public static final String CHANNEL_VEHICLE_PLAN_ENABLED = "vehiclePlanEnabled";
public static final String CHANNEL_HEATING_PLAN_ENABLED = "heatingPlanEnabled";
public static final String CHANNEL_VEHICLE_PLAN_SOC = "vehiclePlanSoC";
public static final String CHANNEL_HEATING_PLAN_TEMPERATURE = "heatingPlanTemperature";
public static final String CHANNEL_VEHICLE_PLAN_TIME = "vehiclePlanTime";
public static final String CHANNEL_HEATING_PLAN_TIME = "heatingPlanTime";
public static final String API_PATH_LOADPOINTS = "/loadpoints";
public static final String API_PATH_VEHICLES = "/vehicles";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "device");
public static final String PROPERTY_TYPE_BATTERY = "battery";
public static final String PROPERTY_TYPE_HEATING = "heating";
public static final String PROPERTY_TYPE_LOADPOINT = "loadpoint";
public static final String PROPERTY_TYPE_PV = "pv";
public static final String PROPERTY_TYPE_SITE = "site";
public static final String PROPERTY_TYPE_STATISTICS = "statistics";
public static final String PROPERTY_TYPE_VEHICLE = "vehicle";
// List of all Channel Type UIDs
public static final ChannelTypeUID CHANNEL_TYPE_UID_BATTERY_CAPACITY = new ChannelTypeUID(BINDING_ID,
CHANNEL_BATTERY_CAPACITY);
public static final ChannelTypeUID CHANNEL_TYPE_UID_BATTERY_POWER = new ChannelTypeUID(BINDING_ID,
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_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);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VERSION = new ChannelTypeUID(BINDING_ID, CHANNEL_VERSION);
public static final ChannelTypeUID CHANNEL_TYPE_UID_AVAILABLE_VERSION = new ChannelTypeUID(BINDING_ID,
CHANNEL_AVAILABLE_VERSION);
public static final String JSON_MEMBER_BATTERY = "battery";
public static final String JSON_MEMBER_LOADPOINTS = "loadpoints";
public static final String JSON_MEMBER_PV = "pv";
public static final String JSON_MEMBER_STATISTICS = "statistics";
public static final String JSON_MEMBER_VEHICLES = "vehicles";
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_ACTIVE_PHASES = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_ACTIVE_PHASES);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_CURRENT = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_CHARGE_CURRENT);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_DURATION = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_CHARGE_DURATION);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_POWER = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_CHARGE_POWER);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_REMAINING_DURATION = new ChannelTypeUID(
BINDING_ID, CHANNEL_LOADPOINT_CHARGE_REMAINING_DURATION);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_REMAINING_ENERGY = new ChannelTypeUID(
BINDING_ID, CHANNEL_LOADPOINT_CHARGE_REMAINING_ENERGY);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGED_ENERGY = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_CHARGED_ENERGY);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGING = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_CHARGING);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CONNECTED = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_CONNECTED);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CONNECTED_DURATION = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_CONNECTED_DURATION);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_ENABLED = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_ENABLED);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_MAX_CURRENT = new ChannelTypeUID(BINDING_ID,
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_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_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_LIMIT_TEMPERATURE = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_LIMIT_TEMPERATURE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_EFFECTIVE_LIMIT_SOC = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_EFFECTIVE_LIMIT_SOC);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_EFFECTIVE_LIMIT_TEMPERATURE = new ChannelTypeUID(
BINDING_ID, CHANNEL_LOADPOINT_EFFECTIVE_LIMIT_TEMPERATURE);
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_ODOMETER = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_VEHICLE_ODOMETER);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_PRESENT = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_VEHICLE_PRESENT);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_RANGE = new ChannelTypeUID(BINDING_ID,
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_TEMPERATURE = new ChannelTypeUID(BINDING_ID,
CHANNEL_LOADPOINT_VEHICLE_TEMPERATURE);
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_LOADPOINT_CHARGER_FEATURE_HEATING = new ChannelTypeUID(
BINDING_ID, CHANNEL_LOADPOINT_CHARGER_FEATURE_HEATING);
public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGER_FEATURE_INTEGRATED_DEVICE = new ChannelTypeUID(
BINDING_ID, CHANNEL_LOADPOINT_CHARGER_FEATURE_INTEGRATED_DEVICE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VEHICLE_TITLE = new ChannelTypeUID(BINDING_ID,
CHANNEL_VEHICLE_TITLE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_HEATING_TITLE = new ChannelTypeUID(BINDING_ID,
CHANNEL_HEATING_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_HEATING_MIN_TEMPERATURE = new ChannelTypeUID(BINDING_ID,
CHANNEL_HEATING_MIN_TEMPERATURE);
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_HEATING_LIMIT_TEMPERATURE = new ChannelTypeUID(BINDING_ID,
CHANNEL_HEATING_LIMIT_TEMPERATURE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VEHICLE_CAPACITY = new ChannelTypeUID(BINDING_ID,
CHANNEL_VEHICLE_CAPACITY);
public static final ChannelTypeUID CHANNEL_TYPE_UID_HEATING_CAPACITY = new ChannelTypeUID(BINDING_ID,
CHANNEL_HEATING_CAPACITY);
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_HEATING_PLAN_ENABLED = new ChannelTypeUID(BINDING_ID,
CHANNEL_HEATING_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_HEATING_PLAN_TEMPERATURE = new ChannelTypeUID(BINDING_ID,
CHANNEL_HEATING_PLAN_TEMPERATURE);
public static final ChannelTypeUID CHANNEL_TYPE_UID_VEHICLE_PLAN_TIME = new ChannelTypeUID(BINDING_ID,
CHANNEL_VEHICLE_PLAN_TIME);
public static final ChannelTypeUID CHANNEL_TYPE_UID_HEATING_PLAN_TIME = new ChannelTypeUID(BINDING_ID,
CHANNEL_HEATING_PLAN_TIME);
public static final int CONNECTION_TIMEOUT_MILLISEC = 5000;
public static final int LONG_CONNECTION_TIMEOUT_MILLISEC = 60000;
public static final String EVCC_REST_API = "/api/";
public static final String NUMBER_CURRENCY = CoreItemFactory.NUMBER + ":Currency";
public static final String NUMBER_DIMENSIONLESS = CoreItemFactory.NUMBER + ":Dimensionless";
public static final String NUMBER_ELECTRIC_CURRENT = CoreItemFactory.NUMBER + ":ElectricCurrent";
public static final String NUMBER_EMISSION_INTENSITY = CoreItemFactory.NUMBER + ":EmissionIntensity";
public static final String NUMBER_ENERGY = CoreItemFactory.NUMBER + ":Energy";
public static final String NUMBER_POWER = CoreItemFactory.NUMBER + ":Power";
public static final String NUMBER_TIME = CoreItemFactory.NUMBER + ":Time";
public static final String NUMBER_LENGTH = CoreItemFactory.NUMBER + ":Length";
public static final String NUMBER_ENERGY_PRICE = CoreItemFactory.NUMBER + ":EnergyPrice";
}
@@ -13,22 +13,17 @@
package org.openhab.binding.evcc.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link EvccConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Florian Hotze - Initial contribution
* @author Marcel Goerentz - Rework the binding
*/
@NonNullByDefault
public class EvccConfiguration {
/**
* URL of the evcc instance, e.g. https://demo.evcc.io
*/
public @Nullable String url;
/**
* Interval for state fetching in seconds.
*/
public int refreshInterval = 60;
public String host = "";
public int pollInterval = 30;
public int port = 7070;
public String scheme = "http";
}
@@ -14,16 +14,26 @@ package org.openhab.binding.evcc.internal;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.binding.evcc.internal.handler.EvccBatteryHandler;
import org.openhab.binding.evcc.internal.handler.EvccBridgeHandler;
import org.openhab.binding.evcc.internal.handler.EvccHeatingHandler;
import org.openhab.binding.evcc.internal.handler.EvccLoadpointHandler;
import org.openhab.binding.evcc.internal.handler.EvccPvHandler;
import org.openhab.binding.evcc.internal.handler.EvccSiteHandler;
import org.openhab.binding.evcc.internal.handler.EvccStatisticsHandler;
import org.openhab.binding.evcc.internal.handler.EvccVehicleHandler;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
@@ -33,33 +43,54 @@ import org.osgi.service.component.annotations.Reference;
* handlers.
*
* @author Florian Hotze - Initial contribution
* @author Marcel Goerentz - Rework the binding
*/
@NonNullByDefault
@Component(configurationPid = "binding.evcc", service = ThingHandlerFactory.class)
public class EvccHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_DEVICE);
private final TimeZoneProvider timeZoneProvider;
private final HttpClientFactory httpClientFactory;
private final ChannelTypeRegistry channelTypeRegistry;
private final TranslationProvider i18nProvider;
private final LocaleProvider localeProvider;
@Activate
public EvccHandlerFactory(final @Reference TimeZoneProvider timeZoneProvider) {
this.timeZoneProvider = timeZoneProvider;
public EvccHandlerFactory(@Reference HttpClientFactory httpClientFactory,
@Reference ChannelTypeRegistry channelTypeRegistry, @Reference TranslationProvider i18nProvider,
@Reference LocaleProvider localeProvider) {
this.httpClientFactory = httpClientFactory;
this.channelTypeRegistry = channelTypeRegistry;
this.i18nProvider = i18nProvider;
this.localeProvider = localeProvider;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
return SUPPORTED_THING_TYPES.contains(thingTypeUID) || THING_TYPE_SERVER.equals(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
ThingHandler handler = null;
ThingTypeUID type = thing.getThingTypeUID();
if (THING_TYPE_DEVICE.equals(thingTypeUID)) {
return new EvccHandler(thing, timeZoneProvider);
if (THING_TYPE_SERVER.equals(type)) {
handler = new EvccBridgeHandler((Bridge) thing, httpClientFactory, i18nProvider, localeProvider);
} else if (THING_TYPE_SITE.equals(type)) {
handler = new EvccSiteHandler(thing, channelTypeRegistry);
} else if (THING_TYPE_VEHICLE.equals(type)) {
handler = new EvccVehicleHandler(thing, channelTypeRegistry);
} else if (THING_TYPE_LOADPOINT.equals(type)) {
handler = new EvccLoadpointHandler(thing, channelTypeRegistry);
} else if (THING_TYPE_HEATING.equals(type)) {
handler = new EvccHeatingHandler(thing, channelTypeRegistry);
} else if (THING_TYPE_BATTERY.equals(type)) {
handler = new EvccBatteryHandler(thing, channelTypeRegistry);
} else if (THING_TYPE_PV.equals(type)) {
handler = new EvccPvHandler(thing, channelTypeRegistry);
} else if (THING_TYPE_STATISTICS.equals(type)) {
handler = new EvccStatisticsHandler(thing, channelTypeRegistry);
}
return null;
return handler;
}
}
@@ -1,173 +0,0 @@
/*
* Copyright (c) 2010-2025 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;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.EVCC_REST_API;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.LONG_CONNECTION_TIMEOUT_MILLISEC;
import java.io.IOException;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.evcc.internal.api.dto.Result;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.net.http.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
/**
* The {@link EvccAPI} is responsible for API calls to evcc.
* 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 {
private final Logger logger = LoggerFactory.getLogger(EvccAPI.class);
private final Gson gson = new Gson();
private final TimeZoneProvider timeZoneProvider;
private String host;
public EvccAPI(String host, TimeZoneProvider timeZoneProvider) {
this.host = (host.endsWith("/") ? host.substring(0, host.length() - 1) : host);
this.timeZoneProvider = timeZoneProvider;
}
/**
* Make a HTTP request.
*
* @param url full request URL
* @param method request method, e.g. GET, POST
* @return the response body
* @throws EvccApiException if HTTP request failed
*/
private String httpRequest(String url, String method) throws EvccApiException {
try {
String response = HttpUtil.executeUrl(method, url, LONG_CONNECTION_TIMEOUT_MILLISEC);
logger.trace("{} {} - {}", method, url, response);
return response;
} catch (IOException e) {
throw new EvccApiException("HTTP request failed for URL " + url, e);
}
}
// End utility functions
// API calls to evcc
/**
* Get the status from evcc.
*
* @return {@link Result} result object from API
* @throws EvccApiException if status request failed
*/
public Result getResult() throws EvccApiException {
final String response = httpRequest(this.host + EVCC_REST_API + "state", "GET");
logger.trace("API Response >> {}", response);
try {
JsonObject jsonObject = gson.fromJson(response, JsonObject.class);
if (jsonObject == null) {
throw new EvccApiException("Invalid response - no json.");
}
Result result = null;
if (jsonObject.has("result")) {
result = gson.fromJson(jsonObject.get("result"), Result.class);
}
if (jsonObject.has("version")) {
// Attribute version available - continue as Result
result = gson.fromJson(jsonObject, Result.class);
}
if (result != null) {
return result;
} else {
throw new EvccApiException("Invalid response - no result: " + response);
}
} catch (JsonSyntaxException e) {
throw new EvccApiException("Error parsing response: " + response, e);
}
}
// Site API calls.
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 setLimitEnergy(int loadpoint, float limitEnergy) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/limitenergy/" + limitEnergy,
"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 {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/phases/" + phases, "POST");
}
public String setMinCurrent(int loadpoint, int minCurrent) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/mincurrent/" + minCurrent, "POST");
}
public String setMaxCurrent(int loadpoint, int maxCurrent) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/maxcurrent/" + maxCurrent, "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 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, Instant planTime) throws EvccApiException {
ZonedDateTime adjustedTime = planTime.atZone(timeZoneProvider.getTimeZone());
String formattedTime = adjustedTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
return httpRequest(
this.host + EVCC_REST_API + "vehicles/" + vehicleName + "/plan/soc/" + planSoC + "/" + formattedTime,
"POST");
}
public String removeVehiclePlan(String vehicleName) throws EvccApiException {
return httpRequest(this.host + EVCC_REST_API + "vehicles/" + vehicleName + "/plan/soc", "DELETE");
}
}
@@ -1,34 +0,0 @@
/*
* Copyright (c) 2010-2025 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;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link EvccApiException} signals that an API request by {@link EvccAPI} failed.
*
* @author Florian Hotze - Initial contribution
*/
@NonNullByDefault
public class EvccApiException extends Exception {
private static final long serialVersionUID = -1935778974024277328L;
public EvccApiException(String message) {
super(message);
}
public EvccApiException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -1,69 +0,0 @@
/*
* Copyright (c) 2010-2025 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 battery object of the status response (/api/state).
* This DTO was written for evcc version 0.123.1
*
* @author MikeTheTux - Initial contribution
*/
public class Battery {
// 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("power")
private float power;
@SerializedName("energy")
private float energy;
@SerializedName("soc")
private float soc;
@SerializedName("capacity")
private float capacity;
@SerializedName("controllable")
private boolean controllable;
/**
* @return battery's capacity
*/
public float getCapacity() {
return capacity;
}
/**
* @return battery's power
*/
public float getPower() {
return power;
}
/**
* @return battery's state of charge
*/
public float getSoC() {
return soc;
}
/**
* @return battery discharge controlable
*/
public boolean getControllable() {
return controllable;
}
}
@@ -1,56 +0,0 @@
/*
* Copyright (c) 2010-2025 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 the grid response (/api/state).
* This DTO was written for evcc version 0.133.0
*
* @author Daniel Kötting - Initial contribution
*/
public class Grid {
// 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("currents")
private Float[] currents;
@SerializedName("energy")
private Float energy;
@SerializedName("power")
private Float power;
/**
* @return grid's currents
*/
public Float[] getCurrents() {
return currents;
}
/**
* @return grid's energy
*/
public Float getEnergy() {
return energy;
}
/**
* @return grid's power or {@code null} if not available
*/
public Float getPower() {
return power;
}
}
@@ -1,298 +0,0 @@
/*
* Copyright (c) 2010-2025 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 loadpoint object of the status response (/api/state).
* 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
* @author Marcel Goerentz - Replace invalid chars with hyphens in vehicleName
*/
public class Loadpoint {
// 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("phasesActive")
private int activePhases;
@SerializedName("chargeCurrent")
private float chargeCurrent;
@SerializedName("chargeDuration")
private long chargeDuration;
@SerializedName("chargePower")
private float chargePower;
@SerializedName("chargeRemainingDuration")
private long chargeRemainingDuration;
@SerializedName("chargeRemainingEnergy")
private float chargeRemainingEnergy;
@SerializedName("chargedEnergy")
private float chargedEnergy;
@SerializedName("charging")
private boolean charging;
@SerializedName("connected")
private boolean connected;
@SerializedName("connectedDuration")
private long connectedDuration;
@SerializedName("enabled")
private boolean enabled;
@SerializedName("maxCurrent")
private float maxCurrent;
@SerializedName("minCurrent")
private float minCurrent;
@SerializedName("mode")
private String mode;
@SerializedName("phasesEnabled")
private int phases;
@SerializedName("limitEnergy")
private float limitEnergy;
@SerializedName("limitSoc")
private float limitSoC;
@SerializedName("targetTime")
private String targetTime;
@SerializedName("title")
private String title;
@SerializedName("vehicleOdometer")
private float vehicleOdometer;
@SerializedName("vehiclePresent")
private boolean vehiclePresent;
@SerializedName("vehicleRange")
private float vehicleRange;
@SerializedName("vehicleSoc")
private float vehicleSoC;
@SerializedName("vehicleName")
private String vehicleName;
@SerializedName("effectiveLimitSoc")
private float effectiveLimitSoC;
@SerializedName("chargerFeatureHeating")
private boolean chargerFeatureHeating;
@SerializedName("chargerFeatureIntegratedDevice")
private boolean chargerFeatureIntegratedDevice;
/**
* @return number of active phases
*/
public int getActivePhases() {
return activePhases;
}
/**
* @return charge current
*/
public float getChargeCurrent() {
return chargeCurrent;
}
/**
* @return charge duration
*/
public long getChargeDuration() {
return chargeDuration;
}
/**
* @return charge power
*/
public float getChargePower() {
return chargePower;
}
/**
* @return charge remaining duration until the target SoC is reached
*/
public long getChargeRemainingDuration() {
return chargeRemainingDuration;
}
/**
* @return charge remaining energy until the target SoC is reached
*/
public float getChargeRemainingEnergy() {
return chargeRemainingEnergy;
}
/**
* @return charged energy
*/
public float getChargedEnergy() {
return chargedEnergy;
}
/**
* @return whether loadpoint is charging a vehicle
*/
public boolean getCharging() {
return charging;
}
/**
* @return whether a vehicle is connected to the loadpoint
*/
public boolean getConnected() {
return connected;
}
/**
* @return vehicle connected duration
*/
public long getConnectedDuration() {
return connectedDuration;
}
/**
* @return whether loadpoint is enabled
*/
public boolean getEnabled() {
return enabled;
}
/**
* @return maximum current
*/
public float getMaxCurrent() {
return maxCurrent;
}
/**
* @return minimum current
*/
public float getMinCurrent() {
return minCurrent;
}
/**
* @return charging mode: off, now, minpv, pv
*/
public String getMode() {
return mode;
}
/**
* @return number of enabled phases
*/
public int getPhases() {
return phases;
}
/**
* @return limit energy
*/
public float getLimitEnergy() {
return limitEnergy;
}
/**
* @return limit state of charge (SoC)
*/
public float getLimitSoC() {
return limitSoC;
}
/**
* @return target time for the target state of charge
*/
public String getTargetTime() {
return targetTime;
}
/**
* @return loadpoint's title/name
*/
public String getTitle() {
return title;
}
/**
* @return vehicle's odometer
*/
public float getVehicleOdometer() {
return vehicleOdometer;
}
/**
* @return whether evcc is able to get data from vehicle
*/
public boolean getVehiclePresent() {
return vehiclePresent;
}
/**
* @return vehicle's range
*/
public float getVehicleRange() {
return vehicleRange;
}
/**
* @return vehicle's state of charge (SoC)
*/
public float getVehicleSoC() {
return vehicleSoC;
}
/**
* @return vehicle's title/name
*/
public String getVehicleName() {
return vehicleName != null ? vehicleName.replaceAll("[^a-zA-Z0-9_-]", "-") : null;
}
/**
* @return effective limit state of charge
*/
public float getEffectiveLimitSoC() {
return effectiveLimitSoC;
}
/**
* @return Charger Feature: Heating
*/
public boolean getChargerFeatureHeating() {
return chargerFeatureHeating;
}
/**
* @return Charger Feature: Integrated Device
*/
public boolean getChargerFeatureIntegratedDevice() {
return chargerFeatureIntegratedDevice;
}
}
@@ -1,36 +0,0 @@
/*
* Copyright (c) 2010-2025 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 PV object of the status response (/api/state).
* This DTO was written for evcc version 0.123.1
*
* @author MikeTheTux - Initial contribution
*/
public class PV {
// 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("power")
private float power;
/**
* @return PV power
*/
public float getPower() {
return power;
}
}
@@ -1,46 +0,0 @@
/*
* Copyright (c) 2010-2025 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;
}
}
@@ -1,252 +0,0 @@
/*
* Copyright (c) 2010-2025 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 java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
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.123.1
*
* @author Florian Hotze - Initial contribution
* @author Luca Arnecke - update to evcc version 0.123.1
* @author Daniel Kötting - update to evcc version 0.133.0
* @author Marcel Goerentz - Replace invalid chars with hyphens in vehicles map
*/
public class Result {
// 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
// "auth" is left out because it does not provide any useful information
@SerializedName("batteryCapacity")
private float batteryCapacity;
@SerializedName("battery")
private Battery[] battery;
@SerializedName("batteryPower")
private float batteryPower;
@SerializedName("batterySoc")
private float batterySoC;
@SerializedName("batteryDischargeControl")
private boolean batteryDischargeControl;
@SerializedName("batteryMode")
private String batteryMode;
@SerializedName("gridPower")
private Float gridPower;
@SerializedName("grid")
private Grid grid;
@SerializedName("gridConfigured")
private boolean gridConfigured;
@SerializedName("homePower")
private float homePower;
@SerializedName("loadpoints")
private Loadpoint[] loadpoints;
@SerializedName("prioritySoc")
private float prioritySoC;
@SerializedName("bufferSoc")
private float bufferSoC;
@SerializedName("bufferStartSoc")
private float bufferStartSoC;
@SerializedName("residualPower")
private float residualPower;
@SerializedName("pv")
private PV[] pv;
@SerializedName("pvPower")
private float pvPower;
@SerializedName("siteTitle")
private String siteTitle;
@SerializedName("vehicles")
private Map<String, Vehicle> vehicles;
@SerializedName("version")
private String version;
@SerializedName("availableVersion")
private String availableVersion;
/**
* @return all configured batteries
*/
public Battery[] getBattery() {
return battery;
}
/**
* @return battery's capacity
*/
public float getBatteryCapacity() {
return batteryCapacity;
}
/**
* @return battery's power
*/
public float getBatteryPower() {
return batteryPower;
}
/**
* @return battery's priority state of charge
*/
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;
}
/**
* @return battery's state of charge
*/
public float getBatterySoC() {
return batterySoC;
}
/**
* @return battery discharge control
*/
public boolean getBatteryDischargeControl() {
return batteryDischargeControl;
}
/**
* @return battery mode
*/
public String getBatteryMode() {
return batteryMode;
}
/**
* @return gridPower (before evcc version 0.133.0)
*/
public Float getGridPower() {
return gridPower;
}
/**
* @return all grid related values (since evcc version 0.133.0)
*/
public Grid getGrid() {
return grid;
}
/**
* @return is grid configured (since evcc version 0.133.0)
*/
public boolean getGridConfigured() {
return gridConfigured;
}
/**
* @return home's power
*/
public float getHomePower() {
return homePower;
}
/**
* @return all configured loadpoints
*/
public Loadpoint[] getLoadpoints() {
return loadpoints;
}
/**
* @return all configured PVs
*/
public PV[] getPV() {
return pv;
}
/**
* @return pv's power
*/
public float getPvPower() {
return pvPower;
}
/**
* @return site's title/name
*/
public String getSiteTitle() {
return siteTitle;
}
public Map<String, Vehicle> getVehicles() {
Map<String, Vehicle> correctedMap = new HashMap<>();
for (Entry<String, Vehicle> entry : vehicles.entrySet()) {
// The key from the vehicles map is used as uid, so it should not contain any disallowed chars
// If necessary replace the forbidden chars with hyphens
String key = entry.getKey();
if (!key.matches("[a-zA-Z0-9_-]+")) {
key = key.replaceAll("[^a-zA-Z0-9_-]", "-");
}
correctedMap.put(key, entry.getValue());
}
return correctedMap;
}
/**
* @return evcc version
*/
public String getVersion() {
return version;
}
/**
* @return evcc available version
*/
public String getAvailableVersion() {
return availableVersion;
}
}
@@ -1,31 +0,0 @@
/*
* Copyright (c) 2010-2025 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 the status response (/api/state).
* This DTO was written for evcc version 0.117.0
*
* @author Florian Hotze - Initial contribution
*/
public class Status {
@SerializedName("result")
private Result result;
public Result getResult() {
return result;
}
}
@@ -1,80 +0,0 @@
/*
* Copyright (c) 2010-2025 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("capacity")
private float capacity;
@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 vehicle's capacity
*/
public float getCapacity() {
return capacity;
}
/**
* @return current plan for vehicle
*/
public Plan getPlan() {
if (plans != null) {
return plans[0];
} else {
return null;
}
}
}
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2010-2025 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.discovery;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.evcc.internal.discovery.mapper.BatteryDiscoveryMapper;
import org.openhab.binding.evcc.internal.discovery.mapper.EvccDiscoveryMapper;
import org.openhab.binding.evcc.internal.discovery.mapper.LoadpointDiscoveryMapper;
import org.openhab.binding.evcc.internal.discovery.mapper.PvDiscoveryMapper;
import org.openhab.binding.evcc.internal.discovery.mapper.SiteDiscoveryMapper;
import org.openhab.binding.evcc.internal.discovery.mapper.StatisticsDiscoveryMapper;
import org.openhab.binding.evcc.internal.discovery.mapper.VehicleDiscoveryMapper;
import org.openhab.binding.evcc.internal.handler.EvccBridgeHandler;
import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ServiceScope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonObject;
/**
* The {@link EvccDiscoveryService} is responsible for scanning the API response for things
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
@Component(scope = ServiceScope.PROTOTYPE, service = EvccDiscoveryService.class, configurationPid = "discovery.evcc")
public class EvccDiscoveryService extends AbstractThingHandlerDiscoveryService<EvccBridgeHandler> {
private static final int TIMEOUT = 5;
private static final int SCAN_INTERVAL_IN_SECONDS = 5; // We can scan every 5 seconds since we are using the cached
// response
private final Logger logger = LoggerFactory.getLogger(getClass());
private final List<EvccDiscoveryMapper> mappers = List.of(new LoadpointDiscoveryMapper(),
new VehicleDiscoveryMapper(), new BatteryDiscoveryMapper(), new SiteDiscoveryMapper(),
new PvDiscoveryMapper(), new StatisticsDiscoveryMapper());
private @Nullable ScheduledFuture<?> evccDiscoveryJob;
public EvccDiscoveryService() {
super(EvccBridgeHandler.class, SUPPORTED_THING_TYPES, TIMEOUT, true);
}
@Override
protected void startScan() {
logger.debug("Starting evcc Discover");
JsonObject state = thingHandler.getCachedEvccState();
if (!state.isEmpty()) {
for (EvccDiscoveryMapper mapper : mappers) {
mapper.discover(state, thingHandler).forEach(thing -> {
logger.debug("Thing discovered {}", thing);
thingDiscovered(thing);
});
}
}
}
@Override
public Set<ThingTypeUID> getSupportedThingTypes() {
return SUPPORTED_THING_TYPES;
}
@Override
protected void startBackgroundDiscovery() {
logger.debug("Start evcc background discovery");
Optional.ofNullable(evccDiscoveryJob).ifPresentOrElse(job -> {
if (job.isCancelled()) {
startNewJob();
}
}, this::startNewJob);
}
private void startNewJob() {
evccDiscoveryJob = scheduler.scheduleWithFixedDelay(this::startScan, 0, SCAN_INTERVAL_IN_SECONDS,
TimeUnit.SECONDS);
}
@Override
protected void stopBackgroundDiscovery() {
logger.debug("Stop evcc device background discovery");
Optional.ofNullable(evccDiscoveryJob)
.ifPresent(backgroundScan -> backgroundScan.cancel(isBackgroundDiscoveryEnabled()));
evccDiscoveryJob = null;
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2025 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.discovery;
import java.text.Normalizer;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Utils} provides utility functions
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Utils {
/**
* This method removes specific local characters (like ä, ö, ü), so we get a sanitized string
*
* @param name that will be sanitized
* @return a sanitized name that has replaced any invalid char
*/
public static String sanitizeName(String name) {
String result = name;
if (!Normalizer.isNormalized(name, Normalizer.Form.NFKD)) {
result = Normalizer.normalize(name, Normalizer.Form.NFKD);
result = result.replaceAll("\\p{M}", "");
}
return result.replaceAll("[^a-zA-Z0-9_]", "-").toLowerCase();
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2010-2025 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.discovery.mapper;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.evcc.internal.EvccBindingConstants;
import org.openhab.binding.evcc.internal.discovery.Utils;
import org.openhab.binding.evcc.internal.handler.EvccBridgeHandler;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* The {@link BatteryDiscoveryMapper} iis responsible for mapping the discovered batteries to discovery results
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class BatteryDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonArray batteries = state.getAsJsonArray(JSON_MEMBER_BATTERY);
if (batteries == null) {
return results;
}
for (int i = 0; i < batteries.size(); i++) {
JsonObject battery = batteries.get(i).getAsJsonObject();
String title = battery.has("title") ? battery.get("title").getAsString().toLowerCase(Locale.ROOT)
: "battery" + i;
ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_BATTERY, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(title)
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_INDEX, i)
.withProperty(PROPERTY_TYPE, PROPERTY_TYPE_BATTERY).withProperty(PROPERTY_TITLE, title)
.withRepresentationProperty(PROPERTY_TITLE).build();
results.add(result);
}
return results;
}
}
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2010-2025 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.discovery.mapper;
import java.util.Collection;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.evcc.internal.handler.EvccBridgeHandler;
import org.openhab.core.config.discovery.DiscoveryResult;
import com.google.gson.JsonObject;
/**
* The {@link EvccDiscoveryMapper} is responsible mapping discovered things to discovery results
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public interface EvccDiscoveryMapper {
/**
* This method returns the discovered things from the JSON response
*
* @param state the JSON response from the API
* @param bridgeHandler the bridge handler
* @return Collection of discovery results
*/
Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler);
}
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2010-2025 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.discovery.mapper;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.evcc.internal.EvccBindingConstants;
import org.openhab.binding.evcc.internal.discovery.Utils;
import org.openhab.binding.evcc.internal.handler.EvccBridgeHandler;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* The {@link LoadpointDiscoveryMapper} is responsible for mapping the discovered loadpoints to discovery results
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class LoadpointDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonArray loadpoints = state.getAsJsonArray(JSON_MEMBER_LOADPOINTS);
if (loadpoints == null) {
return results;
}
for (int i = 0; i < loadpoints.size(); i++) {
JsonObject lp = loadpoints.get(i).getAsJsonObject();
String title = lp.has("title") ? lp.get("title").getAsString().toLowerCase(Locale.ROOT) : "loadpoint" + i;
ThingUID uid = new ThingUID("DUMMY:DUMMY:DUMMY");
Map<String, Object> properties = new HashMap<>();
properties.put(PROPERTY_INDEX, i);
properties.put(PROPERTY_TITLE, title);
if (lp.has(title) && lp.get("chargerFeatureHeating").getAsBoolean()) {
uid = new ThingUID(EvccBindingConstants.THING_TYPE_HEATING, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
properties.put(PROPERTY_TYPE, PROPERTY_TYPE_HEATING);
} else {
uid = new ThingUID(EvccBindingConstants.THING_TYPE_LOADPOINT, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
properties.put(PROPERTY_TYPE, PROPERTY_TYPE_LOADPOINT);
}
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(title)
.withBridge(bridgeHandler.getThing().getUID()).withProperties(properties)
.withRepresentationProperty(PROPERTY_TITLE).build();
results.add(result);
}
return results;
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2010-2025 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.discovery.mapper;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.evcc.internal.EvccBindingConstants;
import org.openhab.binding.evcc.internal.discovery.Utils;
import org.openhab.binding.evcc.internal.handler.EvccBridgeHandler;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* The {@link PvDiscoveryMapper} is responsible for mapping the discovered photovoltaics to discovery results
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class PvDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonArray pvs = state.getAsJsonArray(JSON_MEMBER_PV);
if (pvs == null) {
return results;
}
for (int i = 0; i < pvs.size(); i++) {
JsonObject pv = pvs.get(i).getAsJsonObject();
String title = pv.has("title") ? pv.get("title").getAsString().toLowerCase(Locale.ROOT) : "pv" + i;
ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_PV, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(title)
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_INDEX, i)
.withProperty(PROPERTY_TYPE, PROPERTY_TYPE_PV).withProperty(PROPERTY_TITLE, title)
.withRepresentationProperty(PROPERTY_TITLE).build();
results.add(result);
}
return results;
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2010-2025 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.discovery.mapper;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.evcc.internal.EvccBindingConstants;
import org.openhab.binding.evcc.internal.handler.EvccBridgeHandler;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonObject;
/**
* The {@link SiteDiscoveryMapper} is responsible for mapping the discovered site to a discovery result
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class SiteDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
if (!state.has("siteTitle")) {
return results;
}
String siteTitle = state.get("siteTitle").getAsString();
ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_SITE, bridgeHandler.getThing().getUID(), "site");
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(siteTitle)
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_TYPE, PROPERTY_TYPE_SITE)
.withProperty(PROPERTY_SITE_TITLE, siteTitle).withRepresentationProperty(PROPERTY_SITE_TITLE).build();
results.add(result);
return results;
}
}
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2010-2025 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.discovery.mapper;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.evcc.internal.EvccBindingConstants;
import org.openhab.binding.evcc.internal.handler.EvccBridgeHandler;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonObject;
/**
* The {@link StatisticsDiscoveryMapper} is responsible for mapping the discovered statistics to discovery results
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class StatisticsDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonObject statistics = state.getAsJsonObject(JSON_MEMBER_STATISTICS);
if (statistics == null) {
return results;
}
ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_STATISTICS, bridgeHandler.getThing().getUID(),
"statistics");
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel("Statistics")
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_TYPE, PROPERTY_TYPE_STATISTICS)
.withRepresentationProperty(PROPERTY_TYPE).build();
results.add(result);
return results;
}
}
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2010-2025 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.discovery.mapper;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.JSON_MEMBER_VEHICLES;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.PROPERTY_ID;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.PROPERTY_TYPE;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.PROPERTY_TYPE_VEHICLE;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.evcc.internal.EvccBindingConstants;
import org.openhab.binding.evcc.internal.discovery.Utils;
import org.openhab.binding.evcc.internal.handler.EvccBridgeHandler;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* The {@link VehicleDiscoveryMapper} is responsible for mapping the discovered vehicles to discovery results
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class VehicleDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonObject vehicles = state.getAsJsonObject(JSON_MEMBER_VEHICLES);
if (vehicles == null) {
return results;
}
for (Map.Entry<String, JsonElement> entry : vehicles.entrySet()) {
JsonObject v = entry.getValue().getAsJsonObject();
String id = entry.getKey();
String title = v.has("title") ? v.get("title").getAsString() : id;
ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_VEHICLE, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(title)
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_TYPE, PROPERTY_TYPE_VEHICLE)
.withProperty(PROPERTY_ID, id).withRepresentationProperty(PROPERTY_ID).build();
results.add(result);
}
return results;
}
}
@@ -0,0 +1,396 @@
/*
* Copyright (c) 2010-2025 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.handler;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* The {@link EvccBaseThingHandler} is responsible for building a base class with common methods for the thing handlers
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public abstract class EvccBaseThingHandler extends BaseThingHandler implements EvccThingLifecycleAware {
private final Logger logger = LoggerFactory.getLogger(EvccBaseThingHandler.class);
private final ChannelTypeRegistry channelTypeRegistry;
private final Gson gson = new Gson();
protected @Nullable EvccBridgeHandler bridgeHandler;
protected boolean isInitialized = false;
protected String endpoint = "";
protected String smartCostType = "";
public EvccBaseThingHandler(Thing thing, ChannelTypeRegistry channelTypeRegistry) {
super(thing);
this.channelTypeRegistry = channelTypeRegistry;
}
protected void commonInitialize(JsonObject state) {
ThingBuilder builder = editThing();
for (Map.Entry<@Nullable String, @Nullable JsonElement> entry : state.entrySet()) {
String key = entry.getKey();
JsonElement value = entry.getValue();
if (null == key || value == null) {
continue;
}
String thingKey = getThingKey(key);
// Skip non-primitive values
if (!value.isJsonPrimitive()) {
continue;
}
createChannel(thingKey, builder, value);
}
updateThing(builder.build());
updateStatus(ThingStatus.ONLINE);
isInitialized = true;
Optional.ofNullable(bridgeHandler).ifPresentOrElse(handler -> handler.register(this),
() -> logger.error("No bridgeHandler present when initializing the thing"));
}
@Override
public void initialize() {
updateStatus(ThingStatus.UNKNOWN);
if (getBridge() instanceof Bridge bridge && bridge.getHandler() instanceof EvccBridgeHandler handler) {
bridgeHandler = handler;
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
}
}
@Override
public void dispose() {
Optional.ofNullable(bridgeHandler).ifPresent(handler -> handler.unregister(this));
isInitialized = false;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
String key = Utils.getKeyFromChannelUID(channelUID);
Optional.ofNullable(bridgeHandler).ifPresent(handler -> {
JsonObject state = getStateFromCachedState(handler.getCachedEvccState());
if (!state.isEmpty()) {
JsonElement value = state.get(key);
ItemTypeUnit typeUnit = getItemType(new ChannelTypeUID(BINDING_ID, channelUID.getId()));
if (null != value) {
setItemValue(typeUnit, channelUID, value);
}
}
});
}
}
private void createChannel(String thingKey, ThingBuilder builder, JsonElement value) {
ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, thingKey);
ItemTypeUnit typeUnit = getItemType(channelTypeUID);
String itemType = typeUnit.itemType;
if (!"Unknown".equals(itemType)) {
String label = getChannelLabel(thingKey);
Channel channel = ChannelBuilder.create(new ChannelUID(getThing().getUID(), thingKey)).withLabel(label)
.withType(channelTypeUID).withAcceptedItemType(itemType).build();
if (getThing().getChannels().stream().noneMatch(c -> c.getUID().equals(channel.getUID()))) {
builder.withChannel(channel);
}
} else {
String valString = Objects.requireNonNullElse(value.toString(), "Null");
logUnknownChannelXmlAsync(thingKey, "Hint for type: " + valString);
}
}
private String getChannelLabel(String thingKey) {
String returnValue = thingKey;
@Nullable
String tmp = Optional.ofNullable(bridgeHandler).map(handler -> {
String labelKey = "channel-type.evcc." + thingKey + ".label";
BundleContext ctx = FrameworkUtil.getBundle(EvccBridgeHandler.class).getBundleContext();
TranslationProvider tp = handler.getI18nProvider();
Locale locale = handler.getLocaleProvider().getLocale();
return tp.getText(ctx.getBundle(), labelKey, thingKey, locale);
}).orElse(thingKey);
if (null != tmp) {
returnValue = tmp;
}
return returnValue;
}
private ItemTypeUnit getItemType(ChannelTypeUID channelTypeUID) {
ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUID);
if (null != channelType) {
String itemType = channelType.getItemType();
if (null != itemType) {
Unit<?> unit = Utils.getUnitFromChannelType(itemType);
return new ItemTypeUnit(channelType, unit);
}
}
return new ItemTypeUnit(channelType, Units.ONE);
}
private void setItemValue(ItemTypeUnit itemTypeUnit, ChannelUID channelUID, JsonElement value) {
if (value.isJsonNull() || itemTypeUnit.itemType.isEmpty()) {
return;
}
switch (itemTypeUnit.itemType) {
case CoreItemFactory.NUMBER:
case NUMBER_CURRENCY:
case NUMBER_ENERGY_PRICE:
updateState(channelUID, new DecimalType(value.getAsDouble()));
break;
case NUMBER_DIMENSIONLESS:
case NUMBER_ELECTRIC_CURRENT:
case NUMBER_EMISSION_INTENSITY:
case NUMBER_ENERGY:
case NUMBER_LENGTH:
case NUMBER_POWER:
Double finalValue = "%".equals(itemTypeUnit.unitHint) ? value.getAsDouble() / 100 : value.getAsDouble();
if (channelUID.getId().contains("capacity") || "km".equals(itemTypeUnit.unitHint)) {
updateState(channelUID, new QuantityType<>(finalValue, itemTypeUnit.unit.multiply(1000)));
} else if ("Wh".equals(itemTypeUnit.unitHint)) {
updateState(channelUID, new QuantityType<>(finalValue, itemTypeUnit.unit.divide(1000)));
} else {
updateState(channelUID, new QuantityType<>(finalValue, itemTypeUnit.unit));
}
break;
case NUMBER_TIME:
updateState(channelUID, QuantityType.valueOf(value.getAsDouble() + " s"));
break;
case CoreItemFactory.DATETIME:
updateState(channelUID, new DateTimeType(value.getAsString()));
break;
case CoreItemFactory.STRING:
updateState(channelUID, new StringType(value.getAsString()));
break;
case CoreItemFactory.SWITCH:
updateState(channelUID, value.getAsBoolean() ? OnOffType.ON : OnOffType.OFF);
break;
default:
logUnknownChannelXmlAsync(channelUID.getId(), "Hint for type: " + value.toString());
}
}
protected String getThingKey(String key) {
Map<String, String> props = getThing().getProperties();
if ("batteryGridChargeLimit".equals(key) || "smartCostLimit".equals(key)) {
if ("co2".equals(smartCostType)) {
key += "Co2";
} else {
key += "Price";
}
}
return (props.get("type") + "-" + Utils.sanitizeChannelID(key));
}
@Override
public void updateFromEvccState(JsonObject state) {
if (!isInitialized || state.isEmpty()) {
return;
}
for (Map.Entry<@Nullable String, @Nullable JsonElement> entry : state.entrySet()) {
String key = entry.getKey();
JsonElement value = entry.getValue();
if (null == key || null == value || !value.isJsonPrimitive()) {
continue;
}
String thingKey = getThingKey(key);
ChannelUID channelUID = channelUID(thingKey);
Channel existingChannel = getThing().getChannel(channelUID.getId());
if (existingChannel == null) {
ThingBuilder builder = editThing();
createChannel(thingKey, builder, value);
updateThing(builder.build());
}
setItemValue(getItemType(new ChannelTypeUID(BINDING_ID, channelUID.getId())), channelUID, value);
}
updateStatus(ThingStatus.ONLINE);
}
protected void sendCommand(String url) {
Optional.ofNullable(bridgeHandler).ifPresent(handler -> {
HttpClient httpClient = handler.getHttpClient();
try {
ContentResponse response = httpClient.newRequest(url).timeout(5, TimeUnit.SECONDS)
.method(HttpMethod.POST).header(HttpHeader.ACCEPT, "application/json").send();
if (response.getStatus() == 200) {
logger.debug("Sending command was successful");
} else {
@Nullable
JsonObject responseJson = gson.fromJson(response.getContentAsString(), JsonObject.class);
Optional.ofNullable(responseJson).ifPresent(json -> {
if (json.has("error")) {
logger.debug("Sending command was unsuccessful, got this error:\n {}",
json.get("error").getAsString());
updateStatus(getThing().getStatus(), ThingStatusDetail.COMMUNICATION_ERROR,
json.get("error").getAsString());
} else {
updateStatus(getThing().getStatus(), ThingStatusDetail.COMMUNICATION_ERROR);
logger.warn("evcc API error: HTTP {}", response.getStatus());
}
});
}
} catch (Exception e) {
logger.warn("evcc bridge couldn't call the API", e);
}
});
}
private ChannelUID channelUID(String id) {
return new ChannelUID(getThing().getUID(), id);
}
@Override
public void bridgeStatusChanged(ThingStatusInfo statusInfo) {
switch (statusInfo.getStatus()) {
case OFFLINE:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
break;
case UNINITIALIZED:
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED);
break;
case ONLINE:
if (!isInitialized) {
Bridge bridge = getBridge();
if (bridge == null) {
break;
}
logger.debug("Bridge {} is ONLINE again, initialize evcc {} again...", bridge.getUID(),
getThing().getUID().getId());
initialize();
} else {
updateStatus(ThingStatus.ONLINE);
}
break;
default:
break;
}
}
public void logUnknownChannelXmlAsync(String key, String itemType) {
CompletableFuture.runAsync(() -> logUnknownChannelXml(key, itemType));
}
protected void logUnknownChannelXml(String key, String itemType) {
String xmlSnippet = String.format("""
<channel-type id="%s">
<item-type unitHint="">%s</item-type>
<label>%s</label>
<description>Autogenerated placeholder</description>
<state readOnly="true"></state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
""", key, itemType, Utils.capitalizeWords(key));
Path filePath = Paths.get(System.getProperty("user.dir"), "evcc", "unknown-channels.xml");
try {
// Ensure parent directory exists
if (!Files.exists(filePath.getParent())) {
Files.createDirectories(filePath.getParent());
}
// Check if file exists and contains the ID
if (Files.exists(filePath)) {
String content = Files.readString(filePath, StandardCharsets.UTF_8);
String idPattern = String.format("id=\"%s\"", key);
if (content.contains(idPattern)) {
logger.trace("Channel ID '{}' already exists in file.", key);
return;
}
}
// Append the snippet if it's not already present
Files.writeString(filePath, xmlSnippet, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
logger.debug("Unknown channel definition written to file: {}", filePath.toAbsolutePath());
} catch (IOException e) {
logger.error("Failed to write unknown channel definition to file", e);
}
}
private static class ItemTypeUnit {
private final Unit<?> unit;
private final String unitHint;
private final String itemType;
public ItemTypeUnit(@Nullable ChannelType type, @Nullable Unit<?> unit) {
if (null == type) {
unitHint = "";
itemType = "Unknown";
} else {
String tmp = type.getUnitHint();
unitHint = null != tmp ? tmp : "";
tmp = type.getItemType();
itemType = null != tmp ? tmp : "Unknown";
}
this.unit = Objects.requireNonNullElse(unit, Units.ONE);
}
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2010-2025 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.handler;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import com.google.gson.JsonObject;
/**
* The {@link EvccBatteryHandler} is responsible for fetching the data from the API response for Battery things
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class EvccBatteryHandler extends EvccBaseThingHandler {
private final int index;
public EvccBatteryHandler(Thing thing, ChannelTypeRegistry channelTypeRegistry) {
super(thing, channelTypeRegistry);
Map<String, String> props = thing.getProperties();
String indexString = props.getOrDefault(PROPERTY_INDEX, "0");
index = Integer.parseInt(indexString);
}
@Override
public void initialize() {
super.initialize();
Optional.ofNullable(bridgeHandler).ifPresent(handler -> {
JsonObject stateOpt = handler.getCachedEvccState();
if (stateOpt.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}
JsonObject state = stateOpt.getAsJsonArray(JSON_MEMBER_BATTERY).get(index).getAsJsonObject();
commonInitialize(state);
});
}
@Override
public void updateFromEvccState(JsonObject state) {
state = state.has(JSON_MEMBER_BATTERY) ? state.getAsJsonArray(JSON_MEMBER_BATTERY).get(index).getAsJsonObject()
: new JsonObject();
super.updateFromEvccState(state);
}
@Override
public JsonObject getStateFromCachedState(JsonObject state) {
return state.getAsJsonArray(JSON_MEMBER_BATTERY).get(index).getAsJsonObject();
}
}
@@ -0,0 +1,181 @@
/*
* Copyright (c) 2010-2025 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.handler;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.openhab.binding.evcc.internal.EvccConfiguration;
import org.openhab.binding.evcc.internal.discovery.EvccDiscoveryService;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
/**
* The {@link EvccBridgeHandler} is responsible for creating the bridge and thing
* handlers.
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class EvccBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(EvccBridgeHandler.class);
private final Gson gson = new Gson();
private final HttpClient httpClient;
private final TranslationProvider i18nProvider;
private final LocaleProvider localeProvider;
private final CopyOnWriteArrayList<EvccThingLifecycleAware> listeners = new CopyOnWriteArrayList<>();
private @Nullable ScheduledFuture<?> pollJob;
private volatile JsonObject lastState = new JsonObject();
private String endpoint = "";
public EvccBridgeHandler(Bridge bridge, HttpClientFactory httpClientFactory, TranslationProvider i18nProvider,
LocaleProvider localeProvider) {
super(bridge);
httpClient = httpClientFactory.getCommonHttpClient();
this.i18nProvider = i18nProvider;
this.localeProvider = localeProvider;
}
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Set.of(EvccDiscoveryService.class);
}
@Override
public void initialize() {
EvccConfiguration config = getConfigAs(EvccConfiguration.class);
endpoint = config.scheme + "://" + config.host + ":" + config.port + "/api/state";
startPolling(config.pollInterval);
fetchEvccState().ifPresent(state -> {
this.lastState = state;
updateStatus(ThingStatus.ONLINE);
});
}
@Override
public void dispose() {
Optional.ofNullable(pollJob).ifPresent(job -> job.cancel(true));
listeners.clear();
}
public HttpClient getHttpClient() {
return httpClient;
}
public String getBaseURL() {
return endpoint.substring(0, endpoint.lastIndexOf("/"));
}
public TranslationProvider getI18nProvider() {
return i18nProvider;
}
public LocaleProvider getLocaleProvider() {
return localeProvider;
}
private void startPolling(int refreshInterval) {
if (refreshInterval <= 0) {
refreshInterval = 30;
}
pollJob = scheduler.scheduleWithFixedDelay(() -> fetchEvccState().ifPresent(state -> {
if (!state.isEmpty() && state.has("siteTitle")) {
notifyListeners(state);
this.lastState = state;
}
}), refreshInterval, refreshInterval, TimeUnit.SECONDS);
}
public Optional<JsonObject> fetchEvccState() {
try {
ContentResponse response = httpClient.newRequest(endpoint).timeout(5, TimeUnit.SECONDS)
.header(HttpHeader.ACCEPT, "application/json").send();
if (response.getStatus() == 200) {
@Nullable
JsonObject returnValue = gson.fromJson(response.getContentAsString(), JsonObject.class);
if (returnValue != null && !(returnValue.isEmpty() || returnValue.isJsonNull())) {
updateStatus(ThingStatus.ONLINE);
JsonObject result = returnValue.has("result") ? returnValue.getAsJsonObject("result") : returnValue;
return Optional.of(result);
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
Integer.toString(response.getStatus()));
}
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getLocalizedMessage());
}
return Optional.empty();
}
private void notifyListeners(JsonObject state) {
for (EvccThingLifecycleAware listener : listeners) {
try {
listener.updateFromEvccState(state);
} catch (Exception e) {
if (listener instanceof BaseThingHandler handler) {
logger.warn("Listener {} couldn't parse evcc state", handler.getThing().getUID(), e);
} else {
logger.debug("Listener {} is not instance of BaseThingHandlder", listener, e);
}
}
}
}
public JsonObject getCachedEvccState() {
return lastState;
}
public void register(EvccThingLifecycleAware handler) {
listeners.addIfAbsent(handler);
Optional.of(lastState).ifPresent(handler::updateFromEvccState);
}
public void unregister(EvccThingLifecycleAware handler) {
listeners.remove(handler);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
; // No commands to handle!
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2010-2025 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.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import com.google.gson.JsonObject;
/**
* The {@link EvccHeatingHandler} is responsible for fetching the data from the API response for Heating things
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class EvccHeatingHandler extends EvccLoadpointHandler {
public EvccHeatingHandler(Thing thing, ChannelTypeRegistry channelTypeRegistry) {
super(thing, channelTypeRegistry);
}
@Override
public void initialize() {
super.initialize();
}
@Override
public void updateFromEvccState(JsonObject state) {
super.updateFromEvccState(state);
}
}
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2010-2025 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.handler;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonObject;
/**
* The {@link EvccLoadpointHandler} is responsible for fetching the data from the API response for Loadpoint things
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class EvccLoadpointHandler extends EvccBaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(EvccLoadpointHandler.class);
protected final int index;
private int[] version = {};
public EvccLoadpointHandler(Thing thing, ChannelTypeRegistry channelTypeRegistry) {
super(thing, channelTypeRegistry);
Map<String, String> props = thing.getProperties();
String indexString = props.getOrDefault(PROPERTY_INDEX, "0");
index = Integer.parseInt(indexString);
}
@Override
public void initialize() {
super.initialize();
Optional.ofNullable(bridgeHandler).ifPresent(handler -> {
endpoint = handler.getBaseURL() + API_PATH_LOADPOINTS + "/" + (index + 1);
JsonObject stateOpt = handler.getCachedEvccState();
if (stateOpt.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}
JsonObject state = stateOpt.getAsJsonArray(JSON_MEMBER_LOADPOINTS).get(index).getAsJsonObject();
modifyJSON(state);
commonInitialize(state);
});
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof State) {
String datapoint = Utils.getKeyFromChannelUID(channelUID).toLowerCase();
// Backwardscompatibility for phasesConfigured
if ("configuredPhases".equals(datapoint) && version[0] == 0 && version[1] < 200) {
datapoint = "phases";
}
// Special Handling for enable and disable endpoints
if (datapoint.contains("enable")) {
datapoint += "/enable/" + datapoint.replace("enable", "");
} else if (datapoint.contains("disable")) {
datapoint += "/disable/" + datapoint.replace("disable", "");
}
String value = "";
if (command instanceof OnOffType) {
value = command == OnOffType.ON ? "true" : "false";
} else {
value = command.toString();
if (value.contains(" ")) {
value = value.substring(0, command.toString().indexOf(" "));
}
}
String url = endpoint + "/" + datapoint + "/" + value;
logger.debug("Sending command to this url: {}", url);
sendCommand(url);
} else {
super.handleCommand(channelUID, command);
}
}
@Override
public void updateFromEvccState(JsonObject state) {
version = Utils.convertVersionStringToIntArray(state.get("version").getAsString().split(" ")[0]);
state = state.getAsJsonArray(JSON_MEMBER_LOADPOINTS).get(index).getAsJsonObject();
modifyJSON(state);
super.updateFromEvccState(state);
}
public void modifyJSON(JsonObject state) {
// This is for backward compatibility with older evcc versions
if (state.has("chargeCurrent")) {
state.addProperty("offeredCurrent", state.get("chargeCurrent").getAsDouble());
state.remove("chargeCurrent");
}
if (state.has("vehiclePresent")) {
state.add("connected", state.get("vehiclePresent"));
}
if (state.has("enabled")) {
state.add("charging", state.get("enabled"));
}
if (state.has("phases")) {
state.add("phasesConfigured", state.get("phases"));
}
}
@Override
public JsonObject getStateFromCachedState(JsonObject state) {
return state.has(JSON_MEMBER_LOADPOINTS)
? state.getAsJsonArray(JSON_MEMBER_LOADPOINTS).get(index).getAsJsonObject()
: new JsonObject();
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2010-2025 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.handler;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import com.google.gson.JsonObject;
/**
* The {@link EvccPvHandler} is responsible for fetching the data from the API response for PV things
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class EvccPvHandler extends EvccBaseThingHandler {
private final int index;
public EvccPvHandler(Thing thing, ChannelTypeRegistry channelTypeRegistry) {
super(thing, channelTypeRegistry);
Map<String, String> props = thing.getProperties();
String indexString = props.getOrDefault(PROPERTY_INDEX, "0");
index = Integer.parseInt(indexString);
}
@Override
public void initialize() {
super.initialize();
Optional.ofNullable(bridgeHandler).ifPresent(handler -> {
JsonObject stateOpt = handler.getCachedEvccState();
if (stateOpt.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}
JsonObject state = stateOpt.getAsJsonArray(JSON_MEMBER_PV).get(index).getAsJsonObject();
commonInitialize(state);
});
}
@Override
public void updateFromEvccState(JsonObject state) {
state = state.getAsJsonArray(JSON_MEMBER_PV).get(index).getAsJsonObject();
super.updateFromEvccState(state);
}
@Override
public JsonObject getStateFromCachedState(JsonObject state) {
return state.has(JSON_MEMBER_PV) ? state.getAsJsonArray(JSON_MEMBER_PV).get(index).getAsJsonObject()
: new JsonObject();
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2010-2025 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.handler;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonObject;
/**
* The {@link EvccSiteHandler} is responsible for fetching the data from the API response for Site things
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class EvccSiteHandler extends EvccBaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(EvccSiteHandler.class);
public EvccSiteHandler(Thing thing, ChannelTypeRegistry channelTypeRegistry) {
super(thing, channelTypeRegistry);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof State) {
String datapoint = Utils.getKeyFromChannelUID(channelUID).toLowerCase();
String value = "";
if (command instanceof OnOffType) {
value = command == OnOffType.ON ? "true" : "false";
} else {
value = command.toString();
if (value.contains(" ")) {
value = value.substring(0, command.toString().indexOf(" "));
}
}
String url = endpoint + "/" + datapoint + "/" + value;
logger.debug("Sending command to this url: {}", url);
sendCommand(url);
} else {
super.handleCommand(channelUID, command);
}
}
@Override
public void updateFromEvccState(JsonObject state) {
if (state.has("gridConfigured")) {
modifyJSON(state);
}
super.updateFromEvccState(state);
}
@Override
public void initialize() {
super.initialize();
Optional.ofNullable(bridgeHandler).ifPresent(handler -> {
endpoint = handler.getBaseURL();
JsonObject state = handler.getCachedEvccState();
if (state.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}
// Set the smart cost type
if (state.has("smartCostType")) {
smartCostType = state.get("smartCostType").getAsString();
}
if (state.has("gridConfigured")) {
modifyJSON(state);
}
commonInitialize(state);
});
}
private void modifyJSON(JsonObject state) {
state.add("gridPower", state.getAsJsonObject("grid").get("power"));
state.remove("gridConfigured");
}
@Override
public JsonObject getStateFromCachedState(JsonObject state) {
return state;
}
}
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2010-2025 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.handler;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.ChannelGroupUID;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* The {@link EvccStatisticsHandler} is responsible for fetching the data from the API response for Site things
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class EvccStatisticsHandler extends EvccBaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(EvccStatisticsHandler.class);
public EvccStatisticsHandler(Thing thing, ChannelTypeRegistry channelTypeRegistry) {
super(thing, channelTypeRegistry);
}
@Override
public void initialize() {
super.initialize();
Optional.ofNullable(bridgeHandler).ifPresent(handler -> {
handler.register(this);
updateStatus(ThingStatus.ONLINE);
isInitialized = true;
});
}
@Override
public JsonObject getStateFromCachedState(JsonObject state) {
return new JsonObject();
}
@Override
public void updateFromEvccState(JsonObject state) {
state = state.has(JSON_MEMBER_STATISTICS) ? state.getAsJsonObject(JSON_MEMBER_STATISTICS) : new JsonObject();
for (String statisticsKey : state.keySet()) {
JsonObject statistic = state.getAsJsonObject(statisticsKey);
logger.debug("Extracting statistics for {}", statisticsKey);
for (Map.Entry<@Nullable String, @Nullable JsonElement> entry : statistic.entrySet()) {
String key = entry.getKey();
JsonElement value = entry.getValue();
if (null != key && null != value) {
ChannelGroupUID channelGroupUID = new ChannelGroupUID(thing.getUID(),
Utils.sanitizeChannelID(statisticsKey));
if ("chargedKWh".equals(key)) {
key = "chargedEnergy";
}
ChannelUID channelUID = new ChannelUID(channelGroupUID, Utils.sanitizeChannelID(key));
updateState(channelUID, new DecimalType(value.getAsDouble()));
}
}
}
}
}
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2010-2025 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.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.JsonObject;
/**
* The {@link EvccThingLifecycleAware} is responsible for sharing the evcc api response
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public interface EvccThingLifecycleAware {
/**
* This method shall update the channels from the JSON received from the evcc API
*
* @param state the responded JSON
*/
void updateFromEvccState(JsonObject state);
/**
* This method shall return the to the thing corresponding JSON object
*
* @param state the cached API JSON response
* @return to the thing corresponding JSON object
*/
JsonObject getStateFromCachedState(JsonObject state);
}
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2010-2025 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.handler;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonObject;
/**
* The {@link EvccVehicleHandler} is responsible for fetching the data from the API response for Vehicle things
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class EvccVehicleHandler extends EvccBaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(EvccVehicleHandler.class);
private final @Nullable String vehicleId;
private String endpoint = "";
public EvccVehicleHandler(Thing thing, ChannelTypeRegistry channelTypeRegistry) {
super(thing, channelTypeRegistry);
vehicleId = thing.getProperties().get(PROPERTY_ID);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof State) {
String datapoint = Utils.getKeyFromChannelUID(channelUID).toLowerCase();
String value = command.toString();
if (value.contains(" ")) {
value = value.substring(0, command.toString().indexOf(" "));
}
String url = endpoint + "/" + vehicleId + "/" + datapoint + "/" + value;
logger.debug("Sending command to this url: {}", url);
sendCommand(url);
} else {
super.handleCommand(channelUID, command);
}
}
@Override
public void updateFromEvccState(JsonObject state) {
state = state.getAsJsonObject(JSON_MEMBER_VEHICLES).getAsJsonObject(vehicleId);
super.updateFromEvccState(state);
}
@Override
public void initialize() {
super.initialize();
Optional.ofNullable(bridgeHandler).ifPresent(handler -> {
endpoint = handler.getBaseURL() + API_PATH_VEHICLES;
JsonObject stateOpt = handler.getCachedEvccState();
if (stateOpt.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}
JsonObject state = stateOpt.getAsJsonObject(JSON_MEMBER_VEHICLES).getAsJsonObject(vehicleId);
commonInitialize(state);
});
}
@Override
public JsonObject getStateFromCachedState(JsonObject state) {
return state.has(JSON_MEMBER_VEHICLES) ? state.getAsJsonObject(JSON_MEMBER_VEHICLES).getAsJsonObject(vehicleId)
: new JsonObject();
}
}
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2010-2025 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.handler;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.NUMBER_DIMENSIONLESS;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.NUMBER_ELECTRIC_CURRENT;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.NUMBER_EMISSION_INTENSITY;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.NUMBER_ENERGY;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.NUMBER_LENGTH;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.NUMBER_POWER;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.NUMBER_TIME;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
/**
* The {@link Utils} provides utility methods
*
* @author Marcel Goerentz - Initial contribution
*/
@NonNullByDefault
public class Utils {
private static final Map<String, Unit<?>> UNIT_MAP = Map.of(NUMBER_LENGTH, SIUnits.METRE, NUMBER_POWER, Units.WATT,
NUMBER_ENERGY, Units.WATT_HOUR, NUMBER_TIME, Units.SECOND, NUMBER_ELECTRIC_CURRENT, Units.AMPERE,
NUMBER_DIMENSIONLESS, Units.ONE, NUMBER_EMISSION_INTENSITY, SIUnits.GRAM.divide(Units.KILOWATT_HOUR));
/**
* This method retrieves the unit for the corresponding item type
*
* @param itemType the unit will be received for
* @return Unit<?> that is mapped to the item type
*/
public static Unit<?> getUnitFromChannelType(String itemType) {
Unit<?> unit = UNIT_MAP.get(itemType);
return Objects.requireNonNullElse(unit, Units.ONE);
}
/**
* This method will capitalize the words of a given string
*
* @param input string containing hyphenized words
* @return A string with spaces instead of hyphens and the first letter of each word is capitalized
*/
public static String capitalizeWords(String input) {
String[] allParts = input.split("-");
String[] parts = Arrays.stream(allParts, 1, allParts.length).toArray(String[]::new);
StringJoiner joiner = new StringJoiner(" ");
for (String part : parts) {
if (!part.isEmpty()) {
joiner.add(Character.toUpperCase(part.charAt(0)) + part.substring(1));
}
}
return joiner.toString();
}
/**
* This method will sanitize a given string for a channel ID
*
* @param input camel case string
* @return A string that contains hyphens
*/
public static String sanitizeChannelID(String input) {
return input.replaceAll("(?<!^)(?=[A-Z])", "-").toLowerCase();
}
/**
* This method will get the key from a ChannelUID
*
* @param channelUID which the key shall be retrieved from
* @return the key in camel case
*/
public static String getKeyFromChannelUID(ChannelUID channelUID) {
String[] split = channelUID.getIdWithoutGroup().split("-");
String[] parts = Arrays.stream(split, 1, split.length).toArray(String[]::new);
StringBuilder camelCase = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
if (i == 0) {
camelCase.append(part.toLowerCase());
} else {
camelCase.append(part.substring(0, 1).toUpperCase());
camelCase.append(part.substring(1).toLowerCase());
}
}
return camelCase.toString();
}
/**
* This method will convert the version string into an int array
* If the input is blank, the method will return 0, 0, 0
*
* @param input version string
* @return int array of size 3
*/
public static int[] convertVersionStringToIntArray(String input) {
if (input.isEmpty()) {
return new int[] { 0, 0, 0 };
}
return Arrays.stream(input.split("\\.")).mapToInt(Integer::parseInt).toArray();
}
}
@@ -5,189 +5,386 @@ addon.evcc.description = This is the binding for evcc (electric vehicle charging
# thing types
thing-type.evcc.device.label = evcc installation
thing-type.evcc.device.description = A running evcc installation
thing-type.evcc.battery.label = Battery
thing-type.evcc.battery.description = A battery configured in your evcc instance
thing-type.evcc.heating.label = Heating - Loadpoint
thing-type.evcc.heating.description = A heating loadpoint configured in your evcc instance
thing-type.evcc.loadpoint.label = Loadpoint
thing-type.evcc.loadpoint.description = A loadpoint configured in your evcc instance
thing-type.evcc.pv.label = Photovoltaic
thing-type.evcc.pv.description = A photovoltaic system configured in your evcc instance
thing-type.evcc.server.label = Server
thing-type.evcc.server.description = Connection to evcc instance
thing-type.evcc.site.label = Site
thing-type.evcc.site.description = The site configured in your evcc instance
thing-type.evcc.statistics.label = Statistics
thing-type.evcc.statistics.description = Statistics about your evcc instance
thing-type.evcc.statistics.group.30d.label = Last 30 Days
thing-type.evcc.statistics.group.30d.description = Statistics from the last 30 days
thing-type.evcc.statistics.group.365d.label = Last 365 Days
thing-type.evcc.statistics.group.365d.description = Statistics from the last 365days
thing-type.evcc.statistics.group.this-year.label = Last Year
thing-type.evcc.statistics.group.this-year.description = Statistics from the last year
thing-type.evcc.statistics.group.total.label = Total
thing-type.evcc.statistics.group.total.description = Total statistics
thing-type.evcc.vehicle.label = Vehicle
thing-type.evcc.vehicle.description = A vehicle configured in your evcc instance
# thing types config
thing-type.config.evcc.device.refreshInterval.label = Refresh Interval
thing-type.config.evcc.device.refreshInterval.description = Interval the status is polled in seconds.
thing-type.config.evcc.device.url.label = URL
thing-type.config.evcc.device.url.description = URL of evcc web UI, e.g. https://demo.evcc.io
thing-type.config.evcc.server.host.label = Hostname or IP
thing-type.config.evcc.server.host.description = Address of your evcc instance, for example evcc.local or 192.168.1.2
thing-type.config.evcc.server.pollInterval.label = Polling Interval
thing-type.config.evcc.server.pollInterval.description = How often should the state be polled? (in seconds, default is 30s)
thing-type.config.evcc.server.port.label = Port
thing-type.config.evcc.server.port.description = HTTP-Port of your instance (Default: 7070)
thing-type.config.evcc.server.scheme.label = Protocol Scheme
thing-type.config.evcc.server.scheme.description = The protocol scheme that should be used to connect to your instance
thing-type.config.evcc.server.scheme.option.http = HTTP
thing-type.config.evcc.server.scheme.option.https = HTTPS
# channel group types
channel-group-type.evcc.general.label = General Data
channel-group-type.evcc.loadpoint0.label = Loadpoint 0
channel-group-type.evcc.loadpoint0current.label = Loadpoint 0: Current
channel-group-type.evcc.loadpoint1.label = Loadpoint 1
channel-group-type.evcc.loadpoint1current.label = Loadpoint 1: Current
channel-group-type.evcc.loadpoint2.label = Loadpoint 2
channel-group-type.evcc.loadpoint2current.label = Loadpoint 2: Current
channel-group-type.evcc.loadpoint3.label = Loadpoint 3
channel-group-type.evcc.loadpoint3current.label = Loadpoint 3: Current
channel-group-type.evcc.loadpoint4.label = Loadpoint 4
channel-group-type.evcc.loadpoint4current.label = Loadpoint 4: Current
channel-group-type.evcc.loadpoint5.label = Loadpoint 5
channel-group-type.evcc.loadpoint5current.label = Loadpoint 5: Current
channel-group-type.evcc.loadpoint6.label = Loadpoint 6
channel-group-type.evcc.loadpoint6current.label = Loadpoint 6: Current
channel-group-type.evcc.loadpoint7.label = Loadpoint 7
channel-group-type.evcc.loadpoint7current.label = Loadpoint 7: Current
channel-group-type.evcc.loadpoint8.label = Loadpoint 8
channel-group-type.evcc.loadpoint8current.label = Loadpoint 8: Current
channel-group-type.evcc.loadpoint9.label = Loadpoint 9
channel-group-type.evcc.loadpoint9current.label = Loadpoint 9: Current
channel-group-type.evcc.statistics-group.label = Statistics Group
# channel types
channel-type.evcc.activePhases.label = Charging Active Phases
channel-type.evcc.activePhases.description = Current number of active phases while charging
channel-type.evcc.availableVersion.label = Available Version
channel-type.evcc.availableVersion.description = Available evcc update version
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.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
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 limit SoC is reached
channel-type.evcc.chargeRemainingEnergy.label = Charging Remaining Energy
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.chargerFeatureHeating.label = Charger Feature: Heating
channel-type.evcc.chargerFeatureHeating.description = 'True' for heating device: State of Charge in Degree instead of Percent
channel-type.evcc.chargerFeatureHeating.state.option.ON = True
channel-type.evcc.chargerFeatureHeating.state.option.OFF = False
channel-type.evcc.chargerFeatureIntegratedDevice.label = Charger Feature: Integrated Device
channel-type.evcc.chargerFeatureIntegratedDevice.description = 'True' for integrated device: Operate without a "vehicle" (e.g. heat pump, eBike)
channel-type.evcc.chargerFeatureIntegratedDevice.state.option.ON = True
channel-type.evcc.chargerFeatureIntegratedDevice.state.option.OFF = False
channel-type.evcc.charging.label = Charging State
channel-type.evcc.charging.description = Loadpoint is currently charging
channel-type.evcc.charging.state.option.ON = Charging
channel-type.evcc.charging.state.option.OFF = Not charging
channel-type.evcc.effectiveLimitSoC.label = Effective Charging Limit
channel-type.evcc.effectiveLimitSoC.description = Effective state of charge (SoC) until which the vehicle will be charged
channel-type.evcc.effectiveLimitTemperature.label = Effective Charging Limit Temperature
channel-type.evcc.effectiveLimitTemperature.description = Effective Temperature until which the heating device will be charged
channel-type.evcc.enabled.label = Charging Enabled
channel-type.evcc.enabled.description = Charging enabled (mode not "off")
channel-type.evcc.enabled.state.option.ON = Enabled
channel-type.evcc.enabled.state.option.OFF = Disabled
channel-type.evcc.gridPower.label = Grid Power
channel-type.evcc.gridPower.description = Current power from grid (negative means feed-in)
channel-type.evcc.heatingCapacity.label = Heating Capacity
channel-type.evcc.heatingCapacity.description = Capacity of heating device
channel-type.evcc.heatingLimitTemperature.label = Charging Temperature Limit
channel-type.evcc.heatingLimitTemperature.description = Until which Temperature should the specific heating device be charged
channel-type.evcc.heatingMinTemperature.label = Heating Min Temperature
channel-type.evcc.heatingMinTemperature.description = Minimum Temperature a heating device should have
channel-type.evcc.heatingPlanEnabled.label = Heating Plan Enabled
channel-type.evcc.heatingPlanEnabled.description = Plan for charging enabled
channel-type.evcc.heatingPlanEnabled.state.option.ON = Enabled
channel-type.evcc.heatingPlanEnabled.state.option.OFF = Disabled
channel-type.evcc.heatingPlanTemperature.label = Heating Plan Temperature
channel-type.evcc.heatingPlanTemperature.description = Until which Temperature should heating device be charged in plan
channel-type.evcc.heatingPlanTime.label = Heating Plan Time
channel-type.evcc.heatingPlanTime.description = When the plan Temperature should be reached
channel-type.evcc.heatingTitle.label = Heating Title
channel-type.evcc.heatingTitle.description = Title of heating device
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.limitTemperature.label = Charging Limit Temperature
channel-type.evcc.limitTemperature.description = Until which Temperature should the heating device 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.mode.label = Charging Mode
channel-type.evcc.mode.description = Charging mode: "off", "now", "minpv", "pv"
channel-type.evcc.mode.state.option.off = Off
channel-type.evcc.mode.state.option.now = Fast
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.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
channel-type.evcc.vehicleCapacity.description = Capacity of EV battery
channel-type.evcc.vehicleConnected.label = Vehicle Connected
channel-type.evcc.vehicleConnected.description = Whether vehicle is connected to loadpoint
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
channel-type.evcc.vehiclePresent.state.option.OFF = No data access
channel-type.evcc.vehicleRange.label = Vehicle Range
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.vehicleTemperature.label = Temperature
channel-type.evcc.vehicleTemperature.description = Current Temperature of the heating device
channel-type.evcc.vehicleTitle.label = Vehicle Title
channel-type.evcc.vehicleTitle.description = Title of vehicle
channel-type.evcc.version.label = Version
channel-type.evcc.version.description = Current evcc version
channel-type.evcc.avg-co2-type.label = Average C02
channel-type.evcc.avg-price-type.label = Average Price
channel-type.evcc.battery-capacity.label = Battery Capacity
channel-type.evcc.battery-capacity.description = Capacity of this battery
channel-type.evcc.battery-controllable.label = Battery Controllable
channel-type.evcc.battery-controllable.description = Indicates whether this battery is controllable or not
channel-type.evcc.battery-controllable.state.option.ON = Controllable
channel-type.evcc.battery-controllable.state.option.OFF = Not Controllable
channel-type.evcc.battery-power.label = Battery Power
channel-type.evcc.battery-power.description = Current power from this battery
channel-type.evcc.battery-soc.label = Battery SoC
channel-type.evcc.battery-soc.description = Current state of charge of this battery
channel-type.evcc.battery-title.label = Title
channel-type.evcc.battery-title.description = Battery name displayed in the UI
channel-type.evcc.charged-energy-type.label = Charged Energy
channel-type.evcc.loadpoint-battery-boost.label = Battery Boost
channel-type.evcc.loadpoint-battery-boost.description = Controls and indicates whether battery boost is active or not
channel-type.evcc.loadpoint-battery-boost.state.option.ON = Activated
channel-type.evcc.loadpoint-battery-boost.state.option.OFF = Deactivated
channel-type.evcc.loadpoint-charge-duration.label = Charging Duration
channel-type.evcc.loadpoint-charge-duration.description = Time passed since start of the charging
channel-type.evcc.loadpoint-charge-power.label = Charging Power
channel-type.evcc.loadpoint-charge-power.description = Current power of charging
channel-type.evcc.loadpoint-charge-remaining-duration.label = Charging Remaining Duration
channel-type.evcc.loadpoint-charge-remaining-duration.description = Remaining duration until limit SoC is reached
channel-type.evcc.loadpoint-charge-remaining-energy.label = Charging Remaining Energy
channel-type.evcc.loadpoint-charge-remaining-energy.description = Remaining energy until limit SoC is reached
channel-type.evcc.loadpoint-charge-total-import.label = Charge Total Import
channel-type.evcc.loadpoint-charge-total-import.description = Total imported energy
channel-type.evcc.loadpoint-charged-energy.label = Charged Energy
channel-type.evcc.loadpoint-charged-energy.description = Energy charged since plugged-in
channel-type.evcc.loadpoint-charger-feature-heating.label = Heating
channel-type.evcc.loadpoint-charger-feature-heating.description = Indicates whether the loadpoint is configured for a heating device
channel-type.evcc.loadpoint-charger-feature-heating.state.option.ON = True
channel-type.evcc.loadpoint-charger-feature-heating.state.option.OFF = False
channel-type.evcc.loadpoint-charger-feature-integrated-device.label = Integrated Device
channel-type.evcc.loadpoint-charger-feature-integrated-device.description = Loadpoint has an integrated device and runs without a vehicle (e.g. heatpump, e-bike)
channel-type.evcc.loadpoint-charger-feature-integrated-device.state.option.ON = True
channel-type.evcc.loadpoint-charger-feature-integrated-device.state.option.OFF = False
channel-type.evcc.loadpoint-charger-icon.label = Charger Icon
channel-type.evcc.loadpoint-charger-icon.description = Icon used in the UI for this charger
channel-type.evcc.loadpoint-charger-phases1p3p.label = Phase Switching
channel-type.evcc.loadpoint-charger-phases1p3p.description = Indicates that the loadpoint can switch between 1 and 3 phases
channel-type.evcc.loadpoint-charger-phases1p3p.state.option.ON = True
channel-type.evcc.loadpoint-charger-phases1p3p.state.option.OFF = False
channel-type.evcc.loadpoint-charger-single-phase.label = 1 Phase Charging
channel-type.evcc.loadpoint-charger-single-phase.description = Indicates that the charger can only charge with 1 phase
channel-type.evcc.loadpoint-charger-single-phase.state.option.ON = True
channel-type.evcc.loadpoint-charger-single-phase.state.option.OFF = False
channel-type.evcc.loadpoint-charger-status-reason.label = Charger Status Reason
channel-type.evcc.loadpoint-charger-status-reason.description = Hint why the charging has not started yet
channel-type.evcc.loadpoint-charger-status-reason.state.option.unknown = Unknown
channel-type.evcc.loadpoint-charger-status-reason.state.option.waitingforauthorization = Waiting for authorization
channel-type.evcc.loadpoint-charger-status-reason.state.option.disconnectrequired = Disconnect required
channel-type.evcc.loadpoint-charging.label = Charging State
channel-type.evcc.loadpoint-charging.description = Indicates whether the loadpoint is charging or not
channel-type.evcc.loadpoint-charging.state.option.ON = Charging
channel-type.evcc.loadpoint-charging.state.option.OFF = Not charging
channel-type.evcc.loadpoint-connected-duration.label = Vehicle Conn. Duration
channel-type.evcc.loadpoint-connected-duration.description = Duration the vehicle is connected to the loadpoint
channel-type.evcc.loadpoint-connected.label = Vehicle Connected
channel-type.evcc.loadpoint-connected.description = Indicates whether a vehicle is connected or not
channel-type.evcc.loadpoint-connected.state.option.ON = Connected
channel-type.evcc.loadpoint-connected.state.option.OFF = Not connected
channel-type.evcc.loadpoint-disable-delay.label = Disable Delay
channel-type.evcc.loadpoint-disable-delay.description = Delay in PV mode before stopping the charging if not enough power is available
channel-type.evcc.loadpoint-disable-threshold.label = Disable Threshold
channel-type.evcc.loadpoint-disable-threshold.description = Threshold for the disable delay (negative values means feed in)
channel-type.evcc.loadpoint-effective-limit-soc.label = Active Charging Limit
channel-type.evcc.loadpoint-effective-limit-soc.description = Active state of charge the vehicle will be charged to
channel-type.evcc.loadpoint-effective-limit-temperature.label = Effective Charging Limit Temperature
channel-type.evcc.loadpoint-effective-limit-temperature.description = Effective Temperature until which the heating device will be charged
channel-type.evcc.loadpoint-effective-max-current.label = Active Maximum Current
channel-type.evcc.loadpoint-effective-max-current.description = Effective maximum amperage per connected phase that can be provided to the vehicle
channel-type.evcc.loadpoint-effective-min-current.label = Active Minimum Current
channel-type.evcc.loadpoint-effective-min-current.description = Effective minimum amperage per connected phase that will be provided to the vehicle
channel-type.evcc.loadpoint-effective-plan-id.label = Active Plan ID
channel-type.evcc.loadpoint-effective-plan-id.description = ID of the currently active charging plan
channel-type.evcc.loadpoint-effective-plan-soc.label = Active Plan SoC
channel-type.evcc.loadpoint-effective-plan-soc.description = State of charge limit for the currently active charging plan
channel-type.evcc.loadpoint-effective-plan-time.label = Active Plan Time
channel-type.evcc.loadpoint-effective-plan-time.description = Indicating when the next charge slot starts
channel-type.evcc.loadpoint-effective-priority.label = Active Priority
channel-type.evcc.loadpoint-effective-priority.description = Effective priority of this loadpoint
channel-type.evcc.loadpoint-enable-delay.label = Enable Delay
channel-type.evcc.loadpoint-enable-delay.description = Delay in PV mode before starting the charging when enough power is available
channel-type.evcc.loadpoint-enable-threshold.label = Enable Threshold
channel-type.evcc.loadpoint-enable-threshold.description = Threshold for the enable delay
channel-type.evcc.loadpoint-enabled.label = Loadpoint Enabled
channel-type.evcc.loadpoint-enabled.description = Indicates whether the charger is locked or released
channel-type.evcc.loadpoint-enabled.state.option.ON = Released
channel-type.evcc.loadpoint-enabled.state.option.OFF = Locked
channel-type.evcc.loadpoint-limit-energy.label = Energy Limit
channel-type.evcc.loadpoint-limit-energy.description = Energy limit when charging the vehicle
channel-type.evcc.loadpoint-limit-soc.label = SoC Limit
channel-type.evcc.loadpoint-limit-soc.description = State of charge limit when charging the vehicle
channel-type.evcc.loadpoint-limit-temperature.label = Temperature Limit
channel-type.evcc.loadpoint-limit-temperature.description = Temperature to which the heating device is charged
channel-type.evcc.loadpoint-max-current.label = Maximum Current
channel-type.evcc.loadpoint-max-current.description = Maximum amperage per connected phase that can be provided to the vehicle
channel-type.evcc.loadpoint-min-current.label = Minimum Current
channel-type.evcc.loadpoint-min-current.description = Minimum amperage per connected phase that will be provided to the vehicle
channel-type.evcc.loadpoint-mode.label = Mode
channel-type.evcc.loadpoint-mode.description = Charging Mode
channel-type.evcc.loadpoint-mode.state.option.off = Off
channel-type.evcc.loadpoint-mode.state.option.now = Fast
channel-type.evcc.loadpoint-mode.state.option.minpv = Min + PV
channel-type.evcc.loadpoint-mode.state.option.pv = Only PV
channel-type.evcc.loadpoint-offered-current.label = Offered Current
channel-type.evcc.loadpoint-offered-current.description = Current amperage per connected phase offered to the vehicle
channel-type.evcc.loadpoint-phase-action.label = Phase Scaling
channel-type.evcc.loadpoint-phase-action.description = Indicates phase switching from 1 to 3 or 3 to 1 phases
channel-type.evcc.loadpoint-phase-action.state.option.scale1p = Switching to 1 phase
channel-type.evcc.loadpoint-phase-action.state.option.scale3p = Switching to 3 phases
channel-type.evcc.loadpoint-phase-remaining.label = Phase Remaining Timer
channel-type.evcc.loadpoint-phase-remaining.description = Time until phase switching will occure
channel-type.evcc.loadpoint-phases-active.label = Active Phases
channel-type.evcc.loadpoint-phases-active.description = Current number of active phases
channel-type.evcc.loadpoint-phases-configured.label = Configured Phases
channel-type.evcc.loadpoint-phases-configured.description = Number of configured phases
channel-type.evcc.loadpoint-plan-active.label = Plan Activated
channel-type.evcc.loadpoint-plan-active.description = Indicates whether a plan is active or not
channel-type.evcc.loadpoint-plan-active.state.option.ON = Activated
channel-type.evcc.loadpoint-plan-active.state.option.OFF = Deactivated
channel-type.evcc.loadpoint-plan-energy.label = Plan Energy
channel-type.evcc.loadpoint-plan-energy.description = Charge plan energy goal
channel-type.evcc.loadpoint-plan-overrun.label = Plan Overrun
channel-type.evcc.loadpoint-plan-overrun.description = Indicates that the charge plan goal is not reachable in time
channel-type.evcc.loadpoint-plan-precondition.label = Plan Precondition
channel-type.evcc.loadpoint-plan-precondition.description = Duration for preconditioning the battery
channel-type.evcc.loadpoint-plan-projected-end.label = Plan Projected End
channel-type.evcc.loadpoint-plan-projected-end.description = Projected end of charging
channel-type.evcc.loadpoint-plan-projected-start.label = Plan Projected Start
channel-type.evcc.loadpoint-plan-projected-start.description = Projected start of charging
channel-type.evcc.loadpoint-priority.label = Priority
channel-type.evcc.loadpoint-priority.description = Priority of this loadpoint
channel-type.evcc.loadpoint-pv-action.label = PV Action
channel-type.evcc.loadpoint-pv-action.description = Indicates whether pv charging gets enabled or disabled
channel-type.evcc.loadpoint-pv-action.state.option.inactive = Inactive
channel-type.evcc.loadpoint-pv-action.state.option.pvEnable = Enable PV Timer
channel-type.evcc.loadpoint-pv-action.state.option.pvDisable = Disable PV Timer
channel-type.evcc.loadpoint-pv-remaining.label = Pv Remaining
channel-type.evcc.loadpoint-pv-remaining.description = Timer for enabling or disabling pv charging
channel-type.evcc.loadpoint-session-co2-per-k-wh.label = Session CO2 Per kWh
channel-type.evcc.loadpoint-session-co2-per-k-wh.description = Current session emitted carbon dioxide per kWh
channel-type.evcc.loadpoint-session-energy.label = Session Energy
channel-type.evcc.loadpoint-session-energy.description = Charged energy in the current charging session
channel-type.evcc.loadpoint-session-price-per-k-wh.label = Session Price Per kWh
channel-type.evcc.loadpoint-session-price-per-k-wh.description = Current session charging price per kWh
channel-type.evcc.loadpoint-session-price.label = Session Price
channel-type.evcc.loadpoint-session-price.description = Costs of the current charging session
channel-type.evcc.loadpoint-session-solar-percentage.label = Session Solar Percentage
channel-type.evcc.loadpoint-session-solar-percentage.description = Percentage of solar energy for the current charging session
channel-type.evcc.loadpoint-smart-cost-active.label = Smart Cost Active
channel-type.evcc.loadpoint-smart-cost-active.description = Indicates whether smart charging is active or not
channel-type.evcc.loadpoint-smart-cost-active.state.option.ON = Active
channel-type.evcc.loadpoint-smart-cost-active.state.option.OFF = Inactive
channel-type.evcc.loadpoint-smart-cost-limit-co2.label = Smart Cost Limit
channel-type.evcc.loadpoint-smart-cost-limit-co2.description = Below this limit the smart charging will start
channel-type.evcc.loadpoint-smart-cost-limit-price.label = Smart Cost Limit
channel-type.evcc.loadpoint-smart-cost-limit-price.description = Below this limit the smart charging will start
channel-type.evcc.loadpoint-smart-cost-next-start.label = Smart Cost Next Start
channel-type.evcc.loadpoint-smart-cost-next-start.description = Startpoint for the next smart charging
channel-type.evcc.loadpoint-smart-feed-in-priority-active.label = Smart Feed In Priority Active
channel-type.evcc.loadpoint-smart-feed-in-priority-active.description = Indicates that smart feed in has priority
channel-type.evcc.loadpoint-title.label = Title
channel-type.evcc.loadpoint-title.description = Name of the loadpoint as displayed in the UI
channel-type.evcc.loadpoint-vehicle-climater-active.label = Vehicle Climate Active
channel-type.evcc.loadpoint-vehicle-climater-active.description = Indicates whether the climate control is active or not for the connected vehicle
channel-type.evcc.loadpoint-vehicle-climater-active.state.option.ON = Active
channel-type.evcc.loadpoint-vehicle-climater-active.state.option.OFF = Inactive
channel-type.evcc.loadpoint-vehicle-detection-active.label = Vehicle Detection Active
channel-type.evcc.loadpoint-vehicle-detection-active.description = Indicates whether the vehicle detection is active or not
channel-type.evcc.loadpoint-vehicle-detection-active.state.option.ON = Active
channel-type.evcc.loadpoint-vehicle-detection-active.state.option.OFF = Inactive
channel-type.evcc.loadpoint-vehicle-identity.label = Vehicle Identity
channel-type.evcc.loadpoint-vehicle-identity.description = Transferred vehicle identity
channel-type.evcc.loadpoint-vehicle-limit-soc.label = Vehicle API SoC Limit
channel-type.evcc.loadpoint-vehicle-limit-soc.description = Current vehicle API state of charge limit
channel-type.evcc.loadpoint-vehicle-name.label = Vehicle Name
channel-type.evcc.loadpoint-vehicle-name.description = The unique identifier of the vehicle used in the evcc configuration (containing no whitespaces nor special characters)
channel-type.evcc.loadpoint-vehicle-odometer.label = Vehicle Odometer
channel-type.evcc.loadpoint-vehicle-odometer.description = Total distance travelled by the vehicle
channel-type.evcc.loadpoint-vehicle-range.label = Vehicle Range
channel-type.evcc.loadpoint-vehicle-range.description = Current range of the vehicle
channel-type.evcc.loadpoint-vehicle-soc.label = Vehicle API SoC
channel-type.evcc.loadpoint-vehicle-soc.description = Current state of charge of the vehicle
channel-type.evcc.loadpoint-vehicle-temperature.label = Temperature
channel-type.evcc.loadpoint-vehicle-temperature.description = Current temperature of the heating device
channel-type.evcc.loadpoint-vehicle-title.label = Vehicle Title
channel-type.evcc.loadpoint-vehicle-title.description = The name of the vehicle as displayed in the UI
channel-type.evcc.loadpoint-vehicle-welcome-active.label = Vehicle Welcome Active
channel-type.evcc.loadpoint-vehicle-welcome-active.description = Indicates whether the charger performs an initial charge or not when the vehicle gets connected
channel-type.evcc.loadpoint-vehicle-welcome-active.state.option.ON = Active
channel-type.evcc.loadpoint-vehicle-welcome-active.state.option.OFF = Inactive
channel-type.evcc.pv-power.label = PV Power
channel-type.evcc.pv-power.description = Current power of this photovoltaic system in Watt
channel-type.evcc.pv-title.label = Title
channel-type.evcc.pv-title.description = Name of the photovoltaic system as displayed in the UI
channel-type.evcc.site-available-version.label = Available Version
channel-type.evcc.site-available-version.description = Available evcc update version
channel-type.evcc.site-battery-capacity.label = Site Battery Capacity
channel-type.evcc.site-battery-capacity.description = Accumulated battery capacity for this site
channel-type.evcc.site-battery-discharge-control.label = Battery Discharge Control
channel-type.evcc.site-battery-discharge-control.description = Enable or disable battery discharge control
channel-type.evcc.site-battery-discharge-control.state.option.ON = Enabled
channel-type.evcc.site-battery-discharge-control.state.option.OFF = Disabled
channel-type.evcc.site-battery-energy.label = Battery Energy
channel-type.evcc.site-battery-energy.description = Energy provided from the battery(ies)
channel-type.evcc.site-battery-grid-charge-active.label = Grid Charging Active
channel-type.evcc.site-battery-grid-charge-active.description = Indicates whether the battery gets charged from the grid or not
channel-type.evcc.site-battery-grid-charge-active.state.option.ON = Active
channel-type.evcc.site-battery-grid-charge-active.state.option.OFF = Inactive
channel-type.evcc.site-battery-grid-charge-limit-co2.label = Grid Charging Limit
channel-type.evcc.site-battery-grid-charge-limit-co2.description = Below this smart cost limit the charging from grid will start (currency or co2)
channel-type.evcc.site-battery-grid-charge-limit-price.label = Grid Charging Limit
channel-type.evcc.site-battery-grid-charge-limit-price.description = Below this smart cost limit the charging from grid will start (currency or co2)
channel-type.evcc.site-battery-mode-external.label = Battery Mode
channel-type.evcc.site-battery-mode-external.description = Current battery mode cached from external
channel-type.evcc.site-battery-mode-external.state.option.unknow = Unknown
channel-type.evcc.site-battery-mode-external.state.option.normal = Normal
channel-type.evcc.site-battery-mode-external.state.option.hold = Locked
channel-type.evcc.site-battery-mode-external.state.option.charge = Charging
channel-type.evcc.site-battery-mode.label = Battery Mode
channel-type.evcc.site-battery-mode.description = Current battery mode for the site
channel-type.evcc.site-battery-mode.state.option.unknow = Unknown
channel-type.evcc.site-battery-mode.state.option.normal = Normal
channel-type.evcc.site-battery-mode.state.option.hold = Locked
channel-type.evcc.site-battery-mode.state.option.charge = Charging
channel-type.evcc.site-battery-power.label = Battery Power
channel-type.evcc.site-battery-power.description = Accumulated power distributed by the battery(ies)
channel-type.evcc.site-battery-soc.label = Battery SoC
channel-type.evcc.site-battery-soc.description = Current state of charge of battery(ies)
channel-type.evcc.site-buffer-soc.label = Battery Buffer SoC
channel-type.evcc.site-buffer-soc.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.site-buffer-start-soc.label = Battery Buffer Start SoC
channel-type.evcc.site-buffer-start-soc.description = State of charge for which a charging session in "pv" mode is started, even if there is insufficient solar surplus
channel-type.evcc.site-currency.label = Currency
channel-type.evcc.site-currency.description = Current configured currency
channel-type.evcc.site-demo-mode.label = Demo Mode
channel-type.evcc.site-demo-mode.description = Indicates whether demo mode is active or not
channel-type.evcc.site-demo-mode.state.option.ON = Active
channel-type.evcc.site-demo-mode.state.option.OFF = Inactive
channel-type.evcc.site-eebus.label = EEBUS
channel-type.evcc.site-eebus.description = Indicates whether EEBUS is configured or not
channel-type.evcc.site-eebus.state.option.ON = Configured
channel-type.evcc.site-eebus.state.option.OFF = Not configured
channel-type.evcc.site-green-share-home.label = Green Share Home
channel-type.evcc.site-green-share-home.description = Indicates whether green power is available for home
channel-type.evcc.site-green-share-loadpoints.label = Green Share Loadpoints
channel-type.evcc.site-green-share-loadpoints.description = Indicates whether green power is available for loadpoints
channel-type.evcc.site-grid-power.label = Grid Power
channel-type.evcc.site-grid-power.description = Current power from grid (negative means feed-in)
channel-type.evcc.site-home-power.label = Home Power
channel-type.evcc.site-home-power.description = Current power consumption by home
channel-type.evcc.site-interval.label = Interval
channel-type.evcc.site-interval.description = Refresh interval configured for evcc
channel-type.evcc.site-messaging.label = Site Messaging
channel-type.evcc.site-messaging.description = Indicates whether messaging has been configured or not
channel-type.evcc.site-messaging.state.option.ON = Configured
channel-type.evcc.site-messaging.state.option.OFF = Not configured
channel-type.evcc.site-priority-soc.label = Battery Priority SoC
channel-type.evcc.site-priority-soc.description = State of charge for which the battery has priority over charging the vehicle when charging mode is "pv"
channel-type.evcc.site-pv-energy.label = PV Energy
channel-type.evcc.site-pv-energy.description = Accumulated energy provided through photovoltaic systems
channel-type.evcc.site-pv-power.label = PV Power
channel-type.evcc.site-pv-power.description = Accumulated current power from photovoltaic systems
channel-type.evcc.site-residual-power.label = Grid Residual Power
channel-type.evcc.site-residual-power.description = Target operating point of the surplus regulation at the grid connection (grid meter)
channel-type.evcc.site-site-title.label = Site Title
channel-type.evcc.site-site-title.description = Name of the site as displayed in the UI
channel-type.evcc.site-smart-cost-available.label = Smart Cost Available
channel-type.evcc.site-smart-cost-available.description = Indicates whether smart costs are available or not
channel-type.evcc.site-smart-cost-available.state.option.ON = Available
channel-type.evcc.site-smart-cost-available.state.option.OFF = Not Available
channel-type.evcc.site-smart-cost-type.label = Smart Cost Type
channel-type.evcc.site-smart-cost-type.description = Indicates the smart cost type (price or co2)
channel-type.evcc.site-smart-cost-type.state.option.priceforecast = Price Forecast
channel-type.evcc.site-smart-cost-type.state.option.pricedynamic = Price Dynamic
channel-type.evcc.site-smart-cost-type.state.option.co2 = CO2
channel-type.evcc.site-smart-feed-in-priority-available.label = Smart Feed In Priority Available
channel-type.evcc.site-smart-feed-in-priority-available.description = Indicates whether there is priority for smart feed in or not
channel-type.evcc.site-smart-feed-in-priority-available.state.option.ON = Available
channel-type.evcc.site-smart-feed-in-priority-available.state.option.OFF = Not Available
channel-type.evcc.site-startup.label = Start Up
channel-type.evcc.site-startup.description = Indicates whether restart is needed or not
channel-type.evcc.site-startup.state.option.ON = True
channel-type.evcc.site-startup.state.option.OFF = False
channel-type.evcc.site-tariff-co2-home.label = CO2 Concentration Home
channel-type.evcc.site-tariff-co2-home.description = Indicates the current carbon dioxide concentration per kWh for your home
channel-type.evcc.site-tariff-co2-loadpoints.label = CO2 Concentration Loadpoints
channel-type.evcc.site-tariff-co2-loadpoints.description = Indicates the current carbon dioxide concentration per kWh for your loadpoints
channel-type.evcc.site-tariff-co2.label = CO2 Concentration
channel-type.evcc.site-tariff-co2.description = Indicates the current carbon dioxide concentration per kWh
channel-type.evcc.site-tariff-feed-in.label = Feed in price per kWh
channel-type.evcc.site-tariff-feed-in.description = Indicates the current price per kWh for feed-in to the grid
channel-type.evcc.site-tariff-grid.label = Grid Price
channel-type.evcc.site-tariff-grid.description = Indicates the current price per kWh for consuming from the grid
channel-type.evcc.site-tariff-price-home.label = Home Price
channel-type.evcc.site-tariff-price-home.description = Indicates the current price per kWh for your home
channel-type.evcc.site-tariff-price-loadpoints.label = Loadpoint price per kWh
channel-type.evcc.site-tariff-price-loadpoints.description = Indicates the current price per kWh for loadpoints
channel-type.evcc.site-tariff-solar.label = Solar Tariff
channel-type.evcc.site-tariff-solar.description = Current solar power forecast
channel-type.evcc.site-version.label = Version
channel-type.evcc.site-version.description = Current evcc version
channel-type.evcc.solar-percentage-type.label = Solar Percentage
channel-type.evcc.vehicle-capacity.label = Vehicle Capacity
channel-type.evcc.vehicle-capacity.description = Capacity of vehicle battery
channel-type.evcc.vehicle-icon.label = Vehicle Icon
channel-type.evcc.vehicle-icon.description = Used icon for this vehicle in the UI
channel-type.evcc.vehicle-limit-soc.label = Vehicle Limit SoC
channel-type.evcc.vehicle-limit-soc.description = State of charge to which the vehicle is charged
channel-type.evcc.vehicle-max-current.label = Vehicle Max Current
channel-type.evcc.vehicle-max-current.description = Maximum allowed current that can be provided to charge the vehicle
channel-type.evcc.vehicle-min-current.label = Vehicle Min Current
channel-type.evcc.vehicle-min-current.description = Minimum current that needs to provided to charge the vehicle
channel-type.evcc.vehicle-min-soc.label = Vehicle Min SoC
channel-type.evcc.vehicle-min-soc.description = Minimum state of charge a vehicle should have (fast charging until this limit)
channel-type.evcc.vehicle-plan-precondition.label = Plan Precondition Duration
channel-type.evcc.vehicle-plan-precondition.description = Configured duration for charging the vehicle before departure (preconditioning the battery)
channel-type.evcc.vehicle-plan-soc.label = Planned SoC
channel-type.evcc.vehicle-plan-soc.description = Planned state of charge to which the vehicle will be charged (can be greater than the Vehicle Limit SoC)
channel-type.evcc.vehicle-plan-time.label = Plan Datetime
channel-type.evcc.vehicle-plan-time.description = Planned date and time when the charging should be started
channel-type.evcc.vehicle-title.label = Vehicle Title
channel-type.evcc.vehicle-title.description = Name of the vehicle as displayed in the UI
# channel group types
# thing types config
channel-group-type.evcc.loadpoint.label = Loadpoint
thing-type.config.evcc.server.schema.label = Protocol schema
thing-type.config.evcc.server.schema.description = The protocol schema that should be used to connect to your instance
thing-type.config.evcc.server.schema.option.http = HTTP
thing-type.config.evcc.server.schema.option.https = HTTPS
# 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
offline.configuration-error.no-host = No host configured
offline.communication-error.request-failed = Request failed
channel-type.evcc.site-battery-grid-charge-limit.label = Grid Charging Limit
channel-type.evcc.site-battery-grid-charge-limit.description = Below this smart cost limit the charging from grid will start (currency or co2)
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="evcc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="battery" listed="false">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Battery</label>
<description>A battery configured in your evcc instance</description>
</thing-type>
<channel-type id="battery-capacity">
<item-type unitHint="kWh">Number:Energy</item-type>
<label>Battery Capacity</label>
<description>Capacity of this battery</description>
<category>Energy</category>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="battery-controllable">
<item-type>Switch</item-type>
<label>Battery Controllable</label>
<description>Indicates whether this battery is controllable or not</description>
<category>Switch</category>
<tags>
<tag>Status</tag>
<tag>Mode</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">Controllable</option>
<option value="OFF">Not Controllable</option>
</options>
</state>
</channel-type>
<channel-type id="battery-power">
<item-type unitHint="W">Number:Power</item-type>
<label>Battery Power</label>
<description>Current power from this battery</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="battery-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Battery SoC</label>
<description>Current state of charge of this battery</description>
<category>BatteryLevel</category>
<tags>
<tag>Status</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="battery-title">
<item-type>String</item-type>
<label>Title</label>
<description>Battery name displayed in the UI</description>
<category>Settings</category>
<tags>
<tag>Status</tag>
<tag>Info</tag>
</tags>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="evcc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="heating" listed="false">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Heating - Loadpoint</label>
<description>A heating loadpoint configured in your evcc instance</description>
<representation-property>index</representation-property>
</thing-type>
<!-- This thing is currently using the same channels as a loadpoint does -->
</thing:thing-descriptions>
@@ -0,0 +1,783 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="evcc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="loadpoint" listed="false">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Loadpoint</label>
<description>A loadpoint configured in your evcc instance</description>
<representation-property>index</representation-property>
</thing-type>
<channel-type id="loadpoint-battery-boost">
<item-type>Switch</item-type>
<label>Battery Boost</label>
<description>Controls and indicates whether battery boost is active or not</description>
<category>Switch</category>
<tags>
<tag>Switch</tag>
<tag>Enabled</tag>
</tags>
<state>
<options>
<option value="ON">Activated</option>
<option value="OFF">Deactivated</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-charge-duration">
<item-type unitHint="h">Number:Time</item-type>
<label>Charging Duration</label>
<description>Time passed since start of the charging</description>
<category>Time</category>
<tags>
<tag>Status</tag>
<tag>Duration</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-charge-power">
<item-type unitHint="W">Number:Power</item-type>
<label>Charging Power</label>
<description>Current power of charging</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-charge-total-import">
<item-type unitHint="kWh">Number:Energy</item-type>
<label>Charge Total Import</label>
<description>Total imported energy</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-charge-remaining-duration">
<item-type unitHint="h">Number:Time</item-type>
<label>Charging Remaining Duration</label>
<description>Remaining duration until limit SoC is reached</description>
<category>Time</category>
<tags>
<tag>Forecast</tag>
<tag>Duration</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-charge-remaining-energy">
<item-type unitHint="Wh">Number:Energy</item-type>
<label>Charging Remaining Energy</label>
<description>Remaining energy until limit SoC is reached</description>
<category>Energy</category>
<tags>
<tag>Calculation</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-charged-energy">
<item-type unitHint="kWh">Number:Energy</item-type>
<label>Charged Energy</label>
<description>Energy charged since plugged-in</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-charger-icon">
<item-type>String</item-type>
<label>Charger Icon</label>
<description>Icon used in the UI for this charger</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-charger-feature-heating">
<item-type>Switch</item-type>
<label>Heating</label>
<description>Indicates whether the loadpoint is configured for a heating device</description>
<category>Switch</category>
<tags>
<tag>Switch</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">True</option>
<option value="OFF">False</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-charger-feature-integrated-device">
<item-type>Switch</item-type>
<label>Integrated Device</label>
<description>Loadpoint has an integrated device and runs without a vehicle (e.g. heatpump, e-bike)</description>
<category>Switch</category>
<tags>
<tag>Switch</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">True</option>
<option value="OFF">False</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-charger-phases1p3p">
<item-type>Switch</item-type>
<label>Phase Switching</label>
<description>Indicates that the loadpoint can switch between 1 and 3 phases</description>
<category>Switch</category>
<tags>
<tag>Switch</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">True</option>
<option value="OFF">False</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-charger-single-phase">
<item-type>Switch</item-type>
<label>1 Phase Charging</label>
<description>Indicates that the charger can only charge with 1 phase</description>
<category>Switch</category>
<tags>
<tag>Switch</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">True</option>
<option value="OFF">False</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-charger-status-reason">
<item-type>String</item-type>
<label>Charger Status Reason</label>
<description>Hint why the charging has not started yet</description>
<tags>
<tag>Status</tag>
<tag>Info</tag>
</tags>
<state readOnly="true">
<options>
<option value="unknown">Unknown</option>
<option value="waitingforauthorization">Waiting for authorization</option>
<option value="disconnectrequired">Disconnect required</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-charging">
<item-type>Switch</item-type>
<label>Charging State</label>
<description>Indicates whether the loadpoint is charging or not</description>
<category>Energy</category>
<tags>
<tag>Status</tag>
<tag>Mode</tag>
</tags>
<state pattern="%f %unit%" readOnly="true">
<options>
<option value="ON">Charging</option>
<option value="OFF">Not charging</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-connected">
<item-type>Switch</item-type>
<label>Vehicle Connected</label>
<description>Indicates whether a vehicle is connected or not</description>
<category>Switch</category>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">Connected</option>
<option value="OFF">Not connected</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-connected-duration">
<item-type unitHint="h">Number:Time</item-type>
<label>Vehicle Conn. Duration</label>
<description>Duration the vehicle is connected to the loadpoint</description>
<category>Time</category>
<tags>
<tag>Status</tag>
<tag>Duration</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-disable-delay">
<item-type unitHint="s">Number:Time</item-type>
<label>Disable Delay</label>
<description>Delay in PV mode before stopping the charging if not enough power is available</description>
<category>Time</category>
<tags>
<tag>Setpoint</tag>
<tag>Duration</tag>
</tags>
<state pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-disable-threshold">
<item-type unitHint="W">Number:Power</item-type>
<label>Disable Threshold</label>
<description>Threshold for the disable delay (negative values means feed in)</description>
<category>Energy</category>
<state pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-effective-limit-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Active Charging Limit</label>
<description>Active state of charge the vehicle will be charged to</description>
<category>BatteryLevel</category>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-effective-max-current">
<item-type unitHint="A">Number:ElectricCurrent</item-type>
<label>Active Maximum Current</label>
<description>Effective maximum amperage per connected phase that can be provided to the vehicle</description>
<category>Energy</category>
<state min="0" step="1" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-effective-min-current">
<item-type unitHint="A">Number:ElectricCurrent</item-type>
<label>Active Minimum Current</label>
<description>Effective minimum amperage per connected phase that will be provided to the vehicle</description>
<category>Energy</category>
<state min="0" step="1" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-effective-plan-id">
<item-type>Number</item-type>
<label>Active Plan ID</label>
<description>ID of the currently active charging plan</description>
<state min="0" step="1" max="10" pattern="%.0f" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-effective-plan-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Active Plan SoC</label>
<description>State of charge limit for the currently active charging plan</description>
<category>BatteryLevel</category>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-effective-plan-time">
<item-type>DateTime</item-type>
<label>Active Plan Time</label>
<description>Indicating when the next charge slot starts</description>
<tags>
<tag>Calculation</tag>
<tag>Timestamp</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-plan-projected-end">
<item-type>DateTime</item-type>
<label>Plan Projected End</label>
<description>Projected end of charging</description>
<tags>
<tag>Calculation</tag>
<tag>Timestamp</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-plan-projected-start">
<item-type>DateTime</item-type>
<label>Plan Projected Start</label>
<description>Projected start of charging</description>
<tags>
<tag>Calculation</tag>
<tag>Timestamp</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-effective-priority">
<item-type>Number</item-type>
<label>Active Priority</label>
<description>Effective priority of this loadpoint</description>
<state pattern="%d" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-enable-delay">
<item-type unitHint="s">Number:Time</item-type>
<label>Enable Delay</label>
<description>Delay in PV mode before starting the charging when enough power is available</description>
<category>Time</category>
<state pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-enable-threshold">
<item-type unitHint="W">Number:Power</item-type>
<label>Enable Threshold</label>
<description>Threshold for the enable delay</description>
<category>Energy</category>
<state pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-enabled">
<item-type>Switch</item-type>
<label>Loadpoint Enabled</label>
<description>Indicates whether the charger is locked or released</description>
<state readOnly="true">
<options>
<option value="ON">Released</option>
<option value="OFF">Locked</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-limit-energy">
<item-type unitHint="kWh">Number:Energy</item-type>
<label>Energy Limit</label>
<description>Energy limit when charging the vehicle</description>
<category>BatteryLevel</category>
<tags>
<tag>Control</tag>
<tag>Energy</tag>
</tags>
<state min="0" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-limit-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>SoC Limit</label>
<description>State of charge limit when charging the vehicle</description>
<category>BatteryLevel</category>
<tags>
<tag>Control</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-max-current">
<item-type unitHint="A">Number:ElectricCurrent</item-type>
<label>Maximum Current</label>
<description>Maximum amperage per connected phase that can be provided to the vehicle</description>
<category>Energy</category>
<tags>
<tag>Setpoint</tag>
<tag>Current</tag>
</tags>
<state min="0" step="1" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-min-current">
<item-type unitHint="A">Number:ElectricCurrent</item-type>
<label>Minimum Current</label>
<description>Minimum amperage per connected phase that will be provided to the vehicle</description>
<category>Energy</category>
<tags>
<tag>Setpoint</tag>
<tag>Current</tag>
</tags>
<state min="0" step="1" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-mode">
<item-type>String</item-type>
<label>Mode</label>
<description>Charging Mode</description>
<category>Settings</category>
<tags>
<tag>Control</tag>
<tag>Mode</tag>
</tags>
<state>
<options>
<option value="off">Off</option>
<option value="now">Fast</option>
<option value="minpv">Min + PV</option>
<option value="pv">Only PV</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-offered-current">
<item-type unitHint="A">Number:ElectricCurrent</item-type>
<label>Offered Current</label>
<description>Current amperage per connected phase offered to the vehicle</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Current</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-phase-action">
<item-type>String</item-type>
<label>Phase Scaling</label>
<description>Indicates phase switching from 1 to 3 or 3 to 1 phases</description>
<state>
<options>
<option value="scale1p">Switching to 1 phase</option>
<option value="scale3p">Switching to 3 phases</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-phase-remaining">
<item-type unitHint="s">Number:Time</item-type>
<label>Phase Remaining Timer</label>
<description>Time until phase switching will occure</description>
<tags>
<tag>Status</tag>
<tag>Duration</tag>
</tags>
<state pattern="%d" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-phases-active">
<item-type>Number</item-type>
<label>Active Phases</label>
<description>Current number of active phases</description>
<tags>
<tag>Status</tag>
<tag>Info</tag>
</tags>
<state pattern="%d" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-phases-configured">
<item-type>Number</item-type>
<label>Configured Phases</label>
<description>Number of configured phases</description>
<tags>
<tag>Status</tag>
<tag>Info</tag>
</tags>
<state pattern="%d" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-plan-active">
<item-type>Switch</item-type>
<label>Plan Activated</label>
<description>Indicates whether a plan is active or not</description>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
<state>
<options>
<option value="ON">Activated</option>
<option value="OFF">Deactivated</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-plan-energy">
<item-type unitHint="kWh">Number:Energy</item-type>
<label>Plan Energy</label>
<description>Charge plan energy goal</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-plan-overrun">
<item-type unitHint="s">Number:Time</item-type>
<label>Plan Overrun</label>
<description>Indicates that the charge plan goal is not reachable in time</description>
<tags>
<tag>Forecast</tag>
<tag>Duration</tag>
</tags>
<state pattern="%d %unit%"/>
</channel-type>
<channel-type id="loadpoint-plan-precondition">
<item-type unitHint="s">Number:Time</item-type>
<label>Plan Precondition</label>
<description>Duration for preconditioning the battery</description>
<category>Time</category>
<tags>
<tag>Measurement</tag>
<tag>Duration</tag>
</tags>
<state pattern="%d %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-priority">
<item-type>Number</item-type>
<label>Priority</label>
<description>Priority of this loadpoint</description>
<tags>
<tag>Info</tag>
</tags>
<state pattern="%d" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-pv-action">
<item-type>String</item-type>
<label>PV Action</label>
<description>Indicates whether pv charging gets enabled or disabled</description>
<tags>
<tag>Status</tag>
<tag>Info</tag>
</tags>
<state readOnly="true">
<options>
<option value="inactive">Inactive</option>
<option value="pvEnable">Enable PV Timer</option>
<option value="pvDisable">Disable PV Timer</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-pv-remaining">
<item-type unitHint="s">Number:Time</item-type>
<label>Pv Remaining</label>
<description>Timer for enabling or disabling pv charging</description>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-session-co2-per-k-wh">
<item-type unitHint="g/kWh">Number:EmissionIntensity</item-type>
<label>Session CO2 Per kWh</label>
<description>Current session emitted carbon dioxide per kWh</description>
<tags>
<tag>Measurement</tag>
<tag>Info</tag>
</tags>
<state pattern="%.3f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-session-energy">
<item-type unitHint="kWh">Number:Energy</item-type>
<label>Session Energy</label>
<description>Charged energy in the current charging session</description>
<tags>
<tag>Forecast</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-session-solar-percentage">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Session Solar Percentage</label>
<description>Percentage of solar energy for the current charging session</description>
<tags>
<tag>Forecast</tag>
<tag>Info</tag>
</tags>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-session-price">
<item-type>Number:Currency</item-type>
<label>Session Price</label>
<description>Costs of the current charging session</description>
<tags>
<tag>Measurement</tag>
<tag>Info</tag>
</tags>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-session-price-per-k-wh">
<item-type>Number:EnergyPrice</item-type>
<label>Session Price Per kWh</label>
<description>Current session charging price per kWh</description>
<tags>
<tag>Measurement</tag>
<tag>Info</tag>
</tags>
<state pattern="%.3f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-smart-cost-active">
<item-type>Switch</item-type>
<label>Smart Cost Active</label>
<description>Indicates whether smart charging is active or not</description>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">Active</option>
<option value="OFF">Inactive</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-smart-cost-limit-co2">
<item-type>Number:EmissionIntensity</item-type>
<label>Smart Cost Limit</label>
<description>Below this limit the smart charging will start</description>
<state pattern="%d" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-smart-cost-limit-price">
<item-type>Number:EnergyPrice</item-type>
<label>Smart Cost Limit</label>
<description>Below this limit the smart charging will start</description>
<state pattern="%.3f" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-smart-cost-next-start">
<item-type>DateTime</item-type>
<label>Smart Cost Next Start</label>
<description>Startpoint for the next smart charging</description>
<tags>
<tag>Measurement</tag>
<tag>Timestamp</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-smart-feed-in-priority-active">
<item-type>Switch</item-type>
<label>Smart Feed In Priority Active</label>
<description>Indicates that smart feed in has priority</description>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-title">
<item-type>String</item-type>
<label>Title</label>
<description>Name of the loadpoint as displayed in the UI</description>
<category>Text</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-vehicle-climater-active">
<item-type>Switch</item-type>
<label>Vehicle Climate Active</label>
<description>Indicates whether the climate control is active or not for the connected vehicle</description>
<tags>
<tag>Airconditioning</tag>
<tag>Status</tag>
</tags>
<state>
<options>
<option value="ON">Active</option>
<option value="OFF">Inactive</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-vehicle-detection-active">
<item-type>Switch</item-type>
<label>Vehicle Detection Active</label>
<description>Indicates whether the vehicle detection is active or not</description>
<tags>
<tag>Switch</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">Active</option>
<option value="OFF">Inactive</option>
</options>
</state>
</channel-type>
<channel-type id="loadpoint-vehicle-identity">
<item-type>String</item-type>
<label>Vehicle Identity</label>
<description>Transferred vehicle identity</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-vehicle-limit-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Vehicle API SoC Limit</label>
<description>Current vehicle API state of charge limit</description>
<category>BatteryLevel</category>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-vehicle-name">
<item-type>String</item-type>
<label>Vehicle Name</label>
<description>The unique identifier of the vehicle used in the evcc configuration (containing no whitespaces nor
special
characters)</description>
<category>Settings</category>
<tags>
<tag>Status</tag>
<tag>Info</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-vehicle-odometer">
<item-type unitHint="km">Number:Length</item-type>
<label>Vehicle Odometer</label>
<description>Total distance travelled by the vehicle</description>
<category>MoveControl</category>
<tags>
<tag>Status</tag>
<tag>Info</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-vehicle-range">
<item-type unitHint="km">Number:Length</item-type>
<label>Vehicle Range</label>
<description>Current range of the vehicle</description>
<tags>
<tag>Status</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-vehicle-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Vehicle API SoC</label>
<description>Current state of charge of the vehicle</description>
<category>BatteryLevel</category>
<tags>
<tag>Status</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-vehicle-title">
<item-type>String</item-type>
<label>Vehicle Title</label>
<description>The name of the vehicle as displayed in the UI</description>
<category>Settings</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-vehicle-welcome-active">
<item-type>Switch</item-type>
<label>Vehicle Welcome Active</label>
<description>Indicates whether the charger performs an initial charge or not when the vehicle gets connected</description>
<tags>
<tag>Switch</tag>
<tag>Enabled</tag>
</tags>
<state>
<options>
<option value="ON">Active</option>
<option value="OFF">Inactive</option>
</options>
</state>
</channel-type>
<!-- Heating relevant channels -->
<channel-type id="loadpoint-effective-limit-temperature">
<item-type>Number:Temperature</item-type>
<label>Effective Charging Limit Temperature</label>
<description>Effective Temperature until which the heating device will be charged</description>
<category>Temperature</category>
<tags>
<tag>Setpoint</tag>
<tag>Temperature</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="loadpoint-limit-temperature">
<item-type>Number:Temperature</item-type>
<label>Temperature Limit</label>
<description>Temperature to which the heating device is charged</description>
<category>Temperature</category>
<tags>
<tag>Setpoint</tag>
<tag>Temperature</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="loadpoint-vehicle-temperature">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>Current temperature of the heating device</description>
<category>Temperature</category>
<tags>
<tag>Measurement</tag>
<tag>Temperature</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
</thing:thing-descriptions>
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="evcc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="pv" listed="false">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Photovoltaic</label>
<description>A photovoltaic system configured in your evcc instance</description>
</thing-type>
<!-- Already declared in site.xml maybe evcc is going to change it in the future -->
<channel-type id="pv-power">
<item-type unitHint="W">Number:Power</item-type>
<label>PV Power</label>
<description>Current power of this photovoltaic system in Watt</description>
<category>Power</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="pv-title">
<item-type>String</item-type>
<label>Title</label>
<description>Name of the photovoltaic system as displayed in the UI</description>
<category>Text</category>
<state readOnly="true"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
</thing:thing-descriptions>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="evcc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<bridge-type id="server">
<label>Server</label>
<description>Connection to evcc instance</description>
<config-description>
<parameter name="scheme" type="text">
<label>Protocol Scheme</label>
<description>The protocol scheme that should be used to connect to your instance</description>
<default>http</default>
<options>
<option value="http">HTTP</option>
<option value="https">HTTPS</option>
</options>
</parameter>
<parameter name="host" type="text" required="true">
<label>Hostname or IP</label>
<description>Address of your evcc instance, for example evcc.local or 192.168.1.2</description>
</parameter>
<parameter name="port" type="integer" required="false">
<advanced>true</advanced>
<label>Port</label>
<default>7070</default>
<description>HTTP-Port of your instance (Default: 7070)</description>
</parameter>
<parameter name="pollInterval" type="integer" required="false">
<advanced>true</advanced>
<label>Polling Interval</label>
<default>30</default>
<description>How often should the state be polled? (in seconds, default is 30s)</description>
</parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>
@@ -0,0 +1,414 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="evcc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="site" listed="false">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Site</label>
<description>The site configured in your evcc instance</description>
</thing-type>
<channel-type id="site-available-version">
<item-type>String</item-type>
<label>Available Version</label>
<description>Available evcc update version</description>
<category>Text</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="site-battery-capacity">
<item-type unitHint="kWh">Number:Energy</item-type>
<label>Site Battery Capacity</label>
<description>Accumulated battery capacity for this site</description>
<category>Energy</category>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-battery-discharge-control">
<item-type>Switch</item-type>
<label>Battery Discharge Control</label>
<description>Enable or disable battery discharge control</description>
<category>Switch</category>
<tags>
<tag>Switch</tag>
<tag>Mode</tag>
</tags>
<state>
<options>
<option value="ON">Enabled</option>
<option value="OFF">Disabled</option>
</options>
</state>
</channel-type>
<channel-type id="site-battery-energy">
<item-type unitHint="Wh">Number:Energy</item-type>
<label>Battery Energy</label>
<description>Energy provided from the battery(ies)</description>
<category>Energy</category>
<tags>
<tag>Energy</tag>
<tag>Measurement</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-battery-grid-charge-active">
<item-type>Switch</item-type>
<label>Grid Charging Active</label>
<description>Indicates whether the battery gets charged from the grid or not</description>
<category>Switch</category>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
<state pattern="%.0f" readOnly="true">
<options>
<option value="ON">Active</option>
<option value="OFF">Inactive</option>
</options>
</state>
</channel-type>
<channel-type id="site-battery-grid-charge-limit-co2">
<item-type>Number:EmissionIntensity</item-type>
<label>Grid Charging Limit</label>
<description>Below this smart cost limit the charging from grid will start (currency or co2)</description>
<tags>
<tag>Control</tag>
</tags>
<state pattern="%.0f" readOnly="true"/>
</channel-type>
<channel-type id="site-battery-grid-charge-limit-price">
<item-type>Number:EnergyPrice</item-type>
<label>Grid Charging Limit</label>
<description>Below this smart cost limit the charging from grid will start (currency or co2)</description>
<tags>
<tag>Control</tag>
</tags>
<state pattern="%.3f" readOnly="true"/>
</channel-type>
<channel-type id="site-battery-mode">
<item-type>String</item-type>
<label>Battery Mode</label>
<description>Current battery mode for the site</description>
<category>Battery</category>
<state readOnly="true">
<options>
<option value="unknow">Unknown</option>
<option value="normal">Normal</option>
<option value="hold">Locked</option>
<option value="charge">Charging</option>
</options>
</state>
</channel-type>
<channel-type id="site-battery-mode-external">
<item-type>String</item-type>
<label>Battery Mode</label>
<description>Current battery mode cached from external</description>
<category>Battery</category>
<state readOnly="true">
<options>
<option value="unknow">Unknown</option>
<option value="normal">Normal</option>
<option value="hold">Locked</option>
<option value="charge">Charging</option>
</options>
</state>
</channel-type>
<channel-type id="site-battery-power">
<item-type unitHint="W">Number:Power</item-type>
<label>Battery Power</label>
<description>Accumulated power distributed by the battery(ies)</description>
<category>Energy</category>
<tags>
<tag>Power</tag>
<tag>Measurement</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-battery-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Battery SoC</label>
<description>Current state of charge of battery(ies)</description>
<category>BatteryLevel</category>
<tags>
<tag>Status</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-buffer-soc">
<item-type unitHint="%">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%"/>
</channel-type>
<channel-type id="site-buffer-start-soc">
<item-type unitHint="%">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%"/>
</channel-type>
<channel-type id="site-currency">
<item-type>String</item-type>
<label>Currency</label>
<description>Current configured currency</description>
<category>Text</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="site-demo-mode">
<item-type>Switch</item-type>
<label>Demo Mode</label>
<description>Indicates whether demo mode is active or not</description>
<category>Settings</category>
<state readOnly="true">
<options>
<option value="ON">Active</option>
<option value="OFF">Inactive</option>
</options>
</state>
</channel-type>
<channel-type id="site-eebus">
<item-type>Switch</item-type>
<label>EEBUS</label>
<description>Indicates whether EEBUS is configured or not</description>
<state readOnly="true">
<options>
<option value="ON">Configured</option>
<option value="OFF">Not configured</option>
</options>
</state>
</channel-type>
<channel-type id="site-green-share-home">
<item-type>Number</item-type>
<label>Green Share Home</label>
<description>Indicates whether green power is available for home</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="site-green-share-loadpoints">
<item-type>Number</item-type>
<label>Green Share Loadpoints</label>
<description>Indicates whether green power is available for loadpoints</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="site-grid-power">
<item-type unitHint="W">Number:Power</item-type>
<label>Grid Power</label>
<description>Current power from grid (negative means feed-in)</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-home-power">
<item-type unitHint="W">Number:Power</item-type>
<label>Home Power</label>
<description>Current power consumption by home</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-messaging">
<item-type>Switch</item-type>
<label>Site Messaging</label>
<description>Indicates whether messaging has been configured or not</description>
<state readOnly="true">
<options>
<option value="ON">Configured</option>
<option value="OFF">Not configured</option>
</options>
</state>
</channel-type>
<channel-type id="site-priority-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Battery Priority SoC</label>
<description>State of charge for which the battery has priority over charging the vehicle when charging mode is "pv"
</description>
<category>BatteryLevel</category>
<state min="0" step="0.1" max="100" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="site-interval">
<item-type>Number</item-type>
<label>Interval</label>
<description>Refresh interval configured for evcc</description>
<category>Settings</category>
<state pattern="%d" readOnly="true"/>
</channel-type>
<channel-type id="site-pv-energy">
<item-type unitHint="Wh">Number:Energy</item-type>
<label>PV Energy</label>
<description>Accumulated energy provided through photovoltaic systems</description>
<category>Energy</category>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-pv-power">
<item-type unitHint="W">Number:Power</item-type>
<label>PV Power</label>
<description>Accumulated current power from photovoltaic systems</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-residual-power">
<item-type unitHint="W">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>
<tags>
<tag>Setpoint</tag>
<tag>Power</tag>
</tags>
<state min="0" step="1" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="site-smart-cost-available">
<item-type>Switch</item-type>
<label>Smart Cost Available</label>
<description>Indicates whether smart costs are available or not</description>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">Available</option>
<option value="OFF">Not Available</option>
</options>
</state>
</channel-type>
<channel-type id="site-smart-cost-type">
<item-type>String</item-type>
<label>Smart Cost Type</label>
<description>Indicates the smart cost type (price or co2)</description>
<state readOnly="true">
<options>
<option value="priceforecast">Price Forecast</option>
<option value="pricedynamic">Price Dynamic</option>
<option value="co2">CO2</option>
</options>
</state>
</channel-type>
<channel-type id="site-smart-feed-in-priority-available">
<item-type>Switch</item-type>
<label>Smart Feed In Priority Available</label>
<description>Indicates whether there is priority for smart feed in or not</description>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">Available</option>
<option value="OFF">Not Available</option>
</options>
</state>
</channel-type>
<channel-type id="site-startup">
<item-type>Switch</item-type>
<label>Start Up</label>
<description>Indicates whether restart is needed or not</description>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">True</option>
<option value="OFF">False</option>
</options>
</state>
</channel-type>
<channel-type id="site-tariff-co2">
<item-type unitHint="g/kWh">Number:EmissionIntensity</item-type>
<label>CO2 Concentration</label>
<description>Indicates the current carbon dioxide concentration per kWh</description>
<category>co2</category>
<tags>
<tag>Status</tag>
<tag>CO2</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-tariff-co2-home">
<item-type unitHint="g/kWh">Number:EmissionIntensity</item-type>
<label>CO2 Concentration Home</label>
<description>Indicates the current carbon dioxide concentration per kWh for your home</description>
<category>co2</category>
<tags>
<tag>Status</tag>
<tag>CO2</tag>
</tags>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-tariff-co2-loadpoints">
<item-type unitHint="g/kWh">Number:EmissionIntensity</item-type>
<label>CO2 Concentration Loadpoints</label>
<description>Indicates the current carbon dioxide concentration per kWh for your loadpoints</description>
<category>co2</category>
<tags>
<tag>Status</tag>
<tag>CO2</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-tariff-feed-in">
<item-type>Number:EnergyPrice</item-type>
<label>Feed in price per kWh</label>
<description>Indicates the current price per kWh for feed-in to the grid</description>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-tariff-grid">
<item-type>Number:EnergyPrice</item-type>
<label>Grid Price</label>
<description>Indicates the current price per kWh for consuming from the grid</description>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-tariff-price-home">
<item-type>Number:EnergyPrice</item-type>
<label>Home Price</label>
<description>Indicates the current price per kWh for your home</description>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-tariff-price-loadpoints">
<item-type>Number:EnergyPrice</item-type>
<label>Loadpoint price per kWh</label>
<description>Indicates the current price per kWh for loadpoints</description>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-tariff-solar">
<item-type unitHint="W">Number:Power</item-type>
<label>Solar Tariff</label>
<description>Current solar power forecast</description>
<category>Energy</category>
<tags>
<tag>Forecast</tag>
<tag>Power</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="site-site-title">
<item-type>String</item-type>
<label>Site Title</label>
<description>Name of the site as displayed in the UI</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="site-version">
<item-type>String</item-type>
<label>Version</label>
<description>Current evcc version</description>
<category>Text</category>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="evcc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="statistics" listed="false">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Statistics</label>
<description>Statistics about your evcc instance</description>
<channel-groups>
<channel-group id="30d" typeId="statistics-group">
<label>Last 30 Days</label>
<description>Statistics from the last 30 days</description>
</channel-group>
<channel-group id="365d" typeId="statistics-group">
<label>Last 365 Days</label>
<description>Statistics from the last 365days</description>
</channel-group>
<channel-group id="this-year" typeId="statistics-group">
<label>Last Year</label>
<description>Statistics from the last year</description>
</channel-group>
<channel-group id="total" typeId="statistics-group">
<label>Total</label>
<description>Total statistics</description>
</channel-group>
</channel-groups>
</thing-type>
<channel-group-type id="statistics-group">
<label>Statistics Group</label>
<channels>
<channel id="avg-co2" typeId="avg-co2-type"/>
<channel id="avg-price" typeId="avg-price-type"/>
<channel id="charged-energy" typeId="charged-energy-type"/>
<channel id="solar-percentage" typeId="solar-percentage-type"/>
</channels>
</channel-group-type>
<channel-type id="avg-co2-type">
<item-type unitHint="g/kWh">Number:EmissionIntensity</item-type>
<label>Average C02</label>
<tags>
<tag>Calculation</tag>
<tag>Info</tag>
</tags>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="avg-price-type">
<item-type>Number:EnergyPrice</item-type>
<label>Average Price</label>
<tags>
<tag>Calculation</tag>
<tag>Info</tag>
</tags>
<state pattern="%.2f %unit%"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="charged-energy-type">
<item-type unitHint="kWh">Number:Energy</item-type>
<label>Charged Energy</label>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="solar-percentage-type">
<item-type>Number:Dimensionless</item-type>
<label>Solar Percentage</label>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.2f %unit%"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
</thing:thing-descriptions>
@@ -1,751 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="evcc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="device">
<label>evcc installation</label>
<description>A running evcc installation</description>
<semantic-equipment-tag>EVSE</semantic-equipment-tag>
<channel-groups>
<channel-group id="general" typeId="general"/>
<channel-group id="loadpoint0" typeId="loadpoint0"/>
<channel-group id="loadpoint1" typeId="loadpoint1"/>
<channel-group id="loadpoint2" typeId="loadpoint2"/>
<channel-group id="loadpoint3" typeId="loadpoint3"/>
<channel-group id="loadpoint4" typeId="loadpoint4"/>
<channel-group id="loadpoint5" typeId="loadpoint5"/>
<channel-group id="loadpoint6" typeId="loadpoint6"/>
<channel-group id="loadpoint7" typeId="loadpoint7"/>
<channel-group id="loadpoint8" typeId="loadpoint8"/>
<channel-group id="loadpoint9" typeId="loadpoint9"/>
<channel-group id="loadpoint0current" typeId="loadpoint0current"/>
<channel-group id="loadpoint1current" typeId="loadpoint1current"/>
<channel-group id="loadpoint2current" typeId="loadpoint2current"/>
<channel-group id="loadpoint3current" typeId="loadpoint3current"/>
<channel-group id="loadpoint4current" typeId="loadpoint4current"/>
<channel-group id="loadpoint5current" typeId="loadpoint5current"/>
<channel-group id="loadpoint6current" typeId="loadpoint6current"/>
<channel-group id="loadpoint7current" typeId="loadpoint7current"/>
<channel-group id="loadpoint8current" typeId="loadpoint8current"/>
<channel-group id="loadpoint9current" typeId="loadpoint9current"/>
</channel-groups>
<config-description>
<parameter name="url" type="text" required="true">
<context>network-address</context>
<label>URL</label>
<description>URL of evcc web UI, e.g. https://demo.evcc.io</description>
</parameter>
<parameter name="refreshInterval" type="integer" unit="s" min="15">
<label>Refresh Interval</label>
<description>Interval the status is polled in seconds.</description>
<default>60</default>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<channel-group-type id="general">
<label>General Data</label>
</channel-group-type>
<channel-group-type id="loadpoint0">
<label>Loadpoint 0</label>
</channel-group-type>
<channel-group-type id="loadpoint1">
<label>Loadpoint 1</label>
</channel-group-type>
<channel-group-type id="loadpoint2">
<label>Loadpoint 2</label>
</channel-group-type>
<channel-group-type id="loadpoint3">
<label>Loadpoint 3</label>
</channel-group-type>
<channel-group-type id="loadpoint4">
<label>Loadpoint 4</label>
</channel-group-type>
<channel-group-type id="loadpoint5">
<label>Loadpoint 5</label>
</channel-group-type>
<channel-group-type id="loadpoint6">
<label>Loadpoint 6</label>
</channel-group-type>
<channel-group-type id="loadpoint7">
<label>Loadpoint 7</label>
</channel-group-type>
<channel-group-type id="loadpoint8">
<label>Loadpoint 8</label>
</channel-group-type>
<channel-group-type id="loadpoint9">
<label>Loadpoint 9</label>
</channel-group-type>
<channel-group-type id="loadpoint0current">
<label>Loadpoint 0: Current</label>
</channel-group-type>
<channel-group-type id="loadpoint1current">
<label>Loadpoint 1: Current</label>
</channel-group-type>
<channel-group-type id="loadpoint2current">
<label>Loadpoint 2: Current</label>
</channel-group-type>
<channel-group-type id="loadpoint3current">
<label>Loadpoint 3: Current</label>
</channel-group-type>
<channel-group-type id="loadpoint4current">
<label>Loadpoint 4: Current</label>
</channel-group-type>
<channel-group-type id="loadpoint5current">
<label>Loadpoint 5: Current</label>
</channel-group-type>
<channel-group-type id="loadpoint6current">
<label>Loadpoint 6: Current</label>
</channel-group-type>
<channel-group-type id="loadpoint7current">
<label>Loadpoint 7: Current</label>
</channel-group-type>
<channel-group-type id="loadpoint8current">
<label>Loadpoint 8: Current</label>
</channel-group-type>
<channel-group-type id="loadpoint9current">
<label>Loadpoint 9: Current</label>
</channel-group-type>
<!-- Units and description on: https://docs.evcc.io/docs/reference/configuration/messaging/#msg -->
<channel-type id="batteryCapacity">
<item-type>Number:Energy</item-type>
<label>Battery Capacity</label>
<description>Capacity of (home) battery</description>
<category>Energy</category>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="batteryPower">
<item-type>Number:Power</item-type>
<label>Battery Power</label>
<description>Current power from battery</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="batterySoC">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Battery SoC</label>
<description>Current State of Charge of battery</description>
<category>BatteryLevel</category>
<tags>
<tag>Status</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<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>
<tags>
<tag>Switch</tag>
<tag>Mode</tag>
</tags>
<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>
<tags>
<tag>Status</tag>
<tag>Mode</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="prioritySoC">
<item-type unitHint="%">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>
<category>BatteryLevel</category>
<state min="0" step="0.1" max="100" pattern="%.0f %unit%" readOnly="false"/>
</channel-type>
<channel-type id="bufferSoC">
<item-type unitHint="%">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 unitHint="%">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>
<tags>
<tag>Setpoint</tag>
<tag>Power</tag>
</tags>
<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>
<description>Current power from grid (negative means feed-in)</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="homePower">
<item-type>Number:Power</item-type>
<label>Home Power</label>
<description>Current power taken by home</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="pvPower">
<item-type>Number:Power</item-type>
<label>PV Power</label>
<description>Current power from photovoltaik</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="version">
<item-type>String</item-type>
<label>Version</label>
<description>Current evcc version</description>
<category>Text</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="availableVersion">
<item-type>String</item-type>
<label>Available Version</label>
<description>Available evcc update version</description>
<category>Text</category>
<state readOnly="true"/>
</channel-type>
<!-- Channel Types for loadpoints -->
<channel-type id="activePhases">
<item-type>Number</item-type>
<label>Charging Active Phases</label>
<description>Current number of active phases while charging</description>
<category></category>
<state pattern="%d" readOnly="true"/>
</channel-type>
<channel-type id="chargeCurrent">
<item-type>Number:ElectricCurrent</item-type>
<label>Charging Current</label>
<description>Current amperage per connected phase while charging</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Current</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="chargeDuration">
<item-type>Number:Time</item-type>
<label>Charging Duration</label>
<description>Charging duration</description>
<category>Time</category>
<tags>
<tag>Measurement</tag>
<tag>Duration</tag>
</tags>
<state pattern="%.1f min" readOnly="true"/>
</channel-type>
<channel-type id="chargePower">
<item-type>Number:Power</item-type>
<label>Charging Power</label>
<description>Current power of charging</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="chargeRemainingDuration">
<item-type>Number:Time</item-type>
<label>Charging Remaining Duration</label>
<description>Remaining duration until limit SoC is reached</description>
<category>Time</category>
<tags>
<tag>Measurement</tag>
<tag>Progress</tag>
</tags>
<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 limit SoC is reached</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="chargedEnergy">
<item-type>Number:Energy</item-type>
<label>Charged Energy</label>
<description>Energy charged since plugged-in</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="charging">
<item-type>Switch</item-type>
<label>Charging State</label>
<description>Loadpoint is currently charging</description>
<category>Energy</category>
<tags>
<tag>Status</tag>
<tag>Energy</tag>
</tags>
<state pattern="%f %unit%" readOnly="true">
<options>
<option value="ON">Charging</option>
<option value="OFF">Not charging</option>
</options>
</state>
</channel-type>
<channel-type id="enabled">
<item-type>Switch</item-type>
<label>Charging Enabled</label>
<description>Charging enabled (mode not "off")</description>
<category>Switch</category>
<tags>
<tag>Status</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="true">
<options>
<option value="ON">Enabled</option>
<option value="OFF">Disabled</option>
</options>
</state>
</channel-type>
<channel-type id="maxCurrent">
<item-type>Number:ElectricCurrent</item-type>
<label>Charging max Current</label>
<description>Maximum amperage per connected phase with which the car should be charged</description>
<category>Energy</category>
<state min="0" step="1" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="minCurrent">
<item-type>Number:ElectricCurrent</item-type>
<label>Charging min Current</label>
<description>Minimum amperage per connected phase with which the car should be charged</description>
<category>Energy</category>
<state min="0" step="1" 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>Heating</category>
<tags>
<tag>Status</tag>
<tag>Mode</tag>
</tags>
<state readOnly="false">
<options>
<option value="off">Off</option>
<option value="now">Fast</option>
<option value="minpv">Min + PV</option>
<option value="pv">Only PV</option>
</options>
</state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="phases">
<item-type>Number</item-type>
<label>Charging Enabled Phases</label>
<description>The maximum number of phases which can be used</description>
<category>Energy</category>
<state min="0" step="1" max="3" pattern="%d" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="limitEnergy">
<item-type>Number:Energy</item-type>
<label>Charging Limit Energy</label>
<description>Amount of energy to charge the vehicle with</description>
<category>BatteryLevel</category>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state min="0" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="limitSoC">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Charging Limit SoC</label>
<description>Until which state of charge (SoC) should the vehicle be charged</description>
<category>BatteryLevel</category>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="limitTemperature">
<item-type>Number:Temperature</item-type>
<label>Charging Limit Temperature</label>
<description>Until which Temperature should the heating device be charged</description>
<category>Temperature</category>
<tags>
<tag>Setpoint</tag>
<tag>Temperature</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="effectiveLimitSoC">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Effective Charging Limit</label>
<description>Effective state of charge (SoC) until which the vehicle will be charged</description>
<category>BatteryLevel</category>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="effectiveLimitTemperature">
<item-type>Number:Temperature</item-type>
<label>Effective Charging Limit Temperature</label>
<description>Effective Temperature until which the heating device will be charged</description>
<category>Temperature</category>
<tags>
<tag>Setpoint</tag>
<tag>Temperature</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="title">
<item-type>String</item-type>
<label>Loadpoint Title</label>
<description>Title of loadpoint</description>
<category>PowerOutlet</category>
<tags>
<tag>Status</tag>
<tag>Power</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="vehicleConnected">
<item-type>Switch</item-type>
<label>Vehicle Connected</label>
<description>Whether vehicle is connected to loadpoint</description>
<category>Switch</category>
<state readOnly="true">
<options>
<option value="ON">Connected</option>
<option value="OFF">Not connected</option>
</options>
</state>
</channel-type>
<channel-type id="vehicleConnectedDuration">
<item-type>Number:Time</item-type>
<label>Vehicle Connected Duration</label>
<description>Duration the vehicle is connected to loadpoint</description>
<category>Time</category>
<tags>
<tag>Measurement</tag>
<tag>Duration</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="vehicleOdometer">
<item-type>Number:Length</item-type>
<label>Vehicle Odometer</label>
<description>Total distance travelled by EV</description>
<category>MoveControl</category>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="vehiclePresent">
<item-type>Switch</item-type>
<label>Vehicle Data Access</label>
<description>Whether evcc is able to get data from vehicle</description>
<category>Switch</category>
<state readOnly="true">
<options>
<option value="ON">Data access</option>
<option value="OFF">No data access</option>
</options>
</state>
</channel-type>
<channel-type id="vehicleRange">
<item-type>Number:Length</item-type>
<label>Vehicle Range</label>
<description>Battery range for EV</description>
<category></category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="vehicleSoC">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Vehicle SoC</label>
<description>Current State of Charge of EV</description>
<category>BatteryLevel</category>
<tags>
<tag>Status</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="vehicleTemperature">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>Current Temperature of the heating device</description>
<category>Temperature</category>
<tags>
<tag>Measurement</tag>
<tag>Temperature</tag>
</tags>
<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-type id="chargerFeatureHeating">
<item-type>Switch</item-type>
<label>Charger Feature: Heating</label>
<description>'True' for heating device: State of Charge in Degree instead of Percent</description>
<category>Switch</category>
<state readOnly="true">
<options>
<option value="ON">True</option>
<option value="OFF">False</option>
</options>
</state>
</channel-type>
<channel-type id="chargerFeatureIntegratedDevice">
<item-type>Switch</item-type>
<label>Charger Feature: Integrated Device</label>
<description>'True' for integrated device: Operate without a "vehicle" (e.g. heat pump, eBike)</description>
<category>Switch</category>
<state readOnly="true">
<options>
<option value="ON">True</option>
<option value="OFF">False</option>
</options>
</state>
</channel-type>
<!-- Channel Types for vehicles -->
<channel-type id="vehicleTitle">
<item-type>String</item-type>
<label>Vehicle Title</label>
<description>Title of vehicle</description>
<category>GarageDoor</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="heatingTitle">
<item-type>String</item-type>
<label>Heating Title</label>
<description>Title of heating device</description>
<category>Heating</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="vehicleMinSoC">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Vehicle Min SoC</label>
<description>Minimum state of charge (SoC) a vehicle should have</description>
<category>BatteryLevel</category>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="heatingMinTemperature">
<item-type>Number:Temperature</item-type>
<label>Heating Min Temperature</label>
<description>Minimum Temperature a heating device should have</description>
<category>Temperature</category>
<tags>
<tag>Setpoint</tag>
<tag>Temperature</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicleLimitSoC">
<item-type unitHint="%">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>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="heatingLimitTemperature">
<item-type>Number:Temperature</item-type>
<label>Charging Temperature Limit</label>
<description>Until which Temperature should the specific heating device be charged</description>
<category>Temperature</category>
<tags>
<tag>Setpoint</tag>
<tag>Temperature</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicleCapacity">
<item-type>Number:Energy</item-type>
<label>Vehicle Capacity</label>
<description>Capacity of EV battery</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="heatingCapacity">
<item-type>Number:Energy</item-type>
<label>Heating Capacity</label>
<description>Capacity of heating device</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
</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>
<tags>
<tag>Switch</tag>
<tag>Enabled</tag>
</tags>
<state readOnly="false">
<options>
<option value="ON">Enabled</option>
<option value="OFF">Disabled</option>
</options>
</state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="heatingPlanEnabled">
<item-type>Switch</item-type>
<label>Heating Plan Enabled</label>
<description>Plan for charging enabled</description>
<category>Switch</category>
<tags>
<tag>Switch</tag>
<tag>Enabled</tag>
</tags>
<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 unitHint="%">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>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="heatingPlanTemperature">
<item-type>Number:Temperature</item-type>
<label>Heating Plan Temperature</label>
<description>Until which Temperature should heating device be charged in plan</description>
<category>Temperature</category>
<tags>
<tag>Setpoint</tag>
<tag>Temperature</tag>
</tags>
<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>
<tags>
<tag>Setpoint</tag>
<tag>Timestamp</tag>
</tags>
<state readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="heatingPlanTime">
<item-type>DateTime</item-type>
<label>Heating Plan Time</label>
<description>When the plan Temperature should be reached</description>
<category>Time</category>
<tags>
<tag>Setpoint</tag>
<tag>Timestamp</tag>
</tags>
<state readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
</thing:thing-descriptions>
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="evcc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="vehicle" listed="false">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Vehicle</label>
<description>A vehicle configured in your evcc instance</description>
<representation-property>id</representation-property>
</thing-type>
<channel-type id="vehicle-capacity">
<item-type unitHint="kWh">Number:Energy</item-type>
<label>Vehicle Capacity</label>
<description>Capacity of vehicle battery</description>
<category>Energy</category>
<tags>
<tag>Status</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.0f %unit%" readOnly="true"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicle-icon">
<item-type>String</item-type>
<label>Vehicle Icon</label>
<description>Used icon for this vehicle in the UI</description>
<category>Text</category>
<tags>
<tag>Status</tag>
<tag>Info</tag>
</tags>
<state readOnly="true"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicle-limit-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Vehicle Limit SoC</label>
<description>State of charge to which the vehicle is charged</description>
<category>BatteryLevel</category>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicle-min-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Vehicle Min SoC</label>
<description>Minimum state of charge a vehicle should have (fast charging until this limit)</description>
<category>BatteryLevel</category>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicle-max-current">
<item-type unitHint="A">Number:ElectricCurrent</item-type>
<label>Vehicle Max Current</label>
<description>Maximum allowed current that can be provided to charge the vehicle</description>
<category>Energy</category>
<tags>
<tag>Control</tag>
<tag>Current</tag>
</tags>
<state min="6" step="1" max="32" readOnly="true"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicle-min-current">
<item-type unitHint="A">Number:ElectricCurrent</item-type>
<label>Vehicle Min Current</label>
<description>Minimum current that needs to provided to charge the vehicle</description>
<category>Energy</category>
<tags>
<tag>Control</tag>
<tag>Current</tag>
</tags>
<state min="6" step="1" max="32" readOnly="true"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicle-plan-precondition">
<item-type>Number:Time</item-type>
<label>Plan Precondition Duration</label>
<description>Configured duration for charging the vehicle before departure (preconditioning the battery)</description>
<category>Time</category>
<tags>
<tag>Setpoint</tag>
<tag>Duration</tag>
</tags>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicle-plan-soc">
<item-type unitHint="%">Number:Dimensionless</item-type>
<label>Planned SoC</label>
<description>Planned state of charge to which the vehicle will be charged (can be greater than the Vehicle Limit SoC)</description>
<tags>
<tag>Setpoint</tag>
<tag>Energy</tag>
</tags>
<state readOnly="true"></state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicle-plan-time">
<item-type>DateTime</item-type>
<label>Plan Datetime</label>
<description>Planned date and time when the charging should be started</description>
<state readOnly="true"></state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="vehicle-title">
<item-type>String</item-type>
<label>Vehicle Title</label>
<description>Name of the vehicle as displayed in the UI</description>
<category>Text</category>
<state readOnly="true"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
</thing:thing-descriptions>