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