added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.leapmotion</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,20 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons
== Third-party Content
LeapJava
* License: Leap Motion SDK Agreement
* Project: https://developer.leapmotion.com/sdk/v2
* Source: https://github.com/leapmotion/LeapJava

View File

@@ -0,0 +1,56 @@
# Leap Motion Binding
The [Leap Motion](https://www.leapmotion.com/) controller is a gesture sensoring device that uses stereoscopic cameras and is connected through USB.
As all processing is done in software, it requires quite some powerful computer, such that it unfortunately does not work on single-board computers such as the Raspberry Pi.
In fact, the binding is currently only working on macOS computers with Intel x86 processors.
## Supported Things
There is a single thing of type `controller` defined and only one can be connected to the computer at a time.
## Discovery
The controller is automatically discovered if plugged into a USB port.
## Thing Configuration
The controller does not have any kind of configuration parameters.
## Channels
The controller has a single trigger channel `gesture`.
It generates the following events with a frequency of at most 200ms:
| Event | Description |
|---------------|----------------------------------------------------------------------------|
| nohand | No hand can be seen |
| tap | A tap with a single finger |
| clockwise | Rotating a finger clockwise |
| anticlockwise | Rotating a finger anticlockwise |
| fingersX_YYY | Hand showing X fingers in a height of YYY mm (where YYY can be 1-3 digits) |
## Profiles
This binding specifies 3 profiles for the `gesture` channel to make it easy to link to existing items:
| Profile | Description |
|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| leapmotion:switch | Simulates a toggle switch using the "tap" gesture |
| leapmotion:dimmer | Sends percentage values and supports two modes (configuration parameter `mode=fingers|height`): - fingers: 20% for every shown finger, i.e. 0=0%, 1=20%, 2=40%, 3=60%, 4=80%, 5=100% - height: If hand shows all 5 fingers, its height above the controller determines the value. Higher is brighter. |
| leapmotion:color | Controls a color item by - using taps for switching on and off - height of hand (with 5 fingers shown) to dim - rotating a finger to loop through the colors |
## Full Example
demo.things:
```
Thing leapmotion:controller:1 MyLeapMotion
```
demo.items:
```
Switch DemoSwitch "Switch" { channel="leapmotion:controller:1:gesture" }
Color RGBLight "RGB Light" { channel="leapmotion:controller:1:gesture" }
Dimmer DimmedLight "Dimmer [%d %%]" { channel="leapmotion:controller:1:gesture"[profile="leapmotion:dimmer", mode="fingers"] }
```

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.leapmotion</artifactId>
<name>openHAB Add-ons :: Bundles :: Leap Motion Binding</name>
<dependencies>
<dependency>
<groupId>com.leapmotion.leap</groupId>
<artifactId>leap-java</artifactId>
<version>2.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.leapmotion-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-leapmotion" description="LeapMotion Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.leapmotion/${project.version}</bundle>
<configfile finalname="${openhab.userdata}/tmp/lib/libLeap.dylib" override="false">mvn:${project.groupId}/openhab-addons-external/${project.version}/lib/libLeap</configfile>
<configfile finalname="${openhab.userdata}/tmp/lib/libLeapJava.dylib" override="false">mvn:${project.groupId}/openhab-addons-external/${project.version}/lib/libLeapJava</configfile>
</feature>
</features>

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2020 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.leapmotion.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
* The {@link LeapMotionBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Kai Kreuzer - Initial contribution
*/
@NonNullByDefault
public class LeapMotionBindingConstants {
public static final String BINDING_ID = "leapmotion";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_CONTROLLER = new ThingTypeUID(BINDING_ID, "controller");
// List of all Channel ids
public static final String CHANNEL_GESTURE = "gesture";
public static final ChannelTypeUID CHANNEL_GESTURE_UID = new ChannelTypeUID(BINDING_ID, CHANNEL_GESTURE);
// List of all gestures
public static final String GESTURE_TAP = "tap";
public static final String GESTURE_CLOCKWISE = "clockwise";
public static final String GESTURE_ANTICLOCKWISE = "anticlockwise";
public static final String GESTURE_FINGERS = "fingers";
public static final String GESTURE_NOHAND = "nohand";
}

View File

@@ -0,0 +1,100 @@
/**
* Copyright (c) 2010-2020 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.leapmotion.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.profiles.ProfileCallback;
import org.openhab.core.thing.profiles.ProfileTypeUID;
import org.openhab.core.thing.profiles.TriggerProfile;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link LeapMotionColorProfile} class implements the behavior when being linked to a Color item.
*
* @author Kai Kreuzer - Initial contribution
*/
@NonNullByDefault
public class LeapMotionColorProfile implements TriggerProfile {
private final Logger logger = LoggerFactory.getLogger(LeapMotionColorProfile.class);
private ProfileCallback callback;
private HSBType lastState = HSBType.BLACK;
public LeapMotionColorProfile(ProfileCallback callback) {
this.callback = callback;
}
@Override
public ProfileTypeUID getProfileTypeUID() {
return LeapMotionProfileFactory.UID_COLOR;
}
@Override
public void onStateUpdateFromItem(State state) {
if (state instanceof HSBType) {
lastState = (HSBType) state;
} else {
PercentType currentBrightness = state.as(PercentType.class);
if (currentBrightness != null) {
lastState = new HSBType(lastState.getHue(), lastState.getSaturation(), currentBrightness);
}
}
}
@Override
public void onTriggerFromHandler(String event) {
if (event.equals(LeapMotionBindingConstants.GESTURE_TAP)) {
callback.sendCommand(lastState.getBrightness().equals(PercentType.ZERO) ? OnOffType.ON : OnOffType.OFF);
} else if (event.equals(LeapMotionBindingConstants.GESTURE_CLOCKWISE)) {
HSBType color = changeColor(lastState, true);
callback.sendCommand(color);
lastState = color;
} else if (event.equals(LeapMotionBindingConstants.GESTURE_ANTICLOCKWISE)) {
HSBType color = changeColor(lastState, false);
callback.sendCommand(color);
lastState = color;
} else if (event.startsWith(LeapMotionBindingConstants.GESTURE_FINGERS)) {
// the brightness is determined by the height of the palm over the sensor, where higher means brighter
int fingers = Integer
.valueOf(Character.toString(event.charAt(LeapMotionBindingConstants.GESTURE_FINGERS.length())));
if (fingers == 5) {
try {
int height = Integer
.valueOf(event.substring(LeapMotionBindingConstants.GESTURE_FINGERS.length() + 2));
height = Math.min(100 * height / LeapMotionDimmerProfile.MAX_HEIGHT, 100); // don't use values over
// 100
PercentType brightness = new PercentType(height);
callback.sendCommand(brightness);
lastState = new HSBType(lastState.getHue(), lastState.getSaturation(), brightness);
} catch (NumberFormatException e) {
logger.error("Found illegal format of finger event: {}", event, e);
}
}
}
}
private HSBType changeColor(HSBType color, boolean clockwise) {
int hue = clockwise ? (color.getHue().toBigDecimal().intValue() - 20 + 360) % 360
: (color.getHue().toBigDecimal().intValue() + 20 + 360) % 360;
logger.debug("New hue value: {}", hue);
HSBType newState = new HSBType(new DecimalType(hue), color.getSaturation(), color.getBrightness());
return newState;
}
}

View File

@@ -0,0 +1,88 @@
/**
* Copyright (c) 2010-2020 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.leapmotion.internal;
import java.math.BigDecimal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.profiles.ProfileCallback;
import org.openhab.core.thing.profiles.ProfileContext;
import org.openhab.core.thing.profiles.ProfileTypeUID;
import org.openhab.core.thing.profiles.TriggerProfile;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link LeapMotionDimmerProfile} class implements the behavior when being linked to a Dimmer item.
* It supports two modes: Either the dim level is determined by the number of shown fingers or by the height of the hand
* over the controller.
*
* @author Kai Kreuzer - Initial contribution
*/
@NonNullByDefault
public class LeapMotionDimmerProfile implements TriggerProfile {
static final int MAX_HEIGHT = 400; // in mm over controller
private static final String MODE = "mode";
private final Logger logger = LoggerFactory.getLogger(LeapMotionDimmerProfile.class);
private ProfileCallback callback;
private BigDecimal lastState = BigDecimal.ZERO;
private boolean fingerMode = false;
public LeapMotionDimmerProfile(ProfileCallback callback, ProfileContext profileContext) {
this.callback = callback;
fingerMode = "fingers".equals(profileContext.getConfiguration().get(MODE));
}
@Override
public ProfileTypeUID getProfileTypeUID() {
return LeapMotionProfileFactory.UID_DIMMER;
}
@Override
public void onStateUpdateFromItem(State state) {
PercentType currentState = state.as(PercentType.class);
if (currentState != null) {
lastState = currentState.toBigDecimal();
}
}
@Override
public void onTriggerFromHandler(String event) {
if (event.equals(LeapMotionBindingConstants.GESTURE_TAP)) {
callback.sendCommand(lastState.equals(BigDecimal.ZERO) ? OnOffType.ON : OnOffType.OFF);
} else if (event.startsWith(LeapMotionBindingConstants.GESTURE_FINGERS)) {
int fingers = Integer
.valueOf(Character.toString(event.charAt(LeapMotionBindingConstants.GESTURE_FINGERS.length())));
if (fingerMode) {
// the brightness is determined by the number of shown fingers, 20% for each.
callback.sendCommand(new PercentType(fingers * 20));
} else if (fingers == 5) {
// the brightness is determined by the height of the palm over the sensor, where higher means brighter
try {
int height = Integer
.valueOf(event.substring(LeapMotionBindingConstants.GESTURE_FINGERS.length() + 2));
height = Math.min(100 * height / MAX_HEIGHT, 100); // don't use values over 100
callback.sendCommand(new PercentType(height));
} catch (NumberFormatException e) {
logger.error("Found illegal format of finger event: {}", event, e);
}
}
}
}
}

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2010-2020 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.leapmotion.internal;
import static org.openhab.binding.leapmotion.internal.LeapMotionBindingConstants.THING_TYPE_CONTROLLER;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.leapmotion.internal.handler.LeapMotionHandler;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
/**
* The {@link LeapMotionHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Kai Kreuzer - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.leapmotion")
@NonNullByDefault
public class LeapMotionHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_CONTROLLER);
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_CONTROLLER)) {
return new LeapMotionHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,114 @@
/**
* Copyright (c) 2010-2020 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.leapmotion.internal;
import java.util.Collection;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.profiles.Profile;
import org.openhab.core.thing.profiles.ProfileAdvisor;
import org.openhab.core.thing.profiles.ProfileCallback;
import org.openhab.core.thing.profiles.ProfileContext;
import org.openhab.core.thing.profiles.ProfileFactory;
import org.openhab.core.thing.profiles.ProfileType;
import org.openhab.core.thing.profiles.ProfileTypeBuilder;
import org.openhab.core.thing.profiles.ProfileTypeProvider;
import org.openhab.core.thing.profiles.ProfileTypeUID;
import org.openhab.core.thing.profiles.TriggerProfileType;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.osgi.service.component.annotations.Component;
/**
* This class defines and provides all profiles and their types of this binding.
*
* @author Kai Kreuzer - Initial contribution
*
*/
@NonNullByDefault
@Component
public class LeapMotionProfileFactory implements ProfileFactory, ProfileAdvisor, ProfileTypeProvider {
static final ProfileTypeUID UID_SWITCH = new ProfileTypeUID(LeapMotionBindingConstants.BINDING_ID, "switch");
static final ProfileTypeUID UID_DIMMER = new ProfileTypeUID(LeapMotionBindingConstants.BINDING_ID, "dimmer");
static final ProfileTypeUID UID_COLOR = new ProfileTypeUID(LeapMotionBindingConstants.BINDING_ID, "color");
private static final TriggerProfileType SWITCH_TYPE = ProfileTypeBuilder.newTrigger(UID_SWITCH, "Toggle Switch")
.withSupportedItemTypes(CoreItemFactory.SWITCH, CoreItemFactory.DIMMER, CoreItemFactory.COLOR)
.withSupportedChannelTypeUIDs(LeapMotionBindingConstants.CHANNEL_GESTURE_UID).build();
private static final TriggerProfileType DIMMER_TYPE = ProfileTypeBuilder.newTrigger(UID_DIMMER, "Dimmer Control")
.withSupportedItemTypes(CoreItemFactory.DIMMER, CoreItemFactory.COLOR)
.withSupportedChannelTypeUIDs(LeapMotionBindingConstants.CHANNEL_GESTURE_UID).build();
private static final TriggerProfileType COLOR_TYPE = ProfileTypeBuilder.newTrigger(UID_COLOR, "Color Control")
.withSupportedItemTypes(CoreItemFactory.COLOR)
.withSupportedChannelTypeUIDs(LeapMotionBindingConstants.CHANNEL_GESTURE_UID).build();
@Override
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
return Stream.of(UID_SWITCH, UID_DIMMER, UID_COLOR).collect(Collectors.toSet());
}
@Override
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
return Stream.of(SWITCH_TYPE, DIMMER_TYPE, COLOR_TYPE).collect(Collectors.toSet());
}
@Override
public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(Channel channel, @Nullable String itemType) {
return getSuggestedProfileTypeUID(channel.getChannelTypeUID(), itemType);
}
@Override
public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(ChannelType channelType, @Nullable String itemType) {
return getSuggestedProfileTypeUID(channelType.getUID(), itemType);
}
private @Nullable ProfileTypeUID getSuggestedProfileTypeUID(@Nullable ChannelTypeUID channelTypeUID,
@Nullable String itemType) {
if (LeapMotionBindingConstants.CHANNEL_GESTURE_UID.equals(channelTypeUID) && itemType != null) {
switch (itemType) {
case CoreItemFactory.SWITCH:
return UID_SWITCH;
case CoreItemFactory.DIMMER:
return UID_DIMMER;
case CoreItemFactory.COLOR:
return UID_COLOR;
default:
return null;
}
}
return null;
}
@Override
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
ProfileContext profileContext) {
if (UID_SWITCH.equals(profileTypeUID)) {
return new LeapMotionSwitchProfile(callback);
} else if (UID_DIMMER.equals(profileTypeUID)) {
return new LeapMotionDimmerProfile(callback, profileContext);
} else if (UID_COLOR.equals(profileTypeUID)) {
return new LeapMotionColorProfile(callback);
} else {
return null;
}
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.leapmotion.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.profiles.ProfileCallback;
import org.openhab.core.thing.profiles.ProfileTypeUID;
import org.openhab.core.thing.profiles.TriggerProfile;
import org.openhab.core.types.State;
/**
* The {@link LeapMotionSwitchProfile} class implements the behavior when being linked to a Switch item.
*
* @author Kai Kreuzer - Initial contribution
*/
@NonNullByDefault
public class LeapMotionSwitchProfile implements TriggerProfile {
private ProfileCallback callback;
private boolean lastState = false;
public LeapMotionSwitchProfile(ProfileCallback callback) {
this.callback = callback;
}
@Override
public ProfileTypeUID getProfileTypeUID() {
return LeapMotionProfileFactory.UID_SWITCH;
}
@Override
public void onStateUpdateFromItem(State state) {
lastState = OnOffType.ON.equals(state.as(OnOffType.class));
}
@Override
public void onTriggerFromHandler(String event) {
if (event.equals(LeapMotionBindingConstants.GESTURE_TAP)) {
callback.sendCommand(lastState ? OnOffType.OFF : OnOffType.ON);
}
}
}

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2010-2020 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.leapmotion.internal.discovery;
import java.util.Collections;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.leapmotion.internal.LeapMotionBindingConstants;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import com.leapmotion.leap.Controller;
import com.leapmotion.leap.Listener;
/**
* This is a discovery service, which finds locally attached LeapMotion controllers and adds them to the Inbox
*
* @author Kai Kreuzer - Initial contribution
*
*/
@NonNullByDefault
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.leapmotion")
public class LeapMotionDiscoveryService extends AbstractDiscoveryService {
private @NonNullByDefault({}) Controller leapController;
private @NonNullByDefault({}) Listener listener;
public LeapMotionDiscoveryService() throws IllegalArgumentException {
super(Collections.singleton(LeapMotionBindingConstants.THING_TYPE_CONTROLLER), 10, true);
}
@Override
protected void activate(@Nullable Map<String, @Nullable Object> configProperties) {
leapController = new Controller();
listener = new Listener() {
@Override
public void onConnect(@Nullable Controller c) {
createDiscoveryResult();
}
@Override
public void onDisconnect(@Nullable Controller c) {
removeDiscoveryResult();
}
};
super.activate(configProperties);
}
@Override
protected void deactivate() {
leapController.removeListener(listener);
listener = null;
leapController.delete();
leapController = null;
super.deactivate();
}
@Override
protected void startScan() {
if (leapController.isConnected()) {
createDiscoveryResult();
}
}
@Override
protected void startBackgroundDiscovery() {
super.startBackgroundDiscovery();
leapController.addListener(listener);
}
private void createDiscoveryResult() {
DiscoveryResult result = DiscoveryResultBuilder
.create(new ThingUID(LeapMotionBindingConstants.THING_TYPE_CONTROLLER, "local"))
.withLabel("Leap Motion Controller").build();
thingDiscovered(result);
}
private void removeDiscoveryResult() {
removeOlderResults(System.currentTimeMillis());
}
}

View File

@@ -0,0 +1,173 @@
/**
* Copyright (c) 2010-2020 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.leapmotion.internal.handler;
import static org.openhab.binding.leapmotion.internal.LeapMotionBindingConstants.*;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.leapmotion.leap.CircleGesture;
import com.leapmotion.leap.Controller;
import com.leapmotion.leap.FingerList;
import com.leapmotion.leap.Frame;
import com.leapmotion.leap.Gesture;
import com.leapmotion.leap.GestureList;
import com.leapmotion.leap.Hand;
import com.leapmotion.leap.Listener;
/**
* The {@link LeapMotionHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Kai Kreuzer - Initial contribution
* @author Thomas Eichstädt-Engelen - Initial version of listener logic
*
*/
@NonNullByDefault
public class LeapMotionHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(LeapMotionHandler.class);
private @NonNullByDefault({}) LeapMotionListener listener;
private @NonNullByDefault({}) Controller leapController;
public LeapMotionHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void initialize() {
this.listener = new LeapMotionListener();
leapController = new Controller();
leapController.setPolicyFlags(Controller.PolicyFlag.POLICY_BACKGROUND_FRAMES);
leapController.addListener(this.listener);
if (leapController.isConnected()) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE);
}
}
@Override
public void dispose() {
leapController.removeListener(this.listener);
listener = null;
leapController.delete();
leapController = null;
}
private class LeapMotionListener extends Listener {
private static final int RATE_LIMIT_IN_MS = 200;
private long lastEvent = 0;
private boolean noHand;
public LeapMotionListener() {
}
@Override
public void onConnect(@Nullable Controller controller) {
if (controller != null) {
controller.enableGesture(Gesture.Type.TYPE_KEY_TAP);
controller.enableGesture(Gesture.Type.TYPE_CIRCLE);
}
updateStatus(ThingStatus.ONLINE);
}
@Override
public void onDisconnect(@Nullable Controller controller) {
updateStatus(ThingStatus.OFFLINE);
}
@Override
public void onFrame(@Nullable Controller controller) {
if (controller == null) {
return;
}
Frame frame = controller.frame();
GestureList gestures = frame.gestures();
for (int i = 0; i < gestures.count(); i++) {
Gesture gesture = gestures.get(i);
switch (gesture.type()) {
case TYPE_KEY_TAP:
logger.debug("Key tap");
triggerChannel(CHANNEL_GESTURE, GESTURE_TAP);
break;
case TYPE_CIRCLE:
CircleGesture circle = new CircleGesture(gesture);
// Calculate clock direction using the angle between circle normal and pointable
boolean clockwiseness;
if (circle.pointable().direction().angleTo(circle.normal()) <= Math.PI / 4) {
// Clockwise if angle is less than 90 degrees
clockwiseness = true;
} else {
clockwiseness = false;
}
// Calculate angle swept since last frame
if (circle.state() == com.leapmotion.leap.Gesture.State.STATE_UPDATE) {
if (System.nanoTime() > lastEvent + TimeUnit.MILLISECONDS.toNanos(RATE_LIMIT_IN_MS)) {
logger.debug("Circle (clockwise={})", clockwiseness);
if (clockwiseness) {
triggerChannel(CHANNEL_GESTURE, GESTURE_CLOCKWISE);
} else {
triggerChannel(CHANNEL_GESTURE, GESTURE_ANTICLOCKWISE);
}
lastEvent = System.nanoTime();
}
}
break;
default:
logger.debug("Unknown gesture type.");
break;
}
}
if (!frame.hands().isEmpty()) {
noHand = false;
// Get the first hand
Hand hand = frame.hands().get(0);
// Check if the hand has any fingers
FingerList fingers = hand.fingers();
if (System.nanoTime() > lastEvent + TimeUnit.MILLISECONDS.toNanos(RATE_LIMIT_IN_MS)) {
int height = (int) hand.palmPosition().getY();
logger.debug("Fingers shown {} @ {}", fingers.count(), height);
triggerChannel(CHANNEL_GESTURE, GESTURE_FINGERS + fingers.count() + "_" + height);
lastEvent = System.nanoTime();
}
} else {
if (!noHand) {
noHand = true;
logger.debug("No hand");
triggerChannel(CHANNEL_GESTURE, GESTURE_NOHAND);
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="leapmotion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>Leap Motion Binding</name>
<description>This is the binding for Leap Motion controllers.</description>
</binding:binding>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="profile:leapmotion:dimmer">
<parameter name="mode" type="text" required="false">
<label>Mode</label>
<description>The mode for interpreting gestures. Either it dims depending on the number of shown fingers or it uses
the height of the hand over the controller.</description>
<options>
<option value="fingers">Number of Fingers</option>
<option value="height">Height of Hand</option>
</options>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="leapmotion"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="controller">
<label>Leap Motion Controller</label>
<description>A Leap Motion Gesture Sensor</description>
<channels>
<channel id="gesture" typeId="gesture"/>
</channels>
</thing-type>
<channel-type id="gesture">
<kind>trigger</kind>
<label>Gestures</label>
<description>Provides recognized gestures from the controller</description>
</channel-type>
</thing:thing-descriptions>