[tradfri] Add support for Air Purifier (#14836)
* [tradfri] Added Support for Air Purifier (#7) Added Support for Tradfri Air Purifier: * Added documentation disambiguation Tradfri vs Dirigera * Added Tradfri Air Purifier - fanMode and fanSpeed * Workable Tradfri Air Purifier basic implementation * Tradfri: modified fanMode type and definition * Tradfri Air Purifier: Added disableLed * Tradfri Air Purifier: Added lockPhysicalButton * Tradfri Air Purifier: Added airQuality data * Tradfri Air Purifier: Added filterCheck data * Tradfri Air Purifier: Added translations * Tradfri Air Purifier: Added filter_uptime * Tradfri Air Purifier: code optimization * Documentation for supported Air Purifier channels Fixes #14816 Signed-off-by: Vivien Boistuaud <github@boistuaud.net>
This commit is contained in:
@@ -27,6 +27,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Christoph Weitkamp - Added support for remote controller and motion sensor devices (read-only battery level)
|
||||
* @author Manuel Raffel - Added support for blinds
|
||||
* @author Vivien Boistuaud - Added support for air purifier
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TradfriBindingConstants {
|
||||
@@ -45,6 +46,7 @@ public class TradfriBindingConstants {
|
||||
public static final ThingTypeUID THING_TYPE_MOTION_SENSOR = new ThingTypeUID(BINDING_ID, "0107");
|
||||
public static final ThingTypeUID THING_TYPE_BLINDS = new ThingTypeUID(BINDING_ID, "0202");
|
||||
public static final ThingTypeUID THING_TYPE_OPEN_CLOSE_REMOTE_CONTROL = new ThingTypeUID(BINDING_ID, "0203");
|
||||
public static final ThingTypeUID THING_TYPE_AIR_PURIFIER = new ThingTypeUID(BINDING_ID, "0007");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_LIGHT_TYPES_UIDS = Collections
|
||||
.unmodifiableSet(Stream.of(THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_COLOR_TEMP_LIGHT, THING_TYPE_COLOR_LIGHT)
|
||||
@@ -54,6 +56,8 @@ public class TradfriBindingConstants {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_BLINDS_TYPES_UIDS = Collections.singleton(THING_TYPE_BLINDS);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_AIR_PURIFIER_TYPES_UIDS = Set.of(THING_TYPE_AIR_PURIFIER);
|
||||
|
||||
// List of all Gateway Configuration Properties
|
||||
public static final String GATEWAY_CONFIG_HOST = "host";
|
||||
public static final String GATEWAY_CONFIG_PORT = "port";
|
||||
@@ -70,7 +74,8 @@ public class TradfriBindingConstants {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_DEVICE_TYPES_UIDS = Collections.unmodifiableSet(Stream
|
||||
.of(SUPPORTED_LIGHT_TYPES_UIDS.stream(), SUPPORTED_CONTROLLER_TYPES_UIDS.stream(),
|
||||
SUPPORTED_PLUG_TYPES_UIDS.stream(), SUPPORTED_BLINDS_TYPES_UIDS.stream())
|
||||
SUPPORTED_PLUG_TYPES_UIDS.stream(), SUPPORTED_BLINDS_TYPES_UIDS.stream(),
|
||||
SUPPORTED_AIR_PURIFIER_TYPES_UIDS.stream())
|
||||
.reduce(Stream::concat).orElseGet(Stream::empty).collect(Collectors.toSet()));
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
|
||||
@@ -85,9 +90,20 @@ public class TradfriBindingConstants {
|
||||
public static final String CHANNEL_POSITION = "position";
|
||||
public static final String CHANNEL_BATTERY_LEVEL = "battery_level";
|
||||
public static final String CHANNEL_BATTERY_LOW = "battery_low";
|
||||
public static final String CHANNEL_FAN_MODE = "fan_mode";
|
||||
public static final String CHANNEL_FAN_SPEED = "fan_speed";
|
||||
public static final String CHANNEL_DISABLE_LED = "disable_led";
|
||||
public static final String CHANNEL_LOCK_BUTTON = "lock_button";
|
||||
public static final String CHANNEL_AIR_QUALITY_PM25 = "air_quality_pm25";
|
||||
public static final String CHANNEL_AIR_QUALITY_RATING = "air_quality_rating";
|
||||
public static final String CHANNEL_FILTER_CHECK_NEXT = "filter_check_next";
|
||||
public static final String CHANNEL_FILTER_CHECK_ALARM = "filter_check_alarm";
|
||||
public static final String CHANNEL_FILTER_UPTIME = "filter_uptime";
|
||||
|
||||
// IPSO Objects
|
||||
public static final String DEVICES = "15001";
|
||||
public static final String AIR_PURIFIER = "15025";
|
||||
public static final String AIR_QUALITY = "5907";
|
||||
public static final String AUTH_PATH = "9063";
|
||||
public static final String BLINDS = "15015";
|
||||
public static final String CLIENT_IDENTITY_PROPOSED = "9090";
|
||||
@@ -107,6 +123,9 @@ public class TradfriBindingConstants {
|
||||
public static final String END_TIME_HR = "9048";
|
||||
public static final String END_TIME_MN = "9049";
|
||||
public static final String ERROR_TAG = "errorcode";
|
||||
public static final String FAN_MODE = "5900";
|
||||
public static final String FAN_SPEED = "5908";
|
||||
public static final String FILTER_UPTIME = "5902";
|
||||
public static final String FORCE_CHECK_OTA_UPDATE = "9032";
|
||||
public static final String GATEWAY = "15011";
|
||||
public static final String GATEWAY_DETAILS = "15012";
|
||||
@@ -125,9 +144,11 @@ public class TradfriBindingConstants {
|
||||
public static final String IKEA_MOODS = "9068";
|
||||
public static final String INSTANCE_ID = "9003";
|
||||
public static final String LAST_SEEN = "9020";
|
||||
public static final String LED_DISABLE = "5906";
|
||||
public static final String LIGHT = "3311";
|
||||
public static final int LIGHTS_OFF_SMART_TASK = 2;
|
||||
public static final String LIGHT_SETTING = "15013";
|
||||
public static final String LOCK_PHYSICAL_BUTTON = "5905";
|
||||
public static final int LOSS_OF_INTERNET_CONNECTIVITY = 5001;
|
||||
public static final String MASTER_TOKEN_TAG = "9036";
|
||||
public static final String MAX_MSR_VALUE = "5602";
|
||||
@@ -137,6 +158,7 @@ public class TradfriBindingConstants {
|
||||
public static final String NAME = "9001";
|
||||
public static final int NEW_FIRMWARE_AVAILABLE = 1001;
|
||||
public static final String NEW_PSK_BY_GW = "9091";
|
||||
public static final String NEXT_FILTER_CHECK = "5910";
|
||||
public static final String NOTIFICATION_EVENT = "9015";
|
||||
public static final String NOTIFICATION_NVPAIR = "9017";
|
||||
public static final String NOTIFICATION_STATE = "9014";
|
||||
@@ -201,8 +223,27 @@ public class TradfriBindingConstants {
|
||||
public static final String TYPE_SENSOR = "4";
|
||||
public static final String TYPE_REPEATER = "6";
|
||||
public static final String TYPE_BLINDS = "7";
|
||||
public static final String TYPE_AIR_PURIFIER = "10";
|
||||
|
||||
public static final String DEVICE_VENDOR = "0";
|
||||
public static final String DEVICE_MODEL = "1";
|
||||
public static final String DEVICE_FIRMWARE = "3";
|
||||
public static final String DEVICE_BATTERY_LEVEL = "9";
|
||||
|
||||
// List of Air Purifier Constants
|
||||
public static final int FAN_MODE_OFF = 0;
|
||||
public static final int FAN_MODE_AUTO = 1;
|
||||
public static final int FAN_MODE_SPEED1 = 10;
|
||||
public static final int FAN_MODE_SPEED2 = 20;
|
||||
public static final int FAN_MODE_SPEED3 = 30;
|
||||
public static final int FAN_MODE_SPEED4 = 40;
|
||||
public static final int FAN_MODE_SPEED5 = 50;
|
||||
|
||||
public static final Set<Integer> AIR_PURIFIER_FANMODE = Set.of(FAN_MODE_OFF, FAN_MODE_AUTO, FAN_MODE_SPEED1,
|
||||
FAN_MODE_SPEED2, FAN_MODE_SPEED3, FAN_MODE_SPEED4, FAN_MODE_SPEED5);
|
||||
|
||||
public static final int AIR_PURIFIER_AIR_QUALITY_OK = 36;
|
||||
public static final int AIR_PURIFIER_AIR_QUALITY_BAD = 86;
|
||||
|
||||
public static final int AIR_PURIFIER_AIR_QUALITY_UNDEFINED = 65535;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import static org.openhab.binding.tradfri.internal.TradfriBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.tradfri.internal.handler.TradfriAirPurifierHandler;
|
||||
import org.openhab.binding.tradfri.internal.handler.TradfriBlindHandler;
|
||||
import org.openhab.binding.tradfri.internal.handler.TradfriControllerHandler;
|
||||
import org.openhab.binding.tradfri.internal.handler.TradfriGatewayHandler;
|
||||
@@ -63,6 +64,8 @@ public class TradfriHandlerFactory extends BaseThingHandlerFactory {
|
||||
return new TradfriLightHandler(thing);
|
||||
} else if (SUPPORTED_PLUG_TYPES_UIDS.contains(thingTypeUID)) {
|
||||
return new TradfriPlugHandler(thing);
|
||||
} else if (THING_TYPE_AIR_PURIFIER.equals(thingTypeUID)) {
|
||||
return new TradfriAirPurifierHandler(thing);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import com.google.gson.JsonSyntaxException;
|
||||
* @author Christoph Weitkamp - Added support for remote controller and motion sensor devices (read-only battery level)
|
||||
* @author Andre Fuechsel - fixed the results removal
|
||||
* @author Manuel Raffel - Added support for blinds
|
||||
* @author Vivien Boistuaud - Added support for Air Purifiers
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TradfriDiscoveryService extends AbstractDiscoveryService
|
||||
@@ -158,6 +159,8 @@ public class TradfriDiscoveryService extends AbstractDiscoveryService
|
||||
} else if (TYPE_SENSOR.equals(type) && data.has(SENSOR)) {
|
||||
// Motion sensor
|
||||
thingId = new ThingUID(THING_TYPE_MOTION_SENSOR, bridge, Integer.toString(id));
|
||||
} else if (TYPE_AIR_PURIFIER.equals(type) && data.has(AIR_PURIFIER)) {
|
||||
thingId = new ThingUID(THING_TYPE_AIR_PURIFIER, bridge, Integer.toString(id));
|
||||
}
|
||||
|
||||
if (thingId == null) {
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* 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.tradfri.internal.handler;
|
||||
|
||||
import static org.openhab.binding.tradfri.internal.TradfriBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.tradfri.internal.TradfriCoapClient;
|
||||
import org.openhab.binding.tradfri.internal.model.TradfriAirPurifierData;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link TradfriAirPurifierHandler} is responsible for handling commands and status updates
|
||||
* for Starkvind Air Purifiers.
|
||||
*
|
||||
* @author Vivien Boistuaud - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TradfriAirPurifierHandler extends TradfriThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(TradfriAirPurifierHandler.class);
|
||||
|
||||
public TradfriAirPurifierHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (active) {
|
||||
if (command instanceof RefreshType) {
|
||||
TradfriCoapClient coapClient = this.coapClient;
|
||||
if (coapClient != null) {
|
||||
logger.debug("Refreshing channel {}", channelUID);
|
||||
coapClient.asyncGet(this);
|
||||
} else {
|
||||
logger.debug("coapClient is null!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_FAN_MODE:
|
||||
handleFanModeCommand(command);
|
||||
break;
|
||||
case CHANNEL_DISABLE_LED:
|
||||
handleDisableLed(command);
|
||||
break;
|
||||
case CHANNEL_LOCK_BUTTON:
|
||||
handleLockButton(command);
|
||||
break;
|
||||
default:
|
||||
logger.error("Unknown channel UID {}", channelUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFanModeCommand(Command command) {
|
||||
if (command instanceof Number) {
|
||||
set(new TradfriAirPurifierData().setFanMode((Number) command).getJsonString());
|
||||
} else {
|
||||
logger.debug("Cannot handle command '{}' of type {} for channel '{}'", command, command.getClass(),
|
||||
CHANNEL_FAN_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDisableLed(Command command) {
|
||||
if (command instanceof OnOffType) {
|
||||
set(new TradfriAirPurifierData().setDisableLed((OnOffType) command).getJsonString());
|
||||
} else {
|
||||
logger.debug("Cannot handle command '{}' of type {} for channel '{}'", command, command.getClass(),
|
||||
CHANNEL_DISABLE_LED);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLockButton(Command command) {
|
||||
if (command instanceof OnOffType) {
|
||||
set(new TradfriAirPurifierData().setLockPhysicalButton((OnOffType) command).getJsonString());
|
||||
} else {
|
||||
logger.debug("Cannot handle command '{}' of type {} for channel '{}'", command, command.getClass(),
|
||||
CHANNEL_DISABLE_LED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(JsonElement data) {
|
||||
if (active && !(data.isJsonNull())) {
|
||||
TradfriAirPurifierData state = new TradfriAirPurifierData(data);
|
||||
updateStatus(state.getReachabilityStatus() ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
|
||||
|
||||
State fanMode = state.getFanMode();
|
||||
if (fanMode != null) {
|
||||
updateState(CHANNEL_FAN_MODE, fanMode);
|
||||
}
|
||||
|
||||
State fanSpeed = state.getFanSpeed();
|
||||
if (fanSpeed != null) {
|
||||
updateState(CHANNEL_FAN_SPEED, fanSpeed);
|
||||
}
|
||||
|
||||
State disableLed = state.getDisableLed();
|
||||
if (disableLed != null) {
|
||||
updateState(CHANNEL_DISABLE_LED, disableLed);
|
||||
}
|
||||
|
||||
State lockPhysicalButton = state.getLockPhysicalButton();
|
||||
if (lockPhysicalButton != null) {
|
||||
updateState(CHANNEL_LOCK_BUTTON, lockPhysicalButton);
|
||||
}
|
||||
|
||||
State airQualityPm25 = state.getAirQualityPM25();
|
||||
if (airQualityPm25 != null) {
|
||||
updateState(CHANNEL_AIR_QUALITY_PM25, airQualityPm25);
|
||||
}
|
||||
|
||||
State airQualityRating = state.getAirQualityRating();
|
||||
if (airQualityRating != null) {
|
||||
updateState(CHANNEL_AIR_QUALITY_RATING, airQualityRating);
|
||||
}
|
||||
|
||||
State nextFilterCheckTTL = state.getNextFilterCheckTTL();
|
||||
if (nextFilterCheckTTL != null) {
|
||||
updateState(CHANNEL_FILTER_CHECK_NEXT, nextFilterCheckTTL);
|
||||
}
|
||||
|
||||
State filterCheckAlarm = state.getFilterCheckAlarm();
|
||||
if (filterCheckAlarm != null) {
|
||||
updateState(CHANNEL_FILTER_CHECK_ALARM, filterCheckAlarm);
|
||||
}
|
||||
|
||||
State filterUptime = state.getFilterUptime();
|
||||
if (filterUptime != null) {
|
||||
updateState(CHANNEL_FILTER_UPTIME, filterUptime);
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
"Updating thing for airPurifierId {} to state {fanMode: {}, fanSpeed: {}, disableLed: {}, lockButton: {}, airQualityPm25: {}, airQualityRating: {}, nextFilterCheckTTL: {}, filterCheckAlarm: {}, filterUptime: {}, firmwareVersion: {}, modelId: {}, vendor: {}}",
|
||||
state.getDeviceId(), state.getFanMode(), state.getFanSpeed(), state.getDisableLed(),
|
||||
state.getLockPhysicalButton(), state.getAirQualityPM25(), state.getAirQualityRating(),
|
||||
state.getNextFilterCheckTTL(), state.getFilterCheckAlarm(), state.getFilterUptime(),
|
||||
state.getFirmwareVersion(), state.getModelId(), state.getVendor());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* 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.tradfri.internal.model;
|
||||
|
||||
import static org.openhab.binding.tradfri.internal.TradfriBindingConstants.*;
|
||||
|
||||
import javax.measure.quantity.Dimensionless;
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
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.unit.Units;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
/**
|
||||
* The {@link TradfriAirPurifierData} class is a Java wrapper for the raw JSON data about the air purifier state.
|
||||
*
|
||||
* @author Vivien Boistuaud - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TradfriAirPurifierData extends TradfriDeviceData {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(TradfriAirPurifierData.class);
|
||||
|
||||
public TradfriAirPurifierData() {
|
||||
super(AIR_PURIFIER);
|
||||
}
|
||||
|
||||
public TradfriAirPurifierData(JsonElement json) {
|
||||
super(AIR_PURIFIER, json);
|
||||
}
|
||||
|
||||
public String getJsonString() {
|
||||
return root.toString();
|
||||
}
|
||||
|
||||
public @Nullable DecimalType getFanMode() {
|
||||
JsonElement fanMode = attributes.get(FAN_MODE);
|
||||
if (fanMode != null) {
|
||||
int modeValue = fanMode.getAsInt();
|
||||
if (AIR_PURIFIER_FANMODE.contains(modeValue)) {
|
||||
return new DecimalType(modeValue);
|
||||
} else {
|
||||
logger.debug("Invalid speedMode is '{}': unknown value", modeValue);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public TradfriAirPurifierData setFanMode(Number speedValue) {
|
||||
int speed = speedValue.intValue();
|
||||
if (AIR_PURIFIER_FANMODE.contains(speed)) {
|
||||
attributes.add(FAN_MODE, new JsonPrimitive(speed));
|
||||
} else {
|
||||
logger.debug("Could not set fanMode to '{}': unknown value", speed);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public @Nullable DecimalType getFanSpeed() {
|
||||
JsonElement fanSpeed = attributes.get(FAN_SPEED);
|
||||
if (fanSpeed != null) {
|
||||
int speedValue = fanSpeed.getAsInt();
|
||||
return new DecimalType(speedValue);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable OnOffType getDisableLed() {
|
||||
JsonElement ledOnOff = attributes.get(LED_DISABLE);
|
||||
if (ledOnOff != null) {
|
||||
boolean ledStatus = ledOnOff.getAsInt() != 0;
|
||||
return OnOffType.from(ledStatus);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public TradfriAirPurifierData setDisableLed(OnOffType disableOnOff) {
|
||||
attributes.add(LED_DISABLE, new JsonPrimitive(OnOffType.ON.equals(disableOnOff) ? 1 : 0));
|
||||
return this;
|
||||
}
|
||||
|
||||
public @Nullable OnOffType getLockPhysicalButton() {
|
||||
JsonElement lockPhysicalButton = attributes.get(LOCK_PHYSICAL_BUTTON);
|
||||
if (lockPhysicalButton != null) {
|
||||
boolean isLocked = lockPhysicalButton.getAsInt() != 0;
|
||||
return OnOffType.from(isLocked);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public TradfriAirPurifierData setLockPhysicalButton(OnOffType lockPhysicalButton) {
|
||||
attributes.add(LOCK_PHYSICAL_BUTTON, new JsonPrimitive(OnOffType.ON.equals(lockPhysicalButton) ? 1 : 0));
|
||||
return this;
|
||||
}
|
||||
|
||||
public @Nullable State getAirQualityPM25() {
|
||||
JsonElement airQuality = attributes.get(AIR_QUALITY);
|
||||
if (airQuality != null) {
|
||||
int pm25InPpm = airQuality.getAsInt();
|
||||
if (pm25InPpm != AIR_PURIFIER_AIR_QUALITY_UNDEFINED) {
|
||||
return new QuantityType<Dimensionless>(pm25InPpm, Units.PARTS_PER_MILLION);
|
||||
} else {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable State getAirQualityRating() {
|
||||
State pm25State = getAirQualityPM25();
|
||||
if (pm25State != null) {
|
||||
if (pm25State instanceof Number) {
|
||||
int pm25Value = ((Number) pm25State).intValue();
|
||||
int qualityRating = 1;
|
||||
|
||||
if (pm25Value >= AIR_PURIFIER_AIR_QUALITY_BAD) {
|
||||
qualityRating = 3;
|
||||
} else if (pm25Value >= AIR_PURIFIER_AIR_QUALITY_OK) {
|
||||
qualityRating = 2;
|
||||
}
|
||||
|
||||
return new DecimalType(qualityRating);
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable QuantityType<Time> getNextFilterCheckTTL() {
|
||||
JsonElement nextFilterCheckTTL = attributes.get(NEXT_FILTER_CHECK);
|
||||
if (nextFilterCheckTTL != null) {
|
||||
int remainingMinutes = nextFilterCheckTTL.getAsInt();
|
||||
return new QuantityType<Time>(remainingMinutes, Units.MINUTE);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable OnOffType getFilterCheckAlarm() {
|
||||
QuantityType<Time> ttl = getNextFilterCheckTTL();
|
||||
if (ttl != null) {
|
||||
int ttlValue = ttl.intValue();
|
||||
return ttlValue < 0 ? OnOffType.ON : OnOffType.OFF;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable QuantityType<Time> getFilterUptime() {
|
||||
JsonElement filterUptime = attributes.get(FILTER_UPTIME);
|
||||
if (filterUptime != null) {
|
||||
int filterUptimeMinutes = filterUptime.getAsInt();
|
||||
return new QuantityType<Time>(filterUptimeMinutes, Units.MINUTE);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ addon.tradfri.description = This binding supports IKEA TRÅDFRI lighting devices
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.tradfri.0007.label = Air Purifier
|
||||
thing-type.tradfri.0007.description = This represents the air purifier sensors and controls.
|
||||
thing-type.tradfri.0010.label = On/Off Plug
|
||||
thing-type.tradfri.0010.description = A plug that can be switched on and off.
|
||||
thing-type.tradfri.0100.label = Dimmable Light
|
||||
@@ -43,6 +45,34 @@ thing-type.config.tradfri.device.id.description = The identifier of the device o
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.tradfri.air-quality-pm25.label = PM 2.5
|
||||
channel-type.tradfri.air-quality-pm25.description = Density of Particulate Matter of 2.5μm measured by the Air Purifier, in ppm.
|
||||
channel-type.tradfri.air-quality-rating.label = Air Quality
|
||||
channel-type.tradfri.air-quality-rating.description = An evaluation of the Air Quality between 1 (Good) and 3 (Bad).
|
||||
channel-type.tradfri.air-quality-rating.state.option.1 = Good
|
||||
channel-type.tradfri.air-quality-rating.state.option.2 = OK
|
||||
channel-type.tradfri.air-quality-rating.state.option.3 = Bad
|
||||
channel-type.tradfri.disable-led.label = Disable LED
|
||||
channel-type.tradfri.disable-led.description = Disables the LED's on the Air Purifier.
|
||||
channel-type.tradfri.fan-mode.label = Fan Speed Mode
|
||||
channel-type.tradfri.fan-mode.description = Controls the configured fan speed.
|
||||
channel-type.tradfri.fan-mode.state.option.0 = Power Off
|
||||
channel-type.tradfri.fan-mode.state.option.1 = Automatic
|
||||
channel-type.tradfri.fan-mode.state.option.10 = Speed 1
|
||||
channel-type.tradfri.fan-mode.state.option.20 = Speed 2
|
||||
channel-type.tradfri.fan-mode.state.option.30 = Speed 3
|
||||
channel-type.tradfri.fan-mode.state.option.40 = Speed 4
|
||||
channel-type.tradfri.fan-mode.state.option.50 = Speed 5
|
||||
channel-type.tradfri.fan-speed.label = Current Fan Speed
|
||||
channel-type.tradfri.fan-speed.description = Displays the current fan speed (0..50).
|
||||
channel-type.tradfri.filter-check-alarm.label = Filter Check Alarm
|
||||
channel-type.tradfri.filter-check-alarm.description = When this value is ON, you need to perform a filter check.
|
||||
channel-type.tradfri.filter-check-next.label = Next Filter Check
|
||||
channel-type.tradfri.filter-check-next.description = The number of minutes before the next filter check (or time since check is required if below 0).
|
||||
channel-type.tradfri.filter-uptime.label = Filter Uptime
|
||||
channel-type.tradfri.filter-uptime.description = The duration for which the current filter was used.
|
||||
channel-type.tradfri.lock-button.label = Lock Physical Button
|
||||
channel-type.tradfri.lock-button.description = When ON, locks the physical button on the device.
|
||||
channel-type.tradfri.position.label = Position
|
||||
channel-type.tradfri.position.description = Control the position of the blind or curtain in percent from 0 (open) to 100 (closed).
|
||||
|
||||
|
||||
@@ -176,6 +176,31 @@
|
||||
<config-description-ref uri="thing-type:tradfri:device"/>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="0007">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Air Purifier</label>
|
||||
<description>This represents the air purifier sensors and controls.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="fan_speed" typeId="fan-speed"/>
|
||||
<channel id="fan_mode" typeId="fan-mode"/>
|
||||
<channel id="disable_led" typeId="disable-led"/>
|
||||
<channel id="lock_button" typeId="lock-button"/>
|
||||
<channel id="air_quality_pm25" typeId="air-quality-pm25"/>
|
||||
<channel id="air_quality_rating" typeId="air-quality-rating"/>
|
||||
<channel id="filter_check_next" typeId="filter-check-next"/>
|
||||
<channel id="filter_check_alarm" typeId="filter-check-alarm"/>
|
||||
<channel id="filter_uptime" typeId="filter-uptime"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>id</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:tradfri:device"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="position">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Position</label>
|
||||
@@ -183,4 +208,100 @@
|
||||
<category>Blinds</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fan-mode">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
|
||||
<label>Fan Speed Mode</label>
|
||||
<description>Controls the configured fan speed.</description>
|
||||
|
||||
<state pattern="%d">
|
||||
<options>
|
||||
<option value="0">Power Off</option>
|
||||
<option value="1">Automatic</option>
|
||||
<option value="10">Speed 1</option>
|
||||
<option value="20">Speed 2</option>
|
||||
<option value="30">Speed 3</option>
|
||||
<option value="40">Speed 4</option>
|
||||
<option value="50">Speed 5</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fan-speed">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
|
||||
<label>Current Fan Speed</label>
|
||||
<description>Displays the current fan speed (0..50).</description>
|
||||
<category>Fan</category>
|
||||
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="disable-led">
|
||||
<item-type>Switch</item-type>
|
||||
|
||||
<label>Disable LED</label>
|
||||
<description>Disables the LED's on the Air Purifier.</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lock-button">
|
||||
<item-type>Switch</item-type>
|
||||
|
||||
<label>Lock Physical Button</label>
|
||||
<description>When ON, locks the physical button on the device.</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="air-quality-pm25">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
|
||||
<label>PM 2.5</label>
|
||||
<description>Density of Particulate Matter of 2.5μm measured by the Air Purifier, in ppm.</description>
|
||||
|
||||
<state readOnly="true" pattern="%d ppm"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="air-quality-rating">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
|
||||
<label>Air Quality</label>
|
||||
<description>An evaluation of the Air Quality between 1 (Good) and 3 (Bad).</description>
|
||||
|
||||
<state readOnly="true" pattern="%d">
|
||||
<options>
|
||||
<option value="1">Good</option>
|
||||
<option value="2">OK</option>
|
||||
<option value="3">Bad</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="filter-check-next">
|
||||
<item-type>Number:Time</item-type>
|
||||
|
||||
<label>Next Filter Check</label>
|
||||
<description>The number of minutes before the next filter check (or time since check is required if below 0).</description>
|
||||
<category>Time</category>
|
||||
|
||||
<state readOnly="true" pattern="%.2f d"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="filter-check-alarm">
|
||||
<item-type>Switch</item-type>
|
||||
|
||||
<label>Filter Check Alarm</label>
|
||||
<description>When this value is ON, you need to perform a filter check.</description>
|
||||
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="filter-uptime">
|
||||
<item-type>Number:Time</item-type>
|
||||
|
||||
<label>Filter Uptime</label>
|
||||
<description>The duration for which the current filter was used.</description>
|
||||
<category>Time</category>
|
||||
|
||||
<state readOnly="true" pattern="%.2f week"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.google.gson.JsonParser;
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Christoph Weitkamp - Added support for remote controller and motion sensor devices (read-only battery level)
|
||||
* @author Vivien Boistuaud - Added unit test for Air Purifier
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@@ -92,7 +93,7 @@ public class TradfriDiscoveryServiceTest {
|
||||
|
||||
@Test
|
||||
public void correctSupportedTypes() {
|
||||
assertThat(discovery.getSupportedThingTypes().size(), is(9));
|
||||
assertThat(discovery.getSupportedThingTypes().size(), is(10));
|
||||
assertTrue(discovery.getSupportedThingTypes().contains(THING_TYPE_DIMMABLE_LIGHT));
|
||||
assertTrue(discovery.getSupportedThingTypes().contains(THING_TYPE_COLOR_TEMP_LIGHT));
|
||||
assertTrue(discovery.getSupportedThingTypes().contains(THING_TYPE_COLOR_LIGHT));
|
||||
@@ -102,6 +103,7 @@ public class TradfriDiscoveryServiceTest {
|
||||
assertTrue(discovery.getSupportedThingTypes().contains(THING_TYPE_OPEN_CLOSE_REMOTE_CONTROL));
|
||||
assertTrue(discovery.getSupportedThingTypes().contains(THING_TYPE_ONOFF_PLUG));
|
||||
assertTrue(discovery.getSupportedThingTypes().contains(THING_TYPE_BLINDS));
|
||||
assertTrue(discovery.getSupportedThingTypes().contains(THING_TYPE_AIR_PURIFIER));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -233,4 +235,20 @@ public class TradfriDiscoveryServiceTest {
|
||||
assertThat(discoveryResult.getProperties().get(CONFIG_ID), is(65538));
|
||||
assertThat(discoveryResult.getRepresentationProperty(), is(CONFIG_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validDiscoveryResultAirPurifier() {
|
||||
String json = "{\"3\":{\"0\":\"IKEAofSweden\",\"1\":\"STARKVINDAirpurifier\",\"2\":\"\",\"3\":\"1.0.033\",\"6\":1,\"7\":4364},\"5750\":10,\"9001\":\"Luftreiniger\",\"9002\":1633096623,\"9003\":65548,\"9019\":1,\"9020\":1633096633,\"9054\":0,\"15025\":[{\"5900\":1,\"5902\":2,\"5903\":0,\"5904\":259200,\"5905\":0,\"5906\":0,\"5907\":5,\"5908\":10,\"5909\":2,\"5910\":259198,\"9003\":0}]}";
|
||||
JsonObject data = JsonParser.parseString(json).getAsJsonObject();
|
||||
|
||||
discovery.onUpdate("65548", data);
|
||||
|
||||
assertNotNull(discoveryResult);
|
||||
assertThat(discoveryResult.getFlag(), is(DiscoveryResultFlag.NEW));
|
||||
assertThat(discoveryResult.getThingUID(), is(new ThingUID("tradfri:0007:1:65548")));
|
||||
assertThat(discoveryResult.getThingTypeUID(), is(THING_TYPE_AIR_PURIFIER));
|
||||
assertThat(discoveryResult.getBridgeUID(), is(GATEWAY_THING_UID));
|
||||
assertThat(discoveryResult.getProperties().get(CONFIG_ID), is(65548));
|
||||
assertThat(discoveryResult.getRepresentationProperty(), is(CONFIG_ID));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user