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