2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.bticinosmarther.internal.handler;
15 import static org.openhab.binding.bticinosmarther.internal.SmartherBindingConstants.*;
17 import java.time.Duration;
18 import java.util.List;
19 import java.util.concurrent.Future;
20 import java.util.concurrent.TimeUnit;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.bticinosmarther.internal.api.dto.Chronothermostat;
25 import org.openhab.binding.bticinosmarther.internal.api.dto.Enums.BoostTime;
26 import org.openhab.binding.bticinosmarther.internal.api.dto.Enums.Mode;
27 import org.openhab.binding.bticinosmarther.internal.api.dto.ModuleStatus;
28 import org.openhab.binding.bticinosmarther.internal.api.dto.Notification;
29 import org.openhab.binding.bticinosmarther.internal.api.dto.Program;
30 import org.openhab.binding.bticinosmarther.internal.api.exception.SmartherGatewayException;
31 import org.openhab.binding.bticinosmarther.internal.api.exception.SmartherIllegalPropertyValueException;
32 import org.openhab.binding.bticinosmarther.internal.api.exception.SmartherSubscriptionAlreadyExistsException;
33 import org.openhab.binding.bticinosmarther.internal.config.SmartherModuleConfiguration;
34 import org.openhab.binding.bticinosmarther.internal.model.ModuleSettings;
35 import org.openhab.binding.bticinosmarther.internal.util.StringUtil;
36 import org.openhab.core.cache.ExpiringCache;
37 import org.openhab.core.i18n.TimeZoneProvider;
38 import org.openhab.core.library.types.DecimalType;
39 import org.openhab.core.library.types.OnOffType;
40 import org.openhab.core.library.types.QuantityType;
41 import org.openhab.core.library.types.StringType;
42 import org.openhab.core.library.unit.SIUnits;
43 import org.openhab.core.scheduler.CronScheduler;
44 import org.openhab.core.scheduler.ScheduledCompletableFuture;
45 import org.openhab.core.thing.Bridge;
46 import org.openhab.core.thing.Channel;
47 import org.openhab.core.thing.ChannelUID;
48 import org.openhab.core.thing.Thing;
49 import org.openhab.core.thing.ThingStatus;
50 import org.openhab.core.thing.ThingStatusDetail;
51 import org.openhab.core.thing.ThingStatusInfo;
52 import org.openhab.core.thing.binding.BaseThingHandler;
53 import org.openhab.core.types.Command;
54 import org.openhab.core.types.RefreshType;
55 import org.openhab.core.types.State;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
60 * The {@code SmartherModuleHandler} class is responsible of a single Smarther Chronothermostat, handling the commands
61 * that are sent to one of its channels.
62 * Each Smarther Chronothermostat communicates with the Smarther API via its assigned {@code SmartherBridgeHandler}.
64 * @author Fabio Possieri - Initial contribution
67 public class SmartherModuleHandler extends BaseThingHandler {
69 private static final String DAILY_MIDNIGHT = "1 0 0 * * ? *";
70 private static final long POLL_INITIAL_DELAY = 5;
72 private final Logger logger = LoggerFactory.getLogger(SmartherModuleHandler.class);
74 private final CronScheduler cronScheduler;
75 private final SmartherDynamicStateDescriptionProvider dynamicStateDescriptionProvider;
76 private final ChannelUID programChannelUID;
77 private final ChannelUID endDateChannelUID;
78 private final TimeZoneProvider timeZoneProvider;
80 // Module configuration
81 private SmartherModuleConfiguration config;
83 // Field members assigned in initialize method
84 private @Nullable ScheduledCompletableFuture<Void> jobFuture;
85 private @Nullable Future<?> pollFuture;
86 private @Nullable SmartherBridgeHandler bridgeHandler;
87 private @Nullable ExpiringCache<List<Program>> programCache;
88 private @Nullable ModuleSettings moduleSettings;
90 // Chronothermostat local status
91 private @Nullable Chronothermostat chronothermostat;
94 * Constructs a {@code SmartherModuleHandler} for the given thing, scheduler and dynamic state description provider.
97 * the {@link Thing} thing to be used
99 * the {@link CronScheduler} periodic job scheduler to be used
101 * the {@link SmartherDynamicStateDescriptionProvider} dynamic state description provider to be used
103 public SmartherModuleHandler(Thing thing, CronScheduler scheduler, SmartherDynamicStateDescriptionProvider provider,
104 final TimeZoneProvider timeZoneProvider) {
106 this.cronScheduler = scheduler;
107 this.dynamicStateDescriptionProvider = provider;
108 this.timeZoneProvider = timeZoneProvider;
109 this.programChannelUID = new ChannelUID(thing.getUID(), CHANNEL_SETTINGS_PROGRAM);
110 this.endDateChannelUID = new ChannelUID(thing.getUID(), CHANNEL_SETTINGS_ENDDATE);
111 this.config = new SmartherModuleConfiguration();
114 // ===========================================================================
116 // Chronothermostat thing lifecycle management methods
118 // ===========================================================================
121 public void initialize() {
122 logger.debug("Module[{}] Initialize handler", thing.getUID());
124 final Bridge localBridge = getBridge();
125 if (localBridge == null) {
126 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
130 final SmartherBridgeHandler localBridgeHandler = (SmartherBridgeHandler) localBridge.getHandler();
131 this.bridgeHandler = localBridgeHandler;
132 if (localBridgeHandler == null) {
133 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String.format(
134 "Missing configuration from the Smarther Bridge (UID:%s). Fix configuration or report if this problem remains.",
135 localBridge.getBridgeUID()));
139 this.config = getConfigAs(SmartherModuleConfiguration.class);
140 if (StringUtil.isBlank(config.getPlantId())) {
141 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
142 "The 'Plant Id' property is not set or empty. If you have an older thing please recreate it.");
145 if (StringUtil.isBlank(config.getModuleId())) {
146 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
147 "The 'Module Id' property is not set or empty. If you have an older thing please recreate it.");
150 if (config.getProgramsRefreshPeriod() <= 0) {
151 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
152 "The 'Programs Refresh Period' must be > 0. If you have an older thing please recreate it.");
155 if (config.getStatusRefreshPeriod() <= 0) {
156 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
157 "The 'Module Status Refresh Period' must be > 0. If you have an older thing please recreate it.");
161 // Initialize automatic mode programs local cache
162 final ExpiringCache<List<Program>> localProgramCache = new ExpiringCache<>(
163 Duration.ofHours(config.getProgramsRefreshPeriod()), this::programCacheAction);
164 this.programCache = localProgramCache;
166 // Initialize module local settings
167 final ModuleSettings localModuleSettings = new ModuleSettings(config.getPlantId(), config.getModuleId());
168 this.moduleSettings = localModuleSettings;
170 updateStatus(ThingStatus.UNKNOWN);
175 logger.debug("Module[{}] Finished initializing!", thing.getUID());
179 public void handleCommand(ChannelUID channelUID, Command command) {
181 handleCommandInternal(channelUID, command);
182 updateModuleStatus();
183 } catch (SmartherIllegalPropertyValueException e) {
184 logger.warn("Module[{}] Received command {} with illegal value {} on channel {}", thing.getUID(), command,
185 e.getMessage(), channelUID.getId());
186 } catch (SmartherGatewayException e) {
187 // catch exceptions and handle it in your binding
188 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
193 * Handles the command sent to a given Channel of this Chronothermostat.
196 * the identifier of the Channel
198 * the command sent to the given Channel
200 * @throws {@link SmartherIllegalPropertyValueException}
201 * if the command contains an illegal value that cannot be mapped to any valid enum value
202 * @throws {@link SmartherGatewayException}
203 * in case of communication issues with the Smarther API
205 private void handleCommandInternal(ChannelUID channelUID, Command command)
206 throws SmartherIllegalPropertyValueException, SmartherGatewayException {
207 final ModuleSettings localModuleSettings = this.moduleSettings;
208 if (localModuleSettings == null) {
212 switch (channelUID.getId()) {
213 case CHANNEL_SETTINGS_MODE:
214 if (command instanceof StringType) {
215 localModuleSettings.setMode(Mode.fromValue(command.toString()));
219 case CHANNEL_SETTINGS_TEMPERATURE:
220 if (changeTemperature(command, localModuleSettings)) {
224 case CHANNEL_SETTINGS_PROGRAM:
225 if (command instanceof DecimalType) {
226 localModuleSettings.setProgram(((DecimalType) command).intValue());
230 case CHANNEL_SETTINGS_BOOSTTIME:
231 if (command instanceof DecimalType) {
232 localModuleSettings.setBoostTime(BoostTime.fromValue(((DecimalType) command).intValue()));
236 case CHANNEL_SETTINGS_ENDDATE:
237 if (command instanceof StringType) {
238 localModuleSettings.setEndDate(command.toString());
242 case CHANNEL_SETTINGS_ENDHOUR:
243 if (changeTimeHour(command, localModuleSettings)) {
247 case CHANNEL_SETTINGS_ENDMINUTE:
248 if (changeTimeMinute(command, localModuleSettings)) {
252 case CHANNEL_SETTINGS_POWER:
253 if (command instanceof OnOffType) {
254 if (OnOffType.ON.equals(command)) {
255 // Apply module settings to the remote module
256 if (getBridgeHandler().setModuleStatus(localModuleSettings)) {
257 // Change applied, update module status
258 logger.debug("Module[{}] New settings applied!", thing.getUID());
260 updateChannelState(CHANNEL_SETTINGS_POWER, OnOffType.OFF);
265 case CHANNEL_CONFIG_FETCH_PROGRAMS:
266 if (command instanceof OnOffType) {
267 if (OnOffType.ON.equals(command)) {
269 "Module[{}] Manually triggered channel to remotely fetch the updated programs list",
272 refreshProgramsList();
273 updateChannelState(CHANNEL_CONFIG_FETCH_PROGRAMS, OnOffType.OFF);
280 if (command instanceof RefreshType) {
281 // Avoid logging wrong command when refresh command is sent
285 logger.debug("Module[{}] Received command {} of wrong type {} on channel {}", thing.getUID(), command,
286 command.getClass().getTypeName(), channelUID.getId());
290 * Changes the "temperature" in module settings, based on the received Command.
291 * The new value is checked against the temperature limits allowed by the device.
294 * the command received on temperature Channel
296 * @return {@code true} if the change succeeded, {@code false} otherwise
298 private boolean changeTemperature(Command command, final ModuleSettings settings) {
299 if (!(command instanceof QuantityType)) {
303 QuantityType<?> quantity = (QuantityType<?>) command;
304 QuantityType<?> newMeasure = quantity.toUnit(SIUnits.CELSIUS);
306 // Check remote device temperature limits
307 if (newMeasure != null && newMeasure.doubleValue() >= 7.1 && newMeasure.doubleValue() <= 40.0) {
308 // Only tenth degree increments are allowed
309 double newTemperature = Math.round(newMeasure.doubleValue() * 10) / 10.0;
311 settings.setSetPointTemperature(QuantityType.valueOf(newTemperature, SIUnits.CELSIUS));
318 * Changes the "end hour" for manual mode in module settings, based on the received Command.
319 * The new value is checked against the 24-hours clock allowed range.
322 * the command received on end hour Channel
324 * @return {@code true} if the change succeeded, {@code false} otherwise
326 private boolean changeTimeHour(Command command, final ModuleSettings settings) {
327 if (command instanceof DecimalType) {
328 int endHour = ((DecimalType) command).intValue();
329 if (endHour >= 0 && endHour <= 23) {
330 settings.setEndHour(endHour);
338 * Changes the "end minute" for manual mode in module settings, based on the received Command.
339 * The new value is modified to match a 15 min step increment.
342 * the command received on end minute Channel
344 * @return {@code true} if the change succeeded, {@code false} otherwise
346 private boolean changeTimeMinute(Command command, final ModuleSettings settings) {
347 if (command instanceof DecimalType) {
348 int endMinute = ((DecimalType) command).intValue();
349 if (endMinute >= 0 && endMinute <= 59) {
350 // Only 15 min increments are allowed
351 endMinute = Math.round(endMinute / 15) * 15;
352 settings.setEndMinute(endMinute);
360 * Handles the notification dispatched to this Chronothermostat from the reference Smarther Bridge.
362 * @param notification
363 * the notification to handle
365 public void handleNotification(Notification notification) {
367 final Chronothermostat notificationChrono = notification.getChronothermostat();
368 if (notificationChrono != null) {
369 this.chronothermostat = notificationChrono;
370 if (config.isSettingsAutoupdate()) {
371 final ModuleSettings localModuleSettings = this.moduleSettings;
372 if (localModuleSettings != null) {
373 localModuleSettings.updateFromChronothermostat(notificationChrono);
376 logger.debug("Module[{}] Handle notification: [{}]", thing.getUID(), this.chronothermostat);
377 updateModuleStatus();
379 } catch (SmartherIllegalPropertyValueException e) {
380 logger.warn("Module[{}] Notification has illegal value: [{}]", thing.getUID(), e.getMessage());
385 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
386 if (bridgeStatusInfo.getStatus() != ThingStatus.ONLINE) {
387 // Put module offline when the parent bridge goes offline
388 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Smarther Bridge Offline");
389 logger.debug("Module[{}] Bridge switched {}", thing.getUID(), bridgeStatusInfo.getStatus());
391 // Update the module status when the parent bridge return online
392 logger.debug("Module[{}] Bridge is back ONLINE", thing.getUID());
393 // Restart polling to collect module data
399 public void handleRemoval() {
400 super.handleRemoval();
406 public void dispose() {
407 logger.debug("Module[{}] Dispose handler", thing.getUID());
411 getBridgeHandler().unregisterNotification(config.getPlantId());
412 } catch (SmartherGatewayException e) {
413 logger.warn("Module[{}] API Gateway error during disposing: {}", thing.getUID(), e.getMessage());
415 logger.debug("Module[{}] Finished disposing!", thing.getUID());
418 // ===========================================================================
420 // Chronothermostat data cache management methods
422 // ===========================================================================
425 * Returns the available automatic mode programs to be cached for this Chronothermostat.
427 * @return the available programs to be cached for this Chronothermostat, or {@code null} if the list of available
428 * programs cannot be retrieved
430 private @Nullable List<Program> programCacheAction() {
432 final List<Program> programs = getBridgeHandler().getModulePrograms(config.getPlantId(),
433 config.getModuleId());
434 logger.debug("Module[{}] Available programs: {}", thing.getUID(), programs);
438 } catch (SmartherGatewayException e) {
439 logger.warn("Module[{}] Cannot retrieve available programs: {}", thing.getUID(), e.getMessage());
445 * Sets all the cache to "expired" for this Chronothermostat.
447 private void expireCache() {
448 logger.debug("Module[{}] Invalidating program cache", thing.getUID());
449 final ExpiringCache<List<Program>> localProgramCache = this.programCache;
450 if (localProgramCache != null) {
451 localProgramCache.invalidateValue();
455 // ===========================================================================
457 // Chronothermostat job scheduler methods
459 // ===========================================================================
462 * Starts a new cron scheduler to execute the internal recurring jobs.
464 private synchronized void scheduleJob() {
467 // Schedule daily job to start daily, at midnight
468 final ScheduledCompletableFuture<Void> localJobFuture = cronScheduler.schedule(this::dailyJob, DAILY_MIDNIGHT);
469 this.jobFuture = localJobFuture;
471 logger.debug("Module[{}] Scheduled recurring job {} to start at midnight", thing.getUID(),
472 Integer.toHexString(localJobFuture.hashCode()));
474 // Execute daily job immediately at startup
479 * Cancels all running jobs.
481 * @param mayInterruptIfRunning
482 * {@code true} if the thread executing this task should be interrupted, {@code false} if the in-progress
483 * tasks are allowed to complete
485 private synchronized void stopJob(boolean mayInterruptIfRunning) {
486 final ScheduledCompletableFuture<Void> localJobFuture = this.jobFuture;
487 if (localJobFuture != null) {
488 if (!localJobFuture.isCancelled()) {
489 localJobFuture.cancel(mayInterruptIfRunning);
491 this.jobFuture = null;
496 * Action to be executed by the daily job: refresh the end dates list for "manual" mode.
498 private void dailyJob() {
499 logger.debug("Module[{}] Daily job, refreshing the end dates list for \"manual\" mode", thing.getUID());
500 // Refresh the end dates list for "manual" mode
501 dynamicStateDescriptionProvider.setEndDates(endDateChannelUID, config.getNumberOfEndDays());
502 // If expired, update EndDate in module settings
503 final ModuleSettings localModuleSettings = this.moduleSettings;
504 if (localModuleSettings != null && localModuleSettings.isEndDateExpired()) {
505 localModuleSettings.refreshEndDate();
506 updateChannelState(CHANNEL_SETTINGS_ENDDATE, new StringType(localModuleSettings.getEndDate()));
510 // ===========================================================================
512 // Chronothermostat status polling mechanism methods
514 // ===========================================================================
517 * Starts a new scheduler to periodically poll and update this Chronothermostat status.
519 private void schedulePoll() {
522 // Schedule poll to start after POLL_INITIAL_DELAY sec and run periodically based on status refresh period
523 final Future<?> localPollFuture = scheduler.scheduleWithFixedDelay(this::poll, POLL_INITIAL_DELAY,
524 config.getStatusRefreshPeriod() * 60, TimeUnit.SECONDS);
525 this.pollFuture = localPollFuture;
527 logger.debug("Module[{}] Scheduled poll for {} sec out, then every {} min", thing.getUID(), POLL_INITIAL_DELAY,
528 config.getStatusRefreshPeriod());
532 * Cancels all running poll schedulers.
534 * @param mayInterruptIfRunning
535 * {@code true} if the thread executing this task should be interrupted, {@code false} if the in-progress
536 * tasks are allowed to complete
538 private synchronized void stopPoll(boolean mayInterruptIfRunning) {
539 final Future<?> localPollFuture = this.pollFuture;
540 if (localPollFuture != null) {
541 if (!localPollFuture.isCancelled()) {
542 localPollFuture.cancel(mayInterruptIfRunning);
544 this.pollFuture = null;
549 * Polls to update this Chronothermostat status.
551 * @return {@code true} if the method completes without errors, {@code false} otherwise
553 private synchronized boolean poll() {
555 final Bridge bridge = getBridge();
556 if (bridge != null) {
557 final ThingStatusInfo bridgeStatusInfo = bridge.getStatusInfo();
558 if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
559 ModuleStatus moduleStatus = getBridgeHandler().getModuleStatus(config.getPlantId(),
560 config.getModuleId());
562 final Chronothermostat statusChrono = moduleStatus.toChronothermostat();
563 if (statusChrono != null) {
564 if ((this.chronothermostat == null) || config.isSettingsAutoupdate()) {
565 final ModuleSettings localModuleSettings = this.moduleSettings;
566 if (localModuleSettings != null) {
567 localModuleSettings.updateFromChronothermostat(statusChrono);
570 this.chronothermostat = statusChrono;
571 logger.debug("Module[{}] Status: [{}]", thing.getUID(), this.chronothermostat);
573 throw new SmartherGatewayException("No chronothermostat data found");
576 // Refresh the programs list for "automatic" mode
577 refreshProgramsList();
579 updateModuleStatus();
581 getBridgeHandler().registerNotification(config.getPlantId());
583 // Everything is ok > set the Thing state to Online
584 updateStatus(ThingStatus.ONLINE);
586 } else if (thing.getStatus() != ThingStatus.OFFLINE) {
587 logger.debug("Module[{}] Switched {} as Bridge is not online", thing.getUID(),
588 bridgeStatusInfo.getStatus());
589 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Smarther Bridge Offline");
593 } catch (SmartherIllegalPropertyValueException e) {
594 logger.debug("Module[{}] Illegal property value error during polling: {}", thing.getUID(), e.getMessage());
595 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
596 } catch (SmartherSubscriptionAlreadyExistsException e) {
597 logger.debug("Module[{}] Subscription error during polling: {}", thing.getUID(), e.getMessage());
598 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
599 } catch (SmartherGatewayException e) {
600 logger.warn("Module[{}] API Gateway error during polling: {}", thing.getUID(), e.getMessage());
601 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
602 } catch (RuntimeException e) {
603 // All other exceptions apart from Subscription and Gateway issues
604 logger.warn("Module[{}] Unexpected error during polling, please report if this keeps occurring: ",
606 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
612 // ===========================================================================
614 // Chronothermostat convenience methods
616 // ===========================================================================
619 * Convenience method to check and get the Smarther Bridge handler instance for this Module.
621 * @return the Smarther Bridge handler instance
623 * @throws {@link SmartherGatewayException}
624 * in case the Smarther Bridge handler instance is {@code null}
626 private SmartherBridgeHandler getBridgeHandler() throws SmartherGatewayException {
627 final SmartherBridgeHandler localBridgeHandler = this.bridgeHandler;
628 if (localBridgeHandler == null) {
629 throw new SmartherGatewayException("Smarther Bridge handler instance is null");
631 return localBridgeHandler;
635 * Returns this Chronothermostat plant identifier
637 * @return a string containing the plant identifier
639 public String getPlantId() {
640 return config.getPlantId();
644 * Returns this Chronothermostat module identifier
646 * @return a string containing the module identifier
648 public String getModuleId() {
649 return config.getModuleId();
653 * Checks whether this Chronothermostat matches with the given plant and module identifiers.
656 * the plant identifier to match to
658 * the module identifier to match to
660 * @return {@code true} if the Chronothermostat matches the given plant and module identifiers, {@code false}
663 public boolean isLinkedTo(String plantId, String moduleId) {
664 return (config.getPlantId().equals(plantId) && config.getModuleId().equals(moduleId));
668 * Convenience method to refresh the module programs list from cache.
670 private void refreshProgramsList() {
671 final ExpiringCache<List<Program>> localProgramCache = this.programCache;
672 if (localProgramCache != null) {
673 final List<Program> programs = localProgramCache.getValue();
674 if (programs != null) {
675 dynamicStateDescriptionProvider.setPrograms(programChannelUID, programs);
681 * Convenience method to update the given Channel state "only" if the Channel is linked.
684 * the identifier of the Channel to be updated
686 * the new state to be applied to the given Channel
688 private void updateChannelState(String channelId, State state) {
689 final Channel channel = thing.getChannel(channelId);
691 if (channel != null && isLinked(channel.getUID())) {
692 updateState(channel.getUID(), state);
697 * Convenience method to update the whole status of the Chronothermostat associated to this handler.
698 * Channels are updated based on the local {@code chronothermostat} and {@code moduleSettings} objects.
700 * @throws {@link SmartherIllegalPropertyValueException}
701 * if at least one of the module properties cannot be mapped to any valid enum value
703 private void updateModuleStatus() throws SmartherIllegalPropertyValueException {
704 final Chronothermostat localChrono = this.chronothermostat;
705 if (localChrono != null) {
706 // Update the Measures channels
707 updateChannelState(CHANNEL_MEASURES_TEMPERATURE, localChrono.getThermometer().toState());
708 updateChannelState(CHANNEL_MEASURES_HUMIDITY, localChrono.getHygrometer().toState());
709 // Update the Status channels
710 updateChannelState(CHANNEL_STATUS_STATE, (localChrono.isActive() ? OnOffType.ON : OnOffType.OFF));
711 updateChannelState(CHANNEL_STATUS_FUNCTION,
712 new StringType(StringUtil.capitalize(localChrono.getFunction().toLowerCase())));
713 updateChannelState(CHANNEL_STATUS_MODE,
714 new StringType(StringUtil.capitalize(localChrono.getMode().toLowerCase())));
715 updateChannelState(CHANNEL_STATUS_TEMPERATURE, localChrono.getSetPointTemperature().toState());
716 updateChannelState(CHANNEL_STATUS_ENDTIME,
717 new StringType(localChrono.getActivationTimeLabel(timeZoneProvider)));
718 updateChannelState(CHANNEL_STATUS_TEMP_FORMAT, new StringType(localChrono.getTemperatureFormat()));
719 final Program localProgram = localChrono.getProgram();
720 if (localProgram != null) {
721 updateChannelState(CHANNEL_STATUS_PROGRAM, new StringType(String.valueOf(localProgram.getNumber())));
725 final ModuleSettings localSettings = this.moduleSettings;
726 if (localSettings != null) {
727 // Update the Settings channels
728 updateChannelState(CHANNEL_SETTINGS_MODE, new StringType(localSettings.getMode().getValue()));
729 updateChannelState(CHANNEL_SETTINGS_TEMPERATURE, localSettings.getSetPointTemperature());
730 updateChannelState(CHANNEL_SETTINGS_PROGRAM, new DecimalType(localSettings.getProgram()));
731 updateChannelState(CHANNEL_SETTINGS_BOOSTTIME, new DecimalType(localSettings.getBoostTime().getValue()));
732 updateChannelState(CHANNEL_SETTINGS_ENDDATE, new StringType(localSettings.getEndDate()));
733 updateChannelState(CHANNEL_SETTINGS_ENDHOUR, new DecimalType(localSettings.getEndHour()));
734 updateChannelState(CHANNEL_SETTINGS_ENDMINUTE, new DecimalType(localSettings.getEndMinute()));
735 updateChannelState(CHANNEL_SETTINGS_POWER, OnOffType.OFF);