import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky")
public class LinkyHandlerFactory extends BaseThingHandlerFactory {
+ private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
+
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
private final LocaleProvider localeProvider;
private final Gson gson;
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
final @Reference HttpClientFactory httpClientFactory) {
this.localeProvider = localeProvider;
- this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID);
this.gson = new GsonBuilder().registerTypeAdapter(ZonedDateTime.class,
(JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> ZonedDateTime
.parse(json.getAsJsonPrimitive().getAsString(), formatter))
.create();
+ this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID);
+ }
+
+ @Override
+ protected void activate(ComponentContext componentContext) {
+ super.activate(componentContext);
+ httpClient.getSslContextFactory().setExcludeCipherSuites(new String[0]);
+ httpClient.setFollowRedirects(false);
+ 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
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
- if (supportsThingType(thingTypeUID)) {
- return new LinkyHandler(thing, localeProvider, gson, httpClient);
- }
-
- return null;
+ return supportsThingType(thingTypeUID) ? new LinkyHandler(thing, localeProvider, gson, httpClient) : null;
}
}
private final HttpClient httpClient;
private final Gson gson;
+ private final WeekFields weekFields;
private @Nullable ScheduledFuture<?> refreshJob;
private @Nullable EnedisHttpApi enedisApi;
- private final WeekFields weekFields;
- private final ExpiringDayCache<Consumption> cachedDaylyData;
+ private final ExpiringDayCache<Consumption> cachedDailyData;
private final ExpiringDayCache<Consumption> cachedPowerData;
private final ExpiringDayCache<Consumption> cachedMonthlyData;
private final ExpiringDayCache<Consumption> cachedYearlyData;
super(thing);
this.gson = gson;
this.httpClient = httpClient;
-
this.weekFields = WeekFields.of(localeProvider.getLocale());
- this.cachedDaylyData = new ExpiringDayCache<>("daily cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
+ this.cachedDailyData = new ExpiringDayCache<>("daily cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LocalDate today = LocalDate.now();
- return getConsumptionData(today.minusDays(13), today);
+ return getConsumptionData(today.minusDays(15), today);
});
this.cachedPowerData = new ExpiringDayCache<>("power cache", REFRESH_FIRST_HOUR_OF_DAY, () -> {
LinkyConfiguration config = getConfigAs(LinkyConfiguration.class);
enedisApi = new EnedisHttpApi(config, gson, httpClient);
- try {
- enedisApi.initialize();
- updateStatus(ThingStatus.ONLINE);
-
- if (thing.getProperties().isEmpty()) {
- Map<String, String> properties = discoverAttributes();
- updateProperties(properties);
- }
-
- prmId = thing.getProperties().get(PRM_ID);
- userId = thing.getProperties().get(USER_ID);
+ scheduler.submit(() -> {
+ try {
+ EnedisHttpApi api = this.enedisApi;
+ if (api != null) {
+ api.initialize();
+ updateStatus(ThingStatus.ONLINE);
+
+ if (thing.getProperties().isEmpty()) {
+ Map<String, String> properties = discoverAttributes();
+ updateProperties(properties);
+ }
- final LocalDateTime now = LocalDateTime.now();
- final LocalDateTime nextDayFirstTimeUpdate = now.plusDays(1).withHour(REFRESH_FIRST_HOUR_OF_DAY)
- .truncatedTo(ChronoUnit.HOURS);
+ prmId = thing.getProperties().get(PRM_ID);
+ userId = thing.getProperties().get(USER_ID);
- updateData();
+ 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);
+ updateData();
- } catch (LinkyException e) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
- }
+ refreshJob = scheduler.scheduleWithFixedDelay(this::updateData,
+ ChronoUnit.MINUTES.between(now, nextDayFirstTimeUpdate) % REFRESH_INTERVAL_IN_MIN + 1,
+ REFRESH_INTERVAL_IN_MIN, TimeUnit.MINUTES);
+ } else {
+ throw new LinkyException("Enedis Api is not initialized");
+ }
+ } catch (LinkyException e) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+ }
+ });
}
private Map<String, String> discoverAttributes() throws LinkyException {
private void updateData() {
updatePowerData();
updateDailyData();
+ updateWeeklyData();
updateMonthlyData();
updateYearlyData();
}
private synchronized void updatePowerData() {
if (isLinked(PEAK_POWER) || isLinked(PEAK_TIMESTAMP)) {
- Consumption result = cachedPowerData.getValue();
- if (result != null) {
- updateVAChannel(PEAK_POWER, result.aggregats.days.datas.get(0));
- updateState(PEAK_TIMESTAMP, new DateTimeType(result.aggregats.days.periodes.get(0).dateDebut));
- }
+ cachedPowerData.getValue().ifPresent(values -> {
+ updateVAChannel(PEAK_POWER, values.aggregats.days.datas.get(0));
+ updateState(PEAK_TIMESTAMP, new DateTimeType(values.aggregats.days.periodes.get(0).dateDebut));
+ });
}
}
* Request new dayly/weekly data and updates channels
*/
private synchronized void updateDailyData() {
- if (isLinked(YESTERDAY) || isLinked(LAST_WEEK) || isLinked(THIS_WEEK)) {
- Consumption result = cachedDaylyData.getValue();
- if (result != null) {
- Aggregate days = result.aggregats.days;
-
+ if (isLinked(YESTERDAY) || isLinked(THIS_WEEK)) {
+ cachedDailyData.getValue().ifPresent(values -> {
+ Aggregate days = values.aggregats.days;
int maxValue = days.periodes.size() - 1;
int thisWeekNumber = days.periodes.get(maxValue).dateDebut.get(weekFields.weekOfWeekBasedYear());
double yesterday = days.datas.get(maxValue);
- double lastWeek = 0.0;
- double thisWeek = 0.0;
+ double thisWeek = 0.00;
for (int i = maxValue; i >= 0; i--) {
int weekNumber = days.periodes.get(i).dateDebut.get(weekFields.weekOfWeekBasedYear());
if (weekNumber == thisWeekNumber) {
- thisWeek += days.datas.get(i);
- } else if (weekNumber == thisWeekNumber - 1) {
- lastWeek += days.datas.get(i);
+ Double value = days.datas.get(i);
+ thisWeek += !value.isNaN() ? value : 0;
} else {
break;
}
updateKwhChannel(YESTERDAY, yesterday);
updateKwhChannel(THIS_WEEK, thisWeek);
- updateKwhChannel(LAST_WEEK, lastWeek);
- }
+ });
+ }
+ }
+
+ /**
+ * Request new weekly data and updates channels
+ */
+ private synchronized void updateWeeklyData() {
+ if (isLinked(LAST_WEEK)) {
+ cachedDailyData.getValue().ifPresent(values -> {
+ Aggregate weeks = values.aggregats.weeks;
+ if (weeks.datas.size() > 1) {
+ updateKwhChannel(LAST_WEEK, weeks.datas.get(1));
+ }
+ });
}
}
*/
private synchronized void updateMonthlyData() {
if (isLinked(LAST_MONTH) || isLinked(THIS_MONTH)) {
- Consumption result = cachedMonthlyData.getValue();
- if (result != null) {
- Aggregate months = result.aggregats.months;
- if (months.datas.size() < 2) {
- logger.debug("Received data array too small (required size is 2): {}", months);
- return;
- }
+ cachedMonthlyData.getValue().ifPresent(values -> {
+ Aggregate months = values.aggregats.months;
updateKwhChannel(LAST_MONTH, months.datas.get(0));
- updateKwhChannel(THIS_MONTH, months.datas.get(1));
- }
+ if (months.datas.size() > 1) {
+ updateKwhChannel(THIS_MONTH, months.datas.get(1));
+ } else {
+ updateKwhChannel(THIS_MONTH, Double.NaN);
+ }
+ });
}
}
*/
private synchronized void updateYearlyData() {
if (isLinked(LAST_YEAR) || isLinked(THIS_YEAR)) {
- Consumption result = cachedYearlyData.getValue();
- if (result != null) {
- Aggregate years = result.aggregats.years;
+ cachedYearlyData.getValue().ifPresent(values -> {
+ Aggregate years = values.aggregats.years;
updateKwhChannel(LAST_YEAR, years.datas.get(0));
- updateKwhChannel(THIS_YEAR, years.datas.get(1));
- }
+ if (years.datas.size() > 1) {
+ updateKwhChannel(THIS_YEAR, years.datas.get(1));
+ } else {
+ updateKwhChannel(THIS_YEAR, Double.NaN);
+ }
+ });
}
}
private void updateKwhChannel(String channelId, double consumption) {
logger.debug("Update channel {} with {}", channelId, consumption);
- updateState(channelId,
- !Double.isNaN(consumption) ? new QuantityType<>(consumption, SmartHomeUnits.KILOWATT_HOUR)
- : UnDefType.UNDEF);
+ updateState(channelId, Double.isNaN(consumption) ? UnDefType.UNDEF
+ : new QuantityType<>(consumption, SmartHomeUnits.KILOWATT_HOUR));
}
private void updateVAChannel(String channelId, double power) {
logger.debug("Update channel {} with {}", channelId, power);
updateState(channelId,
- !Double.isNaN(power) ? new QuantityType<>(power, SmartHomeUnits.VOLT_AMPERE) : UnDefType.UNDEF);
+ Double.isNaN(power) ? UnDefType.UNDEF : new QuantityType<>(power, SmartHomeUnits.VOLT_AMPERE));
}
/**
<channel id="yesterday" typeId="consumption">
<label>Yesterday Consumption</label>
</channel>
- <channel id="power" typeId="power"/>
- <channel id="timestamp" typeId="timestamp"/>
+ <channel id="power" typeId="power">
+ <label>Maximum power usage yesterday</label>
+ </channel>
+ <channel id="timestamp" typeId="timestamp">
+ <label>Peak Timestamp</label>
+ <description>Maximum power usage timestamp</description>
+ </channel>
</channels>
</channel-group-type>
<label>This Week Consumption</label>
</channel>
<channel id="lastWeek" typeId="consumption">
- <label>Maximum power usage yesterday</label>
+ <label>Last Week Consumption</label>
</channel>
</channels>
</channel-group-type>
<item-type>Number:Energy</item-type>
<label>Total Consumption</label>
<description>Consumption at given time interval</description>
- <state readOnly="true" pattern="%.3f %unit%"></state>
+ <category>energy</category>
+ <state readOnly="true" pattern="%.3f %unit%"/>
</channel-type>
<channel-type id="power">
<item-type>Number:Power</item-type>
<label>Peak Power</label>
<description>Maximum power usage yesterday</description>
- <state readOnly="true" pattern="%.3f %unit%"></state>
+ <state readOnly="true" pattern="%.3f %unit%"/>
</channel-type>
<channel-type id="timestamp">
<item-type>DateTime</item-type>
- <label>Peak Timestamp</label>
- <description>Maximum power usage timestamp</description>
- <state readOnly="true">
- </state>
+ <label>Timestamp</label>
+ <category>time</category>
+ <state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>