[enocean] Add support for Soda Handles (EEP D2-06-01) (#11230)

* added data validation

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* Revert "added data validation"

This reverts commit 945101056b7d0a79b6d53985cfcef8e06bf0a8aa.

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* added calibration channels

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* added calibration channels

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* initial d20601 impl

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* fixed D20601 temp and battery

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* README updated and changed D20601 to mechanical handle

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* spotless

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* use common events

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* fix spotless

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* Revert "added calibration channels"

This reverts commit 6287b4be19fc37e8d1c2211109628fff513ecc8e.

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* Revert "added calibration channels"

This reverts commit fca8b4b11d1df0345bc02466311cf3f139044562.

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* Revert "Revert "added data validation""

This reverts commit e8ff27fa44461a108f3942f5e9547d72bb280d65.

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* Revert "added data validation"

This reverts commit 8d7bd95629426640d9f63d53c05335f3dcbd9b5b.

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* added more comments and fixed channel labels

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* added state enums

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* added missing modifiers

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* fixed typo in comment

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

* upstream changes

Signed-off-by: Thomas Lauterbach <lauterbachthomas@gmail.com>

Co-authored-by: Thomas Lauterbach <lauterbachthomas@gmail.com>
This commit is contained in:
Thomas Lauterbach
2021-10-16 18:47:58 +02:00
committed by GitHub
parent 1e57450e3f
commit a253250427
7 changed files with 337 additions and 7 deletions

View File

@@ -127,6 +127,7 @@ public class EnOceanBindingConstants {
public static final String CHANNEL_INDOORAIRANALYSIS = "indoorAirAnalysis";
public static final String CHANNEL_PUSHBUTTON = "pushButton";
public static final String CHANNEL_PUSHBUTTON2 = "pushButton2";
public static final String CHANNEL_DOUBLEPRESS = "doublePress";
public static final String CHANNEL_LONGPRESS = "longPress";
@@ -148,6 +149,8 @@ public class EnOceanBindingConstants {
public static final String CHANNEL_WINDOWCALIBRATIONSTATE = "windowCalibrationState";
public static final String CHANNEL_WINDOWCALIBRATIONSTEP = "windowCalibrationStep";
public static final String CHANNEL_WINDOWBREACHEVENT = "windowBreachEvent";
public static final String CHANNEL_PROTECTIONPLUSEVENT = "protectionPlusEvent";
public static final String CHANNEL_VACATIONMODETOGGLEEVENT = "vacationModeToggleEvent";
public static final String CHANNEL_CONTACT = "contact";
public static final String CHANNEL_TEACHINCMD = "teachInCMD";
public static final String CHANNEL_INSTANTPOWER = "instantpower";
@@ -325,6 +328,12 @@ public class EnOceanBindingConstants {
Map.entry(CHANNEL_WINDOWBREACHEVENT,
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_WINDOWBREACHEVENT), null, null,
false, true)),
Map.entry(CHANNEL_PROTECTIONPLUSEVENT,
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_PROTECTIONPLUSEVENT), null,
null, false, true)),
Map.entry(CHANNEL_VACATIONMODETOGGLEEVENT,
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_VACATIONMODETOGGLEEVENT), null,
null, false, true)),
Map.entry(
CHANNEL_BATTERY_VOLTAGE,
new EnOceanChannelDescription(
@@ -347,6 +356,9 @@ public class EnOceanBindingConstants {
Map.entry(CHANNEL_PUSHBUTTON,
new EnOceanChannelDescription(DefaultSystemChannelTypeProvider.SYSTEM_RAWBUTTON.getUID(), null,
"Push button", false, true)),
Map.entry(CHANNEL_PUSHBUTTON2,
new EnOceanChannelDescription(DefaultSystemChannelTypeProvider.SYSTEM_RAWBUTTON.getUID(), null,
"Push button 2", false, true)),
Map.entry(CHANNEL_DOUBLEPRESS,
new EnOceanChannelDescription(DefaultSystemChannelTypeProvider.SYSTEM_RAWBUTTON.getUID(), null,
"Double press", false, true)),

View File

@@ -0,0 +1,275 @@
/**
* 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.D2_06;
import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
import org.openhab.binding.enocean.internal.eep.Base._VLDMessage;
import org.openhab.binding.enocean.internal.messages.ERP1Message;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.CommonTriggerEvents;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* Implementation of the D2_06_01 EEP as used by window handles manufactured by Soda GmbH. All channels except the
* battery channels may be not supported by the physical device (depending on the actual model). If a channel is not
* supported by a device it will transmit a 'not supported' message which is ignored by this implementation.
* Consequently channels that are not supported by the physical device will never send updates to linked items.
*
* @author Thomas Lauterbach - Initial contribution
*/
public class D2_06_01 extends _VLDMessage {
private enum MessageType {
SENSORVALUES(0x00),
CONFIGURATIONREPORT(0x10),
LOGDATA01(0x20),
LOGDATA02(0x21),
LOGDATA03(0x22),
LOGDATA04(0x23),
CONTROLANDSETTINGS(0x80);
private int intValue;
private MessageType(int intValue) {
this.intValue = intValue;
}
private int getIntValue() {
return this.intValue;
}
}
private enum SashState {
// WINDOWSTATEUNDEFINED(0x00, "UNDEFINED"),
NOTTILTED(0x01, "NOT TILTED"),
TILTED(0x02, "TILTED");
private int intValue;
private String textValue;
private SashState(int intValue, String textValue) {
this.intValue = intValue;
this.textValue = textValue;
}
private String getTextValue() {
return this.textValue;
}
private static Optional<SashState> valueOf(int intValue) {
return Arrays.stream(values()).filter(sashState -> sashState.intValue == intValue).findFirst();
}
}
private enum HandleState {
// HANDLEPOSITIONUNDEFINED(0x00, "UNDEFINED"),
HANDLEUP(0x01, "UP"),
HANDLEDOWN(0x02, "DOWN"),
HANDLELEFT(0x03, "LEFT"),
HANDLERIGHT(0x04, "RIGHT");
private int intValue;
private String textValue;
private HandleState(int intValue, String textValue) {
this.intValue = intValue;
this.textValue = textValue;
}
private String getTextValue() {
return this.textValue;
}
private static Optional<HandleState> valueOf(int intValue) {
return Arrays.stream(values()).filter(handleState -> handleState.intValue == intValue).findFirst();
}
}
private enum MotionState {
MOTIONNOTTRIGGERED(0x00, "OFF"),
MOTIONTRIGGERED(0x01, "ON");
private int intValue;
private String textValue;
private MotionState(int intValue, String textValue) {
this.intValue = intValue;
this.textValue = textValue;
}
private String getTextValue() {
return this.textValue;
}
private static Optional<MotionState> valueOf(int intValue) {
return Arrays.stream(values()).filter(motionState -> motionState.intValue == intValue).findFirst();
}
}
public D2_06_01() {
super();
}
public D2_06_01(ERP1Message packet) {
super(packet);
}
protected State getWindowSashState() {
Optional<SashState> sashState = SashState.valueOf(bytes[2] & 0x0F);
if (sashState.isPresent()) {
return new StringType(sashState.get().getTextValue());
}
return UnDefType.UNDEF;
}
protected State getWindowHandleState() {
Optional<HandleState> handleState = HandleState.valueOf(bytes[2] >>> 4);
if (handleState.isPresent()) {
return new StringType(handleState.get().getTextValue());
}
return UnDefType.UNDEF;
}
protected State getMotionState() {
Optional<MotionState> motionState = MotionState.valueOf(bytes[4] >>> 4);
if (motionState.isPresent()) {
return OnOffType.from(motionState.get().getTextValue());
}
return UnDefType.UNDEF;
}
protected State getTemperature() {
double unscaledTemp = (double) (bytes[5] & 0xFF);
if (unscaledTemp <= 250) {
double scaledTemp = unscaledTemp * 0.32 - 20;
return new QuantityType<>(scaledTemp, SIUnits.CELSIUS);
}
return UnDefType.UNDEF;
}
protected State getHumidity() {
int unscaledHumidity = bytes[6] & 0xFF;
if (unscaledHumidity <= 200) {
double scaledHumidity = unscaledHumidity * 0.5;
return new DecimalType(scaledHumidity);
}
return UnDefType.UNDEF;
}
protected State getIllumination() {
int illumination = ((bytes[7] & 0xFF) << 8) | (bytes[8] & 0xFF);
if (illumination <= 60000) {
return new QuantityType<>(illumination, Units.LUX);
}
return UnDefType.UNDEF;
}
protected State getBatteryLevel() {
int unscaledBatteryLevel = ((bytes[9] & 0xFF) >> 3);
if (unscaledBatteryLevel <= 20) {
return new DecimalType(unscaledBatteryLevel * 5);
}
return UnDefType.UNDEF;
}
@Override
protected String convertToEventImpl(String channelId, String channelTypeId, String lastEvent,
Configuration config) {
// Sensor values
if (bytes[0] == MessageType.SENSORVALUES.getIntValue()) {
switch (channelId) {
case CHANNEL_WINDOWBREACHEVENT:
if ((bytes[1] >>> 4) == 0x01) {
return "ALARM";
}
break;
case CHANNEL_PROTECTIONPLUSEVENT:
if ((bytes[1] & 0x0F) == 0x01) {
return "ALARM";
}
break;
case CHANNEL_PUSHBUTTON:
int buttonEvent = bytes[3] >>> 4;
switch (buttonEvent) {
case 0x01:
return CommonTriggerEvents.PRESSED;
case 0x02:
return CommonTriggerEvents.RELEASED;
}
break;
case CHANNEL_PUSHBUTTON2:
int buttonEvent2 = bytes[3] & 0x0F;
switch (buttonEvent2) {
case 0x01:
return CommonTriggerEvents.PRESSED;
case 0x02:
return CommonTriggerEvents.RELEASED;
}
break;
case CHANNEL_VACATIONMODETOGGLEEVENT:
int vacationModeToggleEvent = bytes[4] & 0x0F;
switch (vacationModeToggleEvent) {
case 0x01:
return "ACTIVATED";
case 0x02:
return "DEACTIVATED";
}
break;
}
}
return null;
}
@Override
public State convertToStateImpl(String channelId, String channelTypeId, Function<String, State> getCurrentStateFunc,
Configuration config) {
// Sensor values
if (bytes[0] == MessageType.SENSORVALUES.getIntValue()) {
switch (channelId) {
case CHANNEL_WINDOWSASHSTATE:
return getWindowSashState();
case CHANNEL_WINDOWHANDLESTATE:
return getWindowHandleState();
case CHANNEL_MOTIONDETECTION:
return getMotionState();
case CHANNEL_INDOORAIRTEMPERATURE:
return getTemperature();
case CHANNEL_HUMIDITY:
return getHumidity();
case CHANNEL_ILLUMINATION:
return getIllumination();
case CHANNEL_BATTERY_LEVEL:
return getBatteryLevel();
}
}
return UnDefType.UNDEF;
}
}

View File

@@ -137,6 +137,7 @@ import org.openhab.binding.enocean.internal.eep.D2_01.D2_01_12;
import org.openhab.binding.enocean.internal.eep.D2_01.D2_01_12_NodON;
import org.openhab.binding.enocean.internal.eep.D2_03.D2_03_0A;
import org.openhab.binding.enocean.internal.eep.D2_05.D2_05_00;
import org.openhab.binding.enocean.internal.eep.D2_06.D2_06_01;
import org.openhab.binding.enocean.internal.eep.D2_06.D2_06_50;
import org.openhab.binding.enocean.internal.eep.D2_14.D2_14_30;
import org.openhab.binding.enocean.internal.eep.D2_50.D2_50;
@@ -196,6 +197,10 @@ public enum EEPType {
CHANNEL_WINDOWHANDLESTATE, CHANNEL_CONTACT, CHANNEL_BATTERY_VOLTAGE),
MechanicalHandle03(RORG._4BS, 0x14, 0x0A, false, A5_14_0A.class, THING_TYPE_MECHANICALHANDLE,
CHANNEL_WINDOWHANDLESTATE, CHANNEL_CONTACT, CHANNEL_VIBRATION, CHANNEL_BATTERY_VOLTAGE),
MechanicalHandle04(RORG.VLD, 0x06, 0x01, false, "Soda", 0x0043, D2_06_01.class, THING_TYPE_MECHANICALHANDLE,
CHANNEL_WINDOWHANDLESTATE, CHANNEL_WINDOWSASHSTATE, CHANNEL_MOTIONDETECTION, CHANNEL_INDOORAIRTEMPERATURE,
CHANNEL_HUMIDITY, CHANNEL_ILLUMINATION, CHANNEL_BATTERY_LEVEL, CHANNEL_WINDOWBREACHEVENT,
CHANNEL_PROTECTIONPLUSEVENT, CHANNEL_PUSHBUTTON, CHANNEL_PUSHBUTTON2, CHANNEL_VACATIONMODETOGGLEEVENT),
ContactAndSwitch01(RORG._1BS, 0x00, 0x01, false, D5_00_01.class, THING_TYPE_CONTACT, CHANNEL_CONTACT),
ContactAndSwitch02(RORG._4BS, 0x14, 0x01, false, A5_14_01.class, THING_TYPE_CONTACT, CHANNEL_BATTERY_VOLTAGE,

View File

@@ -26,6 +26,7 @@
<option value="F6_10_01">F6-10-01</option>
<option value="A5_14_09">A5-14-09</option>
<option value="A5_14_0A">A5-14-0A</option>
<option value="D2_06_01_Soda">D2_06_01 (Soda handle)</option>
</options>
<limitToOptions>true</limitToOptions>
</parameter>

View File

@@ -20,7 +20,7 @@
</parameter>
<parameter name="receivingEEPId" type="text" required="true">
<label>EEP</label>
<description>EEP which is used by handle</description>
<description>EEP which is used by device</description>
<options>
<option value="D2_06_50_Siegenia">D2_06_50 (Siegenia Senso Secure)</option>
</options>

View File

@@ -154,6 +154,10 @@
<option value="OPEN">Open</option>
<option value="TILTED">Tilted</option>
<option value="CLOSED">Closed</option>
<option value="UP">Up</option>
<option value="DOWN">Down</option>
<option value="LEFT">Left</option>
<option value="RIGHT">Right</option>
</options>
</state>
</channel-type>
@@ -166,6 +170,7 @@
<options>
<option value="OPEN">Open</option>
<option value="TILTED">Tilted</option>
<option value="NOT TILTED">Not Tilted</option>
<option value="CLOSED">Closed</option>
</options>
</state>
@@ -211,7 +216,30 @@
<description>Is triggered 10 times in 5 seconds when the sensor detects a break-in attempt.</description>
<event>
<options>
<option value="ALARM">alarm</option>
<option value="ALARM">Alarm</option>
</options>
</event>
</channel-type>
<channel-type id="protectionPlusEvent">
<kind>trigger</kind>
<label>Protection Plus Event</label>
<description>Triggered when a Protection Plus capable device detects a break-in attempt.</description>
<event>
<options>
<option value="ALARM">Alarm</option>
</options>
</event>
</channel-type>
<channel-type id="vacationModeToggleEvent">
<kind>trigger</kind>
<label>Vacation Mode Toggle Event</label>
<description>Triggered when the vacation mode has been toggled on the device.</description>
<event>
<options>
<option value="ACTIVATED">Activated</option>
<option value="DEACTIVATED">Deactivated</option>
</options>
</event>
</channel-type>