-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[enocean] Add support for Soda Handles (EEP D2-06-01) #11230
Merged
Merged
Changes from 16 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
df8e0d4
added data validation
09f17f8
Revert "added data validation"
d9ffb7d
added calibration channels
edb214c
added calibration channels
c656476
initial d20601 impl
6996804
fixed D20601 temp and battery
b47f313
README updated and changed D20601 to mechanical handle
45085ca
spotless
d616f39
use common events
9a87ce4
fix spotless
cb211a0
Revert "added calibration channels"
0114165
Revert "added calibration channels"
ca78f5c
Revert "Revert "added data validation""
0cf8e43
Revert "added data validation"
76fbff8
added more comments and fixed channel labels
e50f5be
added state enums
86423c5
added missing modifiers
e4815a0
fixed typo in comment
e99b910
resolved README conflicts
242eef0
upstream changes
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
275 changes: 275 additions & 0 deletions
275
...inding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_06/D2_06_01.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
|
||
int intValue; | ||
lolodomo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private MessageType(int intValue) { | ||
this.intValue = intValue; | ||
} | ||
|
||
int getIntValue() { | ||
lolodomo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return this.intValue; | ||
} | ||
} | ||
|
||
private enum SashState { | ||
|
||
// WINDOWSTATE UNDEFINED(0x00, "UNDEFINED"), | ||
NOTTILTED(0x01, "NOT TILTED"), | ||
TILTED(0x02, "TILTED"); | ||
|
||
int intValue; | ||
String textValue; | ||
|
||
private SashState(int intValue, String textValue) { | ||
this.intValue = intValue; | ||
this.textValue = textValue; | ||
} | ||
|
||
String getTextValue() { | ||
return this.textValue; | ||
} | ||
|
||
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; | ||
} | ||
|
||
String getTextValue() { | ||
return this.textValue; | ||
} | ||
|
||
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; | ||
} | ||
|
||
String getTextValue() { | ||
return this.textValue; | ||
} | ||
|
||
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add @NonNullByDefault
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @lolodomo
I added the annotation but I'm struggling with the annotation of the getCurrentStateFunc parameter of the convertToStateImpl method. If I annotate it with Nullable it will still give me an Exception due to a mismatched annotation. I think this might be due to the fact that State is annotated as NonNull but I can't figure out how to fix that... Any help would be highly appreciated, thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the problem is due to parent classes not being null annotated?
Did you try to annotate them too?
If this is the problem, maybe we can forget my comment as my intention was not to tell you to annotate many classes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be the case but it would go way beyond the scope of this PR to annotate large parts of the binding (no null annotations are used so far).
The correct way to do this for my single class (as the start of an iterative approach) would be as I understand it from the documentation to annotate the parameters with Nullable. This works fine for all parameter except for the Function<String, State> parameter with its generic types. What's special is that the State type is an openHAB core class that is properly annotated while the others are plain basic Java classes that are not annotated at all. I tried a lot of different things without really knowing what I'm doing and finally gave up yesterday night...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok so please forget this comment in the context of this PR and push your updates.