]> git.basschouten.com Git - openhab-addons.git/commitdiff
Support for RadonEye with v2.x.x firmware (#14549)
authorjoerg1985 <16140691+joerg1985@users.noreply.github.com>
Sun, 12 Mar 2023 09:48:45 +0000 (10:48 +0100)
committerGitHub <noreply@github.com>
Sun, 12 Mar 2023 09:48:45 +0000 (10:48 +0100)
Signed-off-by: Jörg Sautter <joerg.sautter@gmx.net>
bundles/org.openhab.binding.bluetooth.radoneye/README.md
bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/AbstractRadoneyeHandler.java
bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeConfiguration.java
bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDataParser.java
bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandler.java
bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/i18n/bluetooth.properties [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/thing/radoneye.xml
bundles/org.openhab.binding.bluetooth.radoneye/src/test/java/org/openhab/binding/bluetooth/radoneye/RadoneyeParserTest.java

index 98e9cb2c5b2a68b1366069f4b45bc541d8d30141..90828c6e5c5fcbbe4375cd7c455c305b4235f693 100644 (file)
@@ -18,10 +18,11 @@ As any other Bluetooth device, RadonEye devices are discovered automatically by
 
 Supported configuration parameters for the things:
 
-| Property                        | Type    | Default | Required | Description                                                     |
-|---------------------------------|---------|---------|----------|-----------------------------------------------------------------|
-| address                         | String  |         | Yes      | Bluetooth address of the device (in format "XX:XX:XX:XX:XX:XX") |
-| refreshInterval                 | Integer | 300     | No       | How often a refresh shall occur in seconds                      |
+| Property        | Type    | Default | Required | Description                                                     |
+|-----------------|---------|---------|----------|-----------------------------------------------------------------|
+| address         | String  |         | Yes      | Bluetooth address of the device (in format "XX:XX:XX:XX:XX:XX") |
+| fwVersion       | Integer | 1       | No       | The major version of the firmware on the device                 |
+| refreshInterval | Integer | 300     | No       | How often a refresh shall occur in seconds                      |
 
 ## Channels
 
index 7b3497865faaa125c6d8808ba454b360ddb14c42..ac3d7fc1aee4da894c799f0c5338723f9c8318dc 100644 (file)
@@ -12,7 +12,6 @@
  */
 package org.openhab.binding.bluetooth.radoneye.internal;
 
-import java.util.Optional;
 import java.util.UUID;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -45,10 +44,9 @@ abstract public class AbstractRadoneyeHandler extends BeaconBluetoothHandler {
     private final Logger logger = LoggerFactory.getLogger(AbstractRadoneyeHandler.class);
 
     private AtomicInteger sinceLastReadSec = new AtomicInteger();
-    private Optional<RadoneyeConfiguration> configuration = Optional.empty();
+    private RadoneyeConfiguration configuration = new RadoneyeConfiguration();
     private @Nullable ScheduledFuture<?> scheduledTask;
 
-    private volatile int refreshInterval;
     private volatile int errorConnectCounter;
     private volatile int errorReadCounter;
     private volatile int errorWriteCounter;
@@ -78,16 +76,14 @@ abstract public class AbstractRadoneyeHandler extends BeaconBluetoothHandler {
     public void initialize() {
         logger.debug("Initialize");
         super.initialize();
-        configuration = Optional.of(getConfigAs(RadoneyeConfiguration.class));
-        logger.debug("Using configuration: {}", configuration.get());
+        configuration = getConfigAs(RadoneyeConfiguration.class);
+        logger.debug("Using configuration: {}", configuration);
         cancelScheduledTask();
-        configuration.ifPresent(cfg -> {
-            refreshInterval = cfg.refreshInterval;
-            logger.debug("Start scheduled task to read device in every {} seconds", refreshInterval);
-            scheduledTask = scheduler.scheduleWithFixedDelay(this::executePeridioc, CHECK_PERIOD_SEC, CHECK_PERIOD_SEC,
-                    TimeUnit.SECONDS);
-        });
-        sinceLastReadSec.set(refreshInterval); // update immediately
+        logger.debug("Start scheduled task to read device in every {} seconds", configuration.refreshInterval);
+        scheduledTask = scheduler.scheduleWithFixedDelay(this::executePeridioc, CHECK_PERIOD_SEC, CHECK_PERIOD_SEC,
+                TimeUnit.SECONDS);
+
+        sinceLastReadSec.set(configuration.refreshInterval); // update immediately
     }
 
     @Override
@@ -293,7 +289,16 @@ abstract public class AbstractRadoneyeHandler extends BeaconBluetoothHandler {
     private boolean isTimeToRead() {
         int sinceLastRead = sinceLastReadSec.get();
         logger.debug("Time since last update: {} sec", sinceLastRead);
-        return sinceLastRead >= refreshInterval;
+        return sinceLastRead >= configuration.refreshInterval;
+    }
+
+    /**
+     * Provides the configured major firmware version
+     *
+     * @return the major firmware version configured
+     */
+    protected int getFwVersion() {
+        return configuration.fwVersion;
     }
 
     /**
index 5ee2d7a2f557899ea3ff14225d307cea486f2b4f..d59de8be36c95a1af8705f08f4c16049c47471ea 100644 (file)
@@ -22,10 +22,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 @NonNullByDefault
 public class RadoneyeConfiguration {
     public String address = "";
-    public int refreshInterval;
+    public int fwVersion = 1;
+    public int refreshInterval = 300;
 
     @Override
     public String toString() {
-        return "[address=" + address + ", refreshInterval=" + refreshInterval + "]";
+        return "[address=" + address + ", fwVersion=" + fwVersion + ", refreshInterval=" + refreshInterval + "]";
     }
 }
index 730834a40aea3caeeffc3c4e1c76c5264906e273..d91b92726b0d6a5a2f37b62c7c518f1ad92ef7e0 100644 (file)
@@ -30,7 +30,8 @@ import org.slf4j.LoggerFactory;
 public class RadoneyeDataParser {
     public static final String RADON = "radon";
 
-    private static final int EXPECTED_DATA_LEN = 20;
+    private static final int EXPECTED_DATA_LEN_V1 = 20;
+    private static final int EXPECTED_DATA_LEN_V2 = 12;
     private static final int EXPECTED_VER_PLUS = 1;
 
     private static final Logger logger = LoggerFactory.getLogger(RadoneyeDataParser.class);
@@ -38,18 +39,32 @@ public class RadoneyeDataParser {
     private RadoneyeDataParser() {
     }
 
-    public static Map<String, Number> parseRd200Data(int[] data) throws RadoneyeParserException {
+    public static Map<String, Number> parseRd200Data(int fwVersion, int[] data) throws RadoneyeParserException {
         logger.debug("Parsed data length: {}", data.length);
         logger.debug("Parsed data: {}", data);
-        if (data.length == EXPECTED_DATA_LEN) {
-            final Map<String, Number> result = new HashMap<>();
 
-            int[] radonArray = subArray(data, 2, 6);
-            result.put(RADON, new BigDecimal(readFloat(radonArray) * 37));
-            return result;
-        } else {
-            throw new RadoneyeParserException(String.format("Illegal data structure length '%d'", data.length));
+        final Map<String, Number> result = new HashMap<>();
+
+        switch (fwVersion) {
+            case 1:
+                if (data.length != EXPECTED_DATA_LEN_V1) {
+                    throw new RadoneyeParserException(String.format("Illegal data structure length '%d'", data.length));
+                }
+
+                int[] radonArray = subArray(data, 2, 6);
+                result.put(RADON, new BigDecimal(readFloat(radonArray) * 37));
+                break;
+            case 2:
+                if (data.length != EXPECTED_DATA_LEN_V2) {
+                    throw new RadoneyeParserException(String.format("Illegal data structure length '%d'", data.length));
+                }
+
+                result.put(RADON, intFromBytes(data[2], data[3]));
+                break;
+            default:
+                throw new UnsupportedOperationException("fwVersion: " + fwVersion + " is not implemented");
         }
+        return result;
     }
 
     private static int intFromBytes(int lowByte, int highByte) {
index a5b36a8b0f78d508ba5438f5041af3d113b21ec3..c1de0b517b51dab8da0eb95d8e9764d40e9041f2 100644 (file)
@@ -33,9 +33,14 @@ import org.slf4j.LoggerFactory;
 @NonNullByDefault
 public class RadoneyeHandler extends AbstractRadoneyeHandler {
 
-    private static final String SERVICE_UUID = "00001523-1212-efde-1523-785feabcd123";
-    private static final String TRIGGER_UID = "00001524-1212-efde-1523-785feabcd123";
-    private static final String DATA_UUID = "00001525-1212-efde-1523-785feabcd123";
+    private static final UUID SERVICE_UUID_V1 = UUID.fromString("00001523-1212-efde-1523-785feabcd123");
+    private static final UUID SERVICE_UUID_V2 = UUID.fromString("00001524-0000-1000-8000-00805f9b34fb");
+    private static final UUID TRIGGER_UID_V1 = UUID.fromString("00001524-1212-efde-1523-785feabcd123");
+    private static final UUID TRIGGER_UID_V2 = UUID.fromString("00001524-0000-1000-8000-00805f9b34fb");
+    private static final UUID DATA_UUID_V1 = UUID.fromString("00001525-1212-efde-1523-785feabcd123");
+    private static final UUID DATA_UUID_V2 = UUID.fromString("00001525-0000-1000-8000-00805f9b34fb");
+    private static final byte[] DATA_TRIGGER_V1 = new byte[] { 0x50 };
+    private static final byte[] DATA_TRIGGER_V2 = new byte[] { 0x50 };
 
     public RadoneyeHandler(Thing thing) {
         super(thing);
@@ -43,15 +48,11 @@ public class RadoneyeHandler extends AbstractRadoneyeHandler {
 
     private final Logger logger = LoggerFactory.getLogger(RadoneyeHandler.class);
 
-    private final UUID dataUuid = UUID.fromString(DATA_UUID);
-    private final UUID triggerUuid = UUID.fromString(TRIGGER_UID);
-    private final byte[] triggerData = new byte[] { 0x50 };
-
     @Override
     protected void updateChannels(int[] is) {
         Map<String, Number> data;
         try {
-            data = RadoneyeDataParser.parseRd200Data(is);
+            data = RadoneyeDataParser.parseRd200Data(getFwVersion(), is);
             logger.debug("Parsed data: {}", data);
             Number radon = data.get(RadoneyeDataParser.RADON);
             logger.debug("Parsed data radon number: {}", radon);
@@ -65,16 +66,40 @@ public class RadoneyeHandler extends AbstractRadoneyeHandler {
 
     @Override
     protected UUID getDataUUID() {
-        return dataUuid;
+        int fwVersion = getFwVersion();
+        switch (fwVersion) {
+            case 1:
+                return DATA_UUID_V1;
+            case 2:
+                return DATA_UUID_V2;
+            default:
+                throw new UnsupportedOperationException("fwVersion: " + fwVersion + " is not implemented");
+        }
     }
 
     @Override
     protected UUID getTriggerUUID() {
-        return triggerUuid;
+        int fwVersion = getFwVersion();
+        switch (fwVersion) {
+            case 1:
+                return TRIGGER_UID_V1;
+            case 2:
+                return TRIGGER_UID_V2;
+            default:
+                throw new UnsupportedOperationException("fwVersion: " + fwVersion + " is not implemented");
+        }
     }
 
     @Override
     protected byte[] getTriggerData() {
-        return triggerData;
+        int fwVersion = getFwVersion();
+        switch (fwVersion) {
+            case 1:
+                return DATA_TRIGGER_V1;
+            case 2:
+                return DATA_TRIGGER_V2;
+            default:
+                throw new UnsupportedOperationException("fwVersion: " + fwVersion + " is not implemented");
+        }
     }
 }
diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/i18n/bluetooth.properties b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/i18n/bluetooth.properties
new file mode 100644 (file)
index 0000000..9aa5a91
--- /dev/null
@@ -0,0 +1,18 @@
+# thing types
+
+thing-type.bluetooth.radoneye_rd200.label = RadonEye RD200
+thing-type.bluetooth.radoneye_rd200.description = Indoor radon monitor
+
+# thing types config
+
+thing-type.config.bluetooth.radoneye_rd200.address.label = Address
+thing-type.config.bluetooth.radoneye_rd200.address.description = Bluetooth address in XX:XX:XX:XX:XX:XX format
+thing-type.config.bluetooth.radoneye_rd200.fwVersion.label = Firmware Version
+thing-type.config.bluetooth.radoneye_rd200.fwVersion.description = The major version of the firmware on the device.
+thing-type.config.bluetooth.radoneye_rd200.refreshInterval.label = Refresh Interval
+thing-type.config.bluetooth.radoneye_rd200.refreshInterval.description = States how often a refresh shall occur in seconds.
+
+# channel types
+
+channel-type.bluetooth.radoneye_radon.label = Radon Current Level
+channel-type.bluetooth.radoneye_radon.description = Radon gas level
index 4992c2a9c591d3a6dbdd54d19ae84809222f0ee8..ba46d39d7345367a31e42c9b60ee8ebcc455093c 100644 (file)
                                <label>Address</label>
                                <description>Bluetooth address in XX:XX:XX:XX:XX:XX format</description>
                        </parameter>
+                       <parameter name="fwVersion" type="integer" min="1" max="2">
+                               <label>Firmware Version</label>
+                               <description>The major version of the firmware on the device.</description>
+                               <default>1</default>
+                       </parameter>
                        <parameter name="refreshInterval" type="integer" min="10">
                                <label>Refresh Interval</label>
-                               <description>States how often a refresh shall occur in seconds. This could have impact to battery lifetime</description>
+                               <description>States how often a refresh shall occur in seconds.</description>
                                <default>300</default>
                        </parameter>
                </config-description>
index 47b0c68f0c7a01c37105e43b39eb174820b53af6..91cfc06733067616e186a0bae6d12c06b69e8e69 100644 (file)
@@ -32,20 +32,28 @@ public class RadoneyeParserTest {
     @Test
     public void testEmptyData() {
         int[] data = {};
-        assertThrows(RadoneyeParserException.class, () -> RadoneyeDataParser.parseRd200Data(data));
+        assertThrows(RadoneyeParserException.class, () -> RadoneyeDataParser.parseRd200Data(1, data));
     }
 
     @Test
     public void testWrongDataLen() throws RadoneyeParserException {
         int[] data = { 1, 55, 51, 0, 122, 0, 61, 0, 119, 9, 11, 194, 169, 2, 46, 0, 0 };
-        assertThrows(RadoneyeParserException.class, () -> RadoneyeDataParser.parseRd200Data(data));
+        assertThrows(RadoneyeParserException.class, () -> RadoneyeDataParser.parseRd200Data(1, data));
     }
 
     @Test
-    public void testParsingRd200() throws RadoneyeParserException {
+    public void testParsingRd200v1() throws RadoneyeParserException {
         int[] data = { 80, 16, 31, -123, 43, 64, 123, 20, 94, 64, 92, -113, -118, 64, 15, 0, 12, 0, 0, 0 };
-        Map<String, Number> result = RadoneyeDataParser.parseRd200Data(data);
+        Map<String, Number> result = RadoneyeDataParser.parseRd200Data(1, data);
 
         assertEquals(99, result.get(RadoneyeDataParser.RADON).intValue());
     }
+
+    @Test
+    public void testParsingRd200v2() throws RadoneyeParserException {
+        int[] data = { 0xff, 0xff, 0x5b, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+        Map<String, Number> result = RadoneyeDataParser.parseRd200Data(2, data);
+
+        assertEquals(91, result.get(RadoneyeDataParser.RADON).intValue());
+    }
 }