diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java index 677141b1e4e97..16cb433713b2e 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java @@ -24,6 +24,7 @@ public interface VeSyncProtocolConstants { String MODE_MANUAL = "manual"; String MODE_SLEEP = "sleep"; String MODE_PET = "pet"; + String MODE_AUTO_HUMIDITY = "humidity"; String MODE_ON = "on"; String MODE_DIM = "dim"; diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirHumidifierHandler.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirHumidifierHandler.java index e193ccde8d47a..6d0213b1f9e72 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirHumidifierHandler.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirHumidifierHandler.java @@ -17,7 +17,9 @@ import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -59,33 +61,51 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { public static final String DEV_FAMILY_600S = "600S"; public static final String DEV_FAMILY_OASIS_MIST = "Oasis Mist"; - public static final VeSyncDeviceMetadata CLASSIC200S = new VeSyncDeviceMetadata(DEV_FAMILY_CLASSIC_200S, - Collections.emptyList(), List.of("Classic200S")); + private static final List AUTO_MAN_SLEEP_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP); - public static final VeSyncDeviceMetadata CLASSIC300S = new VeSyncDeviceMetadata(DEV_FAMILY_CLASSIC_300S, - Arrays.asList("A601S"), List.of("Classic300S")); + private static final List AUTO_MAN_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL); - public static final VeSyncDeviceMetadata DUAL200S = new VeSyncDeviceMetadata(DEV_FAMILY_DUAL_200S, - Arrays.asList("D301S"), List.of("Dual200S")); + private static final List CLASSIC_300S_NIGHT_LIGHT_MODES = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF); + + public static final VeSyncDeviceHumidifierMetadata CLASSIC200S = new VeSyncDeviceHumidifierMetadata( + DEV_FAMILY_CLASSIC_200S, Collections.emptyList(), List.of("Classic200S"), AUTO_MAN_MODES, 1, 3, -1, -1, + false, Collections.emptyList()); + + public static final VeSyncDeviceHumidifierMetadata CLASSIC300S = new VeSyncDeviceHumidifierMetadata( + DEV_FAMILY_CLASSIC_300S, Arrays.asList("A601S"), List.of("Classic300S"), AUTO_MAN_SLEEP_MODES, 1, 3, -1, -1, + false, CLASSIC_300S_NIGHT_LIGHT_MODES); - public static final VeSyncDeviceMetadata LV600S = new VeSyncDeviceMetadata(DEV_FAMILY_600S, Arrays.asList("A602S"), + public static final VeSyncDeviceHumidifierMetadata DUAL200S = new VeSyncDeviceHumidifierMetadata( + DEV_FAMILY_DUAL_200S, Arrays.asList("D301S"), List.of("Dual200S"), AUTO_MAN_MODES, 1, 2, -1, -1, false, Collections.emptyList()); - public static final VeSyncDeviceMetadata OASIS_MIST = new VeSyncDeviceMetadata(DEV_FAMILY_OASIS_MIST, - Arrays.asList("O451S"), Collections.emptyList()); + public static final VeSyncDeviceHumidifierMetadata LV600S = new VeSyncDeviceHumidifierMetadata(DEV_FAMILY_600S, + Arrays.asList("A602S"), Collections.emptyList(), AUTO_MAN_SLEEP_MODES, 1, 3, 0, 3, true, + CLASSIC_300S_NIGHT_LIGHT_MODES); + + public static final VeSyncDeviceHumidifierMetadata OASIS_MIST = new VeSyncDeviceHumidifierMetadata( + DEV_FAMILY_OASIS_MIST, Arrays.asList("O451S"), Collections.emptyList(), AUTO_MAN_SLEEP_MODES, 1, 3, 0, 3, + true, Collections.emptyList()); public static final List SUPPORTED_MODEL_FAMILIES = Arrays.asList(LV600S, CLASSIC300S, CLASSIC200S, DUAL200S, OASIS_MIST); - private static final List CLASSIC_300S_600S_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP); - private static final List CLASSIC_300S_NIGHT_LIGHT_MODES = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF); - private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirHumidifierHandler.class); public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_AIR_HUMIDIFIER); private final Object pollLock = new Object(); + public static final Map DEV_FAMILY_HUMIDIFER_MAP = new HashMap() { + { + put(CLASSIC200S.deviceFamilyName, CLASSIC200S); + put(CLASSIC300S.deviceFamilyName, CLASSIC300S); + put(DUAL200S.deviceFamilyName, DUAL200S); + put(LV600S.deviceFamilyName, LV600S); + put(OASIS_MIST.deviceFamilyName, OASIS_MIST); + } + }; + public VeSyncDeviceAirHumidifierHandler(Thing thing) { super(thing); } @@ -152,6 +172,11 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { if (deviceFamily == null) { return; } + final VeSyncDeviceHumidifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily); + if (devContraints == null) { + logger.warn("Could not find device family for {} during handleCommand", deviceFamily); + return; + } scheduler.submit(() -> { @@ -187,31 +212,27 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { } sendV2BypassControlCommand(DEVICE_SET_HUMIDITY_MODE, - new VeSyncRequestManagedDeviceBypassV2.SetMode(MODE_AUTO), false); + new VeSyncRequestManagedDeviceBypassV2.SetMode( + devContraints.getProtocolMode(MODE_AUTO)), + false); sendV2BypassControlCommand(DEVICE_SET_TARGET_HUMIDITY_MODE, new VeSyncRequestManagedDeviceBypassV2.SetTargetHumidity(targetHumidity)); break; case DEVICE_CHANNEL_MIST_LEVEL: int targetMistLevel = ((QuantityType) command).intValue(); + if (!devContraints.isTargetMistLevelSupported(targetMistLevel)) { + logger.warn("Mist level command for \"{}\" is not valid ({}) API possible options {} -> {}", + command, devContraints.deviceFamilyName, devContraints.targetMinMistLevel, + devContraints.targetMaxMistLevel); + targetMistLevel = targetMistLevel < devContraints.targetMinMistLevel + ? devContraints.targetMinMistLevel + : devContraints.targetMaxMistLevel; + } + // If more devices have this the hope is it's those with the prefix LUH so the check can // be simplified, originally devices mapped 1/5/9 to 1/2/3. - if (DEV_FAMILY_DUAL_200S.equals(deviceFamily)) { - if (targetMistLevel < 1) { - logger.warn("Target Mist Level less than 1 - adjusting to 1 as the valid API value"); - targetMistLevel = 1; - } else if (targetMistLevel > 2) { - logger.warn("Target Mist Level greater than 2 - adjusting to 2 as the valid API value"); - targetMistLevel = 2; - } - } else { - if (targetMistLevel < 1) { - logger.warn("Target Mist Level less than 1 - adjusting to 1 as the valid API value"); - targetMistLevel = 1; - } else if (targetMistLevel > 3) { - logger.warn("Target Mist Level greater than 3 - adjusting to 3 as the valid API value"); - targetMistLevel = 3; - } + if (!DEV_FAMILY_DUAL_200S.equals(deviceFamily)) { // Re-map to what appears to be bitwise encoding of the states switch (targetMistLevel) { case 1: @@ -234,15 +255,17 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { targetMistLevel)); break; case DEVICE_CHANNEL_WARM_LEVEL: - logger.warn("Warm level API is unknown in order to send the command"); int targetWarmMistLevel = ((QuantityType) command).intValue(); - if (targetWarmMistLevel < 0) { - logger.warn("Target Mist Level less than 0 - adjusting to 0 as the valid API value"); - targetWarmMistLevel = 0; - } else if (targetWarmMistLevel > 3) { - logger.warn("Target Mist Level greater than 3 - adjusting to 4 as the valid API value"); - targetWarmMistLevel = 3; + if (!devContraints.isTargetWramMistLevelSupported(targetWarmMistLevel)) { + logger.warn( + "Warm mist level command for \"{}\" is not valid ({}) API possible options {} -> {}", + command, devContraints.deviceFamilyName, devContraints.targetMinWarmMistLevel, + devContraints.targetMaxWarmMistLevel); + targetWarmMistLevel = targetWarmMistLevel < devContraints.targetMinWarmMistLevel + ? devContraints.targetMinWarmMistLevel + : devContraints.targetMaxWarmMistLevel; } + sendV2BypassControlCommand(DEVICE_SET_LEVEL, new VeSyncRequestManagedDeviceBypassV2.SetLevelPayload(0, DEVICE_LEVEL_TYPE_WARM_MIST, targetWarmMistLevel)); @@ -252,26 +275,25 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { final String targetMode = command.toString().toLowerCase(); switch (channelUID.getId()) { case DEVICE_CHANNEL_HUMIDIFIER_MODE: - if (!CLASSIC_300S_600S_MODES.contains(targetMode)) { + if (!devContraints.fanModes.contains(targetMode)) { logger.warn( - "Humidifier mode command for \"{}\" is not valid in the (Classic300S/600S) API possible options {}", - command, String.join(",", CLASSIC_300S_NIGHT_LIGHT_MODES)); + "Humidifier mode command for \"{}\" is not valid in the ({}}) API possible options {}", + command, devContraints.deviceFamilyName, String.join(",", devContraints.fanModes)); return; } sendV2BypassControlCommand(DEVICE_SET_HUMIDITY_MODE, - new VeSyncRequestManagedDeviceBypassV2.SetMode(targetMode)); + new VeSyncRequestManagedDeviceBypassV2.SetMode( + devContraints.getProtocolMode(targetMode))); break; case DEVICE_CHANNEL_AF_NIGHT_LIGHT: - if (!DEV_FAMILY_CLASSIC_300S.equals(deviceFamily) && !DEV_FAMILY_600S.equals(deviceFamily)) { - logger.warn("Humidifier night light is not valid for your device ({}})", deviceFamily); - return; - } - if (!CLASSIC_300S_NIGHT_LIGHT_MODES.contains(targetMode)) { + if (!devContraints.nightLightModes.contains(targetMode)) { logger.warn( - "Humidifier night light mode command for \"{}\" is not valid in the (Classic300S) API possible options {}", - command, String.join(",", CLASSIC_300S_NIGHT_LIGHT_MODES)); + "Humidifier night light command for \"{}\" is not valid in the ({}}) API possible options {}", + command, devContraints.deviceFamilyName, + String.join(",", devContraints.nightLightModes)); return; } + int targetValue; switch (targetMode) { case MODE_OFF: @@ -353,6 +375,10 @@ protected void pollForDeviceData(final ExpiringCache cachedResponse) { updateState(DEVICE_CHANNEL_HUMIDITY, new QuantityType<>(humidifierStatus.result.result.humidity, Units.PERCENT)); updateState(DEVICE_CHANNEL_MIST_LEVEL, new DecimalType(humidifierStatus.result.result.mistLevel)); + // Map back HUMIDITY -> AUTO if necessary for devices where auto is remapped + if (MODE_AUTO_HUMIDITY.equals(humidifierStatus.result.result.mode)) { + humidifierStatus.result.result.mode = MODE_AUTO; + } updateState(DEVICE_CHANNEL_HUMIDIFIER_MODE, new StringType(humidifierStatus.result.result.mode)); // Only the 300S supports nightlight currently of tested devices. diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirPurifierHandler.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirPurifierHandler.java index aa6026e979ad2..c3e3eb90c33a9 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirPurifierHandler.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirPurifierHandler.java @@ -331,8 +331,8 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { logger.warn("Fan speed command for \"{}\" is not valid ({}) API possible options {} -> {}", command, devContraints.deviceFamilyName, String.valueOf(devContraints.minFanSpeed), String.valueOf(devContraints.maxFanSpeed)); - pollForUpdate(); - return; + requestedLevel = requestedLevel < devContraints.minFanSpeed ? devContraints.minFanSpeed + : devContraints.maxFanSpeed; } switch (deviceFamily) { case DEV_FAMILY_VITAL_100S: diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java new file mode 100644 index 0000000000000..f9909031facc9 --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java @@ -0,0 +1,94 @@ +/** + * 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.vesync.internal.handlers; + +import static org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolConstants.MODE_AUTO; +import static org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolConstants.MODE_AUTO_HUMIDITY; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link VeSyncDeviceHumidifierMetadata} class contains the definition for the control of humidifer device types. + * + * @author David Goodyear - Initial contribution + */ +@NonNullByDefault +public class VeSyncDeviceHumidifierMetadata extends VeSyncDeviceMetadata { + + public VeSyncDeviceHumidifierMetadata(final String deviceFamilyName, final List deviceGenerations, + final List nonStandardIds, final List fanModes, final int targetMinMistLevel, + final int targetMaxMistLevel, final int targetMinWarmMistLevel, final int targetMaxWarmMistLevel, + final boolean remapsAutoToHumidity, List nightLightModes) { + super(deviceFamilyName, deviceGenerations, nonStandardIds); + this.fanModes = fanModes; + this.targetMinMistLevel = targetMinMistLevel; + this.targetMaxMistLevel = targetMaxMistLevel; + this.targetMinWarmMistLevel = targetMinWarmMistLevel; + this.targetMaxWarmMistLevel = targetMaxWarmMistLevel; + this.remapsAutoToHumidity = remapsAutoToHumidity; + this.nightLightModes = nightLightModes; + } + + /** + * The fan modes supported by this generation of device + */ + public final List fanModes; + + /** + * The minimum target mist level supported + */ + public final int targetMinMistLevel; + + /** + * The maximum target mist level supported + */ + public final int targetMaxMistLevel; + + public final boolean isTargetMistLevelSupported(final int target) { + return target >= targetMinMistLevel && target <= targetMaxMistLevel; + } + + /** + * The minimum target mist level supported + */ + public final int targetMinWarmMistLevel; + + /** + * The maximum target mist level supported + */ + public final int targetMaxWarmMistLevel; + + public final boolean isTargetWramMistLevelSupported(final int target) { + return target >= targetMinWarmMistLevel && target <= targetMaxWarmMistLevel; + } + + /** + * Stores whether auto in openhab is humidity mode in the protocol + */ + public final boolean remapsAutoToHumidity; + + public String getProtocolMode(final String mode) { + if (!remapsAutoToHumidity) { + return mode; + } else { + if (mode.equals(MODE_AUTO)) { + return MODE_AUTO_HUMIDITY; + } + return mode; + } + } + + public List nightLightModes; +}