]> git.basschouten.com Git - openhab-addons.git/blob
a7b9a87b4b495ac05b207872f32eb664e27346c2
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.openwebnet.internal.handler;
14
15 import static org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants.*;
16
17 import java.util.HashSet;
18 import java.util.Set;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
22 import org.openhab.core.library.types.DecimalType;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.library.types.QuantityType;
25 import org.openhab.core.library.types.StringType;
26 import org.openhab.core.library.unit.SIUnits;
27 import org.openhab.core.thing.ChannelUID;
28 import org.openhab.core.thing.Thing;
29 import org.openhab.core.thing.ThingStatus;
30 import org.openhab.core.thing.ThingStatusDetail;
31 import org.openhab.core.thing.ThingTypeUID;
32 import org.openhab.core.types.Command;
33 import org.openhab.core.types.UnDefType;
34 import org.openwebnet4j.communication.OWNException;
35 import org.openwebnet4j.message.BaseOpenMessage;
36 import org.openwebnet4j.message.FrameException;
37 import org.openwebnet4j.message.MalformedFrameException;
38 import org.openwebnet4j.message.Thermoregulation;
39 import org.openwebnet4j.message.Thermoregulation.WhatThermo;
40 import org.openwebnet4j.message.Where;
41 import org.openwebnet4j.message.WhereThermo;
42 import org.openwebnet4j.message.Who;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 /**
47  * The {@link OpenWebNetThermoregulationHandler} is responsible for handling
48  * commands/messages for Thermoregulation
49  * Things. It extends the abstract {@link OpenWebNetThingHandler}.
50  *
51  * @author Massimo Valla - Initial contribution
52  * @author Andrea Conte - Thermoregulation
53  * @author Gilberto Cocchi - Thermoregulation
54  */
55 @NonNullByDefault
56 public class OpenWebNetThermoregulationHandler extends OpenWebNetThingHandler {
57
58     private final Logger logger = LoggerFactory.getLogger(OpenWebNetThermoregulationHandler.class);
59
60     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.THERMOREGULATION_SUPPORTED_THING_TYPES;
61
62     private double currentSetPointTemp = 11.5d; // 11.5 is the default setTemp used in MyHomeUP mobile app
63
64     private Thermoregulation.Function currentFunction = Thermoregulation.Function.GENERIC;
65     private Thermoregulation.OperationMode currentMode = Thermoregulation.OperationMode.MANUAL;
66
67     private boolean isStandAlone = false;
68
69     private boolean isCentralUnit = false;
70
71     private String programNumber = "1";
72
73     private static Set<String> probesInProtection = new HashSet<String>();
74     private static Set<String> probesInOFF = new HashSet<String>();
75     private static Set<String> probesInManual = new HashSet<String>();
76
77     private static final String CU_REMOTE_CONTROL_ENABLED = "ENABLED";
78     private static final String CU_REMOTE_CONTROL_DISABLED = "DISABLED";
79     private static final String CU_BATTERY_OK = "OK";
80     private static final String CU_BATTERY_KO = "KO";
81     private static final Integer UNDOCUMENTED_WHAT_4001 = 4001;
82     private static final Integer UNDOCUMENTED_WHAT_4002 = 4002;
83
84     public OpenWebNetThermoregulationHandler(Thing thing) {
85         super(thing);
86     }
87
88     @Override
89     public void initialize() {
90         super.initialize();
91
92         ThingTypeUID thingType = thing.getThingTypeUID();
93         isCentralUnit = OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_CU.equals(thingType);
94
95         if (!isCentralUnit) {
96             Object standAloneConfig = getConfig().get(OpenWebNetBindingConstants.CONFIG_PROPERTY_STANDALONE);
97             if (standAloneConfig != null) {
98                 // null in case of thermo_sensor
99                 isStandAlone = Boolean.parseBoolean(standAloneConfig.toString());
100             }
101         } else {
102             // central unit must have WHERE=0
103             if (!deviceWhere.value().equals("0")) {
104                 logger.warn("initialize() Invalid WHERE={} for Central Unit.", deviceWhere.value());
105
106                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
107                         "@text/offline.conf-error-where");
108             }
109
110             // reset state of signal channels (they will be setted when specific messages
111             // are received)
112             updateState(CHANNEL_CU_AT_LEAST_ONE_PROBE_MANUAL, OnOffType.OFF);
113             updateState(CHANNEL_CU_AT_LEAST_ONE_PROBE_OFF, OnOffType.OFF);
114             updateState(CHANNEL_CU_AT_LEAST_ONE_PROBE_PROTECTION, OnOffType.OFF);
115             updateState(CHANNEL_CU_SCENARIO_PROGRAM_NUMBER, new DecimalType(programNumber));
116             updateState(CHANNEL_CU_WEEKLY_PROGRAM_NUMBER, new DecimalType(programNumber));
117             updateState(CHANNEL_CU_FAILURE_DISCOVERED, OnOffType.OFF);
118         }
119     }
120
121     @Override
122     protected void handleChannelCommand(ChannelUID channel, Command command) {
123         switch (channel.getId()) {
124             case CHANNEL_TEMP_SETPOINT:
125                 handleSetpoint(command);
126                 break;
127             case CHANNEL_FUNCTION:
128                 handleFunction(command);
129                 break;
130             case CHANNEL_MODE:
131                 handleMode(command);
132                 break;
133             case CHANNEL_FAN_SPEED:
134                 handleSetFanSpeed(command);
135                 break;
136             case CHANNEL_CU_WEEKLY_PROGRAM_NUMBER:
137             case CHANNEL_CU_SCENARIO_PROGRAM_NUMBER:
138                 handleSetProgramNumber(command);
139                 break;
140             default: {
141                 logger.warn("handleChannelCommand() Unsupported ChannelUID {}", channel.getId());
142             }
143         }
144     }
145
146     @Override
147     protected void requestChannelState(ChannelUID channel) {
148         super.requestChannelState(channel);
149         refreshDevice(false);
150     }
151
152     @Override
153     protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
154         return new WhereThermo(wStr);
155     }
156
157     @Override
158     protected String ownIdPrefix() {
159         return Who.THERMOREGULATION.value().toString();
160     }
161
162     private void handleSetFanSpeed(Command command) {
163         if (command instanceof StringType) {
164             Where w = deviceWhere;
165             if (w != null) {
166                 try {
167                     Thermoregulation.FanCoilSpeed speed = Thermoregulation.FanCoilSpeed.valueOf(command.toString());
168                     send(Thermoregulation.requestWriteFanCoilSpeed(w.value(), speed));
169                 } catch (OWNException e) {
170                     logger.warn("handleSetFanSpeed() {}", e.getMessage());
171                 } catch (IllegalArgumentException e) {
172                     logger.warn("handleSetFanSpeed() Unsupported command {} for thing {}", command,
173                             getThing().getUID());
174                     return;
175                 }
176             }
177         } else {
178             logger.warn("handleSetFanSpeed() Unsupported command {} for thing {}", command, getThing().getUID());
179         }
180     }
181
182     private void handleSetProgramNumber(Command command) {
183         if (command instanceof DecimalType) {
184             if (!isCentralUnit) {
185                 logger.warn("handleSetProgramNumber() This command can be sent only for a Central Unit.");
186                 return;
187             }
188
189             programNumber = command.toString();
190             logger.debug("handleSetProgramNumber() Program number set to {}", programNumber);
191
192             // force OperationMode update if we are already in SCENARIO o WEEKLY mode
193             if (currentMode.isScenario() || currentMode.isWeekly()) {
194                 try {
195                     Thermoregulation.OperationMode new_mode = Thermoregulation.OperationMode
196                             .valueOf(currentMode.mode() + "_" + programNumber);
197                     logger.debug("handleSetProgramNumber() new mode {}", new_mode);
198                     send(Thermoregulation.requestWriteMode(getWhere(""), new_mode, currentFunction,
199                             currentSetPointTemp));
200                 } catch (OWNException e) {
201                     logger.warn("handleSetProgramNumber() {}", e.getMessage());
202                 } catch (IllegalArgumentException e) {
203                     logger.warn("handleSetProgramNumber() Unsupported command {} for thing {}", command,
204                             getThing().getUID());
205                 }
206             }
207
208         } else {
209             logger.warn("handleSetProgramNumber() Unsupported command {} for thing {}", command, getThing().getUID());
210         }
211     }
212
213     private void handleSetpoint(Command command) {
214         if (command instanceof QuantityType || command instanceof DecimalType) {
215             Where w = deviceWhere;
216             if (w != null) {
217                 double newTemp = 0;
218                 if (command instanceof QuantityType) {
219                     QuantityType<?> tempCelsius = ((QuantityType<?>) command).toUnit(SIUnits.CELSIUS);
220                     if (tempCelsius != null) {
221                         newTemp = tempCelsius.doubleValue();
222                     }
223                 } else {
224                     newTemp = ((DecimalType) command).doubleValue();
225                 }
226                 try {
227                     send(Thermoregulation.requestWriteSetpointTemperature(getWhere(w.value()), newTemp,
228                             currentFunction));
229                 } catch (MalformedFrameException | OWNException e) {
230                     logger.warn("handleSetpoint() {}", e.getMessage());
231                 }
232             }
233         } else {
234             logger.warn("handleSetpoint() Unsupported command {} for thing {}", command, getThing().getUID());
235         }
236     }
237
238     private void handleMode(Command command) {
239         if (command instanceof StringType) {
240             Where w = deviceWhere;
241             if (w != null) {
242                 try {
243                     Thermoregulation.OperationMode new_mode = Thermoregulation.OperationMode.OFF;
244
245                     if (isCentralUnit && WhatThermo.isComplex(command.toString())) {
246                         new_mode = Thermoregulation.OperationMode.valueOf(command.toString() + "_" + programNumber);
247
248                         // store current mode
249                         currentMode = new_mode;
250                     } else {
251                         new_mode = Thermoregulation.OperationMode.valueOf(command.toString());
252                     }
253                     send(Thermoregulation.requestWriteMode(getWhere(w.value()), new_mode, currentFunction,
254                             currentSetPointTemp));
255                 } catch (OWNException e) {
256                     logger.warn("handleMode() {}", e.getMessage());
257                 } catch (IllegalArgumentException e) {
258                     logger.warn("handleMode() Unsupported command {} for thing {}", command, getThing().getUID());
259                     return;
260                 }
261             }
262         } else {
263             logger.warn("handleMode() Unsupported command {} for thing {}", command, getThing().getUID());
264         }
265     }
266
267     private String getWhere(String where) {
268         if (isCentralUnit) {
269             return "#0";
270         } else {
271             return isStandAlone ? where : "#" + where;
272         }
273     }
274
275     private void handleFunction(Command command) {
276         if (command instanceof StringType) {
277             Where w = deviceWhere;
278             if (w != null) {
279                 try {
280                     Thermoregulation.Function function = Thermoregulation.Function.valueOf(command.toString());
281                     send(Thermoregulation.requestWriteFunction(w.value(), function));
282                 } catch (OWNException e) {
283                     logger.warn("handleFunction() {}", e.getMessage());
284                 } catch (IllegalArgumentException e) {
285                     logger.warn("handleFunction() Unsupported command {} for thing {}", command, getThing().getUID());
286                     return;
287                 }
288             }
289         } else {
290             logger.warn("handleFunction() Unsupported command {} for thing {}", command, getThing().getUID());
291         }
292     }
293
294     @Override
295     protected void handleMessage(BaseOpenMessage msg) {
296         super.handleMessage(msg);
297
298         if (isCentralUnit) {
299             if (msg.getWhat() == null) {
300                 return;
301             }
302
303             // there isn't a message used for setting OK for battery status so let's assume
304             // it's OK and then change to KO if according message is received
305             updateCUBatteryStatus(CU_BATTERY_OK);
306
307             // same in case of Failure Discovered
308             updateCUFailureDiscovered(OnOffType.OFF);
309
310             if (msg.getWhat() == Thermoregulation.WhatThermo.REMOTE_CONTROL_DISABLED) {
311                 updateCURemoteControlStatus(CU_REMOTE_CONTROL_DISABLED);
312             } else if (msg.getWhat() == Thermoregulation.WhatThermo.REMOTE_CONTROL_ENABLED) {
313                 updateCURemoteControlStatus(CU_REMOTE_CONTROL_ENABLED);
314             } else if (msg.getWhat() == Thermoregulation.WhatThermo.BATTERY_KO) {
315                 updateCUBatteryStatus(CU_BATTERY_KO);
316             } else if (msg.getWhat() == Thermoregulation.WhatThermo.AT_LEAST_ONE_PROBE_OFF) {
317                 updateCUAtLeastOneProbeOff(OnOffType.ON);
318             } else if (msg.getWhat() == Thermoregulation.WhatThermo.AT_LEAST_ONE_PROBE_ANTIFREEZE) {
319                 updateCUAtLeastOneProbeProtection(OnOffType.ON);
320             } else if (msg.getWhat() == Thermoregulation.WhatThermo.AT_LEAST_ONE_PROBE_MANUAL) {
321                 updateCUAtLeastOneProbeManual(OnOffType.ON);
322             } else if (msg.getWhat() == Thermoregulation.WhatThermo.FAILURE_DISCOVERED) {
323                 updateCUFailureDiscovered(OnOffType.ON);
324             } // must intercept all possibile WHATs
325             else if (msg.getWhat() == Thermoregulation.WhatThermo.RELEASE_SENSOR_LOCAL_ADJUST) { // will be implemented
326                                                                                                  // soon
327                 logger.debug("handleMessage() Ignoring unsupported WHAT {}. Frame={}", msg.getWhat(), msg);
328             } else if (msg.getWhat().value() == UNDOCUMENTED_WHAT_4001) {
329                 logger.debug("handleMessage() Ignoring unsupported WHAT {}. Frame={}", msg.getWhat(), msg);
330             } else if (msg.getWhat().value() == UNDOCUMENTED_WHAT_4002) {
331                 logger.debug("handleMessage() Ignoring unsupported WHAT {}. Frame={}", msg.getWhat(), msg);
332             } else {
333                 // check and update values of other channel (mode, function, temp)
334                 updateModeAndFunction((Thermoregulation) msg);
335                 updateSetpoint((Thermoregulation) msg);
336             }
337             return;
338         }
339
340         if (msg.isCommand()) {
341             updateModeAndFunction((Thermoregulation) msg);
342         } else {
343             if (msg.getDim() == null) {
344                 return;
345             }
346             if (msg.getDim() == Thermoregulation.DimThermo.TEMPERATURE
347                     || msg.getDim() == Thermoregulation.DimThermo.PROBE_TEMPERATURE) {
348                 updateTemperature((Thermoregulation) msg);
349             } else if (msg.getDim() == Thermoregulation.DimThermo.TEMP_SETPOINT
350                     || msg.getDim() == Thermoregulation.DimThermo.COMPLETE_PROBE_STATUS) {
351                 updateSetpoint((Thermoregulation) msg);
352             } else if (msg.getDim() == Thermoregulation.DimThermo.VALVES_STATUS) {
353                 updateValveStatus((Thermoregulation) msg);
354             } else if (msg.getDim() == Thermoregulation.DimThermo.ACTUATOR_STATUS) {
355                 updateActuatorStatus((Thermoregulation) msg);
356             } else if (msg.getDim() == Thermoregulation.DimThermo.FAN_COIL_SPEED) {
357                 updateFanCoilSpeed((Thermoregulation) msg);
358             } else if (msg.getDim() == Thermoregulation.DimThermo.OFFSET) {
359                 updateLocalOffset((Thermoregulation) msg);
360             } else {
361                 logger.debug("handleMessage() Ignoring unsupported DIM {} for thing {}. Frame={}", msg.getDim(),
362                         getThing().getUID(), msg);
363             }
364         }
365     }
366
367     private void updateModeAndFunction(Thermoregulation tmsg) {
368         if (tmsg.getWhat() == null) {
369             logger.debug("updateModeAndFunction() Could not parse Mode or Function from {} (what is null)",
370                     tmsg.getFrameValue());
371             return;
372         }
373         Thermoregulation.WhatThermo w = Thermoregulation.WhatThermo.fromValue(tmsg.getWhat().value());
374
375         if (w.getMode() == null) {
376             logger.debug("updateModeAndFunction() Could not parse Mode from: {}", tmsg.getFrameValue());
377             return;
378         }
379
380         if (w.getFunction() == null) {
381             logger.debug("updateModeAndFunction() Could not parse Function from: {}", tmsg.getFrameValue());
382             return;
383         }
384
385         Thermoregulation.OperationMode operationMode = w.getMode();
386         Thermoregulation.Function function = w.getFunction();
387
388         // keep track of thermostats (zones) status
389         if (!isCentralUnit && (!((WhereThermo) deviceWhere).isProbe())) {
390             if (operationMode == Thermoregulation.OperationMode.OFF) {
391                 probesInManual.remove(tmsg.getWhere().value());
392                 probesInProtection.remove(tmsg.getWhere().value());
393                 if (probesInOFF.add(tmsg.getWhere().value())) {
394                     logger.debug("atLeastOneProbeInOFF: added WHERE ---> {}", tmsg.getWhere());
395                 }
396             } else if (operationMode == Thermoregulation.OperationMode.PROTECTION) {
397                 probesInManual.remove(tmsg.getWhere().value());
398                 probesInOFF.remove(tmsg.getWhere().value());
399                 if (probesInProtection.add(tmsg.getWhere().value())) {
400                     logger.debug("atLeastOneProbeInProtection: added WHERE ---> {}", tmsg.getWhere());
401                 }
402             } else if (operationMode == Thermoregulation.OperationMode.MANUAL) {
403                 probesInProtection.remove(tmsg.getWhere().value());
404                 probesInOFF.remove(tmsg.getWhere().value());
405                 if (probesInManual.add(tmsg.getWhere().value())) {
406                     logger.debug("atLeastOneProbeInManual: added WHERE ---> {}", tmsg.getWhere());
407                 }
408             }
409
410             if (probesInOFF.isEmpty()) {
411                 updateCUAtLeastOneProbeOff(OnOffType.OFF);
412             }
413             if (probesInProtection.isEmpty()) {
414                 updateCUAtLeastOneProbeProtection(OnOffType.OFF);
415             }
416             if (probesInManual.isEmpty()) {
417                 updateCUAtLeastOneProbeManual(OnOffType.OFF);
418             }
419         }
420
421         updateState(CHANNEL_FUNCTION, new StringType(function.toString()));
422
423         // must convert from OperationMode to Mode and set ProgramNumber when necessary
424         updateState(CHANNEL_MODE, new StringType(operationMode.mode()));
425         if (operationMode.isScenario()) {
426             logger.debug("updateModeAndFunction() set SCENARIO program to: {}", operationMode.programNumber());
427             updateState(CHANNEL_CU_SCENARIO_PROGRAM_NUMBER, new DecimalType(operationMode.programNumber()));
428         }
429         if (operationMode.isWeekly()) {
430             logger.debug("updateModeAndFunction() set WEEKLY program to: {}", operationMode.programNumber());
431             updateState(CHANNEL_CU_WEEKLY_PROGRAM_NUMBER, new DecimalType(operationMode.programNumber()));
432         }
433
434         // store current function
435         currentFunction = function;
436
437         // in case of central unit store also current operation mode
438         if (isCentralUnit) {
439             currentMode = operationMode;
440         }
441     }
442
443     private void updateTemperature(Thermoregulation tmsg) {
444         try {
445             double temp = Thermoregulation.parseTemperature(tmsg);
446             updateState(CHANNEL_TEMPERATURE, getAsQuantityTypeOrNull(temp, SIUnits.CELSIUS));
447         } catch (FrameException e) {
448             logger.warn("updateTemperature() FrameException on frame {}: {}", tmsg, e.getMessage());
449             updateState(CHANNEL_TEMPERATURE, UnDefType.UNDEF);
450         }
451     }
452
453     private void updateSetpoint(Thermoregulation tmsg) {
454         try {
455             double temp = 11.5d;
456             if (isCentralUnit) {
457                 if (tmsg.getWhat() == null) {
458                     // it should be like *4*WHAT#TTTT*#0##
459                     logger.debug("updateSetpoint() Could not parse function from {} (what is null)",
460                             tmsg.getFrameValue());
461                     return;
462                 }
463
464                 String[] parameters = tmsg.getWhatParams();
465                 if (parameters.length > 0) {
466                     temp = Thermoregulation.decodeTemperature(parameters[0]);
467                     logger.debug("updateSetpoint() parsed temperature from {}: {} ---> {}", tmsg.toStringVerbose(),
468                             parameters[0], temp);
469                 }
470             } else {
471                 temp = Thermoregulation.parseTemperature(tmsg);
472             }
473             updateState(CHANNEL_TEMP_SETPOINT, getAsQuantityTypeOrNull(temp, SIUnits.CELSIUS));
474             currentSetPointTemp = temp;
475         } catch (NumberFormatException e) {
476             logger.warn("updateSetpoint() NumberFormatException on frame {}: {}", tmsg, e.getMessage());
477             updateState(CHANNEL_TEMP_SETPOINT, UnDefType.UNDEF);
478         } catch (FrameException e) {
479             logger.warn("updateSetpoint() FrameException on frame {}: {}", tmsg, e.getMessage());
480             updateState(CHANNEL_TEMP_SETPOINT, UnDefType.UNDEF);
481         }
482     }
483
484     private void updateFanCoilSpeed(Thermoregulation tmsg) {
485         try {
486             Thermoregulation.FanCoilSpeed speed = Thermoregulation.parseFanCoilSpeed(tmsg);
487             updateState(CHANNEL_FAN_SPEED, new StringType(speed.toString()));
488         } catch (FrameException e) {
489             logger.warn("updateFanCoilSpeed() FrameException on frame {}: {}", tmsg, e.getMessage());
490             updateState(CHANNEL_FAN_SPEED, UnDefType.UNDEF);
491         }
492     }
493
494     private void updateValveStatus(Thermoregulation tmsg) {
495         try {
496             Thermoregulation.ValveOrActuatorStatus cv = Thermoregulation.parseValveStatus(tmsg,
497                     Thermoregulation.WhatThermo.CONDITIONING);
498             updateState(CHANNEL_CONDITIONING_VALVES, new StringType(cv.toString()));
499
500             Thermoregulation.ValveOrActuatorStatus hv = Thermoregulation.parseValveStatus(tmsg,
501                     Thermoregulation.WhatThermo.HEATING);
502             updateState(CHANNEL_HEATING_VALVES, new StringType(hv.toString()));
503         } catch (FrameException e) {
504             logger.warn("updateValveStatus() FrameException on frame {}: {}", tmsg, e.getMessage());
505             updateState(CHANNEL_CONDITIONING_VALVES, UnDefType.UNDEF);
506             updateState(CHANNEL_HEATING_VALVES, UnDefType.UNDEF);
507         }
508     }
509
510     private void updateActuatorStatus(Thermoregulation tmsg) {
511         try {
512             Thermoregulation.ValveOrActuatorStatus hv = Thermoregulation.parseActuatorStatus(tmsg);
513             updateState(CHANNEL_ACTUATORS, new StringType(hv.toString()));
514         } catch (FrameException e) {
515             logger.warn("updateActuatorStatus() FrameException on frame {}: {}", tmsg, e.getMessage());
516             updateState(CHANNEL_ACTUATORS, UnDefType.UNDEF);
517         }
518     }
519
520     private void updateLocalOffset(Thermoregulation tmsg) {
521         try {
522             Thermoregulation.LocalOffset offset = Thermoregulation.parseLocalOffset(tmsg);
523             updateState(CHANNEL_LOCAL_OFFSET, new StringType(offset.toString()));
524             logger.debug("updateLocalOffset() {}: {}", tmsg, offset.toString());
525
526         } catch (FrameException e) {
527             logger.warn("updateLocalOffset() FrameException on frame {}: {}", tmsg, e.getMessage());
528             updateState(CHANNEL_LOCAL_OFFSET, UnDefType.UNDEF);
529         }
530     }
531
532     private void updateCURemoteControlStatus(String status) {
533         updateState(CHANNEL_CU_REMOTE_CONTROL, new StringType(status));
534         logger.debug("updateCURemoteControlStatus(): {}", status);
535     }
536
537     private void updateCUBatteryStatus(String status) {
538         updateState(CHANNEL_CU_BATTERY_STATUS, new StringType(status));
539
540         if (status == CU_BATTERY_KO) { // do not log default value (which is automatically setted)
541             logger.debug("updateCUBatteryStatus(): {}", status);
542         }
543     }
544
545     private void updateCUFailureDiscovered(OnOffType status) {
546         updateState(CHANNEL_CU_FAILURE_DISCOVERED, status);
547
548         if (status == OnOffType.ON) { // do not log default value (which is automatically setted)
549             logger.debug("updateCUFailureDiscovered(): {}", status);
550         }
551     }
552
553     private void updateCUAtLeastOneProbeOff(OnOffType status) {
554         updateState(CHANNEL_CU_AT_LEAST_ONE_PROBE_OFF, status);
555         logger.debug("updateCUAtLeastOneProbeOff(): {}", status);
556     }
557
558     private void updateCUAtLeastOneProbeProtection(OnOffType status) {
559         updateState(CHANNEL_CU_AT_LEAST_ONE_PROBE_PROTECTION, status);
560         logger.debug("updateCUAtLeastOneProbeProtection(): {}", status);
561     }
562
563     private void updateCUAtLeastOneProbeManual(OnOffType status) {
564         updateState(CHANNEL_CU_AT_LEAST_ONE_PROBE_MANUAL, status);
565         logger.debug("updateCUAtLeastOneProbeManual(): {}", status);
566     }
567
568     private Boolean channelExists(String channelID) {
569         return thing.getChannel(channelID) != null;
570     }
571
572     @Override
573     protected void refreshDevice(boolean refreshAll) {
574         logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
575         if (isCentralUnit) {
576             // TODO: 4 zone central -> zone #0 CAN be also a zone with its temp.. with
577             // 99-zones central no! let's assume it's a 99 zone
578             try {
579                 send(Thermoregulation.requestStatus("#0"));
580             } catch (OWNException e) {
581                 logger.warn("refreshDevice() central unit returned OWNException {}", e.getMessage());
582             }
583
584             return;
585         }
586
587         if (deviceWhere != null) {
588
589             String w = deviceWhere.value();
590             try {
591                 send(Thermoregulation.requestTemperature(w));
592
593                 if (!((WhereThermo) deviceWhere).isProbe()) {
594                     // for bus_thermo_zone request also other single channels updates
595                     send(Thermoregulation.requestSetPointTemperature(w));
596                     send(Thermoregulation.requestMode(w));
597
598                     // refresh ONLY subscribed channels
599                     if (channelExists(CHANNEL_FAN_SPEED)) {
600                         send(Thermoregulation.requestFanCoilSpeed(w));
601                     }
602
603                     if (channelExists(CHANNEL_CONDITIONING_VALVES) || channelExists(CHANNEL_HEATING_VALVES)) {
604                         send(Thermoregulation.requestValvesStatus(w));
605                     }
606
607                     if (channelExists(CHANNEL_ACTUATORS)) {
608                         send(Thermoregulation.requestActuatorsStatus(w));
609                     }
610
611                     if (channelExists(CHANNEL_LOCAL_OFFSET)) {
612                         send(Thermoregulation.requestLocalOffset(w));
613                     }
614                 }
615             } catch (OWNException e) {
616                 logger.warn("refreshDevice() where='{}' returned OWNException {}", w, e.getMessage());
617             }
618         }
619     }
620 }