]> git.basschouten.com Git - openhab-addons.git/commitdiff
[enocean] Add second action for two rocker switches (Fixes #9750) (#10769)
authorCountBigBang <dietmar.franzen@t-online.de>
Tue, 12 Oct 2021 19:01:57 +0000 (21:01 +0200)
committerGitHub <noreply@github.com>
Tue, 12 Oct 2021 19:01:57 +0000 (21:01 +0200)
 * Added new channel:
   * secondActionPressed: Indicates if second action of rocker switch is pressed too
* Added two new profiles for channel rockerSwitchAction:
   * rockerswitchaction-toggle-switch
   * rockerswitchaction-toggle-player
* EnOceanSensorHandler can now handle extensible channels too
* EEP F6-02 refactoring

Also-by: Dietmar Franzen <dfranzen@fb3.fra-uas.de>
Signed-off-by: Daniel Weber <uni@fruggy.de>
17 files changed:
bundles/org.openhab.binding.enocean/README.md
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRockerSwitchListenerConfig.java
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanProfileRockerSwitchActionConfig.java [new file with mode: 0644]
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02.java [new file with mode: 0644]
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/EnOceanProfileFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/EnOceanProfiles.java [new file with mode: 0644]
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/RockerSwitchActionBaseProfile.java [new file with mode: 0644]
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/RockerSwitchActionTogglePlayerProfile.java [new file with mode: 0644]
bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/RockerSwitchActionToggleSwitchProfile.java [new file with mode: 0644]
bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/config/config.xml
bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/RockerSwitch.xml
bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/channels.xml

index 4f7e407c33478bac06452655684af185fc3aaa1b..693610264d5df283515677b401493984734769eb 100644 (file)
@@ -76,7 +76,7 @@ Hence if your device supports one of the following EEPs the chances are good tha
 |---------------------------------|-------------|---------------|------------------------------|--------------------------------|-----------|
 | bridge                          | -           | -             | repeaterMode, setBaseId      | USB300, EnOceanPi              | -         |
 | pushButton                      | F6-01/D2-03 | 0x01/0x0A     | pushButton, doublePress,<br/>longPress, batteryLevel | NodOn soft button | Manually/Discovery  |
-| rockerSwitch                    | F6-02       | 0x01-02       | rockerswitchA, rockerswitchB | Eltako FT55                    | Discovery |
+| rockerSwitch                    | F6-02       | 0x01-02       | rockerswitchA, rockerswitchB,<br/>rockerSwitchAction | Eltako FT55                    | Discovery |
 | mechanicalHandle                | F6-10       | 0x00-01       | windowHandleState, contact   | Hoppe SecuSignal handles, Eltako TF-FGB | Discovery |
 | contact                         | D5-00       | 0x01          | contact                      | Eltako FTK(E) & TF-FKB            | Discovery |
 | temperatureSensor               | A5-02       | 0x01-30       | temperature                  | Thermokon SR65                 | Discovery |
@@ -102,6 +102,9 @@ Hence if your device supports one of the following EEPs the chances are good tha
 Furthermore following supporting EEP family is available too: A5-11, types 0x03 (rollershutter position status), 0x04 (extended light status) and D0-06 (battery level indication).
 
 A `rockerSwitch` is used to receive messages from a physical EnOcean Rocker Switch.
+Channel `rockerswitchA` and `rockerswitchB` just react if corresponding rocker switch channel is pressed as single action.
+These channels do not emit an event if ChannelA and ChannelB are pressed simultaneously.
+To handle simultaneously pressed channels you have to use the `rockerSwitchAction` channel.
 A `classicDevice` is used for older EnOcean devices which react only on rocker switch messages (like Opus GN-A-R12V-SR-4).
 As these devices do not send their current status, you have to add additional listener channels for each physical Rocker Switch to your thing.
 In this way you can still sync your item status with the physical status of your device whenever it gets modified by a physical rocker switch.
@@ -258,6 +261,7 @@ The channels of a thing are determined automatically based on the chosen EEP.
 | doublePress         | Trigger            | Channel type system:rawbutton, emits PRESSED |
 | longPress           | Trigger            | Channel type system:rawbutton, emits PRESSED and RELEASED events |
 | rockerswitchA/B     | Trigger            | Channel type system:rawrocker, emits DIR1_PRESSED, DIR1_RELEASED, DIR2_PRESSED, DIR2_RELEASED events |
+| rockerSwitchAction  | Trigger            | Emits combined rocker switch actions for channel A and B and RELEASED events |
 | windowHandleState   | String             | Textual representation of handle position (OPEN, CLOSED, TILTED) |
 | windowSashState     | String             | Textual representation of sash position (OPEN, CLOSED, TILTED) |
 | windowCalibrationState | String             | Textual representation of the calibration state (OK, ERROR, INVALID) |
@@ -372,6 +376,14 @@ then
 end
 ```
 
+If you also want to react to simultaneously pressed channels you have to use the `rockerSwitchAction` channel.
+This channel emits events in the following form "DirectionChannelA|DirectionChannelB" (for example "Dir1|Dir2").
+If a channel is not pressed a "-" is emitted.
+To bind this channel to an item you have to use the `rockerswitchaction-toggle-switch` or the `rockerswitchaction-toggle-player` profile.
+To define for which button press combination the linked item should toggle you have to set the configuration parameters `channelAFilter` and `channelBFilter` accordingly.
+The options for these parameters are "*" (any direction), "Dir1", "Dir2", "-" (corresponding channel not pressed at all).
+An example can be found below.
+
 ## Example
 
 ```xtend
@@ -399,6 +411,7 @@ Bridge enocean:bridge:gtwy "EnOcean Gateway" [ path="/dev/ttyAMA0" ] {
 
 ```xtend
 Player Kitchen_Sonos "Sonos" (Kitchen) {channel="sonos:PLAY1:ID:control", channel="enocean:rockerSwitch:gtwy:rs01:rockerswitchA" [profile="system:rawrocker-to-play-pause"]}
+Switch Light_Switch { channel="enocean:rockerSwitch:gtwy:rs01:rockerSwitchAction" [profile="enocean:rockerswitchaction-toggle-switch", channelAFilter="DIR1", channelBFilter="DIR1"]}
 Dimmer Kitchen_Hue "Hue" <light> {channel="enocean:rockerSwitch:gtwy:rs01:rockerswitchB" [profile="system:rawrocker-to-dimmer"], channel="hue:0220:0017884f6626:9:brightness"}
 Rollershutter Kitchen_Rollershutter "Roller shutter" <blinds> (Kitchen) {channel="enocean:rollershutter:gtwy:r01:rollershutter", autoupdate="false"}
 Switch Garage_Light "Switch" {
index a6ab12c07a5ff3fc4f4b1e9926ad918c96614e2b..f705dd6274f987a2b03e6b435eb8ba1a6559fe46 100644 (file)
@@ -132,6 +132,9 @@ public class EnOceanBindingConstants {
 
     public static final String CHANNEL_ROCKERSWITCH_CHANNELA = "rockerswitchA";
     public static final String CHANNEL_ROCKERSWITCH_CHANNELB = "rockerswitchB";
+    public static final String CHANNEL_ROCKERSWITCH_ACTION = "rockerSwitchAction";
+    public static final ChannelTypeUID CHANNELTYPE_ROCKERSWITCH_ACTION_UID = new ChannelTypeUID(BINDING_ID,
+            CHANNEL_ROCKERSWITCH_ACTION);
 
     public static final String CHANNEL_VIRTUALSWITCHA = "virtualSwitchA";
     public static final String CHANNEL_VIRTUALROLLERSHUTTERA = "virtualRollershutterA";
@@ -357,6 +360,9 @@ public class EnOceanBindingConstants {
             Map.entry(CHANNEL_ROCKERSWITCH_CHANNELB,
                     new EnOceanChannelDescription(DefaultSystemChannelTypeProvider.SYSTEM_RAWROCKER.getUID(), null,
                             "Rocker Switch - Channel B", false, false)),
+            Map.entry(CHANNEL_ROCKERSWITCH_ACTION,
+                    new EnOceanChannelDescription(CHANNELTYPE_ROCKERSWITCH_ACTION_UID, null, "Rocker Switch Action",
+                            false, false)),
 
             Map.entry(CHANNEL_VIRTUALSWITCHA,
                     new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_VIRTUALSWITCHA),
index 0212060b04157a29113e10d6bd681dcddaeebcd6..8e2047ff427a86b749cc0f5f7d74e0545c934980 100644 (file)
@@ -19,9 +19,11 @@ package org.openhab.binding.enocean.internal.config;
 public class EnOceanChannelRockerSwitchListenerConfig extends EnOceanChannelRockerSwitchConfigBase {
 
     public String enoceanId;
+    public boolean handleSecondAction;
 
     public EnOceanChannelRockerSwitchListenerConfig() {
         super();
         enoceanId = null;
+        handleSecondAction = false;
     }
 }
diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanProfileRockerSwitchActionConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanProfileRockerSwitchActionConfig.java
new file mode 100644 (file)
index 0000000..92747dd
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enocean.internal.config;
+
+/**
+ * This {@link EnOceanProfileRockerSwitchActionConfig} config class is used for rockerSwitchAction profiles to define in
+ * which case it should react.
+ *
+ * @author Daniel Weber - Initial contribution
+ */
+public class EnOceanProfileRockerSwitchActionConfig {
+
+    public String channelAFilter;
+    public String channelBFilter;
+
+    public EnOceanProfileRockerSwitchActionConfig() {
+        channelAFilter = "*";
+        channelBFilter = "*";
+    }
+}
index f7cb5623df469e248ff642aa0b2eabf8a550ff9f..e212b8f5102bf447b5df54a05b52e84a65e18940 100644 (file)
@@ -179,13 +179,14 @@ public enum EEPType {
             CHANNEL_DOUBLEPRESS, CHANNEL_LONGPRESS, CHANNEL_BATTERY_LEVEL),
 
     RockerSwitch2RockerStyle1(RORG.RPS, 0x02, 0x01, false, F6_02_01.class, THING_TYPE_ROCKERSWITCH,
-            CHANNEL_ROCKERSWITCH_CHANNELA, CHANNEL_ROCKERSWITCH_CHANNELB, CHANNEL_VIRTUALSWITCHA,
-            CHANNEL_VIRTUALROLLERSHUTTERA, CHANNEL_VIRTUALROCKERSWITCHB, CHANNEL_ROCKERSWITCHLISTENERSWITCH,
-            CHANNEL_ROCKERSWITCHLISTENERROLLERSHUTTER),
+            CHANNEL_ROCKERSWITCH_CHANNELA, CHANNEL_ROCKERSWITCH_CHANNELB, CHANNEL_ROCKERSWITCH_ACTION,
+            CHANNEL_VIRTUALSWITCHA, CHANNEL_VIRTUALROLLERSHUTTERA, CHANNEL_VIRTUALROCKERSWITCHB,
+            CHANNEL_ROCKERSWITCHLISTENERSWITCH, CHANNEL_ROCKERSWITCHLISTENERROLLERSHUTTER),
+
     RockerSwitch2RockerStyle2(RORG.RPS, 0x02, 0x02, false, F6_02_02.class, THING_TYPE_ROCKERSWITCH,
-            CHANNEL_ROCKERSWITCH_CHANNELA, CHANNEL_ROCKERSWITCH_CHANNELB, CHANNEL_VIRTUALSWITCHA,
-            CHANNEL_VIRTUALROLLERSHUTTERA, CHANNEL_VIRTUALROCKERSWITCHB, CHANNEL_ROCKERSWITCHLISTENERSWITCH,
-            CHANNEL_ROCKERSWITCHLISTENERROLLERSHUTTER),
+            CHANNEL_ROCKERSWITCH_CHANNELA, CHANNEL_ROCKERSWITCH_CHANNELB, CHANNEL_ROCKERSWITCH_ACTION,
+            CHANNEL_VIRTUALSWITCHA, CHANNEL_VIRTUALROLLERSHUTTERA, CHANNEL_VIRTUALROCKERSWITCHB,
+            CHANNEL_ROCKERSWITCHLISTENERSWITCH, CHANNEL_ROCKERSWITCHLISTENERROLLERSHUTTER),
 
     MechanicalHandle00(RORG.RPS, 0x10, 0x00, false, F6_10_00.class, THING_TYPE_MECHANICALHANDLE,
             CHANNEL_WINDOWHANDLESTATE, CHANNEL_CONTACT),
diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02.java
new file mode 100644 (file)
index 0000000..cab0535
--- /dev/null
@@ -0,0 +1,178 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enocean.internal.eep.F6_02;
+
+import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
+
+import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchConfigBase.SwitchMode;
+import org.openhab.binding.enocean.internal.eep.Base._RPSMessage;
+import org.openhab.binding.enocean.internal.messages.ERP1Message;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.UpDownType;
+import org.openhab.core.thing.CommonTriggerEvents;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+
+/**
+ *
+ * @author Daniel Weber - Initial contribution
+ */
+public abstract class F6_02 extends _RPSMessage {
+
+    final byte AI = 0;
+    final byte A0 = 1;
+    final byte BI = 2;
+    final byte B0 = 3;
+    final byte PRESSED = 16;
+    final byte PRESSED_SEC = 1;
+
+    final String DIR1 = "DIR1";
+    final String DIR2 = "DIR2";
+    final String NODIR = "-";
+
+    int secondByte = -1;
+    int secondStatus = -1;
+
+    public F6_02() {
+        super();
+    }
+
+    public F6_02(ERP1Message packet) {
+        super(packet);
+    }
+
+    private String getChannelADir() {
+        if ((bytes[0] >>> 5) == A0 && (bytes[0] & PRESSED) != 0) {
+            return DIR1;
+        } else if ((bytes[0] >>> 5) == AI && (bytes[0] & PRESSED) != 0) {
+            return DIR2;
+        } else {
+            return NODIR;
+        }
+    }
+
+    private String getChannelBDir() {
+        if ((bytes[0] >>> 5) == B0 && (bytes[0] & PRESSED) != 0) {
+            return DIR1;
+        } else if ((bytes[0] >>> 5) == BI && (bytes[0] & PRESSED) != 0) {
+            return DIR2;
+        } else if (((bytes[0] & 0xf) >>> 1) == B0 && (bytes[0] & PRESSED_SEC) != 0) {
+            return DIR1;
+        } else if (((bytes[0] & 0xf) >>> 1) == BI && (bytes[0] & PRESSED_SEC) != 0) {
+            return DIR2;
+        } else {
+            return NODIR;
+        }
+    }
+
+    protected String getRockerSwitchAction(Configuration config) {
+        String dirA = getChannelADir();
+        String dirB = getChannelBDir();
+
+        return dirA + "|" + dirB;
+    }
+
+    protected String getChannelEvent(byte dir1, byte dir2) {
+        if ((bytes[0] & PRESSED_SEC) != 0) {
+            // Do not emit an event if channelA is pressed together with channelB as it is undetermined which one gets
+            // fired first
+            return null;
+        } else if ((bytes[0] >>> 5) == dir1) {
+            return ((bytes[0] & PRESSED) != 0) ? CommonTriggerEvents.DIR1_PRESSED : CommonTriggerEvents.DIR1_RELEASED;
+        } else if ((bytes[0] >>> 5) == dir2) {
+            return ((bytes[0] & PRESSED) != 0) ? CommonTriggerEvents.DIR2_PRESSED : CommonTriggerEvents.DIR2_RELEASED;
+        } else {
+            return null;
+        }
+    }
+
+    protected State getState(byte dir1, byte dir2, boolean handleSecondAction, SwitchMode switchMode,
+            String channelTypeId, State currentState) {
+        // We are just listening on the pressed event here
+        switch (switchMode) {
+            case RockerSwitch:
+                if ((bytes[0] >>> 5) == dir1) {
+                    if (((bytes[0] & PRESSED) != 0)) {
+                        return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH) ? OnOffType.ON : UpDownType.UP;
+                    }
+                } else if ((bytes[0] >>> 5) == dir2) {
+                    if (((bytes[0] & PRESSED) != 0)) {
+                        return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH) ? OnOffType.OFF
+                                : UpDownType.DOWN;
+                    }
+                } else if (handleSecondAction && ((bytes[0] & 0xf) >>> 1) == dir1) {
+                    if (((bytes[0] & PRESSED_SEC) != 0)) {
+                        return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH) ? OnOffType.ON : UpDownType.UP;
+                    }
+                } else if (handleSecondAction && ((bytes[0] & 0xf) >>> 1) == dir2) {
+                    if (((bytes[0] & PRESSED_SEC) != 0)) {
+                        return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH) ? OnOffType.OFF
+                                : UpDownType.DOWN;
+                    }
+                }
+                break;
+            case ToggleDir1:
+                if ((bytes[0] >>> 5) == dir1) {
+                    if (((bytes[0] & PRESSED) != 0)) {
+                        return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH)
+                                ? (currentState == UnDefType.UNDEF ? OnOffType.ON : inverse((OnOffType) currentState))
+                                : (currentState == UnDefType.UNDEF ? UpDownType.UP
+                                        : inverse((UpDownType) currentState));
+                    }
+                } else if (handleSecondAction && ((bytes[0] & 0xf) >>> 1) == dir1) {
+                    if (((bytes[0] & PRESSED_SEC) != 0)) {
+                        return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH)
+                                ? (currentState == UnDefType.UNDEF ? OnOffType.ON : inverse((OnOffType) currentState))
+                                : (currentState == UnDefType.UNDEF ? UpDownType.UP
+                                        : inverse((UpDownType) currentState));
+                    }
+                }
+                break;
+            case ToggleDir2:
+                if ((bytes[0] >>> 5) == dir2) {
+                    if (((bytes[0] & PRESSED) != 0)) {
+                        return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH)
+                                ? (currentState == UnDefType.UNDEF ? OnOffType.ON : inverse((OnOffType) currentState))
+                                : (currentState == UnDefType.UNDEF ? UpDownType.UP
+                                        : inverse((UpDownType) currentState));
+                    }
+                } else if (handleSecondAction && ((bytes[0] & 0xf) >>> 1) == dir2) {
+                    if (((bytes[0] & PRESSED_SEC) != 0)) {
+                        return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH)
+                                ? (currentState == UnDefType.UNDEF ? OnOffType.ON : inverse((OnOffType) currentState))
+                                : (currentState == UnDefType.UNDEF ? UpDownType.UP
+                                        : inverse((UpDownType) currentState));
+                    }
+                }
+                break;
+            default:
+                break;
+        }
+
+        return UnDefType.UNDEF;
+    }
+
+    protected State inverse(OnOffType currentState) {
+        return currentState == OnOffType.ON ? OnOffType.OFF : OnOffType.ON;
+    }
+
+    protected State inverse(UpDownType currentState) {
+        return currentState == UpDownType.UP ? UpDownType.DOWN : UpDownType.UP;
+    }
+
+    @Override
+    protected boolean validateData(byte[] bytes) {
+        return super.validateData(bytes) && !getBit(bytes[0], 7);
+    }
+}
index 8152c5e90a30ab10b72656f97b8c5571319de2b4..f14d3edc70b3c4ae199d375cb0dd5bdbd12ca87f 100644 (file)
@@ -17,13 +17,11 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
 import java.util.function.Function;
 
 import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchConfigBase.Channel;
-import org.openhab.binding.enocean.internal.config.EnOceanChannelVirtualRockerSwitchConfig;
+import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchListenerConfig;
 import org.openhab.binding.enocean.internal.eep.Base._RPSMessage;
 import org.openhab.binding.enocean.internal.messages.ERP1Message;
 import org.openhab.core.config.core.Configuration;
-import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.StringType;
-import org.openhab.core.library.types.UpDownType;
 import org.openhab.core.thing.CommonTriggerEvents;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.State;
@@ -33,16 +31,7 @@ import org.openhab.core.types.UnDefType;
  *
  * @author Daniel Weber - Initial contribution
  */
-public class F6_02_01 extends _RPSMessage {
-
-    final byte AI = 0;
-    final byte A0 = 1;
-    final byte BI = 2;
-    final byte B0 = 3;
-    final byte PRESSED = 16;
-
-    int secondByte = -1;
-    int secondStatus = -1;
+public class F6_02_01 extends F6_02 {
 
     public F6_02_01() {
         super();
@@ -57,21 +46,23 @@ public class F6_02_01 extends _RPSMessage {
             Configuration config) {
 
         if (t21 && nu) {
-            byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0;
-            byte dir2 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI;
-
-            if ((bytes[0] >>> 5) == dir1) {
-                return ((bytes[0] & PRESSED) != 0) ? CommonTriggerEvents.DIR1_PRESSED
-                        : CommonTriggerEvents.DIR1_RELEASED;
-            } else if ((bytes[0] >>> 5) == dir2) {
-                return ((bytes[0] & PRESSED) != 0) ? CommonTriggerEvents.DIR2_PRESSED
-                        : CommonTriggerEvents.DIR2_RELEASED;
+            if (CHANNEL_ROCKERSWITCH_ACTION.equals(channelTypeId)) {
+                return getRockerSwitchAction(config);
+            } else {
+                byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0;
+                byte dir2 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI;
+
+                return getChannelEvent(dir1, dir2);
             }
         } else if (t21 && !nu) {
-            if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR1_PRESSED)) {
-                return CommonTriggerEvents.DIR1_RELEASED;
-            } else if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR2_PRESSED)) {
-                return CommonTriggerEvents.DIR2_RELEASED;
+            if (CHANNEL_ROCKERSWITCH_ACTION.equals(channelTypeId)) {
+                return CommonTriggerEvents.RELEASED;
+            } else {
+                if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR1_PRESSED)) {
+                    return CommonTriggerEvents.DIR1_RELEASED;
+                } else if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR2_PRESSED)) {
+                    return CommonTriggerEvents.DIR2_RELEASED;
+                }
             }
         }
 
@@ -82,7 +73,7 @@ public class F6_02_01 extends _RPSMessage {
     protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command,
             Function<String, State> getCurrentStateFunc, Configuration config) {
         if (command instanceof StringType) {
-            StringType s = (StringType) command;
+            String s = ((StringType) command).toString();
 
             if (s.equals(CommonTriggerEvents.DIR1_RELEASED) || s.equals(CommonTriggerEvents.DIR2_RELEASED)) {
                 setStatus(_RPSMessage.T21Flag);
@@ -106,72 +97,20 @@ public class F6_02_01 extends _RPSMessage {
     @Override
     protected State convertToStateImpl(String channelId, String channelTypeId,
             Function<String, State> getCurrentStateFunc, Configuration config) {
-        // this method is used by the classic device listener channels to convert an rocker switch message into an
+        // this method is used by the classic device listener channels to convert a rocker switch message into an
         // appropriate item update
         State currentState = getCurrentStateFunc.apply(channelId);
         if (t21 && nu) {
-            EnOceanChannelVirtualRockerSwitchConfig c = config.as(EnOceanChannelVirtualRockerSwitchConfig.class);
+            EnOceanChannelRockerSwitchListenerConfig c = config.as(EnOceanChannelRockerSwitchListenerConfig.class);
             byte dir1 = c.getChannel() == Channel.ChannelA ? A0 : B0;
             byte dir2 = c.getChannel() == Channel.ChannelA ? AI : BI;
 
-            // We are just listening on the pressed event here
-            switch (c.getSwitchMode()) {
-                case RockerSwitch:
-                    if ((bytes[0] >>> 5) == dir1) {
-                        if (((bytes[0] & PRESSED) != 0)) {
-                            return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH) ? OnOffType.ON
-                                    : UpDownType.UP;
-                        }
-                    } else if ((bytes[0] >>> 5) == dir2) {
-                        if (((bytes[0] & PRESSED) != 0)) {
-                            return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH) ? OnOffType.OFF
-                                    : UpDownType.DOWN;
-                        }
-                    }
-                    break;
-                case ToggleDir1:
-                    if ((bytes[0] >>> 5) == dir1) {
-                        if (((bytes[0] & PRESSED) != 0)) {
-                            return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH)
-                                    ? (currentState == UnDefType.UNDEF ? OnOffType.ON
-                                            : inverse((OnOffType) currentState))
-                                    : (currentState == UnDefType.UNDEF ? UpDownType.UP
-                                            : inverse((UpDownType) currentState));
-                        }
-                    }
-                    break;
-                case ToggleDir2:
-                    if ((bytes[0] >>> 5) == dir2) {
-                        if (((bytes[0] & PRESSED) != 0)) {
-                            return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH)
-                                    ? (currentState == UnDefType.UNDEF ? OnOffType.ON
-                                            : inverse((OnOffType) currentState))
-                                    : (currentState == UnDefType.UNDEF ? UpDownType.UP
-                                            : inverse((UpDownType) currentState));
-                        }
-                    }
-                    break;
-                default:
-                    break;
-            }
+            return getState(dir1, dir2, c.handleSecondAction, c.getSwitchMode(), channelTypeId, currentState);
         }
 
         return UnDefType.UNDEF;
     }
 
-    private State inverse(OnOffType currentState) {
-        return currentState == OnOffType.ON ? OnOffType.OFF : OnOffType.ON;
-    }
-
-    private State inverse(UpDownType currentState) {
-        return currentState == UpDownType.UP ? UpDownType.DOWN : UpDownType.UP;
-    }
-
-    @Override
-    protected boolean validateData(byte[] bytes) {
-        return super.validateData(bytes) && !getBit(bytes[0], 7);
-    }
-
     @Override
     public boolean isValidForTeachIn() {
         if (t21) {
index 76e169785171af219d194de8dd6b7d12601c8a8b..b3f41dbb38bc6d17ad86518dec04cbb721c8bdc4 100644 (file)
@@ -17,13 +17,11 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
 import java.util.function.Function;
 
 import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchConfigBase.Channel;
-import org.openhab.binding.enocean.internal.config.EnOceanChannelVirtualRockerSwitchConfig;
+import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchListenerConfig;
 import org.openhab.binding.enocean.internal.eep.Base._RPSMessage;
 import org.openhab.binding.enocean.internal.messages.ERP1Message;
 import org.openhab.core.config.core.Configuration;
-import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.StringType;
-import org.openhab.core.library.types.UpDownType;
 import org.openhab.core.thing.CommonTriggerEvents;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.State;
@@ -33,13 +31,7 @@ import org.openhab.core.types.UnDefType;
  *
  * @author Daniel Weber - Initial contribution
  */
-public class F6_02_02 extends _RPSMessage {
-
-    final byte AI = 0;
-    final byte A0 = 1;
-    final byte BI = 2;
-    final byte B0 = 3;
-    final byte PRESSED = 16;
+public class F6_02_02 extends F6_02 {
 
     public F6_02_02() {
         super();
@@ -54,21 +46,22 @@ public class F6_02_02 extends _RPSMessage {
             Configuration config) {
 
         if (t21 && nu) {
-            byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI;
-            byte dir2 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0;
-
-            if ((bytes[0] >>> 5) == dir1) {
-                return ((bytes[0] & PRESSED) != 0) ? CommonTriggerEvents.DIR1_PRESSED
-                        : CommonTriggerEvents.DIR1_RELEASED;
-            } else if ((bytes[0] >>> 5) == dir2) {
-                return ((bytes[0] & PRESSED) != 0) ? CommonTriggerEvents.DIR2_PRESSED
-                        : CommonTriggerEvents.DIR2_RELEASED;
+            if (CHANNEL_ROCKERSWITCH_ACTION.equals(channelTypeId)) {
+                return getRockerSwitchAction(config);
+            } else {
+                byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI;
+                byte dir2 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0;
+                return getChannelEvent(dir1, dir2);
             }
         } else if (t21 && !nu) {
-            if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR1_PRESSED)) {
-                return CommonTriggerEvents.DIR1_RELEASED;
-            } else if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR2_PRESSED)) {
-                return CommonTriggerEvents.DIR2_RELEASED;
+            if (CHANNEL_ROCKERSWITCH_ACTION.equals(channelTypeId)) {
+                return CommonTriggerEvents.RELEASED;
+            } else {
+                if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR1_PRESSED)) {
+                    return CommonTriggerEvents.DIR1_RELEASED;
+                } else if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR2_PRESSED)) {
+                    return CommonTriggerEvents.DIR2_RELEASED;
+                }
             }
         }
 
@@ -79,7 +72,7 @@ public class F6_02_02 extends _RPSMessage {
     protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command,
             Function<String, State> getCurrentStateFunc, Configuration config) {
         if (command instanceof StringType) {
-            StringType s = (StringType) command;
+            String s = ((StringType) command).toString();
 
             if (s.equals(CommonTriggerEvents.DIR1_RELEASED) || s.equals(CommonTriggerEvents.DIR2_RELEASED)) {
                 setStatus(_RPSMessage.T21Flag);
@@ -103,67 +96,20 @@ public class F6_02_02 extends _RPSMessage {
     @Override
     protected State convertToStateImpl(String channelId, String channelTypeId,
             Function<String, State> getCurrentStateFunc, Configuration config) {
-        // this method is used by the classic device listener channels to convert an rocker switch message into an
+        // this method is used by the classic device listener channels to convert a rocker switch message into an
         // appropriate item update
         State currentState = getCurrentStateFunc.apply(channelId);
         if (t21 && nu) {
-            EnOceanChannelVirtualRockerSwitchConfig c = config.as(EnOceanChannelVirtualRockerSwitchConfig.class);
+            EnOceanChannelRockerSwitchListenerConfig c = config.as(EnOceanChannelRockerSwitchListenerConfig.class);
             byte dir1 = c.getChannel() == Channel.ChannelA ? AI : BI;
             byte dir2 = c.getChannel() == Channel.ChannelA ? A0 : B0;
 
-            // We are just listening on the pressed event here
-            switch (c.getSwitchMode()) {
-                case RockerSwitch:
-                    if ((bytes[0] >>> 5) == dir1) {
-                        if (((bytes[0] & PRESSED) != 0)) {
-                            return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH) ? OnOffType.ON
-                                    : UpDownType.UP;
-                        }
-                    } else if ((bytes[0] >>> 5) == dir2) {
-                        if (((bytes[0] & PRESSED) != 0)) {
-                            return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH) ? OnOffType.OFF
-                                    : UpDownType.DOWN;
-                        }
-                    }
-                    break;
-                case ToggleDir1:
-                    if ((bytes[0] >>> 5) == dir1) {
-                        if (((bytes[0] & PRESSED) != 0)) {
-                            return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH)
-                                    ? (currentState == UnDefType.UNDEF ? OnOffType.ON
-                                            : inverse((OnOffType) currentState))
-                                    : (currentState == UnDefType.UNDEF ? UpDownType.UP
-                                            : inverse((UpDownType) currentState));
-                        }
-                    }
-                    break;
-                case ToggleDir2:
-                    if ((bytes[0] >>> 5) == dir2) {
-                        if (((bytes[0] & PRESSED) != 0)) {
-                            return channelTypeId.equals(CHANNEL_ROCKERSWITCHLISTENERSWITCH)
-                                    ? (currentState == UnDefType.UNDEF ? OnOffType.ON
-                                            : inverse((OnOffType) currentState))
-                                    : (currentState == UnDefType.UNDEF ? UpDownType.UP
-                                            : inverse((UpDownType) currentState));
-                        }
-                    }
-                    break;
-                default:
-                    break;
-            }
+            return getState(dir1, dir2, c.handleSecondAction, c.getSwitchMode(), channelTypeId, currentState);
         }
 
         return UnDefType.UNDEF;
     }
 
-    private State inverse(OnOffType currentState) {
-        return currentState == OnOffType.ON ? OnOffType.OFF : OnOffType.ON;
-    }
-
-    private State inverse(UpDownType currentState) {
-        return currentState == UpDownType.UP ? UpDownType.DOWN : UpDownType.UP;
-    }
-
     @Override
     public boolean isValidForTeachIn() {
         return false; // Never treat a message as F6-02-02, let user decide which orientation of rocker switch is used
index 66c9c28fa069bda6f3ce11647839ce94e5644e48..dd951afdbec8616a4b8b5e38e916d7cdc0d8eb12 100644 (file)
@@ -129,7 +129,8 @@ public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements
 
     protected Predicate<Channel> channelFilter(EEPType eepType, byte[] senderId) {
         return c -> {
-            boolean result = eepType.GetSupportedChannels().containsKey(c.getUID().getId());
+
+            boolean result = eepType.isChannelSupported(c);
             return (isLinked(c.getUID()) || c.getKind() == ChannelKind.TRIGGER) && result;
         };
     }
diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/EnOceanProfileFactory.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/EnOceanProfileFactory.java
new file mode 100644 (file)
index 0000000..e2c2afb
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enocean.internal.profiles;
+
+import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
+import static org.openhab.binding.enocean.internal.profiles.EnOceanProfiles.*;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.library.CoreItemFactory;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.profiles.Profile;
+import org.openhab.core.thing.profiles.ProfileAdvisor;
+import org.openhab.core.thing.profiles.ProfileCallback;
+import org.openhab.core.thing.profiles.ProfileContext;
+import org.openhab.core.thing.profiles.ProfileFactory;
+import org.openhab.core.thing.profiles.ProfileType;
+import org.openhab.core.thing.profiles.ProfileTypeProvider;
+import org.openhab.core.thing.profiles.ProfileTypeUID;
+import org.openhab.core.thing.type.ChannelType;
+import org.openhab.core.thing.type.ChannelTypeRegistry;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link EnOceanProfileFactory} class creates EnOceanProfiles
+ *
+ * @author Daniel Weber - Initial contribution
+ */
+@Component
+@NonNullByDefault
+public class EnOceanProfileFactory implements ProfileFactory, ProfileAdvisor, ProfileTypeProvider {
+
+    private static final Set<ProfileType> SUPPORTED_PROFILE_TYPES = Set.of(ROCKERSWITCHACTION_TOGGLE_SWITCH_TYPE,
+            ROCKERSWITCHACTION_TOGGLE_PLAYER_TYPE);
+
+    private static final Set<ProfileTypeUID> SUPPORTED_PROFILE_TYPE_UIDS = Set.of(ROCKERSWITCHACTION_TOGGLE_SWITCH,
+            ROCKERSWITCHACTION_TOGGLE_PLAYER);
+
+    private final ChannelTypeRegistry channelTypeRegistry;
+
+    @Activate
+    public EnOceanProfileFactory(final @Reference ChannelTypeRegistry channelTypeRegistry) {
+        this.channelTypeRegistry = channelTypeRegistry;
+    }
+
+    @Override
+    public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
+        return Collections.unmodifiableList(SUPPORTED_PROFILE_TYPES.stream().collect(Collectors.toList()));
+    }
+
+    @Override
+    public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(Channel channel, @Nullable String itemType) {
+        ChannelType channelType = channelTypeRegistry.getChannelType(channel.getChannelTypeUID());
+        if (channelType == null) {
+            return null;
+        }
+
+        return getSuggestedProfileTypeUID(channelType, itemType);
+    }
+
+    @Override
+    public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(ChannelType channelType, @Nullable String itemType) {
+
+        if (CHANNELTYPE_ROCKERSWITCH_ACTION_UID.equals(channelType.getUID())) {
+            if (CoreItemFactory.PLAYER.equalsIgnoreCase(itemType)) {
+                return ROCKERSWITCHACTION_TOGGLE_PLAYER;
+            } else if (CoreItemFactory.SWITCH.equalsIgnoreCase(itemType)) {
+                return ROCKERSWITCHACTION_TOGGLE_SWITCH;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
+            ProfileContext profileContext) {
+        if (ROCKERSWITCHACTION_TOGGLE_PLAYER.equals(profileTypeUID)) {
+            return new RockerSwitchActionTogglePlayerProfile(callback, profileContext);
+        } else if (ROCKERSWITCHACTION_TOGGLE_SWITCH.equals(profileTypeUID)) {
+            return new RockerSwitchActionToggleSwitchProfile(callback, profileContext);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
+        return Collections.unmodifiableList(SUPPORTED_PROFILE_TYPE_UIDS.stream().collect(Collectors.toList()));
+    }
+}
diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/EnOceanProfiles.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/EnOceanProfiles.java
new file mode 100644 (file)
index 0000000..ad5288e
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enocean.internal.profiles;
+
+import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.library.CoreItemFactory;
+import org.openhab.core.thing.profiles.ProfileType;
+import org.openhab.core.thing.profiles.ProfileTypeBuilder;
+import org.openhab.core.thing.profiles.ProfileTypeUID;
+
+/**
+ * The {@link EnOceanProfiles} class defines profile constants
+ *
+ * @author Daniel Weber - Initial contribution
+ */
+@NonNullByDefault
+public class EnOceanProfiles {
+    public static final ProfileTypeUID ROCKERSWITCHACTION_TOGGLE_SWITCH = new ProfileTypeUID(BINDING_ID,
+            "rockerswitchaction-toggle-switch");
+
+    public static final ProfileTypeUID ROCKERSWITCHACTION_TOGGLE_PLAYER = new ProfileTypeUID(BINDING_ID,
+            "rockerswitchaction-toggle-player");
+
+    static final ProfileType ROCKERSWITCHACTION_TOGGLE_SWITCH_TYPE = ProfileTypeBuilder
+            .newTrigger(ROCKERSWITCHACTION_TOGGLE_SWITCH, "Rocker Switch Action Toggle Switch")
+            .withSupportedItemTypes(CoreItemFactory.SWITCH)
+            .withSupportedChannelTypeUIDs(CHANNELTYPE_ROCKERSWITCH_ACTION_UID).build();
+
+    static final ProfileType ROCKERSWITCHACTION_TOGGLE_PLAYER_TYPE = ProfileTypeBuilder
+            .newTrigger(ROCKERSWITCHACTION_TOGGLE_PLAYER, "Rocker Switch Action Toggle Player")
+            .withSupportedItemTypes(CoreItemFactory.PLAYER)
+            .withSupportedChannelTypeUIDs(CHANNELTYPE_ROCKERSWITCH_ACTION_UID).build();
+}
diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/RockerSwitchActionBaseProfile.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/RockerSwitchActionBaseProfile.java
new file mode 100644 (file)
index 0000000..6a0dff5
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enocean.internal.profiles;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.enocean.internal.config.EnOceanProfileRockerSwitchActionConfig;
+import org.openhab.core.thing.profiles.ProfileCallback;
+import org.openhab.core.thing.profiles.ProfileContext;
+import org.openhab.core.thing.profiles.TriggerProfile;
+import org.openhab.core.types.State;
+
+/**
+ * @author Daniel Weber - Initial contribution
+ */
+@NonNullByDefault
+public abstract class RockerSwitchActionBaseProfile implements TriggerProfile {
+
+    protected final ProfileCallback callback;
+    protected ProfileContext context;
+
+    protected @Nullable State previousState;
+
+    final String ANYDIR = "*";
+
+    public RockerSwitchActionBaseProfile(ProfileCallback callback, ProfileContext context) {
+        this.callback = callback;
+        this.context = context;
+    }
+
+    protected boolean isEventValid(String event) {
+        String[] directions = event.split("\\|");
+        if (directions.length != 2) {
+            return false;
+        }
+
+        EnOceanProfileRockerSwitchActionConfig config = context.getConfiguration()
+                .as(EnOceanProfileRockerSwitchActionConfig.class);
+        if (!(config.channelAFilter.equals(ANYDIR) || config.channelAFilter.equals(directions[0]))) {
+            return false;
+        } else if (!(config.channelBFilter.equals(ANYDIR) || config.channelBFilter.equals(directions[1]))) {
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/RockerSwitchActionTogglePlayerProfile.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/RockerSwitchActionTogglePlayerProfile.java
new file mode 100644 (file)
index 0000000..d321302
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enocean.internal.profiles;
+
+import static org.openhab.binding.enocean.internal.profiles.EnOceanProfiles.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.library.types.PlayPauseType;
+import org.openhab.core.thing.CommonTriggerEvents;
+import org.openhab.core.thing.profiles.ProfileCallback;
+import org.openhab.core.thing.profiles.ProfileContext;
+import org.openhab.core.thing.profiles.ProfileTypeUID;
+import org.openhab.core.types.State;
+
+/**
+ * The {@link RockerSwitchActionTogglePlayerProfile} is used for channel rockerSwitchAction to be able to bind this
+ * trigger channel directly to a player item
+ * 
+ * @author Daniel Weber - Initial contribution
+ */
+@NonNullByDefault
+public class RockerSwitchActionTogglePlayerProfile extends RockerSwitchActionBaseProfile {
+
+    public RockerSwitchActionTogglePlayerProfile(ProfileCallback callback, ProfileContext context) {
+        super(callback, context);
+    }
+
+    @Override
+    public ProfileTypeUID getProfileTypeUID() {
+        return ROCKERSWITCHACTION_TOGGLE_PLAYER;
+    }
+
+    @Override
+    public void onStateUpdateFromItem(State state) {
+        previousState = state.as(PlayPauseType.class);
+    }
+
+    @Override
+    public void onTriggerFromHandler(String event) {
+        // Ignore released event
+        if (!CommonTriggerEvents.RELEASED.equals(event) && isEventValid(event)) {
+            PlayPauseType newState = PlayPauseType.PLAY.equals(previousState) ? PlayPauseType.PAUSE
+                    : PlayPauseType.PLAY;
+            callback.sendCommand(newState);
+            previousState = newState;
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/RockerSwitchActionToggleSwitchProfile.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/profiles/RockerSwitchActionToggleSwitchProfile.java
new file mode 100644 (file)
index 0000000..2dcf477
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.enocean.internal.profiles;
+
+import static org.openhab.binding.enocean.internal.profiles.EnOceanProfiles.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.CommonTriggerEvents;
+import org.openhab.core.thing.profiles.ProfileCallback;
+import org.openhab.core.thing.profiles.ProfileContext;
+import org.openhab.core.thing.profiles.ProfileTypeUID;
+import org.openhab.core.types.State;
+
+/**
+ * The {@link RockerSwitchActionToggleSwitchProfile} is used for channel rockerSwitchAction to be able to bind this
+ * trigger channel directly to a switch item
+ * 
+ * @author Daniel Weber - Initial contribution
+ */
+@NonNullByDefault
+public class RockerSwitchActionToggleSwitchProfile extends RockerSwitchActionBaseProfile {
+
+    public RockerSwitchActionToggleSwitchProfile(ProfileCallback callback, ProfileContext context) {
+        super(callback, context);
+    }
+
+    @Override
+    public ProfileTypeUID getProfileTypeUID() {
+        return ROCKERSWITCHACTION_TOGGLE_SWITCH;
+    }
+
+    @Override
+    public void onStateUpdateFromItem(State state) {
+        previousState = state.as(OnOffType.class);
+    }
+
+    @Override
+    public void onTriggerFromHandler(String event) {
+        // Ignore released event
+        if (!CommonTriggerEvents.RELEASED.equals(event) && isEventValid(event)) {
+            OnOffType newState = OnOffType.ON.equals(previousState) ? OnOffType.OFF : OnOffType.ON;
+            callback.sendCommand(newState);
+            previousState = newState;
+        }
+    }
+}
index 11790121d1481a7dddf8c17742326106d90931b9..c31eb13426bcfc787e0956f1513d570182ca11f8 100644 (file)
                        </options>
                        <default>channelA</default>
                </parameter>
+               <parameter name="handleSecondAction" type="boolean">
+                       <label>Handle Second Action</label>
+                       <description>React if selected channel is pressed as second action too</description>
+                       <default>false</default>
+               </parameter>
                <parameter name="switchMode" type="text">
                        <label>Switch Mode</label>
                        <options>
                </parameter>
        </config-description>
 
+       <config-description uri="profile:enocean:rockerswitchaction-toggle-switch">
+               <parameter name="channelAFilter" type="text">
+                       <label>Channel A Filter</label>
+                       <default>*</default>
+                       <options>
+                               <option value="*">Any Direction</option>
+                               <option value="DIR1">Direction 1</option>
+                               <option value="DIR2">Direction 2</option>
+                               <option value="-">No Direction</option>
+                       </options>
+               </parameter>
+               <parameter name="channelBFilter" type="text">
+                       <label>Channel B Filter</label>
+                       <default>*</default>
+                       <options>
+                               <option value="*">Any Direction</option>
+                               <option value="DIR1">Direction 1</option>
+                               <option value="DIR2">Direction 2</option>
+                               <option value="-">No Direction</option>
+                       </options>
+               </parameter>
+       </config-description>
+
 </config-description:config-descriptions>
index c8652487d8b165e85a0734c5b47881b7c910f764..c17b070eb5909309026d90c2bb7c544226b6bf92 100644 (file)
@@ -20,6 +20,9 @@
                        <channel typeId="system.rawrocker" id="rockerswitchB">
                                <label>Rocker Switch - Channel B</label>
                        </channel>
+                       <channel id="rockerSwitchAction" typeId="rockerSwitchAction">
+                               <label>Rocker Switch Action</label>
+                       </channel>
                </channels>
 
                <config-description>
index ca28de25dd60e82225f9d306214a8d1b81141672..affa8cdcc4944ce3bf70dd3284d82005eb7a6323 100644 (file)
                <state min="0" max="100" pattern="%.1f rpm" readOnly="true"/>
        </channel-type>
 
+       <channel-type id="rockerSwitchAction">
+               <kind>trigger</kind>
+               <label>Rocker Switch Action</label>
+               <description>Is triggered when a certain combination of rockers is pressed or released.</description>
+               <event>
+                       <options>
+                               <option value="DIR1|-"></option>
+                               <option value="DIR2|-"></option>
+                               <option value="DIR1|DIR1"></option>
+                               <option value="DIR1|DIR2"></option>
+                               <option value="DIR2|DIR1"></option>
+                               <option value="DIR2|DIR2"></option>
+                               <option value="-|DIR1"></option>
+                               <option value="-|DIR2"></option>
+                               <option value="RELEASED"></option>
+                       </options>
+               </event>
+       </channel-type>
+
 </thing:thing-descriptions>