added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.amazondashbutton/.classpath
Normal file
32
bundles/org.openhab.binding.amazondashbutton/.classpath
Normal 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="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="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="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.amazondashbutton/.project
Normal file
23
bundles/org.openhab.binding.amazondashbutton/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.amazondashbutton</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>
|
||||
20
bundles/org.openhab.binding.amazondashbutton/NOTICE
Normal file
20
bundles/org.openhab.binding.amazondashbutton/NOTICE
Normal 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
|
||||
|
||||
pcap4J
|
||||
* License: MIT License
|
||||
* Project: https://www.pcap4j.org
|
||||
* Source: https://github.com/kaitoy/pcap4j
|
||||
156
bundles/org.openhab.binding.amazondashbutton/README.md
Normal file
156
bundles/org.openhab.binding.amazondashbutton/README.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Amazon Dash Button Binding
|
||||
|
||||
The [Amazon Dash Button](https://www.amazon.com/Dash-Buttons/b?node=10667898011) is a cheap and small Wi-Fi connected device to order products from Amazon with the simple press of a button.
|
||||
This Binding allows you to integrate Dash Buttons into your home automation setup.
|
||||
|
||||
The Binding code is inspired by [hortinstein/node-dash-button](https://github.com/hortinstein/node-dash-button).
|
||||
|
||||
**Warning:**
|
||||
The Dash Button will try to contact the Amazon servers every time the button is pressed.
|
||||
This might not be in line with your privacy preferences but can be prevented.
|
||||
Please refer to the ["Preventing Communication with Amazon Servers"](#no-phonehome) section for details.
|
||||
|
||||
**Response Time:**
|
||||
Please be aware, that due to the operation method of this binding, the response time for a button press can be rather high (up to five seconds).
|
||||
You might want to keep that in mind during product selection or task assignment.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The Binding uses [Pcap4J](https://www.pcap4j.org/) in order to capture `ARP` and `BOOTP` requests send by the Amazon Dash Button.
|
||||
Buttons will hence only be usable within the same network as your openHAB instance.
|
||||
|
||||
Start with installing libpcap (for Mac/Linux/Unix) or WinPcap (for Windows) on your computer.
|
||||
They are native libraries that power the core functionalities of Pcap4J.
|
||||
|
||||
**Note:**
|
||||
Pcap4J needs administrator/root privileges.
|
||||
Instructions for Debian/Ubuntu are given below.
|
||||
|
||||
### Installing libpcap on Debian/Ubuntu
|
||||
|
||||
Installing [libpcap](https://www.tcpdump.org/) should be as simple as:
|
||||
|
||||
```shell
|
||||
sudo apt-get install libpcap-dev
|
||||
```
|
||||
|
||||
You can run Pcap4J with a non-root openHAB user by granting capabilities `CAP_NET_RAW` and `CAP_NET_ADMIN` to the openHAB java environment by the following command:
|
||||
|
||||
```shell
|
||||
sudo setcap cap_net_raw,cap_net_admin=eip $(realpath /usr/bin/java)
|
||||
```
|
||||
|
||||
Be aware of other capabilities which were previously set by setcap.
|
||||
**These capabilities will be overwritten!**
|
||||
You can see which capabilities have already been set with the command:
|
||||
|
||||
```shell
|
||||
sudo getcap $(realpath /usr/bin/java)
|
||||
```
|
||||
|
||||
If you need multiple capabilities (like "cap_net_bind_service" for the Network binding), you have to add them like this:
|
||||
|
||||
```shell
|
||||
sudo setcap 'cap_net_raw,cap_net_admin=+eip cap_net_bind_service=+ep' $(realpath /usr/bin/java)
|
||||
```
|
||||
|
||||
You need to restart openHAB for the capabilities change to take effect.
|
||||
|
||||
### Installing WinPcap on Windows
|
||||
|
||||
On a Windows system there are two options to go with.
|
||||
|
||||
1. The preferred solution is [WinPcap](https://www.winpcap.org) if your network interface is supported.
|
||||
2. An alternative option is [npcap](https://github.com/nmap/npcap) with the settings "WinPcap 4.1.3 compatibility" and "Raw 802.11 Packet Capture"
|
||||
|
||||
### Installing libpcap on Other Operating Systems
|
||||
|
||||
The installation methods might differ.
|
||||
A few known operating systems are:
|
||||
|
||||
| Operating System | Command |
|
||||
|:-----------------|:----------------------------|
|
||||
| CentOS | `yum install libpcap-devel` |
|
||||
| Mac | `brew install libpcap` |
|
||||
|
||||
## Setup Dash Button
|
||||
|
||||
Setting up your Dash Button is as simple as following the instructions provided by [Amazon](https://www.amazon.com/Dash-Buttons/b?node=10667898011) **EXCEPT FOR THE LAST STEP**.
|
||||
Follow the instructions to set up the Dash Button in their mobile app.
|
||||
When you get to the step where it asks you to pick which product you want to map it to, just quit the setup process.
|
||||
|
||||
{: #no-phonehome}
|
||||
## Preventing Communication with Amazon Servers
|
||||
|
||||
Every time a Dash Button is pressed a request will be sent to the Amazon servers.
|
||||
If no product was configured for the Button, a notification will be presented by the Amazon app on your smartphone.
|
||||
|
||||
To prevent the Dash Button from contacting the Amazon Servers, block Internet access for the device.
|
||||
Please refer to the documentation of your network's router for details.
|
||||
If your network doesn't provide that option, you can at least deal with the notifications by either uninstalling the Amazon app or disabling notifications for it (possible on most smartphone OSs).
|
||||
|
||||
It has shown that blocking the Dash Button communication with the Amazon servers will provoke reconnection attempts.
|
||||
This increased amount of communication causes a reduced overall battery life.
|
||||
The built-in AAA battery can be easily replaced.
|
||||
|
||||
Preventing the communication with the Amazon servers or the Amazon app is **not** necessary to integrate the Dash Button in openHAB.
|
||||
|
||||
## Supported Things
|
||||
|
||||
There is one supported Thing, the "Amazon Dash Button".
|
||||
|
||||
## Discovery
|
||||
|
||||
Background discovery is not supported as it is not possible to distinguish
|
||||
between a Dash Button and other Amazon devices like the Kindle,
|
||||
a Fire TV or an Echo speaker.
|
||||
|
||||
You can start the discovery process for Dash Button devices manually.
|
||||
While openHAB is in the scanning process, press the button on the Dash to be recognized and added to your Inbox.
|
||||
|
||||
**Caution:**
|
||||
You have to be aware that other Amazon devices might pop up in your Inbox if they send an `ARP` request while scanning for Dash Buttons.
|
||||
You can ignore these devices in your Inbox.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
### Amazon Dash Button
|
||||
|
||||
- `macAddress` - The MAC address of the Amazon Dash Button.
|
||||
|
||||
- `pcapNetworkInterfaceName` - The network interface which receives the packets of the Amazon Dash Button.
|
||||
|
||||
- `packetInterval` - Often a single button press is recognized multiple times.
|
||||
You can specify how long any further detected button pressed should be ignored after one click was processed.
|
||||
The parameter is optional and 5000ms by default.
|
||||
|
||||
For manual definition of a `dashbutton` Thing the MAC address can either be taken from the discovery output or can e.g. be captured through your router/DHCP frontend or with [Wireshark](https://wireshark.org).
|
||||
|
||||
## Channels
|
||||
|
||||
- **press:** Trigger channel for recognizing presses on the Amazon Dash Button.
|
||||
A trigger channel can directly be used in a rule, check the "Full Example" section for one example.
|
||||
Dispatches a `PRESSED` event when a button is pressed.
|
||||
|
||||
The trigger channel `press` is of type `system.rawbutton` to allow the usage of the `rawbutton-toggle-switch` profile.
|
||||
|
||||
## Full Example
|
||||
|
||||
Things:
|
||||
|
||||
```java
|
||||
Thing amazondashbutton:dashbutton:fc-a6-67-0c-aa-c7 "My Dash Button" @ "Living" [ macAddress="fc:a6:67:0c:aa:c7", pcapNetworkInterfaceName="eth0", packetInterval=5000 ]
|
||||
```
|
||||
|
||||
(Pay attention: The MAC address has to be given in two different formats)
|
||||
|
||||
Rules:
|
||||
|
||||
```java
|
||||
rule "My Dash Button pressed"
|
||||
when
|
||||
Channel "amazondashbutton:dashbutton:fc-a6-67-0c-aa-c7:press" triggered
|
||||
then
|
||||
logInfo("amazondashbutton", "My Dash Button has been pressed")
|
||||
end
|
||||
```
|
||||
42
bundles/org.openhab.binding.amazondashbutton/pom.xml
Normal file
42
bundles/org.openhab.binding.amazondashbutton/pom.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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-v4_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.amazondashbutton</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Amazon Dash Button Binding</name>
|
||||
|
||||
<properties>
|
||||
<bnd.importpackage>!org.slf4j.impl.*</bnd.importpackage>
|
||||
<dep.noembedding>jna</dep.noembedding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.pcap4j</groupId>
|
||||
<artifactId>pcap4j-core</artifactId>
|
||||
<version>1.8.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.pcap4j</groupId>
|
||||
<artifactId>pcap4j-packetfactory-static</artifactId>
|
||||
<version>1.8.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.amazondashbutton-${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-amazondashbutton" description="Amazon Dash Button Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-runtime-jna</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.amazondashbutton/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link AmazonDashButtonBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AmazonDashButtonBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "amazondashbutton";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID DASH_BUTTON_THING_TYPE = new ThingTypeUID(BINDING_ID, "dashbutton");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String PRESS = "press";
|
||||
|
||||
// Custom Properties
|
||||
public static final String PROPERTY_MAC_ADDRESS = "macAddress";
|
||||
public static final String PROPERTY_NETWORK_INTERFACE_NAME = "pcapNetworkInterfaceName";
|
||||
public static final String PROPERTY_PACKET_INTERVAL = "packetInterval";
|
||||
}
|
||||
@@ -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.amazondashbutton.internal;
|
||||
|
||||
import static org.openhab.binding.amazondashbutton.internal.AmazonDashButtonBindingConstants.DASH_BUTTON_THING_TYPE;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.openhab.binding.amazondashbutton.internal.handler.AmazonDashButtonHandler;
|
||||
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 AmazonDashButtonHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.amazondashbutton")
|
||||
public class AmazonDashButtonHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(DASH_BUTTON_THING_TYPE);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(DASH_BUTTON_THING_TYPE)) {
|
||||
return new AmazonDashButtonHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal.capturing;
|
||||
|
||||
import org.pcap4j.util.MacAddress;
|
||||
|
||||
/**
|
||||
* The {@link PacketCapturingHandler} is notified if a packet is captured by {@link PacketCapturingService}.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*
|
||||
*/
|
||||
public interface PacketCapturingHandler {
|
||||
/**
|
||||
* Callback method to handle a captured packet.
|
||||
*
|
||||
* @param macAddress The mac address which sent the packet
|
||||
*/
|
||||
public void packetCaptured(MacAddress sourceMacAddress);
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal.capturing;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapNetworkInterfaceWrapper;
|
||||
import org.pcap4j.core.BpfProgram.BpfCompileMode;
|
||||
import org.pcap4j.core.NotOpenException;
|
||||
import org.pcap4j.core.PacketListener;
|
||||
import org.pcap4j.core.PcapHandle;
|
||||
import org.pcap4j.core.PcapNetworkInterface.PromiscuousMode;
|
||||
import org.pcap4j.packet.ArpPacket;
|
||||
import org.pcap4j.packet.EthernetPacket;
|
||||
import org.pcap4j.packet.Packet;
|
||||
import org.pcap4j.packet.UdpPacket;
|
||||
import org.pcap4j.packet.namednumber.ArpOperation;
|
||||
import org.pcap4j.packet.namednumber.UdpPort;
|
||||
import org.pcap4j.util.MacAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PacketCapturingService} is responsible for capturing packets.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class PacketCapturingService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PacketCapturingService.class);
|
||||
|
||||
private static final int READ_TIMEOUT = 10; // [ms]
|
||||
private static final int SNAPLEN = 65536; // [bytes]
|
||||
|
||||
private final PcapNetworkInterfaceWrapper pcapNetworkInterface;
|
||||
|
||||
private PcapHandle pcapHandle;
|
||||
|
||||
public PacketCapturingService(PcapNetworkInterfaceWrapper pcapNetworkInterface) {
|
||||
this.pcapNetworkInterface = pcapNetworkInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #startCapturing(PacketCapturingHandler, String)} with a null MAC address.
|
||||
*
|
||||
* @param packetCapturingHandler The handler to be called every time packet is captured
|
||||
* @return Returns true, if the capturing has been started successfully, otherwise returns false
|
||||
*/
|
||||
public boolean startCapturing(final PacketCapturingHandler packetCapturingHandler) {
|
||||
return startCapturing(packetCapturingHandler, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the capturing in a dedicated thread, so this method returns immediately. Every time a packet is captured,
|
||||
* the {@link PacketCapturingHandler#packetCaptured(MacAddress)} of the given
|
||||
* {@link PacketCapturingHandler} is called.
|
||||
*
|
||||
* It's possible to capture packets sent by a specific MAC address by providing the given parameter. If the
|
||||
* macAddress is null, all MAC addresses are considered.
|
||||
*
|
||||
* @param packetCapturingHandler The handler to be called every time a packet is captured
|
||||
* @param macAddress The source MAC address of the captured packet, might be null in order to deactivate this filter
|
||||
* criteria
|
||||
* @return Returns true, if the capturing has been started successfully, otherwise returns false
|
||||
* @throws IllegalStateException Thrown if {@link PcapHandle#isOpen()} of {@link #pcapHandle} returns true
|
||||
*/
|
||||
|
||||
public boolean startCapturing(final PacketCapturingHandler packetCapturingHandler, final String macAddress) {
|
||||
if (pcapHandle != null) {
|
||||
if (pcapHandle.isOpen()) {
|
||||
throw new IllegalStateException("There is an open pcap handle.");
|
||||
} else {
|
||||
pcapHandle.close();
|
||||
}
|
||||
}
|
||||
try {
|
||||
pcapHandle = pcapNetworkInterface.openLive(SNAPLEN, PromiscuousMode.PROMISCUOUS, READ_TIMEOUT);
|
||||
StringBuilder filterBuilder = new StringBuilder("(arp or port bootps)");
|
||||
if (macAddress != null) {
|
||||
filterBuilder.append(" and ether src " + macAddress);
|
||||
}
|
||||
pcapHandle.setFilter(filterBuilder.toString(), BpfCompileMode.OPTIMIZE);
|
||||
} catch (Exception e) {
|
||||
logger.error("Capturing packets on device {} failed.", pcapNetworkInterface.getName(), e);
|
||||
return false;
|
||||
}
|
||||
ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
pcapHandle.loop(-1, new PacketListener() {
|
||||
|
||||
@Override
|
||||
public void gotPacket(Packet packet) {
|
||||
if (!packet.contains(EthernetPacket.class)) {
|
||||
return;
|
||||
}
|
||||
final EthernetPacket ethernetPacket = packet.get(EthernetPacket.class);
|
||||
final MacAddress sourceMacAddress = ethernetPacket.getHeader().getSrcAddr();
|
||||
if (shouldCapture(packet)) {
|
||||
packetCapturingHandler.packetCaptured(sourceMacAddress);
|
||||
}
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
if (pcapHandle != null && pcapHandle.isOpen()) {
|
||||
pcapHandle.close();
|
||||
pcapHandle = null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
if (macAddress == null) {
|
||||
logger.debug("Started capturing ARP and BOOTP requests for network device {}.",
|
||||
pcapNetworkInterface.getName());
|
||||
} else {
|
||||
logger.debug("Started capturing ARP and BOOTP requests for network device {} and MAC address {}.",
|
||||
pcapNetworkInterface.getName(), macAddress);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given {@link Packet} should be captured.
|
||||
*
|
||||
* @param packet The packet to be checked
|
||||
* @return Returns true, if the packet should be captured, otherwise false
|
||||
*/
|
||||
private boolean shouldCapture(final Packet packet) {
|
||||
if (packet.contains(ArpPacket.class)) {
|
||||
ArpPacket arpPacket = packet.get(ArpPacket.class);
|
||||
if (arpPacket.getHeader().getOperation().equals(ArpOperation.REQUEST)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (packet.contains(UdpPacket.class)) {
|
||||
final UdpPacket udpPacket = packet.get(UdpPacket.class);
|
||||
if (UdpPort.BOOTPS == udpPacket.getHeader().getDstPort()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the capturing. This can be called without calling {@link #startCapturing(PacketCapturingHandler)} or
|
||||
* {@link #startCapturing(PacketCapturingHandler, String)} before.
|
||||
*/
|
||||
public void stopCapturing() {
|
||||
if (pcapHandle != null) {
|
||||
if (pcapHandle.isOpen()) {
|
||||
try {
|
||||
pcapHandle.breakLoop();
|
||||
logger.debug("Stopped capturing ARP and BOOTP requests for network device {}.",
|
||||
pcapNetworkInterface.getName());
|
||||
} catch (NotOpenException e) {
|
||||
// Just ignore
|
||||
}
|
||||
} else {
|
||||
pcapHandle = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tracked {@link PcapNetworkInterfaceWrapper}.
|
||||
*
|
||||
* @return the tracked {@link PcapNetworkInterfaceWrapper}
|
||||
*/
|
||||
public PcapNetworkInterfaceWrapper getPcapNetworkInterface() {
|
||||
return pcapNetworkInterface;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal.config;
|
||||
|
||||
/**
|
||||
* The configuration of the Amazon Dash Button
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class AmazonDashButtonConfig {
|
||||
|
||||
/**
|
||||
* The MAC address of the Amazon Dash Button
|
||||
*/
|
||||
public String macAddress;
|
||||
|
||||
/**
|
||||
* The network interface which receives the packets of the Amazon Dash Button
|
||||
*/
|
||||
public String pcapNetworkInterfaceName;
|
||||
|
||||
/**
|
||||
* Often a single button press is recognized multiple times. You can specify how long any further detected button
|
||||
* pressed should be ignored after one click is handled (in ms).
|
||||
*/
|
||||
public Integer packetInterval;
|
||||
}
|
||||
@@ -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.amazondashbutton.internal.config;
|
||||
|
||||
import static org.openhab.binding.amazondashbutton.internal.AmazonDashButtonBindingConstants.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.amazondashbutton.internal.AmazonDashButtonBindingConstants;
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapNetworkInterfaceService;
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapNetworkInterfaceWrapper;
|
||||
import org.openhab.core.config.core.ConfigOptionProvider;
|
||||
import org.openhab.core.config.core.ParameterOption;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.pcap4j.core.PcapAddress;
|
||||
|
||||
/**
|
||||
* The {@link AmazonDashButtonConfigOptionProvider} is responsible for providing options for the
|
||||
* {@link AmazonDashButtonBindingConstants#PROPERTY_NETWORK_INTERFACE_NAME} property.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*
|
||||
*/
|
||||
@Component(service = ConfigOptionProvider.class)
|
||||
@NonNullByDefault
|
||||
public class AmazonDashButtonConfigOptionProvider implements ConfigOptionProvider {
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ParameterOption> getParameterOptions(URI uri, String param, @Nullable Locale locale) {
|
||||
return getParameterOptions(uri, param, null, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ParameterOption> getParameterOptions(URI uri, String param, @Nullable String context,
|
||||
@Nullable Locale locale) {
|
||||
if ("thing-type".equals(uri.getScheme())) {
|
||||
ThingTypeUID thingtypeUID = new ThingTypeUID(uri.getSchemeSpecificPart());
|
||||
if (thingtypeUID.equals(DASH_BUTTON_THING_TYPE) && PROPERTY_NETWORK_INTERFACE_NAME.equals(param)) {
|
||||
return getPcapNetworkInterfacesOptions();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Collection<ParameterOption> getPcapNetworkInterfacesOptions() {
|
||||
Set<PcapNetworkInterfaceWrapper> pcapNetworkInterfaces = PcapNetworkInterfaceService.instance()
|
||||
.getNetworkInterfaces();
|
||||
List<ParameterOption> options = new ArrayList<>();
|
||||
for (PcapNetworkInterfaceWrapper pcapNetworkInterface : pcapNetworkInterfaces) {
|
||||
String name = pcapNetworkInterface.getName();
|
||||
|
||||
options.add(new ParameterOption(name, getLabel(pcapNetworkInterface)));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private String getLabel(PcapNetworkInterfaceWrapper pcapNetworkInterface) {
|
||||
StringBuilder sb = new StringBuilder(pcapNetworkInterface.getName());
|
||||
List<PcapAddress> addresses = pcapNetworkInterface.getAddresses();
|
||||
final String description = pcapNetworkInterface.getDescription();
|
||||
Set<String> paramStrings = new LinkedHashSet<>();
|
||||
if (description != null && !description.isEmpty()) {
|
||||
paramStrings.add(description);
|
||||
}
|
||||
for (PcapAddress address : addresses) {
|
||||
paramStrings.add(address.getAddress().toString().substring(1));
|
||||
|
||||
}
|
||||
|
||||
boolean hasParams = !paramStrings.isEmpty();
|
||||
if (hasParams) {
|
||||
sb.append(" (");
|
||||
}
|
||||
|
||||
for (Iterator<String> paramIterator = paramStrings.iterator(); paramIterator.hasNext();) {
|
||||
String addressString = paramIterator.next();
|
||||
sb.append(addressString);
|
||||
if (paramIterator.hasNext()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
if (hasParams) {
|
||||
sb.append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.amazondashbutton.internal.AmazonDashButtonBindingConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.binding.amazondashbutton.internal.capturing.PacketCapturingHandler;
|
||||
import org.openhab.binding.amazondashbutton.internal.capturing.PacketCapturingService;
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapNetworkInterfaceListener;
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapNetworkInterfaceService;
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapNetworkInterfaceWrapper;
|
||||
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 org.pcap4j.core.PcapNetworkInterface;
|
||||
import org.pcap4j.util.MacAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link AmazonDashButtonDiscoveryService} is responsible for discovering Amazon Dash Buttons. It does so by
|
||||
* capturing ARP and BOOTP requests from all available network devices.
|
||||
*
|
||||
* While scanning the user has to press the button in order to send an ARP and BOOTP request packet. The
|
||||
* {@link AmazonDashButtonDiscoveryService} captures this packet and checks the device's MAC address which sent the
|
||||
* request against a static list of vendor prefixes ({@link #VENDOR_PREFIXES}).
|
||||
*
|
||||
* If an Amazon MAC address is detected a {@link DiscoveryResult} is built and passed to
|
||||
* {@link #thingDiscovered(DiscoveryResult)}.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*
|
||||
*/
|
||||
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.amazondashbutton")
|
||||
public class AmazonDashButtonDiscoveryService extends AbstractDiscoveryService implements PcapNetworkInterfaceListener {
|
||||
|
||||
private static final int DISCOVER_TIMEOUT_SECONDS = 30;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AmazonDashButtonDiscoveryService.class);
|
||||
|
||||
/**
|
||||
* The Amazon Dash button vendor prefixes
|
||||
*/
|
||||
// @formatter:off
|
||||
private static final Set<String> VENDOR_PREFIXES = Collections.unmodifiableSet(Stream.of(
|
||||
"F0:D2:F1",
|
||||
"88:71:E5",
|
||||
"FC:A1:83",
|
||||
"F0:27:2D",
|
||||
"74:C2:46",
|
||||
"68:37:E9",
|
||||
"78:E1:03",
|
||||
"38:F7:3D",
|
||||
"50:DC:E7",
|
||||
"A0:02:DC",
|
||||
"0C:47:C9",
|
||||
"74:75:48",
|
||||
"AC:63:BE",
|
||||
"FC:A6:67",
|
||||
"18:74:2E",
|
||||
"00:FC:8B",
|
||||
"FC:65:DE",
|
||||
"6C:56:97",
|
||||
"44:65:0D",
|
||||
"50:F5:DA",
|
||||
"68:54:FD",
|
||||
"40:B4:CD",
|
||||
"84:D6:D0",
|
||||
"34:D2:70",
|
||||
"B4:7C:9C"
|
||||
).collect(Collectors.toSet()));
|
||||
// @formatter:on
|
||||
|
||||
/**
|
||||
* Returns true if the passed macAddress is an Amazon MAC address.
|
||||
*
|
||||
* @param macAddress
|
||||
* @return
|
||||
*/
|
||||
private static boolean isAmazonVendor(String macAddress) {
|
||||
String vendorPrefix = macAddress.substring(0, 8).toUpperCase();
|
||||
return VENDOR_PREFIXES.contains(vendorPrefix);
|
||||
}
|
||||
|
||||
private final Map<PcapNetworkInterfaceWrapper, PacketCapturingService> packetCapturingServices = new ConcurrentHashMap<>();
|
||||
|
||||
private boolean explicitScanning = false;
|
||||
private boolean backgroundScanning = false;
|
||||
|
||||
public AmazonDashButtonDiscoveryService() {
|
||||
super(Collections.singleton(DASH_BUTTON_THING_TYPE), DISCOVER_TIMEOUT_SECONDS, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
explicitScanning = true;
|
||||
updateListenerRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
explicitScanning = false;
|
||||
updateListenerRegistry();
|
||||
super.stopScan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
backgroundScanning = true;
|
||||
updateListenerRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
backgroundScanning = false;
|
||||
updateListenerRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPcapNetworkInterfaceAdded(final PcapNetworkInterfaceWrapper networkInterface) {
|
||||
startCapturing(networkInterface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPcapNetworkInterfaceRemoved(PcapNetworkInterfaceWrapper networkInterface) {
|
||||
stopCapturing(networkInterface);
|
||||
}
|
||||
|
||||
private void updateListenerRegistry() {
|
||||
boolean shouldListen = explicitScanning || backgroundScanning;
|
||||
if (shouldListen) {
|
||||
PcapNetworkInterfaceService.instance().registerListener(this);
|
||||
// Start capturing for all network interfaces
|
||||
final Set<PcapNetworkInterfaceWrapper> networkInterfaces = PcapNetworkInterfaceService.instance()
|
||||
.getNetworkInterfaces();
|
||||
for (PcapNetworkInterfaceWrapper pcapNetworkInterface : networkInterfaces) {
|
||||
startCapturing(pcapNetworkInterface);
|
||||
}
|
||||
} else {
|
||||
PcapNetworkInterfaceService.instance().unregisterListener(this);
|
||||
// Stop capturing for all network interfaces
|
||||
final Set<PcapNetworkInterfaceWrapper> networkInterfaces = packetCapturingServices.keySet();
|
||||
for (PcapNetworkInterfaceWrapper pcapNetworkInterface : networkInterfaces) {
|
||||
stopCapturing(pcapNetworkInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops capturing for packets for the given {@link PcapNetworkInterface}.
|
||||
*
|
||||
* @param pcapNetworkInterface The {@link PcapNetworkInterface} the capturing should be stopped for.
|
||||
*/
|
||||
private void stopCapturing(final PcapNetworkInterfaceWrapper pcapNetworkInterface) {
|
||||
final PacketCapturingService packetCapturingService = packetCapturingServices.remove(pcapNetworkInterface);
|
||||
final String interfaceName = pcapNetworkInterface.getName();
|
||||
if (packetCapturingService != null) {
|
||||
packetCapturingService.stopCapturing();
|
||||
logger.debug("Stopped capturing for {}.", interfaceName);
|
||||
} else {
|
||||
logger.warn("No active PacketCapturingService registered for {}.", interfaceName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts capturing for packets for the given {@link PcapNetworkInterface}. If the network interface is already
|
||||
* captured this method returns without doing anything.
|
||||
*
|
||||
* @param pcapNetworkInterface The {@link PcapNetworkInterface} to be captured
|
||||
*/
|
||||
private void startCapturing(final PcapNetworkInterfaceWrapper pcapNetworkInterface) {
|
||||
if (packetCapturingServices.containsKey(pcapNetworkInterface)) {
|
||||
// We already have a tracker
|
||||
return;
|
||||
}
|
||||
|
||||
PacketCapturingService packetCapturingService = new PacketCapturingService(pcapNetworkInterface);
|
||||
|
||||
packetCapturingServices.put(pcapNetworkInterface, packetCapturingService);
|
||||
final String interfaceName = pcapNetworkInterface.getName();
|
||||
final boolean capturingStarted = packetCapturingService.startCapturing(new PacketCapturingHandler() {
|
||||
|
||||
@Override
|
||||
public void packetCaptured(MacAddress macAddress) {
|
||||
String macAdressString = macAddress.toString();
|
||||
|
||||
if (isAmazonVendor(macAdressString)) {
|
||||
logger.debug("Captured a packet from {} which seems to be sent from an Amazon Dash Button device.",
|
||||
macAdressString);
|
||||
ThingUID dashButtonThing = new ThingUID(DASH_BUTTON_THING_TYPE, macAdressString.replace(":", "-"));
|
||||
// @formatter:off
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(dashButtonThing)
|
||||
.withLabel("Dash Button")
|
||||
.withRepresentationProperty(macAdressString)
|
||||
.withProperty(PROPERTY_MAC_ADDRESS, macAdressString)
|
||||
.withProperty(PROPERTY_NETWORK_INTERFACE_NAME, interfaceName)
|
||||
.withProperty(PROPERTY_PACKET_INTERVAL, BigDecimal.valueOf(5000))
|
||||
.build();
|
||||
// @formatter:on
|
||||
thingDiscovered(discoveryResult);
|
||||
} else {
|
||||
logger.trace(
|
||||
"Captured a packet from {} which is ignored as it's not on the list of supported vendor prefixes.",
|
||||
macAdressString);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (capturingStarted) {
|
||||
logger.debug("Started capturing for {}.", interfaceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal.handler;
|
||||
|
||||
import static org.openhab.binding.amazondashbutton.internal.AmazonDashButtonBindingConstants.PRESS;
|
||||
import static org.openhab.core.thing.CommonTriggerEvents.PRESSED;
|
||||
|
||||
import org.openhab.binding.amazondashbutton.internal.capturing.PacketCapturingHandler;
|
||||
import org.openhab.binding.amazondashbutton.internal.capturing.PacketCapturingService;
|
||||
import org.openhab.binding.amazondashbutton.internal.config.AmazonDashButtonConfig;
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapNetworkInterfaceListener;
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapNetworkInterfaceService;
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapNetworkInterfaceWrapper;
|
||||
import org.openhab.binding.amazondashbutton.internal.pcap.PcapUtil;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.pcap4j.util.MacAddress;
|
||||
|
||||
/**
|
||||
* The {@link AmazonDashButtonHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*/
|
||||
public class AmazonDashButtonHandler extends BaseThingHandler implements PcapNetworkInterfaceListener {
|
||||
private PacketCapturingService packetCapturingService;
|
||||
|
||||
private long lastCommandHandled = 0;
|
||||
|
||||
public AmazonDashButtonHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// There are no commands to be handled
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
PcapNetworkInterfaceService.instance().registerListener(this);
|
||||
AmazonDashButtonConfig dashButtonConfig = getConfigAs(AmazonDashButtonConfig.class);
|
||||
final String pcapNetworkInterfaceName = dashButtonConfig.pcapNetworkInterfaceName;
|
||||
final String macAddress = dashButtonConfig.macAddress;
|
||||
final Integer packetInterval = dashButtonConfig.packetInterval;
|
||||
scheduler.submit(() -> {
|
||||
PcapNetworkInterfaceWrapper pcapNetworkInterface = PcapUtil
|
||||
.getNetworkInterfaceByName(pcapNetworkInterfaceName);
|
||||
if (pcapNetworkInterface == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"The networkinterface " + pcapNetworkInterfaceName + " is not present.");
|
||||
return;
|
||||
}
|
||||
|
||||
packetCapturingService = new PacketCapturingService(pcapNetworkInterface);
|
||||
boolean capturingStarted = packetCapturingService.startCapturing(new PacketCapturingHandler() {
|
||||
@Override
|
||||
public void packetCaptured(MacAddress macAddress) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (lastCommandHandled + packetInterval < now) {
|
||||
triggerChannel(PRESS, PRESSED);
|
||||
lastCommandHandled = now;
|
||||
}
|
||||
}
|
||||
}, macAddress);
|
||||
if (capturingStarted) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
|
||||
"The capturing for " + pcapNetworkInterfaceName + " cannot be started.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (packetCapturingService != null) {
|
||||
packetCapturingService.stopCapturing();
|
||||
packetCapturingService = null;
|
||||
}
|
||||
PcapNetworkInterfaceService.instance().unregisterListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPcapNetworkInterfaceAdded(PcapNetworkInterfaceWrapper newNetworkInterface) {
|
||||
if (packetCapturingService != null) {
|
||||
final PcapNetworkInterfaceWrapper trackedPcapNetworkInterface = packetCapturingService
|
||||
.getPcapNetworkInterface();
|
||||
if (trackedPcapNetworkInterface.equals(newNetworkInterface)) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPcapNetworkInterfaceRemoved(PcapNetworkInterfaceWrapper removedNetworkInterface) {
|
||||
if (packetCapturingService != null) {
|
||||
final PcapNetworkInterfaceWrapper trackedPcapNetworkInterface = packetCapturingService
|
||||
.getPcapNetworkInterface();
|
||||
if (trackedPcapNetworkInterface.equals(removedNetworkInterface)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
|
||||
"The networkinterface " + removedNetworkInterface.getName() + " is not present anymore.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal.pcap;
|
||||
|
||||
import org.pcap4j.core.PcapNetworkInterface;
|
||||
|
||||
/**
|
||||
* The {@link PcapNetworkInterfaceListener} is notified whenever a {@link PcapNetworkInterface} is added or removed. A
|
||||
* {@link PcapNetworkInterfaceListener} can be registered by calling
|
||||
* {@link PcapNetworkInterfaceService#registerListener(PcapNetworkInterfaceListener)} and can be unregistered by calling
|
||||
* {@link PcapNetworkInterfaceService#unregisterListener(PcapNetworkInterfaceListener)}.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*
|
||||
*/
|
||||
public interface PcapNetworkInterfaceListener {
|
||||
|
||||
/**
|
||||
* This method is called whenever a new {@link PcapNetworkInterfaceWrapper} is added.
|
||||
*
|
||||
* @param newNetworkInterface The added networkInterface
|
||||
*/
|
||||
public void onPcapNetworkInterfaceAdded(PcapNetworkInterfaceWrapper newNetworkInterface);
|
||||
|
||||
/**
|
||||
* This method is called whenever a {@link PcapNetworkInterfaceWrapper} is removed.
|
||||
*
|
||||
* @param removedNetworkInterface The removed networkInterface
|
||||
*/
|
||||
public void onPcapNetworkInterfaceRemoved(PcapNetworkInterfaceWrapper removedNetworkInterface);
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal.pcap;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.openhab.core.common.ThreadPoolManager;
|
||||
import org.pcap4j.core.PcapNetworkInterface;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PcapNetworkInterfaceService} is a singleton which can be obtained by calling {@link #instance()}.
|
||||
* It provides all available {@link PcapNetworkInterface}s which are bound to an address. These network interfaces can
|
||||
* be retrieved by calling {@link #getNetworkInterfaces()}.
|
||||
*
|
||||
* Moreover the {@link PcapNetworkInterfaceService} provided the possibility to register a
|
||||
* {@link PcapNetworkInterfaceListener}s which are notified on new and removed {@link PcapNetworkInterface}s.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class PcapNetworkInterfaceService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PcapNetworkInterfaceService.class);
|
||||
|
||||
private static PcapNetworkInterfaceService instance = null;
|
||||
private static final String THREADPOOL_NAME = "pcapNetworkInterfaceService";
|
||||
|
||||
private final Set<PcapNetworkInterfaceListener> listeners = new CopyOnWriteArraySet<>();
|
||||
private final Set<PcapNetworkInterfaceWrapper> pcapNetworkInterfaces = new CopyOnWriteArraySet<>();
|
||||
|
||||
private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(THREADPOOL_NAME);
|
||||
|
||||
private ScheduledFuture<?> future = null;
|
||||
|
||||
private final Runnable pollingRunnable = () -> {
|
||||
synchronized (pcapNetworkInterfaces) {
|
||||
final Set<PcapNetworkInterfaceWrapper> determinedNetworkInterfaces = determineBoundNetworkInterfaces();
|
||||
final Set<PcapNetworkInterfaceWrapper> currentNetworkInterfaces = new HashSet<>(pcapNetworkInterfaces);
|
||||
|
||||
final Set<PcapNetworkInterfaceWrapper> newNetworkInterfaces = new HashSet<>(determinedNetworkInterfaces);
|
||||
newNetworkInterfaces.removeIf(currentNetworkInterfaces::contains);
|
||||
|
||||
final Set<PcapNetworkInterfaceWrapper> removedNetworkInterfaces = new HashSet<>(currentNetworkInterfaces);
|
||||
removedNetworkInterfaces.removeIf(determinedNetworkInterfaces::contains);
|
||||
|
||||
pcapNetworkInterfaces.clear();
|
||||
pcapNetworkInterfaces.addAll(determinedNetworkInterfaces);
|
||||
|
||||
for (PcapNetworkInterfaceWrapper pcapNetworkInterface : newNetworkInterfaces) {
|
||||
notifyNetworkInterfacesAdded(pcapNetworkInterface);
|
||||
}
|
||||
|
||||
for (PcapNetworkInterfaceWrapper pcapNetworkInterface : removedNetworkInterfaces) {
|
||||
notifyNetworkInterfacesRemoved(pcapNetworkInterface);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private PcapNetworkInterfaceService() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link PcapNetworkInterfaceService} singleton instance.
|
||||
*
|
||||
* @return The {@link PcapNetworkInterfaceService} singleton
|
||||
*/
|
||||
public static synchronized PcapNetworkInterfaceService instance() {
|
||||
if (instance == null) {
|
||||
instance = new PcapNetworkInterfaceService();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Set} of {@link PcapNetworkInterface}s which are bound to an address.
|
||||
*
|
||||
* @return the network interface set
|
||||
*/
|
||||
public Set<PcapNetworkInterfaceWrapper> getNetworkInterfaces() {
|
||||
synchronized (pcapNetworkInterfaces) {
|
||||
return Collections.unmodifiableSet(pcapNetworkInterfaces.stream().collect(Collectors.toSet()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given {@link PcapNetworkInterfaceListener}. If it is already registered, this method returns
|
||||
* immediately.
|
||||
*
|
||||
* @param networkInterfaceListener The {@link PcapNetworkInterfaceListener} to be registered.
|
||||
*/
|
||||
public void registerListener(PcapNetworkInterfaceListener networkInterfaceListener) {
|
||||
final boolean isAdded = listeners.add(networkInterfaceListener);
|
||||
if (isAdded) {
|
||||
updatePollingState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given {@link PcapNetworkInterfaceListener}. If it is already unregistered, this method returns
|
||||
* immediately.
|
||||
*
|
||||
* @param networkInterfaceListener The {@link PcapNetworkInterfaceListener} to be unregistered.
|
||||
*/
|
||||
public void unregisterListener(PcapNetworkInterfaceListener networkInterfaceListener) {
|
||||
final boolean isRemoved = listeners.remove(networkInterfaceListener);
|
||||
if (isRemoved) {
|
||||
updatePollingState();
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyNetworkInterfacesAdded(PcapNetworkInterfaceWrapper pcapNetworkInterface) {
|
||||
for (PcapNetworkInterfaceListener listener : listeners) {
|
||||
notifyNetworkInterfacesAdded(listener, pcapNetworkInterface);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyNetworkInterfacesRemoved(PcapNetworkInterfaceWrapper pcapNetworkInterface) {
|
||||
for (PcapNetworkInterfaceListener listener : listeners) {
|
||||
notifyNetworkInterfacesRemoved(listener, pcapNetworkInterface);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyNetworkInterfacesAdded(PcapNetworkInterfaceListener listener,
|
||||
PcapNetworkInterfaceWrapper pcapNetworkInterface) {
|
||||
try {
|
||||
listener.onPcapNetworkInterfaceAdded(pcapNetworkInterface);
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while calling onPcapNetworkInterfaceAdded for {}", listener, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyNetworkInterfacesRemoved(PcapNetworkInterfaceListener listener,
|
||||
PcapNetworkInterfaceWrapper pcapNetworkInterface) {
|
||||
try {
|
||||
listener.onPcapNetworkInterfaceRemoved(pcapNetworkInterface);
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while calling onPcapNetworkInterfaceRemoved for {}", listener, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all pcap network interfaces relying on {@link PcapUtil#getAllNetworkInterfaces()}. The list is filtered
|
||||
* as all interfaces which are not bound to an address are excluded.
|
||||
*
|
||||
* @return An {@link Iterable} of all {@link PcapNetworkInterfaceWrapper}s which are bound to an address
|
||||
*/
|
||||
private Set<PcapNetworkInterfaceWrapper> determineBoundNetworkInterfaces() {
|
||||
final Set<PcapNetworkInterfaceWrapper> allNetworkInterfaces = new HashSet<>();
|
||||
PcapUtil.getAllNetworkInterfaces().forEach(allNetworkInterfaces::add);
|
||||
allNetworkInterfaces.removeIf(networkInterface -> {
|
||||
final boolean notSuitable = networkInterface.getAddresses().isEmpty();
|
||||
if (notSuitable) {
|
||||
logger.debug("{} is not a suitable network interfaces as no addresses are bound to it.",
|
||||
networkInterface.getName());
|
||||
}
|
||||
return notSuitable;
|
||||
});
|
||||
return allNetworkInterfaces;
|
||||
}
|
||||
|
||||
private void updatePollingState() {
|
||||
boolean isPolling = future != null;
|
||||
if (isPolling && listeners.isEmpty()) {
|
||||
future.cancel(true);
|
||||
future = null;
|
||||
return;
|
||||
}
|
||||
if (!isPolling && !listeners.isEmpty()) {
|
||||
future = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, 2, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal.pcap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.pcap4j.core.PcapAddress;
|
||||
import org.pcap4j.core.PcapHandle;
|
||||
import org.pcap4j.core.PcapNativeException;
|
||||
import org.pcap4j.core.PcapNetworkInterface;
|
||||
import org.pcap4j.core.PcapNetworkInterface.PromiscuousMode;
|
||||
|
||||
/**
|
||||
* This wrapper is needed as {@link PcapNetworkInterface#equals(Object)} and {@link PcapNetworkInterface#hashCode()} are
|
||||
* not implemented in an appropriate way. The wrapper delegates all methods calls except {@link #equals(Object)} and
|
||||
* {@link #hashCode()} to {@link #pcapNetworkInterface}.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class PcapNetworkInterfaceWrapper {
|
||||
|
||||
/**
|
||||
* The wrapped object
|
||||
*/
|
||||
private final PcapNetworkInterface pcapNetworkInterface;
|
||||
|
||||
/**
|
||||
* Use this Guava function in order to create a {@link PcapNetworkInterfaceWrapper} instance.
|
||||
*/
|
||||
public static final Function<PcapNetworkInterface, PcapNetworkInterfaceWrapper> TRANSFORMER = new Function<PcapNetworkInterface, PcapNetworkInterfaceWrapper>() {
|
||||
|
||||
@Override
|
||||
public PcapNetworkInterfaceWrapper apply(PcapNetworkInterface pcapNetworkInterface) {
|
||||
return new PcapNetworkInterfaceWrapper(pcapNetworkInterface);
|
||||
}
|
||||
};
|
||||
|
||||
private PcapNetworkInterfaceWrapper(PcapNetworkInterface pcapNetworkInterface) {
|
||||
if (pcapNetworkInterface == null) {
|
||||
throw new IllegalArgumentException("Don't pass null.");
|
||||
}
|
||||
this.pcapNetworkInterface = pcapNetworkInterface;
|
||||
}
|
||||
|
||||
public List<PcapAddress> getAddresses() {
|
||||
return pcapNetworkInterface.getAddresses();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return pcapNetworkInterface.getName();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return pcapNetworkInterface.getDescription();
|
||||
}
|
||||
|
||||
public PcapHandle openLive(int arg0, PromiscuousMode arg1, int arg2) throws PcapNativeException {
|
||||
return pcapNetworkInterface.openLive(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final PcapNetworkInterfaceWrapper other = (PcapNetworkInterfaceWrapper) obj;
|
||||
return Objects.equals(this.getName(), other.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return pcapNetworkInterface.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.amazondashbutton.internal.pcap;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.pcap4j.core.PcapNativeException;
|
||||
import org.pcap4j.core.Pcaps;
|
||||
|
||||
/**
|
||||
*
|
||||
* A simple utitlity class which encapsulates {@link Pcaps} and which catches checked exceptions and transforms them
|
||||
* into {@link RuntimeException}s.
|
||||
*
|
||||
* @author Oliver Libutzki - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class PcapUtil {
|
||||
|
||||
/**
|
||||
* Returns all Pcap network interfaces relying on {@link Pcaps#findAllDevs()}.
|
||||
*
|
||||
* @return A {@link Set} of all {@link PcapNetworkInterfaceWrapper}s
|
||||
*/
|
||||
public static Set<PcapNetworkInterfaceWrapper> getAllNetworkInterfaces() {
|
||||
try {
|
||||
final Set<PcapNetworkInterfaceWrapper> allNetworkInterfaces = Collections.unmodifiableSet(Pcaps
|
||||
.findAllDevs().stream().map(PcapNetworkInterfaceWrapper.TRANSFORMER).collect(Collectors.toSet()));
|
||||
return allNetworkInterfaces;
|
||||
} catch (PcapNativeException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Pcap network interface with the given name relying on {@link Pcaps#getDevByName(String)}. If no
|
||||
* interface is found, null is returned.
|
||||
*
|
||||
* @param name The name of the Pcap network interface
|
||||
* @return The network interface with the given name. Returns null, if no interface is found
|
||||
*/
|
||||
public static PcapNetworkInterfaceWrapper getNetworkInterfaceByName(String name) {
|
||||
try {
|
||||
return PcapNetworkInterfaceWrapper.TRANSFORMER.apply(Pcaps.getDevByName(name));
|
||||
} catch (PcapNativeException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Requirement(namespace = ExtenderNamespace.EXTENDER_NAMESPACE, filter = "(osgi.extender=osgi.serviceloader.registrar)")
|
||||
@Requirement(namespace = ExtenderNamespace.EXTENDER_NAMESPACE, filter = "(&(osgi.extender=osgi.serviceloader.processor)(version>=1.0)(!(version>=2.0)))")
|
||||
@Requirement(namespace = "osgi.serviceloader", filter = "(osgi.serviceloader=org.pcap4j.packet.factory.PacketFactoryBinderProvider)", cardinality = Cardinality.MULTIPLE)
|
||||
@Capability(namespace = "osgi.serviceloader", name = "org.pcap4j.packet.factory.PacketFactoryBinderProvider")
|
||||
package org.openhab.binding.amazondashbutton;
|
||||
|
||||
import org.osgi.annotation.bundle.Capability;
|
||||
import org.osgi.annotation.bundle.Requirement;
|
||||
import org.osgi.annotation.bundle.Requirement.Cardinality;
|
||||
import org.osgi.namespace.extender.ExtenderNamespace;
|
||||
/**
|
||||
* Additional information for AmazonDashButton package
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
*
|
||||
*/
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="amazondashbutton" 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>Amazon Dash Button Binding</name>
|
||||
<description>@text/bindingDescription</description>
|
||||
<author>Oliver Libutzki</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,15 @@
|
||||
# binding
|
||||
bindingDescription = This is the binding for the Amazon Dash Button.
|
||||
|
||||
# thing types
|
||||
dashButtonLabel = Amazon Dash Button
|
||||
dashButtonDescription = This is the Amazon Dash Button
|
||||
dashButtonMacAddressLabel = MAC address
|
||||
dashButtonMacAddressDescription = The MAC address of the Amazon Dash Button
|
||||
dashButtonNetworkInterfaceLabel = Network interface
|
||||
dashButtonNetworkInterfaceDescription = The network interface which receives the packets of the Amazon Dash Button
|
||||
dashButtonPacketIntervalLabel = Packet processing interval (in ms)
|
||||
dashButtonPacketIntervalDescription = Often a single button press is recognized multiple times. You can specify how long any further detected button pressed should be ignored after one click is handled (in ms).
|
||||
|
||||
dashButtonPressChannelLabel = Amazon Dash Button press
|
||||
dashButtonPressChannelDescription = Channel for recognizing presses on the Amazon Dash Button
|
||||
@@ -0,0 +1,15 @@
|
||||
# binding
|
||||
bindingDescription = Dies ist das Binding für den Amazon Dash Button.
|
||||
|
||||
# thing types
|
||||
dashButtonLabel = Amazon Dash Button
|
||||
dashButtonDescription = Dies ist der Amazon Dash Button
|
||||
dashButtonMacAddressLabel = MAC-Adresse
|
||||
dashButtonMacAddressDescription = Die MAC-Adresse des Amazon Dash Buttons
|
||||
dashButtonNetworkInterfaceLabel = Netzwerkschnittstelle
|
||||
dashButtonNetworkInterfaceDescription = Die Netzwerkschnittstelle, über den das Paket des Amazon Dash Button empfangen wird
|
||||
dashButtonPacketIntervalLabel = Paketverarbeitungsintervall (in ms)
|
||||
dashButtonPacketIntervalDescription = Häufig führt eine einzelne Button-Betätigung dazu, dass diese mehrfach verarbeitet wird. Es kann angegeben werden, für welchen Zeitraum weitere Betätigungsevents ignoriert werden sollen, nachdem eine Betätigung verarbeitet wurde (in ms).
|
||||
|
||||
dashButtonPressChannelLabel = Eine Betätigung des Amazon Dash Button
|
||||
dashButtonPressChannelDescription = Channel um eine Betätigung des Amazon Dash Button festzustellen
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="amazondashbutton"
|
||||
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="dashbutton">
|
||||
<label>@text/dashButtonLabel</label>
|
||||
<description>@text/dashButtonDescription</description>
|
||||
|
||||
<channels>
|
||||
<channel id="press" typeId="system.rawbutton">
|
||||
<label>@text/dashButtonPressChannelLabel</label>
|
||||
<description>@text/dashButtonPressChannelLabel</description>
|
||||
</channel>
|
||||
</channels>
|
||||
<config-description>
|
||||
<parameter name="pcapNetworkInterfaceName" type="text">
|
||||
<label>@text/dashButtonNetworkInterfaceLabel</label>
|
||||
<description>@text/dashButtonNetworkInterfaceDescription</description>
|
||||
</parameter>
|
||||
<parameter name="macAddress" type="text" pattern="([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})">
|
||||
<label>@text/dashButtonMacAddressLabel</label>
|
||||
<description>@text/dashButtonMacAddressDescription</description>
|
||||
</parameter>
|
||||
<parameter name="packetInterval" type="integer">
|
||||
<label>@text/dashButtonPacketIntervalLabel</label>
|
||||
<description>@text/dashButtonPacketIntervalDescription</description>
|
||||
<default>5000</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user