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