Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[grundfosalpha] Initial contribution #15907

Merged
merged 3 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
/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.grundfosalpha/ @tisoft
/bundles/org.openhab.binding.bluetooth.radoneye/ @petero-dk
/bundles/org.openhab.binding.bluetooth.roaming/ @cpmeister
/bundles/org.openhab.binding.bluetooth.ruuvitag/ @ssalonen
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 @@ -221,6 +221,11 @@
<artifactId>org.openhab.binding.bluetooth.govee</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth.grundfosalpha</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth.radoneye</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,20 @@
*/
package org.openhab.binding.bluetooth.bluez.internal;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.bluez.exceptions.BluezFailedException;
import org.bluez.exceptions.BluezInvalidArgumentsException;
import org.bluez.exceptions.BluezNotReadyException;
import org.bluez.exceptions.BluezNotSupportedException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.freedesktop.dbus.types.Variant;
import org.openhab.binding.bluetooth.AbstractBluetoothBridgeHandler;
import org.openhab.binding.bluetooth.BluetoothAddress;
import org.openhab.binding.bluetooth.bluez.internal.events.AdapterDiscoveringChangedEvent;
Expand Down Expand Up @@ -139,6 +146,15 @@ public void dispose() {
return null;
}

Map<String, Variant<?>> filter = new HashMap<>();
filter.put("DuplicateData", new Variant<>(true));
try {
adapter.setDiscoveryFilter(filter);
} catch (BluezInvalidArgumentsException | BluezFailedException | BluezNotSupportedException
| BluezNotReadyException e) {
throw new RuntimeException(e);
}

// now lets make sure that discovery is turned on
if (!localAdapter.startDiscovery()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Trying to start discovery");
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.bluetooth.grundfosalpha/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
49 changes: 49 additions & 0 deletions bundles/org.openhab.binding.bluetooth.grundfosalpha/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# GrundfosAlpha Binding

This adds support for reading out the data of Grundfos Alpha Pumps with a [Grundfos Alpha Reader](https://product-selection.grundfos.com/products/alpha-reader)

The reverse engineering of the protocol was taken from [https://github.com/JsBergbau/AlphaDecoder](https://github.com/JsBergbau/AlphaDecoder).

## Supported Things

- `mi401`: The Grundfos MI401 ALPHA Reader

## Discovery

All readers are auto-detected as soon as Bluetooth is configured in openHAB and the MI401 device is powered on.

## Thing Configuration

### `mi401` Thing Configuration

| Name | Type | Description | Default | Required | Advanced |
|---------|------|-----------------------------------------------|---------|----------|----------|
| address | text | Bluetooth address in XX:XX:XX:XX:XX:XX format | N/A | yes | no |

## Channels

| Channel | Type | Read/Write | Description |
|------------------|---------------------------|------------|------------------------------------|
| rssi | Number | R | Received Signal Strength Indicator |
| flow-rate | Number:VolumetricFlowRate | R | The flow rate of the pump |
| pump-head | Number:Length | R | The water head above the pump |
| pump-temperature | Number:Temperature | R | The temperature of the pump |
| battery-level | Number:Dimensionless | R | The battery level of the reader |

## Full Example

grundfos_alpha.things (assuming you have a Bluetooth bridge with the ID `bluetooth:bluegiga:adapter1`:

```
jlaur marked this conversation as resolved.
Show resolved Hide resolved
bluetooth:mi401:hci0:sensor1 "Grundfos Alpha Reader 1" (bluetooth:bluegiga:adapter1) [ address="12:34:56:78:9A:BC" ]
```

grundfos_alpha.items:

```
jlaur marked this conversation as resolved.
Show resolved Hide resolved
Number RSSI "RSSI [%.1f dBm]" <QualityOfService> { channel="bluetooth:mi401:hci0:sensor1:rssi" }
Number:VolumetricFlowRate Flow_rate "Flowrate [%.1f %unit%]" <flow> { channel="bluetooth:mi401:hci0:sensor1:flow-rate" }
Number:Length Pump_Head "Pump head [%.1f %unit%]" <water> { channel="bluetooth:mi401:hci0:sensor1:pump-head" }
Number:Temperature Pump_Temperature "Temperature [%.1f %unit%]" <temperature> { channel="bluetooth:mi401:hci0:sensor1:pump-temperature" }
Number:Dimensionless Battery_Level "Battery Level [%d %%]" <Battery> { channel="bluetooth:mi401:hci0:sensor1:battery-level" }
jlaur marked this conversation as resolved.
Show resolved Hide resolved
```
25 changes: 25 additions & 0 deletions bundles/org.openhab.binding.bluetooth.grundfosalpha/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns: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 https://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>4.1.0-SNAPSHOT</version>
</parent>

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

<name>openHAB Add-ons :: Bundles :: GrundfosAlpha Binding</name>

<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bluetooth</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.bluetooth.grundfosalpha-${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-grundfosalpha" description="GrundfosAlpha Binding" 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.grundfosalpha/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2023 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.grundfosalpha.internal;

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

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

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

// List of all Channel ids
public static final String CHANNEL_TYPE_FLOW_RATE = "flow-rate";
public static final String CHANNEL_TYPE_PUMP_HEAD = "pump-head";
public static final String CHANNEL_TYPE_BATTERY_LEVEL = "battery-level";
public static final String CHANNEL_TYPE_PUMP_TEMPERATUR = "pump-temperature";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright (c) 2010-2023 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.grundfosalpha.internal;

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 Grundfos Alpha devices and create discovery results for them.
*
* @author Markus Heberling - Initial contribution
*
*/
@NonNullByDefault
@Component
public class GrundfosAlphaDiscoveryParticipant implements BluetoothDiscoveryParticipant {
private final Logger logger = LoggerFactory.getLogger(GrundfosAlphaDiscoveryParticipant.class);

@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return Set.of(GrundfosAlphaBindingConstants.THING_TYPE_MI401);
}

@Override
public boolean requiresConnection(BluetoothDiscoveryDevice device) {
return false;
}

@Override
public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) {
Integer manufacturerId = device.getManufacturerId();
@Nullable
String name = device.getName();
logger.debug("Discovered device {} with manufacturerId {} and name {}", device.getAddress(), manufacturerId,
name);
if ("MI401".equals(name)) {
return new ThingUID(GrundfosAlphaBindingConstants.THING_TYPE_MI401, 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 = "Grundfos Alpha Reader MI401";
Map<String, Object> properties = new HashMap<>();
properties.put(BluetoothBindingConstants.CONFIGURATION_ADDRESS, device.getAddress().toString());
properties.put(Thing.PROPERTY_VENDOR, "Grundfos");
Integer txPower = device.getTxPower();
if (txPower != null) {
properties.put(BluetoothBindingConstants.PROPERTY_TXPOWER, Integer.toString(txPower));
}

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

import static org.openhab.binding.bluetooth.grundfosalpha.internal.GrundfosAlphaBindingConstants.*;

import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Length;
import javax.measure.quantity.Temperature;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.bluetooth.BeaconBluetoothHandler;
import org.openhab.binding.bluetooth.BluetoothDeviceListener;
import org.openhab.binding.bluetooth.notification.BluetoothScanNotification;
import org.openhab.core.library.dimension.VolumetricFlowRate;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Thing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link GrundfosAlphaHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Markus Heberling - Initial contribution
*/
@NonNullByDefault
public class GrundfosAlphaHandler extends BeaconBluetoothHandler implements BluetoothDeviceListener {

private final Logger logger = LoggerFactory.getLogger(GrundfosAlphaHandler.class);

public GrundfosAlphaHandler(Thing thing) {
super(thing);
}

@Override
public void onScanRecordReceived(BluetoothScanNotification scanNotification) {
super.onScanRecordReceived(scanNotification);
byte[] data = scanNotification.getManufacturerData();
if (data != null && data.length == 21) {
int batteryLevel = (data[5] & 0xFF) * 25;
QuantityType<Dimensionless> quantity = new QuantityType<>(batteryLevel, Units.PERCENT);
updateState(CHANNEL_TYPE_BATTERY_LEVEL, quantity);

float flowRate = ((data[9] & 0xFF) << 8 | (data[8] & 0xFF)) / 6553.5f;
QuantityType<VolumetricFlowRate> quantity2 = new QuantityType<>(flowRate, Units.CUBICMETRE_PER_HOUR);
updateState(CHANNEL_TYPE_FLOW_RATE, quantity2);

float pumpHead = ((data[11] & 0xFF) << 8 | (data[10] & 0xFF)) / 3276.7f;
QuantityType<Length> quantity3 = new QuantityType<>(pumpHead, SIUnits.METRE);
updateState(CHANNEL_TYPE_PUMP_HEAD, quantity3);

float pumpTemperature = data[14] & 0xFF;
QuantityType<Temperature> quantity4 = new QuantityType<>(pumpTemperature, SIUnits.CELSIUS);
updateState(CHANNEL_TYPE_PUMP_TEMPERATUR, quantity4);
}
}
}
Loading