]> git.basschouten.com Git - openhab-addons.git/commitdiff
[grundfosalpha] Initial contribution (#15907)
authorMarkus Heberling <markus@tisoft.de>
Mon, 4 Dec 2023 10:10:10 +0000 (11:10 +0100)
committerGitHub <noreply@github.com>
Mon, 4 Dec 2023 10:10:10 +0000 (11:10 +0100)
* [Bluez] Disable automatic filtering of duplicate data
* Added Grundfos Alpha binding

Signed-off-by: Markus Heberling <markus@heberling.net>
15 files changed:
CODEOWNERS
bom/openhab-addons/pom.xml
bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZBridgeHandler.java
bundles/org.openhab.binding.bluetooth.grundfosalpha/NOTICE [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/README.md [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/pom.xml [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/feature/feature.xml [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaDiscoveryParticipant.java [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/resources/OH-INF/i18n/bluetooth.properties [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/resources/OH-INF/thing/thing-types.xml [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.grundfosalpha/src/test/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaHandlerTest.java [new file with mode: 0644]
bundles/pom.xml

index f7a11b8ec10a2343ca4b770bae6d4d709e3ed12e..b52bad008e5653a2954bec67d8cf1b41857f093e 100644 (file)
@@ -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
index d62ea28b983220df2c4e0d9d94e6b44319fd9500..84e90d18c3f34df46044f3491e16ead094e33770 100644 (file)
       <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>
index 6903f2d5a63c14291de8e94310e0a191e4b7f8d1..25adffdc92c003bb2d9e645abb4bea4b6a02c5b6 100644 (file)
  */
 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;
@@ -139,6 +146,15 @@ public class BlueZBridgeHandler extends AbstractBluetoothBridgeHandler<BlueZBlue
             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");
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/NOTICE b/bundles/org.openhab.binding.bluetooth.grundfosalpha/NOTICE
new file mode 100644 (file)
index 0000000..38d625e
--- /dev/null
@@ -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
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/README.md b/bundles/org.openhab.binding.bluetooth.grundfosalpha/README.md
new file mode 100644 (file)
index 0000000..8404119
--- /dev/null
@@ -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`:
+
+```java
+bluetooth:mi401:hci0:sensor1 "Grundfos Alpha Reader 1" (bluetooth:bluegiga:adapter1) [ address="12:34:56:78:9A:BC" ]
+```
+
+grundfos_alpha.items:
+
+```java
+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" }
+```
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/pom.xml b/bundles/org.openhab.binding.bluetooth.grundfosalpha/pom.xml
new file mode 100644 (file)
index 0000000..0d0b5ce
--- /dev/null
@@ -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>
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/feature/feature.xml b/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/feature/feature.xml
new file mode 100644 (file)
index 0000000..0a09c41
--- /dev/null
@@ -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>
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaBindingConstants.java b/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaBindingConstants.java
new file mode 100644 (file)
index 0000000..f5eb64a
--- /dev/null
@@ -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";
+}
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaDiscoveryParticipant.java b/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaDiscoveryParticipant.java
new file mode 100644 (file)
index 0000000..cefdd83
--- /dev/null
@@ -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();
+    }
+}
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaHandler.java b/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaHandler.java
new file mode 100644 (file)
index 0000000..ac77c4d
--- /dev/null
@@ -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);
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaHandlerFactory.java b/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaHandlerFactory.java
new file mode 100644 (file)
index 0000000..ac0983b
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * 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 java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link GrundfosAlphaHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Markus Heberling - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.bluetooth.grundfosalpha", service = ThingHandlerFactory.class)
+public class GrundfosAlphaHandlerFactory extends BaseThingHandlerFactory {
+
+    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_MI401);
+
+    @Override
+    public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+        return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+    }
+
+    @Override
+    protected @Nullable ThingHandler createHandler(Thing thing) {
+        ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+        if (THING_TYPE_MI401.equals(thingTypeUID)) {
+            return new GrundfosAlphaHandler(thing);
+        }
+
+        return null;
+    }
+}
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/resources/OH-INF/i18n/bluetooth.properties b/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/resources/OH-INF/i18n/bluetooth.properties
new file mode 100644 (file)
index 0000000..aac1109
--- /dev/null
@@ -0,0 +1,18 @@
+# thing types
+
+thing-type.bluetooth.mi401.label = Grundfos Alpha Reader MI401
+thing-type.bluetooth.mi401.description = A Grundfos Alpha Reader MI401
+
+# thing types config
+
+thing-type.config.bluetooth.mi401.address.label = Address
+thing-type.config.bluetooth.mi401.address.description = Bluetooth address in XX:XX:XX:XX:XX:XX format
+
+# channel types
+
+channel-type.bluetooth.grundfos-flow.label = Current Flow
+channel-type.bluetooth.grundfos-flow.description = Current flow
+channel-type.bluetooth.grundfos-head.label = Current Head
+channel-type.bluetooth.grundfos-head.description = Current head
+channel-type.bluetooth.grundfos-temperature.label = Current Pump Temperature
+channel-type.bluetooth.grundfos-temperature.description = Current pump temperature
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644 (file)
index 0000000..61b2f20
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="bluetooth"
+       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">
+
+       <channel-type id="grundfos-flow">
+               <item-type>Number:VolumetricFlowRate</item-type>
+               <label>Current Flow</label>
+               <description>Current flow</description>
+               <category>Flow</category>
+               <state readOnly="true" pattern="%.2f %unit%"/>
+       </channel-type>
+
+       <channel-type id="grundfos-head">
+               <item-type>Number:Length</item-type>
+               <label>Current Head</label>
+               <description>Current head</description>
+               <category>Water</category>
+               <state readOnly="true" pattern="%.2f %unit%"/>
+       </channel-type>
+
+       <channel-type id="grundfos-temperature">
+               <item-type>Number:Temperature</item-type>
+               <label>Current Pump Temperature</label>
+               <description>Current pump temperature</description>
+               <category>Temperature</category>
+               <tags>
+                       <tag>Measurement</tag>
+                       <tag>Temperature</tag>
+               </tags>
+               <state readOnly="true" pattern="%.2f %unit%"/>
+       </channel-type>
+
+       <thing-type id="mi401">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="roaming"/>
+                       <bridge-type-ref id="bluegiga"/>
+                       <bridge-type-ref id="bluez"/>
+               </supported-bridge-type-refs>
+
+               <label>Grundfos Alpha Reader MI401</label>
+               <description>A Grundfos Alpha Reader MI401</description>
+               <category>Pump</category>
+
+               <channels>
+                       <channel id="rssi" typeId="rssi"/>
+                       <channel id="flow-rate" typeId="grundfos-flow"/>
+                       <channel id="pump-head" typeId="grundfos-head"/>
+                       <channel id="pump-temperature" typeId="grundfos-temperature"/>
+                       <channel id="battery-level" typeId="system.battery-level"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="address" type="text">
+                               <label>Address</label>
+                               <description>Bluetooth address in XX:XX:XX:XX:XX:XX format</description>
+                       </parameter>
+               </config-description>
+
+       </thing-type>
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/test/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaHandlerTest.java b/bundles/org.openhab.binding.bluetooth.grundfosalpha/src/test/java/org/openhab/binding/bluetooth/grundfosalpha/internal/GrundfosAlphaHandlerTest.java
new file mode 100644 (file)
index 0000000..840450a
--- /dev/null
@@ -0,0 +1,91 @@
+/**
+ * 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.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openhab.binding.bluetooth.notification.BluetoothScanNotification;
+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.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingStatusInfo;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandlerCallback;
+import org.openhab.core.util.HexUtils;
+
+/**
+ * Test the {@link GrundfosAlphaHandler}.
+ *
+ * @author Markus Heberling - Initial contribution
+ */
+@ExtendWith(MockitoExtension.class)
+class GrundfosAlphaHandlerTest {
+
+    private @Mock Thing thingMock;
+
+    private @Mock ThingHandlerCallback callback;
+
+    @Test
+    public void testMessageType0xf1() {
+        byte[] data = HexUtils.hexToBytes("15f130017a5113030300994109589916613003004005");
+        final BluetoothScanNotification scanNotification = new BluetoothScanNotification();
+        scanNotification.setManufacturerData(data);
+        final GrundfosAlphaHandler handler = new GrundfosAlphaHandler(thingMock);
+        handler.setCallback(callback);
+        handler.onScanRecordReceived(scanNotification);
+
+        verify(callback).statusUpdated(thingMock,
+                new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null));
+
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void testMessageType0xf2() {
+        when(thingMock.getUID()).thenReturn(new ThingUID(GrundfosAlphaBindingConstants.THING_TYPE_MI401, "dummy"));
+
+        byte[] data = HexUtils.hexToBytes("14f23001650305065419b9180f011f007c1878170d");
+        final BluetoothScanNotification scanNotification = new BluetoothScanNotification();
+        scanNotification.setManufacturerData(data);
+        final GrundfosAlphaHandler handler = new GrundfosAlphaHandler(thingMock);
+        handler.setCallback(callback);
+        handler.onScanRecordReceived(scanNotification);
+
+        verify(callback).statusUpdated(thingMock,
+                new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null));
+        verify(callback).stateUpdated(
+                new ChannelUID(thingMock.getUID(), GrundfosAlphaBindingConstants.CHANNEL_TYPE_BATTERY_LEVEL),
+                new QuantityType<>(75, Units.PERCENT));
+        verify(callback).stateUpdated(
+                new ChannelUID(thingMock.getUID(), GrundfosAlphaBindingConstants.CHANNEL_TYPE_FLOW_RATE),
+                new QuantityType<>(0.98939496, Units.CUBICMETRE_PER_HOUR));
+        verify(callback).stateUpdated(
+                new ChannelUID(thingMock.getUID(), GrundfosAlphaBindingConstants.CHANNEL_TYPE_PUMP_HEAD),
+                new QuantityType<>(1.9315165, SIUnits.METRE));
+        verify(callback).stateUpdated(
+                new ChannelUID(thingMock.getUID(), GrundfosAlphaBindingConstants.CHANNEL_TYPE_PUMP_TEMPERATUR),
+                new QuantityType<>(31, SIUnits.CELSIUS));
+
+        verifyNoMoreInteractions(callback);
+    }
+}
index 9282c64c8ab3d02a9c511086493be5811198842a..59e2e699d4a42b4171fb0c674bfe9466bac08384 100644 (file)
@@ -78,6 +78,7 @@
     <module>org.openhab.binding.bluetooth.enoceanble</module>
     <module>org.openhab.binding.bluetooth.generic</module>
     <module>org.openhab.binding.bluetooth.govee</module>
+    <module>org.openhab.binding.bluetooth.grundfosalpha</module>
     <module>org.openhab.binding.bluetooth.radoneye</module>
     <module>org.openhab.binding.bluetooth.roaming</module>
     <module>org.openhab.binding.bluetooth.ruuvitag</module>