From 1e0f27d5ef36273ce5f8851dffab3bbef1d45368 Mon Sep 17 00:00:00 2001 From: dag81 Date: Mon, 27 Mar 2023 07:05:09 +0100 Subject: [PATCH] [veSync] Improve recognition and device support (#14354) * [veSync] Device Identification Updates * [veSync] Alignment to device setups in pyvesync. * [veSync] Addition of new device code for C302S Signed-off-by: David Goodyear --- bundles/org.openhab.binding.vesync/README.md | 110 +++++++++++------ .../vesync/internal/VeSyncConstants.java | 1 + .../discovery/VeSyncDiscoveryService.java | 11 ++ .../handlers/VeSyncBaseDeviceHandler.java | 59 ++++++++- .../handlers/VeSyncBridgeHandler.java | 12 +- .../VeSyncDeviceAirHumidifierHandler.java | 82 ++++++++----- .../VeSyncDeviceAirPurifierHandler.java | 116 ++++++++++-------- .../handlers/VeSyncDeviceMetadata.java | 66 ++++++++++ .../resources/OH-INF/thing/thing-types.xml | 2 + 9 files changed, 328 insertions(+), 131 deletions(-) create mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceMetadata.java diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index a1890aecb..723512307 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -5,7 +5,7 @@ Its current support is for the Air Purifiers & Humidifer's branded as Levoit whi ### 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 @@ -92,21 +92,21 @@ Channel names in **bold** are read/write, everything else is read-only ### 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 @@ -115,7 +115,7 @@ Channel names in **bold** are read/write, everything else is read-only #### Air Purifiers Core 200S/300S/400S Models & Air Humidifier Classic300S/600S Models -``` +```java Bridge vesync:bridge:vesyncServers [username="", password="", airPurifierPollInterval=60] { airPurifier loungeAirFilter [deviceName=""] airPurifier bedroomAirFilter [deviceName=""] @@ -127,44 +127,44 @@ Bridge vesync:bridge:vesyncServers [username="", password="" #### 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" } @@ -179,7 +179,7 @@ Number:Dimensionless LoungeAHMistLevel "Lounge Air Humidifier Mist Level #### 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 } @@ -195,7 +195,22 @@ Number:Dimensionless LoungeAHMistLevel "Lounge Air Humidifier Mist Level #### 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" } @@ -212,7 +227,7 @@ Number:Dimensionless LoungeAHMistLevel "Lounge Air Humidifier Mist Level #### Air Purifier Core 400S / 600S Model -``` +```perl Frame { Switch item=LoungeAPPower label="Power" Text item=LoungeAPFilterRemainingUse label="Filter Remaining" @@ -228,7 +243,7 @@ Frame { #### Air Purifier Core 200S/300S Model -``` +```perl Frame { Switch item=LoungeAPPower label="Power" Text item=LoungeAPFilterRemainingUse label="Filter Remaining" @@ -245,7 +260,7 @@ Frame { #### Air Humidifier Classic 200S / Dual 200S Model -``` +```perl Frame { Switch item=LoungeAHPower Switch item=LoungeAHDisplay @@ -262,7 +277,7 @@ Frame { #### Air Humidifier Classic 300S Model -``` +```perl Frame { Switch item=LoungeAHPower Switch item=LoungeAHDisplay @@ -280,7 +295,24 @@ Frame { #### 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 diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncConstants.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncConstants.java index a4713c1e9..33f5972f7 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncConstants.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncConstants.java @@ -83,6 +83,7 @@ public class VeSyncConstants { 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 diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/discovery/VeSyncDiscoveryService.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/discovery/VeSyncDiscoveryService.java index ac9bb1116..ec0e0c92e 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/discovery/VeSyncDiscoveryService.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/discovery/VeSyncDiscoveryService.java @@ -21,7 +21,10 @@ import java.util.Set; 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; @@ -117,6 +120,10 @@ public class VeSyncDiscoveryService extends AbstractDiscoveryService 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()); @@ -131,6 +138,10 @@ public class VeSyncDiscoveryService extends AbstractDiscoveryService 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()); diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBaseDeviceHandler.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBaseDeviceHandler.java index fb472882d..260a7df85 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBaseDeviceHandler.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBaseDeviceHandler.java @@ -17,11 +17,13 @@ import static org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolCon 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; @@ -57,6 +59,11 @@ import org.slf4j.LoggerFactory; @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---"; @@ -228,6 +235,8 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler { 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; } @@ -436,7 +445,7 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler { /** * 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. @@ -497,12 +506,50 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler { 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 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 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 - true if the device is supported, false if the device isn't. E.g. Unknown model id in meta-data would - * return false. + * @return - The device type prefix for the device being modelled. E.g. LAP or LUH */ - protected abstract boolean isDeviceSupported(); + public abstract String getDeviceFamilyProtocolPrefix(); + + /** + * Subclasses should return list of VeSyncDeviceMetadata definitions that define the + * supported devices by their implementation. + * + * @return - List of VeSyncDeviceMetadata definitions, that defines groups of devices which + * are operationally the same device. + */ + public abstract List getSupportedDeviceMetadata(); + + public static VeSyncDeviceMetadata getDeviceFamilyMetadata(final @Nullable String deviceType, + final String deviceProtocolPrefix, final List metadata) { + if (deviceType == null) { + return UNKNOWN; + } + final String[] idParts = deviceType.split("-"); + if (idParts.length == 3) { + if (!deviceProtocolPrefix.equals(idParts[0])) { + return UNKNOWN; + } + } + List 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()); + } } diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBridgeHandler.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBridgeHandler.java index ec29e183b..bc12f8c2f 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBridgeHandler.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBridgeHandler.java @@ -159,13 +159,19 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie } 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() { 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 ccb57f946..d3b2464c0 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 @@ -16,6 +16,7 @@ import static org.openhab.binding.vesync.internal.VeSyncConstants.*; 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; @@ -48,21 +49,37 @@ import org.slf4j.LoggerFactory; @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 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); - public static final List 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 SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_AIR_HUMIDIFIER); @@ -76,20 +93,18 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { @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; } @@ -122,18 +137,19 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { } @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 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; } @@ -180,7 +196,7 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { 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; @@ -235,8 +251,8 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { 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)) { @@ -314,7 +330,7 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { 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)); @@ -329,7 +345,7 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { 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)); @@ -338,7 +354,7 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { } 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)); } 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 977b907a7..ab6d8ac8c 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 @@ -18,6 +18,7 @@ import static org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolCon 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; @@ -56,16 +57,34 @@ import org.slf4j.LoggerFactory; @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 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 SUPPORTED_MODEL_FAMILIES = Arrays.asList(CORE600S, CORE400S, + CORE300S, CORE200S, PUR131S); private static final List CORE_400S600S_FAN_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP); private static final List CORE_200S300S_FAN_MODES = Arrays.asList(MODE_MANUAL, MODE_SLEEP); @@ -89,15 +108,15 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { @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, @@ -130,18 +149,19 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { } @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 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; } @@ -167,9 +187,9 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { 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 {}", @@ -177,9 +197,8 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { 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 {}", @@ -194,14 +213,13 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { 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 {}", @@ -229,18 +247,17 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { 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"); @@ -264,20 +281,19 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { @Override protected void pollForDeviceData(final ExpiringCache 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; } diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceMetadata.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceMetadata.java new file mode 100644 index 000000000..8c27ac027 --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceMetadata.java @@ -0,0 +1,66 @@ +/** + * 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 deviceGenerations, + final List 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 deviceGenerations; + + /** + * Device Types not following the standard 3 segment convention + */ + final public List 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; + } +} diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/thing/thing-types.xml index b3ef07132..e1ffdac5c 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/thing/thing-types.xml @@ -70,6 +70,7 @@ + macId @@ -114,6 +115,7 @@ + macId