2 * Copyright (c) 2010-2023 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.homematic.internal.communicator.virtual;
15 import static org.openhab.binding.homematic.internal.misc.HomematicConstants.VIRTUAL_DATAPOINT_NAME_BUTTON;
17 import java.util.HashSet;
19 import org.openhab.binding.homematic.internal.misc.MiscUtils;
20 import org.openhab.binding.homematic.internal.model.HmChannel;
21 import org.openhab.binding.homematic.internal.model.HmDatapoint;
22 import org.openhab.binding.homematic.internal.model.HmDevice;
23 import org.openhab.binding.homematic.internal.model.HmValueType;
24 import org.openhab.core.thing.CommonTriggerEvents;
25 import org.openhab.core.thing.DefaultSystemChannelTypeProvider;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
30 * A virtual String datapoint which adds a BUTTON datapoint. It will forward key events to the
31 * system channel {@link DefaultSystemChannelTypeProvider#SYSTEM_BUTTON}.
33 * @author Michael Reitler - Initial contribution
35 public class ButtonVirtualDatapointHandler extends AbstractVirtualDatapointHandler {
36 private final Logger logger = LoggerFactory.getLogger(ButtonVirtualDatapointHandler.class);
38 private static final String LONG_REPEATED_EVENT = "LONG_REPEATED";
39 private static final String LONG_RELEASED_EVENT = "LONG_RELEASED";
41 private HashSet<String> devicesUsingLongStartEvent = new HashSet<>();
44 public String getName() {
45 return VIRTUAL_DATAPOINT_NAME_BUTTON;
49 public void initialize(HmDevice device) {
50 for (HmChannel channel : device.getChannels()) {
51 if (channel.hasPressDatapoint()) {
52 HmDatapoint dp = addDatapoint(device, channel.getNumber(), getName(), HmValueType.STRING, null, false);
54 dp.setOptions(new String[] { CommonTriggerEvents.SHORT_PRESSED, CommonTriggerEvents.LONG_PRESSED,
55 LONG_REPEATED_EVENT, LONG_RELEASED_EVENT });
61 public boolean canHandleEvent(HmDatapoint dp) {
62 return dp.isPressDatapoint();
66 public void handleEvent(VirtualGateway gateway, HmDatapoint dp) {
67 HmChannel channel = dp.getChannel();
68 String deviceSerial = channel.getDevice().getAddress();
69 HmDatapoint vdp = getVirtualDatapoint(channel);
70 int usPos = dp.getName().indexOf("_");
71 String pressType = usPos == -1 ? dp.getName() : dp.getName().substring(usPos + 1);
72 boolean isLongPressActive = CommonTriggerEvents.LONG_PRESSED.equals(vdp.getValue())
73 || LONG_REPEATED_EVENT.equals(vdp.getValue());
74 if (MiscUtils.isTrueValue(dp.getValue())) {
77 vdp.setValue(null); // Force sending new event
78 vdp.setValue(CommonTriggerEvents.SHORT_PRESSED);
82 if (isLongPressActive) {
83 // HM-IP devices do long press repetitions via LONG instead of CONT events,
84 // so clear previous value to force re-triggering of event
86 vdp.setValue(LONG_REPEATED_EVENT);
88 // HM devices start long press via LONG events
89 vdp.setValue(CommonTriggerEvents.LONG_PRESSED);
93 vdp.setValue(CommonTriggerEvents.LONG_PRESSED);
94 devicesUsingLongStartEvent.add(deviceSerial);
97 // Only send release events if we sent a pressed event before
98 vdp.setValue(isLongPressActive ? LONG_RELEASED_EVENT : null);
101 // Clear previous value to force re-triggering of repetition
103 // Only send repetitions if there was a pressed event before
104 // (a CONT might arrive simultaneously with the initial LONG event)
105 if (isLongPressActive) {
106 vdp.setValue(LONG_REPEATED_EVENT);
111 logger.warn("Unexpected vaule '{}' for PRESS virtual datapoint", pressType);
114 String usedStartEvent = devicesUsingLongStartEvent.contains(deviceSerial) ? "LONG_START" : "LONG";
115 if (usedStartEvent.equals(pressType) && LONG_REPEATED_EVENT.equals(vdp.getValue())) {
116 // If we're currently processing a repeated long-press event, don't let the initial LONG
117 // event time out the repetitions, the CONT delay handler will take care of it
118 vdp.setValue(LONG_REPEATED_EVENT);
119 } else if (isLongPressActive) {
120 // We seemingly missed an event (either a CONT or the final LONG_RELEASE),
121 // so end the long press cycle now
122 vdp.setValue(LONG_RELEASED_EVENT);
127 logger.debug("Handled virtual button event on {}:{}: press type {}, value {}, button state {} -> {}",
128 channel.getDevice().getAddress(), channel.getNumber(), pressType, dp.getValue(), vdp.getPreviousValue(),