[linky] Handle case when data from yesterday is still NaN (#9423)

* [linky] Handle case when data from yesterday is still NaN

Fix #9386
* Refresh every 2 hours until data from yesterday are available
* Log yesterday even for month and year requests

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
pull/9586/head
lolodomo 2020-12-29 19:43:31 +01:00 committed by GitHub
parent e9b91327cc
commit 4c685adabc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 147 additions and 72 deletions

View File

@ -256,6 +256,7 @@ public class EnedisHttpApi {
if (data.isEmpty()) { if (data.isEmpty()) {
throw new LinkyException(String.format("Requesting '%s' returned an empty response", url)); throw new LinkyException(String.format("Requesting '%s' returned an empty response", url));
} }
logger.trace("getData returned {}", data);
try { try {
ConsumptionReport report = gson.fromJson(data, ConsumptionReport.class); ConsumptionReport report = gson.fromJson(data, ConsumptionReport.class);
return report.firstLevel.consumptions; return report.firstLevel.consumptions;

View File

@ -80,9 +80,9 @@ public class LinkyCommandExtension extends AbstractConsoleCommandExtension {
console.println(String.format("'%s' is not a Linky thing id", args[0])); console.println(String.format("'%s' is not a Linky thing id", args[0]));
printUsage(console); printUsage(console);
} else if (REPORT.equals(args[1])) { } else if (REPORT.equals(args[1])) {
LocalDate now = LocalDate.now(); LocalDate yesterday = LocalDate.now().minusDays(1);
LocalDate start = now.minusDays(7); LocalDate start = yesterday.minusDays(6);
LocalDate end = now.minusDays(1); LocalDate end = yesterday;
String separator = " "; String separator = " ";
if (args.length >= 3) { if (args.length >= 3) {
try { try {
@ -104,13 +104,13 @@ public class LinkyCommandExtension extends AbstractConsoleCommandExtension {
return; 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"); console.println("Start day must be in the past and before the end day");
printUsage(console); printUsage(console);
return; return;
} }
if (end.isAfter(now.minusDays(1))) { if (end.isAfter(yesterday)) {
end = now.minusDays(1); end = yesterday;
} }
if (args.length >= 5) { if (args.length >= 5) {
separator = args[4]; separator = args[4];

View File

@ -65,8 +65,8 @@ import com.google.gson.Gson;
public class LinkyHandler extends BaseThingHandler { public class LinkyHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(LinkyHandler.class); private final Logger logger = LoggerFactory.getLogger(LinkyHandler.class);
private static final int REFRESH_FIRST_HOUR_OF_DAY = 5; private static final int REFRESH_FIRST_HOUR_OF_DAY = 1;
private static final int REFRESH_INTERVAL_IN_MIN = 360; private static final int REFRESH_INTERVAL_IN_MIN = 120;
private final HttpClient httpClient; private final HttpClient httpClient;
private final Gson gson; private final Gson gson;
@ -91,23 +91,48 @@ public class LinkyHandler extends BaseThingHandler {
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();
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, () -> { this.cachedPowerData = new ExpiringDayCache<>("power cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate to = LocalDate.now().plusDays(1); LocalDate to = LocalDate.now().plusDays(1);
LocalDate from = to.minusDays(2); 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, () -> { this.cachedMonthlyData = new ExpiringDayCache<>("monthly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate today = LocalDate.now(); 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, () -> { this.cachedYearlyData = new ExpiringDayCache<>("yearly cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate today = LocalDate.now(); 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)) { if (isLinked(PEAK_POWER) || isLinked(PEAK_TIMESTAMP)) {
cachedPowerData.getValue().ifPresent(values -> { cachedPowerData.getValue().ifPresent(values -> {
Aggregate days = values.aggregats.days; Aggregate days = values.aggregats.days;
if (days.datas.size() == 0 || days.periodes.size() == 0) { updateVAChannel(PEAK_POWER, days.datas.get(0));
logger.debug("Daily power data are without any period/data"); updateState(PEAK_TIMESTAMP, new DateTimeType(days.periodes.get(0).dateDebut));
} else {
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 * Request new dayly/weekly data and updates channels
*/ */
private synchronized void updateDailyData() { private synchronized void updateDailyData() {
if (isLinked(YESTERDAY) || isLinked(THIS_WEEK)) { if (isLinked(YESTERDAY)) {
cachedDailyData.getValue().ifPresent(values -> { cachedDailyData.getValue().ifPresent(values -> {
Aggregate days = values.aggregats.days; Aggregate days = values.aggregats.days;
if (days.periodes.size() > days.datas.size()) { updateKwhChannel(YESTERDAY, days.datas.get(days.datas.size() - 1));
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);
}); });
} }
} }
@ -224,14 +225,19 @@ public class LinkyHandler extends BaseThingHandler {
* Request new weekly data and updates channels * Request new weekly data and updates channels
*/ */
private synchronized void updateWeeklyData() { private synchronized void updateWeeklyData() {
if (isLinked(LAST_WEEK)) { if (isLinked(LAST_WEEK) || isLinked(THIS_WEEK)) {
cachedDailyData.getValue().ifPresent(values -> { 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; Aggregate weeks = values.aggregats.weeks;
if (weeks.datas.size() > 1) { if (weeks.datas.size() > idxLast) {
updateKwhChannel(LAST_WEEK, weeks.datas.get(1)); updateKwhChannel(LAST_WEEK, weeks.datas.get(idxLast));
}
if (weeks.datas.size() > (idxLast + 1)) {
updateKwhChannel(THIS_WEEK, weeks.datas.get(idxLast + 1));
} else { } else {
logger.debug("Weekly data are without last week data"); updateKwhChannel(THIS_WEEK, 0.0);
updateKwhChannel(LAST_WEEK, Double.NaN);
} }
}); });
} }
@ -244,18 +250,11 @@ public class LinkyHandler extends BaseThingHandler {
if (isLinked(LAST_MONTH) || isLinked(THIS_MONTH)) { if (isLinked(LAST_MONTH) || isLinked(THIS_MONTH)) {
cachedMonthlyData.getValue().ifPresent(values -> { cachedMonthlyData.getValue().ifPresent(values -> {
Aggregate months = values.aggregats.months; Aggregate months = values.aggregats.months;
if (months.datas.size() == 0) { updateKwhChannel(LAST_MONTH, months.datas.get(0));
logger.debug("Monthly data are without any data"); if (months.datas.size() > 1) {
updateKwhChannel(LAST_MONTH, Double.NaN); updateKwhChannel(THIS_MONTH, months.datas.get(1));
updateKwhChannel(THIS_MONTH, Double.NaN);
} else { } else {
updateKwhChannel(LAST_MONTH, months.datas.get(0)); updateKwhChannel(THIS_MONTH, 0.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);
}
} }
}); });
} }
@ -268,18 +267,11 @@ public class LinkyHandler extends BaseThingHandler {
if (isLinked(LAST_YEAR) || isLinked(THIS_YEAR)) { if (isLinked(LAST_YEAR) || isLinked(THIS_YEAR)) {
cachedYearlyData.getValue().ifPresent(values -> { cachedYearlyData.getValue().ifPresent(values -> {
Aggregate years = values.aggregats.years; Aggregate years = values.aggregats.years;
if (years.datas.size() == 0) { updateKwhChannel(LAST_YEAR, years.datas.get(0));
logger.debug("Yearly data are without any data"); if (years.datas.size() > 1) {
updateKwhChannel(LAST_YEAR, Double.NaN); updateKwhChannel(THIS_YEAR, years.datas.get(1));
updateKwhChannel(THIS_YEAR, Double.NaN);
} else { } else {
updateKwhChannel(LAST_YEAR, years.datas.get(0)); updateKwhChannel(THIS_YEAR, 0.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);
}
} }
}); });
} }
@ -315,10 +307,12 @@ public class LinkyHandler extends BaseThingHandler {
List<String> report = new ArrayList<>(); List<String> report = new ArrayList<>();
if (startDay.getYear() == endDay.getYear() && startDay.getMonthValue() == endDay.getMonthValue()) { if (startDay.getYear() == endDay.getYear() && startDay.getMonthValue() == endDay.getMonthValue()) {
// All values in the same month // All values in the same month
Consumption result = getConsumptionData(startDay, endDay); Consumption result = getConsumptionData(startDay, endDay.plusDays(1));
if (result != null) { if (result != null) {
Aggregate days = result.aggregats.days; 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); double consumption = days.datas.get(i);
String line = days.periodes.get(i).dateDebut.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator; String line = days.periodes.get(i).dateDebut.format(DateTimeFormatter.ISO_LOCAL_DATE) + separator;
if (consumption >= 0) { if (consumption >= 0) {
@ -354,7 +348,9 @@ public class LinkyHandler extends BaseThingHandler {
EnedisHttpApi api = this.enedisApi; EnedisHttpApi api = this.enedisApi;
if (api != null) { if (api != null) {
try { try {
return api.getEnergyData(userId, prmId, from, to); Consumption consumption = api.getEnergyData(userId, prmId, from, to);
updateStatus(ThingStatus.ONLINE);
return consumption;
} catch (LinkyException e) { } catch (LinkyException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
} }
@ -368,7 +364,9 @@ public class LinkyHandler extends BaseThingHandler {
EnedisHttpApi api = this.enedisApi; EnedisHttpApi api = this.enedisApi;
if (api != null) { if (api != null) {
try { try {
return api.getPowerData(userId, prmId, from, to); Consumption consumption = api.getPowerData(userId, prmId, from, to);
updateStatus(ThingStatus.ONLINE);
return consumption;
} catch (LinkyException e) { } catch (LinkyException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
} }
@ -410,10 +408,10 @@ public class LinkyHandler extends BaseThingHandler {
boolean connectedBefore = isConnected(); boolean connectedBefore = isConnected();
switch (channelUID.getId()) { switch (channelUID.getId()) {
case YESTERDAY: case YESTERDAY:
case THIS_WEEK:
updateDailyData(); updateDailyData();
break; break;
case LAST_WEEK: case LAST_WEEK:
case THIS_WEEK:
updateWeeklyData(); updateWeeklyData();
break; break;
case LAST_MONTH: 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); 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));
}
}
} }