From 3194ac6998a7ede3ac8e89da53033c6f8a43ed01 Mon Sep 17 00:00:00 2001 From: Jan Gustafsson Date: Sun, 12 Sep 2021 19:57:48 +0200 Subject: [PATCH] [verisure] Adapted to new authentication process and support for non MFA activated user. (#11228) Signed-off-by: Jan Gustafsson --- .../org.openhab.binding.verisure/README.md | 19 ++-- .../internal/VerisureBindingConstants.java | 1 + .../internal/VerisureHandlerFactory.java | 2 +- .../verisure/internal/VerisureSession.java | 100 +++++++++++++----- .../VerisureThingDiscoveryService.java | 5 +- .../handler/VerisureBridgeHandler.java | 11 +- .../VerisureClimateDeviceThingHandler.java | 2 +- .../VerisureDoorWindowThingHandler.java | 2 +- 8 files changed, 100 insertions(+), 42 deletions(-) diff --git a/bundles/org.openhab.binding.verisure/README.md b/bundles/org.openhab.binding.verisure/README.md index 9f9e83fea571e..e02a39df8e403 100644 --- a/bundles/org.openhab.binding.verisure/README.md +++ b/bundles/org.openhab.binding.verisure/README.md @@ -1,12 +1,11 @@ # 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 a rest API behind the Verisure My Pages: 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 +18,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 +30,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 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 +327,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..0987c44b4ac8e 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 @@ -145,6 +145,7 @@ public class VerisureBindingConstants { // GraphQL constants public static final String STATUS = BASEURL + "/uk/status"; + public static final String EXTEND = BASEURL + "/session/extend"; public static final String SETTINGS = BASEURL + "/uk/settings.html?giid="; public static final String SET_INSTALLATION = BASEURL + "/setinstallation?giid="; public static final String BASEURL_API = "https://m-api02.verisure.com"; diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureHandlerFactory.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureHandlerFactory.java index d3a101f5f76d0..2d9f04231fc34 100644 --- a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureHandlerFactory.java +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureHandlerFactory.java @@ -69,7 +69,7 @@ public class VerisureHandlerFactory extends BaseThingHandlerFactory { } private final Logger logger = LoggerFactory.getLogger(VerisureHandlerFactory.class); - private final HttpClient httpClient; + private HttpClient httpClient; @Activate public VerisureHandlerFactory(@Reference HttpClientFactory httpClientFactory) { 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..b27428ea5f8f5 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 @@ -20,6 +20,7 @@ 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 +87,31 @@ 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 @Nullable String passWord = ""; + private @Nullable String vid = ""; + private @Nullable String vsAccess = ""; + private @Nullable 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, @Nullable String userName, + @Nullable 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 +127,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 +263,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 (cookie.getName().equals(VID)) { + vid = cookie.getValue(); + logger.debug("Fetching vid {} from cookie", vid); + } else if (cookie.getName().equals(VS_ACCESS)) { + vsAccess = cookie.getValue(); + logger.debug("Fetching vs-access {} from cookie", vsAccess); + } else if (cookie.getName().equals(VS_STEPUP)) { + vsStepup = cookie.getValue(); + logger.debug("Fetching vs-stepup {} from cookie", vsStepup); } }); } @@ -290,7 +301,6 @@ private boolean areWeLoggedIn() throws ExecutionException, InterruptedException, switch (response.getStatus()) { case HttpStatus.OK_200: if (content.contains(" { String localUserTrackingStatus = userTracking.getStatus(); - if (localUserTrackingStatus != null && localUserTrackingStatus.equals("ACTIVE")) { + if (localUserTrackingStatus != null && "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..87a6ffbf9e75e 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 @@ -105,6 +105,7 @@ 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) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, @@ -116,6 +117,7 @@ public void initialize() { 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) { @@ -125,12 +127,11 @@ 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)) { + logger.warn( + "Failed to login to Verisure, please check your account settings! Is MFA activated!"); 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(); 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..c2b267722e2b4 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 @@ -102,7 +102,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 (status != null && "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..ce05f95e70f44 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 (status != null && "CRITICAL".equals(status)) { return OnOffType.from(true); } }