]> git.basschouten.com Git - openhab-addons.git/commitdiff
[verisure] Adapted to new authentication process and support for non MFA activated...
authorJan Gustafsson <jannegpriv@gmail.com>
Sat, 16 Oct 2021 09:28:08 +0000 (11:28 +0200)
committerGitHub <noreply@github.com>
Sat, 16 Oct 2021 09:28:08 +0000 (11:28 +0200)
* [verisure] Adapted to new authentication process and support for non MFA activated user. (#11228)

Signed-off-by: Jan Gustafsson <jannegpriv@gmail.com>
* Updated after code review.

Signed-off-by: Jan Gustafsson <jannegpriv@gmail.com>
* Updated after code review.

Signed-off-by: Jan Gustafsson <jannegpriv@gmail.com>
* Updated after code review.

Signed-off-by: Jan Gustafsson <jannegpriv@gmail.com>
bundles/org.openhab.binding.verisure/README.md
bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBindingConstants.java
bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBridgeConfiguration.java
bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureSession.java
bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/discovery/VerisureThingDiscoveryService.java
bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBridgeHandler.java
bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureClimateDeviceThingHandler.java
bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureDoorWindowThingHandler.java

index 9f9e83fea571edfd17df1dc21e42fbc1ea3606cb..8f5a0d7ed7090abceb9bdee53a393c196b0c1ee9 100644 (file)
@@ -1,12 +1,9 @@
 # Verisure Binding
 
-This is an openHAB binding for Verisure Alarm System, by Securitas Direct.
+This is an openHAB binding for Verisure Smart Alarms by Verisure Securitas.
 
-This binding uses the rest API behind the Verisure My Pages:
+This binding uses a rest API used by the [Verisure My Pages webpage](https://mypages.verisure.com/login.html)
 
-https://mypages.verisure.com/login.html.
-
-Be aware that Verisure don't approve if you update to often, I have gotten no complaints running with a 10 minutes update interval, but officially you should use 30 minutes.
 
 
 ## Supported Things
@@ -19,7 +16,7 @@ This binding supports the following thing types:
 - Water Detector (climate)
 - Siren (climate)
 - Night Control
-- Yaleman SmartLock
+- Yaleman Doorman SmartLock
 - SmartPlug
 - Door/Window Status
 - User Presence Status
@@ -31,11 +28,14 @@ This binding supports the following thing types:
 
 ## Binding Configuration
 
-You will have to configure the bridge with username and password, these must be the same credentials as used when logging into https://mypages.verisure.com.
+You will have to configure the bridge with username and password of a pre-defined user on [Verisure page](https://mypages.verisure.com) that has not activated Multi Factor Authentication (MFA/2FA). 
+
+Verisure allows you to have more than one user so the suggestion is to use a specific user for automation that has MFA/2FA deactivated.
+**NOTE:** To be able to have full control over all SmartLock/alarm functionality, the user also needs to have Administrator rights.
+
+You must also configure pin-code(s) to be able to lock/unlock the SmartLock(s) and arm/unarm the Alarm(s).
 
-You must also configure your pin-code(s) to be able to lock/unlock the SmartLock(s) and arm/unarm the Alarm(s).
 
-**NOTE:** To be able to have full control over all SmartLock functionality, the user has to have Administrator rights.
 
 ## Discovery
 
@@ -325,7 +325,8 @@ The following channels are supported:
 #### Configuration Options
 
 *   `deviceId` - Device Id
-        *  Since Event Log lacks a Verisure ID, the following naming convention is used for Event Log on site id 123456789: 'el123456789'. Installation ID can be found using DEBUG log settings.   
+     *  Since Event Log lacks a Verisure ID, the following naming convention is used for Event Log on site id 123456789: 'el123456789'. Installation ID can be found using DEBUG log settings.
+             
 
 #### Channels
 
index c7722d722c89bcb47da004bb7052cfceacf81da5..97d0da191345a6a98d292848a9cebbe1563e5047 100644 (file)
@@ -131,22 +131,23 @@ public class VerisureBindingConstants {
     // REST URI constants
     public static final String USERNAME = "username";
     public static final String PASSWORD = "password";
-    public static final String BASEURL = "https://mypages.verisure.com";
-    public static final String LOGON_SUF = BASEURL + "/j_spring_security_check?locale=en_GB";
-    public static final String ALARM_COMMAND = BASEURL + "/remotecontrol/armstatechange.cmd";
-    public static final String SMARTLOCK_LOCK_COMMAND = BASEURL + "/remotecontrol/lockunlock.cmd";
-    public static final String SMARTLOCK_SET_COMMAND = BASEURL + "/overview/setdoorlock.cmd";
-    public static final String SMARTLOCK_AUTORELOCK_COMMAND = BASEURL + "/settings/setautorelock.cmd";
-    public static final String SMARTLOCK_VOLUME_COMMAND = BASEURL + "/settings/setvolume.cmd";
+    public static final String BASE_URL = "https://mypages.verisure.com";
+    public static final String LOGON_SUF = BASE_URL + "/j_spring_security_check?locale=en_GB";
+    public static final String ALARM_COMMAND = BASE_URL + "/remotecontrol/armstatechange.cmd";
+    public static final String SMARTLOCK_LOCK_COMMAND = BASE_URL + "/remotecontrol/lockunlock.cmd";
+    public static final String SMARTLOCK_SET_COMMAND = BASE_URL + "/overview/setdoorlock.cmd";
+    public static final String SMARTLOCK_AUTORELOCK_COMMAND = BASE_URL + "/settings/setautorelock.cmd";
+    public static final String SMARTLOCK_VOLUME_COMMAND = BASE_URL + "/settings/setvolume.cmd";
 
-    public static final String SMARTPLUG_COMMAND = BASEURL + "/settings/smartplug/onoffplug.cmd";
+    public static final String SMARTPLUG_COMMAND = BASE_URL + "/settings/smartplug/onoffplug.cmd";
     public static final String START_REDIRECT = "/uk/start.html";
-    public static final String START_SUF = BASEURL + START_REDIRECT;
+    public static final String START_SUF = BASE_URL + START_REDIRECT;
 
     // GraphQL constants
-    public static final String STATUS = BASEURL + "/uk/status";
-    public static final String SETTINGS = BASEURL + "/uk/settings.html?giid=";
-    public static final String SET_INSTALLATION = BASEURL + "/setinstallation?giid=";
+    public static final String STATUS = BASE_URL + "/uk/status";
+    public static final String EXTEND = BASE_URL + "/session/extend";
+    public static final String SETTINGS = BASE_URL + "/uk/settings.html?giid=";
+    public static final String SET_INSTALLATION = BASE_URL + "/setinstallation?giid=";
     public static final String BASEURL_API = "https://m-api02.verisure.com";
     public static final String START_GRAPHQL = "/graphql";
     public static final String AUTH_TOKEN = "/auth/token";
index 012cdb8e9c25b4dbb977f957719af4c91701f12e..b5af4bd3d286dd8d1969104e4918d97fa75fa6f1 100644 (file)
@@ -23,8 +23,8 @@ import org.eclipse.jdt.annotation.Nullable;
  */
 @NonNullByDefault
 public class VerisureBridgeConfiguration {
-    public @Nullable String username;
-    public @Nullable String password;
-    public int refresh;
+    public String username = "";
+    public String password = "";
+    public int refresh = 600;
     public @Nullable String pin;
 }
index bc87dcc2323898dfc80eb6e37e0491bcfa97cb37..da5542307c980de1dc9b4b8b99893e203a4d3da9 100644 (file)
@@ -17,9 +17,12 @@ import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*;
 import java.math.BigDecimal;
 import java.net.CookieStore;
 import java.net.HttpCookie;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -86,24 +89,30 @@ public class VerisureSession {
     private int apiServerInUseIndex = 0;
     private int numberOfEvents = 15;
     private static final String USER_NAME = "username";
-    private static final String PASSWORD_NAME = "vid";
+    private static final String VID = "vid";
+    private static final String VS_STEPUP = "vs-stepup";
+    private static final String VS_ACCESS = "vs-access";
     private String apiServerInUse = APISERVERLIST.get(apiServerInUseIndex);
     private String authstring = "";
     private @Nullable String csrf;
     private @Nullable String pinCode;
     private HttpClient httpClient;
-    private @Nullable String userName = "";
-    private @Nullable String password = "";
+    private String userName = "";
+    private String password = "";
+    private String vid = "";
+    private String vsAccess = "";
+    private String vsStepup = "";
 
     public VerisureSession(HttpClient httpClient) {
         this.httpClient = httpClient;
     }
 
-    public boolean initialize(@Nullable String authstring, @Nullable String pinCode, @Nullable String userName) {
+    public boolean initialize(@Nullable String authstring, @Nullable String pinCode, String userName, String password) {
         if (authstring != null) {
             this.authstring = authstring.substring(0);
             this.pinCode = pinCode;
             this.userName = userName;
+            this.password = password;
             // Try to login to Verisure
             if (logIn()) {
                 return getInstallations();
@@ -119,12 +128,9 @@ public class VerisureSession {
             if (logIn()) {
                 if (updateStatus()) {
                     return true;
-                } else {
-                    return false;
                 }
-            } else {
-                return false;
             }
+            return false;
         } catch (HttpResponseException e) {
             logger.warn("Failed to do a refresh {}", e.getMessage());
             return false;
@@ -258,15 +264,21 @@ public class VerisureSession {
         }
     }
 
-    private void setPasswordFromCookie() {
+    private void analyzeCookies() {
         CookieStore c = httpClient.getCookieStore();
         List<HttpCookie> cookies = c.getCookies();
         final List<HttpCookie> unmodifiableList = List.of(cookies.toArray(new HttpCookie[] {}));
         unmodifiableList.forEach(cookie -> {
             logger.trace("Response Cookie: {}", cookie);
-            if (cookie.getName().equals(PASSWORD_NAME)) {
-                password = cookie.getValue();
-                logger.debug("Fetching vid {} from cookie", password);
+            if (VID.equals(cookie.getName())) {
+                vid = cookie.getValue();
+                logger.debug("Fetching vid {} from cookie", vid);
+            } else if (VS_ACCESS.equals(cookie.getName())) {
+                vsAccess = cookie.getValue();
+                logger.debug("Fetching vs-access {} from cookie", vsAccess);
+            } else if (VS_STEPUP.equals(cookie.getName())) {
+                vsStepup = cookie.getValue();
+                logger.debug("Fetching vs-stepup {} from cookie", vsStepup);
             }
         });
     }
@@ -290,7 +302,6 @@ public class VerisureSession {
         switch (response.getStatus()) {
             case HttpStatus.OK_200:
                 if (content.contains("<link href=\"/newapp")) {
-                    setPasswordFromCookie();
                     return true;
                 } else {
                     logger.debug("We need to login again!");
@@ -313,9 +324,9 @@ public class VerisureSession {
 
     private <T> @Nullable T getJSONVerisureAPI(String url, Class<T> jsonClass)
             throws ExecutionException, InterruptedException, TimeoutException, JsonSyntaxException {
-        logger.debug("HTTP GET: {}", BASEURL + url);
+        logger.debug("HTTP GET: {}", BASE_URL + url);
 
-        ContentResponse response = httpClient.GET(BASEURL + url + "?_=" + System.currentTimeMillis());
+        ContentResponse response = httpClient.GET(BASE_URL + url + "?_=" + System.currentTimeMillis());
         String content = response.getContentAsString();
         logTraceWithPattern(response.getStatus(), content);
 
@@ -325,6 +336,7 @@ public class VerisureSession {
     private ContentResponse postVerisureAPI(String url, String data, boolean isJSON)
             throws ExecutionException, InterruptedException, TimeoutException {
         logger.debug("postVerisureAPI URL: {} Data:{}", url, data);
+
         Request request = httpClient.newRequest(url).method(HttpMethod.POST);
         if (isJSON) {
             request.header("content-type", "application/json");
@@ -334,14 +346,29 @@ public class VerisureSession {
             }
         }
         request.header("Accept", "application/json");
-        if (!data.equals("empty")) {
-            request.content(new BytesContentProvider(data.getBytes(StandardCharsets.UTF_8)),
-                    "application/x-www-form-urlencoded; charset=UTF-8");
+
+        if (url.contains(AUTH_LOGIN)) {
+            request.header("APPLICATION_ID", "OpenHAB Verisure");
+            String basicAuhentication = Base64.getEncoder().encodeToString((userName + ":" + password).getBytes());
+            request.header("authorization", "Basic " + basicAuhentication);
         } else {
-            logger.debug("Setting cookie with username {} and vid {}", userName, password);
+            if (!vid.isEmpty()) {
+                request.cookie(new HttpCookie(VID, vid));
+                logger.debug("Setting cookie with vid {}", vid);
+            }
+            if (!vsAccess.isEmpty()) {
+                request.cookie(new HttpCookie(VS_ACCESS, vsAccess));
+                logger.debug("Setting cookie with vs-access {}", vsAccess);
+            }
+            logger.debug("Setting cookie with username {}", userName);
             request.cookie(new HttpCookie(USER_NAME, userName));
-            request.cookie(new HttpCookie(PASSWORD_NAME, password));
         }
+
+        if (!"empty".equals(data)) {
+            request.content(new BytesContentProvider(data.getBytes(StandardCharsets.UTF_8)),
+                    "application/x-www-form-urlencoded; charset=UTF-8");
+        }
+
         logger.debug("HTTP POST Request {}.", request.toString());
         return request.send();
     }
@@ -400,6 +427,9 @@ public class VerisureSession {
                         logTraceWithPattern(httpStatus, content);
                         return httpStatus;
                     }
+                } else if (httpStatus == HttpStatus.BAD_REQUEST_400) {
+                    setApiServerInUse(getNextApiServer());
+                    url = apiServerInUse + urlString;
                 } else {
                     logger.debug("Failed to send POST, Http status code: {}", response.getStatus());
                 }
@@ -417,7 +447,11 @@ public class VerisureSession {
         logTraceWithPattern(response.getStatus(), response.getContentAsString());
 
         url = AUTH_LOGIN;
-        return postVerisureAPI(url, "empty");
+        int httpStatusCode = postVerisureAPI(url, "empty");
+        analyzeCookies();
+
+        // return response.getStatus();
+        return httpStatusCode;
     }
 
     private boolean getInstallations() {
@@ -488,10 +522,26 @@ public class VerisureSession {
     private synchronized boolean logIn() {
         try {
             if (!areWeLoggedIn()) {
-                logger.debug("Attempting to log in to mypages.verisure.com");
-                String url = LOGON_SUF;
+                vid = "";
+                vsAccess = "";
+                logger.debug("Attempting to log in to {}, remove all cookies to ensure a fresh session", BASE_URL);
+                URI authUri = new URI(BASE_URL);
+                CookieStore store = httpClient.getCookieStore();
+                store.get(authUri).forEach(cookie -> {
+                    store.remove(authUri, cookie);
+                });
+
+                String url = AUTH_LOGIN;
+                int httpStatusCode = postVerisureAPI(url, "empty");
+                analyzeCookies();
+                if (!vsStepup.isEmpty()) {
+                    logger.warn("MFA is activated on this user! Not supported by binding!");
+                    return false;
+                }
+
+                url = LOGON_SUF;
                 logger.debug("Login URL: {}", url);
-                int httpStatusCode = postVerisureAPI(url, authstring);
+                httpStatusCode = postVerisureAPI(url, authstring);
                 if (httpStatusCode != HttpStatus.OK_200) {
                     logger.debug("Failed to login, HTTP status code: {}", httpStatusCode);
                     return false;
@@ -500,7 +550,7 @@ public class VerisureSession {
             } else {
                 return true;
             }
-        } catch (ExecutionException | InterruptedException | TimeoutException e) {
+        } catch (ExecutionException | InterruptedException | TimeoutException | URISyntaxException e) {
             logger.warn("Failed to login {}", e.getMessage());
         }
         return false;
@@ -617,16 +667,17 @@ public class VerisureSession {
                     // Set location
                     slThing.setLocation(doorLock.getDevice().getArea());
                     slThing.setDeviceId(deviceId);
+
                     // Fetch more info from old endpoint
                     try {
                         VerisureSmartLockDTO smartLockThing = getJSONVerisureAPI(SMARTLOCK_PATH + slThing.getDeviceId(),
                                 VerisureSmartLockDTO.class);
                         logger.debug("REST Response ({})", smartLockThing);
                         slThing.setSmartLockJSON(smartLockThing);
-                        notifyListenersIfChanged(slThing, installation, deviceId);
                     } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException e) {
                         logger.warn("Failed to query for smartlock status: {}", e.getMessage());
                     }
+                    notifyListenersIfChanged(slThing, installation, deviceId);
                 }
             });
 
@@ -740,7 +791,7 @@ public class VerisureSession {
                                 cThing.setBatteryStatus(batteryStatus);
                             }
                         } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException e) {
-                            logger.warn("Failed to query for smartlock status: {}", e.getMessage());
+                            logger.debug("Failed to query for battery status: {}", e.getMessage());
                         }
                         // Set location
                         cThing.setLocation(climate.getDevice().getArea());
@@ -789,7 +840,7 @@ public class VerisureSession {
                             dThing.setBatteryStatus(batteryStatus);
                         }
                     } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException e) {
-                        logger.warn("Failed to query for smartlock status: {}", e.getMessage());
+                        logger.warn("Failed to query for door&window status: {}", e.getMessage());
                     }
                     // Set location
                     dThing.setLocation(doorWindow.getDevice().getArea());
@@ -847,7 +898,7 @@ public class VerisureSession {
                     .getUserTrackings();
             userTrackingList.forEach(userTracking -> {
                 String localUserTrackingStatus = userTracking.getStatus();
-                if (localUserTrackingStatus != null && localUserTrackingStatus.equals("ACTIVE")) {
+                if ("ACTIVE".equals(localUserTrackingStatus)) {
                     VerisureUserPresencesDTO upThing = new VerisureUserPresencesDTO();
                     VerisureUserPresencesDTO.Installation inst = new VerisureUserPresencesDTO.Installation();
                     inst.setUserTrackings(Collections.singletonList(userTracking));
index 9651bfd0c55b425b57c9057522b061486eede5ee..0423f47364dc730d4a3caf3876825a6424dbbfd7 100644 (file)
@@ -75,7 +75,8 @@ public class VerisureThingDiscoveryService extends AbstractDiscoveryService
         String deviceId = thing.getDeviceId();
         if (thingUID != null) {
             if (verisureBridgeHandler != null) {
-                String label = "Device Id: " + deviceId;
+                String className = thing.getClass().getSimpleName();
+                String label = "Type: " + className + " Device Id: " + deviceId;
                 if (thing.getLocation() != null) {
                     label += ", Location: " + thing.getLocation();
                 }
@@ -84,7 +85,7 @@ public class VerisureThingDiscoveryService extends AbstractDiscoveryService
                 }
                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
                         .withLabel(label).withProperty(VerisureThingConfiguration.DEVICE_ID_LABEL, deviceId)
-                        .withRepresentationProperty(deviceId).build();
+                        .withRepresentationProperty(VerisureThingConfiguration.DEVICE_ID_LABEL).build();
                 logger.debug("thinguid: {}, bridge {}, label {}", thingUID, bridgeUID, deviceId);
                 thingDiscovered(discoveryResult);
             }
index 5530a131cc78d9708f8ad9aee213daa272b06ed2..bfaa0e664e650606e7b23de7391776205e77e658 100644 (file)
@@ -14,9 +14,6 @@ package org.openhab.binding.verisure.internal.handler;
 
 import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
@@ -65,7 +62,6 @@ public class VerisureBridgeHandler extends BaseBridgeHandler {
 
     private String authstring = "";
     private @Nullable String pinCode;
-    private static int REFRESH_SEC = 600;
     private @Nullable ScheduledFuture<?> refreshJob;
     private @Nullable ScheduledFuture<?> immediateRefreshJob;
     private @Nullable VerisureSession session;
@@ -104,20 +100,19 @@ public class VerisureBridgeHandler extends BaseBridgeHandler {
     public void initialize() {
         logger.debug("Initializing Verisure Binding");
         VerisureBridgeConfiguration config = getConfigAs(VerisureBridgeConfiguration.class);
-        REFRESH_SEC = config.refresh;
+
         this.pinCode = config.pin;
-        if (config.username == null || config.password == null) {
+        if (config.username.isBlank() || config.password.isBlank()) {
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
                     "Configuration of username and password is mandatory");
-        } else if (REFRESH_SEC < 0) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Refresh time cannot negative!");
+
+        } else if (config.refresh < 10) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                    "Refresh time is lower than min value of 10!");
         } else {
             try {
-                authstring = "j_username=" + config.username + "&j_password="
-                        + URLEncoder.encode(config.password, StandardCharsets.UTF_8.toString())
-                        + "&spring-security-redirect=" + START_REDIRECT;
+                authstring = "j_username=" + config.username;
                 scheduler.execute(() -> {
-
                     if (session == null) {
                         logger.debug("Session is null, let's create a new one");
                         session = new VerisureSession(this.httpClient);
@@ -125,18 +120,15 @@ public class VerisureBridgeHandler extends BaseBridgeHandler {
                     VerisureSession session = this.session;
                     updateStatus(ThingStatus.UNKNOWN);
                     if (session != null) {
-                        if (!session.initialize(authstring, pinCode, config.username)) {
-                            logger.warn("Failed to initialize bridge, please check your credentials!");
+                        if (!session.initialize(authstring, pinCode, config.username, config.password)) {
                             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_REGISTERING_ERROR,
-                                    "Failed to login to Verisure, please check your credentials!");
-                            dispose();
-                            initialize();
+                                    "Failed to login to Verisure, please check your account settings! Is MFA activated?");
                             return;
                         }
-                        startAutomaticRefresh();
+                        startAutomaticRefresh(config.refresh);
                     }
                 });
-            } catch (RuntimeException | UnsupportedEncodingException e) {
+            } catch (RuntimeException e) {
                 logger.warn("Failed to initialize: {}", e.getMessage());
                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
             }
@@ -227,12 +219,12 @@ public class VerisureBridgeHandler extends BaseBridgeHandler {
         }
     }
 
-    private void startAutomaticRefresh() {
+    private void startAutomaticRefresh(int refresh) {
         ScheduledFuture<?> refreshJob = this.refreshJob;
         logger.debug("Start automatic refresh {}", refreshJob);
         if (refreshJob == null || refreshJob.isCancelled()) {
             try {
-                this.refreshJob = scheduler.scheduleWithFixedDelay(this::refreshAndUpdateStatus, 0, REFRESH_SEC,
+                this.refreshJob = scheduler.scheduleWithFixedDelay(this::refreshAndUpdateStatus, 0, refresh,
                         TimeUnit.SECONDS);
                 logger.debug("Scheduling at fixed delay refreshjob {}", this.refreshJob);
             } catch (RejectedExecutionException e) {
index 1ac6f65852875841b2914f86f5a039257e7bf26a..a8e5cb04be1a52b5f7b9488b589a0c9669376d9a 100644 (file)
@@ -15,6 +15,7 @@ package org.openhab.binding.verisure.internal.handler;
 import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*;
 
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import javax.measure.quantity.Dimensionless;
@@ -23,6 +24,7 @@ import javax.measure.quantity.Temperature;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.binding.verisure.internal.dto.VerisureBatteryStatusDTO;
 import org.openhab.binding.verisure.internal.dto.VerisureClimatesDTO;
+import org.openhab.binding.verisure.internal.dto.VerisureClimatesDTO.Climate;
 import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.QuantityType;
 import org.openhab.core.library.types.StringType;
@@ -74,27 +76,35 @@ public class VerisureClimateDeviceThingHandler extends VerisureThingHandler<Veri
                     State state = getValue(channelUID.getId(), climateJSON);
                     updateState(channelUID, state);
                 });
-        String timeStamp = climateJSON.getData().getInstallation().getClimates().get(0).getTemperatureTimestamp();
-        if (timeStamp != null) {
-            updateTimeStamp(timeStamp);
+        List<Climate> climateList = climateJSON.getData().getInstallation().getClimates();
+        if (climateList != null && !climateList.isEmpty()) {
+            String timeStamp = climateList.get(0).getTemperatureTimestamp();
+            if (timeStamp != null) {
+                updateTimeStamp(timeStamp);
+            }
         }
+
         updateInstallationChannels(climateJSON);
     }
 
     public State getValue(String channelId, VerisureClimatesDTO climateJSON) {
+        List<Climate> climateList = climateJSON.getData().getInstallation().getClimates();
         switch (channelId) {
             case CHANNEL_TEMPERATURE:
-                double temperature = climateJSON.getData().getInstallation().getClimates().get(0).getTemperatureValue();
-                return new QuantityType<Temperature>(temperature, SIUnits.CELSIUS);
+                if (climateList != null && !climateList.isEmpty()) {
+                    double temperature = climateList.get(0).getTemperatureValue();
+                    return new QuantityType<Temperature>(temperature, SIUnits.CELSIUS);
+                }
             case CHANNEL_HUMIDITY:
-                if (climateJSON.getData().getInstallation().getClimates().get(0).isHumidityEnabled()) {
-                    double humidity = climateJSON.getData().getInstallation().getClimates().get(0).getHumidityValue();
+                if (climateList != null && !climateList.isEmpty() && climateList.get(0).isHumidityEnabled()) {
+                    double humidity = climateList.get(0).getHumidityValue();
                     return new QuantityType<Dimensionless>(humidity, Units.PERCENT);
                 }
             case CHANNEL_HUMIDITY_ENABLED:
-                boolean humidityEnabled = climateJSON.getData().getInstallation().getClimates().get(0)
-                        .isHumidityEnabled();
-                return OnOffType.from(humidityEnabled);
+                if (climateList != null && !climateList.isEmpty()) {
+                    boolean humidityEnabled = climateList.get(0).isHumidityEnabled();
+                    return OnOffType.from(humidityEnabled);
+                }
             case CHANNEL_LOCATION:
                 String location = climateJSON.getLocation();
                 return location != null ? new StringType(location) : UnDefType.NULL;
@@ -102,7 +112,7 @@ public class VerisureClimateDeviceThingHandler extends VerisureThingHandler<Veri
                 VerisureBatteryStatusDTO batteryStatus = climateJSON.getBatteryStatus();
                 if (batteryStatus != null) {
                     String status = batteryStatus.getStatus();
-                    if (status != null && status.equals("CRITICAL")) {
+                    if ("CRITICAL".equals(status)) {
                         return OnOffType.from(true);
                     }
                 }
index d44d2ff70af591bdb368910f5d675bf1885b02b1..94b4fdeb44c4dd1fd705c2c01ac1bd85d68734f7 100644 (file)
@@ -88,7 +88,7 @@ public class VerisureDoorWindowThingHandler extends VerisureThingHandler<Verisur
                 VerisureBatteryStatusDTO batteryStatus = doorWindowJSON.getBatteryStatus();
                 if (batteryStatus != null) {
                     String status = batteryStatus.getStatus();
-                    if (status != null && status.equals("CRITICAL")) {
+                    if ("CRITICAL".equals(status)) {
                         return OnOffType.from(true);
                     }
                 }