[keba] Improve connection establishment and stability (#10179)
Signed-off-by: Michael Weger <weger.michael@gmx.net> * - worked in review findings - introduced QuantityTypes - removed redundant pwmpilotcurrent (duplicate of maxpilotcurrent) - added maxpilotcurrentdutycyle
This commit is contained in:
parent
6b624f0e1c
commit
a9bddff5ad
@ -4,46 +4,48 @@ This binding integrates the [Keba KeContact EV Charging Stations](https://www.ke
|
||||
|
||||
## 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
|
||||
@ -57,28 +59,28 @@ Thing keba:kecontact:1 [ipAddress="192.168.0.64", refreshInterval=30]
|
||||
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:
|
||||
@ -86,20 +88,63 @@ 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
|
||||
|
||||
@ -44,7 +44,7 @@ public class KebaBindingConstants {
|
||||
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";
|
||||
@ -82,7 +82,7 @@ public class KebaBindingConstants {
|
||||
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<>();
|
||||
|
||||
@ -104,7 +104,7 @@ public class KebaBindingConstants {
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a valid series");
|
||||
throw new IllegalArgumentException("Not a valid series: '" + text + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,27 +16,33 @@ import static org.openhab.binding.keba.internal.KebaBindingConstants.*;
|
||||
|
||||
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;
|
||||
@ -63,14 +69,16 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
|
||||
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);
|
||||
|
||||
@ -94,32 +102,46 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (getConfig().get(IP_ADDRESS) != null && !getConfig().get(IP_ADDRESS).equals("")) {
|
||||
transceiver.registerHandler(this);
|
||||
try {
|
||||
if (isKebaReachable()) {
|
||||
transceiver.registerHandler(this);
|
||||
|
||||
cache = new ExpiringCacheMap<>(
|
||||
Math.max((((BigDecimal) getConfig().get(POLLING_REFRESH_INTERVAL)).intValue()) - 5, 0) * 1000);
|
||||
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()));
|
||||
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");
|
||||
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()) {
|
||||
@ -134,6 +156,12 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
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;
|
||||
}
|
||||
@ -150,9 +178,10 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
|
||||
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) {
|
||||
@ -300,14 +329,15 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
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.");
|
||||
@ -317,24 +347,31 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
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": {
|
||||
@ -367,73 +404,67 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -468,8 +499,8 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
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;
|
||||
}
|
||||
@ -488,18 +519,19 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
} 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) {
|
||||
@ -508,12 +540,12 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
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;
|
||||
@ -555,10 +587,11 @@ public class KeContactHandler extends BaseThingHandler {
|
||||
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;
|
||||
|
||||
@ -48,11 +48,8 @@ import org.slf4j.LoggerFactory;
|
||||
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;
|
||||
@ -406,8 +403,9 @@ public class KeContactTransceiver {
|
||||
};
|
||||
|
||||
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;
|
||||
@ -438,8 +436,7 @@ public class KeContactTransceiver {
|
||||
"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()) {
|
||||
@ -449,8 +446,8 @@ public class KeContactTransceiver {
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<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"/>
|
||||
@ -64,7 +64,7 @@
|
||||
</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>
|
||||
@ -134,82 +134,82 @@
|
||||
<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>
|
||||
@ -248,10 +248,10 @@
|
||||
<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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user