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.
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,
*/
@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) + "}";
}
}
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;
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);
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;
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);
}
}
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;
}
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));
+ }
}
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
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
--- /dev/null
+<?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>
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>
+++ /dev/null
-/**
- * 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)));
- }
-}
--- /dev/null
+/**
+ * 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) {
+ }
+}
*/
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}
*
@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),
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);
}
}
--- /dev/null
+/**
+ * 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);
+ }
+}
+++ /dev/null
-{
- "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
+++ /dev/null
-{
- "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
+++ /dev/null
-{
- "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
--- /dev/null
+{
+ "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
--- /dev/null
+{
+ "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
--- /dev/null
+{
+ "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
--- /dev/null
+{
+ "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
--- /dev/null
+{
+ "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