diff --git a/CODEOWNERS b/CODEOWNERS
index 07176c04242..110ba738a9b 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -90,6 +90,7 @@
/bundles/org.openhab.binding.enturno/ @klocsson
/bundles/org.openhab.binding.epsonprojector/ @mlobstein
/bundles/org.openhab.binding.etherrain/ @dfad1469
+/bundles/org.openhab.binding.evcc/ @florian-h05
/bundles/org.openhab.binding.evohome/ @Nebula83
/bundles/org.openhab.binding.exec/ @kgoderis
/bundles/org.openhab.binding.feed/ @svilenvul
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index e4b65e93f82..3218cb7841b 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -441,6 +441,11 @@
org.openhab.binding.etherrain
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.evcc
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.evohome
diff --git a/bundles/org.openhab.binding.evcc/NOTICE b/bundles/org.openhab.binding.evcc/NOTICE
new file mode 100644
index 00000000000..38d625e3492
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-addons
diff --git a/bundles/org.openhab.binding.evcc/README.md b/bundles/org.openhab.binding.evcc/README.md
new file mode 100644
index 00000000000..8b83a6d3b5b
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/README.md
@@ -0,0 +1,171 @@
+# evcc Binding
+
+This binding integrates [evcc - electric vehicle charging control](https://evcc.io), a project that provides a control center for electric vehicle charging.
+
+evcc controls your wallbox(es) with multiple charging modes and allows you to charge your ev with your photovoltaik'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.
+For more advanced features like calculated savings, you have to visit the web UI of evcc.
+
+## Supported Things
+
+- `device`: A running evcc installation.
+
+## Discovery
+
+No auto discovery supported.
+
+## Thing Configuration
+
+### `device` Thing Configuration
+
+| 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 60 seconds.
+
+## 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#batteryPower | Number:Power | R | Current power from battery. |
+| general#batterySoC | Number:Dimensionless | R | Current State of Charge of battery. |
+| general#batteryPrioritySoC | Number:Dimensionless | R | State of State of Charge for which the battery has priority over charging the ev when charging mode is "pv". |
+| general#gridPower | Number:Power | R | Current power from grid (negative means feed-in) |
+| general#homePower | Number:Power | R | Current power taken by home. |
+| general#pvPower | Number:Power | R | Current power from photovoltaik. |
+
+
+### Loadpoint channels
+
+Those channels exist per configured loadpoint.
+Please note that you have to replace *N* with your loadpoint number.
+
+| Channel | Type | Read/Write | Description |
+|-------------------------------------|------------------------|------------|-----------------------------------------------------------------------------------------------------|
+| loadpointN#activePhases | Number | R | Current number of active phases while charging |
+| loadpointN#chargeCurrent | Number:ElectricCurrent | R | Current amperage per connected phase while charging |
+| loadpointN#chargeDuration | Number:Time | R | Charging duration |
+| loadpointN#chargeRemainingDuration | Number:Time | R | Remaining duration until target SoC is reached |
+| loadpointN#chargeRemainingEnergy | Number:Energy | R | Remaining energy until target SoC is reached |
+| loadpointN#chargePower | Number:Power | R | Current power of charging |
+| loadpointN#chargedEnergy | Number:Energy | R | Energy charged since plugged-in |
+| loadpointN#charging | Switch | R | Loadpoint is currently charging |
+| loadpointN#enabled | Switch | R | Charging enabled (mode is not "off") |
+| loadpointN#hasVehicle | Switch | R | Whether vehicle is configured for loadpoint |
+| loadpointN#maxCurrent | Number:ElectricCurrent | RW | Maximum amperage per connected phase with which the car should be charged |
+| loadpointN#minCurrent | Number:ElectricCurrent | RW | Minimum amperage per connected phase with which the car should be charged |
+| loadpointN#minSoC | Number:Dimensionless | RW | Charge immediately with maximum power up to the defined SoC, if the charge mode is not set to "off" |
+| loadpointN#mode | String | RW | Charging mode: "off", "now", "minpv", "pv" |
+| loadpointN#phases | Number | RW | The maximum number of phases which can be used |
+| loadpointN#targetSoC | Number:Dimensionless | RW | Until which state of charge (SoC) should the vehicle be charged |
+| loadpointN#targetTime | DateTime | RW | When the target SoC should be reached |
+| loadpointN#targetTimeEnabled | Switch | RW | Target time for charging enabled |
+| loadpointN#title | String | R | Title of loadpoint |
+| loadpointN#vehicleConnected | Switch | R | Whether vehicle is connected to loadpoint |
+| loadpointN#vehicleConnectedDuration | Number:Time | R | Duration the vehicle is connected to loadpoint |
+| loadpointN#vehicleCapacity | Number:Energy | R | Capacity of EV battery |
+| loadpointN#vehicleOdometer | Number:Length | R | Total distance travelled by EV |
+| loadpointN#vehiclePresent | Switch | R | Whether evcc is able to get data from vehicle |
+| loadpointN#vehicleRange | Number:Length | R | Battery range for EV |
+| loadpointN#vehicleSoC | Number:Dimensionless | R | Current State of Charge of EV |
+| loadpointN#vehicleTitle | String | R | Name of EV |
+
+## Full Example
+
+### Thing(s)
+
+```
+Thing evcc:device:demo "evcc Demo" [url="https://demo.evcc.io", refreshInterval=60]
+```
+
+### Items
+
+```
+// General
+Number:Power evcc_demo_batteryPower "Battery Power [%.1f kW]" {channel="evcc:device:demo:general#batteryPower"}
+Number:Dimensionless evcc_demo_batterySoC "Battery SoC [%d %%]" {channel="evcc:device:demo:general#batterySoC"}
+Number:Dimensionless evcc_demo_batteryPrioritySoC "Battery Priority SoC [%d %%]" {channel="evcc:device:demo:general#batteryPrioritySoC"}
+Number:Power evcc_demo_gridPower "Grid Power [%.1f kW]" {channel="evcc:device:demo:general#gridPower"}
+Number:Power evcc_demo_homePower "Home Power [%.1f kW]" {channel="evcc:device:demo:general#homePower"}
+Number:Power evcc_demo_pvPower "PV Power [%.1f kW]" {channel="evcc:device:demo:general#pvPower"}
+
+// Loadpoint
+Number evcc_demo_loadpoint0_activePhases "Active Phases [%d]" {channel="evcc:device:demo:loadpoint0#activePhases"}
+Number:ElectricCurrent evcc_demo_loadpoint0_chargeCurrent "Charging current [%.0f A]" {channel="evcc:device:demo:loadpoint0#chargeCurrent"}
+Number:Time evcc_demo_loadpoint0_chargeDuration "Charging duration [%1$tH:%1$tM]" {channel="evcc:device:demo:loadpoint0#chargeDuration"}
+Number:Time evcc_demo_loadpoint0_chargeRemainingDuration "Charging remaining duration [%1$tH:%1$tM]" {channel="evcc:device:demo:loadpoint0#chargeRemainingDuration"}
+Number:Energy evcc_demo_loadpoint0_chargeRemainingEnergy "Charging remaining energy [%.1f kWh]" {channel="evcc:device:demo:loadpoint0#chargeRemainingEnergy"}
+Number:Power evcc_demo_loadpoint0_chargePower "Charging power [%.1f kW]" {channel="evcc:device:demo:loadpoint0#chargePower"}
+Number:Energy evcc_demo_loadpoint0_chargedEnergy "Charged energy [%.1f kWh]" {channel="evcc:device:demo:loadpoint0#chargedEnergy"}
+Switch evcc_demo_loadpoint0_charging "Currently charging [%s]" {channel="evcc:device:demo:loadpoint0#charging"}
+Switch evcc_demo_loadpoint0_enabled "Charging enabled [%s]" {channel="evcc:device:demo:loadpoint0#enabled"}
+Switch evcc_demo_loadpoint0_hasVehicle "Vehicle configured [%s]" {channel="evcc:device:demo:loadpoint0#hasVehicle"}
+Number:ElectricCurrent evcc_demo_loadpoint0_maxCurrent "Maximum current [%.0f A]" {channel="evcc:device:demo:loadpoint0#maxCurrent"}
+Number:ElectricCurrent evcc_demo_loadpoint0_minCurrent "Minimum current [%.0f A]" {channel="evcc:device:demo:loadpoint0#minCurrent"}
+Number:Dimensionless evcc_demo_loadpoint0_minSoC "Minimum SoC [%d %%]" {channel="evcc:device:demo:loadpoint0#minSoC"}
+String evcc_demo_loadpoint0_mode "Mode [%s]" {channel="evcc:device:demo:loadpoint0#mode"}
+Number evcc_demo_loadpoint0_phases "Enabled phases [%d]" {channel="evcc:device:demo:loadpoint0#phases"}
+Number:Dimensionless evcc_demo_loadpoint0_targetSoC "Target SoC [%d %%]" {channel="evcc:device:demo:loadpoint0#targetSoC"}
+DateTime evcc_demo_loadpoint0_targetTime "Target time [%1$td.%1$tm.%1$tY, %1$tH:%1$tM]" {channel="evcc:device:demo:loadpoint0#targetTime"}
+Switch evcc_demo_loadpoint0_targetTimeEnabled "Target time enabled [%s]" {channel="evcc:device:demo:loadpoint0#targetTimeEnabled"}
+String evcc_demo_loadpoint0_title "Loadpoint title [%s]" {channel="evcc:device:demo:loadpoint0#title"}
+// Vehicle on loadpoint
+Switch evcc_demo_loadpoint0_vehicleConnected "Vehicle connected [%s]" {channel="evcc:device:demo:loadpoint0#vehicleConnected"}
+Number:Time evcc_demo_loadpoint0_vehicleConnectedDuration "Vehicle connected duration [%.1f h]" {channel="evcc:device:demo:loadpoint0#vehicleConnectedDuration"}
+Number:Energy evcc_demo_loadpoint0_vehicleCapacity "Vehicle capacity [%.0f kWH]" {channel="evcc:device:demo:loadpoint0#vehicleCapacity"}
+Number:Length evcc_demo_loadpoint0_vehicleOdometer "Vehicle odometer [%.1f km]" {channel="evcc:device:demo:loadpoint0#vehicleOdometer"}
+Switch evcc_demo_loadpoint0_vehiclePresent "Vehicle present [%s]" {channel="evcc:device:demo:loadpoint0#vehiclePresent"}
+Number:Length evcc_demo_loadpoint0_vehicleRange "Vehicle Range [%.0f km]" {channel="evcc:device:demo:loadpoint0#vehicleRange"}
+Number:Dimensionless evcc_demo_loadpoint0_vehicleSoC "Vehicle SoC [%d %%]" {channel="evcc:device:demo:loadpoint0#vehicleSoC"}
+String evcc_demo_loadpoint0_vehicleName "Vehicle name [%s]" {channel="evcc:device:demo:loadpoint0#vehicleTitle"}
+```
+
+### Sitemap
+
+```
+sitemap evcc label="evcc Demo" {
+ Frame label="General" {
+ Text item=evcc_demo_batteryPower
+ Text item=evcc_demo_batterySoC
+ Text item=evcc_demo_gridPower
+ Text item=evcc_demo_homePower
+ Text item=evcc_demo_pvPower
+ }
+ Frame label="Loadpoint 0" {
+ Text item=evcc_demo_loadpoint0_title
+ Text item=evcc_demo_loadpoint0_enabled label="Charging" {
+ Text item=evcc_demo_loadpoint0_charging
+ Text item=evcc_demo_loadpoint0_chargePower
+ Text item=evcc_demo_loadpoint0_chargeCurrent
+ Text item=evcc_demo_loadpoint0_activePhases
+ Text item=evcc_demo_loadpoint0_chargeDuration
+ Text item=evcc_demo_loadpoint0_chargeRemainingDuration
+ Text item=evcc_demo_loadpoint0_chargeRemainingEnergy
+ }
+ Switch item=evcc_demo_loadpoint0_mode mappings=["off"="Stop","now"="Now","minpv"="Min + PV", "pv"="Only PV"]
+ Text label="Charging settings" icon="settings" {
+ Setpoint item=evcc_demo_loadpoint0_targetSoC minValue=5 maxValue=100 step=5
+ Setpoint item=evcc_demo_loadpoint0_minCurrent minValue=6 maxValue=96 step=2
+ Setpoint item=evcc_demo_loadpoint0_maxCurrent minValue=6 maxValue=96 step=2
+ Setpoint item=evcc_demo_loadpoint0_minSoC minValue=0 maxValue=100 step=5
+ Setpoint item=evcc_demo_loadpoint0_phases minValue=1 maxValue=3 step=2
+ }
+ Text item=evcc_demo_loadpoint0_vehicleName label="Vehicle" {
+ Text item=evcc_demo_loadpoint0_vehicleCapacity
+ Text item=evcc_demo_loadpoint0_vehicleOdometer
+ Text item=evcc_demo_loadpoint0_vehicleRange
+ Text item=evcc_demo_loadpoint0_vehicleSoC
+ }
+ }
+}
+```
diff --git a/bundles/org.openhab.binding.evcc/pom.xml b/bundles/org.openhab.binding.evcc/pom.xml
new file mode 100644
index 00000000000..0c00c516c52
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 3.3.0-SNAPSHOT
+
+
+ org.openhab.binding.evcc
+
+ openHAB Add-ons :: Bundles :: evcc Binding
+
+
diff --git a/bundles/org.openhab.binding.evcc/src/main/feature/feature.xml b/bundles/org.openhab.binding.evcc/src/main/feature/feature.xml
new file mode 100644
index 00000000000..0ebf35de625
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/feature/feature.xml
@@ -0,0 +1,9 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ mvn:org.openhab.addons.bundles/org.openhab.binding.evcc/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccBindingConstants.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccBindingConstants.java
new file mode 100644
index 00000000000..e8b10ede4ab
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccBindingConstants.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2010-2022 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;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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.
+ *
+ * @author Florian Hotze - Initial contribution
+ */
+@NonNullByDefault
+public class EvccBindingConstants {
+
+ private static final String BINDING_ID = "evcc";
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "device");
+
+ // List of all Channel Type UIDs
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_BATTERY_POWER = new ChannelTypeUID(BINDING_ID, "batteryPower");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_BATTERY_SOC = new ChannelTypeUID(BINDING_ID, "batterySoC");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_BATTERY_PRIORITY_SOC = new ChannelTypeUID(BINDING_ID,
+ "batteryPrioritySoC");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_GRID_POWER = new ChannelTypeUID(BINDING_ID, "gridPower");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_HOME_POWER = new ChannelTypeUID(BINDING_ID, "homePower");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_PV_POWER = new ChannelTypeUID(BINDING_ID, "pvPower");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_ACTIVE_PHASES = new ChannelTypeUID(BINDING_ID,
+ "activePhases");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_CURRENT = new ChannelTypeUID(BINDING_ID,
+ "chargeCurrent");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_DURATION = new ChannelTypeUID(BINDING_ID,
+ "chargeDuration");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_POWER = new ChannelTypeUID(BINDING_ID,
+ "chargePower");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_REMAINING_DURATION = new ChannelTypeUID(
+ BINDING_ID, "chargeRemainingDuration");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGE_REMAINING_ENERGY = new ChannelTypeUID(
+ BINDING_ID, "chargeRemainingEnergy");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGED_ENERGY = new ChannelTypeUID(BINDING_ID,
+ "chargedEnergy");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CHARGING = new ChannelTypeUID(BINDING_ID, "charging");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CONNECTED = new ChannelTypeUID(BINDING_ID,
+ "vehicleConnected");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_CONNECTED_DURATION = new ChannelTypeUID(BINDING_ID,
+ "vehicleConnectedDuration");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_ENABLED = new ChannelTypeUID(BINDING_ID, "enabled");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_HAS_VEHICLE = new ChannelTypeUID(BINDING_ID,
+ "hasVehicle");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_MAX_CURRENT = new ChannelTypeUID(BINDING_ID,
+ "maxCurrent");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_MIN_CURRENT = new ChannelTypeUID(BINDING_ID,
+ "minCurrent");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_MIN_SOC = new ChannelTypeUID(BINDING_ID, "minSoC");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_MODE = new ChannelTypeUID(BINDING_ID, "mode");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_PHASES = new ChannelTypeUID(BINDING_ID, "phases");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_TARGET_SOC = new ChannelTypeUID(BINDING_ID,
+ "targetSoC");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_TARGET_TIME = new ChannelTypeUID(BINDING_ID,
+ "targetTime");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_TARGET_TIME_ENABLED = new ChannelTypeUID(BINDING_ID,
+ "targetTimeEnabled");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_TITLE = new ChannelTypeUID(BINDING_ID, "title");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_CAPACITY = new ChannelTypeUID(BINDING_ID,
+ "vehicleCapacity");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_ODOMETER = new ChannelTypeUID(BINDING_ID,
+ "vehicleOdometer");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_PRESENT = new ChannelTypeUID(BINDING_ID,
+ "vehiclePresent");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_RANGE = new ChannelTypeUID(BINDING_ID,
+ "vehicleRange");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_SOC = new ChannelTypeUID(BINDING_ID,
+ "vehicleSoC");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_TITLE = new ChannelTypeUID(BINDING_ID,
+ "vehicleTitle");
+
+ // List of all Channel ids
+ public static final String CHANNEL_BATTERY_POWER = "batteryPower";
+ public static final String CHANNEL_BATTERY_SOC = "batterySoC";
+ public static final String CHANNEL_BATTERY_PRIORITY_SOC = "batteryPrioritySoC";
+ public static final String CHANNEL_GRID_POWER = "gridPower";
+ public static final String CHANNEL_HOME_POWER = "homePower";
+ public static final String CHANNEL_PV_POWER = "pvPower";
+ 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_HAS_VEHICLE = "hasVehicle";
+ public static final String CHANNEL_LOADPOINT_MAX_CURRENT = "maxCurrent";
+ public static final String CHANNEL_LOADPOINT_MIN_CURRENT = "minCurrent";
+ public static final String CHANNEL_LOADPOINT_MIN_SOC = "minSoC";
+ public static final String CHANNEL_LOADPOINT_MODE = "mode";
+ public static final String CHANNEL_LOADPOINT_PHASES = "phases";
+ public static final String CHANNEL_LOADPOINT_TARGET_SOC = "targetSoC";
+ public static final String CHANNEL_LOADPOINT_TARGET_TIME = "targetTime";
+ /**
+ * Whether a target time is set on loadpoint.
+ */
+ public static final String CHANNEL_LOADPOINT_TARGET_TIME_ENABLED = "targetTimeEnabled";
+ public static final String CHANNEL_LOADPOINT_TITLE = "title";
+ public static final String CHANNEL_LOADPOINT_VEHICLE_CAPACITY = "vehicleCapacity";
+ public static final String CHANNEL_LOADPOINT_VEHICLE_ODOMETER = "vehicleOdometer";
+ public static final String CHANNEL_LOADPOINT_VEHICLE_PRESENT = "vehiclePresent";
+ public static final String CHANNEL_LOADPOINT_VEHICLE_RANGE = "vehicleRange";
+ public static final String CHANNEL_LOADPOINT_VEHICLE_SOC = "vehicleSoC";
+ public static final String CHANNEL_LOADPOINT_VEHICLE_TITLE = "vehicleTitle";
+
+ public static final int CONNECTION_TIMEOUT_MILLISEC = 5000;
+ public static final int LONG_CONNECTION_TIMEOUT_MILLISEC = 60000;
+ public static final String EVCC_REST_API = "/api/";
+}
diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccConfiguration.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccConfiguration.java
new file mode 100644
index 00000000000..afca0285723
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccConfiguration.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2022 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;
+
+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
+ */
+@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;
+}
diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandler.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandler.java
new file mode 100644
index 00000000000..f1c39488617
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandler.java
@@ -0,0 +1,448 @@
+/**
+ * Copyright (c) 2010-2022 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;
+
+import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeParseException;
+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.api.EvccAPI;
+import org.openhab.binding.evcc.internal.api.EvccApiException;
+import org.openhab.binding.evcc.internal.api.dto.Loadpoint;
+import org.openhab.binding.evcc.internal.api.dto.Result;
+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.MetricPrefix;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+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.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link EvccHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Florian Hotze - Initial contribution
+ */
+@NonNullByDefault
+public class EvccHandler extends BaseThingHandler {
+ private final Logger logger = LoggerFactory.getLogger(EvccHandler.class);
+ private @Nullable EvccAPI evccAPI;
+ private @Nullable ScheduledFuture> statePollingJob;
+
+ private @Nullable Result result;
+
+ private boolean batteryConfigured = false;
+ private boolean gridConfigured = false;
+ private boolean pvConfigured = false;
+
+ private int targetSoC = 100;
+ private boolean targetTimeEnabled = false;
+ private ZonedDateTime targetTimeZDT = ZonedDateTime.now().plusHours(12);
+
+ public EvccHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (command.equals(RefreshType.REFRESH)) {
+ refresh();
+ } else {
+ logger.debug("Handling command {} ({}) for channel {}", command, command.getClass(), channelUID);
+ String groupId = channelUID.getGroupId();
+ if (groupId == null) {
+ return;
+ }
+ String channelIdWithoutGroup = channelUID.getIdWithoutGroup();
+ int loadpoint = Integer.parseInt(groupId.toString().substring(9));
+ EvccAPI evccAPI = this.evccAPI;
+ if (evccAPI == null) {
+ return;
+ }
+ try {
+ switch (channelIdWithoutGroup) {
+ case CHANNEL_LOADPOINT_MODE:
+ if (command instanceof StringType) {
+ evccAPI.setMode(loadpoint, command.toString());
+ }
+ break;
+ case CHANNEL_LOADPOINT_MIN_SOC:
+ if (command instanceof QuantityType) {
+ evccAPI.setMinSoC(loadpoint, ((QuantityType>) command).intValue());
+ }
+ break;
+ case CHANNEL_LOADPOINT_TARGET_SOC:
+ if (command instanceof QuantityType) {
+ evccAPI.setTargetSoC(loadpoint, ((QuantityType>) command).intValue());
+ }
+ break;
+ case CHANNEL_LOADPOINT_TARGET_TIME:
+ if (command instanceof DateTimeType) {
+ targetTimeZDT = ((DateTimeType) command).getZonedDateTime();
+ ChannelUID channel = new ChannelUID(getThing().getUID(), "loadpoint" + loadpoint,
+ CHANNEL_LOADPOINT_TARGET_TIME);
+ updateState(channel, new DateTimeType(targetTimeZDT));
+ if (targetTimeEnabled) {
+ try {
+ evccAPI.setTargetCharge(loadpoint, targetSoC, targetTimeZDT);
+ } catch (DateTimeParseException e) {
+ logger.debug("Failed to set target charge", e);
+ }
+ }
+ }
+ break;
+ case CHANNEL_LOADPOINT_TARGET_TIME_ENABLED:
+ if (command == OnOffType.ON) {
+ evccAPI.setTargetCharge(loadpoint, targetSoC, targetTimeZDT);
+ targetTimeEnabled = true;
+ } else if (command == OnOffType.OFF) {
+ evccAPI.unsetTargetCharge(loadpoint);
+ targetTimeEnabled = false;
+ }
+ break;
+ case CHANNEL_LOADPOINT_PHASES:
+ if (command instanceof DecimalType) {
+ evccAPI.setPhases(loadpoint, ((DecimalType) command).intValue());
+ }
+ break;
+ case CHANNEL_LOADPOINT_MIN_CURRENT:
+ if (command instanceof QuantityType) {
+ evccAPI.setMinCurrent(loadpoint, ((QuantityType>) command).intValue());
+ }
+ break;
+ case CHANNEL_LOADPOINT_MAX_CURRENT:
+ if (command instanceof QuantityType) {
+ evccAPI.setMaxCurrent(loadpoint, ((QuantityType>) command).intValue());
+ }
+ break;
+ default:
+ return;
+ }
+ } catch (EvccApiException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.debug("Failed to handle command {} for channel {}: {}", command, channelUID, e.getMessage());
+ } else {
+ logger.debug("Failed to handle command {} for channel {}: {} -> {}", command, channelUID,
+ e.getMessage(), cause.getMessage());
+ }
+ }
+ refresh();
+ }
+ }
+
+ @Override
+ public void initialize() {
+ EvccConfiguration config = getConfigAs(EvccConfiguration.class);
+ String url = config.url;
+ if (url == null || url.isBlank()) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
+ "@text/offline.configuration-error.no-host");
+ } else {
+ this.evccAPI = new EvccAPI(url);
+ logger.debug("Setting up refresh job ...");
+ statePollingJob = scheduler.scheduleWithFixedDelay(this::refresh, 0, config.refreshInterval,
+ TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Refreshes from evcc.
+ *
+ * First, checks connection and updates Thing status.
+ * Second, creates all available channels.
+ * Third, updates all channels.
+ */
+ private void refresh() {
+ logger.debug("Running refresh job ...");
+ EvccAPI evccAPI = null;
+ evccAPI = this.evccAPI;
+ if (evccAPI == null) {
+ return;
+ }
+ try {
+ this.result = evccAPI.getResult();
+ } catch (EvccApiException e) {
+ logger.debug("Failed to get state");
+ }
+ Result result = this.result;
+ if (result == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
+ "@text/offline.communication-error.request-failed");
+ } else {
+ String sitename = result.getSiteTitle();
+ int numberOfLoadpoints = result.getLoadpoints().length;
+ logger.debug("Found {} loadpoints on site {}.", numberOfLoadpoints, sitename);
+ updateStatus(ThingStatus.ONLINE);
+ batteryConfigured = result.getBatteryConfigured();
+ gridConfigured = result.getGridConfigured();
+ pvConfigured = result.getPvConfigured();
+ createChannelsGeneral();
+ updateChannelsGeneral();
+ for (int i = 0; i < numberOfLoadpoints; i++) {
+ createChannelsLoadpoint(i);
+ updateChannelsLoadpoint(i);
+ }
+ }
+ }
+
+ @Override
+ public void dispose() {
+ ScheduledFuture> statePollingJob = this.statePollingJob;
+ if (statePollingJob != null) {
+ statePollingJob.cancel(true);
+ this.statePollingJob = null;
+ }
+ }
+
+ // Utility functions
+ private void createChannelsGeneral() {
+ final String channelGroup = "general";
+ if (batteryConfigured) {
+ createChannel(CHANNEL_BATTERY_POWER, channelGroup, CHANNEL_TYPE_UID_BATTERY_POWER, "Number:Power");
+ createChannel(CHANNEL_BATTERY_SOC, channelGroup, CHANNEL_TYPE_UID_BATTERY_SOC, "Number:Dimensionless");
+ createChannel(CHANNEL_BATTERY_PRIORITY_SOC, channelGroup, CHANNEL_TYPE_UID_BATTERY_PRIORITY_SOC,
+ "Number:Dimensionless");
+ }
+ if (gridConfigured) {
+ createChannel(CHANNEL_GRID_POWER, channelGroup, CHANNEL_TYPE_UID_GRID_POWER, "Number:Power");
+ }
+ createChannel(CHANNEL_HOME_POWER, channelGroup, CHANNEL_TYPE_UID_HOME_POWER, "Number:Power");
+ if (pvConfigured) {
+ createChannel(CHANNEL_PV_POWER, channelGroup, CHANNEL_TYPE_UID_PV_POWER, "Number:Power");
+ }
+ }
+
+ private void createChannelsLoadpoint(int loadpointId) {
+ final String channelGroup = "loadpoint" + loadpointId;
+ createChannel(CHANNEL_LOADPOINT_ACTIVE_PHASES, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_ACTIVE_PHASES,
+ CoreItemFactory.NUMBER);
+ createChannel(CHANNEL_LOADPOINT_CHARGE_CURRENT, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_CHARGE_CURRENT,
+ "Number:ElectricCurrent");
+ createChannel(CHANNEL_LOADPOINT_CHARGE_DURATION, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_CHARGE_DURATION,
+ "Number:Time");
+ createChannel(CHANNEL_LOADPOINT_CHARGE_POWER, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_CHARGE_POWER,
+ "Number:Power");
+ createChannel(CHANNEL_LOADPOINT_CHARGE_REMAINING_DURATION, channelGroup,
+ CHANNEL_TYPE_UID_LOADPOINT_CHARGE_REMAINING_DURATION, "Number:Time");
+ createChannel(CHANNEL_LOADPOINT_CHARGE_REMAINING_ENERGY, channelGroup,
+ CHANNEL_TYPE_UID_LOADPOINT_CHARGE_REMAINING_ENERGY, "Number:Energy");
+ createChannel(CHANNEL_LOADPOINT_CHARGED_ENERGY, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_CHARGED_ENERGY,
+ "Number:Energy");
+ createChannel(CHANNEL_LOADPOINT_CHARGING, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_CHARGING,
+ CoreItemFactory.SWITCH);
+ createChannel(CHANNEL_LOADPOINT_CONNECTED, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_CONNECTED,
+ CoreItemFactory.SWITCH);
+ createChannel(CHANNEL_LOADPOINT_CONNECTED_DURATION, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_CONNECTED_DURATION,
+ "Number:Time");
+ createChannel(CHANNEL_LOADPOINT_ENABLED, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_ENABLED,
+ CoreItemFactory.SWITCH);
+ createChannel(CHANNEL_LOADPOINT_HAS_VEHICLE, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_HAS_VEHICLE,
+ CoreItemFactory.SWITCH);
+ createChannel(CHANNEL_LOADPOINT_MAX_CURRENT, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_MAX_CURRENT,
+ "Number:ElectricCurrent");
+ createChannel(CHANNEL_LOADPOINT_MIN_CURRENT, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_MIN_CURRENT,
+ "Number:ElectricCurrent");
+ createChannel(CHANNEL_LOADPOINT_MIN_SOC, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_MIN_SOC,
+ "Number:Dimensionless");
+ createChannel(CHANNEL_LOADPOINT_MODE, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_MODE, CoreItemFactory.STRING);
+ createChannel(CHANNEL_LOADPOINT_PHASES, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_PHASES,
+ CoreItemFactory.NUMBER);
+ createChannel(CHANNEL_LOADPOINT_TARGET_SOC, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_TARGET_SOC,
+ "Number:Dimensionless");
+ createChannel(CHANNEL_LOADPOINT_TARGET_TIME, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_TARGET_TIME,
+ CoreItemFactory.DATETIME);
+ createChannel(CHANNEL_LOADPOINT_TARGET_TIME_ENABLED, channelGroup,
+ CHANNEL_TYPE_UID_LOADPOINT_TARGET_TIME_ENABLED, CoreItemFactory.SWITCH);
+ createChannel(CHANNEL_LOADPOINT_TITLE, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_TITLE, CoreItemFactory.STRING);
+ createChannel(CHANNEL_LOADPOINT_VEHICLE_CAPACITY, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_CAPACITY,
+ "Number:Energy");
+ createChannel(CHANNEL_LOADPOINT_VEHICLE_ODOMETER, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_ODOMETER,
+ "Number:Length");
+ createChannel(CHANNEL_LOADPOINT_VEHICLE_PRESENT, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_PRESENT,
+ CoreItemFactory.SWITCH);
+ createChannel(CHANNEL_LOADPOINT_VEHICLE_RANGE, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_RANGE,
+ "Number:Length");
+ createChannel(CHANNEL_LOADPOINT_VEHICLE_SOC, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_SOC,
+ "Number:Dimensionless");
+ createChannel(CHANNEL_LOADPOINT_VEHICLE_TITLE, channelGroup, CHANNEL_TYPE_UID_LOADPOINT_VEHICLE_TITLE,
+ CoreItemFactory.STRING);
+ }
+
+ // Units and description for vars: https://docs.evcc.io/docs/reference/configuration/messaging/#msg
+ private void updateChannelsGeneral() {
+ Result result = this.result;
+ if (result == null) {
+ return;
+ }
+ ChannelUID channel;
+ boolean batteryConfigured = this.batteryConfigured;
+ if (batteryConfigured) {
+ double batteryPower = result.getBatteryPower();
+ channel = new ChannelUID(getThing().getUID(), "general", CHANNEL_BATTERY_POWER);
+ updateState(channel, new QuantityType<>(batteryPower, Units.WATT));
+ int batterySoC = result.getBatterySoC();
+ channel = new ChannelUID(getThing().getUID(), "general", CHANNEL_BATTERY_SOC);
+ updateState(channel, new QuantityType<>(batterySoC, Units.PERCENT));
+ int batteryPrioritySoC = result.getBatterySoC();
+ channel = new ChannelUID(getThing().getUID(), "general", CHANNEL_BATTERY_PRIORITY_SOC);
+ updateState(channel, new QuantityType<>(batteryPrioritySoC, Units.PERCENT));
+ }
+ boolean gridConfigured = this.gridConfigured;
+ if (gridConfigured) {
+ double gridPower = result.getGridPower();
+ channel = new ChannelUID(getThing().getUID(), "general", CHANNEL_GRID_POWER);
+ updateState(channel, new QuantityType<>(gridPower, Units.WATT));
+ }
+ double homePower = result.getHomePower();
+ channel = new ChannelUID(getThing().getUID(), "general", CHANNEL_HOME_POWER);
+ updateState(channel, new QuantityType<>(homePower, Units.WATT));
+ boolean pvConfigured = this.pvConfigured;
+ if (pvConfigured) {
+ double pvPower = result.getPvPower();
+ channel = new ChannelUID(getThing().getUID(), "general", CHANNEL_PV_POWER);
+ updateState(channel, new QuantityType<>(pvPower, Units.WATT));
+ }
+ }
+
+ private void updateChannelsLoadpoint(int loadpointId) {
+ Result result = this.result;
+ if (result == null) {
+ return;
+ }
+ String loadpointName = "loadpoint" + loadpointId;
+ ChannelUID channel;
+ Loadpoint loadpoint = result.getLoadpoints()[loadpointId];
+ int activePhases = loadpoint.getActivePhases();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_ACTIVE_PHASES);
+ updateState(channel, new DecimalType(activePhases));
+ double chargeCurrent = loadpoint.getChargeCurrent();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_CHARGE_CURRENT);
+ updateState(channel, new QuantityType<>(chargeCurrent, Units.AMPERE));
+ long chargeDuration = loadpoint.getChargeDuration();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_CHARGE_DURATION);
+ updateState(channel, new QuantityType<>(chargeDuration, MetricPrefix.NANO(Units.SECOND)));
+ double chargePower = loadpoint.getChargePower();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_CHARGE_POWER);
+ updateState(channel, new QuantityType<>(chargePower, Units.WATT));
+ long chargeRemainingDuration = loadpoint.getChargeRemainingDuration();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_CHARGE_REMAINING_DURATION);
+ updateState(channel, new QuantityType<>(chargeRemainingDuration, MetricPrefix.NANO(Units.SECOND)));
+ double chargeRemainingEnergy = loadpoint.getChargeRemainingEnergy();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_CHARGE_REMAINING_ENERGY);
+ updateState(channel, new QuantityType<>(chargeRemainingEnergy, Units.WATT_HOUR));
+ double chargedEnergy = loadpoint.getChargedEnergy();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_CHARGED_ENERGY);
+ updateState(channel, new QuantityType<>(chargedEnergy, Units.WATT_HOUR));
+ boolean charging = loadpoint.getCharging();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_CHARGING);
+ updateState(channel, OnOffType.from(charging));
+ boolean connected = loadpoint.getConnected();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_CONNECTED);
+ updateState(channel, OnOffType.from(connected));
+ long connectedDuration = loadpoint.getConnectedDuration();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_CONNECTED_DURATION);
+ updateState(channel, new QuantityType<>(connectedDuration, MetricPrefix.NANO(Units.SECOND)));
+ boolean enabled = loadpoint.getEnabled();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_ENABLED);
+ updateState(channel, OnOffType.from(enabled));
+ boolean hasVehicle = loadpoint.getHasVehicle();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_HAS_VEHICLE);
+ updateState(channel, OnOffType.from(hasVehicle));
+ double maxCurrent = loadpoint.getMaxCurrent();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_MAX_CURRENT);
+ updateState(channel, new QuantityType<>(maxCurrent, Units.AMPERE));
+ double minCurrent = loadpoint.getMinCurrent();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_MIN_CURRENT);
+ updateState(channel, new QuantityType<>(minCurrent, Units.AMPERE));
+ int minSoC = loadpoint.getMinSoC();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_MIN_SOC);
+ updateState(channel, new QuantityType<>(minSoC, Units.PERCENT));
+ String mode = loadpoint.getMode();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_MODE);
+ updateState(channel, new StringType(mode));
+ int phases = loadpoint.getPhases();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_PHASES);
+ updateState(channel, new DecimalType(phases));
+ targetSoC = loadpoint.getTargetSoC();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_TARGET_SOC);
+ updateState(channel, new QuantityType<>(targetSoC, Units.PERCENT));
+ String targetTime = loadpoint.getTargetTime();
+ if (targetTime == null) {
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_TARGET_TIME_ENABLED);
+ updateState(channel, OnOffType.OFF);
+ targetTimeEnabled = false;
+ } else {
+ this.targetTimeZDT = ZonedDateTime.parse(targetTime);
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_TARGET_TIME);
+ updateState(channel, new DateTimeType(targetTimeZDT));
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_TARGET_TIME_ENABLED);
+ updateState(channel, OnOffType.ON);
+ targetTimeEnabled = true;
+ }
+ String title = loadpoint.getTitle();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_TITLE);
+ updateState(channel, new StringType(title));
+ double vehicleCapacity = loadpoint.getVehicleCapacity();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_VEHICLE_CAPACITY);
+ updateState(channel, new QuantityType<>(vehicleCapacity, Units.WATT_HOUR));
+ double vehicleOdometer = loadpoint.getVehicleOdometer();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_VEHICLE_ODOMETER);
+ updateState(channel, new QuantityType<>(vehicleOdometer, MetricPrefix.KILO(SIUnits.METRE)));
+ boolean vehiclePresent = loadpoint.getVehiclePresent();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_VEHICLE_PRESENT);
+ updateState(channel, OnOffType.from(vehiclePresent));
+ long vehicleRange = loadpoint.getVehicleRange();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_VEHICLE_RANGE);
+ updateState(channel, new QuantityType<>(vehicleRange, MetricPrefix.KILO(SIUnits.METRE)));
+ int vehicleSoC = loadpoint.getVehicleSoC();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_VEHICLE_SOC);
+ updateState(channel, new QuantityType<>(vehicleSoC, Units.PERCENT));
+ String vehicleTitle = loadpoint.getVehicleTitle();
+ channel = new ChannelUID(getThing().getUID(), loadpointName, CHANNEL_LOADPOINT_VEHICLE_TITLE);
+ updateState(channel, new StringType(vehicleTitle));
+ }
+
+ private void createChannel(String channel, String channelGroupId, ChannelTypeUID channelTypeUID, String itemType) {
+ ChannelUID channelToCheck = new ChannelUID(thing.getUID(), channelGroupId, channel);
+ if (thing.getChannel(channelToCheck) == null) {
+ ThingBuilder thingBuilder = editThing();
+ Channel testchannel = ChannelBuilder
+ .create(new ChannelUID(getThing().getUID(), channelGroupId, channel), itemType)
+ .withType(channelTypeUID).build();
+ thingBuilder.withChannel(testchannel);
+ updateThing(thingBuilder.build());
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandlerFactory.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandlerFactory.java
new file mode 100644
index 00000000000..33ae00a5f54
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandlerFactory.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2010-2022 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;
+
+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.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.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link EvccHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Florian Hotze - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.evcc", service = ThingHandlerFactory.class)
+public class EvccHandlerFactory extends BaseThingHandlerFactory {
+
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_DEVICE);
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+ if (THING_TYPE_DEVICE.equals(thingTypeUID)) {
+ return new EvccHandler(thing);
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccAPI.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccAPI.java
new file mode 100644
index 00000000000..9cc4733408a
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccAPI.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2010-2022 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.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.evcc.internal.api.dto.Result;
+import org.openhab.binding.evcc.internal.api.dto.Status;
+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.JsonSyntaxException;
+
+/**
+ * The {@link EvccAPI} is responsible for API calls to evcc.
+ *
+ * @author Florian Hotze - Initial contribution
+ */
+@NonNullByDefault
+public class EvccAPI {
+ private final Logger logger = LoggerFactory.getLogger(EvccAPI.class);
+ private final Gson gson = new Gson();
+ private String host = "";
+
+ public EvccAPI(String host) {
+ this.host = host;
+ }
+
+ /**
+ * Make a HTTP request.
+ *
+ * @param url full request URL
+ * @param method reguest method, e.g. GET, POST
+ * @return the response body
+ * @throws {@link 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.
+ *
+ * @param host hostname of IP address of the evcc instance
+ * @return {@link Result} result object from API
+ * @throws {@link EvccApiException} if status request failed
+ */
+ public Result getResult() throws EvccApiException {
+ final String response = httpRequest(this.host + EVCC_REST_API + "state", "GET");
+ try {
+ Status status = gson.fromJson(response, Status.class);
+ if (status == null) {
+ throw new EvccApiException("Status is null");
+ }
+ return status.getResult();
+ } catch (JsonSyntaxException e) {
+ throw new EvccApiException("Error parsing response: " + response, e);
+ }
+ }
+
+ // Loadpoint specific API calls.
+ public String setMode(int loadpoint, String mode) throws EvccApiException {
+ return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/mode/" + mode, "POST");
+ }
+
+ public String setMinSoC(int loadpoint, int minSoC) throws EvccApiException {
+ return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/minsoc/" + minSoC, "POST");
+ }
+
+ public String setTargetSoC(int loadpoint, int targetSoC) throws EvccApiException {
+ return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/targetsoc/" + targetSoC, "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");
+ }
+
+ public String setTargetCharge(int loadpoint, int targetSoC, ZonedDateTime targetTime) throws EvccApiException {
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
+ return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/targetcharge/" + targetSoC + "/"
+ + targetTime.toLocalDateTime().format(formatter), "POST");
+ }
+
+ public String unsetTargetCharge(int loadpoint) throws EvccApiException {
+ return httpRequest(this.host + EVCC_REST_API + "loadpoints/" + loadpoint + "/targetcharge", "DELETE");
+ }
+}
diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccApiException.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccApiException.java
new file mode 100644
index 00000000000..f0af01dee3f
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccApiException.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2022 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);
+ }
+}
diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/dto/Loadpoint.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/dto/Loadpoint.java
new file mode 100644
index 00000000000..732af3a1a37
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/dto/Loadpoint.java
@@ -0,0 +1,316 @@
+/**
+ * Copyright (c) 2010-2022 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.91.
+ *
+ * @author Florian Hotze - Initial contribution
+ */
+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("activePhases")
+ private int activePhases;
+
+ @SerializedName("chargeCurrent")
+ private double chargeCurrent;
+
+ @SerializedName("chargeDuration")
+ private long chargeDuration;
+
+ @SerializedName("chargePower")
+ private double chargePower;
+
+ @SerializedName("chargeRemainingDuration")
+ private long chargeRemainingDuration;
+
+ @SerializedName("chargeRemainingEnergy")
+ private double chargeRemainingEnergy;
+
+ @SerializedName("chargedEnergy")
+ private double chargedEnergy;
+
+ @SerializedName("charging")
+ private boolean charging;
+
+ @SerializedName("connected")
+ private boolean connected;
+
+ @SerializedName("connectedDuration")
+ private long connectedDuration;
+
+ @SerializedName("enabled")
+ private boolean enabled;
+
+ @SerializedName("hasVehicle")
+ private boolean hasVehicle;
+
+ @SerializedName("loadpoint")
+ private int loadpoint;
+
+ @SerializedName("maxCurrent")
+ private double maxCurrent;
+
+ @SerializedName("minCurrent")
+ private double minCurrent;
+
+ @SerializedName("minSoC")
+ private int minSoC;
+
+ @SerializedName("mode")
+ private String mode;
+
+ @SerializedName("phases")
+ private int phases;
+
+ @SerializedName("pvAction")
+ private String pvAction;
+
+ @SerializedName("pvRemaining")
+ private long pvRemaining;
+
+ @SerializedName("targetSoC")
+ private int targetSoC;
+
+ @SerializedName("targetTime")
+ private String targetTime;
+
+ @SerializedName("title")
+ private String title;
+
+ @SerializedName("vehicleCapacity")
+ private long vehicleCapacity;
+
+ @SerializedName("vehicleOdometer")
+ private double vehicleOdometer;
+
+ @SerializedName("vehiclePresent")
+ private boolean vehiclePresent;
+
+ @SerializedName("vehicleRange")
+ private long vehicleRange;
+
+ @SerializedName("vehicleSoC")
+ private int vehicleSoC;
+
+ @SerializedName("vehicleTitle")
+ private String vehicleTitle;
+
+ /**
+ * @return number of active phases
+ */
+ public int getActivePhases() {
+ return activePhases;
+ }
+
+ /**
+ * @return charge current
+ */
+ public double getChargeCurrent() {
+ return chargeCurrent;
+ }
+
+ /**
+ * @return charge duration
+ */
+ public long getChargeDuration() {
+ return chargeDuration;
+ }
+
+ /**
+ * @return charge power
+ */
+ public double 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 double getChargeRemainingEnergy() {
+ return chargeRemainingEnergy;
+ }
+
+ /**
+ * @return charged energy
+ */
+ public double 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 whether vehicle is configured for loadpoint
+ */
+ public boolean getHasVehicle() {
+ return hasVehicle;
+ }
+
+ /**
+ * @return loadpoint id
+ */
+ public int getLoadpoint() {
+ return loadpoint;
+ }
+
+ /**
+ * @return maximum current
+ */
+ public double getMaxCurrent() {
+ return maxCurrent;
+ }
+
+ /**
+ * @return minimum current
+ */
+ public double getMinCurrent() {
+ return minCurrent;
+ }
+
+ /**
+ * @return minimum state of charge
+ */
+ public int getMinSoC() {
+ return minSoC;
+ }
+
+ /**
+ * @return charging mode: off, now, minpv, pv
+ */
+ public String getMode() {
+ return mode;
+ }
+
+ /**
+ * @return number of enabled phases
+ */
+ public int getPhases() {
+ return phases;
+ }
+
+ /**
+ * @return the pv action
+ */
+ public String getPvAction() {
+ return pvAction;
+ }
+
+ /**
+ * @return the pv remaining
+ */
+ public long getPvRemaining() {
+ return pvRemaining;
+ }
+
+ /**
+ * @return target state of charge (SoC)
+ */
+ public int getTargetSoC() {
+ return targetSoC;
+ }
+
+ /**
+ * @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 capacity
+ */
+ public double getVehicleCapacity() {
+ return vehicleCapacity;
+ }
+
+ /**
+ * @return vehicle's odometer
+ */
+ public double getVehicleOdometer() {
+ return vehicleOdometer;
+ }
+
+ /**
+ * @return whether evcc is able to get data from vehicle
+ */
+ public boolean getVehiclePresent() {
+ return vehiclePresent;
+ }
+
+ /**
+ * @return vehicle's range
+ */
+ public long getVehicleRange() {
+ return vehicleRange;
+ }
+
+ /**
+ * @return vehicle's state of charge (SoC)
+ */
+ public int getVehicleSoC() {
+ return vehicleSoC;
+ }
+
+ /**
+ * @return vehicle's title/name
+ */
+ public String getVehicleTitle() {
+ return vehicleTitle;
+ }
+}
diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/dto/Result.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/dto/Result.java
new file mode 100644
index 00000000000..6a613e8b235
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/dto/Result.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright (c) 2010-2022 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 result object of the status response (/api/state).
+ * This DTO was written for evcc version 0.91.
+ *
+ * @author Florian Hotze - Initial contribution
+ */
+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
+
+ // TO DO LATER
+ // @SerializedName("auth")
+ // private Auth auth;
+
+ @SerializedName("batteryConfigured")
+ private boolean batteryConfigured;
+
+ @SerializedName("batteryPower")
+ private double batteryPower;
+
+ @SerializedName("batterySoC")
+ private int batterySoC;
+
+ @SerializedName("gridConfigured")
+ private boolean gridConfigured;
+
+ @SerializedName("gridPower")
+ private double gridPower;
+
+ @SerializedName("homePower")
+ private double homePower;
+
+ @SerializedName("loadpoints")
+ private Loadpoint[] loadpoints;
+
+ @SerializedName("prioritySoC")
+ private double batteryPrioritySoC;
+
+ @SerializedName("pvConfigured")
+ private boolean pvConfigured;
+
+ @SerializedName("pvPower")
+ private double pvPower;
+
+ @SerializedName("siteTitle")
+ private String siteTitle;
+
+ /**
+ * @return whether battery is configured
+ */
+ public boolean getBatteryConfigured() {
+ return batteryConfigured;
+ }
+
+ /**
+ * @return battery's power
+ */
+ public double getBatteryPower() {
+ return batteryPower;
+ }
+
+ /**
+ * @return battery's priority state of charge
+ */
+ public double getBatteryPrioritySoC() {
+ return batteryPrioritySoC;
+ }
+
+ /**
+ * @return battery's state of charge
+ */
+ public int getBatterySoC() {
+ return batterySoC;
+ }
+
+ /**
+ * @return whether grid is configured
+ */
+ public boolean getGridConfigured() {
+ return gridConfigured;
+ }
+
+ /**
+ * @return grid's power
+ */
+ public double getGridPower() {
+ return gridPower;
+ }
+
+ /**
+ * @return home's power
+ */
+ public double getHomePower() {
+ return homePower;
+ }
+
+ /**
+ * @return all configured loadpoints
+ */
+ public Loadpoint[] getLoadpoints() {
+ return loadpoints;
+ }
+
+ /**
+ * @return whether pv is configured
+ */
+ public boolean getPvConfigured() {
+ return pvConfigured;
+ }
+
+ /**
+ * @return pv's power
+ */
+ public double getPvPower() {
+ return pvPower;
+ }
+
+ /**
+ * @return site's title/name
+ */
+ public String getSiteTitle() {
+ return siteTitle;
+ }
+}
diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/dto/Status.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/dto/Status.java
new file mode 100644
index 00000000000..b70e1f9e83f
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/dto/Status.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2010-2022 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.91.
+ *
+ * @author Florian Hotze - Initial contribution
+ */
+public class Status {
+
+ @SerializedName("result")
+ private Result result;
+
+ public Result getResult() {
+ return result;
+ }
+}
diff --git a/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/binding/binding.xml
new file mode 100644
index 00000000000..14945a18ebb
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/binding/binding.xml
@@ -0,0 +1,9 @@
+
+
+
+ evcc Binding
+ This is the binding for evcc (electric vehicle charging control) - soak up the sun.
+
+
diff --git a/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/i18n/evcc.properties b/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/i18n/evcc.properties
new file mode 100644
index 00000000000..e63da7c32ad
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/i18n/evcc.properties
@@ -0,0 +1,111 @@
+# binding
+
+binding.evcc.name = evcc Binding
+binding.evcc.description = This is the binding for evcc (electric vehicle charging control) - soak up the sun.
+
+# thing types
+
+thing-type.evcc.device.label = evcc installation
+thing-type.evcc.device.description = A running evcc installation
+
+# 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
+
+# channel group types
+
+channel-group-type.evcc.general.label = General data
+channel-group-type.evcc.loadpoint.label = Loadpoint
+
+# 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.batteryPower.label = Battery Power
+channel-type.evcc.batteryPower.description = Current power from battery
+channel-type.evcc.batteryPrioritySoC.label = Battery Priority SoC
+channel-type.evcc.batteryPrioritySoC.description = State of Charge for which the battery has priority over charging the ev when charging mode is "pv".
+channel-type.evcc.batterySoC.label = Battery SoC
+channel-type.evcc.batterySoC.description = Current State of Charge of battery
+channel-type.evcc.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 target SoC is reached
+channel-type.evcc.chargeRemainingEnergy.label = Charging remaining energy
+channel-type.evcc.chargeRemainingEnergy.description = Remaining energy until target SoC is reached
+channel-type.evcc.chargedEnergy.label = Charged energy
+channel-type.evcc.chargedEnergy.description = Energy charged since plugged-in
+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.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.hasVehicle.label = Loadpoint has vehicle configuration
+channel-type.evcc.hasVehicle.description = Whether vehicle is configured for loadpoint
+channel-type.evcc.hasVehicle.state.option.ON = Configured
+channel-type.evcc.hasVehicle.state.option.OFF = Not configured
+channel-type.evcc.homePower.label = Home Power
+channel-type.evcc.homePower.description = Current power taken by home
+channel-type.evcc.maxCurrent.label = Charging max current
+channel-type.evcc.maxCurrent.description = Maximum amperage per connected phase with which the car should be charged
+channel-type.evcc.minCurrent.label = Charging min current
+channel-type.evcc.minCurrent.description = Minimum amperage per connected phase with which the car should be charged
+channel-type.evcc.minSoC.label = Charging min SoC
+channel-type.evcc.minSoC.description = Charge immediately with maximum power up to the defined SoC, if the charge mode is not set to "off"
+channel-type.evcc.mode.label = Charging mode
+channel-type.evcc.mode.description = Charging mode: "off", "now", "minpv", "pv"
+channel-type.evcc.mode.state.option.off = Off
+channel-type.evcc.mode.state.option.now = Now
+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.pvPower.label = PV Power
+channel-type.evcc.pvPower.description = Current power from photovoltaik
+channel-type.evcc.targetSoC.label = Charging target SoC
+channel-type.evcc.targetSoC.description = Until which state of charge (SoC) should the vehicle be charged
+channel-type.evcc.targetTime.label = Charging target time
+channel-type.evcc.targetTime.description = When the target SoC should be reached
+channel-type.evcc.targetTimeEnabled.label = Charging target time enabled
+channel-type.evcc.targetTimeEnabled.description = Target time for charging enabled
+channel-type.evcc.targetTimeEnabled.state.option.ON = Enabled
+channel-type.evcc.targetTimeEnabled.state.option.OFF = Disabled
+channel-type.evcc.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.vehicleOdometer.label = Vehicle odometer
+channel-type.evcc.vehicleOdometer.description = Total distance travelled by EV
+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.vehicleTitle.label = Vehicle title
+channel-type.evcc.vehicleTitle.description = Name of EV
+
+# channel types
+
+offline.configuration-error.no-host = No host configured
+offline.communication-error.request-failed = Request failed
diff --git a/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/i18n/evcc_de.properties b/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/i18n/evcc_de.properties
new file mode 100644
index 00000000000..8165a05b6ad
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/i18n/evcc_de.properties
@@ -0,0 +1,111 @@
+# binding
+
+binding.evcc.name = evcc Binding
+binding.evcc.description = Dies ist das Binding für evcc (electric vehicle charging control) - Sonne tanken.
+
+# thing types
+
+thing-type.evcc.device.label = evcc Installation
+thing-type.evcc.device.description = Eine aktive evcc Installation.
+
+# thing types config
+
+thing-type.config.evcc.device.refreshInterval.label = Aktualisierungs-Interval
+thing-type.config.evcc.device.refreshInterval.description = Interval in Sekunden, in dem der Status abgerufen wird
+thing-type.config.evcc.device.url.label = URL
+thing-type.config.evcc.device.url.description = URL der evcc Web-Oberfläche, z.B. https://demo.evcc.io
+
+# channel group types
+
+channel-group-type.evcc.general.label = Generelle Daten
+channel-group-type.evcc.loadpoint.label = Ladepunkt
+
+# channel types
+
+channel-type.evcc.activePhases.label = Laden aktive Phasen
+channel-type.evcc.activePhases.description = Anzahl von Phasen, die beim Laden aktiv sind
+channel-type.evcc.batteryPower.label = Batterie Leistung
+channel-type.evcc.batteryPower.description = Aktuelle Leistung der Batterie
+channel-type.evcc.batteryPrioritySoC.label = Batterie Priorität-Ladestand
+channel-type.evcc.batteryPrioritySoC.description = Mindest-Ladestand der Batterie, unter dem die Batterie Priorität vor PV-Laden hat
+channel-type.evcc.batterySoC.label = Batterie Ladestand
+channel-type.evcc.batterySoC.description = Aktueller Ladestand der Batterie
+channel-type.evcc.chargeCurrent.label = Ladestromstärke
+channel-type.evcc.chargeCurrent.description = Aktuelle Stromstärke je Phases beim Laden
+channel-type.evcc.chargeDuration.label = Ladedauer
+channel-type.evcc.chargeDuration.description = Bisherige Ladedauer
+channel-type.evcc.chargePower.label = Ladeleistung
+channel-type.evcc.chargePower.description = Aktuelle Ladeleistung
+channel-type.evcc.chargeRemainingDuration.label = Verbleibende Ladedauer
+channel-type.evcc.chargeRemainingDuration.description = Verbleibende Dauer bis der Ziel-Füllstand erreicht ist
+channel-type.evcc.chargeRemainingEnergy.label = Verbleinbende Ladeenergie
+channel-type.evcc.chargeRemainingEnergy.description = Verbleibende Energie bis der Ziel-Füllstand erreicht ist
+channel-type.evcc.chargedEnergy.label = Geladene Energie
+channel-type.evcc.chargedEnergy.description = Geladene Energie seit das Fahrzeug angeschlossen ist
+channel-type.evcc.charging.label = Ladestatus
+channel-type.evcc.charging.description = Status Ladepunkt
+channel-type.evcc.charging.state.option.ON = Laden
+channel-type.evcc.charging.state.option.OFF = Nicht laden
+channel-type.evcc.enabled.label = Status Laden
+channel-type.evcc.enabled.description = Laden ist freigeschaltet/aktiviert (Modus ist nicht "off")
+channel-type.evcc.enabled.state.option.ON = Aktiviert
+channel-type.evcc.enabled.state.option.OFF = Deaktiviert
+channel-type.evcc.gridPower.label = Netzleistung
+channel-type.evcc.gridPower.description = Aktuelle Leistung vom Stromnetz (negativer Wert means bedeutet Einspeisung)
+channel-type.evcc.hasVehicle.label = Fahrzeug konfiguriert
+channel-type.evcc.hasVehicle.description = Fahrzeug ist für Ladepunkt konfiguriert
+channel-type.evcc.hasVehicle.state.option.ON = Konfiguriert
+channel-type.evcc.hasVehicle.state.option.OFF = Nicht konfiguriert
+channel-type.evcc.homePower.label = Leistungaufnahme Gebäude
+channel-type.evcc.homePower.description = Aktuelle Leistungsaufnahme des Gebäudes
+channel-type.evcc.maxCurrent.label = Laden maximale Stromstärke
+channel-type.evcc.maxCurrent.description = Maximale Stromstärke je Phases mit der das Auto geladen werden soll
+channel-type.evcc.minCurrent.label = Laden minimale Stromstärke
+channel-type.evcc.minCurrent.description = Minimale Stromstärke je Phases mit der das Auto geladen werden soll
+channel-type.evcc.minSoC.label = Minimaler Ladestand Fahrzeug
+channel-type.evcc.minSoC.description = Sofortiges Laden zu diesem Ladestand, wenn der Lademodus nicht "off" ist
+channel-type.evcc.mode.label = Lademodus
+channel-type.evcc.mode.description = Lademodus: "off", "now", "minpv", "pv"
+channel-type.evcc.mode.state.option.off = Aus
+channel-type.evcc.mode.state.option.now = Sofort
+channel-type.evcc.mode.state.option.minpv = Min. + PV
+channel-type.evcc.mode.state.option.pv = Nur PV
+channel-type.evcc.phases.label = Laden aktivierte Phasen
+channel-type.evcc.phases.description = Die maximale Anzahl an nutzbaren Phasen
+channel-type.evcc.pvPower.label = Solar Leistung
+channel-type.evcc.pvPower.description = Aktuelle Leistung von der Solar-Anlage
+channel-type.evcc.targetSoC.label = Ziel-Ladestand
+channel-type.evcc.targetSoC.description = Bis zu welchem Ladestand das Fahrzeug geladen werden soll
+channel-type.evcc.targetTime.label = Laden Ziel-Zeit
+channel-type.evcc.targetTime.description = Wann der Ziel-Ladestand erreicht werden soll
+channel-type.evcc.targetTimeEnabled.label = Laden Ziel-Zeit aktiviert
+channel-type.evcc.targetTimeEnabled.description = Ziel-Zeit für das Laden aktiviert
+channel-type.evcc.targetTimeEnabled.state.option.ON = aktiviert
+channel-type.evcc.targetTimeEnabled.state.option.OFF = deaktiviert
+channel-type.evcc.title.label = Ladepunkt Name
+channel-type.evcc.title.description = Name des Ladepunkts
+channel-type.evcc.vehicleCapacity.label = Fahrzeug Kapazität
+channel-type.evcc.vehicleCapacity.description = Kapazität der Fahrzeug-Batterie
+channel-type.evcc.vehicleConnected.label = Fahrzeug verbunden
+channel-type.evcc.vehicleConnected.description = Ob ein Fahrzeug mit dem Ladepunkt verbunden ist
+channel-type.evcc.vehicleConnected.state.option.ON = Verbunden
+channel-type.evcc.vehicleConnected.state.option.OFF = Nicht verbunden
+channel-type.evcc.vehicleConnectedDuration.label = Dauer Fahrzeug verbunden
+channel-type.evcc.vehicleConnectedDuration.description = Dauer seit der das Fahrzeug mit dem Ladepunkt verbunden ist
+channel-type.evcc.vehicleOdometer.label = Fahrzeug Kilometer-Zähler
+channel-type.evcc.vehicleOdometer.description = Gesamtstrecke die vom Fahrzeug zurückgelegt wurde
+channel-type.evcc.vehiclePresent.label = Fahrzeug Daten-Zugriff
+channel-type.evcc.vehiclePresent.description = Ob evcc auf die Fahrzeug-Daten zugreifen kann
+channel-type.evcc.vehiclePresent.state.option.ON = Daten-Zugriff
+channel-type.evcc.vehiclePresent.state.option.OFF = Kein Daten-Zugriff
+channel-type.evcc.vehicleRange.label = Fahrzeug Reichweite
+channel-type.evcc.vehicleRange.description = Fahrzeug-Batterie Reichweite
+channel-type.evcc.vehicleSoC.label = Fahrzeug Ladestand
+channel-type.evcc.vehicleSoC.description = Aktueller Ladestand der Fahrzeug-Batterie
+channel-type.evcc.vehicleTitle.label = Fahrzeug Name
+channel-type.evcc.vehicleTitle.description = Name des Fahrzeugs
+
+# channel types
+
+offline.configuration-error.no-host = Kein Host konfiguriert
+offline.communication-error.request-failed = Anfrage fehlgeschlagen
diff --git a/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 00000000000..ecbf9481760
--- /dev/null
+++ b/bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,327 @@
+
+
+
+
+
+ evcc installation
+ A running evcc installation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ network-address
+ URL
+ URL of evcc web UI, e.g. https://demo.evcc.io
+
+
+ Refresh Interval
+ Interval the status is polled in seconds.
+ 60
+ true
+
+
+
+
+
+ General data
+
+
+ Loadpoint
+
+
+
+
+ Number:Power
+ Battery Power
+ Current power from battery
+ Energy
+
+
+
+ Number:Dimensionless
+ Battery SoC
+ Current State of Charge of battery
+ batterylevel
+
+
+
+ Number:Dimensionless
+ Battery Priority SoC
+ State of Charge for which the battery has priority over charging the ev when charging mode is "pv".
+ batterylevel
+
+
+
+ Number:Power
+ Grid Power
+ Current power from grid (negative means feed-in)
+ Energy
+
+
+
+ Number:Power
+ Home Power
+ Current power taken by home
+ Energy
+
+
+
+ Number:Power
+ PV Power
+ Current power from photovoltaik
+ Energy
+
+
+
+
+
+ Number
+ Charging active phases
+ Current number of active phases while charging
+
+
+
+
+ Number:ElectricCurrent
+ Charging current
+ Current amperage per connected phase while charging
+ Energy
+
+
+
+ Number:Time
+ Charging duration
+ Charging duration
+ Time
+
+
+
+ Number:Power
+ Charging power
+ Current power of charging
+ Energy
+
+
+
+ Number:Time
+ Charging remaining duration
+ Remaining duration until target SoC is reached
+ Time
+
+
+
+ Number:Energy
+ Charging remaining energy
+ Remaining energy until target SoC is reached
+ Energy
+
+
+
+ Number:Energy
+ Charged energy
+ Energy charged since plugged-in
+ Energy
+
+
+
+ Switch
+ Charging state
+ Loadpoint is currently charging
+ Energy
+
+
+ Charging
+ Not charging
+
+
+
+
+ Switch
+ Charging enabled
+ Charging enabled (mode not "off")
+ Switch
+
+
+ Enabled
+ Disabled
+
+
+
+
+ Switch
+ Loadpoint has vehicle configuration
+ Whether vehicle is configured for loadpoint
+ Switch
+
+
+ Configured
+ Not configured
+
+
+
+
+ Number:ElectricCurrent
+ Charging max current
+ Maximum amperage per connected phase with which the car should be charged
+ Energy
+
+ veto
+
+
+ Number:ElectricCurrent
+ Charging min current
+ Minimum amperage per connected phase with which the car should be charged
+ Energy
+
+ veto
+
+
+ Number:Dimensionless
+ Charging min SoC
+ Charge immediately with maximum power up to the defined SoC, if the charge mode is not set to "off"
+ Batterylevel
+
+ veto
+
+
+ String
+ Charging mode
+ Charging mode: "off", "now", "minpv", "pv"
+ String
+
+
+ Off
+ Now
+ Min + PV
+ Only PV
+
+
+ veto
+
+
+ Number
+ Charging enabled phases
+ The maximum number of phases which can be used
+ Energy
+
+ veto
+
+
+ Number:Dimensionless
+ Charging target SoC
+ Until which state of charge (SoC) should the vehicle be charged
+ Batterylevel
+
+ veto
+
+
+ DateTime
+ Charging target time
+ When the target SoC should be reached
+ Time
+
+ veto
+
+
+ Switch
+ Charging target time enabled
+ Target time for charging enabled
+ Switch
+
+
+ Enabled
+ Disabled
+
+
+ veto
+
+
+ String
+ Loadpoint title
+ Title of loadpoint
+ Text
+
+
+
+ Switch
+ Vehicle connected
+ Whether vehicle is connected to loadpoint
+ Switch
+
+
+ Connected
+ Not connected
+
+
+
+
+ Number:Time
+ Vehicle connected duration
+ Duration the vehicle is connected to loadpoint
+ Time
+
+
+
+ Number:Energy
+ Vehicle capacity
+ Capacity of EV battery
+ Energy
+
+
+
+ Number:Length
+ Vehicle odometer
+ Total distance travelled by EV
+
+
+
+
+ Switch
+ Vehicle data access
+ Whether evcc is able to get data from vehicle
+ Switch
+
+
+ Data access
+ No data access
+
+
+
+
+ Number:Length
+ Vehicle range
+ Battery range for EV
+
+
+
+
+ Number:Dimensionless
+ Vehicle SoC
+ Current State of Charge of EV
+ Batterylevel
+
+
+
+ String
+ Vehicle title
+ Name of EV
+ Text
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index c2274f6475a..3487366834b 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -122,6 +122,7 @@
org.openhab.binding.enturno
org.openhab.binding.epsonprojector
org.openhab.binding.etherrain
+ org.openhab.binding.evcc
org.openhab.binding.evohome
org.openhab.binding.exec
org.openhab.binding.feed