[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
20 changed files with 2086 additions and 0 deletions

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>