]> git.basschouten.com Git - openhab-addons.git/commitdiff
[easee] Migrate charger state API endpoint, add channels (#14614)
authorAlexander Friese <alexf2015@users.noreply.github.com>
Fri, 18 Aug 2023 11:33:19 +0000 (13:33 +0200)
committerGitHub <noreply@github.com>
Fri, 18 Aug 2023 11:33:19 +0000 (13:33 +0200)
* 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>
30 files changed:
bundles/org.openhab.binding.easee/README.md
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/EaseeBindingConstants.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/AbstractCommand.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/AbstractWriteCommand.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/EaseeCommand.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/account/Login.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/account/RefreshToken.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/charger/ChangeConfiguration.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/charger/ChargerState.java [deleted file]
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/charger/GetConfiguration.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/charger/LatestChargingSession.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/charger/SendCommand.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/charger/SendCommandPauseResume.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/charger/SendCommandStartStop.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/circuit/CircuitSettings.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/circuit/DynamicCircuitCurrent.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/circuit/SetCircuitSettings.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/circuit/SetDynamicCircuitCurrents.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/circuit/SetMaxCircuitCurrents.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/circuit/SetOfflineMaxCircuitCurrents.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/site/SiteState.java [new file with mode: 0644]
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/connector/WebInterface.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/handler/EaseeChargerHandler.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/handler/EaseeMasterChargerHandler.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/handler/EaseeSiteHandler.java
bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/model/GenericResponseTransformer.java
bundles/org.openhab.binding.easee/src/main/resources/OH-INF/thing/charger-channel-groups.xml
bundles/org.openhab.binding.easee/src/main/resources/OH-INF/thing/easee-readonly-channel-types.xml
bundles/org.openhab.binding.easee/src/main/resources/OH-INF/thing/things.xml
bundles/org.openhab.binding.easee/src/main/resources/OH-INF/update/instructions.xml [new file with mode: 0644]

index 452dd1c25b14317a29a96de043805e0456e55a04..296fe7eeaf2132079f04b34d06f784c502bde189 100644 (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        |
index cb02ab7f8a24c02ae6f7558c8fcdfa4b4abea2bd..2c97616dd56ada2c3b330624bd649943bf518284 100644 (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}";
index d1bc6686140813fbfca8c760aa5e1d6a829de235..42cfb43d46e2f1b34d3e1fd67e0727b20cc815c4 100644 (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;
+    private final JsonResultProcessor resultProcessor;
 
     /**
      * the constructor
      */
     public AbstractCommand(EaseeThingHandler handler, RetryOnFailure retryOnFailure,
-            ProcessFailureResponse processFailureResponse) {
+            ProcessFailureResponse processFailureResponse, JsonResultProcessor resultProcessor) {
         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;
-    }
-
-    /**
-     * the constructor
-     */
-    public AbstractCommand(EaseeThingHandler handler, RetryOnFailure retryOnFailure,
-            ProcessFailureResponse processFailureResponse, JsonResultProcessor resultProcessor) {
-        this(handler, retryOnFailure, processFailureResponse);
-        this.resultProcessors.add(resultProcessor);
+        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);
-    }
 }
index 2e3a24493b4b70765f1309d0eae4455120c4ad6b..93bfed6a919de2bc333a0b6a1314569cbcb11b38 100644 (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;
     }
index 57fcaa4ad3c86e7645fb68eee367960bb4ecf19c..46b3901fa7f91cecac998884d8460ecdc0b52226 100644 (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);
 }
index cf6826e3e383080e1c078a8ea5039a81e0031ddb..045e0fee8864daa145ece410c15bc82c83454da8 100644 (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());
     }
index 664122d4406f3a5c6036c2e19a8ed56da988976e..7f4bd2fe10dcd33413f12b1e05333823f6006ced 100644 (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);
     }
 
index 4ba56e706885958341dfca257573f2a278251ad1..9ff40968c995d1a6a214a05178eb67bea1a2d57c 100644 (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);
     }
 
diff --git a/bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/charger/ChargerState.java b/bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/charger/ChargerState.java
deleted file mode 100644 (file)
index 2b6a222..0000000
+++ /dev/null
@@ -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;
-    }
-}
index e1067b46a86159cfed28d8fcb087ec725a9f28f0..5f0d4188f2fa4255247a3716fdfbbf139648fa86 100644 (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);
     }
 
index fea34157b96e229ba0f491834381b5e91017c0bf..d45227d6449b9dc9a24af8b95ebeafdcaacb0902 100644 (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);
     }
 
index fc36e1b710d8d6dec691b72887c774473841ed1c..81992f917160af7786963bbb9c0dbbd14730228b 100644 (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
index dadce6c5d893dd92222872d39cc623c47f12375f..6525f0546656b0688c72213eeec1461ff05ee728 100644 (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;
index 77593db4407bcc7cd551c20e4f8d62fb5e58ba57..725ceecad0829abbe127cc5042d39f1941726e50 100644 (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;
index 5c79e06d24df39d2c04881cc5c5fc62f27398bb2..cc43f2dd1ce61af8ca4487258285041b0a83e39b 100644 (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);
     }
index 0afac994229bed748a264f0ef25237391671410e..fe130c615ba6e14a8b47615d0c8054100280b51e 100644 (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);
index 41ecf49c91c38b6e50c690ba6e4e8c2fbddf9ac3..5a6c07ca6ecdb70ce1af39f49df7ff1299149afa 100644 (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);
     }
index ca014726f23d8fc35bc13ff7dccdb08cb524895f..b26c356b24700c2688177c0d38aaa4f549a382d5 100644 (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);
index bde65e201940b10d4a4e11b91758983d19e027b1..f66ed7216b6214ac3af8cfc40b53f235760c83c1 100644 (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);
     }
 
     /**
index ea386ed1dc83f19269aee7e94a1c433fd82fe820..3d597272f2ebcf04719518ee9246371ce737d0e1 100644 (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);
     }
 
     /**
diff --git a/bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/site/SiteState.java b/bundles/org.openhab.binding.easee/src/main/java/org/openhab/binding/easee/internal/command/site/SiteState.java
new file mode 100644 (file)
index 0000000..a2d4199
--- /dev/null
@@ -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);
+            }
+        }
+    }
+}
index 6c6d29cf921145bff632d6db7f26611f2326f795..c14c8e7eef481f60c77bca3ffbfca134f34ed72b 100644 (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;
index 9ac3e1fa5d405d9d756bb63b5fbc4f619fd839a4..508fc42093ae83b8b4e9f2cf31c5f70eee2693b5 100644 (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));
+    }
+
+    public String getId() {
+        return getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
     }
 
-    private void updateProperties(CommunicationStatus status, JsonObject charger) {
+    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(),
index fcf7fc27afa799fdc7722d3cd205bf2ff95e4efc..08543f156a3367a792efddd0200940fa384bf183 100644 (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);
         }
index 612c985fd714d7bbd659af01097414bcb2115aba..ef007fe58eeab53cf27647f8831671cea24796a9 100644 (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();
     }
 
index 02a86f438bb38ea00455106688b2e14888446fad..dc514f3c30db088f30e3e53f9dd8156f1ac64a21 100644 (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)));
index 37bf84f6a88a814386984d834196dc5210bb4f6f..cb929c75ee39c4723b3a747de9093be5b540f9d6 100644 (file)
                                <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>
                                <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>
                                <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>
                                <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">
index 3f2e613fab34e5df478e346c967aa21fb95500f5..eaf3c73ff94500de2845a61a1704d93572188932 100644 (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>
index d0f1049eaf47d1d3bb19795c84b6e88ebef5af37..f5673e1dde797b5e07d258cec183c7d10e42547c 100644 (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>
diff --git a/bundles/org.openhab.binding.easee/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.easee/src/main/resources/OH-INF/update/instructions.xml
new file mode 100644 (file)
index 0000000..8297766
--- /dev/null
@@ -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>