## Supported Things
-The Keba KeContact P20 and P30 stations are supported by this binding, the thing type id is `kecontact`.
-
+The Keba KeContact P20 and P30 stations which are providing the UDP interface (P20 LSA+ socket, P30 c-series and x-series) are supported by this binding, the thing type id is `kecontact`.
## Thing Configuration
-The Keba KeContact P20/30 requires the ip address as the configuration parameter `ipAddress`. Optionally, a refresh interval (in seconds) can be defined as parameter `refreshInterval` that defines the polling of values from the charging station.
-
+The Keba KeContact P20/30 requires the IP address as the configuration parameter `ipAddress`.
+Optionally, a refresh interval (in seconds) can be defined as parameter `refreshInterval` that defines the polling of values from the charging station.
## Channels
All devices support the following channels:
-| Channel ID | Item Type | Read-only | Description |
-|--------------------|-----------|-----------|------------------------------------------------------------------------|
-| state | Number | yes | current operational state of the wallbox |
-| enabled | Switch | no | activation state of the wallbox |
-| maxpresetcurrent | Number | no | maximum current the charging station should deliver to the EV |
-| power | Number | yes | active power delivered by the charging station |
-| wallbox | Switch | yes | plug state of wallbox |
-| vehicle | Switch | yes | plug state of vehicle |
-| locked | Switch | yes | lock state of plug at vehicle |
-| I1/2/3 | Number | yes | current for the given phase |
-| U1/2/3 | Number | yes | voltage for the given phase |
-| output | Switch | no | state of the X1 relais |
-| input | Switch | yes | state of the X2 contact |
-| display | String | yes | display text on wallbox |
-| error1 | String | yes | error code state 1, if in error (see the KeContact FAQ) |
-| error2 | String | yes | error code state 2, if in error (see the KeContact FAQ) |
-| maxsystemcurrent | Number | yes | maximum current the wallbox can deliver |
-| failsafecurrent | Number | yes | maximum current the wallbox can deliver, if network is lost |
-| uptime | DateTime | yes | system uptime since the last reset of the wallbox |
-| sessionconsumption | Number | yes | energy delivered in current session |
-| totalconsumption | Number | yes | total energy delivered since the last reset of the wallbox |
-| authreq | Switch | yes | authentication required |
-| authon | Switch | yes | authentication enabled |
-| sessionrfidtag | String | yes | RFID tag used for the last charging session |
-| sessionrfidclass | String | yes | RFID tag class used for the last charging session |
-| sessionid | Number | yes | session ID of the last charging session |
-| setenergylimit | Number | no | set an energy limit for an already running or the next charging session|
-| authenticate | String | no | authenticate and start a session using RFID tag+RFID class |
+| Channel ID | Item Type | Read-only | Description |
+|---------------------------|---------------------------|-----------|---------------------------------------------------------------------------|
+| state | Number | yes | current operational state of the wallbox |
+| enabled | Switch | no | activation state of the wallbox |
+| maxpresetcurrent | Number:ElectricCurrent | no | maximum current the charging station should deliver to the EV in A |
+| maxpresetcurrentrange | Number:Dimensionless | no | maximum current the charging station should deliver to the EV in % |
+| power | Number:Power | yes | active power delivered by the charging station |
+| wallbox | Switch | yes | plug state of wallbox |
+| vehicle | Switch | yes | plug state of vehicle |
+| locked | Switch | yes | lock state of plug at vehicle |
+| I1/2/3 | Number:ElectricCurrent | yes | current for the given phase |
+| U1/2/3 | Number:ElectricPotential | yes | voltage for the given phase |
+| output | Switch | no | state of the X1 relais |
+| input | Switch | yes | state of the X2 contact |
+| display | String | no | display text on wallbox |
+| error1 | String | yes | error code state 1, if in error (see the KeContact FAQ) |
+| error2 | String | yes | error code state 2, if in error (see the KeContact FAQ) |
+| maxsystemcurrent | Number:ElectricCurrent | yes | maximum current the wallbox can deliver |
+| failsafecurrent | Number:ElectricCurrent | yes | maximum current the wallbox can deliver, if network is lost |
+| uptime | Number:Time | yes | system uptime since the last reset of the wallbox |
+| sessionconsumption | Number:Energy | yes | energy delivered in current session |
+| totalconsumption | Number:Energy | yes | total energy delivered since the last reset of the wallbox |
+| authreq | Switch | yes | authentication required |
+| authon | Switch | yes | authentication enabled |
+| sessionrfidtag | String | yes | RFID tag used for the last charging session |
+| sessionrfidclass | String | yes | RFID tag class used for the last charging session |
+| sessionid | Number | yes | session ID of the last charging session |
+| setenergylimit | Number:Energy | no | set an energy limit for an already running or the next charging session |
+| authenticate | String | no | authenticate and start a session using RFID tag+RFID class |
+| maxpilotcurrent | Number:ElectricCurrent | yes | current offered to the vehicle via control pilot signalization |
+| maxpilotcurrentdutycyle | Number:Dimensionless | yes | duty cycle of the control pilot signal |
## Example
demo.items:
```
-Dimmer KebaCurrentRange {channel="keba:kecontact:1:maxpresetcurrentrange"}
-Number KebaCurrent {channel="keba:kecontact:1:maxpresetcurrent"}
-Number KebaSystemCurrent {channel="keba:kecontact:1:maxsystemcurrent"}
-Number KebaFailSafeCurrent {channel="keba:kecontact:1:failsafecurrent"}
-String KebaState {channel="keba:kecontact:1:state"}
-Switch KebaSwitch {channel="keba:kecontact:1:enabled"}
-Switch KebaWallboxPlugged {channel="keba:kecontact:1:wallbox"}
-Switch KebaVehiclePlugged {channel="keba:kecontact:1:vehicle"}
-Switch KebaPlugLocked {channel="keba:kecontact:1:locked"}
-DateTime KebaUptime "Uptime [%1$tY Y, %1$tm M, %1$td D, %1$tT]" {channel="keba:kecontact:1:uptime"}
-Number KebaI1 {channel="keba:kecontact:1:I1"}
-Number KebaI2 {channel="keba:kecontact:1:I2"}
-Number KebaI3 {channel="keba:kecontact:1:I3"}
-Number KebaU1 {channel="keba:kecontact:1:U1"}
-Number KebaU2 {channel="keba:kecontact:1:U2"}
-Number KebaU3 {channel="keba:kecontact:1:U3"}
-Number KebaPower {channel="keba:kecontact:1:power"}
-Number KebaSessionEnergy {channel="keba:kecontact:1:sessionconsumption"}
-Number KebaTotalEnergy {channel="keba:kecontact:1:totalconsumption"}
-Switch KebaInputSwitch {channel="keba:kecontact:1:input"}
-Switch KebaOutputSwitch {channel="keba:kecontact:1:output"}
-Number KebaSetEnergyLimit {channel="keba:kecontact:1:setenergylimit"}
+Number:Dimensionless KebaCurrentRange "Maximum supply current [%.1f %%]" {channel="keba:kecontact:1:maxpresetcurrentrange"}
+Number:ElectricCurrent KebaCurrent "Maximum supply current [%.3f A]" {channel="keba:kecontact:1:maxpresetcurrent"}
+Number:ElectricCurrent KebaSystemCurrent "Maximum system supply current [%.3f A]" {channel="keba:kecontact:1:maxsystemcurrent"}
+Number:ElectricCurrent KebaFailSafeCurrent "Failsafe supply current [%.3f A]" {channel="keba:kecontact:1:failsafecurrent"}
+String KebaState "Operating State [%s]" {channel="keba:kecontact:1:state"}
+Switch KebaSwitch "Enabled" {channel="keba:kecontact:1:enabled"}
+Switch KebaWallboxPlugged "Plugged into wallbox" {channel="keba:kecontact:1:wallbox"}
+Switch KebaVehiclePlugged "Plugged into vehicle" {channel="keba:kecontact:1:vehicle"}
+Switch KebaPlugLocked "Plug locked" {channel="keba:kecontact:1:locked"}
+DateTime KebaUptime "Uptime [%s s]" {channel="keba:kecontact:1:uptime"}
+Number:ElectricCurrent KebaI1 {channel="keba:kecontact:1:I1"}
+Number:ElectricCurrent KebaI2 {channel="keba:kecontact:1:I2"}
+Number:ElectricCurrent KebaI3 {channel="keba:kecontact:1:I3"}
+Number:ElectricPotential KebaU1 {channel="keba:kecontact:1:U1"}
+Number:ElectricPotential KebaU2 {channel="keba:kecontact:1:U2"}
+Number:ElectricPotential KebaU3 {channel="keba:kecontact:1:U3"}
+Number:Power KebaPower "Energy during current session [%.1f Wh]" {channel="keba:kecontact:1:power"}
+Number:Energy KebaSessionEnergy {channel="keba:kecontact:1:sessionconsumption"}
+Number:Energy KebaTotalEnergy "Energy during all sessions [%.1f Wh]" {channel="keba:kecontact:1:totalconsumption"}
+Switch KebaInputSwitch {channel="keba:kecontact:1:input"}
+Switch KebaOutputSwitch {channel="keba:kecontact:1:output"}
+Number:Energy KebaSetEnergyLimit "Set charge energy limit [%.1f Wh]" {channel="keba:kecontact:1:setenergylimit"}
```
demo.sitemap:
```
sitemap demo label="Main Menu"
{
- Text label="Charging Station" {
- Text item=KebaState label="Operating State [%s]"
- Text item=KebaUptime
- Switch item=KebaSwitch label="Enabled" mappings=[ON=ON, OFF=OFF ]
- Switch item=KebaWallboxPlugged label="Plugged into wallbox" mappings=[ON=ON, OFF=OFF ]
- Switch item=KebaVehiclePlugged label="Plugged into vehicle" mappings=[ON=ON, OFF=OFF ]
- Switch item=KebaPlugLocked label="Plug locked" mappings=[ON=ON, OFF=OFF ]
- Slider item=KebaCurrentRange switchSupport label="Maximum supply current [%.1f %%]"
- Text item=KebaCurrent label="Maximum supply current [%.0f mA]"
- Text item=KebaSystemCurrent label="Maximum system supply current [%.0f mA]"
- Text item=KebaFailSafeCurrent label="Failsafe supply current [%.0f mA]"
- Text item=KebaSessionEnergy label="Energy during current session [%.0f Wh]"
- Text item=KebaTotalEnergy label="Energy during all sessions [%.0f Wh]"
- Switch item=KebaSetEnergyLimit label="Set charge energy limit" mappings=[0="off", 20000="20kWh"]
- }
+ Text label="Charging Station" {
+ Text item=KebaState
+ Text item=KebaUptime
+ Switch item=KebaSwitch
+ Switch item=KebaWallboxPlugged
+ Switch item=KebaVehiclePlugged
+ Switch item=KebaPlugLocked
+ Slider item=KebaCurrentRange
+ Text item=KebaCurrent
+ Text item=KebaSystemCurrent
+ Text item=KebaFailSafeCurrent
+ Text item=KebaSessionEnergy
+ Text item=KebaTotalEnergy
+ Switch item=KebaSetEnergyLimit
+ }
}
```
+
+## Troubleshooting
+
+### Enable Verbose Logging
+
+Enable `DEBUG` or `TRACE` (even more verbose) logging for the logger named:
+
+ org.openhab.binding.keba
+
+If everything is working fine, you see the cyclic reception of `report 1`, `2` & `3` from the station. The frequency is according to the `refreshInterval` configuration.
+
+### UDP Ports used
+
+ Send port = UDP 7090
+
+The Keba station is the server
+
+ Receive port = UDP 7090
+
+This binding is providing the server
+
+UDP port 7090 needs to be available/free on the openHAB server.
+
+
+In order to enable the UDP port 7090 on the Keba station with full functionality, `DIP switch 1.3` must be `ON`.
+With `DIP switch 1.3 OFF` only ident-data can be read (`i` and `report 1`) but not the other reports as well as the commands needed for the write access.
+After setting the DIP switch, you need to `power OFF` and `ON` the station. SW-reset via WebGUI seems not to be sufficient in order to apply the new configuration.
+
+
+The right configuration can be validated as follows:
+
+- WebGUI DSW Settings:
+ - `DIP 1.3 | ON | UDP interface (SmartHome)`
+- UDP response of `report 1`:
+ - `DIP-Sw1` `0x20` Bit is set (enable at least `DEBUG` log-level for the binding)
+
+### Supported stations
+
+- KeContact P20 charging station with network connection (LSA+ socket)
+ - Product code: `KC-P20-xxxxxx2x-xxx` or `KC-P20-xxxxxx3x-xxx`
+ - Firmware version: 2.5 or higher
+- KeContact P30 charging station (c- or x-series) or BMW wallbox
+ - Firmware version 3.05 or higher
public static final String CHANNEL_PLUG_LOCKED = "locked";
public static final String CHANNEL_ENABLED = "enabled";
public static final String CHANNEL_PILOT_CURRENT = "maxpilotcurrent";
- public static final String CHANNEL_PILOT_PWM = "pwmpilotcurrent";
+ public static final String CHANNEL_PILOT_PWM = "maxpilotcurrentdutycyle";
public static final String CHANNEL_MAX_SYSTEM_CURRENT = "maxsystemcurrent";
public static final String CHANNEL_MAX_PRESET_CURRENT_RANGE = "maxpresetcurrentrange";
public static final String CHANNEL_MAX_PRESET_CURRENT = "maxpresetcurrent";
E('0'),
B('1'),
C('2', '3'),
- X('A', 'B', 'C', 'D');
+ X('A', 'B', 'C', 'D', 'E', 'G', 'H');
private final List<Character> things = new ArrayList<>();
}
}
- throw new IllegalArgumentException("Not a valid series");
+ throw new IllegalArgumentException("Not a valid series: '" + text + "'");
}
}
}
import java.io.IOException;
import java.math.BigDecimal;
-import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
import java.nio.ByteBuffer;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.TimeZone;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import javax.measure.quantity.Dimensionless;
+import javax.measure.quantity.ElectricCurrent;
+import javax.measure.quantity.ElectricPotential;
+import javax.measure.quantity.Energy;
+import javax.measure.quantity.Power;
+import javax.measure.quantity.Time;
+
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.keba.internal.KebaBindingConstants.KebaSeries;
import org.openhab.binding.keba.internal.KebaBindingConstants.KebaType;
import org.openhab.core.cache.ExpiringCacheMap;
import org.openhab.core.config.core.Configuration;
-import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
public static final String IP_ADDRESS = "ipAddress";
public static final String POLLING_REFRESH_INTERVAL = "refreshInterval";
+ public static final int POLLING_REFRESH_INTERVAL_DEFAULT = 15;
public static final int REPORT_INTERVAL = 3000;
- public static final int PING_TIME_OUT = 3000;
public static final int BUFFER_SIZE = 1024;
public static final int REMOTE_PORT_NUMBER = 7090;
private static final String CACHE_REPORT_1 = "REPORT_1";
private static final String CACHE_REPORT_2 = "REPORT_2";
private static final String CACHE_REPORT_3 = "REPORT_3";
private static final String CACHE_REPORT_100 = "REPORT_100";
+ public static final int SOCKET_TIME_OUT_MS = 3000;
+ public static final int SOCKET_CHECK_PORT_NUMBER = 80;
private final Logger logger = LoggerFactory.getLogger(KeContactHandler.class);
@Override
public void initialize() {
- if (getConfig().get(IP_ADDRESS) != null && !getConfig().get(IP_ADDRESS).equals("")) {
- transceiver.registerHandler(this);
-
- cache = new ExpiringCacheMap<>(
- Math.max((((BigDecimal) getConfig().get(POLLING_REFRESH_INTERVAL)).intValue()) - 5, 0) * 1000);
-
- cache.put(CACHE_REPORT_1, () -> transceiver.send("report 1", getHandler()));
- cache.put(CACHE_REPORT_2, () -> transceiver.send("report 2", getHandler()));
- cache.put(CACHE_REPORT_3, () -> transceiver.send("report 3", getHandler()));
- cache.put(CACHE_REPORT_100, () -> transceiver.send("report 100", getHandler()));
-
- if (pollingJob == null || pollingJob.isCancelled()) {
- try {
- pollingJob = scheduler.scheduleWithFixedDelay(this::pollingRunnable, 0,
- ((BigDecimal) getConfig().get(POLLING_REFRESH_INTERVAL)).intValue(), TimeUnit.SECONDS);
- } catch (Exception e) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
- "An exception occurred while scheduling the polling job");
+ try {
+ if (isKebaReachable()) {
+ transceiver.registerHandler(this);
+
+ int refreshInterval = getRefreshInterval();
+ cache = new ExpiringCacheMap<>(Math.max(refreshInterval - 5, 0) * 1000);
+
+ cache.put(CACHE_REPORT_1, () -> transceiver.send("report 1", getHandler()));
+ cache.put(CACHE_REPORT_2, () -> transceiver.send("report 2", getHandler()));
+ cache.put(CACHE_REPORT_3, () -> transceiver.send("report 3", getHandler()));
+ cache.put(CACHE_REPORT_100, () -> transceiver.send("report 100", getHandler()));
+
+ if (pollingJob == null || pollingJob.isCancelled()) {
+ pollingJob = scheduler.scheduleWithFixedDelay(this::pollingRunnable, 0, refreshInterval,
+ TimeUnit.SECONDS);
}
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "IP address or port number not set");
}
- } else {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "IP address or port number not set");
+ } catch (IOException e) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ "Exception during initialization of binding: " + e.toString());
}
}
+ private boolean isKebaReachable() throws IOException {
+ boolean isReachable = false;
+ SocketAddress sockAddr = new InetSocketAddress(getIPAddress(), SOCKET_CHECK_PORT_NUMBER);
+ Socket socket = new Socket();
+ try {
+ socket.connect(sockAddr, SOCKET_TIME_OUT_MS);
+ isReachable = true;
+ } finally {
+ socket.close();
+ }
+ logger.debug("isKebaReachable() returns {}", isReachable);
+ return isReachable;
+ }
+
@Override
public void dispose() {
if (pollingJob != null && !pollingJob.isCancelled()) {
return getConfig().get(IP_ADDRESS) != null ? (String) getConfig().get(IP_ADDRESS) : "";
}
+ public int getRefreshInterval() {
+ return getConfig().get(POLLING_REFRESH_INTERVAL) != null
+ ? ((BigDecimal) getConfig().get(POLLING_REFRESH_INTERVAL)).intValue()
+ : POLLING_REFRESH_INTERVAL_DEFAULT;
+ }
+
private KeContactHandler getHandler() {
return this;
}
private void pollingRunnable() {
try {
+ logger.debug("Running pollingRunnable to connect Keba wallbox");
long stamp = System.currentTimeMillis();
- if (!InetAddress.getByName(((String) getConfig().get(IP_ADDRESS))).isReachable(PING_TIME_OUT)) {
- logger.debug("Ping timed out after '{}' milliseconds", System.currentTimeMillis() - stamp);
+ if (!isKebaReachable()) {
+ logger.debug("isKebaReachable() timed out after '{}' milliseconds", System.currentTimeMillis() - stamp);
transceiver.unRegisterHandler(getHandler());
} else {
if (getThing().getStatus() == ThingStatus.ONLINE) {
case "Curr HW": {
int state = entry.getValue().getAsInt();
maxSystemCurrent = state;
- State newState = new DecimalType(state);
+ State newState = new QuantityType<ElectricCurrent>(state / 1000.0, Units.AMPERE);
updateState(CHANNEL_MAX_SYSTEM_CURRENT, newState);
if (maxSystemCurrent != 0) {
if (maxSystemCurrent < maxPresetCurrent) {
transceiver.send("curr " + String.valueOf(maxSystemCurrent), this);
- updateState(CHANNEL_MAX_PRESET_CURRENT, new DecimalType(maxSystemCurrent));
- updateState(CHANNEL_MAX_PRESET_CURRENT_RANGE,
- new PercentType((maxSystemCurrent - 6000) * 100 / (maxSystemCurrent - 6000)));
+ updateState(CHANNEL_MAX_PRESET_CURRENT,
+ new QuantityType<ElectricCurrent>(maxSystemCurrent / 1000.0, Units.AMPERE));
+ updateState(CHANNEL_MAX_PRESET_CURRENT_RANGE, new QuantityType<Dimensionless>(
+ (maxSystemCurrent - 6000) * 100 / (maxSystemCurrent - 6000), Units.PERCENT));
}
} else {
logger.debug("maxSystemCurrent is 0. Ignoring.");
case "Curr user": {
int state = entry.getValue().getAsInt();
maxPresetCurrent = state;
- updateState(CHANNEL_MAX_PRESET_CURRENT, new DecimalType(state));
+ State newState = new QuantityType<ElectricCurrent>(state / 1000.0, Units.AMPERE);
+ updateState(CHANNEL_MAX_PRESET_CURRENT, newState);
if (maxSystemCurrent != 0) {
- updateState(CHANNEL_MAX_PRESET_CURRENT_RANGE,
- new PercentType(Math.min(100, (state - 6000) * 100 / (maxSystemCurrent - 6000))));
+ updateState(CHANNEL_MAX_PRESET_CURRENT_RANGE, new QuantityType<Dimensionless>(
+ Math.min(100, (state - 6000) * 100 / (maxSystemCurrent - 6000)), Units.PERCENT));
}
break;
}
case "Curr FS": {
int state = entry.getValue().getAsInt();
- State newState = new DecimalType(state);
+ State newState = new QuantityType<ElectricCurrent>(state / 1000.0, Units.AMPERE);
updateState(CHANNEL_FAILSAFE_CURRENT, newState);
break;
}
case "Max curr": {
int state = entry.getValue().getAsInt();
maxPresetCurrent = state;
- updateState(CHANNEL_PILOT_CURRENT, new DecimalType(state));
- updateState(CHANNEL_PILOT_PWM, new DecimalType(state));
+ State newState = new QuantityType<ElectricCurrent>(state / 1000.0, Units.AMPERE);
+ updateState(CHANNEL_PILOT_CURRENT, newState);
+ break;
+ }
+ case "Max curr %": {
+ int state = entry.getValue().getAsInt();
+ State newState = new QuantityType<Dimensionless>(state / 10.0, Units.PERCENT);
+ updateState(CHANNEL_PILOT_PWM, newState);
break;
}
case "Output": {
}
case "Sec": {
long state = entry.getValue().getAsLong();
-
- Calendar uptime = Calendar.getInstance();
- uptime.setTimeZone(TimeZone.getTimeZone("GMT"));
- uptime.setTimeInMillis(state * 1000);
- SimpleDateFormat pFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
- pFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
-
- updateState(CHANNEL_UPTIME, new DateTimeType(pFormatter.format(uptime.getTime())));
+ State newState = new QuantityType<Time>(state, Units.SECOND);
+ updateState(CHANNEL_UPTIME, newState);
break;
}
case "U1": {
int state = entry.getValue().getAsInt();
- State newState = new DecimalType(state);
+ State newState = new QuantityType<ElectricPotential>(state, Units.VOLT);
updateState(CHANNEL_U1, newState);
break;
}
case "U2": {
int state = entry.getValue().getAsInt();
- State newState = new DecimalType(state);
+ State newState = new QuantityType<ElectricPotential>(state, Units.VOLT);
updateState(CHANNEL_U2, newState);
break;
}
case "U3": {
int state = entry.getValue().getAsInt();
- State newState = new DecimalType(state);
+ State newState = new QuantityType<ElectricPotential>(state, Units.VOLT);
updateState(CHANNEL_U3, newState);
break;
}
case "I1": {
int state = entry.getValue().getAsInt();
- State newState = new DecimalType(state);
+ State newState = new QuantityType<ElectricCurrent>(state / 1000.0, Units.AMPERE);
updateState(CHANNEL_I1, newState);
break;
}
case "I2": {
int state = entry.getValue().getAsInt();
- State newState = new DecimalType(state);
+ State newState = new QuantityType<ElectricCurrent>(state / 1000.0, Units.AMPERE);
updateState(CHANNEL_I2, newState);
break;
}
case "I3": {
int state = entry.getValue().getAsInt();
- State newState = new DecimalType(state);
+ State newState = new QuantityType<ElectricCurrent>(state / 1000.0, Units.AMPERE);
updateState(CHANNEL_I3, newState);
break;
}
case "P": {
long state = entry.getValue().getAsLong();
- State newState = new DecimalType(state / 1000);
+ State newState = new QuantityType<Power>(state / 1000.0, Units.WATT);
updateState(CHANNEL_POWER, newState);
break;
}
case "PF": {
int state = entry.getValue().getAsInt();
- State newState = new PercentType(state / 10);
+ State newState = new QuantityType<Dimensionless>(state / 10.0, Units.PERCENT);
updateState(CHANNEL_POWER_FACTOR, newState);
break;
}
case "E pres": {
long state = entry.getValue().getAsLong();
- State newState = new DecimalType(state / 10);
+ State newState = new QuantityType<Energy>(state / 10.0, Units.WATT_HOUR);
updateState(CHANNEL_SESSION_CONSUMPTION, newState);
break;
}
case "E total": {
long state = entry.getValue().getAsLong();
- State newState = new DecimalType(state / 10);
+ State newState = new QuantityType<Energy>(state / 10.0, Units.WATT_HOUR);
updateState(CHANNEL_TOTAL_CONSUMPTION, newState);
break;
}
break;
}
case "Setenergy": {
- int state = entry.getValue().getAsInt() / 10;
- State newState = new DecimalType(state);
+ int state = entry.getValue().getAsInt();
+ State newState = new QuantityType<Energy>(state / 10.0, Units.WATT_HOUR);
updateState(CHANNEL_SETENERGY, newState);
break;
}
} else {
switch (channelUID.getId()) {
case CHANNEL_MAX_PRESET_CURRENT: {
- if (command instanceof DecimalType) {
+ if (command instanceof QuantityType<?>) {
+ QuantityType<?> value = ((QuantityType<?>) command).toUnit("mA");
+
transceiver.send(
- "curr " + String.valueOf(
- Math.min(Math.max(6000, ((DecimalType) command).intValue()), maxSystemCurrent)),
+ "curr " + String.valueOf(Math.min(Math.max(6000, value.intValue()), maxSystemCurrent)),
this);
}
break;
}
case CHANNEL_MAX_PRESET_CURRENT_RANGE: {
if (command instanceof OnOffType || command instanceof IncreaseDecreaseType
- || command instanceof PercentType) {
- int newValue = 6000;
+ || command instanceof QuantityType<?>) {
+ long newValue = 6000;
if (command == IncreaseDecreaseType.INCREASE) {
newValue = Math.min(Math.max(6000, maxPresetCurrent + 1), maxSystemCurrent);
} else if (command == IncreaseDecreaseType.DECREASE) {
newValue = maxSystemCurrent;
} else if (command == OnOffType.OFF) {
newValue = 6000;
- } else if (command instanceof PercentType) {
- newValue = 6000 + (maxSystemCurrent - 6000) * ((PercentType) command).intValue() / 100;
+ } else if (command instanceof QuantityType<?>) {
+ QuantityType<?> value = ((QuantityType<?>) command).toUnit("%");
+ newValue = Math.round(6000 + (maxSystemCurrent - 6000) * value.doubleValue() / 100.0);
} else {
return;
}
-
transceiver.send("curr " + String.valueOf(newValue), this);
}
break;
break;
}
case CHANNEL_SETENERGY: {
- if (command instanceof DecimalType) {
+ if (command instanceof QuantityType<?>) {
+ QuantityType<?> value = ((QuantityType<?>) command).toUnit(Units.WATT_HOUR);
transceiver.send(
"setenergy " + String.valueOf(
- Math.min(Math.max(0, ((DecimalType) command).intValue() * 10), 999999999)),
+ Math.min(Math.max(0, Math.round(value.doubleValue() * 10.0)), 999999999)),
this);
}
break;
public class KeContactTransceiver {
public static final int LISTENER_PORT_NUMBER = 7090;
- public static final int REMOTE_PORT_NUMBER = 7090;
public static final int LISTENING_INTERVAL = 100;
public static final int BUFFER_SIZE = 1024;
- public static final String IP_ADDRESS = "ipAddress";
- public static final String POLLING_REFRESH_INTERVAL = "refreshInterval";
private DatagramChannel broadcastChannel;
private SelectionKey broadcastKey;
};
private void establishConnection(KeContactHandler handler) {
+ String ipAddress = handler.getIPAddress();
if (handler.getThing().getStatusInfo().getStatusDetail() != ThingStatusDetail.CONFIGURATION_ERROR
- && handler.getConfig().get(IP_ADDRESS) != null && !handler.getConfig().get(IP_ADDRESS).equals("")) {
+ && !ipAddress.equals("")) {
logger.debug("Establishing the connection to the KEBA KeContact '{}'", handler.getThing().getUID());
DatagramChannel datagramChannel = null;
"An exception occurred while registering a selector");
}
- InetSocketAddress remoteAddress = new InetSocketAddress(
- (String) handler.getConfig().get(IP_ADDRESS), REMOTE_PORT_NUMBER);
+ InetSocketAddress remoteAddress = new InetSocketAddress(ipAddress, LISTENER_PORT_NUMBER);
try {
if (logger.isTraceEnabled()) {
handler.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "");
} catch (Exception e) {
- logger.debug("An exception occurred while connecting connecting to '{}:{}' : {}", new Object[] {
- (String) handler.getConfig().get(IP_ADDRESS), REMOTE_PORT_NUMBER, e.getMessage() });
+ logger.debug("An exception occurred while connecting connecting to '{}:{}' : {}",
+ new Object[] { ipAddress, LISTENER_PORT_NUMBER, e.getMessage() });
handler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"An exception occurred while connecting");
}
<channel id="vehicle" typeId="plugvehicle"/>
<channel id="locked" typeId="locked"/>
<channel id="maxpilotcurrent" typeId="pilotcurrent"/>
- <channel id="pwmpilotcurrent" typeId="pilotrange"/>
+ <channel id="maxpilotcurrentdutycyle" typeId="pilotcurrentdutycyle"/>
<channel id="maxsystemcurrent" typeId="maxcurrent"/>
<channel id="failsafecurrent" typeId="failsafecurrent"/>
<channel id="output" typeId="x2"/>
</parameter>
<parameter name="refreshInterval" type="integer" required="false">
<label>Refresh Interval</label>
- <description>Specifies the refresh interval in seconds.</description>
+ <description>Specifies the refresh interval in seconds</description>
<default>15</default>
</parameter>
</config-description>
<state readOnly="false"></state>
</channel-type>
<channel-type id="current_settable">
- <item-type>Number</item-type>
+ <item-type>Number:ElectricCurrent</item-type>
<label>Preset Current</label>
- <description>Preset Current in mA</description>
- <state pattern="%d mA" readOnly="false"></state>
+ <description>Preset Current</description>
+ <state pattern="%.3f %unit%" readOnly="false"></state>
</channel-type>
<channel-type id="current" advanced="true">
- <item-type>Number</item-type>
+ <item-type>Number:ElectricCurrent</item-type>
<label>Current</label>
- <description>Current in mA</description>
- <state pattern="%d mA" readOnly="true"></state>
+ <description>Current</description>
+ <state pattern="%.3f %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="maxcurrent" advanced="true">
- <item-type>Number</item-type>
+ <item-type>Number:ElectricCurrent</item-type>
<label>Max. System Current</label>
- <description>Maximal System Current in mA</description>
- <state pattern="%d mA" readOnly="true"></state>
+ <description>Maximal System Current</description>
+ <state pattern="%.3f %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="failsafecurrent" advanced="true">
- <item-type>Number</item-type>
+ <item-type>Number:ElectricCurrent</item-type>
<label>Failsafe Current</label>
- <description>Failsafe Current in mA (if network is lost)</description>
- <state pattern="%d mA" readOnly="true"></state>
+ <description>Failsafe Current (if network is lost)</description>
+ <state pattern="%.3f %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="range" advanced="true">
- <item-type>Dimmer</item-type>
+ <item-type>Number:Dimensionless</item-type>
<label>Rel. Current</label>
- <description>Current in % of the 6000-63000 mA range accepted by the wallbox</description>
- <state pattern="%d %%" readOnly="false"></state>
+ <description>Current in % of the 6-63 A range accepted by the wallbox</description>
+ <state pattern="%.1f %%" readOnly="false"></state>
</channel-type>
<channel-type id="pilotcurrent" advanced="true">
- <item-type>Number</item-type>
+ <item-type>Number:ElectricCurrent</item-type>
<label>Pilot Current</label>
- <description>Current preset value via Control pilot in mA</description>
- <state pattern="%d mA" readOnly="true"></state>
+ <description>Current value offered to the vehicle via control pilot signalization (PWM)</description>
+ <state pattern="%.3f %unit%" readOnly="true"></state>
</channel-type>
- <channel-type id="pilotrange" advanced="true">
- <item-type>Number</item-type>
- <label>Pilot Range</label>
- <description>Current preset value via Control pilot in 0,1% of the PWM value</description>
- <state pattern="%d" readOnly="true"></state>
+ <channel-type id="pilotcurrentdutycyle" advanced="true">
+ <item-type>Number:Dimensionless</item-type>
+ <label>Pilot Current Duty Cycle</label>
+ <description>Duty cycle of the control pilot signal</description>
+ <state pattern="%.1f %%" readOnly="true"></state>
</channel-type>
<channel-type id="uptime" advanced="true">
- <item-type>DateTime</item-type>
+ <item-type>Number:Time</item-type>
<label>System Uptime</label>
<description>System uptime since the last reset of the wallbox</description>
- <state readOnly="true"></state>
+ <state pattern="%d %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="voltage" advanced="true">
- <item-type>Number</item-type>
+ <item-type>Number:ElectricPotential</item-type>
<label>Voltage</label>
- <description>Voltage in V</description>
- <state pattern="%d V" readOnly="true"></state>
+ <description>Voltage</description>
+ <state pattern="%d %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="power">
- <item-type>Number</item-type>
+ <item-type>Number:Power</item-type>
<label>Power</label>
- <description>Active Power in W</description>
- <state pattern="%d W" readOnly="true"></state>
+ <description>Active Power</description>
+ <state pattern="%.3f %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="powerfactor" advanced="true">
- <item-type>Number</item-type>
+ <item-type>Number:Dimensionless</item-type>
<label>Power Factor</label>
<description>Power factor (cosphi)</description>
- <state readOnly="true"></state>
+ <state pattern="%.1f %%" readOnly="true"></state>
</channel-type>
<channel-type id="energy" advanced="true">
- <item-type>Number</item-type>
- <label>Energy</label>
- <description>Power consumption in Wh.</description>
- <state pattern="%d Wh" readOnly="true"></state>
+ <item-type>Number:Energy</item-type>
+ <label>Energy Session</label>
+ <description>Power consumption</description>
+ <state pattern="%.1f %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="totalenergy" advanced="true">
- <item-type>Number</item-type>
- <label>Energy</label>
+ <item-type>Number:Energy</item-type>
+ <label>Energy Total</label>
<description>Total energy consumption is added up after each completed charging session</description>
- <state pattern="%d Wh" readOnly="true"></state>
+ <state pattern="%.1f %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="display" advanced="true">
<item-type>String</item-type>
<state readOnly="true"></state>
</channel-type>
<channel-type id="setenergylimit">
- <item-type>Number</item-type>
+ <item-type>Number:Energy</item-type>
<label>Energy Limit</label>
- <description>An energy limit for an already running or the next charging session.</description>
- <state pattern="%d Wh" readOnly="false"></state>
+ <description>An energy limit for an already running or the next charging session</description>
+ <state pattern="%.1f %unit%" readOnly="false"></state>
</channel-type>
<channel-type id="authenticate">
<item-type>String</item-type>