From 43585f32ed4a98551924a5eb3b654031bdbc69ba Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Thu, 25 Jan 2024 14:57:24 +0100
Subject: [PATCH 001/135] update linky binding to use new dataconnect api
Signed-off-by: Laurent ARNAL
---
.../src/main/feature/feature.xml | 2 +
.../linky/internal/LinkyAccountHandler.java | 45 +++
.../linky/internal/LinkyAuthService.java | 202 ++++++++++++
.../linky/internal/LinkyAuthServlet.java | 104 ++++++
.../linky/internal/LinkyBindingConstants.java | 23 +-
.../linky/internal/LinkyConfiguration.java | 9 +-
.../linky/internal/LinkyHandlerFactory.java | 37 ++-
.../linky/internal/api/EnedisHttpApi.java | 301 ++++++++----------
.../linky/internal/dto/AddressInfo.java | 39 +++
.../linky/internal/dto/ContactInfo.java | 27 ++
.../binding/linky/internal/dto/Contracts.java | 46 +++
.../binding/linky/internal/dto/Customer.java | 33 ++
.../internal/dto/CustomerIdResponse.java | 34 ++
.../linky/internal/dto/CustomerReponse.java | 26 ++
.../linky/internal/dto/IdentityDetails.java | 29 ++
.../linky/internal/dto/IdentityInfo.java | 28 ++
.../linky/internal/dto/IntervalReading.java | 33 ++
.../linky/internal/dto/MeterReading.java | 45 +++
.../linky/internal/dto/MeterResponse.java | 30 ++
.../binding/linky/internal/dto/PrmInfo.java | 12 +-
.../linky/internal/dto/ReadingType.java | 38 +++
.../linky/internal/dto/UsagePoint.java | 31 ++
.../linky/internal/dto/UsagePointDetails.java | 38 +++
.../linky/internal/handler/LinkyHandler.java | 32 +-
.../resources/OH-INF/thing/thing-types.xml | 20 +-
.../src/main/resources/templates/index.html | 89 ++++++
26 files changed, 1142 insertions(+), 211 deletions(-)
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerReponse.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityInfo.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePointDetails.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/resources/templates/index.html
diff --git a/bundles/org.openhab.binding.linky/src/main/feature/feature.xml b/bundles/org.openhab.binding.linky/src/main/feature/feature.xml
index 41dad925440..7e83306ff37 100644
--- a/bundles/org.openhab.binding.linky/src/main/feature/feature.xml
+++ b/bundles/org.openhab.binding.linky/src/main/feature/feature.xml
@@ -4,7 +4,9 @@
openhab-runtime-base
+ openhab-core-auth-oauth2client
mvn:org.jsoup/jsoup/1.14.3
mvn:org.openhab.addons.bundles/org.openhab.binding.linky/${project.version}
+
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
new file mode 100644
index 00000000000..1cebc3e7b5e
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
@@ -0,0 +1,45 @@
+/**
+ * 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.linky.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * @author Gaël L'hopital - Initial contribution *
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+@NonNullByDefault
+public interface LinkyAccountHandler {
+
+ /**
+ * @return Returns true if the Spotify Bridge is authorized.
+ */
+ boolean isAuthorized();
+
+ /**
+ * Calls Smartthings Api to obtain refresh and access tokens and persist data with Thing.
+ *
+ * @param redirectUrl The redirect url Smartthings calls back to
+ * @param reqCode The unique code passed by Smartthings to obtain the refresh and access tokens
+ * @return returns the name of the Smartthings user that is authorized
+ */
+ String authorize(String redirectUrl, String reqCode);
+
+ /**
+ * Formats the Url to use to call Smartthings to authorize the application.
+ *
+ * @param redirectUri The uri Smartthings will redirect back to
+ * @return the formatted url that should be used to call Smartthings Web Api with
+ */
+ String formatAuthorizationUrl(String redirectUri);
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
new file mode 100644
index 00000000000..9f6bed55693
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
@@ -0,0 +1,202 @@
+/**
+ * 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.linky.internal;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution *
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+@Component(service = LinkyAuthService.class, configurationPid = "binding.internal.authService")
+@NonNullByDefault
+public class LinkyAuthService {
+
+ private static final String TEMPLATE_PATH = "templates/";
+ private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
+ private static final String ERROR_UKNOWN_BRIDGE = "Returned 'state' by doesn't match any Bridges. Has the bridge been removed?";
+
+ private final Logger logger = LoggerFactory.getLogger(LinkyAuthService.class);
+
+ // private final List handlers = new ArrayList<>();
+
+ private @NonNullByDefault({}) HttpService httpService;
+ private @NonNullByDefault({}) BundleContext bundleContext;
+ private @Nullable LinkyAccountHandler accountHandler;
+
+ @Activate
+ protected void activate(ComponentContext componentContext, Map properties) {
+ try {
+ bundleContext = componentContext.getBundleContext();
+ httpService.registerServlet(LinkyBindingConstants.LINKY_ALIAS, createServlet(), new Hashtable<>(),
+ httpService.createDefaultHttpContext());
+ httpService.registerResources(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS,
+ "web", null);
+ } catch (NamespaceException | ServletException | IOException e) {
+ logger.warn("Error during spotify servlet startup", e);
+ }
+ }
+
+ @Deactivate
+ protected void deactivate(ComponentContext componentContext) {
+ httpService.unregister(LinkyBindingConstants.LINKY_ALIAS);
+ httpService.unregister(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS);
+ }
+
+ /**
+ * Creates a new {@link SpotifyAuthServlet}.
+ *
+ * @return the newly created servlet
+ * @throws IOException thrown when an HTML template could not be read
+ */
+ private HttpServlet createServlet() throws IOException {
+ return new LinkyAuthServlet(this, readTemplate(TEMPLATE_INDEX));
+ }
+
+ /**
+ * Reads a template from file and returns the content as String.
+ *
+ * @param templateName name of the template file to read
+ * @return The content of the template file
+ * @throws IOException thrown when an HTML template could not be read
+ */
+ private String readTemplate(String templateName) throws IOException {
+ final URL index = bundleContext.getBundle().getEntry(templateName);
+
+ if (index == null) {
+ throw new FileNotFoundException(
+ String.format("Cannot find '{}' - failed to initialize Linky servlet", templateName));
+ } else {
+ try (InputStream inputStream = index.openStream()) {
+ return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
+ }
+ }
+ }
+
+ /**
+ * Call with Spotify redirect uri returned State and Code values to get the refresh and access tokens and persist
+ * these values
+ *
+ * @param servletBaseURL the servlet base, which will be the Spotify redirect url
+ * @param state The Spotify returned state value
+ * @param code The Spotify returned code value
+ * @return returns the name of the Spotify user that is authorized
+ */
+ public String authorize(String servletBaseURL, String state, String code) {
+ LinkyAccountHandler accountHandler = getLinkyAccountHandler();
+ if (accountHandler == null) {
+ logger.debug(
+ "Linky redirected with state '{}' but no matching bridge was found. Possible bridge has been removed.",
+ state);
+ throw new RuntimeException(ERROR_UKNOWN_BRIDGE);
+ } else {
+ return accountHandler.authorize(servletBaseURL, code);
+ }
+ }
+
+ /**
+ * @param listener Adds the given handler
+ */
+ public void setLinkyAccountHandler(LinkyAccountHandler accountHandler) {
+ this.accountHandler = accountHandler;
+ }
+
+ /**
+ * @param handler Removes the given handler
+ */
+ public void unsetLinkyAccountHandler(LinkyAccountHandler accountHandler) {
+ this.accountHandler = null;
+ }
+
+ /**
+ * @param listener Adds the given handler
+ */
+ public @Nullable LinkyAccountHandler getLinkyAccountHandler() {
+ return this.accountHandler;
+ }
+
+ /**
+ * @param listener Adds the given handler
+ */
+ /*
+ * public void addSpotifyAccountHandler(SpotifyAccountHandler listener) {
+ * if (!handlers.contains(listener)) {
+ * handlers.add(listener);
+ * }
+ * }
+ */
+
+ /**
+ * @param handler Removes the given handler
+ */
+ /*
+ * public void removeSpotifyAccountHandler(SpotifyAccountHandler handler) {
+ * handlers.remove(handler);
+ * }
+ */
+
+ /**
+ * @return Returns all {@link SpotifyAccountHandler}s.
+ */
+ /*
+ * public List getSpotifyAccountHandlers() {
+ * return handlers;
+ * }
+ */
+
+ /**
+ * Get the {@link SpotifyAccountHandler} that matches the given thing UID.
+ *
+ * @param thingUID UID of the thing to match the handler with
+ * @return the {@link SpotifyAccountHandler} matching the thing UID or null
+ */
+ /*
+ * private @Nullable SpotifyAccountHandler getSpotifyAuthListener(String thingUID) {
+ * final Optional maybeListener = handlers.stream().filter(l -> l.equalsThingUID(thingUID))
+ * .findFirst();
+ * return maybeListener.isPresent() ? maybeListener.get() : null;
+ * }
+ */
+
+ @Reference
+ protected void setHttpService(HttpService httpService) {
+ this.httpService = httpService;
+ }
+
+ protected void unsetHttpService(HttpService httpService) {
+ this.httpService = null;
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
new file mode 100644
index 00000000000..f659de06d36
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -0,0 +1,104 @@
+/**
+ * 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.linky.internal;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The xxx manages the authorization with the Linky Web API. The servlet implements the
+ * Authorization Code flow and saves the resulting refreshToken with the bridge.
+ *
+ * @author Gaël L'hopital - Initial contribution *
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+@NonNullByDefault
+public class LinkyAuthServlet extends HttpServlet {
+
+ private static final long serialVersionUID = -4719613645562518231L;
+
+ private static final Pattern MESSAGE_KEY_PATTERN = Pattern.compile("\\$\\{([^\\}]+)\\}");
+
+ // Keys present in the index.html
+ private static final String KEY_BRIDGE_URI = "bridge.uri";
+
+ private final Logger logger = LoggerFactory.getLogger(LinkyAuthServlet.class);
+ private final LinkyAuthService linkyAuthService;
+ private final String indexTemplate;
+
+ public LinkyAuthServlet(LinkyAuthService linkyAuthService, String indexTemplate) {
+ this.linkyAuthService = linkyAuthService;
+ this.indexTemplate = indexTemplate;
+ }
+
+ @Override
+ protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp)
+ throws ServletException, IOException {
+ logger.debug("Linky auth callback servlet received GET request {}.", req.getRequestURI());
+ final Map replaceMap = new HashMap<>();
+ /*
+ *
+ * final String servletBaseURL = req.getRequestURL().toString();
+ * final String queryString = req.getQueryString();
+ *
+ *
+ * String servletBaseURLSecure = servletBaseURL.replace("http://", "https://").replace("8080", "8443");
+ * handleSmartthingsRedirect(replaceMap, servletBaseURLSecure, queryString);
+ * resp.setContentType(CONTENT_TYPE);
+ * LinkyAccountHandler accountHandler = linkyAuthService.getLinkyAccountHandler();
+ */
+ String uri = "https://mon-compte-particulier.enedis.fr/dataconnect/v1/oauth2/authorize?client_id=e551937c-5250-48bc-b4a6-2323af68db92&duration=P36M&response_type=code";
+
+ // replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
+ replaceMap.put(KEY_BRIDGE_URI, uri);
+ resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
+ resp.getWriter().close();
+ }
+
+ /**
+ * Replaces all keys from the map found in the template with values from the map. If the key is not found the key
+ * will be kept in the template.
+ *
+ * @param template template to replace keys with values
+ * @param map map with key value pairs to replace in the template
+ * @return a template with keys replaced
+ */
+ private String replaceKeysFromMap(String template, Map map) {
+ final Matcher m = MESSAGE_KEY_PATTERN.matcher(template);
+ final StringBuffer sb = new StringBuffer();
+
+ while (m.find()) {
+ try {
+ final String key = m.group(1);
+ m.appendReplacement(sb, Matcher.quoteReplacement(map.getOrDefault(key, "${" + key + '}')));
+ } catch (RuntimeException e) {
+ logger.debug("Error occurred during template filling, cause ", e);
+ }
+ }
+ m.appendTail(sb);
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 9ceacb91fb1..119926c61e3 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -12,6 +12,9 @@
*/
package org.openhab.binding.linky.internal;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
@@ -20,6 +23,7 @@ import org.openhab.core.thing.ThingTypeUID;
* used across the whole binding.
*
* @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API *
*/
@NonNullByDefault
public class LinkyBindingConstants {
@@ -32,7 +36,7 @@ public class LinkyBindingConstants {
// Thing properties
public static final String PUISSANCE = "puissance";
public static final String PRM_ID = "prmId";
- public static final String USER_ID = "av2_interne_id";
+ public static final String USER_ID = "customerId";
// List of all Channel id's
public static final String YESTERDAY = "daily#yesterday";
@@ -44,4 +48,21 @@ public class LinkyBindingConstants {
public static final String LAST_MONTH = "monthly#lastMonth";
public static final String THIS_YEAR = "yearly#thisYear";
public static final String LAST_YEAR = "yearly#lastYear";
+
+ // Authorization related Servlet and resources aliases.
+ public static final String LINKY_ALIAS = "/connectlinky";
+ public static final String LINKY_IMG_ALIAS = "/img";
+
+ /**
+ * Smartthings scopes needed by this binding to work.
+ */
+ public static final String LINKY_SCOPES = Stream.of("r:devices:*", "w:devices:*", "x:devices:*", "r:hubs:*",
+ "r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*",
+ "r:installedapps", "w:installedapps").collect(Collectors.joining(" "));
+
+ // List of Spotify services related urls, information
+ public static final String LINKY_ACCOUNT_URL = "https://api.smartthings.com/oauth";
+ public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "/authorize";
+ public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "/token";
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
index 3c75363fd83..6bfa0f6403b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
@@ -19,15 +19,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
* thing configuration.
*
* @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
@NonNullByDefault
public class LinkyConfiguration {
- public static final String INTERNAL_AUTH_ID = "internalAuthId";
- public String username = "";
- public String password = "";
- public String internalAuthId = "";
+ public String token = "";
+ public String prmId = "";
public boolean seemsValid() {
- return !username.isBlank() && !password.isBlank() && !internalAuthId.isBlank();
+ return !token.isBlank() && !prmId.isBlank();
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 315ed268c12..91882a07146 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -12,8 +12,6 @@
*/
package org.openhab.binding.linky.internal;
-import static org.openhab.binding.linky.internal.LinkyBindingConstants.THING_TYPE_LINKY;
-
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
@@ -27,6 +25,7 @@ import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.openhab.binding.linky.internal.handler.LinkyHandler;
+import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.io.net.http.TrustAllTrustManager;
@@ -50,10 +49,11 @@ import com.google.gson.JsonDeserializer;
* The {@link LinkyHandlerFactory} is responsible for creating things handlers.
*
* @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky")
-public class LinkyHandlerFactory extends BaseThingHandlerFactory {
+public class LinkyHandlerFactory extends BaseThingHandlerFactory implements LinkyAccountHandler {
private static final DateTimeFormatter LINKY_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
private static final int REQUEST_BUFFER_SIZE = 8000;
private static final int RESPONSE_BUFFER_SIZE = 200000;
@@ -65,10 +65,13 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
.create();
private final LocaleProvider localeProvider;
private final HttpClient httpClient;
+ private final OAuthFactory oAuthFactory;
+ private final LinkyAuthService authService;
@Activate
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
- final @Reference HttpClientFactory httpClientFactory) {
+ final @Reference HttpClientFactory httpClientFactory, final @Reference LinkyAuthService authService,
+ final @Reference OAuthFactory oAuthFactory) {
this.localeProvider = localeProvider;
SslContextFactory sslContextFactory = new SslContextFactory.Client();
try {
@@ -85,6 +88,8 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
httpClient.setFollowRedirects(false);
httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
httpClient.setResponseBufferSize(RESPONSE_BUFFER_SIZE);
+ this.oAuthFactory = oAuthFactory;
+ this.authService = authService;
}
@Override
@@ -109,12 +114,32 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
- return THING_TYPE_LINKY.equals(thingTypeUID);
+ return LinkyBindingConstants.THING_TYPE_LINKY.equals(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
- return supportsThingType(thing.getThingTypeUID()) ? new LinkyHandler(thing, localeProvider, gson, httpClient)
+
+ authService.setLinkyAccountHandler(this);
+
+ return supportsThingType(thing.getThingTypeUID())
+ ? new LinkyHandler(thing, localeProvider, gson, httpClient, oAuthFactory)
: null;
}
+
+ @Override
+ public boolean isAuthorized() {
+ return true;
+ }
+
+ @Override
+ public String authorize(String redirectUrl, String reqCode) {
+ return "";
+ }
+
+ @Override
+ public String formatAuthorizationUrl(String redirectUri) {
+ return "";
+ }
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 2c2a755d3ba..fbe2404d647 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -12,8 +12,6 @@
*/
package org.openhab.binding.linky.internal.api;
-import java.net.HttpCookie;
-import java.net.URI;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
@@ -22,8 +20,6 @@ import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import javax.ws.rs.core.MediaType;
@@ -42,13 +38,17 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
-import org.openhab.binding.linky.internal.dto.AuthData;
-import org.openhab.binding.linky.internal.dto.AuthResult;
+import org.openhab.binding.linky.internal.dto.AddressInfo;
import org.openhab.binding.linky.internal.dto.ConsumptionReport;
import org.openhab.binding.linky.internal.dto.ConsumptionReport.Consumption;
-import org.openhab.binding.linky.internal.dto.PrmDetail;
+import org.openhab.binding.linky.internal.dto.ContactInfo;
+import org.openhab.binding.linky.internal.dto.Customer;
+import org.openhab.binding.linky.internal.dto.CustomerIdResponse;
+import org.openhab.binding.linky.internal.dto.CustomerReponse;
+import org.openhab.binding.linky.internal.dto.IdentityInfo;
+import org.openhab.binding.linky.internal.dto.MeterResponse;
import org.openhab.binding.linky.internal.dto.PrmInfo;
-import org.openhab.binding.linky.internal.dto.UserInfo;
+import org.openhab.binding.linky.internal.dto.UsagePoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -59,23 +59,18 @@ import com.google.gson.JsonSyntaxException;
* {@link EnedisHttpApi} wraps the Enedis Webservice.
*
* @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
@NonNullByDefault
public class EnedisHttpApi {
- private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
- private static final String ENEDIS_DOMAIN = ".enedis.fr";
- private static final String URL_APPS_LINCS = "https://alex.microapplications" + ENEDIS_DOMAIN;
- private static final String URL_MON_COMPTE = "https://mon-compte" + ENEDIS_DOMAIN;
- private static final String URL_COMPTE_PART = URL_MON_COMPTE.replace("compte", "compte-particulier");
- private static final String URL_ENEDIS_AUTHENTICATE = URL_APPS_LINCS + "/authenticate?target=" + URL_COMPTE_PART;
- private static final String USER_INFO_CONTRACT_URL = URL_APPS_LINCS + "/mon-compte-client/api/private/v1/userinfos";
- private static final String USER_INFO_URL = URL_APPS_LINCS + "/userinfos";
- private static final String PRM_INFO_BASE_URL = URL_APPS_LINCS + "/mes-mesures/api/private/v1/personnes/";
- private static final String PRM_INFO_URL = URL_APPS_LINCS + "/mes-prms-part/api/private/v2/personnes/%s/prms";
- private static final String MEASURE_URL = PRM_INFO_BASE_URL
- + "%s/prms/%s/donnees-%s?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
- private static final URI COOKIE_URI = URI.create(URL_COMPTE_PART);
- private static final Pattern REQ_PATTERN = Pattern.compile("ReqID%(.*?)%26");
+ private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+ private static final String BASE_URL = "https://www.myelectricaldata.fr/";
+ private static final String CONTRACT_URL = BASE_URL + "contracts";
+ private static final String IDENTITY_URL = BASE_URL + "identity";
+ private static final String CONTACT_URL = BASE_URL + "contact";
+ private static final String ADDRESS_URL = BASE_URL + "addresses";
+ private static final String MEASURE_URL = BASE_URL + "%s/%s/start/%s/end/%s/cache";
private final Logger logger = LoggerFactory.getLogger(EnedisHttpApi.class);
private final Gson gson;
@@ -91,108 +86,6 @@ public class EnedisHttpApi {
}
public void initialize() throws LinkyException {
- logger.debug("Starting login process for user: {}", config.username);
-
- try {
- addCookie(LinkyConfiguration.INTERNAL_AUTH_ID, config.internalAuthId);
- logger.debug("Step 1: getting authentification");
- String data = getContent(URL_ENEDIS_AUTHENTICATE);
-
- logger.debug("Reception request SAML");
- Document htmlDocument = Jsoup.parse(data);
- Element el = htmlDocument.select("form").first();
- Element samlInput = el.select("input[name=SAMLRequest]").first();
-
- logger.debug("Step 2: send SSO SAMLRequest");
- ContentResponse result = httpClient.POST(el.attr("action"))
- .content(getFormContent("SAMLRequest", samlInput.attr("value"))).send();
- if (result.getStatus() != HttpStatus.FOUND_302) {
- throw new LinkyException("Connection failed step 2");
- }
-
- logger.debug("Get the location and the ReqID");
- Matcher m = REQ_PATTERN.matcher(getLocation(result));
- if (!m.find()) {
- throw new LinkyException("Unable to locate ReqId in header");
- }
-
- String reqId = m.group(1);
- String authenticateUrl = URL_MON_COMPTE
- + "/auth/json/authenticate?realm=/enedis&forward=true&spEntityID=SP-ODW-PROD&goto=/auth/SSOPOST/metaAlias/enedis/providerIDP?ReqID%"
- + reqId + "%26index%3Dnull%26acsURL%3D" + URL_APPS_LINCS
- + "/saml/SSO%26spEntityID%3DSP-ODW-PROD%26binding%3Durn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST&AMAuthCookie=";
-
- logger.debug("Step 3: auth1 - retrieve the template, thanks to cookie internalAuthId user is already set");
- result = httpClient.POST(authenticateUrl).header("X-NoSession", "true").header("X-Password", "anonymous")
- .header("X-Requested-With", "XMLHttpRequest").header("X-Username", "anonymous").send();
- if (result.getStatus() != HttpStatus.OK_200) {
- throw new LinkyException("Connection failed step 3 - auth1: %s", result.getContentAsString());
- }
-
- AuthData authData = gson.fromJson(result.getContentAsString(), AuthData.class);
- if (authData == null || authData.callbacks.size() < 2 || authData.callbacks.get(0).input.isEmpty()
- || authData.callbacks.get(1).input.isEmpty() || !config.username
- .equals(Objects.requireNonNull(authData.callbacks.get(0).input.get(0)).valueAsString())) {
- logger.debug("auth1 - invalid template for auth data: {}", result.getContentAsString());
- throw new LinkyException("Authentication error, the authentication_cookie is probably wrong");
- }
-
- authData.callbacks.get(1).input.get(0).value = config.password;
- logger.debug("Step 4: auth2 - send the auth data");
- result = httpClient.POST(authenticateUrl).header(HttpHeader.CONTENT_TYPE, MediaType.APPLICATION_JSON)
- .header("X-NoSession", "true").header("X-Password", "anonymous")
- .header("X-Requested-With", "XMLHttpRequest").header("X-Username", "anonymous")
- .content(new StringContentProvider(gson.toJson(authData))).send();
- if (result.getStatus() != HttpStatus.OK_200) {
- throw new LinkyException("Connection failed step 3 - auth2: %s", result.getContentAsString());
- }
-
- AuthResult authResult = gson.fromJson(result.getContentAsString(), AuthResult.class);
- if (authResult == null) {
- throw new LinkyException("Invalid authentication result data");
- }
-
- logger.debug("Add the tokenId cookie");
- addCookie("enedisExt", authResult.tokenId);
-
- logger.debug("Step 5: retrieve the SAMLresponse");
- data = getContent(URL_MON_COMPTE + "/" + authResult.successUrl);
- htmlDocument = Jsoup.parse(data);
- el = htmlDocument.select("form").first();
- samlInput = el.select("input[name=SAMLResponse]").first();
-
- logger.debug("Step 6: post the SAMLresponse to finish the authentication");
- result = httpClient.POST(el.attr("action")).content(getFormContent("SAMLResponse", samlInput.attr("value")))
- .send();
- if (result.getStatus() != HttpStatus.FOUND_302) {
- throw new LinkyException("Connection failed step 6");
- }
-
- logger.debug("Step 7: retrieve cookieKey");
- result = httpClient.GET(USER_INFO_CONTRACT_URL);
-
- @SuppressWarnings("unchecked")
- HashMap hashRes = gson.fromJson(result.getContentAsString(), HashMap.class);
-
- String cookieKey;
- if (hashRes != null && hashRes.containsKey("cnAlex")) {
- cookieKey = "personne_for_" + hashRes.get("cnAlex");
- } else {
- throw new LinkyException("Connection failed step 7, missing cookieKey");
- }
-
- List lCookie = httpClient.getCookieStore().getCookies();
- Optional cookie = lCookie.stream().filter(it -> it.getName().contains(cookieKey)).findFirst();
-
- String cookieVal = cookie.map(HttpCookie::getValue)
- .orElseThrow(() -> new LinkyException("Connection failed step 7, missing cookieVal"));
-
- addCookie(cookieKey, cookieVal);
-
- connected = true;
- } catch (InterruptedException | TimeoutException | ExecutionException | JsonSyntaxException e) {
- throw new LinkyException(e, "Error opening connection with Enedis webservice");
- }
}
private String getLocation(ContentResponse response) {
@@ -203,14 +96,6 @@ public class EnedisHttpApi {
if (connected) {
logger.debug("Logout process");
connected = false;
- try { // Three times in a row to get disconnected
- String location = getLocation(httpClient.GET(URL_APPS_LINCS + "/logout"));
- location = getLocation(httpClient.GET(location));
- getLocation(httpClient.GET(location));
- httpClient.getCookieStore().removeAll();
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- throw new LinkyException(e, "Error while disconnecting from Enedis webservice");
- }
}
}
@@ -222,27 +107,15 @@ public class EnedisHttpApi {
disconnect();
}
- private void addCookie(String key, String value) {
- HttpCookie cookie = new HttpCookie(key, value);
- cookie.setDomain(ENEDIS_DOMAIN);
- cookie.setPath("/");
- httpClient.getCookieStore().add(COOKIE_URI, cookie);
- }
-
- private FormContentProvider getFormContent(String fieldName, String fieldValue) {
- Fields fields = new Fields();
- fields.put(fieldName, fieldValue);
- return new FormContentProvider(fields);
- }
-
- private String getContent(String url) throws LinkyException {
+ private String getData(String url) throws LinkyException {
try {
- Request request = httpClient.newRequest(url)
- .agent("Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0");
+ Request request = httpClient.newRequest(url);
request = request.method(HttpMethod.GET);
+ request = request.header("Authorization", config.token);
+
ContentResponse result = request.send();
- if (result.getStatus() != HttpStatus.OK_200) {
- throw new LinkyException("Error requesting '%s': %s", url, result.getContentAsString());
+ if (result.getStatus() != 200) {
+ throw new LinkyException("Error requesting '%s' : %s", url, result.getContentAsString());
}
String content = result.getContentAsString();
logger.trace("getContent returned {}", content);
@@ -252,54 +125,134 @@ public class EnedisHttpApi {
}
}
- private T getData(String url, Class clazz) throws LinkyException {
+ public PrmInfo getPrmInfo() throws LinkyException {
+ PrmInfo result = new PrmInfo();
+ Customer customer = getCustomer();
+ UsagePoint usagePoint = customer.usagePoints[0];
+
+ result.contractInfo = usagePoint.contracts;
+ result.usagePointInfo = usagePoint.usagePoint;
+ result.identityInfo = getIdentity();
+ result.addressInfo = getAddress();
+ result.contactInfo = getContact();
+
+ result.prmId = result.usagePointInfo.usagePointId;
+ result.customerId = customer.customerId;
+
+ return result;
+ }
+
+ public Customer getCustomer() throws LinkyException {
if (!connected) {
initialize();
}
- String data = getContent(url);
+ String data = getData(String.format("%s/%s/cache", CONTRACT_URL, config.prmId));
if (data.isEmpty()) {
- throw new LinkyException("Requesting '%s' returned an empty response", url);
+ throw new LinkyException("Requesting '%s' returned an empty response", CONTRACT_URL);
}
try {
- return Objects.requireNonNull(gson.fromJson(data, clazz));
+ CustomerReponse cResponse = gson.fromJson(data, CustomerReponse.class);
+ if (cResponse == null) {
+ throw new LinkyException("Invalid customer data received");
+ }
+ return cResponse.customer;
} catch (JsonSyntaxException e) {
- logger.debug("Invalid JSON response not matching {}: {}", clazz.getName(), data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", url);
+ logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", CONTRACT_URL);
}
}
- public PrmInfo getPrmInfo(String internId) throws LinkyException {
- String url = PRM_INFO_URL.formatted(internId);
- PrmInfo[] prms = getData(url, PrmInfo[].class);
- if (prms.length < 1) {
- throw new LinkyException("Invalid prms data received");
+ public AddressInfo getAddress() throws LinkyException {
+ if (!connected) {
+ initialize();
+ }
+ String data = getData(String.format("%s/%s/cache", ADDRESS_URL, "21454992660003"));
+ if (data.isEmpty()) {
+ throw new LinkyException("Requesting '%s' returned an empty response", ADDRESS_URL);
+ }
+ try {
+ CustomerReponse cResponse = gson.fromJson(data, CustomerReponse.class);
+ if (cResponse == null) {
+ throw new LinkyException("Invalid customer data received");
+ }
+ return cResponse.customer.usagePoints[0].usagePoint.usagePointAddresses;
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", ADDRESS_URL);
}
- return prms[0];
}
- public PrmDetail getPrmDetails(String internId, String prmId) throws LinkyException {
- String url = PRM_INFO_URL.formatted(internId) + "/" + prmId
- + "?embed=SITALI&embed=SITCOM&embed=SITCON&embed=SYNCON";
- return getData(url, PrmDetail.class);
+ public IdentityInfo getIdentity() throws LinkyException {
+ if (!connected) {
+ initialize();
+ }
+ String data = getData(String.format("%s/%s/cache", IDENTITY_URL, "21454992660003"));
+ if (data.isEmpty()) {
+ throw new LinkyException("Requesting '%s' returned an empty response", IDENTITY_URL);
+ }
+ try {
+ CustomerIdResponse iResponse = gson.fromJson(data, CustomerIdResponse.class);
+ if (iResponse == null) {
+ throw new LinkyException("Invalid customer data received");
+ }
+ return iResponse.identity.naturalPerson;
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", IDENTITY_URL);
+ }
}
- public UserInfo getUserInfo() throws LinkyException {
- return getData(USER_INFO_URL, UserInfo.class);
+ public ContactInfo getContact() throws LinkyException {
+ if (!connected) {
+ initialize();
+ }
+ String data = getData(String.format("%s/%s/cache", CONTACT_URL, "21454992660003"));
+ if (data.isEmpty()) {
+ throw new LinkyException("Requesting '%s' returned an empty response", CONTACT_URL);
+ }
+ try {
+ CustomerIdResponse cResponse = gson.fromJson(data, CustomerIdResponse.class);
+ if (cResponse == null) {
+ throw new LinkyException("Invalid customer data received");
+ }
+ return cResponse.contactData;
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", CONTACT_URL);
+ }
}
private Consumption getMeasures(String userId, String prmId, LocalDate from, LocalDate to, String request)
throws LinkyException {
- String url = String.format(MEASURE_URL, userId, prmId, request, from.format(API_DATE_FORMAT),
- to.format(API_DATE_FORMAT));
- ConsumptionReport report = getData(url, ConsumptionReport.class);
- return report.firstLevel.consumptions;
+ String dtStart = from.format(API_DATE_FORMAT);
+ String dtEnd = to.format(API_DATE_FORMAT);
+
+ String url = String.format(MEASURE_URL, request, prmId, dtStart, dtEnd);
+ if (!connected) {
+ initialize();
+ }
+ String data = getData(url);
+ if (data.isEmpty()) {
+ throw new LinkyException("Requesting '%s' returned an empty response", url);
+ }
+ logger.trace("getData returned {}", data);
+ try {
+ MeterResponse meterResponse = gson.fromJson(data, MeterResponse.class);
+ if (meterResponse == null) {
+ throw new LinkyException("No report data received");
+ }
+ return new ConsumptionReport().new Consumption();
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching ConsumptionReport.class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", url);
+ }
}
public Consumption getEnergyData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(userId, prmId, from, to, "energie");
+ return getMeasures(userId, prmId, from, to, "daily_consumption");
}
public Consumption getPowerData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(userId, prmId, from, to, "pmax");
+ return getMeasures(userId, prmId, from, to, "daily_consumption_max_power");
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
new file mode 100644
index 00000000000..bb7170b1649
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class AddressInfo {
+ public String street;
+ public String locality;
+
+ @SerializedName("postalCode")
+ public String postal_code;
+
+ @SerializedName("insee_code")
+ public String inseeCode;
+
+ public String city;
+ public String country;
+
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java
new file mode 100644
index 00000000000..94c40c43253
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class ContactInfo {
+ public String phone;
+ public String email;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
new file mode 100644
index 00000000000..c9bf59b0022
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class Contracts {
+ public String segment;
+
+ @SerializedName("subscribed_power")
+ public String subscribedPower;
+
+ @SerializedName("last_activation_date")
+ public String lastActivationDate;
+
+ @SerializedName("distribution_tariff")
+ public String distributionTariff;
+
+ @SerializedName("offpeak_hours")
+ public String offpeakHours;
+
+ @SerializedName("contract_status")
+ public String contractStatus;
+
+ @SerializedName("last_distribution_tariff_change_date")
+ public String lastDistributionTariffChangeDate;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
new file mode 100644
index 00000000000..de2caae124a
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class Customer {
+ @SerializedName("customer_id")
+ public String customerId;
+
+ @SerializedName("usage_points")
+ public UsagePoint[] usagePoints;
+
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java
new file mode 100644
index 00000000000..f0e46550263
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class CustomerIdResponse {
+ @SerializedName("customer_id")
+ public String customerId;
+
+ public IdentityDetails identity;
+
+ @SerializedName("contact_data")
+ public ContactInfo contactData;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerReponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerReponse.java
new file mode 100644
index 00000000000..1215134483b
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerReponse.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class CustomerReponse {
+ public Customer customer;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java
new file mode 100644
index 00000000000..15a07b6a275
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class IdentityDetails {
+ @SerializedName("natural_person")
+ public IdentityInfo naturalPerson;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityInfo.java
new file mode 100644
index 00000000000..8923636061c
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityInfo.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class IdentityInfo {
+ public String title;
+ public String firstname;
+ public String lastname;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
new file mode 100644
index 00000000000..23bf0d4018c
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class IntervalReading {
+ @SerializedName("value")
+ public String value;
+
+ @SerializedName("date")
+ public String date;
+
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
new file mode 100644
index 00000000000..8d32d1b722d
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class MeterReading {
+ @SerializedName("usage_point_id")
+ public String usagePointId;
+
+ @SerializedName("start")
+ public String startDate;
+
+ @SerializedName("end")
+ public String endDate;
+
+ @SerializedName("quality")
+ public String quality;
+
+ @SerializedName("reading_type")
+ public ReadingType readingType;
+
+ @SerializedName("interval_reading")
+ public IntervalReading[] intervalReading;
+
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
new file mode 100644
index 00000000000..ff307fd9fee
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class MeterResponse {
+ @SerializedName("meter_reading")
+ public MeterReading meterReading;
+
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java
index b03f577de79..9318ac3f322 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java
@@ -12,12 +12,22 @@
*/
package org.openhab.binding.linky.internal.dto;
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
/**
* The {@link UserInfo} holds ids of existing Prms
*
* @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
public class PrmInfo {
- public String idPrm;
+ public String prmId;
+ public String customerId;
+
+ public Contracts contractInfo;
+ public UsagePointDetails usagePointInfo;
+ public ContactInfo contactInfo;
+ public AddressInfo addressInfo;
+ public IdentityInfo identityInfo;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java
new file mode 100644
index 00000000000..454f04fe295
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class ReadingType {
+ @SerializedName("measurement_kind")
+ public String measurementKind;
+
+ @SerializedName("measuring_period")
+ public String measuringPeriod;
+
+ @SerializedName("unit")
+ public String unit;
+
+ @SerializedName("aggregate")
+ public String aggregate;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
new file mode 100644
index 00000000000..aa8d6bbf359
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class UsagePoint {
+ @SerializedName("usage_point")
+ public UsagePointDetails usagePoint;
+
+ public Contracts contracts;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePointDetails.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePointDetails.java
new file mode 100644
index 00000000000..2b2eeb81111
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePointDetails.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ */
+
+public class UsagePointDetails {
+ @SerializedName("usage_point_id")
+ public String usagePointId;
+
+ @SerializedName("usage_point_status")
+ public String usagePointStatus;
+
+ @SerializedName("meter_type")
+ public String meterType;
+
+ @SerializedName("usage_point_addresses")
+ public AddressInfo usagePointAddresses;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index d38809b29a9..f83636f0de6 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -29,6 +29,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
+import org.openhab.binding.linky.internal.LinkyBindingConstants;
import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.api.EnedisHttpApi;
@@ -37,7 +38,8 @@ import org.openhab.binding.linky.internal.dto.ConsumptionReport.Aggregate;
import org.openhab.binding.linky.internal.dto.ConsumptionReport.Consumption;
import org.openhab.binding.linky.internal.dto.PrmDetail;
import org.openhab.binding.linky.internal.dto.PrmInfo;
-import org.openhab.binding.linky.internal.dto.UserInfo;
+import org.openhab.core.auth.client.oauth2.OAuthClientService;
+import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.QuantityType;
@@ -61,6 +63,7 @@ import com.google.gson.Gson;
* sent to one of the channels.
*
* @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
@NonNullByDefault
@@ -81,6 +84,8 @@ public class LinkyHandler extends BaseThingHandler {
private @Nullable ScheduledFuture> refreshJob;
private @Nullable EnedisHttpApi enedisApi;
+ private final OAuthFactory oAuthFactory;
+
private @NonNullByDefault({}) String prmId;
private @NonNullByDefault({}) String userId;
@@ -90,7 +95,10 @@ public class LinkyHandler extends BaseThingHandler {
ALL
}
- public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient) {
+ private @Nullable OAuthClientService oAuthService;
+
+ public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient,
+ OAuthFactory oAuthFactory) {
super(thing);
this.gson = gson;
this.httpClient = httpClient;
@@ -107,6 +115,8 @@ public class LinkyHandler extends BaseThingHandler {
return consumption;
});
+ this.oAuthFactory = oAuthFactory;
+
this.cachedPowerData = new ExpiringDayCache<>("power cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
// We request data for yesterday and the day before yesterday, even if the data for the day before yesterday
// is not needed by the binding. This is only a workaround to an API bug that will return
@@ -152,6 +162,11 @@ public class LinkyHandler extends BaseThingHandler {
LinkyConfiguration config = getConfigAs(LinkyConfiguration.class);
if (config.seemsValid()) {
+
+ OAuthClientService oAuthService = oAuthFactory.createOAuthClientService(thing.getUID().getAsString(),
+ LinkyBindingConstants.LINKY_API_TOKEN_URL, LinkyBindingConstants.LINKY_AUTHORIZE_URL, "clientId",
+ "clientSecret", LinkyBindingConstants.LINKY_SCOPES, true);
+
enedisApi = new EnedisHttpApi(config, gson, httpClient);
scheduler.submit(() -> {
try {
@@ -159,15 +174,9 @@ public class LinkyHandler extends BaseThingHandler {
api.initialize();
updateStatus(ThingStatus.ONLINE);
- if (thing.getProperties().isEmpty()) {
- UserInfo userInfo = api.getUserInfo();
- PrmInfo prmInfo = api.getPrmInfo(userInfo.userProperties.internId);
- PrmDetail details = api.getPrmDetails(userInfo.userProperties.internId, prmInfo.idPrm);
- updateProperties(Map.of(USER_ID, userInfo.userProperties.internId, PUISSANCE,
- details.situationContractuelleDtos[0].structureTarifaire().puissanceSouscrite().valeur()
- + " kVA",
- PRM_ID, prmInfo.idPrm));
- }
+ PrmInfo prmInfo = api.getPrmInfo();
+ updateProperties(Map.of(USER_ID, prmInfo.customerId, PUISSANCE,
+ prmInfo.contractInfo.subscribedPower, PRM_ID, prmInfo.prmId));
prmId = thing.getProperties().get(PRM_ID);
userId = thing.getProperties().get(USER_ID);
@@ -544,4 +553,5 @@ public class LinkyHandler extends BaseThingHandler {
aggregate.datas.get(index));
}
}
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index fbcdbb509e3..778b374ed24 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -20,19 +20,13 @@
-
- Username
- email
- Your Enedis Username
-
-
- Password
- password
- Your Enedis Password
-
-
- Auth ID
- Authentication ID delivered after the captcha (see documentation).
+
+ PrmId
+ Your PrmId
+
+
+ Token
+ Your Enedis token
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html b/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html
new file mode 100644
index 00000000000..b5e0ce91ca5
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html
@@ -0,0 +1,89 @@
+
+
+
+
+
+${pageRefresh}
+Authorize openHAB binding for Smartthings
+
+
+
+
+
+
+
+
+
+
+ SmartThings icon
+
+
+
+
+
+
+
+
+ Authorize openHAB binding for Smartthings
+ On this page you can authorize the openHAB Smartthings biding to access your Smartthings account.
+
+ The redirect URI to use with Smartthings for this openHAB installation is
+ ${redirectUri}
+
+ ${error} ${authorizedUser}
+
+
+ Connect to SmartThings:
+
+
+
+
+
+
+
\ No newline at end of file
From 91215a729427f53c0e3ea7b9ca041ae08fe52f46 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 26 Jan 2024 12:00:04 +0100
Subject: [PATCH 002/135] implements daily meter readings, 1st phase
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyHandlerFactory.java | 13 +-
.../linky/internal/api/EnedisHttpApi.java | 18 +-
.../console/LinkyCommandExtension.java | 10 +-
.../linky/internal/dto/IntervalReading.java | 6 +-
.../linky/internal/dto/MeterReading.java | 4 +
.../linky/internal/handler/LinkyHandler.java | 579 ++++++++++--------
6 files changed, 363 insertions(+), 267 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 91882a07146..fcc2f06a48d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -14,6 +14,7 @@ package org.openhab.binding.linky.internal;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
+import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@@ -55,14 +56,20 @@ import com.google.gson.JsonDeserializer;
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky")
public class LinkyHandlerFactory extends BaseThingHandlerFactory implements LinkyAccountHandler {
private static final DateTimeFormatter LINKY_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
+ private static final DateTimeFormatter LINKY_FORMATTER2 = DateTimeFormatter.ofPattern("uuuu-MM-dd");
private static final int REQUEST_BUFFER_SIZE = 8000;
private static final int RESPONSE_BUFFER_SIZE = 200000;
private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
- private final Gson gson = new GsonBuilder().registerTypeAdapter(ZonedDateTime.class,
- (JsonDeserializer) (json, type, jsonDeserializationContext) -> ZonedDateTime
- .parse(json.getAsJsonPrimitive().getAsString(), LINKY_FORMATTER))
+ private final Gson gson = new GsonBuilder()
+ .registerTypeAdapter(ZonedDateTime.class,
+ (JsonDeserializer) (json, type, jsonDeserializationContext) -> ZonedDateTime
+ .parse(json.getAsJsonPrimitive().getAsString(), LINKY_FORMATTER))
+ .registerTypeAdapter(LocalDate.class,
+ (JsonDeserializer) (json, type, jsonDeserializationContext) -> LocalDate
+ .parse(json.getAsJsonPrimitive().getAsString(), LINKY_FORMATTER2))
.create();
+
private final LocaleProvider localeProvider;
private final HttpClient httpClient;
private final OAuthFactory oAuthFactory;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index fbe2404d647..58827531f18 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -39,13 +39,12 @@ import org.jsoup.nodes.Element;
import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AddressInfo;
-import org.openhab.binding.linky.internal.dto.ConsumptionReport;
-import org.openhab.binding.linky.internal.dto.ConsumptionReport.Consumption;
import org.openhab.binding.linky.internal.dto.ContactInfo;
import org.openhab.binding.linky.internal.dto.Customer;
import org.openhab.binding.linky.internal.dto.CustomerIdResponse;
import org.openhab.binding.linky.internal.dto.CustomerReponse;
import org.openhab.binding.linky.internal.dto.IdentityInfo;
+import org.openhab.binding.linky.internal.dto.MeterReading;
import org.openhab.binding.linky.internal.dto.MeterResponse;
import org.openhab.binding.linky.internal.dto.PrmInfo;
import org.openhab.binding.linky.internal.dto.UsagePoint;
@@ -222,7 +221,7 @@ public class EnedisHttpApi {
}
}
- private Consumption getMeasures(String userId, String prmId, LocalDate from, LocalDate to, String request)
+ private MeterReading getMeasures(String userId, String prmId, LocalDate from, LocalDate to, String request)
throws LinkyException {
String dtStart = from.format(API_DATE_FORMAT);
String dtEnd = to.format(API_DATE_FORMAT);
@@ -241,18 +240,21 @@ public class EnedisHttpApi {
if (meterResponse == null) {
throw new LinkyException("No report data received");
}
- return new ConsumptionReport().new Consumption();
+ return meterResponse.meterReading;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching ConsumptionReport.class: {}", data);
throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", url);
}
}
- public Consumption getEnergyData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
+ public MeterReading getEnergyData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
return getMeasures(userId, prmId, from, to, "daily_consumption");
}
- public Consumption getPowerData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(userId, prmId, from, to, "daily_consumption_max_power");
- }
+ /*
+ * public MeterReading getPowerData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException
+ * {
+ * return getMeasures(userId, prmId, from, to, "daily_consumption_max_power");
+ * }
+ */
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/console/LinkyCommandExtension.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/console/LinkyCommandExtension.java
index 95d54a81224..099a49ae807 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/console/LinkyCommandExtension.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/console/LinkyCommandExtension.java
@@ -57,8 +57,13 @@ public class LinkyCommandExtension extends AbstractConsoleCommandExtension imple
this.thingRegistry = thingRegistry;
}
+
+
+
@Override
public void execute(String[] args, Console console) {
+ /*
+
if (args.length >= 2) {
Thing thing = getThing(args[0]);
ThingHandler thingHandler = null;
@@ -121,8 +126,11 @@ public class LinkyCommandExtension extends AbstractConsoleCommandExtension imple
} else {
printUsage(console);
}
+ */
+
}
-
+
+
@Override
public List getUsages() {
return Arrays
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
index 23bf0d4018c..c998b6b2571 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
@@ -12,6 +12,8 @@
*/
package org.openhab.binding.linky.internal.dto;
+import java.time.LocalDate;
+
import org.eclipse.jetty.jaas.spi.UserInfo;
import com.google.gson.annotations.SerializedName;
@@ -25,9 +27,9 @@ import com.google.gson.annotations.SerializedName;
public class IntervalReading {
@SerializedName("value")
- public String value;
+ public double value;
@SerializedName("date")
- public String date;
+ public LocalDate date;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
index 8d32d1b722d..06a4645a75b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
@@ -42,4 +42,8 @@ public class MeterReading {
@SerializedName("interval_reading")
public IntervalReading[] intervalReading;
+ public IntervalReading[] WeekValue;
+ public IntervalReading[] MonthValue;
+ public IntervalReading[] YearValue;
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index f83636f0de6..2af4c4f9b36 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -16,12 +16,9 @@ import static org.openhab.binding.linky.internal.LinkyBindingConstants.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
-import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.WeekFields;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -34,14 +31,12 @@ import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.api.EnedisHttpApi;
import org.openhab.binding.linky.internal.api.ExpiringDayCache;
-import org.openhab.binding.linky.internal.dto.ConsumptionReport.Aggregate;
-import org.openhab.binding.linky.internal.dto.ConsumptionReport.Consumption;
-import org.openhab.binding.linky.internal.dto.PrmDetail;
+import org.openhab.binding.linky.internal.dto.IntervalReading;
+import org.openhab.binding.linky.internal.dto.MeterReading;
import org.openhab.binding.linky.internal.dto.PrmInfo;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.i18n.LocaleProvider;
-import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.MetricPrefix;
import org.openhab.core.library.unit.Units;
@@ -76,10 +71,11 @@ public class LinkyHandler extends BaseThingHandler {
private final Gson gson;
private final WeekFields weekFields;
- private final ExpiringDayCache cachedDailyData;
- private final ExpiringDayCache cachedPowerData;
- private final ExpiringDayCache cachedMonthlyData;
- private final ExpiringDayCache cachedYearlyData;
+ private final ExpiringDayCache dailyConsumption;
+
+ // private final ExpiringDayCache cachedPowerData;
+ // private final ExpiringDayCache cachedMonthlyData;
+ // private final ExpiringDayCache cachedYearlyData;
private @Nullable ScheduledFuture> refreshJob;
private @Nullable EnedisHttpApi enedisApi;
@@ -103,56 +99,61 @@ public class LinkyHandler extends BaseThingHandler {
this.gson = gson;
this.httpClient = httpClient;
this.weekFields = WeekFields.of(localeProvider.getLocale());
-
- this.cachedDailyData = new ExpiringDayCache<>("daily cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
- LocalDate today = LocalDate.now();
- Consumption consumption = getConsumptionData(today.minusDays(15), today);
- if (consumption != null) {
- logData(consumption.aggregats.days, "Day", false, DateTimeFormatter.ISO_LOCAL_DATE, Target.ALL);
- logData(consumption.aggregats.weeks, "Week", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, Target.ALL);
- consumption = getConsumptionAfterChecks(consumption, Target.LAST);
- }
- return consumption;
- });
-
this.oAuthFactory = oAuthFactory;
- this.cachedPowerData = new ExpiringDayCache<>("power cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
- // We request data for yesterday and the day before yesterday, even if the data for the day before yesterday
- // is not needed by the binding. This is only a workaround to an API bug that will return
- // INTERNAL_SERVER_ERROR rather than the expected data with a NaN value when the data for yesterday is not
- // yet available.
- // By requesting two days, the API is not failing and you get the expected NaN value for yesterday when the
- // data is not yet available.
+ this.dailyConsumption = new ExpiringDayCache<>("dailyConsumption", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate today = LocalDate.now();
- Consumption consumption = getPowerData(today.minusDays(2), today);
- if (consumption != null) {
- logData(consumption.aggregats.days, "Day (peak)", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME,
- Target.ALL);
- consumption = getConsumptionAfterChecks(consumption, Target.LAST);
- }
- return consumption;
+ MeterReading meterReading = getConsumptionData(today.minusDays(1095), today);
+ meterReading = getMeterReadingAfterChecks(meterReading);
+ /*
+ * if (consumption != null) {
+ * logData(consumption.aggregats.days, "Day", false, DateTimeFormatter.ISO_LOCAL_DATE, Target.ALL);
+ * logData(consumption.aggregats.weeks, "Week", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, Target.ALL);
+ *
+ * }
+ */
+ return meterReading;
});
- this.cachedMonthlyData = new ExpiringDayCache<>("monthly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
- LocalDate today = LocalDate.now();
- Consumption consumption = getConsumptionData(today.withDayOfMonth(1).minusMonths(1), today);
- if (consumption != null) {
- logData(consumption.aggregats.months, "Month", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, Target.ALL);
- consumption = getConsumptionAfterChecks(consumption, Target.LAST);
- }
- return consumption;
- });
-
- this.cachedYearlyData = new ExpiringDayCache<>("yearly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
- LocalDate today = LocalDate.now();
- Consumption consumption = getConsumptionData(LocalDate.of(today.getYear() - 1, 1, 1), today);
- if (consumption != null) {
- logData(consumption.aggregats.years, "Year", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, Target.ALL);
- consumption = getConsumptionAfterChecks(consumption, Target.LAST);
- }
- return consumption;
- });
+ /*
+ * this.cachedPowerData = new ExpiringDayCache<>("power cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
+ * // We request data for yesterday and the day before yesterday, even if the data for the day before yesterday
+ * // is not needed by the binding. This is only a workaround to an API bug that will return
+ * // INTERNAL_SERVER_ERROR rather than the expected data with a NaN value when the data for yesterday is not
+ * // yet available.
+ * // By requesting two days, the API is not failing and you get the expected NaN value for yesterday when the
+ * // data is not yet available.
+ * LocalDate today = LocalDate.now();
+ * Consumption consumption = getPowerData(today.minusDays(2), today);
+ * if (consumption != null) {
+ * logData(consumption.aggregats.days, "Day (peak)", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME,
+ * Target.ALL);
+ * consumption = getConsumptionAfterChecks(consumption, Target.LAST);
+ * }
+ * return consumption;
+ * });
+ *
+ *
+ * this.cachedMonthlyData = new ExpiringDayCache<>("monthly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
+ * LocalDate today = LocalDate.now();
+ * Consumption consumption = getConsumptionData(today.withDayOfMonth(1).minusMonths(1), today);
+ * if (consumption != null) {
+ * logData(consumption.aggregats.months, "Month", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, Target.ALL);
+ * consumption = getConsumptionAfterChecks(consumption, Target.LAST);
+ * }
+ * return consumption;
+ * });
+ *
+ * this.cachedYearlyData = new ExpiringDayCache<>("yearly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
+ * LocalDate today = LocalDate.now();
+ * Consumption consumption = getConsumptionData(LocalDate.of(today.getYear() - 1, 1, 1), today);
+ * if (consumption != null) {
+ * logData(consumption.aggregats.years, "Year", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, Target.ALL);
+ * consumption = getConsumptionAfterChecks(consumption, Target.LAST);
+ * }
+ * return consumption;
+ * });
+ */
}
@Override
@@ -207,27 +208,32 @@ public class LinkyHandler extends BaseThingHandler {
*/
private synchronized void updateData() {
boolean connectedBefore = isConnected();
- updatePowerData();
+
updateDailyWeeklyData();
- updateMonthlyData();
- updateYearlyData();
+
+ // updatePowerData();
+ // updateMonthlyData();
+ // updateYearlyData();
+
if (!connectedBefore && isConnected()) {
disconnect();
}
}
- private synchronized void updatePowerData() {
- if (isLinked(PEAK_POWER) || isLinked(PEAK_TIMESTAMP)) {
- cachedPowerData.getValue().ifPresentOrElse(values -> {
- Aggregate days = values.aggregats.days;
- updatekVAChannel(PEAK_POWER, days.datas.get(days.datas.size() - 1));
- updateState(PEAK_TIMESTAMP, new DateTimeType(days.periodes.get(days.datas.size() - 1).dateDebut));
- }, () -> {
- updateKwhChannel(PEAK_POWER, Double.NaN);
- updateState(PEAK_TIMESTAMP, UnDefType.UNDEF);
- });
- }
- }
+ /*
+ * private synchronized void updatePowerData() {
+ * if (isLinked(PEAK_POWER) || isLinked(PEAK_TIMESTAMP)) {
+ * cachedPowerData.getValue().ifPresentOrElse(values -> {
+ * Aggregate days = values.aggregats.days;
+ * updateVAChannel(PEAK_POWER, days.datas.get(days.datas.size() - 1));
+ * updateState(PEAK_TIMESTAMP, new DateTimeType(days.periodes.get(days.datas.size() - 1).dateDebut));
+ * }, () -> {
+ * updateKwhChannel(PEAK_POWER, Double.NaN);
+ * updateState(PEAK_TIMESTAMP, UnDefType.UNDEF);
+ * });
+ * }
+ * }
+ */
private void setCurrentAndPrevious(Aggregate periods, String currentChannel, String previousChannel) {
double currentValue = 0.0;
@@ -245,20 +251,38 @@ public class LinkyHandler extends BaseThingHandler {
/**
* Request new dayly/weekly data and updates channels
*/
+
private synchronized void updateDailyWeeklyData() {
if (isLinked(YESTERDAY) || isLinked(LAST_WEEK) || isLinked(THIS_WEEK)) {
- cachedDailyData.getValue().ifPresentOrElse(values -> {
- Aggregate days = values.aggregats.days;
- updateKwhChannel(YESTERDAY, days.datas.get(days.datas.size() - 1));
- setCurrentAndPrevious(values.aggregats.weeks, THIS_WEEK, LAST_WEEK);
+ dailyConsumption.getValue().ifPresentOrElse(values -> {
+ int dSize = values.intervalReading.length;
+ updateKwhChannel(YESTERDAY, values.intervalReading[dSize - 1].value / 1000.00);
+
+ LocalDate currentDt = LocalDate.now();
+ int idxCurrentYear = currentDt.getYear() - 2021;
+
+ int currentWeek = (currentDt.getDayOfYear() / 7) + 1;
+ int currentMonth = currentDt.getMonthValue();
+
+ int idxCurrentWeek = (52 * idxCurrentYear) + currentWeek;
+ int idxCurrentMonth = (12 * idxCurrentYear) + currentMonth;
+
+ int idxPreviousWeek = idxCurrentWeek - 1;
+ int idxPreviousMonth = idxCurrentMonth - 1;
+ int idxPreviousYear = idxCurrentYear - 1;
+
+ updateKwhChannel(THIS_WEEK, values.WeekValue[idxCurrentWeek].value / 1000.00);
+ updateKwhChannel(LAST_WEEK, values.WeekValue[idxPreviousWeek].value / 1000.00);
+
+ updateKwhChannel(THIS_MONTH, values.MonthValue[idxCurrentMonth].value / 1000.00);
+ updateKwhChannel(LAST_MONTH, values.WeekValue[idxPreviousMonth].value / 1000.00);
+
+ updateKwhChannel(THIS_YEAR, values.YearValue[idxCurrentYear].value / 1000.00);
+ updateKwhChannel(LAST_YEAR, values.YearValue[idxPreviousYear].value / 1000.00);
}, () -> {
updateKwhChannel(YESTERDAY, Double.NaN);
- if (ZonedDateTime.now().get(weekFields.dayOfWeek()) == 1) {
- updateKwhChannel(THIS_WEEK, 0.0);
- updateKwhChannel(LAST_WEEK, Double.NaN);
- } else {
- updateKwhChannel(THIS_WEEK, Double.NaN);
- }
+ updateKwhChannel(THIS_WEEK, Double.NaN);
+ updateKwhChannel(LAST_WEEK, Double.NaN);
});
}
}
@@ -266,36 +290,57 @@ public class LinkyHandler extends BaseThingHandler {
/**
* Request new monthly data and updates channels
*/
- private synchronized void updateMonthlyData() {
- if (isLinked(LAST_MONTH) || isLinked(THIS_MONTH)) {
- cachedMonthlyData.getValue().ifPresentOrElse(
- values -> setCurrentAndPrevious(values.aggregats.months, THIS_MONTH, LAST_MONTH), () -> {
- if (ZonedDateTime.now().getDayOfMonth() == 1) {
- updateKwhChannel(THIS_MONTH, 0.0);
- updateKwhChannel(LAST_MONTH, Double.NaN);
- } else {
- updateKwhChannel(THIS_MONTH, Double.NaN);
- }
- });
- }
- }
+
+ /*
+ * private synchronized void updateMonthlyData() {
+ * if (isLinked(LAST_MONTH) || isLinked(THIS_MONTH)) {
+ * cachedMonthlyData.getValue().ifPresentOrElse(values -> {
+ * Aggregate months = values.aggregats.months;
+ * updateKwhChannel(LAST_MONTH, months.datas.get(0));
+ * if (months.datas.size() > 1) {
+ * updateKwhChannel(THIS_MONTH, months.datas.get(1));
+ * } else {
+ * updateKwhChannel(THIS_MONTH, 0.0);
+ * }
+ * }, () -> {
+ * if (ZonedDateTime.now().getDayOfMonth() == 1) {
+ * updateKwhChannel(THIS_MONTH, 0.0);
+ * updateKwhChannel(LAST_MONTH, Double.NaN);
+ * } else {
+ * updateKwhChannel(THIS_MONTH, Double.NaN);
+ * }
+ * });
+ * }
+ * }
+ */
+
/**
* Request new yearly data and updates channels
*/
- private synchronized void updateYearlyData() {
- if (isLinked(LAST_YEAR) || isLinked(THIS_YEAR)) {
- cachedYearlyData.getValue().ifPresentOrElse(
- values -> setCurrentAndPrevious(values.aggregats.years, THIS_YEAR, LAST_YEAR), () -> {
- if (ZonedDateTime.now().getDayOfYear() == 1) {
- updateKwhChannel(THIS_YEAR, 0.0);
- updateKwhChannel(LAST_YEAR, Double.NaN);
- } else {
- updateKwhChannel(THIS_YEAR, Double.NaN);
- }
- });
- }
- }
+
+ /*
+ * private synchronized void updateYearlyData() {
+ * if (isLinked(LAST_YEAR) || isLinked(THIS_YEAR)) {
+ * cachedYearlyData.getValue().ifPresentOrElse(values -> {
+ * Aggregate years = values.aggregats.years;
+ * updateKwhChannel(LAST_YEAR, years.datas.get(0));
+ * if (years.datas.size() > 1) {
+ * updateKwhChannel(THIS_YEAR, years.datas.get(1));
+ * } else {
+ * updateKwhChannel(THIS_YEAR, 0.0);
+ * }
+ * }, () -> {
+ * if (ZonedDateTime.now().getDayOfYear() == 1) {
+ * updateKwhChannel(THIS_YEAR, 0.0);
+ * updateKwhChannel(LAST_YEAR, Double.NaN);
+ * } else {
+ * updateKwhChannel(THIS_YEAR, Double.NaN);
+ * }
+ * });
+ * }
+ * }
+ */
private void updateKwhChannel(String channelId, double consumption) {
logger.debug("Update channel {} with {}", channelId, consumption);
@@ -318,65 +363,64 @@ public class LinkyHandler extends BaseThingHandler {
*
* @return the report as a list of string
*/
- public synchronized List reportValues(LocalDate startDay, LocalDate endDay, @Nullable String separator) {
- List report = buildReport(startDay, endDay, separator);
- disconnect();
- return report;
- }
- private List buildReport(LocalDate startDay, LocalDate endDay, @Nullable String separator) {
- List report = new ArrayList<>();
- if (startDay.getYear() == endDay.getYear() && startDay.getMonthValue() == endDay.getMonthValue()) {
- // All values in the same month
- Consumption result = getConsumptionData(startDay, endDay.plusDays(1));
- if (result != null) {
- Aggregate days = result.aggregats.days;
- int size = (days.datas == null || days.periodes == null) ? 0
- : (days.datas.size() <= days.periodes.size() ? days.datas.size() : days.periodes.size());
- for (int i = 0; i < size; i++) {
- double consumption = days.datas.get(i);
- LocalDate day = days.periodes.get(i).dateDebut.toLocalDate();
- // Filter data in case it contains data from dates outside the requested period
- if (day.isBefore(startDay) || day.isAfter(endDay)) {
- continue;
- }
- String line = days.periodes.get(i).dateDebut.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator;
- if (consumption >= 0) {
- line += String.valueOf(consumption);
- }
- report.add(line);
- }
- } else {
- LocalDate currentDay = startDay;
- while (!currentDay.isAfter(endDay)) {
- report.add(currentDay.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator);
- currentDay = currentDay.plusDays(1);
- }
- }
- } else {
- // Concatenate the report produced for each month between the two dates
- LocalDate first = startDay;
- do {
- LocalDate last = first.withDayOfMonth(first.lengthOfMonth());
- if (last.isAfter(endDay)) {
- last = endDay;
- }
- report.addAll(buildReport(first, last, separator));
- first = last.plusDays(1);
- } while (!first.isAfter(endDay));
- }
- return report;
- }
+ /*
+ * public synchronized List reportValues(LocalDate startDay, LocalDate endDay, @Nullable String separator) {
+ * List report = buildReport(startDay, endDay, separator);
+ * disconnect();
+ * return report;
+ * }
+ *
+ * private List buildReport(LocalDate startDay, LocalDate endDay, @Nullable String separator) {
+ * List report = new ArrayList<>();
+ * if (startDay.getYear() == endDay.getYear() && startDay.getMonthValue() == endDay.getMonthValue()) {
+ * // All values in the same month
+ * Consumption result = getConsumptionData(startDay, endDay.plusDays(1));
+ * if (result != null) {
+ * Aggregate days = result.aggregats.days;
+ * int size = (days.datas == null || days.periodes == null) ? 0
+ * : (days.datas.size() <= days.periodes.size() ? days.datas.size() : days.periodes.size());
+ * for (int i = 0; i < size; i++) {
+ * double consumption = days.datas.get(i);
+ * String line = days.periodes.get(i).dateDebut.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator;
+ * if (consumption >= 0) {
+ * line += String.valueOf(consumption);
+ * }
+ * report.add(line);
+ * }
+ * } else {
+ * LocalDate currentDay = startDay;
+ * while (!currentDay.isAfter(endDay)) {
+ * report.add(currentDay.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator);
+ * currentDay = currentDay.plusDays(1);
+ * }
+ * }
+ * } else {
+ * // Concatenate the report produced for each month between the two dates
+ * LocalDate first = startDay;
+ * do {
+ * LocalDate last = first.withDayOfMonth(first.lengthOfMonth());
+ * if (last.isAfter(endDay)) {
+ * last = endDay;
+ * }
+ * report.addAll(buildReport(first, last, separator));
+ * first = last.plusDays(1);
+ * } while (!first.isAfter(endDay));
+ * }
+ * return report;
+ * }
+ */
- private @Nullable Consumption getConsumptionData(LocalDate from, LocalDate to) {
+ private @Nullable MeterReading getConsumptionData(LocalDate from, LocalDate to) {
logger.debug("getConsumptionData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
to.format(DateTimeFormatter.ISO_LOCAL_DATE));
+
EnedisHttpApi api = this.enedisApi;
if (api != null) {
try {
- Consumption consumption = api.getEnergyData(userId, prmId, from, to);
+ MeterReading meterReading = api.getEnergyData(userId, prmId, from, to);
updateStatus(ThingStatus.ONLINE);
- return consumption;
+ return meterReading;
} catch (LinkyException e) {
logger.debug("Exception when getting consumption data: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
@@ -385,22 +429,26 @@ public class LinkyHandler extends BaseThingHandler {
return null;
}
- private @Nullable Consumption getPowerData(LocalDate from, LocalDate to) {
- logger.debug("getPowerData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
- to.format(DateTimeFormatter.ISO_LOCAL_DATE));
- EnedisHttpApi api = this.enedisApi;
- if (api != null) {
- try {
- Consumption consumption = api.getPowerData(userId, prmId, from, to);
- updateStatus(ThingStatus.ONLINE);
- return consumption;
- } catch (LinkyException e) {
- logger.debug("Exception when getting power data: {}", e.getMessage(), e);
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
- }
- }
- return null;
- }
+ /*
+ *
+ * private @Nullable Consumption getPowerData(LocalDate from, LocalDate to) {
+ * logger.debug("getPowerData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
+ * to.format(DateTimeFormatter.ISO_LOCAL_DATE));
+ * EnedisHttpApi api = this.enedisApi;
+ * if (api != null) {
+ * try {
+ * Consumption consumption = api.getPowerData(userId, prmId, from, to);
+ * updateStatus(ThingStatus.ONLINE);
+ * return consumption;
+ * } catch (LinkyException e) {
+ * logger.debug("Exception when getting power data: {}", e.getMessage(), e);
+ * updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ * }
+ * }
+ * return null;
+ * }
+ *
+ */
private boolean isConnected() {
EnedisHttpApi api = this.enedisApi;
@@ -443,15 +491,15 @@ public class LinkyHandler extends BaseThingHandler {
break;
case LAST_MONTH:
case THIS_MONTH:
- updateMonthlyData();
+ // updateMonthlyData();
break;
case LAST_YEAR:
case THIS_YEAR:
- updateYearlyData();
+ // updateYearlyData();
break;
case PEAK_POWER:
case PEAK_TIMESTAMP:
- updatePowerData();
+ // updatePowerData();
break;
default:
break;
@@ -464,94 +512,119 @@ public class LinkyHandler extends BaseThingHandler {
}
}
- private @Nullable Consumption getConsumptionAfterChecks(Consumption consumption, Target target) {
+ private @Nullable MeterReading getMeterReadingAfterChecks(@Nullable MeterReading meterReading) {
try {
- checkData(consumption);
+ checkData(meterReading);
} catch (LinkyException e) {
logger.debug("Consumption data: {}", e.getMessage());
return null;
}
- if (target == Target.FIRST && !isDataFirstDayAvailable(consumption)) {
- logger.debug("Data including yesterday are not yet available");
- return null;
+
+ meterReading.WeekValue = new IntervalReading[208];
+ meterReading.MonthValue = new IntervalReading[48];
+ meterReading.YearValue = new IntervalReading[4];
+
+ for (int idx = 0; idx < 208; idx++) {
+ meterReading.WeekValue[idx] = new IntervalReading();
}
- if (target == Target.LAST && !isDataLastDayAvailable(consumption)) {
- logger.debug("Data including yesterday are not yet available");
- return null;
+ for (int idx = 0; idx < 48; idx++) {
+ meterReading.MonthValue[idx] = new IntervalReading();
}
- return consumption;
+ for (int idx = 0; idx < 4; idx++) {
+ meterReading.YearValue[idx] = new IntervalReading();
+ }
+
+ int size = meterReading.intervalReading.length;
+ int baseYear = meterReading.intervalReading[0].date.getYear();
+
+ for (int idx = 0; idx < size; idx++) {
+ IntervalReading ir = meterReading.intervalReading[idx];
+ LocalDate dt = ir.date;
+ double value = ir.value;
+
+ int idxYear = dt.getYear() - baseYear;
+
+ int dayOfYear = dt.getDayOfYear();
+ int week = (dayOfYear / 7) + 1;
+ int month = dt.getMonthValue();
+
+ int idxMonth = (idxYear * 12) + month;
+ int idxWeek = (idxYear * 52) + week;
+
+ meterReading.WeekValue[idxWeek].value += value;
+ meterReading.MonthValue[idxMonth].value += value;
+ meterReading.YearValue[idxYear].value += value;
+ }
+
+ return meterReading;
}
- private void checkData(Consumption consumption) throws LinkyException {
- if (consumption.aggregats.days.periodes.isEmpty()) {
- throw new LinkyException("Invalid consumptions data: no day period");
+ private void checkData(@Nullable MeterReading meterReading) throws LinkyException {
+ if (meterReading.intervalReading.length == 0) {
+ throw new LinkyException("Invalid meterReading data: no day period");
}
- if (consumption.aggregats.days.periodes.size() != consumption.aggregats.days.datas.size()) {
- throw new LinkyException("Invalid consumptions data: not any data for each day period");
- }
- if (consumption.aggregats.weeks.periodes.isEmpty()) {
- throw new LinkyException("Invalid consumptions data: no week period");
- }
- if (consumption.aggregats.weeks.periodes.size() != consumption.aggregats.weeks.datas.size()) {
- throw new LinkyException("Invalid consumptions data: not any data for each week period");
- }
- if (consumption.aggregats.months.periodes.isEmpty()) {
- throw new LinkyException("Invalid consumptions data: no month period");
- }
- if (consumption.aggregats.months.periodes.size() != consumption.aggregats.months.datas.size()) {
- throw new LinkyException("Invalid consumptions data: not any data for each month period");
- }
- if (consumption.aggregats.years.periodes.isEmpty()) {
- throw new LinkyException("Invalid consumptions data: no year period");
- }
- if (consumption.aggregats.years.periodes.size() != consumption.aggregats.years.datas.size()) {
- throw new LinkyException("Invalid consumptions data: not any data for each year period");
+ if (meterReading.intervalReading.length != 1095) {
+ throw new LinkyException("Imcomplete meterReading data < 1095 days");
}
+
}
- private boolean isDataFirstDayAvailable(Consumption consumption) {
- Aggregate days = consumption.aggregats.days;
- logData(days, "First day", false, DateTimeFormatter.ISO_LOCAL_DATE, Target.FIRST);
- return days.datas != null && !days.datas.isEmpty() && !days.datas.get(0).isNaN();
- }
-
- private boolean isDataLastDayAvailable(Consumption consumption) {
- Aggregate days = consumption.aggregats.days;
- logData(days, "Last day", false, DateTimeFormatter.ISO_LOCAL_DATE, Target.LAST);
- return days.datas != null && !days.datas.isEmpty() && !days.datas.get(days.datas.size() - 1).isNaN();
- }
-
- private void logData(Aggregate aggregate, String title, boolean withDateFin, DateTimeFormatter dateTimeFormatter,
- Target target) {
- if (logger.isDebugEnabled()) {
- int size = (aggregate.datas == null || aggregate.periodes == null) ? 0
- : (aggregate.datas.size() <= aggregate.periodes.size() ? aggregate.datas.size()
- : aggregate.periodes.size());
- if (target == Target.FIRST) {
- if (size > 0) {
- logData(aggregate, 0, title, withDateFin, dateTimeFormatter);
- }
- } else if (target == Target.LAST) {
- if (size > 0) {
- logData(aggregate, size - 1, title, withDateFin, dateTimeFormatter);
- }
- } else {
- for (int i = 0; i < size; i++) {
- logData(aggregate, i, title, withDateFin, dateTimeFormatter);
- }
- }
- }
- }
-
- private void logData(Aggregate aggregate, int index, String title, boolean withDateFin,
- DateTimeFormatter dateTimeFormatter) {
- if (withDateFin) {
- logger.debug("{} {} {} value {}", title, aggregate.periodes.get(index).dateDebut.format(dateTimeFormatter),
- aggregate.periodes.get(index).dateFin.format(dateTimeFormatter), aggregate.datas.get(index));
- } else {
- logger.debug("{} {} value {}", title, aggregate.periodes.get(index).dateDebut.format(dateTimeFormatter),
- aggregate.datas.get(index));
- }
- }
+ /*
+ * private @Nullable Consumption getConsumptionAfterChecks(Consumption consumption, Target target) {
+ *
+ *
+ *
+ * return consumption;
+ * }
+ *
+ *
+ *
+ * private boolean isDataFirstDayAvailable(Consumption consumption) {
+ * Aggregate days = consumption.aggregats.days;
+ * logData(days, "First day", false, DateTimeFormatter.ISO_LOCAL_DATE, Target.FIRST);
+ * return days.datas != null && !days.datas.isEmpty() && !days.datas.get(0).isNaN();
+ * }
+ *
+ * private boolean isDataLastDayAvailable(Consumption consumption) {
+ * Aggregate days = consumption.aggregats.days;
+ * logData(days, "Last day", false, DateTimeFormatter.ISO_LOCAL_DATE, Target.LAST);
+ * return days.datas != null && !days.datas.isEmpty() && !days.datas.get(days.datas.size() - 1).isNaN();
+ * }
+ *
+ *
+ * private void logData(Aggregate aggregate, String title, boolean withDateFin, DateTimeFormatter dateTimeFormatter,
+ * Target target) {
+ * if (logger.isDebugEnabled()) {
+ * int size = (aggregate.datas == null || aggregate.periodes == null) ? 0
+ * : (aggregate.datas.size() <= aggregate.periodes.size() ? aggregate.datas.size()
+ * : aggregate.periodes.size());
+ * if (target == Target.FIRST) {
+ * if (size > 0) {
+ * logData(aggregate, 0, title, withDateFin, dateTimeFormatter);
+ * }
+ * } else if (target == Target.LAST) {
+ * if (size > 0) {
+ * logData(aggregate, size - 1, title, withDateFin, dateTimeFormatter);
+ * }
+ * } else {
+ * for (int i = 0; i < size; i++) {
+ * logData(aggregate, i, title, withDateFin, dateTimeFormatter);
+ * }
+ * }
+ * }
+ * }
+ *
+ * private void logData(Aggregate aggregate, int index, String title, boolean withDateFin,
+ * DateTimeFormatter dateTimeFormatter) {
+ * if (withDateFin) {
+ * logger.debug("{} {} {} value {}", title, aggregate.periodes.get(index).dateDebut.format(dateTimeFormatter),
+ * aggregate.periodes.get(index).dateFin.format(dateTimeFormatter), aggregate.datas.get(index));
+ * } else {
+ * logger.debug("{} {} value {}", title, aggregate.periodes.get(index).dateDebut.format(dateTimeFormatter),
+ * aggregate.datas.get(index));
+ * }
+ * }
+ *
+ */
}
From cfda793db1342a14e4589bca4dcff18720b61f77 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 26 Jan 2024 15:55:24 +0100
Subject: [PATCH 003/135] add max consumption data
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyBindingConstants.java | 29 +++++++++----
.../linky/internal/LinkyHandlerFactory.java | 18 +++++++-
.../linky/internal/api/EnedisHttpApi.java | 15 +++----
.../linky/internal/dto/IntervalReading.java | 4 +-
.../linky/internal/dto/MeterReading.java | 8 ++--
.../resources/OH-INF/thing/thing-types.xml | 43 +++++++++++++++----
6 files changed, 82 insertions(+), 35 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 119926c61e3..0e058a9e633 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -39,15 +39,26 @@ public class LinkyBindingConstants {
public static final String USER_ID = "customerId";
// List of all Channel id's
- public static final String YESTERDAY = "daily#yesterday";
- public static final String PEAK_POWER = "daily#power";
- public static final String PEAK_TIMESTAMP = "daily#timestamp";
- public static final String THIS_WEEK = "weekly#thisWeek";
- public static final String LAST_WEEK = "weekly#lastWeek";
- public static final String THIS_MONTH = "monthly#thisMonth";
- public static final String LAST_MONTH = "monthly#lastMonth";
- public static final String THIS_YEAR = "yearly#thisYear";
- public static final String LAST_YEAR = "yearly#lastYear";
+ public static final String DAY_MINUS_1 = "daily#day-1";
+ public static final String DAY_MINUS_2 = "daily#day-2";
+ public static final String DAY_MINUS_3 = "daily#day-3";
+
+ public static final String PEAK_POWER_DAY_MINUS_1 = "daily#maxPower-day-1";
+ public static final String PEAK_POWER_TS_DAY_MINUS_1 = "daily#maxPowerTs-day-1";
+ public static final String PEAK_POWER_DAY_MINUS_2 = "daily#maxPower-day-2";
+ public static final String PEAK_POWER_TS_DAY_MINUS_2 = "daily#maxPowerTs-day-2";
+ public static final String PEAK_POWER_DAY_MINUS_3 = "daily#maxPower-day-3";
+ public static final String PEAK_POWER_TS_DAY_MINUS_3 = "daily#maxPowerTs-day-3";
+
+ public static final String WEEK_MINUS_0 = "weekly#week-0";
+ public static final String WEEK_MINUS_1 = "weekly#week-1";
+ public static final String WEEK_MINUS_2 = "weekly#week-2";
+ public static final String MONTH_MINUS_0 = "monthly#month-0";
+ public static final String MONTH_MINUS_1 = "monthly#month-1";
+ public static final String MONTH_MINUS_2 = "monthly#month-2";
+ public static final String YEAR_MINUS_0 = "yearly#year-0";
+ public static final String YEAR_MINUS_1 = "yearly#year-1";
+ public static final String YEAR_MINUS_2 = "yearly#year-2";
// Authorization related Servlet and resources aliases.
public static final String LINKY_ALIAS = "/connectlinky";
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index fcc2f06a48d..8eac00ca475 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -15,6 +15,7 @@ package org.openhab.binding.linky.internal;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@@ -56,7 +57,9 @@ import com.google.gson.JsonDeserializer;
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky")
public class LinkyHandlerFactory extends BaseThingHandlerFactory implements LinkyAccountHandler {
private static final DateTimeFormatter LINKY_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
- private static final DateTimeFormatter LINKY_FORMATTER2 = DateTimeFormatter.ofPattern("uuuu-MM-dd");
+ private static final DateTimeFormatter LINKY_LOCALDATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd");
+ private static final DateTimeFormatter LINKY_LOCALDATETIME_FORMATTER = DateTimeFormatter
+ .ofPattern("uuuu-MM-dd HH:mm:ss");
private static final int REQUEST_BUFFER_SIZE = 8000;
private static final int RESPONSE_BUFFER_SIZE = 200000;
@@ -67,7 +70,18 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
.parse(json.getAsJsonPrimitive().getAsString(), LINKY_FORMATTER))
.registerTypeAdapter(LocalDate.class,
(JsonDeserializer) (json, type, jsonDeserializationContext) -> LocalDate
- .parse(json.getAsJsonPrimitive().getAsString(), LINKY_FORMATTER2))
+ .parse(json.getAsJsonPrimitive().getAsString(), LINKY_LOCALDATE_FORMATTER))
+ .registerTypeAdapter(LocalDateTime.class,
+ (JsonDeserializer) (json, type, jsonDeserializationContext) -> {
+ try {
+ return LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(),
+ LINKY_LOCALDATETIME_FORMATTER);
+ } catch (Exception ex) {
+ return LocalDate.parse(json.getAsJsonPrimitive().getAsString(), LINKY_LOCALDATE_FORMATTER)
+ .atStartOfDay();
+ }
+ })
+
.create();
private final LocaleProvider localeProvider;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 58827531f18..5903548481b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -165,7 +165,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(String.format("%s/%s/cache", ADDRESS_URL, "21454992660003"));
+ String data = getData(String.format("%s/%s/cache", ADDRESS_URL, config.prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", ADDRESS_URL);
}
@@ -185,7 +185,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(String.format("%s/%s/cache", IDENTITY_URL, "21454992660003"));
+ String data = getData(String.format("%s/%s/cache", IDENTITY_URL, config.prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", IDENTITY_URL);
}
@@ -205,7 +205,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(String.format("%s/%s/cache", CONTACT_URL, "21454992660003"));
+ String data = getData(String.format("%s/%s/cache", CONTACT_URL, config.prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", CONTACT_URL);
}
@@ -251,10 +251,7 @@ public class EnedisHttpApi {
return getMeasures(userId, prmId, from, to, "daily_consumption");
}
- /*
- * public MeterReading getPowerData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException
- * {
- * return getMeasures(userId, prmId, from, to, "daily_consumption_max_power");
- * }
- */
+ public MeterReading getPowerData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
+ return getMeasures(userId, prmId, from, to, "daily_consumption_max_power");
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
index c998b6b2571..7c8055aebea 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
@@ -12,7 +12,7 @@
*/
package org.openhab.binding.linky.internal.dto;
-import java.time.LocalDate;
+import java.time.LocalDateTime;
import org.eclipse.jetty.jaas.spi.UserInfo;
@@ -30,6 +30,6 @@ public class IntervalReading {
public double value;
@SerializedName("date")
- public LocalDate date;
+ public LocalDateTime date;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
index 06a4645a75b..c182bb86a0a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
@@ -40,10 +40,10 @@ public class MeterReading {
public ReadingType readingType;
@SerializedName("interval_reading")
- public IntervalReading[] intervalReading;
+ public IntervalReading[] dayValue;
- public IntervalReading[] WeekValue;
- public IntervalReading[] MonthValue;
- public IntervalReading[] YearValue;
+ public IntervalReading[] weekValue;
+ public IntervalReading[] monthValue;
+ public IntervalReading[] yearValue;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 778b374ed24..8d1f55ad0e7 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -34,50 +34,75 @@
Daily consumption
-
+
Yesterday Consumption
-
-
+
+ Yesterday Consumption
+
+
+ Yesterday Consumption
+
+
+
Peak Timestamp
Maximum power usage timestamp
+
+
+ Peak Timestamp
+ Maximum power usage timestamp
+
+
+
+ Peak Timestamp
+ Maximum power usage timestamp
+
Weekly consumption
-
+
This Week Consumption
-
+
Last Week Consumption
+
+ Last Week Consumption
+
Monthly consumption
-
+
This Month Consumption
-
+
Last Month Consumption
+
+ Last Month Consumption
+
Yearly consumption
-
+
This Year Consumption
-
+
Last Year Consumption
+
+ Last Year Consumption
+
From 3f27bf33658ddae16ec441035b122cbc93f56893 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 26 Jan 2024 17:41:08 +0100
Subject: [PATCH 004/135] reintegrate repporting features inside binding
Signed-off-by: Laurent ARNAL
---
.../console/LinkyCommandExtension.java | 10 +-
.../linky/internal/handler/LinkyHandler.java | 94 ++++++++++---------
2 files changed, 49 insertions(+), 55 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/console/LinkyCommandExtension.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/console/LinkyCommandExtension.java
index 099a49ae807..95d54a81224 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/console/LinkyCommandExtension.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/console/LinkyCommandExtension.java
@@ -57,13 +57,8 @@ public class LinkyCommandExtension extends AbstractConsoleCommandExtension imple
this.thingRegistry = thingRegistry;
}
-
-
-
@Override
public void execute(String[] args, Console console) {
- /*
-
if (args.length >= 2) {
Thing thing = getThing(args[0]);
ThingHandler thingHandler = null;
@@ -126,11 +121,8 @@ public class LinkyCommandExtension extends AbstractConsoleCommandExtension imple
} else {
printUsage(console);
}
- */
-
}
-
-
+
@Override
public List getUsages() {
return Arrays
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 2af4c4f9b36..8d145fa2c0f 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -19,6 +19,8 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.WeekFields;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -364,52 +366,52 @@ public class LinkyHandler extends BaseThingHandler {
* @return the report as a list of string
*/
- /*
- * public synchronized List reportValues(LocalDate startDay, LocalDate endDay, @Nullable String separator) {
- * List report = buildReport(startDay, endDay, separator);
- * disconnect();
- * return report;
- * }
- *
- * private List buildReport(LocalDate startDay, LocalDate endDay, @Nullable String separator) {
- * List report = new ArrayList<>();
- * if (startDay.getYear() == endDay.getYear() && startDay.getMonthValue() == endDay.getMonthValue()) {
- * // All values in the same month
- * Consumption result = getConsumptionData(startDay, endDay.plusDays(1));
- * if (result != null) {
- * Aggregate days = result.aggregats.days;
- * int size = (days.datas == null || days.periodes == null) ? 0
- * : (days.datas.size() <= days.periodes.size() ? days.datas.size() : days.periodes.size());
- * for (int i = 0; i < size; i++) {
- * double consumption = days.datas.get(i);
- * String line = days.periodes.get(i).dateDebut.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator;
- * if (consumption >= 0) {
- * line += String.valueOf(consumption);
- * }
- * report.add(line);
- * }
- * } else {
- * LocalDate currentDay = startDay;
- * while (!currentDay.isAfter(endDay)) {
- * report.add(currentDay.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator);
- * currentDay = currentDay.plusDays(1);
- * }
- * }
- * } else {
- * // Concatenate the report produced for each month between the two dates
- * LocalDate first = startDay;
- * do {
- * LocalDate last = first.withDayOfMonth(first.lengthOfMonth());
- * if (last.isAfter(endDay)) {
- * last = endDay;
- * }
- * report.addAll(buildReport(first, last, separator));
- * first = last.plusDays(1);
- * } while (!first.isAfter(endDay));
- * }
- * return report;
- * }
- */
+ public synchronized List reportValues(LocalDate startDay, LocalDate endDay, @Nullable String separator) {
+ List report = buildReport(startDay, endDay, separator);
+ disconnect();
+ return report;
+ }
+
+ private List buildReport(LocalDate startDay, LocalDate endDay, @Nullable String separator) {
+ List report = new ArrayList<>();
+ if (startDay.getYear() == endDay.getYear() && startDay.getMonthValue() == endDay.getMonthValue()) {
+ // All values in the same month
+ MeterReading meterReading = getConsumptionData(startDay, endDay.plusDays(1));
+ if (meterReading != null) {
+
+ IntervalReading[] days = meterReading.dayValue;
+
+ int size = days.length;
+
+ for (int i = 0; i < size; i++) {
+ double consumption = days[i].value;
+ String line = days[i].date.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator;
+ if (consumption >= 0) {
+ line += String.valueOf(consumption);
+ }
+ report.add(line);
+ }
+ } else {
+ LocalDate currentDay = startDay;
+ while (!currentDay.isAfter(endDay)) {
+ report.add(currentDay.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator);
+ currentDay = currentDay.plusDays(1);
+ }
+ }
+ } else {
+ // Concatenate the report produced for each month between the two dates
+ LocalDate first = startDay;
+ do {
+ LocalDate last = first.withDayOfMonth(first.lengthOfMonth());
+ if (last.isAfter(endDay)) {
+ last = endDay;
+ }
+ report.addAll(buildReport(first, last, separator));
+ first = last.plusDays(1);
+ } while (!first.isAfter(endDay));
+ }
+ return report;
+ }
private @Nullable MeterReading getConsumptionData(LocalDate from, LocalDate to) {
logger.debug("getConsumptionData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
From 3e2b9bba1ce5e371a7f7b70ceba38b8eaab5a9d0 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sat, 27 Jan 2024 09:25:33 +0100
Subject: [PATCH 005/135] refactor authservice to break link between handler
and auth
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyAuthServlet.java | 88 +++++++++--
.../linky/internal/LinkyBindingConstants.java | 10 +-
.../linky/internal/LinkyConfiguration.java | 2 +-
.../linky/internal/LinkyHandlerFactory.java | 141 +++++++++++++++++-
.../linky/internal/api/EnedisHttpApi.java | 32 +++-
.../linky/internal/handler/LinkyHandler.java | 15 +-
.../resources/OH-INF/i18n/linky.properties | 2 +-
.../resources/OH-INF/i18n/linky_fr.properties | 3 +-
.../resources/OH-INF/thing/thing-types.xml | 4 +-
.../src/main/resources/templates/index.html | 17 ++-
10 files changed, 261 insertions(+), 53 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
index f659de06d36..bd15331dbeb 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -13,6 +13,7 @@
package org.openhab.binding.linky.internal;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
@@ -25,6 +26,9 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.UrlEncoded;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -42,8 +46,20 @@ public class LinkyAuthServlet extends HttpServlet {
private static final Pattern MESSAGE_KEY_PATTERN = Pattern.compile("\\$\\{([^\\}]+)\\}");
+ private static final String CONTENT_TYPE = "text/html;charset=UTF-8";
+
+ private static final String HTML_USER_AUTHORIZED = "Addon authorized for user %s.
";
+ private static final String HTML_ERROR = "Call to Enedis failed with error: %s
";
+
+ private static final String HTML_META_REFRESH_CONTENT = " ";
+
// Keys present in the index.html
- private static final String KEY_BRIDGE_URI = "bridge.uri";
+ private static final String KEY_AUTHORIZE_URI = "authorize.uri";
+ private static final String KEY_RETRIEVE_TOKEN_URI = "retrieveToken.uri";
+ private static final String KEY_REDIRECT_URI = "redirectUri";
+ private static final String KEY_AUTHORIZED_USER = "authorizedUser";
+ private static final String KEY_ERROR = "error";
+ private static final String KEY_PAGE_REFRESH = "pageRefresh";
private final Logger logger = LoggerFactory.getLogger(LinkyAuthServlet.class);
private final LinkyAuthService linkyAuthService;
@@ -59,25 +75,67 @@ public class LinkyAuthServlet extends HttpServlet {
throws ServletException, IOException {
logger.debug("Linky auth callback servlet received GET request {}.", req.getRequestURI());
final Map replaceMap = new HashMap<>();
- /*
- *
- * final String servletBaseURL = req.getRequestURL().toString();
- * final String queryString = req.getQueryString();
- *
- *
- * String servletBaseURLSecure = servletBaseURL.replace("http://", "https://").replace("8080", "8443");
- * handleSmartthingsRedirect(replaceMap, servletBaseURLSecure, queryString);
- * resp.setContentType(CONTENT_TYPE);
- * LinkyAccountHandler accountHandler = linkyAuthService.getLinkyAccountHandler();
- */
- String uri = "https://mon-compte-particulier.enedis.fr/dataconnect/v1/oauth2/authorize?client_id=e551937c-5250-48bc-b4a6-2323af68db92&duration=P36M&response_type=code";
- // replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
- replaceMap.put(KEY_BRIDGE_URI, uri);
+ final String servletBaseURL = req.getRequestURL().toString();
+
+ String servletBaseURLSecure = servletBaseURL;
+ // .replace("http://", "https://");
+ // .replace("8080", "8443");
+ servletBaseURLSecure = servletBaseURLSecure + "?state=OK";
+
+ handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString());
+
+ LinkyAccountHandler accountHandler = linkyAuthService.getLinkyAccountHandler();
+
+ resp.setContentType(CONTENT_TYPE);
+ replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
+ replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure);
+ replaceMap.put(KEY_AUTHORIZE_URI, accountHandler.formatAuthorizationUrl(servletBaseURLSecure));
resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
resp.getWriter().close();
}
+ /**
+ * Handles a possible call from Enedis to the redirect_uri. If that is the case Spotify will pass the authorization
+ * codes via the url and these are processed. In case of an error this is shown to the user. If the user was
+ * authorized this is passed on to the handler. Based on all these different outcomes the HTML is generated to
+ * inform the user.
+ *
+ * @param replaceMap a map with key String values that will be mapped in the HTML templates.
+ * @param servletBaseURL the servlet base, which should be used as the Spotify redirect_uri value
+ * @param queryString the query part of the GET request this servlet is processing
+ */
+ private void handleLinkyRedirect(Map replaceMap, String servletBaseURL,
+ @Nullable String queryString) {
+ replaceMap.put(KEY_AUTHORIZED_USER, "");
+ replaceMap.put(KEY_ERROR, "");
+ replaceMap.put(KEY_PAGE_REFRESH, "");
+
+ if (queryString != null) {
+ final MultiMap params = new MultiMap<>();
+ UrlEncoded.decodeTo(queryString, params, StandardCharsets.UTF_8.name());
+ final String reqCode = params.getString("code");
+ final String reqState = params.getString("state");
+ final String reqError = params.getString("error");
+
+ replaceMap.put(KEY_PAGE_REFRESH,
+ params.isEmpty() ? "" : String.format(HTML_META_REFRESH_CONTENT, servletBaseURL));
+
+ if (!StringUtil.isBlank(reqError)) {
+ logger.debug("Spotify redirected with an error: {}", reqError);
+ replaceMap.put(KEY_ERROR, String.format(HTML_ERROR, reqError));
+ } else if (!StringUtil.isBlank(reqState)) {
+ try {
+ replaceMap.put(KEY_AUTHORIZED_USER, String.format(HTML_USER_AUTHORIZED,
+ linkyAuthService.authorize(servletBaseURL, reqState, reqCode)));
+ } catch (RuntimeException e) {
+ logger.debug("Exception during authorizaton: ", e);
+ replaceMap.put(KEY_ERROR, String.format(HTML_ERROR, e.getMessage()));
+ }
+ }
+ }
+ }
+
/**
* Replaces all keys from the map found in the template with values from the map. If the key is not found the key
* will be kept in the template.
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 0e058a9e633..7846f53f37b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -72,8 +72,12 @@ public class LinkyBindingConstants {
"r:installedapps", "w:installedapps").collect(Collectors.joining(" "));
// List of Spotify services related urls, information
- public static final String LINKY_ACCOUNT_URL = "https://api.smartthings.com/oauth";
- public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "/authorize";
- public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "/token";
+ public static final String LINKY_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
+ public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "v1/oauth2/authorize";
+ public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "token";
+
+ public static final String ENEDIS_ACCOUNT_URL = "https://mon-compte-particulier.enedis.fr/";
+ public static final String ENEDIS_AUTHORIZE_URL = ENEDIS_ACCOUNT_URL + "dataconnect/v1/oauth2/authorize";
+ public static final String ENEDIS_API_TOKEN_URL = ENEDIS_ACCOUNT_URL + "token";
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
index 6bfa0f6403b..5847b113b72 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
@@ -27,6 +27,6 @@ public class LinkyConfiguration {
public String prmId = "";
public boolean seemsValid() {
- return !token.isBlank() && !prmId.isBlank();
+ return !prmId.isBlank();
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 8eac00ca475..940d4238493 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -12,12 +12,14 @@
*/
package org.openhab.binding.linky.internal;
+import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
+import java.util.Collection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
@@ -27,11 +29,17 @@ import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.openhab.binding.linky.internal.handler.LinkyHandler;
+import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
+import org.openhab.core.auth.client.oauth2.OAuthClientService;
+import org.openhab.core.auth.client.oauth2.OAuthException;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
+import org.openhab.core.auth.client.oauth2.OAuthResponseException;
+import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.io.net.http.TrustAllTrustManager;
import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
@@ -63,6 +71,9 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
private static final int REQUEST_BUFFER_SIZE = 8000;
private static final int RESPONSE_BUFFER_SIZE = 200000;
+ private final String clientId = "e551937c-5250-48bc-b4a6-2323af68db92";
+ private final String clientSecret = "";
+
private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
private final Gson gson = new GsonBuilder()
.registerTypeAdapter(ZonedDateTime.class,
@@ -88,11 +99,14 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
private final HttpClient httpClient;
private final OAuthFactory oAuthFactory;
private final LinkyAuthService authService;
+ private final boolean oAuthSupport = false;
+ private @Nullable OAuthClientService oAuthService;
+ private final ThingRegistry thingRegistry;
@Activate
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
final @Reference HttpClientFactory httpClientFactory, final @Reference LinkyAuthService authService,
- final @Reference OAuthFactory oAuthFactory) {
+ final @Reference OAuthFactory oAuthFactory, final @Reference ThingRegistry thingRegistry) {
this.localeProvider = localeProvider;
SslContextFactory sslContextFactory = new SslContextFactory.Client();
try {
@@ -105,12 +119,19 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
} catch (KeyManagementException e) {
logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
}
+ this.thingRegistry = thingRegistry;
this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
httpClient.setFollowRedirects(false);
httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
httpClient.setResponseBufferSize(RESPONSE_BUFFER_SIZE);
this.oAuthFactory = oAuthFactory;
this.authService = authService;
+
+ this.oAuthService = oAuthFactory.createOAuthClientService("Linky", LinkyBindingConstants.LINKY_API_TOKEN_URL,
+ LinkyBindingConstants.LINKY_AUTHORIZE_URL, clientId, clientSecret, LinkyBindingConstants.LINKY_SCOPES,
+ true);
+ this.authService.setLinkyAccountHandler(this);
+
}
@Override
@@ -141,26 +162,130 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
- authService.setLinkyAccountHandler(this);
+ if (supportsThingType(thing.getThingTypeUID())) {
+ LinkyHandler handler = new LinkyHandler(thing, localeProvider, gson, httpClient, oAuthFactory);
+ return handler;
+ }
- return supportsThingType(thing.getThingTypeUID())
- ? new LinkyHandler(thing, localeProvider, gson, httpClient, oAuthFactory)
- : null;
+ return null;
}
+ // ===========================================================================
+
@Override
public boolean isAuthorized() {
- return true;
+ final AccessTokenResponse accessTokenResponse = getAccessTokenResponse();
+
+ return accessTokenResponse != null && accessTokenResponse.getAccessToken() != null
+ && accessTokenResponse.getRefreshToken() != null;
+
+ }
+
+ private @Nullable AccessTokenResponse getAccessTokenResponse() {
+ try {
+ OAuthClientService oAuthService = this.oAuthService;
+ return oAuthService == null ? null : oAuthService.getAccessTokenResponse();
+ } catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
+ logger.debug("Exception checking authorization: ", e);
+ return null;
+ }
}
@Override
- public String authorize(String redirectUrl, String reqCode) {
+ public String authorize(String redirectUri, String reqCode) {
+ // Will work only in case of direct oAuth2 authentification to enedis
+ // this is not the case in v1 as we go trough MyElectricalData
+
+ if (oAuthSupport) {
+ try {
+ OAuthClientService oAuthService = this.oAuthService;
+ if (oAuthService == null) {
+ throw new OAuthException("OAuth service is not initialized");
+ }
+ logger.debug("Make call to Smartthings to get access token.");
+ final AccessTokenResponse credentials = oAuthService.getAccessTokenResponseByAuthorizationCode(reqCode,
+ redirectUri);
+ final String user = updateProperties(credentials);
+ logger.debug("Authorized for user: {}", user);
+ return user;
+ } catch (RuntimeException | OAuthException | IOException e) {
+ // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
+ throw new RuntimeException(e.getMessage(), e);
+ } catch (final OAuthResponseException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ // Fallback for MyElectricalData
+ else {
+ String token = "";
+ /*
+ * String token = enedisApi.getToken(clientId, config.prmId);
+ * config.token = token;
+ *
+ * Configuration configuration = getConfig();
+ * configuration.put("token", token);
+ * updateConfiguration(configuration);
+ */
+ return token;
+ }
+ }
+
+ private String updateProperties(AccessTokenResponse credentials) {
+ /*
+ * if (spotifyApi != null) {
+ *
+ * final Me me = spotifyApi.getMe();
+ * final String user = me.getDisplayName() == null ? me.getId() : me.getDisplayName();
+ * final Map props = editProperties();
+ *
+ * props.put(PROPERTY_SPOTIFY_USER, user);
+ * updateProperties(props);
+ * return user;
+ * }
+ */
return "";
}
@Override
public String formatAuthorizationUrl(String redirectUri) {
- return "";
+ // Will work only in case of direct oAuth2 authentification to enedis
+ // this is not the case in v1 as we go trough MyElectricalData
+ if (oAuthSupport) {
+ try {
+ OAuthClientService oAuthService = this.oAuthService;
+ if (oAuthService == null) {
+ throw new OAuthException("OAuth service is not initialized");
+ }
+
+ String uri = oAuthService.getAuthorizationUrl(redirectUri, null, "Linky");
+ return uri;
+ } catch (final OAuthException e) {
+ logger.debug("Error constructing AuthorizationUrl: ", e);
+ return "";
+ }
+ }
+ // Fallback for MyElectricalData
+ else {
+ Collection col = this.thingRegistry.getAll();
+ for (Thing thing : col) {
+ if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
+
+ Configuration config = thing.getConfiguration();
+
+ String prmId = (String) config.get("prmId");
+ prmId = prmId + "";
+
+ }
+ }
+
+ String uri = LinkyBindingConstants.ENEDIS_AUTHORIZE_URL;
+ uri = uri + "?";
+ uri = uri + "&client_id=" + clientId;
+ uri = uri + "&duration=" + "P36M";
+ uri = uri + "&response_type=" + "code";
+ return uri;
+ }
+
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 5903548481b..c77dc4e7e6c 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeoutException;
import javax.ws.rs.core.MediaType;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
@@ -71,14 +72,17 @@ public class EnedisHttpApi {
private static final String ADDRESS_URL = BASE_URL + "addresses";
private static final String MEASURE_URL = BASE_URL + "%s/%s/start/%s/end/%s/cache";
+ private static final String TOKEN_URL = BASE_URL
+ + "v1/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=na&user_type=na&state=na&person_id=-1&usage_points_id=%s";
+
private final Logger logger = LoggerFactory.getLogger(EnedisHttpApi.class);
private final Gson gson;
private final HttpClient httpClient;
- private final LinkyConfiguration config;
+ private @Nullable LinkyConfiguration config;
private boolean connected = false;
- public EnedisHttpApi(LinkyConfiguration config, Gson gson, HttpClient httpClient) {
+ public EnedisHttpApi(@Nullable LinkyConfiguration config, Gson gson, HttpClient httpClient) {
this.gson = gson;
this.httpClient = httpClient;
this.config = config;
@@ -110,9 +114,21 @@ public class EnedisHttpApi {
try {
Request request = httpClient.newRequest(url);
request = request.method(HttpMethod.GET);
- request = request.header("Authorization", config.token);
ContentResponse result = request.send();
+ if (result.getStatus() == 307) {
+ String loc = result.getHeaders().get("Location");
+ url = BASE_URL + loc.substring(1);
+ request = httpClient.newRequest(url);
+ request = request.method(HttpMethod.GET);
+ result = request.send();
+
+ if (result.getStatus() == 307) {
+ loc = result.getHeaders().get("Location");
+ String res = loc.split("/")[3];
+ return res;
+ }
+ }
if (result.getStatus() != 200) {
throw new LinkyException("Error requesting '%s' : %s", url, result.getContentAsString());
}
@@ -254,4 +270,14 @@ public class EnedisHttpApi {
public MeterReading getPowerData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
return getMeasures(userId, prmId, from, to, "daily_consumption_max_power");
}
+
+ public String getToken(String clientId, String prmId) {
+ try {
+ String url = String.format(TOKEN_URL, clientId, prmId);
+ String token = getData(url);
+ return token;
+ } catch (LinkyException e) {
+ return "";
+ }
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 8d145fa2c0f..56001489b79 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -28,7 +28,6 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
-import org.openhab.binding.linky.internal.LinkyBindingConstants;
import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.api.EnedisHttpApi;
@@ -36,7 +35,6 @@ import org.openhab.binding.linky.internal.api.ExpiringDayCache;
import org.openhab.binding.linky.internal.dto.IntervalReading;
import org.openhab.binding.linky.internal.dto.MeterReading;
import org.openhab.binding.linky.internal.dto.PrmInfo;
-import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.library.types.QuantityType;
@@ -75,9 +73,7 @@ public class LinkyHandler extends BaseThingHandler {
private final ExpiringDayCache dailyConsumption;
- // private final ExpiringDayCache cachedPowerData;
- // private final ExpiringDayCache cachedMonthlyData;
- // private final ExpiringDayCache cachedYearlyData;
+ private @Nullable LinkyConfiguration config;
private @Nullable ScheduledFuture> refreshJob;
private @Nullable EnedisHttpApi enedisApi;
@@ -93,8 +89,6 @@ public class LinkyHandler extends BaseThingHandler {
ALL
}
- private @Nullable OAuthClientService oAuthService;
-
public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient,
OAuthFactory oAuthFactory) {
super(thing);
@@ -163,13 +157,8 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("Initializing Linky handler.");
updateStatus(ThingStatus.UNKNOWN);
- LinkyConfiguration config = getConfigAs(LinkyConfiguration.class);
+ config = getConfigAs(LinkyConfiguration.class);
if (config.seemsValid()) {
-
- OAuthClientService oAuthService = oAuthFactory.createOAuthClientService(thing.getUID().getAsString(),
- LinkyBindingConstants.LINKY_API_TOKEN_URL, LinkyBindingConstants.LINKY_AUTHORIZE_URL, "clientId",
- "clientSecret", LinkyBindingConstants.LINKY_SCOPES, true);
-
enedisApi = new EnedisHttpApi(config, gson, httpClient);
scheduler.submit(() -> {
try {
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/i18n/linky.properties b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/i18n/linky.properties
index 9eeaf4460fd..4c38a8b0de1 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/i18n/linky.properties
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/i18n/linky.properties
@@ -43,4 +43,4 @@ channel-type.linky.timestamp.label = Timestamp
# Thing status descriptions
-offline.config-error-mandatory-settings = Username, password and authId are mandatory.
+offline.config-error-mandatory-settings = The PrmId is mandatory.
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/i18n/linky_fr.properties b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/i18n/linky_fr.properties
index a62d52db76e..9dc8f36289e 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/i18n/linky_fr.properties
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/i18n/linky_fr.properties
@@ -43,4 +43,5 @@ channel-type.linky.timestamp.label = Horodatage
# Thing status descriptions
-offline.config-error-mandatory-settings = Le nom d'utilisateur, le mot de passe et l'ID d'authentification sont obligatoires.
+offline.config-error-mandatory-settings = Le PrmId est obligatoire.
+
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 8d1f55ad0e7..b2725d14847 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -24,9 +24,9 @@
PrmId
Your PrmId
-
+
Token
- Your Enedis token
+ Your Enedis token (can be left empty, use the connection page to automatically fill it http://youopenhab/connectlinky)
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html b/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html
index b5e0ce91ca5..0a637709206 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html
+++ b/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html
@@ -4,7 +4,7 @@
${pageRefresh}
-Authorize openHAB binding for Smartthings
+Authorize openHAB Linky binding for Enedis
+
+
-
-
-
-
-
- SmartThings icon
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Authorize openHAB Linky binding for Enedis
On this page you can authorize the openHAB Linky binding to access your Enedis account.
@@ -75,19 +166,48 @@ html {
${error} ${authorizedUser}
+
+ This process need to be done for each electrical meter you have (each prmId).
+ This is a two step process:
+
+ First you will need to do a consent on the enedis Site.
+ For this, click on the Authorize button.
+ You will be redirect to the enedis site. Login as usual with your userName / Password.
+ You will then be redirect to a consentment page.
+ You will have to select which prmId you would like to give the consent, click the checkbox, and then click on the validate button.
+ After this, you will be redirect to myelectricaldata information page.
+
+
+ Second, come back to the /connectlinky page.
+ If you have multiple prm, select the PrmId in the combobox you want to authorize.v
+ Then click the Retrieve token button.
+ It will fill you linky thing configuration with the token.
+
+
+
+
+
+
- Connect to Enedis:
-
-
-
-
-
+
Connect to Enedis:
+
+
+
+
+ Please select your prmId :
+
+ ${prmId.Option}
+
+
+
+
+
From 6f8b6a621ae7f12c7f55a9b31aa8ff4afab5a559 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Thu, 1 Feb 2024 19:31:05 +0100
Subject: [PATCH 008/135] spotless:apply
Signed-off-by: Laurent ARNAL
---
.../src/main/feature/feature.xml | 2 +-
.../linky/internal/LinkyAuthService.java | 2 +-
.../linky/internal/LinkyAuthServlet.java | 2 +-
.../linky/internal/LinkyBindingConstants.java | 1 -
.../linky/internal/LinkyHandlerFactory.java | 4 -
.../linky/internal/dto/AddressInfo.java | 1 -
.../binding/linky/internal/dto/Customer.java | 1 -
.../linky/internal/dto/IntervalReading.java | 1 -
.../linky/internal/dto/MeterReading.java | 1 -
.../linky/internal/dto/MeterResponse.java | 1 -
.../linky/internal/handler/LinkyHandler.java | 12 +--
.../resources/OH-INF/thing/thing-types.xml | 101 +++++++++---------
12 files changed, 57 insertions(+), 72 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/feature/feature.xml b/bundles/org.openhab.binding.linky/src/main/feature/feature.xml
index 7e83306ff37..16185d9fffd 100644
--- a/bundles/org.openhab.binding.linky/src/main/feature/feature.xml
+++ b/bundles/org.openhab.binding.linky/src/main/feature/feature.xml
@@ -7,6 +7,6 @@
openhab-core-auth-oauth2client
mvn:org.jsoup/jsoup/1.14.3
mvn:org.openhab.addons.bundles/org.openhab.binding.linky/${project.version}
-
+
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
index 9f6bed55693..db00174987d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
@@ -199,4 +199,4 @@ public class LinkyAuthService {
protected void unsetHttpService(HttpService httpService) {
this.httpService = null;
}
-}
\ No newline at end of file
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
index e98c810eb56..75ad3d3dd98 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -169,4 +169,4 @@ public class LinkyAuthServlet extends HttpServlet {
m.appendTail(sb);
return sb.toString();
}
-}
\ No newline at end of file
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 4b7289f374c..1787c67f1d6 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -81,5 +81,4 @@ public class LinkyBindingConstants {
public static final String ENEDIS_ACCOUNT_URL = "https://mon-compte-particulier.enedis.fr/";
public static final String ENEDIS_AUTHORIZE_URL = ENEDIS_ACCOUNT_URL + "dataconnect/v1/oauth2/authorize";
public static final String ENEDIS_API_TOKEN_URL = ENEDIS_ACCOUNT_URL + "token";
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index bdf8bb1bd17..0ec0c6f2f35 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -134,7 +134,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
LinkyBindingConstants.LINKY_AUTHORIZE_URL, clientId, clientSecret, LinkyBindingConstants.LINKY_SCOPES,
true);
this.authService.setLinkyAccountHandler(this);
-
}
@Override
@@ -181,7 +180,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
return accessTokenResponse != null && accessTokenResponse.getAccessToken() != null
&& accessTokenResponse.getRefreshToken() != null;
-
}
private @Nullable AccessTokenResponse getAccessTokenResponse() {
@@ -288,7 +286,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
uri = uri + "&response_type=" + "code";
return uri;
}
-
}
@Override
@@ -308,5 +305,4 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
return result.toArray(new String[0]);
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
index bb7170b1649..54f9b19af81 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
@@ -35,5 +35,4 @@ public class AddressInfo {
public String city;
public String country;
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
index de2caae124a..26e7abe6ade 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
@@ -29,5 +29,4 @@ public class Customer {
@SerializedName("usage_points")
public UsagePoint[] usagePoints;
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
index 7c8055aebea..bcfadd57069 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
@@ -31,5 +31,4 @@ public class IntervalReading {
@SerializedName("date")
public LocalDateTime date;
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
index c182bb86a0a..eb967967af1 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
@@ -45,5 +45,4 @@ public class MeterReading {
public IntervalReading[] weekValue;
public IntervalReading[] monthValue;
public IntervalReading[] yearValue;
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
index ff307fd9fee..f5d1f85ad73 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
@@ -26,5 +26,4 @@ import com.google.gson.annotations.SerializedName;
public class MeterResponse {
@SerializedName("meter_reading")
public MeterReading meterReading;
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 03d63b3d4c9..402c68ad2d6 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -39,11 +39,7 @@ import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.library.types.QuantityType;
-<<<<<<< HEAD
-import org.openhab.core.library.unit.MetricPrefix;
-=======
import org.openhab.core.library.types.StringType;
->>>>>>> 8179e0592f (some fixes on connectlinky page)
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
@@ -594,10 +590,9 @@ public class LinkyHandler extends BaseThingHandler {
if (meterReading.intervalReading.length == 0) {
throw new LinkyException("Invalid meterReading data: no day period");
}
- if (meterReading.intervalReading.length != 1095) {
- throw new LinkyException("Imcomplete meterReading data < 1095 days");
- }
-
+ // if (meterReading.intervalReading.length != 1095) {
+ // throw new LinkyException("Imcomplete meterReading data < 1095 days");
+ // }
}
/*
@@ -687,5 +682,4 @@ public class LinkyHandler extends BaseThingHandler {
public void saveConfiguration(Configuration config) {
updateConfiguration(config);
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index f6d4ff1a4f0..43b60a1fb1d 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -12,31 +12,32 @@
https://espace-client-particuliers.enedis.fr/web/espace-particuliers/compteur-linky.
-
+
-
+
-
+
-
-
-
+
+
+
-
- PrmId
- Your PrmId
-
+
+ PrmId
+ Your PrmId
+
Token
- Your Enedis token (can be left empty, use the connection page to automatically fill it http://youopenhab/connectlinky)
+ Your Enedis token (can be left empty, use the connection page to automatically fill it
+ http://youopenhab/connectlinky)
-
-
+
+
@@ -46,26 +47,26 @@
Yesterday Consumption
- Yesterday Consumption
-
-
- Yesterday Consumption
-
+ Yesterday Consumption
+
+
+ Yesterday Consumption
+
Peak Timestamp
Maximum power usage timestamp
-
-
- Peak Timestamp
- Maximum power usage timestamp
-
-
-
- Peak Timestamp
- Maximum power usage timestamp
-
+
+
+ Peak Timestamp
+ Maximum power usage timestamp
+
+
+
+ Peak Timestamp
+ Maximum power usage timestamp
+
@@ -79,8 +80,8 @@
Last Week Consumption
- Last Week Consumption
-
+ Last Week Consumption
+
@@ -93,9 +94,9 @@
Last Month Consumption
-
- Last Month Consumption
-
+
+ Last Month Consumption
+
@@ -108,27 +109,27 @@
Last Year Consumption
-
- Last Year Consumption
-
+
+ Last Year Consumption
+
-
+
- Main
-
-
-
- linkyTestSelect
-
-
-
-
+ Main
+
+
+
+ linkyTestSelect
+
+
+
+
- String
- Test Select
- Test Select
-
+ String
+ Test Select
+ Test Select
+
Number:Energy
From a84023ee3cfb2fc932c8838444fabbdac92a7558 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Thu, 1 Feb 2024 19:38:16 +0100
Subject: [PATCH 009/135] fix code violation
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyAccountHandler.java | 4 +-
.../linky/internal/LinkyAuthService.java | 61 +++----------------
.../linky/internal/LinkyAuthServlet.java | 8 +--
.../linky/internal/LinkyBindingConstants.java | 2 +-
.../linky/internal/LinkyHandlerFactory.java | 14 +----
5 files changed, 16 insertions(+), 73 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
index 17cfb65c510..bc60528b201 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2010-2023 Contributors to the openHAB project
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
@@ -22,7 +22,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
public interface LinkyAccountHandler {
/**
- * @return Returns true if the Spotify Bridge is authorized.
+ * @return Returns true if the Linky Bridge is authorized.
*/
boolean isAuthorized();
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
index db00174987d..237faa3c3f4 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2010-2023 Contributors to the openHAB project
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
@@ -51,8 +51,6 @@ public class LinkyAuthService {
private final Logger logger = LoggerFactory.getLogger(LinkyAuthService.class);
- // private final List handlers = new ArrayList<>();
-
private @NonNullByDefault({}) HttpService httpService;
private @NonNullByDefault({}) BundleContext bundleContext;
private @Nullable LinkyAccountHandler accountHandler;
@@ -66,7 +64,7 @@ public class LinkyAuthService {
httpService.registerResources(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS,
"web", null);
} catch (NamespaceException | ServletException | IOException e) {
- logger.warn("Error during spotify servlet startup", e);
+ logger.warn("Error during linky servlet startup", e);
}
}
@@ -77,7 +75,7 @@ public class LinkyAuthService {
}
/**
- * Creates a new {@link SpotifyAuthServlet}.
+ * Creates a new {@link LinkyAuthServlet}.
*
* @return the newly created servlet
* @throws IOException thrown when an HTML template could not be read
@@ -107,13 +105,13 @@ public class LinkyAuthService {
}
/**
- * Call with Spotify redirect uri returned State and Code values to get the refresh and access tokens and persist
+ * Call with Linky redirect uri returned State and Code values to get the refresh and access tokens and persist
* these values
*
- * @param servletBaseURL the servlet base, which will be the Spotify redirect url
- * @param state The Spotify returned state value
- * @param code The Spotify returned code value
- * @return returns the name of the Spotify user that is authorized
+ * @param servletBaseURL the servlet base, which will be the Linky redirect url
+ * @param state The Linky returned state value
+ * @param code The Linky returned code value
+ * @return returns the name of the Linky user that is authorized
*/
public String authorize(String servletBaseURL, String state, String code) {
LinkyAccountHandler accountHandler = getLinkyAccountHandler();
@@ -148,49 +146,6 @@ public class LinkyAuthService {
return this.accountHandler;
}
- /**
- * @param listener Adds the given handler
- */
- /*
- * public void addSpotifyAccountHandler(SpotifyAccountHandler listener) {
- * if (!handlers.contains(listener)) {
- * handlers.add(listener);
- * }
- * }
- */
-
- /**
- * @param handler Removes the given handler
- */
- /*
- * public void removeSpotifyAccountHandler(SpotifyAccountHandler handler) {
- * handlers.remove(handler);
- * }
- */
-
- /**
- * @return Returns all {@link SpotifyAccountHandler}s.
- */
- /*
- * public List getSpotifyAccountHandlers() {
- * return handlers;
- * }
- */
-
- /**
- * Get the {@link SpotifyAccountHandler} that matches the given thing UID.
- *
- * @param thingUID UID of the thing to match the handler with
- * @return the {@link SpotifyAccountHandler} matching the thing UID or null
- */
- /*
- * private @Nullable SpotifyAccountHandler getSpotifyAuthListener(String thingUID) {
- * final Optional maybeListener = handlers.stream().filter(l -> l.equalsThingUID(thingUID))
- * .findFirst();
- * return maybeListener.isPresent() ? maybeListener.get() : null;
- * }
- */
-
@Reference
protected void setHttpService(HttpService httpService) {
this.httpService = httpService;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
index 75ad3d3dd98..a1c1e42303d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2010-2023 Contributors to the openHAB project
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
@@ -105,13 +105,13 @@ public class LinkyAuthServlet extends HttpServlet {
}
/**
- * Handles a possible call from Enedis to the redirect_uri. If that is the case Spotify will pass the authorization
+ * Handles a possible call from Enedis to the redirect_uri. If that is the case Linky will pass the authorization
* codes via the url and these are processed. In case of an error this is shown to the user. If the user was
* authorized this is passed on to the handler. Based on all these different outcomes the HTML is generated to
* inform the user.
*
* @param replaceMap a map with key String values that will be mapped in the HTML templates.
- * @param servletBaseURL the servlet base, which should be used as the Spotify redirect_uri value
+ * @param servletBaseURL the servlet base, which should be used as the Linky redirect_uri value
* @param queryString the query part of the GET request this servlet is processing
*/
private void handleLinkyRedirect(Map replaceMap, String servletBaseURL,
@@ -132,7 +132,7 @@ public class LinkyAuthServlet extends HttpServlet {
// params.isEmpty() ? "" : String.format(HTML_META_REFRESH_CONTENT, servletBaseURL)
if (!StringUtil.isBlank(reqError)) {
- logger.debug("Spotify redirected with an error: {}", reqError);
+ logger.debug("Linky redirected with an error: {}", reqError);
replaceMap.put(KEY_ERROR, String.format(HTML_ERROR, reqError));
} else if (!StringUtil.isBlank(reqState)) {
try {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 1787c67f1d6..71709ab40b6 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -73,7 +73,7 @@ public class LinkyBindingConstants {
"r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*",
"r:installedapps", "w:installedapps").collect(Collectors.joining(" "));
- // List of Spotify services related urls, information
+ // List of Linky services related urls, information
public static final String LINKY_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "v1/oauth2/authorize";
public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "token";
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 0ec0c6f2f35..f7e9d83d422 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -220,7 +220,7 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
else {
String token = EnedisHttpApi.getToken(httpClient, clientId, reqCode);
- logger.debug("token:" + token);
+ logger.debug("token: {}", token);
Collection col = this.thingRegistry.getAll();
for (Thing thing : col) {
@@ -244,18 +244,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
}
private String updateProperties(AccessTokenResponse credentials) {
- /*
- * if (spotifyApi != null) {
- *
- * final Me me = spotifyApi.getMe();
- * final String user = me.getDisplayName() == null ? me.getId() : me.getDisplayName();
- * final Map props = editProperties();
- *
- * props.put(PROPERTY_SPOTIFY_USER, user);
- * updateProperties(props);
- * return user;
- * }
- */
return "";
}
From 34459b3a483244516b4b34dce33b25a8d4b0e5fe Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 2 Feb 2024 09:49:52 +0100
Subject: [PATCH 010/135] review null annotation and null pointer warning
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyAuthServlet.java | 25 +++---
.../linky/internal/LinkyHandlerFactory.java | 9 +-
.../linky/internal/api/EnedisHttpApi.java | 16 +---
.../linky/internal/dto/TempoResponse.java | 2 +
.../linky/internal/handler/LinkyHandler.java | 87 +++++++++----------
5 files changed, 64 insertions(+), 75 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
index a1c1e42303d..50fc4fab286 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -51,8 +51,6 @@ public class LinkyAuthServlet extends HttpServlet {
private static final String HTML_USER_AUTHORIZED = "Addon authorized for %s.
";
private static final String HTML_ERROR = "Call to Enedis failed with error: %s
";
- private static final String HTML_META_REFRESH_CONTENT = " ";
-
// Keys present in the index.html
private static final String KEY_AUTHORIZE_URI = "authorize.uri";
private static final String KEY_RETRIEVE_TOKEN_URI = "retrieveToken.uri";
@@ -72,16 +70,17 @@ public class LinkyAuthServlet extends HttpServlet {
}
@Override
- protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp)
- throws ServletException, IOException {
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.debug("Linky auth callback servlet received GET request {}.", req.getRequestURI());
final Map replaceMap = new HashMap<>();
- final String servletBaseURL = req.getRequestURL().toString();
+ String servletBaseURL = "";
+ StringBuffer requestURL = req.getRequestURL();
+ if (requestURL != null) {
+ servletBaseURL = requestURL.toString();
+ }
String servletBaseURLSecure = servletBaseURL;
- // .replace("http://", "https://");
- // .replace("8080", "8443");
handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString());
@@ -91,15 +90,19 @@ public class LinkyAuthServlet extends HttpServlet {
StringBuffer optionBuffer = new StringBuffer();
- String[] prmIds = accountHandler.getAllPrmId();
- for (String prmId : prmIds) {
- optionBuffer.append("" + prmId + " ");
+ if (accountHandler != null) {
+ String[] prmIds = accountHandler.getAllPrmId();
+ for (String prmId : prmIds) {
+ optionBuffer.append("" + prmId + " ");
+ }
}
replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString());
replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK");
- replaceMap.put(KEY_AUTHORIZE_URI, accountHandler.formatAuthorizationUrl(servletBaseURLSecure));
+ if (accountHandler != null) {
+ replaceMap.put(KEY_AUTHORIZE_URI, accountHandler.formatAuthorizationUrl(servletBaseURLSecure));
+ }
resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
resp.getWriter().close();
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index f7e9d83d422..5fa56ed77e8 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -100,7 +100,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
private final LocaleProvider localeProvider;
private final HttpClient httpClient;
- private final OAuthFactory oAuthFactory;
private final LinkyAuthService authService;
private final boolean oAuthSupport = false;
private @Nullable OAuthClientService oAuthService;
@@ -126,8 +125,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
httpClient.setFollowRedirects(false);
httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
- httpClient.setResponseBufferSize(RESPONSE_BUFFER_SIZE);
- this.oAuthFactory = oAuthFactory;
this.authService = authService;
this.oAuthService = oAuthFactory.createOAuthClientService("Linky", LinkyBindingConstants.LINKY_API_TOKEN_URL,
@@ -165,7 +162,7 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
protected @Nullable ThingHandler createHandler(Thing thing) {
if (supportsThingType(thing.getThingTypeUID())) {
- LinkyHandler handler = new LinkyHandler(thing, localeProvider, gson, httpClient, oAuthFactory);
+ LinkyHandler handler = new LinkyHandler(thing, localeProvider, gson, httpClient);
return handler;
}
@@ -235,7 +232,9 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
config.put("token", token);
LinkyHandler handler = (LinkyHandler) thing.getHandler();
- handler.saveConfiguration(config);
+ if (handler != null) {
+ handler.saveConfiguration(config);
+ }
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 3cc4ec6ae95..f6cbd3b9ba9 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -24,13 +24,9 @@ import java.util.concurrent.TimeoutException;
import javax.ws.rs.core.MediaType;
import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.util.FormContentProvider;
-import org.eclipse.jetty.client.util.StringContentProvider;
-import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.Fields;
@@ -80,11 +76,11 @@ public class EnedisHttpApi {
private final Logger logger = LoggerFactory.getLogger(EnedisHttpApi.class);
private final Gson gson;
private final HttpClient httpClient;
- private @Nullable LinkyConfiguration config;
+ private LinkyConfiguration config;
private boolean connected = false;
- public EnedisHttpApi(@Nullable LinkyConfiguration config, Gson gson, HttpClient httpClient) {
+ public EnedisHttpApi(LinkyConfiguration config, Gson gson, HttpClient httpClient) {
this.gson = gson;
this.httpClient = httpClient;
this.config = config;
@@ -93,10 +89,6 @@ public class EnedisHttpApi {
public void initialize() throws LinkyException {
}
- private String getLocation(ContentResponse response) {
- return response.getHeaders().get(HttpHeader.LOCATION);
- }
-
private void disconnect() throws LinkyException {
if (connected) {
logger.debug("Logout process");
@@ -127,8 +119,8 @@ public class EnedisHttpApi {
ContentResponse result = request.send();
if (result.getStatus() == 307) {
String loc = result.getHeaders().get("Location");
- url = BASE_URL + loc.substring(1);
- request = httpClient.newRequest(url);
+ String newUrl = BASE_URL + loc.substring(1);
+ request = httpClient.newRequest(newUrl);
request = request.method(HttpMethod.GET);
result = request.send();
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
index 509772ddacf..f799ef7f391 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
@@ -24,5 +24,7 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
*/
public class TempoResponse extends HashMap {
+ @java.io.Serial
+ private static final long serialVersionUID = 362498820763181264L;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 402c68ad2d6..29acd0ad837 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -18,7 +18,6 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
-import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -35,7 +34,6 @@ import org.openhab.binding.linky.internal.api.ExpiringDayCache;
import org.openhab.binding.linky.internal.dto.IntervalReading;
import org.openhab.binding.linky.internal.dto.MeterReading;
import org.openhab.binding.linky.internal.dto.PrmInfo;
-import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.library.types.QuantityType;
@@ -70,17 +68,12 @@ public class LinkyHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(LinkyHandler.class);
private final HttpClient httpClient;
private final Gson gson;
- private final WeekFields weekFields;
private final ExpiringDayCache dailyConsumption;
- private @Nullable LinkyConfiguration config;
-
private @Nullable ScheduledFuture> refreshJob;
private @Nullable EnedisHttpApi enedisApi;
- private final OAuthFactory oAuthFactory;
-
private @NonNullByDefault({}) String prmId;
private @NonNullByDefault({}) String userId;
@@ -90,13 +83,10 @@ public class LinkyHandler extends BaseThingHandler {
ALL
}
- public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient,
- OAuthFactory oAuthFactory) {
+ public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient) {
super(thing);
this.gson = gson;
this.httpClient = httpClient;
- this.weekFields = WeekFields.of(localeProvider.getLocale());
- this.oAuthFactory = oAuthFactory;
this.dailyConsumption = new ExpiringDayCache<>("dailyConsumption", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate today = LocalDate.now();
@@ -158,16 +148,18 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("Initializing Linky handler.");
updateStatus(ThingStatus.UNKNOWN);
- config = getConfigAs(LinkyConfiguration.class);
+ LinkyConfiguration config = getConfigAs(LinkyConfiguration.class);
if (config.seemsValid()) {
- enedisApi = new EnedisHttpApi(config, gson, httpClient);
+
+ EnedisHttpApi api = new EnedisHttpApi(config, gson, httpClient);
+ this.enedisApi = api;
scheduler.submit(() -> {
try {
- enedisApi.initialize();
+ api.initialize();
updateStatus(ThingStatus.ONLINE);
- PrmInfo prmInfo = enedisApi.getPrmInfo();
+ PrmInfo prmInfo = api.getPrmInfo();
updateProperties(Map.of(USER_ID, prmInfo.customerId, PUISSANCE,
prmInfo.contractInfo.subscribedPower, PRM_ID, prmInfo.prmId));
@@ -547,52 +539,53 @@ public class LinkyHandler extends BaseThingHandler {
return null;
}
- meterReading.WeekValue = new IntervalReading[208];
- meterReading.MonthValue = new IntervalReading[48];
- meterReading.YearValue = new IntervalReading[4];
+ if (meterReading != null) {
+ meterReading.weekValue = new IntervalReading[208];
+ meterReading.monthValue = new IntervalReading[48];
+ meterReading.yearValue = new IntervalReading[4];
- for (int idx = 0; idx < 208; idx++) {
- meterReading.WeekValue[idx] = new IntervalReading();
- }
- for (int idx = 0; idx < 48; idx++) {
- meterReading.MonthValue[idx] = new IntervalReading();
- }
- for (int idx = 0; idx < 4; idx++) {
- meterReading.YearValue[idx] = new IntervalReading();
- }
+ for (int idx = 0; idx < 208; idx++) {
+ meterReading.weekValue[idx] = new IntervalReading();
+ }
+ for (int idx = 0; idx < 48; idx++) {
+ meterReading.monthValue[idx] = new IntervalReading();
+ }
+ for (int idx = 0; idx < 4; idx++) {
+ meterReading.yearValue[idx] = new IntervalReading();
+ }
- int size = meterReading.intervalReading.length;
- int baseYear = meterReading.intervalReading[0].date.getYear();
+ int size = meterReading.dayValue.length;
+ int baseYear = meterReading.dayValue[0].date.getYear();
- for (int idx = 0; idx < size; idx++) {
- IntervalReading ir = meterReading.intervalReading[idx];
- LocalDate dt = ir.date;
- double value = ir.value;
+ for (int idx = 0; idx < size; idx++) {
+ IntervalReading ir = meterReading.dayValue[idx];
+ LocalDateTime dt = ir.date;
+ double value = ir.value;
- int idxYear = dt.getYear() - baseYear;
+ int idxYear = dt.getYear() - baseYear;
- int dayOfYear = dt.getDayOfYear();
- int week = (dayOfYear / 7) + 1;
- int month = dt.getMonthValue();
+ int dayOfYear = dt.getDayOfYear();
+ int week = ((dayOfYear - 1) / 7) + 1;
+ int month = dt.getMonthValue();
- int idxMonth = (idxYear * 12) + month;
- int idxWeek = (idxYear * 52) + week;
+ int idxMonth = (idxYear * 12) + month;
+ int idxWeek = (idxYear * 52) + week;
- meterReading.WeekValue[idxWeek].value += value;
- meterReading.MonthValue[idxMonth].value += value;
- meterReading.YearValue[idxYear].value += value;
+ meterReading.weekValue[idxWeek].value += value;
+ meterReading.monthValue[idxMonth].value += value;
+ meterReading.yearValue[idxYear].value += value;
+ }
}
return meterReading;
}
private void checkData(@Nullable MeterReading meterReading) throws LinkyException {
- if (meterReading.intervalReading.length == 0) {
- throw new LinkyException("Invalid meterReading data: no day period");
+ if (meterReading != null) {
+ if (meterReading.dayValue.length == 0) {
+ throw new LinkyException("Invalid meterReading data: no day period");
+ }
}
- // if (meterReading.intervalReading.length != 1095) {
- // throw new LinkyException("Imcomplete meterReading data < 1095 days");
- // }
}
/*
From 4a19a1d243caebd3b0d3cc8306a8a0a8e279b399 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 2 Feb 2024 09:49:52 +0100
Subject: [PATCH 011/135] review null annotation and null pointer warning
Signed-off-by: Laurent ARNAL
---
.../org/openhab/binding/linky/internal/dto/TempoResponse.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
index f799ef7f391..0eede845b6d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
@@ -26,5 +26,4 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
public class TempoResponse extends HashMap {
@java.io.Serial
private static final long serialVersionUID = 362498820763181264L;
-
}
From 7a4a9969d2f9a96cbdee5600b9ffe52a21d53ce7 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 2 Feb 2024 10:44:35 +0100
Subject: [PATCH 012/135] fix code violations
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyAccountHandler.java | 2 +-
.../linky/internal/LinkyAuthService.java | 4 +-
.../linky/internal/LinkyAuthServlet.java | 41 +++++++++-------
.../linky/internal/LinkyHandlerFactory.java | 10 ++--
.../linky/internal/dto/AddressInfo.java | 2 +-
.../binding/linky/internal/dto/AuthData.java | 48 -------------------
.../linky/internal/dto/AuthResult.java | 24 ----------
.../linky/internal/handler/LinkyHandler.java | 3 --
8 files changed, 31 insertions(+), 103 deletions(-)
delete mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java
delete mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthResult.java
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
index bc60528b201..4cffdbbf2b2 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
@@ -33,7 +33,7 @@ public interface LinkyAccountHandler {
* @param reqCode The unique code passed by Smartthings to obtain the refresh and access tokens
* @return returns the name of the Smartthings user that is authorized
*/
- String authorize(String redirectUrl, String reqCode);
+ String authorize(String redirectUrl, String reqCode) throws LinkyException;
/**
* Formats the Url to use to call Smartthings to authorize the application.
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
index 237faa3c3f4..ec1a52f6010 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
@@ -113,13 +113,13 @@ public class LinkyAuthService {
* @param code The Linky returned code value
* @return returns the name of the Linky user that is authorized
*/
- public String authorize(String servletBaseURL, String state, String code) {
+ public String authorize(String servletBaseURL, String state, String code) throws LinkyException {
LinkyAccountHandler accountHandler = getLinkyAccountHandler();
if (accountHandler == null) {
logger.debug(
"Linky redirected with state '{}' but no matching bridge was found. Possible bridge has been removed.",
state);
- throw new RuntimeException(ERROR_UKNOWN_BRIDGE);
+ throw new LinkyException(ERROR_UKNOWN_BRIDGE);
} else {
return accountHandler.authorize(servletBaseURL, code);
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
index 50fc4fab286..41635fff937 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -82,29 +82,36 @@ public class LinkyAuthServlet extends HttpServlet {
String servletBaseURLSecure = servletBaseURL;
- handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString());
+ try {
+ handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString());
- LinkyAccountHandler accountHandler = linkyAuthService.getLinkyAccountHandler();
+ LinkyAccountHandler accountHandler = linkyAuthService.getLinkyAccountHandler();
- resp.setContentType(CONTENT_TYPE);
+ resp.setContentType(CONTENT_TYPE);
- StringBuffer optionBuffer = new StringBuffer();
+ StringBuffer optionBuffer = new StringBuffer();
- if (accountHandler != null) {
- String[] prmIds = accountHandler.getAllPrmId();
- for (String prmId : prmIds) {
- optionBuffer.append("" + prmId + " ");
+ if (accountHandler != null) {
+ String[] prmIds = accountHandler.getAllPrmId();
+ for (String prmId : prmIds) {
+ optionBuffer.append("" + prmId + " ");
+ }
}
- }
- replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString());
- replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
- replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK");
- if (accountHandler != null) {
- replaceMap.put(KEY_AUTHORIZE_URI, accountHandler.formatAuthorizationUrl(servletBaseURLSecure));
+ replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString());
+ replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
+ replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK");
+ if (accountHandler != null) {
+ replaceMap.put(KEY_AUTHORIZE_URI, accountHandler.formatAuthorizationUrl(servletBaseURLSecure));
+ }
+ resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
+ resp.getWriter().close();
+ } catch (LinkyException ex) {
+ resp.setContentType(CONTENT_TYPE);
+ replaceMap.put(KEY_ERROR, "Error during request handling : " + ex.getMessage());
+ resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
+ resp.getWriter().close();
}
- resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
- resp.getWriter().close();
}
/**
@@ -118,7 +125,7 @@ public class LinkyAuthServlet extends HttpServlet {
* @param queryString the query part of the GET request this servlet is processing
*/
private void handleLinkyRedirect(Map replaceMap, String servletBaseURL,
- @Nullable String queryString) {
+ @Nullable String queryString) throws LinkyException {
replaceMap.put(KEY_AUTHORIZED_USER, "");
replaceMap.put(KEY_ERROR, "");
replaceMap.put(KEY_PAGE_REFRESH, "");
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 5fa56ed77e8..3dea43c16f9 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -160,7 +160,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
-
if (supportsThingType(thing.getThingTypeUID())) {
LinkyHandler handler = new LinkyHandler(thing, localeProvider, gson, httpClient);
return handler;
@@ -190,7 +189,7 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
}
@Override
- public String authorize(String redirectUri, String reqCode) {
+ public String authorize(String redirectUri, String reqCode) throws LinkyException {
// Will work only in case of direct oAuth2 authentification to enedis
// this is not the case in v1 as we go trough MyElectricalData
@@ -208,9 +207,9 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
return user;
} catch (RuntimeException | OAuthException | IOException e) {
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
- throw new RuntimeException(e.getMessage(), e);
+ throw new LinkyException("Error during oAuth authorize :" + e.getMessage(), e);
} catch (final OAuthResponseException e) {
- throw new RuntimeException(e.getMessage(), e);
+ throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
}
}
// Fallback for MyElectricalData
@@ -222,7 +221,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
Collection col = this.thingRegistry.getAll();
for (Thing thing : col) {
if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
-
Configuration config = thing.getConfiguration();
String prmId = (String) config.get("prmId");
@@ -281,12 +279,10 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
List result = new ArrayList();
for (Thing thing : col) {
if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
-
Configuration config = thing.getConfiguration();
String prmId = (String) config.get("prmId");
result.add(prmId);
-
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
index 54f9b19af81..b748c17176d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
@@ -28,7 +28,7 @@ public class AddressInfo {
public String locality;
@SerializedName("postalCode")
- public String postal_code;
+ public String postalCode;
@SerializedName("insee_code")
public String inseeCode;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java
deleted file mode 100644
index 27eddb18695..00000000000
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Copyright (c) 2010-2024 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.linky.internal.dto;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link AuthData} holds authentication information
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@NonNullByDefault
-public class AuthData {
- public class AuthDataCallBack {
- public class NameValuePair {
- public @Nullable String name;
- public @Nullable Object value;
-
- public @Nullable String valueAsString() {
- return (value instanceof String stringValue) ? stringValue : null;
- }
- }
-
- public @Nullable String type;
-
- public List output = List.of();
- public List input = List.of();
- }
-
- public @Nullable String authId;
- public @Nullable String template;
- public @Nullable String stage;
- public @Nullable String header;
- public List callbacks = List.of();
-}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthResult.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthResult.java
deleted file mode 100644
index 8339f1436ae..00000000000
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthResult.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (c) 2010-2024 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.linky.internal.dto;
-
-/**
- * The {@link AuthResult} holds informations about the ongoing authentication process
- *
- * @author Gaël L'hopital - Initial contribution
- */
-
-public class AuthResult {
- public String successUrl;
- public String tokenId;
-}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 29acd0ad837..f512181464b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -150,7 +150,6 @@ public class LinkyHandler extends BaseThingHandler {
LinkyConfiguration config = getConfigAs(LinkyConfiguration.class);
if (config.seemsValid()) {
-
EnedisHttpApi api = new EnedisHttpApi(config, gson, httpClient);
this.enedisApi = api;
@@ -378,7 +377,6 @@ public class LinkyHandler extends BaseThingHandler {
// All values in the same month
MeterReading meterReading = getConsumptionData(startDay, endDay.plusDays(1));
if (meterReading != null) {
-
IntervalReading[] days = meterReading.dayValue;
int size = days.length;
@@ -647,7 +645,6 @@ public class LinkyHandler extends BaseThingHandler {
*/
private void logData(IntervalReading[] ivArray, String title, DateTimeFormatter dateTimeFormatter, Target target) {
-
if (logger.isDebugEnabled()) {
int size = ivArray.length;
From cc827a01665a74fc8aae39ad46513704293c30a2 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 29 Mar 2024 13:29:23 +0100
Subject: [PATCH 013/135] fix pull request
Signed-off-by: Laurent ARNAL
---
.../src/main/feature/feature.xml | 1 -
.../linky/internal/LinkyAccountHandler.java | 6 +-
.../linky/internal/LinkyAuthService.java | 2 +-
.../linky/internal/LinkyAuthServlet.java | 14 +-
.../linky/internal/LinkyHandlerFactory.java | 14 +-
.../linky/internal/api/EnedisHttpApi.java | 9 +-
.../linky/internal/dto/AddressInfo.java | 3 +-
.../linky/internal/dto/ContactInfo.java | 3 +-
.../binding/linky/internal/dto/Contracts.java | 3 +-
.../linky/internal/dto/IntervalReading.java | 4 -
.../linky/internal/dto/TempoResponse.java | 2 -
.../src/main/resources/templates/index.html | 428 +++++++++---------
12 files changed, 240 insertions(+), 249 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/feature/feature.xml b/bundles/org.openhab.binding.linky/src/main/feature/feature.xml
index 16185d9fffd..68373fd3f5f 100644
--- a/bundles/org.openhab.binding.linky/src/main/feature/feature.xml
+++ b/bundles/org.openhab.binding.linky/src/main/feature/feature.xml
@@ -7,6 +7,5 @@
openhab-core-auth-oauth2client
mvn:org.jsoup/jsoup/1.14.3
mvn:org.openhab.addons.bundles/org.openhab.binding.linky/${project.version}
-
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
index 4cffdbbf2b2..460f52ff539 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
@@ -12,10 +12,12 @@
*/
package org.openhab.binding.linky.internal;
+import java.util.List;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
- * @author Gaël L'hopital - Initial contribution *
+ * @author Gaël L'hopital - Initial contribution
* @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
@NonNullByDefault
@@ -43,5 +45,5 @@ public interface LinkyAccountHandler {
*/
String formatAuthorizationUrl(String redirectUri);
- String[] getAllPrmId();
+ List getAllPrmId();
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
index ec1a52f6010..012a32d1204 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
@@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory;
/**
*
- * @author Gaël L'hopital - Initial contribution *
+ * @author Gaël L'hopital - Initial contribution
* @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
@Component(service = LinkyAuthService.class, configurationPid = "binding.internal.authService")
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
index 41635fff937..7703521b36a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -15,6 +15,7 @@ package org.openhab.binding.linky.internal;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -36,7 +37,7 @@ import org.slf4j.LoggerFactory;
* The xxx manages the authorization with the Linky Web API. The servlet implements the
* Authorization Code flow and saves the resulting refreshToken with the bridge.
*
- * @author Gaël L'hopital - Initial contribution *
+ * @author Gaël L'hopital - Initial contribution
* @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
@NonNullByDefault
@@ -74,13 +75,10 @@ public class LinkyAuthServlet extends HttpServlet {
logger.debug("Linky auth callback servlet received GET request {}.", req.getRequestURI());
final Map replaceMap = new HashMap<>();
- String servletBaseURL = "";
- StringBuffer requestURL = req.getRequestURL();
- if (requestURL != null) {
- servletBaseURL = requestURL.toString();
- }
+ StringBuffer requestUrl = req.getRequestURL();
+ String servletBaseUrl = requestUrl != null ? requestUrl.toString() : "";
- String servletBaseURLSecure = servletBaseURL;
+ String servletBaseURLSecure = servletBaseUrl;
try {
handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString());
@@ -92,7 +90,7 @@ public class LinkyAuthServlet extends HttpServlet {
StringBuffer optionBuffer = new StringBuffer();
if (accountHandler != null) {
- String[] prmIds = accountHandler.getAllPrmId();
+ List prmIds = accountHandler.getAllPrmId();
for (String prmId : prmIds) {
optionBuffer.append("" + prmId + " ");
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 3dea43c16f9..92f6f5d271b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -54,6 +54,7 @@ import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
@@ -95,8 +96,7 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
.atStartOfDay();
}
})
-
- .create();
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
private final LocaleProvider localeProvider;
private final HttpClient httpClient;
@@ -127,9 +127,9 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
this.authService = authService;
- this.oAuthService = oAuthFactory.createOAuthClientService("Linky", LinkyBindingConstants.LINKY_API_TOKEN_URL,
- LinkyBindingConstants.LINKY_AUTHORIZE_URL, clientId, clientSecret, LinkyBindingConstants.LINKY_SCOPES,
- true);
+ this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
+ LinkyBindingConstants.LINKY_API_TOKEN_URL, LinkyBindingConstants.LINKY_AUTHORIZE_URL, clientId,
+ clientSecret, LinkyBindingConstants.LINKY_SCOPES, true);
this.authService.setLinkyAccountHandler(this);
}
@@ -274,7 +274,7 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
}
@Override
- public String[] getAllPrmId() {
+ public List getAllPrmId() {
Collection col = this.thingRegistry.getAll();
List result = new ArrayList();
for (Thing thing : col) {
@@ -286,6 +286,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
}
}
- return result.toArray(new String[0]);
+ return result;
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index f6cbd3b9ba9..26fb3df629b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -162,7 +162,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(String.format("%s/%s/cache", CONTRACT_URL, config.prmId));
+ String data = getData("%s/%s/cache".formatted(CONTRACT_URL, config.prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", CONTRACT_URL);
}
@@ -182,7 +182,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(String.format("%s/%s/cache", ADDRESS_URL, config.prmId));
+ String data = getData("%s/%s/cache".formatted(ADDRESS_URL, config.prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", ADDRESS_URL);
}
@@ -202,7 +202,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(String.format("%s/%s/cache", IDENTITY_URL, config.prmId));
+ String data = getData("%s/%s/cache".formatted(IDENTITY_URL, config.prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", IDENTITY_URL);
}
@@ -222,7 +222,8 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(String.format("%s/%s/cache", CONTACT_URL, config.prmId));
+ String data = getData("%s/%s/cache".formatted(CONTACT_URL, config.prmId));
+
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", CONTACT_URL);
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
index b748c17176d..7c7f4de0e78 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
@@ -19,8 +19,7 @@ import com.google.gson.annotations.SerializedName;
/**
* The {@link UserInfo} holds informations about energy delivery point
*
- * @author Gaël L'hopital - Initial contribution
- * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ * @author Laurent Arnal - Initial contribution
*/
public class AddressInfo {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java
index 94c40c43253..83cf9f89c86 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java
@@ -17,8 +17,7 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
/**
* The {@link UserInfo} holds informations about energy delivery point
*
- * @author Gaël L'hopital - Initial contribution
- * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ * @author Laurent Arnal - Initial contribution
*/
public class ContactInfo {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
index c9bf59b0022..89dae9f1d2b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
@@ -19,8 +19,7 @@ import com.google.gson.annotations.SerializedName;
/**
* The {@link UserInfo} holds informations about energy delivery point
*
- * @author Gaël L'hopital - Initial contribution
- * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ * @author Laurent Arnal - Initial contribution
*/
public class Contracts {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
index bcfadd57069..ac03f412546 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
@@ -16,8 +16,6 @@ import java.time.LocalDateTime;
import org.eclipse.jetty.jaas.spi.UserInfo;
-import com.google.gson.annotations.SerializedName;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -26,9 +24,7 @@ import com.google.gson.annotations.SerializedName;
*/
public class IntervalReading {
- @SerializedName("value")
public double value;
- @SerializedName("date")
public LocalDateTime date;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
index 0eede845b6d..bbc3b4d39ac 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
@@ -14,8 +14,6 @@ package org.openhab.binding.linky.internal.dto;
import java.util.HashMap;
-import org.eclipse.jetty.jaas.spi.UserInfo;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html b/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html
index fa66b86f72c..199cfc7a496 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html
+++ b/bundles/org.openhab.binding.linky/src/main/resources/templates/index.html
@@ -1,214 +1,214 @@
-
-
-
-
-
-${pageRefresh}
-Authorize openHAB Linky binding for Enedis
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Authorize openHAB Linky binding for Enedis
- On this page you can authorize the openHAB Linky binding to access your Enedis account.
-
- The redirect URI to use with Enedis for this openHAB installation is
- ${redirectUri}
-
- ${error} ${authorizedUser}
-
-
- This process need to be done for each electrical meter you have (each prmId).
- This is a two step process:
-
- First you will need to do a consent on the enedis Site.
- For this, click on the Authorize button.
- You will be redirect to the enedis site. Login as usual with your userName / Password.
- You will then be redirect to a consentment page.
- You will have to select which prmId you would like to give the consent, click the checkbox, and then click on the validate button.
- After this, you will be redirect to myelectricaldata information page.
-
-
- Second, come back to the /connectlinky page.
- If you have multiple prm, select the PrmId in the combobox you want to authorize.v
- Then click the Retrieve token button.
- It will fill you linky thing configuration with the token.
-
-
-
-
-
-
-
-
Connect to Enedis:
-
-
-
-
- Please select your prmId :
-
- ${prmId.Option}
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+${pageRefresh}
+Authorize openHAB Linky binding for Enedis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Authorize openHAB Linky binding for Enedis
+ On this page you can authorize the openHAB Linky binding to access your Enedis account.
+
+ The redirect URI to use with Enedis for this openHAB installation is
+ ${redirectUri}
+
+ ${error} ${authorizedUser}
+
+
+ This process need to be done for each electrical meter you have (each prmId).
+ This is a two step process:
+
+ First you will need to do a consent on the enedis Site.
+ For this, click on the Authorize button.
+ You will be redirect to the enedis site. Login as usual with your userName / Password.
+ You will then be redirect to a consentment page.
+ You will have to select which prmId you would like to give the consent, click the checkbox, and then click on the validate button.
+ After this, you will be redirect to myelectricaldata information page.
+
+
+ Second, come back to the /connectlinky page.
+ If you have multiple prm, select the PrmId in the combobox you want to authorize.v
+ Then click the Retrieve token button.
+ It will fill you linky thing configuration with the token.
+
+
+
+
+
+
+
+
Connect to Enedis:
+
+
+
+
+ Please select your prmId :
+
+ ${prmId.Option}
+
+
+
+
+
+
+
+
+
From 73d36c30a728499d6e3565d3fda31eef606b1d71 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 17 Apr 2024 15:38:29 +0200
Subject: [PATCH 014/135] Start refactoring AuthService to handle direct Enedis
service call and cleaner code
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyAccountHandler.java | 2 +-
.../linky/internal/LinkyAuthService.java | 177 +++++++++++++-----
.../linky/internal/LinkyAuthServlet.java | 14 +-
.../linky/internal/LinkyBindingConstants.java | 19 +-
.../linky/internal/LinkyConfiguration.java | 2 +
.../linky/internal/LinkyHandlerFactory.java | 156 +--------------
.../resources/OH-INF/thing/thing-types.xml | 10 +
7 files changed, 168 insertions(+), 212 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
index 460f52ff539..ca536e3478b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
@@ -35,7 +35,7 @@ public interface LinkyAccountHandler {
* @param reqCode The unique code passed by Smartthings to obtain the refresh and access tokens
* @return returns the name of the Smartthings user that is authorized
*/
- String authorize(String redirectUrl, String reqCode) throws LinkyException;
+ String authorize(String redirectUrl, String reqState, String reqCode) throws LinkyException;
/**
* Formats the Url to use to call Smartthings to authorize the application.
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
index 012a32d1204..96514cf055d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
@@ -17,18 +17,22 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Hashtable;
-import java.util.Map;
+import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
+import org.openhab.core.auth.client.oauth2.OAuthClientService;
+import org.openhab.core.auth.client.oauth2.OAuthException;
+import org.openhab.core.auth.client.oauth2.OAuthFactory;
+import org.openhab.core.auth.client.oauth2.OAuthResponseException;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.HttpService;
@@ -41,28 +45,38 @@ import org.slf4j.LoggerFactory;
* @author Gaël L'hopital - Initial contribution
* @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
-@Component(service = LinkyAuthService.class, configurationPid = "binding.internal.authService")
@NonNullByDefault
-public class LinkyAuthService {
+public class LinkyAuthService implements LinkyAccountHandler {
private static final String TEMPLATE_PATH = "templates/";
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
- private static final String ERROR_UKNOWN_BRIDGE = "Returned 'state' by doesn't match any Bridges. Has the bridge been removed?";
private final Logger logger = LoggerFactory.getLogger(LinkyAuthService.class);
private @NonNullByDefault({}) HttpService httpService;
private @NonNullByDefault({}) BundleContext bundleContext;
- private @Nullable LinkyAccountHandler accountHandler;
- @Activate
- protected void activate(ComponentContext componentContext, Map properties) {
+ private final boolean oAuthSupport = true;
+ private @Nullable OAuthClientService oAuthService;
+
+ public LinkyAuthService(final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
+ final @Reference ComponentContext componentContext) {
+
+ this.httpService = httpService;
+
+ this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
+ LinkyBindingConstants.ENEDIS_API_TOKEN_URL, LinkyBindingConstants.ENEDIS_AUTH_AUTHORIZE_URL,
+ LinkyBindingConstants.clientId, LinkyBindingConstants.clientSecret, LinkyBindingConstants.LINKY_SCOPES,
+ true);
+
try {
bundleContext = componentContext.getBundleContext();
+
httpService.registerServlet(LinkyBindingConstants.LINKY_ALIAS, createServlet(), new Hashtable<>(),
httpService.createDefaultHttpContext());
httpService.registerResources(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS,
"web", null);
+
} catch (NamespaceException | ServletException | IOException e) {
logger.warn("Error during linky servlet startup", e);
}
@@ -104,54 +118,123 @@ public class LinkyAuthService {
}
}
- /**
- * Call with Linky redirect uri returned State and Code values to get the refresh and access tokens and persist
- * these values
- *
- * @param servletBaseURL the servlet base, which will be the Linky redirect url
- * @param state The Linky returned state value
- * @param code The Linky returned code value
- * @return returns the name of the Linky user that is authorized
- */
- public String authorize(String servletBaseURL, String state, String code) throws LinkyException {
- LinkyAccountHandler accountHandler = getLinkyAccountHandler();
- if (accountHandler == null) {
- logger.debug(
- "Linky redirected with state '{}' but no matching bridge was found. Possible bridge has been removed.",
- state);
- throw new LinkyException(ERROR_UKNOWN_BRIDGE);
- } else {
- return accountHandler.authorize(servletBaseURL, code);
+ @Override
+ public String authorize(String redirectUri, String reqState, String reqCode) throws LinkyException {
+ // Will work only in case of direct oAuth2 authentification to enedis
+ // this is not the case in v1 as we go trough MyElectricalData
+
+ if (oAuthSupport) {
+ try {
+ OAuthClientService oAuthService = this.oAuthService;
+ if (oAuthService == null) {
+ throw new OAuthException("OAuth service is not initialized");
+ }
+ logger.debug("Make call to Enedis to get access token.");
+ final AccessTokenResponse credentials = oAuthService
+ .getAccessTokenByClientCredentials(LinkyBindingConstants.LINKY_SCOPES);
+
+ final String user = updateProperties(credentials);
+ logger.debug("Authorized for user: {}", user);
+ return user;
+ } catch (RuntimeException | OAuthException | IOException e) {
+ // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
+ throw new LinkyException("Error during oAuth authorize :" + e.getMessage(), e);
+ } catch (final OAuthResponseException e) {
+ throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
+ }
+ }
+ // Fallback for MyElectricalData
+ else {
+ /*
+ * String token = EnedisHttpApi.getToken(httpClient, LinkyBindingConstants.clientId, reqCode);
+ *
+ * logger.debug("token: {}", token);
+ *
+ * Collection col = this.thingRegistry.getAll();
+ * for (Thing thing : col) {
+ * if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
+ * Configuration config = thing.getConfiguration();
+ * String prmId = (String) config.get("prmId");
+ *
+ * if (!prmId.equals(reqCode)) {
+ * continue;
+ * }
+ *
+ * config.put("token", token);
+ * LinkyHandler handler = (LinkyHandler) thing.getHandler();
+ * if (handler != null) {
+ * handler.saveConfiguration(config);
+ * }
+ * }
+ * }
+ *
+ * return token;
+ */
+
+ return "";
}
}
- /**
- * @param listener Adds the given handler
- */
- public void setLinkyAccountHandler(LinkyAccountHandler accountHandler) {
- this.accountHandler = accountHandler;
+ @Override
+ public boolean isAuthorized() {
+ final AccessTokenResponse accessTokenResponse = getAccessTokenResponse();
+
+ return accessTokenResponse != null && accessTokenResponse.getAccessToken() != null
+ && accessTokenResponse.getRefreshToken() != null;
}
- /**
- * @param handler Removes the given handler
- */
- public void unsetLinkyAccountHandler(LinkyAccountHandler accountHandler) {
- this.accountHandler = null;
+ private @Nullable AccessTokenResponse getAccessTokenResponse() {
+ try {
+ OAuthClientService oAuthService = this.oAuthService;
+ return oAuthService == null ? null : oAuthService.getAccessTokenResponse();
+ } catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
+ logger.debug("Exception checking authorization: ", e);
+ return null;
+ }
}
- /**
- * @param listener Adds the given handler
- */
- public @Nullable LinkyAccountHandler getLinkyAccountHandler() {
- return this.accountHandler;
+ private String updateProperties(AccessTokenResponse credentials) {
+ return credentials.getAccessToken();
}
- @Reference
- protected void setHttpService(HttpService httpService) {
- this.httpService = httpService;
+ @Override
+ public String formatAuthorizationUrl(String redirectUri) {
+ // Will work only in case of direct oAuth2 authentification to enedis
+ // this is not the case in v1 as we go trough MyElectricalData
+ if (oAuthSupport) {
+ try {
+ OAuthClientService oAuthService = this.oAuthService;
+ if (oAuthService == null) {
+ throw new OAuthException("OAuth service is not initialized");
+ }
+
+ String uri = oAuthService.getAuthorizationUrl(redirectUri, null, "Linky");
+ return uri;
+ } catch (final OAuthException e) {
+ logger.debug("Error constructing AuthorizationUrl: ", e);
+ return "";
+ }
+ }
+ return "";
}
- protected void unsetHttpService(HttpService httpService) {
- this.httpService = null;
+ @Override
+ public List getAllPrmId() {
+ List result = new ArrayList();
+ /*
+ * Collection col = this.thingRegistry.getAll();
+ *
+ * for (Thing thing : col) {
+ * if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
+ * Configuration config = thing.getConfiguration();
+ *
+ * String prmId = (String) config.get("prmId");
+ * result.add(prmId);
+ * }
+ * }
+ */
+
+ return result;
}
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
index 7703521b36a..a37b6239cb4 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -83,25 +83,19 @@ public class LinkyAuthServlet extends HttpServlet {
try {
handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString());
- LinkyAccountHandler accountHandler = linkyAuthService.getLinkyAccountHandler();
-
resp.setContentType(CONTENT_TYPE);
StringBuffer optionBuffer = new StringBuffer();
- if (accountHandler != null) {
- List prmIds = accountHandler.getAllPrmId();
- for (String prmId : prmIds) {
- optionBuffer.append("" + prmId + " ");
- }
+ List prmIds = linkyAuthService.getAllPrmId();
+ for (String prmId : prmIds) {
+ optionBuffer.append("" + prmId + " ");
}
replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString());
replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK");
- if (accountHandler != null) {
- replaceMap.put(KEY_AUTHORIZE_URI, accountHandler.formatAuthorizationUrl(servletBaseURLSecure));
- }
+ replaceMap.put(KEY_AUTHORIZE_URI, linkyAuthService.formatAuthorizationUrl(servletBaseURLSecure));
resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
resp.getWriter().close();
} catch (LinkyException ex) {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 71709ab40b6..c4c3b609407 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -30,6 +30,9 @@ public class LinkyBindingConstants {
public static final String BINDING_ID = "linky";
+ public static final String clientId = "_88uJnEjEs_IMf4bjGZJV6gGxYga";
+ public static final String clientSecret = "6lsPfCmu0fEXuKYy3e0e6w8ydIca";
+
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_LINKY = new ThingTypeUID(BINDING_ID, "linky");
@@ -69,16 +72,20 @@ public class LinkyBindingConstants {
/**
* Smartthings scopes needed by this binding to work.
*/
- public static final String LINKY_SCOPES = Stream.of("r:devices:*", "w:devices:*", "x:devices:*", "r:hubs:*",
- "r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*",
- "r:installedapps", "w:installedapps").collect(Collectors.joining(" "));
+ public static final String LINKY_SCOPES = Stream.of("am_application_scope", "default")
+ .collect(Collectors.joining(" "));
+ // "r:devices:*", "w:devices:*", "x:devices:*", "r:hubs:*",
+ // "r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*",
+ // "r:installedapps", "w:installedapps"
// List of Linky services related urls, information
public static final String LINKY_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "v1/oauth2/authorize";
public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "token";
- public static final String ENEDIS_ACCOUNT_URL = "https://mon-compte-particulier.enedis.fr/";
- public static final String ENEDIS_AUTHORIZE_URL = ENEDIS_ACCOUNT_URL + "dataconnect/v1/oauth2/authorize";
- public static final String ENEDIS_API_TOKEN_URL = ENEDIS_ACCOUNT_URL + "token";
+ public static final String ENEDIS_API_ACCOUNT_URL = "https://ext.prod-sandbox.api.enedis.fr/";
+ public static final String ENEDIS_API_TOKEN_URL = ENEDIS_API_ACCOUNT_URL + "oauth2/v3/token";
+
+ public static final String ENEDIS_AUTH_ACCOUNT_URL = "https://mon-compte-particulier.enedis.fr/";
+ public static final String ENEDIS_AUTH_AUTHORIZE_URL = ENEDIS_AUTH_ACCOUNT_URL + "dataconnect/v1/oauth2/authorize";
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
index 5847b113b72..6e16317d4a4 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
@@ -25,6 +25,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
public class LinkyConfiguration {
public String token = "";
public String prmId = "";
+ public String clientId = "";
+ public String clientSecret = "";
public boolean seemsValid() {
return !prmId.isBlank();
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 92f6f5d271b..33ae98a903e 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -12,16 +12,12 @@
*/
package org.openhab.binding.linky.internal;
-import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
@@ -30,19 +26,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.openhab.binding.linky.internal.api.EnedisHttpApi;
import org.openhab.binding.linky.internal.handler.LinkyHandler;
-import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
-import org.openhab.core.auth.client.oauth2.OAuthClientService;
-import org.openhab.core.auth.client.oauth2.OAuthException;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
-import org.openhab.core.auth.client.oauth2.OAuthResponseException;
-import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.io.net.http.TrustAllTrustManager;
import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
@@ -51,6 +40,7 @@ import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -67,7 +57,7 @@ import com.google.gson.JsonDeserializer;
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky")
-public class LinkyHandlerFactory extends BaseThingHandlerFactory implements LinkyAccountHandler {
+public class LinkyHandlerFactory extends BaseThingHandlerFactory {
private static final DateTimeFormatter LINKY_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
private static final DateTimeFormatter LINKY_LOCALDATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd");
private static final DateTimeFormatter LINKY_LOCALDATETIME_FORMATTER = DateTimeFormatter
@@ -75,9 +65,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
private static final int REQUEST_BUFFER_SIZE = 8000;
private static final int RESPONSE_BUFFER_SIZE = 200000;
- private final String clientId = "e551937c-5250-48bc-b4a6-2323af68db92";
- private final String clientSecret = "";
-
private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
private final Gson gson = new GsonBuilder()
.registerTypeAdapter(ZonedDateTime.class,
@@ -100,43 +87,36 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
private final LocaleProvider localeProvider;
private final HttpClient httpClient;
- private final LinkyAuthService authService;
- private final boolean oAuthSupport = false;
- private @Nullable OAuthClientService oAuthService;
- private final ThingRegistry thingRegistry;
@Activate
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
- final @Reference HttpClientFactory httpClientFactory, final @Reference LinkyAuthService authService,
- final @Reference OAuthFactory oAuthFactory, final @Reference ThingRegistry thingRegistry) {
+ final @Reference HttpClientFactory httpClientFactory, final @Reference OAuthFactory oAuthFactory,
+ final @Reference HttpService httpService, ComponentContext componentContext) {
this.localeProvider = localeProvider;
SslContextFactory sslContextFactory = new SslContextFactory.Client();
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
sslContextFactory.setSslContext(sslContext);
+
+ LinkyAuthService authService = new LinkyAuthService(oAuthFactory, httpService, componentContext);
+
} catch (NoSuchAlgorithmException e) {
logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
e);
} catch (KeyManagementException e) {
logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
}
- this.thingRegistry = thingRegistry;
this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
httpClient.setFollowRedirects(false);
httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
- this.authService = authService;
-
- this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
- LinkyBindingConstants.LINKY_API_TOKEN_URL, LinkyBindingConstants.LINKY_AUTHORIZE_URL, clientId,
- clientSecret, LinkyBindingConstants.LINKY_SCOPES, true);
- this.authService.setLinkyAccountHandler(this);
}
@Override
protected void activate(ComponentContext componentContext) {
super.activate(componentContext);
try {
+
httpClient.start();
} catch (Exception e) {
logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
@@ -168,124 +148,4 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
return null;
}
- // ===========================================================================
-
- @Override
- public boolean isAuthorized() {
- final AccessTokenResponse accessTokenResponse = getAccessTokenResponse();
-
- return accessTokenResponse != null && accessTokenResponse.getAccessToken() != null
- && accessTokenResponse.getRefreshToken() != null;
- }
-
- private @Nullable AccessTokenResponse getAccessTokenResponse() {
- try {
- OAuthClientService oAuthService = this.oAuthService;
- return oAuthService == null ? null : oAuthService.getAccessTokenResponse();
- } catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
- logger.debug("Exception checking authorization: ", e);
- return null;
- }
- }
-
- @Override
- public String authorize(String redirectUri, String reqCode) throws LinkyException {
- // Will work only in case of direct oAuth2 authentification to enedis
- // this is not the case in v1 as we go trough MyElectricalData
-
- if (oAuthSupport) {
- try {
- OAuthClientService oAuthService = this.oAuthService;
- if (oAuthService == null) {
- throw new OAuthException("OAuth service is not initialized");
- }
- logger.debug("Make call to Smartthings to get access token.");
- final AccessTokenResponse credentials = oAuthService.getAccessTokenResponseByAuthorizationCode(reqCode,
- redirectUri);
- final String user = updateProperties(credentials);
- logger.debug("Authorized for user: {}", user);
- return user;
- } catch (RuntimeException | OAuthException | IOException e) {
- // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
- throw new LinkyException("Error during oAuth authorize :" + e.getMessage(), e);
- } catch (final OAuthResponseException e) {
- throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
- }
- }
- // Fallback for MyElectricalData
- else {
- String token = EnedisHttpApi.getToken(httpClient, clientId, reqCode);
-
- logger.debug("token: {}", token);
-
- Collection col = this.thingRegistry.getAll();
- for (Thing thing : col) {
- if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
- Configuration config = thing.getConfiguration();
- String prmId = (String) config.get("prmId");
-
- if (!prmId.equals(reqCode)) {
- continue;
- }
-
- config.put("token", token);
- LinkyHandler handler = (LinkyHandler) thing.getHandler();
- if (handler != null) {
- handler.saveConfiguration(config);
- }
- }
- }
-
- return token;
- }
- }
-
- private String updateProperties(AccessTokenResponse credentials) {
- return "";
- }
-
- @Override
- public String formatAuthorizationUrl(String redirectUri) {
- // Will work only in case of direct oAuth2 authentification to enedis
- // this is not the case in v1 as we go trough MyElectricalData
- if (oAuthSupport) {
- try {
- OAuthClientService oAuthService = this.oAuthService;
- if (oAuthService == null) {
- throw new OAuthException("OAuth service is not initialized");
- }
-
- String uri = oAuthService.getAuthorizationUrl(redirectUri, null, "Linky");
- return uri;
- } catch (final OAuthException e) {
- logger.debug("Error constructing AuthorizationUrl: ", e);
- return "";
- }
- }
- // Fallback for MyElectricalData
- else {
- String uri = LinkyBindingConstants.ENEDIS_AUTHORIZE_URL;
- uri = uri + "?";
- uri = uri + "&client_id=" + clientId;
- uri = uri + "&duration=" + "P36M";
- uri = uri + "&response_type=" + "code";
- return uri;
- }
- }
-
- @Override
- public List getAllPrmId() {
- Collection col = this.thingRegistry.getAll();
- List result = new ArrayList();
- for (Thing thing : col) {
- if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
- Configuration config = thing.getConfiguration();
-
- String prmId = (String) config.get("prmId");
- result.add(prmId);
- }
- }
-
- return result;
- }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 43b60a1fb1d..d9bd18e4a86 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -35,6 +35,16 @@
Your Enedis token (can be left empty, use the connection page to automatically fill it
http://youopenhab/connectlinky)
+
+
+ clientId
+ Your Enedis clientId
+
+
+ clientSecret
+ Your Enedis clientSecret
+
+
From 214194e8954f0b2100011f5625fe857d6f22d7d2 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 17 Apr 2024 15:38:29 +0200
Subject: [PATCH 015/135] Start refactoring AuthService to handle direct Enedis
service call and cleaner code
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyAccountHandler.java | 49 ------
.../linky/internal/LinkyAuthService.java | 160 ++++++++----------
.../linky/internal/LinkyHandlerFactory.java | 14 +-
.../linky/internal/api/EnedisHttpApi.java | 22 +--
.../linky/internal/handler/LinkyHandler.java | 8 +-
5 files changed, 99 insertions(+), 154 deletions(-)
delete mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
deleted file mode 100644
index ca536e3478b..00000000000
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAccountHandler.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * Copyright (c) 2010-2024 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.linky.internal;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * @author Gaël L'hopital - Initial contribution
- * @author Laurent Arnal - Rewrite addon to use official dataconect API
- */
-@NonNullByDefault
-public interface LinkyAccountHandler {
-
- /**
- * @return Returns true if the Linky Bridge is authorized.
- */
- boolean isAuthorized();
-
- /**
- * Calls Smartthings Api to obtain refresh and access tokens and persist data with Thing.
- *
- * @param redirectUrl The redirect url Smartthings calls back to
- * @param reqCode The unique code passed by Smartthings to obtain the refresh and access tokens
- * @return returns the name of the Smartthings user that is authorized
- */
- String authorize(String redirectUrl, String reqState, String reqCode) throws LinkyException;
-
- /**
- * Formats the Url to use to call Smartthings to authorize the application.
- *
- * @param redirectUri The uri Smartthings will redirect back to
- * @return the formatted url that should be used to call Smartthings Web Api with
- */
- String formatAuthorizationUrl(String redirectUri);
-
- List getAllPrmId();
-}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
index 96514cf055d..446513b9669 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
@@ -18,6 +18,7 @@ import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
@@ -31,6 +32,9 @@ import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthException;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingRegistry;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Deactivate;
@@ -46,7 +50,7 @@ import org.slf4j.LoggerFactory;
* @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
@NonNullByDefault
-public class LinkyAuthService implements LinkyAccountHandler {
+public class LinkyAuthService {
private static final String TEMPLATE_PATH = "templates/";
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
@@ -56,14 +60,14 @@ public class LinkyAuthService implements LinkyAccountHandler {
private @NonNullByDefault({}) HttpService httpService;
private @NonNullByDefault({}) BundleContext bundleContext;
- private final boolean oAuthSupport = true;
- private @Nullable OAuthClientService oAuthService;
+ private OAuthClientService oAuthService;
+ private final ThingRegistry thingRegistry;
public LinkyAuthService(final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
- final @Reference ComponentContext componentContext) {
+ final @Reference ThingRegistry thingRegistry, final @Reference ComponentContext componentContext) {
this.httpService = httpService;
-
+ this.thingRegistry = thingRegistry;
this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
LinkyBindingConstants.ENEDIS_API_TOKEN_URL, LinkyBindingConstants.ENEDIS_AUTH_AUTHORIZE_URL,
LinkyBindingConstants.clientId, LinkyBindingConstants.clientSecret, LinkyBindingConstants.LINKY_SCOPES,
@@ -118,64 +122,54 @@ public class LinkyAuthService implements LinkyAccountHandler {
}
}
- @Override
public String authorize(String redirectUri, String reqState, String reqCode) throws LinkyException {
// Will work only in case of direct oAuth2 authentification to enedis
// this is not the case in v1 as we go trough MyElectricalData
- if (oAuthSupport) {
- try {
- OAuthClientService oAuthService = this.oAuthService;
- if (oAuthService == null) {
- throw new OAuthException("OAuth service is not initialized");
- }
- logger.debug("Make call to Enedis to get access token.");
- final AccessTokenResponse credentials = oAuthService
- .getAccessTokenByClientCredentials(LinkyBindingConstants.LINKY_SCOPES);
+ try {
- final String user = updateProperties(credentials);
- logger.debug("Authorized for user: {}", user);
- return user;
- } catch (RuntimeException | OAuthException | IOException e) {
- // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
- throw new LinkyException("Error during oAuth authorize :" + e.getMessage(), e);
- } catch (final OAuthResponseException e) {
- throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
- }
- }
- // Fallback for MyElectricalData
- else {
- /*
- * String token = EnedisHttpApi.getToken(httpClient, LinkyBindingConstants.clientId, reqCode);
- *
- * logger.debug("token: {}", token);
- *
- * Collection col = this.thingRegistry.getAll();
- * for (Thing thing : col) {
- * if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
- * Configuration config = thing.getConfiguration();
- * String prmId = (String) config.get("prmId");
- *
- * if (!prmId.equals(reqCode)) {
- * continue;
- * }
- *
- * config.put("token", token);
- * LinkyHandler handler = (LinkyHandler) thing.getHandler();
- * if (handler != null) {
- * handler.saveConfiguration(config);
- * }
- * }
- * }
- *
- * return token;
- */
+ logger.debug("Make call to Enedis to get access token.");
+ final AccessTokenResponse credentials = oAuthService
+ .getAccessTokenByClientCredentials(LinkyBindingConstants.LINKY_SCOPES);
- return "";
+ String accessToken = credentials.getAccessToken();
+
+ logger.debug("Acces token: {}", accessToken);
+ return accessToken;
+ } catch (RuntimeException | OAuthException | IOException e) {
+ // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
+ throw new LinkyException("Error during oAuth authorize :" + e.getMessage(), e);
+ } catch (final OAuthResponseException e) {
+ throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
}
+
+ /*
+ * String token = EnedisHttpApi.getToken(httpClient, LinkyBindingConstants.clientId, reqCode);
+ *
+ * logger.debug("token: {}", token);
+ *
+ * Collection col = this.thingRegistry.getAll();
+ * for (Thing thing : col) {
+ * if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
+ * Configuration config = thing.getConfiguration();
+ * String prmId = (String) config.get("prmId");
+ *
+ * if (!prmId.equals(reqCode)) {
+ * continue;
+ * }
+ *
+ * config.put("token", token);
+ * LinkyHandler handler = (LinkyHandler) thing.getHandler();
+ * if (handler != null) {
+ * handler.saveConfiguration(config);
+ * }
+ * }
+ * }
+ *
+ * return token;
+ */
}
- @Override
public boolean isAuthorized() {
final AccessTokenResponse accessTokenResponse = getAccessTokenResponse();
@@ -183,56 +177,46 @@ public class LinkyAuthService implements LinkyAccountHandler {
&& accessTokenResponse.getRefreshToken() != null;
}
+ public String getToken() {
+ AccessTokenResponse accesToken = getAccessTokenResponse();
+ return accesToken.getAccessToken();
+ }
+
private @Nullable AccessTokenResponse getAccessTokenResponse() {
try {
- OAuthClientService oAuthService = this.oAuthService;
- return oAuthService == null ? null : oAuthService.getAccessTokenResponse();
+ return oAuthService.getAccessTokenResponse();
} catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
logger.debug("Exception checking authorization: ", e);
return null;
}
}
- private String updateProperties(AccessTokenResponse credentials) {
- return credentials.getAccessToken();
- }
-
- @Override
public String formatAuthorizationUrl(String redirectUri) {
// Will work only in case of direct oAuth2 authentification to enedis
// this is not the case in v1 as we go trough MyElectricalData
- if (oAuthSupport) {
- try {
- OAuthClientService oAuthService = this.oAuthService;
- if (oAuthService == null) {
- throw new OAuthException("OAuth service is not initialized");
- }
-
- String uri = oAuthService.getAuthorizationUrl(redirectUri, null, "Linky");
- return uri;
- } catch (final OAuthException e) {
- logger.debug("Error constructing AuthorizationUrl: ", e);
- return "";
- }
+ try {
+ String uri = this.oAuthService.getAuthorizationUrl(redirectUri, LinkyBindingConstants.LINKY_SCOPES,
+ LinkyBindingConstants.BINDING_ID);
+ return uri;
+ } catch (final OAuthException e) {
+ logger.debug("Error constructing AuthorizationUrl: ", e);
+ return "";
}
- return "";
}
- @Override
public List getAllPrmId() {
List result = new ArrayList();
- /*
- * Collection col = this.thingRegistry.getAll();
- *
- * for (Thing thing : col) {
- * if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
- * Configuration config = thing.getConfiguration();
- *
- * String prmId = (String) config.get("prmId");
- * result.add(prmId);
- * }
- * }
- */
+
+ Collection col = this.thingRegistry.getAll();
+
+ for (Thing thing : col) {
+ if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
+ Configuration config = thing.getConfiguration();
+
+ String prmId = (String) config.get("prmId");
+ result.add(prmId);
+ }
+ }
return result;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 33ae98a903e..40fd4e6f55a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -32,6 +32,7 @@ import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.io.net.http.TrustAllTrustManager;
import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
@@ -66,6 +67,9 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
private static final int RESPONSE_BUFFER_SIZE = 200000;
private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
+
+ private LinkyAuthService authService;
+
private final Gson gson = new GsonBuilder()
.registerTypeAdapter(ZonedDateTime.class,
(JsonDeserializer) (json, type, jsonDeserializationContext) -> ZonedDateTime
@@ -91,16 +95,18 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
@Activate
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
final @Reference HttpClientFactory httpClientFactory, final @Reference OAuthFactory oAuthFactory,
- final @Reference HttpService httpService, ComponentContext componentContext) {
+ final @Reference HttpService httpService, final @Reference ThingRegistry thingRegistry,
+ ComponentContext componentContext) {
this.localeProvider = localeProvider;
+
+ authService = new LinkyAuthService(oAuthFactory, httpService, thingRegistry, componentContext);
+
SslContextFactory sslContextFactory = new SslContextFactory.Client();
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
sslContextFactory.setSslContext(sslContext);
- LinkyAuthService authService = new LinkyAuthService(oAuthFactory, httpService, componentContext);
-
} catch (NoSuchAlgorithmException e) {
logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
e);
@@ -141,7 +147,7 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
if (supportsThingType(thing.getThingTypeUID())) {
- LinkyHandler handler = new LinkyHandler(thing, localeProvider, gson, httpClient);
+ LinkyHandler handler = new LinkyHandler(thing, localeProvider, gson, httpClient, authService);
return handler;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 26fb3df629b..d4e4b5950a0 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -28,11 +28,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.util.Fields;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.jsoup.nodes.Element;
+import org.openhab.binding.linky.internal.LinkyAuthService;
import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AddressInfo;
@@ -62,8 +58,10 @@ import com.google.gson.JsonSyntaxException;
public class EnedisHttpApi {
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- private static final String BASE_URL = "https://www.myelectricaldata.fr/";
- private static final String CONTRACT_URL = BASE_URL + "contracts";
+ // private static final String BASE_URL = "https://www.myelectricaldata.fr/";
+ private static final String BASE_URL = "https://ext.prod-sandbox.api.enedis.fr/";
+
+ private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts";
private static final String IDENTITY_URL = BASE_URL + "identity";
private static final String CONTACT_URL = BASE_URL + "contact";
private static final String ADDRESS_URL = BASE_URL + "addresses";
@@ -77,12 +75,14 @@ public class EnedisHttpApi {
private final Gson gson;
private final HttpClient httpClient;
private LinkyConfiguration config;
+ private LinkyAuthService authService;
private boolean connected = false;
- public EnedisHttpApi(LinkyConfiguration config, Gson gson, HttpClient httpClient) {
+ public EnedisHttpApi(LinkyConfiguration config, Gson gson, HttpClient httpClient, LinkyAuthService authService) {
this.gson = gson;
this.httpClient = httpClient;
+ this.authService = authService;
this.config = config;
}
@@ -105,7 +105,7 @@ public class EnedisHttpApi {
}
private String getData(String url) throws LinkyException {
- return getData(url, httpClient, config.token);
+ return getData(url, httpClient, authService.getToken());
}
private static String getData(String url, HttpClient httpClient, String token) throws LinkyException {
@@ -113,7 +113,7 @@ public class EnedisHttpApi {
Request request = httpClient.newRequest(url);
request = request.method(HttpMethod.GET);
if (!("".equals(token))) {
- request = request.header("Authorization", token);
+ request = request.header("Authorization: Bearer ", token);
}
ContentResponse result = request.send();
@@ -162,7 +162,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData("%s/%s/cache".formatted(CONTRACT_URL, config.prmId));
+ String data = getData("%s?usage_point_id=%s".formatted(CONTRACT_URL, config.prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", CONTRACT_URL);
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index f512181464b..50be92f4551 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
+import org.openhab.binding.linky.internal.LinkyAuthService;
import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.api.EnedisHttpApi;
@@ -73,6 +74,7 @@ public class LinkyHandler extends BaseThingHandler {
private @Nullable ScheduledFuture> refreshJob;
private @Nullable EnedisHttpApi enedisApi;
+ private LinkyAuthService authService;
private @NonNullByDefault({}) String prmId;
private @NonNullByDefault({}) String userId;
@@ -83,10 +85,12 @@ public class LinkyHandler extends BaseThingHandler {
ALL
}
- public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient) {
+ public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient,
+ LinkyAuthService authService) {
super(thing);
this.gson = gson;
this.httpClient = httpClient;
+ this.authService = authService;
this.dailyConsumption = new ExpiringDayCache<>("dailyConsumption", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate today = LocalDate.now();
@@ -150,7 +154,7 @@ public class LinkyHandler extends BaseThingHandler {
LinkyConfiguration config = getConfigAs(LinkyConfiguration.class);
if (config.seemsValid()) {
- EnedisHttpApi api = new EnedisHttpApi(config, gson, httpClient);
+ EnedisHttpApi api = new EnedisHttpApi(config, gson, httpClient, authService);
this.enedisApi = api;
scheduler.submit(() -> {
From 5d48bc0120db871975aaa095128bc0847f1f5bd0 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 17 Apr 2024 16:53:16 +0200
Subject: [PATCH 016/135] spotless:apply
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyAuthService.java | 1 -
.../linky/internal/LinkyHandlerFactory.java | 1 -
.../linky/internal/api/EnedisHttpApi.java | 7 +++++--
.../resources/OH-INF/thing/thing-types.xml | 18 +++++++++---------
4 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
index 446513b9669..674091e0a9f 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
@@ -220,5 +220,4 @@ public class LinkyAuthService {
return result;
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 40fd4e6f55a..3b022ab8681 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -153,5 +153,4 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
return null;
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index d4e4b5950a0..0175aca3efa 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -61,7 +61,9 @@ public class EnedisHttpApi {
// private static final String BASE_URL = "https://www.myelectricaldata.fr/";
private static final String BASE_URL = "https://ext.prod-sandbox.api.enedis.fr/";
- private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts";
+ private static final String CONTRACT_URL = BASE_URL
+ + "metering_data_dc/v5/daily_consumption?start=2024-04-01&end=2024-04-17";
+ // private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts";
private static final String IDENTITY_URL = BASE_URL + "identity";
private static final String CONTACT_URL = BASE_URL + "contact";
private static final String ADDRESS_URL = BASE_URL + "addresses";
@@ -162,7 +164,8 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData("%s?usage_point_id=%s".formatted(CONTRACT_URL, config.prmId));
+ // String data = getData("%s?usage_point_id=%s".formatted(CONTRACT_URL, config.prmId));
+ String data = getData("%s&usage_point_id=%s".formatted(CONTRACT_URL, config.prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", CONTRACT_URL);
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index d9bd18e4a86..73c513ca2f9 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -35,16 +35,16 @@
Your Enedis token (can be left empty, use the connection page to automatically fill it
http://youopenhab/connectlinky)
-
+
- clientId
- Your Enedis clientId
-
-
- clientSecret
- Your Enedis clientSecret
-
-
+ clientId
+ Your Enedis clientId
+
+
+ clientSecret
+ Your Enedis clientSecret
+
+
From e45f6a06156dabdc025804dcaa60e3b179c160e5 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 21 Apr 2024 17:14:11 +0200
Subject: [PATCH 017/135] refactor binding (WIP)
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyAuthServlet.java | 13 +-
.../linky/internal/LinkyBindingConstants.java | 7 +
.../linky/internal/LinkyHandlerFactory.java | 79 +++------
.../linky/internal/api/EnedisHttpApi.java | 52 +++---
.../ApiBridgeHandler.java} | 154 +++++++++++++++---
.../internal/handler/EnedisBridgeHandler.java | 55 +++++++
.../linky/internal/handler/LinkyHandler.java | 96 +++++------
.../MyElectricalDataBridgeHandler.java | 55 +++++++
.../resources/OH-INF/thing/thing-types.xml | 59 +++++--
9 files changed, 398 insertions(+), 172 deletions(-)
rename bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/{LinkyAuthService.java => handler/ApiBridgeHandler.java} (61%)
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
index a37b6239cb4..65357178efc 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -30,6 +30,7 @@ import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.UrlEncoded;
+import org.openhab.binding.linky.internal.handler.ApiBridgeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,11 +63,11 @@ public class LinkyAuthServlet extends HttpServlet {
private static final String KEY_PAGE_REFRESH = "pageRefresh";
private final Logger logger = LoggerFactory.getLogger(LinkyAuthServlet.class);
- private final LinkyAuthService linkyAuthService;
private final String indexTemplate;
- public LinkyAuthServlet(LinkyAuthService linkyAuthService, String indexTemplate) {
- this.linkyAuthService = linkyAuthService;
+ private @NonNullByDefault({}) ApiBridgeHandler apiBridgeHandler;
+
+ public LinkyAuthServlet(ApiBridgeHandler apiBridgeHandler, String indexTemplate) {
this.indexTemplate = indexTemplate;
}
@@ -87,7 +88,7 @@ public class LinkyAuthServlet extends HttpServlet {
StringBuffer optionBuffer = new StringBuffer();
- List prmIds = linkyAuthService.getAllPrmId();
+ List prmIds = apiBridgeHandler.getAllPrmId();
for (String prmId : prmIds) {
optionBuffer.append("" + prmId + " ");
}
@@ -95,7 +96,7 @@ public class LinkyAuthServlet extends HttpServlet {
replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString());
replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK");
- replaceMap.put(KEY_AUTHORIZE_URI, linkyAuthService.formatAuthorizationUrl(servletBaseURLSecure));
+ replaceMap.put(KEY_AUTHORIZE_URI, apiBridgeHandler.formatAuthorizationUrl(servletBaseURLSecure));
resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
resp.getWriter().close();
} catch (LinkyException ex) {
@@ -139,7 +140,7 @@ public class LinkyAuthServlet extends HttpServlet {
} else if (!StringUtil.isBlank(reqState)) {
try {
replaceMap.put(KEY_AUTHORIZED_USER, String.format(HTML_USER_AUTHORIZED,
- reqCode + " / " + linkyAuthService.authorize(servletBaseURL, reqState, reqCode)));
+ reqCode + " / " + apiBridgeHandler.authorize(servletBaseURL, reqState, reqCode)));
} catch (RuntimeException e) {
logger.debug("Exception during authorizaton: ", e);
replaceMap.put(KEY_ERROR, String.format(HTML_ERROR, e.getMessage()));
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index c4c3b609407..10a8b8ec1b5 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -12,6 +12,7 @@
*/
package org.openhab.binding.linky.internal;
+import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -34,8 +35,14 @@ public class LinkyBindingConstants {
public static final String clientSecret = "6lsPfCmu0fEXuKYy3e0e6w8ydIca";
// List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_API_ENEDIS_BRIDGE = new ThingTypeUID(BINDING_ID, "EnedisBridge");
+ public static final ThingTypeUID THING_TYPE_API_MYELECTRICALDATA_BRIDGE = new ThingTypeUID(BINDING_ID,
+ "MyElectricalDataBridge");
public static final ThingTypeUID THING_TYPE_LINKY = new ThingTypeUID(BINDING_ID, "linky");
+ public static final Set SUPPORTED_DEVICE_THING_TYPES_UIDS = Set.of(THING_TYPE_API_ENEDIS_BRIDGE,
+ THING_TYPE_API_MYELECTRICALDATA_BRIDGE, THING_TYPE_LINKY);
+
// Thing properties
public static final String PUISSANCE = "puissance";
public static final String PRM_ID = "prmId";
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index 3b022ab8681..bb44205ed0b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -12,25 +12,20 @@
*/
package org.openhab.binding.linky.internal;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.openhab.binding.linky.internal.handler.EnedisBridgeHandler;
import org.openhab.binding.linky.internal.handler.LinkyHandler;
+import org.openhab.binding.linky.internal.handler.MyElectricalDataBridgeHandler;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
-import org.openhab.core.io.net.http.TrustAllTrustManager;
+import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingTypeUID;
@@ -63,12 +58,14 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
private static final DateTimeFormatter LINKY_LOCALDATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd");
private static final DateTimeFormatter LINKY_LOCALDATETIME_FORMATTER = DateTimeFormatter
.ofPattern("uuuu-MM-dd HH:mm:ss");
- private static final int REQUEST_BUFFER_SIZE = 8000;
- private static final int RESPONSE_BUFFER_SIZE = 200000;
private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
- private LinkyAuthService authService;
+ private final HttpClientFactory httpClientFactory;
+ private final OAuthFactory oAuthFactory;
+ private final HttpService httpService;
+ private final ThingRegistry thingRegistry;
+ private final ComponentContext componentContext;
private final Gson gson = new GsonBuilder()
.registerTypeAdapter(ZonedDateTime.class,
@@ -90,7 +87,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
private final LocaleProvider localeProvider;
- private final HttpClient httpClient;
@Activate
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
@@ -99,58 +95,37 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
ComponentContext componentContext) {
this.localeProvider = localeProvider;
- authService = new LinkyAuthService(oAuthFactory, httpService, thingRegistry, componentContext);
+ this.httpClientFactory = httpClientFactory;
+ this.oAuthFactory = oAuthFactory;
+ this.httpService = httpService;
+ this.thingRegistry = thingRegistry;
+ this.componentContext = componentContext;
- SslContextFactory sslContextFactory = new SslContextFactory.Client();
- try {
- SSLContext sslContext = SSLContext.getInstance("SSL");
- sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
- sslContextFactory.setSslContext(sslContext);
-
- } catch (NoSuchAlgorithmException e) {
- logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
- e);
- } catch (KeyManagementException e) {
- logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
- }
- this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
- httpClient.setFollowRedirects(false);
- httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
- }
-
- @Override
- protected void activate(ComponentContext componentContext) {
- super.activate(componentContext);
- try {
-
- httpClient.start();
- } catch (Exception e) {
- logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
- }
- }
-
- @Override
- protected void deactivate(ComponentContext componentContext) {
- super.deactivate(componentContext);
- try {
- httpClient.stop();
- } catch (Exception e) {
- logger.warn("Unable to stop Jetty HttpClient {}", e.getMessage());
- }
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
- return LinkyBindingConstants.THING_TYPE_LINKY.equals(thingTypeUID);
+ return LinkyBindingConstants.SUPPORTED_DEVICE_THING_TYPES_UIDS.contains(thingTypeUID);
+
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
- if (supportsThingType(thing.getThingTypeUID())) {
- LinkyHandler handler = new LinkyHandler(thing, localeProvider, gson, httpClient, authService);
+ if (thing.getThingTypeUID().equals(LinkyBindingConstants.THING_TYPE_API_ENEDIS_BRIDGE)) {
+ EnedisBridgeHandler handler = new EnedisBridgeHandler((Bridge) thing, this.httpClientFactory,
+ this.oAuthFactory, this.httpService, thingRegistry, componentContext, gson);
+ return handler;
+ } else if (thing.getThingTypeUID().equals(LinkyBindingConstants.THING_TYPE_API_MYELECTRICALDATA_BRIDGE)) {
+ MyElectricalDataBridgeHandler handler = new MyElectricalDataBridgeHandler((Bridge) thing,
+ this.httpClientFactory, this.oAuthFactory, this.httpService, thingRegistry, componentContext, gson);
+ return handler;
+ } else if (thing.getThingTypeUID().equals(LinkyBindingConstants.THING_TYPE_LINKY)) {
+ LinkyHandler handler = new LinkyHandler(thing, localeProvider);
return handler;
}
return null;
+
}
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 0175aca3efa..e5e36fb827d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -28,8 +28,6 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
-import org.openhab.binding.linky.internal.LinkyAuthService;
-import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AddressInfo;
import org.openhab.binding.linky.internal.dto.ContactInfo;
@@ -42,6 +40,7 @@ import org.openhab.binding.linky.internal.dto.MeterResponse;
import org.openhab.binding.linky.internal.dto.PrmInfo;
import org.openhab.binding.linky.internal.dto.TempoResponse;
import org.openhab.binding.linky.internal.dto.UsagePoint;
+import org.openhab.binding.linky.internal.handler.ApiBridgeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,14 +55,15 @@ import com.google.gson.JsonSyntaxException;
*/
@NonNullByDefault
public class EnedisHttpApi {
+
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// private static final String BASE_URL = "https://www.myelectricaldata.fr/";
private static final String BASE_URL = "https://ext.prod-sandbox.api.enedis.fr/";
- private static final String CONTRACT_URL = BASE_URL
- + "metering_data_dc/v5/daily_consumption?start=2024-04-01&end=2024-04-17";
- // private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts";
+ // private static final String CONTRACT_URL = BASE_URL
+ // + "metering_data_dc/v5/daily_consumption?start=2024-04-01&end=2024-04-17";
+ private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts";
private static final String IDENTITY_URL = BASE_URL + "identity";
private static final String CONTACT_URL = BASE_URL + "contact";
private static final String ADDRESS_URL = BASE_URL + "addresses";
@@ -76,16 +76,14 @@ public class EnedisHttpApi {
private final Logger logger = LoggerFactory.getLogger(EnedisHttpApi.class);
private final Gson gson;
private final HttpClient httpClient;
- private LinkyConfiguration config;
- private LinkyAuthService authService;
+ private ApiBridgeHandler apiBridgeHandler;
private boolean connected = false;
- public EnedisHttpApi(LinkyConfiguration config, Gson gson, HttpClient httpClient, LinkyAuthService authService) {
+ public EnedisHttpApi(ApiBridgeHandler apiBridgeHandler, Gson gson, HttpClient httpClient) {
this.gson = gson;
this.httpClient = httpClient;
- this.authService = authService;
- this.config = config;
+ this.apiBridgeHandler = apiBridgeHandler;
}
public void initialize() throws LinkyException {
@@ -107,7 +105,7 @@ public class EnedisHttpApi {
}
private String getData(String url) throws LinkyException {
- return getData(url, httpClient, authService.getToken());
+ return getData(url, httpClient, apiBridgeHandler.getToken());
}
private static String getData(String url, HttpClient httpClient, String token) throws LinkyException {
@@ -115,7 +113,8 @@ public class EnedisHttpApi {
Request request = httpClient.newRequest(url);
request = request.method(HttpMethod.GET);
if (!("".equals(token))) {
- request = request.header("Authorization: Bearer ", token);
+ request = request.header("Authorization", "Bearer " + token);
+ request = request.header("accept", "application/json");
}
ContentResponse result = request.send();
@@ -143,16 +142,16 @@ public class EnedisHttpApi {
}
}
- public PrmInfo getPrmInfo() throws LinkyException {
+ public PrmInfo getPrmInfo(String prmId) throws LinkyException {
PrmInfo result = new PrmInfo();
- Customer customer = getCustomer();
+ Customer customer = getCustomer(prmId);
UsagePoint usagePoint = customer.usagePoints[0];
result.contractInfo = usagePoint.contracts;
result.usagePointInfo = usagePoint.usagePoint;
- result.identityInfo = getIdentity();
- result.addressInfo = getAddress();
- result.contactInfo = getContact();
+ result.identityInfo = getIdentity(prmId);
+ result.addressInfo = getAddress(prmId);
+ result.contactInfo = getContact(prmId);
result.prmId = result.usagePointInfo.usagePointId;
result.customerId = customer.customerId;
@@ -160,12 +159,12 @@ public class EnedisHttpApi {
return result;
}
- public Customer getCustomer() throws LinkyException {
+ public Customer getCustomer(String prmId) throws LinkyException {
if (!connected) {
initialize();
}
- // String data = getData("%s?usage_point_id=%s".formatted(CONTRACT_URL, config.prmId));
- String data = getData("%s&usage_point_id=%s".formatted(CONTRACT_URL, config.prmId));
+ String data = getData("%s?usage_point_id=%s".formatted(CONTRACT_URL, prmId));
+ // String data = getData("%s&usage_point_id=%s".formatted(CONTRACT_URL, config.prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", CONTRACT_URL);
}
@@ -181,11 +180,11 @@ public class EnedisHttpApi {
}
}
- public AddressInfo getAddress() throws LinkyException {
+ public AddressInfo getAddress(String prmId) throws LinkyException {
if (!connected) {
initialize();
}
- String data = getData("%s/%s/cache".formatted(ADDRESS_URL, config.prmId));
+ String data = getData("%s/%s/cache".formatted(ADDRESS_URL, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", ADDRESS_URL);
}
@@ -201,11 +200,11 @@ public class EnedisHttpApi {
}
}
- public IdentityInfo getIdentity() throws LinkyException {
+ public IdentityInfo getIdentity(String prmId) throws LinkyException {
if (!connected) {
initialize();
}
- String data = getData("%s/%s/cache".formatted(IDENTITY_URL, config.prmId));
+ String data = getData("%s/%s/cache".formatted(IDENTITY_URL, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", IDENTITY_URL);
}
@@ -221,11 +220,11 @@ public class EnedisHttpApi {
}
}
- public ContactInfo getContact() throws LinkyException {
+ public ContactInfo getContact(String prmId) throws LinkyException {
if (!connected) {
initialize();
}
- String data = getData("%s/%s/cache".formatted(CONTACT_URL, config.prmId));
+ String data = getData("%s/%s/cache".formatted(CONTACT_URL, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", CONTACT_URL);
@@ -312,4 +311,5 @@ public class EnedisHttpApi {
return "";
}
}
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
similarity index 61%
rename from bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
rename to bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 674091e0a9f..fc46973bad7 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthService.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -10,86 +10,197 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.binding.linky.internal;
+package org.openhab.binding.linky.internal.handler;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.openhab.binding.linky.internal.LinkyAuthServlet;
+import org.openhab.binding.linky.internal.LinkyBindingConstants;
+import org.openhab.binding.linky.internal.LinkyException;
+import org.openhab.binding.linky.internal.api.EnedisHttpApi;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthException;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
import org.openhab.core.config.core.Configuration;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.io.net.http.TrustAllTrustManager;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseBridgeHandler;
+import org.openhab.core.types.Command;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
-import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.gson.Gson;
+
/**
+ * {@link ApiBridgeHandler} is the base handler to access enedis data.
*
- * @author Gaël L'hopital - Initial contribution
- * @author Laurent Arnal - Rewrite addon to use official dataconect API
+ * @author Laurent Arnal - Initial contribution
*/
@NonNullByDefault
-public class LinkyAuthService {
+public class ApiBridgeHandler extends BaseBridgeHandler {
+ private final Logger logger = LoggerFactory.getLogger(ApiBridgeHandler.class);
- private static final String TEMPLATE_PATH = "templates/";
- private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
+ private static final int REFRESH_FIRST_HOUR_OF_DAY = 1;
- private final Logger logger = LoggerFactory.getLogger(LinkyAuthService.class);
+ private static final int REQUEST_BUFFER_SIZE = 8000;
private @NonNullByDefault({}) HttpService httpService;
private @NonNullByDefault({}) BundleContext bundleContext;
+ private final HttpClient httpClient;
+ private @Nullable EnedisHttpApi enedisApi;
+ private final Gson gson;
private OAuthClientService oAuthService;
private final ThingRegistry thingRegistry;
- public LinkyAuthService(final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
- final @Reference ThingRegistry thingRegistry, final @Reference ComponentContext componentContext) {
+ private static @Nullable HttpServlet servlet;
+ private static final String TEMPLATE_PATH = "templates/";
+ private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
+
+ public ApiBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
+ final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
+ final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
+ super(bridge);
+
+ SslContextFactory sslContextFactory = new SslContextFactory.Client();
+ try {
+ SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
+ sslContextFactory.setSslContext(sslContext);
+
+ } catch (NoSuchAlgorithmException e) {
+ logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
+ e);
+ } catch (KeyManagementException e) {
+ logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
+ }
+
+ this.gson = gson;
this.httpService = httpService;
this.thingRegistry = thingRegistry;
+ this.bundleContext = componentContext.getBundleContext();
+
+ this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
+ httpClient.setFollowRedirects(false);
+ httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
+
+ try {
+ httpClient.start();
+ } catch (Exception e) {
+ logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
+ }
+
this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
LinkyBindingConstants.ENEDIS_API_TOKEN_URL, LinkyBindingConstants.ENEDIS_AUTH_AUTHORIZE_URL,
LinkyBindingConstants.clientId, LinkyBindingConstants.clientSecret, LinkyBindingConstants.LINKY_SCOPES,
true);
- try {
- bundleContext = componentContext.getBundleContext();
+ registerServlet();
- httpService.registerServlet(LinkyBindingConstants.LINKY_ALIAS, createServlet(), new Hashtable<>(),
- httpService.createDefaultHttpContext());
- httpService.registerResources(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS,
- "web", null);
+ updateStatus(ThingStatus.UNKNOWN);
+ }
+
+ public @Nullable EnedisHttpApi getEnedisApi() {
+ return enedisApi;
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("Initializing Netatmo API bridge handler.");
+
+ updateStatus(ThingStatus.UNKNOWN);
+
+ EnedisHttpApi api = new EnedisHttpApi(this, gson, this.httpClient);
+
+ this.enedisApi = api;
+
+ scheduler.submit(() -> {
+ try {
+ api.initialize();
+ updateStatus(ThingStatus.ONLINE);
+ } catch (LinkyException e) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+ }
+ });
+
+ }
+
+ /*
+ * @Override
+ * protected void deactivate(ComponentContext componentContext) {
+ * super.deactivate(componentContext);
+ * try {
+ * httpClient.stop();
+ * } catch (Exception e) {
+ * logger.warn("Unable to stop Jetty HttpClient {}", e.getMessage());
+ * }
+ * }
+ */
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Shutting down Netatmo API bridge handler.");
+
+ httpService.unregister(LinkyBindingConstants.LINKY_ALIAS);
+ httpService.unregister(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS);
+
+ super.dispose();
+ }
+
+ private void registerServlet() {
+ try {
+ if (servlet == null) {
+ servlet = createServlet();
+
+ httpService.registerServlet(LinkyBindingConstants.LINKY_ALIAS, servlet, new Hashtable<>(),
+ httpService.createDefaultHttpContext());
+ httpService.registerResources(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS,
+ "web", null);
+ }
} catch (NamespaceException | ServletException | IOException e) {
logger.warn("Error during linky servlet startup", e);
}
- }
- @Deactivate
- protected void deactivate(ComponentContext componentContext) {
- httpService.unregister(LinkyBindingConstants.LINKY_ALIAS);
- httpService.unregister(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS);
}
/**
@@ -220,4 +331,5 @@ public class LinkyAuthService {
return result;
}
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
new file mode 100644
index 00000000000..63c997d7aa9
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.auth.client.oauth2.OAuthFactory;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ThingRegistry;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+/**
+ * {@link EnedisBridgeHandler} is the base handler to access enedis data.
+ *
+ * @author Laurent Arnal - Initial contribution
+ */
+@NonNullByDefault
+public class EnedisBridgeHandler extends ApiBridgeHandler {
+ private final Logger logger = LoggerFactory.getLogger(EnedisBridgeHandler.class);
+
+ public EnedisBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
+ final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
+ final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
+ super(bridge, httpClientFactory, oAuthFactory, httpService, thingRegistry, componentContext, gson);
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Shutting down Netatmo API bridge handler.");
+
+ super.dispose();
+ }
+
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 50be92f4551..a8f19e9c61d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -22,12 +22,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.client.HttpClient;
-import org.openhab.binding.linky.internal.LinkyAuthService;
import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.api.EnedisHttpApi;
@@ -51,8 +48,6 @@ import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.gson.Gson;
-
/**
* The {@link LinkyHandler} is responsible for handling commands, which are
* sent to one of the channels.
@@ -67,30 +62,24 @@ public class LinkyHandler extends BaseThingHandler {
private static final int REFRESH_INTERVAL_IN_MIN = 120;
private final Logger logger = LoggerFactory.getLogger(LinkyHandler.class);
- private final HttpClient httpClient;
- private final Gson gson;
private final ExpiringDayCache dailyConsumption;
private @Nullable ScheduledFuture> refreshJob;
- private @Nullable EnedisHttpApi enedisApi;
- private LinkyAuthService authService;
private @NonNullByDefault({}) String prmId;
private @NonNullByDefault({}) String userId;
+ private @Nullable EnedisHttpApi enedisApi;
+
private enum Target {
FIRST,
LAST,
ALL
}
- public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient,
- LinkyAuthService authService) {
+ public LinkyHandler(Thing thing, LocaleProvider localeProvider) {
super(thing);
- this.gson = gson;
- this.httpClient = httpClient;
- this.authService = authService;
this.dailyConsumption = new ExpiringDayCache<>("dailyConsumption", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate today = LocalDate.now();
@@ -150,44 +139,43 @@ public class LinkyHandler extends BaseThingHandler {
@Override
public void initialize() {
logger.debug("Initializing Linky handler.");
+
+ ApiBridgeHandler bridgeHandler = (ApiBridgeHandler) getBridge().getHandler();
+ enedisApi = bridgeHandler.getEnedisApi();
+
updateStatus(ThingStatus.UNKNOWN);
LinkyConfiguration config = getConfigAs(LinkyConfiguration.class);
if (config.seemsValid()) {
- EnedisHttpApi api = new EnedisHttpApi(config, gson, httpClient, authService);
- this.enedisApi = api;
-
scheduler.submit(() -> {
try {
- api.initialize();
- updateStatus(ThingStatus.ONLINE);
- PrmInfo prmInfo = api.getPrmInfo();
+ PrmInfo prmInfo = enedisApi.getPrmInfo(config.prmId);
updateProperties(Map.of(USER_ID, prmInfo.customerId, PUISSANCE,
prmInfo.contractInfo.subscribedPower, PRM_ID, prmInfo.prmId));
- prmId = thing.getProperties().get(PRM_ID);
- userId = thing.getProperties().get(USER_ID);
+ // prmId = thing.getProperties().get(PRM_ID);
+ // userId = thing.getProperties().get(USER_ID);
- updateData();
+ // updateData();
- disconnect();
+ // disconnect();
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime nextDayFirstTimeUpdate = now.plusDays(1).withHour(REFRESH_FIRST_HOUR_OF_DAY)
.truncatedTo(ChronoUnit.HOURS);
- refreshJob = scheduler.scheduleWithFixedDelay(this::updateData,
- ChronoUnit.MINUTES.between(now, nextDayFirstTimeUpdate) % REFRESH_INTERVAL_IN_MIN + 1,
- REFRESH_INTERVAL_IN_MIN, TimeUnit.MINUTES);
- } catch (LinkyException e) {
+ // refreshJob = scheduler.scheduleWithFixedDelay(this::updateData,
+ // ChronoUnit.MINUTES.between(now, nextDayFirstTimeUpdate) % REFRESH_INTERVAL_IN_MIN + 1,
+ // REFRESH_INTERVAL_IN_MIN, TimeUnit.MINUTES);
+
+ updateStatus(ThingStatus.ONLINE);
+ } catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
});
- } else {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "@text/offline.config-error-mandatory-settings");
}
+
}
/**
@@ -420,6 +408,7 @@ public class LinkyHandler extends BaseThingHandler {
to.format(DateTimeFormatter.ISO_LOCAL_DATE));
EnedisHttpApi api = this.enedisApi;
+
if (api != null) {
try {
MeterReading meterReading = api.getEnergyData(userId, prmId, from, to);
@@ -430,29 +419,28 @@ public class LinkyHandler extends BaseThingHandler {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
+
return null;
}
- /*
- *
- * private @Nullable Consumption getPowerData(LocalDate from, LocalDate to) {
- * logger.debug("getPowerData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
- * to.format(DateTimeFormatter.ISO_LOCAL_DATE));
- * EnedisHttpApi api = this.enedisApi;
- * if (api != null) {
- * try {
- * Consumption consumption = api.getPowerData(userId, prmId, from, to);
- * updateStatus(ThingStatus.ONLINE);
- * return consumption;
- * } catch (LinkyException e) {
- * logger.debug("Exception when getting power data: {}", e.getMessage(), e);
- * updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
- * }
- * }
- * return null;
- * }
- *
- */
+ private @Nullable MeterReading getPowerData(LocalDate from, LocalDate to) {
+ logger.debug("getPowerData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
+ to.format(DateTimeFormatter.ISO_LOCAL_DATE));
+ EnedisHttpApi api = this.enedisApi;
+
+ if (api != null) {
+ try {
+ MeterReading meterReading = api.getPowerData(userId, prmId, from, to);
+ updateStatus(ThingStatus.ONLINE);
+ return meterReading;
+ } catch (LinkyException e) {
+ logger.debug("Exception when getting power data: {}", e.getMessage(), e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ }
+ }
+
+ return null;
+ }
private @Nullable String getTempoData() {
logger.debug("getTempoData from");
@@ -468,15 +456,18 @@ public class LinkyHandler extends BaseThingHandler {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
+
return null;
}
private boolean isConnected() {
EnedisHttpApi api = this.enedisApi;
- return api == null ? false : api.isConnected();
+ // return api == null ? false : api.isConnected();
+ return true;
}
private void disconnect() {
+
EnedisHttpApi api = this.enedisApi;
if (api != null) {
try {
@@ -485,6 +476,7 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("disconnect: {}", e.getMessage());
}
}
+
}
@Override
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
new file mode 100644
index 00000000000..0e96673a5f3
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.auth.client.oauth2.OAuthFactory;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ThingRegistry;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+/**
+ * {@link MyElectricalDataBridgeHandler} is the base handler to access enedis data.
+ *
+ * @author Laurent Arnal - Initial contribution
+ */
+@NonNullByDefault
+public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
+ private final Logger logger = LoggerFactory.getLogger(MyElectricalDataBridgeHandler.class);
+
+ public MyElectricalDataBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
+ final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
+ final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
+ super(bridge, httpClientFactory, oAuthFactory, httpService, thingRegistry, componentContext, gson);
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Shutting down Netatmo API bridge handler.");
+
+ super.dispose();
+ }
+
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 73c513ca2f9..b5c655dbfef 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -4,7 +4,51 @@
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">
+
+ EnedisBridge
+
+ Provides your energy consumption data.
+ In order to receive the data, you must activate your account at
+ https://espace-client-particuliers.enedis.fr/web/espace-particuliers/compteur-linky.
+
+
+
+
+ clientId
+ Your Enedis clientId
+
+
+ clientSecret
+ Your Enedis clientSecret
+
+
+
+
+
+
+ EnedisBridge
+
+ Provides your energy consumption data.
+ In order to receive the data, you must activate your account at
+ https://espace-client-particuliers.enedis.fr/web/espace-particuliers/compteur-linky.
+
+
+
+
+ Token
+ Your Enedis token (can be left empty, use the connection page to automatically fill it
+ http://youopenhab/connectlinky)
+
+
+
+
+
+
+
+
+
+
Linky
Provides your energy consumption data.
@@ -30,21 +74,6 @@
PrmId
Your PrmId
-
- Token
- Your Enedis token (can be left empty, use the connection page to automatically fill it
- http://youopenhab/connectlinky)
-
-
-
- clientId
- Your Enedis clientId
-
-
- clientSecret
- Your Enedis clientSecret
-
-
From e04cfea00a909090c28b363885589dbd0e570bbc Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 21 Apr 2024 17:14:11 +0200
Subject: [PATCH 018/135] refactor binding (WIP)
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyHandlerFactory.java | 4 -
.../internal/handler/ApiBridgeHandler.java | 4 -
.../internal/handler/EnedisBridgeHandler.java | 1 -
.../MyElectricalDataBridgeHandler.java | 1 -
.../resources/OH-INF/thing/thing-types.xml | 88 +++++++++----------
5 files changed, 44 insertions(+), 54 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index bb44205ed0b..e85a829151d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -100,13 +100,11 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
this.httpService = httpService;
this.thingRegistry = thingRegistry;
this.componentContext = componentContext;
-
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return LinkyBindingConstants.SUPPORTED_DEVICE_THING_TYPES_UIDS.contains(thingTypeUID);
-
}
@Override
@@ -125,7 +123,5 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
}
return null;
-
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index fc46973bad7..212ce90187a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -155,7 +155,6 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
});
-
}
/*
@@ -173,7 +172,6 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// TODO Auto-generated method stub
-
}
@Override
@@ -200,7 +198,6 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
} catch (NamespaceException | ServletException | IOException e) {
logger.warn("Error during linky servlet startup", e);
}
-
}
/**
@@ -331,5 +328,4 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
return result;
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index 63c997d7aa9..90c3232f721 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -51,5 +51,4 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
super.dispose();
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index 0e96673a5f3..771f95f6cbd 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -51,5 +51,4 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
super.dispose();
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index b5c655dbfef..ae6274f3fb8 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -4,51 +4,51 @@
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">
-
- EnedisBridge
-
- Provides your energy consumption data.
- In order to receive the data, you must activate your account at
- https://espace-client-particuliers.enedis.fr/web/espace-particuliers/compteur-linky.
-
-
-
-
- clientId
- Your Enedis clientId
-
-
- clientSecret
- Your Enedis clientSecret
-
-
-
-
-
-
- EnedisBridge
-
- Provides your energy consumption data.
- In order to receive the data, you must activate your account at
- https://espace-client-particuliers.enedis.fr/web/espace-particuliers/compteur-linky.
-
-
-
-
- Token
- Your Enedis token (can be left empty, use the connection page to automatically fill it
- http://youopenhab/connectlinky)
-
-
-
-
+
+ EnedisBridge
+
+ Provides your energy consumption data.
+ In order to receive the data, you must activate your account at
+ https://espace-client-particuliers.enedis.fr/web/espace-particuliers/compteur-linky.
+
+
+
+
+ clientId
+ Your Enedis clientId
+
+
+ clientSecret
+ Your Enedis clientSecret
+
+
+
+
+
+
+ EnedisBridge
+
+ Provides your energy consumption data.
+ In order to receive the data, you must activate your account at
+ https://espace-client-particuliers.enedis.fr/web/espace-particuliers/compteur-linky.
+
+
+
+
+ Token
+ Your Enedis token (can be left empty, use the connection page to automatically fill it
+ http://youopenhab/connectlinky)
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
Linky
Provides your energy consumption data.
From 4e34da2e3696daebe5fa7718419b5c2991e258fa Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 21 Apr 2024 17:14:11 +0200
Subject: [PATCH 019/135] refactor binding (WIP)
Signed-off-by: Laurent ARNAL
---
.../org/openhab/binding/linky/internal/api/EnedisHttpApi.java | 1 -
.../openhab/binding/linky/internal/handler/LinkyHandler.java | 2 --
2 files changed, 3 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index e5e36fb827d..277d26e3efe 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -311,5 +311,4 @@ public class EnedisHttpApi {
return "";
}
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index a8f19e9c61d..7edd401b074 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -175,7 +175,6 @@ public class LinkyHandler extends BaseThingHandler {
}
});
}
-
}
/**
@@ -476,7 +475,6 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("disconnect: {}", e.getMessage());
}
}
-
}
@Override
From cd53876b0b07ad2b537e92318cc823cc63c41f2a Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Mon, 3 Jun 2024 15:48:56 +0200
Subject: [PATCH 020/135] quick fix on dto objects
Signed-off-by: Laurent ARNAL
---
.../org/openhab/binding/linky/internal/dto/AddressInfo.java | 2 +-
.../java/org/openhab/binding/linky/internal/dto/Contracts.java | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
index 7c7f4de0e78..b7b4f8360e9 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
@@ -26,7 +26,7 @@ public class AddressInfo {
public String street;
public String locality;
- @SerializedName("postalCode")
+ @SerializedName("postal_code")
public String postalCode;
@SerializedName("insee_code")
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
index 89dae9f1d2b..2a04cbe0618 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
@@ -40,6 +40,9 @@ public class Contracts {
@SerializedName("contract_status")
public String contractStatus;
+ @SerializedName("contract_type")
+ public String contractType;
+
@SerializedName("last_distribution_tariff_change_date")
public String lastDistributionTariffChangeDate;
}
From a6717ce69bf277b04263d7d4e26f47ba55cefb8c Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Mon, 3 Jun 2024 15:53:05 +0200
Subject: [PATCH 021/135] add new channels
Signed-off-by: Laurent ARNAL
---
.../resources/OH-INF/thing/thing-types.xml | 70 +++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index ae6274f3fb8..afef803d978 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -161,6 +161,76 @@
linkyTestSelect
+
+ Identity
+
+
+
+ Subscribed Power
+
+
+ Last Activation Date
+
+
+ Distribution Tariff
+
+
+ Offpeak Hours
+
+
+ Contract Status
+
+
+ Contract Type
+
+
+ Last Distribution Tariff ChangeDate
+
+
+ Contract Segment
+
+
+
+
+ UsagePoint Id
+
+
+ UsagePoin Status
+
+
+ UsagePoint Meter Type
+
+
+
+ City
+
+
+ Country
+
+
+ Insee Code
+
+
+ Postal Code
+
+
+ Street
+
+
+
+
+
+
+ Mail
+
+
+ Phone
+
+
+
+
+
+
From f95b6e5691d77357e7ffa7a9c4b7a3a39773b71c Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Mon, 3 Jun 2024 16:34:31 +0200
Subject: [PATCH 022/135] direct Enedis Api call support
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyBindingConstants.java | 45 ++++++++++++++++---
.../linky/internal/api/EnedisHttpApi.java | 39 +++++++++-------
2 files changed, 60 insertions(+), 24 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 10a8b8ec1b5..f80e04c98a0 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -31,6 +31,9 @@ public class LinkyBindingConstants {
public static final String BINDING_ID = "linky";
+ // public final String clientId = "e551937c-5250-48bc-b4a6-2323af68db92";
+ // public final String clientSecret = "";
+
public static final String clientId = "_88uJnEjEs_IMf4bjGZJV6gGxYga";
public static final String clientSecret = "6lsPfCmu0fEXuKYy3e0e6w8ydIca";
@@ -72,6 +75,30 @@ public class LinkyBindingConstants {
public static final String TEST_SELECT = "main#linkyTestSelect";
+ public static final String MAIN_IDENTITY = "main#Identity";
+
+ public static final String MAIN_CONTRACT_SUBSCRIBED_POWER = "main#contractSubscribedPower";
+ public static final String MAIN_CONTRACT_LAST_ACTIVATION_DATE = "main#contractLastActivationDate";
+ public static final String MAIN_CONTRACT_DISTRIBUTION_TARIFF = "main#contractDistributionTariff";
+ public static final String MAIN_CONTRACT_OFF_PEAK_HOURS = "main#contractOffpeakHours";
+ public static final String MAIN_CONTRACT_CONTRACT_STATUS = "main#contractStatus";
+ public static final String MAIN_CONTRACT_CONTRACT_TYPE = "main#contractType";
+ public static final String MAIN_CONTRACT_LAST_DISTRIBUTION_TARIFF_CHANGE_DATE = "main#contractLastDistributionTariffChangeDate";
+ public static final String MAIN_CONTRACT_SEGMENT = "main#contractSegment";
+
+ public static final String MAIN_USAGEPOINT_ID = "main#usagePointId";
+ public static final String MAIN_USAGEPOINT_STATUS = "main#usagePointStatus";
+ public static final String MAIN_USAGEPOINT_METER_TYPE = "main#usagePointMeterType";
+
+ public static final String MAIN_USAGEPOINT_METER_ADDRESS_CITY = "main#usagePointAddressCity";
+ public static final String MAIN_USAGEPOINT_METER_ADDRESS_COUNTRY = "main#usagePointAddressCountry";
+ public static final String MAIN_USAGEPOINT_METER_ADDRESS_INSEE_CODE = "main#usagePointAddressInseeCode";
+ public static final String MAIN_USAGEPOINT_METER_ADDRESS_POSTAL_CODE = "main#usagePointAddressPostalCode";
+ public static final String MAIN_USAGEPOINT_METER_ADDRESS_STREET = "main#usagePointAddressStreet";
+
+ public static final String MAIN_CONTACT_MAIL = "main#contactMail";
+ public static final String MAIN_CONTACT_PHONE = "main#contactPhone";
+
// Authorization related Servlet and resources aliases.
public static final String LINKY_ALIAS = "/connectlinky";
public static final String LINKY_IMG_ALIAS = "/img";
@@ -86,13 +113,17 @@ public class LinkyBindingConstants {
// "r:installedapps", "w:installedapps"
// List of Linky services related urls, information
- public static final String LINKY_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
- public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "v1/oauth2/authorize";
- public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "token";
+ public static final String LINKY_MYELECTRICALDATA_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
+ public static final String LINKY_MYELECTRICALDATA_AUTHORIZE_URL = LINKY_MYELECTRICALDATA_ACCOUNT_URL
+ + "v1/oauth2/authorize";
+ public static final String LINKY_MYELECTRICALDATA_API_TOKEN_URL = LINKY_MYELECTRICALDATA_ACCOUNT_URL + "token";
- public static final String ENEDIS_API_ACCOUNT_URL = "https://ext.prod-sandbox.api.enedis.fr/";
- public static final String ENEDIS_API_TOKEN_URL = ENEDIS_API_ACCOUNT_URL + "oauth2/v3/token";
+ public static final String ENEDIS_ACCOUNT_URL_PROD = "https://mon-compte-particulier.enedis.fr/";
+ public static final String ENEDIS_AUTHORIZE_URL_PROD = ENEDIS_ACCOUNT_URL_PROD + "dataconnect/v1/oauth2/authorize";
+ public static final String ENEDIS_API_TOKEN_URL_PROD = ENEDIS_ACCOUNT_URL_PROD + "oauth2/v3/token";
- public static final String ENEDIS_AUTH_ACCOUNT_URL = "https://mon-compte-particulier.enedis.fr/";
- public static final String ENEDIS_AUTH_AUTHORIZE_URL = ENEDIS_AUTH_ACCOUNT_URL + "dataconnect/v1/oauth2/authorize";
+ public static final String ENEDIS_ACCOUNT_URL_PREPROD = "https://ext.prod-sandbox.api.enedis.fr/";
+ public static final String ENEDIS_AUTHORIZE_URL_PREPROD = ENEDIS_ACCOUNT_URL_PREPROD
+ + "dataconnect/v1/oauth2/authorize";
+ public static final String ENEDIS_API_TOKEN_URL_PREPROD = ENEDIS_ACCOUNT_URL_PREPROD + "oauth2/v3/token";
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 277d26e3efe..917b2d7565d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -58,16 +58,17 @@ public class EnedisHttpApi {
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- // private static final String BASE_URL = "https://www.myelectricaldata.fr/";
private static final String BASE_URL = "https://ext.prod-sandbox.api.enedis.fr/";
- // private static final String CONTRACT_URL = BASE_URL
- // + "metering_data_dc/v5/daily_consumption?start=2024-04-01&end=2024-04-17";
private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts";
- private static final String IDENTITY_URL = BASE_URL + "identity";
- private static final String CONTACT_URL = BASE_URL + "contact";
- private static final String ADDRESS_URL = BASE_URL + "addresses";
- private static final String MEASURE_URL = BASE_URL + "%s/%s/start/%s/end/%s/cache";
+ private static final String IDENTITY_URL = BASE_URL + "customers_i/v5/identity";
+ private static final String CONTACT_URL = BASE_URL + "customers_cd/v5/contact_data";
+ private static final String ADDRESS_URL = BASE_URL + "customers_upa/v5/usage_points/addresses";
+ private static final String MEASURE_DAILY_CONSUMPTION_URL = BASE_URL
+ + "metering_data_dc/v5/daily_consumption?usage_point_id=%s&start=%s&end=%s";
+ private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ + "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
+
private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
private static final String TOKEN_URL = BASE_URL
@@ -114,7 +115,7 @@ public class EnedisHttpApi {
request = request.method(HttpMethod.GET);
if (!("".equals(token))) {
request = request.header("Authorization", "Bearer " + token);
- request = request.header("accept", "application/json");
+ request = request.header("Accept", "application/json");
}
ContentResponse result = request.send();
@@ -159,12 +160,16 @@ public class EnedisHttpApi {
return result;
}
+ public String formatUrl(String apiUrl, String prmId) {
+ // return "%s/%s/cache".formatted(apiUrl, config.prmId);
+ return "%s?usage_point_id=%s".formatted(apiUrl, prmId);
+ }
+
public Customer getCustomer(String prmId) throws LinkyException {
if (!connected) {
initialize();
}
- String data = getData("%s?usage_point_id=%s".formatted(CONTRACT_URL, prmId));
- // String data = getData("%s&usage_point_id=%s".formatted(CONTRACT_URL, config.prmId));
+ String data = getData(formatUrl(CONTRACT_URL, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", CONTRACT_URL);
}
@@ -184,7 +189,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData("%s/%s/cache".formatted(ADDRESS_URL, prmId));
+ String data = getData(formatUrl(ADDRESS_URL, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", ADDRESS_URL);
}
@@ -204,7 +209,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData("%s/%s/cache".formatted(IDENTITY_URL, prmId));
+ String data = getData(formatUrl(IDENTITY_URL, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", IDENTITY_URL);
}
@@ -224,7 +229,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData("%s/%s/cache".formatted(CONTACT_URL, prmId));
+ String data = getData(formatUrl(CONTACT_URL, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", CONTACT_URL);
@@ -241,12 +246,12 @@ public class EnedisHttpApi {
}
}
- private MeterReading getMeasures(String userId, String prmId, LocalDate from, LocalDate to, String request)
+ private MeterReading getMeasures(String apiUrl, String userId, String prmId, LocalDate from, LocalDate to)
throws LinkyException {
String dtStart = from.format(API_DATE_FORMAT);
String dtEnd = to.format(API_DATE_FORMAT);
- String url = String.format(MEASURE_URL, request, prmId, dtStart, dtEnd);
+ String url = String.format(apiUrl, prmId, dtStart, dtEnd);
if (!connected) {
initialize();
}
@@ -268,11 +273,11 @@ public class EnedisHttpApi {
}
public MeterReading getEnergyData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(userId, prmId, from, to, "daily_consumption");
+ return getMeasures(MEASURE_DAILY_CONSUMPTION_URL, userId, prmId, from, to);
}
public MeterReading getPowerData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(userId, prmId, from, to, "daily_consumption_max_power");
+ return getMeasures(MEASURE_MAX_POWER_URL, userId, prmId, from, to);
}
public String getTempoData() throws LinkyException {
From bd28b37441764f6c5eb8ff7b5fb64bc9ebe6ecf9 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Mon, 3 Jun 2024 16:35:14 +0200
Subject: [PATCH 023/135] refactor binding
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyHandlerFactory.java | 5 +-
.../internal/handler/ApiBridgeHandler.java | 24 +++-
.../linky/internal/handler/LinkyHandler.java | 115 +++++++++++++-----
3 files changed, 107 insertions(+), 37 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index e85a829151d..ea4a91f73b6 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -37,8 +37,6 @@ import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.HttpService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
@@ -59,7 +57,8 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
private static final DateTimeFormatter LINKY_LOCALDATETIME_FORMATTER = DateTimeFormatter
.ofPattern("uuuu-MM-dd HH:mm:ss");
- private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
+ // private final String clientId = "e551937c-5250-48bc-b4a6-2323af68db92";
+ // private final String clientSecret = "";
private final HttpClientFactory httpClientFactory;
private final OAuthFactory oAuthFactory;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 212ce90187a..be9a0bf88ce 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -72,8 +72,6 @@ import com.google.gson.Gson;
public class ApiBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(ApiBridgeHandler.class);
- private static final int REFRESH_FIRST_HOUR_OF_DAY = 1;
-
private static final int REQUEST_BUFFER_SIZE = 8000;
private @NonNullByDefault({}) HttpService httpService;
@@ -124,7 +122,7 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
}
this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
- LinkyBindingConstants.ENEDIS_API_TOKEN_URL, LinkyBindingConstants.ENEDIS_AUTH_AUTHORIZE_URL,
+ LinkyBindingConstants.ENEDIS_API_TOKEN_URL_PREPROD, LinkyBindingConstants.ENEDIS_AUTHORIZE_URL_PREPROD,
LinkyBindingConstants.clientId, LinkyBindingConstants.clientSecret, LinkyBindingConstants.LINKY_SCOPES,
true);
@@ -285,11 +283,29 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
&& accessTokenResponse.getRefreshToken() != null;
}
- public String getToken() {
+ public String getToken() throws LinkyException {
+
AccessTokenResponse accesToken = getAccessTokenResponse();
+ if (accesToken == null) {
+ accesToken = getAccessTokenByClientCredentials();
+ }
+
+ if (accesToken == null) {
+ throw new LinkyException("no token");
+ }
+
return accesToken.getAccessToken();
}
+ private @Nullable AccessTokenResponse getAccessTokenByClientCredentials() {
+ try {
+ return oAuthService.getAccessTokenByClientCredentials(LinkyBindingConstants.LINKY_SCOPES);
+ } catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
+ logger.debug("Exception checking authorization: ", e);
+ return null;
+ }
+ }
+
private @Nullable AccessTokenResponse getAccessTokenResponse() {
try {
return oAuthService.getAccessTokenResponse();
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 7edd401b074..3cb0fb9d8cc 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@@ -37,6 +38,7 @@ import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
@@ -69,7 +71,7 @@ public class LinkyHandler extends BaseThingHandler {
private @NonNullByDefault({}) String prmId;
private @NonNullByDefault({}) String userId;
-
+ private @Nullable LinkyConfiguration config;
private @Nullable EnedisHttpApi enedisApi;
private enum Target {
@@ -140,40 +142,99 @@ public class LinkyHandler extends BaseThingHandler {
public void initialize() {
logger.debug("Initializing Linky handler.");
- ApiBridgeHandler bridgeHandler = (ApiBridgeHandler) getBridge().getHandler();
+ Bridge bridge = getBridge();
+ if (bridge == null) {
+ return;
+ }
+
+ ApiBridgeHandler bridgeHandler = (ApiBridgeHandler) bridge.getHandler();
+ if (bridgeHandler == null) {
+ return;
+ }
enedisApi = bridgeHandler.getEnedisApi();
updateStatus(ThingStatus.UNKNOWN);
- LinkyConfiguration config = getConfigAs(LinkyConfiguration.class);
+ config = getConfigAs(LinkyConfiguration.class);
if (config.seemsValid()) {
scheduler.submit(() -> {
try {
- PrmInfo prmInfo = enedisApi.getPrmInfo(config.prmId);
- updateProperties(Map.of(USER_ID, prmInfo.customerId, PUISSANCE,
- prmInfo.contractInfo.subscribedPower, PRM_ID, prmInfo.prmId));
+ EnedisHttpApi api = this.enedisApi;
+ LinkyConfiguration config = this.config;
- // prmId = thing.getProperties().get(PRM_ID);
- // userId = thing.getProperties().get(USER_ID);
+ if (api != null && config != null) {
+ PrmInfo prmInfo = api.getPrmInfo(config.prmId);
+ updateProperties(Map.of(USER_ID, prmInfo.customerId, PUISSANCE,
+ prmInfo.contractInfo.subscribedPower, PRM_ID, prmInfo.prmId));
- // updateData();
+ // prmId = thing.getProperties().get(PRM_ID);
+ // userId = thing.getProperties().get(USER_ID);
- // disconnect();
+ updateMetaData();
+ updateData();
- final LocalDateTime now = LocalDateTime.now();
- final LocalDateTime nextDayFirstTimeUpdate = now.plusDays(1).withHour(REFRESH_FIRST_HOUR_OF_DAY)
- .truncatedTo(ChronoUnit.HOURS);
+ disconnect();
- // refreshJob = scheduler.scheduleWithFixedDelay(this::updateData,
- // ChronoUnit.MINUTES.between(now, nextDayFirstTimeUpdate) % REFRESH_INTERVAL_IN_MIN + 1,
- // REFRESH_INTERVAL_IN_MIN, TimeUnit.MINUTES);
+ final LocalDateTime now = LocalDateTime.now();
+ final LocalDateTime nextDayFirstTimeUpdate = now.plusDays(1).withHour(REFRESH_FIRST_HOUR_OF_DAY)
+ .truncatedTo(ChronoUnit.HOURS);
- updateStatus(ThingStatus.ONLINE);
- } catch (Exception e) {
+ refreshJob = scheduler.scheduleWithFixedDelay(this::updateData,
+ ChronoUnit.MINUTES.between(now, nextDayFirstTimeUpdate) % REFRESH_INTERVAL_IN_MIN + 1,
+ REFRESH_INTERVAL_IN_MIN, TimeUnit.MINUTES);
+ }
+ } catch (LinkyException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
});
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "@text/offline.config-error-mandatory-settings");
+ }
+ }
+
+ private synchronized void updateMetaData() {
+ EnedisHttpApi api = this.enedisApi;
+ LinkyConfiguration config = this.config;
+
+ if (api != null && config != null) {
+ try {
+ PrmInfo info = api.getPrmInfo(config.prmId);
+ String title = info.identityInfo.title;
+ String firstName = info.identityInfo.firstname;
+ String lastName = info.identityInfo.lastname;
+
+ updateState(MAIN_IDENTITY, new StringType(title + " " + firstName + " " + lastName));
+
+ updateState(MAIN_CONTRACT_SEGMENT, new StringType(info.contractInfo.segment));
+ updateState(MAIN_CONTRACT_CONTRACT_STATUS, new StringType(info.contractInfo.contractStatus));
+ updateState(MAIN_CONTRACT_CONTRACT_TYPE, new StringType(info.contractInfo.contractType));
+ updateState(MAIN_CONTRACT_DISTRIBUTION_TARIFF, new StringType(info.contractInfo.distributionTariff));
+ updateState(MAIN_CONTRACT_LAST_ACTIVATION_DATE, new StringType(info.contractInfo.lastActivationDate));
+ updateState(MAIN_CONTRACT_LAST_DISTRIBUTION_TARIFF_CHANGE_DATE,
+ new StringType(info.contractInfo.lastDistributionTariffChangeDate));
+ updateState(MAIN_CONTRACT_OFF_PEAK_HOURS, new StringType(info.contractInfo.offpeakHours));
+ updateState(MAIN_CONTRACT_SEGMENT, new StringType(info.contractInfo.segment));
+ updateState(MAIN_CONTRACT_SUBSCRIBED_POWER, new StringType(info.contractInfo.subscribedPower));
+
+ updateState(MAIN_USAGEPOINT_ID, new StringType(info.usagePointInfo.usagePointId));
+ updateState(MAIN_USAGEPOINT_STATUS, new StringType(info.usagePointInfo.usagePointStatus));
+ updateState(MAIN_USAGEPOINT_METER_TYPE, new StringType(info.usagePointInfo.meterType));
+
+ updateState(MAIN_USAGEPOINT_METER_ADDRESS_CITY, new StringType(info.addressInfo.city));
+ updateState(MAIN_USAGEPOINT_METER_ADDRESS_COUNTRY, new StringType(info.addressInfo.country));
+ updateState(MAIN_USAGEPOINT_METER_ADDRESS_POSTAL_CODE, new StringType(info.addressInfo.postalCode));
+ updateState(MAIN_USAGEPOINT_METER_ADDRESS_INSEE_CODE, new StringType(info.addressInfo.inseeCode));
+ updateState(MAIN_USAGEPOINT_METER_ADDRESS_STREET, new StringType(info.addressInfo.street));
+
+ updateState(MAIN_CONTACT_MAIL, new StringType(info.contactInfo.email));
+ updateState(MAIN_CONTACT_PHONE, new StringType(info.contactInfo.phone));
+
+ } catch (LinkyException e) {
+ logger.debug("Exception when getting consumption data: {}", e.getMessage(), e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ }
}
}
@@ -189,7 +250,7 @@ public class LinkyHandler extends BaseThingHandler {
// updateMonthlyData();
// updateYearlyData();
- String tempoData = getTempoData();
+ // String tempoData = getTempoData();
// LinkedTreeMap obj = gson.fromJson(tempoData, LinkedTreeMap.class);
@@ -205,8 +266,6 @@ public class LinkyHandler extends BaseThingHandler {
* }
*/
- updateState(TEST_SELECT, new StringType(tempoData));
-
if (!connectedBefore && isConnected()) {
disconnect();
}
@@ -407,7 +466,6 @@ public class LinkyHandler extends BaseThingHandler {
to.format(DateTimeFormatter.ISO_LOCAL_DATE));
EnedisHttpApi api = this.enedisApi;
-
if (api != null) {
try {
MeterReading meterReading = api.getEnergyData(userId, prmId, from, to);
@@ -418,15 +476,14 @@ public class LinkyHandler extends BaseThingHandler {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
-
return null;
}
private @Nullable MeterReading getPowerData(LocalDate from, LocalDate to) {
logger.debug("getPowerData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
to.format(DateTimeFormatter.ISO_LOCAL_DATE));
- EnedisHttpApi api = this.enedisApi;
+ EnedisHttpApi api = this.enedisApi;
if (api != null) {
try {
MeterReading meterReading = api.getPowerData(userId, prmId, from, to);
@@ -437,10 +494,10 @@ public class LinkyHandler extends BaseThingHandler {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
-
return null;
}
+ /*
private @Nullable String getTempoData() {
logger.debug("getTempoData from");
@@ -455,18 +512,16 @@ public class LinkyHandler extends BaseThingHandler {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
-
return null;
}
-
+ */
+
private boolean isConnected() {
EnedisHttpApi api = this.enedisApi;
- // return api == null ? false : api.isConnected();
- return true;
+ return api == null ? false : api.isConnected();
}
private void disconnect() {
-
EnedisHttpApi api = this.enedisApi;
if (api != null) {
try {
From 4fa244e771329a3406791d55eb499169f46439f0 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Mon, 3 Jun 2024 17:06:48 +0200
Subject: [PATCH 024/135] code cleanup
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyBindingConstants.java | 6 ---
.../linky/internal/LinkyHandlerFactory.java | 3 --
.../linky/internal/api/EnedisHttpApi.java | 12 ++---
.../internal/handler/ApiBridgeHandler.java | 33 ++-----------
.../linky/internal/handler/LinkyHandler.java | 48 +++++++++----------
5 files changed, 33 insertions(+), 69 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index f80e04c98a0..3958d9cbc63 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -31,12 +31,6 @@ public class LinkyBindingConstants {
public static final String BINDING_ID = "linky";
- // public final String clientId = "e551937c-5250-48bc-b4a6-2323af68db92";
- // public final String clientSecret = "";
-
- public static final String clientId = "_88uJnEjEs_IMf4bjGZJV6gGxYga";
- public static final String clientSecret = "6lsPfCmu0fEXuKYy3e0e6w8ydIca";
-
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_API_ENEDIS_BRIDGE = new ThingTypeUID(BINDING_ID, "EnedisBridge");
public static final ThingTypeUID THING_TYPE_API_MYELECTRICALDATA_BRIDGE = new ThingTypeUID(BINDING_ID,
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index ea4a91f73b6..b33412af330 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -57,9 +57,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
private static final DateTimeFormatter LINKY_LOCALDATETIME_FORMATTER = DateTimeFormatter
.ofPattern("uuuu-MM-dd HH:mm:ss");
- // private final String clientId = "e551937c-5250-48bc-b4a6-2323af68db92";
- // private final String clientSecret = "";
-
private final HttpClientFactory httpClientFactory;
private final OAuthFactory oAuthFactory;
private final HttpService httpService;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 917b2d7565d..f5eefb0d091 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -161,7 +161,6 @@ public class EnedisHttpApi {
}
public String formatUrl(String apiUrl, String prmId) {
- // return "%s/%s/cache".formatted(apiUrl, config.prmId);
return "%s?usage_point_id=%s".formatted(apiUrl, prmId);
}
@@ -246,8 +245,7 @@ public class EnedisHttpApi {
}
}
- private MeterReading getMeasures(String apiUrl, String userId, String prmId, LocalDate from, LocalDate to)
- throws LinkyException {
+ private MeterReading getMeasures(String apiUrl, String prmId, LocalDate from, LocalDate to) throws LinkyException {
String dtStart = from.format(API_DATE_FORMAT);
String dtEnd = to.format(API_DATE_FORMAT);
@@ -272,12 +270,12 @@ public class EnedisHttpApi {
}
}
- public MeterReading getEnergyData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(MEASURE_DAILY_CONSUMPTION_URL, userId, prmId, from, to);
+ public MeterReading getEnergyData(String prmId, LocalDate from, LocalDate to) throws LinkyException {
+ return getMeasures(MEASURE_DAILY_CONSUMPTION_URL, prmId, from, to);
}
- public MeterReading getPowerData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(MEASURE_MAX_POWER_URL, userId, prmId, from, to);
+ public MeterReading getPowerData(String prmId, LocalDate from, LocalDate to) throws LinkyException {
+ return getMeasures(MEASURE_MAX_POWER_URL, prmId, from, to);
}
public String getTempoData() throws LinkyException {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index be9a0bf88ce..e955bbd3002 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -35,6 +35,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.openhab.binding.linky.internal.LinkyAuthServlet;
import org.openhab.binding.linky.internal.LinkyBindingConstants;
+import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.api.EnedisHttpApi;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
@@ -84,6 +85,7 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
private final ThingRegistry thingRegistry;
private static @Nullable HttpServlet servlet;
+ private @Nullable LinkyConfiguration config;
private static final String TEMPLATE_PATH = "templates/";
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
@@ -121,10 +123,11 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
}
+ config = getConfigAs(LinkyConfiguration.class);
+
this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
LinkyBindingConstants.ENEDIS_API_TOKEN_URL_PREPROD, LinkyBindingConstants.ENEDIS_AUTHORIZE_URL_PREPROD,
- LinkyBindingConstants.clientId, LinkyBindingConstants.clientSecret, LinkyBindingConstants.LINKY_SCOPES,
- true);
+ config.clientId, config.clientSecret, LinkyBindingConstants.LINKY_SCOPES, true);
registerServlet();
@@ -248,32 +251,6 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
} catch (final OAuthResponseException e) {
throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
}
-
- /*
- * String token = EnedisHttpApi.getToken(httpClient, LinkyBindingConstants.clientId, reqCode);
- *
- * logger.debug("token: {}", token);
- *
- * Collection col = this.thingRegistry.getAll();
- * for (Thing thing : col) {
- * if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
- * Configuration config = thing.getConfiguration();
- * String prmId = (String) config.get("prmId");
- *
- * if (!prmId.equals(reqCode)) {
- * continue;
- * }
- *
- * config.put("token", token);
- * LinkyHandler handler = (LinkyHandler) thing.getHandler();
- * if (handler != null) {
- * handler.saveConfiguration(config);
- * }
- * }
- * }
- *
- * return token;
- */
}
public boolean isAuthorized() {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 3cb0fb9d8cc..37f52362a8c 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -68,9 +68,6 @@ public class LinkyHandler extends BaseThingHandler {
private final ExpiringDayCache dailyConsumption;
private @Nullable ScheduledFuture> refreshJob;
-
- private @NonNullByDefault({}) String prmId;
- private @NonNullByDefault({}) String userId;
private @Nullable LinkyConfiguration config;
private @Nullable EnedisHttpApi enedisApi;
@@ -168,9 +165,6 @@ public class LinkyHandler extends BaseThingHandler {
updateProperties(Map.of(USER_ID, prmInfo.customerId, PUISSANCE,
prmInfo.contractInfo.subscribedPower, PRM_ID, prmInfo.prmId));
- // prmId = thing.getProperties().get(PRM_ID);
- // userId = thing.getProperties().get(USER_ID);
-
updateMetaData();
updateData();
@@ -194,6 +188,10 @@ public class LinkyHandler extends BaseThingHandler {
}
}
+ public @Nullable LinkyConfiguration getLinkyConfig() {
+ return config;
+ }
+
private synchronized void updateMetaData() {
EnedisHttpApi api = this.enedisApi;
LinkyConfiguration config = this.config;
@@ -468,7 +466,7 @@ public class LinkyHandler extends BaseThingHandler {
EnedisHttpApi api = this.enedisApi;
if (api != null) {
try {
- MeterReading meterReading = api.getEnergyData(userId, prmId, from, to);
+ MeterReading meterReading = api.getEnergyData(config.prmId, from, to);
updateStatus(ThingStatus.ONLINE);
return meterReading;
} catch (LinkyException e) {
@@ -486,7 +484,7 @@ public class LinkyHandler extends BaseThingHandler {
EnedisHttpApi api = this.enedisApi;
if (api != null) {
try {
- MeterReading meterReading = api.getPowerData(userId, prmId, from, to);
+ MeterReading meterReading = api.getPowerData(config.prmId, from, to);
updateStatus(ThingStatus.ONLINE);
return meterReading;
} catch (LinkyException e) {
@@ -498,24 +496,24 @@ public class LinkyHandler extends BaseThingHandler {
}
/*
- private @Nullable String getTempoData() {
- logger.debug("getTempoData from");
+ * private @Nullable String getTempoData() {
+ * logger.debug("getTempoData from");
+ *
+ * EnedisHttpApi api = this.enedisApi;
+ * if (api != null) {
+ * try {
+ * String result = api.getTempoData();
+ * updateStatus(ThingStatus.ONLINE);
+ * return result;
+ * } catch (LinkyException e) {
+ * logger.debug("Exception when getting power data: {}", e.getMessage(), e);
+ * updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ * }
+ * }
+ * return null;
+ * }
+ */
- EnedisHttpApi api = this.enedisApi;
- if (api != null) {
- try {
- String result = api.getTempoData();
- updateStatus(ThingStatus.ONLINE);
- return result;
- } catch (LinkyException e) {
- logger.debug("Exception when getting power data: {}", e.getMessage(), e);
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
- }
- }
- return null;
- }
- */
-
private boolean isConnected() {
EnedisHttpApi api = this.enedisApi;
return api == null ? false : api.isConnected();
From fe75abe9790c8951b9961d876ec311309792518c Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Mon, 3 Jun 2024 18:10:56 +0200
Subject: [PATCH 025/135] fix MyElectricalData api
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 129 ++++++++++--------
.../internal/handler/ApiBridgeHandler.java | 23 +++-
.../internal/handler/EnedisBridgeHandler.java | 57 ++++++++
.../MyElectricalDataBridgeHandler.java | 57 ++++++++
4 files changed, 209 insertions(+), 57 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index f5eefb0d091..268e54d743c 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -31,6 +31,7 @@ import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AddressInfo;
import org.openhab.binding.linky.internal.dto.ContactInfo;
+import org.openhab.binding.linky.internal.dto.Contracts;
import org.openhab.binding.linky.internal.dto.Customer;
import org.openhab.binding.linky.internal.dto.CustomerIdResponse;
import org.openhab.binding.linky.internal.dto.CustomerReponse;
@@ -40,7 +41,9 @@ import org.openhab.binding.linky.internal.dto.MeterResponse;
import org.openhab.binding.linky.internal.dto.PrmInfo;
import org.openhab.binding.linky.internal.dto.TempoResponse;
import org.openhab.binding.linky.internal.dto.UsagePoint;
+import org.openhab.binding.linky.internal.dto.UsagePointDetails;
import org.openhab.binding.linky.internal.handler.ApiBridgeHandler;
+import org.openhab.binding.linky.internal.handler.MyElectricalDataBridgeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,22 +61,6 @@ public class EnedisHttpApi {
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- private static final String BASE_URL = "https://ext.prod-sandbox.api.enedis.fr/";
-
- private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts";
- private static final String IDENTITY_URL = BASE_URL + "customers_i/v5/identity";
- private static final String CONTACT_URL = BASE_URL + "customers_cd/v5/contact_data";
- private static final String ADDRESS_URL = BASE_URL + "customers_upa/v5/usage_points/addresses";
- private static final String MEASURE_DAILY_CONSUMPTION_URL = BASE_URL
- + "metering_data_dc/v5/daily_consumption?usage_point_id=%s&start=%s&end=%s";
- private static final String MEASURE_MAX_POWER_URL = BASE_URL
- + "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
-
- private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
-
- private static final String TOKEN_URL = BASE_URL
- + "v1/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=na&user_type=na&state=na&person_id=-1&usage_points_id=%s";
-
private final Logger logger = LoggerFactory.getLogger(EnedisHttpApi.class);
private final Gson gson;
private final HttpClient httpClient;
@@ -106,22 +93,23 @@ public class EnedisHttpApi {
}
private String getData(String url) throws LinkyException {
- return getData(url, httpClient, apiBridgeHandler.getToken());
+ return getData(apiBridgeHandler, url, httpClient, apiBridgeHandler.getToken());
}
- private static String getData(String url, HttpClient httpClient, String token) throws LinkyException {
+ private static String getData(ApiBridgeHandler apiBridgeHandler, String url, HttpClient httpClient, String token)
+ throws LinkyException {
try {
Request request = httpClient.newRequest(url);
request = request.method(HttpMethod.GET);
if (!("".equals(token))) {
- request = request.header("Authorization", "Bearer " + token);
+ request = request.header("Authorization", "" + token);
request = request.header("Accept", "application/json");
}
ContentResponse result = request.send();
if (result.getStatus() == 307) {
String loc = result.getHeaders().get("Location");
- String newUrl = BASE_URL + loc.substring(1);
+ String newUrl = apiBridgeHandler.getBaseUrl() + loc.substring(1);
request = httpClient.newRequest(newUrl);
request = request.method(HttpMethod.GET);
result = request.send();
@@ -145,32 +133,71 @@ public class EnedisHttpApi {
public PrmInfo getPrmInfo(String prmId) throws LinkyException {
PrmInfo result = new PrmInfo();
- Customer customer = getCustomer(prmId);
- UsagePoint usagePoint = customer.usagePoints[0];
- result.contractInfo = usagePoint.contracts;
- result.usagePointInfo = usagePoint.usagePoint;
- result.identityInfo = getIdentity(prmId);
- result.addressInfo = getAddress(prmId);
- result.contactInfo = getContact(prmId);
+ if (apiBridgeHandler instanceof MyElectricalDataBridgeHandler) {
+ result.contractInfo = new Contracts();
+ result.addressInfo = new AddressInfo();
+ result.contactInfo = new ContactInfo();
+ result.identityInfo = new IdentityInfo();
+ result.usagePointInfo = new UsagePointDetails();
- result.prmId = result.usagePointInfo.usagePointId;
- result.customerId = customer.customerId;
+ result.contractInfo.subscribedPower = "12Kva";
+ result.contactInfo.email = "lxxyyy@domain.net";
+ result.contactInfo.phone = "--.--.--.--.--";
+ result.contractInfo.contractStatus = "unknow";
+ result.contractInfo.contractType = "unknow";
+ result.contractInfo.distributionTariff = "unknow";
+ result.contractInfo.lastActivationDate = "unknow";
+ result.contractInfo.lastDistributionTariffChangeDate = "unknow";
+ result.contractInfo.segment = "unknow";
+ result.contractInfo.offpeakHours = "unknow";
+
+ result.addressInfo.city = "Ville";
+ result.addressInfo.country = "France";
+ result.addressInfo.postalCode = "xxxxx";
+ result.addressInfo.inseeCode = "0";
+ result.addressInfo.street = "xx Rue de yyyyyy";
+
+ result.identityInfo.firstname = "Laurent";
+ result.identityInfo.lastname = "ARNAL";
+ result.identityInfo.title = "M.";
+
+ result.usagePointInfo.meterType = "unknow";
+ result.usagePointInfo.usagePointId = "unknow";
+ result.usagePointInfo.usagePointStatus = "unknow";
+
+ result.prmId = prmId;
+ result.customerId = "xxxxxxxxxx";
+
+ } else {
+ Customer customer = getCustomer(prmId);
+ UsagePoint usagePoint = customer.usagePoints[0];
+
+ result.contractInfo = usagePoint.contracts;
+ result.usagePointInfo = usagePoint.usagePoint;
+ result.identityInfo = getIdentity(prmId);
+ result.addressInfo = getAddress(prmId);
+ result.contactInfo = getContact(prmId);
+
+ result.prmId = result.usagePointInfo.usagePointId;
+ result.customerId = customer.customerId;
+ }
return result;
}
public String formatUrl(String apiUrl, String prmId) {
- return "%s?usage_point_id=%s".formatted(apiUrl, prmId);
+ return apiUrl.formatted(prmId);
}
public Customer getCustomer(String prmId) throws LinkyException {
if (!connected) {
initialize();
}
- String data = getData(formatUrl(CONTRACT_URL, prmId));
+ String contractUrl = apiBridgeHandler.getContractUrl();
+ String data = getData(formatUrl(contractUrl, prmId));
if (data.isEmpty()) {
- throw new LinkyException("Requesting '%s' returned an empty response", CONTRACT_URL);
+ throw new LinkyException("Requesting '%s' returned an empty response", contractUrl);
}
try {
CustomerReponse cResponse = gson.fromJson(data, CustomerReponse.class);
@@ -180,7 +207,7 @@ public class EnedisHttpApi {
return cResponse.customer;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", CONTRACT_URL);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", contractUrl);
}
}
@@ -188,9 +215,10 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(formatUrl(ADDRESS_URL, prmId));
+ String addressUrl = apiBridgeHandler.getAddressUrl();
+ String data = getData(formatUrl(addressUrl, prmId));
if (data.isEmpty()) {
- throw new LinkyException("Requesting '%s' returned an empty response", ADDRESS_URL);
+ throw new LinkyException("Requesting '%s' returned an empty response", addressUrl);
}
try {
CustomerReponse cResponse = gson.fromJson(data, CustomerReponse.class);
@@ -200,7 +228,7 @@ public class EnedisHttpApi {
return cResponse.customer.usagePoints[0].usagePoint.usagePointAddresses;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", ADDRESS_URL);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", addressUrl);
}
}
@@ -208,9 +236,10 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(formatUrl(IDENTITY_URL, prmId));
+ String identityUrl = apiBridgeHandler.getIdentityUrl();
+ String data = getData(formatUrl(identityUrl, prmId));
if (data.isEmpty()) {
- throw new LinkyException("Requesting '%s' returned an empty response", IDENTITY_URL);
+ throw new LinkyException("Requesting '%s' returned an empty response", identityUrl);
}
try {
CustomerIdResponse iResponse = gson.fromJson(data, CustomerIdResponse.class);
@@ -220,7 +249,7 @@ public class EnedisHttpApi {
return iResponse.identity.naturalPerson;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", IDENTITY_URL);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", identityUrl);
}
}
@@ -228,10 +257,11 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(formatUrl(CONTACT_URL, prmId));
+ String contactUrl = apiBridgeHandler.getContactUrl();
+ String data = getData(formatUrl(contactUrl, prmId));
if (data.isEmpty()) {
- throw new LinkyException("Requesting '%s' returned an empty response", CONTACT_URL);
+ throw new LinkyException("Requesting '%s' returned an empty response", contactUrl);
}
try {
CustomerIdResponse cResponse = gson.fromJson(data, CustomerIdResponse.class);
@@ -241,7 +271,7 @@ public class EnedisHttpApi {
return cResponse.contactData;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", CONTACT_URL);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", contactUrl);
}
}
@@ -271,15 +301,15 @@ public class EnedisHttpApi {
}
public MeterReading getEnergyData(String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(MEASURE_DAILY_CONSUMPTION_URL, prmId, from, to);
+ return getMeasures(apiBridgeHandler.getDailyConsumptionUrl(), prmId, from, to);
}
public MeterReading getPowerData(String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(MEASURE_MAX_POWER_URL, prmId, from, to);
+ return getMeasures(apiBridgeHandler.getMaxPowerUrl(), prmId, from, to);
}
public String getTempoData() throws LinkyException {
- String url = String.format(TEMPO_URL, "2024-01-01", "2024-01-31");
+ String url = String.format(apiBridgeHandler.getTempoUrl(), "2024-01-01", "2024-01-31");
if (!connected) {
initialize();
}
@@ -305,13 +335,4 @@ public class EnedisHttpApi {
// return data;
}
- public static String getToken(HttpClient httpClient, String clientId, String prmId) {
- try {
- String url = String.format(TOKEN_URL, clientId, prmId);
- String token = getData(url, httpClient, "");
- return token;
- } catch (LinkyException e) {
- return "";
- }
- }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index e955bbd3002..15e1f0a105f 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -70,7 +70,7 @@ import com.google.gson.Gson;
* @author Laurent Arnal - Initial contribution
*/
@NonNullByDefault
-public class ApiBridgeHandler extends BaseBridgeHandler {
+public abstract class ApiBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(ApiBridgeHandler.class);
private static final int REQUEST_BUFFER_SIZE = 8000;
@@ -85,7 +85,7 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
private final ThingRegistry thingRegistry;
private static @Nullable HttpServlet servlet;
- private @Nullable LinkyConfiguration config;
+ protected @Nullable LinkyConfiguration config;
private static final String TEMPLATE_PATH = "templates/";
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
@@ -271,7 +271,7 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
throw new LinkyException("no token");
}
- return accesToken.getAccessToken();
+ return "Bearer " + accesToken.getAccessToken();
}
private @Nullable AccessTokenResponse getAccessTokenByClientCredentials() {
@@ -321,4 +321,21 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
return result;
}
+
+ public abstract String getBaseUrl();
+
+ public abstract String getContactUrl();
+
+ public abstract String getContractUrl();
+
+ public abstract String getIdentityUrl();
+
+ public abstract String getAddressUrl();
+
+ public abstract String getDailyConsumptionUrl();
+
+ public abstract String getMaxPowerUrl();
+
+ public abstract String getTempoUrl();
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index 90c3232f721..188e724b6ff 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -34,6 +34,23 @@ import com.google.gson.Gson;
public class EnedisBridgeHandler extends ApiBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(EnedisBridgeHandler.class);
+ private static final String BASE_URL = "https://ext.prod-sandbox.api.enedis.fr/";
+
+ private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts?usage_point_id=%s";
+ private static final String IDENTITY_URL = BASE_URL + "customers_i/v5/identity?usage_point_id=%s";
+ private static final String CONTACT_URL = BASE_URL + "customers_cd/v5/contact_data?usage_point_id=%s";
+ private static final String ADDRESS_URL = BASE_URL + "customers_upa/v5/usage_points/addresses?usage_point_id=%s";
+ private static final String MEASURE_DAILY_CONSUMPTION_URL = BASE_URL
+ + "metering_data_dc/v5/daily_consumption?usage_point_id=%s&start=%s&end=%s";
+ private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ + "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
+
+ private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
+
+ // private static final String TOKEN_URL = BASE_URL
+ // +
+ // "v1/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=na&user_type=na&state=na&person_id=-1&usage_points_id=%s";
+
public EnedisBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
@@ -51,4 +68,44 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
super.dispose();
}
+
+ @Override
+ public String getBaseUrl() {
+ return BASE_URL;
+ }
+
+ @Override
+ public String getContactUrl() {
+ return CONTACT_URL;
+ }
+
+ @Override
+ public String getContractUrl() {
+ return CONTRACT_URL;
+ }
+
+ @Override
+ public String getIdentityUrl() {
+ return IDENTITY_URL;
+ }
+
+ @Override
+ public String getAddressUrl() {
+ return ADDRESS_URL;
+ }
+
+ @Override
+ public String getDailyConsumptionUrl() {
+ return MEASURE_DAILY_CONSUMPTION_URL;
+ }
+
+ @Override
+ public String getMaxPowerUrl() {
+ return MEASURE_MAX_POWER_URL;
+ }
+
+ @Override
+ public String getTempoUrl() {
+ return TEMPO_URL;
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index 771f95f6cbd..bca359736eb 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -13,6 +13,7 @@
package org.openhab.binding.linky.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
@@ -34,6 +35,17 @@ import com.google.gson.Gson;
public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(MyElectricalDataBridgeHandler.class);
+ private static final String BASE_URL = "https://www.myelectricaldata.fr/";
+
+ private static final String CONTRACT_URL = BASE_URL + "contracts/%s/";
+ private static final String IDENTITY_URL = BASE_URL + "identity/%s/";
+ private static final String CONTACT_URL = BASE_URL + "contact/%s/";
+ private static final String ADDRESS_URL = BASE_URL + "addresses/%s/";
+ private static final String MEASURE_DAILY_CONSUMPTION_URL = BASE_URL + "daily_consumption/%s/start/%s/end/%s";
+ private static final String MEASURE_MAX_POWER_URL = BASE_URL + "daily_consumption_max_power/%s/start/%s/end/%s";
+
+ private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
+
public MyElectricalDataBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
@@ -51,4 +63,49 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
super.dispose();
}
+
+ @Override
+ public String getToken() throws LinkyException {
+ return config.token;
+ }
+
+ @Override
+ public String getBaseUrl() {
+ return BASE_URL;
+ }
+
+ @Override
+ public String getContactUrl() {
+ return CONTACT_URL;
+ }
+
+ @Override
+ public String getContractUrl() {
+ return CONTRACT_URL;
+ }
+
+ @Override
+ public String getIdentityUrl() {
+ return IDENTITY_URL;
+ }
+
+ @Override
+ public String getAddressUrl() {
+ return ADDRESS_URL;
+ }
+
+ @Override
+ public String getDailyConsumptionUrl() {
+ return MEASURE_DAILY_CONSUMPTION_URL;
+ }
+
+ @Override
+ public String getMaxPowerUrl() {
+ return MEASURE_MAX_POWER_URL;
+ }
+
+ @Override
+ public String getTempoUrl() {
+ return TEMPO_URL;
+ }
}
From d1920f21db5b85e630833a6205fb31b1dc0afb9a Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Tue, 4 Jun 2024 15:51:56 +0200
Subject: [PATCH 026/135] review consent / authorisation flow to be compatible
with both MyElectricalData and Enedis
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyAuthServlet.java | 1 +
.../linky/internal/LinkyBindingConstants.java | 16 ++---
.../linky/internal/api/EnedisHttpApi.java | 55 +++++++++-------
.../internal/handler/ApiBridgeHandler.java | 43 +++++++------
.../internal/handler/EnedisBridgeHandler.java | 31 +++++++--
.../linky/internal/handler/LinkyHandler.java | 8 +--
.../MyElectricalDataBridgeHandler.java | 63 ++++++++++++++++++-
.../resources/OH-INF/thing/thing-types.xml | 12 ++--
8 files changed, 161 insertions(+), 68 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
index 65357178efc..2feb6f85275 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyAuthServlet.java
@@ -69,6 +69,7 @@ public class LinkyAuthServlet extends HttpServlet {
public LinkyAuthServlet(ApiBridgeHandler apiBridgeHandler, String indexTemplate) {
this.indexTemplate = indexTemplate;
+ this.apiBridgeHandler = apiBridgeHandler;
}
@Override
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 3958d9cbc63..77465e8eb46 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -106,18 +106,20 @@ public class LinkyBindingConstants {
// "r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*",
// "r:installedapps", "w:installedapps"
- // List of Linky services related urls, information
- public static final String LINKY_MYELECTRICALDATA_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
- public static final String LINKY_MYELECTRICALDATA_AUTHORIZE_URL = LINKY_MYELECTRICALDATA_ACCOUNT_URL
- + "v1/oauth2/authorize";
- public static final String LINKY_MYELECTRICALDATA_API_TOKEN_URL = LINKY_MYELECTRICALDATA_ACCOUNT_URL + "token";
-
public static final String ENEDIS_ACCOUNT_URL_PROD = "https://mon-compte-particulier.enedis.fr/";
public static final String ENEDIS_AUTHORIZE_URL_PROD = ENEDIS_ACCOUNT_URL_PROD + "dataconnect/v1/oauth2/authorize";
public static final String ENEDIS_API_TOKEN_URL_PROD = ENEDIS_ACCOUNT_URL_PROD + "oauth2/v3/token";
public static final String ENEDIS_ACCOUNT_URL_PREPROD = "https://ext.prod-sandbox.api.enedis.fr/";
- public static final String ENEDIS_AUTHORIZE_URL_PREPROD = ENEDIS_ACCOUNT_URL_PREPROD
+ public static final String ENEDIS_AUTHORIZE_URL_PREPROD = ENEDIS_ACCOUNT_URL_PROD
+ "dataconnect/v1/oauth2/authorize";
public static final String ENEDIS_API_TOKEN_URL_PREPROD = ENEDIS_ACCOUNT_URL_PREPROD + "oauth2/v3/token";
+
+ // List of Linky services related urls, information
+ public static final String LINKY_MYELECTRICALDATA_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
+ // public static final String LINKY_MYELECTRICALDATA_AUTHORIZE_URL = LINKY_MYELECTRICALDATA_ACCOUNT_URL
+ // + "v1/oauth2/authorize?usage_points_id=21454992660003&user_type=aa&person_id=-1";
+ public static final String LINKY_MYELECTRICALDATA_AUTHORIZE_URL = ENEDIS_AUTHORIZE_URL_PROD;
+ public static final String LINKY_MYELECTRICALDATA_API_TOKEN_URL = LINKY_MYELECTRICALDATA_ACCOUNT_URL
+ + "v1/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=na&user_type=na&state=na&person_id=-1&usage_points_id=%s";
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 268e54d743c..0bdab767a17 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -43,6 +43,7 @@ import org.openhab.binding.linky.internal.dto.TempoResponse;
import org.openhab.binding.linky.internal.dto.UsagePoint;
import org.openhab.binding.linky.internal.dto.UsagePointDetails;
import org.openhab.binding.linky.internal.handler.ApiBridgeHandler;
+import org.openhab.binding.linky.internal.handler.LinkyHandler;
import org.openhab.binding.linky.internal.handler.MyElectricalDataBridgeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -92,8 +93,12 @@ public class EnedisHttpApi {
disconnect();
}
- private String getData(String url) throws LinkyException {
- return getData(apiBridgeHandler, url, httpClient, apiBridgeHandler.getToken());
+ public String getData(LinkyHandler handler, String url) throws LinkyException {
+ return getData(apiBridgeHandler, url, httpClient, apiBridgeHandler.getToken(handler));
+ }
+
+ public String getData(String url) throws LinkyException {
+ return getData(apiBridgeHandler, url, httpClient, "");
}
private static String getData(ApiBridgeHandler apiBridgeHandler, String url, HttpClient httpClient, String token)
@@ -131,7 +136,7 @@ public class EnedisHttpApi {
}
}
- public PrmInfo getPrmInfo(String prmId) throws LinkyException {
+ public PrmInfo getPrmInfo(LinkyHandler handler, String prmId) throws LinkyException {
PrmInfo result = new PrmInfo();
if (apiBridgeHandler instanceof MyElectricalDataBridgeHandler) {
@@ -170,14 +175,14 @@ public class EnedisHttpApi {
result.customerId = "xxxxxxxxxx";
} else {
- Customer customer = getCustomer(prmId);
+ Customer customer = getCustomer(handler, prmId);
UsagePoint usagePoint = customer.usagePoints[0];
result.contractInfo = usagePoint.contracts;
result.usagePointInfo = usagePoint.usagePoint;
- result.identityInfo = getIdentity(prmId);
- result.addressInfo = getAddress(prmId);
- result.contactInfo = getContact(prmId);
+ result.identityInfo = getIdentity(handler, prmId);
+ result.addressInfo = getAddress(handler, prmId);
+ result.contactInfo = getContact(handler, prmId);
result.prmId = result.usagePointInfo.usagePointId;
result.customerId = customer.customerId;
@@ -190,12 +195,12 @@ public class EnedisHttpApi {
return apiUrl.formatted(prmId);
}
- public Customer getCustomer(String prmId) throws LinkyException {
+ public Customer getCustomer(LinkyHandler handler, String prmId) throws LinkyException {
if (!connected) {
initialize();
}
String contractUrl = apiBridgeHandler.getContractUrl();
- String data = getData(formatUrl(contractUrl, prmId));
+ String data = getData(handler, formatUrl(contractUrl, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", contractUrl);
}
@@ -211,12 +216,12 @@ public class EnedisHttpApi {
}
}
- public AddressInfo getAddress(String prmId) throws LinkyException {
+ public AddressInfo getAddress(LinkyHandler handler, String prmId) throws LinkyException {
if (!connected) {
initialize();
}
String addressUrl = apiBridgeHandler.getAddressUrl();
- String data = getData(formatUrl(addressUrl, prmId));
+ String data = getData(handler, formatUrl(addressUrl, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", addressUrl);
}
@@ -232,12 +237,12 @@ public class EnedisHttpApi {
}
}
- public IdentityInfo getIdentity(String prmId) throws LinkyException {
+ public IdentityInfo getIdentity(LinkyHandler handler, String prmId) throws LinkyException {
if (!connected) {
initialize();
}
String identityUrl = apiBridgeHandler.getIdentityUrl();
- String data = getData(formatUrl(identityUrl, prmId));
+ String data = getData(handler, formatUrl(identityUrl, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", identityUrl);
}
@@ -253,12 +258,12 @@ public class EnedisHttpApi {
}
}
- public ContactInfo getContact(String prmId) throws LinkyException {
+ public ContactInfo getContact(LinkyHandler handler, String prmId) throws LinkyException {
if (!connected) {
initialize();
}
String contactUrl = apiBridgeHandler.getContactUrl();
- String data = getData(formatUrl(contactUrl, prmId));
+ String data = getData(handler, formatUrl(contactUrl, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", contactUrl);
@@ -275,7 +280,8 @@ public class EnedisHttpApi {
}
}
- private MeterReading getMeasures(String apiUrl, String prmId, LocalDate from, LocalDate to) throws LinkyException {
+ private MeterReading getMeasures(LinkyHandler handler, String apiUrl, String prmId, LocalDate from, LocalDate to)
+ throws LinkyException {
String dtStart = from.format(API_DATE_FORMAT);
String dtEnd = to.format(API_DATE_FORMAT);
@@ -283,7 +289,7 @@ public class EnedisHttpApi {
if (!connected) {
initialize();
}
- String data = getData(url);
+ String data = getData(handler, url);
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", url);
}
@@ -300,20 +306,22 @@ public class EnedisHttpApi {
}
}
- public MeterReading getEnergyData(String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(apiBridgeHandler.getDailyConsumptionUrl(), prmId, from, to);
+ public MeterReading getEnergyData(LinkyHandler handler, String prmId, LocalDate from, LocalDate to)
+ throws LinkyException {
+ return getMeasures(handler, apiBridgeHandler.getDailyConsumptionUrl(), prmId, from, to);
}
- public MeterReading getPowerData(String prmId, LocalDate from, LocalDate to) throws LinkyException {
- return getMeasures(apiBridgeHandler.getMaxPowerUrl(), prmId, from, to);
+ public MeterReading getPowerData(LinkyHandler handler, String prmId, LocalDate from, LocalDate to)
+ throws LinkyException {
+ return getMeasures(handler, apiBridgeHandler.getMaxPowerUrl(), prmId, from, to);
}
- public String getTempoData() throws LinkyException {
+ public String getTempoData(LinkyHandler handler) throws LinkyException {
String url = String.format(apiBridgeHandler.getTempoUrl(), "2024-01-01", "2024-01-31");
if (!connected) {
initialize();
}
- String data = getData(url);
+ String data = getData(handler, url);
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", url);
}
@@ -334,5 +342,4 @@ public class EnedisHttpApi {
// return data;
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 15e1f0a105f..c8503879ab3 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -82,7 +82,7 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
private @Nullable EnedisHttpApi enedisApi;
private final Gson gson;
private OAuthClientService oAuthService;
- private final ThingRegistry thingRegistry;
+ protected final ThingRegistry thingRegistry;
private static @Nullable HttpServlet servlet;
protected @Nullable LinkyConfiguration config;
@@ -125,9 +125,19 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
config = getConfigAs(LinkyConfiguration.class);
- this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
- LinkyBindingConstants.ENEDIS_API_TOKEN_URL_PREPROD, LinkyBindingConstants.ENEDIS_AUTHORIZE_URL_PREPROD,
- config.clientId, config.clientSecret, LinkyBindingConstants.LINKY_SCOPES, true);
+ String tokenUrl = "";
+ String authorizeUrl = "";
+ if (this instanceof MyElectricalDataBridgeHandler) {
+ tokenUrl = LinkyBindingConstants.LINKY_MYELECTRICALDATA_API_TOKEN_URL;
+ authorizeUrl = LinkyBindingConstants.LINKY_MYELECTRICALDATA_AUTHORIZE_URL;
+ } else if (this instanceof EnedisBridgeHandler) {
+ tokenUrl = LinkyBindingConstants.ENEDIS_API_TOKEN_URL_PREPROD;
+ authorizeUrl = LinkyBindingConstants.ENEDIS_AUTHORIZE_URL_PREPROD;
+
+ }
+
+ this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID, tokenUrl,
+ authorizeUrl, config.clientId, config.clientSecret, LinkyBindingConstants.LINKY_SCOPES, true);
registerServlet();
@@ -138,6 +148,10 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
return enedisApi;
}
+ public abstract String getClientId();
+
+ public abstract String getClientSecret();
+
@Override
public void initialize() {
logger.debug("Initializing Netatmo API bridge handler.");
@@ -260,21 +274,9 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
&& accessTokenResponse.getRefreshToken() != null;
}
- public String getToken() throws LinkyException {
+ public abstract String getToken(LinkyHandler handler) throws LinkyException;
- AccessTokenResponse accesToken = getAccessTokenResponse();
- if (accesToken == null) {
- accesToken = getAccessTokenByClientCredentials();
- }
-
- if (accesToken == null) {
- throw new LinkyException("no token");
- }
-
- return "Bearer " + accesToken.getAccessToken();
- }
-
- private @Nullable AccessTokenResponse getAccessTokenByClientCredentials() {
+ protected @Nullable AccessTokenResponse getAccessTokenByClientCredentials() {
try {
return oAuthService.getAccessTokenByClientCredentials(LinkyBindingConstants.LINKY_SCOPES);
} catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
@@ -283,7 +285,7 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
}
}
- private @Nullable AccessTokenResponse getAccessTokenResponse() {
+ protected @Nullable AccessTokenResponse getAccessTokenResponse() {
try {
return oAuthService.getAccessTokenResponse();
} catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
@@ -293,8 +295,6 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
}
public String formatAuthorizationUrl(String redirectUri) {
- // Will work only in case of direct oAuth2 authentification to enedis
- // this is not the case in v1 as we go trough MyElectricalData
try {
String uri = this.oAuthService.getAuthorizationUrl(redirectUri, LinkyBindingConstants.LINKY_SCOPES,
LinkyBindingConstants.BINDING_ID);
@@ -337,5 +337,4 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
public abstract String getMaxPowerUrl();
public abstract String getTempoUrl();
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index 188e724b6ff..c8be15ebd68 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -13,6 +13,8 @@
package org.openhab.binding.linky.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.linky.internal.LinkyException;
+import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
@@ -47,10 +49,6 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
- // private static final String TOKEN_URL = BASE_URL
- // +
- // "v1/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=na&user_type=na&state=na&person_id=-1&usage_points_id=%s";
-
public EnedisBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
@@ -62,6 +60,16 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
super.initialize();
}
+ @Override
+ public String getClientId() {
+ return config.clientId;
+ }
+
+ @Override
+ public String getClientSecret() {
+ return config.clientSecret;
+ }
+
@Override
public void dispose() {
logger.debug("Shutting down Netatmo API bridge handler.");
@@ -69,6 +77,21 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
super.dispose();
}
+ @Override
+ public String getToken(LinkyHandler handler) throws LinkyException {
+
+ AccessTokenResponse accesToken = getAccessTokenResponse();
+ if (accesToken == null) {
+ accesToken = getAccessTokenByClientCredentials();
+ }
+
+ if (accesToken == null) {
+ throw new LinkyException("no token");
+ }
+
+ return "Bearer " + accesToken.getAccessToken();
+ }
+
@Override
public String getBaseUrl() {
return BASE_URL;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 37f52362a8c..a17647136d8 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -161,7 +161,7 @@ public class LinkyHandler extends BaseThingHandler {
LinkyConfiguration config = this.config;
if (api != null && config != null) {
- PrmInfo prmInfo = api.getPrmInfo(config.prmId);
+ PrmInfo prmInfo = api.getPrmInfo(this, config.prmId);
updateProperties(Map.of(USER_ID, prmInfo.customerId, PUISSANCE,
prmInfo.contractInfo.subscribedPower, PRM_ID, prmInfo.prmId));
@@ -198,7 +198,7 @@ public class LinkyHandler extends BaseThingHandler {
if (api != null && config != null) {
try {
- PrmInfo info = api.getPrmInfo(config.prmId);
+ PrmInfo info = api.getPrmInfo(this, config.prmId);
String title = info.identityInfo.title;
String firstName = info.identityInfo.firstname;
String lastName = info.identityInfo.lastname;
@@ -466,7 +466,7 @@ public class LinkyHandler extends BaseThingHandler {
EnedisHttpApi api = this.enedisApi;
if (api != null) {
try {
- MeterReading meterReading = api.getEnergyData(config.prmId, from, to);
+ MeterReading meterReading = api.getEnergyData(this, config.prmId, from, to);
updateStatus(ThingStatus.ONLINE);
return meterReading;
} catch (LinkyException e) {
@@ -484,7 +484,7 @@ public class LinkyHandler extends BaseThingHandler {
EnedisHttpApi api = this.enedisApi;
if (api != null) {
try {
- MeterReading meterReading = api.getPowerData(config.prmId, from, to);
+ MeterReading meterReading = api.getPowerData(this, config.prmId, from, to);
updateStatus(ThingStatus.ONLINE);
return meterReading;
} catch (LinkyException e) {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index bca359736eb..eaaab2623be 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -12,11 +12,18 @@
*/
package org.openhab.binding.linky.internal.handler;
+import java.util.Collection;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.linky.internal.LinkyBindingConstants;
+import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
+import org.openhab.binding.linky.internal.api.EnedisHttpApi;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
+import org.openhab.core.config.core.Configuration;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Reference;
@@ -46,6 +53,8 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
+ // https://www.myelectricaldata.fr/v1/oauth2/authorize?response_type=code&client_id=&state=linky&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fconnectlinky&scope=am_application_scope+default&user_type=aa&person_id=-1&usage_points_id=aa
+
public MyElectricalDataBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
@@ -57,6 +66,54 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
super.initialize();
}
+ @Override
+ public String getClientId() {
+ return "e551937c-5250-48bc-b4a6-2323af68db92";
+ }
+
+ @Override
+ public String getClientSecret() {
+ return "";
+ }
+
+ @Override
+ public String formatAuthorizationUrl(String redirectUri) {
+ return super.formatAuthorizationUrl("");
+ }
+
+ @Override
+ public String authorize(String redirectUri, String reqState, String reqCode) throws LinkyException {
+ String url = String.format(LinkyBindingConstants.LINKY_MYELECTRICALDATA_API_TOKEN_URL, getClientId(), reqCode);
+ EnedisHttpApi enedisApi = getEnedisApi();
+ if (enedisApi == null) {
+ return "";
+ }
+ String token = enedisApi.getData(url);
+
+ logger.debug("token: {}", token);
+
+ Collection col = this.thingRegistry.getAll();
+
+ for (Thing thing : col) {
+ if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
+ Configuration config = thing.getConfiguration();
+ String prmId = (String) config.get("prmId");
+
+ if (!prmId.equals(reqCode)) {
+ continue;
+ }
+
+ config.put("token", token);
+ LinkyHandler handler = (LinkyHandler) thing.getHandler();
+ if (handler != null) {
+ handler.saveConfiguration(config);
+ }
+
+ }
+ }
+ return token;
+ }
+
@Override
public void dispose() {
logger.debug("Shutting down Netatmo API bridge handler.");
@@ -65,7 +122,11 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
}
@Override
- public String getToken() throws LinkyException {
+ public String getToken(LinkyHandler handler) throws LinkyException {
+ LinkyConfiguration config = handler.getLinkyConfig();
+ if (config == null) {
+ return "";
+ }
return config.token;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index afef803d978..d5042020c83 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -26,7 +26,7 @@
- EnedisBridge
+ MyElectricalDataBridge
Provides your energy consumption data.
In order to receive the data, you must activate your account at
@@ -34,11 +34,6 @@
-
- Token
- Your Enedis token (can be left empty, use the connection page to automatically fill it
- http://youopenhab/connectlinky)
-
@@ -74,6 +69,11 @@
PrmId
Your PrmId
+
+ Token
+ Your Enedis token (can be left empty, use the connection page to automatically fill it
+ http://youopenhab/connectlinky)
+
From 004074010aea6e3b27225360e11b421decacbd2f Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Tue, 4 Jun 2024 17:25:00 +0200
Subject: [PATCH 027/135] realign channel naming conventions
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyBindingConstants.java | 34 ++++++++++---------
.../resources/OH-INF/thing/thing-types.xml | 26 +++++++-------
2 files changed, 31 insertions(+), 29 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 77465e8eb46..b5561cfd059 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -46,28 +46,30 @@ public class LinkyBindingConstants {
public static final String USER_ID = "customerId";
// List of all Channel id's
- public static final String DAY_MINUS_1 = "daily#day-1";
+ public static final String DAY_MINUS_1 = "daily#yesterday";
public static final String DAY_MINUS_2 = "daily#day-2";
public static final String DAY_MINUS_3 = "daily#day-3";
- public static final String PEAK_POWER_DAY_MINUS_1 = "daily#maxPower-day-1";
- public static final String PEAK_POWER_TS_DAY_MINUS_1 = "daily#maxPowerTs-day-1";
- public static final String PEAK_POWER_DAY_MINUS_2 = "daily#maxPower-day-2";
- public static final String PEAK_POWER_TS_DAY_MINUS_2 = "daily#maxPowerTs-day-2";
- public static final String PEAK_POWER_DAY_MINUS_3 = "daily#maxPower-day-3";
- public static final String PEAK_POWER_TS_DAY_MINUS_3 = "daily#maxPowerTs-day-3";
+ public static final String PEAK_POWER_DAY_MINUS_1 = "daily#power";
+ public static final String PEAK_POWER_TS_DAY_MINUS_1 = "daily#timestamp";
- public static final String WEEK_MINUS_0 = "weekly#week-0";
- public static final String WEEK_MINUS_1 = "weekly#week-1";
+ public static final String PEAK_POWER_DAY_MINUS_2 = "daily#power-2";
+ public static final String PEAK_POWER_TS_DAY_MINUS_2 = "daily#timestamp-2";
+
+ public static final String PEAK_POWER_DAY_MINUS_3 = "daily#power-3";
+ public static final String PEAK_POWER_TS_DAY_MINUS_3 = "daily#timestamp-3";
+
+ public static final String WEEK_MINUS_0 = "weekly#thisWeek";
+ public static final String WEEK_MINUS_1 = "weekly#lastWeek";
public static final String WEEK_MINUS_2 = "weekly#week-2";
- public static final String MONTH_MINUS_0 = "monthly#month-0";
- public static final String MONTH_MINUS_1 = "monthly#month-1";
- public static final String MONTH_MINUS_2 = "monthly#month-2";
- public static final String YEAR_MINUS_0 = "yearly#year-0";
- public static final String YEAR_MINUS_1 = "yearly#year-1";
- public static final String YEAR_MINUS_2 = "yearly#year-2";
- public static final String TEST_SELECT = "main#linkyTestSelect";
+ public static final String MONTH_MINUS_0 = "monthly#thisMonth";
+ public static final String MONTH_MINUS_1 = "monthly#lastMonth";
+ public static final String MONTH_MINUS_2 = "monthly#month-2";
+
+ public static final String YEAR_MINUS_0 = "yearly#thisYear";
+ public static final String YEAR_MINUS_1 = "yearly#lastYear";
+ public static final String YEAR_MINUS_2 = "yearly#year-2";
public static final String MAIN_IDENTITY = "main#Identity";
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index d5042020c83..115c37e9e58 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -82,7 +82,7 @@
Daily consumption
-
+
Yesterday Consumption
@@ -91,18 +91,18 @@
Yesterday Consumption
-
-
+
+
Peak Timestamp
Maximum power usage timestamp
-
-
+
+
Peak Timestamp
Maximum power usage timestamp
-
-
+
+
Peak Timestamp
Maximum power usage timestamp
@@ -112,10 +112,10 @@
Weekly consumption
-
+
This Week Consumption
-
+
Last Week Consumption
@@ -127,10 +127,10 @@
Monthly consumption
-
+
This Month Consumption
-
+
Last Month Consumption
@@ -142,10 +142,10 @@
Yearly consumption
-
+
This Year Consumption
-
+
Last Year Consumption
From ef5a168be0ece6d33dc3c4728edb1b024ada9624 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Tue, 4 Jun 2024 17:26:40 +0200
Subject: [PATCH 028/135] review null pointer warnings
Signed-off-by: Laurent ARNAL
---
.../linky/internal/handler/ApiBridgeHandler.java | 2 +-
.../binding/linky/internal/handler/LinkyHandler.java | 11 +++++------
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index c8503879ab3..410ea26aafd 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -85,7 +85,7 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
protected final ThingRegistry thingRegistry;
private static @Nullable HttpServlet servlet;
- protected @Nullable LinkyConfiguration config;
+ protected LinkyConfiguration config;
private static final String TEMPLATE_PATH = "templates/";
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index a17647136d8..575ffcbe643 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -68,7 +68,7 @@ public class LinkyHandler extends BaseThingHandler {
private final ExpiringDayCache dailyConsumption;
private @Nullable ScheduledFuture> refreshJob;
- private @Nullable LinkyConfiguration config;
+ private LinkyConfiguration config;
private @Nullable EnedisHttpApi enedisApi;
private enum Target {
@@ -80,6 +80,8 @@ public class LinkyHandler extends BaseThingHandler {
public LinkyHandler(Thing thing, LocaleProvider localeProvider) {
super(thing);
+ config = getConfigAs(LinkyConfiguration.class);
+
this.dailyConsumption = new ExpiringDayCache<>("dailyConsumption", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate today = LocalDate.now();
MeterReading meterReading = getConsumptionData(today.minusDays(1095), today);
@@ -152,15 +154,13 @@ public class LinkyHandler extends BaseThingHandler {
updateStatus(ThingStatus.UNKNOWN);
- config = getConfigAs(LinkyConfiguration.class);
if (config.seemsValid()) {
scheduler.submit(() -> {
try {
EnedisHttpApi api = this.enedisApi;
- LinkyConfiguration config = this.config;
- if (api != null && config != null) {
+ if (api != null) {
PrmInfo prmInfo = api.getPrmInfo(this, config.prmId);
updateProperties(Map.of(USER_ID, prmInfo.customerId, PUISSANCE,
prmInfo.contractInfo.subscribedPower, PRM_ID, prmInfo.prmId));
@@ -194,9 +194,8 @@ public class LinkyHandler extends BaseThingHandler {
private synchronized void updateMetaData() {
EnedisHttpApi api = this.enedisApi;
- LinkyConfiguration config = this.config;
- if (api != null && config != null) {
+ if (api != null) {
try {
PrmInfo info = api.getPrmInfo(this, config.prmId);
String title = info.identityInfo.title;
From de194e15b7a16a91317f4387ae847de8703ce13e Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Tue, 4 Jun 2024 17:40:18 +0200
Subject: [PATCH 029/135] div fixes based on clinic review
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyBindingConstants.java | 2 ++
.../linky/internal/dto/AddressInfo.java | 7 -------
.../binding/linky/internal/dto/Contracts.java | 16 ----------------
.../binding/linky/internal/dto/Customer.java | 4 ----
.../linky/internal/dto/CustomerIdResponse.java | 6 ------
.../linky/internal/dto/IdentityDetails.java | 3 ---
.../linky/internal/dto/IntervalReading.java | 1 -
.../linky/internal/dto/MeterReading.java | 3 ---
.../linky/internal/dto/MeterResponse.java | 3 ---
.../linky/internal/dto/ReadingType.java | 9 ---------
.../binding/linky/internal/dto/UsagePoint.java | 4 ----
.../internal/handler/ApiBridgeHandler.java | 18 +++++++-----------
.../internal/handler/EnedisBridgeHandler.java | 4 +---
.../handler/MyElectricalDataBridgeHandler.java | 16 +++++++++-------
14 files changed, 19 insertions(+), 77 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index b5561cfd059..34e0dce65b2 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -124,4 +124,6 @@ public class LinkyBindingConstants {
public static final String LINKY_MYELECTRICALDATA_AUTHORIZE_URL = ENEDIS_AUTHORIZE_URL_PROD;
public static final String LINKY_MYELECTRICALDATA_API_TOKEN_URL = LINKY_MYELECTRICALDATA_ACCOUNT_URL
+ "v1/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=na&user_type=na&state=na&person_id=-1&usage_points_id=%s";
+
+ public static final String LINKY_MYELECTRICALDATA_CLIENT_ID = "e551937c-5250-48bc-b4a6-2323af68db92";
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
index b7b4f8360e9..d7deef5c98a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
@@ -14,8 +14,6 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
-import com.google.gson.annotations.SerializedName;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -25,13 +23,8 @@ import com.google.gson.annotations.SerializedName;
public class AddressInfo {
public String street;
public String locality;
-
- @SerializedName("postal_code")
public String postalCode;
-
- @SerializedName("insee_code")
public String inseeCode;
-
public String city;
public String country;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
index 2a04cbe0618..aa7a505c504 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
@@ -14,8 +14,6 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
-import com.google.gson.annotations.SerializedName;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -24,25 +22,11 @@ import com.google.gson.annotations.SerializedName;
public class Contracts {
public String segment;
-
- @SerializedName("subscribed_power")
public String subscribedPower;
-
- @SerializedName("last_activation_date")
public String lastActivationDate;
-
- @SerializedName("distribution_tariff")
public String distributionTariff;
-
- @SerializedName("offpeak_hours")
public String offpeakHours;
-
- @SerializedName("contract_status")
public String contractStatus;
-
- @SerializedName("contract_type")
public String contractType;
-
- @SerializedName("last_distribution_tariff_change_date")
public String lastDistributionTariffChangeDate;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
index 26e7abe6ade..a41a555643b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
@@ -14,8 +14,6 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
-import com.google.gson.annotations.SerializedName;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -24,9 +22,7 @@ import com.google.gson.annotations.SerializedName;
*/
public class Customer {
- @SerializedName("customer_id")
public String customerId;
- @SerializedName("usage_points")
public UsagePoint[] usagePoints;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java
index f0e46550263..62bb3d7cff4 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java
@@ -14,8 +14,6 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
-import com.google.gson.annotations.SerializedName;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -24,11 +22,7 @@ import com.google.gson.annotations.SerializedName;
*/
public class CustomerIdResponse {
- @SerializedName("customer_id")
public String customerId;
-
public IdentityDetails identity;
-
- @SerializedName("contact_data")
public ContactInfo contactData;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java
index 15a07b6a275..45d99606ee1 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java
@@ -14,8 +14,6 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
-import com.google.gson.annotations.SerializedName;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -24,6 +22,5 @@ import com.google.gson.annotations.SerializedName;
*/
public class IdentityDetails {
- @SerializedName("natural_person")
public IdentityInfo naturalPerson;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
index ac03f412546..7cd1a23658b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IntervalReading.java
@@ -25,6 +25,5 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
public class IntervalReading {
public double value;
-
public LocalDateTime date;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
index eb967967af1..e7155eb238c 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
@@ -24,7 +24,6 @@ import com.google.gson.annotations.SerializedName;
*/
public class MeterReading {
- @SerializedName("usage_point_id")
public String usagePointId;
@SerializedName("start")
@@ -33,10 +32,8 @@ public class MeterReading {
@SerializedName("end")
public String endDate;
- @SerializedName("quality")
public String quality;
- @SerializedName("reading_type")
public ReadingType readingType;
@SerializedName("interval_reading")
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
index f5d1f85ad73..726987e602f 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
@@ -14,8 +14,6 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
-import com.google.gson.annotations.SerializedName;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -24,6 +22,5 @@ import com.google.gson.annotations.SerializedName;
*/
public class MeterResponse {
- @SerializedName("meter_reading")
public MeterReading meterReading;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java
index 454f04fe295..4642ea53edc 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java
@@ -14,8 +14,6 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
-import com.google.gson.annotations.SerializedName;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -24,15 +22,8 @@ import com.google.gson.annotations.SerializedName;
*/
public class ReadingType {
- @SerializedName("measurement_kind")
public String measurementKind;
-
- @SerializedName("measuring_period")
public String measuringPeriod;
-
- @SerializedName("unit")
public String unit;
-
- @SerializedName("aggregate")
public String aggregate;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
index aa8d6bbf359..f0cd3cd421e 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
@@ -14,8 +14,6 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
-import com.google.gson.annotations.SerializedName;
-
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -24,8 +22,6 @@ import com.google.gson.annotations.SerializedName;
*/
public class UsagePoint {
- @SerializedName("usage_point")
public UsagePointDetails usagePoint;
-
public Contracts contracts;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 410ea26aafd..e2dd36cdf7a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -75,12 +75,11 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
private static final int REQUEST_BUFFER_SIZE = 8000;
- private @NonNullByDefault({}) HttpService httpService;
- private @NonNullByDefault({}) BundleContext bundleContext;
+ private HttpService httpService;
+ private BundleContext bundleContext;
private final HttpClient httpClient;
- private @Nullable EnedisHttpApi enedisApi;
- private final Gson gson;
+ private EnedisHttpApi enedisApi;
private OAuthClientService oAuthService;
protected final ThingRegistry thingRegistry;
@@ -108,7 +107,6 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
}
- this.gson = gson;
this.httpService = httpService;
this.thingRegistry = thingRegistry;
this.bundleContext = componentContext.getBundleContext();
@@ -117,6 +115,8 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
httpClient.setFollowRedirects(false);
httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
+ this.enedisApi = new EnedisHttpApi(this, gson, this.httpClient);
+
try {
httpClient.start();
} catch (Exception e) {
@@ -154,17 +154,13 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
@Override
public void initialize() {
- logger.debug("Initializing Netatmo API bridge handler.");
+ logger.debug("Initializing Linky API bridge handler.");
updateStatus(ThingStatus.UNKNOWN);
- EnedisHttpApi api = new EnedisHttpApi(this, gson, this.httpClient);
-
- this.enedisApi = api;
-
scheduler.submit(() -> {
try {
- api.initialize();
+ enedisApi.initialize();
updateStatus(ThingStatus.ONLINE);
} catch (LinkyException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index c8be15ebd68..de49cb2a96c 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -47,8 +47,6 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
- private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
-
public EnedisBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
@@ -129,6 +127,6 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
@Override
public String getTempoUrl() {
- return TEMPO_URL;
+ return "";
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index eaaab2623be..4762ac9ffd6 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -44,12 +44,14 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
private static final String BASE_URL = "https://www.myelectricaldata.fr/";
- private static final String CONTRACT_URL = BASE_URL + "contracts/%s/";
- private static final String IDENTITY_URL = BASE_URL + "identity/%s/";
- private static final String CONTACT_URL = BASE_URL + "contact/%s/";
- private static final String ADDRESS_URL = BASE_URL + "addresses/%s/";
- private static final String MEASURE_DAILY_CONSUMPTION_URL = BASE_URL + "daily_consumption/%s/start/%s/end/%s";
- private static final String MEASURE_MAX_POWER_URL = BASE_URL + "daily_consumption_max_power/%s/start/%s/end/%s";
+ private static final String CONTRACT_URL = BASE_URL + "contracts/%s/cache/";
+ private static final String IDENTITY_URL = BASE_URL + "identity/%s/cache/";
+ private static final String CONTACT_URL = BASE_URL + "contact/%s/cache/";
+ private static final String ADDRESS_URL = BASE_URL + "addresses/%s/cache/";
+ private static final String MEASURE_DAILY_CONSUMPTION_URL = BASE_URL
+ + "daily_consumption/%s/start/%s/end/%s/cache/";
+ private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ + "daily_consumption_max_power/%s/start/%s/end/%s/cache/";
private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
@@ -68,7 +70,7 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
@Override
public String getClientId() {
- return "e551937c-5250-48bc-b4a6-2323af68db92";
+ return LinkyBindingConstants.LINKY_MYELECTRICALDATA_CLIENT_ID;
}
@Override
From ae793ff83b2c3515ef1b93299462cfadab282b10 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 5 Jun 2024 08:03:15 +0200
Subject: [PATCH 030/135] review thing-types
Signed-off-by: Laurent ARNAL
---
.../resources/OH-INF/thing/thing-types.xml | 62 +++++++++++--------
1 file changed, 35 insertions(+), 27 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 115c37e9e58..42a07fc2d05 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -154,76 +154,82 @@
+
+ Tempo
+
+
+ linkyTestSelect
+
+
+
+
Main
-
- linkyTestSelect
-
-
+
Identity
-
+
Subscribed Power
-
+
Last Activation Date
-
+
Distribution Tariff
-
+
Offpeak Hours
-
+
Contract Status
-
+
Contract Type
-
+
Last Distribution Tariff ChangeDate
-
+
Contract Segment
-
+
UsagePoint Id
-
+
UsagePoin Status
-
+
UsagePoint Meter Type
-
+
City
-
+
Country
-
+
Insee Code
-
+
Postal Code
-
+
Street
-
+
Mail
-
+
Phone
@@ -234,11 +240,13 @@
-
- String
- Test Select
- Test Select
-
+
+ String
+ Status of Sensors
+
+
+
+
Number:Energy
From 62cdfe16225dee75b3e61c5fa48e15f8725a6450 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Mon, 17 Jun 2024 21:25:24 +0200
Subject: [PATCH 031/135] WIP : support time series
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyBindingConstants.java | 6 +
.../linky/internal/api/EnedisHttpApi.java | 7 +-
.../linky/internal/handler/LinkyHandler.java | 112 ++++++++++++------
.../resources/OH-INF/thing/thing-types.xml | 47 +++++---
4 files changed, 119 insertions(+), 53 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 34e0dce65b2..f398169d458 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -46,6 +46,9 @@ public class LinkyBindingConstants {
public static final String USER_ID = "customerId";
// List of all Channel id's
+
+ public static final String CONSUMPTION = "timeseries#consumption";
+
public static final String DAY_MINUS_1 = "daily#yesterday";
public static final String DAY_MINUS_2 = "daily#day-2";
public static final String DAY_MINUS_3 = "daily#day-3";
@@ -73,6 +76,9 @@ public class LinkyBindingConstants {
public static final String MAIN_IDENTITY = "main#Identity";
+ public static final String TEMPO_TEMPO_INFO = "tempo#tempoInfo";
+ public static final String TEMPO_TEMPO_INFO_TIME_SERIES = "tempo#tempoInfoTimeSeries";
+
public static final String MAIN_CONTRACT_SUBSCRIBED_POWER = "main#contractSubscribedPower";
public static final String MAIN_CONTRACT_LAST_ACTIVATION_DATE = "main#contractLastActivationDate";
public static final String MAIN_CONTRACT_DISTRIBUTION_TARIFF = "main#contractDistributionTariff";
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 0bdab767a17..2d8c8281ca9 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -316,8 +316,8 @@ public class EnedisHttpApi {
return getMeasures(handler, apiBridgeHandler.getMaxPowerUrl(), prmId, from, to);
}
- public String getTempoData(LinkyHandler handler) throws LinkyException {
- String url = String.format(apiBridgeHandler.getTempoUrl(), "2024-01-01", "2024-01-31");
+ public TempoResponse getTempoData(LinkyHandler handler) throws LinkyException {
+ String url = String.format(apiBridgeHandler.getTempoUrl(), "2024-01-01", "2024-06-30");
if (!connected) {
initialize();
}
@@ -333,8 +333,7 @@ public class EnedisHttpApi {
throw new LinkyException("No report data received");
}
- return "{\"2024-01-20\":\"WHITE\",\"2024-01-21\":\"RED\",\"array\":[\"2024-01-20\",\"2024-01-21\"]}";
- // return tempResponse.tempoDayInfo;
+ return tempResponse;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching ConsumptionReport.class: {}", data);
throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", url);
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 575ffcbe643..49c17c01d5a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -14,11 +14,17 @@ package org.openhab.binding.linky.internal.handler;
import static org.openhab.binding.linky.internal.LinkyBindingConstants.*;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
@@ -33,8 +39,11 @@ import org.openhab.binding.linky.internal.api.ExpiringDayCache;
import org.openhab.binding.linky.internal.dto.IntervalReading;
import org.openhab.binding.linky.internal.dto.MeterReading;
import org.openhab.binding.linky.internal.dto.PrmInfo;
+import org.openhab.binding.linky.internal.dto.TempoResponse;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.Units;
@@ -46,6 +55,8 @@ import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
+import org.openhab.core.types.TimeSeries;
+import org.openhab.core.types.TimeSeries.Policy;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -241,27 +252,60 @@ public class LinkyHandler extends BaseThingHandler {
private synchronized void updateData() {
boolean connectedBefore = isConnected();
- updateDailyWeeklyData();
-
+ // updateEnergyData();
// updatePowerData();
- // updateMonthlyData();
- // updateYearlyData();
- // String tempoData = getTempoData();
+ TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
+ TempoResponse tempoData = getTempoData();
- // LinkedTreeMap obj = gson.fromJson(tempoData, LinkedTreeMap.class);
+ tempoData.forEach((k, v) -> {
+ try {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+ Date date = df.parse(k);
+ long epoch = date.getTime();
+ Instant timestamp = Instant.ofEpochMilli(epoch);
- /*
- *
- *
- * ArrayList list = new ArrayList();
- * for (Object key : obj.keySet()) {
- * Object val = obj.get(key);
- *
- * Pair keyValue = new ImmutablePair(key, val);
- * list.add(keyValue);
- * }
- */
+ int val = 0;
+ if (v.equals("WHITE")) {
+ val = 0;
+ }
+ if (v.equals("BLUE")) {
+ val = 1;
+ }
+ if (v.equals("RED")) {
+ val = 2;
+ }
+ timeSeries.add(timestamp, new DecimalType(val));
+ } catch (ParseException ex) {
+
+ }
+
+ });
+
+ sendTimeSeries(TEMPO_TEMPO_INFO_TIME_SERIES, timeSeries);
+ updateState(TEMPO_TEMPO_INFO_TIME_SERIES, new DecimalType(1));
+
+ TimeSeries timeSeries2 = new TimeSeries(Policy.REPLACE);
+
+ LocalDate today = LocalDate.now();
+ MeterReading meterReading = getConsumptionData(today.minusDays(1095), today);
+ meterReading = getMeterReadingAfterChecks(meterReading);
+ if (meterReading != null) {
+
+ IntervalReading[] iv = meterReading.dayValue;
+
+ for (int i = 0; i < iv.length; i++) {
+
+ // iv[i].value
+
+ Instant timestamp = iv[i].date.toInstant(ZoneOffset.UTC);
+ timeSeries2.add(timestamp, new DecimalType(iv[i].value));
+
+ }
+ }
+
+ sendTimeSeries(CONSUMPTION, timeSeries2);
+ updateState(CONSUMPTION, new DecimalType(0));
if (!connectedBefore && isConnected()) {
disconnect();
@@ -494,24 +538,22 @@ public class LinkyHandler extends BaseThingHandler {
return null;
}
- /*
- * private @Nullable String getTempoData() {
- * logger.debug("getTempoData from");
- *
- * EnedisHttpApi api = this.enedisApi;
- * if (api != null) {
- * try {
- * String result = api.getTempoData();
- * updateStatus(ThingStatus.ONLINE);
- * return result;
- * } catch (LinkyException e) {
- * logger.debug("Exception when getting power data: {}", e.getMessage(), e);
- * updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
- * }
- * }
- * return null;
- * }
- */
+ private @Nullable TempoResponse getTempoData() {
+ logger.debug("getTempoData from");
+
+ EnedisHttpApi api = this.enedisApi;
+ if (api != null) {
+ try {
+ TempoResponse result = api.getTempoData(this);
+ updateStatus(ThingStatus.ONLINE);
+ return result;
+ } catch (LinkyException e) {
+ logger.debug("Exception when getting power data: {}", e.getMessage(), e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ }
+ }
+ return null;
+ }
private boolean isConnected() {
EnedisHttpApi api = this.enedisApi;
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 42a07fc2d05..8975e898632 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -54,10 +54,12 @@
+
+
@@ -79,6 +81,15 @@
+
+ Timeseries channel
+
+
+ Consumption
+
+
+
+
Daily consumption
@@ -91,16 +102,19 @@
Yesterday Consumption
+
Peak Timestamp
Maximum power usage timestamp
+
Peak Timestamp
Maximum power usage timestamp
+
Peak Timestamp
@@ -154,14 +168,17 @@
-
- Tempo
-
-
- linkyTestSelect
-
-
-
+
+ Tempo
+
+
+ Tempo day information
+
+
+ Tempo day information
+
+
+
Main
@@ -240,13 +257,15 @@
-
- String
- Status of Sensors
-
-
+
+ String
+ Information
+ an information
+ energy
+
+
+
-
Number:Energy
From 1241ad3ef49dda55e062ae16ec9c099b831924c4 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Tue, 18 Jun 2024 17:11:35 +0200
Subject: [PATCH 032/135] some quick fixes on last clinique review
Signed-off-by: Laurent ARNAL
---
.../binding/linky/internal/LinkyBindingConstants.java | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index f398169d458..70a16ed427c 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -74,7 +74,7 @@ public class LinkyBindingConstants {
public static final String YEAR_MINUS_1 = "yearly#lastYear";
public static final String YEAR_MINUS_2 = "yearly#year-2";
- public static final String MAIN_IDENTITY = "main#Identity";
+ public static final String MAIN_IDENTITY = "main#identity";
public static final String TEMPO_TEMPO_INFO = "tempo#tempoInfo";
public static final String TEMPO_TEMPO_INFO_TIME_SERIES = "tempo#tempoInfoTimeSeries";
@@ -110,9 +110,6 @@ public class LinkyBindingConstants {
*/
public static final String LINKY_SCOPES = Stream.of("am_application_scope", "default")
.collect(Collectors.joining(" "));
- // "r:devices:*", "w:devices:*", "x:devices:*", "r:hubs:*",
- // "r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*",
- // "r:installedapps", "w:installedapps"
public static final String ENEDIS_ACCOUNT_URL_PROD = "https://mon-compte-particulier.enedis.fr/";
public static final String ENEDIS_AUTHORIZE_URL_PROD = ENEDIS_ACCOUNT_URL_PROD + "dataconnect/v1/oauth2/authorize";
From 8a7e4bd3a7fef3ba2c606c5f73855709a6046625 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Tue, 13 Aug 2024 18:38:22 +0200
Subject: [PATCH 033/135] add WebBrigeHandler
Signed-off-by: Laurent ARNAL
---
.../handler/EnedisWebBridgeHandler.java | 258 ++++++++++++++++++
.../resources/OH-INF/thing/thing-types.xml | 29 ++
2 files changed, 287 insertions(+)
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
new file mode 100644
index 00000000000..b72838c90e4
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -0,0 +1,258 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.handler;
+
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.openhab.binding.linky.internal.LinkyConfiguration;
+import org.openhab.binding.linky.internal.LinkyException;
+import org.openhab.binding.linky.internal.api.EnedisHttpApi;
+import org.openhab.binding.linky.internal.dto.AuthData;
+import org.openhab.binding.linky.internal.dto.AuthResult;
+import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
+import org.openhab.core.auth.client.oauth2.OAuthFactory;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ThingRegistry;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * {@link EnedisBridgeHandler} is the base handler to access enedis data.
+ *
+ * @author Laurent Arnal - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class EnedisWebBridgeHandler extends ApiBridgeHandler {
+ private final Logger logger = LoggerFactory.getLogger(EnedisWebBridgeHandler.class);
+
+ private static final String BASE_URL = "https://ext.prod-sandbox.api.enedis.fr/";
+
+ private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts?usage_point_id=%s";
+ private static final String IDENTITY_URL = BASE_URL + "customers_i/v5/identity?usage_point_id=%s";
+ private static final String CONTACT_URL = BASE_URL + "customers_cd/v5/contact_data?usage_point_id=%s";
+ private static final String ADDRESS_URL = BASE_URL + "customers_upa/v5/usage_points/addresses?usage_point_id=%s";
+ private static final String MEASURE_DAILY_CONSUMPTION_URL = BASE_URL
+ + "metering_data_dc/v5/daily_consumption?usage_point_id=%s&start=%s&end=%s";
+ private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ + "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
+
+ private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
+
+ private static final String URL_APPS_LINCS = "https://alex.microapplications" + EnedisHttpApi.ENEDIS_DOMAIN;
+ private static final String URL_ENEDIS_AUTHENTICATE = URL_APPS_LINCS + "/authenticate?target="
+ + EnedisHttpApi.URL_COMPTE_PART;
+ private static final String USER_INFO_URL = URL_APPS_LINCS + "/userinfos";
+ private static final String PRM_INFO_BASE_URL = URL_APPS_LINCS + "/mes-mesures/api/private/v1/personnes/";
+ private static final String PRM_INFO_URL = PRM_INFO_BASE_URL + "null/prms";
+ private static final String MEASURE_URL = PRM_INFO_BASE_URL
+ + "%s/prms/%s/donnees-%s?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
+ private static final Pattern REQ_PATTERN = Pattern.compile("ReqID%(.*?)%26");
+
+ private boolean connected = false;
+
+ public EnedisWebBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
+ final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
+ final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
+ super(bridge, httpClientFactory, oAuthFactory, httpService, thingRegistry, componentContext, gson);
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+
+ try {
+ doAuth();
+ } catch (LinkyException ex) {
+ logger.debug("Exception: {}", ex.getMessage());
+ }
+ }
+
+ @Override
+ public String getClientId() {
+ return config.clientId;
+ }
+
+ @Override
+ public String getClientSecret() {
+ return config.clientSecret;
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Shutting down Netatmo API bridge handler.");
+ try {
+ enedisApi.disconnect();
+ } catch (LinkyException ex) {
+ logger.debug("Exception: {}", ex.getMessage());
+ }
+ super.dispose();
+ }
+
+ @Override
+ public String getToken(LinkyHandler handler) throws LinkyException {
+
+ AccessTokenResponse accesToken = getAccessTokenResponse();
+ if (accesToken == null) {
+ accesToken = getAccessTokenByClientCredentials();
+ }
+
+ if (accesToken == null) {
+ throw new LinkyException("no token");
+ }
+
+ return "Bearer " + accesToken.getAccessToken();
+ }
+
+ @Override
+ public String getBaseUrl() {
+ return BASE_URL;
+ }
+
+ @Override
+ public String getContactUrl() {
+ return CONTACT_URL;
+ }
+
+ @Override
+ public String getContractUrl() {
+ return CONTRACT_URL;
+ }
+
+ @Override
+ public String getIdentityUrl() {
+ return IDENTITY_URL;
+ }
+
+ @Override
+ public String getAddressUrl() {
+ return ADDRESS_URL;
+ }
+
+ @Override
+ public String getDailyConsumptionUrl() {
+ return MEASURE_DAILY_CONSUMPTION_URL;
+ }
+
+ @Override
+ public String getMaxPowerUrl() {
+ return MEASURE_MAX_POWER_URL;
+ }
+
+ @Override
+ public String getTempoUrl() {
+ return "";
+ }
+
+ public void doAuth() throws LinkyException {
+ logger.debug("Starting login process for user : {}", config.username);
+
+ try {
+ enedisApi.addCookie(LinkyConfiguration.INTERNAL_AUTH_ID, config.internalAuthId);
+ logger.debug("Step 1 : getting authentification");
+ String data = enedisApi.getData(URL_ENEDIS_AUTHENTICATE);
+
+ logger.debug("Reception request SAML");
+ Document htmlDocument = Jsoup.parse(data);
+ Element el = htmlDocument.select("form").first();
+ Element samlInput = el.select("input[name=SAMLRequest]").first();
+
+ logger.debug("Step 2 : send SSO SAMLRequest");
+ ContentResponse result = httpClient.POST(el.attr("action"))
+ .content(enedisApi.getFormContent("SAMLRequest", samlInput.attr("value"))).send();
+ if (result.getStatus() != 302) {
+ throw new LinkyException("Connection failed step 2");
+ }
+
+ logger.debug("Get the location and the ReqID");
+ Matcher m = REQ_PATTERN.matcher(enedisApi.getLocation(result));
+ if (!m.find()) {
+ throw new LinkyException("Unable to locate ReqId in header");
+ }
+
+ String reqId = m.group(1);
+ String authenticateUrl = EnedisHttpApi.URL_MON_COMPTE
+ + "/auth/json/authenticate?realm=/enedis&forward=true&spEntityID=SP-ODW-PROD&goto=/auth/SSOPOST/metaAlias/enedis/providerIDP?ReqID%"
+ + reqId + "%26index%3Dnull%26acsURL%3D" + URL_APPS_LINCS
+ + "/saml/SSO%26spEntityID%3DSP-ODW-PROD%26binding%3Durn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST&AMAuthCookie=";
+
+ logger.debug("Step 3 : auth1 - retrieve the template, thanks to cookie internalAuthId user is already set");
+ result = httpClient.POST(authenticateUrl).header("X-NoSession", "true").header("X-Password", "anonymous")
+ .header("X-Requested-With", "XMLHttpRequest").header("X-Username", "anonymous").send();
+ if (result.getStatus() != 200) {
+ throw new LinkyException("Connection failed step 3 - auth1 : %s", result.getContentAsString());
+ }
+
+ AuthData authData = gson.fromJson(result.getContentAsString(), AuthData.class);
+ if (authData == null || authData.callbacks.size() < 2 || authData.callbacks.get(0).input.isEmpty()
+ || authData.callbacks.get(1).input.isEmpty() || !config.username
+ .equals(Objects.requireNonNull(authData.callbacks.get(0).input.get(0)).valueAsString())) {
+ logger.debug("auth1 - invalid template for auth data: {}", result.getContentAsString());
+ throw new LinkyException("Authentication error, the authentication_cookie is probably wrong");
+ }
+
+ authData.callbacks.get(1).input.get(0).value = config.password;
+ logger.debug("Step 4 : auth2 - send the auth data");
+ result = httpClient.POST(authenticateUrl).header(HttpHeader.CONTENT_TYPE, "application/json")
+ .header("X-NoSession", "true").header("X-Password", "anonymous")
+ .header("X-Requested-With", "XMLHttpRequest").header("X-Username", "anonymous")
+ .content(new StringContentProvider(gson.toJson(authData))).send();
+ if (result.getStatus() != 200) {
+ throw new LinkyException("Connection failed step 3 - auth2 : %s", result.getContentAsString());
+ }
+
+ AuthResult authResult = gson.fromJson(result.getContentAsString(), AuthResult.class);
+ if (authResult == null) {
+ throw new LinkyException("Invalid authentication result data");
+ }
+
+ logger.debug("Add the tokenId cookie");
+ enedisApi.addCookie("enedisExt", authResult.tokenId);
+
+ logger.debug("Step 5 : retrieve the SAMLresponse");
+ data = enedisApi.getData(EnedisHttpApi.URL_MON_COMPTE + "/" + authResult.successUrl);
+ htmlDocument = Jsoup.parse(data);
+ el = htmlDocument.select("form").first();
+ samlInput = el.select("input[name=SAMLResponse]").first();
+
+ logger.debug("Step 6 : post the SAMLresponse to finish the authentication");
+ result = httpClient.POST(el.attr("action"))
+ .content(enedisApi.getFormContent("SAMLResponse", samlInput.attr("value"))).send();
+ if (result.getStatus() != 302) {
+ throw new LinkyException("Connection failed step 6");
+ }
+ connected = true;
+ } catch (InterruptedException | TimeoutException | ExecutionException | JsonSyntaxException e) {
+ throw new LinkyException(e, "Error opening connection with Enedis webservice");
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 8975e898632..9ab4b67965b 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -25,6 +25,34 @@
+
+ EnedisWebBridge
+
+ Provides your energy consumption data.
+ In order to receive the data, you must activate your account at
+ https://espace-client-particuliers.enedis.fr/web/espace-particuliers/compteur-linky.
+
+
+
+
+ Username
+ email
+ Your Enedis Username
+
+
+ Password
+ password
+ Your Enedis Password
+
+
+ Auth ID
+ Authentication ID delivered after the captcha (see documentation).
+
+
+
+
+
+
MyElectricalDataBridge
@@ -41,6 +69,7 @@
+
From 238d906b72c48e440a9edccbbe90b0f2cc4df70f Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Tue, 13 Aug 2024 18:38:51 +0200
Subject: [PATCH 034/135] start backporting code from old binding
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyBindingConstants.java | 4 +-
.../linky/internal/LinkyConfiguration.java | 6 +
.../linky/internal/LinkyHandlerFactory.java | 10 +-
.../linky/internal/api/EnedisHttpApi.java | 114 +++++++++---------
.../binding/linky/internal/dto/AuthData.java | 48 ++++++++
.../linky/internal/dto/AuthResult.java | 24 ++++
.../internal/handler/ApiBridgeHandler.java | 15 ++-
7 files changed, 157 insertions(+), 64 deletions(-)
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthResult.java
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 70a16ed427c..449f5ebde7a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -33,17 +33,19 @@ public class LinkyBindingConstants {
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_API_ENEDIS_BRIDGE = new ThingTypeUID(BINDING_ID, "EnedisBridge");
+ public static final ThingTypeUID THING_TYPE_API_WEB_ENEDIS_BRIDGE = new ThingTypeUID(BINDING_ID, "EnedisWebBridge");
public static final ThingTypeUID THING_TYPE_API_MYELECTRICALDATA_BRIDGE = new ThingTypeUID(BINDING_ID,
"MyElectricalDataBridge");
public static final ThingTypeUID THING_TYPE_LINKY = new ThingTypeUID(BINDING_ID, "linky");
public static final Set SUPPORTED_DEVICE_THING_TYPES_UIDS = Set.of(THING_TYPE_API_ENEDIS_BRIDGE,
- THING_TYPE_API_MYELECTRICALDATA_BRIDGE, THING_TYPE_LINKY);
+ THING_TYPE_API_WEB_ENEDIS_BRIDGE, THING_TYPE_API_MYELECTRICALDATA_BRIDGE, THING_TYPE_LINKY);
// Thing properties
public static final String PUISSANCE = "puissance";
public static final String PRM_ID = "prmId";
public static final String USER_ID = "customerId";
+ public static final String AV2_ID = "av2_interne_id";
// List of all Channel id's
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
index 6e16317d4a4..15de2ca1e26 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyConfiguration.java
@@ -23,11 +23,17 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
*/
@NonNullByDefault
public class LinkyConfiguration {
+ public static final String INTERNAL_AUTH_ID = "internalAuthId";
+
public String token = "";
public String prmId = "";
public String clientId = "";
public String clientSecret = "";
+ public String username = "";
+ public String password = "";
+ public String internalAuthId = "";
+
public boolean seemsValid() {
return !prmId.isBlank();
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
index b33412af330..3e77a025297 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyHandlerFactory.java
@@ -20,6 +20,7 @@ import java.time.format.DateTimeFormatter;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.linky.internal.handler.EnedisBridgeHandler;
+import org.openhab.binding.linky.internal.handler.EnedisWebBridgeHandler;
import org.openhab.binding.linky.internal.handler.LinkyHandler;
import org.openhab.binding.linky.internal.handler.MyElectricalDataBridgeHandler;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
@@ -38,7 +39,6 @@ import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.HttpService;
-import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
@@ -80,7 +80,9 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
.atStartOfDay();
}
})
- .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+ .create();
+
+ // .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
private final LocaleProvider localeProvider;
@@ -109,6 +111,10 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
EnedisBridgeHandler handler = new EnedisBridgeHandler((Bridge) thing, this.httpClientFactory,
this.oAuthFactory, this.httpService, thingRegistry, componentContext, gson);
return handler;
+ } else if (thing.getThingTypeUID().equals(LinkyBindingConstants.THING_TYPE_API_WEB_ENEDIS_BRIDGE)) {
+ EnedisWebBridgeHandler handler = new EnedisWebBridgeHandler((Bridge) thing, this.httpClientFactory,
+ this.oAuthFactory, this.httpService, thingRegistry, componentContext, gson);
+ return handler;
} else if (thing.getThingTypeUID().equals(LinkyBindingConstants.THING_TYPE_API_MYELECTRICALDATA_BRIDGE)) {
MyElectricalDataBridgeHandler handler = new MyElectricalDataBridgeHandler((Bridge) thing,
this.httpClientFactory, this.oAuthFactory, this.httpService, thingRegistry, componentContext, gson);
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 2d8c8281ca9..b413beb1e18 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -12,6 +12,8 @@
*/
package org.openhab.binding.linky.internal.api;
+import java.net.HttpCookie;
+import java.net.URI;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
@@ -27,11 +29,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.FormContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.util.Fields;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AddressInfo;
import org.openhab.binding.linky.internal.dto.ContactInfo;
-import org.openhab.binding.linky.internal.dto.Contracts;
import org.openhab.binding.linky.internal.dto.Customer;
import org.openhab.binding.linky.internal.dto.CustomerIdResponse;
import org.openhab.binding.linky.internal.dto.CustomerReponse;
@@ -41,10 +45,8 @@ import org.openhab.binding.linky.internal.dto.MeterResponse;
import org.openhab.binding.linky.internal.dto.PrmInfo;
import org.openhab.binding.linky.internal.dto.TempoResponse;
import org.openhab.binding.linky.internal.dto.UsagePoint;
-import org.openhab.binding.linky.internal.dto.UsagePointDetails;
import org.openhab.binding.linky.internal.handler.ApiBridgeHandler;
import org.openhab.binding.linky.internal.handler.LinkyHandler;
-import org.openhab.binding.linky.internal.handler.MyElectricalDataBridgeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -67,6 +69,11 @@ public class EnedisHttpApi {
private final HttpClient httpClient;
private ApiBridgeHandler apiBridgeHandler;
+ public static final String ENEDIS_DOMAIN = ".enedis.fr";
+ public static final String URL_MON_COMPTE = "https://mon-compte" + EnedisHttpApi.ENEDIS_DOMAIN;
+ public static final String URL_COMPTE_PART = URL_MON_COMPTE.replace("compte", "compte-particulier");
+ public static final URI COOKIE_URI = URI.create(URL_COMPTE_PART);
+
private boolean connected = false;
public EnedisHttpApi(ApiBridgeHandler apiBridgeHandler, Gson gson, HttpClient httpClient) {
@@ -76,13 +83,16 @@ public class EnedisHttpApi {
}
public void initialize() throws LinkyException {
+ connected = true;
}
- private void disconnect() throws LinkyException {
- if (connected) {
- logger.debug("Logout process");
- connected = false;
- }
+ public void disconnect() throws LinkyException {
+
+ // if (connected) {
+ logger.debug("Logout process");
+ connected = false;
+ httpClient.getCookieStore().removeAll();
+ // }
}
public boolean isConnected() {
@@ -93,6 +103,23 @@ public class EnedisHttpApi {
disconnect();
}
+ public FormContentProvider getFormContent(String fieldName, String fieldValue) {
+ Fields fields = new Fields();
+ fields.put(fieldName, fieldValue);
+ return new FormContentProvider(fields);
+ }
+
+ public void addCookie(String key, String value) {
+ HttpCookie cookie = new HttpCookie(key, value);
+ cookie.setDomain(ENEDIS_DOMAIN);
+ cookie.setPath("/");
+ httpClient.getCookieStore().add(COOKIE_URI, cookie);
+ }
+
+ public String getLocation(ContentResponse response) {
+ return response.getHeaders().get(HttpHeader.LOCATION);
+ }
+
public String getData(LinkyHandler handler, String url) throws LinkyException {
return getData(apiBridgeHandler, url, httpClient, apiBridgeHandler.getToken(handler));
}
@@ -136,57 +163,34 @@ public class EnedisHttpApi {
}
}
+ /*
+ * private String getData(String url) throws LinkyException {
+ * try {
+ * ContentResponse result = httpClient.GET(url);
+ * if (result.getStatus() != 200) {
+ * throw new LinkyException("Error requesting '%s' : %s", url, result.getContentAsString());
+ * }
+ * return result.getContentAsString();
+ * } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ * throw new LinkyException(e, "Error getting url : '%s'", url);
+ * }
+ * }
+ */
+
public PrmInfo getPrmInfo(LinkyHandler handler, String prmId) throws LinkyException {
PrmInfo result = new PrmInfo();
- if (apiBridgeHandler instanceof MyElectricalDataBridgeHandler) {
- result.contractInfo = new Contracts();
- result.addressInfo = new AddressInfo();
- result.contactInfo = new ContactInfo();
- result.identityInfo = new IdentityInfo();
- result.usagePointInfo = new UsagePointDetails();
+ Customer customer = getCustomer(handler, prmId);
+ UsagePoint usagePoint = customer.usagePoints[0];
- result.contractInfo.subscribedPower = "12Kva";
- result.contactInfo.email = "lxxyyy@domain.net";
- result.contactInfo.phone = "--.--.--.--.--";
- result.contractInfo.contractStatus = "unknow";
- result.contractInfo.contractType = "unknow";
- result.contractInfo.distributionTariff = "unknow";
- result.contractInfo.lastActivationDate = "unknow";
- result.contractInfo.lastDistributionTariffChangeDate = "unknow";
- result.contractInfo.segment = "unknow";
- result.contractInfo.offpeakHours = "unknow";
+ result.contractInfo = usagePoint.contracts;
+ result.usagePointInfo = usagePoint.usagePoint;
+ result.identityInfo = getIdentity(handler, prmId);
+ result.addressInfo = getAddress(handler, prmId);
+ result.contactInfo = getContact(handler, prmId);
- result.addressInfo.city = "Ville";
- result.addressInfo.country = "France";
- result.addressInfo.postalCode = "xxxxx";
- result.addressInfo.inseeCode = "0";
- result.addressInfo.street = "xx Rue de yyyyyy";
-
- result.identityInfo.firstname = "Laurent";
- result.identityInfo.lastname = "ARNAL";
- result.identityInfo.title = "M.";
-
- result.usagePointInfo.meterType = "unknow";
- result.usagePointInfo.usagePointId = "unknow";
- result.usagePointInfo.usagePointStatus = "unknow";
-
- result.prmId = prmId;
- result.customerId = "xxxxxxxxxx";
-
- } else {
- Customer customer = getCustomer(handler, prmId);
- UsagePoint usagePoint = customer.usagePoints[0];
-
- result.contractInfo = usagePoint.contracts;
- result.usagePointInfo = usagePoint.usagePoint;
- result.identityInfo = getIdentity(handler, prmId);
- result.addressInfo = getAddress(handler, prmId);
- result.contactInfo = getContact(handler, prmId);
-
- result.prmId = result.usagePointInfo.usagePointId;
- result.customerId = customer.customerId;
- }
+ result.prmId = result.usagePointInfo.usagePointId;
+ result.customerId = customer.customerId;
return result;
}
@@ -317,7 +321,7 @@ public class EnedisHttpApi {
}
public TempoResponse getTempoData(LinkyHandler handler) throws LinkyException {
- String url = String.format(apiBridgeHandler.getTempoUrl(), "2024-01-01", "2024-06-30");
+ String url = String.format(apiBridgeHandler.getTempoUrl(), "2024-01-01", "2024-08-30");
if (!connected) {
initialize();
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java
new file mode 100644
index 00000000000..27eddb18695
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link AuthData} holds authentication information
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class AuthData {
+ public class AuthDataCallBack {
+ public class NameValuePair {
+ public @Nullable String name;
+ public @Nullable Object value;
+
+ public @Nullable String valueAsString() {
+ return (value instanceof String stringValue) ? stringValue : null;
+ }
+ }
+
+ public @Nullable String type;
+
+ public List output = List.of();
+ public List input = List.of();
+ }
+
+ public @Nullable String authId;
+ public @Nullable String template;
+ public @Nullable String stage;
+ public @Nullable String header;
+ public List callbacks = List.of();
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthResult.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthResult.java
new file mode 100644
index 00000000000..8339f1436ae
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthResult.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+/**
+ * The {@link AuthResult} holds informations about the ongoing authentication process
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+
+public class AuthResult {
+ public String successUrl;
+ public String tokenId;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index e2dd36cdf7a..744619d5703 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -78,8 +78,8 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
private HttpService httpService;
private BundleContext bundleContext;
- private final HttpClient httpClient;
- private EnedisHttpApi enedisApi;
+ protected final HttpClient httpClient;
+ protected EnedisHttpApi enedisApi;
private OAuthClientService oAuthService;
protected final ThingRegistry thingRegistry;
@@ -89,6 +89,8 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
private static final String TEMPLATE_PATH = "templates/";
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
+ protected final Gson gson;
+
public ApiBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
@@ -107,15 +109,14 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
}
+ this.gson = gson;
this.httpService = httpService;
this.thingRegistry = thingRegistry;
this.bundleContext = componentContext.getBundleContext();
this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
- httpClient.setFollowRedirects(false);
- httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
-
- this.enedisApi = new EnedisHttpApi(this, gson, this.httpClient);
+ this.httpClient.setFollowRedirects(false);
+ this.httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
try {
httpClient.start();
@@ -123,6 +124,8 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
}
+ this.enedisApi = new EnedisHttpApi(this, gson, this.httpClient);
+
config = getConfigAs(LinkyConfiguration.class);
String tokenUrl = "";
From 598efa7102b3b81c1681ad0f3484dd170c5d1ea9 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 14 Aug 2024 11:00:32 +0200
Subject: [PATCH 035/135] add support for PrmInfo and UserInfo from old API
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 160 ++++++++++++------
.../linky/internal/dto/WebPrmInfo.java | 47 +++++
.../linky/internal/dto/WebUserInfo.java | 40 +++++
.../internal/handler/ApiBridgeHandler.java | 40 +++--
.../internal/handler/EnedisBridgeHandler.java | 5 +
.../handler/EnedisWebBridgeHandler.java | 41 +----
.../linky/internal/handler/LinkyHandler.java | 57 ++++---
.../MyElectricalDataBridgeHandler.java | 5 +
8 files changed, 271 insertions(+), 124 deletions(-)
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/WebPrmInfo.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/WebUserInfo.java
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index b413beb1e18..68b3eb91e59 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -16,10 +16,7 @@ import java.net.HttpCookie;
import java.net.URI;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
+import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@@ -36,6 +33,7 @@ import org.eclipse.jetty.util.Fields;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AddressInfo;
import org.openhab.binding.linky.internal.dto.ContactInfo;
+import org.openhab.binding.linky.internal.dto.Contracts;
import org.openhab.binding.linky.internal.dto.Customer;
import org.openhab.binding.linky.internal.dto.CustomerIdResponse;
import org.openhab.binding.linky.internal.dto.CustomerReponse;
@@ -45,6 +43,9 @@ import org.openhab.binding.linky.internal.dto.MeterResponse;
import org.openhab.binding.linky.internal.dto.PrmInfo;
import org.openhab.binding.linky.internal.dto.TempoResponse;
import org.openhab.binding.linky.internal.dto.UsagePoint;
+import org.openhab.binding.linky.internal.dto.UsagePointDetails;
+import org.openhab.binding.linky.internal.dto.WebPrmInfo;
+import org.openhab.binding.linky.internal.dto.WebUserInfo;
import org.openhab.binding.linky.internal.handler.ApiBridgeHandler;
import org.openhab.binding.linky.internal.handler.LinkyHandler;
import org.slf4j.Logger;
@@ -74,7 +75,12 @@ public class EnedisHttpApi {
public static final String URL_COMPTE_PART = URL_MON_COMPTE.replace("compte", "compte-particulier");
public static final URI COOKIE_URI = URI.create(URL_COMPTE_PART);
- private boolean connected = false;
+ private static final String URL_APPS_LINCS = "https://alex.microapplications" + EnedisHttpApi.ENEDIS_DOMAIN;
+ private static final String USER_INFO_URL = URL_APPS_LINCS + "/userinfos";
+ private static final String PRM_INFO_BASE_URL = URL_APPS_LINCS + "/mes-mesures/api/private/v1/personnes/";
+ private static final String PRM_INFO_URL = PRM_INFO_BASE_URL + "null/prms";
+ private static final String MEASURE_URL = PRM_INFO_BASE_URL
+ + "%s/prms/%s/donnees-%s?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
public EnedisHttpApi(ApiBridgeHandler apiBridgeHandler, Gson gson, HttpClient httpClient) {
this.gson = gson;
@@ -82,27 +88,6 @@ public class EnedisHttpApi {
this.apiBridgeHandler = apiBridgeHandler;
}
- public void initialize() throws LinkyException {
- connected = true;
- }
-
- public void disconnect() throws LinkyException {
-
- // if (connected) {
- logger.debug("Logout process");
- connected = false;
- httpClient.getCookieStore().removeAll();
- // }
- }
-
- public boolean isConnected() {
- return connected;
- }
-
- public void dispose() throws LinkyException {
- disconnect();
- }
-
public FormContentProvider getFormContent(String fieldName, String fieldValue) {
Fields fields = new Fields();
fields.put(fieldName, fieldValue);
@@ -178,21 +163,92 @@ public class EnedisHttpApi {
*/
public PrmInfo getPrmInfo(LinkyHandler handler, String prmId) throws LinkyException {
+ if (!apiBridgeHandler.isConnected()) {
+ apiBridgeHandler.initialize();
+ }
+
PrmInfo result = new PrmInfo();
+ if (false) {
+ Customer customer = getCustomer(handler, prmId);
+ UsagePoint usagePoint = customer.usagePoints[0];
- Customer customer = getCustomer(handler, prmId);
- UsagePoint usagePoint = customer.usagePoints[0];
+ result.contractInfo = usagePoint.contracts;
+ result.usagePointInfo = usagePoint.usagePoint;
+ result.identityInfo = getIdentity(handler, prmId);
+ result.addressInfo = getAddress(handler, prmId);
+ result.contactInfo = getContact(handler, prmId);
- result.contractInfo = usagePoint.contracts;
- result.usagePointInfo = usagePoint.usagePoint;
- result.identityInfo = getIdentity(handler, prmId);
- result.addressInfo = getAddress(handler, prmId);
- result.contactInfo = getContact(handler, prmId);
+ result.prmId = result.usagePointInfo.usagePointId;
+ result.customerId = customer.customerId;
- result.prmId = result.usagePointInfo.usagePointId;
- result.customerId = customer.customerId;
+ return result;
+ } else {
+ WebPrmInfo[] webPrmsInfo;
+ WebUserInfo webUserInfo;
- return result;
+ String data = getData(PRM_INFO_URL);
+ if (data.isEmpty()) {
+ throw new LinkyException("Requesting '%s' returned an empty response", PRM_INFO_URL);
+ }
+ try {
+ webPrmsInfo = gson.fromJson(data, WebPrmInfo[].class);
+ if (webPrmsInfo == null || webPrmsInfo.length < 1) {
+ throw new LinkyException("Invalid prms data received");
+ }
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", PRM_INFO_URL);
+ }
+
+ data = getData(USER_INFO_URL);
+ if (data.isEmpty()) {
+ throw new LinkyException("Requesting '%s' returned an empty response", USER_INFO_URL);
+ }
+ try {
+ webUserInfo = gson.fromJson(data, WebUserInfo.class);
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching UserInfo.class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", USER_INFO_URL);
+ }
+
+ WebPrmInfo webPrmInfo = Arrays.stream(webPrmsInfo).filter(x -> x.prmId.equals(prmId)).findAny()
+ .orElseThrow();
+
+ result.addressInfo = new AddressInfo();
+ result.addressInfo.street = webPrmInfo.adresse.adresseLigneQuatre;
+ result.addressInfo.city = webPrmInfo.adresse.adresseLigneSix;
+ result.addressInfo.postalCode = webPrmInfo.adresse.adresseLigneSix;
+ result.addressInfo.country = webPrmInfo.adresse.adresseLigneSept;
+
+ result.contactInfo = new ContactInfo();
+ result.identityInfo = new IdentityInfo();
+ result.contractInfo = new Contracts();
+ result.usagePointInfo = new UsagePointDetails();
+
+ result.contactInfo.email = webUserInfo.userProperties.mail;
+ result.contactInfo.phone = "";
+
+ result.identityInfo.firstname = webUserInfo.userProperties.firstName;
+ result.identityInfo.lastname = webUserInfo.userProperties.name;
+ result.identityInfo.title = "";
+ // webUserInfo.userProperties.internId;
+ // webUserInfo.userProperties.personalInfo;
+
+ result.contractInfo.contractStatus = "";
+ result.contractInfo.contractType = "";
+ result.contractInfo.distributionTariff = "";
+ result.contractInfo.lastActivationDate = "";
+ result.contractInfo.lastDistributionTariffChangeDate = "";
+ result.contractInfo.offpeakHours = "";
+ result.contractInfo.segment = webPrmInfo.segment;
+ result.contractInfo.subscribedPower = "" + webPrmInfo.puissanceSouscrite;
+
+ result.prmId = prmId;
+ result.customerId = "";
+
+ return result;
+
+ }
}
public String formatUrl(String apiUrl, String prmId) {
@@ -200,9 +256,10 @@ public class EnedisHttpApi {
}
public Customer getCustomer(LinkyHandler handler, String prmId) throws LinkyException {
- if (!connected) {
- initialize();
+ if (!apiBridgeHandler.isConnected()) {
+ apiBridgeHandler.initialize();
}
+
String contractUrl = apiBridgeHandler.getContractUrl();
String data = getData(handler, formatUrl(contractUrl, prmId));
if (data.isEmpty()) {
@@ -221,8 +278,8 @@ public class EnedisHttpApi {
}
public AddressInfo getAddress(LinkyHandler handler, String prmId) throws LinkyException {
- if (!connected) {
- initialize();
+ if (!apiBridgeHandler.isConnected()) {
+ apiBridgeHandler.initialize();
}
String addressUrl = apiBridgeHandler.getAddressUrl();
String data = getData(handler, formatUrl(addressUrl, prmId));
@@ -242,8 +299,8 @@ public class EnedisHttpApi {
}
public IdentityInfo getIdentity(LinkyHandler handler, String prmId) throws LinkyException {
- if (!connected) {
- initialize();
+ if (!apiBridgeHandler.isConnected()) {
+ apiBridgeHandler.initialize();
}
String identityUrl = apiBridgeHandler.getIdentityUrl();
String data = getData(handler, formatUrl(identityUrl, prmId));
@@ -263,8 +320,8 @@ public class EnedisHttpApi {
}
public ContactInfo getContact(LinkyHandler handler, String prmId) throws LinkyException {
- if (!connected) {
- initialize();
+ if (!apiBridgeHandler.isConnected()) {
+ apiBridgeHandler.initialize();
}
String contactUrl = apiBridgeHandler.getContactUrl();
String data = getData(handler, formatUrl(contactUrl, prmId));
@@ -290,9 +347,10 @@ public class EnedisHttpApi {
String dtEnd = to.format(API_DATE_FORMAT);
String url = String.format(apiUrl, prmId, dtStart, dtEnd);
- if (!connected) {
- initialize();
+ if (!apiBridgeHandler.isConnected()) {
+ apiBridgeHandler.initialize();
}
+
String data = getData(handler, url);
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", url);
@@ -322,9 +380,15 @@ public class EnedisHttpApi {
public TempoResponse getTempoData(LinkyHandler handler) throws LinkyException {
String url = String.format(apiBridgeHandler.getTempoUrl(), "2024-01-01", "2024-08-30");
- if (!connected) {
- initialize();
+
+ if (url.isEmpty()) {
+ return new TempoResponse();
}
+
+ if (!apiBridgeHandler.isConnected()) {
+ apiBridgeHandler.initialize();
+ }
+
String data = getData(handler, url);
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", url);
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/WebPrmInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/WebPrmInfo.java
new file mode 100644
index 00000000000..aa0ce6684e9
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/WebPrmInfo.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+
+public class WebPrmInfo {
+ public class Adresse {
+ public Object adresseLigneUn;
+ public String adresseLigneDeux;
+ public Object adresseLigneTrois;
+ public String adresseLigneQuatre;
+ public Object adresseLigneCinq;
+ public String adresseLigneSix;
+ public String adresseLigneSept;
+ }
+
+ public String prmId;
+ public String dateFinRole;
+ public String segment;
+ public Adresse adresse;
+ public String typeCompteur;
+ public String niveauOuvertureServices;
+ public String communiquant;
+ public long dateSoutirage;
+ public String dateInjection;
+ public int departement;
+ public int puissanceSouscrite;
+ public String codeCalendrier;
+ public String codeTitulaire;
+ public boolean collecteActivee;
+ public boolean multiTitulaire;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/WebUserInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/WebUserInfo.java
new file mode 100644
index 00000000000..f79c33dcc4a
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/WebUserInfo.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about the user account
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+
+public class WebUserInfo {
+ public class UserProperties {
+ @SerializedName("av2_interne_id")
+ public String internId;
+ @SerializedName("av2_prenom")
+ public String firstName;
+ @SerializedName("av2_mail")
+ public String mail;
+ @SerializedName("av2_nom")
+ public String name;
+ @SerializedName("av2_infos_personnalisees")
+ public String personalInfo;
+ }
+
+ public String username;
+ public boolean connected;
+ public UserProperties userProperties;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 744619d5703..2600ed0ec7d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -91,6 +91,8 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
protected final Gson gson;
+ protected boolean connected = false;
+
public ApiBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
@@ -163,7 +165,7 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
scheduler.submit(() -> {
try {
- enedisApi.initialize();
+ connectionInit();
updateStatus(ThingStatus.ONLINE);
} catch (LinkyException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
@@ -171,6 +173,32 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
});
}
+ protected abstract void connectionInit() throws LinkyException;
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ public void disconnect() {
+ if (connected) {
+ logger.debug("Logout process");
+ connected = false;
+ httpClient.getCookieStore().removeAll();
+ }
+
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Shutting down Netatmo API bridge handler.");
+ disconnect();
+
+ httpService.unregister(LinkyBindingConstants.LINKY_ALIAS);
+ httpService.unregister(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS);
+
+ super.dispose();
+ }
+
/*
* @Override
* protected void deactivate(ComponentContext componentContext) {
@@ -188,16 +216,6 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
// TODO Auto-generated method stub
}
- @Override
- public void dispose() {
- logger.debug("Shutting down Netatmo API bridge handler.");
-
- httpService.unregister(LinkyBindingConstants.LINKY_ALIAS);
- httpService.unregister(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS);
-
- super.dispose();
- }
-
private void registerServlet() {
try {
if (servlet == null) {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index de49cb2a96c..07fdecdd2bc 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -75,6 +75,11 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
super.dispose();
}
+ @Override
+ public void connectionInit() {
+
+ }
+
@Override
public String getToken(LinkyHandler handler) throws LinkyException {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index b72838c90e4..f67666cd37e 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -31,7 +31,6 @@ import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.api.EnedisHttpApi;
import org.openhab.binding.linky.internal.dto.AuthData;
import org.openhab.binding.linky.internal.dto.AuthResult;
-import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
@@ -71,14 +70,8 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
private static final String URL_APPS_LINCS = "https://alex.microapplications" + EnedisHttpApi.ENEDIS_DOMAIN;
private static final String URL_ENEDIS_AUTHENTICATE = URL_APPS_LINCS + "/authenticate?target="
+ EnedisHttpApi.URL_COMPTE_PART;
- private static final String USER_INFO_URL = URL_APPS_LINCS + "/userinfos";
- private static final String PRM_INFO_BASE_URL = URL_APPS_LINCS + "/mes-mesures/api/private/v1/personnes/";
- private static final String PRM_INFO_URL = PRM_INFO_BASE_URL + "null/prms";
- private static final String MEASURE_URL = PRM_INFO_BASE_URL
- + "%s/prms/%s/donnees-%s?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
- private static final Pattern REQ_PATTERN = Pattern.compile("ReqID%(.*?)%26");
- private boolean connected = false;
+ private static final Pattern REQ_PATTERN = Pattern.compile("ReqID%(.*?)%26");
public EnedisWebBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
@@ -89,12 +82,6 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
@Override
public void initialize() {
super.initialize();
-
- try {
- doAuth();
- } catch (LinkyException ex) {
- logger.debug("Exception: {}", ex.getMessage());
- }
}
@Override
@@ -107,30 +94,9 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
return config.clientSecret;
}
- @Override
- public void dispose() {
- logger.debug("Shutting down Netatmo API bridge handler.");
- try {
- enedisApi.disconnect();
- } catch (LinkyException ex) {
- logger.debug("Exception: {}", ex.getMessage());
- }
- super.dispose();
- }
-
@Override
public String getToken(LinkyHandler handler) throws LinkyException {
-
- AccessTokenResponse accesToken = getAccessTokenResponse();
- if (accesToken == null) {
- accesToken = getAccessTokenByClientCredentials();
- }
-
- if (accesToken == null) {
- throw new LinkyException("no token");
- }
-
- return "Bearer " + accesToken.getAccessToken();
+ return "";
}
@Override
@@ -173,7 +139,8 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
return "";
}
- public void doAuth() throws LinkyException {
+ @Override
+ protected void connectionInit() throws LinkyException {
logger.debug("Starting login process for user : {}", config.username);
try {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 49c17c01d5a..625d2c8f7b8 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -506,15 +506,17 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("getConsumptionData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
to.format(DateTimeFormatter.ISO_LOCAL_DATE));
- EnedisHttpApi api = this.enedisApi;
- if (api != null) {
- try {
- MeterReading meterReading = api.getEnergyData(this, config.prmId, from, to);
- updateStatus(ThingStatus.ONLINE);
- return meterReading;
- } catch (LinkyException e) {
- logger.debug("Exception when getting consumption data: {}", e.getMessage(), e);
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ if (false) {
+ EnedisHttpApi api = this.enedisApi;
+ if (api != null) {
+ try {
+ MeterReading meterReading = api.getEnergyData(this, config.prmId, from, to);
+ updateStatus(ThingStatus.ONLINE);
+ return meterReading;
+ } catch (LinkyException e) {
+ logger.debug("Exception when getting consumption data: {}", e.getMessage(), e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ }
}
}
return null;
@@ -524,15 +526,17 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("getPowerData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
to.format(DateTimeFormatter.ISO_LOCAL_DATE));
- EnedisHttpApi api = this.enedisApi;
- if (api != null) {
- try {
- MeterReading meterReading = api.getPowerData(this, config.prmId, from, to);
- updateStatus(ThingStatus.ONLINE);
- return meterReading;
- } catch (LinkyException e) {
- logger.debug("Exception when getting power data: {}", e.getMessage(), e);
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ if (false) {
+ EnedisHttpApi api = this.enedisApi;
+ if (api != null) {
+ try {
+ MeterReading meterReading = api.getPowerData(this, config.prmId, from, to);
+ updateStatus(ThingStatus.ONLINE);
+ return meterReading;
+ } catch (LinkyException e) {
+ logger.debug("Exception when getting power data: {}", e.getMessage(), e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ }
}
}
return null;
@@ -556,19 +560,16 @@ public class LinkyHandler extends BaseThingHandler {
}
private boolean isConnected() {
- EnedisHttpApi api = this.enedisApi;
- return api == null ? false : api.isConnected();
+ Bridge bridge = getBridge();
+ ApiBridgeHandler bridgeHandler = (ApiBridgeHandler) bridge.getHandler();
+ if (bridgeHandler == null) {
+ return false;
+ }
+
+ return bridgeHandler.isConnected();
}
private void disconnect() {
- EnedisHttpApi api = this.enedisApi;
- if (api != null) {
- try {
- api.dispose();
- } catch (LinkyException e) {
- logger.debug("disconnect: {}", e.getMessage());
- }
- }
}
@Override
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index 4762ac9ffd6..12f84e13578 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -63,6 +63,11 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
super(bridge, httpClientFactory, oAuthFactory, httpService, thingRegistry, componentContext, gson);
}
+ @Override
+ public void connectionInit() {
+
+ }
+
@Override
public void initialize() {
super.initialize();
From 4e26f72a156bee5cdbceca48710e7b911f4704c1 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 14 Aug 2024 11:54:37 +0200
Subject: [PATCH 036/135] start some code cleanup
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 40 +++++---------
.../internal/handler/ApiBridgeHandler.java | 3 +
.../internal/handler/EnedisBridgeHandler.java | 9 +++
.../handler/EnedisWebBridgeHandler.java | 54 +++++++++++-------
.../linky/internal/handler/LinkyHandler.java | 55 ++++++++++---------
.../MyElectricalDataBridgeHandler.java | 8 +++
6 files changed, 96 insertions(+), 73 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 68b3eb91e59..e7e5cb6d4a2 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -13,9 +13,7 @@
package org.openhab.binding.linky.internal.api;
import java.net.HttpCookie;
-import java.net.URI;
import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@@ -47,6 +45,7 @@ import org.openhab.binding.linky.internal.dto.UsagePointDetails;
import org.openhab.binding.linky.internal.dto.WebPrmInfo;
import org.openhab.binding.linky.internal.dto.WebUserInfo;
import org.openhab.binding.linky.internal.handler.ApiBridgeHandler;
+import org.openhab.binding.linky.internal.handler.EnedisWebBridgeHandler;
import org.openhab.binding.linky.internal.handler.LinkyHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -63,25 +62,11 @@ import com.google.gson.JsonSyntaxException;
@NonNullByDefault
public class EnedisHttpApi {
- private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
-
private final Logger logger = LoggerFactory.getLogger(EnedisHttpApi.class);
private final Gson gson;
private final HttpClient httpClient;
private ApiBridgeHandler apiBridgeHandler;
- public static final String ENEDIS_DOMAIN = ".enedis.fr";
- public static final String URL_MON_COMPTE = "https://mon-compte" + EnedisHttpApi.ENEDIS_DOMAIN;
- public static final String URL_COMPTE_PART = URL_MON_COMPTE.replace("compte", "compte-particulier");
- public static final URI COOKIE_URI = URI.create(URL_COMPTE_PART);
-
- private static final String URL_APPS_LINCS = "https://alex.microapplications" + EnedisHttpApi.ENEDIS_DOMAIN;
- private static final String USER_INFO_URL = URL_APPS_LINCS + "/userinfos";
- private static final String PRM_INFO_BASE_URL = URL_APPS_LINCS + "/mes-mesures/api/private/v1/personnes/";
- private static final String PRM_INFO_URL = PRM_INFO_BASE_URL + "null/prms";
- private static final String MEASURE_URL = PRM_INFO_BASE_URL
- + "%s/prms/%s/donnees-%s?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
-
public EnedisHttpApi(ApiBridgeHandler apiBridgeHandler, Gson gson, HttpClient httpClient) {
this.gson = gson;
this.httpClient = httpClient;
@@ -96,9 +81,9 @@ public class EnedisHttpApi {
public void addCookie(String key, String value) {
HttpCookie cookie = new HttpCookie(key, value);
- cookie.setDomain(ENEDIS_DOMAIN);
+ cookie.setDomain(EnedisWebBridgeHandler.ENEDIS_DOMAIN);
cookie.setPath("/");
- httpClient.getCookieStore().add(COOKIE_URI, cookie);
+ httpClient.getCookieStore().add(EnedisWebBridgeHandler.COOKIE_URI, cookie);
}
public String getLocation(ContentResponse response) {
@@ -186,9 +171,12 @@ public class EnedisHttpApi {
WebPrmInfo[] webPrmsInfo;
WebUserInfo webUserInfo;
- String data = getData(PRM_INFO_URL);
+ String prm_info_url = apiBridgeHandler.getContractUrl();
+ String user_info_url = apiBridgeHandler.getContactUrl();
+
+ String data = getData(prm_info_url);
if (data.isEmpty()) {
- throw new LinkyException("Requesting '%s' returned an empty response", PRM_INFO_URL);
+ throw new LinkyException("Requesting '%s' returned an empty response", prm_info_url);
}
try {
webPrmsInfo = gson.fromJson(data, WebPrmInfo[].class);
@@ -197,18 +185,18 @@ public class EnedisHttpApi {
}
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", PRM_INFO_URL);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", prm_info_url);
}
- data = getData(USER_INFO_URL);
+ data = getData(user_info_url);
if (data.isEmpty()) {
- throw new LinkyException("Requesting '%s' returned an empty response", USER_INFO_URL);
+ throw new LinkyException("Requesting '%s' returned an empty response", user_info_url);
}
try {
webUserInfo = gson.fromJson(data, WebUserInfo.class);
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching UserInfo.class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", USER_INFO_URL);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", user_info_url);
}
WebPrmInfo webPrmInfo = Arrays.stream(webPrmsInfo).filter(x -> x.prmId.equals(prmId)).findAny()
@@ -343,8 +331,8 @@ public class EnedisHttpApi {
private MeterReading getMeasures(LinkyHandler handler, String apiUrl, String prmId, LocalDate from, LocalDate to)
throws LinkyException {
- String dtStart = from.format(API_DATE_FORMAT);
- String dtEnd = to.format(API_DATE_FORMAT);
+ String dtStart = from.format(apiBridgeHandler.getApiDateFormat());
+ String dtEnd = to.format(apiBridgeHandler.getApiDateFormat());
String url = String.format(apiUrl, prmId, dtStart, dtEnd);
if (!apiBridgeHandler.isConnected()) {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 2600ed0ec7d..2b5c7506e6a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -19,6 +19,7 @@ import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
@@ -354,4 +355,6 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
public abstract String getMaxPowerUrl();
public abstract String getTempoUrl();
+
+ public abstract DateTimeFormatter getApiDateFormat();
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index 07fdecdd2bc..abce1aa8fee 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -12,6 +12,8 @@
*/
package org.openhab.binding.linky.internal.handler;
+import java.time.format.DateTimeFormatter;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
@@ -47,6 +49,8 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
+ private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
public EnedisBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
@@ -134,4 +138,9 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
public String getTempoUrl() {
return "";
}
+
+ @Override
+ public DateTimeFormatter getApiDateFormat() {
+ return API_DATE_FORMAT;
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index f67666cd37e..b0c486fbe06 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -12,6 +12,7 @@
*/
package org.openhab.binding.linky.internal.handler;
+import java.net.URI;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
@@ -28,7 +29,6 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
-import org.openhab.binding.linky.internal.api.EnedisHttpApi;
import org.openhab.binding.linky.internal.dto.AuthData;
import org.openhab.binding.linky.internal.dto.AuthResult;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
@@ -54,25 +54,33 @@ import com.google.gson.JsonSyntaxException;
public class EnedisWebBridgeHandler extends ApiBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(EnedisWebBridgeHandler.class);
- private static final String BASE_URL = "https://ext.prod-sandbox.api.enedis.fr/";
+ public static final String ENEDIS_DOMAIN = ".enedis.fr";
+
+ private static final String BASE_URL = "https://alex.microapplications" + ENEDIS_DOMAIN;
+
+ public static final String URL_MON_COMPTE = "https://mon-compte" + ENEDIS_DOMAIN;
+ public static final String URL_COMPTE_PART = URL_MON_COMPTE.replace("compte", "compte-particulier");
+ public static final URI COOKIE_URI = URI.create(URL_COMPTE_PART);
+
+ private static final String USER_INFO_URL = BASE_URL + "/userinfos";
+ private static final String PRM_INFO_BASE_URL = BASE_URL + "/mes-mesures/api/private/v1/personnes/";
+ private static final String PRM_INFO_URL = PRM_INFO_BASE_URL + "null/prms";
+
+ private static final String MEASURE_DAILY_CONSUMPTION_URL = PRM_INFO_BASE_URL
+ + "undefined/prms/%s/donnees-energie?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
- private static final String CONTRACT_URL = BASE_URL + "customers_upc/v5/usage_points/contracts?usage_point_id=%s";
- private static final String IDENTITY_URL = BASE_URL + "customers_i/v5/identity?usage_point_id=%s";
- private static final String CONTACT_URL = BASE_URL + "customers_cd/v5/contact_data?usage_point_id=%s";
- private static final String ADDRESS_URL = BASE_URL + "customers_upa/v5/usage_points/addresses?usage_point_id=%s";
- private static final String MEASURE_DAILY_CONSUMPTION_URL = BASE_URL
- + "metering_data_dc/v5/daily_consumption?usage_point_id=%s&start=%s&end=%s";
private static final String MEASURE_MAX_POWER_URL = BASE_URL
- + "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
+ + "undefined/prms/%s/donnees-pmax?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
- private static final String URL_APPS_LINCS = "https://alex.microapplications" + EnedisHttpApi.ENEDIS_DOMAIN;
- private static final String URL_ENEDIS_AUTHENTICATE = URL_APPS_LINCS + "/authenticate?target="
- + EnedisHttpApi.URL_COMPTE_PART;
+ private static final String URL_ENEDIS_AUTHENTICATE = BASE_URL + "/authenticate?target=" + URL_COMPTE_PART;
private static final Pattern REQ_PATTERN = Pattern.compile("ReqID%(.*?)%26");
+ private static final String BASE_MYELECT_URL = "https://www.myelectricaldata.fr/";
+ private static final String TEMPO_URL = BASE_MYELECT_URL + "rte/tempo/%s/%s";
+
public EnedisWebBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
@@ -106,22 +114,22 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
@Override
public String getContactUrl() {
- return CONTACT_URL;
+ return USER_INFO_URL;
}
@Override
public String getContractUrl() {
- return CONTRACT_URL;
+ return PRM_INFO_URL;
}
@Override
public String getIdentityUrl() {
- return IDENTITY_URL;
+ return USER_INFO_URL;
}
@Override
public String getAddressUrl() {
- return ADDRESS_URL;
+ return PRM_INFO_URL;
}
@Override
@@ -136,7 +144,12 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
@Override
public String getTempoUrl() {
- return "";
+ return TEMPO_URL;
+ }
+
+ @Override
+ public DateTimeFormatter getApiDateFormat() {
+ return API_DATE_FORMAT;
}
@Override
@@ -167,9 +180,9 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
}
String reqId = m.group(1);
- String authenticateUrl = EnedisHttpApi.URL_MON_COMPTE
+ String authenticateUrl = URL_MON_COMPTE
+ "/auth/json/authenticate?realm=/enedis&forward=true&spEntityID=SP-ODW-PROD&goto=/auth/SSOPOST/metaAlias/enedis/providerIDP?ReqID%"
- + reqId + "%26index%3Dnull%26acsURL%3D" + URL_APPS_LINCS
+ + reqId + "%26index%3Dnull%26acsURL%3D" + BASE_URL
+ "/saml/SSO%26spEntityID%3DSP-ODW-PROD%26binding%3Durn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST&AMAuthCookie=";
logger.debug("Step 3 : auth1 - retrieve the template, thanks to cookie internalAuthId user is already set");
@@ -206,7 +219,7 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
enedisApi.addCookie("enedisExt", authResult.tokenId);
logger.debug("Step 5 : retrieve the SAMLresponse");
- data = enedisApi.getData(EnedisHttpApi.URL_MON_COMPTE + "/" + authResult.successUrl);
+ data = enedisApi.getData(URL_MON_COMPTE + "/" + authResult.successUrl);
htmlDocument = Jsoup.parse(data);
el = htmlDocument.select("form").first();
samlInput = el.select("input[name=SAMLResponse]").first();
@@ -222,4 +235,5 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
throw new LinkyException(e, "Error opening connection with Enedis webservice");
}
}
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 625d2c8f7b8..bdf00fa30b3 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -506,19 +506,18 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("getConsumptionData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
to.format(DateTimeFormatter.ISO_LOCAL_DATE));
- if (false) {
- EnedisHttpApi api = this.enedisApi;
- if (api != null) {
- try {
- MeterReading meterReading = api.getEnergyData(this, config.prmId, from, to);
- updateStatus(ThingStatus.ONLINE);
- return meterReading;
- } catch (LinkyException e) {
- logger.debug("Exception when getting consumption data: {}", e.getMessage(), e);
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
- }
+ EnedisHttpApi api = this.enedisApi;
+ if (api != null) {
+ try {
+ MeterReading meterReading = api.getEnergyData(this, config.prmId, from, to);
+ updateStatus(ThingStatus.ONLINE);
+ return meterReading;
+ } catch (LinkyException e) {
+ logger.debug("Exception when getting consumption data: {}", e.getMessage(), e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
+
return null;
}
@@ -526,19 +525,18 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("getPowerData from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
to.format(DateTimeFormatter.ISO_LOCAL_DATE));
- if (false) {
- EnedisHttpApi api = this.enedisApi;
- if (api != null) {
- try {
- MeterReading meterReading = api.getPowerData(this, config.prmId, from, to);
- updateStatus(ThingStatus.ONLINE);
- return meterReading;
- } catch (LinkyException e) {
- logger.debug("Exception when getting power data: {}", e.getMessage(), e);
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
- }
+ EnedisHttpApi api = this.enedisApi;
+ if (api != null) {
+ try {
+ MeterReading meterReading = api.getPowerData(this, config.prmId, from, to);
+ updateStatus(ThingStatus.ONLINE);
+ return meterReading;
+ } catch (LinkyException e) {
+ logger.debug("Exception when getting power data: {}", e.getMessage(), e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
+
return null;
}
@@ -561,12 +559,15 @@ public class LinkyHandler extends BaseThingHandler {
private boolean isConnected() {
Bridge bridge = getBridge();
- ApiBridgeHandler bridgeHandler = (ApiBridgeHandler) bridge.getHandler();
- if (bridgeHandler == null) {
- return false;
- }
+ if (bridge != null) {
+ ApiBridgeHandler bridgeHandler = (ApiBridgeHandler) bridge.getHandler();
+ if (bridgeHandler == null) {
+ return false;
+ }
- return bridgeHandler.isConnected();
+ return bridgeHandler.isConnected();
+ }
+ return false;
}
private void disconnect() {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index 12f84e13578..302dcfa1e0d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -12,6 +12,7 @@
*/
package org.openhab.binding.linky.internal.handler;
+import java.time.format.DateTimeFormatter;
import java.util.Collection;
import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -55,6 +56,8 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
+ private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
// https://www.myelectricaldata.fr/v1/oauth2/authorize?response_type=code&client_id=&state=linky&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fconnectlinky&scope=am_application_scope+default&user_type=aa&person_id=-1&usage_points_id=aa
public MyElectricalDataBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
@@ -176,4 +179,9 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
public String getTempoUrl() {
return TEMPO_URL;
}
+
+ @Override
+ public DateTimeFormatter getApiDateFormat() {
+ return API_DATE_FORMAT;
+ }
}
From 3669afa1d2f1dbd44fd7cf994a76030e736345b9 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 14 Aug 2024 12:37:13 +0200
Subject: [PATCH 037/135] first working version with old web apis
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 14 +++++++
.../linky/internal/dto/MeterReading.java | 31 +++++++++++++++-
.../linky/internal/handler/LinkyHandler.java | 37 ++++++++++++-------
3 files changed, 67 insertions(+), 15 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index e7e5cb6d4a2..2cabaae7493 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -30,6 +30,7 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.util.Fields;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AddressInfo;
+import org.openhab.binding.linky.internal.dto.ConsumptionReport;
import org.openhab.binding.linky.internal.dto.ContactInfo;
import org.openhab.binding.linky.internal.dto.Contracts;
import org.openhab.binding.linky.internal.dto.Customer;
@@ -345,10 +346,23 @@ public class EnedisHttpApi {
}
logger.trace("getData returned {}", data);
try {
+ // See if with have response header from old Web API
+ if (data.startsWith("{\"1\":{\"CONS\"")) {
+ // If so, decode to ConsumptionReport, and convert to new Format
+ ConsumptionReport consomptionReport = gson.fromJson(data, ConsumptionReport.class);
+ if (consomptionReport == null) {
+ throw new LinkyException("No report data received");
+ }
+
+ return MeterReading.fromComsumptionReport(consomptionReport);
+ }
+
+ // Else decode directly to new API format
MeterResponse meterResponse = gson.fromJson(data, MeterResponse.class);
if (meterResponse == null) {
throw new LinkyException("No report data received");
}
+
return meterResponse.meterReading;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching ConsumptionReport.class: {}", data);
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
index e7155eb238c..842e2f60696 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
@@ -38,8 +38,37 @@ public class MeterReading {
@SerializedName("interval_reading")
public IntervalReading[] dayValue;
-
public IntervalReading[] weekValue;
public IntervalReading[] monthValue;
public IntervalReading[] yearValue;
+
+ public static MeterReading fromComsumptionReport(ConsumptionReport comsumptionReport) {
+ MeterReading result = new MeterReading();
+ result.readingType = new ReadingType();
+
+ result.dayValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.days);
+ result.weekValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.weeks);
+ result.monthValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.months);
+ result.yearValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.years);
+
+ return result;
+ }
+
+ public static IntervalReading[] fromAgregat(ConsumptionReport.Aggregate agregat) {
+
+ int size = agregat.datas.size();
+ IntervalReading[] result = new IntervalReading[size];
+
+ for (int i = 0; i < size; i++) {
+ Double data = agregat.datas.get(i);
+ ConsumptionReport.Period period = agregat.periodes.get(i);
+ String label = agregat.labels.get(i);
+
+ result[i] = new IntervalReading();
+ result[i].value = data;
+ result[i].date = period.dateDebut.toLocalDateTime();
+ }
+
+ return result;
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index bdf00fa30b3..8b319692fff 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -252,8 +252,8 @@ public class LinkyHandler extends BaseThingHandler {
private synchronized void updateData() {
boolean connectedBefore = isConnected();
- // updateEnergyData();
- // updatePowerData();
+ updateEnergyData();
+ updatePowerData();
TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
TempoResponse tempoData = getTempoData();
@@ -344,11 +344,14 @@ public class LinkyHandler extends BaseThingHandler {
* Request new dayly/weekly data and updates channels
*/
- private synchronized void updateDailyWeeklyData() {
- if (isLinked(YESTERDAY) || isLinked(LAST_WEEK) || isLinked(THIS_WEEK)) {
- dailyConsumption.getValue().ifPresentOrElse(values -> {
- int dSize = values.intervalReading.length;
- updateKwhChannel(YESTERDAY, values.intervalReading[dSize - 1].value / 1000.00);
+ private synchronized void updateEnergyData() {
+ dailyConsumption.getValue().ifPresentOrElse(values -> {
+ int dSize = values.dayValue.length;
+ double divider = 1000.00;
+ divider = 1.00;
+ updateKwhChannel(DAY_MINUS_1, values.dayValue[dSize - 1].value / divider);
+ updateKwhChannel(DAY_MINUS_2, values.dayValue[dSize - 2].value / divider);
+ updateKwhChannel(DAY_MINUS_3, values.dayValue[dSize - 3].value / divider);
LocalDate currentDt = LocalDate.now();
int idxCurrentYear = currentDt.getYear() - 2021;
@@ -359,15 +362,21 @@ public class LinkyHandler extends BaseThingHandler {
int idxCurrentWeek = (52 * idxCurrentYear) + currentWeek;
int idxCurrentMonth = (12 * idxCurrentYear) + currentMonth;
- int idxPreviousWeek = idxCurrentWeek - 1;
- int idxPreviousMonth = idxCurrentMonth - 1;
- int idxPreviousYear = idxCurrentYear - 1;
+ updateKwhChannel(WEEK_MINUS_0, values.weekValue[idxCurrentWeek].value / divider);
+ updateKwhChannel(WEEK_MINUS_1, values.weekValue[idxCurrentWeek - 1].value / divider);
+ updateKwhChannel(WEEK_MINUS_2, values.weekValue[idxCurrentWeek - 2].value / divider);
- updateKwhChannel(THIS_WEEK, values.WeekValue[idxCurrentWeek].value / 1000.00);
- updateKwhChannel(LAST_WEEK, values.WeekValue[idxPreviousWeek].value / 1000.00);
+ updateKwhChannel(MONTH_MINUS_0, values.monthValue[idxCurrentMonth].value / divider);
+ updateKwhChannel(MONTH_MINUS_1, values.monthValue[idxCurrentMonth - 1].value / divider);
+ updateKwhChannel(MONTH_MINUS_2, values.monthValue[idxCurrentMonth - 2].value / divider);
- updateKwhChannel(THIS_MONTH, values.MonthValue[idxCurrentMonth].value / 1000.00);
- updateKwhChannel(LAST_MONTH, values.WeekValue[idxPreviousMonth].value / 1000.00);
+ updateKwhChannel(YEAR_MINUS_0, values.yearValue[idxCurrentYear].value / divider);
+ updateKwhChannel(YEAR_MINUS_1, values.yearValue[idxCurrentYear - 1].value / divider);
+ updateKwhChannel(YEAR_MINUS_2, values.yearValue[idxCurrentYear - 2].value / divider);
+ }, () -> {
+ updateKwhChannel(DAY_MINUS_1, Double.NaN);
+ updateKwhChannel(DAY_MINUS_2, Double.NaN);
+ updateKwhChannel(DAY_MINUS_3, Double.NaN);
updateKwhChannel(THIS_YEAR, values.YearValue[idxCurrentYear].value / 1000.00);
updateKwhChannel(LAST_YEAR, values.YearValue[idxPreviousYear].value / 1000.00);
From dee0612adb4e04e08737fadef3a157e9cb836dc2 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 14 Aug 2024 12:39:37 +0200
Subject: [PATCH 038/135] spotless:apply
Signed-off-by: Laurent ARNAL
---
.../openhab/binding/linky/internal/handler/ApiBridgeHandler.java | 1 -
.../binding/linky/internal/handler/EnedisBridgeHandler.java | 1 -
.../binding/linky/internal/handler/EnedisWebBridgeHandler.java | 1 -
.../linky/internal/handler/MyElectricalDataBridgeHandler.java | 1 -
4 files changed, 4 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 2b5c7506e6a..23c449cd671 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -186,7 +186,6 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
connected = false;
httpClient.getCookieStore().removeAll();
}
-
}
@Override
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index abce1aa8fee..d865cfafe14 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -81,7 +81,6 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
@Override
public void connectionInit() {
-
}
@Override
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index b0c486fbe06..095066519a8 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -235,5 +235,4 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
throw new LinkyException(e, "Error opening connection with Enedis webservice");
}
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index 302dcfa1e0d..59aa454ac26 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -68,7 +68,6 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
@Override
public void connectionInit() {
-
}
@Override
From f3d492fc1d1e7c320f6924f96d515a8ac27a8d92 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 14 Aug 2024 12:49:52 +0200
Subject: [PATCH 039/135] fix maxpower web api url
Signed-off-by: Laurent ARNAL
---
.../handler/EnedisWebBridgeHandler.java | 2 +-
.../linky/internal/handler/LinkyHandler.java | 52 ++++++++++---------
2 files changed, 28 insertions(+), 26 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index 095066519a8..73ad851a3cd 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -69,7 +69,7 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
private static final String MEASURE_DAILY_CONSUMPTION_URL = PRM_INFO_BASE_URL
+ "undefined/prms/%s/donnees-energie?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
- private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ private static final String MEASURE_MAX_POWER_URL = PRM_INFO_BASE_URL
+ "undefined/prms/%s/donnees-pmax?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 8b319692fff..e0c48a29b9c 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -312,32 +312,34 @@ public class LinkyHandler extends BaseThingHandler {
}
}
- /*
- * private synchronized void updatePowerData() {
- * if (isLinked(PEAK_POWER) || isLinked(PEAK_TIMESTAMP)) {
- * cachedPowerData.getValue().ifPresentOrElse(values -> {
- * Aggregate days = values.aggregats.days;
- * updateVAChannel(PEAK_POWER, days.datas.get(days.datas.size() - 1));
- * updateState(PEAK_TIMESTAMP, new DateTimeType(days.periodes.get(days.datas.size() - 1).dateDebut));
- * }, () -> {
- * updateKwhChannel(PEAK_POWER, Double.NaN);
- * updateState(PEAK_TIMESTAMP, UnDefType.UNDEF);
- * });
- * }
- * }
- */
+ private synchronized void updatePowerData() {
+ dailyConsumptionMaxPower.getValue().ifPresentOrElse(values -> {
+ int dSize = values.dayValue.length;
+ double divider = 1000.00;
+ divider = 1.00;
- private void setCurrentAndPrevious(Aggregate periods, String currentChannel, String previousChannel) {
- double currentValue = 0.0;
- double previousValue = 0.0;
- if (!periods.datas.isEmpty()) {
- currentValue = periods.datas.get(periods.datas.size() - 1);
- if (periods.datas.size() > 1) {
- previousValue = periods.datas.get(periods.datas.size() - 2);
- }
- }
- updateKwhChannel(currentChannel, currentValue);
- updateKwhChannel(previousChannel, previousValue);
+ updateVAChannel(PEAK_POWER_DAY_MINUS_1, values.dayValue[dSize - 1].value / divider);
+ updateState(PEAK_POWER_TS_DAY_MINUS_1,
+ new DateTimeType(values.dayValue[dSize - 1].date.atZone(ZoneId.systemDefault())));
+
+ updateVAChannel(PEAK_POWER_DAY_MINUS_2, values.dayValue[dSize - 2].value / divider);
+ updateState(PEAK_POWER_TS_DAY_MINUS_2,
+ new DateTimeType(values.dayValue[dSize - 2].date.atZone(ZoneId.systemDefault())));
+
+ updateVAChannel(PEAK_POWER_DAY_MINUS_3, values.dayValue[dSize - 3].value / divider);
+ updateState(PEAK_POWER_TS_DAY_MINUS_3,
+ new DateTimeType(values.dayValue[dSize - 3].date.atZone(ZoneId.systemDefault())));
+
+ }, () -> {
+ updateKwhChannel(PEAK_POWER_DAY_MINUS_1, Double.NaN);
+ updateState(PEAK_POWER_TS_DAY_MINUS_1, UnDefType.UNDEF);
+
+ updateKwhChannel(PEAK_POWER_DAY_MINUS_2, Double.NaN);
+ updateState(PEAK_POWER_TS_DAY_MINUS_2, UnDefType.UNDEF);
+
+ updateKwhChannel(PEAK_POWER_DAY_MINUS_3, Double.NaN);
+ updateState(PEAK_POWER_TS_DAY_MINUS_3, UnDefType.UNDEF);
+ });
}
/**
From 9120ccfcd96c846b4c673d52ec7c035679db1de3 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Wed, 14 Aug 2024 14:28:52 +0200
Subject: [PATCH 040/135] progress on code refactoring
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 164 ++++++++----------
.../linky/internal/dto/ContactInfo.java | 8 +
.../binding/linky/internal/dto/Customer.java | 36 ++++
.../linky/internal/dto/IdentityInfo.java | 10 ++
.../handler/EnedisWebBridgeHandler.java | 2 +-
5 files changed, 124 insertions(+), 96 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 2cabaae7493..46adbdedbcc 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -14,13 +14,13 @@ package org.openhab.binding.linky.internal.api;
import java.net.HttpCookie;
import java.time.LocalDate;
-import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import javax.ws.rs.core.MediaType;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
@@ -32,7 +32,6 @@ import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AddressInfo;
import org.openhab.binding.linky.internal.dto.ConsumptionReport;
import org.openhab.binding.linky.internal.dto.ContactInfo;
-import org.openhab.binding.linky.internal.dto.Contracts;
import org.openhab.binding.linky.internal.dto.Customer;
import org.openhab.binding.linky.internal.dto.CustomerIdResponse;
import org.openhab.binding.linky.internal.dto.CustomerReponse;
@@ -42,7 +41,6 @@ import org.openhab.binding.linky.internal.dto.MeterResponse;
import org.openhab.binding.linky.internal.dto.PrmInfo;
import org.openhab.binding.linky.internal.dto.TempoResponse;
import org.openhab.binding.linky.internal.dto.UsagePoint;
-import org.openhab.binding.linky.internal.dto.UsagePointDetails;
import org.openhab.binding.linky.internal.dto.WebPrmInfo;
import org.openhab.binding.linky.internal.dto.WebUserInfo;
import org.openhab.binding.linky.internal.handler.ApiBridgeHandler;
@@ -154,97 +152,27 @@ public class EnedisHttpApi {
}
PrmInfo result = new PrmInfo();
- if (false) {
- Customer customer = getCustomer(handler, prmId);
- UsagePoint usagePoint = customer.usagePoints[0];
+ Customer customer = getContract(handler, prmId);
+ UsagePoint usagePoint = customer.usagePoints[0];
- result.contractInfo = usagePoint.contracts;
- result.usagePointInfo = usagePoint.usagePoint;
- result.identityInfo = getIdentity(handler, prmId);
- result.addressInfo = getAddress(handler, prmId);
- result.contactInfo = getContact(handler, prmId);
+ result.contractInfo = usagePoint.contracts;
+ result.usagePointInfo = usagePoint.usagePoint;
- result.prmId = result.usagePointInfo.usagePointId;
- result.customerId = customer.customerId;
+ result.identityInfo = getIdentity(handler, prmId);
+ result.contactInfo = getContact(handler, prmId);
- return result;
- } else {
- WebPrmInfo[] webPrmsInfo;
- WebUserInfo webUserInfo;
+ result.prmId = result.usagePointInfo.usagePointId;
+ result.customerId = customer.customerId;
- String prm_info_url = apiBridgeHandler.getContractUrl();
- String user_info_url = apiBridgeHandler.getContactUrl();
+ return result;
- String data = getData(prm_info_url);
- if (data.isEmpty()) {
- throw new LinkyException("Requesting '%s' returned an empty response", prm_info_url);
- }
- try {
- webPrmsInfo = gson.fromJson(data, WebPrmInfo[].class);
- if (webPrmsInfo == null || webPrmsInfo.length < 1) {
- throw new LinkyException("Invalid prms data received");
- }
- } catch (JsonSyntaxException e) {
- logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", prm_info_url);
- }
-
- data = getData(user_info_url);
- if (data.isEmpty()) {
- throw new LinkyException("Requesting '%s' returned an empty response", user_info_url);
- }
- try {
- webUserInfo = gson.fromJson(data, WebUserInfo.class);
- } catch (JsonSyntaxException e) {
- logger.debug("invalid JSON response not matching UserInfo.class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", user_info_url);
- }
-
- WebPrmInfo webPrmInfo = Arrays.stream(webPrmsInfo).filter(x -> x.prmId.equals(prmId)).findAny()
- .orElseThrow();
-
- result.addressInfo = new AddressInfo();
- result.addressInfo.street = webPrmInfo.adresse.adresseLigneQuatre;
- result.addressInfo.city = webPrmInfo.adresse.adresseLigneSix;
- result.addressInfo.postalCode = webPrmInfo.adresse.adresseLigneSix;
- result.addressInfo.country = webPrmInfo.adresse.adresseLigneSept;
-
- result.contactInfo = new ContactInfo();
- result.identityInfo = new IdentityInfo();
- result.contractInfo = new Contracts();
- result.usagePointInfo = new UsagePointDetails();
-
- result.contactInfo.email = webUserInfo.userProperties.mail;
- result.contactInfo.phone = "";
-
- result.identityInfo.firstname = webUserInfo.userProperties.firstName;
- result.identityInfo.lastname = webUserInfo.userProperties.name;
- result.identityInfo.title = "";
- // webUserInfo.userProperties.internId;
- // webUserInfo.userProperties.personalInfo;
-
- result.contractInfo.contractStatus = "";
- result.contractInfo.contractType = "";
- result.contractInfo.distributionTariff = "";
- result.contractInfo.lastActivationDate = "";
- result.contractInfo.lastDistributionTariffChangeDate = "";
- result.contractInfo.offpeakHours = "";
- result.contractInfo.segment = webPrmInfo.segment;
- result.contractInfo.subscribedPower = "" + webPrmInfo.puissanceSouscrite;
-
- result.prmId = prmId;
- result.customerId = "";
-
- return result;
-
- }
}
public String formatUrl(String apiUrl, String prmId) {
return apiUrl.formatted(prmId);
}
- public Customer getCustomer(LinkyHandler handler, String prmId) throws LinkyException {
+ public Customer getContract(LinkyHandler handler, String prmId) throws LinkyException {
if (!apiBridgeHandler.isConnected()) {
apiBridgeHandler.initialize();
}
@@ -255,22 +183,48 @@ public class EnedisHttpApi {
throw new LinkyException("Requesting '%s' returned an empty response", contractUrl);
}
try {
- CustomerReponse cResponse = gson.fromJson(data, CustomerReponse.class);
- if (cResponse == null) {
- throw new LinkyException("Invalid customer data received");
+ if (data.startsWith("[{\"adresse\"")) {
+ try {
+ WebPrmInfo[] webPrmsInfo = gson.fromJson(data, WebPrmInfo[].class);
+ if (webPrmsInfo == null || webPrmsInfo.length < 1) {
+ throw new LinkyException("Invalid prms data received");
+ }
+ return Customer.fromWebPrmInfos(webPrmsInfo, prmId);
+
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", contractUrl);
+ }
+ } else {
+ CustomerReponse cResponse = gson.fromJson(data, CustomerReponse.class);
+ if (cResponse == null) {
+ throw new LinkyException("Invalid customer data received");
+ }
+
+ AddressInfo addressInfo = getAddress(handler, prmId);
+ if (addressInfo != null) {
+ cResponse.customer.usagePoints[0].usagePoint.usagePointAddresses = addressInfo;
+ }
+
+ return cResponse.customer;
}
- return cResponse.customer;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", contractUrl);
}
}
+ @Nullable
public AddressInfo getAddress(LinkyHandler handler, String prmId) throws LinkyException {
if (!apiBridgeHandler.isConnected()) {
apiBridgeHandler.initialize();
}
String addressUrl = apiBridgeHandler.getAddressUrl();
+
+ if (addressUrl.isEmpty()) {
+ return null;
+ }
+
String data = getData(handler, formatUrl(addressUrl, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", addressUrl);
@@ -297,11 +251,21 @@ public class EnedisHttpApi {
throw new LinkyException("Requesting '%s' returned an empty response", identityUrl);
}
try {
- CustomerIdResponse iResponse = gson.fromJson(data, CustomerIdResponse.class);
- if (iResponse == null) {
- throw new LinkyException("Invalid customer data received");
+ if (data.contains("av2_interne_id")) {
+ try {
+ WebUserInfo webUserInfo = gson.fromJson(data, WebUserInfo.class);
+ return IdentityInfo.fromWebUserInfo(webUserInfo);
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching UserInfo.class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", identityUrl);
+ }
+ } else {
+ CustomerIdResponse iResponse = gson.fromJson(data, CustomerIdResponse.class);
+ if (iResponse == null) {
+ throw new LinkyException("Invalid customer data received");
+ }
+ return iResponse.identity.naturalPerson;
}
- return iResponse.identity.naturalPerson;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", identityUrl);
@@ -319,11 +283,21 @@ public class EnedisHttpApi {
throw new LinkyException("Requesting '%s' returned an empty response", contactUrl);
}
try {
- CustomerIdResponse cResponse = gson.fromJson(data, CustomerIdResponse.class);
- if (cResponse == null) {
- throw new LinkyException("Invalid customer data received");
+ if (data.contains("av2_interne_id")) {
+ try {
+ WebUserInfo webUserInfo = gson.fromJson(data, WebUserInfo.class);
+ return ContactInfo.fromWebUserInfo(webUserInfo);
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching UserInfo.class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", contactUrl);
+ }
+ } else {
+ CustomerIdResponse cResponse = gson.fromJson(data, CustomerIdResponse.class);
+ if (cResponse == null) {
+ throw new LinkyException("Invalid customer data received");
+ }
+ return cResponse.contactData;
}
- return cResponse.contactData;
} catch (JsonSyntaxException e) {
logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", contactUrl);
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java
index 83cf9f89c86..72c1654f047 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContactInfo.java
@@ -23,4 +23,12 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
public class ContactInfo {
public String phone;
public String email;
+
+ public static ContactInfo fromWebUserInfo(WebUserInfo webUserInfo) {
+ ContactInfo result = new ContactInfo();
+
+ result.email = webUserInfo.userProperties.mail;
+
+ return result;
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
index a41a555643b..bbda35b2618 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
@@ -12,6 +12,8 @@
*/
package org.openhab.binding.linky.internal.dto;
+import java.util.Arrays;
+
import org.eclipse.jetty.jaas.spi.UserInfo;
/**
@@ -25,4 +27,38 @@ public class Customer {
public String customerId;
public UsagePoint[] usagePoints;
+
+ public static Customer fromWebPrmInfos(WebPrmInfo[] webPrmsInfo, String prmId) {
+ Customer result = new Customer();
+
+ WebPrmInfo webPrmInfo = Arrays.stream(webPrmsInfo).filter(x -> x.prmId.equals(prmId)).findAny().orElseThrow();
+
+ result.usagePoints = new UsagePoint[1];
+ result.usagePoints[0] = new UsagePoint();
+
+ result.usagePoints[0].usagePoint = new UsagePointDetails();
+ result.usagePoints[0].usagePoint.meterType = "";
+ result.usagePoints[0].usagePoint.usagePointId = "";
+ result.usagePoints[0].usagePoint.usagePointStatus = "";
+
+ result.usagePoints[0].usagePoint.usagePointAddresses = new AddressInfo();
+ result.usagePoints[0].usagePoint.usagePointAddresses.city = webPrmInfo.adresse.adresseLigneSix;
+ result.usagePoints[0].usagePoint.usagePointAddresses.country = webPrmInfo.adresse.adresseLigneSept;
+ result.usagePoints[0].usagePoint.usagePointAddresses.inseeCode = "";
+ result.usagePoints[0].usagePoint.usagePointAddresses.locality = "";
+ result.usagePoints[0].usagePoint.usagePointAddresses.postalCode = webPrmInfo.adresse.adresseLigneSix;
+ result.usagePoints[0].usagePoint.usagePointAddresses.street = webPrmInfo.adresse.adresseLigneQuatre;
+
+ result.usagePoints[0].contracts = new Contracts();
+ result.usagePoints[0].contracts.contractStatus = "";
+ result.usagePoints[0].contracts.contractType = "";
+ result.usagePoints[0].contracts.distributionTariff = "";
+ result.usagePoints[0].contracts.lastActivationDate = "";
+ result.usagePoints[0].contracts.lastDistributionTariffChangeDate = "";
+ result.usagePoints[0].contracts.offpeakHours = "";
+ result.usagePoints[0].contracts.segment = webPrmInfo.segment;
+ result.usagePoints[0].contracts.subscribedPower = "" + webPrmInfo.puissanceSouscrite;
+
+ return result;
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityInfo.java
index 8923636061c..86c33a4b80c 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityInfo.java
@@ -25,4 +25,14 @@ public class IdentityInfo {
public String title;
public String firstname;
public String lastname;
+
+ public static IdentityInfo fromWebUserInfo(WebUserInfo webUserInfo) {
+ IdentityInfo result = new IdentityInfo();
+
+ result.firstname = webUserInfo.userProperties.firstName;
+ result.lastname = webUserInfo.userProperties.name;
+ result.title = "";
+
+ return result;
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index 73ad851a3cd..59a8dca6bd3 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -129,7 +129,7 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
@Override
public String getAddressUrl() {
- return PRM_INFO_URL;
+ return "";
}
@Override
From 7855ca89ebc87483e49c33c115558c2cd7c27dfd Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 12:34:23 +0200
Subject: [PATCH 041/135] review channel definitions review tempo information
readings
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyBindingConstants.java | 8 +-
.../linky/internal/handler/LinkyHandler.java | 273 +++++++++---------
.../resources/OH-INF/thing/thing-types.xml | 59 ++--
3 files changed, 174 insertions(+), 166 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 449f5ebde7a..26cd64fc872 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -49,7 +49,10 @@ public class LinkyBindingConstants {
// List of all Channel id's
- public static final String CONSUMPTION = "timeseries#consumption";
+ public static final String DAYLY_CONSUMPTION = "daily#consumption";
+ public static final String WEEKLY_CONSUMPTION = "weekly#consumption";
+ public static final String MONTHLY_CONSUMPTION = "monthly#consumption";
+ public static final String YEARLY_CONSUMPTION = "yearly#consumption";
public static final String DAY_MINUS_1 = "daily#yesterday";
public static final String DAY_MINUS_2 = "daily#day-2";
@@ -78,7 +81,8 @@ public class LinkyBindingConstants {
public static final String MAIN_IDENTITY = "main#identity";
- public static final String TEMPO_TEMPO_INFO = "tempo#tempoInfo";
+ public static final String TEMPO_TODAY_INFO = "tempo#tempoInfoToday";
+ public static final String TEMPO_TOMORROW_INFO = "tempo#tempoInfoTomorrow";
public static final String TEMPO_TEMPO_INFO_TIME_SERIES = "tempo#tempoInfoTimeSeries";
public static final String MAIN_CONTRACT_SUBSCRIBED_POWER = "main#contractSubscribedPower";
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index e0c48a29b9c..501c92c365f 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -77,6 +77,8 @@ public class LinkyHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(LinkyHandler.class);
private final ExpiringDayCache dailyConsumption;
+ private final ExpiringDayCache dailyConsumptionMaxPower;
+ private final ExpiringDayCache tempoInformation;
private @Nullable ScheduledFuture> refreshJob;
private LinkyConfiguration config;
@@ -107,6 +109,7 @@ public class LinkyHandler extends BaseThingHandler {
return meterReading;
});
+<<<<<<< HEAD
/*
* this.cachedPowerData = new ExpiringDayCache<>("power cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
* // We request data for yesterday and the day before yesterday, even if the data for the day before yesterday
@@ -146,6 +149,32 @@ public class LinkyHandler extends BaseThingHandler {
* return consumption;
* });
*/
+=======
+ // We request data for yesterday and the day before yesterday, even if the data for the day before yesterday
+ // is not needed by the binding. This is only a workaround to an API bug that will return
+ // INTERNAL_SERVER_ERROR rather than the expected data with a NaN value when the data for yesterday is not yet
+ // available.
+ // By requesting two days, the API is not failing and you get the expected NaN value for yesterday when the data
+ // is not yet available.
+ this.dailyConsumptionMaxPower = new ExpiringDayCache<>("dailyConsumptionMaxPower", REFRESH_FIRST_HOUR_OF_DAY,
+ () -> {
+ LocalDate today = LocalDate.now();
+ MeterReading meterReading = getPowerData(today.minusDays(1095), today);
+ meterReading = getMeterReadingAfterChecks(meterReading);
+ if (meterReading != null) {
+ logData(meterReading.dayValue, "Day (peak)", DateTimeFormatter.ISO_LOCAL_DATE, Target.ALL);
+ }
+ return meterReading;
+ });
+
+ // Read Tempo Information
+ this.tempoInformation = new ExpiringDayCache<>("tempoInformation", REFRESH_FIRST_HOUR_OF_DAY, () -> {
+ LocalDate today = LocalDate.now();
+
+ TempoResponse tempoData = getTempoData(today.minusDays(1095), today.plusDays(1));
+ return tempoData;
+ });
+>>>>>>> 7a863e3128 (review channel definitions)
}
@Override
@@ -254,64 +283,61 @@ public class LinkyHandler extends BaseThingHandler {
updateEnergyData();
updatePowerData();
-
- TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
- TempoResponse tempoData = getTempoData();
-
- tempoData.forEach((k, v) -> {
- try {
- SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
- Date date = df.parse(k);
- long epoch = date.getTime();
- Instant timestamp = Instant.ofEpochMilli(epoch);
-
- int val = 0;
- if (v.equals("WHITE")) {
- val = 0;
- }
- if (v.equals("BLUE")) {
- val = 1;
- }
- if (v.equals("RED")) {
- val = 2;
- }
- timeSeries.add(timestamp, new DecimalType(val));
- } catch (ParseException ex) {
-
- }
-
- });
-
- sendTimeSeries(TEMPO_TEMPO_INFO_TIME_SERIES, timeSeries);
- updateState(TEMPO_TEMPO_INFO_TIME_SERIES, new DecimalType(1));
-
- TimeSeries timeSeries2 = new TimeSeries(Policy.REPLACE);
-
- LocalDate today = LocalDate.now();
- MeterReading meterReading = getConsumptionData(today.minusDays(1095), today);
- meterReading = getMeterReadingAfterChecks(meterReading);
- if (meterReading != null) {
-
- IntervalReading[] iv = meterReading.dayValue;
-
- for (int i = 0; i < iv.length; i++) {
-
- // iv[i].value
-
- Instant timestamp = iv[i].date.toInstant(ZoneOffset.UTC);
- timeSeries2.add(timestamp, new DecimalType(iv[i].value));
-
- }
- }
-
- sendTimeSeries(CONSUMPTION, timeSeries2);
- updateState(CONSUMPTION, new DecimalType(0));
+ updateTempoTimeSeries();
if (!connectedBefore && isConnected()) {
disconnect();
}
}
+ private synchronized void updateTempoTimeSeries() {
+ tempoInformation.getValue().ifPresentOrElse(values -> {
+ LocalDate today = LocalDate.now();
+ TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
+
+ values.forEach((k, v) -> {
+ try {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+ Date date = df.parse(k);
+ long epoch = date.getTime();
+ Instant timestamp = Instant.ofEpochMilli(epoch);
+
+ timeSeries.add(timestamp, new DecimalType(getTempoIdx(v)));
+ } catch (ParseException ex) {
+
+ }
+ });
+
+ int tomorrowDayIdx = values.size();
+ Object[] tempoValues = values.values().toArray();
+
+ updateTempoChannel(TEMPO_TODAY_INFO, getTempoIdx((String) tempoValues[tomorrowDayIdx - 2]));
+ updateTempoChannel(TEMPO_TOMORROW_INFO, getTempoIdx((String) tempoValues[tomorrowDayIdx - 1]));
+
+ sendTimeSeries(TEMPO_TEMPO_INFO_TIME_SERIES, timeSeries);
+ updateState(TEMPO_TEMPO_INFO_TIME_SERIES,
+ new DecimalType(getTempoIdx((String) tempoValues[tomorrowDayIdx - 2])));
+ }, () -> {
+ updateTempoChannel(TEMPO_TODAY_INFO, -1);
+ updateTempoChannel(TEMPO_TOMORROW_INFO, -1);
+ });
+ }
+
+ private int getTempoIdx(String color) {
+ int val = 0;
+ if (color.equals("BLUE")) {
+ val = 0;
+ }
+ if (color.equals("WHITE")) {
+ val = 1;
+ }
+ if (color.equals("RED")) {
+ val = 2;
+ }
+
+ return val;
+ }
+
private synchronized void updatePowerData() {
dailyConsumptionMaxPower.getValue().ifPresentOrElse(values -> {
int dSize = values.dayValue.length;
@@ -355,14 +381,10 @@ public class LinkyHandler extends BaseThingHandler {
updateKwhChannel(DAY_MINUS_2, values.dayValue[dSize - 2].value / divider);
updateKwhChannel(DAY_MINUS_3, values.dayValue[dSize - 3].value / divider);
- LocalDate currentDt = LocalDate.now();
- int idxCurrentYear = currentDt.getYear() - 2021;
-
- int currentWeek = (currentDt.getDayOfYear() / 7) + 1;
- int currentMonth = currentDt.getMonthValue();
-
- int idxCurrentWeek = (52 * idxCurrentYear) + currentWeek;
- int idxCurrentMonth = (12 * idxCurrentYear) + currentMonth;
+ LocalDate currentDt = LocalDate.now();
+ int idxCurrentYear = values.yearValue.length - 1;
+ int idxCurrentWeek = values.weekValue.length - 1;
+ int idxCurrentMonth = values.monthValue.length - 1;
updateKwhChannel(WEEK_MINUS_0, values.weekValue[idxCurrentWeek].value / divider);
updateKwhChannel(WEEK_MINUS_1, values.weekValue[idxCurrentWeek - 1].value / divider);
@@ -375,6 +397,11 @@ public class LinkyHandler extends BaseThingHandler {
updateKwhChannel(YEAR_MINUS_0, values.yearValue[idxCurrentYear].value / divider);
updateKwhChannel(YEAR_MINUS_1, values.yearValue[idxCurrentYear - 1].value / divider);
updateKwhChannel(YEAR_MINUS_2, values.yearValue[idxCurrentYear - 2].value / divider);
+
+ updateConsumptionTimeSeries(DAYLY_CONSUMPTION, values.dayValue);
+ updateConsumptionTimeSeries(WEEKLY_CONSUMPTION, values.weekValue);
+ updateConsumptionTimeSeries(MONTHLY_CONSUMPTION, values.monthValue);
+ updateConsumptionTimeSeries(YEARLY_CONSUMPTION, values.yearValue);
}, () -> {
updateKwhChannel(DAY_MINUS_1, Double.NaN);
updateKwhChannel(DAY_MINUS_2, Double.NaN);
@@ -390,60 +417,19 @@ public class LinkyHandler extends BaseThingHandler {
}
}
- /**
- * Request new monthly data and updates channels
- */
+ private synchronized void updateConsumptionTimeSeries(String channel, IntervalReading[] iv) {
+ TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
+ LocalDate today = LocalDate.now();
- /*
- * private synchronized void updateMonthlyData() {
- * if (isLinked(LAST_MONTH) || isLinked(THIS_MONTH)) {
- * cachedMonthlyData.getValue().ifPresentOrElse(values -> {
- * Aggregate months = values.aggregats.months;
- * updateKwhChannel(LAST_MONTH, months.datas.get(0));
- * if (months.datas.size() > 1) {
- * updateKwhChannel(THIS_MONTH, months.datas.get(1));
- * } else {
- * updateKwhChannel(THIS_MONTH, 0.0);
- * }
- * }, () -> {
- * if (ZonedDateTime.now().getDayOfMonth() == 1) {
- * updateKwhChannel(THIS_MONTH, 0.0);
- * updateKwhChannel(LAST_MONTH, Double.NaN);
- * } else {
- * updateKwhChannel(THIS_MONTH, Double.NaN);
- * }
- * });
- * }
- * }
- */
+ for (int i = 0; i < iv.length; i++) {
+ Instant timestamp = iv[i].date.toInstant(ZoneOffset.UTC);
+ timeSeries.add(timestamp, new DecimalType(iv[i].value));
+ }
+ sendTimeSeries(channel, timeSeries);
+ updateState(channel, new DecimalType(iv[iv.length - 1].value));
- /**
- * Request new yearly data and updates channels
- */
-
- /*
- * private synchronized void updateYearlyData() {
- * if (isLinked(LAST_YEAR) || isLinked(THIS_YEAR)) {
- * cachedYearlyData.getValue().ifPresentOrElse(values -> {
- * Aggregate years = values.aggregats.years;
- * updateKwhChannel(LAST_YEAR, years.datas.get(0));
- * if (years.datas.size() > 1) {
- * updateKwhChannel(THIS_YEAR, years.datas.get(1));
- * } else {
- * updateKwhChannel(THIS_YEAR, 0.0);
- * }
- * }, () -> {
- * if (ZonedDateTime.now().getDayOfYear() == 1) {
- * updateKwhChannel(THIS_YEAR, 0.0);
- * updateKwhChannel(LAST_YEAR, Double.NaN);
- * } else {
- * updateKwhChannel(THIS_YEAR, Double.NaN);
- * }
- * });
- * }
- * }
- */
+ }
private void updateKwhChannel(String channelId, double consumption) {
logger.debug("Update channel {} with {}", channelId, consumption);
@@ -457,6 +443,11 @@ public class LinkyHandler extends BaseThingHandler {
: new QuantityType<>(power, MetricPrefix.KILO(Units.VOLT_AMPERE)));
}
+ private void updateTempoChannel(String channelId, int tempoValue) {
+ logger.debug("Update channel {} with {}", channelId, tempoValue);
+ updateState(channelId, new DecimalType(tempoValue));
+ }
+
/**
* Produce a report of all daily values between two dates
*
@@ -551,13 +542,13 @@ public class LinkyHandler extends BaseThingHandler {
return null;
}
- private @Nullable TempoResponse getTempoData() {
+ private @Nullable TempoResponse getTempoData(LocalDate from, LocalDate to) {
logger.debug("getTempoData from");
EnedisHttpApi api = this.enedisApi;
if (api != null) {
try {
- TempoResponse result = api.getTempoData(this);
+ TempoResponse result = api.getTempoData(this, from, to);
updateStatus(ThingStatus.ONLINE);
return result;
} catch (LinkyException e) {
@@ -639,40 +630,42 @@ public class LinkyHandler extends BaseThingHandler {
}
if (meterReading != null) {
- meterReading.weekValue = new IntervalReading[208];
- meterReading.monthValue = new IntervalReading[48];
- meterReading.yearValue = new IntervalReading[4];
+ if (meterReading.weekValue == null) {
+ meterReading.weekValue = new IntervalReading[208];
+ meterReading.monthValue = new IntervalReading[48];
+ meterReading.yearValue = new IntervalReading[4];
- for (int idx = 0; idx < 208; idx++) {
- meterReading.weekValue[idx] = new IntervalReading();
- }
- for (int idx = 0; idx < 48; idx++) {
- meterReading.monthValue[idx] = new IntervalReading();
- }
- for (int idx = 0; idx < 4; idx++) {
- meterReading.yearValue[idx] = new IntervalReading();
- }
+ for (int idx = 0; idx < 208; idx++) {
+ meterReading.weekValue[idx] = new IntervalReading();
+ }
+ for (int idx = 0; idx < 48; idx++) {
+ meterReading.monthValue[idx] = new IntervalReading();
+ }
+ for (int idx = 0; idx < 4; idx++) {
+ meterReading.yearValue[idx] = new IntervalReading();
+ }
- int size = meterReading.dayValue.length;
- int baseYear = meterReading.dayValue[0].date.getYear();
+ int size = meterReading.dayValue.length;
+ int baseYear = meterReading.dayValue[0].date.getYear();
- for (int idx = 0; idx < size; idx++) {
- IntervalReading ir = meterReading.dayValue[idx];
- LocalDateTime dt = ir.date;
- double value = ir.value;
+ for (int idx = 0; idx < size; idx++) {
+ IntervalReading ir = meterReading.dayValue[idx];
+ LocalDateTime dt = ir.date;
+ double value = ir.value;
- int idxYear = dt.getYear() - baseYear;
+ int idxYear = dt.getYear() - baseYear;
- int dayOfYear = dt.getDayOfYear();
- int week = ((dayOfYear - 1) / 7) + 1;
- int month = dt.getMonthValue();
+ int dayOfYear = dt.getDayOfYear();
+ int week = ((dayOfYear - 1) / 7) + 1;
+ int month = dt.getMonthValue();
- int idxMonth = (idxYear * 12) + month;
- int idxWeek = (idxYear * 52) + week;
+ int idxMonth = (idxYear * 12) + month;
+ int idxWeek = (idxYear * 52) + week;
- meterReading.weekValue[idxWeek].value += value;
- meterReading.monthValue[idxMonth].value += value;
- meterReading.yearValue[idxYear].value += value;
+ meterReading.weekValue[idxWeek].value += value;
+ meterReading.monthValue[idxMonth].value += value;
+ meterReading.yearValue[idxYear].value += value;
+ }
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 9ab4b67965b..cfbbd08dd9c 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -88,8 +88,6 @@
-
-
@@ -110,14 +108,6 @@
-
- Timeseries channel
-
-
- Consumption
-
-
-
Daily consumption
@@ -131,6 +121,11 @@
Yesterday Consumption
+
+ Consumption
+
+
+
@@ -164,6 +159,9 @@
Last Week Consumption
+
+ Consumption
+
@@ -179,6 +177,9 @@
Last Month Consumption
+
+ Consumption
+
@@ -194,16 +195,22 @@
Last Year Consumption
+
+ Consumption
+
Tempo
-
- Tempo day information
+
+ Tempo Today color
-
+
+ Tempo Today color
+
+
Tempo day information
@@ -242,7 +249,6 @@
Contract Segment
-
UsagePoint Id
@@ -269,22 +275,27 @@
Street
-
-
-
Mail
Phone
-
-
-
-
-
+
+
+ Number
+ Tempo color information
+ This status describes the tempo color of a day.
+
+
+ Blue
+ White
+ Red
+
+
+
String
@@ -294,8 +305,6 @@
-
-
Number:Energy
Total Consumption
@@ -317,4 +326,6 @@
time
+
+
From 8283ea8273802c2640d0e93267563894fa331dbe Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 12:35:23 +0200
Subject: [PATCH 042/135] fix tempo period reading
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 27 +++++++------------
1 file changed, 9 insertions(+), 18 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 46adbdedbcc..a022604c2f7 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -132,20 +132,6 @@ public class EnedisHttpApi {
}
}
- /*
- * private String getData(String url) throws LinkyException {
- * try {
- * ContentResponse result = httpClient.GET(url);
- * if (result.getStatus() != 200) {
- * throw new LinkyException("Error requesting '%s' : %s", url, result.getContentAsString());
- * }
- * return result.getContentAsString();
- * } catch (InterruptedException | ExecutionException | TimeoutException e) {
- * throw new LinkyException(e, "Error getting url : '%s'", url);
- * }
- * }
- */
-
public PrmInfo getPrmInfo(LinkyHandler handler, String prmId) throws LinkyException {
if (!apiBridgeHandler.isConnected()) {
apiBridgeHandler.initialize();
@@ -161,11 +147,13 @@ public class EnedisHttpApi {
result.identityInfo = getIdentity(handler, prmId);
result.contactInfo = getContact(handler, prmId);
+ result.addressInfo = result.usagePointInfo.usagePointAddresses;
+
result.prmId = result.usagePointInfo.usagePointId;
- result.customerId = customer.customerId;
+ result.customerId = "";
+ // customer.customerId;
return result;
-
}
public String formatUrl(String apiUrl, String prmId) {
@@ -354,8 +342,11 @@ public class EnedisHttpApi {
return getMeasures(handler, apiBridgeHandler.getMaxPowerUrl(), prmId, from, to);
}
- public TempoResponse getTempoData(LinkyHandler handler) throws LinkyException {
- String url = String.format(apiBridgeHandler.getTempoUrl(), "2024-01-01", "2024-08-30");
+ public TempoResponse getTempoData(LinkyHandler handler, LocalDate from, LocalDate to) throws LinkyException {
+ String dtStart = from.format(apiBridgeHandler.getApiDateFormatYearsFirst());
+ String dtEnd = to.format(apiBridgeHandler.getApiDateFormatYearsFirst());
+
+ String url = String.format(apiBridgeHandler.getTempoUrl(), dtStart, dtEnd);
if (url.isEmpty()) {
return new TempoResponse();
From d227dd44cf03d9d38231716c463ea700596170e9 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 12:35:38 +0200
Subject: [PATCH 043/135] order tempo information
Signed-off-by: Laurent ARNAL
---
.../org/openhab/binding/linky/internal/dto/TempoResponse.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
index bbc3b4d39ac..3994c836ed6 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.java
@@ -12,7 +12,7 @@
*/
package org.openhab.binding.linky.internal.dto;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
/**
* The {@link UserInfo} holds informations about energy delivery point
@@ -21,7 +21,7 @@ import java.util.HashMap;
* @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
-public class TempoResponse extends HashMap {
+public class TempoResponse extends LinkedHashMap {
@java.io.Serial
private static final long serialVersionUID = 362498820763181264L;
}
From 983fbb3ae09f18c54c197d26f158d79d06e7d9f3 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 12:36:19 +0200
Subject: [PATCH 044/135] datetimeformatter for tempo
Signed-off-by: Laurent ARNAL
---
.../binding/linky/internal/handler/ApiBridgeHandler.java | 2 ++
.../linky/internal/handler/EnedisBridgeHandler.java | 7 +++++++
.../linky/internal/handler/EnedisWebBridgeHandler.java | 6 ++++++
.../internal/handler/MyElectricalDataBridgeHandler.java | 7 +++++++
4 files changed, 22 insertions(+)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 23c449cd671..e1d130ec507 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -356,4 +356,6 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
public abstract String getTempoUrl();
public abstract DateTimeFormatter getApiDateFormat();
+
+ public abstract DateTimeFormatter getApiDateFormatYearsFirst();
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index d865cfafe14..6ef79aa81fd 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -50,6 +50,7 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
+ "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ private static final DateTimeFormatter API_DATE_FORMAT_YEAR_FIRST = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public EnedisBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
@@ -142,4 +143,10 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
public DateTimeFormatter getApiDateFormat() {
return API_DATE_FORMAT;
}
+
+ @Override
+ public DateTimeFormatter getApiDateFormatYearsFirst() {
+ return API_DATE_FORMAT_YEAR_FIRST;
+ }
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index 59a8dca6bd3..373cf189153 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -73,6 +73,7 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
+ "undefined/prms/%s/donnees-pmax?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
+ private static final DateTimeFormatter API_DATE_FORMAT_YEAR_FIRST = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final String URL_ENEDIS_AUTHENTICATE = BASE_URL + "/authenticate?target=" + URL_COMPTE_PART;
@@ -152,6 +153,11 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
return API_DATE_FORMAT;
}
+ @Override
+ public DateTimeFormatter getApiDateFormatYearsFirst() {
+ return API_DATE_FORMAT_YEAR_FIRST;
+ }
+
@Override
protected void connectionInit() throws LinkyException {
logger.debug("Starting login process for user : {}", config.username);
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index 59aa454ac26..ea6f1b11ca7 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -57,6 +57,7 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ private static final DateTimeFormatter API_DATE_FORMAT_YEAR_FIRST = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// https://www.myelectricaldata.fr/v1/oauth2/authorize?response_type=code&client_id=&state=linky&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fconnectlinky&scope=am_application_scope+default&user_type=aa&person_id=-1&usage_points_id=aa
@@ -183,4 +184,10 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
public DateTimeFormatter getApiDateFormat() {
return API_DATE_FORMAT;
}
+
+ @Override
+ public DateTimeFormatter getApiDateFormatYearsFirst() {
+ return API_DATE_FORMAT_YEAR_FIRST;
+ }
+
}
From e51d6c89710515173cb7e37aa6d70bc4d6010795 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 15:50:27 +0200
Subject: [PATCH 045/135] fix refresh
Signed-off-by: Laurent ARNAL
---
.../linky/internal/handler/LinkyHandler.java | 50 +++----------------
1 file changed, 8 insertions(+), 42 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 501c92c365f..83807f82851 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -109,47 +109,6 @@ public class LinkyHandler extends BaseThingHandler {
return meterReading;
});
-<<<<<<< HEAD
- /*
- * this.cachedPowerData = new ExpiringDayCache<>("power cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
- * // We request data for yesterday and the day before yesterday, even if the data for the day before yesterday
- * // is not needed by the binding. This is only a workaround to an API bug that will return
- * // INTERNAL_SERVER_ERROR rather than the expected data with a NaN value when the data for yesterday is not
- * // yet available.
- * // By requesting two days, the API is not failing and you get the expected NaN value for yesterday when the
- * // data is not yet available.
- * LocalDate today = LocalDate.now();
- * Consumption consumption = getPowerData(today.minusDays(2), today);
- * if (consumption != null) {
- * logData(consumption.aggregats.days, "Day (peak)", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME,
- * Target.ALL);
- * consumption = getConsumptionAfterChecks(consumption, Target.LAST);
- * }
- * return consumption;
- * });
- *
- *
- * this.cachedMonthlyData = new ExpiringDayCache<>("monthly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
- * LocalDate today = LocalDate.now();
- * Consumption consumption = getConsumptionData(today.withDayOfMonth(1).minusMonths(1), today);
- * if (consumption != null) {
- * logData(consumption.aggregats.months, "Month", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, Target.ALL);
- * consumption = getConsumptionAfterChecks(consumption, Target.LAST);
- * }
- * return consumption;
- * });
- *
- * this.cachedYearlyData = new ExpiringDayCache<>("yearly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
- * LocalDate today = LocalDate.now();
- * Consumption consumption = getConsumptionData(LocalDate.of(today.getYear() - 1, 1, 1), today);
- * if (consumption != null) {
- * logData(consumption.aggregats.years, "Year", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, Target.ALL);
- * consumption = getConsumptionAfterChecks(consumption, Target.LAST);
- * }
- * return consumption;
- * });
- */
-=======
// We request data for yesterday and the day before yesterday, even if the data for the day before yesterday
// is not needed by the binding. This is only a workaround to an API bug that will return
// INTERNAL_SERVER_ERROR rather than the expected data with a NaN value when the data for yesterday is not yet
@@ -174,7 +133,6 @@ public class LinkyHandler extends BaseThingHandler {
TempoResponse tempoData = getTempoData(today.minusDays(1095), today.plusDays(1));
return tempoData;
});
->>>>>>> 7a863e3128 (review channel definitions)
}
@Override
@@ -592,6 +550,7 @@ public class LinkyHandler extends BaseThingHandler {
if (command instanceof RefreshType) {
logger.debug("Refreshing channel {}", channelUID.getId());
boolean connectedBefore = isConnected();
+<<<<<<< HEAD
switch (channelUID.getId()) {
case YESTERDAY:
case LAST_WEEK:
@@ -613,6 +572,13 @@ public class LinkyHandler extends BaseThingHandler {
default:
break;
}
+=======
+
+ updateEnergyData();
+ updatePowerData();
+ updateTempoTimeSeries();
+
+>>>>>>> d96bda22d5 (fix refresh)
if (!connectedBefore && isConnected()) {
disconnect();
}
From a18a159e5b7ee0a0a7d9b977a810c953ff16e205 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 15:59:41 +0200
Subject: [PATCH 046/135] fix channel definition for max power
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 2 --
.../resources/OH-INF/thing/thing-types.xml | 27 ++++++++++++-------
2 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index a022604c2f7..819744b1d88 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -373,7 +373,5 @@ public class EnedisHttpApi {
logger.debug("invalid JSON response not matching ConsumptionReport.class: {}", data);
throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", url);
}
-
- // return data;
}
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index cfbbd08dd9c..4c8ebd0e7e3 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -127,22 +127,31 @@
-
+
+ Peak Value Yesterday
+ Maximum power usage value for Yesterday
+
- Peak Timestamp
- Maximum power usage timestamp
+ Peak Timestamp Yesterday
+ Maximum power usage timestamp for Yesterday
-
+
+ Peak Value Day-2
+ Maximum power usage value for Day-2
+
- Peak Timestamp
- Maximum power usage timestamp
+ Peak Timestamp Day-2
+ Maximum power usage timestamp for Day-2
-
+
+ Peak Value Day-3
+ Maximum power usage value for Day-3
+
- Peak Timestamp
- Maximum power usage timestamp
+ Peak Timestamp Day-3
+ Maximum power usage timestamp for Day-3
From 5ef473d297c9e31afb1bc22b7780d10b44dd878f Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 16:30:53 +0200
Subject: [PATCH 047/135] add timeseries for maxPower
Signed-off-by: Laurent ARNAL
---
.../linky/internal/LinkyBindingConstants.java | 5 ++
.../linky/internal/handler/LinkyHandler.java | 52 +++++++++----------
.../resources/OH-INF/thing/thing-types.xml | 17 ++++++
3 files changed, 48 insertions(+), 26 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 26cd64fc872..20422b8f88d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -58,6 +58,11 @@ public class LinkyBindingConstants {
public static final String DAY_MINUS_2 = "daily#day-2";
public static final String DAY_MINUS_3 = "daily#day-3";
+ public static final String PEAK_POWER_DAILY = "daily#maxPower";
+ public static final String PEAK_POWER_WEEKLY = "weekly#maxPower";
+ public static final String PEAK_POWER_MONTHLY = "monthly#maxPower";
+ public static final String PEAK_POWER_YEARLY = "yearly#maxPower";
+
public static final String PEAK_POWER_DAY_MINUS_1 = "daily#power";
public static final String PEAK_POWER_TS_DAY_MINUS_1 = "daily#timestamp";
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 83807f82851..790197ccca4 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -314,6 +314,11 @@ public class LinkyHandler extends BaseThingHandler {
updateState(PEAK_POWER_TS_DAY_MINUS_3,
new DateTimeType(values.dayValue[dSize - 3].date.atZone(ZoneId.systemDefault())));
+ updateMaxPowerTimeSeries(PEAK_POWER_DAILY, values.dayValue);
+ updateMaxPowerTimeSeries(PEAK_POWER_WEEKLY, values.weekValue);
+ updateMaxPowerTimeSeries(PEAK_POWER_MONTHLY, values.monthValue);
+ updateMaxPowerTimeSeries(PEAK_POWER_YEARLY, values.yearValue);
+
}, () -> {
updateKwhChannel(PEAK_POWER_DAY_MINUS_1, Double.NaN);
updateState(PEAK_POWER_TS_DAY_MINUS_1, UnDefType.UNDEF);
@@ -375,7 +380,27 @@ public class LinkyHandler extends BaseThingHandler {
}
}
- private synchronized void updateConsumptionTimeSeries(String channel, IntervalReading[] iv) {
+ private synchronized void updateMaxPowerTimeSeries(String channel, IntervalReading[] iv) {
+ TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
+ LocalDate today = LocalDate.now();
+
+ for (int i = 0; i < iv.length; i++) {
+ try {
+ if (Double.isNaN(iv[i].value)) {
+ continue;
+ }
+ Instant timestamp = iv[i].date.toInstant(ZoneOffset.UTC);
+ timeSeries.add(timestamp, new DecimalType(iv[i].value));
+ } catch (Exception ex) {
+ logger.debug("aa");
+ }
+ }
+
+ sendTimeSeries(channel, timeSeries);
+ updateState(channel, new DecimalType(iv[iv.length - 1].value));
+ }
+
+ private synchronized void updateConsumptionTimeSeries(String channel, IntervalReading[] iv) {
TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
LocalDate today = LocalDate.now();
@@ -386,7 +411,6 @@ public class LinkyHandler extends BaseThingHandler {
sendTimeSeries(channel, timeSeries);
updateState(channel, new DecimalType(iv[iv.length - 1].value));
-
}
private void updateKwhChannel(String channelId, double consumption) {
@@ -550,35 +574,11 @@ public class LinkyHandler extends BaseThingHandler {
if (command instanceof RefreshType) {
logger.debug("Refreshing channel {}", channelUID.getId());
boolean connectedBefore = isConnected();
-<<<<<<< HEAD
- switch (channelUID.getId()) {
- case YESTERDAY:
- case LAST_WEEK:
- case THIS_WEEK:
- updateDailyWeeklyData();
- break;
- case LAST_MONTH:
- case THIS_MONTH:
- // updateMonthlyData();
- break;
- case LAST_YEAR:
- case THIS_YEAR:
- // updateYearlyData();
- break;
- case PEAK_POWER:
- case PEAK_TIMESTAMP:
- // updatePowerData();
- break;
- default:
- break;
- }
-=======
updateEnergyData();
updatePowerData();
updateTempoTimeSeries();
->>>>>>> d96bda22d5 (fix refresh)
if (!connectedBefore && isConnected()) {
disconnect();
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 4c8ebd0e7e3..34c2e366862 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -126,6 +126,11 @@
+
+ Peak Value
+ Maximum power usage value
+
+
Peak Value Yesterday
@@ -170,6 +175,10 @@
Consumption
+
+
+ Peak Value
+ Maximum power usage value
@@ -188,6 +197,10 @@
Consumption
+
+
+ Peak Value
+ Maximum power usage value
@@ -206,6 +219,10 @@
Consumption
+
+
+ Peak Value
+ Maximum power usage value
From 59809ecbbefe1d7992db193edef9cd30f7464e58 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 16:32:47 +0200
Subject: [PATCH 048/135] spotless:apply
Signed-off-by: Laurent ARNAL
---
.../internal/handler/EnedisBridgeHandler.java | 1 -
.../MyElectricalDataBridgeHandler.java | 1 -
.../resources/OH-INF/thing/thing-types.xml | 110 +++++++++---------
3 files changed, 55 insertions(+), 57 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index 6ef79aa81fd..fcb7d236435 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -148,5 +148,4 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
public DateTimeFormatter getApiDateFormatYearsFirst() {
return API_DATE_FORMAT_YEAR_FIRST;
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index ea6f1b11ca7..dde9c24f165 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -189,5 +189,4 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
public DateTimeFormatter getApiDateFormatYearsFirst() {
return API_DATE_FORMAT_YEAR_FIRST;
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 34c2e366862..46a0f95c07e 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -121,39 +121,39 @@
Yesterday Consumption
-
- Consumption
-
-
-
-
- Peak Value
- Maximum power usage value
-
+
+ Consumption
+
+
+
+
+ Peak Value
+ Maximum power usage value
+
- Peak Value Yesterday
- Maximum power usage value for Yesterday
-
+ Peak Value Yesterday
+ Maximum power usage value for Yesterday
+
Peak Timestamp Yesterday
Maximum power usage timestamp for Yesterday
- Peak Value Day-2
- Maximum power usage value for Day-2
-
+ Peak Value Day-2
+ Maximum power usage value for Day-2
+
Peak Timestamp Day-2
Maximum power usage timestamp for Day-2
- Peak Value Day-3
- Maximum power usage value for Day-3
-
+ Peak Value Day-3
+ Maximum power usage value for Day-3
+
Peak Timestamp Day-3
Maximum power usage timestamp for Day-3
@@ -173,13 +173,13 @@
Last Week Consumption
-
- Consumption
-
-
- Peak Value
- Maximum power usage value
-
+
+ Consumption
+
+
+ Peak Value
+ Maximum power usage value
+
@@ -195,13 +195,13 @@
Last Month Consumption
-
- Consumption
-
-
- Peak Value
- Maximum power usage value
-
+
+ Consumption
+
+
+ Peak Value
+ Maximum power usage value
+
@@ -217,13 +217,13 @@
Last Year Consumption
-
- Consumption
-
-
- Peak Value
- Maximum power usage value
-
+
+ Consumption
+
+
+ Peak Value
+ Maximum power usage value
+
@@ -233,9 +233,9 @@
Tempo Today color
-
- Tempo Today color
-
+
+ Tempo Today color
+
Tempo day information
@@ -309,18 +309,18 @@
-
+
- Number
- Tempo color information
- This status describes the tempo color of a day.
-
-
- Blue
- White
- Red
-
-
+ Number
+ Tempo color information
+ This status describes the tempo color of a day.
+
+
+ Blue
+ White
+ Red
+
+
@@ -352,6 +352,6 @@
time
-
-
+
+
From d6ad4a25a2b0cc137a1cbe235cab959957c0bc5d Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 17:51:03 +0200
Subject: [PATCH 049/135] put back SerializedName because conflict with gson
decoding for old api
Signed-off-by: Laurent ARNAL
---
.../binding/linky/internal/dto/AddressInfo.java | 6 ++++++
.../binding/linky/internal/dto/Contracts.java | 16 ++++++++++++++++
.../binding/linky/internal/dto/Customer.java | 4 ++++
.../linky/internal/dto/CustomerIdResponse.java | 4 ++++
.../linky/internal/dto/IdentityDetails.java | 3 +++
.../binding/linky/internal/dto/MeterReading.java | 2 ++
.../linky/internal/dto/MeterResponse.java | 3 +++
.../binding/linky/internal/dto/ReadingType.java | 9 +++++++++
.../binding/linky/internal/dto/UsagePoint.java | 3 +++
9 files changed, 50 insertions(+)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
index d7deef5c98a..80293849024 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AddressInfo.java
@@ -14,6 +14,8 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
+import com.google.gson.annotations.SerializedName;
+
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -23,7 +25,11 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
public class AddressInfo {
public String street;
public String locality;
+
+ @SerializedName("postal_code")
public String postalCode;
+
+ @SerializedName("insee_code")
public String inseeCode;
public String city;
public String country;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
index aa7a505c504..2a04cbe0618 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
@@ -14,6 +14,8 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
+import com.google.gson.annotations.SerializedName;
+
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -22,11 +24,25 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
public class Contracts {
public String segment;
+
+ @SerializedName("subscribed_power")
public String subscribedPower;
+
+ @SerializedName("last_activation_date")
public String lastActivationDate;
+
+ @SerializedName("distribution_tariff")
public String distributionTariff;
+
+ @SerializedName("offpeak_hours")
public String offpeakHours;
+
+ @SerializedName("contract_status")
public String contractStatus;
+
+ @SerializedName("contract_type")
public String contractType;
+
+ @SerializedName("last_distribution_tariff_change_date")
public String lastDistributionTariffChangeDate;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
index bbda35b2618..b79a551d877 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
@@ -16,6 +16,8 @@ import java.util.Arrays;
import org.eclipse.jetty.jaas.spi.UserInfo;
+import com.google.gson.annotations.SerializedName;
+
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -24,8 +26,10 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
*/
public class Customer {
+ @SerializedName("customer_id")
public String customerId;
+ @SerializedName("usage_points")
public UsagePoint[] usagePoints;
public static Customer fromWebPrmInfos(WebPrmInfo[] webPrmsInfo, String prmId) {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java
index 62bb3d7cff4..3975fb4641a 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerIdResponse.java
@@ -14,6 +14,8 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
+import com.google.gson.annotations.SerializedName;
+
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -22,7 +24,9 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
*/
public class CustomerIdResponse {
+ @SerializedName("customer_id")
public String customerId;
public IdentityDetails identity;
+ @SerializedName("contact_data")
public ContactInfo contactData;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java
index 45d99606ee1..15a07b6a275 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/IdentityDetails.java
@@ -14,6 +14,8 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
+import com.google.gson.annotations.SerializedName;
+
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -22,5 +24,6 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
*/
public class IdentityDetails {
+ @SerializedName("natural_person")
public IdentityInfo naturalPerson;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
index 842e2f60696..4b5435ea052 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
@@ -24,6 +24,7 @@ import com.google.gson.annotations.SerializedName;
*/
public class MeterReading {
+ @SerializedName("usage_point_id")
public String usagePointId;
@SerializedName("start")
@@ -34,6 +35,7 @@ public class MeterReading {
public String quality;
+ @SerializedName("reading_type")
public ReadingType readingType;
@SerializedName("interval_reading")
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
index 726987e602f..f5d1f85ad73 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterResponse.java
@@ -14,6 +14,8 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
+import com.google.gson.annotations.SerializedName;
+
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -22,5 +24,6 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
*/
public class MeterResponse {
+ @SerializedName("meter_reading")
public MeterReading meterReading;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java
index 4642ea53edc..454f04fe295 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ReadingType.java
@@ -14,6 +14,8 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
+import com.google.gson.annotations.SerializedName;
+
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -22,8 +24,15 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
*/
public class ReadingType {
+ @SerializedName("measurement_kind")
public String measurementKind;
+
+ @SerializedName("measuring_period")
public String measuringPeriod;
+
+ @SerializedName("unit")
public String unit;
+
+ @SerializedName("aggregate")
public String aggregate;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
index f0cd3cd421e..235b6cd965e 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
@@ -14,6 +14,8 @@ package org.openhab.binding.linky.internal.dto;
import org.eclipse.jetty.jaas.spi.UserInfo;
+import com.google.gson.annotations.SerializedName;
+
/**
* The {@link UserInfo} holds informations about energy delivery point
*
@@ -22,6 +24,7 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
*/
public class UsagePoint {
+ @SerializedName("usage_point")
public UsagePointDetails usagePoint;
public Contracts contracts;
}
From 03089291cea0f85c285459e3b2d9a019137390ed Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 17:51:25 +0200
Subject: [PATCH 050/135] add constructor to PrmInfo
Signed-off-by: Laurent ARNAL
---
.../openhab/binding/linky/internal/dto/PrmInfo.java | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java
index 9318ac3f322..aec269a4137 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java
@@ -22,6 +22,16 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
*/
public class PrmInfo {
+
+ public PrmInfo() {
+ customerId = "";
+ contractInfo = new Contracts();
+ identityInfo = new IdentityInfo();
+ addressInfo = new AddressInfo();
+ contactInfo = new ContactInfo();
+ usagePointInfo = new UsagePointDetails();
+ }
+
public String prmId;
public String customerId;
From 58f9f38b0f146d2f8f0b9105fc7fed47beda2df9 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 21:13:06 +0200
Subject: [PATCH 051/135] prevent failure if we can't read contract info from
API
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 24 +++++++++++--------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index 819744b1d88..edbe18ff5a5 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -138,20 +138,24 @@ public class EnedisHttpApi {
}
PrmInfo result = new PrmInfo();
- Customer customer = getContract(handler, prmId);
- UsagePoint usagePoint = customer.usagePoints[0];
+ result.prmId = prmId;
- result.contractInfo = usagePoint.contracts;
- result.usagePointInfo = usagePoint.usagePoint;
+ result.contractInfo.subscribedPower = "Na";
- result.identityInfo = getIdentity(handler, prmId);
- result.contactInfo = getContact(handler, prmId);
+ try {
+ Customer customer = getContract(handler, prmId);
+ UsagePoint usagePoint = customer.usagePoints[0];
- result.addressInfo = result.usagePointInfo.usagePointAddresses;
+ result.contractInfo = usagePoint.contracts;
+ result.usagePointInfo = usagePoint.usagePoint;
- result.prmId = result.usagePointInfo.usagePointId;
- result.customerId = "";
- // customer.customerId;
+ result.identityInfo = getIdentity(handler, prmId);
+ result.contactInfo = getContact(handler, prmId);
+
+ result.addressInfo = result.usagePointInfo.usagePointAddresses;
+ } catch (Exception ex) {
+ logger.debug("Warning, unable to read contract info");
+ }
return result;
}
From 909d075e04788784fd4e3522adb13abd58863cb1 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 21:13:55 +0200
Subject: [PATCH 052/135] add divider support to handle difference of return
value between old web api and new api
Signed-off-by: Laurent ARNAL
---
.../binding/linky/internal/handler/ApiBridgeHandler.java | 2 ++
.../binding/linky/internal/handler/EnedisBridgeHandler.java | 5 +++++
.../linky/internal/handler/EnedisWebBridgeHandler.java | 5 +++++
.../internal/handler/MyElectricalDataBridgeHandler.java | 5 +++++
4 files changed, 17 insertions(+)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index e1d130ec507..48eb9087695 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -339,6 +339,8 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
return result;
}
+ public abstract double getDivider();
+
public abstract String getBaseUrl();
public abstract String getContactUrl();
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index fcb7d236435..bffcbfd6224 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -99,6 +99,11 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
return "Bearer " + accesToken.getAccessToken();
}
+ @Override
+ public double getDivider() {
+ return 1000.00;
+ }
+
@Override
public String getBaseUrl() {
return BASE_URL;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index 373cf189153..c7906213b74 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -108,6 +108,11 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
return "";
}
+ @Override
+ public double getDivider() {
+ return 1.00;
+ }
+
@Override
public String getBaseUrl() {
return BASE_URL;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index dde9c24f165..6bdc4d9b551 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -140,6 +140,11 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
return config.token;
}
+ @Override
+ public double getDivider() {
+ return 1000.00;
+ }
+
@Override
public String getBaseUrl() {
return BASE_URL;
From 187a37c151e66e399b43427e65c7c31b8893f872 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Fri, 16 Aug 2024 21:14:38 +0200
Subject: [PATCH 053/135] review calculation of agregats for new API to align
to old Web API (WIP)
Signed-off-by: Laurent ARNAL
---
.../linky/internal/handler/LinkyHandler.java | 60 +++++++++++++++----
1 file changed, 48 insertions(+), 12 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 790197ccca4..4cc3d7635bb 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -60,6 +60,9 @@ import org.openhab.core.types.TimeSeries.Policy;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.threeten.extra.Months;
+import org.threeten.extra.Weeks;
+import org.threeten.extra.Years;
/**
* The {@link LinkyHandler} is responsible for handling commands, which are
@@ -83,6 +86,7 @@ public class LinkyHandler extends BaseThingHandler {
private @Nullable ScheduledFuture> refreshJob;
private LinkyConfiguration config;
private @Nullable EnedisHttpApi enedisApi;
+ private double divider = 1.00;
private enum Target {
FIRST,
@@ -149,6 +153,7 @@ public class LinkyHandler extends BaseThingHandler {
return;
}
enedisApi = bridgeHandler.getEnedisApi();
+ divider = bridgeHandler.getDivider();
updateStatus(ThingStatus.UNKNOWN);
@@ -299,8 +304,6 @@ public class LinkyHandler extends BaseThingHandler {
private synchronized void updatePowerData() {
dailyConsumptionMaxPower.getValue().ifPresentOrElse(values -> {
int dSize = values.dayValue.length;
- double divider = 1000.00;
- divider = 1.00;
updateVAChannel(PEAK_POWER_DAY_MINUS_1, values.dayValue[dSize - 1].value / divider);
updateState(PEAK_POWER_TS_DAY_MINUS_1,
@@ -338,8 +341,7 @@ public class LinkyHandler extends BaseThingHandler {
private synchronized void updateEnergyData() {
dailyConsumption.getValue().ifPresentOrElse(values -> {
int dSize = values.dayValue.length;
- double divider = 1000.00;
- divider = 1.00;
+
updateKwhChannel(DAY_MINUS_1, values.dayValue[dSize - 1].value / divider);
updateKwhChannel(DAY_MINUS_2, values.dayValue[dSize - 2].value / divider);
updateKwhChannel(DAY_MINUS_3, values.dayValue[dSize - 3].value / divider);
@@ -597,40 +599,74 @@ public class LinkyHandler extends BaseThingHandler {
if (meterReading != null) {
if (meterReading.weekValue == null) {
- meterReading.weekValue = new IntervalReading[208];
- meterReading.monthValue = new IntervalReading[48];
- meterReading.yearValue = new IntervalReading[4];
- for (int idx = 0; idx < 208; idx++) {
+ LocalDate startDate = meterReading.dayValue[0].date.toLocalDate();
+ LocalDate endDate = meterReading.dayValue[meterReading.dayValue.length - 1].date.toLocalDate();
+
+ int weeksNum = Weeks.between(startDate, endDate).getAmount() + 1;
+ int monthsNum = Months.between(startDate, endDate).getAmount() + 1;
+ int yearsNum = Years.between(startDate, endDate).getAmount() + 2;
+
+ meterReading.weekValue = new IntervalReading[weeksNum];
+ meterReading.monthValue = new IntervalReading[monthsNum];
+ meterReading.yearValue = new IntervalReading[yearsNum];
+
+ for (int idx = 0; idx < weeksNum; idx++) {
meterReading.weekValue[idx] = new IntervalReading();
}
- for (int idx = 0; idx < 48; idx++) {
+ for (int idx = 0; idx < monthsNum; idx++) {
meterReading.monthValue[idx] = new IntervalReading();
}
- for (int idx = 0; idx < 4; idx++) {
+ for (int idx = 0; idx < yearsNum; idx++) {
meterReading.yearValue[idx] = new IntervalReading();
}
int size = meterReading.dayValue.length;
int baseYear = meterReading.dayValue[0].date.getYear();
+ int baseMonth = meterReading.dayValue[0].date.getMonthValue();
+ int baseDayOfYear = meterReading.dayValue[0].date.getDayOfYear();
+ int baseWeek = ((baseDayOfYear - 1) / 7) + 1;
for (int idx = 0; idx < size; idx++) {
IntervalReading ir = meterReading.dayValue[idx];
LocalDateTime dt = ir.date;
double value = ir.value;
+ /*
+ * int idxWeek = Weeks.between(startDate, dt).getAmount();
+ * int idxMonth = Months.between(startDate, dt).getAmount();
+ * int idxYear = dt.getYear() - baseYear;
+ */
+
int idxYear = dt.getYear() - baseYear;
int dayOfYear = dt.getDayOfYear();
int week = ((dayOfYear - 1) / 7) + 1;
int month = dt.getMonthValue();
- int idxMonth = (idxYear * 12) + month;
- int idxWeek = (idxYear * 52) + week;
+ int idxMonth = (idxYear * 12) + month - baseMonth;
+ int idxWeek = (idxYear * 52) + week - baseWeek;
+
+ if (idxWeek >= weeksNum) {
+ continue;
+ }
+ if (idxMonth >= monthsNum) {
+ continue;
+ }
meterReading.weekValue[idxWeek].value += value;
meterReading.monthValue[idxMonth].value += value;
meterReading.yearValue[idxYear].value += value;
+
+ if (meterReading.weekValue[idxWeek].date == null) {
+ meterReading.weekValue[idxWeek].date = dt;
+ }
+ if (meterReading.monthValue[idxMonth].date == null) {
+ meterReading.monthValue[idxMonth].date = LocalDateTime.of(dt.getYear(), month, 1, 0, 0);
+ }
+ if (meterReading.yearValue[idxYear].date == null) {
+ meterReading.yearValue[idxYear].date = dt;
+ }
}
}
}
From aecf0c1ed1e52c716a761c6f876be027afc98c2d Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sat, 17 Aug 2024 17:58:02 +0200
Subject: [PATCH 054/135] fix aggregate calculation for new API
Signed-off-by: Laurent ARNAL
---
.../linky/internal/handler/LinkyHandler.java | 52 +++++++++----------
1 file changed, 25 insertions(+), 27 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 4cc3d7635bb..b018c9384e2 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -23,9 +23,11 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
+import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -407,6 +409,10 @@ public class LinkyHandler extends BaseThingHandler {
LocalDate today = LocalDate.now();
for (int i = 0; i < iv.length; i++) {
+ if (iv[i].date == null) {
+ continue;
+ }
+
Instant timestamp = iv[i].date.toInstant(ZoneOffset.UTC);
timeSeries.add(timestamp, new DecimalType(iv[i].value));
}
@@ -603,8 +609,8 @@ public class LinkyHandler extends BaseThingHandler {
LocalDate startDate = meterReading.dayValue[0].date.toLocalDate();
LocalDate endDate = meterReading.dayValue[meterReading.dayValue.length - 1].date.toLocalDate();
- int weeksNum = Weeks.between(startDate, endDate).getAmount() + 1;
- int monthsNum = Months.between(startDate, endDate).getAmount() + 1;
+ int weeksNum = Weeks.between(startDate, endDate).getAmount() + 2;
+ int monthsNum = Months.between(startDate, endDate).getAmount() + 2;
int yearsNum = Years.between(startDate, endDate).getAmount() + 2;
meterReading.weekValue = new IntervalReading[weeksNum];
@@ -632,40 +638,32 @@ public class LinkyHandler extends BaseThingHandler {
LocalDateTime dt = ir.date;
double value = ir.value;
- /*
- * int idxWeek = Weeks.between(startDate, dt).getAmount();
- * int idxMonth = Months.between(startDate, dt).getAmount();
- * int idxYear = dt.getYear() - baseYear;
- */
-
int idxYear = dt.getYear() - baseYear;
-
- int dayOfYear = dt.getDayOfYear();
- int week = ((dayOfYear - 1) / 7) + 1;
int month = dt.getMonthValue();
+ int weekOfYear = dt.get(WeekFields.of(Locale.FRANCE).weekOfYear());
int idxMonth = (idxYear * 12) + month - baseMonth;
- int idxWeek = (idxYear * 52) + week - baseWeek;
+ int idxWeek = (idxYear * 52) + weekOfYear - baseWeek;
- if (idxWeek >= weeksNum) {
- continue;
+ if (idxWeek < weeksNum) {
+ meterReading.weekValue[idxWeek].value += value;
+ if (meterReading.weekValue[idxWeek].date == null) {
+ meterReading.weekValue[idxWeek].date = dt;
+ }
}
- if (idxMonth >= monthsNum) {
- continue;
+ if (idxMonth < monthsNum) {
+ meterReading.monthValue[idxMonth].value += value;
+ if (meterReading.monthValue[idxMonth].date == null) {
+ meterReading.monthValue[idxMonth].date = LocalDateTime.of(dt.getYear(), month, 1, 0, 0);
+ }
}
- meterReading.weekValue[idxWeek].value += value;
- meterReading.monthValue[idxMonth].value += value;
- meterReading.yearValue[idxYear].value += value;
+ if (idxYear < yearsNum) {
+ meterReading.yearValue[idxYear].value += value;
- if (meterReading.weekValue[idxWeek].date == null) {
- meterReading.weekValue[idxWeek].date = dt;
- }
- if (meterReading.monthValue[idxMonth].date == null) {
- meterReading.monthValue[idxMonth].date = LocalDateTime.of(dt.getYear(), month, 1, 0, 0);
- }
- if (meterReading.yearValue[idxYear].date == null) {
- meterReading.yearValue[idxYear].date = dt;
+ if (meterReading.yearValue[idxYear].date == null) {
+ meterReading.yearValue[idxYear].date = LocalDateTime.of(dt.getYear(), 1, 1, 0, 0);
+ }
}
}
}
From fb66f387b2f296c45d8bd5ab79d86dd85e7241fb Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sat, 17 Aug 2024 18:02:10 +0200
Subject: [PATCH 055/135] fix divider for timeseries
Signed-off-by: Laurent ARNAL
---
.../linky/internal/handler/LinkyHandler.java | 33 ++++++++++---------
1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index b018c9384e2..403acfb6151 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -307,15 +307,15 @@ public class LinkyHandler extends BaseThingHandler {
dailyConsumptionMaxPower.getValue().ifPresentOrElse(values -> {
int dSize = values.dayValue.length;
- updateVAChannel(PEAK_POWER_DAY_MINUS_1, values.dayValue[dSize - 1].value / divider);
+ updateVAChannel(PEAK_POWER_DAY_MINUS_1, values.dayValue[dSize - 1].value);
updateState(PEAK_POWER_TS_DAY_MINUS_1,
new DateTimeType(values.dayValue[dSize - 1].date.atZone(ZoneId.systemDefault())));
- updateVAChannel(PEAK_POWER_DAY_MINUS_2, values.dayValue[dSize - 2].value / divider);
+ updateVAChannel(PEAK_POWER_DAY_MINUS_2, values.dayValue[dSize - 2].value);
updateState(PEAK_POWER_TS_DAY_MINUS_2,
new DateTimeType(values.dayValue[dSize - 2].date.atZone(ZoneId.systemDefault())));
- updateVAChannel(PEAK_POWER_DAY_MINUS_3, values.dayValue[dSize - 3].value / divider);
+ updateVAChannel(PEAK_POWER_DAY_MINUS_3, values.dayValue[dSize - 3].value);
updateState(PEAK_POWER_TS_DAY_MINUS_3,
new DateTimeType(values.dayValue[dSize - 3].date.atZone(ZoneId.systemDefault())));
@@ -344,26 +344,26 @@ public class LinkyHandler extends BaseThingHandler {
dailyConsumption.getValue().ifPresentOrElse(values -> {
int dSize = values.dayValue.length;
- updateKwhChannel(DAY_MINUS_1, values.dayValue[dSize - 1].value / divider);
- updateKwhChannel(DAY_MINUS_2, values.dayValue[dSize - 2].value / divider);
- updateKwhChannel(DAY_MINUS_3, values.dayValue[dSize - 3].value / divider);
+ updateKwhChannel(DAY_MINUS_1, values.dayValue[dSize - 1].value);
+ updateKwhChannel(DAY_MINUS_2, values.dayValue[dSize - 2].value);
+ updateKwhChannel(DAY_MINUS_3, values.dayValue[dSize - 3].value);
LocalDate currentDt = LocalDate.now();
int idxCurrentYear = values.yearValue.length - 1;
int idxCurrentWeek = values.weekValue.length - 1;
int idxCurrentMonth = values.monthValue.length - 1;
- updateKwhChannel(WEEK_MINUS_0, values.weekValue[idxCurrentWeek].value / divider);
- updateKwhChannel(WEEK_MINUS_1, values.weekValue[idxCurrentWeek - 1].value / divider);
- updateKwhChannel(WEEK_MINUS_2, values.weekValue[idxCurrentWeek - 2].value / divider);
+ updateKwhChannel(WEEK_MINUS_0, values.weekValue[idxCurrentWeek].value);
+ updateKwhChannel(WEEK_MINUS_1, values.weekValue[idxCurrentWeek - 1].value);
+ updateKwhChannel(WEEK_MINUS_2, values.weekValue[idxCurrentWeek - 2].value);
- updateKwhChannel(MONTH_MINUS_0, values.monthValue[idxCurrentMonth].value / divider);
- updateKwhChannel(MONTH_MINUS_1, values.monthValue[idxCurrentMonth - 1].value / divider);
- updateKwhChannel(MONTH_MINUS_2, values.monthValue[idxCurrentMonth - 2].value / divider);
+ updateKwhChannel(MONTH_MINUS_0, values.monthValue[idxCurrentMonth].value);
+ updateKwhChannel(MONTH_MINUS_1, values.monthValue[idxCurrentMonth - 1].value);
+ updateKwhChannel(MONTH_MINUS_2, values.monthValue[idxCurrentMonth - 2].value);
- updateKwhChannel(YEAR_MINUS_0, values.yearValue[idxCurrentYear].value / divider);
- updateKwhChannel(YEAR_MINUS_1, values.yearValue[idxCurrentYear - 1].value / divider);
- updateKwhChannel(YEAR_MINUS_2, values.yearValue[idxCurrentYear - 2].value / divider);
+ updateKwhChannel(YEAR_MINUS_0, values.yearValue[idxCurrentYear].value);
+ updateKwhChannel(YEAR_MINUS_1, values.yearValue[idxCurrentYear - 1].value);
+ updateKwhChannel(YEAR_MINUS_2, values.yearValue[idxCurrentYear - 2].value);
updateConsumptionTimeSeries(DAYLY_CONSUMPTION, values.dayValue);
updateConsumptionTimeSeries(WEEKLY_CONSUMPTION, values.weekValue);
@@ -605,7 +605,6 @@ public class LinkyHandler extends BaseThingHandler {
if (meterReading != null) {
if (meterReading.weekValue == null) {
-
LocalDate startDate = meterReading.dayValue[0].date.toLocalDate();
LocalDate endDate = meterReading.dayValue[meterReading.dayValue.length - 1].date.toLocalDate();
@@ -637,6 +636,8 @@ public class LinkyHandler extends BaseThingHandler {
IntervalReading ir = meterReading.dayValue[idx];
LocalDateTime dt = ir.date;
double value = ir.value;
+ value = value / divider;
+ ir.value = value;
int idxYear = dt.getYear() - baseYear;
int month = dt.getMonthValue();
From a8fe3becf675a5c03c39a71097b9e1f7e6a88bcb Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sat, 17 Aug 2024 18:44:01 +0200
Subject: [PATCH 056/135] start load curve implementations
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 5 ++
.../internal/handler/ApiBridgeHandler.java | 2 +
.../internal/handler/EnedisBridgeHandler.java | 6 +++
.../handler/EnedisWebBridgeHandler.java | 7 +++
.../linky/internal/handler/LinkyHandler.java | 49 +++++++++++++++++--
.../MyElectricalDataBridgeHandler.java | 7 +++
6 files changed, 72 insertions(+), 4 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index edbe18ff5a5..b3c434abaf1 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -341,6 +341,11 @@ public class EnedisHttpApi {
return getMeasures(handler, apiBridgeHandler.getDailyConsumptionUrl(), prmId, from, to);
}
+ public MeterReading getLoadCurveData(LinkyHandler handler, String prmId, LocalDate from, LocalDate to)
+ throws LinkyException {
+ return getMeasures(handler, apiBridgeHandler.getLoadCurveUrl(), prmId, from, to);
+ }
+
public MeterReading getPowerData(LinkyHandler handler, String prmId, LocalDate from, LocalDate to)
throws LinkyException {
return getMeasures(handler, apiBridgeHandler.getMaxPowerUrl(), prmId, from, to);
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 48eb9087695..9257e84d282 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -355,6 +355,8 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
public abstract String getMaxPowerUrl();
+ public abstract String getLoadCurveUrl();
+
public abstract String getTempoUrl();
public abstract DateTimeFormatter getApiDateFormat();
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index bffcbfd6224..6c487cdcb45 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -48,6 +48,7 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
+ "metering_data_dc/v5/daily_consumption?usage_point_id=%s&start=%s&end=%s";
private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
+ private static final String LOAD_CURVE_CONSUMPTION_URL = BASE_URL + "??";
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter API_DATE_FORMAT_YEAR_FIRST = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@@ -139,6 +140,11 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
return MEASURE_MAX_POWER_URL;
}
+ @Override
+ public String getLoadCurveUrl() {
+ return LOAD_CURVE_CONSUMPTION_URL;
+ }
+
@Override
public String getTempoUrl() {
return "";
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index c7906213b74..bdb642b6f04 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -72,6 +72,8 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
private static final String MEASURE_MAX_POWER_URL = PRM_INFO_BASE_URL
+ "undefined/prms/%s/donnees-pmax?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
+ private static final String LOAD_CURVE_CONSUMPTION_URL = BASE_URL + "??";
+
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
private static final DateTimeFormatter API_DATE_FORMAT_YEAR_FIRST = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@@ -148,6 +150,11 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
return MEASURE_MAX_POWER_URL;
}
+ @Override
+ public String getLoadCurveUrl() {
+ return LOAD_CURVE_CONSUMPTION_URL;
+ }
+
@Override
public String getTempoUrl() {
return TEMPO_URL;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 403acfb6151..f48e6e14fbe 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -83,6 +83,7 @@ public class LinkyHandler extends BaseThingHandler {
private final ExpiringDayCache dailyConsumption;
private final ExpiringDayCache dailyConsumptionMaxPower;
+ private final ExpiringDayCache loadCurveConsumption;
private final ExpiringDayCache tempoInformation;
private @Nullable ScheduledFuture> refreshJob;
@@ -139,6 +140,17 @@ public class LinkyHandler extends BaseThingHandler {
TempoResponse tempoData = getTempoData(today.minusDays(1095), today.plusDays(1));
return tempoData;
});
+
+ // Comsuption Load Curve
+ this.loadCurveConsumption = new ExpiringDayCache<>("loadCurveConsumption", REFRESH_FIRST_HOUR_OF_DAY, () -> {
+ LocalDate today = LocalDate.now();
+ MeterReading meterReading = getLoadCurveConsumption(today.minusDays(7), today);
+ meterReading = getMeterReadingAfterChecks(meterReading);
+ if (meterReading != null) {
+ logData(meterReading.dayValue, "Day (peak)", DateTimeFormatter.ISO_LOCAL_DATE, Target.ALL);
+ }
+ return meterReading;
+ });
}
@Override
@@ -179,6 +191,8 @@ public class LinkyHandler extends BaseThingHandler {
final LocalDateTime nextDayFirstTimeUpdate = now.plusDays(1).withHour(REFRESH_FIRST_HOUR_OF_DAY)
.truncatedTo(ChronoUnit.HOURS);
+ updateStatus(ThingStatus.ONLINE);
+
refreshJob = scheduler.scheduleWithFixedDelay(this::updateData,
ChronoUnit.MINUTES.between(now, nextDayFirstTimeUpdate) % REFRESH_INTERVAL_IN_MIN + 1,
REFRESH_INTERVAL_IN_MIN, TimeUnit.MINUTES);
@@ -249,6 +263,7 @@ public class LinkyHandler extends BaseThingHandler {
updateEnergyData();
updatePowerData();
updateTempoTimeSeries();
+ updateLoadCurveData();
if (!connectedBefore && isConnected()) {
disconnect();
@@ -339,7 +354,6 @@ public class LinkyHandler extends BaseThingHandler {
/**
* Request new dayly/weekly data and updates channels
*/
-
private synchronized void updateEnergyData() {
dailyConsumption.getValue().ifPresentOrElse(values -> {
int dSize = values.dayValue.length;
@@ -384,6 +398,17 @@ public class LinkyHandler extends BaseThingHandler {
}
}
+ /**
+ * Request new dayly/weekly data and updates channels
+ */
+ private synchronized void updateLoadCurveData() {
+ loadCurveConsumption.getValue().ifPresentOrElse(values -> {
+ int dSize = values.dayValue.length;
+ logger.debug("load curve");
+ }, () -> {
+ });
+ }
+
private synchronized void updateMaxPowerTimeSeries(String channel, IntervalReading[] iv) {
TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
LocalDate today = LocalDate.now();
@@ -502,7 +527,24 @@ public class LinkyHandler extends BaseThingHandler {
if (api != null) {
try {
MeterReading meterReading = api.getEnergyData(this, config.prmId, from, to);
- updateStatus(ThingStatus.ONLINE);
+ return meterReading;
+ } catch (LinkyException e) {
+ logger.debug("Exception when getting consumption data: {}", e.getMessage(), e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
+ }
+ }
+
+ return null;
+ }
+
+ private @Nullable MeterReading getLoadCurveConsumption(LocalDate from, LocalDate to) {
+ logger.debug("getLoadCurveConsumption from {} to {}", from.format(DateTimeFormatter.ISO_LOCAL_DATE),
+ to.format(DateTimeFormatter.ISO_LOCAL_DATE));
+
+ EnedisHttpApi api = this.enedisApi;
+ if (api != null) {
+ try {
+ MeterReading meterReading = api.getLoadCurveData(this, config.prmId, from, to);
return meterReading;
} catch (LinkyException e) {
logger.debug("Exception when getting consumption data: {}", e.getMessage(), e);
@@ -521,7 +563,6 @@ public class LinkyHandler extends BaseThingHandler {
if (api != null) {
try {
MeterReading meterReading = api.getPowerData(this, config.prmId, from, to);
- updateStatus(ThingStatus.ONLINE);
return meterReading;
} catch (LinkyException e) {
logger.debug("Exception when getting power data: {}", e.getMessage(), e);
@@ -539,7 +580,6 @@ public class LinkyHandler extends BaseThingHandler {
if (api != null) {
try {
TempoResponse result = api.getTempoData(this, from, to);
- updateStatus(ThingStatus.ONLINE);
return result;
} catch (LinkyException e) {
logger.debug("Exception when getting power data: {}", e.getMessage(), e);
@@ -586,6 +626,7 @@ public class LinkyHandler extends BaseThingHandler {
updateEnergyData();
updatePowerData();
updateTempoTimeSeries();
+ updateLoadCurveData();
if (!connectedBefore && isConnected()) {
disconnect();
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index 6bdc4d9b551..0b54d74d775 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -53,6 +53,8 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
+ "daily_consumption/%s/start/%s/end/%s/cache/";
private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ "daily_consumption_max_power/%s/start/%s/end/%s/cache/";
+ private static final String LOAD_CURVE_CONSUMPTION_URL = BASE_URL
+ + "consumption_load_curve/%s/start/%s/end/%s/cache/";
private static final String TEMPO_URL = BASE_URL + "rte/tempo/%s/%s";
@@ -180,6 +182,11 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
return MEASURE_MAX_POWER_URL;
}
+ @Override
+ public String getLoadCurveUrl() {
+ return LOAD_CURVE_CONSUMPTION_URL;
+ }
+
@Override
public String getTempoUrl() {
return TEMPO_URL;
From 54560e1c60ad00c01485307ee605f55a447aad84 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sat, 17 Aug 2024 18:49:54 +0200
Subject: [PATCH 057/135] add loadcurve url for other bridge
Signed-off-by: Laurent ARNAL
---
.../binding/linky/internal/handler/EnedisBridgeHandler.java | 3 ++-
.../binding/linky/internal/handler/EnedisWebBridgeHandler.java | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index 6c487cdcb45..0d3eb7a1ff3 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -48,7 +48,8 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
+ "metering_data_dc/v5/daily_consumption?usage_point_id=%s&start=%s&end=%s";
private static final String MEASURE_MAX_POWER_URL = BASE_URL
+ "metering_data_dcmp/v5/daily_consumption_max_power?usage_point_id=%s&start=%s&end=%s";
- private static final String LOAD_CURVE_CONSUMPTION_URL = BASE_URL + "??";
+ private static final String LOAD_CURVE_CONSUMPTION_URL = BASE_URL
+ + "metering_data_clc/v5/consumption_load_curve?usage_point_id=%s&start=%s&end=%s";
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter API_DATE_FORMAT_YEAR_FIRST = DateTimeFormatter.ofPattern("yyyy-MM-dd");
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index bdb642b6f04..93f8c430525 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -72,7 +72,8 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
private static final String MEASURE_MAX_POWER_URL = PRM_INFO_BASE_URL
+ "undefined/prms/%s/donnees-pmax?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
- private static final String LOAD_CURVE_CONSUMPTION_URL = BASE_URL + "??";
+ private static final String LOAD_CURVE_CONSUMPTION_URL = PRM_INFO_BASE_URL
+ + "undefined/prms/%s/courbe-de-charge?dateDebut=%s&dateFin=%s&mesuretypecode=CONS";
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
private static final DateTimeFormatter API_DATE_FORMAT_YEAR_FIRST = DateTimeFormatter.ofPattern("yyyy-MM-dd");
From ea69cd7a347fb46ab2563d89936d5503f51f0792 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 10:05:43 +0200
Subject: [PATCH 058/135] adapt code to support LoadCurve for old Web API
Signed-off-by: Laurent ARNAL
---
.../binding/linky/internal/dto/AuthData.java | 5 +++
.../linky/internal/dto/ConsumptionReport.java | 3 ++
.../linky/internal/dto/MeterReading.java | 38 ++++++++++++++++---
.../handler/EnedisWebBridgeHandler.java | 2 +-
.../linky/internal/handler/LinkyHandler.java | 5 +--
5 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java
index 27eddb18695..8d92ddd2c02 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/AuthData.java
@@ -29,6 +29,11 @@ public class AuthData {
public @Nullable String name;
public @Nullable Object value;
+ public NameValuePair(String name, Object value) {
+ this.name = name;
+ this.value = value;
+ }
+
public @Nullable String valueAsString() {
return (value instanceof String stringValue) ? stringValue : null;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ConsumptionReport.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ConsumptionReport.java
index e2d57620d2b..e96b7af797f 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ConsumptionReport.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ConsumptionReport.java
@@ -52,6 +52,9 @@ public class ConsumptionReport {
public String grandeurMetier;
public String grandeurPhysique;
public String unite;
+ public String mesuresPasEnum;
+ public List labels;
+ public List data;
}
public class FirstLevel {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
index 4b5435ea052..e978ef63cff 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
@@ -12,6 +12,10 @@
*/
package org.openhab.binding.linky.internal.dto;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
import org.eclipse.jetty.jaas.spi.UserInfo;
import com.google.gson.annotations.SerializedName;
@@ -48,10 +52,15 @@ public class MeterReading {
MeterReading result = new MeterReading();
result.readingType = new ReadingType();
- result.dayValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.days);
- result.weekValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.weeks);
- result.monthValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.months);
- result.yearValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.years);
+ if (comsumptionReport.firstLevel.consumptions.aggregats != null) {
+ result.dayValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.days);
+ result.weekValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.weeks);
+ result.monthValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.months);
+ result.yearValue = fromAgregat(comsumptionReport.firstLevel.consumptions.aggregats.years);
+ } else {
+ result.dayValue = fromLabelsAndDatas(comsumptionReport.firstLevel.consumptions.labels,
+ comsumptionReport.firstLevel.consumptions.data);
+ }
return result;
}
@@ -64,7 +73,6 @@ public class MeterReading {
for (int i = 0; i < size; i++) {
Double data = agregat.datas.get(i);
ConsumptionReport.Period period = agregat.periodes.get(i);
- String label = agregat.labels.get(i);
result[i] = new IntervalReading();
result[i].value = data;
@@ -73,4 +81,24 @@ public class MeterReading {
return result;
}
+
+ public static IntervalReading[] fromLabelsAndDatas(List labels, List datas) {
+
+ int size = datas.size();
+ IntervalReading[] result = new IntervalReading[size];
+
+ for (int i = 0; i < size; i++) {
+ Double data = datas.get(i);
+ String label = labels.get(i);
+ // 2024-08-11T00:00:00.000+0200
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS[X]");
+ ZonedDateTime dt = ZonedDateTime.parse(label, formatter);
+
+ result[i] = new IntervalReading();
+ result[i].value = data;
+ result[i].date = dt.toLocalDateTime();
+ }
+
+ return result;
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index 93f8c430525..e44d24a6f25 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -172,7 +172,7 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
}
@Override
- protected void connectionInit() throws LinkyException {
+ protected synchronized void connectionInit() throws LinkyException {
logger.debug("Starting login process for user : {}", config.username);
try {
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index f48e6e14fbe..cd6983c4505 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -272,7 +272,6 @@ public class LinkyHandler extends BaseThingHandler {
private synchronized void updateTempoTimeSeries() {
tempoInformation.getValue().ifPresentOrElse(values -> {
- LocalDate today = LocalDate.now();
TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
values.forEach((k, v) -> {
@@ -362,7 +361,6 @@ public class LinkyHandler extends BaseThingHandler {
updateKwhChannel(DAY_MINUS_2, values.dayValue[dSize - 2].value);
updateKwhChannel(DAY_MINUS_3, values.dayValue[dSize - 3].value);
- LocalDate currentDt = LocalDate.now();
int idxCurrentYear = values.yearValue.length - 1;
int idxCurrentWeek = values.weekValue.length - 1;
int idxCurrentMonth = values.monthValue.length - 1;
@@ -407,11 +405,11 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("load curve");
}, () -> {
});
+
}
private synchronized void updateMaxPowerTimeSeries(String channel, IntervalReading[] iv) {
TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
- LocalDate today = LocalDate.now();
for (int i = 0; i < iv.length; i++) {
try {
@@ -431,7 +429,6 @@ public class LinkyHandler extends BaseThingHandler {
private synchronized void updateConsumptionTimeSeries(String channel, IntervalReading[] iv) {
TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
- LocalDate today = LocalDate.now();
for (int i = 0; i < iv.length; i++) {
if (iv[i].date == null) {
From ac782362724fe393aed03ad8ff5b22dfb3fb8add Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 10:10:58 +0200
Subject: [PATCH 059/135] add new channel for LoadCurve
Signed-off-by: Laurent ARNAL
---
.../binding/linky/internal/LinkyBindingConstants.java | 2 ++
.../src/main/resources/OH-INF/thing/thing-types.xml | 10 ++++++++++
2 files changed, 12 insertions(+)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
index 20422b8f88d..675da484aa8 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/LinkyBindingConstants.java
@@ -49,6 +49,8 @@ public class LinkyBindingConstants {
// List of all Channel id's
+ public static final String LOAD_CURVE = "loadCurve#power";
+
public static final String DAYLY_CONSUMPTION = "daily#consumption";
public static final String WEEKLY_CONSUMPTION = "weekly#consumption";
public static final String MONTHLY_CONSUMPTION = "monthly#consumption";
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index 46a0f95c07e..fe99a5eaceb 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -84,6 +84,7 @@
+
@@ -109,6 +110,15 @@
+
+ Load curve
+
+
+ Load Curve power
+
+
+
+
Daily consumption
From 33bdf0c972572184402987708966350cbb508ce5 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 10:26:12 +0200
Subject: [PATCH 060/135] add update for load Curve channel
Signed-off-by: Laurent ARNAL
---
.../openhab/binding/linky/internal/handler/LinkyHandler.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index cd6983c4505..55520f979fb 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -402,7 +402,7 @@ public class LinkyHandler extends BaseThingHandler {
private synchronized void updateLoadCurveData() {
loadCurveConsumption.getValue().ifPresentOrElse(values -> {
int dSize = values.dayValue.length;
- logger.debug("load curve");
+ updateMaxPowerTimeSeries(LOAD_CURVE, values.dayValue);
}, () -> {
});
From bf13ac3e840911a6bc19a4333e0312cce4e646fd Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 10:36:38 +0200
Subject: [PATCH 061/135] spotless:apply
Signed-off-by: Laurent ARNAL
---
.../linky/internal/handler/LinkyHandler.java | 1 -
.../resources/OH-INF/thing/thing-types.xml | 18 +++++++++---------
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 55520f979fb..2dd90dfe589 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -405,7 +405,6 @@ public class LinkyHandler extends BaseThingHandler {
updateMaxPowerTimeSeries(LOAD_CURVE, values.dayValue);
}, () -> {
});
-
}
private synchronized void updateMaxPowerTimeSeries(String channel, IntervalReading[] iv) {
diff --git a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
index fe99a5eaceb..44edf5dbe12 100644
--- a/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.linky/src/main/resources/OH-INF/thing/thing-types.xml
@@ -110,15 +110,15 @@
-
- Load curve
-
-
- Load Curve power
-
-
-
-
+
+ Load curve
+
+
+ Load Curve power
+
+
+
+
Daily consumption
From 51a0dc5b8fef37bae735e25e0f3c6fe15c1b1da6 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 10:55:28 +0200
Subject: [PATCH 062/135] fix some typo
Signed-off-by: Laurent ARNAL
---
.../binding/linky/internal/dto/MeterReading.java | 1 -
.../linky/internal/handler/LinkyHandler.java | 14 +++++++-------
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
index e978ef63cff..c24e128ce96 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/MeterReading.java
@@ -90,7 +90,6 @@ public class MeterReading {
for (int i = 0; i < size; i++) {
Double data = datas.get(i);
String label = labels.get(i);
- // 2024-08-11T00:00:00.000+0200
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS[X]");
ZonedDateTime dt = ZonedDateTime.parse(label, formatter);
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 2dd90dfe589..a655d4559e3 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -333,10 +333,10 @@ public class LinkyHandler extends BaseThingHandler {
updateState(PEAK_POWER_TS_DAY_MINUS_3,
new DateTimeType(values.dayValue[dSize - 3].date.atZone(ZoneId.systemDefault())));
- updateMaxPowerTimeSeries(PEAK_POWER_DAILY, values.dayValue);
- updateMaxPowerTimeSeries(PEAK_POWER_WEEKLY, values.weekValue);
- updateMaxPowerTimeSeries(PEAK_POWER_MONTHLY, values.monthValue);
- updateMaxPowerTimeSeries(PEAK_POWER_YEARLY, values.yearValue);
+ updatePowerTimeSeries(PEAK_POWER_DAILY, values.dayValue);
+ updatePowerTimeSeries(PEAK_POWER_WEEKLY, values.weekValue);
+ updatePowerTimeSeries(PEAK_POWER_MONTHLY, values.monthValue);
+ updatePowerTimeSeries(PEAK_POWER_YEARLY, values.yearValue);
}, () -> {
updateKwhChannel(PEAK_POWER_DAY_MINUS_1, Double.NaN);
@@ -397,17 +397,17 @@ public class LinkyHandler extends BaseThingHandler {
}
/**
- * Request new dayly/weekly data and updates channels
+ * Request new loadCurve data and updates channels
*/
private synchronized void updateLoadCurveData() {
loadCurveConsumption.getValue().ifPresentOrElse(values -> {
int dSize = values.dayValue.length;
- updateMaxPowerTimeSeries(LOAD_CURVE, values.dayValue);
+ updatePowerTimeSeries(LOAD_CURVE, values.dayValue);
}, () -> {
});
}
- private synchronized void updateMaxPowerTimeSeries(String channel, IntervalReading[] iv) {
+ private synchronized void updatePowerTimeSeries(String channel, IntervalReading[] iv) {
TimeSeries timeSeries = new TimeSeries(Policy.REPLACE);
for (int i = 0; i < iv.length; i++) {
From f05d7cf3af2e31fce2a8beede03aac47ecf15f72 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 11:50:32 +0200
Subject: [PATCH 063/135] reformat contract reading and BridgeHandler hierarchy
Signed-off-by: Laurent ARNAL
---
.../linky/internal/api/EnedisHttpApi.java | 137 +++++--------
.../linky/internal/dto/ContractDetails.java | 48 +++++
.../binding/linky/internal/dto/Contracts.java | 52 +++--
.../binding/linky/internal/dto/Customer.java | 68 -------
.../linky/internal/dto/CustomerReponse.java | 2 +-
.../binding/linky/internal/dto/PrmInfo.java | 4 +-
.../linky/internal/dto/UsagePoint.java | 2 +-
.../internal/handler/ApiBridgeHandler.java | 180 ++++-------------
.../internal/handler/EnedisBridgeHandler.java | 1 +
.../handler/EnedisWebBridgeHandler.java | 42 ++--
.../internal/handler/LinkyBridgeHandler.java | 186 ++++++++++++++++++
.../linky/internal/handler/LinkyHandler.java | 4 +-
.../MyElectricalDataBridgeHandler.java | 1 +
13 files changed, 389 insertions(+), 338 deletions(-)
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContractDetails.java
delete mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
create mode 100644 bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
index b3c434abaf1..8264bbdd813 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/api/EnedisHttpApi.java
@@ -32,7 +32,7 @@ import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AddressInfo;
import org.openhab.binding.linky.internal.dto.ConsumptionReport;
import org.openhab.binding.linky.internal.dto.ContactInfo;
-import org.openhab.binding.linky.internal.dto.Customer;
+import org.openhab.binding.linky.internal.dto.Contracts;
import org.openhab.binding.linky.internal.dto.CustomerIdResponse;
import org.openhab.binding.linky.internal.dto.CustomerReponse;
import org.openhab.binding.linky.internal.dto.IdentityInfo;
@@ -41,10 +41,9 @@ import org.openhab.binding.linky.internal.dto.MeterResponse;
import org.openhab.binding.linky.internal.dto.PrmInfo;
import org.openhab.binding.linky.internal.dto.TempoResponse;
import org.openhab.binding.linky.internal.dto.UsagePoint;
-import org.openhab.binding.linky.internal.dto.WebPrmInfo;
import org.openhab.binding.linky.internal.dto.WebUserInfo;
-import org.openhab.binding.linky.internal.handler.ApiBridgeHandler;
import org.openhab.binding.linky.internal.handler.EnedisWebBridgeHandler;
+import org.openhab.binding.linky.internal.handler.LinkyBridgeHandler;
import org.openhab.binding.linky.internal.handler.LinkyHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,12 +63,12 @@ public class EnedisHttpApi {
private final Logger logger = LoggerFactory.getLogger(EnedisHttpApi.class);
private final Gson gson;
private final HttpClient httpClient;
- private ApiBridgeHandler apiBridgeHandler;
+ private LinkyBridgeHandler linkyBridgeHandler;
- public EnedisHttpApi(ApiBridgeHandler apiBridgeHandler, Gson gson, HttpClient httpClient) {
+ public EnedisHttpApi(LinkyBridgeHandler linkyBridgeHandler, Gson gson, HttpClient httpClient) {
this.gson = gson;
this.httpClient = httpClient;
- this.apiBridgeHandler = apiBridgeHandler;
+ this.linkyBridgeHandler = linkyBridgeHandler;
}
public FormContentProvider getFormContent(String fieldName, String fieldValue) {
@@ -90,15 +89,15 @@ public class EnedisHttpApi {
}
public String getData(LinkyHandler handler, String url) throws LinkyException {
- return getData(apiBridgeHandler, url, httpClient, apiBridgeHandler.getToken(handler));
+ return getData(linkyBridgeHandler, url, httpClient, linkyBridgeHandler.getToken(handler));
}
public String getData(String url) throws LinkyException {
- return getData(apiBridgeHandler, url, httpClient, "");
+ return getData(linkyBridgeHandler, url, httpClient, "");
}
- private static String getData(ApiBridgeHandler apiBridgeHandler, String url, HttpClient httpClient, String token)
- throws LinkyException {
+ private static String getData(LinkyBridgeHandler linkyBridgeHandler, String url, HttpClient httpClient,
+ String token) throws LinkyException {
try {
Request request = httpClient.newRequest(url);
request = request.method(HttpMethod.GET);
@@ -110,7 +109,7 @@ public class EnedisHttpApi {
ContentResponse result = request.send();
if (result.getStatus() == 307) {
String loc = result.getHeaders().get("Location");
- String newUrl = apiBridgeHandler.getBaseUrl() + loc.substring(1);
+ String newUrl = linkyBridgeHandler.getBaseUrl() + loc.substring(1);
request = httpClient.newRequest(newUrl);
request = request.method(HttpMethod.GET);
result = request.send();
@@ -133,8 +132,8 @@ public class EnedisHttpApi {
}
public PrmInfo getPrmInfo(LinkyHandler handler, String prmId) throws LinkyException {
- if (!apiBridgeHandler.isConnected()) {
- apiBridgeHandler.initialize();
+ if (!linkyBridgeHandler.isConnected()) {
+ linkyBridgeHandler.initialize();
}
PrmInfo result = new PrmInfo();
@@ -143,12 +142,16 @@ public class EnedisHttpApi {
result.contractInfo.subscribedPower = "Na";
try {
- Customer customer = getContract(handler, prmId);
- UsagePoint usagePoint = customer.usagePoints[0];
+ Contracts contract = getContract(handler, prmId);
+ UsagePoint usagePoint = contract.usagePoints[0];
+
+ AddressInfo addressInfo = getAddress(handler, prmId);
+ if (addressInfo != null) {
+ usagePoint.usagePoint.usagePointAddresses = addressInfo;
+ }
result.contractInfo = usagePoint.contracts;
result.usagePointInfo = usagePoint.usagePoint;
-
result.identityInfo = getIdentity(handler, prmId);
result.contactInfo = getContact(handler, prmId);
@@ -164,54 +167,26 @@ public class EnedisHttpApi {
return apiUrl.formatted(prmId);
}
- public Customer getContract(LinkyHandler handler, String prmId) throws LinkyException {
- if (!apiBridgeHandler.isConnected()) {
- apiBridgeHandler.initialize();
+ public Contracts getContract(LinkyHandler handler, String prmId) throws LinkyException {
+ if (!linkyBridgeHandler.isConnected()) {
+ linkyBridgeHandler.initialize();
}
- String contractUrl = apiBridgeHandler.getContractUrl();
+ String contractUrl = linkyBridgeHandler.getContractUrl();
String data = getData(handler, formatUrl(contractUrl, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", contractUrl);
}
- try {
- if (data.startsWith("[{\"adresse\"")) {
- try {
- WebPrmInfo[] webPrmsInfo = gson.fromJson(data, WebPrmInfo[].class);
- if (webPrmsInfo == null || webPrmsInfo.length < 1) {
- throw new LinkyException("Invalid prms data received");
- }
- return Customer.fromWebPrmInfos(webPrmsInfo, prmId);
- } catch (JsonSyntaxException e) {
- logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", contractUrl);
- }
- } else {
- CustomerReponse cResponse = gson.fromJson(data, CustomerReponse.class);
- if (cResponse == null) {
- throw new LinkyException("Invalid customer data received");
- }
-
- AddressInfo addressInfo = getAddress(handler, prmId);
- if (addressInfo != null) {
- cResponse.customer.usagePoints[0].usagePoint.usagePointAddresses = addressInfo;
- }
-
- return cResponse.customer;
- }
- } catch (JsonSyntaxException e) {
- logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", contractUrl);
- }
+ return linkyBridgeHandler.decodeCustomerResponse(data, prmId);
}
@Nullable
public AddressInfo getAddress(LinkyHandler handler, String prmId) throws LinkyException {
- if (!apiBridgeHandler.isConnected()) {
- apiBridgeHandler.initialize();
+ if (!linkyBridgeHandler.isConnected()) {
+ linkyBridgeHandler.initialize();
}
- String addressUrl = apiBridgeHandler.getAddressUrl();
+ String addressUrl = linkyBridgeHandler.getAddressUrl();
if (addressUrl.isEmpty()) {
return null;
@@ -234,41 +209,23 @@ public class EnedisHttpApi {
}
public IdentityInfo getIdentity(LinkyHandler handler, String prmId) throws LinkyException {
- if (!apiBridgeHandler.isConnected()) {
- apiBridgeHandler.initialize();
+ if (!linkyBridgeHandler.isConnected()) {
+ linkyBridgeHandler.initialize();
}
- String identityUrl = apiBridgeHandler.getIdentityUrl();
+ String identityUrl = linkyBridgeHandler.getIdentityUrl();
String data = getData(handler, formatUrl(identityUrl, prmId));
if (data.isEmpty()) {
throw new LinkyException("Requesting '%s' returned an empty response", identityUrl);
}
- try {
- if (data.contains("av2_interne_id")) {
- try {
- WebUserInfo webUserInfo = gson.fromJson(data, WebUserInfo.class);
- return IdentityInfo.fromWebUserInfo(webUserInfo);
- } catch (JsonSyntaxException e) {
- logger.debug("invalid JSON response not matching UserInfo.class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", identityUrl);
- }
- } else {
- CustomerIdResponse iResponse = gson.fromJson(data, CustomerIdResponse.class);
- if (iResponse == null) {
- throw new LinkyException("Invalid customer data received");
- }
- return iResponse.identity.naturalPerson;
- }
- } catch (JsonSyntaxException e) {
- logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
- throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response", identityUrl);
- }
+
+ return linkyBridgeHandler.decodeIdentityResponse(data, prmId);
}
public ContactInfo getContact(LinkyHandler handler, String prmId) throws LinkyException {
- if (!apiBridgeHandler.isConnected()) {
- apiBridgeHandler.initialize();
+ if (!linkyBridgeHandler.isConnected()) {
+ linkyBridgeHandler.initialize();
}
- String contactUrl = apiBridgeHandler.getContactUrl();
+ String contactUrl = linkyBridgeHandler.getContactUrl();
String data = getData(handler, formatUrl(contactUrl, prmId));
if (data.isEmpty()) {
@@ -298,12 +255,12 @@ public class EnedisHttpApi {
private MeterReading getMeasures(LinkyHandler handler, String apiUrl, String prmId, LocalDate from, LocalDate to)
throws LinkyException {
- String dtStart = from.format(apiBridgeHandler.getApiDateFormat());
- String dtEnd = to.format(apiBridgeHandler.getApiDateFormat());
+ String dtStart = from.format(linkyBridgeHandler.getApiDateFormat());
+ String dtEnd = to.format(linkyBridgeHandler.getApiDateFormat());
String url = String.format(apiUrl, prmId, dtStart, dtEnd);
- if (!apiBridgeHandler.isConnected()) {
- apiBridgeHandler.initialize();
+ if (!linkyBridgeHandler.isConnected()) {
+ linkyBridgeHandler.initialize();
}
String data = getData(handler, url);
@@ -338,31 +295,31 @@ public class EnedisHttpApi {
public MeterReading getEnergyData(LinkyHandler handler, String prmId, LocalDate from, LocalDate to)
throws LinkyException {
- return getMeasures(handler, apiBridgeHandler.getDailyConsumptionUrl(), prmId, from, to);
+ return getMeasures(handler, linkyBridgeHandler.getDailyConsumptionUrl(), prmId, from, to);
}
public MeterReading getLoadCurveData(LinkyHandler handler, String prmId, LocalDate from, LocalDate to)
throws LinkyException {
- return getMeasures(handler, apiBridgeHandler.getLoadCurveUrl(), prmId, from, to);
+ return getMeasures(handler, linkyBridgeHandler.getLoadCurveUrl(), prmId, from, to);
}
public MeterReading getPowerData(LinkyHandler handler, String prmId, LocalDate from, LocalDate to)
throws LinkyException {
- return getMeasures(handler, apiBridgeHandler.getMaxPowerUrl(), prmId, from, to);
+ return getMeasures(handler, linkyBridgeHandler.getMaxPowerUrl(), prmId, from, to);
}
public TempoResponse getTempoData(LinkyHandler handler, LocalDate from, LocalDate to) throws LinkyException {
- String dtStart = from.format(apiBridgeHandler.getApiDateFormatYearsFirst());
- String dtEnd = to.format(apiBridgeHandler.getApiDateFormatYearsFirst());
+ String dtStart = from.format(linkyBridgeHandler.getApiDateFormatYearsFirst());
+ String dtEnd = to.format(linkyBridgeHandler.getApiDateFormatYearsFirst());
- String url = String.format(apiBridgeHandler.getTempoUrl(), dtStart, dtEnd);
+ String url = String.format(linkyBridgeHandler.getTempoUrl(), dtStart, dtEnd);
if (url.isEmpty()) {
return new TempoResponse();
}
- if (!apiBridgeHandler.isConnected()) {
- apiBridgeHandler.initialize();
+ if (!linkyBridgeHandler.isConnected()) {
+ linkyBridgeHandler.initialize();
}
String data = getData(handler, url);
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContractDetails.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContractDetails.java
new file mode 100644
index 00000000000..91cd6840748
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/ContractDetails.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.dto;
+
+import org.eclipse.jetty.jaas.spi.UserInfo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link UserInfo} holds informations about energy delivery point
+ *
+ * @author Laurent Arnal - Initial contribution
+ */
+
+public class ContractDetails {
+ public String segment;
+
+ @SerializedName("subscribed_power")
+ public String subscribedPower;
+
+ @SerializedName("last_activation_date")
+ public String lastActivationDate;
+
+ @SerializedName("distribution_tariff")
+ public String distributionTariff;
+
+ @SerializedName("offpeak_hours")
+ public String offpeakHours;
+
+ @SerializedName("contract_status")
+ public String contractStatus;
+
+ @SerializedName("contract_type")
+ public String contractType;
+
+ @SerializedName("last_distribution_tariff_change_date")
+ public String lastDistributionTariffChangeDate;
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
index 2a04cbe0618..47aa9d13890 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Contracts.java
@@ -12,6 +12,8 @@
*/
package org.openhab.binding.linky.internal.dto;
+import java.util.Arrays;
+
import org.eclipse.jetty.jaas.spi.UserInfo;
import com.google.gson.annotations.SerializedName;
@@ -19,30 +21,48 @@ import com.google.gson.annotations.SerializedName;
/**
* The {@link UserInfo} holds informations about energy delivery point
*
- * @author Laurent Arnal - Initial contribution
+ * @author Gaël L'hopital - Initial contribution
+ * @author Laurent Arnal - Rewrite addon to use official dataconect API
*/
public class Contracts {
- public String segment;
+ @SerializedName("customer_id")
+ public String customerId;
- @SerializedName("subscribed_power")
- public String subscribedPower;
+ @SerializedName("usage_points")
+ public UsagePoint[] usagePoints;
- @SerializedName("last_activation_date")
- public String lastActivationDate;
+ public static Contracts fromWebPrmInfos(WebPrmInfo[] webPrmsInfo, String prmId) {
+ Contracts result = new Contracts();
- @SerializedName("distribution_tariff")
- public String distributionTariff;
+ WebPrmInfo webPrmInfo = Arrays.stream(webPrmsInfo).filter(x -> x.prmId.equals(prmId)).findAny().orElseThrow();
- @SerializedName("offpeak_hours")
- public String offpeakHours;
+ result.usagePoints = new UsagePoint[1];
+ result.usagePoints[0] = new UsagePoint();
- @SerializedName("contract_status")
- public String contractStatus;
+ result.usagePoints[0].usagePoint = new UsagePointDetails();
+ result.usagePoints[0].usagePoint.meterType = "";
+ result.usagePoints[0].usagePoint.usagePointId = "";
+ result.usagePoints[0].usagePoint.usagePointStatus = "";
- @SerializedName("contract_type")
- public String contractType;
+ result.usagePoints[0].usagePoint.usagePointAddresses = new AddressInfo();
+ result.usagePoints[0].usagePoint.usagePointAddresses.city = webPrmInfo.adresse.adresseLigneSix;
+ result.usagePoints[0].usagePoint.usagePointAddresses.country = webPrmInfo.adresse.adresseLigneSept;
+ result.usagePoints[0].usagePoint.usagePointAddresses.inseeCode = "";
+ result.usagePoints[0].usagePoint.usagePointAddresses.locality = "";
+ result.usagePoints[0].usagePoint.usagePointAddresses.postalCode = webPrmInfo.adresse.adresseLigneSix;
+ result.usagePoints[0].usagePoint.usagePointAddresses.street = webPrmInfo.adresse.adresseLigneQuatre;
- @SerializedName("last_distribution_tariff_change_date")
- public String lastDistributionTariffChangeDate;
+ result.usagePoints[0].contracts = new ContractDetails();
+ result.usagePoints[0].contracts.contractStatus = "";
+ result.usagePoints[0].contracts.contractType = "";
+ result.usagePoints[0].contracts.distributionTariff = "";
+ result.usagePoints[0].contracts.lastActivationDate = "";
+ result.usagePoints[0].contracts.lastDistributionTariffChangeDate = "";
+ result.usagePoints[0].contracts.offpeakHours = "";
+ result.usagePoints[0].contracts.segment = webPrmInfo.segment;
+ result.usagePoints[0].contracts.subscribedPower = "" + webPrmInfo.puissanceSouscrite;
+
+ return result;
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
deleted file mode 100644
index b79a551d877..00000000000
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/Customer.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * Copyright (c) 2010-2024 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.linky.internal.dto;
-
-import java.util.Arrays;
-
-import org.eclipse.jetty.jaas.spi.UserInfo;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link UserInfo} holds informations about energy delivery point
- *
- * @author Gaël L'hopital - Initial contribution
- * @author Laurent Arnal - Rewrite addon to use official dataconect API
- */
-
-public class Customer {
- @SerializedName("customer_id")
- public String customerId;
-
- @SerializedName("usage_points")
- public UsagePoint[] usagePoints;
-
- public static Customer fromWebPrmInfos(WebPrmInfo[] webPrmsInfo, String prmId) {
- Customer result = new Customer();
-
- WebPrmInfo webPrmInfo = Arrays.stream(webPrmsInfo).filter(x -> x.prmId.equals(prmId)).findAny().orElseThrow();
-
- result.usagePoints = new UsagePoint[1];
- result.usagePoints[0] = new UsagePoint();
-
- result.usagePoints[0].usagePoint = new UsagePointDetails();
- result.usagePoints[0].usagePoint.meterType = "";
- result.usagePoints[0].usagePoint.usagePointId = "";
- result.usagePoints[0].usagePoint.usagePointStatus = "";
-
- result.usagePoints[0].usagePoint.usagePointAddresses = new AddressInfo();
- result.usagePoints[0].usagePoint.usagePointAddresses.city = webPrmInfo.adresse.adresseLigneSix;
- result.usagePoints[0].usagePoint.usagePointAddresses.country = webPrmInfo.adresse.adresseLigneSept;
- result.usagePoints[0].usagePoint.usagePointAddresses.inseeCode = "";
- result.usagePoints[0].usagePoint.usagePointAddresses.locality = "";
- result.usagePoints[0].usagePoint.usagePointAddresses.postalCode = webPrmInfo.adresse.adresseLigneSix;
- result.usagePoints[0].usagePoint.usagePointAddresses.street = webPrmInfo.adresse.adresseLigneQuatre;
-
- result.usagePoints[0].contracts = new Contracts();
- result.usagePoints[0].contracts.contractStatus = "";
- result.usagePoints[0].contracts.contractType = "";
- result.usagePoints[0].contracts.distributionTariff = "";
- result.usagePoints[0].contracts.lastActivationDate = "";
- result.usagePoints[0].contracts.lastDistributionTariffChangeDate = "";
- result.usagePoints[0].contracts.offpeakHours = "";
- result.usagePoints[0].contracts.segment = webPrmInfo.segment;
- result.usagePoints[0].contracts.subscribedPower = "" + webPrmInfo.puissanceSouscrite;
-
- return result;
- }
-}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerReponse.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerReponse.java
index 1215134483b..e940fff4660 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerReponse.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/CustomerReponse.java
@@ -22,5 +22,5 @@ import org.eclipse.jetty.jaas.spi.UserInfo;
*/
public class CustomerReponse {
- public Customer customer;
+ public Contracts customer;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java
index aec269a4137..d2413001cf3 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/PrmInfo.java
@@ -25,7 +25,7 @@ public class PrmInfo {
public PrmInfo() {
customerId = "";
- contractInfo = new Contracts();
+ contractInfo = new ContractDetails();
identityInfo = new IdentityInfo();
addressInfo = new AddressInfo();
contactInfo = new ContactInfo();
@@ -35,7 +35,7 @@ public class PrmInfo {
public String prmId;
public String customerId;
- public Contracts contractInfo;
+ public ContractDetails contractInfo;
public UsagePointDetails usagePointInfo;
public ContactInfo contactInfo;
public AddressInfo addressInfo;
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
index 235b6cd965e..6cafa867629 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/UsagePoint.java
@@ -26,5 +26,5 @@ import com.google.gson.annotations.SerializedName;
public class UsagePoint {
@SerializedName("usage_point")
public UsagePointDetails usagePoint;
- public Contracts contracts;
+ public ContractDetails contracts;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index 9257e84d282..b64c2a39386 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -17,28 +17,23 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
-import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.openhab.binding.linky.internal.LinkyAuthServlet;
import org.openhab.binding.linky.internal.LinkyBindingConstants;
-import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
-import org.openhab.binding.linky.internal.api.EnedisHttpApi;
+import org.openhab.binding.linky.internal.dto.Contracts;
+import org.openhab.binding.linky.internal.dto.CustomerIdResponse;
+import org.openhab.binding.linky.internal.dto.CustomerReponse;
+import org.openhab.binding.linky.internal.dto.IdentityInfo;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthException;
@@ -46,16 +41,10 @@ import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.io.net.http.HttpClientFactory;
-import org.openhab.core.io.net.http.TrustAllTrustManager;
import org.openhab.core.thing.Bridge;
-import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.binding.BaseBridgeHandler;
-import org.openhab.core.types.Command;
-import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.HttpService;
@@ -64,6 +53,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
/**
* {@link ApiBridgeHandler} is the base handler to access enedis data.
@@ -71,65 +61,21 @@ import com.google.gson.Gson;
* @author Laurent Arnal - Initial contribution
*/
@NonNullByDefault
-public abstract class ApiBridgeHandler extends BaseBridgeHandler {
+public abstract class ApiBridgeHandler extends LinkyBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(ApiBridgeHandler.class);
- private static final int REQUEST_BUFFER_SIZE = 8000;
-
- private HttpService httpService;
- private BundleContext bundleContext;
-
- protected final HttpClient httpClient;
- protected EnedisHttpApi enedisApi;
private OAuthClientService oAuthService;
- protected final ThingRegistry thingRegistry;
private static @Nullable HttpServlet servlet;
- protected LinkyConfiguration config;
private static final String TEMPLATE_PATH = "templates/";
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
- protected final Gson gson;
-
- protected boolean connected = false;
-
public ApiBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
- super(bridge);
- SslContextFactory sslContextFactory = new SslContextFactory.Client();
- try {
- SSLContext sslContext = SSLContext.getInstance("SSL");
- sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
- sslContextFactory.setSslContext(sslContext);
-
- } catch (NoSuchAlgorithmException e) {
- logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
- e);
- } catch (KeyManagementException e) {
- logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
- }
-
- this.gson = gson;
- this.httpService = httpService;
- this.thingRegistry = thingRegistry;
- this.bundleContext = componentContext.getBundleContext();
-
- this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
- this.httpClient.setFollowRedirects(false);
- this.httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
-
- try {
- httpClient.start();
- } catch (Exception e) {
- logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
- }
-
- this.enedisApi = new EnedisHttpApi(this, gson, this.httpClient);
-
- config = getConfigAs(LinkyConfiguration.class);
+ super(bridge, httpClientFactory, oAuthFactory, httpService, thingRegistry, componentContext, gson);
String tokenUrl = "";
String authorizeUrl = "";
@@ -139,7 +85,6 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
} else if (this instanceof EnedisBridgeHandler) {
tokenUrl = LinkyBindingConstants.ENEDIS_API_TOKEN_URL_PREPROD;
authorizeUrl = LinkyBindingConstants.ENEDIS_AUTHORIZE_URL_PREPROD;
-
}
this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID, tokenUrl,
@@ -150,72 +95,10 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
updateStatus(ThingStatus.UNKNOWN);
}
- public @Nullable EnedisHttpApi getEnedisApi() {
- return enedisApi;
- }
-
public abstract String getClientId();
public abstract String getClientSecret();
- @Override
- public void initialize() {
- logger.debug("Initializing Linky API bridge handler.");
-
- updateStatus(ThingStatus.UNKNOWN);
-
- scheduler.submit(() -> {
- try {
- connectionInit();
- updateStatus(ThingStatus.ONLINE);
- } catch (LinkyException e) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
- }
- });
- }
-
- protected abstract void connectionInit() throws LinkyException;
-
- public boolean isConnected() {
- return connected;
- }
-
- public void disconnect() {
- if (connected) {
- logger.debug("Logout process");
- connected = false;
- httpClient.getCookieStore().removeAll();
- }
- }
-
- @Override
- public void dispose() {
- logger.debug("Shutting down Netatmo API bridge handler.");
- disconnect();
-
- httpService.unregister(LinkyBindingConstants.LINKY_ALIAS);
- httpService.unregister(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS);
-
- super.dispose();
- }
-
- /*
- * @Override
- * protected void deactivate(ComponentContext componentContext) {
- * super.deactivate(componentContext);
- * try {
- * httpClient.stop();
- * } catch (Exception e) {
- * logger.warn("Unable to stop Jetty HttpClient {}", e.getMessage());
- * }
- * }
- */
-
- @Override
- public void handleCommand(ChannelUID channelUID, Command command) {
- // TODO Auto-generated method stub
- }
-
private void registerServlet() {
try {
if (servlet == null) {
@@ -291,8 +174,6 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
&& accessTokenResponse.getRefreshToken() != null;
}
- public abstract String getToken(LinkyHandler handler) throws LinkyException;
-
protected @Nullable AccessTokenResponse getAccessTokenByClientCredentials() {
try {
return oAuthService.getAccessTokenByClientCredentials(LinkyBindingConstants.LINKY_SCOPES);
@@ -339,27 +220,32 @@ public abstract class ApiBridgeHandler extends BaseBridgeHandler {
return result;
}
- public abstract double getDivider();
+ @Override
+ public Contracts decodeCustomerResponse(String data, String prmId) throws LinkyException {
+ try {
+ CustomerReponse cResponse = gson.fromJson(data, CustomerReponse.class);
+ if (cResponse == null) {
+ throw new LinkyException("Invalid customer data received");
+ }
+ return cResponse.customer;
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching CustomerReponse.class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response");
+ }
+ }
- public abstract String getBaseUrl();
+ @Override
+ public IdentityInfo decodeIdentityResponse(String data, String prmId) throws LinkyException {
+ try {
+ CustomerIdResponse iResponse = gson.fromJson(data, CustomerIdResponse.class);
+ if (iResponse == null) {
+ throw new LinkyException("Invalid customer data received");
+ }
+ return iResponse.identity.naturalPerson;
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching CustomerIdResponse.class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response");
+ }
+ }
- public abstract String getContactUrl();
-
- public abstract String getContractUrl();
-
- public abstract String getIdentityUrl();
-
- public abstract String getAddressUrl();
-
- public abstract String getDailyConsumptionUrl();
-
- public abstract String getMaxPowerUrl();
-
- public abstract String getLoadCurveUrl();
-
- public abstract String getTempoUrl();
-
- public abstract DateTimeFormatter getApiDateFormat();
-
- public abstract DateTimeFormatter getApiDateFormatYearsFirst();
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index 0d3eb7a1ff3..6ac0afd3f6d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -160,4 +160,5 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
public DateTimeFormatter getApiDateFormatYearsFirst() {
return API_DATE_FORMAT_YEAR_FIRST;
}
+
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
index e44d24a6f25..289868fa687 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisWebBridgeHandler.java
@@ -31,6 +31,10 @@ import org.openhab.binding.linky.internal.LinkyConfiguration;
import org.openhab.binding.linky.internal.LinkyException;
import org.openhab.binding.linky.internal.dto.AuthData;
import org.openhab.binding.linky.internal.dto.AuthResult;
+import org.openhab.binding.linky.internal.dto.Contracts;
+import org.openhab.binding.linky.internal.dto.IdentityInfo;
+import org.openhab.binding.linky.internal.dto.WebPrmInfo;
+import org.openhab.binding.linky.internal.dto.WebUserInfo;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
@@ -51,7 +55,7 @@ import com.google.gson.JsonSyntaxException;
*
*/
@NonNullByDefault
-public class EnedisWebBridgeHandler extends ApiBridgeHandler {
+public class EnedisWebBridgeHandler extends LinkyBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(EnedisWebBridgeHandler.class);
public static final String ENEDIS_DOMAIN = ".enedis.fr";
@@ -96,16 +100,6 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
super.initialize();
}
- @Override
- public String getClientId() {
- return config.clientId;
- }
-
- @Override
- public String getClientSecret() {
- return config.clientSecret;
- }
-
@Override
public String getToken(LinkyHandler handler) throws LinkyException {
return "";
@@ -254,4 +248,30 @@ public class EnedisWebBridgeHandler extends ApiBridgeHandler {
throw new LinkyException(e, "Error opening connection with Enedis webservice");
}
}
+
+ @Override
+ public Contracts decodeCustomerResponse(String data, String prmId) throws LinkyException {
+ try {
+ WebPrmInfo[] webPrmsInfo = gson.fromJson(data, WebPrmInfo[].class);
+ if (webPrmsInfo == null || webPrmsInfo.length < 1) {
+ throw new LinkyException("Invalid prms data received");
+ }
+ return Contracts.fromWebPrmInfos(webPrmsInfo, prmId);
+
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching PrmInfo[].class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response");
+ }
+ }
+
+ @Override
+ public IdentityInfo decodeIdentityResponse(String data, String prmId) throws LinkyException {
+ try {
+ WebUserInfo webUserInfo = gson.fromJson(data, WebUserInfo.class);
+ return IdentityInfo.fromWebUserInfo(webUserInfo);
+ } catch (JsonSyntaxException e) {
+ logger.debug("invalid JSON response not matching UserInfo.class: {}", data);
+ throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response");
+ }
+ }
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
new file mode 100644
index 00000000000..3e250f53dfd
--- /dev/null
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
@@ -0,0 +1,186 @@
+package org.openhab.binding.linky.internal.handler;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.time.format.DateTimeFormatter;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.openhab.binding.linky.internal.LinkyBindingConstants;
+import org.openhab.binding.linky.internal.LinkyConfiguration;
+import org.openhab.binding.linky.internal.LinkyException;
+import org.openhab.binding.linky.internal.api.EnedisHttpApi;
+import org.openhab.binding.linky.internal.dto.Contracts;
+import org.openhab.binding.linky.internal.dto.IdentityInfo;
+import org.openhab.core.auth.client.oauth2.OAuthFactory;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.io.net.http.TrustAllTrustManager;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseBridgeHandler;
+import org.openhab.core.types.Command;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+@NonNullByDefault
+public abstract class LinkyBridgeHandler extends BaseBridgeHandler {
+ private final Logger logger = LoggerFactory.getLogger(LinkyBridgeHandler.class);
+
+ protected HttpService httpService;
+ protected BundleContext bundleContext;
+ protected final HttpClient httpClient;
+ protected EnedisHttpApi enedisApi;
+ protected final ThingRegistry thingRegistry;
+
+ protected LinkyConfiguration config;
+ protected final Gson gson;
+ protected boolean connected = false;
+
+ private static final int REQUEST_BUFFER_SIZE = 8000;
+
+ public LinkyBridgeHandler(Bridge bridge, final @Reference HttpClientFactory httpClientFactory,
+ final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
+ final @Reference ThingRegistry thingRegistry, ComponentContext componentContext, Gson gson) {
+ super(bridge);
+
+ SslContextFactory sslContextFactory = new SslContextFactory.Client();
+ try {
+ SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
+ sslContextFactory.setSslContext(sslContext);
+
+ } catch (NoSuchAlgorithmException e) {
+ logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
+ e);
+ } catch (KeyManagementException e) {
+ logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
+ }
+
+ this.gson = gson;
+ this.httpService = httpService;
+ this.thingRegistry = thingRegistry;
+ this.bundleContext = componentContext.getBundleContext();
+
+ this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
+ this.httpClient.setFollowRedirects(false);
+ this.httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
+
+ try {
+ httpClient.start();
+ } catch (Exception e) {
+ logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
+ }
+
+ this.enedisApi = new EnedisHttpApi(this, gson, this.httpClient);
+
+ config = getConfigAs(LinkyConfiguration.class);
+
+ String tokenUrl = "";
+ String authorizeUrl = "";
+ if (this instanceof MyElectricalDataBridgeHandler) {
+ tokenUrl = LinkyBindingConstants.LINKY_MYELECTRICALDATA_API_TOKEN_URL;
+ authorizeUrl = LinkyBindingConstants.LINKY_MYELECTRICALDATA_AUTHORIZE_URL;
+ } else if (this instanceof EnedisBridgeHandler) {
+ tokenUrl = LinkyBindingConstants.ENEDIS_API_TOKEN_URL_PREPROD;
+ authorizeUrl = LinkyBindingConstants.ENEDIS_AUTHORIZE_URL_PREPROD;
+
+ }
+
+ updateStatus(ThingStatus.UNKNOWN);
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("Initializing Linky API bridge handler.");
+
+ updateStatus(ThingStatus.UNKNOWN);
+
+ scheduler.submit(() -> {
+ try {
+ connectionInit();
+ updateStatus(ThingStatus.ONLINE);
+ } catch (LinkyException e) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+ }
+ });
+ }
+
+ protected abstract void connectionInit() throws LinkyException;
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ public void disconnect() {
+ if (connected) {
+ logger.debug("Logout process");
+ connected = false;
+ httpClient.getCookieStore().removeAll();
+ }
+ }
+
+ public @Nullable EnedisHttpApi getEnedisApi() {
+ return enedisApi;
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Shutting down Linky API bridge handler.");
+ disconnect();
+
+ httpService.unregister(LinkyBindingConstants.LINKY_ALIAS);
+ httpService.unregister(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS);
+
+ super.dispose();
+ }
+
+ public abstract String getToken(LinkyHandler handler) throws LinkyException;
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ // TODO Auto-generated method stub
+ }
+
+ public abstract double getDivider();
+
+ public abstract String getBaseUrl();
+
+ public abstract String getContactUrl();
+
+ public abstract String getContractUrl();
+
+ public abstract String getIdentityUrl();
+
+ public abstract String getAddressUrl();
+
+ public abstract String getDailyConsumptionUrl();
+
+ public abstract String getMaxPowerUrl();
+
+ public abstract String getLoadCurveUrl();
+
+ public abstract String getTempoUrl();
+
+ public abstract DateTimeFormatter getApiDateFormat();
+
+ public abstract DateTimeFormatter getApiDateFormatYearsFirst();
+
+ public abstract Contracts decodeCustomerResponse(String data, String prmId) throws LinkyException;
+
+ public abstract IdentityInfo decodeIdentityResponse(String data, String prmId) throws LinkyException;
+
+}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index a655d4559e3..28d8e53bd00 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -162,7 +162,7 @@ public class LinkyHandler extends BaseThingHandler {
return;
}
- ApiBridgeHandler bridgeHandler = (ApiBridgeHandler) bridge.getHandler();
+ LinkyBridgeHandler bridgeHandler = (LinkyBridgeHandler) bridge.getHandler();
if (bridgeHandler == null) {
return;
}
@@ -588,7 +588,7 @@ public class LinkyHandler extends BaseThingHandler {
private boolean isConnected() {
Bridge bridge = getBridge();
if (bridge != null) {
- ApiBridgeHandler bridgeHandler = (ApiBridgeHandler) bridge.getHandler();
+ LinkyBridgeHandler bridgeHandler = (LinkyBridgeHandler) bridge.getHandler();
if (bridgeHandler == null) {
return false;
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index 0b54d74d775..d912d26ab4b 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -201,4 +201,5 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
public DateTimeFormatter getApiDateFormatYearsFirst() {
return API_DATE_FORMAT_YEAR_FIRST;
}
+
}
From 6cff83e42e4747bd52e4f944a81362687c2046f9 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 11:55:51 +0200
Subject: [PATCH 064/135] mvn spotless:apply
Signed-off-by: Laurent ARNAL
---
.../openhab/binding/linky/internal/handler/ApiBridgeHandler.java | 1 -
.../binding/linky/internal/handler/EnedisBridgeHandler.java | 1 -
.../binding/linky/internal/handler/LinkyBridgeHandler.java | 1 -
.../linky/internal/handler/MyElectricalDataBridgeHandler.java | 1 -
4 files changed, 4 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
index b64c2a39386..d0f86a80c4d 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/ApiBridgeHandler.java
@@ -247,5 +247,4 @@ public abstract class ApiBridgeHandler extends LinkyBridgeHandler {
throw new LinkyException(e, "Requesting '%s' returned an invalid JSON response");
}
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
index 6ac0afd3f6d..0d3eb7a1ff3 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/EnedisBridgeHandler.java
@@ -160,5 +160,4 @@ public class EnedisBridgeHandler extends ApiBridgeHandler {
public DateTimeFormatter getApiDateFormatYearsFirst() {
return API_DATE_FORMAT_YEAR_FIRST;
}
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
index 3e250f53dfd..62de854d9d5 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
@@ -182,5 +182,4 @@ public abstract class LinkyBridgeHandler extends BaseBridgeHandler {
public abstract Contracts decodeCustomerResponse(String data, String prmId) throws LinkyException;
public abstract IdentityInfo decodeIdentityResponse(String data, String prmId) throws LinkyException;
-
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
index d912d26ab4b..0b54d74d775 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/MyElectricalDataBridgeHandler.java
@@ -201,5 +201,4 @@ public class MyElectricalDataBridgeHandler extends ApiBridgeHandler {
public DateTimeFormatter getApiDateFormatYearsFirst() {
return API_DATE_FORMAT_YEAR_FIRST;
}
-
}
From bda53f3dcf4e766f1a76c86161ac82e3ffa27270 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 11:57:28 +0200
Subject: [PATCH 065/135] code cleanup
Signed-off-by: Laurent ARNAL
---
.../linky/internal/handler/LinkyBridgeHandler.java | 11 -----------
.../binding/linky/internal/handler/LinkyHandler.java | 1 -
2 files changed, 12 deletions(-)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
index 62de854d9d5..d1938129595 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
@@ -89,17 +89,6 @@ public abstract class LinkyBridgeHandler extends BaseBridgeHandler {
config = getConfigAs(LinkyConfiguration.class);
- String tokenUrl = "";
- String authorizeUrl = "";
- if (this instanceof MyElectricalDataBridgeHandler) {
- tokenUrl = LinkyBindingConstants.LINKY_MYELECTRICALDATA_API_TOKEN_URL;
- authorizeUrl = LinkyBindingConstants.LINKY_MYELECTRICALDATA_AUTHORIZE_URL;
- } else if (this instanceof EnedisBridgeHandler) {
- tokenUrl = LinkyBindingConstants.ENEDIS_API_TOKEN_URL_PREPROD;
- authorizeUrl = LinkyBindingConstants.ENEDIS_AUTHORIZE_URL_PREPROD;
-
- }
-
updateStatus(ThingStatus.UNKNOWN);
}
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
index 28d8e53bd00..084e75ea77e 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyHandler.java
@@ -401,7 +401,6 @@ public class LinkyHandler extends BaseThingHandler {
*/
private synchronized void updateLoadCurveData() {
loadCurveConsumption.getValue().ifPresentOrElse(values -> {
- int dSize = values.dayValue.length;
updatePowerTimeSeries(LOAD_CURVE, values.dayValue);
}, () -> {
});
From 4acbd828ce923c46a3b4adc80c22b1fd4c7392bb Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 12:06:10 +0200
Subject: [PATCH 066/135] fix missing headers
Signed-off-by: Laurent ARNAL
---
.../internal/handler/LinkyBridgeHandler.java | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
index d1938129595..9f2fce1e1eb 100644
--- a/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
+++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/handler/LinkyBridgeHandler.java
@@ -1,3 +1,15 @@
+/**
+ * Copyright (c) 2010-2024 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.linky.internal.handler;
import java.security.KeyManagementException;
@@ -36,6 +48,11 @@ import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
+/**
+ * {@link LinkyBridgeHandler} is the base handler to access enedis data.
+ *
+ * @author Laurent Arnal - Initial contribution
+ */
@NonNullByDefault
public abstract class LinkyBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(LinkyBridgeHandler.class);
From 087c4a8bc8564f9416f58cb1485a2d053ba33a53 Mon Sep 17 00:00:00 2001
From: Laurent ARNAL
Date: Sun, 18 Aug 2024 14:48:26 +0200
Subject: [PATCH 067/135] start reviewing the documentation
Signed-off-by: Laurent ARNAL
---
bundles/org.openhab.binding.linky/README.md | 243 ++++++++++++++++--
.../doc/GraphConso.png | Bin 0 -> 39058 bytes
.../doc/TempoGraph.png | Bin 0 -> 60646 bytes
.../MyElectricalDataBridgeHandler.java | 1 +
4 files changed, 226 insertions(+), 18 deletions(-)
create mode 100644 bundles/org.openhab.binding.linky/doc/GraphConso.png
create mode 100644 bundles/org.openhab.binding.linky/doc/TempoGraph.png
diff --git a/bundles/org.openhab.binding.linky/README.md b/bundles/org.openhab.binding.linky/README.md
index 1f7aa92d1ee..441b2cab81b 100644
--- a/bundles/org.openhab.binding.linky/README.md
+++ b/bundles/org.openhab.binding.linky/README.md
@@ -1,27 +1,42 @@
# Linky Binding
This binding uses the API provided by Enedis to retrieve your energy consumption data.
-You need to create an Enedis account [here](https://espace-client-connexion.enedis.fr/auth/UI/Login?realm=particuliers) if you don't have one already.
+You need to create an Enedis account [here](https://mon-compte-client.enedis.fr/) if you don't have one already.
Please ensure that you have accepted their conditions, and check that you can see graphs on the website.
Especially, check hourly view/graph. Enedis may ask for permission the first time to start collecting hourly data.
+
The binding will not provide these informations unless this step is ok.
+This new binding version is able to use multiple bridge to access the consumption data.
+You can use :
+
+- The EnedisWebBridge : this one will use the old Enedis API, base on the enedis web site to gather the data.
+- The MyElectricalDataBridge : this one will use the new Rest Enedis API. We will use the MyElectricalData proxy site to access the data.
+- The EnedisBridge : this one will also use the new Rest Enedis API, and will directly gather data from Enedis Site.
+
+There is advantage and disadvantage for each method.
+
+- EnedisWebBridge is the old way to go.
+
## Supported Things
There is one supported thing : the `linky` thing is retrieving the consumption of your home from the [Linky electric meter](https://www.enedis.fr/linky-compteur-communicant).
+You can have multiple linky thing in your setup if you have different house / linky linked to your account.
+
+
## Discovery
This binding does not provide discovery service.
## Binding Configuration
-The binding has no configuration options, all configuration is done at Thing level.
+To retrieve data, Linky thing will be need to be linked to a LinkyBridge. LinkyBridge can be today select between EnedisWebBridge, MyElectricalDataBridge and EnedisBridge.
-## Thing Configuration
-
-The thing has the following configuration parameters:
+If you select EnedisWebBridge, you will need :
+- To create an Enedis account : https://mon-compte-client.enedis.fr/
+- To fill the bridge with you information : username, Password, and also InternalAuthId.
| Parameter | Description |
|----------------|--------------------------------|
@@ -29,7 +44,7 @@ The thing has the following configuration parameters:
| password | Your Enedis platform password. |
| internalAuthId | The internal authID |
-This version is now compatible with the new API of Enedis (deployed from june 2020).
+This version is now compatible with the new version of Enedis WEB API (deployed from june 2020).
To avoid the captcha login, it is necessary to log before on a classical browser (e.g Chrome, Firefox) and to retrieve the user cookies (internalAuthId).
Instructions given for Firefox :
@@ -43,21 +58,213 @@ Instructions given for Firefox :
1. Disconnect from your Enedis account
1. Repeat steps 1, 2. You should arrive directly on step 5, then open the developer tool window (F12) and select "Stockage" tab. In the "Cookies" entry, select "https://mon-compte-enedis.fr". You'll find an entry named "internalAuthId", copy this value in your openHAB configuration.
+
+If you select MyElectricalDataBridge, you will need :
+- To create an Enedis account : https://mon-compte-client.enedis.fr/
+- To do a consent request on the MyElectricalData site : https://www.myelectricaldata.fr/
+- To fill the token provided by MyElectricalData into your Linky things config.
+
+These steps can also be done using the connectlinky page available from your openhab: http://localhost:8080/connectlinky.
+You're wil have:
+- to select your PrmId from the combobox.
+- to click the "Authorized Bridge".
+- to follow the authorized process on the enedis page : please select only one prmId there as the token is specific to each Linky.
+- then go back to the /connectlinky page, and click on the "Retrieve token" link to fill the Linky things with the token.
+
+
+## Thing Configuration
+
+The thing has the following configuration parameters:
+
+| Parameter | Description |
+|----------------|---------------------------------------------------------------------------------------------|
+| PrmId | The PrmId link to the linky Handler. |
+| token | Optional : need if a token necessary to access this Linky thing (use for MyElectricaldata) |
+
+
## Channels
-The information that is retrieved is available as these channels:
+The information that is retrieved is available as many different groups.
+
+- The Main group will give information about the contract linked to this linky.
+You will find the following channel:
+
+| Channel ID | Item Type | Description |
+|---------------------------------------------------|----------------|-----------------------------------------------|
+| main#identitiy | String | The full name of the contract older |
+| main#contractSubscribedPower | String | The subscribed max Power |
+| main#contractLastActivationDate | String | |
+| main#contractDistributionTariff | String | |
+| main#contractOffpeakHours | String | The OffPeakHour link to your contract |
+| main#contractLastDistributionTariffChangeDate | String | |
+| main#contractSegment | String | |
+| main#usagePointId | String | |
+| main#usagePointStatus | String | |
+| main#usagePointMeterType | String | |
+| main#usagePointAddressCity | String | |
+| main#usagePointAddressCountry | String | |
+| main#usagePointAddressInseeCode | String | |
+| main#usagePointAddressPostalCode | String | |
+| main#usagePointAddressStreet | String | |
+| main#contactMail | String | |
+| main#contactPhone | String | |
+
+
+- The tempo group will give information about the tempo day color link to a tempo contract
+
+| Channel ID | Item Type | Description |
+|---------------------------------------------------|----------------|----------------------------------------------------------------------------|
+| tempo#tempoInfoToday | String | The tempo color for the current day |
+| tempo#tempoInfoTomorrow | String | The tempo color for the tomorrow |
+| tempo#tempoInfoTimeSeries | String | A timeseries channel that will expose full tempo information for one year |
+
+
+Using the timeseries channel, you will be able to esealy create a calendar graph to show the tempo calendar.
+You will need for this to enable a timeseries persistence framework.
+Graph definitions will look like this
+
+```java
+config:
+ chartType: month
+ future: false
+ label: Tempo
+ period: M
+ sidebar: true
+slots:
+ calendar:
+ - component: oh-calendar-axis
+ config:
+ cellSize: 10
+ dayLabel:
+ firstDay: 1
+ fontSize: 16
+ margin: 20
+ left: center
+ monthLabel:
+ color: "#c0c0ff"
+ fontSize: 30
+ margin: 20
+ orient: vertical
+ top: middle
+ yearLabel:
+ color: "#c0c0ff"
+ fontSize: 30
+ margin: 50
+ dataZoom:
+ - component: oh-chart-datazoom
+ config:
+ orient: horizontal
+ show: true
+ type: slider
+ grid: []
+ legend:
+ - component: oh-chart-legend
+ config:
+ show: false
+ series:
+ - component: oh-calendar-series
+ config:
+ aggregationFunction: average
+ calendarIndex: 0
+ coordinateSystem: calendar
+ item: Linky_Melody_Tempo
+ label:
+ formatter: =v=> JSON.stringify(v.data[0]).substring(1,11)
+ show: true
+ smartFormatter: false
+ name: Series 1
+ service: inmemory
+ type: heatmap
+ title:
+ - component: oh-chart-title
+ config:
+ show: true
+ text: Calendrier Tempo
+ toolbox:
+ - component: oh-chart-toolbox
+ config:
+ presetFeatures:
+ - saveAsImage
+ - restore
+ - dataView
+ - dataZoom
+ - magicType
+ show: true
+ tooltip:
+ - component: oh-chart-tooltip
+ config:
+ formatter: "{c}"
+ show: true
+ visualMap:
+ - component: oh-chart-visualmap
+ config:
+ bottom: 0
+ calculable: true
+ inRange:
+ color:
+ - "#0000ff"
+ - "#ffffff"
+ - "#ff0000"
+ left: center
+ max: 2
+ min: 0
+ orient: horizontal
+ presetPalette: ""
+ show: false
+ type: continuous
+ xAxis: []
+ yAxis: []
+
+```
+
+The resulting graph will look like this:
+
+![TempoGraph](doc/TempoGraph.png)
+
+
+| Channel ID | Item Type | Description |
+|-----------------------|---------------|---------------------------------------|
+| daily#yesterday | Number:Energy | Yesterday energy usage |
+| daily#day-2 | Number:Energy | Day-2 energy usage |
+| daily#day-3 | Number:Energy | Day-3 energy usage |
+| daily#consumption | Number:Energy | timeseries for consumption |
+| daily#power | Number:Power | Yesterday's peak power usage |
+| daily#timestamp | DateTime | Timestamp of the power peak |
+| daily#power-2 | Number:Power | Day-2's peak power usage |
+| daily#timestamp-2 | DateTime | Timestamp Day-2's of the power peak |
+| daily#power-3 | Number:Power | Day-3's peak power usage |
+| daily#timestamp-3 | DateTime | Timestamp Day-3's of the power peak |
+| daily#mawPower | Number:Power | timeseries for maxPower |
+
+
+| Channel ID | Item Type | Description |
+|-----------------------|---------------|------------------------------|
+| weekly#thisWeek | Number:Energy | Current week energy usage |
+| weekly#lastWeek | Number:Energy | Last week energy usage |
+| weekly#week-2 | Number:Energy | Last week energy usage |
+| weekly#consumption | Number:Energy | Last week energy usage |
+| weekly#maxPower | Number:Energy | Last week energy usage |
+
+
+| Channel ID | Item Type | Description |
+|-----------------------|---------------|------------------------------|
+| monthly#thisMonth | Number:Energy | Current month energy usage |
+| monthly#lastMonth | Number:Energy | Last month energy usage |
+| monthly#month-2 | Number:Energy | Last month energy usage |
+| monthly#consumption | Number:Energy | Last month energy usage |
+| monthly#maxPower | Number:Energy | Last month energy usage |
+
+| Channel ID | Item Type | Description |
+|-----------------------|---------------|------------------------------|
+| yearly#thisYear | Number:Energy | Current year energy usage |
+| yearly#lastYear | Number:Energy | Last year energy usage |
+| yearly#year-2 | Number:Energy | year-2 energy usage |
+| yearly#consumption | Number:Energy | Last year energy usage |
+| yearly#maxPower | Number:Energy | Last year energy usage |
+
+![TempoGraph](doc/GraphConso.png)
+
-| Channel ID | Item Type | Description |
-|-------------------|---------------|------------------------------|
-| daily#yesterday | Number:Energy | Yesterday energy usage |
-| daily#power | Number:Power | Yesterday's peak power usage |
-| daily#timestamp | DateTime | Timestamp of the power peak |
-| weekly#thisWeek | Number:Energy | Current week energy usage |
-| weekly#lastWeek | Number:Energy | Last week energy usage |
-| monthly#thisMonth | Number:Energy | Current month energy usage |
-| monthly#lastMonth | Number:Energy | Last month energy usage |
-| yearly#thisYear | Number:Energy | Current year energy usage |
-| yearly#lastYear | Number:Energy | Last year energy usage |
## Console Commands
diff --git a/bundles/org.openhab.binding.linky/doc/GraphConso.png b/bundles/org.openhab.binding.linky/doc/GraphConso.png
new file mode 100644
index 0000000000000000000000000000000000000000..686adaae6ee222fcad6f36b3ae8cba4fb59bf916
GIT binary patch
literal 39058
zcmeFa2~<<(*Drh+6zAAdMMa216$>I(Kx7Q9TG}GSR*}jSM}ka283F{7SpP~9g<1q;
zN}^T~5F#K12uVa#3J3ui0)!Ce0D%M&LdbkqKwGP}@4esphWEQyR$Q(O=bY!+&mMmJ
zxA${S{9#w8mCH6P0{~#DOOt{kD3^W{cf*FFFq`i-%8k
ze!X_{-0))EsckdS1@yeLahjjqd;Dc|iP%huVQK)umlpv*&rTIB0RRA?%|c@d004v+
zOC8m3EnNZilpmbeTnzw#<8@suaH3Ks2|R~jfK2v^FiID8I70H`qP+n`KG%A=9&Z3(
z0qsq|t&UY7xm^BplPx@u9QtAwM(g5F*oMr74RZjX@0j{D9-d{lRVq{nqwQvBtc)+7
zoIB+LI~VqFpd`y4HJc1|kYv&%nKUJrMiuv>Y;A0eaUqs)I!-au3WZ@5qKg+V)+c!<
zfYEa&+wyqmSy0swEk2gh{PZR!zUq
zk9uYu(&xB;e~p-pfk>>~L-%VQc6VohRdcm81(T*2&nIec0!~DKiUm$cZ6q}kh8_}n
zeIL!&4AW^DGEodz?A|@q;sh=@UNVyUEKMqvy8A?(t@~{o7$YwNw1GpOnyZ0BFe_7l
zGWB#hk*1Wk<)fuC8N4w=#K0&Rs#?`Frb;R0*3{H&-MTfP<6)F+k_3yHevX-o!$hhS
zVm(t2i}Q<`pFDY@3$?oeQMJ3n3=9o%@;;RuuL_mTPOiakvD&w92cQA8rUTkQE2D9#
zqn?>hPiX)HqG*LQW?1IaXqbU&rBbN}i}{l8prxgSsG5FZgpau(of)$X=;#S&c;jOP
zbzQ~ro=+ApUOdv2or%O>ym(O}F&3eX|4u#
zH!gJqcE_?F17Xtx&l3|9QGAM6C5Nc!@*fHE@9|c>ky|0Fr4wBx&S|?8bAw4q2DYgX
zDuPX|g98Q*F_DwqrO9AeSzwnFI5upmuUaDHK*T+vWBY$ri#F8Za%2yK86Xw1z{SXME
z{N~xWhAN*QYPKzMm&fA=rKRD5HIBLhlS_qbvD*uQjBy#CMmJlO5tG)Xt+52SHKNvF
zYOpJ?VcojU^7Dhw3Vagu%+Q0^t)w!Ut&NQ@9cO>OgFe!jNeW^{%uaNfEGAsMc(JLe
z$vSi-Mm}9D5z-X1OjFOYjSex9VjfFNOKa=at=x_RS>Gj)_9g(E`{oK6JY
zXP1w|2d333U+3K1UI^5y%1TOvR2i=Uk5401$%aT*qV#qH*!)lCwK8g4uJ5+Q@lvgc
z)@tCePyk^_8WCurJFN+U7G~CAyl#fvbvXUWB3S$N=CY$MD`~wSd-!;Pi_?%KN
zGbbX8%cO*6MC`>sly3waUAUv84<^=H{n5VbVs0;d=fG8BUbNZRRXopTi6gL^RT_bh
z9=@wU+Oy`&k}SQ%O5xaxiUb=^F+NY;rGfQ8@+snD-z4Y{^`iI%@-t`KW!DaRO}n}A
zw|P&udh5))qH;7%`A25HdUi)7b@h4T^$Fzz`FXw0l><5CRr7u+L}8rpe5ISDuHG0Q
z*6Kzj7}I{Qhemj7Zvp_IPwF;uRXw9aDfo%XC29xvM3nz>jn&a%
zBa{4?S1#_7pY!Cu8AH0lKfwY3faSWUn_TTGKHJdO*T+^)vX7zrE~MZeJ`{XsRIMh6*)aK){10>yARjya{p_Dnr34~
zwT8A)f}!ULdc?aLZs=m}7C-|400wc7yL?-eVr*jKq5Mh8QFL)}y%8%c#q2?+vy@3w
zFsrMpxqgm+pTc*HqnN@D2V=a&bBuzh3Rg%)@TRPa$mxNKP)|ik5Qm;^D}H);`SaF8^E0}@U||0)fI^-Jm)_p(G@N|@mMxN3mu$o+
z^d^V&Kdp%arG0;I(eky2vy-s|?fC?IB`yvl9m`2KhmSz#SbVg&
z1_bdsax*vPt3^Jq&M>;)9p#X8d|{NojnMIsJ$s6M8#Cg8$FZ{6P)^fulDr+JXdif<
z0L42{&b8i$9XVr+pHA~SqLn8!Fw|rz92Yh6GTl63
zt!c*G;bHoPZ7F6CrkpeSmd&ek#E_htZA%L5x-ijMM3a$eHRvw3rt&s4Jr)vQ9y2CY~jAA~3e
zhiZ-7aoM&=oIPqb2anE(ahM6$OII&uX$Rf}UCG__r?P(T9iW!t*%WBTsj*l0wr<_(
zNeWgB{O0Zxb?=VAPWaNCQ;&-pNzYu1ExfDrddsj=DhA9d?k_$%Zx$8@m0xUcfUt(@
zg;p4$RU8P+{X
ze@#qG{HNc*&UO`_O<@hVdzJ+rAI?eteFMJemuLBTFPy)RsSMaQzpk`nPENHHaj%S<
z^i=J6L-`c(+KP$cZ(?6;Sw_v%7yY&jT^X?8u-f+hSCA0^0ROCc5g#6$_sQ4|M)Q|>
z_fXzi9YP75P7!rHWwXUoEOTE6>DwNFk^8`I;w)A|Hkl0i%zDoQ-VBmW@zAfLIT87
zATU9!1Ysb1Qx_2qg3%GP6BTvPCjS=Za`yrs6t71@r7COT_N3EJip$HplGLES&p-G)
zknxa6#PF&lmo9Z3baY?gSWqxYZ^Bva-hB(UZDx$)%!WOldzo&YjMq{F=F5&64@$$?
z<|VLtzNV+V>$i4Tfh|T^Y+EHT4jY;1k|V5wy9M1`KF&^}7~-f$dwBErfKB*#Z5N$V
zb)h}OPP$VWSQm?l`5dC<*CinCvj2%X#Q72M4`7O4$QLc(a*tntHU>jAIvCpPJ@vzL
zdzd875HnDbZL9yyH{UEsoEyT`YQD0Pt>{2w9vBdER=%SCJz4DoDHQp|~3^~oZN?Hn~
zKqe9D85^ql1YoEXuyMRp`zyOw1m8qDIryo&^x@}NF^wmE`LE&MUpLQ?Cx
z*T&|U@%4=K`M<8x;INrIJ^v8fN|DB>SZtpKp3OoyRfrC4)QnwNX|-efym@X=hK|~(
z9(R64%Dm=bGacW{QvRn^?)?PkgNOdm)}qTvT#;RrKPkW5Qg5~%0DxzkfFIgyF%EMO
z!_IKdwaLBVX1gZrq6J>VX)6E#IJFwP<9p!yL+Y_@+N1Hy9c{w1;3!~&vR~e!3!n72
zMfR?Y27o7zw*x=i1a5Ykf*c#HW>pP$MMq$_Sk|XyHD;sLH<1dhO+9U42n)Yu`IU+jfzX%qg?Rr~sn2YwXJN6q=L*l$dvDyhk)UU`OBq<#K&-N~lPfxZnq~0KJYr@i
zDyrrl{v>IX2ssUIe*Jmv0rYHJHiMy>d&r5Hct8u;EGz2K);xSl^YG9-gGbs2xO6ZW
z5M-~sy83o+FBQ&t$s`f;j~bu7;WwD-4nx5s=i*XRXW?+V{?zhA5G1qQAaL-$DUqV&
z)g`mt<#tEoft$C0&!W^Y#qlN(Hr|$0f=5LS=UH;-cv@H;JrWq_axr7W5-qL$36MtK
z^2{ZUK|!+KTQo{BMQ9yH1x9RVu4ap?Slv#O~!vu+n=oC!)f)?b-x_L7%X
zq)_T*AT7X|(u4D$sw6*Xbcn8b_DJRVIY$CECyD@@1^
zegmfcLefS2#`yB3J({VvJfCc#o1OpwfaCxul+-vr?#s|qwhp*C61qMd#!y3epdSj
zZQ!u;+XMP)0X09^rn&myQuQ@&7y&mnsSnWr+CF^?-F`doodCc;{^KwY;yz0Aqi(*A
z)*nsiqeJ?C)^C#Rfa{5g-s{$v<`-Dl98;4NoQX7aGc17u&0-rBH3u_q%q*CthT
zFfyhpk0fwj41|Qg=iTSwh?mJbXL+5s6sElA<8P>0M8@~;Mx%M&0~@gI-GDR?iJz2y
z`ff~`PYe1ymcAR7=3#3{!PnYqPSVutaimJAb??V7FlTCBm}1IRMP&HPD%6XYGH_OV
zZ*Mwgv@I9Ru~Us-LxC$%FJ7K)pgACUF~jtO?elT-<8a%Y08TRw|W(V`G{Wpr&p{
zi={PSDxE4Rft$$^r4wDD2nr>neY}*@l+EXIF)`;`+8cH
z&)-mu7-3C;a}9t%>5%
zJkKFx9CpuV1`&573O=(A>1?Ljiu7gT@jp_Ctc-F{ix=(EL!;
z{&=BzZ1mqrbmOUu2|-5;(<`^ob35jP`a+Jr1^U-8E7xm`;=V}g*{+LKzA9rqRh?h-
z7Y6uKLxLRvVDemwOK;uqJ`kOs%VuI8i94N_GgZnY
zTYgqi>{z#!)+M7WadB~pxSystO_*CzT><5%U(8;1u3QDScCfRHkw2>&=7N;Mt>J3U
zuRd;4hDyY}bokB}>8!%ob^WUPwHC%?zWVYfrM%|4sX`@bn;WG0wr&Nyhd1O0nAo~Y
zzRR=z3yu7#gb(yrW9vQ3czy1fpyAR}HDxs{``VFRfhw}Bq$8AbZ89M>Oe6yHCv94)Y7u94U1`%o
zKEAu9-P9ZPsBr8L1)ZNR-9il4{0mT58vz=te+3Tw(7wbmAvGc#Y_k`w_mgCRK$*N`
zTe3JTYzx5p!F6d@!O2Diw`^8PtVP#t#6%Vr6;@yr_jFWKJ-EsdW##>?s_8Q|PtC~P
zl~+b$o0Vda{Ko`COc3G-(-B?K{CnDYt>EwT(gBMoGb+2I$6=~p0P~<<71;-6@7Hw)
z6zGV-R4MfeMZw%4&FEfiQV3LrzL-sNpJ0
zx7)g$GX2Mmixd0zJ3*^xzY7eDxSlSH2_%Qf(NV*U@=i_x)OMDq-}57z5kBQ7sfJO5
z!Ng9;Ho(nCV`loQf)$r|%u8dYMKGbG9J|+0-*OF(#E`jDBiV6qJ6lNvjf?-#*srg2
zB*wP*X^I#Vzs`BaoZWD?X%BF$|m!rnPego=*fO*x5;X6HpII`%Z@dL-&QE#xsh+
zk#uviF!{DEXd{J_{&43*)+;6}bYd9Pt*ZS!|38I@Qa!9;)V+$La6(b3ifVnmi%sqE
zV)W21oTmR~sbl1RJH~yP;~?tfR{@@k=DOsriwH6c?2JAMtYkc#xT)1_e;n}VZiq(6v-@iE4?o}B>@un4%rJPc&o^=lKzT$6
z60CRBY+gf(99B}5gP!A3o-xLapH-wtPC1@i2fb8SZnl%XM)R;_<#TF&!;ej1TtngJ
z`Wl#b>kWtlUNLzMUJJ=kO+Le6h~a!{HIYh@A0@#4=9|dF8h+33D|3nU0(-&t34rz?
zsOIW}tY3kdT0wzNxQmk@6tg{|_8xL2v0QUBfe4DJtdx;J442A95BouMxVvPuo>e4~=eOuR|B#FS{t`J>o8Rq|+FcIYE7cYh)(=ET;x?M)%_4
zBNu~vn2(pf=Gku;0gLeS9K`ORrH-G4YiTu4HxOs*3TM(kGuy61sFH1j;OuGKCoN=L
z{i((wAdGWu(z!n8KoWS?s}O>0>U5B-9%Y#-lFf{clkbWBtl?oPAgzogP?~S)Cs;Iu
zd_F+pP2o|FMr_~v!{}8njmI&pYK6TM+pDK2kX5afwhE07J@3S*z6-
zEOT6=CkxEwHthFAGg+342$)+H+`%<6qwjk@3Ee
zf!x2VAw%68Yn(xlQ9i^DeTx
zpa1R*=^xi&LD~e`^bMZ@T{7v`1tLcj-!v#_^2Cd@MsPmdH2!|=99WikF2|2JhQwN@+$?-5hR=`7v+*zg}Sn
za=b0cj3OU70Qd*7#4qIHD{(Zsirm$0(;M4?mD*}hd=^RaX
zt5$NuXdcfkApaZ42Cc)a-hMSKVu`};0^04mUjnQL^E^7!Hgdcf%evL5APn+(Y->^4
zIrZi}qY=_xo~?tLlP(MCs>Vc@2ahi=QM1*9LKn{sare~}dus4LIoSvaeO>5L9;k8m
zOgyBx!+Ko6Zc){8GVfD&t51fJA7SOd3^LS&II|^YCSFo-1a|AJ#wf^k1k-IfPkgsU
zo$8Gf^tg%JJcH~NkVA?}R#x~-jH>!V6faLUxZGt@$JJH+p~0x^q=@8npK9D7MwDzA
z_e^q8RCbvjXE!Ik;pY#oP7tT*k`8aMH)JB2rHylNvd8@tBv
zyLPfiml5)L{q7#!I@NLEO*45L>`Lz
zeUA67Vk)TJ5Bjcb-Y!_)a7Ss3Qp{{<%y#jpNuzF3mGmSENpI=z?>}7&E$nU!WgH=^
z6Cf7dRwhjXrCo<8!)E86^-3O=@c%G_{zR$ZTwWBUUnVxRMeo7vX0+H_$R?*5Dn8U=
z=W)~tMcIB(6Xi=))mx}m4RH^FCEE7B?bnJGAlTL%(9%eYc`W%
zVP)Z~DCLAnwW*;>qnJGE8vSUcF!u`5f&Z;G!PYkrB`Xo=ZeK*Gld*JHHZZ_FEs;ah
z?j_Kf2~X4O1zPM$o2pVOLfBNJ*&KEY_$=lPw*SiQt&;9%QI@(`K|m;4e5R)Ux_z=%
z^N}yz)Wm>5$0Ca?I3$2o>YBD2ly)qsyW7(OBRMZ#
zT9>9ebO^{eru&Z;K^&RioH5{bTr3%0LHI6h@+F#FS`j-%uB-u7m28?oOLW(ZbvE8GQUw$5#J;8E6Dh$**{E-F6k5^ke50xFD{#9Pke9pkZD{kZk@!q5BBVk5*c~B5
z=~lHWJ-juN-f@MhYvj5rZ4we0)_bSS79NNfx&}Cj4=elPlWWKsiFVTOg+q7_F9%8N
zjT!Bnp3w1yj9;3yt=H3TM3c9vF4iXEdmbsKVna6wIdt#Nh|wvvYpTAjYqEDn&Wpd2
zOPut0!vuau&m|;I+IaL{R0hdDI&O+%y`Eud3`;XkAIy(epFBpI+{(lQA3g<
z_e}TT(R4)wm*#LxH8##mO{{iz2*gWUeYo@GuYUq+L|@J3mlY^#K0iOeq4*vZ;eJFl
zWP&YRjr9%86uLTZ0qo}na0u=yzTeAT%PUX6m`z(-W>vPLN?v`|h0{gy%;;Xc-Fw0u
zZQR@=Za6tA+_^$g|L<$ms7qsZ#35Y;_lclnQAnv-J&ZAkO3l@aW*IPH!mRr{Xr8$)nc#P$sSv
zbN?l_EFheE(%W|!4xtV#m22O54@>y*VH+G@U3s{
zbd4OX%6vht4qAQ#OC@U@y>${t;J`2gJnRk>bhnitRKJ>I@7KJ+(eFd&{7Jx~<*cAF
zItURPs*(m##6A&*ha)TxdvcnGgjio1>s6b>qZmcCJO*D23+io&{9R6>$+yU#B%r9Q
z@W@(RJQw@o?yJ+py$nkc`&TaZ-!-I6>+o}9t!DedB)wIRX_N|?mf1wtW8qX~0VVls
zXb091RT?GZZM7Q8?pvg;wEm;2jc7H4aRD(Q!L$4k*05DPq!TsK2(?v;iljyy($MKQl_*S}WAT3hrW#A;zsE>`)hw^p+~+A*yUb`p;NH
z?LjTqVuL%2$u(%}9nk|jZwp{@!Q4&og-ieLDO$e(f->8}wyQ*I@F4~=OUP{fKjTg)
zaJ6deGi)&JUkz@-oi7mnACbdruH~K7=gr6e7JmMFAP4sZ4L{~hr^2!UdR8&2aJGgx
z*C$#)sQ;9y`+o_vxBas}(Zy~d&T2NBBT6R%?e>TjWpzn2Glu$xhK4rL1DX6)EKwWJ
z79L1LMhw0%pcuJkZ8S7=*b_2vcUV?p1g$LOkU$JmkM8g@eRES&A*v_wN$xlCyHCq#
zbpN>vjn#K+i@}|(sEKU4E$`M$7e0%IvTFFEpqx=CUom6~Z*k3#y5XCE>
znNxdMD{&G``)DtXUY~c^xwg-FdC5)Ts7aTugHMr4T!JJrJILgFsxd21-mM}a?9uF)du<5;&lPCm=F(-0B-sEraEdSjZ$Z+^)
zu7;SZ*_`dZ)DfCS2W-#-L#a}#4E8|oHdC#a8NstI&I#(vwG!^0X<$a$ThfM4NTvuA
z5An`czYMS30ny(^t|4v(@$3!kiM)r>MTm
z_MYR?j$r#RM(fJ|;*wi|>sEyH(9E(9@i1Q-^E?!@{g?$nxurvxrmex=Xa4i#^F_ep
z$#{miu(LT-wH&pbtr@}3dLd#)>w8E|R{aM(_1xZ8zeI@bRX#i|sHP{RModB?EiihI
zrZ3^mSehN-Q=_(csEy&bK!)qTtYe2(^IQwWDJffR#GR;#0;rf}eVWPVSOoX9pau@i
zp5V>JCwuBkdnHxoAV(;r!U`iK%5_#5*cWXlkLPTb6%8*AtCF9V>A1#J4WtG9qD>gv
z`_C+)|4zU=N_^B#&>)NP4(|e0bhqw8*+EW9$Dzv7f5r;x$ad}%Y~k&u&vEH?>%gOVC3wktse#KM%j5OGNx>Al2BhB7Db!n^5)yROD)TGIt_E14$YHM?h`eC^1#
zn4xr%M@F}fYbtxKr0@1=XjPGwiXymV#_KAReXGRP78qPgs_L;D&S~6>I4NtgS3n!V
zNt-}bAakdZfuqd%=`N#YtZ-Bb{1gN2$BV~!
z);5M%-$0t`vVA6UW0Pw3c9e^OED24{H>2B&A+(%|*uK%CWOm1O$et)M?GY!Fzsg>I
zL5Q(ynQ9-wQ4B7b?etS{oW|FhPI}Nv#o`iujQBu}J9~Bl5;@cO+-i=Y@vgGv#{(AB
zD$zz;^aVv(uN}Zr%<$SXSH%_=%h-lQ%+&MyljI}|639+)p;etPkV{ELmNKr~Y0}S5
z@ML+4+2G(BnM}uZ-J0-d2
zD65Ry{x$ts!AWz@Udz&zzIqxik{@Wk@2#S_#%Kzt+tXHu5o~>w-9sB8t2@nHs1z>C
z%tripm7U~8Z&X6Jr_ExTS7;yG_pItfOmQ)}Aat}&f-=ITueoD-d~gL75LcX}f4AlgA*&`asF_6uI=4s2fO|ktDP%rN5xo;|pP1b6D#=Et|9x)LsPDp&H
zO>yvv=~djhTM!!3|63v3!OI;zloldv;&hoCX*Rbacm)CDDxsB*GWOX1=XBM4j?zvl
z=>OKamhhL%E!SgthZYksQICVC^LV`dP7tqN%jpwa0GBKci+y3Ix+=d=?PQfGKy=H!
zWveEF$1rooq}?#_yZ#m-)h49rm6DJvhmmnEHwp6n!nLJVWvdP?bEF0O1Kq8Ae+6rV
z&2-jn3jirmk_~s%B{V;YQaYjprA7n=jq1Kh&3;ByE+VKRi~BSAs|?ofqn>e*;IBH@
z?lsbEex}a#Q!A-ug%c|X@jCFPpl-~RpC_YvNC&Zi!`{kH5Em*%L*Sv;5
zfm`L6OqZVHZmcJ2)e~3P>fL4x-IIY}!a)H2CS1MibfQ6_BU+1hthqQC0Y=7TP+5BGCRs^+)`w6z#6FxG6o2CwDM
zMvD`g7QrH7D=WzZYagL-U5d#<>J{TGnfZJ8(w(U>nojcA;HlFQTu`-jqFPTJNS;ZZ
zqF1#SlS?tIvR*cXEK6R)l7S4fW4>tq9k_W9ujA7re5Q}W$U<0ZA5wBpe1cyjU;D^*
zmv=sY8DY$P!KgBiX*?jR=7u1Kqde=qs^n=*C~++ohcOGV_vFg1#Kl2z8=JrtLfp>9
z!ywmv(Gx=}X?pWpr~VdOtbnupSE84mrY@6OMh)=2P`QsaZ3;fHzZRPH5_+X+*P`zi
z5hf5uV9nS^;?6eDnq45rRTm3~7g7{h!D51b*~HD!2G#NlV?CppyWew}MS#V*vAGM+
zlYFMDzax*ZF{*p`DKnQh)xSPnY+d|z8ahN>DChRIq(S`X
zU==-Q!v;@jTwEM9Yh&=(x)^yWrE72)Jws1wGlgNM=HioUk~cbBkaHX3BGNE_{72U)
z&eK*0PVrF|nU|?%`zz0KL9-$-taA2A8zaI%T^jy*F=3@XN#3ul<>9^*^46R4F(JoL
zD0uGC%E;+~+#`^tC}sP4u|t^nT6#k&CYIMDsRw`e=a$aD^V#b9DjUKqSM{~rzH4Np
z*-W#`xR({n+HZzwMl+7AT#1qiJA{ZN2BHVOPsK&yx+n-$bK!j@#11*k#><(5A}(pR
z!_wWMpO=H=2Z6ozyoh!~S
z4c%?#pqk6^=BL#(%V56H{7yCE4W-`*_bmC3MG*~if>U)cD9C&N^0aHJU)67&6S~Ed
z;O;7J2uDL>NigWE|D`RifA3(Z@>uFPn7I+F?0>Zl5(U(oE$L=Dl%GWLpT(YxB1c92
z&=G%sF#)rYCWlW68Zc!F;ultz6}8K@eMCsJFz?>iV0$+P}N3Z0}cJ-NVpuJj5x{oA@S3
z374sSfXe!jDkj>mZ)X06IC=pfmGJJ$*SEUE`}ha5REqg;u4L~=Lbd|lQAqpt@*sNA
zeCLfcRTmR|PEH9dIHj67tjKFr9HIGH1!ddk_|j09wZ2!k52)A>p4a0l__*HW{Q|^P
z4hcg0lFe@)@`DBki1Ga57!alBLg3uQd>c+Kz6rsK4IIy}I_jQ+)4CdF{5)-JNNzm}fR@HPHFW2m4v930*hY~D%GA}2RXup`&Y$3XQT$x>?
z7Z5$aZ+Sy^B1Gh?-nTq#f{mbxo-60YH>*`cM0-UEe{q@ULUt!@w6_;=BSItk{>V*o
z7afXqOI2@-7K4>1)%`~g5u-C!o;vrbd0d}^fK+_^c=ni0qyT(d%BOpQhA1nf>S1K8kl*2n|UFkP7v-hh6Pi>*+k6O=MVPp(l8~-P
zaqn-3e#Z*ie*!9R%ZBX;0iE_o3?*w8V#+4X54S+7d52vIdl)I;NWBun1ko~9&A9dlQF1$hWA9dlQ
zE_~F5cgc>AG2)|N_~;it`h|~vVc|B_AIzr0GZzt#?m)&d77z8+EXD+
zp%eVy?E3y7Z$BQf%n?q1EH^265Qd*SDWlTj9bo
zyxqD^I`YR#3<%IK8_O)Wr^5<@AlAv@7L@GpURi<)FGS4HJ8ikl6B#f&xPCb1t-RMlxjiUH4Ewh!gsJI?5(3J}b-JLMB8
zjNWY3$emnpvGHV0Qd(e7RjI%>C>TyU0%SxlnHeW`cu!{cIeqgD)uZJ6*j2>CXcU)~
zgo~cN_Z)AQDczj#sEbaKcI)$FmBaP9;JjCB(KC`Gbn4^LyYw9NA6q8c8SHFZgt8XD
zD_y#oTMxabLUB%TBaEqCq@dOze&3T&nf8x0-)Hrq=+6sKx_?Y`k=H{;O1?2RXm6=*q+W8msF!f&J&Xsd$XzOj88Q##$k6XoCML1DmRlmH`~j
zGs<0aslHXv@zUtYqSHpkkS7Iu-TiP%XI(gflNK;$9gLWmi=I2=B#gMAvDGyhmV^^^E2>SD)3pB`t|Jk7U1A^pqlvMrw+LScOEE})m?PT
zp26NY9`4IfWhNhlQSDboIyo^;myGn-pB`bE`=XW41z^NLy!oMh80AwAY$vL(`Z$t)
zOOg|Z4?cGeynemguw$vGI_c48GdiOLg~&mGRb_t3p(#13H4`V>x&ee04!H
z8}dk+A6^h-AYtR|Q4!Dc5Ii$gLX7xYdMFN4S{e=;PP#2c&-J(cb}yf&*gtE+IYNI~
zz-HZqpl3#lKoF^&3I^{rL4=3#i1Hgp*<-DcSViL%nR0Ga{jeqFvrt_lc(#M0)hgfx
z7$Yi0Mx$iLm?4*$Rw#t%Ns>?@4${tt)|oWcG0DlVHy9X9@_6a?a32ZjM`$d?cmJ??
z`I;9mUMyaG#X&kJym+x%egh0u%{<4aacG~>%f+|5=#*D%$YDthHh(pC$52m&&^fih
zp$GZ8I)j+pCY(Kz*HaPBrNwfy4`yexI8E94`G}s1{QjqxZyd=%dsR;4i}ld2*bwE)
zrB+V47Tq1a73W)1@fe5D{->9Nm=P4^3lz$;TPPKFFcYFtGFQCv981~=M$bs%b&X6g
z^2R;8cR%h^B=K=y3e^&?%YiB7B_nA4b?eqeV_F6f9l<+Oz9EBVsuWFkyu+`jKnF%_
zFv_RA7b7xomL*5xzFmiqlHak@Q|UHba#2%zQ(IC0{EK*vihww5YVP_+zfWkpK){w};J
z>v(Zk`$(gww|3Za@ABQn=Lb`vYcUd$mA-@OR0Z%^*TqY5%BnNN&kj23dU%LqmCrBe
zX|Oz;5?AazUK!0T^Nw{|>>!I7W3V66LN+@yWn_^0wzL!!(-`x_Iy!$UCr2@o^9SdgjNJQF6Lix;nrk;`O;I7xHz
zICSF?t!ASH+RG$d)QORYB7N$SpMwkTIwi#x&ow;9?~*75kg0uh*qK^Cc70=cj;9^o=$BN
z{#$JVpQhSOgh-0EOSB1@h!ZD9Qy9G!s24AfL+x^u+UK7G7OFhhH#hR&0+QfXjXgC03WC+^4nU=2gJrxVHi)K~QJ`jNtDS$E-u9nz=%~Dp_yb
z8e~)1V;<@xPd@>oxDYuft4j(sH?ZC_)r105XQgD_IC~HXgU2tI#mT^#wg{yYUM@-1
zCM?(cCW7bx#~nKA_^@?nt1Yru-WBQ->>uo+P3Wz0T3e;6dl`p&NnAW`9X59_jr|!m
z_`Y0wl*B%M)SW%Lf^hi$V!~3rU0!~HfoN%0nvG4Rv4ghPb>P4coThBpOmz;_PW1Gm
zR+{%mPO~~;WR!6E{FF&z1$8f
zkWC2B>gKcucU6Xt6}PV1dlFYw{o5i${H&YY=Dly-&s_>@LZ4nZdOA$dL0
zm23xdRPRWmr>+kE2e*5#MsfF3IXKL)5Mrj1aLiM{yU3T}K&(mua!xJGh}jHPrv8|J
ztiT#I+taRj7|ys@Asm6&!hMb$;XY5$)6q$y(x!AZMt!BzwXNKaAT*-dNum)2r{kDz
zB-tepB-E$ZG;N7v7siC9>Q@|0$kx$*zSQxv(ivlpa;LPfb)pBKe?TWVk;l74q$-00
zxKw#LK3CxRWN%RS1udvw9->qD?1-72Ey~B8aj^tWkF7W}d3}G6
zuk_cOwrz)smT+SDJ7ZxvScPR
z%d-;Fq=1TGra*;YO;b(>bvgj4sZ);UQ-WZE!Zei!@N9~RLMJ@26-5P6kpFU;|8K4L
z+xvXkANIYTXRmAD&$aLSdiGkE8&)Z-F}^A6%ChgG-ESg|6sOx$G4BFk_J+Ru)u1oV
zX`XkbTiR!v29|LHg!$Z|&m7IVrEF~4(!^L`S<2hLTZ=moV@-x)#9st~J#9bdoD!P0
za^cvk25)$l-Lm1fveVeW(bQiwjlYL{lT?B;U9J3Tgtsij9Gd?iu4$wT=fX&`Fx+OU
zhX2&jeg9k>Q-N~?v4jS^TA=}XuzrYX=
z4d>(AdPDT4FZ9!CGqdyx$-Gueoq3DBpO#JiP>UTgC*5DIZzPI@hpFKj`{^`b4#F_b
zyePxVRG35aF{D)W{jQOFOKwRfiT!k1!fc6O=Ob&OmkZ^5n`o8dcNWm}FDy$){Zsk+
z?x0oR$a3R({jHjv@^uMk&&NJrE?6y7quHaYev!-O-j^_1KZ>?4m}XJFBk^$B<;4kL
z%gTBzb@tVTbmg(#3ty}{7h#8&yMeK&3AKHxVb5RvtL`pe9nsQeYy^CDR+eC2B8_JK
zHE8&URsW{py;iPgN-Gi{>AQnAhItq#9+!kx8k!f-pyBY3_cyI+o$v+)-~7K=ef}@s
z#q;Z;qv=`amvc6+iT!h*XNo2vz!dkdaCcL%DrrLt6
z{DW@i4JWkq@U;A;6K-y9{OkV>T^(WhpkTM+W1y{`msTp&o;Er#&TGa8r_;GCSFp6#
zO4>gT!cZ{{d-=tHk&4slU^z=<51lC`6jZ)$6^o17ccP3bz77oAeO}w$W1gOs@$g?m
z%}o+Y!k^gjRLR={E-)<{cul7Lso^IO;CT%*WDCNUo_9mO5PqzIF^)Ijs=TZV%ZYHWIv_r7}B5q;t#XhqN&+2e$
zya4iSXqfJvD9BqAY+if}6c0OpLCPCw?o`7p3RDL!*N%;czp?=H!{y=JLZ-*co1zv-
zm2U8rpN$y0vN(G?EiO8JN2uOVdlP&pX?Esj#UWZ;bb1z61xuPOhaI{UO%0>1gbN&Y
zLQY00+G?`)hXvJnNkchysM~-f#2{(0{JXSSYZy*&^7F4*P+;_I{Y&
z;N+DodjHYki}Y&4msuR0VG-}6E=GE#uMfyOKoP7q&`@`~D5wTWR@DhF~!n`9tD&id($iTR;mV6yTkbx8CBRNE2<+
zxt;k9EWy5&Uy%+@B>Ij-edpa-rMIcmTrcV}pes~xq11~dxD?}uh{KMBzkgoKkoQpG
zzWns&%KB$zIA$om!32u>Nbe^jefA^4XwLuld-d=X=9
zcQ$)WlBo71>ytuM_3A`msbOlSRQiFVWkgco2umz(0*srq9vSh9l(_Snq7(dZaeeZ;
zME@dp3hUgn@w$&1kJ0u@M}Y+tvtn$vpA>c=-*$?u9$$cy8ozgv@VY8LqQ!zZD2K4PGs@vb1NW|FZ-T
zdJRyCkwQ=9iV>4HLZXL
z!cQHW+%-78wvg}14l1p$%OJ$>s!DXN8o7@=T=!)9@5vi?IsOj0k-l8DlRIFKpR~rq
z4i5Btlg`Ihs$V`td?Xj=J!?usKR=A&aGSh}yw>}wbXn7YtxLh_ygcXw@R0{2=RzZq
z-kOE!yBQRCJr`?b?QXY4CUfJ}Bvw++1KqWN*^|e+Olx+patwRI?c;2HIG1$aT+2)k
z05Ub{j8{OHkchZszql9jyBz;Q#Xd&p0G9c6)yRFXXH-QjGOVl&Te&b3PV!yo4p-0C
zUfpH7v^#gyW{Y5u{O;Z3=;-xX81gxi7L}m3`Ix@=gS&^iy?E@3x~Ak5zvYX!kdl3@
zpA+MPaN19QQE;*darPtJ#FS-0Z)Rp7GK@h`>9RahuwnYC0}u1828QpCj7ocRks^tl
zoP<&g3LFaajM2;MSwzbW@WIQoZ+dR0XCeVF57OexR}MSb4Fg^6?d{bRvr|>yTxkbb
zQ(nPC_nX>`d;t_xneAz#7COqE40c`Qqh=jO2S!@0sM&fisyB>y~>
z-<#9i3(as)>|+R|Zc8CP6BWP_A#lB_l>d}Ph#_h8Gb{oqW-S3!nlih=IpTer9XgpOaRDWYXd
zI0Q*P(3v^ApG8R{G0}6+0c^eo@?%{F$5ylg9;U=r>fglnJ@3s2{PqkD=6#=Uh%e+-
z_aKo)d3l8qSBIF$%sS4aU%7Qy{0|7qz)g=Su=E)@rcXX=#(7*;=c+!%{NlAlN#($n
zk5{{rYeN=_=nF+58{TI)JDu{EN>WEIUUkIByHF7;-y(+mWUK%$R-wZ((Sh>wh?KGo
z16ep+rtc1)dkmx)E?nh3gqKzxR(>774M$pk!&Pg`AEX>q4Bp+SG$U%>{eene&0gxi
z+3QO%+?kxN&C8RL?AG$L&=D&sZ2Q;=(<0#lT-Bm!#;JzqC8l%17Y`@*#ZdXCz
zk9=BO?Y8g+-FFWp#q8r3P6ob=8}#N?;8G|pgA8p?hVnOd@g8%ilgjlBY0t?hYZ2#M
zGLBU7p26+q0_Pr8ro1Mit=<3!9*z`o>alh1e7>lh4G^AH9$p0$4Y4W>e1Ek!|5`Mk
z54X``#pc1J?}Ona>(|`O9*z4csQ}l(K!NDUwREFp&Ze;p3S0tZ4q)}W_?2<~g63W*
zzs3A@`O*bRh1fOn3?2C?9l2#;VLDKMqzZs%z)PEo5)sE0O4F}rcwffD$1eUFKi+wc
z{Qh~Mes8Js5zc%j2i(b3x4z7{Xr;3pUFW!b4Cw9wwwAUr|Ko4mIo#gvyz-I6rT$3m
z4xQA?J>q|cco2RQI*7EUrxlvvfa=FuSyaZg6ZXh@Lx=8edJ^cDNCLxkXGxUI`~HO6
z8&V6X3d-WkSNVljmKJEM*c4l9rLPKUb
zV6&hhPZZ`NShr7cKR}((Ey_EuiS$VwlVraI@A(iZzlXHBN5w|zyK?=PGdP%aj?46t
zhEy1$?>QiP*}S%pFW$M&{S5ER@>Hv+UFL_u(sd}roLT=NPrJvFtc1KxrK639=>yO)
z@>}#eN0?8Qp=(CMXWQySeBwfodb3jAT_xYOa*+7g_A$$ndf#*{&wgEq>nZ8TFHFKq
z85hFudwS-N=1EJ`a6?LM(KT-1-9j`Z98OJjs^wcPcK+v(>)hX9@<=@K8H{b%JXNVlSB$P
ztQGEoB94$kog2?-?v-qiLYtgaqI$zV40Eg!?^p#SXB3+d8
zy7iVOFRNk_@h<+SsciYuC?FHoum>0}-kZmAHMRTo
zZO?6VnF$22G*IAElFFSO@5~ION@AnQq1SUP7?C2yg1^zev)#;9=-~+4KFJgpHgBCX
z!6v>ayLtOh5)oq(p~{1$-}@nCeh3-7zD{`w>U|dX9qdhUQJ#a;L