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.openhab.binding.enocean.internal.eep.Base._VLDMessage;
22 import org.openhab.binding.enocean.internal.messages.ERP1Message;
23 import org.openhab.core.config.core.Configuration;
24 import org.openhab.core.library.types.DecimalType;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.library.types.QuantityType;
27 import org.openhab.core.library.types.StringType;
28 import org.openhab.core.library.unit.SIUnits;
29 import org.openhab.core.library.unit.Units;
30 import org.openhab.core.thing.CommonTriggerEvents;
31 import org.openhab.core.types.State;
32 import org.openhab.core.types.UnDefType;
35 * Implementation of the D2_06_01 EEP as used by window handles manufactured by Soda GmbH. All channels except the
36 * battery channels may be not supported by the physical device (depending on the actual model). If a channel is not
37 * supported by a device it will transmit a 'not supported' message which is ignored by this implementation.
38 * Consequently channels that are not supported by the physical device will never send updates to linked items.
40 * @author Thomas Lauterbach - Initial contribution
42 public class D2_06_01 extends _VLDMessage {
44 private enum MessageType {
47 CONFIGURATIONREPORT(0x10),
52 CONTROLANDSETTINGS(0x80);
56 private MessageType(int intValue) {
57 this.intValue = intValue;
60 private int getIntValue() {
65 private enum SashState {
67 // WINDOWSTATEUNDEFINED(0x00, "UNDEFINED"),
68 NOTTILTED(0x01, "NOT TILTED"),
69 TILTED(0x02, "TILTED");
72 private String textValue;
74 private SashState(int intValue, String textValue) {
75 this.intValue = intValue;
76 this.textValue = textValue;
79 private String getTextValue() {
80 return this.textValue;
83 private static Optional<SashState> valueOf(int intValue) {
84 return Arrays.stream(values()).filter(sashState -> sashState.intValue == intValue).findFirst();
88 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 {
115 MOTIONNOTTRIGGERED(0x00, "OFF"),
116 MOTIONTRIGGERED(0x01, "ON");
118 private int intValue;
119 private String textValue;
121 private MotionState(int intValue, String textValue) {
122 this.intValue = intValue;
123 this.textValue = textValue;
126 private String getTextValue() {
127 return this.textValue;
130 private static Optional<MotionState> valueOf(int intValue) {
131 return Arrays.stream(values()).filter(motionState -> motionState.intValue == intValue).findFirst();
139 public D2_06_01(ERP1Message packet) {
143 protected State getWindowSashState() {
144 Optional<SashState> sashState = SashState.valueOf(bytes[2] & 0x0F);
145 if (sashState.isPresent()) {
146 return new StringType(sashState.get().getTextValue());
148 return UnDefType.UNDEF;
151 protected State getWindowHandleState() {
152 Optional<HandleState> handleState = HandleState.valueOf(bytes[2] >>> 4);
153 if (handleState.isPresent()) {
154 return new StringType(handleState.get().getTextValue());
156 return UnDefType.UNDEF;
159 protected State getMotionState() {
160 Optional<MotionState> motionState = MotionState.valueOf(bytes[4] >>> 4);
161 if (motionState.isPresent()) {
162 return OnOffType.from(motionState.get().getTextValue());
164 return UnDefType.UNDEF;
167 protected State getTemperature() {
168 double unscaledTemp = (double) (bytes[5] & 0xFF);
169 if (unscaledTemp <= 250) {
170 double scaledTemp = unscaledTemp * 0.32 - 20;
171 return new QuantityType<>(scaledTemp, SIUnits.CELSIUS);
173 return UnDefType.UNDEF;
176 protected State getHumidity() {
177 int unscaledHumidity = bytes[6] & 0xFF;
178 if (unscaledHumidity <= 200) {
179 double scaledHumidity = unscaledHumidity * 0.5;
180 return new DecimalType(scaledHumidity);
182 return UnDefType.UNDEF;
185 protected State getIllumination() {
186 int illumination = ((bytes[7] & 0xFF) << 8) | (bytes[8] & 0xFF);
187 if (illumination <= 60000) {
188 return new QuantityType<>(illumination, Units.LUX);
190 return UnDefType.UNDEF;
193 protected State getBatteryLevel() {
194 int unscaledBatteryLevel = ((bytes[9] & 0xFF) >> 3);
195 if (unscaledBatteryLevel <= 20) {
196 return new DecimalType(unscaledBatteryLevel * 5);
198 return UnDefType.UNDEF;
202 protected String convertToEventImpl(String channelId, String channelTypeId, String lastEvent,
203 Configuration config) {
206 if (bytes[0] == MessageType.SENSORVALUES.getIntValue()) {
208 case CHANNEL_WINDOWBREACHEVENT:
209 if ((bytes[1] >>> 4) == 0x01) {
213 case CHANNEL_PROTECTIONPLUSEVENT:
214 if ((bytes[1] & 0x0F) == 0x01) {
218 case CHANNEL_PUSHBUTTON:
219 int buttonEvent = bytes[3] >>> 4;
220 switch (buttonEvent) {
222 return CommonTriggerEvents.PRESSED;
224 return CommonTriggerEvents.RELEASED;
227 case CHANNEL_PUSHBUTTON2:
228 int buttonEvent2 = bytes[3] & 0x0F;
229 switch (buttonEvent2) {
231 return CommonTriggerEvents.PRESSED;
233 return CommonTriggerEvents.RELEASED;
236 case CHANNEL_VACATIONMODETOGGLEEVENT:
237 int vacationModeToggleEvent = bytes[4] & 0x0F;
238 switch (vacationModeToggleEvent) {
242 return "DEACTIVATED";
251 public State convertToStateImpl(String channelId, String channelTypeId, Function<String, State> getCurrentStateFunc,
252 Configuration config) {
255 if (bytes[0] == MessageType.SENSORVALUES.getIntValue()) {
257 case CHANNEL_WINDOWSASHSTATE:
258 return getWindowSashState();
259 case CHANNEL_WINDOWHANDLESTATE:
260 return getWindowHandleState();
261 case CHANNEL_MOTIONDETECTION:
262 return getMotionState();
263 case CHANNEL_INDOORAIRTEMPERATURE:
264 return getTemperature();
265 case CHANNEL_HUMIDITY:
266 return getHumidity();
267 case CHANNEL_ILLUMINATION:
268 return getIllumination();
269 case CHANNEL_BATTERY_LEVEL:
270 return getBatteryLevel();
273 return UnDefType.UNDEF;