diff --git a/bundles/org.openhab.binding.enocean/README.md b/bundles/org.openhab.binding.enocean/README.md index 693610264d5df..4fdbb85f1d581 100644 --- a/bundles/org.openhab.binding.enocean/README.md +++ b/bundles/org.openhab.binding.enocean/README.md @@ -68,6 +68,7 @@ This binding is developed on and tested with the following devices * Hoppe SecuSignal window handles * Rocker switches (NodOn, Eltako FT55 etc) * Siegenia Senso Secure window sensors + * Soda window handles However, because of the standardized EnOcean protocol it is more important which EEP this binding supports. Hence if your device supports one of the following EEPs the chances are good that your device is also supported by this binding. @@ -77,7 +78,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,
longPress, batteryLevel | NodOn soft button | Manually/Discovery | | rockerSwitch | F6-02 | 0x01-02 | rockerswitchA, rockerswitchB,
rockerSwitchAction | Eltako FT55 | Discovery | -| mechanicalHandle | F6-10 | 0x00-01 | windowHandleState, contact | Hoppe SecuSignal handles, Eltako TF-FGB | Discovery | +| mechanicalHandle | F6-10/D2-06 | 0x00-01/0x01 | windowHandleState, contact and a lot more for soda handles³ | Hoppe SecuSignal handles, Eltako TF-FGB, Soda handles | Discovery | | contact | D5-00 | 0x01 | contact | Eltako FTK(E) & TF-FKB | Discovery | | temperatureSensor | A5-02 | 0x01-30 | temperature | Thermokon SR65 | Discovery | | temperatureHumiditySensor | A5-04 | 0x01-03 | humidity, temperature | Eltako FTSB | Discovery | @@ -99,6 +100,11 @@ Hence if your device supports one of the following EEPs the chances are good tha ² These are just examples of supported devices +³ Note that the soda handles potentially contain a wide range of different sensors and buttons. +However the amount of built-in sensors and buttons may vary between different models. +In case your particular device does not contain one of the potentially supported features the corresponding channel will never trigger an update. +Please see the manual of your particular model to check which channels should be supported before opening an issue. + 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. @@ -183,7 +189,7 @@ If you change the SenderId of your thing, you have to pair again the thing with | | enoceanId | EnOceanId of device this thing belongs to | hex value as string | | rockerSwitch | receivingEEPId | | F6_02_01, F6_02_02 | | | enoceanId | | | -| mechanicalHandle | receivingEEPId | | F6_10_00, F6_10_01, A5_14_09 | +| mechanicalHandle | receivingEEPId | | F6_10_00, F6_10_01, A5_14_09, D2_06_01 | | | enoceanId | | | | | receivingSIGEEP | | | | contact | receivingEEPId | | D5_00_01, A5_14_01_ELTAKO | @@ -258,12 +264,13 @@ The channels of a thing are determined automatically based on the chosen EEP. | repeaterMode | String | Set repeater level to 1, 2 or disable | | setBaseId | String | Changes the BaseId of your gateway. This can only be done 10 times! So use it with care. | | pushButton | Trigger | Channel type system:rawbutton, emits PRESSED and RELEASED events | +| pushButton2 | Trigger | Channel type system:rawbutton, emits PRESSED and RELEASED events | | 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) | +| windowHandleState | String | Textual representation of handle position (UP, DOWN, LEFT, RIGHT for the D2_06_01 EEP and OPEN, CLOSED, TILTED for all others) | +| windowSashState | String | Textual representation of sash position (TILTED or NOT TILTED for the D2_06_01 EEP and OPEN, CLOSED, TILTED for all others) | | windowCalibrationState | String | Textual representation of the calibration state (OK, ERROR, INVALID) | | windowCalibrationStep | String | Textual representation of the next step that must be performed for calibrating the device (e.g. NONE, SASH CLOSED HANDLE CLOSED, SASH CLOSED HANDLE OPEN, SASH OPEN HANDLE TILTED, and so on) | | contact | Contact | State OPEN/CLOSED (tilted handle => OPEN) | @@ -328,7 +335,9 @@ The channels of a thing are determined automatically based on the chosen EEP. | repeatCount | Number | Number of repeaters involved in the transmission of the telegram | | lastReceived | DateTime | Date and time the last telegram was received | | statusRequestEvent | Trigger | Emits event 'requestAnswer' | -| windowBreachEvent | Trigger | Emits event 'ALARM' | +| windowBreachEvent | Trigger | Emits event 'ALARM' | +| protectionPlusEvent | Trigger | Emits event 'ALARM' | +| vacationModeToggleEvent | Trigger | Emits events 'ACTIVATED', 'DEACTIVATED' | Items linked to bi-directional actuators (actuator sends status messages back) should always disable the `autoupdate`. diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java index f705dd6274f98..6f4f261892963 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java @@ -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)), diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_06/D2_06_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_06/D2_06_01.java new file mode 100644 index 0000000000000..cef5fe3d51455 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_06/D2_06_01.java @@ -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 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 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 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.valueOf(bytes[2] & 0x0F); + if (sashState.isPresent()) { + return new StringType(sashState.get().getTextValue()); + } + return UnDefType.UNDEF; + } + + protected State getWindowHandleState() { + Optional handleState = HandleState.valueOf(bytes[2] >>> 4); + if (handleState.isPresent()) { + return new StringType(handleState.get().getTextValue()); + } + return UnDefType.UNDEF; + } + + protected State getMotionState() { + Optional 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 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; + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java index e212b8f5102bf..50ce6afb73efa 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java @@ -137,6 +137,7 @@ 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, diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MechanicalHandle.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MechanicalHandle.xml index 775eab0a85be2..488fe1c5eef3d 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MechanicalHandle.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MechanicalHandle.xml @@ -26,6 +26,7 @@ + true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/WindowSashHandleSensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/WindowSashHandleSensor.xml index c38a7d5bd3bb2..e57bf0fdae1a4 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/WindowSashHandleSensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/WindowSashHandleSensor.xml @@ -20,7 +20,7 @@ - EEP which is used by handle + EEP which is used by device diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/channels.xml index affa8cdcc4944..9de583532fddc 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/channels.xml @@ -154,6 +154,10 @@ + + + + @@ -166,6 +170,7 @@ + @@ -211,7 +216,30 @@ Is triggered 10 times in 5 seconds when the sensor detects a break-in attempt. - + + + + + + + trigger + + Triggered when a Protection Plus capable device detects a break-in attempt. + + + + + + + + + trigger + + Triggered when the vacation mode has been toggled on the device. + + + +