### Verified Models
Air Filtering models supported are Core300S, Core400S.
-Air Humidifier models supported are Dual 200S, Classic 300S, 600S.
+Air Humidifier models supported are Dual 200S, Classic 300S, 600S, OasisMist Smart Humidifier
### Awaiting User Verification Models
### AirHumidifier Thing
-| Channel | Type | Description | Model's Supported | Controllable Values |
-|----------------------------|----------------------|---------------------------------------------------------------|----------------------------|---------------------|
-| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 200S, Dual200S, 300S, 600S | [ON, OFF] |
-| **display** | Switch | Whether the display is enabled (display is shown) | 200S, Dual200S, 300S, 600S | [ON, OFF] |
-| waterLacking | Switch | Indicator whether the unit is lacking water | 200S, Dual200S, 300S, 600S | |
-| humidityHigh | Switch | Indicator for high humidity | 200S, Dual200S, 300S, 600S | |
-| waterTankLifted | Switch | Indicator for whether the water tank is removed | 200S, Dual200S, 300S, 600S | |
-| **stopAtHumiditySetpoint** | Switch | Whether the unit is set to stop when the set point is reached | 200S, Dual200S, 300S, 600S | [ON, OFF] |
-| humidity | Number:Dimensionless | Indicator for the currently measured humidity % level | 200S, Dual200S, 300S, 600S | |
-| **mistLevel** | Number:Dimensionless | The current mist level set | 300S | [1...2] |
-| **mistLevel** | Number:Dimensionless | The current mist level set | 200S, Dual200S, 600S | [1...3] |
-| **humidifierMode** | String | The current mode of operation | 200S, Dual200S, 300S, 600S | [auto, sleep] |
-| **nightLightMode** | String | The night light mode | 200S, Dual200S, 300S | [on, dim, off] |
-| **humiditySetpoint** | Number:Dimensionless | Humidity % set point to reach | 200S, Dual200S, 300S, 600S | [30...80] |
-| warmEnabled | Switch | Indicator for warm mist mode | 600S | |
+| Channel | Type | Description | Model's Supported | Controllable Values |
+|----------------------------|----------------------|---------------------------------------------------------------|---------------------------------------|---------------------|
+| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 200S, Dual200S, 300S, 600S, OasisMist | [ON, OFF] |
+| **display** | Switch | Whether the display is enabled (display is shown) | 200S, Dual200S, 300S, 600S, OasisMist | [ON, OFF] |
+| waterLacking | Switch | Indicator whether the unit is lacking water | 200S, Dual200S, 300S, 600S, OasisMist | |
+| humidityHigh | Switch | Indicator for high humidity | 200S, Dual200S, 300S, 600S, OasisMist | |
+| waterTankLifted | Switch | Indicator for whether the water tank is removed | 200S, Dual200S, 300S, 600S, OasisMist | |
+| **stopAtHumiditySetpoint** | Switch | Whether the unit is set to stop when the set point is reached | 200S, Dual200S, 300S, 600S, OasisMist | [ON, OFF] |
+| humidity | Number:Dimensionless | Indicator for the currently measured humidity % level | 200S, Dual200S, 300S, 600S, OasisMist | |
+| **mistLevel** | Number:Dimensionless | The current mist level set | 300S | [1...2] |
+| **mistLevel** | Number:Dimensionless | The current mist level set | 200S, Dual200S, 600S, OasisMist | [1...3] |
+| **humidifierMode** | String | The current mode of operation | 200S, Dual200S, 300S, 600S, OasisMist | [auto, sleep] |
+| **nightLightMode** | String | The night light mode | 200S, Dual200S, 300S | [on, dim, off] |
+| **humiditySetpoint** | Number:Dimensionless | Humidity % set point to reach | 200S, Dual200S, 300S, 600S, OasisMist | [30...80] |
+| warmEnabled | Switch | Indicator for warm mist mode | 600S, OasisMist | |
## Full Example
#### Air Purifiers Core 200S/300S/400S Models & Air Humidifier Classic300S/600S Models
-```
+```java
Bridge vesync:bridge:vesyncServers [username="<USERNAME>", password="<PASSWORD>", airPurifierPollInterval=60] {
airPurifier loungeAirFilter [deviceName="<DEVICE NAME FROM APP>"]
airPurifier bedroomAirFilter [deviceName="<DEVICE NAME FROM APP>"]
#### Air Purifier Core 400S / 600S Model
-```
-Switch LoungeAPPower "Lounge Air Purifier Power" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:enabled" }
-Switch LoungeAPDisplay "Lounge Air Purifier Display" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:display" }
+```java
+Switch LoungeAPPower "Lounge Air Purifier Power" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:enabled" }
+Switch LoungeAPDisplay "Lounge Air Purifier Display" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:display" }
Switch LoungeAPControlsLock "Lounge Air Purifier Controls Locked" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:childLock" }
Number:Dimensionless LoungeAPFilterRemainingUse "Lounge Air Purifier Filter Remaining [%.0f %unit%]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:filterLifePercentage" }
String LoungeAPMode "Lounge Air Purifier Mode [%s]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:fanMode" }
Number:Dimensionless LoungeAPManualFanSpeed "Lounge Air Purifier Manual Fan Speed" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:manualFanSpeed" }
-Number:Density LoungeAPAirQuality "Lounge Air Purifier Air Quality [%.0f% %unit%]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:airQualityPM25" }
-Number LoungeAPErrorCode "Lounge Air Purifier Error Code" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:errorCode" }
-String LoungeAPAutoMode "Lounge Air Purifier Auto Mode" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoMode" }
-Number LoungeAPAutoRoomSize "Lounge Air Purifier Auto Room Size [%.0f% sqft]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoRoomSize" }
-Number:Time LoungeAPTimerLeft "Lounge Air Purifier Timer Left [%1$Tp]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:timerRemain" }
+Number:Density LoungeAPAirQuality "Lounge Air Purifier Air Quality [%.0f% %unit%]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:airQualityPM25" }
+Number LoungeAPErrorCode "Lounge Air Purifier Error Code" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:errorCode" }
+String LoungeAPAutoMode "Lounge Air Purifier Auto Mode" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoMode" }
+Number LoungeAPAutoRoomSize "Lounge Air Purifier Auto Room Size [%.0f% sqft]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoRoomSize" }
+Number:Time LoungeAPTimerLeft "Lounge Air Purifier Timer Left [%1$Tp]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:timerRemain" }
DateTime LoungeAPTimerExpiry "Lounge Air Purifier Timer Expiry [%1$tA %1$tI:%1$tM %1$Tp]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:timerExpiry" }
Number LoungeAPSchedulesCount "Lounge Air Purifier Schedules Count" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:schedulesCount" }
```
#### Air Purifier Core 200S/300S Model
-```
-Switch LoungeAPPower "Lounge Air Purifier Power" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:enabled" }
-Switch LoungeAPDisplay "Lounge Air Purifier Display" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:display" }
+```java
+Switch LoungeAPPower "Lounge Air Purifier Power" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:enabled" }
+Switch LoungeAPDisplay "Lounge Air Purifier Display" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:display" }
String LoungeAPNightLightMode "Lounge Air Purifier Night Light Mode" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:nightLightMode" }
Switch LoungeAPControlsLock "Lounge Air Purifier Controls Locked" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:childLock" }
Number:Dimensionless LoungeAPFilterRemainingUse "Lounge Air Purifier Filter Remaining [%.0f %unit%]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:filterLifePercentage" }
String LoungeAPMode "Lounge Air Purifier Mode [%s]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:fanMode" }
Number:Dimensionless LoungeAPManualFanSpeed "Lounge Air Purifier Manual Fan Speed" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:manualFanSpeed" }
-Number:Density LoungeAPAirQuality "Lounge Air Purifier Air Quality [%.0f%]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:airQuality" }
-Number LoungeAPErrorCode "Lounge Air Purifier Error Code" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:errorCode" }
-String LoungeAPAutoMode "Lounge Air Purifier Auto Mode" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoMode" }
-Number LoungeAPAutoRoomSize "Lounge Air Purifier Auto Room Size [%.0f% sqft]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoRoomSize" }
-Number:Time LoungeAPTimerLeft "Lounge Air Purifier Timer Left [%1$Tp]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:timerRemain" }
+Number:Density LoungeAPAirQuality "Lounge Air Purifier Air Quality [%.0f%]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:airQuality" }
+Number LoungeAPErrorCode "Lounge Air Purifier Error Code" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:errorCode" }
+String LoungeAPAutoMode "Lounge Air Purifier Auto Mode" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoMode" }
+Number LoungeAPAutoRoomSize "Lounge Air Purifier Auto Room Size [%.0f% sqft]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoRoomSize" }
+Number:Time LoungeAPTimerLeft "Lounge Air Purifier Timer Left [%1$Tp]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:timerRemain" }
DateTime LoungeAPTimerExpiry "Lounge Air Purifier Timer Expiry [%1$tA %1$tI:%1$tM %1$Tp]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:timerExpiry" }
Number LoungeAPSchedulesCount "Lounge Air Purifier Schedules Count" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:schedulesCount" }
```
#### Air Humidifier Classic 200S / Dual 200S Model
-```
+```java
Switch LoungeAHPower "Lounge Air Humidifier Power" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:enabled" }
Switch LoungeAHDisplay "Lounge Air Humidifier Display" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:display" }
String LoungeAHMode "Lounge Air Humidifier Mode" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:humidifierMode" }
#### Air Humidifier Classic 300S Model
-```
+```java
Switch LoungeAHPower "Lounge Air Humidifier Power" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:enabled" }
Switch LoungeAHDisplay "Lounge Air Humidifier Display" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:display" }
String LoungeAHNightLightMode "Lounge Air Humidifier Night Light Mode" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:nightLightMode }
#### Air Humidifier 600S Model
+```java
+Switch LoungeAHPower "Lounge Air Humidifier Power" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:enabled" }
+Switch LoungeAHDisplay "Lounge Air Humidifier Display" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:display" }
+String LoungeAHMode "Lounge Air Humidifier Mode" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:humidifierMode" }
+Switch LoungeAHWaterLacking "Lounge Air Humidifier Water Lacking" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:waterLacking" }
+Switch LoungeAHHighHumidity "Lounge Air Humidifier High Humidity" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:humidityHigh" }
+Switch LoungeAHWaterTankRemoved "Lounge Air Humidifier Water Tank Removed" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:waterTankLifted" }
+Number:Dimensionless LoungeAHHumidity "Lounge Air Humidifier Measured Humidity [%.0f %unit%]" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:humidity" }
+Switch LoungeAHTargetStop "Lounge Air Humidifier Stop at target" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:stopAtTargetLevel" }
+Number:Dimensionless LoungeAHTarget "Lounge Air Humidifier Target Humidity [%.0f %unit%]" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:humiditySetpoint" }
+Number:Dimensionless LoungeAHMistLevel "Lounge Air Humidifier Mist Level" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:mistLevel" }
```
+
+#### Air Humidifier Oasis Mist Smart Model
+
+```java
Switch LoungeAHPower "Lounge Air Humidifier Power" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:enabled" }
Switch LoungeAHDisplay "Lounge Air Humidifier Display" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:display" }
String LoungeAHMode "Lounge Air Humidifier Mode" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:humidifierMode" }
#### Air Purifier Core 400S / 600S Model
-```
+```perl
Frame {
Switch item=LoungeAPPower label="Power"
Text item=LoungeAPFilterRemainingUse label="Filter Remaining"
#### Air Purifier Core 200S/300S Model
-```
+```perl
Frame {
Switch item=LoungeAPPower label="Power"
Text item=LoungeAPFilterRemainingUse label="Filter Remaining"
#### Air Humidifier Classic 200S / Dual 200S Model
-```
+```perl
Frame {
Switch item=LoungeAHPower
Switch item=LoungeAHDisplay
#### Air Humidifier Classic 300S Model
-```
+```perl
Frame {
Switch item=LoungeAHPower
Switch item=LoungeAHDisplay
#### Air Humidifier 600S Model
+```perl
+Frame {
+ Switch item=LoungeAHPower
+ Switch item=LoungeAHDisplay
+ Switch item=LoungeAHMode label="Mode" mappings=[auto="Auto", sleep="Sleeping"] icon="settings"
+ Text icon="none" item=LoungeAHWaterLacking
+ Text icon="none" item=LoungeAHHighHumidity
+ Text icon="none" item=LoungeAHWaterTankRemoved
+ Text icon="none" item=LoungeAHHumidity
+ Switch item=LoungeAHTargetStop
+ Slider item=LoungeAHTarget minValue=30 maxValue=80
+ Slider item=LoungeAHMistLevel minValue=1 maxValue=3
+}
```
+
+#### Air Humidifier Oasis Mist Smart Model
+
+```perl
Frame {
Switch item=LoungeAHPower
Switch item=LoungeAHDisplay
public static final String DEVICE_PROP_DEVICE_NAME = "Device Name";
public static final String DEVICE_PROP_DEVICE_TYPE = "Device Type";
public static final String DEVICE_PROP_DEVICE_MAC_ID = "MAC Id";
+ public static final String DEVICE_PROP_DEVICE_FAMILY = "Device Family";
public static final String DEVICE_PROP_DEVICE_UUID = "UUID";
// Property name for config constants
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.vesync.internal.handlers.VeSyncBaseDeviceHandler;
import org.openhab.binding.vesync.internal.handlers.VeSyncBridgeHandler;
+import org.openhab.binding.vesync.internal.handlers.VeSyncDeviceAirHumidifierHandler;
+import org.openhab.binding.vesync.internal.handlers.VeSyncDeviceAirPurifierHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
final String deviceUUID = apMeta.getUuid();
properties.put(DEVICE_PROP_DEVICE_NAME, apMeta.getDeviceName());
properties.put(DEVICE_PROP_DEVICE_TYPE, apMeta.getDeviceType());
+ properties.put(DEVICE_PROP_DEVICE_FAMILY,
+ VeSyncBaseDeviceHandler.getDeviceFamilyMetadata(apMeta.getDeviceType(),
+ VeSyncDeviceAirHumidifierHandler.DEV_TYPE_FAMILY_AIR_HUMIDIFIER,
+ VeSyncDeviceAirHumidifierHandler.SUPPORTED_MODEL_FAMILIES));
properties.put(DEVICE_PROP_DEVICE_MAC_ID, apMeta.getMacId());
properties.put(DEVICE_PROP_DEVICE_UUID, deviceUUID);
properties.put(DEVICE_PROP_CONFIG_DEVICE_MAC, apMeta.getMacId());
final String deviceUUID = apMeta.getUuid();
properties.put(DEVICE_PROP_DEVICE_NAME, apMeta.getDeviceName());
properties.put(DEVICE_PROP_DEVICE_TYPE, apMeta.getDeviceType());
+ properties.put(DEVICE_PROP_DEVICE_FAMILY,
+ VeSyncBaseDeviceHandler.getDeviceFamilyMetadata(apMeta.getDeviceType(),
+ VeSyncDeviceAirPurifierHandler.DEV_TYPE_FAMILY_AIR_PURIFIER,
+ VeSyncDeviceAirPurifierHandler.SUPPORTED_MODEL_FAMILIES));
properties.put(DEVICE_PROP_DEVICE_MAC_ID, apMeta.getMacId());
properties.put(DEVICE_PROP_DEVICE_UUID, deviceUUID);
properties.put(DEVICE_PROP_CONFIG_DEVICE_MAC, apMeta.getMacId());
import java.time.Duration;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import javax.validation.constraints.NotNull;
@NonNullByDefault
public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler {
+ public static final String DEV_FAMILY_UNKNOWN = "UNKNOWN";
+
+ public static final VeSyncDeviceMetadata UNKNOWN = new VeSyncDeviceMetadata(DEV_FAMILY_UNKNOWN,
+ Collections.emptyList(), Collections.emptyList());
+
private final Logger logger = LoggerFactory.getLogger(VeSyncBaseDeviceHandler.class);
private static final String MARKER_INVALID_DEVICE_KEY = "---INVALID---";
newProps.put(DEVICE_PROP_DEVICE_MAC_ID, metadata.getMacId());
newProps.put(DEVICE_PROP_DEVICE_NAME, metadata.getDeviceName());
newProps.put(DEVICE_PROP_DEVICE_TYPE, metadata.getDeviceType());
+ newProps.put(DEVICE_PROP_DEVICE_FAMILY,
+ getDeviceFamilyMetadata(metadata.getDeviceType()).getDeviceFamilyName());
newProps.put(DEVICE_PROP_DEVICE_UUID, metadata.getUuid());
return newProps;
}
/**
* Send a BypassV2 command to the device. The body of the response is returned.
- *
+ *
* @param method - the V2 bypass method
* @param payload - The payload to send in within the V2 bypass command
* @return - The body of the response, or EMPTY_STRING if the command could not be issued.
public void updateBridgeBasedPolls(VeSyncBridgeConfiguration config) {
}
+ protected boolean isDeviceSupported() {
+ final String deviceType = getThing().getProperties().get(DEVICE_PROP_DEVICE_TYPE);
+ return !getDeviceFamilyMetadata(deviceType).getDeviceFamilyName().equals(DEV_FAMILY_UNKNOWN);
+ }
+
+ /**
+ * Subclasses should return the protocol prefix for the device type being modelled.
+ * E.g. LUH = The Humidifier Devices; LAP = The Air Purifiers;
+ * if irrelevant return a string that will not be used in the protocol e.g. __??__
+ *
+ * @return - The device type prefix for the device being modelled. E.g. LAP or LUH
+ */
+ public abstract String getDeviceFamilyProtocolPrefix();
+
/**
- * Subclasses should implement this method, and return true if the device is a model it can support
- * interoperability with. If it cannot be determind to be a mode
+ * Subclasses should return list of VeSyncDeviceMetadata definitions that define the
+ * supported devices by their implementation.
*
- * @return - true if the device is supported, false if the device isn't. E.g. Unknown model id in meta-data would
- * return false.
+ * @return - List of VeSyncDeviceMetadata definitions, that defines groups of devices which
+ * are operationally the same device.
*/
- protected abstract boolean isDeviceSupported();
+ public abstract List<VeSyncDeviceMetadata> getSupportedDeviceMetadata();
+
+ public static VeSyncDeviceMetadata getDeviceFamilyMetadata(final @Nullable String deviceType,
+ final String deviceProtocolPrefix, final List<VeSyncDeviceMetadata> metadata) {
+ if (deviceType == null) {
+ return UNKNOWN;
+ }
+ final String[] idParts = deviceType.split("-");
+ if (idParts.length == 3) {
+ if (!deviceProtocolPrefix.equals(idParts[0])) {
+ return UNKNOWN;
+ }
+ }
+ List<VeSyncDeviceMetadata> foundMatch = metadata.stream()
+ .filter(x -> x.deviceTypeIdMatches(deviceType, idParts)).collect(Collectors.toList());
+ if (foundMatch.size() == 1) {
+ return foundMatch.get(0);
+ } else {
+ return UNKNOWN;
+ }
+ }
+
+ public VeSyncDeviceMetadata getDeviceFamilyMetadata(final @Nullable String deviceType) {
+ return getDeviceFamilyMetadata(deviceType, getDeviceFamilyProtocolPrefix(), getSupportedDeviceMetadata());
+ }
}
}
public java.util.stream.Stream<@NotNull VeSyncManagedDeviceBase> getAirPurifiersMetadata() {
- return api.getMacLookupMap().values().stream()
- .filter(x -> VeSyncDeviceAirPurifierHandler.SUPPORTED_DEVICE_TYPES.contains(x.deviceType));
+ return api.getMacLookupMap().values().stream().filter(x -> !VeSyncBaseDeviceHandler
+ .getDeviceFamilyMetadata(x.getDeviceType(), VeSyncDeviceAirPurifierHandler.DEV_TYPE_FAMILY_AIR_PURIFIER,
+ VeSyncDeviceAirPurifierHandler.SUPPORTED_MODEL_FAMILIES)
+ .equals(VeSyncBaseDeviceHandler.UNKNOWN));
}
public java.util.stream.Stream<@NotNull VeSyncManagedDeviceBase> getAirHumidifiersMetadata() {
return api.getMacLookupMap().values().stream()
- .filter(x -> VeSyncDeviceAirHumidifierHandler.SUPPORTED_DEVICE_TYPES.contains(x.deviceType));
+ .filter(x -> !VeSyncBaseDeviceHandler
+ .getDeviceFamilyMetadata(x.getDeviceType(),
+ VeSyncDeviceAirHumidifierHandler.DEV_TYPE_FAMILY_AIR_HUMIDIFIER,
+ VeSyncDeviceAirHumidifierHandler.SUPPORTED_MODEL_FAMILIES)
+ .equals(VeSyncBaseDeviceHandler.UNKNOWN));
}
protected void updateThings() {
import static org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolConstants.*;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@NonNullByDefault
public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
+ public static final String DEV_TYPE_FAMILY_AIR_HUMIDIFIER = "LUH";
+
public static final int DEFAULT_AIR_PURIFIER_POLL_RATE = 120;
- // "Device Type" values
- public static final String DEV_TYPE_DUAL_200S = "Dual200S";
- public static final String DEV_TYPE_CLASSIC_200S = "Classic200S";
- public static final String DEV_TYPE_CORE_301S = "LUH-D301S-WEU";
- public static final String DEV_TYPE_CLASSIC_300S = "Classic300S";
- public static final String DEV_TYPE_600S = "LUH-A602S-WUS";
- public static final String DEV_TYPE_600S_EU = "LUH-A602S-WEU";
+
+ public static final String DEV_FAMILY_CLASSIC_200S = "Classic 200S";
+ public static final String DEV_FAMILY_CLASSIC_300S = "Classic 300S";
+ public static final String DEV_FAMILY_DUAL_200S = "Dual 200S";
+ 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"));
+
+ public static final VeSyncDeviceMetadata CLASSIC300S = new VeSyncDeviceMetadata(DEV_FAMILY_CLASSIC_300S,
+ Arrays.asList("A601S"), List.of("Classic300S"));
+
+ public static final VeSyncDeviceMetadata DUAL200S = new VeSyncDeviceMetadata(DEV_FAMILY_DUAL_200S,
+ Arrays.asList("D301S"), List.of("Dual200S"));
+
+ public static final VeSyncDeviceMetadata LV600S = new VeSyncDeviceMetadata(DEV_FAMILY_600S, Arrays.asList("A602S"),
+ Collections.emptyList());
+
+ public static final VeSyncDeviceMetadata OASIS_MIST = new VeSyncDeviceMetadata(DEV_FAMILY_OASIS_MIST,
+ Arrays.asList("O451S"), 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);
- public static final List<String> SUPPORTED_DEVICE_TYPES = List.of(DEV_TYPE_DUAL_200S, DEV_TYPE_CLASSIC_200S,
- DEV_TYPE_CLASSIC_300S, DEV_TYPE_CORE_301S, DEV_TYPE_600S, DEV_TYPE_600S_EU);
-
private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirHumidifierHandler.class);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_AIR_HUMIDIFIER);
@Override
protected String[] getChannelsToRemove() {
String[] toRemove = new String[] {};
- final String deviceType = getThing().getProperties().get(DEVICE_PROP_DEVICE_TYPE);
- if (deviceType != null) {
- switch (deviceType) {
- case DEV_TYPE_CLASSIC_300S:
+ final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY);
+ if (deviceFamily != null) {
+ switch (deviceFamily) {
+ case DEV_FAMILY_CLASSIC_300S:
toRemove = new String[] { DEVICE_CHANNEL_WARM_ENABLED, DEVICE_CHANNEL_WARM_LEVEL };
break;
- case DEV_TYPE_DUAL_200S:
- case DEV_TYPE_CLASSIC_200S:
- case DEV_TYPE_CORE_301S:
+ case DEV_FAMILY_DUAL_200S:
+ case DEV_FAMILY_CLASSIC_200S:
toRemove = new String[] { DEVICE_CHANNEL_WARM_ENABLED, DEVICE_CHANNEL_WARM_LEVEL,
DEVICE_CHANNEL_AF_NIGHT_LIGHT };
break;
- case DEV_TYPE_600S:
- case DEV_TYPE_600S_EU:
+ case DEV_FAMILY_OASIS_MIST:
toRemove = new String[] { DEVICE_CHANNEL_AF_NIGHT_LIGHT };
break;
}
}
@Override
- protected boolean isDeviceSupported() {
- final String deviceType = getThing().getProperties().get(DEVICE_PROP_DEVICE_TYPE);
- if (deviceType == null) {
- return false;
- }
- return SUPPORTED_DEVICE_TYPES.contains(deviceType);
+ public String getDeviceFamilyProtocolPrefix() {
+ return DEV_TYPE_FAMILY_AIR_HUMIDIFIER;
+ }
+
+ @Override
+ public List<VeSyncDeviceMetadata> getSupportedDeviceMetadata() {
+ return SUPPORTED_MODEL_FAMILIES;
}
@Override
public void handleCommand(final ChannelUID channelUID, final Command command) {
- final String deviceType = getThing().getProperties().get(DEVICE_PROP_DEVICE_TYPE);
- if (deviceType == null) {
+ final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY);
+ if (deviceFamily == null) {
return;
}
int targetMistLevel = ((QuantityType<?>) command).intValue();
// 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_TYPE_CORE_301S.equals(deviceType)) {
+ 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;
new VeSyncRequestManagedDeviceBypassV2.SetMode(targetMode));
break;
case DEVICE_CHANNEL_AF_NIGHT_LIGHT:
- if (!DEV_TYPE_CLASSIC_300S.equals(deviceType) && !DEV_TYPE_CORE_301S.equals(deviceType)) {
- logger.warn("Humidifier night light is not valid for your device ({}})", deviceType);
+ 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)) {
return;
}
- final String deviceType = getThing().getProperties().get(DEVICE_PROP_DEVICE_TYPE);
+ final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY);
updateState(DEVICE_CHANNEL_ENABLED, OnOffType.from(humidifierStatus.result.result.enabled));
updateState(DEVICE_CHANNEL_DISPLAY_ENABLED, OnOffType.from(humidifierStatus.result.result.display));
updateState(DEVICE_CHANNEL_HUMIDIFIER_MODE, new StringType(humidifierStatus.result.result.mode));
// Only the 300S supports nightlight currently of tested devices.
- if (DEV_TYPE_CLASSIC_300S.equals(deviceType) || DEV_TYPE_CORE_301S.equals(deviceType)) {
+ if (DEV_FAMILY_CLASSIC_300S.equals(deviceFamily) || DEV_FAMILY_600S.equals(deviceFamily)) {
// Map the numeric that only applies to the same modes as the Air Filter 300S series.
if (humidifierStatus.result.result.nightLightBrightness == 0) {
updateState(DEVICE_CHANNEL_AF_NIGHT_LIGHT, new StringType(MODE_OFF));
} else {
updateState(DEVICE_CHANNEL_AF_NIGHT_LIGHT, new StringType(MODE_DIM));
}
- } else if (DEV_TYPE_600S.equals(deviceType) || DEV_TYPE_600S_EU.equals(deviceType)) {
+ } else if (DEV_FAMILY_600S.equals(deviceFamily) || DEV_FAMILY_OASIS_MIST.equals(deviceFamily)) {
updateState(DEVICE_CHANNEL_WARM_ENABLED, OnOffType.from(humidifierStatus.result.result.warnEnabled));
updateState(DEVICE_CHANNEL_WARM_LEVEL, new DecimalType(humidifierStatus.result.result.warmLevel));
}
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@NonNullByDefault
public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
+ public static final String DEV_TYPE_FAMILY_AIR_PURIFIER = "LAP";
+
public static final int DEFAULT_AIR_PURIFIER_POLL_RATE = 120;
- // "Device Type" values
- public static final String DEV_TYPE_CORE_600S = "LAP-C601S-WUS";
- public static final String DEV_TYPE_CORE_400S = "Core400S";
- public static final String DEV_TYPE_CORE_300S = "Core300S";
- public static final String DEV_TYPE_CORE_201S = "LAP-C201S-AUSR";
- public static final String DEV_TYPE_CORE_200S = "Core200S";
- public static final String DEV_TYPE_LV_PUR131S = "LV-PUR131S";
- public static final List<String> SUPPORTED_DEVICE_TYPES = Arrays.asList(DEV_TYPE_CORE_600S, DEV_TYPE_CORE_400S,
- DEV_TYPE_CORE_300S, DEV_TYPE_CORE_201S, DEV_TYPE_CORE_200S, DEV_TYPE_LV_PUR131S);
+
+ public static final String DEV_FAMILY_CORE_200S = "200S";
+ public static final String DEV_FAMILY_CORE_300S = "300S";
+ public static final String DEV_FAMILY_CORE_400S = "400S";
+ public static final String DEV_FAMILY_CORE_600S = "600S";
+
+ public static final String DEV_FAMILY_PUR_131S = "131S";
+
+ public static final VeSyncDeviceMetadata CORE200S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_200S,
+ Arrays.asList("C201S", "C202S"), List.of("Core200S"));
+
+ public static final VeSyncDeviceMetadata CORE300S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_300S,
+ List.of("C301S", "C302S"), List.of("Core300S"));
+
+ public static final VeSyncDeviceMetadata CORE400S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_400S, List.of("C401S"),
+ List.of("Core400S"));
+
+ public static final VeSyncDeviceMetadata CORE600S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_600S, List.of("C601S"),
+ List.of("Core600S"));
+
+ public static final VeSyncDeviceMetadata PUR131S = new VeSyncDeviceMetadata(DEV_FAMILY_PUR_131S,
+ Collections.emptyList(), Arrays.asList("LV-PUR131S", "LV-RH131S"));
+
+ public static final List<VeSyncDeviceMetadata> SUPPORTED_MODEL_FAMILIES = Arrays.asList(CORE600S, CORE400S,
+ CORE300S, CORE200S, PUR131S);
private static final List<String> CORE_400S600S_FAN_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP);
private static final List<String> CORE_200S300S_FAN_MODES = Arrays.asList(MODE_MANUAL, MODE_SLEEP);
@Override
protected @NotNull String[] getChannelsToRemove() {
+ final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY);
String[] toRemove = new String[] {};
- final String deviceType = getThing().getProperties().get(DEVICE_PROP_DEVICE_TYPE);
- if (deviceType != null) {
- switch (deviceType) {
- case DEV_TYPE_CORE_600S:
- case DEV_TYPE_CORE_400S:
+ if (deviceFamily != null) {
+ switch (deviceFamily) {
+ case DEV_FAMILY_CORE_600S:
+ case DEV_FAMILY_CORE_400S:
toRemove = new String[] { DEVICE_CHANNEL_AF_NIGHT_LIGHT };
break;
- case DEV_TYPE_LV_PUR131S:
+ case DEV_FAMILY_PUR_131S:
toRemove = new String[] { DEVICE_CHANNEL_AF_NIGHT_LIGHT, DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE,
DEVICE_CHANNEL_AF_CONFIG_AUTO_MODE_PREF, DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME,
DEVICE_CHANNEL_AIR_FILTER_LIFE_PERCENTAGE_REMAINING, DEVICE_CHANNEL_AIRQUALITY_PM25,
}
@Override
- protected boolean isDeviceSupported() {
- final String deviceType = getThing().getProperties().get(DEVICE_PROP_DEVICE_TYPE);
- if (deviceType == null) {
- return false;
- }
- return SUPPORTED_DEVICE_TYPES.contains(deviceType);
+ public String getDeviceFamilyProtocolPrefix() {
+ return DEV_TYPE_FAMILY_AIR_PURIFIER;
+ }
+
+ @Override
+ public List<VeSyncDeviceMetadata> getSupportedDeviceMetadata() {
+ return SUPPORTED_MODEL_FAMILIES;
}
@Override
public void handleCommand(final ChannelUID channelUID, final Command command) {
- final String deviceType = getThing().getProperties().get(DEVICE_PROP_DEVICE_TYPE);
- if (deviceType == null) {
+ final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY);
+ if (deviceFamily == null) {
return;
}
switch (channelUID.getId()) {
case DEVICE_CHANNEL_FAN_MODE_ENABLED:
final String targetFanMode = command.toString().toLowerCase();
- switch (deviceType) {
- case DEV_TYPE_CORE_600S:
- case DEV_TYPE_CORE_400S:
+ switch (deviceFamily) {
+ case DEV_FAMILY_CORE_600S:
+ case DEV_FAMILY_CORE_400S:
if (!CORE_400S600S_FAN_MODES.contains(targetFanMode)) {
logger.warn(
"Fan mode command for \"{}\" is not valid in the (Core400S) API possible options {}",
return;
}
break;
- case DEV_TYPE_CORE_200S:
- case DEV_TYPE_CORE_201S:
- case DEV_TYPE_CORE_300S:
+ case DEV_FAMILY_CORE_200S:
+ case DEV_FAMILY_CORE_300S:
if (!CORE_200S300S_FAN_MODES.contains(targetFanMode)) {
logger.warn(
"Fan mode command for \"{}\" is not valid in the (Core200S/Core300S) API possible options {}",
break;
case DEVICE_CHANNEL_AF_NIGHT_LIGHT:
final String targetNightLightMode = command.toString().toLowerCase();
- switch (deviceType) {
- case DEV_TYPE_CORE_600S:
- case DEV_TYPE_CORE_400S:
+ switch (deviceFamily) {
+ case DEV_FAMILY_CORE_600S:
+ case DEV_FAMILY_CORE_400S:
logger.warn("Core400S API does not support night light");
return;
- case DEV_TYPE_CORE_200S:
- case DEV_TYPE_CORE_201S:
- case DEV_TYPE_CORE_300S:
+ case DEV_FAMILY_CORE_200S:
+ case DEV_FAMILY_CORE_300S:
if (!CORE_200S300S_NIGHT_LIGHT_MODES.contains(targetNightLightMode)) {
logger.warn(
"Night light mode command for \"{}\" is not valid in the (Core200S/Core300S) API possible options {}",
requestedLevel = 1;
}
- switch (deviceType) {
- case DEV_TYPE_CORE_600S:
- case DEV_TYPE_CORE_400S:
+ switch (deviceFamily) {
+ case DEV_FAMILY_CORE_600S:
+ case DEV_FAMILY_CORE_400S:
if (requestedLevel > 4) {
logger.warn(
"Fan speed command greater than 4 - adjusting to 4 as the valid (Core400S) API value");
requestedLevel = 4;
}
break;
- case DEV_TYPE_CORE_200S:
- case DEV_TYPE_CORE_201S:
- case DEV_TYPE_CORE_300S:
+ case DEV_FAMILY_CORE_200S:
+ case DEV_FAMILY_CORE_300S:
if (requestedLevel > 3) {
logger.warn(
"Fan speed command greater than 3 - adjusting to 3 as the valid (Core200S/Core300S) API value");
@Override
protected void pollForDeviceData(final ExpiringCache<String> cachedResponse) {
- final String deviceType = getThing().getProperties().get(DEVICE_PROP_DEVICE_TYPE);
- if (deviceType == null) {
+ final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY);
+ if (deviceFamily == null) {
return;
}
- switch (deviceType) {
- case DEV_TYPE_CORE_600S:
- case DEV_TYPE_CORE_400S:
- case DEV_TYPE_CORE_300S:
- case DEV_TYPE_CORE_201S:
- case DEV_TYPE_CORE_200S:
+ switch (deviceFamily) {
+ case DEV_FAMILY_CORE_600S:
+ case DEV_FAMILY_CORE_400S:
+ case DEV_FAMILY_CORE_300S:
+ case DEV_FAMILY_CORE_200S:
processV2BypassPoll(cachedResponse);
break;
- case DEV_TYPE_LV_PUR131S:
+ case DEV_FAMILY_PUR_131S:
processV1AirPurifierPoll(cachedResponse);
break;
}
--- /dev/null
+/**
+ * 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 java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link VeSyncDeviceMetadata} class contains the definition for the identification of multiple device types,
+ * to a single family of devices.
+ *
+ * New Device Type Ids are formatted as [DeviceType][-][Device Generation][-][Device Region]
+ *
+ * @author David Goodyear - Initial contribution
+ */
+@NonNullByDefault
+public class VeSyncDeviceMetadata {
+
+ public VeSyncDeviceMetadata(final String deviceFamilyName, final List<String> deviceGenerations,
+ final List<String> nonStandardIds) {
+ this.deviceFamilyName = deviceFamilyName;
+ this.deviceGenerations = deviceGenerations;
+ this.nonStandardIds = nonStandardIds;
+ }
+
+ /**
+ * The name of the family the set of ID's represents.
+ *
+ */
+ final public String deviceFamilyName;
+
+ /**
+ * The version id, that represents the specific model of the device
+ */
+ final public List<String> deviceGenerations;
+
+ /**
+ * Device Types not following the standard 3 segment convention
+ */
+ final public List<String> nonStandardIds;
+
+ public boolean deviceTypeIdMatches(final String deviceType, final String[] deviceTypeSegments) {
+ if (nonStandardIds.contains(deviceType)) {
+ return true;
+ }
+ if (deviceTypeSegments.length == 3) {
+ return deviceGenerations.contains(deviceTypeSegments[1]);
+ }
+ return false;
+ }
+
+ public String getDeviceFamilyName() {
+ return deviceFamilyName;
+ }
+}
<property name="Device Name"/>
<property name="Device Type"/>
<property name="MAC Id"/>
+ <property name="Device Family"/>
</properties>
<representation-property>macId</representation-property>
<property name="Device Name"/>
<property name="Device Type"/>
<property name="MAC Id"/>
+ <property name="Device Family"/>
</properties>
<representation-property>macId</representation-property>