Skip to content

Commit

Permalink
[veSync] Vital, 131 Purifiers and warm mist adjustments
Browse files Browse the repository at this point in the history
Device support enhancements
Signed-off-by: David Goodyear <[email protected]>
  • Loading branch information
dag81 committed Jul 31, 2023
1 parent e717794 commit 43b02e1
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> 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<String> 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<String> 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<VeSyncDeviceMetadata> SUPPORTED_MODEL_FAMILIES = Arrays.asList(LV600S, CLASSIC300S,
CLASSIC200S, DUAL200S, OASIS_MIST);

private static final List<String> CLASSIC_300S_600S_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP);
private static final List<String> 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<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_AIR_HUMIDIFIER);

private final Object pollLock = new Object();

public static final Map<String, VeSyncDeviceHumidifierMetadata> DEV_FAMILY_HUMIDIFER_MAP = new HashMap<String, VeSyncDeviceHumidifierMetadata>() {
{
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);
}
Expand Down Expand Up @@ -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(() -> {

Expand Down Expand Up @@ -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:
Expand All @@ -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));
Expand All @@ -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:
Expand Down Expand Up @@ -353,6 +375,10 @@ protected void pollForDeviceData(final ExpiringCache<String> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> deviceGenerations,
final List<String> nonStandardIds, final List<String> fanModes, final int targetMinMistLevel,
final int targetMaxMistLevel, final int targetMinWarmMistLevel, final int targetMaxWarmMistLevel,
final boolean remapsAutoToHumidity, List<String> 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<String> 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<String> nightLightModes;
}

0 comments on commit 43b02e1

Please sign in to comment.