From 601415db170a158f2305af7809d04ede4e7c585e Mon Sep 17 00:00:00 2001 From: Patrick Fink Date: Sat, 5 Dec 2020 19:11:38 +0100 Subject: [PATCH] [bluetooth.enoceanble] Initial contribution EnOcean BLE Binding (#9223) * [bluetooth.enoceanble] Initial contribution EnOcean BLE Binding Signed-off-by: Patrick Fink * Put @Nullable annotation inline Signed-off-by: Patrick Fink Co-authored-by: Connor Petty * [bluetooth.enoceanble] Add binding to CODEOWNERS Signed-off-by: Patrick Fink * [bluetooth.enoceanble] Remove obsolete transport serial feature Signed-off-by: Patrick Fink * [bluetooth.enoceanble] Add binding to footer.xml Signed-off-by: Patrick Fink * [bluetooth.enoceanble] Replace ruuvitag leftovers Signed-off-by: Patrick Fink * [bluetooth.enoceanble] Remove tinyB reference Signed-off-by: Patrick Fink Co-authored-by: Connor Petty --- CODEOWNERS | 1 + .../NOTICE | 14 +++ .../README.md | 60 ++++++++++ .../pom.xml | 25 ++++ .../src/main/feature/feature.xml | 11 ++ .../internal/EnoceanBleBindingConstants.java | 35 ++++++ .../EnoceanBleDiscoveryParticipant.java | 84 ++++++++++++++ .../internal/EnoceanBleHandlerFactory.java | 54 +++++++++ .../internal/EnoceanBlePtm215Event.java | 67 +++++++++++ .../internal/EnoceanBleRockerHandler.java | 83 ++++++++++++++ .../resources/OH-INF/thing/enoceanble.xml | 30 +++++ .../internal/EnoceanBlePtm215EventTest.java | 107 ++++++++++++++++++ bundles/pom.xml | 1 + .../src/main/resources/footer.xml | 1 + 14 files changed, 573 insertions(+) create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/NOTICE create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/README.md create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/pom.xml create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleBindingConstants.java create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleDiscoveryParticipant.java create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleHandlerFactory.java create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBlePtm215Event.java create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleRockerHandler.java create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/src/main/resources/OH-INF/thing/enoceanble.xml create mode 100644 bundles/org.openhab.binding.bluetooth.enoceanble/src/test/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBlePtm215EventTest.java diff --git a/CODEOWNERS b/CODEOWNERS index c9b9feb2b26..bdd35f5975c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/NOTICE b/bundles/org.openhab.binding.bluetooth.enoceanble/NOTICE new file mode 100644 index 00000000000..4205fa32370 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/NOTICE @@ -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 + diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/README.md b/bundles/org.openhab.binding.bluetooth.enoceanble/README.md new file mode 100644 index 00000000000..02f6514fd8c --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/README.md @@ -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"] } +``` \ No newline at end of file diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/pom.xml b/bundles/org.openhab.binding.bluetooth.enoceanble/pom.xml new file mode 100644 index 00000000000..0099468adcc --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/pom.xml @@ -0,0 +1,25 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.0.0-SNAPSHOT + + + org.openhab.binding.bluetooth.enoceanble + + openHAB Add-ons :: Bundles :: EnOcean Bluetooth Adapter + + + + org.openhab.addons.bundles + org.openhab.binding.bluetooth + ${project.version} + provided + + + diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/feature/feature.xml b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/feature/feature.xml new file mode 100644 index 00000000000..7ac7db069a1 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/feature/feature.xml @@ -0,0 +1,11 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${project.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth/${project.version} + mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.enoceanble/${project.version} + + + diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleBindingConstants.java b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleBindingConstants.java new file mode 100644 index 00000000000..686758b2b1b --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleBindingConstants.java @@ -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"; +} diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleDiscoveryParticipant.java b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleDiscoveryParticipant.java new file mode 100644 index 00000000000..41ddaf764b6 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleDiscoveryParticipant.java @@ -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 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 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(); + } +} diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleHandlerFactory.java b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleHandlerFactory.java new file mode 100644 index 00000000000..a1c05a93f59 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleHandlerFactory.java @@ -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 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; + } +} diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBlePtm215Event.java b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBlePtm215Event.java new file mode 100644 index 00000000000..047923be387 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBlePtm215Event.java @@ -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"); + } +} diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleRockerHandler.java b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleRockerHandler.java new file mode 100644 index 00000000000..658bb6162e1 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBleRockerHandler.java @@ -0,0 +1,83 @@ +/** + * 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.BeaconBluetoothHandler; +import org.openhab.binding.bluetooth.notification.BluetoothScanNotification; +import org.openhab.core.thing.CommonTriggerEvents; +import org.openhab.core.thing.Thing; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link EnoceanBleRockerHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Patrick Fink - Initial contribution + */ +@NonNullByDefault +public class EnoceanBleRockerHandler extends BeaconBluetoothHandler { + + private final Logger logger = LoggerFactory.getLogger(EnoceanBleRockerHandler.class); + + public EnoceanBleRockerHandler(Thing thing) { + super(thing); + } + + @Override + public void onScanRecordReceived(BluetoothScanNotification scanNotification) { + super.onScanRecordReceived(scanNotification); + final byte[] manufacturerData = scanNotification.getManufacturerData(); + logger.debug("Received new scan notification for {}: {}", device.getAddress(), manufacturerData); + try { + if (manufacturerData != null && manufacturerData.length > 0) { + EnoceanBlePtm215Event event = new EnoceanBlePtm215Event(manufacturerData); + logger.debug("Parsed manufacturer data to PTM215B event: {}", event); + triggerChannel(resolveChannel(event), resolveTriggerEvent(event)); + } + } catch (IllegalStateException e) { + logger.warn("PTM215B event could not be parsed correctly, exception occured:", e); + } + } + + protected String resolveChannel(EnoceanBlePtm215Event event) { + if (event.isButton1()) { + return EnoceanBleBindingConstants.CHANNEL_ID_ROCKER1; + } + if (event.isButton2()) { + return EnoceanBleBindingConstants.CHANNEL_ID_ROCKER2; + } + throw new IllegalStateException( + "PTM215B event cannot be resolved correctly to a channel, probably received message is invalid"); + } + + protected String resolveTriggerEvent(EnoceanBlePtm215Event event) { + if (event.isDir1()) { + if (event.isPressed()) { + return CommonTriggerEvents.DIR1_PRESSED; + } else { + return CommonTriggerEvents.DIR1_RELEASED; + } + } + if (event.isDir2()) { + if (event.isPressed()) { + return CommonTriggerEvents.DIR2_PRESSED; + } else { + return CommonTriggerEvents.DIR2_RELEASED; + } + } + throw new IllegalStateException( + "PTM215B event cannot be resolved correctly to an openHAB event, probably received message is invalid"); + } +} diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/resources/OH-INF/thing/enoceanble.xml b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/resources/OH-INF/thing/enoceanble.xml new file mode 100644 index 00000000000..6f353cdb39c --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/src/main/resources/OH-INF/thing/enoceanble.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + An Enocean BLE Rocker (PTM 215B) + + + + + + + + + + Bluetooth address in XX:XX:XX:XX:XX:XX format + + + + + diff --git a/bundles/org.openhab.binding.bluetooth.enoceanble/src/test/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBlePtm215EventTest.java b/bundles/org.openhab.binding.bluetooth.enoceanble/src/test/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBlePtm215EventTest.java new file mode 100644 index 00000000000..45552ec9816 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.enoceanble/src/test/java/org/openhab/binding/bluetooth/enoceanble/internal/EnoceanBlePtm215EventTest.java @@ -0,0 +1,107 @@ +/** + * 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 static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * {@link EnoceanBlePtm215EventTest} is testing the {@link EnoceanBlePtm215Event} class + * using some sample manufacturerData + * + * @author Patrick Fink - Initial contribution + */ +@NonNullByDefault +public class EnoceanBlePtm215EventTest { + + @Test + public void testButton1Dir1Pressed() { + byte[] button1Dir2PressedBytes = { -38, 3, 53, 5, 0, 0, 17, 54, 81, 61, 54 }; + EnoceanBlePtm215Event button1Dir2PressedEvent = new EnoceanBlePtm215Event(button1Dir2PressedBytes); + + assertTrue(button1Dir2PressedEvent.isPressed()); + assertTrue(button1Dir2PressedEvent.isButton1()); + assertTrue(button1Dir2PressedEvent.isDir1()); + } + + @Test + public void testButton1Dir1Released() { + byte[] button1Dir2ReleasedBytes = { -38, 3, 54, 5, 0, 0, 16, -83, -25, 103, 3 }; + EnoceanBlePtm215Event button1Dir2ReleasedEvent = new EnoceanBlePtm215Event(button1Dir2ReleasedBytes); + + assertFalse(button1Dir2ReleasedEvent.isPressed()); + assertTrue(button1Dir2ReleasedEvent.isButton1()); + assertTrue(button1Dir2ReleasedEvent.isDir1()); + } + + @Test + public void testButton1Dir2Pressed() { + byte[] button1Dir2PressedBytes = { -38, 3, -97, 32, 0, 0, 9, -99, -15, 16, -74 }; + EnoceanBlePtm215Event button1Dir2PressedEvent = new EnoceanBlePtm215Event(button1Dir2PressedBytes); + + assertTrue(button1Dir2PressedEvent.isPressed()); + assertTrue(button1Dir2PressedEvent.isButton1()); + assertTrue(button1Dir2PressedEvent.isDir2()); + } + + @Test + public void testButton1Dir2Released() { + byte[] button1Dir2ReleasedBytes = { -38, 3, -96, 32, 0, 0, 8, -87, -96, 98, 74 }; + EnoceanBlePtm215Event button1Dir2ReleasedEvent = new EnoceanBlePtm215Event(button1Dir2ReleasedBytes); + + assertFalse(button1Dir2ReleasedEvent.isPressed()); + assertTrue(button1Dir2ReleasedEvent.isButton1()); + assertTrue(button1Dir2ReleasedEvent.isDir2()); + } + + @Test + public void testButton2Dir1Pressed() { + byte[] button2Dir1PressedBytes = { -38, 3, -91, 32, 0, 0, 5, 89, 104, 24, -67 }; + EnoceanBlePtm215Event button2Dir1PressedEvent = new EnoceanBlePtm215Event(button2Dir1PressedBytes); + + assertTrue(button2Dir1PressedEvent.isPressed()); + assertTrue(button2Dir1PressedEvent.isButton2()); + assertTrue(button2Dir1PressedEvent.isDir1()); + } + + @Test + public void testButton2Dir1Released() { + byte[] button2Dir1ReleasedBytes = { -38, 3, -90, 32, 0, 0, 4, 120, 52, -115, 61 }; + EnoceanBlePtm215Event button2Dir1ReleasedEvent = new EnoceanBlePtm215Event(button2Dir1ReleasedBytes); + assertFalse(button2Dir1ReleasedEvent.isPressed()); + assertTrue(button2Dir1ReleasedEvent.isButton2()); + assertTrue(button2Dir1ReleasedEvent.isDir1()); + } + + @Test + public void testButton2Dir2Pressed() { + byte[] button2Dir2PressedBytes = { -38, 3, -95, 32, 0, 0, 3, -105, -33, 62, 45 }; + EnoceanBlePtm215Event button2Dir1PressedEvent = new EnoceanBlePtm215Event(button2Dir2PressedBytes); + assertTrue(button2Dir1PressedEvent.isPressed()); + assertTrue(button2Dir1PressedEvent.isButton2()); + assertTrue(button2Dir1PressedEvent.isDir2()); + } + + @Test + public void testButton2Dir2Released() { + byte[] button2Dir2ReleasedBytes = { -38, 3, -94, 32, 0, 0, 2, 59, 118, 52, -69 }; + EnoceanBlePtm215Event button2Dir2ReleasedEvent = new EnoceanBlePtm215Event(button2Dir2ReleasedBytes); + + assertFalse(button2Dir2ReleasedEvent.isPressed()); + assertTrue(button2Dir2ReleasedEvent.isButton2()); + assertTrue(button2Dir2ReleasedEvent.isDir2()); + } +} diff --git a/bundles/pom.xml b/bundles/pom.xml index 9b25b075474..f1dd83fb821 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -59,6 +59,7 @@ org.openhab.binding.bluetooth.bluez org.openhab.binding.bluetooth.blukii org.openhab.binding.bluetooth.daikinmadoka + org.openhab.binding.bluetooth.enoceanble org.openhab.binding.bluetooth.generic org.openhab.binding.bluetooth.roaming org.openhab.binding.bluetooth.ruuvitag diff --git a/features/openhab-addons/src/main/resources/footer.xml b/features/openhab-addons/src/main/resources/footer.xml index 9180ad4c761..e99c26921b7 100644 --- a/features/openhab-addons/src/main/resources/footer.xml +++ b/features/openhab-addons/src/main/resources/footer.xml @@ -10,6 +10,7 @@ mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.bluegiga/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.blukii/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.daikinmadoka/${project.version} + mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.enoceanble/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.generic/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.roaming/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.ruuvitag/${project.version}