* 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>
This commit is contained in:
parent
cc92423d90
commit
4b13a26f15
@ -76,7 +76,7 @@ Hence if your device supports one of the following EEPs the chances are good tha
|
|||||||
|---------------------------------|-------------|---------------|------------------------------|--------------------------------|-----------|
|
|---------------------------------|-------------|---------------|------------------------------|--------------------------------|-----------|
|
||||||
| bridge | - | - | repeaterMode, setBaseId | USB300, EnOceanPi | - |
|
| bridge | - | - | repeaterMode, setBaseId | USB300, EnOceanPi | - |
|
||||||
| pushButton | F6-01/D2-03 | 0x01/0x0A | pushButton, doublePress,<br/>longPress, batteryLevel | NodOn soft button | Manually/Discovery |
|
| 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 |
|
| 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 |
|
| contact | D5-00 | 0x01 | contact | Eltako FTK(E) & TF-FKB | Discovery |
|
||||||
| temperatureSensor | A5-02 | 0x01-30 | temperature | Thermokon SR65 | 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).
|
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.
|
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).
|
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.
|
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.
|
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 |
|
| doublePress | Trigger | Channel type system:rawbutton, emits PRESSED |
|
||||||
| longPress | Trigger | Channel type system:rawbutton, emits PRESSED and RELEASED events |
|
| 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 |
|
| 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) |
|
| windowHandleState | String | Textual representation of handle position (OPEN, CLOSED, TILTED) |
|
||||||
| windowSashState | String | Textual representation of sash 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) |
|
| windowCalibrationState | String | Textual representation of the calibration state (OK, ERROR, INVALID) |
|
||||||
@ -372,6 +376,14 @@ then
|
|||||||
end
|
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
|
## Example
|
||||||
|
|
||||||
```xtend
|
```xtend
|
||||||
@ -399,6 +411,7 @@ Bridge enocean:bridge:gtwy "EnOcean Gateway" [ path="/dev/ttyAMA0" ] {
|
|||||||
|
|
||||||
```xtend
|
```xtend
|
||||||
Player Kitchen_Sonos "Sonos" (Kitchen) {channel="sonos:PLAY1:ID:control", channel="enocean:rockerSwitch:gtwy:rs01:rockerswitchA" [profile="system:rawrocker-to-play-pause"]}
|
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"}
|
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"}
|
Rollershutter Kitchen_Rollershutter "Roller shutter" <blinds> (Kitchen) {channel="enocean:rollershutter:gtwy:r01:rollershutter", autoupdate="false"}
|
||||||
Switch Garage_Light "Switch" {
|
Switch Garage_Light "Switch" {
|
||||||
|
|||||||
@ -132,6 +132,9 @@ public class EnOceanBindingConstants {
|
|||||||
|
|
||||||
public static final String CHANNEL_ROCKERSWITCH_CHANNELA = "rockerswitchA";
|
public static final String CHANNEL_ROCKERSWITCH_CHANNELA = "rockerswitchA";
|
||||||
public static final String CHANNEL_ROCKERSWITCH_CHANNELB = "rockerswitchB";
|
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_VIRTUALSWITCHA = "virtualSwitchA";
|
||||||
public static final String CHANNEL_VIRTUALROLLERSHUTTERA = "virtualRollershutterA";
|
public static final String CHANNEL_VIRTUALROLLERSHUTTERA = "virtualRollershutterA";
|
||||||
@ -357,6 +360,9 @@ public class EnOceanBindingConstants {
|
|||||||
Map.entry(CHANNEL_ROCKERSWITCH_CHANNELB,
|
Map.entry(CHANNEL_ROCKERSWITCH_CHANNELB,
|
||||||
new EnOceanChannelDescription(DefaultSystemChannelTypeProvider.SYSTEM_RAWROCKER.getUID(), null,
|
new EnOceanChannelDescription(DefaultSystemChannelTypeProvider.SYSTEM_RAWROCKER.getUID(), null,
|
||||||
"Rocker Switch - Channel B", false, false)),
|
"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,
|
Map.entry(CHANNEL_VIRTUALSWITCHA,
|
||||||
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_VIRTUALSWITCHA),
|
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_VIRTUALSWITCHA),
|
||||||
|
|||||||
@ -19,9 +19,11 @@ package org.openhab.binding.enocean.internal.config;
|
|||||||
public class EnOceanChannelRockerSwitchListenerConfig extends EnOceanChannelRockerSwitchConfigBase {
|
public class EnOceanChannelRockerSwitchListenerConfig extends EnOceanChannelRockerSwitchConfigBase {
|
||||||
|
|
||||||
public String enoceanId;
|
public String enoceanId;
|
||||||
|
public boolean handleSecondAction;
|
||||||
|
|
||||||
public EnOceanChannelRockerSwitchListenerConfig() {
|
public EnOceanChannelRockerSwitchListenerConfig() {
|
||||||
super();
|
super();
|
||||||
enoceanId = null;
|
enoceanId = null;
|
||||||
|
handleSecondAction = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 = "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -179,13 +179,14 @@ public enum EEPType {
|
|||||||
CHANNEL_DOUBLEPRESS, CHANNEL_LONGPRESS, CHANNEL_BATTERY_LEVEL),
|
CHANNEL_DOUBLEPRESS, CHANNEL_LONGPRESS, CHANNEL_BATTERY_LEVEL),
|
||||||
|
|
||||||
RockerSwitch2RockerStyle1(RORG.RPS, 0x02, 0x01, false, F6_02_01.class, THING_TYPE_ROCKERSWITCH,
|
RockerSwitch2RockerStyle1(RORG.RPS, 0x02, 0x01, false, F6_02_01.class, THING_TYPE_ROCKERSWITCH,
|
||||||
CHANNEL_ROCKERSWITCH_CHANNELA, CHANNEL_ROCKERSWITCH_CHANNELB, CHANNEL_VIRTUALSWITCHA,
|
CHANNEL_ROCKERSWITCH_CHANNELA, CHANNEL_ROCKERSWITCH_CHANNELB, CHANNEL_ROCKERSWITCH_ACTION,
|
||||||
CHANNEL_VIRTUALROLLERSHUTTERA, CHANNEL_VIRTUALROCKERSWITCHB, CHANNEL_ROCKERSWITCHLISTENERSWITCH,
|
CHANNEL_VIRTUALSWITCHA, CHANNEL_VIRTUALROLLERSHUTTERA, CHANNEL_VIRTUALROCKERSWITCHB,
|
||||||
CHANNEL_ROCKERSWITCHLISTENERROLLERSHUTTER),
|
CHANNEL_ROCKERSWITCHLISTENERSWITCH, CHANNEL_ROCKERSWITCHLISTENERROLLERSHUTTER),
|
||||||
|
|
||||||
RockerSwitch2RockerStyle2(RORG.RPS, 0x02, 0x02, false, F6_02_02.class, THING_TYPE_ROCKERSWITCH,
|
RockerSwitch2RockerStyle2(RORG.RPS, 0x02, 0x02, false, F6_02_02.class, THING_TYPE_ROCKERSWITCH,
|
||||||
CHANNEL_ROCKERSWITCH_CHANNELA, CHANNEL_ROCKERSWITCH_CHANNELB, CHANNEL_VIRTUALSWITCHA,
|
CHANNEL_ROCKERSWITCH_CHANNELA, CHANNEL_ROCKERSWITCH_CHANNELB, CHANNEL_ROCKERSWITCH_ACTION,
|
||||||
CHANNEL_VIRTUALROLLERSHUTTERA, CHANNEL_VIRTUALROCKERSWITCHB, CHANNEL_ROCKERSWITCHLISTENERSWITCH,
|
CHANNEL_VIRTUALSWITCHA, CHANNEL_VIRTUALROLLERSHUTTERA, CHANNEL_VIRTUALROCKERSWITCHB,
|
||||||
CHANNEL_ROCKERSWITCHLISTENERROLLERSHUTTER),
|
CHANNEL_ROCKERSWITCHLISTENERSWITCH, CHANNEL_ROCKERSWITCHLISTENERROLLERSHUTTER),
|
||||||
|
|
||||||
MechanicalHandle00(RORG.RPS, 0x10, 0x00, false, F6_10_00.class, THING_TYPE_MECHANICALHANDLE,
|
MechanicalHandle00(RORG.RPS, 0x10, 0x00, false, F6_10_00.class, THING_TYPE_MECHANICALHANDLE,
|
||||||
CHANNEL_WINDOWHANDLESTATE, CHANNEL_CONTACT),
|
CHANNEL_WINDOWHANDLESTATE, CHANNEL_CONTACT),
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,13 +17,11 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchConfigBase.Channel;
|
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.eep.Base._RPSMessage;
|
||||||
import org.openhab.binding.enocean.internal.messages.ERP1Message;
|
import org.openhab.binding.enocean.internal.messages.ERP1Message;
|
||||||
import org.openhab.core.config.core.Configuration;
|
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.StringType;
|
||||||
import org.openhab.core.library.types.UpDownType;
|
|
||||||
import org.openhab.core.thing.CommonTriggerEvents;
|
import org.openhab.core.thing.CommonTriggerEvents;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
import org.openhab.core.types.State;
|
import org.openhab.core.types.State;
|
||||||
@ -33,16 +31,7 @@ import org.openhab.core.types.UnDefType;
|
|||||||
*
|
*
|
||||||
* @author Daniel Weber - Initial contribution
|
* @author Daniel Weber - Initial contribution
|
||||||
*/
|
*/
|
||||||
public class F6_02_01 extends _RPSMessage {
|
public class F6_02_01 extends F6_02 {
|
||||||
|
|
||||||
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 F6_02_01() {
|
public F6_02_01() {
|
||||||
super();
|
super();
|
||||||
@ -57,23 +46,25 @@ public class F6_02_01 extends _RPSMessage {
|
|||||||
Configuration config) {
|
Configuration config) {
|
||||||
|
|
||||||
if (t21 && nu) {
|
if (t21 && nu) {
|
||||||
|
if (CHANNEL_ROCKERSWITCH_ACTION.equals(channelTypeId)) {
|
||||||
|
return getRockerSwitchAction(config);
|
||||||
|
} else {
|
||||||
byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0;
|
byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0;
|
||||||
byte dir2 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI;
|
byte dir2 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI;
|
||||||
|
|
||||||
if ((bytes[0] >>> 5) == dir1) {
|
return getChannelEvent(dir1, dir2);
|
||||||
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 if (t21 && !nu) {
|
} else if (t21 && !nu) {
|
||||||
|
if (CHANNEL_ROCKERSWITCH_ACTION.equals(channelTypeId)) {
|
||||||
|
return CommonTriggerEvents.RELEASED;
|
||||||
|
} else {
|
||||||
if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR1_PRESSED)) {
|
if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR1_PRESSED)) {
|
||||||
return CommonTriggerEvents.DIR1_RELEASED;
|
return CommonTriggerEvents.DIR1_RELEASED;
|
||||||
} else if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR2_PRESSED)) {
|
} else if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR2_PRESSED)) {
|
||||||
return CommonTriggerEvents.DIR2_RELEASED;
|
return CommonTriggerEvents.DIR2_RELEASED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -82,7 +73,7 @@ public class F6_02_01 extends _RPSMessage {
|
|||||||
protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command,
|
protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command,
|
||||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||||
if (command instanceof StringType) {
|
if (command instanceof StringType) {
|
||||||
StringType s = (StringType) command;
|
String s = ((StringType) command).toString();
|
||||||
|
|
||||||
if (s.equals(CommonTriggerEvents.DIR1_RELEASED) || s.equals(CommonTriggerEvents.DIR2_RELEASED)) {
|
if (s.equals(CommonTriggerEvents.DIR1_RELEASED) || s.equals(CommonTriggerEvents.DIR2_RELEASED)) {
|
||||||
setStatus(_RPSMessage.T21Flag);
|
setStatus(_RPSMessage.T21Flag);
|
||||||
@ -106,72 +97,20 @@ public class F6_02_01 extends _RPSMessage {
|
|||||||
@Override
|
@Override
|
||||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
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
|
// appropriate item update
|
||||||
State currentState = getCurrentStateFunc.apply(channelId);
|
State currentState = getCurrentStateFunc.apply(channelId);
|
||||||
if (t21 && nu) {
|
if (t21 && nu) {
|
||||||
EnOceanChannelVirtualRockerSwitchConfig c = config.as(EnOceanChannelVirtualRockerSwitchConfig.class);
|
EnOceanChannelRockerSwitchListenerConfig c = config.as(EnOceanChannelRockerSwitchListenerConfig.class);
|
||||||
byte dir1 = c.getChannel() == Channel.ChannelA ? A0 : B0;
|
byte dir1 = c.getChannel() == Channel.ChannelA ? A0 : B0;
|
||||||
byte dir2 = c.getChannel() == Channel.ChannelA ? AI : BI;
|
byte dir2 = c.getChannel() == Channel.ChannelA ? AI : BI;
|
||||||
|
|
||||||
// We are just listening on the pressed event here
|
return getState(dir1, dir2, c.handleSecondAction, c.getSwitchMode(), channelTypeId, currentState);
|
||||||
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 UnDefType.UNDEF;
|
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
|
@Override
|
||||||
public boolean isValidForTeachIn() {
|
public boolean isValidForTeachIn() {
|
||||||
if (t21) {
|
if (t21) {
|
||||||
|
|||||||
@ -17,13 +17,11 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchConfigBase.Channel;
|
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.eep.Base._RPSMessage;
|
||||||
import org.openhab.binding.enocean.internal.messages.ERP1Message;
|
import org.openhab.binding.enocean.internal.messages.ERP1Message;
|
||||||
import org.openhab.core.config.core.Configuration;
|
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.StringType;
|
||||||
import org.openhab.core.library.types.UpDownType;
|
|
||||||
import org.openhab.core.thing.CommonTriggerEvents;
|
import org.openhab.core.thing.CommonTriggerEvents;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
import org.openhab.core.types.State;
|
import org.openhab.core.types.State;
|
||||||
@ -33,13 +31,7 @@ import org.openhab.core.types.UnDefType;
|
|||||||
*
|
*
|
||||||
* @author Daniel Weber - Initial contribution
|
* @author Daniel Weber - Initial contribution
|
||||||
*/
|
*/
|
||||||
public class F6_02_02 extends _RPSMessage {
|
public class F6_02_02 extends F6_02 {
|
||||||
|
|
||||||
final byte AI = 0;
|
|
||||||
final byte A0 = 1;
|
|
||||||
final byte BI = 2;
|
|
||||||
final byte B0 = 3;
|
|
||||||
final byte PRESSED = 16;
|
|
||||||
|
|
||||||
public F6_02_02() {
|
public F6_02_02() {
|
||||||
super();
|
super();
|
||||||
@ -54,23 +46,24 @@ public class F6_02_02 extends _RPSMessage {
|
|||||||
Configuration config) {
|
Configuration config) {
|
||||||
|
|
||||||
if (t21 && nu) {
|
if (t21 && nu) {
|
||||||
|
if (CHANNEL_ROCKERSWITCH_ACTION.equals(channelTypeId)) {
|
||||||
|
return getRockerSwitchAction(config);
|
||||||
|
} else {
|
||||||
byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI;
|
byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI;
|
||||||
byte dir2 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0;
|
byte dir2 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0;
|
||||||
|
return getChannelEvent(dir1, dir2);
|
||||||
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 if (t21 && !nu) {
|
} else if (t21 && !nu) {
|
||||||
|
if (CHANNEL_ROCKERSWITCH_ACTION.equals(channelTypeId)) {
|
||||||
|
return CommonTriggerEvents.RELEASED;
|
||||||
|
} else {
|
||||||
if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR1_PRESSED)) {
|
if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR1_PRESSED)) {
|
||||||
return CommonTriggerEvents.DIR1_RELEASED;
|
return CommonTriggerEvents.DIR1_RELEASED;
|
||||||
} else if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR2_PRESSED)) {
|
} else if (lastEvent != null && lastEvent.equals(CommonTriggerEvents.DIR2_PRESSED)) {
|
||||||
return CommonTriggerEvents.DIR2_RELEASED;
|
return CommonTriggerEvents.DIR2_RELEASED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -79,7 +72,7 @@ public class F6_02_02 extends _RPSMessage {
|
|||||||
protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command,
|
protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command,
|
||||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||||
if (command instanceof StringType) {
|
if (command instanceof StringType) {
|
||||||
StringType s = (StringType) command;
|
String s = ((StringType) command).toString();
|
||||||
|
|
||||||
if (s.equals(CommonTriggerEvents.DIR1_RELEASED) || s.equals(CommonTriggerEvents.DIR2_RELEASED)) {
|
if (s.equals(CommonTriggerEvents.DIR1_RELEASED) || s.equals(CommonTriggerEvents.DIR2_RELEASED)) {
|
||||||
setStatus(_RPSMessage.T21Flag);
|
setStatus(_RPSMessage.T21Flag);
|
||||||
@ -103,67 +96,20 @@ public class F6_02_02 extends _RPSMessage {
|
|||||||
@Override
|
@Override
|
||||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
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
|
// appropriate item update
|
||||||
State currentState = getCurrentStateFunc.apply(channelId);
|
State currentState = getCurrentStateFunc.apply(channelId);
|
||||||
if (t21 && nu) {
|
if (t21 && nu) {
|
||||||
EnOceanChannelVirtualRockerSwitchConfig c = config.as(EnOceanChannelVirtualRockerSwitchConfig.class);
|
EnOceanChannelRockerSwitchListenerConfig c = config.as(EnOceanChannelRockerSwitchListenerConfig.class);
|
||||||
byte dir1 = c.getChannel() == Channel.ChannelA ? AI : BI;
|
byte dir1 = c.getChannel() == Channel.ChannelA ? AI : BI;
|
||||||
byte dir2 = c.getChannel() == Channel.ChannelA ? A0 : B0;
|
byte dir2 = c.getChannel() == Channel.ChannelA ? A0 : B0;
|
||||||
|
|
||||||
// We are just listening on the pressed event here
|
return getState(dir1, dir2, c.handleSecondAction, c.getSwitchMode(), channelTypeId, currentState);
|
||||||
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 UnDefType.UNDEF;
|
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
|
@Override
|
||||||
public boolean isValidForTeachIn() {
|
public boolean isValidForTeachIn() {
|
||||||
return false; // Never treat a message as F6-02-02, let user decide which orientation of rocker switch is used
|
return false; // Never treat a message as F6-02-02, let user decide which orientation of rocker switch is used
|
||||||
|
|||||||
@ -129,7 +129,8 @@ public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements
|
|||||||
|
|
||||||
protected Predicate<Channel> channelFilter(EEPType eepType, byte[] senderId) {
|
protected Predicate<Channel> channelFilter(EEPType eepType, byte[] senderId) {
|
||||||
return c -> {
|
return c -> {
|
||||||
boolean result = eepType.GetSupportedChannels().containsKey(c.getUID().getId());
|
|
||||||
|
boolean result = eepType.isChannelSupported(c);
|
||||||
return (isLinked(c.getUID()) || c.getKind() == ChannelKind.TRIGGER) && result;
|
return (isLinked(c.getUID()) || c.getKind() == ChannelKind.TRIGGER) && result;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -29,6 +29,11 @@
|
|||||||
</options>
|
</options>
|
||||||
<default>channelA</default>
|
<default>channelA</default>
|
||||||
</parameter>
|
</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">
|
<parameter name="switchMode" type="text">
|
||||||
<label>Switch Mode</label>
|
<label>Switch Mode</label>
|
||||||
<options>
|
<options>
|
||||||
@ -56,4 +61,27 @@
|
|||||||
</parameter>
|
</parameter>
|
||||||
</config-description>
|
</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>
|
</config-description:config-descriptions>
|
||||||
|
|||||||
@ -20,6 +20,9 @@
|
|||||||
<channel typeId="system.rawrocker" id="rockerswitchB">
|
<channel typeId="system.rawrocker" id="rockerswitchB">
|
||||||
<label>Rocker Switch - Channel B</label>
|
<label>Rocker Switch - Channel B</label>
|
||||||
</channel>
|
</channel>
|
||||||
|
<channel id="rockerSwitchAction" typeId="rockerSwitchAction">
|
||||||
|
<label>Rocker Switch Action</label>
|
||||||
|
</channel>
|
||||||
</channels>
|
</channels>
|
||||||
|
|
||||||
<config-description>
|
<config-description>
|
||||||
|
|||||||
@ -810,4 +810,23 @@
|
|||||||
<state min="0" max="100" pattern="%.1f rpm" readOnly="true"/>
|
<state min="0" max="100" pattern="%.1f rpm" readOnly="true"/>
|
||||||
</channel-type>
|
</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>
|
</thing:thing-descriptions>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user