This commit is contained in:
lo92fr 2025-01-08 22:48:27 +01:00 committed by GitHub
commit 9788bb2f18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 116 additions and 70 deletions

View File

@ -26,6 +26,7 @@ public class LinkyConfiguration {
public String username = ""; public String username = "";
public String password = ""; public String password = "";
public String internalAuthId = ""; public String internalAuthId = "";
public String timezone = "";
public boolean seemsValid() { public boolean seemsValid() {
return !username.isBlank() && !password.isBlank() && !internalAuthId.isBlank(); return !username.isBlank() && !password.isBlank() && !internalAuthId.isBlank();

View File

@ -12,12 +12,16 @@
*/ */
package org.openhab.binding.linky.internal; package org.openhab.binding.linky.internal;
import static java.time.temporal.ChronoField.*;
import static org.openhab.binding.linky.internal.LinkyBindingConstants.THING_TYPE_LINKY; import static org.openhab.binding.linky.internal.LinkyBindingConstants.THING_TYPE_LINKY;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
@ -28,6 +32,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.openhab.binding.linky.internal.handler.LinkyHandler; import org.openhab.binding.linky.internal.handler.LinkyHandler;
import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.io.net.http.TrustAllTrustManager; import org.openhab.core.io.net.http.TrustAllTrustManager;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
@ -55,21 +60,43 @@ import com.google.gson.JsonDeserializer;
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky") @Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky")
public class LinkyHandlerFactory extends BaseThingHandlerFactory { 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_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 = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-dd'T'HH:mm").optionalStart().appendLiteral(':').appendValue(SECOND_OF_MINUTE, 2)
.optionalStart().appendFraction(NANO_OF_SECOND, 0, 9, true).toFormatter();
private static final int REQUEST_BUFFER_SIZE = 8000; private static final int REQUEST_BUFFER_SIZE = 8000;
private static final int RESPONSE_BUFFER_SIZE = 200000; private static final int RESPONSE_BUFFER_SIZE = 200000;
private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class); private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
private final Gson gson = new GsonBuilder().registerTypeAdapter(ZonedDateTime.class, private final Gson gson = new GsonBuilder()
(JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> ZonedDateTime .registerTypeAdapter(ZonedDateTime.class,
.parse(json.getAsJsonPrimitive().getAsString(), LINKY_FORMATTER)) (JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> ZonedDateTime
.parse(json.getAsJsonPrimitive().getAsString(), LINKY_FORMATTER))
.registerTypeAdapter(LocalDate.class,
(JsonDeserializer<LocalDate>) (json, type, jsonDeserializationContext) -> LocalDate
.parse(json.getAsJsonPrimitive().getAsString(), LINKY_LOCALDATE_FORMATTER))
.registerTypeAdapter(LocalDateTime.class,
(JsonDeserializer<LocalDateTime>) (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(); .create();
private final LocaleProvider localeProvider; private final LocaleProvider localeProvider;
private final HttpClient httpClient; private final HttpClient httpClient;
private final TimeZoneProvider timeZoneProvider;
@Activate @Activate
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider, public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
final @Reference HttpClientFactory httpClientFactory) { final @Reference HttpClientFactory httpClientFactory, final @Reference TimeZoneProvider timeZoneProvider) {
this.localeProvider = localeProvider; this.localeProvider = localeProvider;
this.timeZoneProvider = timeZoneProvider;
SslContextFactory sslContextFactory = new SslContextFactory.Client(); SslContextFactory sslContextFactory = new SslContextFactory.Client();
try { try {
SSLContext sslContext = SSLContext.getInstance("SSL"); SSLContext sslContext = SSLContext.getInstance("SSL");
@ -114,7 +141,8 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory {
@Override @Override
protected @Nullable ThingHandler createHandler(Thing thing) { protected @Nullable ThingHandler createHandler(Thing thing) {
return supportsThingType(thing.getThingTypeUID()) ? new LinkyHandler(thing, localeProvider, gson, httpClient) return supportsThingType(thing.getThingTypeUID())
? new LinkyHandler(thing, localeProvider, gson, httpClient, timeZoneProvider)
: null; : null;
} }
} }

View File

@ -62,7 +62,7 @@ import com.google.gson.JsonSyntaxException;
*/ */
@NonNullByDefault @NonNullByDefault
public class EnedisHttpApi { public class EnedisHttpApi {
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy"); private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final String ENEDIS_DOMAIN = ".enedis.fr"; 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_APPS_LINCS = "https://alex.microapplications" + ENEDIS_DOMAIN;
private static final String URL_MON_COMPTE = "https://mon-compte" + ENEDIS_DOMAIN; private static final String URL_MON_COMPTE = "https://mon-compte" + ENEDIS_DOMAIN;
@ -70,10 +70,10 @@ public class EnedisHttpApi {
private static final String URL_ENEDIS_AUTHENTICATE = URL_APPS_LINCS + "/authenticate?target=" + URL_COMPTE_PART; 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_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 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_BASE_URL = URL_APPS_LINCS + "/mes-mesures-prm/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 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 private static final String MEASURE_URL = PRM_INFO_BASE_URL
+ "%s/prms/%s/donnees-%s?dateDebut=%s&dateFin=%s&mesuretypecode=CONS"; + "%s/prms/%s/donnees-energetiques?mesuresTypeCode=%s&mesuresCorrigees=false&typeDonnees=CONS&dateDebut=%s";
private static final URI COOKIE_URI = URI.create(URL_COMPTE_PART); private static final URI COOKIE_URI = URI.create(URL_COMPTE_PART);
private static final Pattern REQ_PATTERN = Pattern.compile("ReqID%(.*?)%26"); private static final Pattern REQ_PATTERN = Pattern.compile("ReqID%(.*?)%26");
@ -289,17 +289,16 @@ public class EnedisHttpApi {
private Consumption getMeasures(String userId, String prmId, LocalDate from, LocalDate to, String request) private Consumption getMeasures(String userId, String prmId, LocalDate from, LocalDate to, String request)
throws LinkyException { throws LinkyException {
String url = String.format(MEASURE_URL, userId, prmId, request, from.format(API_DATE_FORMAT), 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); ConsumptionReport report = getData(url, ConsumptionReport.class);
return report.firstLevel.consumptions; return report.consumptions;
} }
public Consumption getEnergyData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException { 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, "ENERGIE");
} }
public Consumption getPowerData(String userId, String prmId, LocalDate from, LocalDate to) throws LinkyException { 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, "PMAX");
} }
} }

View File

@ -12,7 +12,8 @@
*/ */
package org.openhab.binding.linky.internal.dto; package org.openhab.binding.linky.internal.dto;
import java.time.ZonedDateTime; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
@ -22,28 +23,30 @@ import com.google.gson.annotations.SerializedName;
* returned by API calls * returned by API calls
* *
* @author Gaël L'hopital - Initial contribution * @author Gaël L'hopital - Initial contribution
* @author Laurent ARNAL - fix to handle new Dto format after enedis site modifications
*/ */
public class ConsumptionReport { public class ConsumptionReport {
public class Period {
public String grandeurPhysiqueEnum; public class Data {
public ZonedDateTime dateDebut; public LocalDateTime dateDebut;
public ZonedDateTime dateFin; public LocalDateTime dateFin;
public Double valeur;
} }
public class Aggregate { public class Aggregate {
public List<String> labels; @SerializedName("donnees")
public List<Period> periodes; public List<Data> datas;
public List<Double> datas; public String unite;
} }
public class ChronoData { public class ChronoData {
@SerializedName("JOUR") @SerializedName("jour")
public Aggregate days; public Aggregate days;
@SerializedName("SEMAINE") @SerializedName("semaine")
public Aggregate weeks; public Aggregate weeks;
@SerializedName("MOIS") @SerializedName("mois")
public Aggregate months; public Aggregate months;
@SerializedName("ANNEE") @SerializedName("annee")
public Aggregate years; public Aggregate years;
} }
@ -51,14 +54,10 @@ public class ConsumptionReport {
public ChronoData aggregats; public ChronoData aggregats;
public String grandeurMetier; public String grandeurMetier;
public String grandeurPhysique; public String grandeurPhysique;
public String unite; public LocalDate dateDebut;
public LocalDate dateFin;
} }
public class FirstLevel { @SerializedName("cons")
@SerializedName("CONS") public Consumption consumptions;
public Consumption consumptions;
}
@SerializedName("1")
public FirstLevel firstLevel;
} }

View File

@ -16,6 +16,7 @@ import static org.openhab.binding.linky.internal.LinkyBindingConstants.*;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
@ -38,7 +39,9 @@ import org.openhab.binding.linky.internal.dto.ConsumptionReport.Consumption;
import org.openhab.binding.linky.internal.dto.PrmDetail; import org.openhab.binding.linky.internal.dto.PrmDetail;
import org.openhab.binding.linky.internal.dto.PrmInfo; import org.openhab.binding.linky.internal.dto.PrmInfo;
import org.openhab.binding.linky.internal.dto.UserInfo; import org.openhab.binding.linky.internal.dto.UserInfo;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.MetricPrefix; import org.openhab.core.library.unit.MetricPrefix;
@ -65,6 +68,9 @@ import com.google.gson.Gson;
@NonNullByDefault @NonNullByDefault
public class LinkyHandler extends BaseThingHandler { public class LinkyHandler extends BaseThingHandler {
private final TimeZoneProvider timeZoneProvider;
private ZoneId zoneId = ZoneId.systemDefault();
private static final int REFRESH_FIRST_HOUR_OF_DAY = 1; private static final int REFRESH_FIRST_HOUR_OF_DAY = 1;
private static final int REFRESH_INTERVAL_IN_MIN = 120; private static final int REFRESH_INTERVAL_IN_MIN = 120;
@ -90,11 +96,13 @@ public class LinkyHandler extends BaseThingHandler {
ALL ALL
} }
public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient) { public LinkyHandler(Thing thing, LocaleProvider localeProvider, Gson gson, HttpClient httpClient,
TimeZoneProvider timeZoneProvider) {
super(thing); super(thing);
this.gson = gson; this.gson = gson;
this.httpClient = httpClient; this.httpClient = httpClient;
this.weekFields = WeekFields.of(localeProvider.getLocale()); this.weekFields = WeekFields.of(localeProvider.getLocale());
this.timeZoneProvider = timeZoneProvider;
this.cachedDailyData = new ExpiringDayCache<>("daily cache", REFRESH_FIRST_HOUR_OF_DAY, () -> { this.cachedDailyData = new ExpiringDayCache<>("daily cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate today = LocalDate.now(); LocalDate today = LocalDate.now();
@ -150,6 +158,19 @@ public class LinkyHandler extends BaseThingHandler {
logger.debug("Initializing Linky handler."); logger.debug("Initializing Linky handler.");
updateStatus(ThingStatus.UNKNOWN); updateStatus(ThingStatus.UNKNOWN);
// update the timezone if not set to default to openhab default timezone
Configuration thingConfig = getConfig();
String val = (String) thingConfig.get("timezone");
if (val == null || val.isBlank()) {
zoneId = this.timeZoneProvider.getTimeZone();
thingConfig.put("timezone", zoneId.getId());
} else {
zoneId = ZoneId.of(val);
}
updateConfiguration(thingConfig);
LinkyConfiguration config = getConfigAs(LinkyConfiguration.class); LinkyConfiguration config = getConfigAs(LinkyConfiguration.class);
if (config.seemsValid()) { if (config.seemsValid()) {
enedisApi = new EnedisHttpApi(config, gson, httpClient); enedisApi = new EnedisHttpApi(config, gson, httpClient);
@ -211,8 +232,9 @@ public class LinkyHandler extends BaseThingHandler {
if (isLinked(PEAK_POWER) || isLinked(PEAK_TIMESTAMP)) { if (isLinked(PEAK_POWER) || isLinked(PEAK_TIMESTAMP)) {
cachedPowerData.getValue().ifPresentOrElse(values -> { cachedPowerData.getValue().ifPresentOrElse(values -> {
Aggregate days = values.aggregats.days; Aggregate days = values.aggregats.days;
updatekVAChannel(PEAK_POWER, days.datas.get(days.datas.size() - 1)); updatekVAChannel(PEAK_POWER, days.datas.get(days.datas.size() - 1).valeur);
updateState(PEAK_TIMESTAMP, new DateTimeType(days.periodes.get(days.datas.size() - 1).dateDebut)); updateState(PEAK_TIMESTAMP,
new DateTimeType(days.datas.get(days.datas.size() - 1).dateDebut.atZone(zoneId)));
}, () -> { }, () -> {
updateKwhChannel(PEAK_POWER, Double.NaN); updateKwhChannel(PEAK_POWER, Double.NaN);
updateState(PEAK_TIMESTAMP, UnDefType.UNDEF); updateState(PEAK_TIMESTAMP, UnDefType.UNDEF);
@ -224,9 +246,9 @@ public class LinkyHandler extends BaseThingHandler {
double currentValue = 0.0; double currentValue = 0.0;
double previousValue = 0.0; double previousValue = 0.0;
if (!periods.datas.isEmpty()) { if (!periods.datas.isEmpty()) {
currentValue = periods.datas.get(periods.datas.size() - 1); currentValue = periods.datas.get(periods.datas.size() - 1).valeur;
if (periods.datas.size() > 1) { if (periods.datas.size() > 1) {
previousValue = periods.datas.get(periods.datas.size() - 2); previousValue = periods.datas.get(periods.datas.size() - 2).valeur;
} }
} }
updateKwhChannel(currentChannel, currentValue); updateKwhChannel(currentChannel, currentValue);
@ -240,7 +262,7 @@ public class LinkyHandler extends BaseThingHandler {
if (isLinked(YESTERDAY) || isLinked(LAST_WEEK) || isLinked(THIS_WEEK)) { if (isLinked(YESTERDAY) || isLinked(LAST_WEEK) || isLinked(THIS_WEEK)) {
cachedDailyData.getValue().ifPresentOrElse(values -> { cachedDailyData.getValue().ifPresentOrElse(values -> {
Aggregate days = values.aggregats.days; Aggregate days = values.aggregats.days;
updateKwhChannel(YESTERDAY, days.datas.get(days.datas.size() - 1)); updateKwhChannel(YESTERDAY, days.datas.get(days.datas.size() - 1).valeur);
setCurrentAndPrevious(values.aggregats.weeks, THIS_WEEK, LAST_WEEK); setCurrentAndPrevious(values.aggregats.weeks, THIS_WEEK, LAST_WEEK);
}, () -> { }, () -> {
updateKwhChannel(YESTERDAY, Double.NaN); updateKwhChannel(YESTERDAY, Double.NaN);
@ -322,16 +344,15 @@ public class LinkyHandler extends BaseThingHandler {
Consumption result = getConsumptionData(startDay, endDay.plusDays(1)); Consumption result = getConsumptionData(startDay, endDay.plusDays(1));
if (result != null) { if (result != null) {
Aggregate days = result.aggregats.days; Aggregate days = result.aggregats.days;
int size = (days.datas == null || days.periodes == null) ? 0 int size = (days.datas == null) ? 0 : days.datas.size();
: (days.datas.size() <= days.periodes.size() ? days.datas.size() : days.periodes.size());
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
double consumption = days.datas.get(i); double consumption = days.datas.get(i).valeur;
LocalDate day = days.periodes.get(i).dateDebut.toLocalDate(); LocalDate day = days.datas.get(i).dateDebut.toLocalDate();
// Filter data in case it contains data from dates outside the requested period // Filter data in case it contains data from dates outside the requested period
if (day.isBefore(startDay) || day.isAfter(endDay)) { if (day.isBefore(startDay) || day.isAfter(endDay)) {
continue; continue;
} }
String line = days.periodes.get(i).dateDebut.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator; String line = days.datas.get(i).dateDebut.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator;
if (consumption >= 0) { if (consumption >= 0) {
line += String.valueOf(consumption); line += String.valueOf(consumption);
} }
@ -474,50 +495,36 @@ public class LinkyHandler extends BaseThingHandler {
} }
private void checkData(Consumption consumption) throws LinkyException { private void checkData(Consumption consumption) throws LinkyException {
if (consumption.aggregats.days.periodes.isEmpty()) { if (consumption.aggregats.days.datas.isEmpty()) {
throw new LinkyException("Invalid consumptions data: no day period"); throw new LinkyException("Invalid consumptions data: no day period");
} }
if (consumption.aggregats.days.periodes.size() != consumption.aggregats.days.datas.size()) { if (consumption.aggregats.weeks != null && consumption.aggregats.weeks.datas.isEmpty()) {
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"); throw new LinkyException("Invalid consumptions data: no week period");
} }
if (consumption.aggregats.weeks.periodes.size() != consumption.aggregats.weeks.datas.size()) { if (consumption.aggregats.months != null && consumption.aggregats.months.datas.isEmpty()) {
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"); throw new LinkyException("Invalid consumptions data: no month period");
} }
if (consumption.aggregats.months.periodes.size() != consumption.aggregats.months.datas.size()) { if (consumption.aggregats.years != null && consumption.aggregats.years.datas.isEmpty()) {
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"); 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");
}
} }
private boolean isDataFirstDayAvailable(Consumption consumption) { private boolean isDataFirstDayAvailable(Consumption consumption) {
Aggregate days = consumption.aggregats.days; Aggregate days = consumption.aggregats.days;
logData(days, "First day", false, DateTimeFormatter.ISO_LOCAL_DATE, Target.FIRST); logData(days, "First day", false, DateTimeFormatter.ISO_LOCAL_DATE, Target.FIRST);
return days.datas != null && !days.datas.isEmpty() && !days.datas.get(0).isNaN(); return days.datas != null && !days.datas.isEmpty() && !days.datas.get(0).valeur.isNaN();
} }
private boolean isDataLastDayAvailable(Consumption consumption) { private boolean isDataLastDayAvailable(Consumption consumption) {
Aggregate days = consumption.aggregats.days; Aggregate days = consumption.aggregats.days;
logData(days, "Last day", false, DateTimeFormatter.ISO_LOCAL_DATE, Target.LAST); 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(); return days.datas != null && !days.datas.isEmpty() && !days.datas.get(days.datas.size() - 1).valeur.isNaN();
} }
private void logData(Aggregate aggregate, String title, boolean withDateFin, DateTimeFormatter dateTimeFormatter, private void logData(Aggregate aggregate, String title, boolean withDateFin, DateTimeFormatter dateTimeFormatter,
Target target) { Target target) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
int size = (aggregate.datas == null || aggregate.periodes == null) ? 0 int size = (aggregate.datas == null) ? 0 : aggregate.datas.size();
: (aggregate.datas.size() <= aggregate.periodes.size() ? aggregate.datas.size()
: aggregate.periodes.size());
if (target == Target.FIRST) { if (target == Target.FIRST) {
if (size > 0) { if (size > 0) {
logData(aggregate, 0, title, withDateFin, dateTimeFormatter); logData(aggregate, 0, title, withDateFin, dateTimeFormatter);
@ -537,11 +544,11 @@ public class LinkyHandler extends BaseThingHandler {
private void logData(Aggregate aggregate, int index, String title, boolean withDateFin, private void logData(Aggregate aggregate, int index, String title, boolean withDateFin,
DateTimeFormatter dateTimeFormatter) { DateTimeFormatter dateTimeFormatter) {
if (withDateFin) { if (withDateFin) {
logger.debug("{} {} {} value {}", title, aggregate.periodes.get(index).dateDebut.format(dateTimeFormatter), logger.debug("{} {} {} value {}", title, aggregate.datas.get(index).dateDebut.format(dateTimeFormatter),
aggregate.periodes.get(index).dateFin.format(dateTimeFormatter), aggregate.datas.get(index)); aggregate.datas.get(index).dateFin.format(dateTimeFormatter), aggregate.datas.get(index).valeur);
} else { } else {
logger.debug("{} {} value {}", title, aggregate.periodes.get(index).dateDebut.format(dateTimeFormatter), logger.debug("{} {} value {}", title, aggregate.datas.get(index).dateDebut.format(dateTimeFormatter),
aggregate.datas.get(index)); aggregate.datas.get(index).valeur);
} }
} }
} }

View File

@ -14,6 +14,8 @@ thing-type.config.linky.linky.internalAuthId.label = Auth ID
thing-type.config.linky.linky.internalAuthId.description = Authentication ID delivered after the captcha (see documentation). thing-type.config.linky.linky.internalAuthId.description = Authentication ID delivered after the captcha (see documentation).
thing-type.config.linky.linky.password.label = Password thing-type.config.linky.linky.password.label = Password
thing-type.config.linky.linky.password.description = Your Enedis Password thing-type.config.linky.linky.password.description = Your Enedis Password
thing-type.config.linky.linky.timezone.label = timezone
thing-type.config.linky.linky.timezone.description = The timezone associated with your Point of delivery. Will default to openHAB default timezone. You will need to change this if your Linky is located in a different timezone that your openHAB location. You can use an offset, or a label like Europe/Paris
thing-type.config.linky.linky.username.label = Username thing-type.config.linky.linky.username.label = Username
thing-type.config.linky.linky.username.description = Your Enedis Username thing-type.config.linky.linky.username.description = Your Enedis Username
@ -41,6 +43,6 @@ channel-type.linky.power.label = Yesterday Peak Power
channel-type.linky.power.description = Maximum power usage yesterday channel-type.linky.power.description = Maximum power usage yesterday
channel-type.linky.timestamp.label = Timestamp channel-type.linky.timestamp.label = Timestamp
# Thing status descriptions # thing status descriptions
offline.config-error-mandatory-settings = Username, password and authId are mandatory. offline.config-error-mandatory-settings = Username, password and authId are mandatory.

View File

@ -34,6 +34,16 @@
<label>Auth ID</label> <label>Auth ID</label>
<description>Authentication ID delivered after the captcha (see documentation).</description> <description>Authentication ID delivered after the captcha (see documentation).</description>
</parameter> </parameter>
<parameter name="timezone" type="text" required="false">
<label>timezone</label>
<description>The timezone associated with your Point of delivery.
Will default to openHAB default timezone.
You will
need to change this if your Linky is located in a different timezone that your openHAB location.
You can use an
offset, or a label like Europe/Paris</description>
</parameter>
</config-description> </config-description>
</thing-type> </thing-type>