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 1cebc3e7b5e..17cfb65c510 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 @@ -42,4 +42,6 @@ public interface LinkyAccountHandler { * @return the formatted url that should be used to call Smartthings Web Api with */ String formatAuthorizationUrl(String redirectUri); + + String[] getAllPrmId(); } 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 2d9986d457c..e98c810eb56 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 @@ -48,7 +48,7 @@ public class LinkyAuthServlet extends HttpServlet { 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_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 = ""; @@ -57,6 +57,7 @@ public class LinkyAuthServlet extends HttpServlet { 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_PRMID_OPTION = "prmId.Option"; private static final String KEY_AUTHORIZED_USER = "authorizedUser"; private static final String KEY_ERROR = "error"; private static final String KEY_PAGE_REFRESH = "pageRefresh"; @@ -81,15 +82,23 @@ public class LinkyAuthServlet extends HttpServlet { 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); + + StringBuffer optionBuffer = new StringBuffer(); + + String[] prmIds = accountHandler.getAllPrmId(); + for (String prmId : prmIds) { + optionBuffer.append(""); + } + + replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString()); replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure); - replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure); + replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK"); replaceMap.put(KEY_AUTHORIZE_URI, accountHandler.formatAuthorizationUrl(servletBaseURLSecure)); resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap)); resp.getWriter().close(); @@ -128,7 +137,7 @@ public class LinkyAuthServlet extends HttpServlet { } else if (!StringUtil.isBlank(reqState)) { try { replaceMap.put(KEY_AUTHORIZED_USER, String.format(HTML_USER_AUTHORIZED, - linkyAuthService.authorize(servletBaseURL, reqState, reqCode))); + reqCode + " / " + linkyAuthService.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 7846f53f37b..4b7289f374c 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 @@ -60,6 +60,8 @@ public class LinkyBindingConstants { 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"; + // Authorization related Servlet and resources aliases. public static final String LINKY_ALIAS = "/connectlinky"; public static final String LINKY_IMG_ALIAS = "/img"; 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 940d4238493..bdf8bb1bd17 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 @@ -19,7 +19,9 @@ 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; @@ -28,6 +30,7 @@ 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; @@ -217,15 +220,27 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link } // 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); - */ + 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(); + handler.saveConfiguration(config); + } + } + return token; } } @@ -266,18 +281,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link } // 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; @@ -288,4 +291,22 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link } + @Override + public String[] 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.toArray(new String[0]); + } + } 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 7ecaedd4529..3cc4ec6ae95 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 @@ -48,6 +48,7 @@ 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.TempoResponse; import org.openhab.binding.linky.internal.dto.UsagePoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,6 +72,7 @@ public class EnedisHttpApi { 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 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"; @@ -111,10 +113,16 @@ public class EnedisHttpApi { } private String getData(String url) throws LinkyException { + return getData(url, httpClient, config.token); + } + + private static String getData(String url, HttpClient httpClient, String token) throws LinkyException { try { Request request = httpClient.newRequest(url); request = request.method(HttpMethod.GET); - request = request.header("Authorization", config.token); + if (!("".equals(token))) { + request = request.header("Authorization", token); + } ContentResponse result = request.send(); if (result.getStatus() == 307) { @@ -272,10 +280,37 @@ public class EnedisHttpApi { return getMeasures(userId, prmId, from, to, "daily_consumption_max_power"); } - public String getToken(String clientId, String prmId) { + public String getTempoData() throws LinkyException { + String url = String.format(TEMPO_URL, "2024-01-01", "2024-01-31"); + 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 { + TempoResponse tempResponse = gson.fromJson(data, TempoResponse.class); + if (tempResponse == null) { + 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; + } 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); + } + + // 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); + 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/dto/TempoDayInfo.java b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoDayInfo.java new file mode 100644 index 00000000000..b5ba925c7d1 --- /dev/null +++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoDayInfo.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 TempoDayInfo { + public String tempoDay; + public String tempoVal; +} 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 new file mode 100644 index 00000000000..509772ddacf --- /dev/null +++ b/bundles/org.openhab.binding.linky/src/main/java/org/openhab/binding/linky/internal/dto/TempoResponse.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 java.util.HashMap; + +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 TempoResponse extends HashMap { + +} 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 56001489b79..03d63b3d4c9 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 @@ -36,9 +36,14 @@ 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; +<<<<<<< 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; @@ -160,13 +165,13 @@ public class LinkyHandler extends BaseThingHandler { config = getConfigAs(LinkyConfiguration.class); if (config.seemsValid()) { enedisApi = new EnedisHttpApi(config, gson, httpClient); + scheduler.submit(() -> { try { - EnedisHttpApi api = this.enedisApi; - api.initialize(); + enedisApi.initialize(); updateStatus(ThingStatus.ONLINE); - PrmInfo prmInfo = api.getPrmInfo(); + PrmInfo prmInfo = enedisApi.getPrmInfo(); updateProperties(Map.of(USER_ID, prmInfo.customerId, PUISSANCE, prmInfo.contractInfo.subscribedPower, PRM_ID, prmInfo.prmId)); @@ -206,6 +211,24 @@ public class LinkyHandler extends BaseThingHandler { // updateMonthlyData(); // updateYearlyData(); + String tempoData = getTempoData(); + + // LinkedTreeMap obj = gson.fromJson(tempoData, LinkedTreeMap.class); + + /* + * + * + * ArrayList list = new ArrayList(); + * for (Object key : obj.keySet()) { + * Object val = obj.get(key); + * + * Pair keyValue = new ImmutablePair(key, val); + * list.add(keyValue); + * } + */ + + updateState(TEST_SELECT, new StringType(tempoData)); + if (!connectedBefore && isConnected()) { disconnect(); } @@ -441,6 +464,23 @@ public class LinkyHandler extends BaseThingHandler { * */ + 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 boolean isConnected() { EnedisHttpApi api = this.enedisApi; return api == null ? false : api.isConnected(); @@ -618,4 +658,34 @@ public class LinkyHandler extends BaseThingHandler { * */ + private void logData(IntervalReading[] ivArray, String title, DateTimeFormatter dateTimeFormatter, Target target) { + + if (logger.isDebugEnabled()) { + int size = ivArray.length; + + if (target == Target.FIRST) { + if (size > 0) { + logData(ivArray, 0, title, dateTimeFormatter); + } + } else if (target == Target.LAST) { + if (size > 0) { + logData(ivArray, size - 1, title, dateTimeFormatter); + } + } else { + for (int i = 0; i < size; i++) { + logData(ivArray, i, title, dateTimeFormatter); + } + } + } + } + + private void logData(IntervalReading[] ivArray, int index, String title, DateTimeFormatter dateTimeFormatter) { + IntervalReading iv = ivArray[index]; + logger.debug("{} {} value {}", title, iv.date.format(dateTimeFormatter), iv.value); + } + + 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 b2725d14847..f6d4ff1a4f0 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,12 +12,18 @@ https://espace-client-particuliers.enedis.fr/web/espace-particuliers/compteur-linky. + + + + + + @@ -29,6 +35,8 @@ Your Enedis token (can be left empty, use the connection page to automatically fill it http://youopenhab/connectlinky) + + @@ -105,6 +113,22 @@ + + + + + + + + + + + + + String + + Test Select + Number:Energy diff --git a/bundles/org.openhab.binding.linky/src/main/resources/templates/enedis.png b/bundles/org.openhab.binding.linky/src/main/resources/templates/enedis.png new file mode 100644 index 00000000000..a28c3f31bbd Binary files /dev/null and b/bundles/org.openhab.binding.linky/src/main/resources/templates/enedis.png differ diff --git a/bundles/org.openhab.binding.linky/src/main/resources/templates/enedisSmall.png b/bundles/org.openhab.binding.linky/src/main/resources/templates/enedisSmall.png new file mode 100644 index 00000000000..159281d2c39 Binary files /dev/null and b/bundles/org.openhab.binding.linky/src/main/resources/templates/enedisSmall.png differ 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 0a637709206..fa66b86f72c 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 @@ -12,11 +12,6 @@ html { font-family: "Roboto", Helvetica, Arial, sans-serif; } -.logo { - display: block; - margin: auto; - width: 100%; -} .block { border: 1px solid #bbb; @@ -37,7 +32,70 @@ html { } .button { + margin-left:30px; margin-bottom: 10px; + float:left; +} + +.olList { + margin-top:20px; +} + +.olList li { + margin:20px; +} + + + + +.box { + float:left; + transform: translate(0%, -30%); +} + +.box select { + background-color: #0563af; + color: white; + padding: 12px; + width: 350px; + border: none; + font-size: 20px; + box-shadow: 0 5px 25px rgba(0, 0, 0, 0.2); + -webkit-appearance: button; + appearance: button; + outline: none; +} + +/* Style the arrow inside the select element: */ +.box::before { + content: "\f13a"; + font-family: FontAwesome; + position: absolute; + top: 0; + right: 0; + width: 50px; + height: 100%; + text-align: center; + font-size: 28px; + line-height: 45px; + color: rgba(255, 255, 255, 0.5); + background-color: rgba(255, 255, 255, 0.1); + pointer-events: none; +} + + +.logo { + display: inline; +} + +.logoEnedis { + display: inline; +} + +.logoTransfer { + display: inline; + margin-left:300px; + top: -30px; } .button a { @@ -51,22 +109,55 @@ html { text-decoration: none; } + + - + + +


+

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:
+

    +
  1. 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.
    +
  2. + +
  3. 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 : + +
+ + +