2 * Copyright (c) 2010-2024 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.openwebnet.internal.handler;
15 import static org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants.*;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
24 import org.openhab.core.library.types.DecimalType;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.library.types.QuantityType;
27 import org.openhab.core.library.types.StringType;
28 import org.openhab.core.library.unit.SIUnits;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.ThingTypeUID;
34 import org.openhab.core.types.Command;
35 import org.openhab.core.types.UnDefType;
36 import org.openwebnet4j.communication.OWNException;
37 import org.openwebnet4j.communication.Response;
38 import org.openwebnet4j.message.BaseOpenMessage;
39 import org.openwebnet4j.message.FrameException;
40 import org.openwebnet4j.message.MalformedFrameException;
41 import org.openwebnet4j.message.Thermoregulation;
42 import org.openwebnet4j.message.Thermoregulation.DimThermo;
43 import org.openwebnet4j.message.Thermoregulation.Function;
44 import org.openwebnet4j.message.Thermoregulation.OperationMode;
45 import org.openwebnet4j.message.Thermoregulation.WhatThermo;
46 import org.openwebnet4j.message.Thermoregulation.WhatThermoType;
47 import org.openwebnet4j.message.Where;
48 import org.openwebnet4j.message.WhereThermo;
49 import org.openwebnet4j.message.Who;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * The {@link OpenWebNetThermoregulationHandler} is responsible for handling
55 * commands/messages for Thermoregulation Things. It extends the abstract
56 * {@link OpenWebNetThingHandler}.
58 * @author Massimo Valla - Initial contribution. Added support for 4-zones CU.
59 * Rafactoring and fixed CU state channels updates. Completed support
60 * for HOLIDAY/VACATION modes and refactored mode handling.
61 * @author Gilberto Cocchi - Initial contribution.
62 * @author Andrea Conte - Added support for 99-zone CU and CU state channels.
65 public class OpenWebNetThermoregulationHandler extends OpenWebNetThingHandler {
67 private final Logger logger = LoggerFactory.getLogger(OpenWebNetThermoregulationHandler.class);
69 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.THERMOREGULATION_SUPPORTED_THING_TYPES;
71 private double currentSetPointTemp = 20.0d;
73 private Thermoregulation.@Nullable Function currentFunction = null;
74 private Thermoregulation.@Nullable OperationMode currentMode = null;
75 private int currentWeeklyPrgNum = 1;
76 private int currentScenarioPrgNum = 1;
77 private int currentVacationDays = 1;
79 private boolean isStandAlone = true; // true if zone is not associated to a CU
80 private boolean isCentralUnit = false;
82 private boolean cuAtLeastOneProbeOff = false;
83 private boolean cuAtLeastOneProbeProtection = false;
84 private boolean cuAtLeastOneProbeManual = false;
85 private String cuBatteryStatus = CU_BATTERY_OK;
86 private boolean cuFailureDiscovered = false;
88 private @Nullable ScheduledFuture<?> cuStateChannelsUpdateSchedule;
90 public static final int CU_STATE_CHANNELS_UPDATE_DELAY = 1500; // msec
92 private static final String CU_REMOTE_CONTROL_ENABLED = "ENABLED";
93 private static final String CU_REMOTE_CONTROL_DISABLED = "DISABLED";
94 private static final String CU_BATTERY_OK = "OK";
95 private static final String CU_BATTERY_KO = "KO";
96 private static final String MODE_WEEKLY = "WEEKLY";
98 public OpenWebNetThermoregulationHandler(Thing thing) {
103 public void initialize() {
105 ThingTypeUID thingType = thing.getThingTypeUID();
106 isCentralUnit = OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_CU.equals(thingType);
107 if (!isCentralUnit) {
108 if (!((WhereThermo) deviceWhere).isProbe()) {
109 Object standAloneConfig = getConfig().get(OpenWebNetBindingConstants.CONFIG_PROPERTY_STANDALONE);
110 if (standAloneConfig != null) {
111 isStandAlone = Boolean.parseBoolean(standAloneConfig.toString());
113 logger.debug("@@@@ THERMO ZONE INITIALIZE isStandAlone={}", isStandAlone);
116 // central unit must have WHERE=#0 or WHERE=0 or WHERE=#0#n
117 String w = deviceWhere.value();
118 if (w == null || !("0".equals(w) || "#0".equals(w) || w.startsWith("#0#"))) {
119 logger.warn("initialize() Invalid WHERE={} for Central Unit.", deviceWhere.value());
120 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
121 "@text/offline.conf-error-where");
128 protected void handleChannelCommand(ChannelUID channel, Command command) {
129 switch (channel.getId()) {
130 case CHANNEL_TEMP_SETPOINT:
131 handleSetpoint(command);
133 case CHANNEL_FUNCTION:
134 handleFunction(command);
139 case CHANNEL_FAN_SPEED:
140 handleSetFanSpeed(command);
142 case CHANNEL_CU_WEEKLY_PROGRAM_NUMBER:
143 case CHANNEL_CU_SCENARIO_PROGRAM_NUMBER:
144 handleSetProgramNumber(channel, command);
146 case CHANNEL_CU_VACATION_DAYS:
147 handleVacationDays(channel, command);
150 logger.warn("handleChannelCommand() Unsupported ChannelUID {}", channel.getId());
156 protected void requestChannelState(ChannelUID channel) {
157 super.requestChannelState(channel);
158 refreshDevice(false);
162 protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
163 return new WhereThermo(wStr);
167 protected String ownIdPrefix() {
168 return Who.THERMOREGULATION.value().toString();
171 private void handleSetFanSpeed(Command command) {
172 if (command instanceof StringType) {
173 Where w = deviceWhere;
175 logger.warn("handleSetFanSpeed() Where is null for thing {}", getThing().getUID());
179 Thermoregulation.FanCoilSpeed speed = Thermoregulation.FanCoilSpeed.valueOf(command.toString());
180 send(Thermoregulation.requestWriteFanCoilSpeed(w.value(), speed));
181 } catch (OWNException e) {
182 logger.warn("handleSetFanSpeed() {}", e.getMessage());
183 } catch (IllegalArgumentException e) {
184 logger.warn("handleSetFanSpeed() Unsupported command {} for thing {}", command, getThing().getUID());
188 logger.warn("handleSetFanSpeed() Unsupported command {} for thing {}", command, getThing().getUID());
192 private void handleSetProgramNumber(ChannelUID channel, Command command) {
193 if (command instanceof DecimalType) {
194 if (!isCentralUnit) {
195 logger.warn("handleSetProgramNumber() This command can be sent only for a Central Unit.");
198 Where w = deviceWhere;
200 logger.warn("handleSetProgramNumber() Where is null for thing {}", getThing().getUID());
204 int programNumber = ((DecimalType) command).intValue();
205 boolean updateOpMode = false;
207 if (CHANNEL_CU_WEEKLY_PROGRAM_NUMBER.equals(channel.getId())) {
208 if (programNumber < 1 || programNumber > 3) {
209 logger.warn("handleSetProgramNumber() Invalid program number {} for thing {}", command,
210 getThing().getUID());
213 updateOpMode = (currentMode == Thermoregulation.OperationMode.WEEKLY);
214 currentWeeklyPrgNum = programNumber;
215 logger.debug("handleSetProgramNumber() currentWeeklyPrgNum changed to: {}", programNumber);
217 if (programNumber < 1 || programNumber > 16) {
218 logger.warn("handleSetProgramNumber() Invalid program number {} for thing {}", command,
219 getThing().getUID());
222 updateOpMode = (currentMode == Thermoregulation.OperationMode.SCENARIO);
223 currentScenarioPrgNum = programNumber;
224 logger.debug("handleSetProgramNumber() currentScenarioPrgNum changed to: {}", programNumber);
227 // force OperationMode update if we are already in SCENARIO or WEEKLY mode
230 send(Thermoregulation.requestWriteWeeklyScenarioMode(getWhere(w.value()), currentMode,
231 currentFunction, programNumber));
232 } catch (OWNException e) {
233 logger.warn("handleSetProgramNumber() {}", e.getMessage());
235 } else { // just update channel
236 updateState(channel, new DecimalType(programNumber));
239 logger.warn("handleSetProgramNumber() Unsupported command {} for thing {}", command, getThing().getUID());
243 private void handleVacationDays(ChannelUID channel, Command command) {
244 if (command instanceof DecimalType) {
245 Where w = deviceWhere;
247 logger.warn("handleVacationDays() Where is null for thing {}", getThing().getUID());
250 if (!isCentralUnit) {
251 logger.warn("handleVacationDays() This command can be sent only for a Central Unit.");
254 int vacationDays = ((DecimalType) command).intValue();
256 if (vacationDays < 1 || vacationDays > 255) {
257 logger.warn("handleVacationDays() vacation days must be between 1 and 255");
261 currentVacationDays = vacationDays;
262 logger.debug("handleVacationDays() currentVacationDays changed to: {}", currentVacationDays);
264 // force OperationMode update if we are already in VACATION mode
265 if (currentMode == Thermoregulation.OperationMode.VACATION) {
267 send(Thermoregulation.requestWriteVacationMode(getWhere(w.value()), currentFunction,
268 currentVacationDays, currentWeeklyPrgNum));
269 } catch (OWNException e) {
270 logger.warn("handleVacationDays() {}", e.getMessage());
271 } catch (IllegalArgumentException e) {
272 logger.warn("handleVacationDays() Unsupported command {} for thing {}", command,
273 getThing().getUID());
275 } else { // just update channel
276 updateState(channel, new DecimalType(currentVacationDays));
279 logger.warn("handleVacationDays() Unsupported command {} for thing {}", command, getThing().getUID());
283 private void handleSetpoint(Command command) {
284 if (command instanceof QuantityType || command instanceof DecimalType) {
285 Where w = deviceWhere;
287 logger.warn("handleSetpoint() Where is null for thing {}", getThing().getUID());
291 if (command instanceof QuantityType) {
292 QuantityType<?> tempCelsius = ((QuantityType<?>) command).toUnit(SIUnits.CELSIUS);
293 if (tempCelsius != null) {
294 newTemp = tempCelsius.doubleValue();
297 newTemp = ((DecimalType) command).doubleValue();
299 if (newTemp >= 5.0 && newTemp <= 40.0) {
302 res = send(Thermoregulation.requestWriteSetpointTemperature(getWhere(w.value()), newTemp,
304 // For zones no setPoint temperature confirmation message is returned from gw,
305 // so we update channel with current requested temp on successful response.
306 if (!isCentralUnit && res != null && res.isSuccess()) {
307 updateState(CHANNEL_TEMP_SETPOINT, getAsQuantityTypeOrNull(newTemp, SIUnits.CELSIUS));
308 currentSetPointTemp = newTemp;
310 } catch (MalformedFrameException | OWNException e) {
311 logger.warn("handleSetpoint() {}", e.getMessage());
315 logger.info("handleSetpoint() Setpoint temperature must be between 5°C and 40°C for thing {}",
316 getThing().getUID());
319 logger.warn("handleSetpoint() Unsupported command {} for thing {}", command, getThing().getUID());
323 private void handleMode(Command command) {
324 if (command instanceof StringType) {
325 Where w = deviceWhere;
327 logger.warn("handleMode() Where is null for thing {}", getThing().getUID());
331 String whStr = getWhere(w.value());
332 String cmdStr = command.toString().toUpperCase();
333 OperationMode newMode;
335 newMode = OperationMode.valueOf(cmdStr);
336 } catch (IllegalArgumentException e) {
337 logger.warn("handleMode() Unsupported command {} for thing {}", command, getThing().getUID());
340 Thermoregulation tMsg = null;
341 if (isCentralUnit) { // CU accepted modes: MANUAL, OFF, PROTECTION, WEEKLY, SCENARIO, HOLIDAY,
347 tMsg = Thermoregulation.requestWriteMode(whStr, newMode, currentFunction, currentSetPointTemp);
351 int programNumber = (MODE_WEEKLY.equals(cmdStr) ? currentWeeklyPrgNum : currentScenarioPrgNum);
352 tMsg = Thermoregulation.requestWriteWeeklyScenarioMode(whStr, newMode, currentFunction,
356 tMsg = Thermoregulation.requestWriteHolidayMode(whStr, currentFunction, currentWeeklyPrgNum);
359 tMsg = Thermoregulation.requestWriteVacationMode(whStr, currentFunction, currentVacationDays,
360 currentWeeklyPrgNum);
363 logger.warn("handleMode() Unsupported command {} for CU thing {}", command,
364 getThing().getUID());
366 } else {// Zone accepted modes: MANUAL, OFF, PROTECTION, AUTO
369 tMsg = Thermoregulation.requestWriteMode(whStr, newMode, currentFunction, currentSetPointTemp);
374 tMsg = Thermoregulation.requestWriteMode(whStr, newMode, Function.GENERIC, currentSetPointTemp);
377 logger.warn("handleMode() Unsupported command {} for ZONE thing {}", command,
378 getThing().getUID());
385 } catch (OWNException e) {
386 logger.warn("handleMode() {}", e.getMessage());
389 logger.warn("handleMode() Unsupported command {} for thing {}", command, getThing().getUID());
394 private String getWhere(String where) {
396 if (where.charAt(0) == '#') {
398 } else { // to support old configurations for CU with where="0"
402 return isStandAlone ? where : "#" + where;
406 private void handleFunction(Command command) {
407 if (command instanceof StringType) {
408 Where w = deviceWhere;
410 logger.warn("handleFunction() Where is null for thing {}", getThing().getUID());
414 Thermoregulation.Function function = Thermoregulation.Function.valueOf(command.toString());
415 send(Thermoregulation.requestWriteFunction(w.value(), function));
416 } catch (OWNException e) {
417 logger.warn("handleFunction() {}", e.getMessage());
418 } catch (IllegalArgumentException e) {
419 logger.warn("handleFunction() Unsupported command {} for thing {}", command, getThing().getUID());
423 logger.warn("handleFunction() Unsupported command {} for thing {}", command, getThing().getUID());
428 protected void handleMessage(BaseOpenMessage msg) {
429 super.handleMessage(msg);
430 logger.debug("@@@@ Thermo.handleMessage(): {}", msg.toStringVerbose());
431 Thermoregulation tmsg = (Thermoregulation) msg;
433 WhatThermo tWhat = (WhatThermo) msg.getWhat();
435 logger.debug("handleMessage() Ignoring unsupported WHAT {}. Frame={}", tWhat, msg);
438 if (tWhat.value() > 40) {
439 // it's a CU mode event, CU state events will follow shortly, so let's reset
443 switch (tWhat.getType()) {
444 case AT_LEAST_ONE_PROBE_ANTIFREEZE:
445 cuAtLeastOneProbeProtection = true;
447 case AT_LEAST_ONE_PROBE_MANUAL:
448 cuAtLeastOneProbeManual = true;
450 case AT_LEAST_ONE_PROBE_OFF:
451 cuAtLeastOneProbeOff = true;
454 cuBatteryStatus = CU_BATTERY_KO;
456 case FAILURE_DISCOVERED:
457 cuFailureDiscovered = true;
459 case RELEASE_SENSOR_LOCAL_ADJUST:
460 logger.debug("handleMessage(): Ignoring unsupported WHAT {}. Frame={}", tWhat, msg);
462 case REMOTE_CONTROL_DISABLED:
463 updateCURemoteControlStatus(CU_REMOTE_CONTROL_DISABLED);
465 case REMOTE_CONTROL_ENABLED:
466 updateCURemoteControlStatus(CU_REMOTE_CONTROL_ENABLED);
469 // check and update values of other channels (mode, function, temp)
470 updateModeAndFunction(tmsg);
471 updateSetpoint(tmsg);
477 if (tmsg.isCommand()) {
478 updateModeAndFunction(tmsg);
480 DimThermo dim = (DimThermo) tmsg.getDim();
483 updateSetpoint(tmsg);
485 case COMPLETE_PROBE_STATUS:
486 updateTargetTemperature(tmsg);
488 case PROBE_TEMPERATURE:
490 updateTemperature(tmsg);
492 case ACTUATOR_STATUS:
493 updateActuatorStatus(tmsg);
496 updateFanCoilSpeed(tmsg);
499 updateLocalOffset(tmsg);
502 updateValveStatus(tmsg);
505 logger.debug("handleMessage() Ignoring unsupported DIM {} for thing {}. Frame={}", tmsg.getDim(),
506 getThing().getUID(), tmsg);
512 private void updateModeAndFunction(Thermoregulation tmsg) {
513 if (tmsg.getWhat() == null) {
514 logger.warn("updateModeAndFunction() Could not parse What {} (WHAT is null)", tmsg.getFrameValue());
517 Thermoregulation.WhatThermo what = tmsg.new WhatThermo(tmsg.getWhat().value());
519 if (what.getFunction() == null) {
520 logger.warn("updateModeAndFunction() Could not parse Function from: {}", tmsg.getFrameValue());
523 // update Function if it's not GENERIC
524 Thermoregulation.Function function = what.getFunction();
525 if (function != Function.GENERIC) {
526 if (currentFunction != function) {
527 updateState(CHANNEL_FUNCTION, new StringType(function.toString()));
528 currentFunction = function;
531 if (what.getType() == WhatThermoType.HEATING || what.getType() == WhatThermoType.CONDITIONING
532 || what.getType() == WhatThermoType.GENERIC) {
533 // *4*1*z## and *4*0*z## do not tell us which mode is the zone now, so let's
539 Thermoregulation.OperationMode operationMode = null;
541 operationMode = what.getMode();
543 if (operationMode != null) {
544 // set ProgramNumber/vacationDays channels when necessary
545 switch (operationMode) {
547 updateVacationDays(tmsg);
551 updateProgramNumber(tmsg);
556 if (!isCentralUnit && !(operationMode == OperationMode.AUTO || operationMode == OperationMode.OFF
557 || operationMode == OperationMode.PROTECTION || operationMode == OperationMode.MANUAL)) {
558 logger.warn("updateModeAndFunction() Unsupported mode for zone {} from message: {}",
559 getThing().getUID(), tmsg.getFrameValue());
562 if (currentMode != operationMode) {
563 updateState(CHANNEL_MODE, new StringType(operationMode.name()));
564 currentMode = operationMode;
568 logger.warn("updateModeAndFunction() Unrecognized mode from message: {}", tmsg.getFrameValue());
572 private void updateVacationDays(Thermoregulation tmsg) {
574 Integer vDays = ((WhatThermo) tmsg.getWhat()).vacationDays();
576 logger.debug("{} - updateVacationDays() set VACATION DAYS to: {}", getThing().getUID(), vDays);
577 updateState(CHANNEL_CU_VACATION_DAYS, new DecimalType(vDays));
578 currentVacationDays = vDays;
582 private void updateProgramNumber(Thermoregulation tmsg) {
584 WhatThermo wt = (WhatThermo) tmsg.getWhat();
588 Integer prNum = wt.programNumber();
590 if (wt.getMode() == OperationMode.SCENARIO) {
591 logger.debug("{} - updateProgramNumber() set SCENARIO program to: {}", getThing().getUID(), prNum);
592 updateState(CHANNEL_CU_SCENARIO_PROGRAM_NUMBER, new DecimalType(prNum));
593 currentScenarioPrgNum = prNum;
594 } else if (wt.getMode() == OperationMode.WEEKLY) {
595 logger.debug("{} - updateProgramNumber() set WEEKLY program to: {}", getThing().getUID(), prNum);
596 updateState(CHANNEL_CU_WEEKLY_PROGRAM_NUMBER, new DecimalType(prNum));
597 currentWeeklyPrgNum = prNum;
602 private void updateTemperature(Thermoregulation tmsg) {
604 double temp = Thermoregulation.parseTemperature(tmsg);
605 updateState(CHANNEL_TEMPERATURE, getAsQuantityTypeOrNull(temp, SIUnits.CELSIUS));
606 } catch (FrameException e) {
607 logger.warn("updateTemperature() FrameException on frame {}: {}", tmsg, e.getMessage());
608 updateState(CHANNEL_TEMPERATURE, UnDefType.UNDEF);
612 private void updateTargetTemperature(Thermoregulation tmsg) {
614 double temp = Thermoregulation.parseTemperature(tmsg);
615 updateState(CHANNEL_TEMP_TARGET, getAsQuantityTypeOrNull(temp, SIUnits.CELSIUS));
616 } catch (FrameException e) {
617 logger.warn("updateTargetTemperature() FrameException on frame {}: {}", tmsg, e.getMessage());
618 updateState(CHANNEL_TEMP_TARGET, UnDefType.UNDEF);
622 private void updateSetpoint(Thermoregulation tmsg) {
626 WhatThermo tw = (WhatThermo) tmsg.getWhat();
628 logger.warn("updateSetpoint() Could not parse what from {} (what is null)", tmsg.getFrameValue());
631 String[] parameters = tmsg.getWhatParams();
632 if (parameters.length > 0 && tw.getType() == WhatThermoType.MANUAL) {
633 // manual setpoint frame should be like *4*110#TTTT*#0##
634 newTemp = Thermoregulation.decodeTemperature(parameters[0]);
635 logger.debug("updateSetpoint() parsed temperature from {}: {} ---> {}", tmsg.toStringVerbose(),
636 parameters[0], newTemp);
639 newTemp = Thermoregulation.parseTemperature(tmsg);
642 updateState(CHANNEL_TEMP_SETPOINT, getAsQuantityTypeOrNull(newTemp, SIUnits.CELSIUS));
643 currentSetPointTemp = newTemp;
645 } catch (NumberFormatException e) {
646 logger.warn("updateSetpoint() NumberFormatException on frame {}: {}", tmsg, e.getMessage());
647 updateState(CHANNEL_TEMP_SETPOINT, UnDefType.UNDEF);
648 } catch (FrameException e) {
649 logger.warn("updateSetpoint() FrameException on frame {}: {}", tmsg, e.getMessage());
650 updateState(CHANNEL_TEMP_SETPOINT, UnDefType.UNDEF);
654 private void updateFanCoilSpeed(Thermoregulation tmsg) {
656 Thermoregulation.FanCoilSpeed speed = Thermoregulation.parseFanCoilSpeed(tmsg);
657 updateState(CHANNEL_FAN_SPEED, new StringType(speed.toString()));
658 } catch (NumberFormatException e) {
659 logger.warn("updateFanCoilSpeed() NumberFormatException on frame {}: {}", tmsg, e.getMessage());
660 updateState(CHANNEL_FAN_SPEED, UnDefType.UNDEF);
661 } catch (FrameException e) {
662 logger.warn("updateFanCoilSpeed() FrameException on frame {}: {}", tmsg, e.getMessage());
663 updateState(CHANNEL_FAN_SPEED, UnDefType.UNDEF);
667 private void updateValveStatus(Thermoregulation tmsg) {
669 Thermoregulation.ValveOrActuatorStatus cv = Thermoregulation.parseValveStatus(tmsg,
670 Thermoregulation.WhatThermoType.CONDITIONING);
671 updateState(CHANNEL_CONDITIONING_VALVES, new StringType(cv.toString()));
672 updateHeatingCooling(false, cv);
674 Thermoregulation.ValveOrActuatorStatus hv = Thermoregulation.parseValveStatus(tmsg,
675 Thermoregulation.WhatThermoType.HEATING);
676 updateState(CHANNEL_HEATING_VALVES, new StringType(hv.toString()));
677 updateHeatingCooling(true, hv);
679 } catch (FrameException e) {
680 logger.warn("updateValveStatus() FrameException on frame {}: {}", tmsg, e.getMessage());
681 updateState(CHANNEL_CONDITIONING_VALVES, UnDefType.UNDEF);
682 updateState(CHANNEL_HEATING_VALVES, UnDefType.UNDEF);
686 private void updateHeatingCooling(boolean heating, Thermoregulation.ValveOrActuatorStatus v) {
687 boolean state = false;
709 updateState(CHANNEL_HEATING, OnOffType.from(state));
711 updateState(CHANNEL_COOLING, OnOffType.from(state));
715 private void updateActuatorStatus(Thermoregulation tmsg) {
717 Thermoregulation.ValveOrActuatorStatus hv = Thermoregulation.parseActuatorStatus(tmsg);
718 updateState(CHANNEL_ACTUATORS, new StringType(hv.toString()));
719 } catch (FrameException e) {
720 logger.warn("updateActuatorStatus() FrameException on frame {}: {}", tmsg, e.getMessage());
721 updateState(CHANNEL_ACTUATORS, UnDefType.UNDEF);
725 private void updateLocalOffset(Thermoregulation tmsg) {
727 Thermoregulation.LocalOffset offset = Thermoregulation.parseLocalOffset(tmsg);
728 updateState(CHANNEL_LOCAL_OFFSET, new StringType(offset.toString()));
729 logger.debug("updateLocalOffset() {}: {}", tmsg, offset.toString());
731 } catch (FrameException e) {
732 logger.warn("updateLocalOffset() FrameException on frame {}: {}", tmsg, e.getMessage());
733 updateState(CHANNEL_LOCAL_OFFSET, UnDefType.UNDEF);
737 private void updateCURemoteControlStatus(String status) {
738 updateState(CHANNEL_CU_REMOTE_CONTROL, new StringType(status));
739 logger.debug("updateCURemoteControlStatus(): {}", status);
742 private void resetCUState() {
743 logger.debug("########### resetting CU state");
744 cuAtLeastOneProbeOff = false;
745 cuAtLeastOneProbeProtection = false;
746 cuAtLeastOneProbeManual = false;
747 cuBatteryStatus = CU_BATTERY_OK;
748 cuFailureDiscovered = false;
750 cuStateChannelsUpdateSchedule = scheduler.schedule(() -> {
751 updateCUStateChannels();
752 }, CU_STATE_CHANNELS_UPDATE_DELAY, TimeUnit.MILLISECONDS);
755 private void updateCUStateChannels() {
756 updateState(CHANNEL_CU_AT_LEAST_ONE_PROBE_OFF, OnOffType.from(cuAtLeastOneProbeOff));
757 updateState(CHANNEL_CU_AT_LEAST_ONE_PROBE_PROTECTION, OnOffType.from(cuAtLeastOneProbeProtection));
758 updateState(CHANNEL_CU_AT_LEAST_ONE_PROBE_MANUAL, OnOffType.from(cuAtLeastOneProbeManual));
759 updateState(CHANNEL_CU_BATTERY_STATUS, new StringType(cuBatteryStatus));
760 updateState(CHANNEL_CU_FAILURE_DISCOVERED, OnOffType.from(cuFailureDiscovered));
763 private Boolean channelExists(String channelID) {
764 return thing.getChannel(channelID) != null;
768 protected void refreshDevice(boolean refreshAll) {
769 logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
771 Where w = deviceWhere;
773 logger.warn("refreshDevice() Where is null for thing {}", getThing().getUID());
777 String whereStr = w.value();
781 send(Thermoregulation.requestStatus(getWhere(whereStr)));
782 } catch (OWNException e) {
783 logger.warn("refreshDevice() central unit returned OWNException {}", e.getMessage());
789 send(Thermoregulation.requestTemperature(whereStr));
791 if (!((WhereThermo) w).isProbe()) {
792 // for bus_thermo_zone request also other single channels updates
793 send(Thermoregulation.requestSetPointTemperature(whereStr));
794 send(Thermoregulation.requestMode(whereStr));
796 // refresh ONLY subscribed channels
797 if (channelExists(CHANNEL_FAN_SPEED)) {
798 send(Thermoregulation.requestFanCoilSpeed(whereStr));
800 if (channelExists(CHANNEL_CONDITIONING_VALVES) || channelExists(CHANNEL_HEATING_VALVES)) {
801 send(Thermoregulation.requestValvesStatus(whereStr));
803 if (channelExists(CHANNEL_ACTUATORS)) {
804 send(Thermoregulation.requestActuatorsStatus(whereStr));
806 if (channelExists(CHANNEL_LOCAL_OFFSET)) {
807 send(Thermoregulation.requestLocalOffset(whereStr));
810 } catch (OWNException e) {
811 logger.warn("refreshDevice() where='{}' returned OWNException {}", whereStr, e.getMessage());
816 public void dispose() {
817 ScheduledFuture<?> s = cuStateChannelsUpdateSchedule;
820 logger.debug("dispose() - scheduler stopped.");