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
*/
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;
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;
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
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;
}
/**
@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 + "]";
}
}
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);
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) {
@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);
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);
@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");
+ }
}
}
--- /dev/null
+# 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
<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>
@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());
+ }
}