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