diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/README.md b/bundles/org.openhab.binding.bluetooth.radoneye/README.md index 98e9cb2c5..90828c6e5 100644 --- a/bundles/org.openhab.binding.bluetooth.radoneye/README.md +++ b/bundles/org.openhab.binding.bluetooth.radoneye/README.md @@ -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 diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/AbstractRadoneyeHandler.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/AbstractRadoneyeHandler.java index 7b3497865..ac3d7fc1a 100644 --- a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/AbstractRadoneyeHandler.java +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/AbstractRadoneyeHandler.java @@ -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 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; } /** diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeConfiguration.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeConfiguration.java index 5ee2d7a2f..d59de8be3 100644 --- a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeConfiguration.java +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeConfiguration.java @@ -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 + "]"; } } diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDataParser.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDataParser.java index 730834a40..d91b92726 100644 --- a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDataParser.java +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeDataParser.java @@ -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 parseRd200Data(int[] data) throws RadoneyeParserException { + public static Map 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 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 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) { diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandler.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandler.java index a5b36a8b0..c1de0b517 100644 --- a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandler.java +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/java/org/openhab/binding/bluetooth/radoneye/internal/RadoneyeHandler.java @@ -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 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 index 000000000..9aa5a913e --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/i18n/bluetooth.properties @@ -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 diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/thing/radoneye.xml b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/thing/radoneye.xml index 4992c2a9c..ba46d39d7 100644 --- a/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/thing/radoneye.xml +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/main/resources/OH-INF/thing/radoneye.xml @@ -24,9 +24,14 @@ Bluetooth address in XX:XX:XX:XX:XX:XX format + + + The major version of the firmware on the device. + 1 + - States how often a refresh shall occur in seconds. This could have impact to battery lifetime + States how often a refresh shall occur in seconds. 300 diff --git a/bundles/org.openhab.binding.bluetooth.radoneye/src/test/java/org/openhab/binding/bluetooth/radoneye/RadoneyeParserTest.java b/bundles/org.openhab.binding.bluetooth.radoneye/src/test/java/org/openhab/binding/bluetooth/radoneye/RadoneyeParserTest.java index 47b0c68f0..91cfc0673 100644 --- a/bundles/org.openhab.binding.bluetooth.radoneye/src/test/java/org/openhab/binding/bluetooth/radoneye/RadoneyeParserTest.java +++ b/bundles/org.openhab.binding.bluetooth.radoneye/src/test/java/org/openhab/binding/bluetooth/radoneye/RadoneyeParserTest.java @@ -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 result = RadoneyeDataParser.parseRd200Data(data); + Map 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 result = RadoneyeDataParser.parseRd200Data(2, data); + + assertEquals(91, result.get(RadoneyeDataParser.RADON).intValue()); + } }