Skip to content

Commit

Permalink
[bluetooth.enoceanble] Initial contribution EnOcean BLE Binding (open…
Browse files Browse the repository at this point in the history
…hab#9223)

* [bluetooth.enoceanble] Initial contribution EnOcean BLE Binding

Signed-off-by: Patrick Fink <mail@pfink.de>

* Put @nullable annotation inline

Signed-off-by: Patrick Fink <mail@pfink.de>

Co-authored-by: Connor Petty <mistercpp2000@gmail.com>

* [bluetooth.enoceanble] Add binding to CODEOWNERS

Signed-off-by: Patrick Fink <mail@pfink.de>

* [bluetooth.enoceanble] Remove obsolete transport serial feature

Signed-off-by: Patrick Fink <mail@pfink.de>

* [bluetooth.enoceanble] Add binding to footer.xml

Signed-off-by: Patrick Fink <mail@pfink.de>

* [bluetooth.enoceanble] Replace ruuvitag leftovers

Signed-off-by: Patrick Fink <mail@pfink.de>

* [bluetooth.enoceanble] Remove tinyB reference

Signed-off-by: Patrick Fink <mail@pfink.de>

Co-authored-by: Connor Petty <mistercpp2000@gmail.com>
Signed-off-by: Christian Grasser <info@christiangrasser.at>
  • Loading branch information
2 people authored and Christian Grasser committed Dec 7, 2020
1 parent 5517641 commit 10f60a3
Show file tree
Hide file tree
Showing 14 changed files with 573 additions and 0 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
/bundles/org.openhab.binding.bluetooth.bluez/ @cdjackson @kaikreuzer
/bundles/org.openhab.binding.bluetooth.blukii/ @kaikreuzer
/bundles/org.openhab.binding.bluetooth.daikinmadoka/ @blafois
/bundles/org.openhab.binding.bluetooth.enoceanble/ @pfink
/bundles/org.openhab.binding.bluetooth.generic/ @cpmeister
/bundles/org.openhab.binding.bluetooth.roaming/ @cpmeister
/bundles/org.openhab.binding.bluetooth.ruuvitag/ @ssalonen
Expand Down
14 changes: 14 additions & 0 deletions bundles/org.openhab.binding.bluetooth.enoceanble/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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

60 changes: 60 additions & 0 deletions bundles/org.openhab.binding.bluetooth.enoceanble/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# EnOcean BLE Binding

This binding adds support for the
[EnOcean BLE PTM215B](https://www.enocean.com/de/produkte/enocean_module_24ghz_ble/ptm-215b/) rocker.

**Currently, it does not work with the
[BlueZ Binding](https://www.openhab.org/addons/bindings/bluetooth.bluez)**.
The recommended / tested Bluetooth bridge binding is the
[BlueGiga Binding](https://www.openhab.org/addons/bindings/bluetooth.bluegiga).

## Supported Things

Only a single thing type is added by this extension:

| Thing Type ID | Description |
| --------------- | ------------------------- |
| ptm215b | The EnOcean PTM 215B Rocker |


These rockers are battery-less, the necessary energy for the BLE transmission is generated by the button click itself
and completely depleted after 3 attempts of a "fire & forget" (unidirectional) BLE transmission.
This means, the rockers are only "on" during the button click, so there is no possibility for openHAB to actively
retrieve the online status. Therefore, initial online status will always be "UNKNOWN" until the rocker is used.

## Discovery

As any other Bluetooth device, EnOcean BLE rockers are discovered automatically by the corresponding bridge. It's
necessary to click any button of the rocker up to 15 times in quick succession to get it discovered.

## Thing Configuration

There is only a single configuration parameter `address`, which corresponds to the Bluetooth address of the device
(in format "XX:XX:XX:XX:XX:XX"). Normally, the address is printed on the back of the rocker.

## Channels

An EnOcean PTM 215B Rocker has the following channels:

| Channel ID | Item Type | Description |
| ------------------------- | ------------------------ | ------------------------------ |
| rocker1 | system.rawrocker | In case of a dual rocker, the first rocker. In case of a single rocker, usually this channel has to be used. Anyhow, it depends from the implementation of the manufacturer of the rocker, so it could be that events are coming in on the second rocker channel. |
| rocker2 | system.rawrocker | In case of a dual rocker, the second rocker. |

## Example

demo.things:

```
Bridge bluetooth:bluegiga:bluegiga0 "Bluegiga Adapter" [ port="/dev/ttyBLUEGIGA", discovery=false ] {
Thing ptm215b rocker_livingroom "Rocker Living Room" [ address = "E2:15:00:00:53:F9" ]
Thing ptm215b rocker_kitchen "Rocker Kitchen" [ address = "E2:15:00:00:53:98" ]
}
```

demo.items:

```
Dimmer Light_LivingRoom { channel="milight:rgbLed:milight2:4:ledbrightness", channel="bluetooth:ptm215b:bluegiga0:rocker_livingroom:rocker1" [profile="rawrocker-to-on-off"], channel="bluetooth:ptm215b:bluegiga0:rocker_kitchen:rocker1" [profile="rawrocker-to-on-off"] } // We have a combined kitchen / livingroom, so we control the living room lights with switches from the living room and from the kitchen
Switch Light_Kitchen { channel="hue:group:1:kitchen-bulbs:switch", channel="bluetooth:ptm215b:bluegiga0:rocker_kitchen:rocker2" [profile="rawrocker-to-on-off"] }
```
25 changes: 25 additions & 0 deletions bundles/org.openhab.binding.bluetooth.enoceanble/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.bluetooth.enoceanble</artifactId>

<name>openHAB Add-ons :: Bundles :: EnOcean Bluetooth Adapter</name>

<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.bluetooth.enoceanble-${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/${project.version}/xml/features</repository>

<feature name="openhab-binding-bluetooth-enoceanble" description="Bluetooth Binding EnOcean BLE" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth/${project.version}</bundle>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.enoceanble/${project.version}</bundle>
</feature>

</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* 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.bluetooth.enoceanble.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.bluetooth.BluetoothBindingConstants;
import org.openhab.core.thing.ThingTypeUID;

/**
* The {@link EnoceanBleBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Patrick Fink - Initial contribution
*/
@NonNullByDefault
public class EnoceanBleBindingConstants {

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_PTM215B = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID,
"ptm215b");

// Channel IDs
public static final String CHANNEL_ID_ROCKER1 = "rocker1";
public static final String CHANNEL_ID_ROCKER2 = "rocker2";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* 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.bluetooth.enoceanble.internal;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bluetooth.BluetoothBindingConstants;
import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryDevice;
import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryParticipant;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This discovery participant is able to recognize enoceanble devices and create discovery results for them.
*
* @author Patrick Fink - Initial contribution
*
*/
@NonNullByDefault
@Component
public class EnoceanBleDiscoveryParticipant implements BluetoothDiscoveryParticipant {
private final Logger logger = LoggerFactory.getLogger(EnoceanBleDiscoveryParticipant.class);

private static final int ENOCEAN_COMPANY_ID = 986;

@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return Collections.singleton(EnoceanBleBindingConstants.THING_TYPE_PTM215B);
}

@Override
public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) {
Integer manufacturerId = device.getManufacturerId();
logger.warn("Discovered device {} with manufacturerId {} and name {}", device.getAddress(), manufacturerId,
device.getName());
if (manufacturerId != null && manufacturerId == ENOCEAN_COMPANY_ID) {
return new ThingUID(EnoceanBleBindingConstants.THING_TYPE_PTM215B, device.getAdapter().getUID(),
device.getAddress().toString().toLowerCase().replace(":", ""));
}
return null;
}

@Override
public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
ThingUID thingUID = getThingUID(device);
if (thingUID == null) {
return null;
}
String label = "Enocean PTM215B Rocker";
Map<String, Object> properties = new HashMap<>();
properties.put(BluetoothBindingConstants.CONFIGURATION_ADDRESS, device.getAddress().toString());
properties.put(Thing.PROPERTY_VENDOR, "EnOcean GmbH");
Integer txPower = device.getTxPower();
if (txPower != null) {
properties.put(BluetoothBindingConstants.PROPERTY_TXPOWER, Integer.toString(txPower));
}

// Create the discovery result and add to the inbox
return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withRepresentationProperty(BluetoothBindingConstants.CONFIGURATION_ADDRESS)
.withBridge(device.getAdapter().getUID()).withLabel(label).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* 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.bluetooth.enoceanble.internal;

import java.util.Collections;
import java.util.Set;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
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 EnoceanBleHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Patrick Fink - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.enoceanble")
public class EnoceanBleHandlerFactory extends BaseThingHandlerFactory {

private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.singleton(EnoceanBleBindingConstants.THING_TYPE_PTM215B);

@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(EnoceanBleBindingConstants.THING_TYPE_PTM215B)) {
return new EnoceanBleRockerHandler(thing);
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* 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.bluetooth.enoceanble.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The {@link EnoceanBlePtm215Event} class is parsing the BLE manufacturer data into an event object.
*
* @author Patrick Fink - Initial contribution
*/
@NonNullByDefault
public class EnoceanBlePtm215Event {

private static final byte PRESSED = 0x1;

private static final byte BUTTON1_DIR1 = 0x10;
private static final byte BUTTON1_DIR2 = 0x8;
private static final byte BUTTON2_DIR1 = 0x4;
private static final byte BUTTON2_DIR2 = 0x2;

private final byte byteState;

public EnoceanBlePtm215Event(byte[] manufacturerData) {
byteState = manufacturerData[6];
}

public boolean isPressed() {
return checkFlag(PRESSED);
}

public boolean isButton1() {
return checkFlag(BUTTON1_DIR1) || checkFlag(BUTTON1_DIR2);
}

public boolean isButton2() {
return checkFlag(BUTTON2_DIR1) || checkFlag(BUTTON2_DIR2);
}

public boolean isDir1() {
return checkFlag(BUTTON1_DIR1) || checkFlag(BUTTON2_DIR1);
}

public boolean isDir2() {
return checkFlag(BUTTON1_DIR2) || checkFlag(BUTTON2_DIR2);
}

private boolean checkFlag(int flag) {
return (byteState & flag) == flag;
}

@Override
public String toString() {
return "Button " + (isButton1() ? 1 : 2) + " Dir " + (isDir1() ? 1 : 2) + " "
+ (isPressed() ? "PRESSED" : "RELEASED");
}
}
Loading

0 comments on commit 10f60a3

Please sign in to comment.