2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.enocean.internal.eep.D2_06;
15 import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
17 import java.util.Arrays;
18 import java.util.Optional;
19 import java.util.function.Function;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.enocean.internal.eep.Base._VLDMessage;
24 import org.openhab.binding.enocean.internal.messages.ERP1Message;
25 import org.openhab.core.config.core.Configuration;
26 import org.openhab.core.library.types.DecimalType;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.library.types.QuantityType;
29 import org.openhab.core.library.types.StringType;
30 import org.openhab.core.library.unit.SIUnits;
31 import org.openhab.core.library.unit.Units;
32 import org.openhab.core.thing.CommonTriggerEvents;
33 import org.openhab.core.types.State;
34 import org.openhab.core.types.UnDefType;
37 * Implementation of the D2_06_01 EEP as used by window handles manufactured by Soda GmbH. All channels except the
38 * battery channels may be not supported by the physical device (depending on the actual model). If a channel is not
39 * supported by a device it will transmit a 'not supported' message which is ignored by this implementation.
40 * Consequently channels that are not supported by the physical device will never send updates to linked items.
42 * @author Thomas Lauterbach - Initial contribution
45 public class D2_06_01 extends _VLDMessage {
47 private enum MessageType {
49 CONFIGURATIONREPORT(0x10),
54 CONTROLANDSETTINGS(0x80);
58 private MessageType(int intValue) {
59 this.intValue = intValue;
62 private int getIntValue() {
67 private enum SashState {
68 // WINDOWSTATEUNDEFINED(0x00, "UNDEFINED"),
69 NOTTILTED(0x01, "NOT TILTED"),
70 TILTED(0x02, "TILTED");
73 private String textValue;
75 private SashState(int intValue, String textValue) {
76 this.intValue = intValue;
77 this.textValue = textValue;
80 private String getTextValue() {
81 return this.textValue;
84 private static Optional<SashState> valueOf(int intValue) {
85 return Arrays.stream(values()).filter(sashState -> sashState.intValue == intValue).findFirst();
89 private enum HandleState {
90 // HANDLEPOSITIONUNDEFINED(0x00, "UNDEFINED"),
92 HANDLEDOWN(0x02, "DOWN"),
93 HANDLELEFT(0x03, "LEFT"),
94 HANDLERIGHT(0x04, "RIGHT");
97 private String textValue;
99 private HandleState(int intValue, String textValue) {
100 this.intValue = intValue;
101 this.textValue = textValue;
104 private String getTextValue() {
105 return this.textValue;
108 private static Optional<HandleState> valueOf(int intValue) {
109 return Arrays.stream(values()).filter(handleState -> handleState.intValue == intValue).findFirst();
113 private enum MotionState {
114 MOTIONNOTTRIGGERED(0x00, "OFF"),
115 MOTIONTRIGGERED(0x01, "ON");
117 private int intValue;
118 private String textValue;
120 private MotionState(int intValue, String textValue) {
121 this.intValue = intValue;
122 this.textValue = textValue;
125 private String getTextValue() {
126 return this.textValue;
129 private static Optional<MotionState> valueOf(int intValue) {
130 return Arrays.stream(values()).filter(motionState -> motionState.intValue == intValue).findFirst();
138 public D2_06_01(ERP1Message packet) {
142 protected State getWindowSashState() {
143 Optional<SashState> sashState = SashState.valueOf(bytes[2] & 0x0F);
144 if (sashState.isPresent()) {
145 return new StringType(sashState.get().getTextValue());
147 return UnDefType.UNDEF;
150 protected State getWindowHandleState() {
151 Optional<HandleState> handleState = HandleState.valueOf(bytes[2] >>> 4);
152 if (handleState.isPresent()) {
153 return new StringType(handleState.get().getTextValue());
155 return UnDefType.UNDEF;
158 protected State getMotionState() {
159 Optional<MotionState> motionState = MotionState.valueOf(bytes[4] >>> 4);
160 if (motionState.isPresent()) {
161 return OnOffType.from(motionState.get().getTextValue());
163 return UnDefType.UNDEF;
166 protected State getTemperature() {
167 double unscaledTemp = (double) (bytes[5] & 0xFF);
168 if (unscaledTemp <= 250) {
169 double scaledTemp = unscaledTemp * 0.32 - 20;
170 return new QuantityType<>(scaledTemp, SIUnits.CELSIUS);
172 return UnDefType.UNDEF;
175 protected State getHumidity() {
176 int unscaledHumidity = bytes[6] & 0xFF;
177 if (unscaledHumidity <= 200) {
178 double scaledHumidity = unscaledHumidity * 0.5;
179 return new DecimalType(scaledHumidity);
181 return UnDefType.UNDEF;
184 protected State getIllumination() {
185 int illumination = ((bytes[7] & 0xFF) << 8) | (bytes[8] & 0xFF);
186 if (illumination <= 60000) {
187 return new QuantityType<>(illumination, Units.LUX);
189 return UnDefType.UNDEF;
192 protected State getBatteryLevel() {
193 int unscaledBatteryLevel = ((bytes[9] & 0xFF) >> 3);
194 if (unscaledBatteryLevel <= 20) {
195 return new DecimalType(unscaledBatteryLevel * 5);
197 return UnDefType.UNDEF;
201 protected @Nullable String convertToEventImpl(String channelId, String channelTypeId, String lastEvent,
202 Configuration config) {
204 if (bytes[0] == MessageType.SENSORVALUES.getIntValue()) {
206 case CHANNEL_WINDOWBREACHEVENT:
207 if ((bytes[1] >>> 4) == 0x01) {
211 case CHANNEL_PROTECTIONPLUSEVENT:
212 if ((bytes[1] & 0x0F) == 0x01) {
216 case CHANNEL_PUSHBUTTON:
217 int buttonEvent = bytes[3] >>> 4;
218 switch (buttonEvent) {
220 return CommonTriggerEvents.PRESSED;
222 return CommonTriggerEvents.RELEASED;
225 case CHANNEL_PUSHBUTTON2:
226 int buttonEvent2 = bytes[3] & 0x0F;
227 switch (buttonEvent2) {
229 return CommonTriggerEvents.PRESSED;
231 return CommonTriggerEvents.RELEASED;
234 case CHANNEL_VACATIONMODETOGGLEEVENT:
235 int vacationModeToggleEvent = bytes[4] & 0x0F;
236 switch (vacationModeToggleEvent) {
240 return "DEACTIVATED";
249 public State convertToStateImpl(String channelId, String channelTypeId,
250 Function<String, @Nullable State> getCurrentStateFunc, Configuration config) {
252 if (bytes[0] == MessageType.SENSORVALUES.getIntValue()) {
254 case CHANNEL_WINDOWSASHSTATE:
255 return getWindowSashState();
256 case CHANNEL_WINDOWHANDLESTATE:
257 return getWindowHandleState();
258 case CHANNEL_MOTIONDETECTION:
259 return getMotionState();
260 case CHANNEL_INDOORAIRTEMPERATURE:
261 return getTemperature();
262 case CHANNEL_HUMIDITY:
263 return getHumidity();
264 case CHANNEL_ILLUMINATION:
265 return getIllumination();
266 case CHANNEL_BATTERY_LEVEL:
267 return getBatteryLevel();
270 return UnDefType.UNDEF;