]> git.basschouten.com Git - openhab-addons.git/commitdiff
[deconz] Cleanup code and improve tests, edit channels to vibration sensor (#14641)
authorJ-N-K <github@klug.nrw>
Tue, 21 Mar 2023 21:46:53 +0000 (22:46 +0100)
committerGitHub <noreply@github.com>
Tue, 21 Mar 2023 21:46:53 +0000 (22:46 +0100)
* [deconz] Cleanup code and improve tests

Signed-off-by: Jan N. Klug <github@klug.nrw>
20 files changed:
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/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/thing/sensor-channel-types.xml [new file with mode: 0644]
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 [deleted file]
bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/internal/handler/BaseDeconzThingHandlerTest.java [new file with mode: 0644]
bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/internal/handler/SensorThermostatThingHandlerTest.java
bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/internal/handler/SensorThingHandlerTest.java [new file with mode: 0644]
bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/airquality.json [deleted file]
bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/carbonmonoxide.json [deleted file]
bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/fire.json [deleted file]
bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/airquality.json [new file with mode: 0644]
bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/carbonmonoxide.json [new file with mode: 0644]
bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/fire.json [new file with mode: 0644]
bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/switch.json [new file with mode: 0644]
bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/vibration.json [new file with mode: 0644]

index d580134e468f23c6ad904cddf9c67fc6b39ff85a..01de1a5d38f86286210a4a871d36bb683427d36e 100644 (file)
@@ -124,44 +124,47 @@ Bridge deconz:deconz:homeserver [ host="192.168.0.10", apikey="ABCDEFGHIJ" ]
 
 The sensor devices support some of the following channels:
 
-| Channel Type ID    | Item Type                | Access Mode | Description                                                                               | Thing types                                       |
-|--------------------|--------------------------|-------------|-------------------------------------------------------------------------------------------|---------------------------------------------------|
-| presence           | Switch                   | R           | Status of presence: `ON` = presence; `OFF` = no-presence                                  | presencesensor                                    |
-| enabled            | Switch                   | R/W         | This channel activates or deactivates the sensor                                          | presencesensor                                    |
-| last_updated       | DateTime                 | R           | Timestamp when the sensor was last updated                                                | all, except daylightsensor                        |
-| last_seen          | DateTime                 | R           | Timestamp when the sensor was last seen                                                   | all, except daylightsensor                        |
-| power              | Number:Power             | R           | Power usage in Watts                                                                      | powersensor, sometimes for consumptionsensor      |
-| consumption        | Number:Energy            | R           | Energy in Watt*Hour                                                                       | consumptionsensor                                 |
-| voltage            | Number:ElectricPotential | R           | Voltage in V                                                                              | some powersensors                                 |
-| current            | Number:ElectricCurrent   | R           | Current in mA                                                                             | some powersensors                                 |
-| button             | Number                   | R           | Last pressed button id on a switch                                                        | switch, colorcontrol                              |
-| gesture            | Number                   | R           | A gesture that was performed with the switch                                              | switch                                            |
-| lightlux           | Number:Illuminance       | R           | Light illuminance in Lux                                                                  | lightsensor                                       |
-| light_level        | Number                   | R           | Light level                                                                               | lightsensor                                       |
-| dark               | Switch                   | R           | Light level is below the darkness threshold                                               | lightsensor, sometimes for presencesensor         |
-| daylight           | Switch                   | R           | Light level is above the daylight threshold                                               | lightsensor                                       |
-| temperature        | Number:Temperature       | R           | Temperature in ˚C                                                                         | temperaturesensor, some Xiaomi sensors,thermostat |
-| humidity           | Number:Dimensionless     | R           | Humidity in %                                                                             | humiditysensor                                    |
-| pressure           | Number:Pressure          | R           | Pressure in hPa                                                                           | pressuresensor                                    |
-| open               | Contact                  | R           | Status of contacts: `OPEN`; `CLOSED`                                                      | openclosesensor                                   |
-| waterleakage       | Switch                   | R           | Status of water leakage: `ON` = water leakage detected; `OFF` = no water leakage detected | waterleakagesensor                                |
-| fire               | Switch                   | R           | Status of a fire: `ON` = fire was detected; `OFF` = no fire detected                      | firesensor                                        |
-| alarm              | Switch                   | R           | Status of an alarm: `ON` = alarm was triggered; `OFF` = no alarm                          | alarmsensor                                       |
-| tampered           | Switch                   | R           | Status of a zone: `ON` = zone is being tampered; `OFF` = zone is not tampered             | any IAS sensor                                    |
-| vibration          | Switch                   | R           | Status of vibration: `ON` = vibration was detected; `OFF` = no vibration                  | alarmsensor                                       |
-| light              | String                   | R           | Light level: `Daylight`; `Sunset`; `Dark`                                                 | daylightsensor                                    |
-| value              | Number                   | R           | Sun position: `130` = dawn; `140` = sunrise; `190` = sunset; `210` = dusk                 | daylightsensor                                    |
-| 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                                    |
-| color              | Color                    | R           | Color set by remote                                                                       | colorcontrol                                      |
-| windowopen         | Contact                  | R           | `windowopen` status is reported by some thermostats                                       | thermostat                                        |
-| externalwindowopen | Contact                  | R/W         | forward a status to a thermostat (some devices)                                           | thermostat                                        |
-| on                 | Switch                   | R           | some thermostats report their output state as switch                                      | thermostat                                        |
-| locked             | Switch                   | R/W         | reports/sets the child lock on some thermostats                                           | thermostat                                        |
-| airquality         | String                   | R           | Airquality as string                                                                      | airqualitysensor                                  |
-| airqualityppb      | Number:Dimensionless     | R           | Airquality (in parts-per-billion)                                                         | airqualitysensor                                  |
-| moisture           | Number:Dimensionless     | R           | Moisture                                                                                  | moisturesensor                                    |
+| Channel Type ID       | Item Type                | Access Mode | Description                                                                               | Thing types                                       |
+|-----------------------|--------------------------|-------------|-------------------------------------------------------------------------------------------|---------------------------------------------------|
+| airquality            | String                   | R           | Airquality as string                                                                      | airqualitysensor                                  |
+| airqualityppb         | Number:Dimensionless     | R           | Airquality (in parts-per-billion)                                                         | airqualitysensor                                  |
+| alarm                 | Switch                   | R           | Status of an alarm: `ON` = alarm was triggered; `OFF` = no alarm                          | alarmsensor                                       |
+| battery_level         | Number                   | R           | Battery level (in %)                                                                      | any battery-powered sensor                        |
+| battery_low           | Switch                   | R           | Battery level low: `ON`; `OFF`                                                            | any battery-powered sensor                        |
+| button                | Number                   | R           | Last pressed button id on a switch                                                        | switch, colorcontrol                              |
+| carbonmonoxide        | Switch                   | R           | `ON` = carbon monoxide detected                                                           | carbonmonoxide                                    |
+| color                 | Color                    | R           | Color set by remote                                                                       | colorcontrol                                      |
+| consumption           | Number:Energy            | R           | Energy in Watt*Hour                                                                       | consumptionsensor                                 |
+| current               | Number:ElectricCurrent   | R           | Current in mA                                                                             | some powersensors                                 |
+| dark                  | Switch                   | R           | Light level is below the darkness threshold                                               | lightsensor, sometimes for presencesensor         |
+| daylight              | Switch                   | R           | Light level is above the daylight threshold                                               | lightsensor                                       |
+| enabled               | Switch                   | R/W         | This channel activates or deactivates the sensor                                          | presencesensor                                    |
+| externalwindowopen    | Contact                  | R/W         | forward a status to a thermostat (some devices)                                           | thermostat                                        |
+| fire                  | Switch                   | R           | Status of a fire: `ON` = fire was detected; `OFF` = no fire detected                      | firesensor                                        |
+| gesture               | Number                   | R           | A gesture that was performed with the switch                                              | switch                                            |
+| humidity              | Number:Dimensionless     | R           | Humidity in %                                                                             | humiditysensor                                    |
+| last_updated          | DateTime                 | R           | Timestamp when the sensor was last updated                                                | all, except daylightsensor                        |
+| last_seen             | DateTime                 | R           | Timestamp when the sensor was last seen                                                   | all, except daylightsensor                        |
+| light                 | String                   | R           | Light level: `Daylight`; `Sunset`; `Dark`                                                 | daylightsensor                                    |
+| lightlux              | Number:Illuminance       | R           | Light illuminance in Lux                                                                  | lightsensor                                       |
+| light_level           | Number                   | R           | Light level                                                                               | lightsensor                                       |
+| locked                | Switch                   | R/W         | reports/sets the child lock on some thermostats                                           | thermostat                                        |
+| moisture              | Number:Dimensionless     | R           | Moisture                                                                                  | moisturesensor                                    |
+| on                    | Switch                   | R           | some thermostats report their output state as switch                                      | thermostat                                        |
+| open                  | Contact                  | R           | Status of contacts: `OPEN`; `CLOSED`                                                      | openclosesensor                                   |
+| orientation_x, _y, _z | Number                   | R           | Orientation of vibration sensor                                                           | vibrationsensor                                   |
+| power                 | Number:Power             | R           | Power usage in Watts                                                                      | powersensor, sometimes for consumptionsensor      |
+| presence              | Switch                   | R           | Status of presence: `ON` = presence; `OFF` = no-presence                                  | presencesensor                                    |
+| pressure              | Number:Pressure          | R           | Pressure in hPa                                                                           | pressuresensor                                    |
+| tampered              | Switch                   | R           | Status of a zone: `ON` = zone is being tampered; `OFF` = zone is not tampered             | any IAS sensor                                    |
+| temperature           | Number:Temperature       | R           | Temperature in ˚C                                                                         | temperaturesensor, some Xiaomi sensors,thermostat |
+| tiltangle             | Number:Angle             | R           | Tilt angle of vibration sensor                                                            | vibrationsensor                                   |
+| value                 | Number                   | R           | Sun position: `130` = dawn; `140` = sunrise; `190` = sunset; `210` = dusk                 | daylightsensor                                    |
+| vibration             | Switch                   | R           | Vibration detected                                                                        | vibrationsensor                                   |
+| vibrationstrength     | Number                   | R           | Strength of detected vibration (value is device-dependent)                                | vibrationsensor                                   |
+| voltage               | Number:ElectricPotential | R           | Voltage in V                                                                              | some powersensors                                 |
+| waterleakage          | Switch                   | R           | Status of water leakage: `ON` = water leakage detected; `OFF` = no water leakage detected | waterleakagesensor                                |
+| windowopen            | Contact                  | R           | `windowopen` status is reported by some thermostats                                       | thermostat                                        |
 
 **NOTE:** Beside other non-mandatory channels, the `battery_level` and `battery_low` channels will be added to the Thing during runtime if the sensor is battery-powered.
 The specification of your sensor depends on the deCONZ capabilities.
index c29e7063ab0f6734f377be6aba16c6fca1a293f1..b345641b6f82eadad879926adb59396b48e9f511 100644 (file)
@@ -72,64 +72,70 @@ public class BindingConstants {
     public static final ThingTypeUID THING_TYPE_LIGHTGROUP = new ThingTypeUID(BINDING_ID, "lightgroup");
 
     // sensor channel ids
-    public static final String CHANNEL_PRESENCE = "presence";
-    public static final String CHANNEL_ENABLED = "enabled";
-    public static final String CHANNEL_LAST_UPDATED = "last_updated";
-    public static final String CHANNEL_LAST_SEEN = "last_seen";
-    public static final String CHANNEL_POWER = "power";
+    public static final String CHANNEL_AIRQUALITY = "airquality";
+    public static final String CHANNEL_AIRQUALITYPPB = "airqualityppb";
+    public static final String CHANNEL_ALARM = "alarm";
+    public static final String CHANNEL_BATTERY_LEVEL = "battery_level";
+    public static final String CHANNEL_BATTERY_LOW = "battery_low";
+    public static final String CHANNEL_BUTTON = "button";
+    public static final String CHANNEL_BUTTONEVENT = "buttonevent";
+    public static final String CHANNEL_CARBONMONOXIDE = "carbonmonoxide";
     public static final String CHANNEL_CONSUMPTION = "consumption";
     public static final String CHANNEL_CONSUMPTION_2 = "consumption2";
-    public static final String CHANNEL_VOLTAGE = "voltage";
     public static final String CHANNEL_CURRENT = "current";
-    public static final String CHANNEL_VALUE = "value";
-    public static final String CHANNEL_TEMPERATURE = "temperature";
-    public static final String CHANNEL_HUMIDITY = "humidity";
-    public static final String CHANNEL_PRESSURE = "pressure";
-    public static final String CHANNEL_LIGHT = "light";
-    public static final String CHANNEL_LIGHT_LUX = "lightlux";
-    public static final String CHANNEL_LIGHT_LEVEL = "light_level";
     public static final String CHANNEL_DARK = "dark";
     public static final String CHANNEL_DAYLIGHT = "daylight";
-    public static final String CHANNEL_BUTTON = "button";
-    public static final String CHANNEL_BUTTONEVENT = "buttonevent";
+    public static final String CHANNEL_ENABLED = "enabled";
+    public static final String CHANNEL_EXTERNAL_WINDOW_OPEN = "externalwindowopen";
+    public static final String CHANNEL_FIRE = "fire";
     public static final String CHANNEL_GESTURE = "gesture";
     public static final String CHANNEL_GESTUREEVENT = "gestureevent";
+    public static final String CHANNEL_HEATSETPOINT = "heatsetpoint";
+    public static final String CHANNEL_HUMIDITY = "humidity";
+    public static final String CHANNEL_LIGHT = "light";
+    public static final String CHANNEL_LIGHT_LUX = "lightlux";
+    public static final String CHANNEL_LIGHT_LEVEL = "light_level";
+    public static final String CHANNEL_PRESENCE = "presence";
+    public static final String CHANNEL_LAST_SEEN = "last_seen";
+    public static final String CHANNEL_LAST_UPDATED = "last_updated";
+    public static final String CHANNEL_MOISTURE = "moisture";
     public static final String CHANNEL_OPENCLOSE = "open";
-    public static final String CHANNEL_WATERLEAKAGE = "waterleakage";
-    public static final String CHANNEL_FIRE = "fire";
-    public static final String CHANNEL_ALARM = "alarm";
+    public static final String CHANNEL_ORIENTATION_X = "orientation_x";
+    public static final String CHANNEL_ORIENTATION_Y = "orientation_y";
+    public static final String CHANNEL_ORIENTATION_Z = "orientation_z";
+    public static final String CHANNEL_POWER = "power";
+    public static final String CHANNEL_PRESSURE = "pressure";
     public static final String CHANNEL_TAMPERED = "tampered";
-    public static final String CHANNEL_VIBRATION = "vibration";
-    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_MOISTURE = "moisture";
-    public static final String CHANNEL_HEATSETPOINT = "heatsetpoint";
+    public static final String CHANNEL_TEMPERATURE = "temperature";
+    public static final String CHANNEL_TEMPERATURE_OFFSET = "offset";
     public static final String CHANNEL_THERMOSTAT_MODE = "mode";
     public static final String CHANNEL_THERMOSTAT_LOCKED = "locked";
-    public static final String CHANNEL_TEMPERATURE_OFFSET = "offset";
     public static final String CHANNEL_THERMOSTAT_ON = "on";
+    public static final String CHANNEL_TILTANGLE = "tiltangle";
     public static final String CHANNEL_VALVE_POSITION = "valve";
+    public static final String CHANNEL_VIBRATION = "vibration";
+    public static final String CHANNEL_VIBRATION_STRENGTH = "vibrationstrength";
+    public static final String CHANNEL_VOLTAGE = "voltage";
+    public static final String CHANNEL_VALUE = "value";
+    public static final String CHANNEL_WATERLEAKAGE = "waterleakage";
     public static final String CHANNEL_WINDOW_OPEN = "windowopen";
-    public static final String CHANNEL_EXTERNAL_WINDOW_OPEN = "externalwindowopen";
 
     // group + light channel ids
-    public static final String CHANNEL_SWITCH = "switch";
-    public static final String CHANNEL_BRIGHTNESS = "brightness";
-    public static final String CHANNEL_COLOR_TEMPERATURE = "color_temperature";
-    public static final String CHANNEL_COLOR = "color";
-    public static final String CHANNEL_POSITION = "position";
     public static final String CHANNEL_ALERT = "alert";
     public static final String CHANNEL_ALL_ON = "all_on";
     public static final String CHANNEL_ANY_ON = "any_on";
-    public static final String CHANNEL_LOCK = "lock";
+    public static final String CHANNEL_BRIGHTNESS = "brightness";
+    public static final String CHANNEL_COLOR = "color";
+    public static final String CHANNEL_COLOR_TEMPERATURE = "color_temperature";
     public static final String CHANNEL_EFFECT = "effect";
     public static final String CHANNEL_EFFECT_SPEED = "effectSpeed";
-    public static final String CHANNEL_SCENE = "scene";
+    public static final String CHANNEL_LOCK = "lock";
     public static final String CHANNEL_ONTIME = "ontime";
 
+    public static final String CHANNEL_POSITION = "position";
+    public static final String CHANNEL_SCENE = "scene";
+    public static final String CHANNEL_SWITCH = "switch";
+
     // channel uids
     public static final ChannelTypeUID CHANNEL_EFFECT_TYPE_UID = new ChannelTypeUID(BINDING_ID, CHANNEL_EFFECT);
     public static final ChannelTypeUID CHANNEL_EFFECT_SPEED_TYPE_UID = new ChannelTypeUID(BINDING_ID,
index ab95549276259723da7eef4bc6c89816cb1a995e..f1fd3dc505f23f2f4f2ee189eeef1f76db22b60c 100644 (file)
@@ -28,80 +28,55 @@ import org.eclipse.jdt.annotation.Nullable;
  */
 @NonNullByDefault
 public class SensorState {
-    /** Some presence sensors, the daylight sensor and all light sensors provide the "dark" boolean. */
+    public @Nullable String airquality;
+    public @Nullable Integer airqualityppb;
+    public @Nullable Boolean alarm;
+    public @Nullable Integer battery;
+    public @Nullable Integer buttonevent;
+    public @Nullable Boolean carbonmonoxide;
+    public @Nullable Double consumption;
+    public @Nullable Double consumption2;
+    public @Nullable Double current;
     public @Nullable Boolean dark;
-    /** The daylight sensor and all light sensors provides the "daylight" boolean. */
     public @Nullable Boolean daylight;
-    /** Light sensors provide a light level value. */
+    public @Nullable Boolean fire;
+    public @Nullable Integer gesture;
+    public @Nullable Double humidity;
+    public @Nullable String lastupdated;
     public @Nullable Integer lightlevel;
-    /** Light sensors provide a lux value. */
+    public @Nullable Boolean lowbattery;
     public @Nullable Integer lux;
-    /** Temperature sensors provide a degrees value. */
-    public @Nullable Double temperature;
-    /** Humidity sensors provide a percent value. */
-    public @Nullable Double humidity;
-    /** OpenClose sensors provide a boolean value. */
+    public @Nullable Integer moisture;
+    public @Nullable Boolean on;
     public @Nullable Boolean open;
-    /** fire sensors provide a boolean value. */
-    public @Nullable Boolean fire;
-    /** water sensors provide a boolean value. */
-    public @Nullable Boolean water;
-    /** alarm sensors provide a boolean value. */
-    public @Nullable Boolean alarm;
-    /** IAS Zone sensors provide a boolean value. */
-    public @Nullable Boolean tampered;
-    /** vibration sensors provide a boolean value. */
-    public @Nullable Boolean vibration;
-    /** carbonmonoxide sensors provide a boolean value. */
-    public @Nullable Boolean carbonmonoxide;
-    /** Pressure sensors provide a hPa value. */
-    public @Nullable Integer pressure;
-    /** Presence sensors provide this boolean. */
-    public @Nullable Boolean presence;
-    /** Power sensors provide this value in Watts. */
+    public Integer @Nullable [] orientation;
     public @Nullable Double power;
-    /** Batttery sensors provide this value */
-    public @Nullable Integer battery;
-    /** Consumption sensors provide this value in Watts/hour. */
-    public @Nullable Boolean lowbattery;
-    /** Consumption sensors provide this value in Watts/hour. */
-    public @Nullable Double consumption;
-    public @Nullable Double consumption2;
-    /** Power sensors provide this value in Volt. */
-    public @Nullable Double voltage;
-    /** Power sensors provide this value in Milliampere. */
-    public @Nullable Double current;
-    /** Light sensors and the daylight sensor provide a status integer that can have various semantics. */
+    public @Nullable Boolean presence;
+    public @Nullable Integer pressure;
     public @Nullable Integer status;
-    /** Switches provide this value. */
-    public @Nullable Integer buttonevent;
-    /** Switches may provide this value. */
-    public @Nullable Integer gesture;
-    /** Thermostat may provide this value. */
+    public @Nullable Boolean tampered;
+    public @Nullable Double temperature;
+    public @Nullable Integer tiltangle;
     public @Nullable Integer valve;
-    public @Nullable Boolean on;
-    /** air quality sensors provide this value */
-    public @Nullable String airquality;
-    public @Nullable Integer airqualityppb;
-    /** moisture sensors provide this value */
-    public @Nullable Integer moisture;
-    /** Thermostats may provide this value */
+    public @Nullable Boolean vibration;
+    public @Nullable Integer vibrationstrength;
+    public @Nullable Double voltage;
+    public @Nullable Boolean water;
     public @Nullable String windowopen;
-    /** deCONZ sends a last update string with every event. */
-    public @Nullable String lastupdated;
-    /** color controllers send xy values */
     public double @Nullable [] xy;
 
     @Override
     public String toString() {
-        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 + ", lowbattery=" + lowbattery + ", consumption="
-                + consumption + ", voltage=" + voltage + ", current=" + current + ", status=" + status
-                + ", buttonevent=" + buttonevent + ", gesture=" + gesture + ", valve=" + valve + ", airquality='"
-                + airquality + "'" + ", airqualityppb=" + airqualityppb + ", windowopen='" + windowopen + "'"
-                + ", lastupdated='" + lastupdated + "'" + ", xy=" + Arrays.toString(xy) + "}";
+        return "SensorState{" + "airquality='" + airquality + "'" + ", airqualityppb=" + airqualityppb + ", alarm="
+                + alarm + ", battery=" + battery + ", buttonevent=" + buttonevent + ", carbonmonoxide=" + carbonmonoxide
+                + ", consumption=" + consumption + ", consumption2=" + consumption2 + ", current=" + current + ", dark="
+                + dark + ", daylight=" + daylight + ", fire=" + fire + ", gesture=" + gesture + ", humidity=" + humidity
+                + ", lastupdated='" + lastupdated + "'" + ", lightlevel=" + lightlevel + ", lowbattery=" + lowbattery
+                + ", lux=" + lux + ", moisture=" + moisture + ", on=" + on + ", open=" + open + ", orientation="
+                + Arrays.toString(orientation) + ", power=" + power + ", presence=" + presence + ", pressure="
+                + pressure + ", status=" + status + ", tampered=" + tampered + ", temperature=" + temperature
+                + ", tiltangle=" + tiltangle + ", valve=" + valve + ", vibration=" + vibration + ", vibrationstrength="
+                + vibrationstrength + ", voltage=" + voltage + ", water=" + water + ", windowopen='" + windowopen + "'"
+                + ", xy=" + Arrays.toString(xy) + "}";
     }
 }
index 004d769d098dc3b8c445d809c07ab35b6ec0093d..1f5f0e428e65ef873acd2be99fd6c4e55a2ff34e 100644 (file)
@@ -33,6 +33,7 @@ import org.openhab.core.thing.ThingStatusDetail;
 import org.openhab.core.thing.binding.builder.ThingBuilder;
 import org.openhab.core.thing.type.ChannelKind;
 import org.openhab.core.types.Command;
+import org.openhab.core.types.UnDefType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -182,6 +183,8 @@ public abstract class SensorBaseThingHandler extends DeconzBaseThingHandler {
                 String lastUpdated = newState.lastupdated;
                 if (lastUpdated != null && !"none".equals(lastUpdated)) {
                     updateState(channelUID, Util.convertTimestampToDateTime(lastUpdated));
+                } else if ("none".equals(lastUpdated)) {
+                    updateState(channelUID, UnDefType.UNDEF);
                 }
             }
             case CHANNEL_BATTERY_LOW -> updateSwitchChannel(channelUID, newState.lowbattery);
index 0d2322dcc6eb78972bf901d00ee872b3fb77f1f1..20e84a80413ba72e37d621a4b586871390ced445 100644 (file)
@@ -28,7 +28,6 @@ import org.openhab.binding.deconz.internal.dto.SensorUpdateConfig;
 import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.OpenClosedType;
 import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.types.StringType;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingTypeUID;
@@ -107,73 +106,55 @@ public class SensorThingHandler extends SensorBaseThingHandler {
     protected void valueUpdated(ChannelUID channelUID, SensorState newState, boolean initializing) {
         super.valueUpdated(channelUID, newState, initializing);
         switch (channelUID.getId()) {
+            case CHANNEL_AIRQUALITY -> updateStringChannel(channelUID, newState.airquality);
+            case CHANNEL_AIRQUALITYPPB ->
+                updateQuantityTypeChannel(channelUID, newState.airqualityppb, PARTS_PER_BILLION);
+            case CHANNEL_ALARM -> updateSwitchChannel(channelUID, newState.alarm);
             case CHANNEL_BATTERY_LEVEL -> updateDecimalTypeChannel(channelUID, newState.battery);
-            case CHANNEL_LIGHT -> {
-                Boolean dark = newState.dark;
-                if (dark != null) {
-                    Boolean daylight = newState.daylight;
-                    if (dark) { // if it's dark, it's dark ;)
-                        updateState(channelUID, new StringType("Dark"));
-                    } else if (daylight != null) { // if its not dark, it might be between darkness and daylight
-                        if (daylight) {
-                            updateState(channelUID, new StringType("Daylight"));
-                        } else {
-                            updateState(channelUID, new StringType("Sunset"));
-                        }
-                    } else { // if no daylight value is known, we assume !dark means daylight
-                        updateState(channelUID, new StringType("Daylight"));
-                    }
-                }
-            }
-            case CHANNEL_POWER -> updateQuantityTypeChannel(channelUID, newState.power, WATT);
-            case CHANNEL_CONSUMPTION -> updateQuantityTypeChannel(channelUID, newState.consumption, WATT_HOUR);
-            case CHANNEL_VOLTAGE -> updateQuantityTypeChannel(channelUID, newState.voltage, VOLT);
-            case CHANNEL_CURRENT -> updateQuantityTypeChannel(channelUID, newState.current, MILLI(AMPERE));
-            case CHANNEL_LIGHT_LUX -> updateQuantityTypeChannel(channelUID, newState.lux, LUX);
+            case CHANNEL_BUTTON -> updateDecimalTypeChannel(channelUID, newState.buttonevent);
+            case CHANNEL_BUTTONEVENT -> triggerChannel(channelUID, newState.buttonevent, initializing);
+            case CHANNEL_CARBONMONOXIDE -> updateSwitchChannel(channelUID, newState.carbonmonoxide);
             case CHANNEL_COLOR -> {
                 final double @Nullable [] xy = newState.xy;
                 if (xy != null && xy.length == 2) {
                     updateState(channelUID, ColorUtil.xyToHsv(xy));
                 }
             }
-            case CHANNEL_LIGHT_LEVEL -> updateDecimalTypeChannel(channelUID, newState.lightlevel);
+            case CHANNEL_CONSUMPTION -> updateQuantityTypeChannel(channelUID, newState.consumption, WATT_HOUR);
+            case CHANNEL_CURRENT -> updateQuantityTypeChannel(channelUID, newState.current, MILLI(AMPERE));
             case CHANNEL_DARK -> updateSwitchChannel(channelUID, newState.dark);
             case CHANNEL_DAYLIGHT -> updateSwitchChannel(channelUID, newState.daylight);
-            case CHANNEL_TEMPERATURE -> updateQuantityTypeChannel(channelUID, newState.temperature, CELSIUS, 1.0 / 100);
+            case CHANNEL_FIRE -> updateSwitchChannel(channelUID, newState.fire);
+            case CHANNEL_GESTURE -> updateDecimalTypeChannel(channelUID, newState.gesture);
+            case CHANNEL_GESTUREEVENT -> triggerChannel(channelUID, newState.gesture, initializing);
             case CHANNEL_HUMIDITY -> updateQuantityTypeChannel(channelUID, newState.humidity, PERCENT, 1.0 / 100);
-            case CHANNEL_PRESSURE -> updateQuantityTypeChannel(channelUID, newState.pressure, HECTO(PASCAL));
-            case CHANNEL_PRESENCE -> updateSwitchChannel(channelUID, newState.presence);
-            case CHANNEL_VALUE -> updateDecimalTypeChannel(channelUID, newState.status);
+            case CHANNEL_LIGHT -> updateStringChannel(channelUID, getLightState(newState));
+            case CHANNEL_LIGHT_LEVEL -> updateDecimalTypeChannel(channelUID, newState.lightlevel);
+            case CHANNEL_LIGHT_LUX -> updateQuantityTypeChannel(channelUID, newState.lux, LUX);
+            case CHANNEL_MOISTURE -> updateQuantityTypeChannel(channelUID, newState.moisture, PERCENT);
             case CHANNEL_OPENCLOSE -> {
                 Boolean open = newState.open;
                 if (open != null) {
                     updateState(channelUID, open ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
                 }
             }
-            case CHANNEL_WATERLEAKAGE -> updateSwitchChannel(channelUID, newState.water);
-            case CHANNEL_FIRE -> updateSwitchChannel(channelUID, newState.fire);
-            case CHANNEL_ALARM -> updateSwitchChannel(channelUID, newState.alarm);
+            case CHANNEL_ORIENTATION_X ->
+                updateDecimalTypeChannel(channelUID, newState.orientation != null ? newState.orientation[0] : null);
+            case CHANNEL_ORIENTATION_Y ->
+                updateDecimalTypeChannel(channelUID, newState.orientation != null ? newState.orientation[1] : null);
+            case CHANNEL_ORIENTATION_Z ->
+                updateDecimalTypeChannel(channelUID, newState.orientation != null ? newState.orientation[2] : null);
+            case CHANNEL_POWER -> updateQuantityTypeChannel(channelUID, newState.power, WATT);
+            case CHANNEL_PRESENCE -> updateSwitchChannel(channelUID, newState.presence);
+            case CHANNEL_PRESSURE -> updateQuantityTypeChannel(channelUID, newState.pressure, HECTO(PASCAL));
             case CHANNEL_TAMPERED -> updateSwitchChannel(channelUID, newState.tampered);
+            case CHANNEL_TEMPERATURE -> updateQuantityTypeChannel(channelUID, newState.temperature, CELSIUS, 1.0 / 100);
+            case CHANNEL_TILTANGLE -> updateQuantityTypeChannel(channelUID, newState.tiltangle, DEGREE_ANGLE);
+            case CHANNEL_VALUE -> updateDecimalTypeChannel(channelUID, newState.status);
             case CHANNEL_VIBRATION -> updateSwitchChannel(channelUID, newState.vibration);
-            case CHANNEL_CARBONMONOXIDE -> updateSwitchChannel(channelUID, newState.carbonmonoxide);
-            case CHANNEL_AIRQUALITY -> updateStringChannel(channelUID, newState.airquality);
-            case CHANNEL_AIRQUALITYPPB ->
-                updateQuantityTypeChannel(channelUID, newState.airqualityppb, PARTS_PER_BILLION);
-            case CHANNEL_MOISTURE -> updateQuantityTypeChannel(channelUID, newState.moisture, PERCENT);
-            case CHANNEL_BUTTON -> updateDecimalTypeChannel(channelUID, newState.buttonevent);
-            case CHANNEL_BUTTONEVENT -> {
-                Integer buttonevent = newState.buttonevent;
-                if (buttonevent != null && !initializing) {
-                    triggerChannel(channelUID, String.valueOf(buttonevent));
-                }
-            }
-            case CHANNEL_GESTURE -> updateDecimalTypeChannel(channelUID, newState.gesture);
-            case CHANNEL_GESTUREEVENT -> {
-                Integer gesture = newState.gesture;
-                if (gesture != null && !initializing) {
-                    triggerChannel(channelUID, String.valueOf(gesture));
-                }
-            }
+            case CHANNEL_VIBRATION_STRENGTH -> updateDecimalTypeChannel(channelUID, newState.vibrationstrength);
+            case CHANNEL_VOLTAGE -> updateQuantityTypeChannel(channelUID, newState.voltage, VOLT);
+            case CHANNEL_WATERLEAKAGE -> updateSwitchChannel(channelUID, newState.water);
         }
     }
 
@@ -220,6 +201,26 @@ public class SensorThingHandler extends SensorBaseThingHandler {
             thingEdited = true;
         }
 
+        // vibration sensors
+        if (sensorState.tiltangle != null && createChannel(thingBuilder, CHANNEL_TILTANGLE, ChannelKind.STATE)) {
+            thingEdited = true;
+        }
+        if (sensorState.vibrationstrength != null
+                && createChannel(thingBuilder, CHANNEL_VIBRATION_STRENGTH, ChannelKind.STATE)) {
+            thingEdited = true;
+        }
+        if (sensorState.orientation != null) {
+            if (createChannel(thingBuilder, CHANNEL_ORIENTATION_X, ChannelKind.STATE)) {
+                thingEdited = true;
+            }
+            if (createChannel(thingBuilder, CHANNEL_ORIENTATION_Y, ChannelKind.STATE)) {
+                thingEdited = true;
+            }
+            if (createChannel(thingBuilder, CHANNEL_ORIENTATION_Z, ChannelKind.STATE)) {
+                thingEdited = true;
+            }
+        }
+
         return thingEdited;
     }
 
@@ -227,4 +228,36 @@ public class SensorThingHandler extends SensorBaseThingHandler {
     protected List<String> getConfigChannels() {
         return CONFIG_CHANNELS;
     }
+
+    /**
+     * Determine the light state from a state message
+     *
+     * @param newState the {@link SensorState} message
+     * @return <code>Dark</code>, <code>Daylight</code>, <code>Sunset</code>
+     */
+    private @Nullable String getLightState(SensorState newState) {
+        Boolean dark = newState.dark;
+        if (dark == null) {
+            return null;
+        }
+        Boolean daylight = newState.daylight;
+        if (dark) { // if it's dark, it's dark ;)
+            return "Dark";
+        } else if (daylight != null) { // if its not dark, it might be between darkness and daylight
+            if (daylight) {
+                return "Daylight";
+            } else {
+                return "Sunset";
+            }
+        } else { // if no daylight value is known, we assume !dark means daylight
+            return "Daylight";
+        }
+    }
+
+    private void triggerChannel(ChannelUID channelUID, @Nullable Integer value, boolean initializing) {
+        if (value == null || initializing) {
+            return;
+        }
+        triggerChannel(channelUID, String.valueOf(value));
+    }
 }
index 0dd13ca1ffd8ac8d0ab8403bf0fb851a31aad929..1cf05de8bb1466ff6cc1376451cbcb7638186802 100644 (file)
@@ -174,6 +174,9 @@ channel-type.deconz.ontime.label = On Time
 channel-type.deconz.ontime.description = Time that the light stays on before switched off automatically (0=forever)
 channel-type.deconz.open.label = Open/Close
 channel-type.deconz.open.description = Open/Close detected
+channel-type.deconz.orientation_x.label = Orientation X
+channel-type.deconz.orientation_y.label = Orientation Y
+channel-type.deconz.orientation_z.label = Orientation Z
 channel-type.deconz.position.label = Position
 channel-type.deconz.power.label = Power
 channel-type.deconz.power.description = Current power usage
@@ -184,12 +187,15 @@ channel-type.deconz.tampered.label = Tampered
 channel-type.deconz.tampered.description = A zone is being tampered.
 channel-type.deconz.temperature.label = Temperature
 channel-type.deconz.temperature.description = Current temperature
+channel-type.deconz.tiltangle.label = Tilt Angle
 channel-type.deconz.value.label = Daylight Value
 channel-type.deconz.value.description = Dawn is around 130, sunrise at 140, sunset at 190, and dusk at 210
 channel-type.deconz.valve.label = Valve position
 channel-type.deconz.valve.description = Current valve position
 channel-type.deconz.vibration.label = Vibration
 channel-type.deconz.vibration.description = Vibration was detected.
+channel-type.deconz.vibrationstrength.label = Vibrationstrength
+channel-type.deconz.vibrationstrength.description = Vibration strength, value is device-dependent.
 channel-type.deconz.voltage.label = Voltage
 channel-type.deconz.voltage.description = Current voltage
 channel-type.deconz.waterleakage.label = Water Leakage
diff --git a/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-channel-types.xml b/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-channel-types.xml
new file mode 100644 (file)
index 0000000..974ac70
--- /dev/null
@@ -0,0 +1,329 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="deconz"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <channel-type id="airquality">
+               <item-type>String</item-type>
+               <label>Air Quality</label>
+               <description>Current air quality level based on volatile organic compounds (VOCs) measurement. Example: good or poor,
+                       ...</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="airqualityppb">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Air Quality (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"/>
+       </channel-type>
+
+       <channel-type id="alarm">
+               <item-type>Switch</item-type>
+               <label>Alarm</label>
+               <description>Alarm was triggered.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="button">
+               <item-type>Number</item-type>
+               <label>Button</label>
+               <description>The Button that was last pressed on the switch.</description>
+               <state readOnly="true" pattern="%d"/>
+       </channel-type>
+
+       <channel-type id="buttonevent">
+               <kind>Trigger</kind>
+               <label>Button Trigger</label>
+               <description>This channel is triggered on a button event. The trigger payload consists of the button event number.
+               </description>
+               <event></event>
+       </channel-type>
+
+       <channel-type id="carbonmonoxide">
+               <item-type>Switch</item-type>
+               <label>Carbon-monoxide</label>
+               <description>Carbon-monoxide was detected.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="consumption">
+               <item-type>Number:Energy</item-type>
+               <label>Consumption</label>
+               <description>Current consumption</description>
+               <category>Energy</category>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="current">
+               <item-type>Number:ElectricCurrent</item-type>
+               <label>Current</label>
+               <description>Current current</description>
+               <category>Energy</category>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="dark">
+               <item-type>Switch</item-type>
+               <label>Dark</label>
+               <description>Light level is below the darkness threshold.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="daylight">
+               <item-type>Switch</item-type>
+               <label>Daylight</label>
+               <description>Light level is above the daylight threshold.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="externalwindowopen">
+               <item-type>Contact</item-type>
+               <label>External Window Open</label>
+       </channel-type>
+
+       <channel-type id="fire">
+               <item-type>Switch</item-type>
+               <label>Fire</label>
+               <description>A fire was detected.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="gesture">
+               <item-type>Number</item-type>
+               <label>Gesture</label>
+               <description>A gesture that was performed with the switch.</description>
+               <state readOnly="true" pattern="%d">
+                       <options>
+                               <option value="0">None</option>
+                               <option value="1">Shake</option>
+                               <option value="2">Drop</option>
+                               <option value="3">Flip 90</option>
+                               <option value="4">Flip 180</option>
+                               <option value="5">Push</option>
+                               <option value="6">Double Tap</option>
+                               <option value="7">Rotate Clockwise</option>
+                               <option value="8">Rotate Counter Clockwise</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="gestureevent">
+               <kind>Trigger</kind>
+               <label>Gesture Trigger</label>
+               <description>This channel is triggered on a gesture event. The trigger payload consists of the gesture event number.</description>
+               <event></event>
+       </channel-type>
+
+       <channel-type id="heatsetpoint">
+               <item-type>Number:Temperature</item-type>
+               <label>Target Temperature</label>
+               <description>Target temperature</description>
+               <category>Heating</category>
+               <state pattern="%.1f %unit%" step="0.5" max="28" min="6"/>
+       </channel-type>
+
+       <channel-type id="humidity">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Humidity</label>
+               <description>Current humidity</description>
+               <category>Humidity</category>
+               <state readOnly="true" pattern="%.2f %unit%"/>
+       </channel-type>
+
+       <channel-type id="last_seen">
+               <item-type>DateTime</item-type>
+               <label>Last Seen</label>
+               <description>The date and time when the sensor was last seen.</description>
+               <category>Time</category>
+               <state readOnly="true" pattern="%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"/>
+       </channel-type>
+
+       <channel-type id="last_updated">
+               <item-type>DateTime</item-type>
+               <label>Last Updated</label>
+               <description>The date and time when the sensor was last updated.</description>
+               <category>Time</category>
+               <state readOnly="true" pattern="%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"/>
+       </channel-type>
+
+       <channel-type id="light">
+               <item-type>String</item-type>
+               <label>Lightlevel</label>
+               <state readOnly="true">
+                       <options>
+                               <option value="daylight">Daylight</option>
+                               <option value="sunset">Sunset</option>
+                               <option value="dark">Dark</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="lightlux">
+               <item-type>Number:Illuminance</item-type>
+               <label>Illuminance</label>
+               <description>Current light illuminance</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="light_level" advanced="true">
+               <item-type>Number</item-type>
+               <label>Light Level</label>
+               <description>Current light level.</description>
+               <state readOnly="true" pattern="%d"/>
+       </channel-type>
+
+       <channel-type id="locked">
+               <item-type>Switch</item-type>
+               <label>Locked</label>
+               <description>Status of this thermostat's child lock.</description>
+               <category>Lock</category>
+       </channel-type>
+
+       <channel-type id="mode">
+               <item-type>String</item-type>
+               <label>Mode</label>
+               <description>Current mode</description>
+               <category>Heating</category>
+               <state>
+                       <options>
+                               <option value="AUTO">auto</option>
+                               <option value="HEAT">heat</option>
+                               <option value="OFF">off</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="moisture">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Moisture</label>
+               <description>Current moisture</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="offset">
+               <item-type>Number:Temperature</item-type>
+               <label>Offset</label>
+               <description>Temperature offset</description>
+               <state pattern="%.2f %unit%" step="0.01"/>
+       </channel-type>
+
+       <channel-type id="on">
+               <item-type>Switch</item-type>
+               <label>Heater State</label>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="open">
+               <item-type>Contact</item-type>
+               <label>Open/Close</label>
+               <description>Open/Close detected</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="orientation_x">
+               <item-type>Number</item-type>
+               <label>Orientation X</label>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="orientation_y">
+               <item-type>Number</item-type>
+               <label>Orientation Y</label>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="orientation_z">
+               <item-type>Number</item-type>
+               <label>Orientation Z</label>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="power">
+               <item-type>Number:Power</item-type>
+               <label>Power</label>
+               <description>Current power usage</description>
+               <category>Energy</category>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="pressure">
+               <item-type>Number:Pressure</item-type>
+               <label>Pressure</label>
+               <description>Current pressure</description>
+               <category>Pressure</category>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="tampered">
+               <item-type>Switch</item-type>
+               <label>Tampered</label>
+               <description>A zone is being tampered.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="temperature">
+               <item-type>Number:Temperature</item-type>
+               <label>Temperature</label>
+               <description>Current temperature</description>
+               <category>Temperature</category>
+               <state readOnly="true" pattern="%.2f %unit%"/>
+       </channel-type>
+
+       <channel-type id="tiltangle">
+               <item-type>Number:Angle</item-type>
+               <label>Tilt Angle</label>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="value">
+               <item-type>Number</item-type>
+               <label>Daylight Value</label>
+               <description>Dawn is around 130, sunrise at 140, sunset at 190, and dusk at 210</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="valve">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Valve position</label>
+               <description>Current valve position</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="vibration">
+               <item-type>Switch</item-type>
+               <label>Vibration</label>
+               <description>Vibration was detected.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="vibrationstrength">
+               <item-type>Number</item-type>
+               <label>Vibrationstrength</label>
+               <description>Vibration strength, value is device-dependent.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="voltage">
+               <item-type>Number:ElectricPotential</item-type>
+               <label>Voltage</label>
+               <description>Current voltage</description>
+               <category>Energy</category>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="waterleakage">
+               <item-type>Switch</item-type>
+               <label>Water Leakage</label>
+               <description>Water leakage detected</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="windowopen">
+               <item-type>Contact</item-type>
+               <label>Window Open</label>
+       </channel-type>
+
+</thing:thing-descriptions>
index 05c9454cbfcdfa0ebc65e16e3ca05842dda67da2..90703c0f3d792b0462f9f332ce922769ef6fbe17 100644 (file)
        xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
        xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
 
-       <thing-type id="presencesensor">
+       <!-- thing types -->
+
+       <thing-type id="airqualitysensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Presence Sensor</label>
+               <label>Air Quality Sensor</label>
                <channels>
-                       <channel typeId="system.motion" id="presence"/>
+                       <channel typeId="airquality" id="airquality"/>
+                       <channel typeId="airqualityppb" id="airqualityppb"/>
                        <channel typeId="last_updated" id="last_updated"/>
-                       <channel typeId="system.power" id="enabled"/>
                </channels>
-
-               <properties>
-                       <property name="thingTypeVersion">1</property>
-               </properties>
-
                <representation-property>uid</representation-property>
-
                <config-description-ref uri="thing-type:deconz:sensor"/>
        </thing-type>
 
-       <channel-type id="last_updated">
-               <item-type>DateTime</item-type>
-               <label>Last Updated</label>
-               <description>The date and time when the sensor was last updated.</description>
-               <category>Time</category>
-               <state readOnly="true" pattern="%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"/>
-       </channel-type>
-
-       <channel-type id="last_seen">
-               <item-type>DateTime</item-type>
-               <label>Last Seen</label>
-               <description>The date and time when the sensor was last seen.</description>
-               <category>Time</category>
-               <state readOnly="true" pattern="%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"/>
-       </channel-type>
-
-       <thing-type id="powersensor">
+       <thing-type id="alarmsensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Power Sensor</label>
+               <label>Alarm Sensor</label>
                <channels>
-                       <channel typeId="power" id="power"/>
+                       <channel typeId="alarm" id="alarm"/>
                        <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="power">
-               <item-type>Number:Power</item-type>
-               <label>Power</label>
-               <description>Current power usage</description>
-               <category>Energy</category>
-               <state readOnly="true" pattern="%.1f %unit%"/>
-       </channel-type>
-
-       <channel-type id="voltage">
-               <item-type>Number:ElectricPotential</item-type>
-               <label>Voltage</label>
-               <description>Current voltage</description>
-               <category>Energy</category>
-               <state readOnly="true" pattern="%.1f %unit%"/>
-       </channel-type>
-
-       <channel-type id="current">
-               <item-type>Number:ElectricCurrent</item-type>
-               <label>Current</label>
-               <description>Current current</description>
-               <category>Energy</category>
-               <state readOnly="true" pattern="%.1f %unit%"/>
-       </channel-type>
-
-       <thing-type id="consumptionsensor">
+       <thing-type id="carbonmonoxidesensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Consumption Sensor</label>
+               <label>Carbon-monoxide Sensor</label>
                <channels>
-                       <channel typeId="consumption" id="consumption"/>
+                       <channel typeId="carbonmonoxide" id="carbonmonoxide"/>
                        <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="consumption">
-               <item-type>Number:Energy</item-type>
-               <label>Consumption</label>
-               <description>Current consumption</description>
-               <category>Energy</category>
-               <state readOnly="true" pattern="%.1f %unit%"/>
-       </channel-type>
-
-       <thing-type id="colorcontrol">
+       <thing-type id="batterysensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Color Controller</label>
+               <label>Battery Sensor</label>
                <channels>
-                       <channel typeId="system.color" id="color"/>
-                       <channel typeId="buttonevent" id="buttonevent"/>
-                       <channel typeId="button" id="button"/>
+                       <channel typeId="system.battery-level" id="battery_level"/>
                        <channel typeId="last_updated" id="last_updated"/>
                </channels>
-
                <properties>
                        <property name="thingTypeVersion">1</property>
                </properties>
-
                <representation-property>uid</representation-property>
-
                <config-description-ref uri="thing-type:deconz:sensor"/>
        </thing-type>
 
-       <thing-type id="switch">
+       <thing-type id="colorcontrol">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Switch/Button</label>
+               <label>Color Controller</label>
                <channels>
+                       <channel typeId="system.color" id="color"/>
                        <channel typeId="buttonevent" id="buttonevent"/>
                        <channel typeId="button" id="button"/>
                        <channel typeId="last_updated" id="last_updated"/>
                </channels>
-
+               <properties>
+                       <property name="thingTypeVersion">1</property>
+               </properties>
                <representation-property>uid</representation-property>
-
                <config-description-ref uri="thing-type:deconz:sensor"/>
        </thing-type>
 
-       <channel-type id="buttonevent">
-               <kind>Trigger</kind>
-               <label>Button Trigger</label>
-               <description>This channel is triggered on a button event. The trigger payload consists of the button event number.
-               </description>
-               <event></event>
-       </channel-type>
-
-       <channel-type id="button">
-               <item-type>Number</item-type>
-               <label>Button</label>
-               <description>The Button that was last pressed on the switch.</description>
-               <state readOnly="true" pattern="%d"/>
-       </channel-type>
-
-       <channel-type id="gestureevent">
-               <kind>Trigger</kind>
-               <label>Gesture Trigger</label>
-               <description>This channel is triggered on a gesture event. The trigger payload consists of the gesture event number.</description>
-               <event></event>
-       </channel-type>
-
-       <channel-type id="gesture">
-               <item-type>Number</item-type>
-               <label>Gesture</label>
-               <description>A gesture that was performed with the switch.</description>
-               <state readOnly="true" pattern="%d">
-                       <options>
-                               <option value="0">None</option>
-                               <option value="1">Shake</option>
-                               <option value="2">Drop</option>
-                               <option value="3">Flip 90</option>
-                               <option value="4">Flip 180</option>
-                               <option value="5">Push</option>
-                               <option value="6">Double Tap</option>
-                               <option value="7">Rotate Clockwise</option>
-                               <option value="8">Rotate Counter Clockwise</option>
-                       </options>
-               </state>
-       </channel-type>
-
-       <thing-type id="lightsensor">
+       <thing-type id="consumptionsensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Light Sensor</label>
+               <label>Consumption Sensor</label>
                <channels>
-                       <channel typeId="lightlux" id="lightlux"/>
-                       <channel typeId="light_level" id="light_level"/>
-                       <channel typeId="dark" id="dark"/>
-                       <channel typeId="daylight" id="daylight"/>
+                       <channel typeId="consumption" id="consumption"/>
                        <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="lightlux">
-               <item-type>Number:Illuminance</item-type>
-               <label>Illuminance</label>
-               <description>Current light illuminance</description>
-               <state readOnly="true" pattern="%.1f %unit%"/>
-       </channel-type>
-
-       <channel-type id="light_level" advanced="true">
-               <item-type>Number</item-type>
-               <label>Light Level</label>
-               <description>Current light level.</description>
-               <state readOnly="true" pattern="%d"/>
-       </channel-type>
-
-       <channel-type id="dark">
-               <item-type>Switch</item-type>
-               <label>Dark</label>
-               <description>Light level is below the darkness threshold.</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <channel-type id="daylight">
-               <item-type>Switch</item-type>
-               <label>Daylight</label>
-               <description>Light level is above the daylight threshold.</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <thing-type id="temperaturesensor">
+       <thing-type id="daylightsensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Temperature Sensor</label>
+               <label>Daylight Sensor</label>
                <channels>
-                       <channel typeId="temperature" id="temperature"/>
-                       <channel typeId="last_updated" id="last_updated"/>
+                       <channel typeId="value" id="value"/>
+                       <channel typeId="light" id="light"/>
                </channels>
-
                <representation-property>uid</representation-property>
-
                <config-description-ref uri="thing-type:deconz:sensor"/>
        </thing-type>
 
-       <channel-type id="temperature">
-               <item-type>Number:Temperature</item-type>
-               <label>Temperature</label>
-               <description>Current temperature</description>
-               <category>Temperature</category>
-               <state readOnly="true" pattern="%.2f %unit%"/>
-       </channel-type>
-
-       <thing-type id="humiditysensor">
+       <thing-type id="firesensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Humidity Sensor</label>
+               <label>Fire Sensor</label>
                <channels>
-                       <channel typeId="humidity" id="humidity"/>
+                       <channel typeId="fire" id="fire"/>
                        <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="humidity">
-               <item-type>Number:Dimensionless</item-type>
-               <label>Humidity</label>
-               <description>Current humidity</description>
-               <category>Humidity</category>
-               <state readOnly="true" pattern="%.2f %unit%"/>
-       </channel-type>
-
-       <thing-type id="pressuresensor">
+       <thing-type id="humiditysensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Pressure Sensor</label>
+               <label>Humidity Sensor</label>
                <channels>
-                       <channel typeId="pressure" id="pressure"/>
+                       <channel typeId="humidity" id="humidity"/>
                        <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="pressure">
-               <item-type>Number:Pressure</item-type>
-               <label>Pressure</label>
-               <description>Current pressure</description>
-               <category>Pressure</category>
-               <state readOnly="true" pattern="%.1f %unit%"/>
-       </channel-type>
-
-       <thing-type id="daylightsensor">
+       <thing-type id="lightsensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Daylight Sensor</label>
+               <label>Light Sensor</label>
                <channels>
-                       <channel typeId="value" id="value"/>
-                       <channel typeId="light" id="light"/>
+                       <channel typeId="lightlux" id="lightlux"/>
+                       <channel typeId="light_level" id="light_level"/>
+                       <channel typeId="dark" id="dark"/>
+                       <channel typeId="daylight" id="daylight"/>
+                       <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="value">
-               <item-type>Number</item-type>
-               <label>Daylight Value</label>
-               <description>Dawn is around 130, sunrise at 140, sunset at 190, and dusk at 210</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <channel-type id="light">
-               <item-type>String</item-type>
-               <label>Lightlevel</label>
-               <state readOnly="true">
-                       <options>
-                               <option value="daylight">Daylight</option>
-                               <option value="sunset">Sunset</option>
-                               <option value="dark">Dark</option>
-                       </options>
-               </state>
-       </channel-type>
-
-       <thing-type id="openclosesensor">
+       <thing-type id="moisturesensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Open/Close Sensor</label>
+               <label>Moisture Sensor</label>
                <channels>
-                       <channel typeId="open" id="open"/>
+                       <channel typeId="moisture" id="moisture"/>
                        <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="open">
-               <item-type>Contact</item-type>
-               <label>Open/Close</label>
-               <description>Open/Close detected</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <thing-type id="waterleakagesensor">
+       <thing-type id="powersensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Water Leakage Sensor</label>
+               <label>Power Sensor</label>
                <channels>
-                       <channel typeId="waterleakage" id="waterleakage"/>
+                       <channel typeId="power" id="power"/>
                        <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="waterleakage">
-               <item-type>Switch</item-type>
-               <label>Water Leakage</label>
-               <description>Water leakage detected</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <thing-type id="firesensor">
+       <thing-type id="openclosesensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Fire Sensor</label>
+               <label>Open/Close Sensor</label>
                <channels>
-                       <channel typeId="fire" id="fire"/>
+                       <channel typeId="open" id="open"/>
                        <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="fire">
-               <item-type>Switch</item-type>
-               <label>Fire</label>
-               <description>A fire was detected.</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <thing-type id="alarmsensor">
+       <thing-type id="presencesensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Alarm Sensor</label>
+               <label>Presence Sensor</label>
                <channels>
-                       <channel typeId="alarm" id="alarm"/>
+                       <channel typeId="system.motion" id="presence"/>
                        <channel typeId="last_updated" id="last_updated"/>
+                       <channel typeId="system.power" id="enabled"/>
                </channels>
-
+               <properties>
+                       <property name="thingTypeVersion">1</property>
+               </properties>
                <representation-property>uid</representation-property>
-
                <config-description-ref uri="thing-type:deconz:sensor"/>
        </thing-type>
 
-       <channel-type id="alarm">
-               <item-type>Switch</item-type>
-               <label>Alarm</label>
-               <description>Alarm was triggered.</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <channel-type id="tampered">
-               <item-type>Switch</item-type>
-               <label>Tampered</label>
-               <description>A zone is being tampered.</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <thing-type id="vibrationsensor">
+       <thing-type id="pressuresensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Vibration Sensor</label>
+               <label>Pressure Sensor</label>
                <channels>
-                       <channel typeId="vibration" id="vibration"/>
+                       <channel typeId="pressure" id="pressure"/>
                        <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="vibration">
-               <item-type>Switch</item-type>
-               <label>Vibration</label>
-               <description>Vibration was detected.</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <thing-type id="batterysensor">
+       <thing-type id="switch">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Battery Sensor</label>
+               <label>Switch/Button</label>
                <channels>
-                       <channel typeId="system.battery-level" id="battery_level"/>
+                       <channel typeId="buttonevent" id="buttonevent"/>
+                       <channel typeId="button" id="button"/>
                        <channel typeId="last_updated" id="last_updated"/>
                </channels>
-
-               <properties>
-                       <property name="thingTypeVersion">1</property>
-               </properties>
-
                <representation-property>uid</representation-property>
-
                <config-description-ref uri="thing-type:deconz:sensor"/>
        </thing-type>
 
-       <thing-type id="carbonmonoxidesensor">
+       <thing-type id="temperaturesensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Carbon-monoxide Sensor</label>
+               <label>Temperature Sensor</label>
                <channels>
-                       <channel typeId="carbonmonoxide" id="carbonmonoxide"/>
+                       <channel typeId="temperature" id="temperature"/>
                        <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="carbonmonoxide">
-               <item-type>Switch</item-type>
-               <label>Carbon-monoxide</label>
-               <description>Carbon-monoxide was detected.</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <thing-type id="airqualitysensor">
+       <thing-type id="thermostat">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Air Quality Sensor</label>
+               <label>Thermostat</label>
+               <description>A Thermostat sensor/actor</description>
                <channels>
-                       <channel typeId="airquality" id="airquality"/>
-                       <channel typeId="airqualityppb" id="airqualityppb"/>
+                       <channel typeId="temperature" id="temperature"/>
+                       <channel typeId="heatsetpoint" id="heatsetpoint"/>
+                       <channel typeId="mode" id="mode"/>
+                       <channel typeId="offset" id="offset"/>
                        <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</label>
-               <description>Current air quality level based on volatile organic compounds (VOCs) measurement. Example: good or poor,
-                       ...</description>
-               <state readOnly="true"/>
-       </channel-type>
-
-       <channel-type id="airqualityppb">
-               <item-type>Number:Dimensionless</item-type>
-               <label>Air Quality (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"/>
-       </channel-type>
-
-       <thing-type id="moisturesensor">
+       <thing-type id="vibrationsensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Moisture Sensor</label>
+               <label>Vibration Sensor</label>
                <channels>
-                       <channel typeId="moisture" id="moisture"/>
+                       <channel typeId="vibration" id="vibration"/>
                        <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="moisture">
-               <item-type>Number:Dimensionless</item-type>
-               <label>Moisture</label>
-               <description>Current moisture</description>
-               <state readOnly="true" pattern="%.1f %unit%"/>
-       </channel-type>
-
-       <thing-type id="thermostat">
+       <thing-type id="waterleakagesensor">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="deconz"/>
                </supported-bridge-type-refs>
-               <label>Thermostat</label>
-               <description>A Thermostat sensor/actor</description>
+               <label>Water Leakage Sensor</label>
                <channels>
-                       <channel typeId="temperature" id="temperature"/>
-                       <channel typeId="heatsetpoint" id="heatsetpoint"/>
-                       <channel typeId="mode" id="mode"/>
-                       <channel typeId="offset" id="offset"/>
+                       <channel typeId="waterleakage" id="waterleakage"/>
                        <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="locked">
-               <item-type>Switch</item-type>
-               <label>Locked</label>
-               <description>Status of this thermostat's child lock.</description>
-               <category>Lock</category>
-       </channel-type>
-       <channel-type id="windowopen">
-               <item-type>Contact</item-type>
-               <label>Window Open</label>
-       </channel-type>
-       <channel-type id="externalwindowopen">
-               <item-type>Contact</item-type>
-               <label>External Window Open</label>
-       </channel-type>
-       <channel-type id="heatsetpoint">
-               <item-type>Number:Temperature</item-type>
-               <label>Target Temperature</label>
-               <description>Target temperature</description>
-               <category>Heating</category>
-               <state pattern="%.1f %unit%" step="0.5" max="28" min="6"/>
-       </channel-type>
-       <channel-type id="mode">
-               <item-type>String</item-type>
-               <label>Mode</label>
-               <description>Current mode</description>
-               <category>Heating</category>
-               <state>
-                       <options>
-                               <option value="AUTO">auto</option>
-                               <option value="HEAT">heat</option>
-                               <option value="OFF">off</option>
-                       </options>
-               </state>
-       </channel-type>
-       <channel-type id="offset">
-               <item-type>Number:Temperature</item-type>
-               <label>Offset</label>
-               <description>Temperature offset</description>
-               <state pattern="%.2f %unit%" step="0.01"/>
-       </channel-type>
-       <channel-type id="valve">
-               <item-type>Number:Dimensionless</item-type>
-               <label>Valve position</label>
-               <description>Current valve position</description>
-               <state readOnly="true" pattern="%.1f %unit%"/>
-       </channel-type>
-       <channel-type id="on">
-               <item-type>Switch</item-type>
-               <label>Heater State</label>
-               <state readOnly="true"/>
-       </channel-type>
-
 </thing:thing-descriptions>
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
deleted file mode 100644 (file)
index dee22cb..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.deconz;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.openhab.binding.deconz.internal.BindingConstants.*;
-
-import java.io.IOException;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.openhab.binding.deconz.internal.dto.SensorMessage;
-import org.openhab.binding.deconz.internal.handler.SensorThingHandler;
-import org.openhab.binding.deconz.internal.types.LightType;
-import org.openhab.binding.deconz.internal.types.LightTypeDeserializer;
-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.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandlerCallback;
-import org.openhab.core.thing.binding.builder.ChannelBuilder;
-import org.openhab.core.thing.binding.builder.ThingBuilder;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
-/**
- * This class provides tests for deconz sensors
- *
- * @author Jan N. Klug - Initial contribution
- * @author Lukas Agethen - Added Thermostat
- * @author Philipp Schneider - Added air quality sensor
- */
-@ExtendWith(MockitoExtension.class)
-@NonNullByDefault
-public class SensorsTest {
-    private @NonNullByDefault({}) Gson gson;
-
-    private @Mock @NonNullByDefault({}) ThingHandlerCallback thingHandlerCallback;
-
-    @BeforeEach
-    public void initialize() {
-        GsonBuilder gsonBuilder = new GsonBuilder();
-        gsonBuilder.registerTypeAdapter(LightType.class, new LightTypeDeserializer());
-        gson = gsonBuilder.create();
-    }
-
-    @Test
-    public void carbonmonoxideSensorUpdateTest() throws IOException {
-        SensorMessage sensorMessage = DeconzTest.getObjectFromJson("carbonmonoxide.json", SensorMessage.class, gson);
-        assertNotNull(sensorMessage);
-
-        ThingUID thingUID = new ThingUID("deconz", "sensor");
-        ChannelUID channelUID = new ChannelUID(thingUID, "carbonmonoxide");
-        Thing sensor = ThingBuilder.create(THING_TYPE_CARBONMONOXIDE_SENSOR, thingUID)
-                .withChannel(ChannelBuilder.create(channelUID, "Switch").build()).build();
-        SensorThingHandler sensorThingHandler = new SensorThingHandler(sensor, gson);
-        sensorThingHandler.setCallback(thingHandlerCallback);
-
-        sensorThingHandler.messageReceived(sensorMessage);
-        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 QuantityType<>("129 ppb")));
-    }
-
-    @Test
-    public void fireSensorUpdateTest() throws IOException {
-        SensorMessage sensorMessage = DeconzTest.getObjectFromJson("fire.json", SensorMessage.class, gson);
-        assertNotNull(sensorMessage);
-
-        ThingUID thingUID = new ThingUID("deconz", "sensor");
-        ChannelUID channelBatteryLevelUID = new ChannelUID(thingUID, CHANNEL_BATTERY_LEVEL);
-        ChannelUID channelFireUID = new ChannelUID(thingUID, CHANNEL_FIRE);
-        ChannelUID channelTamperedUID = new ChannelUID(thingUID, CHANNEL_TAMPERED);
-        ChannelUID channelLastSeenUID = new ChannelUID(thingUID, CHANNEL_LAST_SEEN);
-
-        Thing sensor = ThingBuilder.create(THING_TYPE_FIRE_SENSOR, thingUID)
-                .withChannel(ChannelBuilder.create(channelBatteryLevelUID, "Number").build())
-                .withChannel(ChannelBuilder.create(channelFireUID, "Switch").build())
-                .withChannel(ChannelBuilder.create(channelTamperedUID, "Switch").build())
-                .withChannel(ChannelBuilder.create(channelLastSeenUID, "DateTime").build()).build();
-        SensorThingHandler sensorThingHandler = new SensorThingHandler(sensor, gson);
-        sensorThingHandler.setCallback(thingHandlerCallback);
-
-        sensorThingHandler.messageReceived(sensorMessage);
-
-        Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelFireUID), eq(OnOffType.OFF));
-        Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelBatteryLevelUID), eq(new DecimalType(98)));
-    }
-}
diff --git a/bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/internal/handler/BaseDeconzThingHandlerTest.java b/bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/internal/handler/BaseDeconzThingHandlerTest.java
new file mode 100644 (file)
index 0000000..0d9d1a0
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+ * Copyright (c) 2010-2023 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.deconz.internal.handler;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.openhab.binding.deconz.internal.BindingConstants.BINDING_ID;
+import static org.openhab.binding.deconz.internal.BindingConstants.BRIDGE_TYPE;
+import static org.openhab.binding.deconz.internal.BindingConstants.CONFIG_ID;
+import static org.openhab.binding.deconz.internal.BindingConstants.THING_TYPE_THERMOSTAT;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.jupiter.api.BeforeEach;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.openhab.binding.deconz.DeconzTest;
+import org.openhab.binding.deconz.internal.dto.BridgeFullState;
+import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
+import org.openhab.binding.deconz.internal.dto.SensorMessage;
+import org.openhab.binding.deconz.internal.netutils.WebSocketConnection;
+import org.openhab.binding.deconz.internal.types.LightType;
+import org.openhab.binding.deconz.internal.types.LightTypeDeserializer;
+import org.openhab.binding.deconz.internal.types.ResourceType;
+import org.openhab.binding.deconz.internal.types.ThermostatMode;
+import org.openhab.binding.deconz.internal.types.ThermostatModeGsonTypeAdapter;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.test.java.JavaTest;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingStatusInfo;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandlerCallback;
+import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.openhab.core.types.State;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * The {@link BaseDeconzThingHandlerTest} is the base class for test classes that are used to test subclasses of
+ * {@link DeconzBaseThingHandler}
+ *
+ * @author Jan N. Klug - Initial contribution
+ */
+@NonNullByDefault
+public class BaseDeconzThingHandlerTest extends JavaTest {
+    private static final ThingUID BRIDGE_UID = new ThingUID(BRIDGE_TYPE, "bridge");
+    private static final ThingUID THING_UID = new ThingUID(THING_TYPE_THERMOSTAT, "thing");
+    protected @NonNullByDefault({}) DeconzBaseMessage deconzMessage;
+    private @Mock @NonNullByDefault({}) Bridge bridge;
+    private @Mock @NonNullByDefault({}) ThingHandlerCallback callback;
+    private @Mock @NonNullByDefault({}) DeconzBridgeHandler bridgeHandler;
+    private @Mock @NonNullByDefault({}) WebSocketConnection webSocketConnection;
+    private @Mock @NonNullByDefault({}) BridgeFullState bridgeFullState;
+    private @NonNullByDefault({}) Gson gson;
+    private @NonNullByDefault({}) Thing thing;
+    private @NonNullByDefault({}) DeconzBaseThingHandler thingHandler;
+
+    @BeforeEach
+    public void setupMocks() {
+        GsonBuilder gsonBuilder = new GsonBuilder();
+        gsonBuilder.registerTypeAdapter(LightType.class, new LightTypeDeserializer());
+        gsonBuilder.registerTypeAdapter(ThermostatMode.class, new ThermostatModeGsonTypeAdapter());
+        gson = gsonBuilder.create();
+
+        when(callback.getBridge(BRIDGE_UID)).thenReturn(bridge);
+        when(callback.createChannelBuilder(any(ChannelUID.class), any(ChannelTypeUID.class)))
+                .thenAnswer(i -> ChannelBuilder.create((ChannelUID) i.getArgument(0)).withType(i.getArgument(1)));
+        doAnswer(i -> {
+            thing = i.getArgument(0);
+            thingHandler.thingUpdated(thing);
+            return null;
+        }).when(callback).thingUpdated(any(Thing.class));
+
+        when(bridge.getStatusInfo()).thenReturn(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, ""));
+        when(bridge.getHandler()).thenReturn(bridgeHandler);
+
+        when(bridgeHandler.getWebSocketConnection()).thenReturn(webSocketConnection);
+        when(bridgeHandler.getBridgeFullState())
+                .thenReturn(CompletableFuture.completedFuture(Optional.of(bridgeFullState)));
+
+        when(bridgeFullState.getMessage(ResourceType.SENSORS, "1")).thenAnswer(i -> deconzMessage);
+    }
+
+    protected void createThing(ThingTypeUID thingTypeUID, List<String> channels,
+            BiFunction<Thing, Gson, DeconzBaseThingHandler> handlerSupplier) {
+        ThingBuilder thingBuilder = ThingBuilder.create(thingTypeUID, THING_UID);
+        thingBuilder.withBridge(BRIDGE_UID);
+        for (String channelId : channels) {
+            Channel channel = ChannelBuilder.create(new ChannelUID(THING_UID, channelId))
+                    .withType(new ChannelTypeUID(BINDING_ID, channelId)).build();
+            thingBuilder.withChannel(channel);
+        }
+        thingBuilder.withConfiguration(new Configuration(Map.of(CONFIG_ID, "1")));
+        thing = thingBuilder.build();
+
+        thingHandler = handlerSupplier.apply(thing, gson);
+        thingHandler.setCallback(callback);
+    }
+
+    protected void assertThing(String fileName, Set<TestParam> expected) throws IOException {
+        deconzMessage = DeconzTest.getObjectFromJson(fileName, SensorMessage.class, gson);
+
+        thingHandler.initialize();
+
+        ArgumentCaptor<ThingStatusInfo> captor = ArgumentCaptor.forClass(ThingStatusInfo.class);
+        verify(callback, atLeast(2).description("assertQuantityOfStatusUpdates")).statusUpdated(eq(thing),
+                captor.capture());
+
+        List<ThingStatusInfo> statusInfoList = captor.getAllValues();
+        assertThat("assertFirstThingStatus", statusInfoList.get(0).getStatus(), is(ThingStatus.UNKNOWN));
+        assertThat("assertLastThingStatus", statusInfoList.get(statusInfoList.size() - 1).getStatus(),
+                is(ThingStatus.ONLINE));
+
+        assertThat("assertChannelCount:" + getAllChannels(thing), thing.getChannels().size(), is(expected.size()));
+        for (TestParam testParam : expected) {
+            Channel channel = thing.getChannel(testParam.channelId());
+            assertThat("assertNonNullChannel:" + testParam.channelId, channel, is(notNullValue()));
+
+            State state = testParam.state;
+            if (channel != null && state != null) {
+                verify(callback, times(3).description(channel + " did not receive an update"))
+                        .stateUpdated(eq(channel.getUID()), eq(state));
+            }
+        }
+    }
+
+    private String getAllChannels(Thing thing) {
+        return thing.getChannels().stream().map(Channel::getUID).map(ChannelUID::getId)
+                .collect(Collectors.joining(","));
+    }
+
+    protected record TestParam(String channelId, @Nullable State state) {
+    }
+}
index 9c41c9b4b31b0dca6f2fe34c691613c8e1688be4..b4075c90936287da8c7cc826c98d87fb97ea002b 100644 (file)
  */
 package org.openhab.binding.deconz.internal.handler;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 import static org.openhab.binding.deconz.internal.BindingConstants.*;
 
 import java.io.IOException;
 import java.util.List;
-import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.CompletableFuture;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoSettings;
 import org.mockito.quality.Strictness;
-import org.openhab.binding.deconz.DeconzTest;
 import org.openhab.binding.deconz.internal.Util;
-import org.openhab.binding.deconz.internal.dto.BridgeFullState;
-import org.openhab.binding.deconz.internal.dto.SensorMessage;
-import org.openhab.binding.deconz.internal.netutils.WebSocketConnection;
-import org.openhab.binding.deconz.internal.types.LightType;
-import org.openhab.binding.deconz.internal.types.LightTypeDeserializer;
-import org.openhab.binding.deconz.internal.types.ResourceType;
-import org.openhab.binding.deconz.internal.types.ThermostatMode;
-import org.openhab.binding.deconz.internal.types.ThermostatModeGsonTypeAdapter;
-import org.openhab.core.config.core.Configuration;
 import org.openhab.core.library.types.DecimalType;
 import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.OpenClosedType;
 import org.openhab.core.library.types.QuantityType;
 import org.openhab.core.library.types.StringType;
-import org.openhab.core.test.java.JavaTest;
-import org.openhab.core.thing.Bridge;
-import org.openhab.core.thing.Channel;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.ThingStatusInfo;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandlerCallback;
-import org.openhab.core.thing.binding.builder.ChannelBuilder;
-import org.openhab.core.thing.binding.builder.ThingBuilder;
-import org.openhab.core.thing.type.ChannelTypeUID;
-import org.openhab.core.types.State;
 import org.openhab.core.types.UnDefType;
 
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
 /**
  * The {@link SensorThermostatThingHandlerTest} contains test classes for the {@link SensorThermostatThingHandler}
  *
@@ -83,72 +40,20 @@ import com.google.gson.GsonBuilder;
 @ExtendWith(MockitoExtension.class)
 @MockitoSettings(strictness = Strictness.LENIENT)
 @NonNullByDefault
-public class SensorThermostatThingHandlerTest extends JavaTest {
-
-    private static final ThingUID BRIDGE_UID = new ThingUID(BRIDGE_TYPE, "bridge");
-    private static final ThingUID THING_UID = new ThingUID(THING_TYPE_THERMOSTAT, "thing");
-
-    private @Mock @NonNullByDefault({}) Bridge bridge;
-    private @Mock @NonNullByDefault({}) ThingHandlerCallback callback;
-
-    private @Mock @NonNullByDefault({}) DeconzBridgeHandler bridgeHandler;
-    private @Mock @NonNullByDefault({}) WebSocketConnection webSocketConnection;
-    private @Mock @NonNullByDefault({}) BridgeFullState bridgeFullState;
-
-    private @NonNullByDefault({}) Gson gson;
-    private @NonNullByDefault({}) Thing thing;
-    private @NonNullByDefault({}) SensorThermostatThingHandler thingHandler;
-    private @NonNullByDefault({}) SensorMessage sensorMessage;
-
-    @BeforeEach
-    public void setup() {
-        GsonBuilder gsonBuilder = new GsonBuilder();
-        gsonBuilder.registerTypeAdapter(LightType.class, new LightTypeDeserializer());
-        gsonBuilder.registerTypeAdapter(ThermostatMode.class, new ThermostatModeGsonTypeAdapter());
-        gson = gsonBuilder.create();
-
-        ThingBuilder thingBuilder = ThingBuilder.create(THING_TYPE_THERMOSTAT, THING_UID);
-        thingBuilder.withBridge(BRIDGE_UID);
-        for (String channelId : List.of(CHANNEL_TEMPERATURE, CHANNEL_HEATSETPOINT, CHANNEL_THERMOSTAT_MODE,
-                CHANNEL_TEMPERATURE_OFFSET, CHANNEL_LAST_UPDATED)) {
-            Channel channel = ChannelBuilder.create(new ChannelUID(THING_UID, channelId))
-                    .withType(new ChannelTypeUID(BINDING_ID, channelId)).build();
-            thingBuilder.withChannel(channel);
-        }
-        thingBuilder.withConfiguration(new Configuration(Map.of(CONFIG_ID, "1")));
-        thing = thingBuilder.build();
-
-        thingHandler = new SensorThermostatThingHandler(thing, gson);
-        thingHandler.setCallback(callback);
-
-        when(callback.getBridge(BRIDGE_UID)).thenReturn(bridge);
-        when(callback.createChannelBuilder(any(ChannelUID.class), any(ChannelTypeUID.class)))
-                .thenAnswer(i -> ChannelBuilder.create((ChannelUID) i.getArgument(0)).withType(i.getArgument(1)));
-        doAnswer(i -> {
-            thing = i.getArgument(0);
-            thingHandler.thingUpdated(thing);
-            return null;
-        }).when(callback).thingUpdated(any(Thing.class));
-
-        when(bridge.getStatusInfo()).thenReturn(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, ""));
-        when(bridge.getHandler()).thenReturn(bridgeHandler);
-
-        when(bridgeHandler.getWebSocketConnection()).thenReturn(webSocketConnection);
-        when(bridgeHandler.getBridgeFullState())
-                .thenReturn(CompletableFuture.completedFuture(Optional.of(bridgeFullState)));
-
-        when(bridgeFullState.getMessage(ResourceType.SENSORS, "1")).thenAnswer(i -> sensorMessage);
-    }
+public class SensorThermostatThingHandlerTest extends BaseDeconzThingHandlerTest {
 
     @Test
     public void testDanfoss() throws IOException {
+        createThing(THING_TYPE_THERMOSTAT, List.of(CHANNEL_HEATSETPOINT, CHANNEL_LAST_UPDATED, CHANNEL_TEMPERATURE,
+                CHANNEL_TEMPERATURE_OFFSET, CHANNEL_THERMOSTAT_MODE), SensorThermostatThingHandler::new);
+
         Set<TestParam> expected = Set.of(
                 // standard channels
-                new TestParam(CHANNEL_TEMPERATURE, new QuantityType<>("21.45 °C")),
                 new TestParam(CHANNEL_HEATSETPOINT, new QuantityType<>("21.00 °C")),
-                new TestParam(CHANNEL_THERMOSTAT_MODE, new StringType("HEAT")),
-                new TestParam(CHANNEL_TEMPERATURE_OFFSET, new QuantityType<>("0.0 °C")),
                 new TestParam(CHANNEL_LAST_UPDATED, Util.convertTimestampToDateTime("2023-03-18T05:52:29.506")),
+                new TestParam(CHANNEL_TEMPERATURE, new QuantityType<>("21.45 °C")),
+                new TestParam(CHANNEL_TEMPERATURE_OFFSET, new QuantityType<>("0.0 °C")),
+                new TestParam(CHANNEL_THERMOSTAT_MODE, new StringType("HEAT")),
                 // battery
                 new TestParam(CHANNEL_BATTERY_LEVEL, new DecimalType(41)),
                 new TestParam(CHANNEL_BATTERY_LOW, OnOffType.OFF),
@@ -156,99 +61,80 @@ public class SensorThermostatThingHandlerTest extends JavaTest {
                 new TestParam(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime("2023-03-18T05:58Z")),
                 // dynamic channels
                 new TestParam(CHANNEL_EXTERNAL_WINDOW_OPEN, OpenClosedType.CLOSED),
-                new TestParam(CHANNEL_VALVE_POSITION, new QuantityType<>("1 %")),
                 new TestParam(CHANNEL_THERMOSTAT_LOCKED, OnOffType.OFF),
                 new TestParam(CHANNEL_THERMOSTAT_ON, OnOffType.OFF),
+                new TestParam(CHANNEL_VALVE_POSITION, new QuantityType<>("1 %")),
                 new TestParam(CHANNEL_WINDOW_OPEN, OpenClosedType.CLOSED));
 
-        assertThermostat("json/thermostat/danfoss.json", expected);
+        assertThing("json/thermostat/danfoss.json", expected);
     }
 
     @Test
     public void testNamron() throws IOException {
+        createThing(THING_TYPE_THERMOSTAT, List.of(CHANNEL_TEMPERATURE, CHANNEL_HEATSETPOINT, CHANNEL_THERMOSTAT_MODE,
+                CHANNEL_TEMPERATURE_OFFSET, CHANNEL_LAST_UPDATED), SensorThermostatThingHandler::new);
+
         Set<TestParam> expected = Set.of(
                 // standard channels
-                new TestParam(CHANNEL_TEMPERATURE, new QuantityType<>("20.39 °C")),
                 new TestParam(CHANNEL_HEATSETPOINT, new QuantityType<>("22.00 °C")),
-                new TestParam(CHANNEL_THERMOSTAT_MODE, new StringType("OFF")),
-                new TestParam(CHANNEL_TEMPERATURE_OFFSET, new QuantityType<>("0.0 °C")),
                 new TestParam(CHANNEL_LAST_UPDATED, Util.convertTimestampToDateTime("2023-03-18T18:10:39.296")),
+                new TestParam(CHANNEL_TEMPERATURE, new QuantityType<>("20.39 °C")),
+                new TestParam(CHANNEL_TEMPERATURE_OFFSET, new QuantityType<>("0.0 °C")),
+                new TestParam(CHANNEL_THERMOSTAT_MODE, new StringType("OFF")),
                 // last seen
                 new TestParam(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime("2023-03-18T18:10Z")),
                 // dynamic channels
                 new TestParam(CHANNEL_THERMOSTAT_LOCKED, OnOffType.OFF),
                 new TestParam(CHANNEL_THERMOSTAT_ON, OnOffType.OFF));
 
-        assertThermostat("json/thermostat/namron_ZB_E1.json", expected);
+        assertThing("json/thermostat/namron_ZB_E1.json", expected);
     }
 
     @Test
     public void testEurotronicValid() throws IOException {
+        createThing(THING_TYPE_THERMOSTAT, List.of(CHANNEL_HEATSETPOINT, CHANNEL_LAST_UPDATED, CHANNEL_TEMPERATURE,
+                CHANNEL_TEMPERATURE_OFFSET, CHANNEL_THERMOSTAT_MODE), SensorThermostatThingHandler::new);
+
         Set<TestParam> expected = Set.of(
                 // standard channels
-                new TestParam(CHANNEL_TEMPERATURE, new QuantityType<>("16.50 °C")),
                 new TestParam(CHANNEL_HEATSETPOINT, new QuantityType<>("25.00 °C")),
-                new TestParam(CHANNEL_THERMOSTAT_MODE, new StringType("AUTO")),
-                new TestParam(CHANNEL_TEMPERATURE_OFFSET, new QuantityType<>("0.0 °C")),
                 new TestParam(CHANNEL_LAST_UPDATED, Util.convertTimestampToDateTime("2020-05-31T20:24:55.819")),
+                new TestParam(CHANNEL_TEMPERATURE, new QuantityType<>("16.50 °C")),
+                new TestParam(CHANNEL_TEMPERATURE_OFFSET, new QuantityType<>("0.0 °C")),
+                new TestParam(CHANNEL_THERMOSTAT_MODE, new StringType("AUTO")),
                 // battery
                 new TestParam(CHANNEL_BATTERY_LEVEL, new DecimalType(85)),
                 new TestParam(CHANNEL_BATTERY_LOW, OnOffType.OFF),
                 // last seen
                 new TestParam(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime("2020-05-31T20:24:55.819")),
                 // dynamic channels
-                new TestParam(CHANNEL_VALVE_POSITION, new QuantityType<>("99 %")),
-                new TestParam(CHANNEL_THERMOSTAT_ON, OnOffType.ON));
+                new TestParam(CHANNEL_THERMOSTAT_ON, OnOffType.ON),
+                new TestParam(CHANNEL_VALVE_POSITION, new QuantityType<>("99 %")));
 
-        assertThermostat("json/thermostat/eurotronic.json", expected);
+        assertThing("json/thermostat/eurotronic.json", expected);
     }
 
     @Test
     public void testEurotronicInvalid() throws IOException {
+        createThing(THING_TYPE_THERMOSTAT, List.of(CHANNEL_HEATSETPOINT, CHANNEL_LAST_UPDATED, CHANNEL_TEMPERATURE,
+                CHANNEL_TEMPERATURE_OFFSET, CHANNEL_THERMOSTAT_MODE), SensorThermostatThingHandler::new);
+
         Set<TestParam> expected = Set.of(
                 // standard channels
-                new TestParam(CHANNEL_TEMPERATURE, new QuantityType<>("16.50 °C")),
                 new TestParam(CHANNEL_HEATSETPOINT, new QuantityType<>("25.00 °C")),
-                new TestParam(CHANNEL_THERMOSTAT_MODE, new StringType("AUTO")),
-                new TestParam(CHANNEL_TEMPERATURE_OFFSET, new QuantityType<>("0.0 °C")),
                 new TestParam(CHANNEL_LAST_UPDATED, Util.convertTimestampToDateTime("2020-05-31T20:24:55.819")),
+                new TestParam(CHANNEL_TEMPERATURE, new QuantityType<>("16.50 °C")),
+                new TestParam(CHANNEL_TEMPERATURE_OFFSET, new QuantityType<>("0.0 °C")),
+                new TestParam(CHANNEL_THERMOSTAT_MODE, new StringType("AUTO")),
                 // battery
                 new TestParam(CHANNEL_BATTERY_LEVEL, new DecimalType(85)),
                 new TestParam(CHANNEL_BATTERY_LOW, OnOffType.OFF),
                 // last seen
                 new TestParam(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime("2020-05-31T20:24:55.819")),
                 // dynamic channels
-                new TestParam(CHANNEL_VALVE_POSITION, UnDefType.UNDEF),
-                new TestParam(CHANNEL_THERMOSTAT_ON, OnOffType.ON));
-
-        assertThermostat("json/thermostat/eurotronic-invalid.json", expected);
-    }
-
-    private void assertThermostat(String fileName, Set<TestParam> expected) throws IOException {
-        sensorMessage = DeconzTest.getObjectFromJson(fileName, SensorMessage.class, gson);
-
-        thingHandler.initialize();
-
-        ArgumentCaptor<ThingStatusInfo> captor = ArgumentCaptor.forClass(ThingStatusInfo.class);
-        verify(callback, times(6)).statusUpdated(eq(thing), captor.capture());
-
-        List<ThingStatusInfo> statusInfoList = captor.getAllValues();
-        assertThat(statusInfoList.get(0).getStatus(), is(ThingStatus.UNKNOWN));
-        assertThat(statusInfoList.get(5).getStatus(), is(ThingStatus.ONLINE));
-
-        assertThat(thing.getChannels().size(), is(expected.size()));
-        for (TestParam testParam : expected) {
-            Channel channel = thing.getChannel(testParam.channelId());
-            assertThat(channel + "expected but missing", channel, is(notNullValue()));
-
-            State state = testParam.state;
-            if (state != null) {
-                verify(callback, times(3).description(channel + " did not receive an update"))
-                        .stateUpdated(eq(channel.getUID()), eq(state));
-            }
-        }
-    }
+                new TestParam(CHANNEL_THERMOSTAT_ON, OnOffType.ON),
+                new TestParam(CHANNEL_VALVE_POSITION, UnDefType.UNDEF));
 
-    private record TestParam(String channelId, @Nullable State state) {
+        assertThing("json/thermostat/eurotronic-invalid.json", expected);
     }
 }
diff --git a/bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/internal/handler/SensorThingHandlerTest.java b/bundles/org.openhab.binding.deconz/src/test/java/org/openhab/binding/deconz/internal/handler/SensorThingHandlerTest.java
new file mode 100644 (file)
index 0000000..897833e
--- /dev/null
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2010-2023 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.deconz.internal.handler;
+
+import static org.openhab.binding.deconz.internal.BindingConstants.*;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.openhab.binding.deconz.internal.Util;
+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.types.UnDefType;
+
+/**
+ * The {@link SensorThingHandlerTest} contains test classes for the {@link SensorThingHandler}
+ *
+ * @author Jan N. Klug - Initial contribution
+ */
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+@NonNullByDefault
+public class SensorThingHandlerTest extends BaseDeconzThingHandlerTest {
+
+    @Test
+    public void testAirQuality() throws IOException {
+        createThing(THING_TYPE_AIRQUALITY_SENSOR,
+                List.of(CHANNEL_AIRQUALITY, CHANNEL_AIRQUALITYPPB, CHANNEL_LAST_UPDATED), SensorThingHandler::new);
+
+        Set<TestParam> expected = Set.of(
+                // standard channels
+                new TestParam(CHANNEL_AIRQUALITY, new StringType("good")),
+                new TestParam(CHANNEL_AIRQUALITYPPB, new QuantityType<>("129 ppb")),
+                new TestParam(CHANNEL_LAST_UPDATED, Util.convertTimestampToDateTime("2021-12-29T01:18:41.184")),
+                // battery
+                new TestParam(CHANNEL_BATTERY_LEVEL, new DecimalType(100)),
+                new TestParam(CHANNEL_BATTERY_LOW, OnOffType.OFF),
+                // last seen
+                new TestParam(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime("2021-12-29T01:18Z")));
+
+        assertThing("json/sensors/airquality.json", expected);
+    }
+
+    @Test
+    public void testCarbonMonoxide() throws IOException {
+        createThing(THING_TYPE_CARBONMONOXIDE_SENSOR, List.of(CHANNEL_CARBONMONOXIDE, CHANNEL_LAST_UPDATED),
+                SensorThingHandler::new);
+
+        Set<TestParam> expected = Set.of(
+                // standard channels
+                new TestParam(CHANNEL_CARBONMONOXIDE, OnOffType.ON),
+                new TestParam(CHANNEL_LAST_UPDATED, UnDefType.UNDEF),
+                // battery
+                new TestParam(CHANNEL_BATTERY_LEVEL, new DecimalType(100)),
+                new TestParam(CHANNEL_BATTERY_LOW, OnOffType.OFF));
+
+        assertThing("json/sensors/carbonmonoxide.json", expected);
+    }
+
+    @Test
+    public void testFire() throws IOException {
+        createThing(THING_TYPE_FIRE_SENSOR, List.of(CHANNEL_FIRE, CHANNEL_LAST_UPDATED), SensorThingHandler::new);
+
+        Set<TestParam> expected = Set.of(
+                // standard channels
+                new TestParam(CHANNEL_FIRE, OnOffType.OFF), new TestParam(CHANNEL_LAST_UPDATED, UnDefType.UNDEF),
+                // battery
+                new TestParam(CHANNEL_BATTERY_LEVEL, new DecimalType(98)),
+                new TestParam(CHANNEL_BATTERY_LOW, OnOffType.OFF));
+
+        assertThing("json/sensors/fire.json", expected);
+    }
+
+    @Test
+    public void testSwitch() throws IOException {
+        createThing(THING_TYPE_SWITCH, List.of(CHANNEL_BUTTON, CHANNEL_BUTTONEVENT, CHANNEL_LAST_UPDATED),
+                SensorThingHandler::new);
+
+        Set<TestParam> expected = Set.of(
+                // standard channels
+                new TestParam(CHANNEL_BUTTON, new DecimalType(1002)), new TestParam(CHANNEL_BUTTONEVENT, null),
+                new TestParam(CHANNEL_LAST_UPDATED, Util.convertTimestampToDateTime("2022-02-15T13:36:16.271")),
+                // battery
+                new TestParam(CHANNEL_BATTERY_LEVEL, new DecimalType(7)),
+                new TestParam(CHANNEL_BATTERY_LOW, OnOffType.ON));
+
+        assertThing("json/sensors/switch.json", expected);
+    }
+
+    @Test
+    public void testVibration() throws IOException {
+        createThing(THING_TYPE_VIBRATION_SENSOR, List.of(CHANNEL_LAST_UPDATED, CHANNEL_VIBRATION),
+                SensorThingHandler::new);
+
+        Set<TestParam> expected = Set.of(
+                // standard channels
+                new TestParam(CHANNEL_LAST_UPDATED, Util.convertTimestampToDateTime("2022-09-09T18:13:44.653")),
+                new TestParam(CHANNEL_VIBRATION, OnOffType.ON),
+                // battery
+                new TestParam(CHANNEL_BATTERY_LEVEL, new DecimalType(100)),
+                new TestParam(CHANNEL_BATTERY_LOW, OnOffType.OFF),
+                // last seen
+                new TestParam(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime("2022-09-09T18:13Z")),
+                // dynamic channels
+                new TestParam(CHANNEL_ORIENTATION_X, new DecimalType(3)),
+                new TestParam(CHANNEL_ORIENTATION_Y, new DecimalType(-1)),
+                new TestParam(CHANNEL_ORIENTATION_Z, new DecimalType(-87)),
+                new TestParam(CHANNEL_TEMPERATURE, new QuantityType<>("26.00 °C")),
+                new TestParam(CHANNEL_TILTANGLE, new QuantityType<>("176 °")),
+                new TestParam(CHANNEL_VIBRATION_STRENGTH, new DecimalType(110)));
+
+        assertThing("json/sensors/vibration.json", expected);
+    }
+}
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
deleted file mode 100644 (file)
index 3d511b0..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-    "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
diff --git a/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/carbonmonoxide.json b/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/carbonmonoxide.json
deleted file mode 100644 (file)
index b50e421..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "config": {
-    "battery": 100,
-    "on": true,
-    "reachable": true
-  },
-  "ep": 1,
-  "etag": "1902d2cdb9d256624398f9ec3d35481d",
-  "manufacturername": "Heiman",
-  "modelid": "COSensor-N",
-  "name": "CO Sensor",
-  "state": {
-    "carbonmonoxide": true,
-    "lastupdated": "none",
-    "lowbattery": null,
-    "tampered": null
-  },
-  "swversion": "2018.4.22",
-  "type": "ZHACarbonMonoxide",
-  "uniqueid": "00:28:9f:00:03:eb:80:2a-01-0500"
-}
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/fire.json b/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/fire.json
deleted file mode 100644 (file)
index 2a70804..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "config": {
-        "battery": 98,
-        "on": true,
-        "pending" : [],
-        "reachable": true
-    },
-    "ep": 1,
-    "etag": "717549a99371f3ea1a5f0b40f1537094",
-    "lastseen": null,
-    "manufacturername": "Heimann",
-    "modelid": "SMOK_YDLV10",
-    "name": "Smoke Sensor",
-    "state": {
-        "fire": false,
-        "lastupdated": "none",
-        "lowbattery": null,
-        "tampered": null
-    },
-    "swversion": "20150330",
-    "type": "ZHAFire",
-    "uniqueid": "00:15:8d:00:01:ff:8a:09-01-0500"
-}
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/airquality.json b/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/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
diff --git a/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/carbonmonoxide.json b/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/carbonmonoxide.json
new file mode 100644 (file)
index 0000000..b50e421
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "config": {
+    "battery": 100,
+    "on": true,
+    "reachable": true
+  },
+  "ep": 1,
+  "etag": "1902d2cdb9d256624398f9ec3d35481d",
+  "manufacturername": "Heiman",
+  "modelid": "COSensor-N",
+  "name": "CO Sensor",
+  "state": {
+    "carbonmonoxide": true,
+    "lastupdated": "none",
+    "lowbattery": null,
+    "tampered": null
+  },
+  "swversion": "2018.4.22",
+  "type": "ZHACarbonMonoxide",
+  "uniqueid": "00:28:9f:00:03:eb:80:2a-01-0500"
+}
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/fire.json b/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/fire.json
new file mode 100644 (file)
index 0000000..2a70804
--- /dev/null
@@ -0,0 +1,23 @@
+{
+    "config": {
+        "battery": 98,
+        "on": true,
+        "pending" : [],
+        "reachable": true
+    },
+    "ep": 1,
+    "etag": "717549a99371f3ea1a5f0b40f1537094",
+    "lastseen": null,
+    "manufacturername": "Heimann",
+    "modelid": "SMOK_YDLV10",
+    "name": "Smoke Sensor",
+    "state": {
+        "fire": false,
+        "lastupdated": "none",
+        "lowbattery": null,
+        "tampered": null
+    },
+    "swversion": "20150330",
+    "type": "ZHAFire",
+    "uniqueid": "00:15:8d:00:01:ff:8a:09-01-0500"
+}
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/switch.json b/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/switch.json
new file mode 100644 (file)
index 0000000..dda03a8
--- /dev/null
@@ -0,0 +1,24 @@
+{
+  "config": {
+    "alert": "none",
+    "battery": 7,
+    "group": "8",
+    "on": true,
+    "reachable": true
+  },
+  "ep": 1,
+  "etag": "43c4c267ebf1239c94791be02d3927c8",
+  "lastannounced": null,
+  "lastseen": null,
+  "manufacturername": "IKEA of Sweden",
+  "mode": 3,
+  "modelid": "TRADFRI remote control",
+  "name": "TRÅDFR Laura",
+  "state": {
+    "buttonevent": 1002,
+    "lastupdated": "2022-02-15T13:36:16.271"
+  },
+  "swversion": "2.3.014",
+  "type": "ZHASwitch",
+  "uniqueid": "90:fd:9f:ff:fe:17:75:a3-01-1000"
+}
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/vibration.json b/bundles/org.openhab.binding.deconz/src/test/resources/org/openhab/binding/deconz/json/sensors/vibration.json
new file mode 100644 (file)
index 0000000..c6441fc
--- /dev/null
@@ -0,0 +1,32 @@
+{
+  "config": {
+    "battery": 100,
+    "on": true,
+    "pending": [],
+    "reachable": true,
+    "sensitivity": null,
+    "sensitivitymax": 21,
+    "temperature": 2600
+  },
+  "ep": 1,
+  "etag": "6c0452972eb8183b5604899c66a3e32f",
+  "lastannounced": null,
+  "lastseen": "2022-09-09T18:13Z",
+  "manufacturername": "LUMI",
+  "modelid": "lumi.vibration.aq1",
+  "name": "Vibration Sensor",
+  "state": {
+    "lastupdated": "2022-09-09T18:13:44.653",
+    "orientation": [
+      3,
+      -1,
+      -87
+    ],
+    "tiltangle": 176,
+    "vibration": true,
+    "vibrationstrength": 110
+  },
+  "swversion": "20180130",
+  "type": "ZHAVibration",
+  "uniqueid": "00:15:8d:00:08:52:a6:2c-01-0101"
+}
\ No newline at end of file