2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.innogysmarthome.internal.handler;
15 import static org.openhab.binding.innogysmarthome.internal.InnogyBindingConstants.*;
17 import java.time.format.DateTimeFormatter;
18 import java.time.format.FormatStyle;
20 import java.util.Map.Entry;
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;
55 * The {@link InnogyDeviceHandler} is responsible for handling the {@link Device}s and their commands, which are
56 * sent to one of the channels.
58 * @author Oliver Kuhl - Initial contribution
61 public class InnogyDeviceHandler extends BaseThingHandler implements DeviceStatusListener {
63 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = SUPPORTED_DEVICE_THING_TYPES;
65 private static final String DEBUG = "DEBUG";
66 private static final String LONG_PRESS = "LongPress";
67 private static final String SHORT_PRESS = "ShortPress";
69 private final Logger logger = LoggerFactory.getLogger(InnogyDeviceHandler.class);
70 private final Object lock = new Object();
72 private String deviceId = "";
73 private @Nullable InnogyBridgeHandler bridgeHandler;
76 * Constructs a new {@link InnogyDeviceHandler} for the given {@link Thing}.
80 public InnogyDeviceHandler(final Thing thing) {
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);
89 final InnogyBridgeHandler innogyBridgeHandler = getInnogyBridgeHandler();
90 if (innogyBridgeHandler == null) {
91 logger.warn("BridgeHandler not found. Cannot handle command without bridge.");
94 if (!ThingStatus.ONLINE.equals(innogyBridgeHandler.getThing().getStatus())) {
95 logger.debug("Cannot handle command - bridge is not online. Command ignored.");
99 if (command instanceof RefreshType) {
101 final Device device = innogyBridgeHandler.getDeviceById(deviceId);
102 if (device != null) {
103 onDeviceStateChanged(device);
109 if (CHANNEL_SWITCH.equals(channelUID.getId())) {
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\"}}");
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\" }}");
126 if (command instanceof OnOffType) {
127 innogyBridgeHandler.commandSwitchDevice(deviceId, OnOffType.ON.equals(command));
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);
139 innogyBridgeHandler.commandSetDimmLevel(deviceId, 0);
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);
153 innogyBridgeHandler.commandSetRollerShutterStop(deviceId, ShutterAction.ShutterActions.UP);
155 } else if (command instanceof UpDownType) {
156 if (UpDownType.DOWN.equals(command)) {
157 innogyBridgeHandler.commandSetRollerShutterStop(deviceId, ShutterAction.ShutterActions.DOWN);
159 innogyBridgeHandler.commandSetRollerShutterStop(deviceId, ShutterAction.ShutterActions.UP);
161 } else if (command instanceof StopMoveType) {
162 if (StopMoveType.STOP.equals(command)) {
163 innogyBridgeHandler.commandSetRollerShutterStop(deviceId, ShutterAction.ShutterActions.STOP);
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());
175 } else if (CHANNEL_OPERATION_MODE.equals(channelUID.getId())) {
176 if (command instanceof StringType) {
177 final String autoModeCommand = command.toString();
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);
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);
191 } else if (CHANNEL_ALARM.equals(channelUID.getId())) {
192 if (command instanceof OnOffType) {
193 innogyBridgeHandler.commandSwitchAlarm(deviceId, OnOffType.ON.equals(command));
196 logger.debug("UNSUPPORTED channel {} for device {}.", channelUID.getId(), deviceId);
201 public void initialize() {
202 logger.debug("Initializing innogy SmartHome device handler.");
203 initializeThing(getBridge() == null ? null : getBridge().getStatus());
207 public void dispose() {
208 if (bridgeHandler != null) {
209 bridgeHandler.unregisterDeviceStatusListener(this);
214 public void bridgeStatusChanged(final ThingStatusInfo bridgeStatusInfo) {
215 logger.debug("bridgeStatusChanged {}", bridgeStatusInfo);
216 initializeThing(bridgeStatusInfo.getStatus());
220 * Initializes the {@link Thing} corresponding to the given status of the bridge.
222 * @param bridgeStatus
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
231 if (getInnogyBridgeHandler() != null) {
232 if (bridgeStatus == ThingStatus.ONLINE) {
233 if (initializeProperties()) {
234 updateStatus(ThingStatus.ONLINE);
236 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.GONE,
237 "Device not found in innogy config. Was it removed?");
240 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
243 updateStatus(ThingStatus.OFFLINE);
246 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "device id unknown");
251 * Initializes all properties of the {@link Device}, like vendor, serialnumber etc.
253 private boolean initializeProperties() {
254 synchronized (this.lock) {
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());
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());
269 if (device.isBatteryPowered()) {
270 properties.put(PROPERTY_BATTERY_POWERED, "yes");
272 properties.put(PROPERTY_BATTERY_POWERED, "no");
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");
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());
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());
297 if (device.getConfig().getTimeOfAcceptance() != null) {
298 properties.put(PROPERTY_TIME_OF_ACCEPTANCE, device.getConfig().getTimeOfAcceptance()
299 .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
301 if (device.getConfig().getTimeOfDiscovery() != null) {
302 properties.put(PROPERTY_TIME_OF_DISCOVERY, device.getConfig().getTimeOfDiscovery()
303 .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
306 updateProperties(properties);
308 onDeviceStateChanged(device);
311 logger.warn("initializeProperties: The device with id {} isn't found", deviceId);
318 * Returns the {@link Device} associated with this {@link InnogyDeviceHandler} (referenced by the
319 * {@link InnogyDeviceHandler#deviceId}).
321 * @return the {@link Device} or null, if not found or no {@link InnogyBridgeHandler} is available
323 private @Nullable Device getDevice() {
324 if (getInnogyBridgeHandler() != null) {
325 return getInnogyBridgeHandler().getDeviceById(deviceId);
331 * Returns the innogy bridge handler.
333 * @return the {@link InnogyBridgeHandler} or null
335 private @Nullable InnogyBridgeHandler getInnogyBridgeHandler() {
336 synchronized (this.lock) {
337 if (this.bridgeHandler == null) {
339 final Bridge bridge = getBridge();
340 if (bridge == null) {
344 final ThingHandler handler = bridge.getHandler();
345 if (handler instanceof InnogyBridgeHandler) {
346 this.bridgeHandler = (InnogyBridgeHandler) handler;
347 this.bridgeHandler.registerDeviceStatusListener(this);
352 return this.bridgeHandler;
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(),
365 logger.debug("onDeviceStateChanged called with device {}/{}", device.getConfig().getName(), device.getId());
368 if (device.hasDeviceState()) {
370 Boolean reachable = null;
371 if (device.getDeviceState().hasIsReachableState()) {
372 reachable = device.getDeviceState().isReachable();
375 if (reachable != null && !reachable) {
376 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device not reachable.");
378 } else if ((reachable != null && reachable) || DEVICE_VARIABLE_ACTUATOR.equals(device.getType())) {
379 if (device.getDeviceState().deviceIsIncluded()) {
380 updateStatus(ThingStatus.ONLINE);
382 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
383 "State is " + device.getDeviceState().getDeviceInclusionState());
388 if (device.isBatteryPowered()) {
389 if (device.hasLowBattery()) {
390 updateState(CHANNEL_BATTERY_LOW, OnOffType.ON);
392 updateState(CHANNEL_BATTERY_LOW, OnOffType.OFF);
397 for (final Entry<String, Capability> entry : device.getCapabilityMap().entrySet()) {
398 final Capability c = entry.getValue();
400 logger.debug("->capability:{} ({}/{})", c.getId(), c.getType(), c.getName());
402 if (c.getCapabilityState() == null) {
403 logger.debug("Capability not available for device {} ({})", device.getConfig().getName(),
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);
413 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
414 c.getCapabilityState().getId(), c.getId());
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);
422 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
423 c.getCapabilityState().getId(), c.getId());
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));
432 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
433 c.getCapabilityState().getId(), c.getId());
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));
443 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
444 c.getCapabilityState().getId(), c.getId());
447 case Capability.TYPE_TEMPERATURESENSOR:
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));
455 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
456 c.getCapabilityState().getId(), c.getId());
460 final Boolean temperatureSensorFrostWarningState = c.getCapabilityState()
461 .getTemperatureSensorFrostWarningState();
462 if (temperatureSensorFrostWarningState != null) {
463 updateState(CHANNEL_FROST_WARNING,
464 temperatureSensorFrostWarningState ? OnOffType.ON : OnOffType.OFF);
466 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
467 c.getCapabilityState().getId(), c.getId());
471 case Capability.TYPE_THERMOSTATACTUATOR:
473 final Double thermostatActuatorPointTemperatureState = c.getCapabilityState()
474 .getThermostatActuatorPointTemperatureState();
475 if (thermostatActuatorPointTemperatureState != null) {
476 final DecimalType pointTemp = new DecimalType(thermostatActuatorPointTemperatureState);
478 "Update CHANNEL_SET_TEMPERATURE: state:{}->decType:{} (DeviceName {}, Capab-ID:{})",
479 thermostatActuatorPointTemperatureState, pointTemp, device.getConfig().getName(),
481 updateState(CHANNEL_SET_TEMPERATURE, pointTemp);
483 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
484 c.getCapabilityState().getId(), c.getId());
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);
494 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
495 c.getCapabilityState().getId(), c.getId());
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);
505 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
506 c.getCapabilityState().getId(), c.getId());
509 case Capability.TYPE_HUMIDITYSENSOR:
511 final Double humidityState = c.getCapabilityState().getHumiditySensorHumidityState();
512 if (humidityState != null) {
513 final DecimalType humidity = new DecimalType(humidityState);
514 updateState(CHANNEL_HUMIDITY, humidity);
516 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
517 c.getCapabilityState().getId(), c.getId());
521 final Boolean humiditySensorMoldWarningState = c.getCapabilityState()
522 .getHumiditySensorMoldWarningState();
523 if (humiditySensorMoldWarningState != null) {
524 updateState(CHANNEL_MOLD_WARNING,
525 humiditySensorMoldWarningState ? OnOffType.ON : OnOffType.OFF);
527 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
528 c.getCapabilityState().getId(), c.getId());
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);
536 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
537 c.getCapabilityState().getId(), c.getId());
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);
545 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
546 c.getCapabilityState().getId(), c.getId());
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);
554 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
555 c.getCapabilityState().getId(), c.getId());
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);
565 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
566 c.getCapabilityState().getId(), c.getId());
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);
575 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
576 c.getCapabilityState().getId(), c.getId());
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);
595 triggerChannel(CHANNEL_BUTTON + channelIndex, triggerEvent);
596 updateState(String.format(CHANNEL_BUTTON_COUNT, channelIndex), pushCount);
598 logger.debug("Button index {} not supported.", buttonIndexState);
600 // Button handled so remove state to avoid re-trigger.
601 c.getCapabilityState().setPushButtonSensorButtonIndexState(null);
602 c.getCapabilityState().setPushButtonSensorButtonIndexType(null);
604 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
605 c.getCapabilityState().getId(), c.getId());
608 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
609 c.getCapabilityState().getId(), c.getId());
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);
624 case Capability.TYPE_POWERCONSUMPTIONSENSOR:
625 updateStateForEnergyChannel(CHANNEL_POWER_CONSUMPTION_WATT,
626 c.getCapabilityState().getPowerConsumptionSensorPowerConsumptionWattState(), c);
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);
640 case Capability.TYPE_GENERATIONMETERPOWERCONSUMPTIONSENSOR:
641 updateStateForEnergyChannel(CHANNEL_POWER_GENERATION_WATT,
642 c.getCapabilityState().getGenerationMeterPowerConsumptionSensorPowerInWattState(), c);
644 case Capability.TYPE_TWOWAYMETERENERGYCONSUMPTIONSENSOR:
645 updateStateForEnergyChannel(CHANNEL_ENERGY_MONTH_KWH,
646 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInKWhState(),
648 updateStateForEnergyChannel(CHANNEL_TOTAL_ENERGY,
649 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorTotalEnergyState(), c);
650 updateStateForEnergyChannel(CHANNEL_ENERGY_MONTH_EURO,
651 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInEuroState(),
653 updateStateForEnergyChannel(CHANNEL_ENERGY_DAY_EURO,
654 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerDayInEuroState(),
656 updateStateForEnergyChannel(CHANNEL_ENERGY_DAY_KWH,
657 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerDayInKWhState(),
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);
672 case Capability.TYPE_TWOWAYMETERPOWERCONSUMPTIONSENSOR:
673 updateStateForEnergyChannel(CHANNEL_POWER_WATT,
674 c.getCapabilityState().getTwoWayMeterPowerConsumptionSensorPowerInWattState(), c);
677 logger.debug("Unsupported capability type {}.", c.getType());
685 * Updates the state for the {@link Channel} of an energy {@link Device}.
691 private void updateStateForEnergyChannel(final String channelId, @Nullable final Double state,
692 final Capability capability) {
694 final DecimalType newValue = new DecimalType(state);
695 updateState(channelId, newValue);
697 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", capability.getType(),
698 capability.getCapabilityState().getId(), capability.getId());
703 public void onDeviceStateChanged(final Device changedDevice, final Event event) {
704 synchronized (this.lock) {
705 Device device = changedDevice;
706 if (!deviceId.equals(device.getId())) {
710 logger.trace("DeviceId {} relevant for this handler.", device.getId());
712 if (event.isLinkedtoCapability()) {
713 boolean deviceChanged = false;
714 final String linkedCapabilityId = event.getSourceId();
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());
721 CapabilityState capabilityState;
722 if (capability.hasState()) {
723 capabilityState = capability.getCapabilityState();
726 if (capability.isTypeVariableActuator()) {
727 capabilityState.setVariableActuatorState(event.getProperties().getValue());
728 deviceChanged = true;
731 } else if (capability.isTypeSwitchActuator()) {
732 capabilityState.setSwitchActuatorState(event.getProperties().getOnState());
733 deviceChanged = true;
736 } else if (capability.isTypeDimmerActuator()) {
737 capabilityState.setDimmerActuatorState(event.getProperties().getDimLevel());
738 deviceChanged = true;
740 // RollerShutterActuator
741 } else if (capability.isTypeRollerShutterActuator()) {
742 capabilityState.setRollerShutterActuatorState(event.getProperties().getShutterLevel());
743 deviceChanged = true;
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);
754 if (tmpFrostWarningState != null) {
755 capabilityState.setTemperatureSensorFrostWarningState(tmpFrostWarningState);
757 deviceChanged = true;
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
764 final Double tmpPointTemperatureState = event.getProperties().getPointTemperature();
765 final String tmpOperationModeState = event.getProperties().getOperationMode();
766 final Boolean tmpWindowReductionActiveState = event.getProperties().getWindowReductionActive();
768 if (tmpPointTemperatureState != null) {
769 capabilityState.setThermostatActuatorPointTemperatureState(tmpPointTemperatureState);
771 if (tmpOperationModeState != null) {
772 capabilityState.setThermostatActuatorOperationModeState(tmpOperationModeState);
774 if (tmpWindowReductionActiveState != null) {
776 .setThermostatActuatorWindowReductionActiveState(tmpWindowReductionActiveState);
778 deviceChanged = true;
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);
789 if (tmpMoldWarningState != null) {
790 capabilityState.setHumiditySensorMoldWarningState(tmpMoldWarningState);
792 deviceChanged = true;
795 } else if (capability.isTypeWindowDoorSensor()) {
796 capabilityState.setWindowDoorSensorState(event.getProperties().getIsOpen());
797 deviceChanged = true;
799 // SmokeDetectorSensor
800 } else if (capability.isTypeSmokeDetectorSensor()) {
801 capabilityState.setSmokeDetectorSensorState(event.getProperties().getIsSmokeAlarm());
802 deviceChanged = true;
805 } else if (capability.isTypeAlarmActuator()) {
806 capabilityState.setAlarmActuatorState(event.getProperties().getOnState());
807 deviceChanged = true;
809 // MotionDetectionSensor
810 } else if (capability.isTypeMotionDetectionSensor()) {
811 capabilityState.setMotionDetectionSensorState(event.getProperties().getMotionDetectedCount());
812 deviceChanged = true;
815 } else if (capability.isTypeLuminanceSensor()) {
816 capabilityState.setLuminanceSensorState(event.getProperties().getLuminance());
817 deviceChanged = true;
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
824 // type is also not always present if null will be interpreted as a normal key press.
825 final Integer tmpButtonIndex = event.getProperties().getLastPressedButtonIndex();
827 if (tmpButtonIndex != null) {
828 capabilityState.setPushButtonSensorButtonIndexState(tmpButtonIndex);
830 .setPushButtonSensorButtonIndexType(event.getProperties().getLastKeyPressType());
832 final Integer tmpLastKeyPressCounter = event.getProperties().getLastKeyPressCounter();
834 if (tmpLastKeyPressCounter != null) {
835 capabilityState.setPushButtonSensorCounterState(tmpLastKeyPressCounter);
837 deviceChanged = true;
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;
854 // PowerConsumptionSensor
855 } else if (capability.isTypePowerConsumptionSensor()) {
856 capabilityState.setPowerConsumptionSensorPowerConsumptionWattState(
857 event.getProperties().getPowerConsumptionWatt());
858 deviceChanged = true;
860 // GenerationMeterEnergySensor
861 } else if (capability.isTypeGenerationMeterEnergySensor()) {
862 capabilityState.setGenerationMeterEnergySensorEnergyPerMonthInKWhState(
863 event.getProperties().getEnergyPerMonthInKWh());
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;
874 // GenerationMeterPowerConsumptionSensor
875 } else if (capability.isTypeGenerationMeterPowerConsumptionSensor()) {
876 capabilityState.setGenerationMeterPowerConsumptionSensorPowerInWattState(
877 event.getProperties().getPowerInWatt());
878 deviceChanged = true;
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;
894 // TwoWayMeterEnergyFeedSensor
895 } else if (capability.isTypeTwoWayMeterEnergyFeedSensor()) {
896 capabilityState.setTwoWayMeterEnergyFeedSensorEnergyPerMonthInKWhState(
897 event.getProperties().getEnergyPerMonthInKWh());
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;
908 // TwoWayMeterPowerConsumptionSensor
909 } else if (capability.isTypeTwoWayMeterPowerConsumptionSensor()) {
910 capabilityState.setTwoWayMeterPowerConsumptionSensorPowerInWattState(
911 event.getProperties().getPowerInWatt());
912 deviceChanged = true;
915 logger.debug("Unsupported capability type {}.", capability.getType());
918 logger.debug("Capability {} has no state (yet?) - refreshing device.", capability.getName());
921 final InnogyBridgeHandler innogyBridgeHandler = getInnogyBridgeHandler();
922 if (innogyBridgeHandler != null) {
923 device = innogyBridgeHandler.refreshDevice(deviceId);
925 if (device != null) {
926 capabilityMap = device.getCapabilityMap();
927 capability = capabilityMap.get(linkedCapabilityId);
928 if (capability.hasState()) {
929 deviceChanged = true;
933 if (deviceChanged && device != null) {
934 onDeviceStateChanged(device);
937 } else if (event.isLinkedtoDevice()) {
938 if (device.hasDeviceState()) {
939 onDeviceStateChanged(device);
941 logger.debug("Device {}/{} has no state.", device.getConfig().getName(), device.getId());
948 * Returns the inverted value. Currently only rollershutter channels are supported.
951 * @return the value or the inverted value
953 private int invertValueIfConfigured(final String channelId, final int value) {
954 if (!CHANNEL_ROLLERSHUTTER.equals(channelId)) {
955 logger.debug("Channel {} cannot be inverted.", channelId);
960 final Channel channel = getThing().getChannel(channelId);
961 if (channel == null) {
962 logger.debug("Channel {} was null! Value not inverted.", channelId);
965 final Boolean invert = (Boolean) channel.getConfiguration().get("invert");
966 return invert != null && invert ? value : (100 - value);