]> git.basschouten.com Git - openhab-addons.git/blob
36a9c49f093b3e7e11ddeef5276f5c239492ee75
[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.openhab.core.thing.DefaultSystemChannelTypeProvider;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30  * A virtual String datapoint which adds a BUTTON datapoint. It will forward key events to the
31  * system channel {@link DefaultSystemChannelTypeProvider#SYSTEM_BUTTON}.
32  *
33  * @author Michael Reitler - Initial contribution
34  */
35 public class ButtonVirtualDatapointHandler extends AbstractVirtualDatapointHandler {
36     private final Logger logger = LoggerFactory.getLogger(ButtonVirtualDatapointHandler.class);
37
38     private static final String LONG_REPEATED_EVENT = "LONG_REPEATED";
39     private static final String LONG_RELEASED_EVENT = "LONG_RELEASED";
40
41     private HashSet<String> devicesUsingLongStartEvent = new HashSet<>();
42
43     @Override
44     public String getName() {
45         return VIRTUAL_DATAPOINT_NAME_BUTTON;
46     }
47
48     @Override
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);
53                 dp.setTrigger(true);
54                 dp.setOptions(new String[] { CommonTriggerEvents.SHORT_PRESSED, CommonTriggerEvents.LONG_PRESSED,
55                         LONG_REPEATED_EVENT, LONG_RELEASED_EVENT });
56             }
57         }
58     }
59
60     @Override
61     public boolean canHandleEvent(HmDatapoint dp) {
62         return dp.isPressDatapoint();
63     }
64
65     @Override
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())) {
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 (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
85                         vdp.setValue(null);
86                         vdp.setValue(LONG_REPEATED_EVENT);
87                     } else {
88                         // HM devices start long press via LONG events
89                         vdp.setValue(CommonTriggerEvents.LONG_PRESSED);
90                     }
91                     break;
92                 case "LONG_START":
93                     vdp.setValue(CommonTriggerEvents.LONG_PRESSED);
94                     devicesUsingLongStartEvent.add(deviceSerial);
95                     break;
96                 case "LONG_RELEASE":
97                     // Only send release events if we sent a pressed event before
98                     vdp.setValue(isLongPressActive ? LONG_RELEASED_EVENT : null);
99                     break;
100                 case "CONT":
101                     // Clear previous value to force re-triggering of repetition
102                     vdp.setValue(null);
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);
107                     }
108                     break;
109                 default:
110                     vdp.setValue(null);
111                     logger.warn("Unexpected vaule '{}' for PRESS virtual datapoint", pressType);
112             }
113         } else {
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);
123             } else {
124                 vdp.setValue(null);
125             }
126         }
127         logger.debug("Handled virtual button event on {}:{}: press type {}, value {}, button state {} -> {}",
128                 channel.getDevice().getAddress(), channel.getNumber(), pressType, dp.getValue(), vdp.getPreviousValue(),
129                 vdp.getValue());
130     }
131 }