2 * Copyright (c) 2010-2021 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.*;
32 import org.openhab.core.thing.Bridge;
33 import org.openhab.core.thing.Channel;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.CommonTriggerEvents;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.ThingStatusInfo;
40 import org.openhab.core.thing.ThingTypeUID;
41 import org.openhab.core.thing.binding.BaseThingHandler;
42 import org.openhab.core.thing.binding.ThingHandler;
43 import org.openhab.core.types.Command;
44 import org.openhab.core.types.RefreshType;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * The {@link InnogyDeviceHandler} is responsible for handling the {@link Device}s and their commands, which are
50 * sent to one of the channels.
52 * @author Oliver Kuhl - Initial contribution
55 public class InnogyDeviceHandler extends BaseThingHandler implements DeviceStatusListener {
57 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = SUPPORTED_DEVICE_THING_TYPES;
59 private static final String DEBUG = "DEBUG";
60 private static final String LONG_PRESS = "LongPress";
61 private static final String SHORT_PRESS = "ShortPress";
63 private final Logger logger = LoggerFactory.getLogger(InnogyDeviceHandler.class);
64 private final Object lock = new Object();
66 private String deviceId = "";
67 private @Nullable InnogyBridgeHandler bridgeHandler;
70 * Constructs a new {@link InnogyDeviceHandler} for the given {@link Thing}.
74 public InnogyDeviceHandler(final Thing thing) {
79 public void handleCommand(final ChannelUID channelUID, final Command command) {
80 logger.debug("handleCommand called for channel '{}' of type '{}' with command '{}'", channelUID,
81 getThing().getThingTypeUID().getId(), command);
83 final InnogyBridgeHandler innogyBridgeHandler = getInnogyBridgeHandler();
84 if (innogyBridgeHandler == null) {
85 logger.warn("BridgeHandler not found. Cannot handle command without bridge.");
88 if (!ThingStatus.ONLINE.equals(innogyBridgeHandler.getThing().getStatus())) {
89 logger.debug("Cannot handle command - bridge is not online. Command ignored.");
93 if (command instanceof RefreshType) {
95 final Device device = innogyBridgeHandler.getDeviceById(deviceId);
97 onDeviceStateChanged(device);
103 if (CHANNEL_SWITCH.equals(channelUID.getId())) {
107 final Device device = innogyBridgeHandler.getDeviceById(deviceId);
108 if (device != null && DEBUG.equals(device.getConfig().getName())) {
109 logger.debug("DEBUG SWITCH ACTIVATED!");
110 if (OnOffType.ON.equals(command)) {
111 innogyBridgeHandler.onEvent(
112 "{\"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 innogyBridgeHandler.onEvent(
115 "{\"sequenceNumber\": -1,\"type\": \"MessageDeleted\",\"desc\": \"/desc/event/MessageDeleted\",\"namespace\": \"core.RWE\",\"timestamp\": \"2019-07-07T19:15:39.2100000Z\",\"data\": { \"id\": \"6e5ce2290cd247208f95a5b53736958b\" }}");
120 if (command instanceof OnOffType) {
121 innogyBridgeHandler.commandSwitchDevice(deviceId, OnOffType.ON.equals(command));
125 } else if (CHANNEL_DIMMER.equals(channelUID.getId())) {
126 if (command instanceof DecimalType) {
127 final DecimalType dimLevel = (DecimalType) command;
128 innogyBridgeHandler.commandSetDimmLevel(deviceId, dimLevel.intValue());
129 } else if (command instanceof OnOffType) {
130 if (OnOffType.ON.equals(command)) {
131 innogyBridgeHandler.commandSetDimmLevel(deviceId, 100);
133 innogyBridgeHandler.commandSetDimmLevel(deviceId, 0);
138 } else if (CHANNEL_ROLLERSHUTTER.equals(channelUID.getId())) {
139 if (command instanceof DecimalType) {
140 final DecimalType rollerShutterLevel = (DecimalType) command;
141 innogyBridgeHandler.commandSetRollerShutterLevel(deviceId,
142 invertValueIfConfigured(CHANNEL_ROLLERSHUTTER, rollerShutterLevel.intValue()));
143 } else if (command instanceof OnOffType) {
144 if (OnOffType.ON.equals(command)) {
145 innogyBridgeHandler.commandSetRollerShutterStop(deviceId, ShutterAction.ShutterActions.DOWN);
147 innogyBridgeHandler.commandSetRollerShutterStop(deviceId, ShutterAction.ShutterActions.UP);
149 } else if (command instanceof UpDownType) {
150 if (UpDownType.DOWN.equals(command)) {
151 innogyBridgeHandler.commandSetRollerShutterStop(deviceId, ShutterAction.ShutterActions.DOWN);
153 innogyBridgeHandler.commandSetRollerShutterStop(deviceId, ShutterAction.ShutterActions.UP);
155 } else if (command instanceof StopMoveType) {
156 if (StopMoveType.STOP.equals(command)) {
157 innogyBridgeHandler.commandSetRollerShutterStop(deviceId, ShutterAction.ShutterActions.STOP);
162 } else if (CHANNEL_SET_TEMPERATURE.equals(channelUID.getId())) {
163 if (command instanceof DecimalType) {
164 final DecimalType pointTemperature = (DecimalType) command;
165 innogyBridgeHandler.commandUpdatePointTemperature(deviceId, pointTemperature.doubleValue());
169 } else if (CHANNEL_OPERATION_MODE.equals(channelUID.getId())) {
170 if (command instanceof StringType) {
171 final String autoModeCommand = command.toString();
173 if (CapabilityState.STATE_VALUE_OPERATION_MODE_AUTO.equals(autoModeCommand)) {
174 innogyBridgeHandler.commandSetOperationMode(deviceId, true);
175 } else if (CapabilityState.STATE_VALUE_OPERATION_MODE_MANUAL.equals(autoModeCommand)) {
176 innogyBridgeHandler.commandSetOperationMode(deviceId, false);
178 logger.warn("Could not set operationmode. Invalid value '{}'! Only '{}' or '{}' allowed.",
179 autoModeCommand, CapabilityState.STATE_VALUE_OPERATION_MODE_AUTO,
180 CapabilityState.STATE_VALUE_OPERATION_MODE_MANUAL);
185 } else if (CHANNEL_ALARM.equals(channelUID.getId())) {
186 if (command instanceof OnOffType) {
187 innogyBridgeHandler.commandSwitchAlarm(deviceId, OnOffType.ON.equals(command));
190 logger.debug("UNSUPPORTED channel {} for device {}.", channelUID.getId(), deviceId);
195 public void initialize() {
196 logger.debug("Initializing innogy SmartHome device handler.");
197 initializeThing(getBridge() == null ? null : getBridge().getStatus());
201 public void dispose() {
202 if (bridgeHandler != null) {
203 bridgeHandler.unregisterDeviceStatusListener(this);
208 public void bridgeStatusChanged(final ThingStatusInfo bridgeStatusInfo) {
209 logger.debug("bridgeStatusChanged {}", bridgeStatusInfo);
210 initializeThing(bridgeStatusInfo.getStatus());
214 * Initializes the {@link Thing} corresponding to the given status of the bridge.
216 * @param bridgeStatus
218 private void initializeThing(@Nullable final ThingStatus bridgeStatus) {
219 logger.debug("initializeThing thing {} bridge status {}", getThing().getUID(), bridgeStatus);
220 final String configDeviceId = (String) getConfig().get(PROPERTY_ID);
221 if (configDeviceId != null) {
222 deviceId = configDeviceId;
223 // note: this call implicitly registers our handler as a listener on
225 if (getInnogyBridgeHandler() != null) {
226 if (bridgeStatus == ThingStatus.ONLINE) {
227 if (initializeProperties()) {
228 updateStatus(ThingStatus.ONLINE);
230 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.GONE,
231 "Device not found in innogy config. Was it removed?");
234 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
237 updateStatus(ThingStatus.OFFLINE);
240 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "device id unknown");
245 * Initializes all properties of the {@link Device}, like vendor, serialnumber etc.
247 private boolean initializeProperties() {
248 synchronized (this.lock) {
250 final Device device = getDevice();
251 if (device != null) {
252 final Map<String, String> properties = editProperties();
253 properties.put(PROPERTY_ID, device.getId());
254 properties.put(PROPERTY_PROTOCOL_ID, device.getConfig().getProtocolId());
255 if (device.hasSerialNumber()) {
256 properties.put(Thing.PROPERTY_SERIAL_NUMBER, device.getSerialnumber());
258 properties.put(Thing.PROPERTY_VENDOR, device.getManufacturer());
259 properties.put(PROPERTY_VERSION, device.getVersion());
260 if (device.hasLocation()) {
261 properties.put(PROPERTY_LOCATION, device.getLocation().getName());
263 if (device.isBatteryPowered()) {
264 properties.put(PROPERTY_BATTERY_POWERED, "yes");
266 properties.put(PROPERTY_BATTERY_POWERED, "no");
268 if (device.isController()) {
269 properties.put(PROPERTY_DEVICE_TYPE, "Controller");
270 } else if (device.isVirtualDevice()) {
271 properties.put(PROPERTY_DEVICE_TYPE, "Virtual");
272 } else if (device.isRadioDevice()) {
273 properties.put(PROPERTY_DEVICE_TYPE, "Radio");
277 if (DEVICE_RST.equals(device.getType()) || DEVICE_RST2.equals(device.getType())
278 || DEVICE_WRT.equals(device.getType())) {
279 properties.put(PROPERTY_DISPLAY_CURRENT_TEMPERATURE,
280 device.getConfig().getDisplayCurrentTemperature());
284 if (DEVICE_ANALOG_METER.equals(device.getType()) || DEVICE_GENERATION_METER.equals(device.getType())
285 || DEVICE_SMART_METER.equals(device.getType())
286 || DEVICE_TWO_WAY_METER.equals(device.getType())) {
287 properties.put(PROPERTY_METER_ID, device.getConfig().getMeterId());
288 properties.put(PROPERTY_METER_FIRMWARE_VERSION, device.getConfig().getMeterFirmwareVersion());
291 if (device.getConfig().getTimeOfAcceptance() != null) {
292 properties.put(PROPERTY_TIME_OF_ACCEPTANCE, device.getConfig().getTimeOfAcceptance()
293 .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
295 if (device.getConfig().getTimeOfDiscovery() != null) {
296 properties.put(PROPERTY_TIME_OF_DISCOVERY, device.getConfig().getTimeOfDiscovery()
297 .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
300 updateProperties(properties);
302 onDeviceStateChanged(device);
305 logger.warn("initializeProperties: The device with id {} isn't found", deviceId);
312 * Returns the {@link Device} associated with this {@link InnogyDeviceHandler} (referenced by the
313 * {@link InnogyDeviceHandler#deviceId}).
315 * @return the {@link Device} or null, if not found or no {@link InnogyBridgeHandler} is available
317 private @Nullable Device getDevice() {
318 if (getInnogyBridgeHandler() != null) {
319 return getInnogyBridgeHandler().getDeviceById(deviceId);
325 * Returns the innogy bridge handler.
327 * @return the {@link InnogyBridgeHandler} or null
329 private @Nullable InnogyBridgeHandler getInnogyBridgeHandler() {
330 synchronized (this.lock) {
331 if (this.bridgeHandler == null) {
333 final Bridge bridge = getBridge();
334 if (bridge == null) {
338 final ThingHandler handler = bridge.getHandler();
339 if (handler instanceof InnogyBridgeHandler) {
340 this.bridgeHandler = (InnogyBridgeHandler) handler;
341 this.bridgeHandler.registerDeviceStatusListener(this);
346 return this.bridgeHandler;
351 public void onDeviceStateChanged(final Device device) {
352 synchronized (this.lock) {
353 if (!deviceId.equals(device.getId())) {
354 logger.trace("DeviceId {} not relevant for this handler (responsible for id {})", device.getId(),
359 logger.debug("onDeviceStateChanged called with device {}/{}", device.getConfig().getName(), device.getId());
362 if (device.hasDeviceState()) {
364 Boolean reachable = null;
365 if (device.getDeviceState().hasIsReachableState()) {
366 reachable = device.getDeviceState().isReachable();
369 if (reachable != null && !reachable) {
370 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device not reachable.");
372 } else if ((reachable != null && reachable) || DEVICE_VARIABLE_ACTUATOR.equals(device.getType())) {
373 if (device.getDeviceState().deviceIsIncluded()) {
374 updateStatus(ThingStatus.ONLINE);
376 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
377 "State is " + device.getDeviceState().getDeviceInclusionState());
382 if (device.isBatteryPowered()) {
383 if (device.hasLowBattery()) {
384 updateState(CHANNEL_BATTERY_LOW, OnOffType.ON);
386 updateState(CHANNEL_BATTERY_LOW, OnOffType.OFF);
391 for (final Entry<String, Capability> entry : device.getCapabilityMap().entrySet()) {
392 final Capability c = entry.getValue();
394 logger.debug("->capability:{} ({}/{})", c.getId(), c.getType(), c.getName());
396 if (c.getCapabilityState() == null) {
397 logger.debug("Capability not available for device {} ({})", device.getConfig().getName(),
401 switch (c.getType()) {
402 case Capability.TYPE_VARIABLEACTUATOR:
403 final Boolean variableActuatorState = c.getCapabilityState().getVariableActuatorState();
404 if (variableActuatorState != null) {
405 updateState(CHANNEL_SWITCH, variableActuatorState ? OnOffType.ON : OnOffType.OFF);
407 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
408 c.getCapabilityState().getId(), c.getId());
411 case Capability.TYPE_SWITCHACTUATOR:
412 final Boolean switchActuatorState = c.getCapabilityState().getSwitchActuatorState();
413 if (switchActuatorState != null) {
414 updateState(CHANNEL_SWITCH, switchActuatorState ? OnOffType.ON : OnOffType.OFF);
416 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
417 c.getCapabilityState().getId(), c.getId());
420 case Capability.TYPE_DIMMERACTUATOR:
421 final Integer dimLevel = c.getCapabilityState().getDimmerActuatorState();
422 if (dimLevel != null) {
423 logger.debug("Dimlevel state {}", dimLevel);
424 updateState(CHANNEL_DIMMER, new PercentType(dimLevel));
426 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
427 c.getCapabilityState().getId(), c.getId());
430 case Capability.TYPE_ROLLERSHUTTERACTUATOR:
431 Integer rollerShutterLevel = c.getCapabilityState().getRollerShutterActuatorState();
432 if (rollerShutterLevel != null) {
433 rollerShutterLevel = invertValueIfConfigured(CHANNEL_ROLLERSHUTTER, rollerShutterLevel);
434 logger.debug("RollerShutterlevel state {}", rollerShutterLevel);
435 updateState(CHANNEL_ROLLERSHUTTER, new PercentType(rollerShutterLevel));
437 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
438 c.getCapabilityState().getId(), c.getId());
441 case Capability.TYPE_TEMPERATURESENSOR:
443 final Double temperatureSensorState = c.getCapabilityState()
444 .getTemperatureSensorTemperatureState();
445 if (temperatureSensorState != null) {
446 logger.debug("-> Temperature sensor state: {}", temperatureSensorState);
447 updateState(CHANNEL_TEMPERATURE, new DecimalType(temperatureSensorState));
449 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
450 c.getCapabilityState().getId(), c.getId());
454 final Boolean temperatureSensorFrostWarningState = c.getCapabilityState()
455 .getTemperatureSensorFrostWarningState();
456 if (temperatureSensorFrostWarningState != null) {
457 updateState(CHANNEL_FROST_WARNING,
458 temperatureSensorFrostWarningState ? OnOffType.ON : OnOffType.OFF);
460 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
461 c.getCapabilityState().getId(), c.getId());
465 case Capability.TYPE_THERMOSTATACTUATOR:
467 final Double thermostatActuatorPointTemperatureState = c.getCapabilityState()
468 .getThermostatActuatorPointTemperatureState();
469 if (thermostatActuatorPointTemperatureState != null) {
470 final DecimalType pointTemp = new DecimalType(thermostatActuatorPointTemperatureState);
472 "Update CHANNEL_SET_TEMPERATURE: state:{}->decType:{} (DeviceName {}, Capab-ID:{})",
473 thermostatActuatorPointTemperatureState, pointTemp, device.getConfig().getName(),
475 updateState(CHANNEL_SET_TEMPERATURE, pointTemp);
477 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
478 c.getCapabilityState().getId(), c.getId());
482 final String thermostatActuatorOperationModeState = c.getCapabilityState()
483 .getThermostatActuatorOperationModeState();
484 if (thermostatActuatorOperationModeState != null) {
485 final StringType operationMode = new StringType(thermostatActuatorOperationModeState);
486 updateState(CHANNEL_OPERATION_MODE, operationMode);
488 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
489 c.getCapabilityState().getId(), c.getId());
492 // window reduction active
493 final Boolean thermostatActuatorWindowReductionActiveState = c.getCapabilityState()
494 .getThermostatActuatorWindowReductionActiveState();
495 if (thermostatActuatorWindowReductionActiveState != null) {
496 updateState(CHANNEL_WINDOW_REDUCTION_ACTIVE,
497 thermostatActuatorWindowReductionActiveState ? OnOffType.ON : OnOffType.OFF);
499 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
500 c.getCapabilityState().getId(), c.getId());
503 case Capability.TYPE_HUMIDITYSENSOR:
505 final Double humidityState = c.getCapabilityState().getHumiditySensorHumidityState();
506 if (humidityState != null) {
507 final DecimalType humidity = new DecimalType(humidityState);
508 updateState(CHANNEL_HUMIDITY, humidity);
510 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
511 c.getCapabilityState().getId(), c.getId());
515 final Boolean humiditySensorMoldWarningState = c.getCapabilityState()
516 .getHumiditySensorMoldWarningState();
517 if (humiditySensorMoldWarningState != null) {
518 updateState(CHANNEL_MOLD_WARNING,
519 humiditySensorMoldWarningState ? OnOffType.ON : OnOffType.OFF);
521 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
522 c.getCapabilityState().getId(), c.getId());
525 case Capability.TYPE_WINDOWDOORSENSOR:
526 final Boolean contactState = c.getCapabilityState().getWindowDoorSensorState();
527 if (contactState != null) {
528 updateState(CHANNEL_CONTACT, contactState ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
530 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
531 c.getCapabilityState().getId(), c.getId());
534 case Capability.TYPE_SMOKEDETECTORSENSOR:
535 final Boolean smokeState = c.getCapabilityState().getSmokeDetectorSensorState();
536 if (smokeState != null) {
537 updateState(CHANNEL_SMOKE, smokeState ? OnOffType.ON : OnOffType.OFF);
539 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
540 c.getCapabilityState().getId(), c.getId());
543 case Capability.TYPE_ALARMACTUATOR:
544 final Boolean alarmState = c.getCapabilityState().getAlarmActuatorState();
545 if (alarmState != null) {
546 updateState(CHANNEL_ALARM, alarmState ? OnOffType.ON : OnOffType.OFF);
548 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
549 c.getCapabilityState().getId(), c.getId());
552 case Capability.TYPE_MOTIONDETECTIONSENSOR:
553 final Integer motionState = c.getCapabilityState().getMotionDetectionSensorState();
554 if (motionState != null) {
555 final DecimalType motionCount = new DecimalType(motionState);
556 logger.debug("Motion state {} -> count {}", motionState, motionCount);
557 updateState(CHANNEL_MOTION_COUNT, motionCount);
559 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
560 c.getCapabilityState().getId(), c.getId());
563 case Capability.TYPE_LUMINANCESENSOR:
564 final Double luminanceState = c.getCapabilityState().getLuminanceSensorState();
565 if (luminanceState != null) {
566 final DecimalType luminance = new DecimalType(luminanceState);
567 updateState(CHANNEL_LUMINANCE, luminance);
569 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
570 c.getCapabilityState().getId(), c.getId());
573 case Capability.TYPE_PUSHBUTTONSENSOR:
574 final Integer pushCountState = c.getCapabilityState().getPushButtonSensorCounterState();
575 final Integer buttonIndexState = c.getCapabilityState().getPushButtonSensorButtonIndexState();
576 logger.debug("Pushbutton index {} count {}", buttonIndexState, pushCountState);
577 if (pushCountState != null) {
578 final DecimalType pushCount = new DecimalType(pushCountState);
579 // prevent error when buttonIndexState is null
580 if (buttonIndexState != null) {
581 if (buttonIndexState >= 0 && buttonIndexState <= 7) {
582 final int channelIndex = buttonIndexState + 1;
583 final String type = c.getCapabilityState().getPushButtonSensorButtonIndexType();
584 final String triggerEvent = SHORT_PRESS.equals(type)
585 ? CommonTriggerEvents.SHORT_PRESSED
586 : (LONG_PRESS.equals(type) ? CommonTriggerEvents.LONG_PRESSED
587 : CommonTriggerEvents.PRESSED);
589 triggerChannel(CHANNEL_BUTTON + channelIndex, triggerEvent);
590 updateState(String.format(CHANNEL_BUTTON_COUNT, channelIndex), pushCount);
592 logger.debug("Button index {} not supported.", buttonIndexState);
594 // Button handled so remove state to avoid re-trigger.
595 c.getCapabilityState().setPushButtonSensorButtonIndexState(null);
596 c.getCapabilityState().setPushButtonSensorButtonIndexType(null);
598 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
599 c.getCapabilityState().getId(), c.getId());
602 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", c.getType(),
603 c.getCapabilityState().getId(), c.getId());
606 case Capability.TYPE_ENERGYCONSUMPTIONSENSOR:
607 updateStateForEnergyChannel(CHANNEL_ENERGY_CONSUMPTION_MONTH_KWH,
608 c.getCapabilityState().getEnergyConsumptionSensorEnergyConsumptionMonthKWhState(), c);
609 updateStateForEnergyChannel(CHANNEL_ABOLUTE_ENERGY_CONSUMPTION,
610 c.getCapabilityState().getEnergyConsumptionSensorAbsoluteEnergyConsumptionState(), c);
611 updateStateForEnergyChannel(CHANNEL_ENERGY_CONSUMPTION_MONTH_EURO,
612 c.getCapabilityState().getEnergyConsumptionSensorEnergyConsumptionMonthEuroState(), c);
613 updateStateForEnergyChannel(CHANNEL_ENERGY_CONSUMPTION_DAY_EURO,
614 c.getCapabilityState().getEnergyConsumptionSensorEnergyConsumptionDayEuroState(), c);
615 updateStateForEnergyChannel(CHANNEL_ENERGY_CONSUMPTION_DAY_KWH,
616 c.getCapabilityState().getEnergyConsumptionSensorEnergyConsumptionDayKWhState(), c);
618 case Capability.TYPE_POWERCONSUMPTIONSENSOR:
619 updateStateForEnergyChannel(CHANNEL_POWER_CONSUMPTION_WATT,
620 c.getCapabilityState().getPowerConsumptionSensorPowerConsumptionWattState(), c);
622 case Capability.TYPE_GENERATIONMETERENERGYSENSOR:
623 updateStateForEnergyChannel(CHANNEL_ENERGY_GENERATION_MONTH_KWH,
624 c.getCapabilityState().getGenerationMeterEnergySensorEnergyPerMonthInKWhState(), c);
625 updateStateForEnergyChannel(CHANNEL_TOTAL_ENERGY_GENERATION,
626 c.getCapabilityState().getGenerationMeterEnergySensorTotalEnergyState(), c);
627 updateStateForEnergyChannel(CHANNEL_ENERGY_GENERATION_MONTH_EURO,
628 c.getCapabilityState().getGenerationMeterEnergySensorEnergyPerMonthInEuroState(), c);
629 updateStateForEnergyChannel(CHANNEL_ENERGY_GENERATION_DAY_EURO,
630 c.getCapabilityState().getGenerationMeterEnergySensorEnergyPerDayInEuroState(), c);
631 updateStateForEnergyChannel(CHANNEL_ENERGY_GENERATION_DAY_KWH,
632 c.getCapabilityState().getGenerationMeterEnergySensorEnergyPerDayInKWhState(), c);
634 case Capability.TYPE_GENERATIONMETERPOWERCONSUMPTIONSENSOR:
635 updateStateForEnergyChannel(CHANNEL_POWER_GENERATION_WATT,
636 c.getCapabilityState().getGenerationMeterPowerConsumptionSensorPowerInWattState(), c);
638 case Capability.TYPE_TWOWAYMETERENERGYCONSUMPTIONSENSOR:
639 updateStateForEnergyChannel(CHANNEL_ENERGY_MONTH_KWH,
640 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInKWhState(),
642 updateStateForEnergyChannel(CHANNEL_TOTAL_ENERGY,
643 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorTotalEnergyState(), c);
644 updateStateForEnergyChannel(CHANNEL_ENERGY_MONTH_EURO,
645 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInEuroState(),
647 updateStateForEnergyChannel(CHANNEL_ENERGY_DAY_EURO,
648 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerDayInEuroState(),
650 updateStateForEnergyChannel(CHANNEL_ENERGY_DAY_KWH,
651 c.getCapabilityState().getTwoWayMeterEnergyConsumptionSensorEnergyPerDayInKWhState(),
654 case Capability.TYPE_TWOWAYMETERENERGYFEEDSENSOR:
655 updateStateForEnergyChannel(CHANNEL_ENERGY_FEED_MONTH_KWH,
656 c.getCapabilityState().getTwoWayMeterEnergyFeedSensorEnergyPerMonthInKWhState(), c);
657 updateStateForEnergyChannel(CHANNEL_TOTAL_ENERGY_FED,
658 c.getCapabilityState().getTwoWayMeterEnergyFeedSensorTotalEnergyState(), c);
659 updateStateForEnergyChannel(CHANNEL_ENERGY_FEED_MONTH_EURO,
660 c.getCapabilityState().getTwoWayMeterEnergyFeedSensorEnergyPerMonthInEuroState(), c);
661 updateStateForEnergyChannel(CHANNEL_ENERGY_FEED_DAY_EURO,
662 c.getCapabilityState().getTwoWayMeterEnergyFeedSensorEnergyPerDayInEuroState(), c);
663 updateStateForEnergyChannel(CHANNEL_ENERGY_FEED_DAY_KWH,
664 c.getCapabilityState().getTwoWayMeterEnergyFeedSensorEnergyPerDayInKWhState(), c);
666 case Capability.TYPE_TWOWAYMETERPOWERCONSUMPTIONSENSOR:
667 updateStateForEnergyChannel(CHANNEL_POWER_WATT,
668 c.getCapabilityState().getTwoWayMeterPowerConsumptionSensorPowerInWattState(), c);
671 logger.debug("Unsupported capability type {}.", c.getType());
679 * Updates the state for the {@link Channel} of an energy {@link Device}.
685 private void updateStateForEnergyChannel(final String channelId, @Nullable final Double state,
686 final Capability capability) {
688 final DecimalType newValue = new DecimalType(state);
689 updateState(channelId, newValue);
691 logger.debug("State for {} is STILL NULL!! cstate-id: {}, c-id: {}", capability.getType(),
692 capability.getCapabilityState().getId(), capability.getId());
697 public void onDeviceStateChanged(final Device changedDevice, final Event event) {
698 synchronized (this.lock) {
699 Device device = changedDevice;
700 if (!deviceId.equals(device.getId())) {
704 logger.trace("DeviceId {} relevant for this handler.", device.getId());
706 if (event.isLinkedtoCapability()) {
707 boolean deviceChanged = false;
708 final String linkedCapabilityId = event.getSourceId();
710 Map<String, Capability> capabilityMap = device.getCapabilityMap();
711 Capability capability = capabilityMap.get(linkedCapabilityId);
712 logger.trace("Loaded Capability {}, {} with id {}, device {} from device id {}", capability.getType(),
713 capability.getName(), capability.getId(), capability.getDeviceLink(), device.getId());
715 CapabilityState capabilityState;
716 if (capability.hasState()) {
717 capabilityState = capability.getCapabilityState();
720 if (capability.isTypeVariableActuator()) {
721 capabilityState.setVariableActuatorState(event.getProperties().getValue());
722 deviceChanged = true;
725 } else if (capability.isTypeSwitchActuator()) {
726 capabilityState.setSwitchActuatorState(event.getProperties().getOnState());
727 deviceChanged = true;
730 } else if (capability.isTypeDimmerActuator()) {
731 capabilityState.setDimmerActuatorState(event.getProperties().getDimLevel());
732 deviceChanged = true;
734 // RollerShutterActuator
735 } else if (capability.isTypeRollerShutterActuator()) {
736 capabilityState.setRollerShutterActuatorState(event.getProperties().getShutterLevel());
737 deviceChanged = true;
740 } else if (capability.isTypeTemperatureSensor()) {
741 // when values are changed, they come with separate events
742 // values should only updated when they are not null
743 final Double tmpTemperatureState = event.getProperties().getTemperature();
744 final Boolean tmpFrostWarningState = event.getProperties().getFrostWarning();
745 if (tmpTemperatureState != null) {
746 capabilityState.setTemperatureSensorTemperatureState(tmpTemperatureState);
748 if (tmpFrostWarningState != null) {
749 capabilityState.setTemperatureSensorFrostWarningState(tmpFrostWarningState);
751 deviceChanged = true;
753 // ThermostatActuator
754 } else if (capability.isTypeThermostatActuator()) {
755 // when values are changed, they come with separate events
756 // values should only updated when they are not null
758 final Double tmpPointTemperatureState = event.getProperties().getPointTemperature();
759 final String tmpOperationModeState = event.getProperties().getOperationMode();
760 final Boolean tmpWindowReductionActiveState = event.getProperties().getWindowReductionActive();
762 if (tmpPointTemperatureState != null) {
763 capabilityState.setThermostatActuatorPointTemperatureState(tmpPointTemperatureState);
765 if (tmpOperationModeState != null) {
766 capabilityState.setThermostatActuatorOperationModeState(tmpOperationModeState);
768 if (tmpWindowReductionActiveState != null) {
770 .setThermostatActuatorWindowReductionActiveState(tmpWindowReductionActiveState);
772 deviceChanged = true;
775 } else if (capability.isTypeHumiditySensor()) {
776 // when values are changed, they come with separate events
777 // values should only updated when they are not null
778 final Double tmpHumidityState = event.getProperties().getHumidity();
779 final Boolean tmpMoldWarningState = event.getProperties().getMoldWarning();
780 if (tmpHumidityState != null) {
781 capabilityState.setHumiditySensorHumidityState(tmpHumidityState);
783 if (tmpMoldWarningState != null) {
784 capabilityState.setHumiditySensorMoldWarningState(tmpMoldWarningState);
786 deviceChanged = true;
789 } else if (capability.isTypeWindowDoorSensor()) {
790 capabilityState.setWindowDoorSensorState(event.getProperties().getIsOpen());
791 deviceChanged = true;
793 // SmokeDetectorSensor
794 } else if (capability.isTypeSmokeDetectorSensor()) {
795 capabilityState.setSmokeDetectorSensorState(event.getProperties().getIsSmokeAlarm());
796 deviceChanged = true;
799 } else if (capability.isTypeAlarmActuator()) {
800 capabilityState.setAlarmActuatorState(event.getProperties().getOnState());
801 deviceChanged = true;
803 // MotionDetectionSensor
804 } else if (capability.isTypeMotionDetectionSensor()) {
805 capabilityState.setMotionDetectionSensorState(event.getProperties().getMotionDetectedCount());
806 deviceChanged = true;
809 } else if (capability.isTypeLuminanceSensor()) {
810 capabilityState.setLuminanceSensorState(event.getProperties().getLuminance());
811 deviceChanged = true;
814 } else if (capability.isTypePushButtonSensor()) {
815 // Some devices send both StateChanged and ButtonPressed. But only one should be handled.
816 // If ButtonPressed is send lastPressedButtonIndex is not set in StateChanged so ignore
818 // type is also not always present if null will be interpreted as a normal key press.
819 final Integer tmpButtonIndex = event.getProperties().getLastPressedButtonIndex();
821 if (tmpButtonIndex != null) {
822 capabilityState.setPushButtonSensorButtonIndexState(tmpButtonIndex);
824 .setPushButtonSensorButtonIndexType(event.getProperties().getLastKeyPressType());
826 final Integer tmpLastKeyPressCounter = event.getProperties().getLastKeyPressCounter();
828 if (tmpLastKeyPressCounter != null) {
829 capabilityState.setPushButtonSensorCounterState(tmpLastKeyPressCounter);
831 deviceChanged = true;
834 // EnergyConsumptionSensor
835 } else if (capability.isTypeEnergyConsumptionSensor()) {
836 capabilityState.setEnergyConsumptionSensorEnergyConsumptionMonthKWhState(
837 event.getProperties().getEnergyConsumptionMonthKWh());
838 capabilityState.setEnergyConsumptionSensorAbsoluteEnergyConsumptionState(
839 event.getProperties().getAbsoluteEnergyConsumption());
840 capabilityState.setEnergyConsumptionSensorEnergyConsumptionMonthEuroState(
841 event.getProperties().getEnergyConsumptionMonthEuro());
842 capabilityState.setEnergyConsumptionSensorEnergyConsumptionDayEuroState(
843 event.getProperties().getEnergyConsumptionDayEuro());
844 capabilityState.setEnergyConsumptionSensorEnergyConsumptionDayKWhState(
845 event.getProperties().getEnergyConsumptionDayKWh());
846 deviceChanged = true;
848 // PowerConsumptionSensor
849 } else if (capability.isTypePowerConsumptionSensor()) {
850 capabilityState.setPowerConsumptionSensorPowerConsumptionWattState(
851 event.getProperties().getPowerConsumptionWatt());
852 deviceChanged = true;
854 // GenerationMeterEnergySensor
855 } else if (capability.isTypeGenerationMeterEnergySensor()) {
856 capabilityState.setGenerationMeterEnergySensorEnergyPerMonthInKWhState(
857 event.getProperties().getEnergyPerMonthInKWh());
859 .setGenerationMeterEnergySensorTotalEnergyState(event.getProperties().getTotalEnergy());
860 capabilityState.setGenerationMeterEnergySensorEnergyPerMonthInEuroState(
861 event.getProperties().getEnergyPerMonthInEuro());
862 capabilityState.setGenerationMeterEnergySensorEnergyPerDayInEuroState(
863 event.getProperties().getEnergyPerDayInEuro());
864 capabilityState.setGenerationMeterEnergySensorEnergyPerDayInKWhState(
865 event.getProperties().getEnergyPerDayInKWh());
866 deviceChanged = true;
868 // GenerationMeterPowerConsumptionSensor
869 } else if (capability.isTypeGenerationMeterPowerConsumptionSensor()) {
870 capabilityState.setGenerationMeterPowerConsumptionSensorPowerInWattState(
871 event.getProperties().getPowerInWatt());
872 deviceChanged = true;
874 // TwoWayMeterEnergyConsumptionSensor
875 } else if (capability.isTypeTwoWayMeterEnergyConsumptionSensor()) {
876 capabilityState.setTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInKWhState(
877 event.getProperties().getEnergyPerMonthInKWh());
878 capabilityState.setTwoWayMeterEnergyConsumptionSensorTotalEnergyState(
879 event.getProperties().getTotalEnergy());
880 capabilityState.setTwoWayMeterEnergyConsumptionSensorEnergyPerMonthInEuroState(
881 event.getProperties().getEnergyPerMonthInEuro());
882 capabilityState.setTwoWayMeterEnergyConsumptionSensorEnergyPerDayInEuroState(
883 event.getProperties().getEnergyPerDayInEuro());
884 capabilityState.setTwoWayMeterEnergyConsumptionSensorEnergyPerDayInKWhState(
885 event.getProperties().getEnergyPerDayInKWh());
886 deviceChanged = true;
888 // TwoWayMeterEnergyFeedSensor
889 } else if (capability.isTypeTwoWayMeterEnergyFeedSensor()) {
890 capabilityState.setTwoWayMeterEnergyFeedSensorEnergyPerMonthInKWhState(
891 event.getProperties().getEnergyPerMonthInKWh());
893 .setTwoWayMeterEnergyFeedSensorTotalEnergyState(event.getProperties().getTotalEnergy());
894 capabilityState.setTwoWayMeterEnergyFeedSensorEnergyPerMonthInEuroState(
895 event.getProperties().getEnergyPerMonthInEuro());
896 capabilityState.setTwoWayMeterEnergyFeedSensorEnergyPerDayInEuroState(
897 event.getProperties().getEnergyPerDayInEuro());
898 capabilityState.setTwoWayMeterEnergyFeedSensorEnergyPerDayInKWhState(
899 event.getProperties().getEnergyPerDayInKWh());
900 deviceChanged = true;
902 // TwoWayMeterPowerConsumptionSensor
903 } else if (capability.isTypeTwoWayMeterPowerConsumptionSensor()) {
904 capabilityState.setTwoWayMeterPowerConsumptionSensorPowerInWattState(
905 event.getProperties().getPowerInWatt());
906 deviceChanged = true;
909 logger.debug("Unsupported capability type {}.", capability.getType());
912 logger.debug("Capability {} has no state (yet?) - refreshing device.", capability.getName());
915 final InnogyBridgeHandler innogyBridgeHandler = getInnogyBridgeHandler();
916 if (innogyBridgeHandler != null) {
917 device = innogyBridgeHandler.refreshDevice(deviceId);
919 if (device != null) {
920 capabilityMap = device.getCapabilityMap();
921 capability = capabilityMap.get(linkedCapabilityId);
922 if (capability.hasState()) {
923 deviceChanged = true;
927 if (deviceChanged && device != null) {
928 onDeviceStateChanged(device);
931 } else if (event.isLinkedtoDevice()) {
932 if (device.hasDeviceState()) {
933 onDeviceStateChanged(device);
935 logger.debug("Device {}/{} has no state.", device.getConfig().getName(), device.getId());
942 * Returns the inverted value. Currently only rollershutter channels are supported.
945 * @return the value or the inverted value
947 private int invertValueIfConfigured(final String channelId, final int value) {
948 if (!CHANNEL_ROLLERSHUTTER.equals(channelId)) {
949 logger.debug("Channel {} cannot be inverted.", channelId);
954 final Channel channel = getThing().getChannel(channelId);
955 if (channel == null) {
956 logger.debug("Channel {} was null! Value not inverted.", channelId);
959 final Boolean invert = (Boolean) channel.getConfiguration().get("invert");
960 return invert != null && invert ? value : (100 - value);