Skip to content

Commit

Permalink
[bluetooth.govee] Govee Bluetooth Binding initial contribution (openh…
Browse files Browse the repository at this point in the history
…ab#8610)

Signed-off-by: Connor Petty <[email protected]>
  • Loading branch information
cpmeister authored Jan 25, 2021
1 parent f5ee685 commit 239e33a
Show file tree
Hide file tree
Showing 40 changed files with 2,813 additions and 0 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
/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.govee/ @cpmeister
/bundles/org.openhab.binding.bluetooth.roaming/ @cpmeister
/bundles/org.openhab.binding.bluetooth.ruuvitag/ @ssalonen
/bundles/org.openhab.binding.boschindego/ @jofleck
Expand Down
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@
<artifactId>org.openhab.binding.bluetooth.generic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth.govee</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth.roaming</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.bluetooth.govee/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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
68 changes: 68 additions & 0 deletions bundles/org.openhab.binding.bluetooth.govee/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Govee

This extension adds support for [Govee](https://www.govee.com/) Bluetooth Devices.

## Supported Things

Only two thing types are supported by this extension at the moment.

| Thing Type ID | Description | Supported Models |
|------------------------|-------------------------------------------|-------------------------------------------------------------|
| goveeHygrometer | Govee Thermo-Hygrometer | H5051,H5071 |
| goveeHygrometerMonitor | Govee Thermo-Hygrometer w/ Warning Alarms | H5052,H5072,H5074,H5075,H5101,H5102,H5177,H5179,B5175,B5178 |

## Discovery

As any other Bluetooth device, Govee devices are discovered automatically by the corresponding bridge.

## Thing Configuration

Govee things have the following configuration parameters:

| Thing | Parameter | Required | Default | Description |
|-----------------------------|-------------------------|----------|---------|-----------------------------------------------------------------------------------|
| all | address | yes | | The Bluetooth address of the device (in format "XX:XX:XX:XX:XX:XX") |
| all | refreshInterval | | 300 | How often, in seconds, the sensor data of the device should be refreshed |
| goveeHygrometer<sup>1</sup> | temperatureCalibration | no | | Offset to apply to temperature<sup>2</sup> sensor readings |
| goveeHygrometer<sup>1</sup> | humidityCalibration | no | | Offset to apply to humidity sensor readings |
| goveeHygrometerMonitor | temperatureWarningAlarm | | false | Enables warning alarms to be broadcast when temperature is out of specified range |
| goveeHygrometerMonitor | temperatureWarningMin | | 0 | The lower safe temperature<sup>2</sup> threshold <sup>3</sup> |
| goveeHygrometerMonitor | temperatureWarningMax | | 0 | The upper safe temperature<sup>2</sup> threshold <sup>3</sup> |
| goveeHygrometerMonitor | humidityWarningAlarm | | false | Enables warning alarms to be broadcast when humidity is out of specified range |
| goveeHygrometerMonitor | humidityWarningMin | | 0 | The lower safe humidity threshold <sup>3</sup> |
| goveeHygrometerMonitor | humidityWarningMax | | 0 | The upper safe humidity threshold <sup>3</sup> |

1. Available to both `goveeHygrometer` and `goveeHygrometerMonitor` thing types.
2. In °C
3. Only applies if alarm feature is enabled

## Channels

Govee things have the following channels in addition to the default bluetooth channels:

| Thing | Channel ID | Item Type | Description |
|-----------------------------|------------------|------------------------|----------------------------------------------------------------|
| goveeHygrometer<sup>1</sup> | temperature | Number:Temperature | The measured temperature |
| goveeHygrometer<sup>1</sup> | humidity | Number:Dimensionless | The measured relative humidity |
| goveeHygrometer<sup>1</sup> | battery | Number:Dimensionless | The measured battery percentage |
| goveeHygrometerMonitor | temperatureAlarm | Switch | Indicates if current temperature is out of range. <sup>2</sup> |
| goveeHygrometerMonitor | humidityAlarm | Switch | Indicates if current humidity is out of range. <sup>2</sup> |

1. Available to both `goveeHygrometer` and `goveeHygrometerMonitor` thing types.
2. Only applies if warning alarms are enabled in the configuration.

## Example

demo.things:

```
bluetooth:goveeHygrometer:hci0:beacon "Govee Temperature Humidity Monitor" (bluetooth:bluez:hci0) [ address="12:34:56:78:9A:BC" ]
```

demo.items:

```
Number:Temperature temperature "Room Temperature [%.1f %unit%]" { channel="bluetooth:goveeHygrometer:hci0:beacon:temperature" }
Number:Dimensionless humidity "Humidity [%.0f %unit%]" { channel="bluetooth:goveeHygrometer:hci0:beacon:humidity" }
Number:Dimensionless battery "Battery [%.0f %unit%]" { channel="bluetooth:goveeHygrometer:hci0:beacon:battery" }
```
34 changes: 34 additions & 0 deletions bundles/org.openhab.binding.bluetooth.govee/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>

<artifactId>org.openhab.binding.bluetooth.govee</artifactId>

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

<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>

</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.bluetooth.govee-${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-bluetooth-govee" description="Bluetooth Binding Govee" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth/${project.version}</bundle>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.govee/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.bluetooth.gattserial;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* @author Connor Petty - Initial Contribution
*
*/
@NonNullByDefault
public interface GattMessage {

public byte[] getPayload();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.bluetooth.gattserial;

import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
* @author Connor Petty - Initial Contribution
*
*/
@NonNullByDefault
public abstract class GattSocket<T extends GattMessage, R extends GattMessage> {

private static final Future<?> COMPLETED_FUTURE = CompletableFuture.completedFuture(null);

private final Deque<MessageProcessor> messageProcessors = new ConcurrentLinkedDeque<>();

public void registerMessageHandler(MessageHandler<T, R> messageHandler) {
// we need to use a dummy future since ConcurrentHashMap doesn't allow null values
messageProcessors.addFirst(new MessageProcessor(messageHandler, COMPLETED_FUTURE));
}

protected abstract ScheduledExecutorService getScheduler();

public void sendMessage(MessageServicer<T, R> messageServicer) {
T message = messageServicer.createMessage();

CompletableFuture<@Nullable Void> messageFuture = sendMessage(message);

Future<?> timeoutFuture = getScheduler().schedule(() -> {
messageFuture.completeExceptionally(new TimeoutException("Timeout while waiting for response"));
}, messageServicer.getTimeout(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS);

MessageProcessor processor = new MessageProcessor(messageServicer, timeoutFuture);
messageProcessors.addLast(processor);

messageFuture.whenComplete((v, ex) -> {
if (ex instanceof CompletionException) {
ex = ex.getCause();
}
if (ex != null) {
if (messageServicer.handleFailedMessage(message, ex)) {
timeoutFuture.cancel(false);
messageProcessors.remove(processor);
}
}
});
}

public CompletableFuture<@Nullable Void> sendMessage(T message) {
List<byte[]> packets = createPackets(message);
var futures = packets.stream()//
.map(this::sendPacket)//
.toArray(CompletableFuture[]::new);

return CompletableFuture.allOf(futures);
}

protected List<byte[]> createPackets(T message) {
return List.of(message.getPayload());
}

protected abstract void parsePacket(byte[] packet, Consumer<R> messageHandler);

protected abstract CompletableFuture<@Nullable Void> sendPacket(byte[] value);

public void receivePacket(byte[] packet) {
parsePacket(packet, this::handleMessage);
}

private void handleMessage(R message) {
for (Iterator<MessageProcessor> it = messageProcessors.iterator(); it.hasNext();) {
MessageProcessor processor = it.next();
if (processor.messageHandler.handleReceivedMessage(message)) {
processor.timeoutFuture.cancel(false);
it.remove();
// we want to return after the first message servicer handles the message
if (processor.timeoutFuture != COMPLETED_FUTURE) {
return;
}
}
}
}

private class MessageProcessor {
private MessageHandler<T, R> messageHandler;
private Future<?> timeoutFuture;

public MessageProcessor(MessageHandler<T, R> messageHandler, Future<?> timeoutFuture) {
this.messageHandler = messageHandler;
this.timeoutFuture = timeoutFuture;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.bluetooth.gattserial;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* @author Connor Petty - Initial Contribution
*
*/
@NonNullByDefault
public interface MessageHandler<T extends GattMessage, R extends GattMessage> {

/**
*
* @param payload
* @return true if this handler should be removed from the handler list
*/
public boolean handleReceivedMessage(R message);

/**
*
* @param payload
* @return true if this handler should be removed from the handler list
*/
public boolean handleFailedMessage(T message, Throwable th);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.bluetooth.gattserial;

import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* @author Connor Petty - Initial Contribution
*
*/
@NonNullByDefault
public interface MessageServicer<T extends GattMessage, R extends GattMessage>
extends MessageHandler<T, R>, MessageSupplier<T> {

public long getTimeout(TimeUnit unit);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.bluetooth.gattserial;

/**
* @author Connor Petty - Initial Contribution
*
*/
public interface MessageSupplier<M extends GattMessage> {

public M createMessage();
}
Loading

0 comments on commit 239e33a

Please sign in to comment.