]> git.basschouten.com Git - openhab-addons.git/commitdiff
[deconz] Support for air quality sensor (#11885)
authorPhilipp S <philipp56@hotmail.com>
Thu, 6 Jan 2022 08:44:59 +0000 (09:44 +0100)
committerGitHub <noreply@github.com>
Thu, 6 Jan 2022 08:44:59 +0000 (09:44 +0100)
[deconz] Support for air quality sensor

Signed-off-by: Philipp Schneider <philipp.schneider@nixo-soft.de>
bundles/org.openhab.binding.deconz/README.md
bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/BindingConstants.java
bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/discovery/ThingDiscoveryService.java
bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/dto/SensorState.java
bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorBaseThingHandler.java
bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/handler/SensorThingHandler.java
bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz.properties
bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/i18n/deconz_de.properties
bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-thing-types.xml
bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/SensorsTest.java
bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/airquality.json [new file with mode: 0644]

index 22ef7af1e7fd43f73a0f3ebac807e98d179f9b4a..86bef7780d01ff238adc880b6ec5300689770f5c 100644 (file)
@@ -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]"          <motion>        { channel="deconz:presencesensor:homeserver:livingroom-presence:presence" }
 Number:Temperature      Livingroom_Temperature  "Temperature Livingroom [%.1f °C]"  <temperature>   { channel="deconz:temperaturesensor:homeserver:livingroom-temperature:temperature" }
 Number:Dimensionless    Livingroom_Humidity     "Humidity Livingroom [%.1f %%]"     <humidity>      { 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]"    <pressure>      { channel="deconz:pressuresensor:homeserver:livingroom-pressure:pressure" }
 Contact                 Livingroom_Window       "Window Livingroom [%s]"            <door>          { channel="deconz:openclosesensor:homeserver:livingroom-window:open" }
 Switch                  Basement_Water_Leakage  "Basement Water Leakage [%s]"                       { channel="deconz:waterleakagesensor:homeserver:basement-water-leakage:waterleakage" }
index 0cf5c0c0d2cea7909a71ade9dac4485f7e0a1ca3..68929121071c0962171b8e98b22eaa48909c4a4d 100644 (file)
@@ -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";
index eb922db1ba07a59e247ea3b8a7701fa0d5870ec0..c199d89c0a59e59441987e38bb91c91454fdc2f4 100644 (file)
@@ -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;
index 2e532c5cbf6ea0335101e0c3b8ccba6ac5a6c48a..6de44210a269141ddd89bcb52ac45a8124bfcb5d 100644 (file)
@@ -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) + '}';
     }
 }
index f54998b58b84a3c0e97a6e639e0e8407f2e57aaa..71fdaec5f36642afc7ebda3346c593dd553aa4e6 100644 (file)
@@ -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;
index 6665de05652a5477cdeb74e0b6215e17c501c187..79608fcf4252f16d0a3d070b8b1c6dbe6a9387f2 100644 (file)
@@ -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<String> 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;
index b8fbf06f0df4eefd9339b6014703d143668e684e..5433e4710f5e560de9acb543fa309e0177c9c000 100644 (file)
@@ -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
index 596956a64e9b7e5b9f308c7e3dea8518833ce41a..9edb892a4513b4209341beb766cf9b9c9a01730c 100644 (file)
@@ -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
index b79088666d06bfbd2b1d89f577f6ce4c2e5d20b3..b0e77a96c0a18d056462296a34d6e3260926a5ba 100644 (file)
                <state readOnly="true"/>
        </channel-type>
 
+
+       <thing-type id="airqualitysensor">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="deconz"/>
+               </supported-bridge-type-refs>
+               <label>Air quality Sensor</label>
+               <description>An air quality sensor</description>
+               <channels>
+                       <channel typeId="airquality" id="airquality"/>
+                       <channel typeId="airqualityppb" id="airqualityppb"/>
+                       <channel typeId="last_updated" id="last_updated"/>
+               </channels>
+
+               <representation-property>uid</representation-property>
+
+               <config-description-ref uri="thing-type:deconz:sensor"/>
+       </thing-type>
+
+       <channel-type id="airquality">
+               <item-type>String</item-type>
+               <label>Air quality level</label>
+               <description>Current air quality level based on volatile organic compounds (VOCs) measurement. Example: good or poor,
+                       ...</description>
+               <state readOnly="true" pattern="%s"></state>
+       </channel-type>
+
+       <channel-type id="airqualityppb">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Air quality in ppb</label>
+               <description>Current air quality based on measurements of volatile organic compounds (VOCs). The measured value is
+                       specified in ppb (parts per billion).</description>
+               <state readOnly="true" pattern="%d"></state>
+       </channel-type>
+
+
        <thing-type id="thermostat">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
index 2eee55e1f6d2f6b9128b7cc160c4df905336eb6d..a9b7a4469091a2dd22dba773a04b3b96de0c8c17 100644 (file)
@@ -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 (file)
index 0000000..3d511b0
--- /dev/null
@@ -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