Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[enocean] Add second action for two rocker switches #10769

Merged
merged 6 commits into from
Oct 12, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion bundles/org.openhab.binding.enocean/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,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/>secondActionPressed, rockerSwitchAction | Eltako FT55 | Discovery |
fruggy83 marked this conversation as resolved.
Show resolved Hide resolved
| 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 |
Expand All @@ -100,6 +100,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 `rockerswitchA` just react if corresponding rocker switch channel is pressed as single action.
fruggy83 marked this conversation as resolved.
Show resolved Hide resolved
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 and configure it accordingly.
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.
Expand Down Expand Up @@ -254,6 +257,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) |
| contact | Contact | State OPEN/CLOSED (tilted handle => OPEN) |
| temperature | Number:Temperature | Temperature in degree Celsius |
Expand Down Expand Up @@ -336,6 +340,8 @@ Some channels can be configured with parameters.
| totalusage | validateValue | Filter out increases more than 10.0 kWh and decreases less than 1.0 kWh | true / false |
| | tariff | Tariff info or measurement channel to listen to | 0-15 |
| contact | inverted | Swap OPEN / CLOSED. Set True for Eltako FPE-2. | true / false. Defaults to false. |
| rockerSwitchAction | channelAFilter | Defines for which channel A events this trigger should fire | *: Any Direction, DIR1, DIR2, -:No Direction. Defaults to * |
| rockerSwitchAction | channelBFilter | Defines for which channel B events this trigger should fire | *: Any Direction, DIR1, DIR2, -:No Direction. Defaults to * |

Possible declaration in Thing DSL:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,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";
Expand Down Expand Up @@ -293,8 +296,7 @@ public class EnOceanBindingConstants {
Map.entry(CHANNEL_INDOORAIRANALYSIS,
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_INDOORAIRANALYSIS),
CoreItemFactory.STRING)),
Map.entry(
CHANNEL_SETPOINT,
Map.entry(CHANNEL_SETPOINT,
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_SETPOINT),
CoreItemFactory.NUMBER)),
Map.entry(CHANNEL_CONTACT,
Expand Down Expand Up @@ -338,6 +340,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),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* 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;

/**
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a short line of Javadoc here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The javadoc has to go above the author tag (see other classes as a reference).

* @author Daniel Weber - Initial contribution
* This config class is used for channel rockerSwitchAction to define in which case it triggers.
*/
public class EnOceanChannelRockerSwitchActionConfig {

public String channelAFilter;
public String channelBFilter;

public EnOceanChannelRockerSwitchActionConfig() {
channelAFilter = "*";
channelBFilter = "*";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
public class EnOceanChannelRockerSwitchListenerConfig extends EnOceanChannelRockerSwitchConfigBase {

public String enoceanId;
public boolean handleSecondAction;

public EnOceanChannelRockerSwitchListenerConfig() {
super();
enoceanId = null;
handleSecondAction = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,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),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/**
* 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.EnOceanChannelRockerSwitchActionConfig;
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 ANYDIR = "*";
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 {
return NODIR;
}
}

protected String getRockerSwitchAction(Configuration config) {
EnOceanChannelRockerSwitchActionConfig conf = config.as(EnOceanChannelRockerSwitchActionConfig.class);
String dirA = getChannelADir();
String dirB = getChannelBDir();

if (!(conf.channelAFilter.equals(ANYDIR) || conf.channelAFilter.equals(dirA))) {
return null;
} else if (!(conf.channelBFilter.equals(ANYDIR) || conf.channelBFilter.equals(dirB))) {
return null;
} else {
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);
}
}
Loading