]> git.basschouten.com Git - openhab-addons.git/blob
57ff70f0baa4028151d15727ba9ae0fda878b7e4
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.plugwise.internal.handler;
14
15 import static java.util.stream.Collectors.*;
16 import static org.openhab.binding.plugwise.internal.PlugwiseBindingConstants.*;
17 import static org.openhab.core.thing.ThingStatus.*;
18
19 import java.time.Duration;
20 import java.time.LocalDateTime;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.concurrent.TimeUnit;
24 import java.util.stream.Stream;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.plugwise.internal.PlugwiseDeviceTask;
29 import org.openhab.binding.plugwise.internal.PlugwiseUtils;
30 import org.openhab.binding.plugwise.internal.config.PlugwiseRelayConfig;
31 import org.openhab.binding.plugwise.internal.config.PlugwiseRelayConfig.PowerStateChanging;
32 import org.openhab.binding.plugwise.internal.protocol.AcknowledgementMessage;
33 import org.openhab.binding.plugwise.internal.protocol.AcknowledgementMessage.ExtensionCode;
34 import org.openhab.binding.plugwise.internal.protocol.ClockGetRequestMessage;
35 import org.openhab.binding.plugwise.internal.protocol.ClockGetResponseMessage;
36 import org.openhab.binding.plugwise.internal.protocol.ClockSetRequestMessage;
37 import org.openhab.binding.plugwise.internal.protocol.InformationRequestMessage;
38 import org.openhab.binding.plugwise.internal.protocol.InformationResponseMessage;
39 import org.openhab.binding.plugwise.internal.protocol.Message;
40 import org.openhab.binding.plugwise.internal.protocol.PowerBufferRequestMessage;
41 import org.openhab.binding.plugwise.internal.protocol.PowerBufferResponseMessage;
42 import org.openhab.binding.plugwise.internal.protocol.PowerCalibrationRequestMessage;
43 import org.openhab.binding.plugwise.internal.protocol.PowerCalibrationResponseMessage;
44 import org.openhab.binding.plugwise.internal.protocol.PowerChangeRequestMessage;
45 import org.openhab.binding.plugwise.internal.protocol.PowerInformationRequestMessage;
46 import org.openhab.binding.plugwise.internal.protocol.PowerInformationResponseMessage;
47 import org.openhab.binding.plugwise.internal.protocol.PowerLogIntervalSetRequestMessage;
48 import org.openhab.binding.plugwise.internal.protocol.RealTimeClockGetRequestMessage;
49 import org.openhab.binding.plugwise.internal.protocol.RealTimeClockGetResponseMessage;
50 import org.openhab.binding.plugwise.internal.protocol.RealTimeClockSetRequestMessage;
51 import org.openhab.binding.plugwise.internal.protocol.field.DeviceType;
52 import org.openhab.binding.plugwise.internal.protocol.field.Energy;
53 import org.openhab.binding.plugwise.internal.protocol.field.MACAddress;
54 import org.openhab.binding.plugwise.internal.protocol.field.PowerCalibration;
55 import org.openhab.core.config.core.Configuration;
56 import org.openhab.core.library.types.OnOffType;
57 import org.openhab.core.library.types.QuantityType;
58 import org.openhab.core.library.types.StringType;
59 import org.openhab.core.library.unit.SmartHomeUnits;
60 import org.openhab.core.thing.Bridge;
61 import org.openhab.core.thing.ChannelUID;
62 import org.openhab.core.thing.Thing;
63 import org.openhab.core.thing.ThingStatus;
64 import org.openhab.core.thing.ThingStatusDetail;
65 import org.openhab.core.types.Command;
66 import org.openhab.core.types.UnDefType;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 /**
71  * <p>
72  * The {@link PlugwiseRelayDeviceHandler} handles channel updates and commands for a Plugwise device with a relay.
73  * Relay devices are the Circle, Circle+ and Stealth.
74  * </p>
75  * <p>
76  * A Circle maintains current energy usage by counting 'pulses' in a one or eight-second interval. Furthermore, it
77  * stores hourly energy usage as well in a buffer. Each entry in the buffer contains usage for the last 4 full hours of
78  * consumption. In order to convert pulses to energy (kWh) or power (W), a calculation is made in the {@link Energy}
79  * class with {@link PowerCalibration} data.
80  * </p>
81  * <p>
82  * A Circle+ is a special Circle. There is one Circle+ in a Plugwise network. The Circle+ serves as a master controller
83  * in a Plugwise network. It also provides clock data to the other devices and sends messages from and to the Stick.
84  * </p>
85  * <p>
86  * A Stealth behaves like a Circle but it has a more compact form factor.
87  * </p>
88  *
89  * @author Wouter Born, Karel Goderis - Initial contribution
90  */
91 @NonNullByDefault
92 public class PlugwiseRelayDeviceHandler extends AbstractPlugwiseThingHandler {
93
94     private static final int INVALID_WATT_THRESHOLD = 10000;
95     private static final int POWER_STATE_RETRIES = 3;
96
97     private class PendingPowerStateChange {
98         final OnOffType onOff;
99         int retries;
100
101         PendingPowerStateChange(OnOffType onOff) {
102             this.onOff = onOff;
103         }
104     }
105
106     private final PlugwiseDeviceTask clockUpdateTask = new PlugwiseDeviceTask("Clock update", scheduler) {
107         @Override
108         public Duration getConfiguredInterval() {
109             return getChannelUpdateInterval(CHANNEL_CLOCK);
110         }
111
112         @Override
113         public void runTask() {
114             sendMessage(new ClockGetRequestMessage(macAddress));
115         }
116
117         @Override
118         public boolean shouldBeScheduled() {
119             return thing.getStatus() == ONLINE && isLinked(CHANNEL_CLOCK);
120         }
121     };
122
123     private final PlugwiseDeviceTask currentPowerUpdateTask = new PlugwiseDeviceTask("Current power update",
124             scheduler) {
125         @Override
126         public Duration getConfiguredInterval() {
127             return getChannelUpdateInterval(CHANNEL_POWER);
128         }
129
130         @Override
131         public void runTask() {
132             if (isCalibrated()) {
133                 sendMessage(new PowerInformationRequestMessage(macAddress));
134             }
135         }
136
137         @Override
138         public boolean shouldBeScheduled() {
139             return thing.getStatus() == ONLINE && (isLinked(CHANNEL_POWER)
140                     || configuration.getPowerStateChanging() != PowerStateChanging.COMMAND_SWITCHING);
141         }
142     };
143
144     private final PlugwiseDeviceTask energyUpdateTask = new PlugwiseDeviceTask("Energy update", scheduler) {
145         @Override
146         public Duration getConfiguredInterval() {
147             return getChannelUpdateInterval(CHANNEL_ENERGY);
148         }
149
150         @Override
151         public void runTask() {
152             if (isRecentLogAddressKnown()) {
153                 updateEnergy();
154             }
155         }
156
157         @Override
158         public boolean shouldBeScheduled() {
159             return thing.getStatus() == ONLINE && isLinked(CHANNEL_ENERGY);
160         }
161     };
162
163     private final PlugwiseDeviceTask informationUpdateTask = new PlugwiseDeviceTask("Information update", scheduler) {
164         @Override
165         public Duration getConfiguredInterval() {
166             return PlugwiseUtils.minComparable(getChannelUpdateInterval(CHANNEL_STATE),
167                     getChannelUpdateInterval(CHANNEL_ENERGY));
168         }
169
170         @Override
171         public void runTask() {
172             updateInformation();
173         }
174
175         @Override
176         public boolean shouldBeScheduled() {
177             return thing.getStatus() == ONLINE && (isLinked(CHANNEL_STATE) || isLinked(CHANNEL_ENERGY));
178         }
179     };
180
181     private final PlugwiseDeviceTask realTimeClockUpdateTask = new PlugwiseDeviceTask("Real-time clock update",
182             scheduler) {
183         @Override
184         public Duration getConfiguredInterval() {
185             return getChannelUpdateInterval(CHANNEL_REAL_TIME_CLOCK);
186         }
187
188         @Override
189         public void runTask() {
190             sendMessage(new RealTimeClockGetRequestMessage(macAddress));
191         }
192
193         @Override
194         public boolean shouldBeScheduled() {
195             return thing.getStatus() == ONLINE && deviceType == DeviceType.CIRCLE_PLUS
196                     && isLinked(CHANNEL_REAL_TIME_CLOCK);
197         }
198     };
199
200     private final PlugwiseDeviceTask setClockTask = new PlugwiseDeviceTask("Set clock", scheduler) {
201         @Override
202         public Duration getConfiguredInterval() {
203             return Duration.ofDays(1);
204         }
205
206         @Override
207         public void runTask() {
208             if (deviceType == DeviceType.CIRCLE_PLUS) {
209                 // The Circle+ real-time clock needs to be updated first to prevent clock sync issues
210                 sendCommandMessage(new RealTimeClockSetRequestMessage(macAddress, LocalDateTime.now()));
211                 scheduler.schedule(() -> {
212                     sendCommandMessage(new ClockSetRequestMessage(macAddress, LocalDateTime.now()));
213                 }, 5, TimeUnit.SECONDS);
214             } else {
215                 sendCommandMessage(new ClockSetRequestMessage(macAddress, LocalDateTime.now()));
216             }
217         }
218
219         @Override
220         public boolean shouldBeScheduled() {
221             return thing.getStatus() == ONLINE;
222         }
223     };
224
225     private final List<PlugwiseDeviceTask> recurringTasks = Stream
226             .of(clockUpdateTask, currentPowerUpdateTask, energyUpdateTask, informationUpdateTask,
227                     realTimeClockUpdateTask, setClockTask)
228             .collect(collectingAndThen(toList(), Collections::unmodifiableList));
229
230     private final Logger logger = LoggerFactory.getLogger(PlugwiseRelayDeviceHandler.class);
231     private final DeviceType deviceType;
232
233     private int recentLogAddress = -1;
234
235     private @NonNullByDefault({}) PlugwiseRelayConfig configuration;
236     private @NonNullByDefault({}) MACAddress macAddress;
237
238     private @Nullable PowerCalibration calibration;
239     private @Nullable Energy energy;
240     private @Nullable PendingPowerStateChange pendingPowerStateChange;
241
242     // Flag that keeps track of the pending "measurement interval" device configuration update. When the corresponding
243     // Thing configuration parameter changes it is set to true. When the Circle/Stealth goes online a command is sent to
244     // update the device configuration. When the Circle/Stealth acknowledges the command the flag is again set to false.
245     private boolean updateMeasurementInterval;
246
247     public PlugwiseRelayDeviceHandler(Thing thing) {
248         super(thing);
249         deviceType = getDeviceType();
250     }
251
252     private void calibrate() {
253         sendFastUpdateMessage(new PowerCalibrationRequestMessage(macAddress));
254     }
255
256     @Override
257     public void channelLinked(ChannelUID channelUID) {
258         updateTasks(recurringTasks);
259     }
260
261     @Override
262     public void channelUnlinked(ChannelUID channelUID) {
263         updateTasks(recurringTasks);
264     }
265
266     private void correctPowerState(OnOffType powerState) {
267         if (configuration.getPowerStateChanging() == PowerStateChanging.ALWAYS_OFF && (powerState != OnOffType.OFF)) {
268             logger.debug("Correcting power state of {} ({}) to off", deviceType, macAddress);
269             handleOnOffCommand(OnOffType.OFF);
270         } else if (configuration.getPowerStateChanging() == PowerStateChanging.ALWAYS_ON
271                 && (powerState != OnOffType.ON)) {
272             logger.debug("Correcting power state of {} ({}) to on", deviceType, macAddress);
273             handleOnOffCommand(OnOffType.ON);
274         }
275     }
276
277     private double correctSign(double value) {
278         return configuration.isSuppliesPower() ? -Math.abs(value) : Math.abs(value);
279     }
280
281     @Override
282     public void dispose() {
283         stopTasks(recurringTasks);
284         super.dispose();
285     }
286
287     @Override
288     protected MACAddress getMACAddress() {
289         return macAddress;
290     }
291
292     private void handleAcknowledgement(AcknowledgementMessage message) {
293         boolean oldConfigurationPending = isConfigurationPending();
294
295         ExtensionCode extensionCode = message.getExtensionCode();
296         switch (extensionCode) {
297             case CLOCK_SET_ACK:
298                 logger.debug("Received ACK for clock set of {} ({})", deviceType, macAddress);
299                 sendMessage(new ClockGetRequestMessage(macAddress));
300                 break;
301             case ON_ACK:
302                 logger.debug("Received ACK for switching on {} ({})", deviceType, macAddress);
303                 updateState(CHANNEL_STATE, OnOffType.ON);
304                 break;
305             case ON_OFF_NACK:
306                 logger.debug("Received NACK for switching on/off {} ({})", deviceType, macAddress);
307                 break;
308             case OFF_ACK:
309                 logger.debug("Received ACK for switching off {} ({})", deviceType, macAddress);
310                 updateState(CHANNEL_STATE, OnOffType.OFF);
311                 break;
312             case POWER_LOG_INTERVAL_SET_ACK:
313                 logger.debug("Received ACK for power log interval set of {} ({})", deviceType, macAddress);
314                 updateMeasurementInterval = false;
315                 break;
316             case REAL_TIME_CLOCK_SET_ACK:
317                 logger.debug("Received ACK for setting real-time clock of {} ({})", deviceType, macAddress);
318                 sendMessage(new RealTimeClockGetRequestMessage(macAddress));
319                 break;
320             case REAL_TIME_CLOCK_SET_NACK:
321                 logger.debug("Received NACK for setting real-time clock of {} ({})", deviceType, macAddress);
322                 break;
323             default:
324                 logger.debug("{} ({}) {} acknowledgement", deviceType, macAddress, extensionCode);
325                 break;
326         }
327
328         boolean newConfigurationPending = isConfigurationPending();
329
330         if (oldConfigurationPending != newConfigurationPending && !newConfigurationPending) {
331             Configuration newConfiguration = editConfiguration();
332             newConfiguration.put(CONFIG_PROPERTY_UPDATE_CONFIGURATION, false);
333             updateConfiguration(newConfiguration);
334         }
335
336         updateStatusOnDetailChange();
337     }
338
339     private void handleCalibrationResponse(PowerCalibrationResponseMessage message) {
340         boolean wasCalibrated = isCalibrated();
341         calibration = message.getCalibration();
342         logger.debug("{} ({}) calibrated: {}", deviceType, macAddress, calibration);
343         if (!wasCalibrated) {
344             if (isRecentLogAddressKnown()) {
345                 updateEnergy();
346             } else {
347                 updateInformation();
348             }
349             sendFastUpdateMessage(new PowerInformationRequestMessage(macAddress));
350         }
351     }
352
353     private void handleClockGetResponse(ClockGetResponseMessage message) {
354         updateState(CHANNEL_CLOCK, new StringType(message.getTime()));
355     }
356
357     @Override
358     public void handleCommand(ChannelUID channelUID, Command command) {
359         logger.debug("Handling command '{}' for {} ({}) channel '{}'", command, deviceType, macAddress,
360                 channelUID.getId());
361         if (CHANNEL_STATE.equals(channelUID.getId()) && (command instanceof OnOffType)) {
362             if (configuration.getPowerStateChanging() == PowerStateChanging.COMMAND_SWITCHING) {
363                 OnOffType onOff = (OnOffType) command;
364                 pendingPowerStateChange = new PendingPowerStateChange(onOff);
365                 handleOnOffCommand(onOff);
366             } else {
367                 OnOffType onOff = configuration.getPowerStateChanging() == PowerStateChanging.ALWAYS_ON ? OnOffType.ON
368                         : OnOffType.OFF;
369                 logger.debug("Ignoring {} ({}) power state change (always {})", deviceType, macAddress, onOff);
370                 updateState(CHANNEL_STATE, onOff);
371             }
372         }
373     }
374
375     private void handleInformationResponse(InformationResponseMessage message) {
376         recentLogAddress = message.getLogAddress();
377         OnOffType powerState = message.getPowerState() ? OnOffType.ON : OnOffType.OFF;
378
379         PendingPowerStateChange change = pendingPowerStateChange;
380         if (change != null) {
381             if (powerState == change.onOff) {
382                 pendingPowerStateChange = null;
383             } else {
384                 // Power state change message may be lost or the informationUpdateTask may have queried the power
385                 // state just before the power state change message arrived
386                 if (change.retries < POWER_STATE_RETRIES) {
387                     change.retries++;
388                     logger.warn("Retrying to switch {} ({}) {} (retry #{})", deviceType, macAddress, change.onOff,
389                             change.retries);
390                     handleOnOffCommand(change.onOff);
391                 } else {
392                     logger.warn("Failed to switch {} ({}) {} after {} retries", deviceType, macAddress, change.onOff,
393                             change.retries);
394                     pendingPowerStateChange = null;
395                 }
396             }
397         }
398
399         if (pendingPowerStateChange == null) {
400             updateState(CHANNEL_STATE, powerState);
401             correctPowerState(powerState);
402         }
403
404         if (energy == null && isCalibrated()) {
405             updateEnergy();
406         }
407
408         updateProperties(message);
409     }
410
411     private void handleOnOffCommand(OnOffType command) {
412         sendCommandMessage(new PowerChangeRequestMessage(macAddress, command == OnOffType.ON));
413         sendFastUpdateMessage(new InformationRequestMessage(macAddress));
414
415         // Measurements take 2 seconds to become stable
416         scheduler.schedule(() -> sendFastUpdateMessage(new PowerInformationRequestMessage(macAddress)), 2,
417                 TimeUnit.SECONDS);
418     }
419
420     private void handlePowerBufferResponse(PowerBufferResponseMessage message) {
421         PowerCalibration localCalibration = calibration;
422         if (localCalibration == null) {
423             calibrate();
424             return;
425         }
426
427         Energy mostRecentEnergy = message.getMostRecentDatapoint();
428
429         if (mostRecentEnergy != null) {
430             // When the current time is '11:44:55.888' and the measurement interval 1 hour, then the end of the most
431             // recent energy measurement interval is at '11:00:00.000'
432             LocalDateTime oneIntervalAgo = LocalDateTime.now().minus(configuration.getMeasurementInterval());
433
434             boolean isLastInterval = mostRecentEnergy.getEnd().isAfter(oneIntervalAgo);
435             if (isLastInterval) {
436                 mostRecentEnergy.setInterval(configuration.getMeasurementInterval());
437                 energy = mostRecentEnergy;
438                 logger.trace("Updating {} ({}) energy with: {}", deviceType, macAddress, mostRecentEnergy);
439                 updateState(CHANNEL_ENERGY, new QuantityType<>(correctSign(mostRecentEnergy.tokWh(localCalibration)),
440                         SmartHomeUnits.KILOWATT_HOUR));
441                 LocalDateTime start = mostRecentEnergy.getStart();
442                 updateState(CHANNEL_ENERGY_STAMP,
443                         start != null ? PlugwiseUtils.newDateTimeType(start) : UnDefType.NULL);
444             } else {
445                 logger.trace("Most recent energy in buffer of {} ({}) is older than one interval ago: {}", deviceType,
446                         macAddress, mostRecentEnergy);
447             }
448         } else {
449             logger.trace("Most recent energy in buffer of {} ({}) is null", deviceType, macAddress);
450         }
451     }
452
453     private void handlePowerInformationResponse(PowerInformationResponseMessage message) {
454         PowerCalibration localCalibration = calibration;
455         if (localCalibration == null) {
456             calibrate();
457             return;
458         }
459
460         Energy one = message.getOneSecond();
461         double watt = one.toWatt(localCalibration);
462         if (watt > INVALID_WATT_THRESHOLD) {
463             logger.debug("{} ({}) is in a kind of error state, skipping power information response", deviceType,
464                     macAddress);
465             return;
466         }
467
468         updateState(CHANNEL_POWER, new QuantityType<>(correctSign(watt), SmartHomeUnits.WATT));
469     }
470
471     private void handleRealTimeClockGetResponse(RealTimeClockGetResponseMessage message) {
472         updateState(CHANNEL_REAL_TIME_CLOCK, PlugwiseUtils.newDateTimeType(message.getDateTime()));
473     }
474
475     @Override
476     public void handleReponseMessage(Message message) {
477         updateLastSeen();
478
479         switch (message.getType()) {
480             case ACKNOWLEDGEMENT_V1:
481             case ACKNOWLEDGEMENT_V2:
482                 handleAcknowledgement((AcknowledgementMessage) message);
483                 break;
484             case CLOCK_GET_RESPONSE:
485                 handleClockGetResponse(((ClockGetResponseMessage) message));
486                 break;
487             case DEVICE_INFORMATION_RESPONSE:
488                 handleInformationResponse((InformationResponseMessage) message);
489                 break;
490             case POWER_BUFFER_RESPONSE:
491                 handlePowerBufferResponse((PowerBufferResponseMessage) message);
492                 break;
493             case POWER_CALIBRATION_RESPONSE:
494                 handleCalibrationResponse(((PowerCalibrationResponseMessage) message));
495                 break;
496             case POWER_INFORMATION_RESPONSE:
497                 handlePowerInformationResponse((PowerInformationResponseMessage) message);
498                 break;
499             case REAL_TIME_CLOCK_GET_RESPONSE:
500                 handleRealTimeClockGetResponse((RealTimeClockGetResponseMessage) message);
501                 break;
502             default:
503                 logger.trace("Received unhandled {} message from {} ({})", message.getType(), deviceType, macAddress);
504                 break;
505         }
506     }
507
508     @Override
509     public void initialize() {
510         configuration = getConfigAs(PlugwiseRelayConfig.class);
511         macAddress = configuration.getMACAddress();
512         if (!isInitialized()) {
513             setUpdateCommandFlags(null, configuration);
514         }
515         if (configuration.isTemporarilyNotInNetwork()) {
516             updateStatus(OFFLINE);
517         }
518         updateTasks(recurringTasks);
519         super.initialize();
520     }
521
522     private boolean isCalibrated() {
523         return calibration != null;
524     }
525
526     @Override
527     protected boolean isConfigurationPending() {
528         return updateMeasurementInterval;
529     }
530
531     private boolean isRecentLogAddressKnown() {
532         return recentLogAddress >= 0;
533     }
534
535     @Override
536     protected void sendConfigurationUpdateCommands() {
537         logger.debug("Sending {} ({}) configuration update commands", deviceType, macAddress);
538
539         if (updateMeasurementInterval) {
540             logger.debug("Sending command to update {} ({}) power log measurement interval", deviceType, macAddress);
541             Duration consumptionInterval = configuration.isSuppliesPower() ? Duration.ZERO
542                     : configuration.getMeasurementInterval();
543             Duration productionInterval = configuration.isSuppliesPower() ? configuration.getMeasurementInterval()
544                     : Duration.ZERO;
545             sendCommandMessage(
546                     new PowerLogIntervalSetRequestMessage(macAddress, consumptionInterval, productionInterval));
547         }
548
549         super.sendConfigurationUpdateCommands();
550     }
551
552     private void setUpdateCommandFlags(@Nullable PlugwiseRelayConfig oldConfiguration,
553             PlugwiseRelayConfig newConfiguration) {
554         boolean fullUpdate = newConfiguration.isUpdateConfiguration() && !isConfigurationPending();
555         if (fullUpdate) {
556             logger.debug("Updating all configuration properties of {} ({})", deviceType, macAddress);
557         }
558
559         updateMeasurementInterval = fullUpdate || (oldConfiguration != null
560                 && (!oldConfiguration.getMeasurementInterval().equals(newConfiguration.getMeasurementInterval())));
561         if (updateMeasurementInterval) {
562             logger.debug("Updating {} ({}) power log interval when online", deviceType, macAddress);
563         }
564     }
565
566     @Override
567     protected boolean shouldOnlineTaskBeScheduled() {
568         Bridge bridge = getBridge();
569         return !configuration.isTemporarilyNotInNetwork() && (bridge != null && bridge.getStatus() == ONLINE);
570     }
571
572     @Override
573     protected void updateConfiguration(Configuration configuration) {
574         PlugwiseRelayConfig oldConfiguration = this.configuration;
575         PlugwiseRelayConfig newConfiguration = configuration.as(PlugwiseRelayConfig.class);
576
577         setUpdateCommandFlags(oldConfiguration, newConfiguration);
578
579         configuration.put(CONFIG_PROPERTY_UPDATE_CONFIGURATION, isConfigurationPending());
580
581         super.updateConfiguration(configuration);
582     }
583
584     private void updateEnergy() {
585         int previousLogAddress = recentLogAddress - 1;
586         while (previousLogAddress <= recentLogAddress) {
587             PowerBufferRequestMessage message = new PowerBufferRequestMessage(macAddress, previousLogAddress);
588             previousLogAddress = previousLogAddress + 1;
589             sendMessage(message);
590         }
591     }
592
593     @Override
594     protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
595         super.updateStatus(status, statusDetail, description);
596
597         if (status == ONLINE) {
598             if (!isCalibrated()) {
599                 calibrate();
600             }
601             if (editProperties().isEmpty()) {
602                 updateInformation();
603             }
604         }
605
606         updateTasks(recurringTasks);
607     }
608 }