]> git.basschouten.com Git - openhab-addons.git/blob
589c015c550880477b1f3a1b9f6ed378e38b4b29
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.homematic.internal.communicator.virtual;
14
15 import static org.openhab.binding.homematic.internal.misc.HomematicConstants.VIRTUAL_DATAPOINT_NAME_BUTTON;
16
17 import java.util.HashSet;
18
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.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 /**
29  * A virtual String datapoint which adds a BUTTON datapoint. It will forward key events to the
30  * system channel {@link org.openhab.core.thing.DefaultSystemChannelTypeProvider#SYSTEM_BUTTON}.
31  *
32  * @author Michael Reitler - Initial contribution
33  */
34 public class ButtonVirtualDatapointHandler extends AbstractVirtualDatapointHandler {
35     private final Logger logger = LoggerFactory.getLogger(ButtonVirtualDatapointHandler.class);
36
37     private static final String LONG_REPEATED_EVENT = "LONG_REPEATED";
38     private static final String LONG_RELEASED_EVENT = "LONG_RELEASED";
39
40     private HashSet<String> devicesUsingLongStartEvent = new HashSet<>();
41
42     @Override
43     public String getName() {
44         return VIRTUAL_DATAPOINT_NAME_BUTTON;
45     }
46
47     @Override
48     public void initialize(HmDevice device) {
49         for (HmChannel channel : device.getChannels()) {
50             if (channel.hasPressDatapoint()) {
51                 HmDatapoint dp = addDatapoint(device, channel.getNumber(), getName(), HmValueType.STRING, null, false);
52                 dp.setTrigger(true);
53                 dp.setOptions(new String[] { CommonTriggerEvents.SHORT_PRESSED, CommonTriggerEvents.LONG_PRESSED,
54                         LONG_REPEATED_EVENT, LONG_RELEASED_EVENT });
55             }
56         }
57     }
58
59     @Override
60     public boolean canHandleEvent(HmDatapoint dp) {
61         return dp.isPressDatapoint();
62     }
63
64     @Override
65     public void handleEvent(VirtualGateway gateway, HmDatapoint dp) {
66         HmChannel channel = dp.getChannel();
67         String deviceSerial = channel.getDevice().getAddress();
68         HmDatapoint vdp = getVirtualDatapoint(channel);
69         int usPos = dp.getName().indexOf("_");
70         String pressType = usPos == -1 ? dp.getName() : dp.getName().substring(usPos + 1);
71         boolean usesLongStart = devicesUsingLongStartEvent.contains(deviceSerial);
72         boolean isLongPressActive = CommonTriggerEvents.LONG_PRESSED.equals(vdp.getValue())
73                 || LONG_REPEATED_EVENT.equals(vdp.getValue());
74         if (MiscUtils.isTrueValue(dp.getValue())) {
75             switch (pressType) {
76                 case "SHORT": {
77                     vdp.setValue(null); // Force sending new event
78                     vdp.setValue(CommonTriggerEvents.SHORT_PRESSED);
79                     break;
80                 }
81                 case "LONG":
82                     if (usesLongStart) {
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
85                         if (isLongPressActive) {
86                             vdp.setValue(null);
87                             vdp.setValue(LONG_REPEATED_EVENT);
88                         }
89                     } else {
90                         // HM devices start long press via LONG events, but also may keep sending them
91                         // alongside CONT repetition events. In case a long press is already active, we just
92                         // acknowledge those events by setting the value again, to make sure to not re-trigger events
93                         vdp.setValue(isLongPressActive ? LONG_REPEATED_EVENT : CommonTriggerEvents.LONG_PRESSED);
94                     }
95                     break;
96                 case "LONG_START":
97                     vdp.setValue(CommonTriggerEvents.LONG_PRESSED);
98                     devicesUsingLongStartEvent.add(deviceSerial);
99                     break;
100                 case "LONG_RELEASE":
101                     // Only send release events if we sent a pressed event before
102                     vdp.setValue(isLongPressActive ? LONG_RELEASED_EVENT : null);
103                     break;
104                 case "CONT":
105                     // Clear previous value to force re-triggering of repetition
106                     vdp.setValue(null);
107                     // Only send repetitions if there was a pressed event before
108                     // (a CONT might arrive simultaneously with the initial LONG event)
109                     if (isLongPressActive) {
110                         vdp.setValue(LONG_REPEATED_EVENT);
111                     }
112                     break;
113                 default:
114                     vdp.setValue(null);
115                     logger.warn("Unexpected value '{}' for PRESS virtual datapoint", pressType);
116             }
117         } else {
118             String usedStartEvent = usesLongStart ? "LONG_START" : "LONG";
119             if (usedStartEvent.equals(pressType) && LONG_REPEATED_EVENT.equals(vdp.getValue())) {
120                 // If we're currently processing a repeated long-press event, don't let the initial LONG
121                 // event time out the repetitions, the CONT delay handler will take care of it
122                 vdp.setValue(LONG_REPEATED_EVENT);
123             } else if (isLongPressActive) {
124                 // We seemingly missed an event (either a CONT or the final LONG_RELEASE),
125                 // so end the long press cycle now
126                 vdp.setValue(LONG_RELEASED_EVENT);
127             } else {
128                 vdp.setValue(null);
129             }
130         }
131         logger.debug("Handled virtual button event on {}:{}: press type {}, value {}, button state {} -> {}",
132                 channel.getDevice().getAddress(), channel.getNumber(), pressType, dp.getValue(), vdp.getPreviousValue(),
133                 vdp.getValue());
134     }
135 }