[evcc] Initial contribution (#12611)

Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
This commit is contained in:
Florian Hotze 2022-05-12 19:31:45 +02:00 committed by GitHub
parent 9c0541c2c9
commit 2e1fbdd86f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 2086 additions and 0 deletions

View File

@ -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

View File

@ -441,6 +441,11 @@
<artifactId>org.openhab.binding.etherrain</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.evcc</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.evohome</artifactId>

View File

@ -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

View File

@ -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]" <energy> {channel="evcc:device:demo:general#batteryPower"}
Number:Dimensionless evcc_demo_batterySoC "Battery SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:general#batterySoC"}
Number:Dimensionless evcc_demo_batteryPrioritySoC "Battery Priority SoC [%d %%]" <batterylevel> {channel="evcc:device:demo:general#batteryPrioritySoC"}
Number:Power evcc_demo_gridPower "Grid Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#gridPower"}
Number:Power evcc_demo_homePower "Home Power [%.1f kW]" <energy> {channel="evcc:device:demo:general#homePower"}
Number:Power evcc_demo_pvPower "PV Power [%.1f kW]" <energy> {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]" <energy> {channel="evcc:device:demo:loadpoint0#chargeCurrent"}
Number:Time evcc_demo_loadpoint0_chargeDuration "Charging duration [%1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0#chargeDuration"}
Number:Time evcc_demo_loadpoint0_chargeRemainingDuration "Charging remaining duration [%1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0#chargeRemainingDuration"}
Number:Energy evcc_demo_loadpoint0_chargeRemainingEnergy "Charging remaining energy [%.1f kWh]" <energy> {channel="evcc:device:demo:loadpoint0#chargeRemainingEnergy"}
Number:Power evcc_demo_loadpoint0_chargePower "Charging power [%.1f kW]" <energy> {channel="evcc:device:demo:loadpoint0#chargePower"}
Number:Energy evcc_demo_loadpoint0_chargedEnergy "Charged energy [%.1f kWh]" <energy> {channel="evcc:device:demo:loadpoint0#chargedEnergy"}
Switch evcc_demo_loadpoint0_charging "Currently charging [%s]" <battery> {channel="evcc:device:demo:loadpoint0#charging"}
Switch evcc_demo_loadpoint0_enabled "Charging enabled [%s]" <switch> {channel="evcc:device:demo:loadpoint0#enabled"}
Switch evcc_demo_loadpoint0_hasVehicle "Vehicle configured [%s]" <switch> {channel="evcc:device:demo:loadpoint0#hasVehicle"}
Number:ElectricCurrent evcc_demo_loadpoint0_maxCurrent "Maximum current [%.0f A]" <energy> {channel="evcc:device:demo:loadpoint0#maxCurrent"}
Number:ElectricCurrent evcc_demo_loadpoint0_minCurrent "Minimum current [%.0f A]" <energy> {channel="evcc:device:demo:loadpoint0#minCurrent"}
Number:Dimensionless evcc_demo_loadpoint0_minSoC "Minimum SoC [%d %%]" <batterylevel> {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 %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0#targetSoC"}
DateTime evcc_demo_loadpoint0_targetTime "Target time [%1$td.%1$tm.%1$tY, %1$tH:%1$tM]" <time> {channel="evcc:device:demo:loadpoint0#targetTime"}
Switch evcc_demo_loadpoint0_targetTimeEnabled "Target time enabled [%s]" <switch> {channel="evcc:device:demo:loadpoint0#targetTimeEnabled"}
String evcc_demo_loadpoint0_title "Loadpoint title [%s]" <text> {channel="evcc:device:demo:loadpoint0#title"}
// Vehicle on loadpoint
Switch evcc_demo_loadpoint0_vehicleConnected "Vehicle connected [%s]" <switch> {channel="evcc:device:demo:loadpoint0#vehicleConnected"}
Number:Time evcc_demo_loadpoint0_vehicleConnectedDuration "Vehicle connected duration [%.1f h]" <time> {channel="evcc:device:demo:loadpoint0#vehicleConnectedDuration"}
Number:Energy evcc_demo_loadpoint0_vehicleCapacity "Vehicle capacity [%.0f kWH]" <batterylevel> {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]" <switch> {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 %%]" <batterylevel> {channel="evcc:device:demo:loadpoint0#vehicleSoC"}
String evcc_demo_loadpoint0_vehicleName "Vehicle name [%s]" <text> {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
}
}
}
```

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.evcc</artifactId>
<name>openHAB Add-ons :: Bundles :: evcc Binding</name>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.evcc-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-evcc" description="evcc Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.evcc/${project.version}</bundle>
</feature>
</features>

View File

@ -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/";
}

View File

@ -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;
}

View File

@ -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());
}
}
}

View File

@ -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<ThingTypeUID> 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;
}
}

View File

@ -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");
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="evcc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>evcc Binding</name>
<description>This is the binding for evcc (electric vehicle charging control) - soak up the sun.</description>
</binding:binding>

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,327 @@
<?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>
<channel-groups>
<channel-group id="general" typeId="general"/>
<channel-group id="loadpoint0" typeId="loadpoint"/>
<channel-group id="loadpoint1" typeId="loadpoint"/>
<channel-group id="loadpoint2" typeId="loadpoint"/>
<channel-group id="loadpoint3" typeId="loadpoint"/>
<channel-group id="loadpoint4" typeId="loadpoint"/>
<channel-group id="loadpoint5" typeId="loadpoint"/>
<channel-group id="loadpoint6" typeId="loadpoint"/>
<channel-group id="loadpoint7" typeId="loadpoint"/>
<channel-group id="loadpoint8" typeId="loadpoint"/>
<channel-group id="loadpoint9" typeId="loadpoint"/>
</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="loadpoint">
<label>Loadpoint</label>
</channel-group-type>
<!-- Units and description on: https://docs.evcc.io/docs/reference/configuration/messaging/#msg -->
<channel-type id="batteryPower">
<item-type>Number:Power</item-type>
<label>Battery Power</label>
<description>Current power from battery</description>
<category>Energy</category>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="batterySoC">
<item-type>Number:Dimensionless</item-type>
<label>Battery SoC</label>
<description>Current State of Charge of battery</description>
<category>batterylevel</category>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="batteryPrioritySoC">
<item-type>Number:Dimensionless</item-type>
<label>Battery Priority SoC</label>
<description>State of Charge for which the battery has priority over charging the ev when charging mode is "pv".</description>
<category>batterylevel</category>
<state pattern="%.0f %unit%" readOnly="true"/>
</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>
<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>
<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>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<!-- Channels 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>
<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>
<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>
<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 target SoC is reached</description>
<category>Time</category>
<state pattern="%.1f min" readOnly="true"/>
</channel-type>
<channel-type id="chargeRemainingEnergy">
<item-type>Number:Energy</item-type>
<label>Charging remaining energy</label>
<description>Remaining energy until target SoC is reached</description>
<category>Energy</category>
<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>
<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>
<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>
<state readOnly="true">
<options>
<option value="ON">Enabled</option>
<option value="OFF">Disabled</option>
</options>
</state>
</channel-type>
<channel-type id="hasVehicle">
<item-type>Switch</item-type>
<label>Loadpoint has vehicle configuration</label>
<description>Whether vehicle is configured for loadpoint</description>
<category>Switch</category>
<state readOnly="true">
<options>
<option value="ON">Configured</option>
<option value="OFF">Not configured</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="minSoC">
<item-type>Number:Dimensionless</item-type>
<label>Charging min SoC</label>
<description>Charge immediately with maximum power up to the defined SoC, if the charge mode is not set to "off"</description>
<category>Batterylevel</category>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="mode">
<item-type>String</item-type>
<label>Charging mode</label>
<description>Charging mode: "off", "now", "minpv", "pv"</description>
<category>String</category>
<state readOnly="false">
<options>
<option value="off">Off</option>
<option value="now">Now</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="targetSoC">
<item-type>Number:Dimensionless</item-type>
<label>Charging target SoC</label>
<description>Until which state of charge (SoC) should the vehicle be charged</description>
<category>Batterylevel</category>
<state min="0" step="1" max="100" pattern="%.0f %unit%" readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="targetTime">
<item-type>DateTime</item-type>
<label>Charging target time</label>
<description>When the target SoC should be reached</description>
<category>Time</category>
<state readOnly="false"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="targetTimeEnabled">
<item-type>Switch</item-type>
<label>Charging target time enabled</label>
<description>Target time for charging enabled</description>
<category>Switch</category>
<state readOnly="false">
<options>
<option value="ON">Enabled</option>
<option value="OFF">Disabled</option>
</options>
</state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="title">
<item-type>String</item-type>
<label>Loadpoint title</label>
<description>Title of loadpoint</description>
<category>Text</category>
<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>
<state pattern="%.1f %unit%" readOnly="true"/>
</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>
<state pattern="%.0f %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></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>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="vehicleSoC">
<item-type>Number:Dimensionless</item-type>
<label>Vehicle SoC</label>
<description>Current State of Charge of EV</description>
<category>Batterylevel</category>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="vehicleTitle">
<item-type>String</item-type>
<label>Vehicle title</label>
<description>Name of EV</description>
<category>Text</category>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -122,6 +122,7 @@
<module>org.openhab.binding.enturno</module>
<module>org.openhab.binding.epsonprojector</module>
<module>org.openhab.binding.etherrain</module>
<module>org.openhab.binding.evcc</module>
<module>org.openhab.binding.evohome</module>
<module>org.openhab.binding.exec</module>
<module>org.openhab.binding.feed</module>