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