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.openhab.core.util.StringUtils;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
61 * The {@code SmartherModuleHandler} class is responsible of a single Smarther Chronothermostat, handling the commands
62 * that are sent to one of its channels.
63 * Each Smarther Chronothermostat communicates with the Smarther API via its assigned {@code SmartherBridgeHandler}.
65 * @author Fabio Possieri - Initial contribution
68 public class SmartherModuleHandler extends BaseThingHandler {
70 private static final String DAILY_MIDNIGHT = "1 0 0 * * ? *";
71 private static final long POLL_INITIAL_DELAY = 5;
73 private final Logger logger = LoggerFactory.getLogger(SmartherModuleHandler.class);
75 private final CronScheduler cronScheduler;
76 private final SmartherDynamicStateDescriptionProvider dynamicStateDescriptionProvider;
77 private final ChannelUID programChannelUID;
78 private final ChannelUID endDateChannelUID;
79 private final TimeZoneProvider timeZoneProvider;
81 // Module configuration
82 private SmartherModuleConfiguration config;
84 // Field members assigned in initialize method
85 private @Nullable ScheduledCompletableFuture<Void> jobFuture;
86 private @Nullable Future<?> pollFuture;
87 private @Nullable SmartherBridgeHandler bridgeHandler;
88 private @Nullable ExpiringCache<List<Program>> programCache;
89 private @Nullable ModuleSettings moduleSettings;
91 // Chronothermostat local status
92 private @Nullable Chronothermostat chronothermostat;
95 * Constructs a {@code SmartherModuleHandler} for the given thing, scheduler and dynamic state description provider.
98 * the {@link Thing} thing to be used
100 * the {@link CronScheduler} periodic job scheduler to be used
102 * the {@link SmartherDynamicStateDescriptionProvider} dynamic state description provider to be used
104 public SmartherModuleHandler(Thing thing, CronScheduler scheduler, SmartherDynamicStateDescriptionProvider provider,
105 final TimeZoneProvider timeZoneProvider) {
107 this.cronScheduler = scheduler;
108 this.dynamicStateDescriptionProvider = provider;
109 this.timeZoneProvider = timeZoneProvider;
110 this.programChannelUID = new ChannelUID(thing.getUID(), CHANNEL_SETTINGS_PROGRAM);
111 this.endDateChannelUID = new ChannelUID(thing.getUID(), CHANNEL_SETTINGS_ENDDATE);
112 this.config = new SmartherModuleConfiguration();
115 // ===========================================================================
117 // Chronothermostat thing lifecycle management methods
119 // ===========================================================================
122 public void initialize() {
123 logger.debug("Module[{}] Initialize handler", thing.getUID());
125 final Bridge localBridge = getBridge();
126 if (localBridge == null) {
127 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
131 final SmartherBridgeHandler localBridgeHandler = (SmartherBridgeHandler) localBridge.getHandler();
132 this.bridgeHandler = localBridgeHandler;
133 if (localBridgeHandler == null) {
134 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String.format(
135 "Missing configuration from the Smarther Bridge (UID:%s). Fix configuration or report if this problem remains.",
136 localBridge.getBridgeUID()));
140 this.config = getConfigAs(SmartherModuleConfiguration.class);
141 if (StringUtil.isBlank(config.getPlantId())) {
142 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
143 "The 'Plant Id' property is not set or empty. If you have an older thing please recreate it.");
146 if (StringUtil.isBlank(config.getModuleId())) {
147 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
148 "The 'Module Id' property is not set or empty. If you have an older thing please recreate it.");
151 if (config.getProgramsRefreshPeriod() <= 0) {
152 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
153 "The 'Programs Refresh Period' must be > 0. If you have an older thing please recreate it.");
156 if (config.getStatusRefreshPeriod() <= 0) {
157 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
158 "The 'Module Status Refresh Period' must be > 0. If you have an older thing please recreate it.");
162 // Initialize automatic mode programs local cache
163 final ExpiringCache<List<Program>> localProgramCache = new ExpiringCache<>(
164 Duration.ofHours(config.getProgramsRefreshPeriod()), this::programCacheAction);
165 this.programCache = localProgramCache;
167 // Initialize module local settings
168 final ModuleSettings localModuleSettings = new ModuleSettings(config.getPlantId(), config.getModuleId());
169 this.moduleSettings = localModuleSettings;
171 updateStatus(ThingStatus.UNKNOWN);
176 logger.debug("Module[{}] Finished initializing!", thing.getUID());
180 public void handleCommand(ChannelUID channelUID, Command command) {
182 handleCommandInternal(channelUID, command);
183 updateModuleStatus();
184 } catch (SmartherIllegalPropertyValueException e) {
185 logger.warn("Module[{}] Received command {} with illegal value {} on channel {}", thing.getUID(), command,
186 e.getMessage(), channelUID.getId());
187 } catch (SmartherGatewayException e) {
188 // catch exceptions and handle it in your binding
189 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
194 * Handles the command sent to a given Channel of this Chronothermostat.
197 * the identifier of the Channel
199 * the command sent to the given Channel
201 * @throws {@link SmartherIllegalPropertyValueException}
202 * if the command contains an illegal value that cannot be mapped to any valid enum value
203 * @throws {@link SmartherGatewayException}
204 * in case of communication issues with the Smarther API
206 private void handleCommandInternal(ChannelUID channelUID, Command command)
207 throws SmartherIllegalPropertyValueException, SmartherGatewayException {
208 final ModuleSettings localModuleSettings = this.moduleSettings;
209 if (localModuleSettings == null) {
213 switch (channelUID.getId()) {
214 case CHANNEL_SETTINGS_MODE:
215 if (command instanceof StringType) {
216 localModuleSettings.setMode(Mode.fromValue(command.toString()));
220 case CHANNEL_SETTINGS_TEMPERATURE:
221 if (changeTemperature(command, localModuleSettings)) {
225 case CHANNEL_SETTINGS_PROGRAM:
226 if (command instanceof DecimalType decimalCommand) {
227 localModuleSettings.setProgram(decimalCommand.intValue());
231 case CHANNEL_SETTINGS_BOOSTTIME:
232 if (command instanceof DecimalType decimalCommand) {
233 localModuleSettings.setBoostTime(BoostTime.fromValue(decimalCommand.intValue()));
237 case CHANNEL_SETTINGS_ENDDATE:
238 if (command instanceof StringType) {
239 localModuleSettings.setEndDate(command.toString());
243 case CHANNEL_SETTINGS_ENDHOUR:
244 if (changeTimeHour(command, localModuleSettings)) {
248 case CHANNEL_SETTINGS_ENDMINUTE:
249 if (changeTimeMinute(command, localModuleSettings)) {
253 case CHANNEL_SETTINGS_POWER:
254 if (command instanceof OnOffType) {
255 if (OnOffType.ON.equals(command)) {
256 // Apply module settings to the remote module
257 if (getBridgeHandler().setModuleStatus(localModuleSettings)) {
258 // Change applied, update module status
259 logger.debug("Module[{}] New settings applied!", thing.getUID());
261 updateChannelState(CHANNEL_SETTINGS_POWER, OnOffType.OFF);
266 case CHANNEL_CONFIG_FETCH_PROGRAMS:
267 if (command instanceof OnOffType) {
268 if (OnOffType.ON.equals(command)) {
270 "Module[{}] Manually triggered channel to remotely fetch the updated programs list",
273 refreshProgramsList();
274 updateChannelState(CHANNEL_CONFIG_FETCH_PROGRAMS, OnOffType.OFF);
281 if (command instanceof RefreshType) {
282 // Avoid logging wrong command when refresh command is sent
286 logger.debug("Module[{}] Received command {} of wrong type {} on channel {}", thing.getUID(), command,
287 command.getClass().getTypeName(), channelUID.getId());
291 * Changes the "temperature" in module settings, based on the received Command.
292 * The new value is checked against the temperature limits allowed by the device.
295 * the command received on temperature Channel
297 * @return {@code true} if the change succeeded, {@code false} otherwise
299 private boolean changeTemperature(Command command, final ModuleSettings settings) {
300 if (!(command instanceof QuantityType)) {
304 QuantityType<?> quantity = (QuantityType<?>) command;
305 QuantityType<?> newMeasure = quantity.toUnit(SIUnits.CELSIUS);
307 // Check remote device temperature limits
308 if (newMeasure != null && newMeasure.doubleValue() >= 7.1 && newMeasure.doubleValue() <= 40.0) {
309 // Only tenth degree increments are allowed
310 double newTemperature = Math.round(newMeasure.doubleValue() * 10) / 10.0;
312 settings.setSetPointTemperature(QuantityType.valueOf(newTemperature, SIUnits.CELSIUS));
319 * Changes the "end hour" for manual mode in module settings, based on the received Command.
320 * The new value is checked against the 24-hours clock allowed range.
323 * the command received on end hour Channel
325 * @return {@code true} if the change succeeded, {@code false} otherwise
327 private boolean changeTimeHour(Command command, final ModuleSettings settings) {
328 if (command instanceof DecimalType decimalCommand) {
329 int endHour = decimalCommand.intValue();
330 if (endHour >= 0 && endHour <= 23) {
331 settings.setEndHour(endHour);
339 * Changes the "end minute" for manual mode in module settings, based on the received Command.
340 * The new value is modified to match a 15 min step increment.
343 * the command received on end minute Channel
345 * @return {@code true} if the change succeeded, {@code false} otherwise
347 private boolean changeTimeMinute(Command command, final ModuleSettings settings) {
348 if (command instanceof DecimalType decimalCommand) {
349 int endMinute = decimalCommand.intValue();
350 if (endMinute >= 0 && endMinute <= 59) {
351 // Only 15 min increments are allowed
352 endMinute = Math.round(endMinute / 15) * 15;
353 settings.setEndMinute(endMinute);
361 * Handles the notification dispatched to this Chronothermostat from the reference Smarther Bridge.
363 * @param notification
364 * the notification to handle
366 public void handleNotification(Notification notification) {
368 final Chronothermostat notificationChrono = notification.getChronothermostat();
369 if (notificationChrono != null) {
370 this.chronothermostat = notificationChrono;
371 if (config.isSettingsAutoupdate()) {
372 final ModuleSettings localModuleSettings = this.moduleSettings;
373 if (localModuleSettings != null) {
374 localModuleSettings.updateFromChronothermostat(notificationChrono);
377 logger.debug("Module[{}] Handle notification: [{}]", thing.getUID(), this.chronothermostat);
378 updateModuleStatus();
380 } catch (SmartherIllegalPropertyValueException e) {
381 logger.warn("Module[{}] Notification has illegal value: [{}]", thing.getUID(), e.getMessage());
386 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
387 if (bridgeStatusInfo.getStatus() != ThingStatus.ONLINE) {
388 // Put module offline when the parent bridge goes offline
389 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Smarther Bridge Offline");
390 logger.debug("Module[{}] Bridge switched {}", thing.getUID(), bridgeStatusInfo.getStatus());
392 // Update the module status when the parent bridge return online
393 logger.debug("Module[{}] Bridge is back ONLINE", thing.getUID());
394 // Restart polling to collect module data
400 public void handleRemoval() {
401 super.handleRemoval();
407 public void dispose() {
408 logger.debug("Module[{}] Dispose handler", thing.getUID());
412 getBridgeHandler().unregisterNotification(config.getPlantId());
413 } catch (SmartherGatewayException e) {
414 logger.warn("Module[{}] API Gateway error during disposing: {}", thing.getUID(), e.getMessage());
416 logger.debug("Module[{}] Finished disposing!", thing.getUID());
419 // ===========================================================================
421 // Chronothermostat data cache management methods
423 // ===========================================================================
426 * Returns the available automatic mode programs to be cached for this Chronothermostat.
428 * @return the available programs to be cached for this Chronothermostat, or {@code null} if the list of available
429 * programs cannot be retrieved
431 private @Nullable List<Program> programCacheAction() {
433 final List<Program> programs = getBridgeHandler().getModulePrograms(config.getPlantId(),
434 config.getModuleId());
435 logger.debug("Module[{}] Available programs: {}", thing.getUID(), programs);
439 } catch (SmartherGatewayException e) {
440 logger.warn("Module[{}] Cannot retrieve available programs: {}", thing.getUID(), e.getMessage());
446 * Sets all the cache to "expired" for this Chronothermostat.
448 private void expireCache() {
449 logger.debug("Module[{}] Invalidating program cache", thing.getUID());
450 final ExpiringCache<List<Program>> localProgramCache = this.programCache;
451 if (localProgramCache != null) {
452 localProgramCache.invalidateValue();
456 // ===========================================================================
458 // Chronothermostat job scheduler methods
460 // ===========================================================================
463 * Starts a new cron scheduler to execute the internal recurring jobs.
465 private synchronized void scheduleJob() {
468 // Schedule daily job to start daily, at midnight
469 final ScheduledCompletableFuture<Void> localJobFuture = cronScheduler.schedule(this::dailyJob, DAILY_MIDNIGHT);
470 this.jobFuture = localJobFuture;
472 logger.debug("Module[{}] Scheduled recurring job {} to start at midnight", thing.getUID(),
473 Integer.toHexString(localJobFuture.hashCode()));
475 // Execute daily job immediately at startup
480 * Cancels all running jobs.
482 * @param mayInterruptIfRunning
483 * {@code true} if the thread executing this task should be interrupted, {@code false} if the in-progress
484 * tasks are allowed to complete
486 private synchronized void stopJob(boolean mayInterruptIfRunning) {
487 final ScheduledCompletableFuture<Void> localJobFuture = this.jobFuture;
488 if (localJobFuture != null) {
489 if (!localJobFuture.isCancelled()) {
490 localJobFuture.cancel(mayInterruptIfRunning);
492 this.jobFuture = null;
497 * Action to be executed by the daily job: refresh the end dates list for "manual" mode.
499 private void dailyJob() {
500 logger.debug("Module[{}] Daily job, refreshing the end dates list for \"manual\" mode", thing.getUID());
501 // Refresh the end dates list for "manual" mode
502 dynamicStateDescriptionProvider.setEndDates(endDateChannelUID, config.getNumberOfEndDays());
503 // If expired, update EndDate in module settings
504 final ModuleSettings localModuleSettings = this.moduleSettings;
505 if (localModuleSettings != null && localModuleSettings.isEndDateExpired()) {
506 localModuleSettings.refreshEndDate();
507 updateChannelState(CHANNEL_SETTINGS_ENDDATE, new StringType(localModuleSettings.getEndDate()));
511 // ===========================================================================
513 // Chronothermostat status polling mechanism methods
515 // ===========================================================================
518 * Starts a new scheduler to periodically poll and update this Chronothermostat status.
520 private void schedulePoll() {
523 // Schedule poll to start after POLL_INITIAL_DELAY sec and run periodically based on status refresh period
524 final Future<?> localPollFuture = scheduler.scheduleWithFixedDelay(this::poll, POLL_INITIAL_DELAY,
525 config.getStatusRefreshPeriod() * 60, TimeUnit.SECONDS);
526 this.pollFuture = localPollFuture;
528 logger.debug("Module[{}] Scheduled poll for {} sec out, then every {} min", thing.getUID(), POLL_INITIAL_DELAY,
529 config.getStatusRefreshPeriod());
533 * Cancels all running poll schedulers.
535 * @param mayInterruptIfRunning
536 * {@code true} if the thread executing this task should be interrupted, {@code false} if the in-progress
537 * tasks are allowed to complete
539 private synchronized void stopPoll(boolean mayInterruptIfRunning) {
540 final Future<?> localPollFuture = this.pollFuture;
541 if (localPollFuture != null) {
542 if (!localPollFuture.isCancelled()) {
543 localPollFuture.cancel(mayInterruptIfRunning);
545 this.pollFuture = null;
550 * Polls to update this Chronothermostat status.
552 * @return {@code true} if the method completes without errors, {@code false} otherwise
554 private synchronized boolean poll() {
556 final Bridge bridge = getBridge();
557 if (bridge != null) {
558 final ThingStatusInfo bridgeStatusInfo = bridge.getStatusInfo();
559 if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
560 ModuleStatus moduleStatus = getBridgeHandler().getModuleStatus(config.getPlantId(),
561 config.getModuleId());
563 final Chronothermostat statusChrono = moduleStatus.toChronothermostat();
564 if (statusChrono != null) {
565 if ((this.chronothermostat == null) || config.isSettingsAutoupdate()) {
566 final ModuleSettings localModuleSettings = this.moduleSettings;
567 if (localModuleSettings != null) {
568 localModuleSettings.updateFromChronothermostat(statusChrono);
571 this.chronothermostat = statusChrono;
572 logger.debug("Module[{}] Status: [{}]", thing.getUID(), this.chronothermostat);
574 throw new SmartherGatewayException("No chronothermostat data found");
577 // Refresh the programs list for "automatic" mode
578 refreshProgramsList();
580 updateModuleStatus();
582 getBridgeHandler().registerNotification(config.getPlantId());
584 // Everything is ok > set the Thing state to Online
585 updateStatus(ThingStatus.ONLINE);
587 } else if (thing.getStatus() != ThingStatus.OFFLINE) {
588 logger.debug("Module[{}] Switched {} as Bridge is not online", thing.getUID(),
589 bridgeStatusInfo.getStatus());
590 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Smarther Bridge Offline");
594 } catch (SmartherIllegalPropertyValueException e) {
595 logger.debug("Module[{}] Illegal property value error during polling: {}", thing.getUID(), e.getMessage());
596 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
597 } catch (SmartherSubscriptionAlreadyExistsException e) {
598 logger.debug("Module[{}] Subscription error during polling: {}", thing.getUID(), e.getMessage());
599 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
600 } catch (SmartherGatewayException e) {
601 logger.warn("Module[{}] API Gateway error during polling: {}", thing.getUID(), e.getMessage());
602 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
603 } catch (RuntimeException e) {
604 // All other exceptions apart from Subscription and Gateway issues
605 logger.warn("Module[{}] Unexpected error during polling, please report if this keeps occurring: ",
607 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
613 // ===========================================================================
615 // Chronothermostat convenience methods
617 // ===========================================================================
620 * Convenience method to check and get the Smarther Bridge handler instance for this Module.
622 * @return the Smarther Bridge handler instance
624 * @throws {@link SmartherGatewayException}
625 * in case the Smarther Bridge handler instance is {@code null}
627 private SmartherBridgeHandler getBridgeHandler() throws SmartherGatewayException {
628 final SmartherBridgeHandler localBridgeHandler = this.bridgeHandler;
629 if (localBridgeHandler == null) {
630 throw new SmartherGatewayException("Smarther Bridge handler instance is null");
632 return localBridgeHandler;
636 * Returns this Chronothermostat plant identifier
638 * @return a string containing the plant identifier
640 public String getPlantId() {
641 return config.getPlantId();
645 * Returns this Chronothermostat module identifier
647 * @return a string containing the module identifier
649 public String getModuleId() {
650 return config.getModuleId();
654 * Checks whether this Chronothermostat matches with the given plant and module identifiers.
657 * the plant identifier to match to
659 * the module identifier to match to
661 * @return {@code true} if the Chronothermostat matches the given plant and module identifiers, {@code false}
664 public boolean isLinkedTo(String plantId, String moduleId) {
665 return (config.getPlantId().equals(plantId) && config.getModuleId().equals(moduleId));
669 * Convenience method to refresh the module programs list from cache.
671 private void refreshProgramsList() {
672 final ExpiringCache<List<Program>> localProgramCache = this.programCache;
673 if (localProgramCache != null) {
674 final List<Program> programs = localProgramCache.getValue();
675 if (programs != null) {
676 dynamicStateDescriptionProvider.setPrograms(programChannelUID, programs);
682 * Convenience method to update the given Channel state "only" if the Channel is linked.
685 * the identifier of the Channel to be updated
687 * the new state to be applied to the given Channel
689 private void updateChannelState(String channelId, State state) {
690 final Channel channel = thing.getChannel(channelId);
692 if (channel != null && isLinked(channel.getUID())) {
693 updateState(channel.getUID(), state);
698 * Convenience method to update the whole status of the Chronothermostat associated to this handler.
699 * Channels are updated based on the local {@code chronothermostat} and {@code moduleSettings} objects.
701 * @throws {@link SmartherIllegalPropertyValueException}
702 * if at least one of the module properties cannot be mapped to any valid enum value
704 private void updateModuleStatus() throws SmartherIllegalPropertyValueException {
705 final Chronothermostat localChrono = this.chronothermostat;
706 if (localChrono != null) {
707 // Update the Measures channels
708 updateChannelState(CHANNEL_MEASURES_TEMPERATURE, localChrono.getThermometer().toState());
709 updateChannelState(CHANNEL_MEASURES_HUMIDITY, localChrono.getHygrometer().toState());
710 // Update the Status channels
711 updateChannelState(CHANNEL_STATUS_STATE, OnOffType.from((localChrono.isActive())));
712 updateChannelState(CHANNEL_STATUS_FUNCTION,
713 new StringType(StringUtils.capitalize(localChrono.getFunction().toLowerCase())));
714 updateChannelState(CHANNEL_STATUS_MODE,
715 new StringType(StringUtils.capitalize(localChrono.getMode().toLowerCase())));
716 updateChannelState(CHANNEL_STATUS_TEMPERATURE, localChrono.getSetPointTemperature().toState());
717 updateChannelState(CHANNEL_STATUS_ENDTIME,
718 new StringType(localChrono.getActivationTimeLabel(timeZoneProvider)));
719 updateChannelState(CHANNEL_STATUS_TEMP_FORMAT, new StringType(localChrono.getTemperatureFormat()));
720 final Program localProgram = localChrono.getProgram();
721 if (localProgram != null) {
722 updateChannelState(CHANNEL_STATUS_PROGRAM, new StringType(String.valueOf(localProgram.getNumber())));
726 final ModuleSettings localSettings = this.moduleSettings;
727 if (localSettings != null) {
728 // Update the Settings channels
729 updateChannelState(CHANNEL_SETTINGS_MODE, new StringType(localSettings.getMode().getValue()));
730 updateChannelState(CHANNEL_SETTINGS_TEMPERATURE, localSettings.getSetPointTemperature());
731 updateChannelState(CHANNEL_SETTINGS_PROGRAM, new DecimalType(localSettings.getProgram()));
732 updateChannelState(CHANNEL_SETTINGS_BOOSTTIME, new DecimalType(localSettings.getBoostTime().getValue()));
733 updateChannelState(CHANNEL_SETTINGS_ENDDATE, new StringType(localSettings.getEndDate()));
734 updateChannelState(CHANNEL_SETTINGS_ENDHOUR, new DecimalType(localSettings.getEndHour()));
735 updateChannelState(CHANNEL_SETTINGS_ENDMINUTE, new DecimalType(localSettings.getEndMinute()));
736 updateChannelState(CHANNEL_SETTINGS_POWER, OnOffType.OFF);