From 60f3c9320317076ee242c6a6ba861618c22a8044 Mon Sep 17 00:00:00 2001 From: eugen Date: Wed, 15 Jul 2020 21:04:13 +0200 Subject: [PATCH] [homekit] add support for air quality sensor (#8125) * add air quality sensor * add example to README Signed-off-by: Eugen Freiter --- bundles/org.openhab.io.homekit/README.md | 34 +++++++-- .../internal/HomekitAccessoryType.java | 1 + .../internal/HomekitCharacteristicType.java | 8 +++ .../accessories/HomekitAccessoryFactory.java | 2 + .../HomekitAirQualitySensorImpl.java | 71 +++++++++++++++++++ .../HomekitCharacteristicFactory.java | 50 +++++++++++++ 6 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAirQualitySensorImpl.java diff --git a/bundles/org.openhab.io.homekit/README.md b/bundles/org.openhab.io.homekit/README.md index 00b7ffb9b0222..a43a062283fc5 100644 --- a/bundles/org.openhab.io.homekit/README.md +++ b/bundles/org.openhab.io.homekit/README.md @@ -130,6 +130,19 @@ A full list of supported accessory types can be found in the table *below*. | Accessory Tag | Mandatory Characteristics | Optional Characteristics | Supported OH items | Description | |:---------------------|:----------------------------|:-----------------------------|:-------------------------|:-----------------------------------------------------------------| +| AirQualitySensor | | | | Air Quality Sensor which can measure different parameters | +| | AirQuality | | String | Air quality state, possible values (UNKNOWN,EXCELLENT,GOOD,FAIR,INFERIOR,POOR). Custom mapping can be defined at item level, e.g. [EXCELLENT="BEST", POOR="BAD"] | +| | | OzoneDensity | Number | Ozone density in micrograms/m3, max 1000 | +| | | NitrogenDioxideDensity | Number | NO2 density in micrograms/m3, max 1000 | +| | | SulphurDioxideDensity | Number | SO2 density in micrograms/m3, max 1000 | +| | | PM25Density | Number | PM2.5 micrometer particulate density in micrograms/m3, max 1000 | +| | | PM10Density | Number | PM10 micrometer particulate density in micrograms/m3, max 1000 | +| | | VOCDensity | Number | VOC Density in micrograms/m3, max 1000 | +| | | Name | String | Name of the sensor | +| | | ActiveStatus | Switch, Contact | Working status | +| | | FaultStatus | Switch, Contact | Fault status | +| | | TamperedStatus | Switch, Contact | Tampered status | +| | | BatteryLowStatus | Switch, Contact | Battery status | | LeakSensor | | | | Leak Sensor | | | LeakDetectedState | | Switch, Contact | Leak sensor state (ON=Leak Detected, OFF=no leak) | | | | Name | String | Name of the sensor | @@ -138,35 +151,35 @@ A full list of supported accessory types can be found in the table *below*. | | | TamperedStatus | Switch, Contact | Tampered status | | | | BatteryLowStatus | Switch, Contact | Battery status | | MotionSensor | | | | Motion Sensor | -| | MotionDetectedState | | Switc, Contact | Motion sensor state (ON=motion detected, OFF=no motion) | +| | MotionDetectedState | | Switch, Contact | Motion sensor state (ON=motion detected, OFF=no motion) | | | | Name | String | Name of the sensor | | | | ActiveStatus | Switch, Contact | Working status | | | | FaultStatus | Switch, Contact | Fault status | | | | TamperedStatus | Switch, Contact | Tampered status | | | | BatteryLowStatus | Switch, Contact | Battery status | -| OccupancySensor | | | | Occupancy Sensor | -| | OccupancyDetectedState | | SwitchItem, Contact Item | Occupancy sensor state (ON=occupied, OFF=not occupied | +| OccupancySensor | | | | Occupancy Sensor | +| | OccupancyDetectedState | | Switch, Contact | Occupancy sensor state (ON=occupied, OFF=not occupied) | | | | Name | String | Name of the sensor | | | | ActiveStatus | Switch, Contact | Working status | | | | FaultStatus | Switch, Contact | Fault status | | | | TamperedStatus | Switch, Contact | Tampered status | | | | BatteryLowStatus | Switch, Contact | Battery status | | ContactSensor | | | | Contact Sensor,An accessory with on/off state that can be viewed in HomeKit but not changed such as a contact sensor for a door or window | -| | ContactSensorState | | SwitchItem, Contact Item | Contact sensor state (ON=open, OFF=closed) | +| | ContactSensorState | | Switch, Contact | Contact sensor state (ON=open, OFF=closed) | | | | Name | String | Name of the sensor | | | | ActiveStatus | Switch, Contact | Working status | | | | FaultStatus | Switch, Contact | Fault status | | | | TamperedStatus | Switch, Contact | Tampered status | | | | BatteryLowStatus | Switch, Contact | Battery status | | SmokeSensor | | | | Smoke Sensor | -| | SmokeDetectedState | | SwitchItem, Contact Item | Smoke sensor state (ON=smoke detected, OFF=no smoke) | +| | SmokeDetectedState | | Switch, Contact | Smoke sensor state (ON=smoke detected, OFF=no smoke) | | | | Name | String | Name of the sensor | | | | ActiveStatus | Switch, Contact | Working status | | | | FaultStatus | Switch, Contact | Fault status | | | | TamperedStatus | Switch, Contact | Tampered status | | | | BatteryLowStatus | Switch, Contact | Battery status | | LightSensor | | | | Light sensor | -| | LightLevel | | Number | Light level in lux | +| | LightLevel | | Number | Light level in lux | | | | Name | String | Name of the sensor | | | | ActiveStatus | Switch, Contact | Working status | | | | FaultStatus | Switch, Contact | Fault status | @@ -402,6 +415,15 @@ Switch contactsensor_active "Contact Sensor Active" Switch contactsensor_fault "Contact Sensor Fault" (gContactSensor) {homekit="ContactSensor.FaultStatus"} Switch contactsensor_tampered "Contact Sensor Tampered" (gContactSensor) {homekit="ContactSensor.TamperedStatus"} +Group gAirQualitySensor "Air Quality Sensor" {homekit="AirQualitySensor"} +String airquality "Air Quality" (gAirQualitySensor) {homekit="AirQuality"} +Number ozone "Ozone Density" (gAirQualitySensor) {homekit="OzoneDensity"} +Number voc "VOC Density" (gAirQualitySensor) {homekit="VOCDensity"} +Number nitrogen "Nitrogen Density" (gAirQualitySensor) {homekit="NitrogenDioxideDensity"} +Number sulphur "Sulphur Density" (gAirQualitySensor) {homekit="SulphurDioxideDensity"} +Number pm25 "PM25 Density" (gAirQualitySensor) {homekit="PM25Density"} +Number pm10 "PM10 Density" (gAirQualitySensor) {homekit="PM10Density"} + Group gSecuritySystem "Security System Group" {homekit="SecuritySystem"} String security_current_state "Security Current State" (gSecuritySystem) {homekit="SecuritySystem.CurrentSecuritySystemState"} String security_target_state "Security Target State" (gSecuritySystem) {homekit="SecuritySystem.TargetSecuritySystemState"} diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryType.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryType.java index 23afdb93abe2e..b39b9e338184c 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryType.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryType.java @@ -45,6 +45,7 @@ public enum HomekitAccessoryType { GARAGE_DOOR_OPENER("GarageDoorOpener"), HEATER_COOLER("HeaterCooler"), LIGHT_SENSOR("LightSensor"), + AIR_QUALITY_SENSOR("AirQualitySensor"), DUMMY("Dummy"), @Deprecated() BLINDS("Blinds"), diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCharacteristicType.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCharacteristicType.java index 4bb33e7bee1e5..d8000d52ce27d 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCharacteristicType.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCharacteristicType.java @@ -101,6 +101,14 @@ public enum HomekitCharacteristicType { COOLING_THRESHOLD_TEMPERATURE("CoolingThresholdTemperature"), HEATING_THRESHOLD_TEMPERATURE("HeatingThresholdTemperature"), + AIR_QUALITY("AirQuality"), + OZONE_DENSITY("OzoneDensity"), + NITROGEN_DIOXIDE_DENSITY("NitrogenDioxideDensity"), + SULPHUR_DIOXIDE_DENSITY("SulphurDioxideDensity"), + PM25_DENSITY("PM25Density"), + PM10_DENSITY("PM10Density"), + VOC_DENSITY("VOCDensity"), + @Deprecated() OLD_BATTERY_LEVEL("homekit:BatteryLevel"), @Deprecated() diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java index 3741c6a3dea0e..8ed2a57350742 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java @@ -74,6 +74,7 @@ public class HomekitAccessoryFactory { put(CONTACT_SENSOR, new HomekitCharacteristicType[] { CONTACT_SENSOR_STATE }); put(SMOKE_SENSOR, new HomekitCharacteristicType[] { SMOKE_DETECTED_STATE }); put(HUMIDITY_SENSOR, new HomekitCharacteristicType[] { RELATIVE_HUMIDITY }); + put(AIR_QUALITY_SENSOR, new HomekitCharacteristicType[] { AIR_QUALITY }); put(SWITCH, new HomekitCharacteristicType[] { ON_STATE }); put(CARBON_DIOXIDE_SENSOR, new HomekitCharacteristicType[] { CARBON_DIOXIDE_DETECTED_STATE }); put(CARBON_MONOXIDE_SENSOR, new HomekitCharacteristicType[] { CARBON_MONOXIDE_DETECTED_STATE }); @@ -111,6 +112,7 @@ public class HomekitAccessoryFactory { put(CONTACT_SENSOR, HomekitContactSensorImpl.class); put(SMOKE_SENSOR, HomekitSmokeSensorImpl.class); put(HUMIDITY_SENSOR, HomekitHumiditySensorImpl.class); + put(AIR_QUALITY_SENSOR, HomekitAirQualitySensorImpl.class); put(SWITCH, HomekitSwitchImpl.class); put(CARBON_DIOXIDE_SENSOR, HomekitCarbonDioxideSensorImpl.class); put(CARBON_MONOXIDE_SENSOR, HomekitCarbonMonoxideSensorImpl.class); diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAirQualitySensorImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAirQualitySensorImpl.java new file mode 100644 index 0000000000000..56ae48e56e247 --- /dev/null +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAirQualitySensorImpl.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2010-2020 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.io.homekit.internal.accessories; + +import static org.openhab.io.homekit.internal.HomekitCharacteristicType.AIR_QUALITY; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.openhab.io.homekit.internal.HomekitAccessoryUpdater; +import org.openhab.io.homekit.internal.HomekitSettings; +import org.openhab.io.homekit.internal.HomekitTaggedItem; + +import io.github.hapjava.accessories.AirQualityAccessory; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airquality.AirQualityEnum; +import io.github.hapjava.services.impl.AirQualityService; + +/** + * Air Quality sensor accessory. + * + * @author Eugen Freiter - Initial contribution + */ +public class HomekitAirQualitySensorImpl extends AbstractHomekitAccessoryImpl implements AirQualityAccessory { + private final Map qualityStateMapping = new EnumMap( + AirQualityEnum.class) { + { + put(AirQualityEnum.UNKNOWN, "UNKNOWN"); + put(AirQualityEnum.EXCELLENT, "EXCELLENT"); + put(AirQualityEnum.GOOD, "GOOD"); + put(AirQualityEnum.FAIR, "FAIR"); + put(AirQualityEnum.INFERIOR, "INFERIOR"); + put(AirQualityEnum.POOR, "POOR"); + } + }; + + public HomekitAirQualitySensorImpl(HomekitTaggedItem taggedItem, List mandatoryCharacteristics, + HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException { + super(taggedItem, mandatoryCharacteristics, updater, settings); + updateMapping(AIR_QUALITY, qualityStateMapping); + getServices().add(new AirQualityService(this)); + } + + @Override + public CompletableFuture getAirQuality() { + return CompletableFuture + .completedFuture(getKeyFromMapping(AIR_QUALITY, qualityStateMapping, AirQualityEnum.UNKNOWN)); + } + + @Override + public void subscribeAirQuality(final HomekitCharacteristicChangeCallback callback) { + subscribe(AIR_QUALITY, callback); + } + + @Override + public void unsubscribeAirQuality() { + unsubscribe(AIR_QUALITY); + } +} diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java index d9a7a04bede64..79ac722c0edd0 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java @@ -49,6 +49,12 @@ import io.github.hapjava.characteristics.CharacteristicEnum; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airquality.NitrogenDioxideDensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.OzoneDensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.PM10DensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.PM25DensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.SulphurDioxideDensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.VOCDensityCharacteristic; import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryEnum; @@ -124,6 +130,12 @@ public class HomekitCharacteristicFactory { put(COOLING_THRESHOLD_TEMPERATURE, HomekitCharacteristicFactory::createCoolingThresholdCharacteristic); put(HEATING_THRESHOLD_TEMPERATURE, HomekitCharacteristicFactory::createHeatingThresholdCharacteristic); put(REMAINING_DURATION, HomekitCharacteristicFactory::createRemainingDurationCharacteristic); + put(OZONE_DENSITY, HomekitCharacteristicFactory::createOzoneDensityCharacteristic); + put(NITROGEN_DIOXIDE_DENSITY, HomekitCharacteristicFactory::createNitrogenDioxideDensityCharacteristic); + put(SULPHUR_DIOXIDE_DENSITY, HomekitCharacteristicFactory::createSulphurDioxideDensityCharacteristic); + put(PM25_DENSITY, HomekitCharacteristicFactory::createPM25DensityCharacteristic); + put(PM10_DENSITY, HomekitCharacteristicFactory::createPM10DensityCharacteristic); + put(VOC_DENSITY, HomekitCharacteristicFactory::createVOCDensityCharacteristic); // LEGACY put(OLD_BATTERY_LOW_STATUS, HomekitCharacteristicFactory::createStatusLowBatteryCharacteristic); } @@ -578,4 +590,42 @@ private static HeatingThresholdTemperatureCharacteristic createHeatingThresholdC getSubscriber(taggedItem, HEATING_THRESHOLD_TEMPERATURE, updater), getUnsubscriber(taggedItem, HEATING_THRESHOLD_TEMPERATURE, updater)); } + + private static OzoneDensityCharacteristic createOzoneDensityCharacteristic(final HomekitTaggedItem taggedItem, + HomekitAccessoryUpdater updater) { + return new OzoneDensityCharacteristic(getDoubleSupplier(taggedItem), + getSubscriber(taggedItem, OZONE_DENSITY, updater), getUnsubscriber(taggedItem, OZONE_DENSITY, updater)); + } + + private static NitrogenDioxideDensityCharacteristic createNitrogenDioxideDensityCharacteristic( + final HomekitTaggedItem taggedItem, HomekitAccessoryUpdater updater) { + return new NitrogenDioxideDensityCharacteristic(getDoubleSupplier(taggedItem), + getSubscriber(taggedItem, NITROGEN_DIOXIDE_DENSITY, updater), + getUnsubscriber(taggedItem, NITROGEN_DIOXIDE_DENSITY, updater)); + } + + private static SulphurDioxideDensityCharacteristic createSulphurDioxideDensityCharacteristic( + final HomekitTaggedItem taggedItem, HomekitAccessoryUpdater updater) { + return new SulphurDioxideDensityCharacteristic(getDoubleSupplier(taggedItem), + getSubscriber(taggedItem, SULPHUR_DIOXIDE_DENSITY, updater), + getUnsubscriber(taggedItem, SULPHUR_DIOXIDE_DENSITY, updater)); + } + + private static PM25DensityCharacteristic createPM25DensityCharacteristic(final HomekitTaggedItem taggedItem, + HomekitAccessoryUpdater updater) { + return new PM25DensityCharacteristic(getDoubleSupplier(taggedItem), + getSubscriber(taggedItem, PM25_DENSITY, updater), getUnsubscriber(taggedItem, PM25_DENSITY, updater)); + } + + private static PM10DensityCharacteristic createPM10DensityCharacteristic(final HomekitTaggedItem taggedItem, + HomekitAccessoryUpdater updater) { + return new PM10DensityCharacteristic(getDoubleSupplier(taggedItem), + getSubscriber(taggedItem, PM10_DENSITY, updater), getUnsubscriber(taggedItem, PM10_DENSITY, updater)); + } + + private static VOCDensityCharacteristic createVOCDensityCharacteristic(final HomekitTaggedItem taggedItem, + HomekitAccessoryUpdater updater) { + return new VOCDensityCharacteristic(getDoubleSupplier(taggedItem), + getSubscriber(taggedItem, VOC_DENSITY, updater), getUnsubscriber(taggedItem, VOC_DENSITY, updater)); + } }