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