[easee] Migrate charger state API endpoint, add channels (#14614)

* reworked the way to retrieve Charger state as API endpoint is now deprecated.
* fixed result processing: removed default handling by bridge handler which caused everything to go offline if a single charger had faulty config.
* additional channels / removed obsolete firmware channel

Signed-off-by: Alexander Friese <af944580@googlemail.com>
This commit is contained in:
Alexander Friese 2023-08-18 13:33:19 +02:00 committed by GitHub
parent 79ecb2e4d0
commit a435ed2cd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 675 additions and 201 deletions

View File

@ -63,23 +63,42 @@ The settings that start with "dynamic" can be changed frequently, the others are
| state#chargerOpMode | Number | no | 0=Offline, 1=Disconnected, 2=AwaitingStart, 3=Charging, 4=Completed, 5=Error, 6=ReadyToCharge, 7=AwaitingAuthentication, 8=Deauthenticating | |
| state#totalPower | Number:Power | no | current session total power (all phases) | |
| state#sessionEnergy | Number:Energy | no | current session | |
| state#energyPerHour | Number:Energy | no | energy per hour | |
| state#wiFiRSSI | Number:Power | no | | |
| state#cellRSSI | Number:Power | no | | |
| state#dynamicCircuitCurrentP1 | Number:ElectricCurrent | no | | |
| state#dynamicCircuitCurrentP2 | Number:ElectricCurrent | no | | |
| state#dynamicCircuitCurrentP3 | Number:ElectricCurrent | no | | |
| state#latestPulse | DateTime | no | | |
| state#chargerFirmware | Number | no | | |
| state#latestFirmware | Number | no | | |
| state#voltage | Number:ElectricPotential | no | | |
| state#inCurrentT2 | Number:ElectricCurrent | no | | |
| state#inCurrentT3 | Number:ElectricCurrent | no | | |
| state#inCurrentT4 | Number:ElectricCurrent | no | | |
| state#inCurrentT5 | Number:ElectricCurrent | no | | |
| state#outputCurrent | Number:ElectricCurrent | no | | |
| state#inVoltageT1T2 | Number:ElectricPotential | no | | |
| state#inVoltageT1T3 | Number:ElectricPotential | no | | |
| state#inVoltageT1T4 | Number:ElectricPotential | no | | |
| state#inVoltageT1T5 | Number:ElectricPotential | no | | |
| state#inVoltageT2T3 | Number:ElectricPotential | no | | |
| state#inVoltageT2T4 | Number:ElectricPotential | no | | |
| state#inVoltageT2T5 | Number:ElectricPotential | no | | |
| state#inVoltageT3T4 | Number:ElectricPotential | no | | |
| state#inVoltageT3T5 | Number:ElectricPotential | no | | |
| state#inVoltageT4T5 | Number:ElectricPotential | no | | |
| state#ledMode | Number | no | | |
| state#cableRating | Number:ElectricCurrent | no | | |
| state#isOnline | Switch | no | | |
| state#dynamicChargerCurrent | Number:ElectricCurrent | yes | | 0, 6-32 |
| state#reasonForNoCurrent | Number | no | 0=OK, 2=DynamicCircuitCurrentLimitTooLow, 27=DynamicCircuitCurrentCharging, 52=DynamicChargerCurrentLimitTooLow, 55=NotAuthorized, 79=CarLimit, 81=CarLimitedCharging | |
| state#lifetimeEnergy | Number:Energy | no | | |
| state#errorCode | Number | no | | |
| state#fatalErrorCode | Number | no | | |
| config#lockCablePermanently | Switch | yes | | ON/OFF |
| config#authorizationRequired | Switch | yes | | ON/OFF |
| config#limitToSinglePhaseCharging | Switch | yes | | ON/OFF |
| state#connectedToCloud | Switch | no | | |
| config#lockCablePermanently | Switch | yes | | true/false |
| config#authorizationRequired | Switch | yes | | true/false |
| config#limitToSinglePhaseCharging | Switch | yes | | true/false |
| config#phaseMode | Number | yes | 1=1phase, 2=auto, 3=3phase | 1-3 |
| config#maxChargerCurrent | Number:ElectricCurrent | no | write access not yet implemented | |
| commands#genericCommand | String | yes | Generic Endpoint to send commands | reboot, update_firmware, poll_all, smart_charging, start_charging, stop_charging, pause_charging, resume_charging, toggle_charging, override_schedule |

View File

@ -55,7 +55,7 @@ public class EaseeBindingConstants {
public static final String CHANNEL_TYPE_VOLT = "Number:ElectricPotential";
public static final String CHANNEL_TYPE_AMPERE = "Number:ElectricCurrent";
public static final String CHANNEL_TYPE_KWH = "Number:Energy";
public static final String CHANNEL_TYPE_KW = "Number:Power";
public static final String CHANNEL_TYPE_POWER = "Number:Power";
public static final String CHANNEL_TYPE_DATE = "DateTime";
public static final String CHANNEL_TYPE_STRING = "String";
public static final String CHANNEL_TYPE_NUMBER = "Number";
@ -63,6 +63,7 @@ public class EaseeBindingConstants {
public static final String CHANNEL_TYPEPREFIX_RW = "rw";
public static final String CHANNEL_TYPENAME_INTEGER = "type-integer";
public static final String CHANNEL_TYPENAME_RSSI = "type-rssi";
// Channels with specific handling
public static final String CHANNEL_CHARGER_OP_MODE = "chargerOpMode";
@ -88,6 +89,7 @@ public class EaseeBindingConstants {
public static final String JSON_KEY_GENERIC_NAME = "name";
public static final String JSON_KEY_CIRCUIT_NAME = "panelName";
public static final String JSON_KEY_CIRCUIT_ID = "circuitId";
public static final String JSON_KEY_CHARGER_ID = "chargerID";
public static final String JSON_KEY_CIRCUITS = "circuits";
public static final String JSON_KEY_CHARGERS = "chargers";
public static final String JSON_KEY_BACK_PLATE = "backPlate";
@ -99,6 +101,9 @@ public class EaseeBindingConstants {
public static final String JSON_KEY_AUTH_ACCESS_TOKEN = "accessToken";
public static final String JSON_KEY_AUTH_REFRESH_TOKEN = "refreshToken";
public static final String JSON_KEY_AUTH_EXPIRES_IN = "expiresIn";
public static final String JSON_KEY_CIRCUIT_STATES = "circuitStates";
public static final String JSON_KEY_CHARGER_STATES = "chargerStates";
public static final String JSON_KEY_CHARGER_STATE = "chargerState";
// Write Commands
public static final String COMMAND_CHANGE_CONFIGURATION = "ChangeConfiguration";
@ -130,7 +135,7 @@ public class EaseeBindingConstants {
public static final String REFRESH_TOKEN_URL = API_BASE_URL + "/accounts/refresh_token";
public static final String GET_SITE_URL = API_BASE_URL + "/sites/{siteId}";
public static final String CHARGER_URL = API_BASE_URL + "/chargers/{id}";
public static final String STATE_URL = API_BASE_URL + "/chargers/{id}/state";
public static final String SITE_STATE_URL = API_BASE_URL + "/sites/{siteId}/state";
public static final String GET_CONFIGURATION_URL = API_BASE_URL + "/chargers/{id}/config";
public static final String CHANGE_CONFIGURATION_URL = API_BASE_URL + "/chargers/{id}/settings";
public static final String COMMANDS_URL = API_BASE_URL + "/chargers/{id}/commands/{command}";

View File

@ -18,8 +18,6 @@ import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -87,7 +85,7 @@ public abstract class AbstractCommand extends BufferingResponseListener implemen
/**
* generic transformer which just transfers all values in a plain map.
*/
private final GenericResponseTransformer transformer;
protected final GenericResponseTransformer transformer;
/**
* retry counter.
@ -107,29 +105,20 @@ public abstract class AbstractCommand extends BufferingResponseListener implemen
/**
* allows further processing of the json result data, if set.
*/
private List<JsonResultProcessor> resultProcessors;
/**
* the constructor
*/
public AbstractCommand(EaseeThingHandler handler, RetryOnFailure retryOnFailure,
ProcessFailureResponse processFailureResponse) {
this.gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).create();
this.communicationStatus = new CommunicationStatus();
this.resultProcessors = new ArrayList<>();
this.transformer = new GenericResponseTransformer(handler);
this.handler = handler;
this.processFailureResponse = processFailureResponse;
this.retryOnFailure = retryOnFailure;
}
private final JsonResultProcessor resultProcessor;
/**
* the constructor
*/
public AbstractCommand(EaseeThingHandler handler, RetryOnFailure retryOnFailure,
ProcessFailureResponse processFailureResponse, JsonResultProcessor resultProcessor) {
this(handler, retryOnFailure, processFailureResponse);
this.resultProcessors.add(resultProcessor);
this.gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).create();
this.communicationStatus = new CommunicationStatus();
this.transformer = new GenericResponseTransformer(handler);
this.handler = handler;
this.processFailureResponse = processFailureResponse;
this.retryOnFailure = retryOnFailure;
this.resultProcessor = resultProcessor;
}
/**
@ -237,7 +226,7 @@ public abstract class AbstractCommand extends BufferingResponseListener implemen
* @param json
* @return
*/
private @Nullable JsonObject transform(@Nullable String json) {
protected @Nullable JsonObject transform(@Nullable String json) {
if (json != null) {
try {
return gson.fromJson(json, JsonObject.class);
@ -283,18 +272,16 @@ public abstract class AbstractCommand extends BufferingResponseListener implemen
}
/**
* calls the registered resultPRocessors.
* calls the registered resultProcessor.
*
* @param jsonObject
*/
protected final void processResult(JsonObject jsonObject) {
for (JsonResultProcessor processor : resultProcessors) {
try {
processor.processResult(getCommunicationStatus(), jsonObject);
} catch (Exception ex) {
// this should not happen
logger.warn("Exception caught: {}", ex.getMessage(), ex);
}
try {
resultProcessor.processResult(getCommunicationStatus(), jsonObject);
} catch (Exception ex) {
// this should not happen
logger.warn("Exception caught: {}", ex.getMessage(), ex);
}
}
@ -320,9 +307,4 @@ public abstract class AbstractCommand extends BufferingResponseListener implemen
* @return Url
*/
protected abstract String getURL();
@Override
public void registerResultProcessor(JsonResultProcessor resultProcessor) {
this.resultProcessors.add(resultProcessor);
}
}

View File

@ -47,8 +47,9 @@ public abstract class AbstractWriteCommand extends AbstractCommand {
* @param config
*/
public AbstractWriteCommand(EaseeThingHandler handler, Channel channel, Command command,
RetryOnFailure retryOnFailure, ProcessFailureResponse processFailureResponse) {
super(handler, retryOnFailure, processFailureResponse);
RetryOnFailure retryOnFailure, ProcessFailureResponse processFailureResponse,
JsonResultProcessor resultProcessor) {
super(handler, retryOnFailure, processFailureResponse, resultProcessor);
this.channel = channel;
this.command = command;
}

View File

@ -37,11 +37,4 @@ public interface EaseeCommand extends SuccessListener, FailureListener, ContentL
* @throws ValidationException
*/
void performAction(HttpClient asyncclient, String token) throws ValidationException;
/**
* sets a result processor for json result data
*
* @param resultProcessor
*/
void registerResultProcessor(JsonResultProcessor resultProcessor);
}

View File

@ -23,6 +23,7 @@ import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeBridgeHandler;
import com.google.gson.JsonObject;
@ -47,9 +48,9 @@ public class Login extends AbstractCommand {
private final LoginData loginData;
public Login(EaseeBridgeHandler handler) {
public Login(EaseeBridgeHandler handler, JsonResultProcessor resultProcessor) {
// flags do not matter as "onComplete" is overwritten in this class.
super(handler, RetryOnFailure.NO, ProcessFailureResponse.NO);
super(handler, RetryOnFailure.NO, ProcessFailureResponse.NO, resultProcessor);
loginData = new LoginData(handler.getBridgeConfiguration().getUsername(),
handler.getBridgeConfiguration().getPassword());
}

View File

@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeBridgeHandler;
/**
@ -40,8 +41,9 @@ public class RefreshToken extends Login {
private final RefreshData refreshData;
public RefreshToken(EaseeBridgeHandler handler, String accessToken, String refreshToken) {
super(handler);
public RefreshToken(EaseeBridgeHandler handler, String accessToken, String refreshToken,
JsonResultProcessor resultProcessor) {
super(handler, resultProcessor);
refreshData = new RefreshData(accessToken, refreshToken);
}

View File

@ -19,6 +19,7 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractWriteCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
import org.openhab.binding.easee.internal.model.ValidationException;
import org.openhab.core.thing.Channel;
@ -33,8 +34,9 @@ import org.openhab.core.types.Command;
public class ChangeConfiguration extends AbstractWriteCommand {
private final String url;
public ChangeConfiguration(EaseeThingHandler handler, String chargerId, Channel channel, Command command) {
super(handler, channel, command, RetryOnFailure.YES, ProcessFailureResponse.YES);
public ChangeConfiguration(EaseeThingHandler handler, String chargerId, Channel channel, Command command,
JsonResultProcessor resultProcessor) {
super(handler, channel, command, RetryOnFailure.YES, ProcessFailureResponse.YES, resultProcessor);
this.url = CHANGE_CONFIGURATION_URL.replaceAll("\\{id\\}", chargerId);
}

View File

@ -1,54 +0,0 @@
/**
* Copyright (c) 2010-2023 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.easee.internal.command.charger;
import static org.openhab.binding.easee.internal.EaseeBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractCommand;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
/**
* implements the state api call of the charger.
*
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
public class ChargerState extends AbstractCommand {
private final String url;
public ChargerState(EaseeThingHandler handler, String chargerId) {
// retry does not make much sense as it is a polling command, command should always succeed therefore update
// handler on failure.
super(handler, RetryOnFailure.NO, ProcessFailureResponse.YES);
this.url = STATE_URL.replaceAll("\\{id\\}", chargerId);
}
@Override
protected Request prepareRequest(Request requestToPrepare) {
requestToPrepare.method(HttpMethod.GET);
return requestToPrepare;
}
@Override
protected String getURL() {
return url;
}
@Override
protected String getChannelGroup() {
return CHANNEL_GROUP_CHARGER_STATE;
}
}

View File

@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
/**
@ -29,10 +30,10 @@ import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
public class GetConfiguration extends AbstractCommand {
private final String url;
public GetConfiguration(EaseeThingHandler handler, String chargerId) {
public GetConfiguration(EaseeThingHandler handler, String chargerId, JsonResultProcessor resultProcessor) {
// retry does not make much sense as it is a polling command, command should always succeed therefore update
// handler on failure.
super(handler, RetryOnFailure.NO, ProcessFailureResponse.YES);
super(handler, RetryOnFailure.NO, ProcessFailureResponse.YES, resultProcessor);
this.url = GET_CONFIGURATION_URL.replaceAll("\\{id\\}", chargerId);
}

View File

@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
/**
@ -29,10 +30,10 @@ import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
public class LatestChargingSession extends AbstractCommand {
private final String url;
public LatestChargingSession(EaseeThingHandler handler, String chargerId) {
public LatestChargingSession(EaseeThingHandler handler, String chargerId, JsonResultProcessor resultProcessor) {
// retry does not make much sense as it is a polling command, command might fail if no charging sessions are
// available, therefore just ignore failure.
super(handler, RetryOnFailure.NO, ProcessFailureResponse.NO);
super(handler, RetryOnFailure.NO, ProcessFailureResponse.NO, resultProcessor);
this.url = LATEST_CHARGING_SESSION_URL.replaceAll("\\{id\\}", chargerId);
}

View File

@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractWriteCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
import org.openhab.core.thing.Channel;
import org.openhab.core.types.Command;
@ -39,8 +40,9 @@ public class SendCommand extends AbstractWriteCommand {
* @param channel the channel that triggered this command
* @param command the command to be send
*/
public SendCommand(EaseeThingHandler handler, String chargerId, Channel channel, Command command) {
this(handler, channel, command);
public SendCommand(EaseeThingHandler handler, String chargerId, Channel channel, Command command,
JsonResultProcessor resultProcessor) {
this(handler, channel, command, resultProcessor);
this.url = COMMANDS_URL.replaceAll("\\{id\\}", chargerId).replaceAll("\\{command\\}", getCommandValue());
}
@ -51,8 +53,8 @@ public class SendCommand extends AbstractWriteCommand {
* @param channel the channel that triggered this command
* @param command the command to be send
*/
SendCommand(EaseeThingHandler handler, Channel channel, Command command) {
super(handler, channel, command, RetryOnFailure.YES, ProcessFailureResponse.YES);
SendCommand(EaseeThingHandler handler, Channel channel, Command command, JsonResultProcessor resultProcessor) {
super(handler, channel, command, RetryOnFailure.YES, ProcessFailureResponse.YES, resultProcessor);
}
@Override

View File

@ -15,6 +15,7 @@ package org.openhab.binding.easee.internal.command.charger;
import static org.openhab.binding.easee.internal.EaseeBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Channel;
@ -28,8 +29,9 @@ import org.openhab.core.types.Command;
@NonNullByDefault
public class SendCommandPauseResume extends SendCommand {
public SendCommandPauseResume(EaseeThingHandler handler, String chargerId, Channel channel, Command command) {
super(handler, channel, command);
public SendCommandPauseResume(EaseeThingHandler handler, String chargerId, Channel channel, Command command,
JsonResultProcessor resultProcessor) {
super(handler, channel, command, resultProcessor);
String value;
if (command.equals(OnOffType.ON)) {
value = CMD_VAL_PAUSE_CHARGING;

View File

@ -15,6 +15,7 @@ package org.openhab.binding.easee.internal.command.charger;
import static org.openhab.binding.easee.internal.EaseeBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Channel;
@ -28,8 +29,9 @@ import org.openhab.core.types.Command;
@NonNullByDefault
public class SendCommandStartStop extends SendCommand {
public SendCommandStartStop(EaseeThingHandler handler, String chargerId, Channel channel, Command command) {
super(handler, channel, command);
public SendCommandStartStop(EaseeThingHandler handler, String chargerId, Channel channel, Command command,
JsonResultProcessor resultProcessor) {
super(handler, channel, command, resultProcessor);
String value;
if (command.equals(OnOffType.ON)) {
value = CMD_VAL_START_CHARGING;

View File

@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
/**
@ -29,8 +30,8 @@ import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
public class CircuitSettings extends AbstractCommand {
private final String url;
public CircuitSettings(EaseeThingHandler handler, String circuitId) {
super(handler, RetryOnFailure.NO, ProcessFailureResponse.YES);
public CircuitSettings(EaseeThingHandler handler, String circuitId, JsonResultProcessor resultProcessor) {
super(handler, RetryOnFailure.NO, ProcessFailureResponse.YES, resultProcessor);
String siteId = handler.getBridgeConfiguration().getSiteId();
this.url = CIRCUIT_SETTINGS_URL.replaceAll("\\{siteId\\}", siteId).replaceAll("\\{circuitId\\}", circuitId);
}

View File

@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
/**
@ -29,8 +30,8 @@ import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
public class DynamicCircuitCurrent extends AbstractCommand {
private final String url;
public DynamicCircuitCurrent(EaseeThingHandler handler, String circuitId) {
super(handler, RetryOnFailure.NO, ProcessFailureResponse.YES);
public DynamicCircuitCurrent(EaseeThingHandler handler, String circuitId, JsonResultProcessor resultProcessor) {
super(handler, RetryOnFailure.NO, ProcessFailureResponse.YES, resultProcessor);
String siteId = handler.getBridgeConfiguration().getSiteId();
this.url = DYNAMIC_CIRCUIT_CURRENT_URL.replaceAll("\\{siteId\\}", siteId).replaceAll("\\{circuitId\\}",
circuitId);

View File

@ -19,6 +19,7 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractWriteCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
import org.openhab.binding.easee.internal.model.ValidationException;
import org.openhab.core.thing.Channel;
@ -33,8 +34,9 @@ import org.openhab.core.types.Command;
public class SetCircuitSettings extends AbstractWriteCommand {
private final String url;
public SetCircuitSettings(EaseeThingHandler handler, Channel channel, Command command, String circuitId) {
super(handler, channel, command, RetryOnFailure.YES, ProcessFailureResponse.YES);
public SetCircuitSettings(EaseeThingHandler handler, Channel channel, Command command, String circuitId,
JsonResultProcessor resultProcessor) {
super(handler, channel, command, RetryOnFailure.YES, ProcessFailureResponse.YES, resultProcessor);
String siteId = handler.getBridgeConfiguration().getSiteId();
this.url = CIRCUIT_SETTINGS_URL.replaceAll("\\{siteId\\}", siteId).replaceAll("\\{circuitId\\}", circuitId);
}

View File

@ -22,6 +22,7 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.command.AbstractWriteCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
import org.openhab.binding.easee.internal.model.ValidationException;
import org.openhab.core.thing.Channel;
@ -39,8 +40,9 @@ public class SetDynamicCircuitCurrents extends AbstractWriteCommand {
private static final String PHASE3 = "phase3";
private final String url;
public SetDynamicCircuitCurrents(EaseeThingHandler handler, Channel channel, Command command, String circuitId) {
super(handler, channel, command, RetryOnFailure.YES, ProcessFailureResponse.YES);
public SetDynamicCircuitCurrents(EaseeThingHandler handler, Channel channel, Command command, String circuitId,
JsonResultProcessor resultProcessor) {
super(handler, channel, command, RetryOnFailure.YES, ProcessFailureResponse.YES, resultProcessor);
String siteId = handler.getBridgeConfiguration().getSiteId();
this.url = DYNAMIC_CIRCUIT_CURRENT_URL.replaceAll("\\{siteId\\}", siteId).replaceAll("\\{circuitId\\}",
circuitId);

View File

@ -16,6 +16,7 @@ import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
import org.openhab.binding.easee.internal.model.ValidationException;
import org.openhab.core.thing.Channel;
@ -32,8 +33,9 @@ public class SetMaxCircuitCurrents extends SetCircuitSettings {
private static final String PHASE2 = "maxCircuitCurrentP2";
private static final String PHASE3 = "maxCircuitCurrentP3";
public SetMaxCircuitCurrents(EaseeThingHandler handler, Channel channel, Command command, String circuitId) {
super(handler, channel, command, circuitId);
public SetMaxCircuitCurrents(EaseeThingHandler handler, Channel channel, Command command, String circuitId,
JsonResultProcessor resultProcessor) {
super(handler, channel, command, circuitId, resultProcessor);
}
/**

View File

@ -16,6 +16,7 @@ import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
import org.openhab.binding.easee.internal.model.ValidationException;
import org.openhab.core.thing.Channel;
@ -32,8 +33,9 @@ public class SetOfflineMaxCircuitCurrents extends SetCircuitSettings {
private static final String PHASE2 = "offlineMaxCircuitCurrentP2";
private static final String PHASE3 = "offlineMaxCircuitCurrentP3";
public SetOfflineMaxCircuitCurrents(EaseeThingHandler handler, Channel channel, Command command, String circuitId) {
super(handler, channel, command, circuitId);
public SetOfflineMaxCircuitCurrents(EaseeThingHandler handler, Channel channel, Command command, String circuitId,
JsonResultProcessor resultProcessor) {
super(handler, channel, command, circuitId, resultProcessor);
}
/**

View File

@ -0,0 +1,116 @@
/**
* Copyright (c) 2010-2023 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.easee.internal.command.site;
import static org.openhab.binding.easee.internal.EaseeBindingConstants.*;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.easee.internal.Utils;
import org.openhab.binding.easee.internal.command.AbstractCommand;
import org.openhab.binding.easee.internal.command.JsonResultProcessor;
import org.openhab.binding.easee.internal.handler.EaseeChargerHandler;
import org.openhab.binding.easee.internal.handler.EaseeThingHandler;
import org.openhab.binding.easee.internal.model.GenericResponseTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* implements the state api call of the site in order to retrieve charger states.
*
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
public class SiteState extends AbstractCommand {
private final String url;
private final Map<String, EaseeChargerHandler> chargerHandlers;
/**
* logger
*/
private final Logger logger = LoggerFactory.getLogger(SiteState.class);
public SiteState(EaseeThingHandler handler, String siteId, Map<String, EaseeChargerHandler> chargerHandlers,
JsonResultProcessor resultProcessor) {
// retry does not make much sense as it is a polling command, command should always succeed therefore update
// handler on failure.
super(handler, RetryOnFailure.NO, ProcessFailureResponse.YES, resultProcessor);
this.url = SITE_STATE_URL.replaceAll("\\{siteId\\}", siteId);
this.chargerHandlers = chargerHandlers;
}
@Override
protected Request prepareRequest(Request requestToPrepare) {
requestToPrepare.method(HttpMethod.GET);
return requestToPrepare;
}
@Override
protected String getURL() {
return url;
}
@Override
protected String getChannelGroup() {
return CHANNEL_GROUP_CHARGER_STATE;
}
/**
* override default behaviour: extract ChargerState from SiteState
*/
@Override
protected void onCompleteCodeOk(@Nullable String json) {
JsonObject jsonObject = transform(json);
if (jsonObject != null) {
logger.debug("success");
JsonArray circuitStates = jsonObject.getAsJsonArray(JSON_KEY_CIRCUIT_STATES);
for (JsonElement circuitState : circuitStates) {
JsonArray chargerDataArray = circuitState.getAsJsonObject().getAsJsonArray(JSON_KEY_CHARGER_STATES);
for (JsonElement chargerData : chargerDataArray) {
processChargerStateData(chargerData.getAsJsonObject());
}
}
}
}
/**
* processes charger data, also sets online status retrieved from API.
*
* @param chargerData
*/
private void processChargerStateData(JsonObject chargerData) {
JsonElement chargerId = chargerData.get(JSON_KEY_CHARGER_ID);
String id = chargerId != null ? chargerId.getAsString() : null;
if (id != null) {
EaseeChargerHandler handler = chargerHandlers.get(id);
if (handler != null) {
GenericResponseTransformer transformer = new GenericResponseTransformer(handler);
JsonElement chargerState = chargerData.getAsJsonObject().get(JSON_KEY_CHARGER_STATE);
JsonObject jsonObject = chargerState.getAsJsonObject();
Boolean isOnline = Utils.getAsBool(jsonObject, JSON_KEY_ONLINE);
handler.updateChannelStatus(transformer.transform(jsonObject, getChannelGroup()));
handler.setOnline(isOnline == null ? false : isOnline);
}
}
}
}

View File

@ -59,7 +59,7 @@ public class WebInterface implements AtomicReferenceTrait {
/**
* handler for updating bridge status
*/
private final StatusHandler statusHandler;
private final StatusHandler bridgeStatusHandler;
/**
* holds authentication status
@ -135,7 +135,8 @@ public class WebInterface implements AtomicReferenceTrait {
switch (status.getHttpCode()) {
case BAD_REQUEST:
statusHandler.updateStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, msg);
bridgeStatusHandler.updateStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
msg);
setAuthenticated(false);
break;
case OK:
@ -151,14 +152,15 @@ public class WebInterface implements AtomicReferenceTrait {
logger.debug("access token refreshed: {}, expiry: {}", Utils.formatDate(tokenRefreshDate),
Utils.formatDate(tokenExpiry));
statusHandler.updateStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE,
bridgeStatusHandler.updateStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE,
STATUS_TOKEN_VALIDATED);
setAuthenticated(true);
handler.startDiscovery();
break;
}
default:
statusHandler.updateStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, msg);
bridgeStatusHandler.updateStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
msg);
setAuthenticated(false);
}
}
@ -207,8 +209,7 @@ public class WebInterface implements AtomicReferenceTrait {
*/
private synchronized void authenticate() {
setAuthenticated(false);
EaseeCommand loginCommand = new Login(handler);
loginCommand.registerResultProcessor(this::processAuthenticationResult);
EaseeCommand loginCommand = new Login(handler, this::processAuthenticationResult);
try {
loginCommand.performAction(httpClient, accessToken);
} catch (ValidationException e) {
@ -227,8 +228,8 @@ public class WebInterface implements AtomicReferenceTrait {
logger.debug("access token needs to be refreshed, last refresh: {}, expiry: {}",
Utils.formatDate(tokenRefreshDate), Utils.formatDate(tokenExpiry));
EaseeCommand refreshCommand = new RefreshToken(handler, accessToken, refreshToken);
refreshCommand.registerResultProcessor(this::processAuthenticationResult);
EaseeCommand refreshCommand = new RefreshToken(handler, accessToken, refreshToken,
this::processAuthenticationResult);
try {
refreshCommand.performAction(httpClient, accessToken);
} catch (ValidationException e) {
@ -245,28 +246,9 @@ public class WebInterface implements AtomicReferenceTrait {
private void executeCommand() throws ValidationException {
EaseeCommand command = commandQueue.poll();
if (command != null) {
command.registerResultProcessor(this::processExecutionResult);
command.performAction(httpClient, accessToken);
}
}
private void processExecutionResult(CommunicationStatus status, JsonObject jsonObject) {
String msg = Utils.getAsString(jsonObject, JSON_KEY_ERROR_TITLE);
if (msg == null || msg.isBlank()) {
msg = status.getMessage();
}
switch (status.getHttpCode()) {
case OK:
case ACCEPTED:
// no action needed as the thing is already online.
break;
default:
statusHandler.updateStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, msg);
setAuthenticated(false);
}
}
}
/**
@ -275,9 +257,9 @@ public class WebInterface implements AtomicReferenceTrait {
* @param config Bridge configuration
*/
public WebInterface(ScheduledExecutorService scheduler, EaseeBridgeHandler handler, HttpClient httpClient,
StatusHandler statusHandler) {
StatusHandler bridgeStatusHandler) {
this.handler = handler;
this.statusHandler = statusHandler;
this.bridgeStatusHandler = bridgeStatusHandler;
this.scheduler = scheduler;
this.httpClient = httpClient;
this.tokenExpiry = OUTDATED_DATE;

View File

@ -27,7 +27,6 @@ import org.openhab.binding.easee.internal.Utils;
import org.openhab.binding.easee.internal.command.EaseeCommand;
import org.openhab.binding.easee.internal.command.charger.ChangeConfiguration;
import org.openhab.binding.easee.internal.command.charger.Charger;
import org.openhab.binding.easee.internal.command.charger.ChargerState;
import org.openhab.binding.easee.internal.command.charger.GetConfiguration;
import org.openhab.binding.easee.internal.command.charger.LatestChargingSession;
import org.openhab.binding.easee.internal.command.charger.SendCommand;
@ -72,16 +71,20 @@ public class EaseeChargerHandler extends BaseThingHandler implements EaseeThingH
@Override
public void initialize() {
logger.debug("About to initialize Charger");
String chargerId = getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
logger.debug("Easee Charger initialized with id: {}", chargerId);
logger.debug("Easee Charger initialized with id: {}", getId());
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, STATUS_WAITING_FOR_BRIDGE);
startPolling();
enqueueCommand(new Charger(this, chargerId, this::updateProperties));
enqueueCommand(new Charger(this, getId(), this::updatePropertiesAndOnlineStatus));
}
private void updateProperties(CommunicationStatus status, JsonObject charger) {
public String getId() {
return getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
}
private void updatePropertiesAndOnlineStatus(CommunicationStatus status, JsonObject charger) {
updateOnlineStatus(status, charger);
Map<String, String> properties = editProperties();
String backPlateId = Utils.getAsString(charger.getAsJsonObject(JSON_KEY_BACK_PLATE), JSON_KEY_GENERIC_ID);
@ -123,35 +126,48 @@ public class EaseeChargerHandler extends BaseThingHandler implements EaseeThingH
String chargerId = getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
logger.debug("polling charger data for {}", chargerId);
ChargerState state = new ChargerState(this, chargerId);
state.registerResultProcessor(this::updateStatusInfo);
enqueueCommand(state);
// proceed if charger is online
if (getThing().getStatus() == ThingStatus.ONLINE) {
enqueueCommand(new GetConfiguration(this, chargerId));
enqueueCommand(new LatestChargingSession(this, chargerId));
enqueueCommand(new GetConfiguration(this, chargerId, this::updateOnlineStatus));
enqueueCommand(new LatestChargingSession(this, chargerId, this::updateOnlineStatus));
}
}
/**
* updates status depending on online information received from the API.
* updates online status depending on online information received from the API. this is called by the SiteState
* Command which retrieves whole site data inclusing charger status.
*
* @param status
* @param jsonObject
*/
private void updateStatusInfo(CommunicationStatus status, JsonObject jsonObject) {
Boolean isOnline = Utils.getAsBool(jsonObject, JSON_KEY_ONLINE);
if (isOnline == null) {
super.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, STATUS_NO_VALID_DATA);
} else if (isOnline) {
public void setOnline(boolean isOnline) {
if (isOnline) {
super.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
} else {
super.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, STATUS_NO_CONNECTION);
}
}
/**
* result processor to handle online status updates
*
* @param status of command execution
* @param jsonObject json respone result
*/
protected final void updateOnlineStatus(CommunicationStatus status, JsonObject jsonObject) {
String msg = Utils.getAsString(jsonObject, JSON_KEY_ERROR_TITLE);
if (msg == null || msg.isBlank()) {
msg = status.getMessage();
}
switch (status.getHttpCode()) {
case OK:
case ACCEPTED:
super.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
break;
default:
super.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, msg);
}
}
/**
* Disposes the thing.
*/
@ -214,13 +230,13 @@ public class EaseeChargerHandler extends BaseThingHandler implements EaseeThingH
switch (Utils.getWriteCommand(channel)) {
case COMMAND_CHANGE_CONFIGURATION:
return new ChangeConfiguration(this, chargerId, channel, command);
return new ChangeConfiguration(this, chargerId, channel, command, this::updateOnlineStatus);
case COMMAND_SEND_COMMAND:
return new SendCommand(this, chargerId, channel, command);
return new SendCommand(this, chargerId, channel, command, this::updateOnlineStatus);
case COMMAND_SEND_COMMAND_START_STOP:
return new SendCommandStartStop(this, chargerId, channel, command);
return new SendCommandStartStop(this, chargerId, channel, command, this::updateOnlineStatus);
case COMMAND_SEND_COMMAND_PAUSE_RESUME:
return new SendCommandPauseResume(this, chargerId, channel, command);
return new SendCommandPauseResume(this, chargerId, channel, command, this::updateOnlineStatus);
default:
// this should not happen
logger.error("write command '{}' not found for channel '{}'", command.toString(),

View File

@ -57,8 +57,8 @@ public class EaseeMasterChargerHandler extends EaseeChargerHandler {
String circuitId = getConfig().get(EaseeBindingConstants.THING_CONFIG_CIRCUIT_ID).toString();
logger.debug("polling circuit data for {}", circuitId);
enqueueCommand(new DynamicCircuitCurrent(this, circuitId));
enqueueCommand(new CircuitSettings(this, circuitId));
enqueueCommand(new DynamicCircuitCurrent(this, circuitId, this::updateOnlineStatus));
enqueueCommand(new CircuitSettings(this, circuitId, this::updateOnlineStatus));
}
}
@ -68,13 +68,13 @@ public class EaseeMasterChargerHandler extends EaseeChargerHandler {
switch (Utils.getWriteCommand(channel)) {
case COMMAND_SET_CIRCUIT_SETTINGS:
return new SetCircuitSettings(this, channel, command, circuitId);
return new SetCircuitSettings(this, channel, command, circuitId, this::updateOnlineStatus);
case COMMAND_SET_DYNAMIC_CIRCUIT_CURRENTS:
return new SetDynamicCircuitCurrents(this, channel, command, circuitId);
return new SetDynamicCircuitCurrents(this, channel, command, circuitId, this::updateOnlineStatus);
case COMMAND_SET_MAX_CIRCUIT_CURRENTS:
return new SetMaxCircuitCurrents(this, channel, command, circuitId);
return new SetMaxCircuitCurrents(this, channel, command, circuitId, this::updateOnlineStatus);
case COMMAND_SET_OFFLINE_MAX_CIRCUIT_CURRENTS:
return new SetOfflineMaxCircuitCurrents(this, channel, command, circuitId);
return new SetOfflineMaxCircuitCurrents(this, channel, command, circuitId, this::updateOnlineStatus);
default:
return super.buildEaseeCommand(command, channel);
}

View File

@ -16,14 +16,21 @@ import static org.openhab.binding.easee.internal.EaseeBindingConstants.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.easee.internal.AtomicReferenceTrait;
import org.openhab.binding.easee.internal.EaseeBindingConstants;
import org.openhab.binding.easee.internal.Utils;
import org.openhab.binding.easee.internal.command.EaseeCommand;
import org.openhab.binding.easee.internal.command.site.GetSite;
import org.openhab.binding.easee.internal.command.site.SiteState;
import org.openhab.binding.easee.internal.config.EaseeConfiguration;
import org.openhab.binding.easee.internal.connector.CommunicationStatus;
import org.openhab.binding.easee.internal.connector.WebInterface;
@ -46,9 +53,14 @@ import com.google.gson.JsonObject;
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
public class EaseeSiteHandler extends BaseBridgeHandler implements EaseeBridgeHandler {
public class EaseeSiteHandler extends BaseBridgeHandler implements EaseeBridgeHandler, AtomicReferenceTrait {
private final Logger logger = LoggerFactory.getLogger(EaseeSiteHandler.class);
/**
* Schedule for polling live data
*/
private final AtomicReference<@Nullable Future<?>> dataPollingJobReference;
private @Nullable DiscoveryService discoveryService;
/**
@ -58,6 +70,7 @@ public class EaseeSiteHandler extends BaseBridgeHandler implements EaseeBridgeHa
public EaseeSiteHandler(Bridge bridge, HttpClient httpClient) {
super(bridge);
this.dataPollingJobReference = new AtomicReference<>(null);
this.webInterface = new WebInterface(scheduler, this, httpClient, super::updateStatus);
}
@ -69,6 +82,7 @@ public class EaseeSiteHandler extends BaseBridgeHandler implements EaseeBridgeHa
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, STATUS_WAITING_FOR_LOGIN);
webInterface.start();
startPolling();
enqueueCommand(new GetSite(this, this::updateProperties));
}
@ -86,12 +100,77 @@ public class EaseeSiteHandler extends BaseBridgeHandler implements EaseeBridgeHa
updateProperties(properties);
}
/**
* Start the polling.
*/
private void startPolling() {
updateJobReference(dataPollingJobReference, scheduler.scheduleWithFixedDelay(this::pollingRun,
POLLING_INITIAL_DELAY, getBridgeConfiguration().getDataPollingInterval(), TimeUnit.SECONDS));
}
/**
* Poll the Easee Cloud API one time.
*/
void pollingRun() {
String siteId = getConfig().get(EaseeBindingConstants.THING_CONFIG_SITE_ID).toString();
logger.debug("polling site data for {}", siteId);
SiteState state = new SiteState(this, siteId, getChildChargerHandlers(), this::updateOnlineStatus);
enqueueCommand(state);
// proceed if site is online
if (getThing().getStatus() == ThingStatus.ONLINE) {
// add further polling commands here
}
}
/**
* creates a map containing all child chargers/masterchargers identified by their unique id.
*
* @return the map created
*/
private Map<String, EaseeChargerHandler> getChildChargerHandlers() {
Map<String, EaseeChargerHandler> chargerHandlers = new HashMap<>();
getThing().getThings().stream().filter(x -> x.getThingTypeUID().equals(THING_TYPE_CHARGER)
|| x.getThingTypeUID().equals(THING_TYPE_MASTER_CHARGER)).forEach(y -> {
EaseeChargerHandler handler = (EaseeChargerHandler) y.getHandler();
if (handler != null) {
chargerHandlers.put(handler.getId(), handler);
}
});
return chargerHandlers;
}
/**
* result processor to handle online status updates
*
* @param status of command execution
* @param jsonObject json respone result
*/
protected final void updateOnlineStatus(CommunicationStatus status, JsonObject jsonObject) {
String msg = Utils.getAsString(jsonObject, JSON_KEY_ERROR_TITLE);
if (msg == null || msg.isBlank()) {
msg = status.getMessage();
}
switch (status.getHttpCode()) {
case OK:
case ACCEPTED:
super.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
break;
default:
super.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, msg);
}
}
/**
* Disposes the bridge.
*/
@Override
public void dispose() {
logger.debug("Handler disposed.");
cancelJobReference(dataPollingJobReference);
webInterface.dispose();
}

View File

@ -73,6 +73,7 @@ public class GenericResponseTransformer {
result.put(channel, UnDefType.NULL);
} else {
try {
String channelTypeId = Utils.getChannelTypeId(channel);
switch (channelType) {
case CHANNEL_TYPE_SWITCH:
result.put(channel, OnOffType.from(Boolean.parseBoolean(value)));
@ -87,9 +88,16 @@ public class GenericResponseTransformer {
result.put(channel, new QuantityType<>(Double.parseDouble(value),
MetricPrefix.KILO(Units.WATT_HOUR)));
break;
case CHANNEL_TYPE_KW:
result.put(channel,
new QuantityType<>(Double.parseDouble(value), MetricPrefix.KILO(Units.WATT)));
case CHANNEL_TYPE_POWER:
if (channelTypeId.equals(CHANNEL_TYPENAME_RSSI)) {
// explicit type long is needed in case of integer/long values otherwise automatic
// transformation to a decimal type is applied.
result.put(channel,
new QuantityType<>(Long.parseLong(value), Units.DECIBEL_MILLIWATTS));
} else {
result.put(channel, new QuantityType<>(Double.parseDouble(value),
MetricPrefix.KILO(Units.WATT)));
}
break;
case CHANNEL_TYPE_DATE:
result.put(channel, new DateTimeType(Utils.parseDate(value)));
@ -98,7 +106,7 @@ public class GenericResponseTransformer {
result.put(channel, new StringType(value));
break;
case CHANNEL_TYPE_NUMBER:
if (Utils.getChannelTypeId(channel).contains(CHANNEL_TYPENAME_INTEGER)) {
if (channelTypeId.contains(CHANNEL_TYPENAME_INTEGER)) {
// explicit type long is needed in case of integer/long values otherwise automatic
// transformation to a decimal type is applied.
result.put(channel, new DecimalType(Long.parseLong(value)));

View File

@ -30,6 +30,18 @@
<label>Session Energy</label>
<description>Energy for current session.</description>
</channel>
<channel id="energyPerHour" typeId="type-energy">
<label>Energy per Hour</label>
<description>Energy transferred per hour.</description>
</channel>
<channel id="wiFiRSSI" typeId="type-rssi">
<label>Wi-Fi RSSI</label>
<description>Wi-Fi signal quality.</description>
</channel>
<channel id="cellRSSI" typeId="type-rssi">
<label>Cell RSSI</label>
<description>Cell signal quality.</description>
</channel>
<channel id="dynamicCircuitCurrentP1" typeId="type-current">
<label>Dynamic Circuit Current P1</label>
<description>Dynamic set circuit current for phase 1.</description>
@ -50,14 +62,26 @@
<label>Current Firmware</label>
<description>Current Firmware of the wallbox.</description>
</channel>
<channel id="latestFirmware" typeId="type-integer">
<label>Latest Firmware</label>
<description>Latest Firmware which is available for the wallbox.</description>
</channel>
<channel id="voltage" typeId="type-volt">
<label>Voltage</label>
<description>Voltage</description>
</channel>
<channel id="inCurrentT2" typeId="type-current">
<label>Input Current T2</label>
<description>Input Current on phase T2</description>
</channel>
<channel id="inCurrentT3" typeId="type-current">
<label>Input Current T3</label>
<description>Input Current on phase T3</description>
</channel>
<channel id="inCurrentT4" typeId="type-current">
<label>Input Current T4</label>
<description>Input Current on phase T4</description>
</channel>
<channel id="inCurrentT5" typeId="type-current">
<label>Input Current T5</label>
<description>Input Current on phase T5</description>
</channel>
<channel id="outputCurrent" typeId="type-current">
<label>Output Current</label>
<description>Actual charging current.</description>
@ -66,6 +90,54 @@
<label>Online</label>
<description>Online status of the wallbox.</description>
</channel>
<channel id="inVoltageT1T2" typeId="type-volt">
<label>Input Voltage T1T2</label>
<description>Input voltage between phase T1 and T2</description>
</channel>
<channel id="inVoltageT1T3" typeId="type-volt">
<label>Input Voltage T1T3</label>
<description>Input voltage between phase T1 and T3</description>
</channel>
<channel id="inVoltageT1T4" typeId="type-volt">
<label>Input Voltage T1T4</label>
<description>Input voltage between phase T1 and T4</description>
</channel>
<channel id="inVoltageT1T5" typeId="type-volt">
<label>Input Voltage T1T5</label>
<description>Input voltage between phase T1 and T5</description>
</channel>
<channel id="inVoltageT2T3" typeId="type-volt">
<label>Input Voltage T2T3</label>
<description>Input voltage between phase T2 and T3</description>
</channel>
<channel id="inVoltageT2T4" typeId="type-volt">
<label>Input Voltage T2T4</label>
<description>Input voltage between phase T2 and T4</description>
</channel>
<channel id="inVoltageT2T5" typeId="type-volt">
<label>Input Voltage T2T5</label>
<description>Input voltage between phase T2 and T5</description>
</channel>
<channel id="inVoltageT3T4" typeId="type-volt">
<label>Input Voltage T3T4</label>
<description>Input voltage between phase T3 and T4</description>
</channel>
<channel id="inVoltageT3T5" typeId="type-volt">
<label>Input Voltage T3T5</label>
<description>Input voltage between phase T3 and T5</description>
</channel>
<channel id="inVoltageT4T5" typeId="type-volt">
<label>Input Voltage T4T5</label>
<description>Input voltage between phase T4 and T5</description>
</channel>
<channel id="ledMode" typeId="type-integer">
<label>Led Mode</label>
<description>Led Mode.</description>
</channel>
<channel id="cableRating" typeId="type-current">
<label>Cable Rating</label>
<description>Rating of the connected cable.</description>
</channel>
<channel id="dynamicChargerCurrent" typeId="rwtype-current">
<label>Dynamic Charger Current</label>
<description>Dynamic set charging current.</description>
@ -90,6 +162,10 @@
<label>Fatal Error Code</label>
<description>Fatal Error Code.</description>
</channel>
<channel id="connectedToCloud" typeId="type-switch">
<label>Connected to Cloud</label>
<description>Cloud connection status of the wallbox.</description>
</channel>
</channels>
</channel-group-type>
<channel-group-type id="charger-config">

View File

@ -3,6 +3,13 @@
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">
<channel-type id="type-rssi">
<item-type>Number:Power</item-type>
<label>Received Signal Strength Indicator</label>
<category>QualityOfService</category>
<state pattern="%d %unit%" readOnly="true">
</state>
</channel-type>
<channel-type id="type-power">
<item-type>Number:Power</item-type>
<label>Power</label>

View File

@ -22,6 +22,9 @@
<channel-group typeId="circuit-dynamicCurrent" id="dynamicCurrent"/>
<channel-group typeId="circuit-settings" id="settings"/>
</channel-groups>
<properties>
<property name="thingTypeVersion">1</property>
</properties>
<config-description-ref uri="thing-type:easee:mastercharger"/>
</thing-type>
<thing-type id="charger">
@ -36,6 +39,9 @@
<channel-group typeId="charger-commands" id="commands"/>
<channel-group typeId="charger-latestSession" id="latestSession"/>
</channel-groups>
<properties>
<property name="thingTypeVersion">1</property>
</properties>
<config-description-ref uri="thing-type:easee:charger"/>
</thing-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
<thing-type uid="easee:mastercharger">
<instruction-set targetVersion="1">
<add-channel id="energyPerHour" groupIds="state">
<type>easee:type-energy</type>
<label>Energy per Hour</label>
<description>Energy transferred per hour.</description>
</add-channel>
<add-channel id="wiFiRSSI" groupIds="state">
<type>easee:type-rssi</type>
<label>Wi-Fi RSSI</label>
<description>Wi-Fi signal quality.</description>
</add-channel>
<add-channel id="cellRSSI" groupIds="state">
<type>easee:type-rssi</type>
<label>Cell RSSI</label>
<description>Cell signal quality.</description>
</add-channel>
<add-channel id="inCurrentT2" groupIds="state">
<type>easee:type-current</type>
<label>Input Current T2</label>
<description>Input Current on phase T2</description>
</add-channel>
<add-channel id="inCurrentT3" groupIds="state">
<type>easee:type-current</type>
<label>Input Current T3</label>
<description>Input Current on phase T3</description>
</add-channel>
<add-channel id="inCurrentT4" groupIds="state">
<type>easee:type-current</type>
<label>Input Current T4</label>
<description>Input Current on phase T4</description>
</add-channel>
<add-channel id="inCurrentT5" groupIds="state">
<type>easee:type-current</type>
<label>Input Current T5</label>
<description>Input Current on phase T5</description>
</add-channel>
<add-channel id="inVoltageT1T2" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T1T2</label>
<description>Input voltage between phase T1 and T2</description>
</add-channel>
<add-channel id="inVoltageT1T3" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T1T3</label>
<description>Input Voltage between phase T1 and T3</description>
</add-channel>
<add-channel id="inVoltageT1T4" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T1T4</label>
<description>Input Voltage between phase T1 and T4</description>
</add-channel>
<add-channel id="inVoltageT1T5" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T1T5</label>
<description>Input Voltage between phase T1 and T5</description>
</add-channel>
<add-channel id="inVoltageT2T3" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T2T3</label>
<description>Input Voltage between phase T2 and T3</description>
</add-channel>
<add-channel id="inVoltageT2T4" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T2T4</label>
<description>Input Voltage between phase T2 and T4</description>
</add-channel>
<add-channel id="inVoltageT2T5" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T2T5</label>
<description>Input Voltage between phase T2 and T5</description>
</add-channel>
<add-channel id="inVoltageT3T4" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T3T4</label>
<description>Input Voltage between phase T3 and T4</description>
</add-channel>
<add-channel id="inVoltageT3T5" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T3T5</label>
<description>Input Voltage between phase T3 and T5</description>
</add-channel>
<add-channel id="inVoltageT4T5" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T4T5</label>
<description>Input Voltage between phase T4 and T5</description>
</add-channel>
<add-channel id="ledMode" groupIds="state">
<type>easee:type-integer</type>
<label>Led Mode</label>
<description>Led Mode.</description>
</add-channel>
<add-channel id="cableRating" groupIds="state">
<type>easee:type-current</type>
<label>Cable Rating</label>
<description>Rating of the connected cable.</description>
</add-channel>
<add-channel id="connectedToCloud" groupIds="state">
<type>easee:type-switch</type>
<label>Connected to Cloud</label>
<description>Cloud connection status of the wallbox.</description>
</add-channel>
<remove-channel id="latestFirmware" groupIds="state"/>
</instruction-set>
</thing-type>
<thing-type uid="easee:charger">
<instruction-set targetVersion="1">
<add-channel id="energyPerHour" groupIds="state">
<type>easee:type-energy</type>
<label>Energy per Hour</label>
<description>Energy transferred per hour.</description>
</add-channel>
<add-channel id="wiFiRSSI" groupIds="state">
<type>easee:type-rssi</type>
<label>Wi-Fi RSSI</label>
<description>Wi-Fi signal quality.</description>
</add-channel>
<add-channel id="cellRSSI" groupIds="state">
<type>easee:type-rssi</type>
<label>Cell RSSI</label>
<description>Cell signal quality.</description>
</add-channel>
<add-channel id="inCurrentT2" groupIds="state">
<type>easee:type-current</type>
<label>Input Current T2</label>
<description>Input Current on phase T2</description>
</add-channel>
<add-channel id="inCurrentT3" groupIds="state">
<type>easee:type-current</type>
<label>Input Current T3</label>
<description>Input Current on phase T3</description>
</add-channel>
<add-channel id="inCurrentT4" groupIds="state">
<type>easee:type-current</type>
<label>Input Current T4</label>
<description>Input Current on phase T4</description>
</add-channel>
<add-channel id="inCurrentT5" groupIds="state">
<type>easee:type-current</type>
<label>Input Current T5</label>
<description>Input Current on phase T5</description>
</add-channel>
<add-channel id="inVoltageT1T2" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T1T2</label>
<description>Input voltage between phase T1 and T2</description>
</add-channel>
<add-channel id="inVoltageT1T3" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T1T3</label>
<description>Input Voltage between phase T1 and T3</description>
</add-channel>
<add-channel id="inVoltageT1T4" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T1T4</label>
<description>Input Voltage between phase T1 and T4</description>
</add-channel>
<add-channel id="inVoltageT1T5" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T1T5</label>
<description>Input Voltage between phase T1 and T5</description>
</add-channel>
<add-channel id="inVoltageT2T3" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T2T3</label>
<description>Input Voltage between phase T2 and T3</description>
</add-channel>
<add-channel id="inVoltageT2T4" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T2T4</label>
<description>Input Voltage between phase T2 and T4</description>
</add-channel>
<add-channel id="inVoltageT2T5" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T2T5</label>
<description>Input Voltage between phase T2 and T5</description>
</add-channel>
<add-channel id="inVoltageT3T4" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T3T4</label>
<description>Input Voltage between phase T3 and T4</description>
</add-channel>
<add-channel id="inVoltageT3T5" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T3T5</label>
<description>Input Voltage between phase T3 and T5</description>
</add-channel>
<add-channel id="inVoltageT4T5" groupIds="state">
<type>easee:type-volt</type>
<label>Input Voltage T4T5</label>
<description>Input Voltage between phase T4 and T5</description>
</add-channel>
<add-channel id="ledMode" groupIds="state">
<type>easee:type-integer</type>
<label>Led Mode</label>
<description>Led Mode.</description>
</add-channel>
<add-channel id="cableRating" groupIds="state">
<type>easee:type-current</type>
<label>Cable Rating</label>
<description>Rating of the connected cable.</description>
</add-channel>
<add-channel id="connectedToCloud" groupIds="state">
<type>easee:type-switch</type>
<label>Connected to Cloud</label>
<description>Cloud connection status of the wallbox.</description>
</add-channel>
<remove-channel id="latestFirmware" groupIds="state"/>
</instruction-set>
</thing-type>
</update:update-descriptions>