]> git.basschouten.com Git - openhab-addons.git/blob
705f59dbc073bb586777879eae3d97854a27b4f4
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.livisismarthome.internal.handler;
14
15 import static org.openhab.binding.livisismarthome.internal.LivisiBindingConstants.*;
16
17 import java.time.format.DateTimeFormatter;
18 import java.time.format.FormatStyle;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Optional;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.livisismarthome.internal.client.api.entity.action.ShutterActionType;
26 import org.openhab.binding.livisismarthome.internal.client.api.entity.capability.CapabilityDTO;
27 import org.openhab.binding.livisismarthome.internal.client.api.entity.capability.CapabilityStateDTO;
28 import org.openhab.binding.livisismarthome.internal.client.api.entity.device.DeviceDTO;
29 import org.openhab.binding.livisismarthome.internal.client.api.entity.event.EventDTO;
30 import org.openhab.binding.livisismarthome.internal.client.api.entity.event.EventPropertiesDTO;
31 import org.openhab.binding.livisismarthome.internal.listener.DeviceStatusListener;
32 import org.openhab.core.library.types.DecimalType;
33 import org.openhab.core.library.types.OnOffType;
34 import org.openhab.core.library.types.OpenClosedType;
35 import org.openhab.core.library.types.PercentType;
36 import org.openhab.core.library.types.QuantityType;
37 import org.openhab.core.library.types.StopMoveType;
38 import org.openhab.core.library.types.StringType;
39 import org.openhab.core.library.types.UpDownType;
40 import org.openhab.core.library.unit.SIUnits;
41 import org.openhab.core.library.unit.Units;
42 import org.openhab.core.thing.Bridge;
43 import org.openhab.core.thing.Channel;
44 import org.openhab.core.thing.ChannelUID;
45 import org.openhab.core.thing.CommonTriggerEvents;
46 import org.openhab.core.thing.Thing;
47 import org.openhab.core.thing.ThingStatus;
48 import org.openhab.core.thing.ThingStatusDetail;
49 import org.openhab.core.thing.ThingStatusInfo;
50 import org.openhab.core.thing.binding.BaseThingHandler;
51 import org.openhab.core.thing.binding.ThingHandler;
52 import org.openhab.core.types.Command;
53 import org.openhab.core.types.RefreshType;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 /**
58  * The {@link LivisiDeviceHandler} is responsible for handling the {@link DeviceDTO}s and their commands, which are
59  * sent to one of the channels.
60  *
61  * @author Oliver Kuhl - Initial contribution
62  * @author Sven Strohschein - Renamed from Innogy to Livisi
63  */
64 @NonNullByDefault
65 public class LivisiDeviceHandler extends BaseThingHandler implements DeviceStatusListener {
66
67     private static final int MIN_TEMPERATURE_CELSIUS = 6;
68     private static final int MAX_TEMPERATURE_CELSIUS = 30;
69     private static final String LONG_PRESS = "LongPress";
70     private static final String SHORT_PRESS = "ShortPress";
71
72     private final Logger logger = LoggerFactory.getLogger(LivisiDeviceHandler.class);
73     private final Object lock = new Object();
74
75     private String deviceId = "";
76     private @Nullable LivisiBridgeHandler bridgeHandler;
77
78     /**
79      * Constructs a new {@link LivisiDeviceHandler} for the given {@link Thing}.
80      *
81      * @param thing device thing
82      */
83     public LivisiDeviceHandler(final Thing thing) {
84         super(thing);
85     }
86
87     @Override
88     public void handleCommand(final ChannelUID channelUID, final Command command) {
89         logger.debug("handleCommand called for channel '{}' of type '{}' with command '{}'", channelUID,
90                 getThing().getThingTypeUID().getId(), command);
91
92         if (ThingStatus.ONLINE.equals(getThing().getStatus())) {
93             final Optional<LivisiBridgeHandler> bridgeHandlerOptional = getBridgeHandler();
94             if (bridgeHandlerOptional.isPresent()) {
95                 LivisiBridgeHandler bridgeHandler = bridgeHandlerOptional.get();
96                 if (command instanceof RefreshType) {
97                     final Optional<DeviceDTO> device = bridgeHandler.getDeviceById(deviceId);
98                     device.ifPresent(this::onDeviceStateChanged);
99                 } else {
100                     executeCommand(channelUID, command, bridgeHandler);
101                 }
102             } else {
103                 logger.warn("BridgeHandler not found. Cannot handle command without bridge.");
104             }
105         } else {
106             logger.debug("Cannot handle command - thing is not online. Command ignored.");
107         }
108     }
109
110     private void executeCommand(ChannelUID channelUID, Command command, LivisiBridgeHandler bridgeHandler) {
111         if (CHANNEL_SWITCH.equals(channelUID.getId())) {
112             commandSwitchDevice(command, bridgeHandler);
113         } else if (CHANNEL_DIMMER.equals(channelUID.getId())) {
114             commandSetDimLevel(command, bridgeHandler);
115         } else if (CHANNEL_ROLLERSHUTTER.equals(channelUID.getId())) {
116             commandRollerShutter(command, bridgeHandler);
117         } else if (CHANNEL_TARGET_TEMPERATURE.equals(channelUID.getId())) {
118             commandUpdatePointTemperature(command, bridgeHandler);
119         } else if (CHANNEL_OPERATION_MODE.equals(channelUID.getId())) {
120             commandSetOperationMode(command, bridgeHandler);
121         } else if (CHANNEL_ALARM.equals(channelUID.getId())) {
122             commandSwitchAlarm(command, bridgeHandler);
123         } else {
124             logger.debug("UNSUPPORTED channel {} for device {}.", channelUID.getId(), deviceId);
125         }
126     }
127
128     private void commandSwitchDevice(Command command, LivisiBridgeHandler bridgeHandler) {
129         if (command instanceof OnOffType) {
130             bridgeHandler.commandSwitchDevice(deviceId, OnOffType.ON.equals(command));
131         }
132     }
133
134     private void commandSetDimLevel(Command command, LivisiBridgeHandler bridgeHandler) {
135         if (command instanceof DecimalType dimLevel) {
136             bridgeHandler.commandSetDimLevel(deviceId, dimLevel.intValue());
137         } else if (command instanceof OnOffType) {
138             if (OnOffType.ON.equals(command)) {
139                 bridgeHandler.commandSetDimLevel(deviceId, 100);
140             } else {
141                 bridgeHandler.commandSetDimLevel(deviceId, 0);
142             }
143         }
144     }
145
146     private void commandRollerShutter(Command command, LivisiBridgeHandler bridgeHandler) {
147         if (command instanceof DecimalType rollerShutterLevel) {
148             bridgeHandler.commandSetRollerShutterLevel(deviceId,
149                     invertRollerShutterValueIfConfigured(rollerShutterLevel.intValue()));
150         } else if (command instanceof OnOffType) {
151             if (OnOffType.ON.equals(command)) {
152                 bridgeHandler.commandSetRollerShutterStop(deviceId, ShutterActionType.DOWN);
153             } else {
154                 bridgeHandler.commandSetRollerShutterStop(deviceId, ShutterActionType.UP);
155             }
156         } else if (command instanceof UpDownType) {
157             if (UpDownType.DOWN.equals(command)) {
158                 bridgeHandler.commandSetRollerShutterStop(deviceId, ShutterActionType.DOWN);
159             } else {
160                 bridgeHandler.commandSetRollerShutterStop(deviceId, ShutterActionType.UP);
161             }
162         } else if (command instanceof StopMoveType) {
163             if (StopMoveType.STOP.equals(command)) {
164                 bridgeHandler.commandSetRollerShutterStop(deviceId, ShutterActionType.STOP);
165             }
166         }
167     }
168
169     private void commandUpdatePointTemperature(Command command, LivisiBridgeHandler bridgeHandler) {
170         if (command instanceof QuantityType temperatureCommand) {
171             final QuantityType<?> pointTemperatureCommand = temperatureCommand.toUnit(SIUnits.CELSIUS);
172             if (pointTemperatureCommand != null) {
173                 commandUpdatePointTemperature(pointTemperatureCommand.doubleValue(), bridgeHandler);
174             }
175         } else if (command instanceof DecimalType temperatureCommand) {
176             commandUpdatePointTemperature(temperatureCommand.doubleValue(), bridgeHandler);
177         }
178     }
179
180     private void commandUpdatePointTemperature(double pointTemperature, LivisiBridgeHandler bridgeHandler) {
181         if (pointTemperature < MIN_TEMPERATURE_CELSIUS) {
182             pointTemperature = MIN_TEMPERATURE_CELSIUS;
183             logger.debug(
184                     "pointTemperature set to value {} (instead of value '{}'), because it is the minimal possible value!",
185                     MIN_TEMPERATURE_CELSIUS, pointTemperature);
186         } else if (pointTemperature > MAX_TEMPERATURE_CELSIUS) {
187             pointTemperature = MAX_TEMPERATURE_CELSIUS;
188             logger.debug(
189                     "pointTemperature set to value {} (instead of value '{}'), because it is the maximal possible value!",
190                     MAX_TEMPERATURE_CELSIUS, pointTemperature);
191         }
192         bridgeHandler.commandUpdatePointTemperature(deviceId, pointTemperature);
193     }
194
195     private void commandSetOperationMode(Command command, LivisiBridgeHandler bridgeHandler) {
196         if (command instanceof StringType) {
197             final String autoModeCommand = command.toString();
198
199             if (CapabilityStateDTO.STATE_VALUE_OPERATION_MODE_AUTO.equals(autoModeCommand)) {
200                 bridgeHandler.commandSetOperationMode(deviceId, true);
201             } else if (CapabilityStateDTO.STATE_VALUE_OPERATION_MODE_MANUAL.equals(autoModeCommand)) {
202                 bridgeHandler.commandSetOperationMode(deviceId, false);
203             } else {
204                 logger.warn("Could not set operationMode. Invalid value '{}'! Only '{}' or '{}' allowed.",
205                         autoModeCommand, CapabilityStateDTO.STATE_VALUE_OPERATION_MODE_AUTO,
206                         CapabilityStateDTO.STATE_VALUE_OPERATION_MODE_MANUAL);
207             }
208         }
209     }
210
211     private void commandSwitchAlarm(Command command, LivisiBridgeHandler bridgeHandler) {
212         if (command instanceof OnOffType) {
213             bridgeHandler.commandSwitchAlarm(deviceId, OnOffType.ON.equals(command));
214         }
215     }
216
217     @Override
218     public void initialize() {
219         logger.debug("Initializing LIVISI SmartHome device handler.");
220         initializeThing(isBridgeOnline());
221     }
222
223     @Override
224     public void dispose() {
225         unregisterListeners(bridgeHandler, deviceId);
226     }
227
228     private static void unregisterListeners(@Nullable LivisiBridgeHandler bridgeHandler, String deviceId) {
229         if (bridgeHandler != null) {
230             bridgeHandler.unregisterDeviceStatusListener(deviceId);
231         }
232     }
233
234     @Override
235     public void bridgeStatusChanged(final ThingStatusInfo bridgeStatusInfo) {
236         logger.debug("bridgeStatusChanged {}", bridgeStatusInfo);
237         initializeThing(ThingStatus.ONLINE == bridgeStatusInfo.getStatus());
238     }
239
240     /**
241      * Initializes the {@link Thing} corresponding to the given status of the bridge.
242      * 
243      * @param isBridgeOnline true if the bridge thing is online, otherwise false
244      */
245     private void initializeThing(final boolean isBridgeOnline) {
246         logger.debug("initializeThing thing {} bridge online status: {}", getThing().getUID(), isBridgeOnline);
247         final String configDeviceId = (String) getConfig().get(PROPERTY_ID);
248         if (configDeviceId != null) {
249             deviceId = configDeviceId;
250
251             Optional<LivisiBridgeHandler> bridgeHandler = registerAtBridgeHandler();
252             if (bridgeHandler.isPresent()) {
253                 if (isBridgeOnline) {
254                     initializeProperties();
255
256                     Optional<DeviceDTO> deviceOptional = getDevice();
257                     if (deviceOptional.isPresent()) {
258                         DeviceDTO device = deviceOptional.get();
259                         if (device.isReachable() != null && !device.isReachable()) {
260                             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
261                                     "@text/error.notReachable");
262                         } else {
263                             updateStatus(ThingStatus.ONLINE);
264                         }
265                     } else {
266                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.GONE, "@text/error.deviceNotFound");
267                     }
268                 } else {
269                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
270                 }
271             } else {
272                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
273                         "@text/error.bridgeHandlerMissing");
274             }
275         } else {
276             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.deviceIdUnknown");
277         }
278     }
279
280     /**
281      * Initializes all properties of the {@link DeviceDTO}, like vendor, serialnumber etc.
282      */
283     private void initializeProperties() {
284         synchronized (this.lock) {
285             final Optional<DeviceDTO> deviceOptional = getDevice();
286             if (deviceOptional.isPresent()) {
287                 DeviceDTO device = deviceOptional.get();
288
289                 final Map<String, String> properties = editProperties();
290                 properties.put(PROPERTY_ID, device.getId());
291                 properties.put(PROPERTY_PROTOCOL_ID, device.getConfig().getProtocolId());
292                 if (device.hasSerialNumber()) {
293                     properties.put(Thing.PROPERTY_SERIAL_NUMBER, device.getSerialNumber());
294                 }
295                 properties.put(Thing.PROPERTY_VENDOR, device.getManufacturer());
296                 properties.put(PROPERTY_VERSION, device.getVersion());
297                 if (device.hasLocation()) {
298                     properties.put(PROPERTY_LOCATION, device.getLocation().getName());
299                 }
300                 if (device.isBatteryPowered()) {
301                     properties.put(PROPERTY_BATTERY_POWERED, "yes");
302                 } else {
303                     properties.put(PROPERTY_BATTERY_POWERED, "no");
304                 }
305                 if (device.isController()) {
306                     properties.put(PROPERTY_DEVICE_TYPE, "Controller");
307                 } else if (device.isVirtualDevice()) {
308                     properties.put(PROPERTY_DEVICE_TYPE, "Virtual");
309                 } else if (device.isRadioDevice()) {
310                     properties.put(PROPERTY_DEVICE_TYPE, "Radio");
311                 }
312
313                 // Thermostat
314                 if (DEVICE_RST.equals(device.getType()) || DEVICE_RST2.equals(device.getType())
315                         || DEVICE_WRT.equals(device.getType())) {
316                     properties.put(PROPERTY_DISPLAY_CURRENT_TEMPERATURE,
317                             device.getConfig().getDisplayCurrentTemperature());
318                 }
319
320                 // Meter
321                 if (DEVICE_ANALOG_METER.equals(device.getType()) || DEVICE_GENERATION_METER.equals(device.getType())
322                         || DEVICE_SMART_METER.equals(device.getType())
323                         || DEVICE_TWO_WAY_METER.equals(device.getType())) {
324                     properties.put(PROPERTY_METER_ID, device.getConfig().getMeterId());
325                     properties.put(PROPERTY_METER_FIRMWARE_VERSION, device.getConfig().getMeterFirmwareVersion());
326                 }
327
328                 if (device.getConfig().getTimeOfAcceptance() != null) {
329                     properties.put(PROPERTY_TIME_OF_ACCEPTANCE, device.getConfig().getTimeOfAcceptance()
330                             .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
331                 }
332                 if (device.getConfig().getTimeOfDiscovery() != null) {
333                     properties.put(PROPERTY_TIME_OF_DISCOVERY, device.getConfig().getTimeOfDiscovery()
334                             .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
335                 }
336
337                 updateProperties(properties);
338
339                 onDeviceStateChanged(device);
340             } else {
341                 logger.debug("initializeProperties: The device with id {} isn't found", deviceId);
342             }
343         }
344     }
345
346     @Override
347     public void onDeviceStateChanged(final DeviceDTO device) {
348         synchronized (this.lock) {
349             updateChannels(device, false);
350         }
351     }
352
353     @Override
354     public void onDeviceStateChanged(final DeviceDTO device, final EventDTO event) {
355         synchronized (this.lock) {
356             if (event.isLinkedtoCapability()) {
357                 final String linkedCapabilityId = event.getSourceId();
358
359                 CapabilityDTO capability = device.getCapabilityMap().get(linkedCapabilityId);
360                 if (capability != null) {
361                     logger.trace("Loaded Capability {}, {} with id {}, device {} from device id {}",
362                             capability.getType(), capability.getName(), capability.getId(), capability.getDeviceLink(),
363                             device.getId());
364
365                     if (capability.hasState()) {
366                         boolean deviceChanged = updateDevice(event, capability);
367                         if (deviceChanged) {
368                             updateChannels(device, true);
369                         }
370                     } else {
371                         logger.debug("Capability {} has no state (yet?) - refreshing device.", capability.getName());
372
373                         Optional<DeviceDTO> deviceOptional = refreshDevice(linkedCapabilityId);
374                         deviceOptional.ifPresent((d) -> updateChannels(d, true));
375                     }
376                 }
377             } else if (event.isLinkedtoDevice()) {
378                 if (device.hasDeviceState()) {
379                     updateChannels(device, true);
380                 } else {
381                     logger.debug("Device {}/{} has no state.", device.getConfig().getName(), device.getId());
382                 }
383             }
384         }
385     }
386
387     private Optional<DeviceDTO> refreshDevice(String linkedCapabilityId) {
388         Optional<LivisiBridgeHandler> bridgeHandler = registerAtBridgeHandler();
389         Optional<DeviceDTO> deviceOptional = bridgeHandler.flatMap(bh -> bh.refreshDevice(deviceId));
390         if (deviceOptional.isPresent()) {
391             DeviceDTO device = deviceOptional.get();
392             CapabilityDTO capability = device.getCapabilityMap().get(linkedCapabilityId);
393             if (capability != null && capability.hasState()) {
394                 return Optional.of(device);
395             }
396         }
397         return Optional.empty();
398     }
399
400     private boolean updateDevice(EventDTO event, CapabilityDTO capability) {
401         CapabilityStateDTO capabilityState = capability.getCapabilityState();
402
403         // VariableActuator
404         if (capability.isTypeVariableActuator()) {
405             capabilityState.setVariableActuatorState(event.getProperties().getValue());
406
407             // SwitchActuator
408         } else if (capability.isTypeSwitchActuator()) {
409             capabilityState.setSwitchActuatorState(event.getProperties().getOnState());
410
411             // DimmerActuator
412         } else if (capability.isTypeDimmerActuator()) {
413             capabilityState.setDimmerActuatorState(event.getProperties().getDimLevel());
414
415             // RollerShutterActuator
416         } else if (capability.isTypeRollerShutterActuator()) {
417             capabilityState.setRollerShutterActuatorState(event.getProperties().getShutterLevel());
418
419             // TemperatureSensor
420         } else if (capability.isTypeTemperatureSensor()) {
421             // when values are changed, they come with separate events
422             // values should only updated when they are not null
423             final Double temperature = event.getProperties().getTemperature();
424             final Boolean frostWarning = event.getProperties().getFrostWarning();
425             if (temperature != null) {
426                 capabilityState.setTemperatureSensorTemperatureState(temperature);
427             }
428             if (frostWarning != null) {
429                 capabilityState.setTemperatureSensorFrostWarningState(frostWarning);
430             }
431
432             // ThermostatActuator
433         } else if (capability.isTypeThermostatActuator()) {
434             // when values are changed, they come with separate events
435             // values should only updated when they are not null
436
437             final Double pointTemperature = event.getProperties().getPointTemperature();
438             final String operationMode = event.getProperties().getOperationMode();
439             final Boolean windowReductionActive = event.getProperties().getWindowReductionActive();
440
441             if (pointTemperature != null) {
442                 capabilityState.setThermostatActuatorPointTemperatureState(pointTemperature);
443             }
444             if (operationMode != null) {
445                 capabilityState.setThermostatActuatorOperationModeState(operationMode);
446             }
447             if (windowReductionActive != null) {
448                 capabilityState.setThermostatActuatorWindowReductionActiveState(windowReductionActive);
449             }
450
451             // HumiditySensor
452         } else if (capability.isTypeHumiditySensor()) {
453             // when values are changed, they come with separate events
454             // values should only updated when they are not null
455             final Double humidity = event.getProperties().getHumidity();
456             final Boolean moldWarning = event.getProperties().getMoldWarning();
457             if (humidity != null) {
458                 capabilityState.setHumiditySensorHumidityState(humidity);
459             }
460             if (moldWarning != null) {
461                 capabilityState.setHumiditySensorMoldWarningState(moldWarning);
462             }
463
464             // WindowDoorSensor
465         } else if (capability.isTypeWindowDoorSensor()) {
466             capabilityState.setWindowDoorSensorState(event.getProperties().getIsOpen());
467
468             // SmokeDetectorSensor
469         } else if (capability.isTypeSmokeDetectorSensor()) {
470             capabilityState.setSmokeDetectorSensorState(event.getProperties().getIsSmokeAlarm());
471
472             // AlarmActuator
473         } else if (capability.isTypeAlarmActuator()) {
474             capabilityState.setAlarmActuatorState(event.getProperties().getOnState());
475
476             // MotionDetectionSensor
477         } else if (capability.isTypeMotionDetectionSensor()) {
478             capabilityState.setMotionDetectionSensorState(event.getProperties().getMotionDetectedCount());
479
480             // LuminanceSensor
481         } else if (capability.isTypeLuminanceSensor()) {
482             capabilityState.setLuminanceSensorState(event.getProperties().getLuminance());
483
484             // PushButtonSensor
485         } else if (capability.isTypePushButtonSensor()) {
486             if (event.isButtonPressedEvent()) {
487                 // Some devices send both StateChanged and ButtonPressed. But only the ButtonPressed should be handled,
488                 // therefore it is checked for button pressed event (button index is set).
489                 EventPropertiesDTO properties = event.getProperties();
490                 capabilityState.setPushButtonSensorButtonIndexState(properties.getKeyPressButtonIndex());
491                 capabilityState.setPushButtonSensorButtonIndexType(properties.getKeyPressType());
492                 capabilityState.setPushButtonSensorCounterState(properties.getKeyPressCounter());
493             }
494
495             // EnergyConsumptionSensor
496         } else if (capability.isTypeEnergyConsumptionSensor()) {
497             capabilityState.setEnergyConsumptionSensorEnergyConsumptionMonthKWhState(
498                     event.getProperties().getEnergyConsumptionMonthKWh());
499             capabilityState.setEnergyConsumptionSensorAbsoluteEnergyConsumptionState(
500                     event.getProperties().getAbsoluteEnergyConsumption());
501             capabilityState.setEnergyConsumptionSensorEnergyConsumptionMonthEuroState(
502                     event.getProperties().getEnergyConsumptionMonthEuro());
503             capabilityState.setEnergyConsumptionSensorEnergyConsumptionDayEuroState(
504                     event.getProperties().getEnergyConsumptionDayEuro());
505             capabilityState.setEnergyConsumptionSensorEnergyConsumptionDayKWhState(
506                     event.getProperties().getEnergyConsumptionDayKWh());
507
508             // PowerConsumptionSensor
509         } else if (capability.isTypePowerConsumptionSensor()) {
510             capabilityState.setPowerConsumptionSensorPowerConsumptionWattState(
511                     event.getProperties().getPowerConsumptionWatt());
512
513             // GenerationMeterEnergySensor
514         } else if (capability.isTypeGenerationMeterEnergySensor()) {
515             capabilityState.setGenerationMeterEnergySensorEnergyPerMonthInKWhState(
516                     event.getProperties().getEnergyPerMonthInKWh());
517             capabilityState.setGenerationMeterEnergySensorTotalEnergyState(event.getProperties().getTotalEnergy());
518             capabilityState.setGenerationMeterEnergySensorEnergyPerMonthInEuroState(
519                     event.getProperties().getEnergyPerMonthInEuro());
520             capabilityState.setGenerationMeterEnergySensorEnergyPerDayInEuroState(
521                     event.getProperties().getEnergyPerDayInEuro());
522             capabilityState
523                     .setGenerationMeterEnergySensorEnergyPerDayInKWhState(event.getProperties().getEnergyPerDayInKWh());
524
525             // GenerationMeterPowerConsumptionSensor
526         } else if (capability.isTypeGenerationMeterPowerConsumptionSensor()) {
527             capabilityState
528                     .setGenerationMeterPowerConsumptionSensorPowerInWattState(event.getProperties().getPowerInWatt());
529
530             // TwoWayMeterEnergyConsumptionSensor
531         } else if (capability.isTypeTwoWayMeterEnergyConsumptionSensor()) {
532             capabilityState.setTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInKWhState(
533                     event.getProperties().getEnergyPerMonthInKWh());
534             capabilityState
535                     .setTwoWayMeterEnergyConsumptionSensorTotalEnergyState(event.getProperties().getTotalEnergy());
536             capabilityState.setTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInEuroState(
537                     event.getProperties().getEnergyPerMonthInEuro());
538             capabilityState.setTwoWayMeterEnergyConsumptionSensorEnergyPerDayInEuroState(
539                     event.getProperties().getEnergyPerDayInEuro());
540             capabilityState.setTwoWayMeterEnergyConsumptionSensorEnergyPerDayInKWhState(
541                     event.getProperties().getEnergyPerDayInKWh());
542
543             // TwoWayMeterEnergyFeedSensor
544         } else if (capability.isTypeTwoWayMeterEnergyFeedSensor()) {
545             capabilityState.setTwoWayMeterEnergyFeedSensorEnergyPerMonthInKWhState(
546                     event.getProperties().getEnergyPerMonthInKWh());
547             capabilityState.setTwoWayMeterEnergyFeedSensorTotalEnergyState(event.getProperties().getTotalEnergy());
548             capabilityState.setTwoWayMeterEnergyFeedSensorEnergyPerMonthInEuroState(
549                     event.getProperties().getEnergyPerMonthInEuro());
550             capabilityState.setTwoWayMeterEnergyFeedSensorEnergyPerDayInEuroState(
551                     event.getProperties().getEnergyPerDayInEuro());
552             capabilityState
553                     .setTwoWayMeterEnergyFeedSensorEnergyPerDayInKWhState(event.getProperties().getEnergyPerDayInKWh());
554
555             // TwoWayMeterPowerConsumptionSensor
556         } else if (capability.isTypeTwoWayMeterPowerConsumptionSensor()) {
557             capabilityState
558                     .setTwoWayMeterPowerConsumptionSensorPowerInWattState(event.getProperties().getPowerInWatt());
559
560         } else {
561             logger.debug("Unsupported capability type {}.", capability.getType());
562             return false;
563         }
564         return true;
565     }
566
567     private void updateChannels(DeviceDTO device, boolean isChangedByEvent) {
568         // DEVICE STATES
569         final boolean isReachable = updateStatus(device);
570
571         if (isReachable) {
572             updateDeviceChannels(device);
573
574             // CAPABILITY STATES
575             for (final Entry<String, CapabilityDTO> entry : device.getCapabilityMap().entrySet()) {
576                 final CapabilityDTO capability = entry.getValue();
577
578                 logger.debug("->capability:{} ({}/{})", capability.getId(), capability.getType(), capability.getName());
579
580                 if (capability.hasState()) {
581                     updateCapabilityChannels(device, capability, isChangedByEvent);
582                 } else {
583                     logger.debug("Capability not available for device {} ({})", device.getConfig().getName(),
584                             device.getType());
585                 }
586             }
587         }
588     }
589
590     private boolean updateStatus(DeviceDTO device) {
591         Boolean reachable = device.isReachable();
592         if (reachable != null) {
593             if (reachable) {
594                 updateStatus(ThingStatus.ONLINE);
595             } else {
596                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.notReachable");
597                 return false;
598             }
599         }
600         return true;
601     }
602
603     private void updateDeviceChannels(DeviceDTO device) {
604         if (device.isBatteryPowered()) {
605             updateState(CHANNEL_BATTERY_LOW, OnOffType.from(device.hasLowBattery()));
606         }
607     }
608
609     private void updateCapabilityChannels(DeviceDTO device, CapabilityDTO capability, boolean isChangedByEvent) {
610         switch (capability.getType()) {
611             case CapabilityDTO.TYPE_VARIABLEACTUATOR:
612                 updateVariableActuatorChannels(capability);
613                 break;
614             case CapabilityDTO.TYPE_SWITCHACTUATOR:
615                 updateSwitchActuatorChannels(capability);
616                 break;
617             case CapabilityDTO.TYPE_DIMMERACTUATOR:
618                 updateDimmerActuatorChannels(capability);
619                 break;
620             case CapabilityDTO.TYPE_ROLLERSHUTTERACTUATOR:
621                 updateRollerShutterActuatorChannels(capability);
622                 break;
623             case CapabilityDTO.TYPE_TEMPERATURESENSOR:
624                 updateTemperatureSensorChannels(capability);
625                 break;
626             case CapabilityDTO.TYPE_THERMOSTATACTUATOR:
627                 updateThermostatActuatorChannels(device, capability);
628                 break;
629             case CapabilityDTO.TYPE_HUMIDITYSENSOR:
630                 updateHumiditySensorChannels(capability);
631                 break;
632             case CapabilityDTO.TYPE_WINDOWDOORSENSOR:
633                 updateWindowDoorSensorChannels(capability);
634                 break;
635             case CapabilityDTO.TYPE_SMOKEDETECTORSENSOR:
636                 updateSmokeDetectorChannels(capability);
637                 break;
638             case CapabilityDTO.TYPE_ALARMACTUATOR:
639                 updateAlarmActuatorChannels(capability);
640                 break;
641             case CapabilityDTO.TYPE_MOTIONDETECTIONSENSOR:
642                 updateMotionDetectionSensorChannels(capability);
643                 break;
644             case CapabilityDTO.TYPE_LUMINANCESENSOR:
645                 updateLuminanceSensorChannels(capability);
646                 break;
647             case CapabilityDTO.TYPE_PUSHBUTTONSENSOR:
648                 updatePushButtonSensorChannels(capability, isChangedByEvent);
649                 break;
650             case CapabilityDTO.TYPE_ENERGYCONSUMPTIONSENSOR:
651                 updateEnergyConsumptionSensorChannels(capability);
652                 break;
653             case CapabilityDTO.TYPE_POWERCONSUMPTIONSENSOR:
654                 updateStateForEnergyChannelWatt(CHANNEL_POWER_CONSUMPTION_WATT,
655                         capability.getCapabilityState().getPowerConsumptionSensorPowerConsumptionWattState(),
656                         capability);
657                 break;
658             case CapabilityDTO.TYPE_GENERATIONMETERENERGYSENSOR:
659                 updateGenerationMeterEnergySensorChannels(capability);
660                 break;
661             case CapabilityDTO.TYPE_GENERATIONMETERPOWERCONSUMPTIONSENSOR:
662                 updateStateForEnergyChannelWatt(CHANNEL_POWER_GENERATION_WATT,
663                         capability.getCapabilityState().getGenerationMeterPowerConsumptionSensorPowerInWattState(),
664                         capability);
665                 break;
666             case CapabilityDTO.TYPE_TWOWAYMETERENERGYCONSUMPTIONSENSOR:
667                 updateTwoWayMeterEnergyConsumptionSensorChannels(capability);
668                 break;
669             case CapabilityDTO.TYPE_TWOWAYMETERENERGYFEEDSENSOR:
670                 updateTwoWayMeterEnergyFeedSensorChannels(capability);
671                 break;
672             case CapabilityDTO.TYPE_TWOWAYMETERPOWERCONSUMPTIONSENSOR:
673                 updateStateForEnergyChannelWatt(CHANNEL_POWER_WATT,
674                         capability.getCapabilityState().getTwoWayMeterPowerConsumptionSensorPowerInWattState(),
675                         capability);
676                 break;
677             default:
678                 logger.debug("Unsupported capability type {}.", capability.getType());
679                 break;
680         }
681     }
682
683     private void updateVariableActuatorChannels(CapabilityDTO capability) {
684         final Boolean variableActuatorState = capability.getCapabilityState().getVariableActuatorState();
685         if (variableActuatorState != null) {
686             updateState(CHANNEL_SWITCH, OnOffType.from(variableActuatorState));
687         } else {
688             logStateNull(capability);
689         }
690     }
691
692     private void updateSwitchActuatorChannels(CapabilityDTO capability) {
693         final Boolean switchActuatorState = capability.getCapabilityState().getSwitchActuatorState();
694         if (switchActuatorState != null) {
695             updateState(CHANNEL_SWITCH, OnOffType.from(switchActuatorState));
696         } else {
697             logStateNull(capability);
698         }
699     }
700
701     private void updateDimmerActuatorChannels(CapabilityDTO capability) {
702         final Integer dimLevel = capability.getCapabilityState().getDimmerActuatorState();
703         if (dimLevel != null) {
704             logger.debug("Dimlevel state {}", dimLevel);
705             updateState(CHANNEL_DIMMER, new PercentType(dimLevel));
706         } else {
707             logStateNull(capability);
708         }
709     }
710
711     private void updateRollerShutterActuatorChannels(CapabilityDTO capability) {
712         Integer rollerShutterLevel = capability.getCapabilityState().getRollerShutterActuatorState();
713         if (rollerShutterLevel != null) {
714             rollerShutterLevel = invertRollerShutterValueIfConfigured(rollerShutterLevel);
715             logger.debug("RollerShutterlevel state {}", rollerShutterLevel);
716             updateState(CHANNEL_ROLLERSHUTTER, new PercentType(rollerShutterLevel));
717         } else {
718             logStateNull(capability);
719         }
720     }
721
722     private void updateTemperatureSensorChannels(CapabilityDTO capability) {
723         // temperature
724         final Double temperature = capability.getCapabilityState().getTemperatureSensorTemperatureState();
725         if (temperature != null) {
726             logger.debug("-> Temperature sensor state: {}", temperature);
727             updateState(CHANNEL_CURRENT_TEMPERATURE, QuantityType.valueOf(temperature, SIUnits.CELSIUS));
728         } else {
729             logStateNull(capability);
730         }
731
732         // frost warning
733         final Boolean frostWarning = capability.getCapabilityState().getTemperatureSensorFrostWarningState();
734         if (frostWarning != null) {
735             updateState(CHANNEL_FROST_WARNING, OnOffType.from(frostWarning));
736         } else {
737             logStateNull(capability);
738         }
739     }
740
741     private void updateThermostatActuatorChannels(DeviceDTO device, CapabilityDTO capability) {
742         // point temperature
743         final Double pointTemperature = capability.getCapabilityState().getThermostatActuatorPointTemperatureState();
744         if (pointTemperature != null) {
745             logger.debug("Update CHANNEL_SET_TEMPERATURE: state:{} (DeviceName {}, Capab-ID:{})", pointTemperature,
746                     device.getConfig().getName(), capability.getId());
747             updateState(CHANNEL_TARGET_TEMPERATURE, QuantityType.valueOf(pointTemperature, SIUnits.CELSIUS));
748         } else {
749             logStateNull(capability);
750         }
751
752         // operation mode
753         final String operationMode = capability.getCapabilityState().getThermostatActuatorOperationModeState();
754         if (operationMode != null) {
755             updateState(CHANNEL_OPERATION_MODE, new StringType(operationMode));
756         } else {
757             logStateNull(capability);
758         }
759
760         // window reduction active
761         final Boolean windowReductionActive = capability.getCapabilityState()
762                 .getThermostatActuatorWindowReductionActiveState();
763         if (windowReductionActive != null) {
764             updateState(CHANNEL_WINDOW_REDUCTION_ACTIVE, OnOffType.from(windowReductionActive));
765         } else {
766             logStateNull(capability);
767         }
768     }
769
770     private void updateHumiditySensorChannels(CapabilityDTO capability) {
771         // humidity
772         final Double humidity = capability.getCapabilityState().getHumiditySensorHumidityState();
773         if (humidity != null) {
774             updateState(CHANNEL_HUMIDITY, QuantityType.valueOf(humidity, Units.PERCENT));
775         } else {
776             logStateNull(capability);
777         }
778
779         // mold warning
780         final Boolean moldWarning = capability.getCapabilityState().getHumiditySensorMoldWarningState();
781         if (moldWarning != null) {
782             updateState(CHANNEL_MOLD_WARNING, OnOffType.from(moldWarning));
783         } else {
784             logStateNull(capability);
785         }
786     }
787
788     private void updateWindowDoorSensorChannels(CapabilityDTO capability) {
789         final Boolean contactState = capability.getCapabilityState().getWindowDoorSensorState();
790         if (contactState != null) {
791             updateState(CHANNEL_CONTACT, toOpenClosedType(contactState));
792         } else {
793             logStateNull(capability);
794         }
795     }
796
797     private void updateSmokeDetectorChannels(CapabilityDTO capability) {
798         final Boolean smokeState = capability.getCapabilityState().getSmokeDetectorSensorState();
799         if (smokeState != null) {
800             updateState(CHANNEL_SMOKE, OnOffType.from(smokeState));
801         } else {
802             logStateNull(capability);
803         }
804     }
805
806     private void updateAlarmActuatorChannels(CapabilityDTO capability) {
807         final Boolean alarmState = capability.getCapabilityState().getAlarmActuatorState();
808         if (alarmState != null) {
809             updateState(CHANNEL_ALARM, OnOffType.from(alarmState));
810         } else {
811             logStateNull(capability);
812         }
813     }
814
815     private void updateMotionDetectionSensorChannels(CapabilityDTO capability) {
816         final Integer motionCount = capability.getCapabilityState().getMotionDetectionSensorState();
817         if (motionCount != null) {
818             logger.debug("Motion state {} -> count {}", motionCount, motionCount);
819             updateState(CHANNEL_MOTION_COUNT, new DecimalType(motionCount));
820         } else {
821             logStateNull(capability);
822         }
823     }
824
825     private void updateLuminanceSensorChannels(CapabilityDTO capability) {
826         final Double luminance = capability.getCapabilityState().getLuminanceSensorState();
827         if (luminance != null) {
828             updateState(CHANNEL_LUMINANCE, QuantityType.valueOf(luminance, Units.PERCENT));
829         } else {
830             logStateNull(capability);
831         }
832     }
833
834     private void updatePushButtonSensorChannels(CapabilityDTO capability, boolean isChangedByEvent) {
835         final Integer pushCount = capability.getCapabilityState().getPushButtonSensorCounterState();
836         final Integer buttonIndex = capability.getCapabilityState().getPushButtonSensorButtonIndexState();
837         final String type = capability.getCapabilityState().getPushButtonSensorButtonIndexType();
838         logger.debug("Pushbutton index {}, count {}, type {}", buttonIndex, pushCount, type);
839         if (buttonIndex != null && pushCount != null) {
840             if (buttonIndex >= 0 && buttonIndex <= 7) {
841                 final int channelIndex = buttonIndex + 1;
842                 updateState(String.format(CHANNEL_BUTTON_COUNT, channelIndex), new DecimalType(pushCount));
843
844                 if (isChangedByEvent) {
845                     triggerButtonChannels(type, channelIndex);
846                 }
847
848                 // Button handled so remove state to avoid re-trigger.
849                 capability.getCapabilityState().setPushButtonSensorButtonIndexState(null);
850                 capability.getCapabilityState().setPushButtonSensorButtonIndexType(null);
851             }
852         } else {
853             logStateNull(capability);
854         }
855     }
856
857     private void triggerButtonChannels(@Nullable String type, int channelIndex) {
858         if (type != null) {
859             if (SHORT_PRESS.equals(type)) {
860                 triggerChannel(CHANNEL_BUTTON + channelIndex, CommonTriggerEvents.SHORT_PRESSED);
861             } else if (LONG_PRESS.equals(type)) {
862                 triggerChannel(CHANNEL_BUTTON + channelIndex, CommonTriggerEvents.LONG_PRESSED);
863             }
864         }
865         triggerChannel(CHANNEL_BUTTON + channelIndex, CommonTriggerEvents.PRESSED);
866     }
867
868     private void updateEnergyConsumptionSensorChannels(CapabilityDTO capability) {
869         updateStateForEnergyChannelKiloWattHour(CHANNEL_ENERGY_CONSUMPTION_MONTH_KWH,
870                 capability.getCapabilityState().getEnergyConsumptionSensorEnergyConsumptionMonthKWhState(), capability);
871         updateStateForEnergyChannelKiloWattHour(CHANNEL_ABOLUTE_ENERGY_CONSUMPTION,
872                 capability.getCapabilityState().getEnergyConsumptionSensorAbsoluteEnergyConsumptionState(), capability);
873         updateStateForEnergyChannelEuro(CHANNEL_ENERGY_CONSUMPTION_MONTH_EURO,
874                 capability.getCapabilityState().getEnergyConsumptionSensorEnergyConsumptionMonthEuroState(),
875                 capability);
876         updateStateForEnergyChannelEuro(CHANNEL_ENERGY_CONSUMPTION_DAY_EURO,
877                 capability.getCapabilityState().getEnergyConsumptionSensorEnergyConsumptionDayEuroState(), capability);
878         updateStateForEnergyChannelKiloWattHour(CHANNEL_ENERGY_CONSUMPTION_DAY_KWH,
879                 capability.getCapabilityState().getEnergyConsumptionSensorEnergyConsumptionDayKWhState(), capability);
880     }
881
882     private void updateGenerationMeterEnergySensorChannels(CapabilityDTO capability) {
883         updateStateForEnergyChannelKiloWattHour(CHANNEL_ENERGY_GENERATION_MONTH_KWH,
884                 capability.getCapabilityState().getGenerationMeterEnergySensorEnergyPerMonthInKWhState(), capability);
885         updateStateForEnergyChannelKiloWattHour(CHANNEL_TOTAL_ENERGY_GENERATION,
886                 capability.getCapabilityState().getGenerationMeterEnergySensorTotalEnergyState(), capability);
887         updateStateForEnergyChannelEuro(CHANNEL_ENERGY_GENERATION_MONTH_EURO,
888                 capability.getCapabilityState().getGenerationMeterEnergySensorEnergyPerMonthInEuroState(), capability);
889         updateStateForEnergyChannelEuro(CHANNEL_ENERGY_GENERATION_DAY_EURO,
890                 capability.getCapabilityState().getGenerationMeterEnergySensorEnergyPerDayInEuroState(), capability);
891         updateStateForEnergyChannelKiloWattHour(CHANNEL_ENERGY_GENERATION_DAY_KWH,
892                 capability.getCapabilityState().getGenerationMeterEnergySensorEnergyPerDayInKWhState(), capability);
893     }
894
895     private void updateTwoWayMeterEnergyConsumptionSensorChannels(CapabilityDTO capability) {
896         updateStateForEnergyChannelKiloWattHour(CHANNEL_ENERGY_MONTH_KWH,
897                 capability.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInKWhState(),
898                 capability);
899         updateStateForEnergyChannelKiloWattHour(CHANNEL_TOTAL_ENERGY,
900                 capability.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorTotalEnergyState(), capability);
901         updateStateForEnergyChannelEuro(CHANNEL_ENERGY_MONTH_EURO,
902                 capability.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInEuroState(),
903                 capability);
904         updateStateForEnergyChannelEuro(CHANNEL_ENERGY_DAY_EURO,
905                 capability.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerDayInEuroState(),
906                 capability);
907         updateStateForEnergyChannelKiloWattHour(CHANNEL_ENERGY_DAY_KWH,
908                 capability.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerDayInKWhState(),
909                 capability);
910     }
911
912     private void updateTwoWayMeterEnergyFeedSensorChannels(CapabilityDTO capability) {
913         updateStateForEnergyChannelKiloWattHour(CHANNEL_ENERGY_FEED_MONTH_KWH,
914                 capability.getCapabilityState().getTwoWayMeterEnergyFeedSensorEnergyPerMonthInKWhState(), capability);
915         updateStateForEnergyChannelKiloWattHour(CHANNEL_TOTAL_ENERGY_FED,
916                 capability.getCapabilityState().getTwoWayMeterEnergyFeedSensorTotalEnergyState(), capability);
917         updateStateForEnergyChannelEuro(CHANNEL_ENERGY_FEED_MONTH_EURO,
918                 capability.getCapabilityState().getTwoWayMeterEnergyFeedSensorEnergyPerMonthInEuroState(), capability);
919         updateStateForEnergyChannelEuro(CHANNEL_ENERGY_FEED_DAY_EURO,
920                 capability.getCapabilityState().getTwoWayMeterEnergyFeedSensorEnergyPerDayInEuroState(), capability);
921         updateStateForEnergyChannelKiloWattHour(CHANNEL_ENERGY_FEED_DAY_KWH,
922                 capability.getCapabilityState().getTwoWayMeterEnergyFeedSensorEnergyPerDayInKWhState(), capability);
923     }
924
925     private void updateStateForEnergyChannelEuro(final String channelId, @Nullable final Double state,
926             final CapabilityDTO capability) {
927         if (state != null) {
928             updateState(channelId, new DecimalType(state));
929         } else {
930             logStateNull(capability);
931         }
932     }
933
934     private void updateStateForEnergyChannelWatt(final String channelId, @Nullable final Double state,
935             final CapabilityDTO capability) {
936         if (state != null) {
937             updateState(channelId, QuantityType.valueOf(state, Units.WATT));
938         } else {
939             logStateNull(capability);
940         }
941     }
942
943     private void updateStateForEnergyChannelKiloWattHour(final String channelId, @Nullable final Double state,
944             final CapabilityDTO capability) {
945         if (state != null) {
946             updateState(channelId, QuantityType.valueOf(state, Units.KILOWATT_HOUR));
947         } else {
948             logStateNull(capability);
949         }
950     }
951
952     /**
953      * Returns the inverted value. Currently only rollershutter channels are supported.
954      *
955      * @param value value to become inverted
956      * @return the value or the inverted value
957      */
958     private int invertRollerShutterValueIfConfigured(final int value) {
959         @Nullable
960         final Channel channel = getThing().getChannel(CHANNEL_ROLLERSHUTTER);
961         if (channel == null) {
962             logger.debug("Channel {} was null! Value not inverted.", CHANNEL_ROLLERSHUTTER);
963             return value;
964         }
965         final Boolean invert = (Boolean) channel.getConfiguration().get(INVERT_CHANNEL_PARAMETER);
966         if (invert != null && invert) {
967             return value;
968         }
969         return 100 - value;
970     }
971
972     /**
973      * Returns the {@link DeviceDTO} associated with this {@link LivisiDeviceHandler} (referenced by the
974      * {@link LivisiDeviceHandler#deviceId}).
975      *
976      * @return the {@link DeviceDTO} or null, if not found or no {@link LivisiBridgeHandler} is available
977      */
978     private Optional<DeviceDTO> getDevice() {
979         return getBridgeHandler().flatMap(bridgeHandler -> bridgeHandler.getDeviceById(deviceId));
980     }
981
982     private Optional<LivisiBridgeHandler> registerAtBridgeHandler() {
983         synchronized (this.lock) {
984             if (this.bridgeHandler == null) {
985                 @Nullable
986                 final Bridge bridge = getBridge();
987                 if (bridge == null) {
988                     return Optional.empty();
989                 }
990                 @Nullable
991                 final ThingHandler handler = bridge.getHandler();
992                 if (handler instanceof LivisiBridgeHandler bridgeHandler) {
993                     bridgeHandler.registerDeviceStatusListener(deviceId, this);
994                     this.bridgeHandler = bridgeHandler;
995                 } else {
996                     return Optional.empty(); // also called when the handler is NULL
997                 }
998             }
999             return getBridgeHandler();
1000         }
1001     }
1002
1003     /**
1004      * Returns the LIVISI bridge handler.
1005      *
1006      * @return the {@link LivisiBridgeHandler} or null
1007      */
1008     private Optional<LivisiBridgeHandler> getBridgeHandler() {
1009         return Optional.ofNullable(this.bridgeHandler);
1010     }
1011
1012     private boolean isBridgeOnline() {
1013         @Nullable
1014         Bridge bridge = getBridge();
1015         if (bridge != null) {
1016             return ThingStatus.ONLINE == bridge.getStatus();
1017         }
1018         return false;
1019     }
1020
1021     private void logStateNull(CapabilityDTO capability) {
1022         logger.debug("State for {} is STILL NULL!! cstate-id: {}, capability-id: {}", capability.getType(),
1023                 capability.getCapabilityState().getId(), capability.getId());
1024     }
1025
1026     private static OpenClosedType toOpenClosedType(boolean isOpen) {
1027         if (isOpen) {
1028             return OpenClosedType.OPEN;
1029         }
1030         return OpenClosedType.CLOSED;
1031     }
1032 }