diff --git a/bundles/org.openhab.binding.verisure/README.md b/bundles/org.openhab.binding.verisure/README.md index 9f9e83fea571e..8f5a0d7ed7090 100644 --- a/bundles/org.openhab.binding.verisure/README.md +++ b/bundles/org.openhab.binding.verisure/README.md @@ -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 diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBindingConstants.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBindingConstants.java index c7722d722c89b..97d0da191345a 100644 --- a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBindingConstants.java +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBindingConstants.java @@ -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"; diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBridgeConfiguration.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBridgeConfiguration.java index 012cdb8e9c25b..b5af4bd3d286d 100644 --- a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBridgeConfiguration.java +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBridgeConfiguration.java @@ -23,8 +23,8 @@ */ @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; } diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureSession.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureSession.java index bc87dcc232389..da5542307c980 100644 --- a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureSession.java +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureSession.java @@ -17,9 +17,12 @@ 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 boolean refresh() { 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 void configureInstallationInstance(BigDecimal installationId) } } - private void setPasswordFromCookie() { + private void analyzeCookies() { CookieStore c = httpClient.getCookieStore(); List cookies = c.getCookies(); final List 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 @@ private boolean areWeLoggedIn() throws ExecutionException, InterruptedException, switch (response.getStatus()) { case HttpStatus.OK_200: if (content.contains(" @Nullable T getJSONVerisureAPI(String url, Class 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 @@ private boolean areWeLoggedIn() throws ExecutionException, InterruptedException, 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 @@ private ContentResponse postVerisureAPI(String url, String data, boolean isJSON) } } 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 @@ private int postVerisureAPI(String urlString, String data) { 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 @@ private int setSessionCookieAuthLogin() throws ExecutionException, InterruptedEx 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 @@ private boolean getInstallations() { 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 @@ private synchronized boolean logIn() { } 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 @@ private synchronized void updateSmartLockStatus(VerisureInstallation installatio // 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 @@ private synchronized void updateClimateStatus(VerisureInstallation installation) 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 @@ private synchronized void updateDoorWindowStatus(VerisureInstallation installati 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 @@ private synchronized void updateUserPresenceStatus(VerisureInstallation installa .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)); diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/discovery/VerisureThingDiscoveryService.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/discovery/VerisureThingDiscoveryService.java index 9651bfd0c55b4..0423f47364dc7 100644 --- a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/discovery/VerisureThingDiscoveryService.java +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/discovery/VerisureThingDiscoveryService.java @@ -75,7 +75,8 @@ private void onThingAddedInternal(VerisureThingDTO thing) { 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 @@ private void onThingAddedInternal(VerisureThingDTO thing) { } 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); } diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBridgeHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBridgeHandler.java index 5530a131cc78d..bfaa0e664e650 100644 --- a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBridgeHandler.java +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBridgeHandler.java @@ -14,9 +14,6 @@ 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 void handleCommand(ChannelUID channelUID, Command command) { 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 void initialize() { 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 @@ void scheduleImmediateRefresh(int refreshDelay) { } } - 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) { diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureClimateDeviceThingHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureClimateDeviceThingHandler.java index 1ac6f65852875..a8e5cb04be1a5 100644 --- a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureClimateDeviceThingHandler.java +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureClimateDeviceThingHandler.java @@ -15,6 +15,7 @@ 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 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 @@ private void updateClimateDeviceState(VerisureClimatesDTO climateJSON) { State state = getValue(channelUID.getId(), climateJSON); updateState(channelUID, state); }); - String timeStamp = climateJSON.getData().getInstallation().getClimates().get(0).getTemperatureTimestamp(); - if (timeStamp != null) { - updateTimeStamp(timeStamp); + List 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 climateList = climateJSON.getData().getInstallation().getClimates(); switch (channelId) { case CHANNEL_TEMPERATURE: - double temperature = climateJSON.getData().getInstallation().getClimates().get(0).getTemperatureValue(); - return new QuantityType(temperature, SIUnits.CELSIUS); + if (climateList != null && !climateList.isEmpty()) { + double temperature = climateList.get(0).getTemperatureValue(); + return new QuantityType(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(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 State getValue(String channelId, VerisureClimatesDTO climateJSON) { 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); } } diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureDoorWindowThingHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureDoorWindowThingHandler.java index d44d2ff70af59..94b4fdeb44c4d 100644 --- a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureDoorWindowThingHandler.java +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureDoorWindowThingHandler.java @@ -88,7 +88,7 @@ public State getValue(String channelId, DoorWindow doorWindow, VerisureDoorWindo 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); } }