diff --git a/bundles/org.openhab.binding.deconz/README.md b/bundles/org.openhab.binding.deconz/README.md index 22ef7af1e7f..86bef7780d0 100644 --- a/bundles/org.openhab.binding.deconz/README.md +++ b/bundles/org.openhab.binding.deconz/README.md @@ -26,6 +26,7 @@ These sensors are supported: | Vibration Sensor | ZHAVibration | `vibrationsensor` | | deCONZ Artificial Daylight Sensor | deCONZ specific: simulated sensor | `daylightsensor` | | Carbon-Monoxide Sensor | ZHACarbonmonoxide | `carbonmonoxide` | +| Air quality Sensor | ZHAAirQuality | `airqualitysensor` | | Color Controller | ZBT-Remote-ALL-RGBW | `colorcontrol` | @@ -150,6 +151,8 @@ The sensor devices support some of the following channels: | battery_level | Number | R | Battery level (in %) | any battery-powered sensor | | battery_low | Switch | R | Battery level low: `ON`; `OFF` | any battery-powered sensor | | carbonmonoxide | Switch | R | `ON` = carbon monoxide detected | carbonmonoxide | +| airquality | String | R | Current air quality level | airqualitysensor | +| airqualityppb | Number:Dimensionless | R | Current air quality ppb (parts per billion) | airqualitysensor | | color | Color | R | Color set by remote | colorcontrol | | windowopen | Contact | R | `windowopen` status is reported by some thermostats | thermostat | @@ -218,6 +221,7 @@ Bridge deconz:deconz:homeserver [ host="192.168.0.10", apikey="ABCDEFGHIJ" ] { presencesensor livingroom-presence "Livingroom Presence" [ id="1" ] temperaturesensor livingroom-temperature "Livingroom Temperature" [ id="2" ] humiditysensor livingroom-humidity "Livingroom Humidity" [ id="3" ] + airqualitysensor livingroom-voc "Livingroom Voc" [ id="9" ] pressuresensor livingroom-pressure "Livingroom Pressure" [ id="4" ] openclosesensor livingroom-window "Livingroom Window" [ id="5" ] switch livingroom-hue-tap "Livingroom Hue Tap" [ id="6" ] @@ -235,6 +239,8 @@ Bridge deconz:deconz:homeserver [ host="192.168.0.10", apikey="ABCDEFGHIJ" ] { Switch Livingroom_Presence "Presence Livingroom [%s]" { channel="deconz:presencesensor:homeserver:livingroom-presence:presence" } Number:Temperature Livingroom_Temperature "Temperature Livingroom [%.1f °C]" { channel="deconz:temperaturesensor:homeserver:livingroom-temperature:temperature" } Number:Dimensionless Livingroom_Humidity "Humidity Livingroom [%.1f %%]" { channel="deconz:humiditysensor:homeserver:livingroom-humidity:humidity" } +String Livingroom_voc_label "Air quality Livingroom [%s]" { channel="deconz:airqualitysensor:homeserver:livingroom-voc:airquality" } +Number:Dimensionless Livingroom_voc "Air quality [%d ppb]" { channel="deconz:airqualitysensor:homeserver:livingroom-voc:airqualityppb" } Number:Pressure Livingroom_Pressure "Pressure Livingroom [%.1f hPa]" { channel="deconz:pressuresensor:homeserver:livingroom-pressure:pressure" } Contact Livingroom_Window "Window Livingroom [%s]" { channel="deconz:openclosesensor:homeserver:livingroom-window:open" } Switch Basement_Water_Leakage "Basement Water Leakage [%s]" { channel="deconz:waterleakagesensor:homeserver:basement-water-leakage:waterleakage" } diff --git a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/BindingConstants.java b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/BindingConstants.java index 0cf5c0c0d2c..68929121071 100644 --- a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/BindingConstants.java +++ b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/BindingConstants.java @@ -49,6 +49,7 @@ public class BindingConstants { public static final ThingTypeUID THING_TYPE_BATTERY_SENSOR = new ThingTypeUID(BINDING_ID, "batterysensor"); public static final ThingTypeUID THING_TYPE_CARBONMONOXIDE_SENSOR = new ThingTypeUID(BINDING_ID, "carbonmonoxidesensor"); + public static final ThingTypeUID THING_TYPE_AIRQUALITY_SENSOR = new ThingTypeUID(BINDING_ID, "airqualitysensor"); // Special sensor - Thermostat public static final ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat"); @@ -98,6 +99,8 @@ public class BindingConstants { public static final String CHANNEL_BATTERY_LEVEL = "battery_level"; public static final String CHANNEL_BATTERY_LOW = "battery_low"; public static final String CHANNEL_CARBONMONOXIDE = "carbonmonoxide"; + public static final String CHANNEL_AIRQUALITY = "airquality"; + public static final String CHANNEL_AIRQUALITYPPB = "airqualityppb"; public static final String CHANNEL_HEATSETPOINT = "heatsetpoint"; public static final String CHANNEL_THERMOSTAT_MODE = "mode"; public static final String CHANNEL_TEMPERATURE_OFFSET = "offset"; diff --git a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/discovery/ThingDiscoveryService.java b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/discovery/ThingDiscoveryService.java index eb922db1ba0..c199d89c0a5 100644 --- a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/discovery/ThingDiscoveryService.java +++ b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/discovery/ThingDiscoveryService.java @@ -281,6 +281,8 @@ public class ThingDiscoveryService extends AbstractDiscoveryService implements D thingTypeUID = THING_TYPE_BATTERY_SENSOR; // ZHABattery } else if (sensor.type.contains("ZHAThermostat")) { thingTypeUID = THING_TYPE_THERMOSTAT; // ZHAThermostat + } else if (sensor.type.contains("ZHAAirQuality")) { + thingTypeUID = THING_TYPE_AIRQUALITY_SENSOR; } else { logger.debug("Unknown type {}", sensor.type); return; diff --git a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/dto/SensorState.java b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/dto/SensorState.java index 2e532c5cbf6..6de44210a26 100644 --- a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/dto/SensorState.java +++ b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/dto/SensorState.java @@ -54,6 +54,10 @@ public class SensorState { public @Nullable Boolean vibration; /** carbonmonoxide sensors provide a boolean value. */ public @Nullable Boolean carbonmonoxide; + /** airquality sensors provide a string value. */ + public @Nullable String airquality; + /** airquality sensors provide a integer value. */ + public @Nullable Integer airqualityppb; /** Pressure sensors provide a hPa value. */ public @Nullable Integer pressure; /** Presence sensors provide this boolean. */ @@ -88,10 +92,11 @@ public class SensorState { return "SensorState{" + "dark=" + dark + ", daylight=" + daylight + ", lightlevel=" + lightlevel + ", lux=" + lux + ", temperature=" + temperature + ", humidity=" + humidity + ", open=" + open + ", fire=" + fire + ", water=" + water + ", alarm=" + alarm + ", tampered=" + tampered + ", vibration=" + vibration - + ", carbonmonoxide=" + carbonmonoxide + ", pressure=" + pressure + ", presence=" + presence - + ", power=" + power + ", battery=" + battery + ", consumption=" + consumption + ", voltage=" + voltage - + ", current=" + current + ", status=" + status + ", buttonevent=" + buttonevent + ", gesture=" - + gesture + ", valve=" + valve + ", windowopen='" + windowopen + '\'' + ", lastupdated='" + lastupdated - + '\'' + ", xy=" + Arrays.toString(xy) + '}'; + + ", carbonmonoxide=" + carbonmonoxide + ", airquality=" + airquality + ", airqualityppb=" + + airqualityppb + ", pressure=" + pressure + ", presence=" + presence + ", power=" + power + + ", battery=" + battery + ", consumption=" + consumption + ", voltage=" + voltage + ", current=" + + current + ", status=" + status + ", buttonevent=" + buttonevent + ", gesture=" + gesture + ", valve=" + + valve + ", windowopen='" + windowopen + '\'' + ", lastupdated='" + lastupdated + '\'' + ", xy=" + + Arrays.toString(xy) + '}'; } } diff --git a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorBaseThingHandler.java b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorBaseThingHandler.java index f54998b58b8..71fdaec5f36 100644 --- a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorBaseThingHandler.java +++ b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorBaseThingHandler.java @@ -33,6 +33,7 @@ import org.openhab.binding.deconz.internal.types.ResourceType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.StringType; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -250,6 +251,10 @@ public abstract class SensorBaseThingHandler extends DeconzBaseThingHandler { updateState(channelUID, OnOffType.from(value)); } + protected void updateStringChannel(ChannelUID channelUID, @Nullable String value) { + updateState(channelUID, new StringType(value)); + } + protected void updateDecimalTypeChannel(ChannelUID channelUID, @Nullable Number value) { if (value == null) { return; diff --git a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorThingHandler.java b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorThingHandler.java index 6665de05652..79608fcf425 100644 --- a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorThingHandler.java +++ b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorThingHandler.java @@ -60,7 +60,7 @@ public class SensorThingHandler extends SensorBaseThingHandler { THING_TYPE_TEMPERATURE_SENSOR, THING_TYPE_HUMIDITY_SENSOR, THING_TYPE_PRESSURE_SENSOR, THING_TYPE_SWITCH, THING_TYPE_OPENCLOSE_SENSOR, THING_TYPE_WATERLEAKAGE_SENSOR, THING_TYPE_FIRE_SENSOR, THING_TYPE_ALARM_SENSOR, THING_TYPE_VIBRATION_SENSOR, THING_TYPE_BATTERY_SENSOR, - THING_TYPE_CARBONMONOXIDE_SENSOR, THING_TYPE_COLOR_CONTROL); + THING_TYPE_CARBONMONOXIDE_SENSOR, THING_TYPE_AIRQUALITY_SENSOR, THING_TYPE_COLOR_CONTROL); private static final List CONFIG_CHANNELS = List.of(CHANNEL_BATTERY_LEVEL, CHANNEL_BATTERY_LOW, CHANNEL_ENABLED, CHANNEL_TEMPERATURE); @@ -196,6 +196,12 @@ public class SensorThingHandler extends SensorBaseThingHandler { case CHANNEL_CARBONMONOXIDE: updateSwitchChannel(channelUID, newState.carbonmonoxide); break; + case CHANNEL_AIRQUALITY: + updateStringChannel(channelUID, newState.airquality); + break; + case CHANNEL_AIRQUALITYPPB: + updateDecimalTypeChannel(channelUID, newState.airqualityppb); + break; case CHANNEL_BUTTON: updateDecimalTypeChannel(channelUID, newState.buttonevent); break; diff --git a/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz.properties b/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz.properties index b8fbf06f0df..5433e4710f5 100644 --- a/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz.properties +++ b/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz.properties @@ -10,6 +10,8 @@ thing-type.deconz.alarmsensor.description = An alarm sensor thing-type.deconz.batterysensor.label = Battery Sensor thing-type.deconz.batterysensor.description = A battery sensor thing-type.deconz.carbonmonoxidesensor.label = Carbon-monoxide Sensor +thing-type.deconz.airqualitysensor.label = Air quality Sensor +thing-type.deconz.airqualitysensor.description = An air quality sensor thing-type.deconz.colorcontrol.label = Color Controller thing-type.deconz.colorlight.label = Color Light thing-type.deconz.colorlight.description = A dimmable light with adjustable color. @@ -112,6 +114,10 @@ channel-type.deconz.buttonevent.label = Button Trigger channel-type.deconz.buttonevent.description = This channel is triggered on a button event. The trigger payload consists of the button event number. channel-type.deconz.carbonmonoxide.label = Carbon-monoxide channel-type.deconz.carbonmonoxide.description = Carbon-monoxide was detected. +channel-type.deconz.airquality.label = Air quality level +channel-type.deconz.airquality.description = Current air quality level based on volatile organic compounds (VOCs) measurement. Example: good or poor, ... +channel-type.deconz.airqualityppb.label = Air quality in ppb +channel-type.deconz.airqualityppb.description = Current air quality based on measurements of volatile organic compounds (VOCs). The measured value is specified in ppb (parts per billion). channel-type.deconz.consumption.label = Consumption channel-type.deconz.consumption.description = Current consumption channel-type.deconz.ct.label = Color Temperature diff --git a/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz_de.properties b/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz_de.properties index 596956a64e9..9edb892a451 100644 --- a/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz_de.properties +++ b/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz_de.properties @@ -10,6 +10,8 @@ thing-type.deconz.alarmsensor.description = Ein Alarmsensor thing-type.deconz.batterysensor.label = Batteriesensor thing-type.deconz.batterysensor.description = Ein Batteriesensor thing-type.deconz.carbonmonoxidesensor.label = Kohlenmonoxid-Sensor +thing-type.deconz.airqualitysensor.label = Luftqualit\u00e4tssensor +thing-type.deconz.airqualitysensor.description = Ein Luftqualit\u00e4tssensor thing-type.deconz.colorcontrol.label = Farbregler thing-type.deconz.colorlight.label = Farbige Lampe thing-type.deconz.colorlight.description = Dimmbare Lampe mit einstellbarer Farbe. @@ -112,6 +114,10 @@ channel-type.deconz.buttonevent.label = Schaltflächen-Trigger channel-type.deconz.buttonevent.description = Dieser Channel wird bei Bestätigung einer Schaltfläche ausgelöst. Der Trigger-Payload besteht aus der Nummer der Schaltfläche. channel-type.deconz.carbonmonoxide.label = Kohlenmonoxid channel-type.deconz.carbonmonoxide.description = Zeigt an, ob ein erhöhter Kohlenmonoxid-Wert erkannt wurde. +channel-type.deconz.airquality.label = Luftqualit\u00e4t +channel-type.deconz.airquality.description = Derzeitige Luftqualit\u00e4t auf der Grundlage von Messungen fl\u00fcchtiger organischer Verbindungen (VOCs). Beispiel: good oder poor, ... +channel-type.deconz.airqualityppb.label = Luftqualit\u00e4t in ppb +channel-type.deconz.airqualityppb.description = Derzeitige Luftqualit\u00e4t auf der Grundlage von Messungen der fl\u00fcchtigen organischen Verbindungen (VOC). Der Messwert wird in ppb (parts per billion) angegeben. channel-type.deconz.consumption.label = Verbrauch channel-type.deconz.consumption.description = Zeigt den aktuellen Verbrauch an. channel-type.deconz.ct.label = Farbtemperatur diff --git a/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-thing-types.xml b/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-thing-types.xml index b79088666d0..b0e77a96c0a 100644 --- a/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-thing-types.xml +++ b/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-thing-types.xml @@ -491,6 +491,41 @@ + + + + + + + An air quality sensor + + + + + + + uid + + + + + + String + + Current air quality level based on volatile organic compounds (VOCs) measurement. Example: good or poor, + ... + + + + + Number:Dimensionless + + Current air quality based on measurements of volatile organic compounds (VOCs). The measured value is + specified in ppb (parts per billion). + + + + diff --git a/bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/SensorsTest.java b/bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/SensorsTest.java index 2eee55e1f6d..a9b7a446909 100644 --- a/bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/SensorsTest.java +++ b/bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/SensorsTest.java @@ -53,6 +53,7 @@ import com.google.gson.GsonBuilder; * * @author Jan N. Klug - Initial contribution * @author Lukas Agethen - Added Thermostat + * @author Philipp Schneider - Added air quality sensor */ @ExtendWith(MockitoExtension.class) @NonNullByDefault @@ -85,6 +86,46 @@ public class SensorsTest { Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUID), eq(OnOffType.ON)); } + @Test + public void airQualitySensorUpdateTest() throws IOException { + // ARRANGE + SensorMessage sensorMessage = DeconzTest.getObjectFromJson("airquality.json", SensorMessage.class, gson); + assertNotNull(sensorMessage); + + ThingUID thingUID = new ThingUID("deconz", "sensor"); + ChannelUID channelUID = new ChannelUID(thingUID, "airquality"); + Thing sensor = ThingBuilder.create(THING_TYPE_AIRQUALITY_SENSOR, thingUID) + .withChannel(ChannelBuilder.create(channelUID, "String").build()).build(); + SensorThingHandler sensorThingHandler = new SensorThingHandler(sensor, gson); + sensorThingHandler.setCallback(thingHandlerCallback); + + // ACT + sensorThingHandler.messageReceived("", sensorMessage); + + // ASSERT + Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUID), eq(StringType.valueOf("good"))); + } + + @Test + public void airQualityPpbSensorUpdateTest() throws IOException { + // ARRANGE + SensorMessage sensorMessage = DeconzTest.getObjectFromJson("airquality.json", SensorMessage.class, gson); + assertNotNull(sensorMessage); + + ThingUID thingUID = new ThingUID("deconz", "sensor"); + ChannelUID channelUID = new ChannelUID(thingUID, "airqualityppb"); + Thing sensor = ThingBuilder.create(THING_TYPE_AIRQUALITY_SENSOR, thingUID) + .withChannel(ChannelBuilder.create(channelUID, "Number").build()).build(); + SensorThingHandler sensorThingHandler = new SensorThingHandler(sensor, gson); + sensorThingHandler.setCallback(thingHandlerCallback); + + // ACT + sensorThingHandler.messageReceived("", sensorMessage); + + // ASSERT + Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUID), eq(new DecimalType(129))); + } + @Test public void thermostatSensorUpdateTest() throws IOException { SensorMessage sensorMessage = DeconzTest.getObjectFromJson("thermostat.json", SensorMessage.class, gson); diff --git a/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/airquality.json b/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/airquality.json new file mode 100644 index 00000000000..3d511b0e783 --- /dev/null +++ b/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/airquality.json @@ -0,0 +1,22 @@ +{ + "config": { + "battery": 100, + "on": true, + "reachable": true + }, + "ep": 38, + "etag": "ca904b42f63d0ccccccccccccccccccccc", + "lastannounced": "2021-12-28T23:59:02Z", + "lastseen": "2021-12-29T01:18Z", + "manufacturername": "Develco Products A/S", + "modelid": "AQSZB-110", + "name": "AirQuality 4", + "state": { + "airquality": "good", + "airqualityppb": 129, + "lastupdated": "2021-12-29T01:18:41.184" + }, + "swversion": "2021-10-28 08:59", + "type": "ZHAAirQuality", + "uniqueid": "00:00:00:00:00:00:00:00-00-fc03" +} \ No newline at end of file