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 50581eabf2d..2f9a706ce63 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 @@ -256,6 +256,7 @@ public class EnedisHttpApi { if (data.isEmpty()) { throw new LinkyException(String.format("Requesting '%s' returned an empty response", url)); } + logger.trace("getData returned {}", data); try { ConsumptionReport report = gson.fromJson(data, ConsumptionReport.class); return report.firstLevel.consumptions; 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 36a8ec9ab27..e0bd586d62c 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 @@ -80,9 +80,9 @@ public class LinkyCommandExtension extends AbstractConsoleCommandExtension { console.println(String.format("'%s' is not a Linky thing id", args[0])); printUsage(console); } else if (REPORT.equals(args[1])) { - LocalDate now = LocalDate.now(); - LocalDate start = now.minusDays(7); - LocalDate end = now.minusDays(1); + LocalDate yesterday = LocalDate.now().minusDays(1); + LocalDate start = yesterday.minusDays(6); + LocalDate end = yesterday; String separator = " "; if (args.length >= 3) { try { @@ -104,13 +104,13 @@ public class LinkyCommandExtension extends AbstractConsoleCommandExtension { return; } } - if (!start.isBefore(now) || start.isAfter(end)) { + if (start.isAfter(yesterday) || start.isAfter(end)) { console.println("Start day must be in the past and before the end day"); printUsage(console); return; } - if (end.isAfter(now.minusDays(1))) { - end = now.minusDays(1); + if (end.isAfter(yesterday)) { + end = yesterday; } if (args.length >= 5) { separator = args[4]; 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 2da73b75370..f2badd66e6f 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 @@ -65,8 +65,8 @@ import com.google.gson.Gson; public class LinkyHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(LinkyHandler.class); - private static final int REFRESH_FIRST_HOUR_OF_DAY = 5; - private static final int REFRESH_INTERVAL_IN_MIN = 360; + private static final int REFRESH_FIRST_HOUR_OF_DAY = 1; + private static final int REFRESH_INTERVAL_IN_MIN = 120; private final HttpClient httpClient; private final Gson gson; @@ -91,23 +91,48 @@ public class LinkyHandler extends BaseThingHandler { this.cachedDailyData = new ExpiringDayCache<>("daily cache", REFRESH_FIRST_HOUR_OF_DAY, () -> { LocalDate today = LocalDate.now(); - return getConsumptionData(today.minusDays(15), today); + Consumption consumption = getConsumptionData(today.minusDays(15), today); + if (consumption != null) { + logData(consumption.aggregats.days, "Day", false, DateTimeFormatter.ISO_LOCAL_DATE, false); + logData(consumption.aggregats.weeks, "Week", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, false); + consumption = getConsumptionAfterChecks(consumption); + } + return consumption; }); this.cachedPowerData = new ExpiringDayCache<>("power cache", REFRESH_FIRST_HOUR_OF_DAY, () -> { LocalDate to = LocalDate.now().plusDays(1); LocalDate from = to.minusDays(2); - return getPowerData(from, to); + Consumption consumption = getPowerData(from, to); + if (consumption != null) { + try { + checkData(consumption); + } catch (LinkyException e) { + logger.debug("Power data: {}", e.getMessage()); + return null; + } + } + return consumption; }); this.cachedMonthlyData = new ExpiringDayCache<>("monthly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> { LocalDate today = LocalDate.now(); - return getConsumptionData(today.withDayOfMonth(1).minusMonths(1), today); + Consumption consumption = getConsumptionData(today.withDayOfMonth(1).minusMonths(1), today); + if (consumption != null) { + logData(consumption.aggregats.months, "Month", true, DateTimeFormatter.ISO_LOCAL_DATE_TIME, false); + consumption = getConsumptionAfterChecks(consumption); + } + return consumption; }); this.cachedYearlyData = new ExpiringDayCache<>("yearly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> { LocalDate today = LocalDate.now(); - return getConsumptionData(LocalDate.of(today.getYear() - 1, 1, 1), today); + 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, false); + consumption = getConsumptionAfterChecks(consumption); + } + return consumption; }); } @@ -178,12 +203,8 @@ public class LinkyHandler extends BaseThingHandler { if (isLinked(PEAK_POWER) || isLinked(PEAK_TIMESTAMP)) { cachedPowerData.getValue().ifPresent(values -> { Aggregate days = values.aggregats.days; - if (days.datas.size() == 0 || days.periodes.size() == 0) { - logger.debug("Daily power data are without any period/data"); - } else { - updateVAChannel(PEAK_POWER, days.datas.get(0)); - updateState(PEAK_TIMESTAMP, new DateTimeType(days.periodes.get(0).dateDebut)); - } + updateVAChannel(PEAK_POWER, days.datas.get(0)); + updateState(PEAK_TIMESTAMP, new DateTimeType(days.periodes.get(0).dateDebut)); }); } } @@ -192,30 +213,10 @@ public class LinkyHandler extends BaseThingHandler { * Request new dayly/weekly data and updates channels */ private synchronized void updateDailyData() { - if (isLinked(YESTERDAY) || isLinked(THIS_WEEK)) { + if (isLinked(YESTERDAY)) { cachedDailyData.getValue().ifPresent(values -> { Aggregate days = values.aggregats.days; - if (days.periodes.size() > days.datas.size()) { - logger.debug("Daily data are invalid: not a data for each period"); - return; - } - int maxValue = days.periodes.size() - 1; - int thisWeekNumber = days.periodes.get(maxValue).dateDebut.get(weekFields.weekOfWeekBasedYear()); - double yesterday = days.datas.get(maxValue); - double thisWeek = 0.00; - - for (int i = maxValue; i >= 0; i--) { - int weekNumber = days.periodes.get(i).dateDebut.get(weekFields.weekOfWeekBasedYear()); - if (weekNumber == thisWeekNumber) { - Double value = days.datas.get(i); - thisWeek += !value.isNaN() ? value : 0; - } else { - break; - } - } - - updateKwhChannel(YESTERDAY, yesterday); - updateKwhChannel(THIS_WEEK, thisWeek); + updateKwhChannel(YESTERDAY, days.datas.get(days.datas.size() - 1)); }); } } @@ -224,14 +225,19 @@ public class LinkyHandler extends BaseThingHandler { * Request new weekly data and updates channels */ private synchronized void updateWeeklyData() { - if (isLinked(LAST_WEEK)) { + if (isLinked(LAST_WEEK) || isLinked(THIS_WEEK)) { cachedDailyData.getValue().ifPresent(values -> { + Aggregate days = values.aggregats.days; + int idxLast = days.periodes.get(days.periodes.size() - 1).dateDebut.get(weekFields.dayOfWeek()) == 7 ? 2 + : 1; Aggregate weeks = values.aggregats.weeks; - if (weeks.datas.size() > 1) { - updateKwhChannel(LAST_WEEK, weeks.datas.get(1)); + if (weeks.datas.size() > idxLast) { + updateKwhChannel(LAST_WEEK, weeks.datas.get(idxLast)); + } + if (weeks.datas.size() > (idxLast + 1)) { + updateKwhChannel(THIS_WEEK, weeks.datas.get(idxLast + 1)); } else { - logger.debug("Weekly data are without last week data"); - updateKwhChannel(LAST_WEEK, Double.NaN); + updateKwhChannel(THIS_WEEK, 0.0); } }); } @@ -244,18 +250,11 @@ public class LinkyHandler extends BaseThingHandler { if (isLinked(LAST_MONTH) || isLinked(THIS_MONTH)) { cachedMonthlyData.getValue().ifPresent(values -> { Aggregate months = values.aggregats.months; - if (months.datas.size() == 0) { - logger.debug("Monthly data are without any data"); - updateKwhChannel(LAST_MONTH, Double.NaN); - updateKwhChannel(THIS_MONTH, Double.NaN); + updateKwhChannel(LAST_MONTH, months.datas.get(0)); + if (months.datas.size() > 1) { + updateKwhChannel(THIS_MONTH, months.datas.get(1)); } else { - updateKwhChannel(LAST_MONTH, months.datas.get(0)); - if (months.datas.size() > 1) { - updateKwhChannel(THIS_MONTH, months.datas.get(1)); - } else { - logger.debug("Monthly data are without current month data"); - updateKwhChannel(THIS_MONTH, Double.NaN); - } + updateKwhChannel(THIS_MONTH, 0.0); } }); } @@ -268,18 +267,11 @@ public class LinkyHandler extends BaseThingHandler { if (isLinked(LAST_YEAR) || isLinked(THIS_YEAR)) { cachedYearlyData.getValue().ifPresent(values -> { Aggregate years = values.aggregats.years; - if (years.datas.size() == 0) { - logger.debug("Yearly data are without any data"); - updateKwhChannel(LAST_YEAR, Double.NaN); - updateKwhChannel(THIS_YEAR, Double.NaN); + updateKwhChannel(LAST_YEAR, years.datas.get(0)); + if (years.datas.size() > 1) { + updateKwhChannel(THIS_YEAR, years.datas.get(1)); } else { - updateKwhChannel(LAST_YEAR, years.datas.get(0)); - if (years.datas.size() > 1) { - updateKwhChannel(THIS_YEAR, years.datas.get(1)); - } else { - logger.debug("Yearly data are without current year data"); - updateKwhChannel(THIS_YEAR, Double.NaN); - } + updateKwhChannel(THIS_YEAR, 0.0); } }); } @@ -315,10 +307,12 @@ public class LinkyHandler extends BaseThingHandler { List report = new ArrayList<>(); if (startDay.getYear() == endDay.getYear() && startDay.getMonthValue() == endDay.getMonthValue()) { // All values in the same month - Consumption result = getConsumptionData(startDay, endDay); + Consumption result = getConsumptionData(startDay, endDay.plusDays(1)); if (result != null) { Aggregate days = result.aggregats.days; - for (int i = 0; i < days.datas.size(); i++) { + 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) { @@ -354,7 +348,9 @@ public class LinkyHandler extends BaseThingHandler { EnedisHttpApi api = this.enedisApi; if (api != null) { try { - return api.getEnergyData(userId, prmId, from, to); + Consumption consumption = api.getEnergyData(userId, prmId, from, to); + updateStatus(ThingStatus.ONLINE); + return consumption; } catch (LinkyException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); } @@ -368,7 +364,9 @@ public class LinkyHandler extends BaseThingHandler { EnedisHttpApi api = this.enedisApi; if (api != null) { try { - return api.getPowerData(userId, prmId, from, to); + Consumption consumption = api.getPowerData(userId, prmId, from, to); + updateStatus(ThingStatus.ONLINE); + return consumption; } catch (LinkyException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); } @@ -410,10 +408,10 @@ public class LinkyHandler extends BaseThingHandler { boolean connectedBefore = isConnected(); switch (channelUID.getId()) { case YESTERDAY: - case THIS_WEEK: updateDailyData(); break; case LAST_WEEK: + case THIS_WEEK: updateWeeklyData(); break; case LAST_MONTH: @@ -438,4 +436,80 @@ public class LinkyHandler extends BaseThingHandler { logger.debug("The Linky binding is read-only and can not handle command {}", command); } } + + private @Nullable Consumption getConsumptionAfterChecks(Consumption consumption) { + try { + checkData(consumption); + } catch (LinkyException e) { + logger.debug("Consumption data: {}", e.getMessage()); + return null; + } + if (!isDataLastDayAvailable(consumption)) { + logger.debug("Data including yesterday are not yet available"); + return null; + } + return consumption; + } + + public void checkData(Consumption consumption) throws LinkyException { + if (consumption.aggregats.days.periodes.size() == 0) { + throw new LinkyException("invalid consumptions data: no day period"); + } + if (consumption.aggregats.days.periodes.size() != consumption.aggregats.days.datas.size()) { + throw new LinkyException("invalid consumptions data: not one data for each day period"); + } + if (consumption.aggregats.weeks.periodes.size() == 0) { + 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 one data for each week period"); + } + if (consumption.aggregats.months.periodes.size() == 0) { + 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 one data for each month period"); + } + if (consumption.aggregats.years.periodes.size() == 0) { + 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 one data for each year period"); + } + } + + private boolean isDataLastDayAvailable(Consumption consumption) { + Aggregate days = consumption.aggregats.days; + logData(days, "Last day", false, DateTimeFormatter.ISO_LOCAL_DATE, true); + return days.datas != null && days.datas.size() > 0 && !days.datas.get(days.datas.size() - 1).isNaN(); + } + + private void logData(Aggregate aggregate, String title, boolean withDateFin, DateTimeFormatter dateTimeFormatter, + boolean onlyLast) { + 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 (onlyLast) { + 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)); + } + } }