diff --git a/bundles/org.openhab.binding.bluetooth.airthings/README.md b/bundles/org.openhab.binding.bluetooth.airthings/README.md index fe97bbc46917f..1f85091762d19 100644 --- a/bundles/org.openhab.binding.bluetooth.airthings/README.md +++ b/bundles/org.openhab.binding.bluetooth.airthings/README.md @@ -6,11 +6,11 @@ This extension adds support for [Airthings](https://www.airthings.com) indoor ai Following thing types are supported by this extension: -| Thing Type ID | Description | -| ------------------- | ------------------------- | -| airthings_wave_plus | Airthings Wave Plus | -| airthings_wave_mini | Airthings Wave Mini | - +| Thing Type ID | Description | +| ------------------- | -------------------------------------- | +| airthings_wave_plus | Airthings Wave Plus | +| airthings_wave_mini | Airthings Wave Mini | +| airthings_wave_gen1 | Airthings Wave 1st Gen (SN 2900xxxxxx) | ## Discovery @@ -44,6 +44,16 @@ The `Airthings Wave Plus` thing has additionally the following channels: | radon_st_avg | Number:Density | The measured radon short term average level | | radon_lt_avg | Number:Density | The measured radon long term average level | +The `Airthings Wave Gen 1` thing has the following channels: + +| Channel ID | Item Type | Description | +| ------------------ | ------------------------ | ------------------------------------------- | +| radon_st_avg | Number:Density | The measured radon short term average level | +| radon_lt_avg | Number:Density | The measured radon long term average level | +| temperature | Number:Temperature | The measured temperature | +| humidity | Number:Dimensionless | The measured humidity | + +Note: For the `Airthings Wave Gen 1`, only one channel can be updated at each refreshInterval, so it will take refreshInterval x 4 cycles to sequentially update all 4 channels ## Example diff --git a/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsBindingConstants.java b/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsBindingConstants.java index de8a43c8f0ae7..74345b604e070 100644 --- a/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsBindingConstants.java +++ b/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsBindingConstants.java @@ -36,6 +36,7 @@ * * @author Pauli Anttila - Initial contribution * @author Kai Kreuzer - Added Airthings Wave Mini support + * @author Davy Wong - Added Airthings Wave Gen 1 support */ @NonNullByDefault public class AirthingsBindingConstants { @@ -45,9 +46,11 @@ public class AirthingsBindingConstants { BluetoothBindingConstants.BINDING_ID, "airthings_wave_plus"); public static final ThingTypeUID THING_TYPE_AIRTHINGS_WAVE_MINI = new ThingTypeUID( BluetoothBindingConstants.BINDING_ID, "airthings_wave_mini"); + public static final ThingTypeUID THING_TYPE_AIRTHINGS_WAVE_GEN1 = new ThingTypeUID( + BluetoothBindingConstants.BINDING_ID, "airthings_wave_gen1"); public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_AIRTHINGS_WAVE_PLUS, - THING_TYPE_AIRTHINGS_WAVE_MINI); + THING_TYPE_AIRTHINGS_WAVE_MINI, THING_TYPE_AIRTHINGS_WAVE_GEN1); // Channel IDs public static final String CHANNEL_ID_HUMIDITY = "humidity"; diff --git a/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsDiscoveryParticipant.java b/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsDiscoveryParticipant.java index 91eb0ee9558c2..99c640dbca993 100644 --- a/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsDiscoveryParticipant.java @@ -33,6 +33,7 @@ * * @author Pauli Anttila - Initial contribution * @author Kai Kreuzer - Added Airthings Wave Mini support + * @author Davy Wong - Added Airthings Wave Gen 1 support * */ @NonNullByDefault @@ -43,6 +44,7 @@ public class AirthingsDiscoveryParticipant implements BluetoothDiscoveryParticip private static final String WAVE_PLUS_MODEL = "2930"; private static final String WAVE_MINI_MODEL = "2920"; + private static final String WAVE_GEN1_MODEL = "2900"; // Wave 1st Gen SN 2900xxxxxx @Override public Set getSupportedThingTypeUIDs() { @@ -60,6 +62,10 @@ public Set getSupportedThingTypeUIDs() { return new ThingUID(AirthingsBindingConstants.THING_TYPE_AIRTHINGS_WAVE_MINI, device.getAdapter().getUID(), device.getAddress().toString().toLowerCase().replace(":", "")); } + if (WAVE_GEN1_MODEL.equals(device.getModel())) { + return new ThingUID(AirthingsBindingConstants.THING_TYPE_AIRTHINGS_WAVE_GEN1, + device.getAdapter().getUID(), device.getAddress().toString().toLowerCase().replace(":", "")); + } } return null; } @@ -79,6 +85,9 @@ public Set getSupportedThingTypeUIDs() { if (WAVE_MINI_MODEL.equals(device.getModel())) { return createResult(device, thingUID, "Airthings Wave Mini"); } + if (WAVE_GEN1_MODEL.equals(device.getModel())) { + return createResult(device, thingUID, "Airthings Wave Gen 1"); + } return null; } diff --git a/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsHandlerFactory.java b/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsHandlerFactory.java index 1b1c2b5b0c42b..6c5a4701712c2 100644 --- a/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsHandlerFactory.java +++ b/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsHandlerFactory.java @@ -26,6 +26,7 @@ * * @author Pauli Anttila - Initial contribution * @author Kai Kreuzer - Added Airthings Wave Mini support + * @author Davy Wong - Added Airthings Wave Gen 1 support */ @NonNullByDefault @Component(service = ThingHandlerFactory.class, configurationPid = "binding.airthings") @@ -45,6 +46,9 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (thingTypeUID.equals(AirthingsBindingConstants.THING_TYPE_AIRTHINGS_WAVE_MINI)) { return new AirthingsWaveMiniHandler(thing); } + if (thingTypeUID.equals(AirthingsBindingConstants.THING_TYPE_AIRTHINGS_WAVE_GEN1)) { + return new AirthingsWaveGen1Handler(thing); + } return null; } } diff --git a/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsWaveGen1Handler.java b/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsWaveGen1Handler.java new file mode 100644 index 0000000000000..8ad7324490722 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.airthings/src/main/java/org/openhab/binding/bluetooth/airthings/internal/AirthingsWaveGen1Handler.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2010-2021 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.airthings.internal; + +import static org.openhab.binding.bluetooth.airthings.internal.AirthingsBindingConstants.*; + +import java.util.UUID; + +import org.eclipse.jdt.annotation.NonNullByDefault; +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 AirthingsWaveGen1Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Davy Wong - Added Airthings Wave Gen 1 support + */ +@NonNullByDefault +public class AirthingsWaveGen1Handler extends AbstractAirthingsHandler { + + private static final String HUMIDITY_UUID = "00002a6f-0000-1000-8000-00805f9b34fb"; // 0x2A6F + private static final String TEMPERATURE_UUID = "00002a6e-0000-1000-8000-00805f9b34fb"; // 0x2A6E + private static final String RADON_STA_UUID = "b42e01aa-ade7-11e4-89d3-123b93f75cba"; + private static final String RADON_LTA_UUID = "b42e0a4c-ade7-11e4-89d3-123b93f75cba"; + + private int intResult; + private double dblResult; + private volatile ReadSensor readSensor = ReadSensor.RADON_STA; + + private enum ReadSensor { + TEMPERATURE, + HUMIDITY, + RADON_STA, + RADON_LTA, + } + + public AirthingsWaveGen1Handler(Thing thing) { + super(thing); + } + + private final Logger logger = LoggerFactory.getLogger(AirthingsWaveGen1Handler.class); + + @Override + protected void updateChannels(int[] is) { + int[] rawdata; + rawdata = is; + if (rawdata.length == 2) { + switch (readSensor) { + case TEMPERATURE: + dblResult = intFromBytes(rawdata[0], rawdata[1]) / 100D; + logger.debug("Parsed data 1: {}", String.format("[temperature=%.1f °C]", dblResult)); + readSensor = ReadSensor.HUMIDITY; + logger.debug("Change next readSensor to: {}", readSensor); + logger.debug("Update channel 1"); + updateState(CHANNEL_ID_TEMPERATURE, + QuantityType.valueOf(Double.valueOf(dblResult), SIUnits.CELSIUS)); + logger.debug("Update channel 1 done"); + break; + case HUMIDITY: + dblResult = intFromBytes(rawdata[0], rawdata[1]) / 100D; + logger.debug("Parsed data 2: {}", String.format("[humidity=%.1f %%rH]", dblResult)); + readSensor = ReadSensor.RADON_STA; + logger.debug("Change next readSensor to: {}", readSensor); + logger.debug("Update channel 2"); + updateState(CHANNEL_ID_HUMIDITY, QuantityType.valueOf(Double.valueOf(dblResult), Units.PERCENT)); + logger.debug("Update channel 2 done"); + break; + case RADON_STA: + intResult = intFromBytes(rawdata[0], rawdata[1]); + logger.debug("Parsed data 3: {}", String.format("[radonShortTermAvg=%d Bq/m3]", intResult)); + readSensor = ReadSensor.RADON_LTA; + logger.debug("Change next readSensor to: {}", readSensor); + logger.debug("Update channel 3"); + updateState(CHANNEL_ID_RADON_ST_AVG, + QuantityType.valueOf(Double.valueOf(intResult), BECQUEREL_PER_CUBIC_METRE)); + logger.debug("Update channel 3 done"); + break; + case RADON_LTA: + intResult = intFromBytes(rawdata[0], rawdata[1]); + logger.debug("Parsed data 4: {}", String.format("[radonLongTermAvg=%d Bq/m3]", intResult)); + readSensor = ReadSensor.TEMPERATURE; + logger.debug("Change next readSensor to: {}", readSensor); + logger.debug("Update channel 4"); + updateState(CHANNEL_ID_RADON_LT_AVG, + QuantityType.valueOf(Double.valueOf(intResult), BECQUEREL_PER_CUBIC_METRE)); + logger.debug("Update channel 4 done"); + break; + } + } else { + logger.debug("Illegal data structure length '%d'", String.valueOf(rawdata).length()); + } + } + + @Override + protected UUID getDataUUID() { + switch (readSensor) { + case TEMPERATURE: + logger.debug("Return UUID Temperature"); + return UUID.fromString(TEMPERATURE_UUID); + case HUMIDITY: + logger.debug("Return UUID Humidity"); + return UUID.fromString(HUMIDITY_UUID); + case RADON_STA: + logger.debug("Return UUID Radon STA"); + return UUID.fromString(RADON_STA_UUID); + case RADON_LTA: + logger.debug("Return UUID Radon LTA"); + return UUID.fromString(RADON_LTA_UUID); + default: + logger.debug("Return UUID Default"); + return UUID.fromString(RADON_STA_UUID); + } + } + + private int intFromBytes(int lowByte, int highByte) { + return (highByte & 0xFF) << 8 | (lowByte & 0xFF); + } + +} diff --git a/bundles/org.openhab.binding.bluetooth.airthings/src/main/resources/OH-INF/thing/airthings.xml b/bundles/org.openhab.binding.bluetooth.airthings/src/main/resources/OH-INF/thing/airthings.xml index 3f7419ac5f0d4..03d7156d4b9b4 100644 --- a/bundles/org.openhab.binding.bluetooth.airthings/src/main/resources/OH-INF/thing/airthings.xml +++ b/bundles/org.openhab.binding.bluetooth.airthings/src/main/resources/OH-INF/thing/airthings.xml @@ -69,6 +69,37 @@ + + + + + + + + + Smart Radon Monitor + + + + + + + + + + + + + + Bluetooth address in XX:XX:XX:XX:XX:XX format + + + + States how often a refresh shall occur in seconds. This could have impact to battery lifetime + 300 + + + Number:Dimensionless