From 504a0d460bb5d9d0beb55e3a84f27e5b1eeae98a Mon Sep 17 00:00:00 2001 From: David Goodyear Date: Sun, 23 Jul 2023 15:49:56 +0100 Subject: [PATCH 01/33] [veSync] Device support enhancements Device support enhancements Signed-off-by: David Goodyear --- bundles/org.openhab.binding.vesync/README.md | 198 +++++++--- .../vesync/internal/VeSyncConstants.java | 4 +- .../internal/api/VeSyncV2ApiHelper.java | 5 +- .../discovery/VeSyncDiscoveryService.java | 3 + .../dto/requests/VeSyncProtocolConstants.java | 9 +- .../internal/dto/requests/VeSyncRequest.java | 5 + .../VeSyncRequestManagedDeviceBypassV2.java | 69 ++++ .../dto/requests/VeSyncRequestV1Command.java | 45 +++ .../VeSyncRequestV1ManagedDeviceDetails.java | 3 +- .../dto/requests/VeSyncRequestV1SetLevel.java | 36 ++ .../dto/requests/VeSyncRequestV1SetMode.java | 36 ++ .../requests/VeSyncRequestV1SetStatus.java | 36 ++ .../VeSyncV2BypassPurifierStatus.java | 3 +- .../VeSyncV2Ver2BypassHumidifierStatus.java | 109 ++++++ .../VeSyncV2Ver2BypassPurifierStatus.java | 159 ++++++++ ...yncV1AirPurifierDeviceDetailsResponse.java | 23 ++ .../handlers/VeSyncBaseDeviceHandler.java | 29 +- .../VeSyncDeviceAirHumidifierHandler.java | 212 ++++++++--- .../VeSyncDeviceAirPurifierHandler.java | 342 ++++++++++++------ .../VeSyncDeviceHumidifierMetadata.java | 97 +++++ .../VeSyncDevicePurifierMetadata.java | 71 ++++ .../resources/OH-INF/i18n/vesync.properties | 5 + .../resources/OH-INF/thing/thing-types.xml | 21 +- 23 files changed, 1310 insertions(+), 210 deletions(-) create mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java create mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetLevel.java create mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetMode.java create mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetStatus.java create mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassHumidifierStatus.java create mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java create mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java create mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDevicePurifierMetadata.java diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index 9ffcd7d1965e9..c56279288362c 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -9,8 +9,8 @@ Air Humidifier models supported are Dual 200S, Classic 300S, 600S, OasisMist Sma ## Awaiting User Verification Models -Air Filtering models supported are Core200S and Core600S. -Air Humidifier Classic 200S (Same as 300S without the nightlight from initial checks) +Air Filtering models supported are Core200S, Core600S, 131S models and the Vital 100S, 200S. +Air Humidifier Classic 200S (Same as 300S without the nightlight from initial checks), OasisMist 1000 Smart Humidifier ## Supported Things @@ -66,43 +66,48 @@ Channel names in **bold** are read/write, everything else is read-only ### AirPurifier Thing -| Channel | Type | Description | Model's Supported | Controllable Values | -|----------------------|----------------------|------------------------------------------------------------|-------------------|-----------------------| -| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 600S, 400S, 300S | [ON, OFF] | -| **childLock** | Switch | Whether the child lock (display lock is enabled) | 600S, 400S, 300S | [ON, OFF] | -| **display** | Switch | Whether the display is enabled (display is shown) | 600S, 400S, 300S | [ON, OFF] | -| **fanMode** | String | The operation mode of the fan | 600S, 400S | [auto, manual, sleep] | -| **fanMode** | String | The operation mode of the fan | 200S, 300S, | [manual, sleep] | -| **manualFanSpeed** | Number:Dimensionless | The speed of the fan when in manual mode | 600S, 400S | [1...4] | -| **manualFanSpeed** | Number:Dimensionless | The speed of the fan when in manual mode | 300S | [1...3] | -| **nightLightMode** | String | The night lights mode | 200S, 300S | [on, dim, off] | -| filterLifePercentage | Number:Dimensionless | The remaining filter life as a percentage | 600S, 400S, 300S | | -| airQuality | Number:Dimensionless | The air quality as represented by the Core200S / Core300S | 600S, 400S, 300S | | -| airQualityPM25 | Number:Density | The air quality as represented by the Core400S | 600S, 400S, 300S | | -| errorCode | Number:Dimensionless | The error code reported by the device | 600S, 400S, 300S | | -| timerExpiry | DateTime | The expected expiry time of the current timer | 600S, 400S | | -| schedulesCount | Number:Dimensionless | The number schedules configured | 600S, 400S | | -| configDisplayForever | Switch | Config: Whether the display will disable when not active | 600S, 400S, 300S | | -| configAutoMode | String | Config: The mode of operation when auto is active | 600S, 400S, 300S | | -| configAutoRoomSize | Number:Dimensionless | Config: The room size set when auto utilises the room size | 600S, 400S, 300S | | +| Channel | Type | Description | Model's Supported | Controllable Values | Unit | +|----------------------|----------------------|------------------------------------------------------------|------------------------------------------------|----------------------------|-------| +| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 131S, 600S, 400S, 300S, Vital 100S, Vital 200S | [ON, OFF] | | +| **childLock** | Switch | Whether the child lock (display lock is enabled) | 600S, 400S, 300S, Vital 100S, Vital 200S | [ON, OFF] | | +| **display** | Switch | Whether the display is enabled (display is shown) | 131S, 600S, 400S, 300S, Vital 100S, Vital 200S | [ON, OFF] | | +| **fanMode** | String | The operation mode of the fan | 131S, 600S, 400S, Vital 100S | [auto, manual, sleep] | | +| **fanMode** | String | The operation mode of the fan | 200S, 300S, | [manual, sleep] | | +| **fanMode** | String | The operation mode of the fan | Vital 200S | [auto, manual, sleep, pet] | | +| **manualFanSpeed** | Number:Dimensionless | The speed of the fan when in manual mode | 600S, 400S | [1...4] | | +| **manualFanSpeed** | Number:Dimensionless | The speed of the fan when in manual mode | 131S, 300S | [1...3] | | +| **manualFanSpeed** | Number:Dimensionless | The speed of the fan when in manual mode | Vital 100S,Vital 200S | [1...5] | | +| **nightLightMode** | String | The night lights mode | 200S, 300S | [on, dim, off] | | +| filterLifePercentage | Number:Dimensionless | The remaining filter life as a percentage | 131S, 600S, 400S, 300S, Vital 100S, Vital 200S | | | +| airQuality | Number:Dimensionless | The air quality as represented by the Core200S / Core300S | 131S, 600S, 400S, 300S, Vital 100S, Vital 200S | | | +| airQualityPM25 | Number:Density | The air quality as represented by the Core400S | 600S, 400S, 300S, Vital 100S, Vital 200S | | µg/m³ | +| errorCode | Number:Dimensionless | The error code reported by the device | 600S, 400S, 300S, Vital 100S, Vital 200S | | | +| timerExpiry | DateTime | The expected expiry time of the current timer | 600S, 400S | | | +| schedulesCount | Number:Dimensionless | The number schedules configured | 600S, 400S | | | +| configDisplayForever | Switch | Config: Whether the display will disable when not active | 600S, 400S, 300S | | | +| configAutoRoomSize | Number:Dimensionless | Config: The room size set when auto utilises the room size | 600S, 400S, 300S | | | ### AirHumidifier Thing -| 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 | | +| Channel | Type | Description | Model's Supported | Controllable Values | +|----------------------------|----------------------|---------------------------------------------------------------|------------------------------------------------------|---------------------| +| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | +| **display** | Switch | Whether the display is enabled (display is shown) | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | +| waterLacking | Switch | Indicator whether the unit is lacking water | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | +| 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, OasisMist1000 | | +| **stopAtHumiditySetpoint** | Switch | Whether the unit is set to stop when the set point is reached | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | +| humidity | Number:Dimensionless | Indicator for the currently measured humidity % level | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | +| **mistLevel** | Number:Dimensionless | The current mist level set | 300S | [1...2] | +| **mistLevel** | Number:Dimensionless | The current mist level set | 200S, Dual200S, 600S, OasisMist, OasisMist1000 | [1...3] | +| **humidifierMode** | String | The current mode of operation | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [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, OasisMist1000 | [30...80] | +| warmEnabled | Switch | Indicator for warm mist mode | 600S, OasisMist | | +| **warmLevel** | Number:Dimensionless | The current warm mist level set | 600S, OasisMist | [0..3] | +| errorCode | Number:Dimensionless | The error code reported by the device | OasisMist1000 | | +| timerExpiry | DateTime | The expected expiry time of the current timer | OasisMist1000 | | +| schedulesCount | Number:Dimensionless | The number schedules configured | OasisMist1000 | | ## Full Example @@ -129,7 +134,7 @@ Switch LoungeAPControlsLock "Lounge Air Purifier Controls 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:Density LoungeAPAirQuality "Lounge Air Purifier Air Quality [%.0f% %unit%]" { unit="µg/m³",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" } @@ -137,7 +142,7 @@ DateTime LoungeAPTimerExpiry "Lounge Air Purifier Timer Ex Number LoungeAPSchedulesCount "Lounge Air Purifier Schedules Count" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:schedulesCount" } ``` -#### Air Purifier Core 200S/300S Model +#### Air Purifier Core 200S / 300S Model ```java Switch LoungeAPPower "Lounge Air Purifier Power" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:enabled" } @@ -147,7 +152,7 @@ Switch LoungeAPControlsLock "Lounge Air Purifier Controls 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:Density LoungeAPAirQuality "Lounge Air Purifier Air Quality [%.0f%]" { unit="µg/m³",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" } @@ -155,6 +160,30 @@ DateTime LoungeAPTimerExpiry "Lounge Air Purifier Timer Ex Number LoungeAPSchedulesCount "Lounge Air Purifier Schedules Count" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:schedulesCount" } ``` +#### Air Purifier 131s Models + +```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" } +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:Dimensionless LoungeAPAirQuality "Lounge Air Purifier Air Quality" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:airQuality" } +``` + +#### Air Purifier Vital 100s / 200s Models + +```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" } +``` + #### Air Humidifier Classic 200S / Dual 200S Model ```java @@ -199,6 +228,7 @@ Number:Dimensionless LoungeAHHumidity "Lounge Air Humidifier Measured H 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" } +Number:Dimensionless LoungeAHWarmMistLevel "Lounge Air Humidifier Warm Mist Level" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:warmLevel" } ``` #### Air Humidifier Oasis Mist Smart Model @@ -214,8 +244,29 @@ Number:Dimensionless LoungeAHHumidity "Lounge Air Humidifier Measured H 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" } +Number:Dimensionless LoungeAHWarmMistLevel "Lounge Air Humidifier Warm Mist Level" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:warmLevel" } ``` +#### Air Humidifier Oasis Mist 1000 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" } +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" } +Number:Dimensionless LoungeAHWarmMistLevel "Lounge Air Humidifier Warm Mist Level" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:warmLevel" } +DateTime LoungeAHTimerExpiry "Lounge Air Humidifier Timer Expiry [%1$tA %1$tI:%1$tM %1$Tp]" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:timerExpiry" } +Number LoungeAHSchedulesCount "Lounge Air Humidifier Schedules Count" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:schedulesCount" } +Number LoungeAHErrorCode "Lounge Air Humidifier Error Code" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:errorCode" } +``` + + ### Configuration (*.sitemap) #### Air Purifier Core 400S / 600S Model @@ -234,7 +285,7 @@ Frame { } ``` -#### Air Purifier Core 200S/300S Model +#### Air Purifier Core 200S / 300S Model ```perl Frame { @@ -251,6 +302,47 @@ Frame { } ``` +#### Air Purifier 131s Models + +```perl +Frame { + Switch item=LoungeAPPower label="Power" + Text item=LoungeAPFilterRemainingUse label="Filter Remaining" + Switch item=LoungeAPDisplay label="Display" + Text item=LoungeAPAirQuality label="Air Quality [%.0f]" + Switch item=LoungeAPMode label="Mode" mappings=[auto="Auto",manual="Manual Fan Control", sleep="Sleeping"] icon="settings" + Switch item=LoungeAPManualFanSpeed label="Manual Fan Speed [%.0f]" mappings=[1="1", 2="2", 3="3"] icon="settings" +} +``` + +#### Air Purifier Vital 100S Models + +```perl +Frame { + Switch item=LoungeAPPower label="Power" + Text item=LoungeAPFilterRemainingUse label="Filter Remaining" + Switch item=LoungeAPDisplay label="Display" + Text item=LoungeAPAirQuality label="Air Quality [%.0f (PM2.5)]" + Switch item=LoungeAPControlsLock label="Controls Locked" + Switch item=LoungeAPMode label="Mode" mappings=[auto="Auto", manual="Manual Fan Control", sleep="Sleeping"] icon="settings" + Switch item=LoungeAPManualFanSpeed label="Manual Fan Speed [%.0f]" mappings=[1="1", 2="2", 3="3", 4="4", 5="5"] icon="settings" +} +``` + +#### Air Purifier Vital 200S Models + +```perl +Frame { + Switch item=LoungeAPPower label="Power" + Text item=LoungeAPFilterRemainingUse label="Filter Remaining" + Switch item=LoungeAPDisplay label="Display" + Text item=LoungeAPAirQuality label="Air Quality [%.0f (PM2.5)]" + Switch item=LoungeAPControlsLock label="Controls Locked" + Switch item=LoungeAPMode label="Mode" mappings=[auto="Auto", manual="Manual Fan Control", sleep="Sleeping", pet="Pet"] icon="settings" + Switch item=LoungeAPManualFanSpeed label="Manual Fan Speed [%.0f]" mappings=[1="1", 2="2", 3="3", 4="4", 5="5"] icon="settings" +} +``` + #### Air Humidifier Classic 200S / Dual 200S Model ```perl @@ -282,7 +374,7 @@ Frame { Text icon="none" item=LoungeAHHumidity Switch item=LoungeAHTargetStop Slider item=LoungeAHTarget minValue=30 maxValue=80 - Slider item=LoungeAHMistLevel minValue=1 maxValue=3 + Slider item=LoungeAHMistLevel minValue=0 maxValue=3 } ``` @@ -292,7 +384,7 @@ Frame { Frame { Switch item=LoungeAHPower Switch item=LoungeAHDisplay - Switch item=LoungeAHMode label="Mode" mappings=[auto="Auto", sleep="Sleeping"] icon="settings" + Switch item=LoungeAHMode label="Mode" mappings=[auto="Auto", manual="Manual Control", sleep="Sleeping"] icon="settings" Text icon="none" item=LoungeAHWaterLacking Text icon="none" item=LoungeAHHighHumidity Text icon="none" item=LoungeAHWaterTankRemoved @@ -300,6 +392,7 @@ Frame { Switch item=LoungeAHTargetStop Slider item=LoungeAHTarget minValue=30 maxValue=80 Slider item=LoungeAHMistLevel minValue=1 maxValue=3 + Slider item=LoungeAHWarmMistLevel minValue=0 maxValue=3 } ``` @@ -317,6 +410,27 @@ Frame { Switch item=LoungeAHTargetStop Slider item=LoungeAHTarget minValue=30 maxValue=80 Slider item=LoungeAHMistLevel minValue=1 maxValue=3 + Slider item=LoungeAHWarmMistLevel minValue=1 maxValue=3 +} +``` + +#### Air Humidifier Oasis Mist 1000 Smart 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 + Slider item=LoungeAHWarmMistLevel minValue=1 maxValue=3 + Text item=LoungeAHTimerExpiry label="Timer Shutdown @" icon="clock" + Text item=LoungeAHErrorCode label="Error Code [%.0f]" } ``` 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 9c2880a4105fd..48850f4444a53 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 @@ -30,7 +30,7 @@ public class VeSyncConstants { public static final Gson GSON = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setPrettyPrinting() - .disableHtmlEscaping().serializeNulls().create(); + .disableHtmlEscaping().create(); private static final String BINDING_ID = "vesync"; @@ -65,6 +65,8 @@ public class VeSyncConstants { public static final String DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE = "configAutoRoomSize"; public static final String DEVICE_CHANNEL_AF_SCHEDULES_COUNT = "schedulesCount"; public static final String DEVICE_CHANNEL_AF_NIGHT_LIGHT = "nightLightMode"; + public static final String DEVICE_CHANNEL_AF_LIGHT_DETECTION = "lightDetection"; + public static final String DEVICE_CHANNEL_AF_LIGHT_DETECTED = "lightDetected"; // Humidity related channels public static final String DEVICE_CHANNEL_WATER_LACKS = "waterLacking"; diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java index 801ce26db004b..e491f3cd8ce88 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java @@ -167,12 +167,13 @@ public String reqV1Authorized(final String url, final VeSyncAuthenticatedRequest private String directReqV1Authorized(final String url, final VeSyncAuthenticatedRequest requestData) throws AuthenticationException { try { - Request request = httpClient.POST(url); + Request request = httpClient.newRequest(url).method(requestData.httpMethod); // No headers for login request.content(new StringContentProvider(VeSyncConstants.GSON.toJson(requestData))); - logger.debug("POST @ {} with content\r\n{}", url, VeSyncConstants.GSON.toJson(requestData)); + logger.debug("{} @ {} with content\r\n{}", requestData.httpMethod, url, + VeSyncConstants.GSON.toJson(requestData)); request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8"); 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 0bb8e36baf99c..6af53e590c6e4 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 @@ -85,6 +85,9 @@ protected void stopBackgroundDiscovery() { @Override protected void startScan() { + if (bridgeHandler == null) { + return; + } // If the bridge is not online no other thing devices can be found, so no reason to scan at this moment. removeOlderResults(getTimestampOfLastScan()); if (ThingStatus.ONLINE.equals(thingHandler.getThing().getStatus())) { diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java index c6925f727f0f4..741ff024bf2fe 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java @@ -23,6 +23,8 @@ public interface VeSyncProtocolConstants { String MODE_AUTO = "auto"; 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"; @@ -42,6 +44,7 @@ public interface VeSyncProtocolConstants { String DEVICE_GET_HUMIDIFIER_STATUS = "getHumidifierStatus"; String DEVICE_LEVEL_TYPE_MIST = "mist"; + String DEVICE_LEVEL_TYPE_WARM_MIST = "warm"; // Air Purifier Commands String DEVICE_SET_PURIFIER_MODE = "setPurifierMode"; @@ -49,12 +52,16 @@ public interface VeSyncProtocolConstants { String DEVICE_SET_NIGHT_LIGHT = "setNightLight"; String DEVICE_GET_PURIFIER_STATUS = "getPurifierStatus"; String DEVICE_LEVEL_TYPE_WIND = "wind"; + String DEVICE_SET_LIGHT_DETECTION = "setLightDetection"; /** * Base URL for AUTHENTICATION REQUESTS */ String PROTOCOL = "https"; - String HOST_ENDPOINT = PROTOCOL + "://smartapi.vesync.com/cloud"; + String SERVER_ADDRESS = "smartapi.vesync.com"; + String SERVER_ENDPOINT = PROTOCOL + "://" + SERVER_ADDRESS; + + String HOST_ENDPOINT = SERVER_ENDPOINT + "/cloud"; String V1_LOGIN_ENDPOINT = HOST_ENDPOINT + "/v1/user/login"; String V1_MANAGED_DEVICES_ENDPOINT = HOST_ENDPOINT + "/v1/deviceManaged/devices"; String V2_BYPASS_ENDPOINT = HOST_ENDPOINT + "/v2/deviceManaged/bypassV2"; diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java index 2b17baed81605..cfe2dccf99355 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.vesync.internal.dto.requests; +import javax.ws.rs.HttpMethod; + import com.google.gson.annotations.SerializedName; /** @@ -21,6 +23,8 @@ */ public class VeSyncRequest { + public transient String httpMethod; + @SerializedName("timeZone") public String timeZone = "America/New_York"; @@ -44,5 +48,6 @@ public class VeSyncRequest { public VeSyncRequest() { traceId = String.valueOf(System.currentTimeMillis()); + httpMethod = HttpMethod.POST; } } diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestManagedDeviceBypassV2.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestManagedDeviceBypassV2.java index 70f6feb1de71f..4b3ea744b14bf 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestManagedDeviceBypassV2.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestManagedDeviceBypassV2.java @@ -55,6 +55,75 @@ public class VesyncManagedDeviceBase { public static class EmptyPayload { } + public static class SetLightDetectionPayload extends EmptyPayload { + + public SetLightDetectionPayload(final boolean enabled) { + lightDetectionSwitch = enabled ? 1 : 0; + } + + @SerializedName("lightDetectionSwitch") + public int lightDetectionSwitch = -1; + } + + public static class SetPowerPayload extends EmptyPayload { + + public SetPowerPayload(final boolean enabled, final int switchIdx) { + this.powerSwitch = enabled ? 1 : 0; + this.switchIdx = switchIdx; + } + + @SerializedName("switchIdx") + public int switchIdx = -1; + + @SerializedName("powerSwitch") + public int powerSwitch = -1; + } + + public static class SetChildLockPayload extends EmptyPayload { + + public SetChildLockPayload(final boolean enabled) { + this.childLockSwitch = enabled ? 1 : 0; + } + + @SerializedName("childLockSwitch") + public int childLockSwitch = -1; + } + + public static class SetScreenSwitchPayload extends EmptyPayload { + + public SetScreenSwitchPayload(final boolean enabled) { + this.screenSwitch = enabled ? 1 : 0; + } + + @SerializedName("screenSwitch") + public int screenSwitch = -1; + } + + public static class SetManualSpeedLevelPayload extends EmptyPayload { + + public SetManualSpeedLevelPayload(final int manualSpeedLevel) { + this.manualSpeedLevel = manualSpeedLevel; + } + + @SerializedName("levelIdx") + public int levelIdx = 0; + + @SerializedName("levelType") + public String levelType = "wind"; + + @SerializedName("manualSpeedLevel") + public int manualSpeedLevel = -1; + } + + public static class SetWorkModePayload extends EmptyPayload { + public SetWorkModePayload(final String workMode) { + this.workMode = workMode; + } + + @SerializedName("workMode") + public String workMode = ""; + } + public static class SetSwitchPayload extends EmptyPayload { public SetSwitchPayload(final boolean enabled, final int id) { diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java new file mode 100644 index 0000000000000..a1452f09695df --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java @@ -0,0 +1,45 @@ +/** + * 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.dto.requests; + +import javax.ws.rs.HttpMethod; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link VeSyncRequestV1Command} is the Java class as a DTO to define the base implementation of a V1 command for + * the Vesync + * API. + * + * @author David Goodyear - Initial contribution + */ +public class VeSyncRequestV1Command extends VeSyncAuthenticatedRequest { + + @SerializedName("uuid") + public String uuid = null; + + public VeSyncRequestV1Command(final String deviceUuid) { + // Exclude fields that shouldn't be there by setting to null + super.phoneOS = null; + super.phoneBrand = null; + super.method = null; + super.appVersion = null; + super.httpMethod = HttpMethod.PUT; + // Set the required payload parameters + uuid = deviceUuid; + } + + public String getUuid() { + return uuid; + } +} diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1ManagedDeviceDetails.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1ManagedDeviceDetails.java index d0cd5a8e32477..3d6fc580a8d28 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1ManagedDeviceDetails.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1ManagedDeviceDetails.java @@ -18,7 +18,8 @@ import com.google.gson.annotations.SerializedName; /** - * The {@link VeSyncRequestV1ManagedDeviceDetails} is the Java class as a DTO to hold login credentials for the Vesync + * The {@link VeSyncRequestV1ManagedDeviceDetails} is the Java class as a DTO to request the managed device details for + * the Vesync * API. * * @author David Goodyear - Initial contribution diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetLevel.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetLevel.java new file mode 100644 index 0000000000000..ea50568d0cf76 --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetLevel.java @@ -0,0 +1,36 @@ +/** + * 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.dto.requests; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link VeSyncRequestV1SetLevel} is the Java class as a DTO define a V1 Set Level command for the Vesync + * API. + * + * @author David Goodyear - Initial contribution + */ +public class VeSyncRequestV1SetLevel extends VeSyncRequestV1Command { + + @SerializedName("level") + public Integer level = null; + + public VeSyncRequestV1SetLevel(final String deviceUuid, final int level) { + super(deviceUuid); + this.level = level; + } + + public Integer getLevel() { + return level; + } +} diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetMode.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetMode.java new file mode 100644 index 0000000000000..b96f6763d7978 --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetMode.java @@ -0,0 +1,36 @@ +/** + * 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.dto.requests; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link VeSyncRequestV1SetMode} is the Java class as a DTO define a V1 Set Mode command for the Vesync + * API. + * + * @author David Goodyear - Initial contribution + */ +public class VeSyncRequestV1SetMode extends VeSyncRequestV1Command { + + @SerializedName("mode") + public String mode = null; + + public VeSyncRequestV1SetMode(final String deviceUuid, final String mode) { + super(deviceUuid); + this.mode = mode; + } + + public String getMode() { + return mode; + } +} diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetStatus.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetStatus.java new file mode 100644 index 0000000000000..80a1d292be7cd --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetStatus.java @@ -0,0 +1,36 @@ +/** + * 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.dto.requests; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link VeSyncRequestV1SetStatus} is the Java class as a DTO define a V1 Set Status command for the Vesync + * API. + * + * @author David Goodyear - Initial contribution + */ +public class VeSyncRequestV1SetStatus extends VeSyncRequestV1Command { + + @SerializedName("status") + public String status = null; + + public VeSyncRequestV1SetStatus(final String deviceUuid, final String status) { + super(deviceUuid); + this.status = status; + } + + public String getStatus() { + return status; + } +} diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2BypassPurifierStatus.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2BypassPurifierStatus.java index 9d8c0e8b851aa..2379471083bd6 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2BypassPurifierStatus.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2BypassPurifierStatus.java @@ -16,8 +16,7 @@ /** * The {@link VeSyncV2BypassPurifierStatus} is a Java class used as a DTO to hold the Vesync's API's common response - * data, - * in regards to an Air Purifier device. + * data, in regards to an Air Purifier device. * * @author David Goodyear - Initial contribution */ diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassHumidifierStatus.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassHumidifierStatus.java new file mode 100644 index 0000000000000..d0175a5cb01e4 --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassHumidifierStatus.java @@ -0,0 +1,109 @@ +/** + * 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.dto.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link VeSyncV2Ver2BypassHumidifierStatus} is a Java class used as a DTO to hold the Vesync's API's common + * response data, in regard's to a Air Humidifier based device, using the latest encoding protocol scheme. + * + * @author David Goodyear - Initial contribution + */ +public class VeSyncV2Ver2BypassHumidifierStatus extends VeSyncResponse { + + @SerializedName("result") + public VeSyncV2Ver2BypassHumidifierStatus.HumidifierStatus result; + + public class HumidifierStatus extends VeSyncResponse { + + @SerializedName("result") + public VeSyncV2Ver2BypassHumidifierStatus.HumidifierStatus.AirHumidifierStatus result; + + public class AirHumidifierStatus { + + @SerializedName("powerSwitch") + public int powerSwitch; + + public boolean getPowerSwitch() { + return powerSwitch == 1; + } + + @SerializedName("virtualLevel") + public int virtualLevel; + + @SerializedName("mistLevel") + public int mistLevel; + + @SerializedName("workMode") + public String workMode; + + @SerializedName("waterLacksState") + public int waterLacksState; + + public boolean getWaterLacksState() { + return waterLacksState == 1; + } + + @SerializedName("targetHumidity") + public int targetHumidity; + + @SerializedName("autoStopState") + public int autoStopState; + + public boolean getAutoStopState() { + return autoStopState == 1; + } + + @SerializedName("screenState") + public int screenState; + + public boolean getScreenState() { + return screenState == 1; + } + + @SerializedName("screenSwitch") + public int screenSwitch; + + public boolean getScreenSwitch() { + return screenSwitch == 1; + } + + @SerializedName("humidity") + public int humidity; + + @SerializedName("waterTankLifted") + public int waterTankLifted; + + public boolean getWaterTankLifted() { + return waterTankLifted == 1; + } + + @SerializedName("autoStopSwitch") + public int autoStopSwitch; + + public boolean getAutoStopSwitch() { + return autoStopSwitch == 1; + } + + @SerializedName("scheduleCount") + public int scheduleCount; + + @SerializedName("timerRemain") + public int timerRemain; + + @SerializedName("errorCode") + public int errorCode; + } + } +} diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java new file mode 100644 index 0000000000000..4fd8b3d0b4543 --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java @@ -0,0 +1,159 @@ +/** + * 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.dto.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link VeSyncV2Ver2BypassPurifierStatus} is a Java class used as a DTO to hold the Vesync's API's common + * response data, in regards to an Air Purifier based device, using the latest encoding protocol scheme. + * + * @author David Goodyear - Initial contribution + */ +public class VeSyncV2Ver2BypassPurifierStatus extends VeSyncResponse { + + @SerializedName("result") + public PurifierStatus result; + + public class PurifierStatus extends VeSyncResponse { + + @SerializedName("result") + public AirPurifierStatus result; + + public class AirPurifierStatus { + @SerializedName("AQLevel") + public int airQuality; + + @SerializedName("powerSwitch") + public int powerSwitch; + + public boolean getPowerSwitch() { + return powerSwitch == 1; + } + + @SerializedName("workMode") + public String workMode; + + @SerializedName("fanSpeedLevel") + public int fanSpeedLevel; + + @SerializedName("manualSpeedLevel") + public int manualSpeedLevel; + + @SerializedName("filterLifePercent") + public int filterLifePercent; + + @SerializedName("childLockSwitch") + public int childLockSwitch; + + public boolean getChildLockSwitch() { + return childLockSwitch == 1; + } + + @SerializedName("screenState") + public int screenState; + + public boolean getScreenState() { + return screenState == 1; + } + + @SerializedName("lightDetectionSwitch") + public int lightDetectionSwitch; + + public boolean getLightDetectionSwitch() { + return lightDetectionSwitch == 1; + } + + @SerializedName("environmentLightState") + public int environmentLightState; + + public boolean getEnvironmentLightState() { + return environmentLightState == 1; + } + + @SerializedName("screenSwitch") + public int screenSwitch; + + public boolean getScreenSwitch() { + return screenSwitch == 1; + } + + @SerializedName("PM25") + public int PM25; + + @SerializedName("timerRemain") + public int timerRemain; + + @SerializedName("scheduleCount") + public int scheduleCount; + + @SerializedName("efficientModeTimeRemain") + public int efficientModeTimeRemain; + + @SerializedName("errorCode") + public int errorCode; + + @SerializedName("autoPreference") + public VeSyncV2Ver2BypassPurifierStatus.PurifierStatus.AirPurifierStatus.AirPurifierConfigAutoPref autoPreference; + + public class AirPurifierConfigAutoPref { + @SerializedName("autoPreferenceType") + public String autoType; + + @SerializedName("roomSize") + public int roomSize; + } + + @SerializedName("sleepPreference") + public VeSyncV2Ver2BypassPurifierStatus.PurifierStatus.AirPurifierStatus.AirPurifierSleepPref sleepPreference; + + public class AirPurifierSleepPref { + @SerializedName("sleepPreferenceType") + public String sleepPreferenceType; + + @SerializedName("cleaningBeforeBedSwitch") + public int cleaningBeforeBedSwitch; + + @SerializedName("cleaningBeforeBedSpeedLevel") + public int cleaningBeforeBedSpeedLevel; + + @SerializedName("cleaningBeforeBedMinutes") + public int cleaningBeforeBedMinutes; + + @SerializedName("whiteNoiseSleepAidSwitch") + public int whiteNoiseSleepAidSwitch; + + @SerializedName("whiteNoiseSleepAidSpeedLevel") + public int whiteNoiseSleepAidSpeedLevel; + + @SerializedName("whiteNoiseSleepAidMinutes") + public int whiteNoiseSleepAidMinutes; + + @SerializedName("duringSleepSpeedLevel") + public int duringSleepSpeedLevel; + + @SerializedName("duringSleepMinutes") + public int duringSleepMinutes; + + @SerializedName("afterWakeUpPowerSwitch") + public int afterWakeUpPowerSwitch; + + @SerializedName("afterWakeUpWorkMode") + public String afterWakeUpWorkMode; + + @SerializedName("afterWakeUpFanSpeedLevel") + public String afterWakeUpFanSpeedLevel; + } + } + } +} diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/v1/VeSyncV1AirPurifierDeviceDetailsResponse.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/v1/VeSyncV1AirPurifierDeviceDetailsResponse.java index 7dae44c14bf80..ae1e190d9f3ef 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/v1/VeSyncV1AirPurifierDeviceDetailsResponse.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/v1/VeSyncV1AirPurifierDeviceDetailsResponse.java @@ -53,6 +53,29 @@ public String getMode() { return mode; } + @SerializedName("activeTime") + public int activeTime; + + public int getActiveTime() { + return activeTime; + } + + @SerializedName("filterLife") + public FilterLife filter; + + public int getFilterPercent() { + return filter.getPercent(); + } + + public class FilterLife { + @SerializedName("percent") + public int percent; + + public int getPercent() { + return percent; + } + } + @SerializedName("deviceName") public String deviceName; 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 44a8288b678a7..1140c3c64ee09 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 @@ -32,6 +32,7 @@ import org.openhab.binding.vesync.internal.VeSyncBridgeConfiguration; import org.openhab.binding.vesync.internal.VeSyncDeviceConfiguration; import org.openhab.binding.vesync.internal.dto.requests.VeSyncAuthenticatedRequest; +import org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolConstants; import org.openhab.binding.vesync.internal.dto.requests.VeSyncRequestManagedDeviceBypassV2; import org.openhab.binding.vesync.internal.dto.responses.VeSyncManagedDeviceBase; import org.openhab.binding.vesync.internal.exceptions.AuthenticationException; @@ -47,6 +48,7 @@ import org.openhab.core.thing.binding.BridgeHandler; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.builder.ThingBuilder; +import org.openhab.core.types.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -410,7 +412,20 @@ protected final String sendV2BypassControlCommand(final String method, return result; } - public final String sendV1Command(final String method, final String url, final VeSyncAuthenticatedRequest request) { + protected final String sendV1ControlCommand(final String urlPath, final VeSyncAuthenticatedRequest request) { + return sendV1ControlCommand(urlPath, request, true); + } + + protected final String sendV1ControlCommand(final String urlPath, final VeSyncAuthenticatedRequest request, + final boolean readbackDevice) { + final String result = sendV1Command(urlPath, request); + if (!result.equals(EMPTY_STRING) && readbackDevice) { + performReadbackPoll(); + } + return result; + } + + public final String sendV1Command(final String urlPath, final VeSyncAuthenticatedRequest request) { if (ThingStatus.OFFLINE.equals(this.thing.getStatus())) { logger.debug("Command blocked as device is offline"); return EMPTY_STRING; @@ -422,6 +437,7 @@ public final String sendV1Command(final String method, final String url, final V } VeSyncClient client = getVeSyncClient(); if (client != null) { + final String url = VeSyncProtocolConstants.SERVER_ENDPOINT + "/" + urlPath; return client.reqV2Authorized(url, deviceLookupKey, request); } else { throw new DeviceUnknownException("Missing client"); @@ -547,4 +563,15 @@ public static VeSyncDeviceMetadata getDeviceFamilyMetadata(final @Nullable Strin public VeSyncDeviceMetadata getDeviceFamilyMetadata(final @Nullable String deviceType) { return getDeviceFamilyMetadata(deviceType, getDeviceFamilyProtocolPrefix(), getSupportedDeviceMetadata()); } + + @Override + protected void updateState(final String channelID, final State state) { + // In case of any unexpected decoding issues log them, so that the necessary adjustments can + // be done. (Not expected but just in case). + try { + super.updateState(channelID, state); + } catch (final Exception e) { + logger.warn("Please report issue - could not update channel {} with error {}", channelID, e.toString()); + } + } } 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 fbab426ec3fb6..b6b898a2b68bc 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 @@ -15,17 +15,26 @@ import static org.openhab.binding.vesync.internal.VeSyncConstants.*; import static org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolConstants.*; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; 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; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.vesync.internal.VeSyncBridgeConfiguration; import org.openhab.binding.vesync.internal.VeSyncConstants; import org.openhab.binding.vesync.internal.dto.requests.VeSyncRequestManagedDeviceBypassV2; +import org.openhab.binding.vesync.internal.dto.responses.VeSyncResponse; import org.openhab.binding.vesync.internal.dto.responses.VeSyncV2BypassHumidifierStatus; +import org.openhab.binding.vesync.internal.dto.responses.VeSyncV2Ver2BypassHumidifierStatus; import org.openhab.core.cache.ExpiringCache; +import org.openhab.core.library.items.DateTimeItem; +import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; @@ -47,6 +56,7 @@ * @author David Goodyear - Initial contribution */ @NonNullByDefault +@SuppressWarnings("serial") public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { public static final String DEV_TYPE_FAMILY_AIR_HUMIDIFIER = "LUH"; @@ -59,33 +69,58 @@ 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")); + public static final String DEV_FAMILY_OASIS_MIST_1000 = "Oasis Mist 1000"; - public static final VeSyncDeviceMetadata CLASSIC300S = new VeSyncDeviceMetadata(DEV_FAMILY_CLASSIC_300S, - Arrays.asList("A601S"), List.of("Classic300S")); + private static final List AUTO_MAN_SLEEP_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP); - public static final VeSyncDeviceMetadata DUAL200S = new VeSyncDeviceMetadata(DEV_FAMILY_DUAL_200S, - Arrays.asList("D301S"), List.of("Dual200S")); + private static final List AUTO_MAN_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL); - public static final VeSyncDeviceMetadata LV600S = new VeSyncDeviceMetadata(DEV_FAMILY_600S, Arrays.asList("A602S"), + private static final List CLASSIC_300S_NIGHT_LIGHT_MODES = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF); + + public static final VeSyncDeviceHumidifierMetadata CLASSIC200S = new VeSyncDeviceHumidifierMetadata(1, + 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(1, + 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 VeSyncDeviceHumidifierMetadata DUAL200S = new VeSyncDeviceHumidifierMetadata(1, + 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(1, 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(1, + DEV_FAMILY_OASIS_MIST, Arrays.asList("O451S"), Collections.emptyList(), AUTO_MAN_SLEEP_MODES, 1, 3, 0, 3, + true, Collections.emptyList()); + + public static final VeSyncDeviceHumidifierMetadata OASIS_MIST_1000 = new VeSyncDeviceHumidifierMetadata(2, + DEV_FAMILY_OASIS_MIST_1000, Arrays.asList("M101S"), Collections.emptyList(), AUTO_MAN_SLEEP_MODES, 1, 3, 0, + 3, false, Collections.emptyList()); public static final List SUPPORTED_MODEL_FAMILIES = Arrays.asList(LV600S, CLASSIC300S, CLASSIC200S, DUAL200S, OASIS_MIST); - private static final List CLASSIC_300S_600S_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP); - private static final List CLASSIC_300S_NIGHT_LIGHT_MODES = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF); - private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirHumidifierHandler.class); public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_AIR_HUMIDIFIER); private final Object pollLock = new Object(); + public static final Map DEV_FAMILY_HUMIDIFER_MAP = new HashMap() { + { + put(CLASSIC200S.deviceFamilyName, CLASSIC200S); + put(CLASSIC300S.deviceFamilyName, CLASSIC300S); + put(DUAL200S.deviceFamilyName, DUAL200S); + put(LV600S.deviceFamilyName, LV600S); + put(OASIS_MIST.deviceFamilyName, OASIS_MIST); + put(OASIS_MIST_1000.deviceFamilyName, OASIS_MIST_1000); + } + }; + public VeSyncDeviceAirHumidifierHandler(Thing thing) { super(thing); } @@ -97,15 +132,22 @@ protected String[] getChannelsToRemove() { if (deviceFamily != null) { switch (deviceFamily) { case DEV_FAMILY_CLASSIC_300S: - toRemove = new String[] { DEVICE_CHANNEL_WARM_ENABLED, DEVICE_CHANNEL_WARM_LEVEL }; + toRemove = new String[] { DEVICE_CHANNEL_WARM_ENABLED, DEVICE_CHANNEL_WARM_LEVEL, + DEVICE_CHANNEL_AF_SCHEDULES_COUNT, DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME }; break; 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, DEVICE_CHANNEL_AF_SCHEDULES_COUNT, + DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME }; + break; + case DEV_FAMILY_OASIS_MIST_1000: toRemove = new String[] { DEVICE_CHANNEL_WARM_ENABLED, DEVICE_CHANNEL_WARM_LEVEL, DEVICE_CHANNEL_AF_NIGHT_LIGHT }; break; case DEV_FAMILY_OASIS_MIST: - toRemove = new String[] { DEVICE_CHANNEL_AF_NIGHT_LIGHT }; + toRemove = new String[] { DEVICE_CHANNEL_AF_NIGHT_LIGHT, DEVICE_CHANNEL_AF_SCHEDULES_COUNT, + DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME }; break; } } @@ -152,6 +194,11 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { if (deviceFamily == null) { return; } + final VeSyncDeviceHumidifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily); + if (devContraints == null) { + logger.warn("Could not find device family for {} during handleCommand", deviceFamily); + return; + } scheduler.submit(() -> { @@ -187,31 +234,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 = quantityCommand.intValue(); + int targetMistLevel = ((QuantityType) command).intValue(); + if (!devContraints.isTargetMistLevelSupported(targetMistLevel)) { + logger.warn("Mist level command for \"{}\" is not valid ({}) API possible options {} -> {}", + command, devContraints.deviceFamilyName, devContraints.targetMinMistLevel, + devContraints.targetMaxMistLevel); + targetMistLevel = targetMistLevel < devContraints.targetMinMistLevel + ? devContraints.targetMinMistLevel + : devContraints.targetMaxMistLevel; + } + // If more devices have this the hope is it's those with the prefix LUH so the check can // be simplified, originally devices mapped 1/5/9 to 1/2/3. - if (DEV_FAMILY_DUAL_200S.equals(deviceFamily)) { - if (targetMistLevel < 1) { - logger.warn("Target Mist Level less than 1 - adjusting to 1 as the valid API value"); - targetMistLevel = 1; - } else if (targetMistLevel > 2) { - logger.warn("Target Mist Level greater than 2 - adjusting to 2 as the valid API value"); - targetMistLevel = 2; - } - } else { - if (targetMistLevel < 1) { - logger.warn("Target Mist Level less than 1 - adjusting to 1 as the valid API value"); - targetMistLevel = 1; - } else if (targetMistLevel > 3) { - logger.warn("Target Mist Level greater than 3 - adjusting to 3 as the valid API value"); - targetMistLevel = 3; - } + if (!DEV_FAMILY_DUAL_200S.equals(deviceFamily)) { // Re-map to what appears to be bitwise encoding of the states switch (targetMistLevel) { case 1: @@ -234,33 +277,45 @@ 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 (!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)); break; } } else if (command instanceof StringType) { 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: @@ -289,7 +344,16 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { @Override protected void pollForDeviceData(final ExpiringCache cachedResponse) { String response; - VeSyncV2BypassHumidifierStatus humidifierStatus; + VeSyncResponse humidifierStatus; + + final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY); + + final VeSyncDeviceHumidifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily); + if (devContraints == null) { + logger.warn("Could not find device family for {} during pollForDeviceData", deviceFamily); + return; + } + synchronized (pollLock) { response = cachedResponse.getValue(); boolean cachedDataUsed = response != null; @@ -305,7 +369,11 @@ protected void pollForDeviceData(final ExpiringCache cachedResponse) { return; } - humidifierStatus = VeSyncConstants.GSON.fromJson(response, VeSyncV2BypassHumidifierStatus.class); + if (devContraints.protocolV2Version == 2) { + humidifierStatus = VeSyncConstants.GSON.fromJson(response, VeSyncV2Ver2BypassHumidifierStatus.class); + } else { + humidifierStatus = VeSyncConstants.GSON.fromJson(response, VeSyncV2BypassHumidifierStatus.class); + } if (humidifierStatus == null) { return; @@ -325,13 +393,20 @@ protected void pollForDeviceData(final ExpiringCache cachedResponse) { updateStatus(ThingStatus.ONLINE); } + if (devContraints.protocolV2Version != 2) { + parseV2Ver1Poll((VeSyncV2BypassHumidifierStatus) humidifierStatus, deviceFamily); + } else { + parseV2Ver2Poll((VeSyncV2Ver2BypassHumidifierStatus) humidifierStatus); + } + } + + private void parseV2Ver1Poll(final VeSyncV2BypassHumidifierStatus humidifierStatus, + final @Nullable String deviceFamily) { if (!"0".equals(humidifierStatus.result.getCode())) { - logger.warn("Check correct Thing type has been set - API gave a unexpected response for an Air Humidifier"); + logger.warn("Check Thing type has been set - API gave a unexpected response for an Air Humidifier"); return; } - 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_WATER_LACKS, OnOffType.from(humidifierStatus.result.result.waterLacks)); @@ -342,6 +417,10 @@ protected void pollForDeviceData(final ExpiringCache cachedResponse) { updateState(DEVICE_CHANNEL_HUMIDITY, new QuantityType<>(humidifierStatus.result.result.humidity, Units.PERCENT)); updateState(DEVICE_CHANNEL_MIST_LEVEL, new DecimalType(humidifierStatus.result.result.mistLevel)); + // Map back HUMIDITY -> AUTO if necessary for devices where auto is remapped + if (MODE_AUTO_HUMIDITY.equals(humidifierStatus.result.result.mode)) { + humidifierStatus.result.result.mode = MODE_AUTO; + } updateState(DEVICE_CHANNEL_HUMIDIFIER_MODE, new StringType(humidifierStatus.result.result.mode)); // Only the 300S supports nightlight currently of tested devices. @@ -354,12 +433,43 @@ protected void pollForDeviceData(final ExpiringCache cachedResponse) { } else { updateState(DEVICE_CHANNEL_AF_NIGHT_LIGHT, new StringType(MODE_DIM)); } - } else if (DEV_FAMILY_600S.equals(deviceFamily) || DEV_FAMILY_OASIS_MIST.equals(deviceFamily)) { + } + 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)); } - updateState(DEVICE_CHANNEL_CONFIG_TARGET_HUMIDITY, new QuantityType<>(humidifierStatus.result.result.configuration.autoTargetHumidity, Units.PERCENT)); } + + private void parseV2Ver2Poll(final VeSyncV2Ver2BypassHumidifierStatus humidifierStatus) { + if (!"0".equals(humidifierStatus.result.getCode())) { + logger.warn("Check Thing type has been set - API gave a unexpected response for an Air Humidifier"); + return; + } + + updateState(DEVICE_CHANNEL_ENABLED, OnOffType.from(humidifierStatus.result.result.getPowerSwitch())); + updateState(DEVICE_CHANNEL_DISPLAY_ENABLED, OnOffType.from(humidifierStatus.result.result.getScreenSwitch())); + updateState(DEVICE_CHANNEL_WATER_LACKS, OnOffType.from(humidifierStatus.result.result.getWaterLacksState())); + updateState(DEVICE_CHANNEL_WATER_TANK_LIFTED, + OnOffType.from(humidifierStatus.result.result.getWaterTankLifted())); + updateState(DEVICE_CHANNEL_STOP_AT_TARGET, OnOffType.from(humidifierStatus.result.result.getAutoStopSwitch())); + updateState(DEVICE_CHANNEL_HUMIDITY, + new QuantityType<>(humidifierStatus.result.result.humidity, Units.PERCENT)); + updateState(DEVICE_CHANNEL_MIST_LEVEL, new DecimalType(humidifierStatus.result.result.mistLevel)); + if (MODE_AUTO_HUMIDITY.equals(humidifierStatus.result.result.workMode)) { + humidifierStatus.result.result.workMode = MODE_AUTO; + } + updateState(DEVICE_CHANNEL_HUMIDIFIER_MODE, new StringType(humidifierStatus.result.result.workMode)); + updateState(DEVICE_CHANNEL_CONFIG_TARGET_HUMIDITY, + new QuantityType<>(humidifierStatus.result.result.targetHumidity, Units.PERCENT)); + updateState(DEVICE_CHANNEL_ERROR_CODE, new DecimalType(humidifierStatus.result.result.errorCode)); + updateState(DEVICE_CHANNEL_AF_SCHEDULES_COUNT, new DecimalType(humidifierStatus.result.result.scheduleCount)); + if (humidifierStatus.result.result.timerRemain > 0) { + updateState(DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME, new DateTimeType(LocalDateTime.now() + .plus(humidifierStatus.result.result.timerRemain, ChronoUnit.MINUTES).toString())); + } else { + updateState(DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME, new DateTimeItem("nullEnforcements").getState()); + } + } } 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 5c9ce2028e912..308be6d165b48 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 @@ -19,8 +19,11 @@ import java.time.temporal.ChronoUnit; 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 java.util.stream.Collectors; import javax.validation.constraints.NotNull; @@ -29,7 +32,12 @@ import org.openhab.binding.vesync.internal.VeSyncConstants; import org.openhab.binding.vesync.internal.dto.requests.VeSyncRequestManagedDeviceBypassV2; import org.openhab.binding.vesync.internal.dto.requests.VeSyncRequestV1ManagedDeviceDetails; +import org.openhab.binding.vesync.internal.dto.requests.VeSyncRequestV1SetLevel; +import org.openhab.binding.vesync.internal.dto.requests.VeSyncRequestV1SetMode; +import org.openhab.binding.vesync.internal.dto.requests.VeSyncRequestV1SetStatus; +import org.openhab.binding.vesync.internal.dto.responses.VeSyncResponse; import org.openhab.binding.vesync.internal.dto.responses.VeSyncV2BypassPurifierStatus; +import org.openhab.binding.vesync.internal.dto.responses.VeSyncV2Ver2BypassPurifierStatus; import org.openhab.binding.vesync.internal.dto.responses.v1.VeSyncV1AirPurifierDeviceDetailsResponse; import org.openhab.core.cache.ExpiringCache; import org.openhab.core.library.items.DateTimeItem; @@ -55,6 +63,7 @@ * @author David Goodyear - Initial contribution */ @NonNullByDefault +@SuppressWarnings("serial") public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { public static final String DEV_TYPE_FAMILY_AIR_PURIFIER = "LAP"; @@ -68,27 +77,54 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { 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 String DEV_FAMILY_VITAL_100S = "V100S"; - public static final VeSyncDeviceMetadata CORE300S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_300S, - List.of("C301S", "C302S"), List.of("Core300S")); + public static final String DEV_FAMILY_VITAL_200S = "V200S"; - public static final VeSyncDeviceMetadata CORE400S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_400S, List.of("C401S"), - List.of("Core400S")); + private static final List FAN_MODES_WITH_PET = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP, MODE_PET); - public static final VeSyncDeviceMetadata CORE600S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_600S, List.of("C601S"), - List.of("Core600S")); + private static final List FAN_MODES_NO_PET = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP); + private static final List FAN_MODES_MAN_SLEEP = Arrays.asList(MODE_MANUAL, MODE_SLEEP); + private static final List NIGHT_LIGHTS = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF); - public static final VeSyncDeviceMetadata PUR131S = new VeSyncDeviceMetadata(DEV_FAMILY_PUR_131S, - Collections.emptyList(), Arrays.asList("LV-PUR131S", "LV-RH131S")); + private static final List NO_NIGHT_LIGHTS = Collections.emptyList(); + public static final VeSyncDevicePurifierMetadata CORE200S = new VeSyncDevicePurifierMetadata(1, + DEV_FAMILY_CORE_200S, Arrays.asList("C201S", "C202S"), List.of("Core200S"), FAN_MODES_MAN_SLEEP, 1, 3, + NIGHT_LIGHTS); - public static final List SUPPORTED_MODEL_FAMILIES = Arrays.asList(CORE600S, CORE400S, - CORE300S, CORE200S, PUR131S); + public static final VeSyncDevicePurifierMetadata CORE300S = new VeSyncDevicePurifierMetadata(1, + DEV_FAMILY_CORE_300S, List.of("C301S", "C302S"), List.of("Core300S"), FAN_MODES_MAN_SLEEP, 1, 3, + NIGHT_LIGHTS); - 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); - private static final List CORE_200S300S_NIGHT_LIGHT_MODES = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF); + public static final VeSyncDevicePurifierMetadata CORE400S = new VeSyncDevicePurifierMetadata(1, + DEV_FAMILY_CORE_400S, List.of("C401S"), List.of("Core400S"), FAN_MODES_NO_PET, 1, 4, NO_NIGHT_LIGHTS); + + public static final VeSyncDevicePurifierMetadata CORE600S = new VeSyncDevicePurifierMetadata(1, + DEV_FAMILY_CORE_600S, List.of("C601S"), List.of("Core600S"), FAN_MODES_NO_PET, 1, 4, NO_NIGHT_LIGHTS); + + public static final VeSyncDevicePurifierMetadata VITAL100S = new VeSyncDevicePurifierMetadata(2, + DEV_FAMILY_VITAL_100S, List.of("V102S"), Collections.emptyList(), FAN_MODES_NO_PET, 1, 5, NO_NIGHT_LIGHTS); + + public static final VeSyncDevicePurifierMetadata VITAL200S = new VeSyncDevicePurifierMetadata(2, + DEV_FAMILY_VITAL_200S, List.of("V201S"), Collections.emptyList(), FAN_MODES_WITH_PET, 1, 5, + NO_NIGHT_LIGHTS); + + public static final VeSyncDevicePurifierMetadata PUR131S = new VeSyncDevicePurifierMetadata(1, DEV_FAMILY_PUR_131S, + Collections.emptyList(), Arrays.asList("LV-PUR131S", "LV-RH131S"), FAN_MODES_NO_PET, 1, 3, NO_NIGHT_LIGHTS); + + public static final Map DEV_FAMILY_HUMIDIFER_MAP = new HashMap() { + { + put(PUR131S.deviceFamilyName, PUR131S); + put(CORE200S.deviceFamilyName, CORE200S); + put(CORE300S.deviceFamilyName, CORE300S); + put(CORE400S.deviceFamilyName, CORE400S); + put(CORE600S.deviceFamilyName, CORE600S); + put(VITAL100S.deviceFamilyName, VITAL100S); + put(VITAL200S.deviceFamilyName, VITAL200S); + } + }; + public static final List SUPPORTED_MODEL_FAMILIES = DEV_FAMILY_HUMIDIFER_MAP.values().stream() + .collect(Collectors.toList()); private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirPurifierHandler.class); @@ -114,16 +150,28 @@ public void initialize() { switch (deviceFamily) { case DEV_FAMILY_CORE_600S: case DEV_FAMILY_CORE_400S: - toRemove = new String[] { DEVICE_CHANNEL_AF_NIGHT_LIGHT }; + toRemove = new String[] { DEVICE_CHANNEL_AF_NIGHT_LIGHT, DEVICE_CHANNEL_AF_LIGHT_DETECTION, + DEVICE_CHANNEL_AF_LIGHT_DETECTED }; break; 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, - DEVICE_CHANNEL_AF_SCHEDULES_COUNT, DEVICE_CHANNEL_AF_CONFIG_DISPLAY_FOREVER }; + DEVICE_CHANNEL_AIRQUALITY_PM25, DEVICE_CHANNEL_AF_SCHEDULES_COUNT, + DEVICE_CHANNEL_AF_CONFIG_DISPLAY_FOREVER, DEVICE_CHANNEL_ERROR_CODE, + DEVICE_CHANNEL_CHILD_LOCK_ENABLED, DEVICE_CHANNEL_AF_LIGHT_DETECTION, + DEVICE_CHANNEL_AF_LIGHT_DETECTED }; + break; + case DEV_FAMILY_VITAL_100S: + case DEV_FAMILY_VITAL_200S: + toRemove = new String[] { DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME, DEVICE_CHANNEL_AF_SCHEDULES_COUNT, + DEVICE_CHANNEL_AF_NIGHT_LIGHT, DEVICE_CHANNEL_AF_CONFIG_AUTO_MODE_PREF, + DEVICE_CHANNEL_AF_CONFIG_DISPLAY_FOREVER, DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE, + DEVICE_CHANNEL_AF_LIGHT_DETECTION, DEVICE_CHANNEL_AF_LIGHT_DETECTED, + DEVICE_CHANNEL_ERROR_CODE }; break; default: - toRemove = new String[] { DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME, DEVICE_CHANNEL_AF_SCHEDULES_COUNT }; + toRemove = new String[] { DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME, DEVICE_CHANNEL_AF_SCHEDULES_COUNT, + DEVICE_CHANNEL_AF_LIGHT_DETECTION, DEVICE_CHANNEL_AF_LIGHT_DETECTED }; } } return toRemove; @@ -164,111 +212,153 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { if (deviceFamily == null) { return; } + final String deviceUuid = getThing().getProperties().get(DEVICE_PROP_DEVICE_UUID); + if (deviceUuid == null) { + return; + } + final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily); + if (devContraints == null) { + logger.warn("Could not find device family for {} during handleCommand", deviceFamily); + return; + } scheduler.submit(() -> { if (command instanceof OnOffType) { switch (channelUID.getId()) { case DEVICE_CHANNEL_ENABLED: - sendV2BypassControlCommand(DEVICE_SET_SWITCH, - new VeSyncRequestManagedDeviceBypassV2.SetSwitchPayload(command.equals(OnOffType.ON), - 0)); + switch (deviceFamily) { + case DEV_FAMILY_VITAL_100S: + case DEV_FAMILY_VITAL_200S: + sendV2BypassControlCommand(DEVICE_SET_SWITCH, + new VeSyncRequestManagedDeviceBypassV2.SetPowerPayload( + command.equals(OnOffType.ON), 0)); + break; + case DEV_FAMILY_PUR_131S: + sendV1ControlCommand("131airPurifier/v1/device/deviceStatus", + new VeSyncRequestV1SetStatus(deviceUuid, + command.equals(OnOffType.ON) ? "on" : "off")); + break; + default: + sendV2BypassControlCommand(DEVICE_SET_SWITCH, + new VeSyncRequestManagedDeviceBypassV2.SetSwitchPayload( + command.equals(OnOffType.ON), 0)); + } break; case DEVICE_CHANNEL_DISPLAY_ENABLED: - sendV2BypassControlCommand(DEVICE_SET_DISPLAY, - new VeSyncRequestManagedDeviceBypassV2.SetState(command.equals(OnOffType.ON))); + switch (deviceFamily) { + case DEV_FAMILY_VITAL_100S: + case DEV_FAMILY_VITAL_200S: + sendV2BypassControlCommand(DEVICE_SET_DISPLAY, + new VeSyncRequestManagedDeviceBypassV2.SetScreenSwitchPayload( + command.equals(OnOffType.ON))); + break; + case DEV_FAMILY_PUR_131S: + sendV1ControlCommand("131airPurifier/v1/device/updateScreen", + new VeSyncRequestV1SetStatus(deviceUuid, + command.equals(OnOffType.ON) ? "on" : "off")); + break; + default: + sendV2BypassControlCommand(DEVICE_SET_DISPLAY, + new VeSyncRequestManagedDeviceBypassV2.SetState(command.equals(OnOffType.ON))); + + break; + } break; case DEVICE_CHANNEL_CHILD_LOCK_ENABLED: - sendV2BypassControlCommand(DEVICE_SET_CHILD_LOCK, - new VeSyncRequestManagedDeviceBypassV2.SetChildLock(command.equals(OnOffType.ON))); + switch (deviceFamily) { + case DEV_FAMILY_VITAL_100S: + case DEV_FAMILY_VITAL_200S: + sendV2BypassControlCommand(DEVICE_SET_CHILD_LOCK, + new VeSyncRequestManagedDeviceBypassV2.SetChildLockPayload( + command.equals(OnOffType.ON))); + break; + default: + sendV2BypassControlCommand(DEVICE_SET_CHILD_LOCK, + new VeSyncRequestManagedDeviceBypassV2.SetChildLock( + command.equals(OnOffType.ON))); + break; + } + break; + case DEVICE_CHANNEL_AF_LIGHT_DETECTION: + sendV2BypassControlCommand(DEVICE_SET_LIGHT_DETECTION, + new VeSyncRequestManagedDeviceBypassV2.SetLightDetectionPayload( + command.equals(OnOffType.ON))); break; } } else if (command instanceof StringType) { switch (channelUID.getId()) { case DEVICE_CHANNEL_FAN_MODE_ENABLED: final String targetFanMode = command.toString().toLowerCase(); + + if (!devContraints.isFanModeSupported(targetFanMode)) { + logger.warn("Fan mode command for \"{}\" is not valid in the ({}) API possible options {}", + command, devContraints.deviceFamilyName, String.join(",", devContraints.fanModes)); + pollForUpdate(); + return; + } 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 {}", - command, String.join(",", CORE_400S600S_FAN_MODES)); - return; - } + case DEV_FAMILY_VITAL_100S: + case DEV_FAMILY_VITAL_200S: + sendV2BypassControlCommand(DEVICE_SET_PURIFIER_MODE, + new VeSyncRequestManagedDeviceBypassV2.SetWorkModePayload(targetFanMode)); break; - 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 {}", - command, String.join(",", CORE_200S300S_FAN_MODES)); - return; - } + case DEV_FAMILY_PUR_131S: + sendV1ControlCommand("131airPurifier/v1/device/updateMode", + new VeSyncRequestV1SetMode(deviceUuid, targetFanMode)); break; + default: + sendV2BypassControlCommand(DEVICE_SET_PURIFIER_MODE, + new VeSyncRequestManagedDeviceBypassV2.SetMode(targetFanMode)); } - - sendV2BypassControlCommand(DEVICE_SET_PURIFIER_MODE, - new VeSyncRequestManagedDeviceBypassV2.SetMode(targetFanMode)); break; case DEVICE_CHANNEL_AF_NIGHT_LIGHT: final String targetNightLightMode = command.toString().toLowerCase(); - switch (deviceFamily) { - case DEV_FAMILY_CORE_600S: - case DEV_FAMILY_CORE_400S: - logger.warn("Core400S API does not support night light"); - return; - 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 {}", - command, String.join(",", CORE_200S300S_NIGHT_LIGHT_MODES)); - return; - } - - sendV2BypassControlCommand(DEVICE_SET_NIGHT_LIGHT, - new VeSyncRequestManagedDeviceBypassV2.SetNightLight(targetNightLightMode)); - - break; + if (!devContraints.isNightLightModeSupported(targetNightLightMode)) { + logger.warn( + "Night light mode command for \"{}\" is not valid in the ({}) API possible options {}", + command, devContraints.deviceFamilyName, + String.join(",", devContraints.nightLightModes)); + pollForUpdate(); + return; } + sendV2BypassControlCommand(DEVICE_SET_NIGHT_LIGHT, + new VeSyncRequestManagedDeviceBypassV2.SetNightLight(targetNightLightMode)); break; } } else if (command instanceof QuantityType quantityCommand) { switch (channelUID.getId()) { case DEVICE_CHANNEL_FAN_SPEED_ENABLED: - // If the fan speed is being set enforce manual mode - sendV2BypassControlCommand(DEVICE_SET_PURIFIER_MODE, - new VeSyncRequestManagedDeviceBypassV2.SetMode(MODE_MANUAL), false); - - int requestedLevel = quantityCommand.intValue(); - if (requestedLevel < 1) { - logger.warn("Fan speed command less than 1 - adjusting to 1 as the valid API value"); - requestedLevel = 1; + int requestedLevel = ((QuantityType) command).intValue(); + if (!devContraints.isFanSpeedSupported(requestedLevel)) { + logger.warn("Fan speed command for \"{}\" is not valid ({}) API possible options {} -> {}", + command, devContraints.deviceFamilyName, String.valueOf(devContraints.minFanSpeed), + String.valueOf(devContraints.maxFanSpeed)); + requestedLevel = requestedLevel < devContraints.minFanSpeed ? devContraints.minFanSpeed + : devContraints.maxFanSpeed; } - 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; - } + case DEV_FAMILY_VITAL_100S: + case DEV_FAMILY_VITAL_200S: + sendV2BypassControlCommand(DEVICE_SET_PURIFIER_MODE, + new VeSyncRequestManagedDeviceBypassV2.SetWorkModePayload(MODE_MANUAL)); + sendV2BypassControlCommand(DEVICE_SET_LEVEL, + new VeSyncRequestManagedDeviceBypassV2.SetManualSpeedLevelPayload( + requestedLevel)); break; - 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"); - requestedLevel = 3; - } + case DEV_FAMILY_PUR_131S: + sendV1ControlCommand("131airPurifier/v1/device/updateMode", + new VeSyncRequestV1SetMode(deviceUuid, MODE_MANUAL), false); + sendV1ControlCommand("131airPurifier/v1/device/updateSpeed", + new VeSyncRequestV1SetLevel(deviceUuid, requestedLevel)); break; + default: + sendV2BypassControlCommand(DEVICE_SET_PURIFIER_MODE, + new VeSyncRequestManagedDeviceBypassV2.SetMode(MODE_MANUAL), false); + sendV2BypassControlCommand(DEVICE_SET_LEVEL, + new VeSyncRequestManagedDeviceBypassV2.SetLevelPayload(0, + DEVICE_LEVEL_TYPE_WIND, requestedLevel)); } - - sendV2BypassControlCommand(DEVICE_SET_LEVEL, - new VeSyncRequestManagedDeviceBypassV2.SetLevelPayload(0, DEVICE_LEVEL_TYPE_WIND, - requestedLevel)); break; } } else if (command instanceof RefreshType) { @@ -285,17 +375,10 @@ protected void pollForDeviceData(final ExpiringCache cachedResponse) { if (deviceFamily == null) { return; } - - 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_FAMILY_PUR_131S: - processV1AirPurifierPoll(cachedResponse); - break; + if (!DEV_FAMILY_PUR_131S.equals(deviceFamily)) { + processV2BypassPoll(cachedResponse); + } else { + processV1AirPurifierPoll(cachedResponse); } } @@ -312,7 +395,7 @@ private void processV1AirPurifierPoll(final ExpiringCache cachedResponse boolean cachedDataUsed = response != null; if (response == null) { logger.trace("Requesting fresh response"); - response = sendV1Command("POST", "https://smartapi.vesync.com/131airPurifier/v1/device/deviceDetail", + response = sendV1Command("131airPurifier/v1/device/deviceDetail", new VeSyncRequestV1ManagedDeviceDetails(deviceUuid)); } else { logger.trace("Using cached response {}", response); @@ -353,11 +436,22 @@ private void processV1AirPurifierPoll(final ExpiringCache cachedResponse updateState(DEVICE_CHANNEL_FAN_SPEED_ENABLED, new DecimalType(String.valueOf(purifierStatus.getLevel()))); updateState(DEVICE_CHANNEL_DISPLAY_ENABLED, OnOffType.from(MODE_ON.equals(purifierStatus.getScreenStatus()))); updateState(DEVICE_CHANNEL_AIRQUALITY_BASIC, new DecimalType(purifierStatus.getAirQuality())); + updateState(DEVICE_CHANNEL_AIR_FILTER_LIFE_PERCENTAGE_REMAINING, + new QuantityType<>(purifierStatus.filter.getPercent(), Units.PERCENT)); } private void processV2BypassPoll(final ExpiringCache cachedResponse) { + final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY); + + final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily); + if (devContraints == null) { + logger.warn("Could not find device family for {} during handleCommand", deviceFamily); + return; + } + String response; - VeSyncV2BypassPurifierStatus purifierStatus; + VeSyncResponse purifierStatus = null; + synchronized (pollLock) { response = cachedResponse.getValue(); boolean cachedDataUsed = response != null; @@ -372,8 +466,11 @@ private void processV2BypassPoll(final ExpiringCache cachedResponse) { if (response.equals(EMPTY_STRING)) { return; } - - purifierStatus = VeSyncConstants.GSON.fromJson(response, VeSyncV2BypassPurifierStatus.class); + if (devContraints.protocolV2Version == 2) { + purifierStatus = VeSyncConstants.GSON.fromJson(response, VeSyncV2Ver2BypassPurifierStatus.class); + } else { + purifierStatus = VeSyncConstants.GSON.fromJson(response, VeSyncV2BypassPurifierStatus.class); + } if (purifierStatus == null) { return; @@ -393,6 +490,14 @@ private void processV2BypassPoll(final ExpiringCache cachedResponse) { updateStatus(ThingStatus.ONLINE); } + if (devContraints.protocolV2Version == 2) { + parseV2Ver2Poll((VeSyncV2Ver2BypassPurifierStatus) purifierStatus); + } else { + parseV2Ver1Poll((VeSyncV2BypassPurifierStatus) purifierStatus); + } + } + + private void parseV2Ver1Poll(final VeSyncV2BypassPurifierStatus purifierStatus) { if (!"0".equals(purifierStatus.result.getCode())) { logger.warn("Check Thing type has been set - API gave a unexpected response for an Air Purifier"); return; @@ -409,13 +514,10 @@ private void processV2BypassPoll(final ExpiringCache cachedResponse) { updateState(DEVICE_CHANNEL_AIRQUALITY_BASIC, new DecimalType(purifierStatus.result.result.airQuality)); updateState(DEVICE_CHANNEL_AIRQUALITY_PM25, new QuantityType<>(purifierStatus.result.result.airQualityValue, Units.MICROGRAM_PER_CUBICMETRE)); - updateState(DEVICE_CHANNEL_AF_CONFIG_DISPLAY_FOREVER, OnOffType.from(purifierStatus.result.result.configuration.displayForever)); - updateState(DEVICE_CHANNEL_AF_CONFIG_AUTO_MODE_PREF, new StringType(purifierStatus.result.result.configuration.autoPreference.autoType)); - updateState(DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE, new DecimalType(purifierStatus.result.result.configuration.autoPreference.roomSize)); @@ -436,4 +538,28 @@ private void processV2BypassPoll(final ExpiringCache cachedResponse) { updateState(DEVICE_CHANNEL_AF_NIGHT_LIGHT, new DecimalType(purifierStatus.result.result.nightLight)); } } + + private void parseV2Ver2Poll(final VeSyncV2Ver2BypassPurifierStatus purifierStatus) { + if (!"0".equals(purifierStatus.result.getCode())) { + logger.warn("Check Thing type has been set - API gave a unexpected response for an Air Purifier"); + return; + } + + updateState(DEVICE_CHANNEL_ENABLED, OnOffType.from(purifierStatus.result.result.getPowerSwitch())); + updateState(DEVICE_CHANNEL_CHILD_LOCK_ENABLED, + OnOffType.from(purifierStatus.result.result.getChildLockSwitch())); + updateState(DEVICE_CHANNEL_AIRQUALITY_BASIC, new DecimalType(purifierStatus.result.result.airQuality)); + updateState(DEVICE_CHANNEL_AIRQUALITY_PM25, + new QuantityType<>(purifierStatus.result.result.PM25, Units.MICROGRAM_PER_CUBICMETRE)); + updateState(DEVICE_CHANNEL_AIR_FILTER_LIFE_PERCENTAGE_REMAINING, + new QuantityType<>(purifierStatus.result.result.filterLifePercent, Units.PERCENT)); + updateState(DEVICE_CHANNEL_AF_LIGHT_DETECTION, + OnOffType.from(purifierStatus.result.result.getLightDetectionSwitch())); + updateState(DEVICE_CHANNEL_AF_LIGHT_DETECTED, + OnOffType.from(purifierStatus.result.result.getEnvironmentLightState())); + updateState(DEVICE_CHANNEL_DISPLAY_ENABLED, OnOffType.from(purifierStatus.result.result.getScreenSwitch())); + updateState(DEVICE_CHANNEL_FAN_MODE_ENABLED, new StringType(purifierStatus.result.result.workMode)); + updateState(DEVICE_CHANNEL_FAN_SPEED_ENABLED, new DecimalType(purifierStatus.result.result.fanSpeedLevel)); + updateState(DEVICE_CHANNEL_ERROR_CODE, new DecimalType(purifierStatus.result.result.errorCode)); + } } diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java new file mode 100644 index 0000000000000..f0401924e98a7 --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java @@ -0,0 +1,97 @@ +/** + * 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 int v2version, final String deviceFamilyName, + final List deviceGenerations, final List nonStandardIds, final List fanModes, + final int targetMinMistLevel, final int targetMaxMistLevel, final int targetMinWarmMistLevel, + final int targetMaxWarmMistLevel, final boolean remapsAutoToHumidity, List nightLightModes) { + super(deviceFamilyName, deviceGenerations, nonStandardIds); + this.fanModes = fanModes; + this.targetMinMistLevel = targetMinMistLevel; + this.targetMaxMistLevel = targetMaxMistLevel; + this.targetMinWarmMistLevel = targetMinWarmMistLevel; + this.targetMaxWarmMistLevel = targetMaxWarmMistLevel; + this.remapsAutoToHumidity = remapsAutoToHumidity; + this.nightLightModes = nightLightModes; + this.protocolV2Version = v2version; + } + + public final int protocolV2Version; + + /** + * The fan modes supported by this generation of device + */ + public final List fanModes; + + /** + * The minimum target mist level supported + */ + public final int targetMinMistLevel; + + /** + * The maximum target mist level supported + */ + public final int targetMaxMistLevel; + + public final boolean isTargetMistLevelSupported(final int target) { + return target >= targetMinMistLevel && target <= targetMaxMistLevel; + } + + /** + * The minimum target mist level supported + */ + public final int targetMinWarmMistLevel; + + /** + * The maximum target mist level supported + */ + public final int targetMaxWarmMistLevel; + + public final boolean isTargetWramMistLevelSupported(final int target) { + return target >= targetMinWarmMistLevel && target <= targetMaxWarmMistLevel; + } + + /** + * Stores whether auto in openhab is humidity mode in the protocol + */ + public final boolean remapsAutoToHumidity; + + public String getProtocolMode(final String mode) { + if (!remapsAutoToHumidity) { + return mode; + } else { + if (mode.equals(MODE_AUTO)) { + return MODE_AUTO_HUMIDITY; + } + return mode; + } + } + + public List nightLightModes; +} diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDevicePurifierMetadata.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDevicePurifierMetadata.java new file mode 100644 index 0000000000000..0c1c1568439ff --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDevicePurifierMetadata.java @@ -0,0 +1,71 @@ +/** + * 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 VeSyncDevicePurifierMetadata} class contains the definition for the control of humidifer device types. + * + * @author David Goodyear - Initial contribution + */ +@NonNullByDefault +public class VeSyncDevicePurifierMetadata extends VeSyncDeviceMetadata { + + public VeSyncDevicePurifierMetadata(final int v2version, final String deviceFamilyName, + final List deviceGenerations, final List nonStandardIds, final List fanModes, + final int minFanSpeed, final int maxFanSpeed, final List nightLightModes) { + super(deviceFamilyName, deviceGenerations, nonStandardIds); + this.fanModes = fanModes; + this.minFanSpeed = minFanSpeed; + this.maxFanSpeed = maxFanSpeed; + this.nightLightModes = nightLightModes; + this.protocolV2Version = v2version; + } + + public final int protocolV2Version; + + /** + * The fan modes supported by this generation of device + */ + public final List fanModes; + + /** + * The minimum fan speed supported + */ + public final int minFanSpeed; + + /** + * The maximum fan speed supported + */ + public final int maxFanSpeed; + + /** + * The night light supported by this generation of device + */ + public final List nightLightModes; + + public final boolean isFanModeSupported(final String fanMode) { + return fanModes.contains(fanMode); + } + + public final boolean isFanSpeedSupported(final int speed) { + return speed >= minFanSpeed && speed <= maxFanSpeed; + } + + public final boolean isNightLightModeSupported(final String nightLightMode) { + return nightLightModes.contains(nightLightMode); + } +} diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties index 240a2cf0e09a8..466c8930f680c 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties @@ -45,6 +45,7 @@ channel-type.vesync.airPurifierModeType.description = The operating mode the air channel-type.vesync.airPurifierModeType.state.option.auto = Auto channel-type.vesync.airPurifierModeType.state.option.manual = Manual Fan Control channel-type.vesync.airPurifierModeType.state.option.sleep = Sleeping Auto +channel-type.vesync.airPurifierModeType.state.option.pet = Pet Auto channel-type.vesync.airQualityPM25.label = Air Quality PPM2.5 channel-type.vesync.airQualityPM25.description = Indicator of current air quality channel-type.vesync.deviceAFConfigAutoPrefRoomSizeType.label = Config: Room size @@ -66,6 +67,10 @@ channel-type.vesync.deviceAFNightLight.state.option.off = Off channel-type.vesync.deviceAFTimerExpiry.label = Auto Off Expiry channel-type.vesync.deviceAFTimerExpiry.description = The time when the auto off timer will be reached channel-type.vesync.deviceAFTimerExpiry.state.pattern = %1$tF %1$tR +channel-type.vesync.deviceAFLightDetection.label = Light Detection +channel-type.vesync.deviceAFLightDetection.description = If the devices light detection is enabled +channel-type.vesync.deviceAFLightDetected.label = Light Detected +channel-type.vesync.deviceAFLightDetected.description = Indicator if the device detects light channel-type.vesync.deviceAirQualityBasicType.label = Air Quality channel-type.vesync.deviceAirQualityBasicType.description = System representation of air quality channel-type.vesync.deviceAutomaticStopReachTargetType.label = Stop @ Set Point 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 786f2f804b524..2263b59a52282 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 @@ -64,6 +64,9 @@ + + + @@ -109,6 +112,8 @@ + + @@ -169,6 +174,7 @@ + @@ -221,6 +227,20 @@ Configuration: If the devices display is enabled forever + + Switch + + If the devices light detection is enabled + + + + + Switch + + Indicator if the device detects light + + + String @@ -332,5 +352,4 @@ - From 0eb55a1c4c9a8c8e9a47fa9f6dc92f3f9aa1f364 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 6 Oct 2024 03:01:33 +0100 Subject: [PATCH 02/33] [veSync] Rebase [veSync] Rebase Signed-off-by: dag81 --- .../vesync/internal/discovery/VeSyncDiscoveryService.java | 3 --- .../internal/dto/requests/VeSyncRequestV1Command.java | 2 +- .../internal/dto/requests/VeSyncRequestV1SetLevel.java | 2 +- .../internal/dto/requests/VeSyncRequestV1SetMode.java | 2 +- .../internal/dto/requests/VeSyncRequestV1SetStatus.java | 2 +- .../dto/responses/VeSyncV2Ver2BypassHumidifierStatus.java | 2 +- .../dto/responses/VeSyncV2Ver2BypassPurifierStatus.java | 2 +- .../internal/handlers/VeSyncDeviceHumidifierMetadata.java | 2 +- .../internal/handlers/VeSyncDevicePurifierMetadata.java | 2 +- .../src/main/resources/OH-INF/i18n/vesync.properties | 8 ++++---- 10 files changed, 12 insertions(+), 15 deletions(-) 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 6af53e590c6e4..0bb8e36baf99c 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 @@ -85,9 +85,6 @@ protected void stopBackgroundDiscovery() { @Override protected void startScan() { - if (bridgeHandler == null) { - return; - } // If the bridge is not online no other thing devices can be found, so no reason to scan at this moment. removeOlderResults(getTimestampOfLastScan()); if (ThingStatus.ONLINE.equals(thingHandler.getThing().getStatus())) { diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java index a1452f09695df..0b7b46449b358 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetLevel.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetLevel.java index ea50568d0cf76..aa132cb782fe5 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetLevel.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetLevel.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetMode.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetMode.java index b96f6763d7978..92b8eff8c9a7b 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetMode.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetMode.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetStatus.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetStatus.java index 80a1d292be7cd..fc0a51a7c0f78 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetStatus.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1SetStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassHumidifierStatus.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassHumidifierStatus.java index d0175a5cb01e4..2820b86321278 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassHumidifierStatus.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassHumidifierStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java index 4fd8b3d0b4543..12d4ba6da7525 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java index f0401924e98a7..d1494f9be42e3 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDevicePurifierMetadata.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDevicePurifierMetadata.java index 0c1c1568439ff..4b6c0a95bfc3d 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDevicePurifierMetadata.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDevicePurifierMetadata.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties index 466c8930f680c..de97c54680d62 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties @@ -59,6 +59,10 @@ channel-type.vesync.deviceAFConfigAutoScheduleCountType.label = Config: Schedule channel-type.vesync.deviceAFConfigAutoScheduleCountType.description = The current number of schedules configured channel-type.vesync.deviceAFConfigDisplayForever.label = Config: Display Forever channel-type.vesync.deviceAFConfigDisplayForever.description = Configuration: If the devices display is enabled forever +channel-type.vesync.deviceAFLightDetected.label = Light Detected +channel-type.vesync.deviceAFLightDetected.description = Indicator if the device detects light +channel-type.vesync.deviceAFLightDetection.label = Light Detection +channel-type.vesync.deviceAFLightDetection.description = If the devices light detection is enabled channel-type.vesync.deviceAFNightLight.label = Night Light channel-type.vesync.deviceAFNightLight.description = The operating mode of the night light functionality channel-type.vesync.deviceAFNightLight.state.option.on = On @@ -67,10 +71,6 @@ channel-type.vesync.deviceAFNightLight.state.option.off = Off channel-type.vesync.deviceAFTimerExpiry.label = Auto Off Expiry channel-type.vesync.deviceAFTimerExpiry.description = The time when the auto off timer will be reached channel-type.vesync.deviceAFTimerExpiry.state.pattern = %1$tF %1$tR -channel-type.vesync.deviceAFLightDetection.label = Light Detection -channel-type.vesync.deviceAFLightDetection.description = If the devices light detection is enabled -channel-type.vesync.deviceAFLightDetected.label = Light Detected -channel-type.vesync.deviceAFLightDetected.description = Indicator if the device detects light channel-type.vesync.deviceAirQualityBasicType.label = Air Quality channel-type.vesync.deviceAirQualityBasicType.description = System representation of air quality channel-type.vesync.deviceAutomaticStopReachTargetType.label = Stop @ Set Point From 6d1034a89b2571af3f4228d0af0aa819291ec3ed Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 6 Oct 2024 17:23:07 +0100 Subject: [PATCH 03/33] [veSync] SCA Adjustments [veSync] SCA Adjustments Signed-off-by: dag81 --- .../internal/dto/requests/VeSyncProtocolConstants.java | 6 ++++-- .../responses/VeSyncV2Ver2BypassPurifierStatus.java | 2 +- .../internal/handlers/VeSyncBaseDeviceHandler.java | 6 +----- .../handlers/VeSyncDeviceAirPurifierHandler.java | 10 +++++----- .../vesync/internal/handlers/VeSyncDeviceMetadata.java | 9 ++------- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java index 741ff024bf2fe..8b488faf75dec 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java @@ -57,8 +57,10 @@ public interface VeSyncProtocolConstants { /** * Base URL for AUTHENTICATION REQUESTS */ - String PROTOCOL = "https"; - String SERVER_ADDRESS = "smartapi.vesync.com"; + // String PROTOCOL = "https"; + String PROTOCOL = "http"; + // String SERVER_ADDRESS = "smartapi.vesync.com"; + String SERVER_ADDRESS = "10.2.0.101"; String SERVER_ENDPOINT = PROTOCOL + "://" + SERVER_ADDRESS; String HOST_ENDPOINT = SERVER_ENDPOINT + "/cloud"; diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java index 12d4ba6da7525..863d1ca68ccc9 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncV2Ver2BypassPurifierStatus.java @@ -89,7 +89,7 @@ public boolean getScreenSwitch() { } @SerializedName("PM25") - public int PM25; + public int pm25; @SerializedName("timerRemain") public int timerRemain; 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 1140c3c64ee09..e7a6b6b526a97 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 @@ -154,11 +154,7 @@ protected boolean isDeviceOnline() { @Nullable VeSyncManagedDeviceBase metadata = veSyncBridgeHandler.api.getMacLookupMap().get(deviceLookupKey); - if (metadata == null) { - return false; - } - - return ("online".equals(metadata.connectionStatus)); + return !(metadata == null) || "online".equals(metadata.connectionStatus); } return false; } 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 308be6d165b48..0475f7cbe43b9 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 @@ -112,7 +112,7 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { public static final VeSyncDevicePurifierMetadata PUR131S = new VeSyncDevicePurifierMetadata(1, DEV_FAMILY_PUR_131S, Collections.emptyList(), Arrays.asList("LV-PUR131S", "LV-RH131S"), FAN_MODES_NO_PET, 1, 3, NO_NIGHT_LIGHTS); - public static final Map DEV_FAMILY_HUMIDIFER_MAP = new HashMap() { + public static final Map DEV_FAMILY_PURIFIER_MAP = new HashMap() { { put(PUR131S.deviceFamilyName, PUR131S); put(CORE200S.deviceFamilyName, CORE200S); @@ -123,7 +123,7 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { put(VITAL200S.deviceFamilyName, VITAL200S); } }; - public static final List SUPPORTED_MODEL_FAMILIES = DEV_FAMILY_HUMIDIFER_MAP.values().stream() + public static final List SUPPORTED_MODEL_FAMILIES = DEV_FAMILY_PURIFIER_MAP.values().stream() .collect(Collectors.toList()); private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirPurifierHandler.class); @@ -216,7 +216,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { if (deviceUuid == null) { return; } - final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily); + final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_PURIFIER_MAP.get(deviceFamily); if (devContraints == null) { logger.warn("Could not find device family for {} during handleCommand", deviceFamily); return; @@ -443,7 +443,7 @@ private void processV1AirPurifierPoll(final ExpiringCache cachedResponse private void processV2BypassPoll(final ExpiringCache cachedResponse) { final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY); - final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily); + final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_PURIFIER_MAP.get(deviceFamily); if (devContraints == null) { logger.warn("Could not find device family for {} during handleCommand", deviceFamily); return; @@ -550,7 +550,7 @@ private void parseV2Ver2Poll(final VeSyncV2Ver2BypassPurifierStatus purifierStat OnOffType.from(purifierStatus.result.result.getChildLockSwitch())); updateState(DEVICE_CHANNEL_AIRQUALITY_BASIC, new DecimalType(purifierStatus.result.result.airQuality)); updateState(DEVICE_CHANNEL_AIRQUALITY_PM25, - new QuantityType<>(purifierStatus.result.result.PM25, Units.MICROGRAM_PER_CUBICMETRE)); + new QuantityType<>(purifierStatus.result.result.pm25, Units.MICROGRAM_PER_CUBICMETRE)); updateState(DEVICE_CHANNEL_AIR_FILTER_LIFE_PERCENTAGE_REMAINING, new QuantityType<>(purifierStatus.result.result.filterLifePercent, Units.PERCENT)); updateState(DEVICE_CHANNEL_AF_LIGHT_DETECTION, 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 index 431829b633df1..5dc25bcc54d35 100644 --- 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 @@ -51,13 +51,8 @@ public VeSyncDeviceMetadata(final String deviceFamilyName, final List de public final 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; + return nonStandardIds.contains(deviceType) + || (deviceTypeSegments.length == 3 && deviceGenerations.contains(deviceTypeSegments[1])); } public String getDeviceFamilyName() { From 5bdb78674e2118641baf2865cb36c3086003c7c7 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 6 Oct 2024 18:13:19 +0100 Subject: [PATCH 04/33] [veSync] SCA Adjustments Fix [veSync] SCA Adjustments Fix Signed-off-by: dag81 --- .../internal/dto/requests/VeSyncProtocolConstants.java | 6 ++---- .../vesync/internal/handlers/VeSyncBaseDeviceHandler.java | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java index 8b488faf75dec..741ff024bf2fe 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java @@ -57,10 +57,8 @@ public interface VeSyncProtocolConstants { /** * Base URL for AUTHENTICATION REQUESTS */ - // String PROTOCOL = "https"; - String PROTOCOL = "http"; - // String SERVER_ADDRESS = "smartapi.vesync.com"; - String SERVER_ADDRESS = "10.2.0.101"; + String PROTOCOL = "https"; + String SERVER_ADDRESS = "smartapi.vesync.com"; String SERVER_ENDPOINT = PROTOCOL + "://" + SERVER_ADDRESS; String HOST_ENDPOINT = SERVER_ENDPOINT + "/cloud"; 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 e7a6b6b526a97..a1834d119c3da 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 @@ -153,8 +153,7 @@ protected boolean isDeviceOnline() { if (bridgeHandler instanceof VeSyncBridgeHandler veSyncBridgeHandler) { @Nullable VeSyncManagedDeviceBase metadata = veSyncBridgeHandler.api.getMacLookupMap().get(deviceLookupKey); - - return !(metadata == null) || "online".equals(metadata.connectionStatus); + return metadata != null && "online".equals(metadata.connectionStatus); } return false; } From 6073de348fbf27b230534432a683dc7cf6fed19a Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 6 Oct 2024 18:33:08 +0100 Subject: [PATCH 05/33] [veSync] PR corrections [veSync] PR corrections Signed-off-by: dag81 --- .../binding/vesync/internal/dto/requests/VeSyncRequest.java | 4 ++-- .../vesync/internal/dto/requests/VeSyncRequestV1Command.java | 5 ++--- .../dto/requests/VeSyncRequestV1ManagedDeviceDetails.java | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java index cfe2dccf99355..f4d7224dcee85 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.vesync.internal.dto.requests; -import javax.ws.rs.HttpMethod; +import org.eclipse.jetty.http.HttpMethod; import com.google.gson.annotations.SerializedName; @@ -23,7 +23,7 @@ */ public class VeSyncRequest { - public transient String httpMethod; + public transient HttpMethod httpMethod; @SerializedName("timeZone") public String timeZone = "America/New_York"; diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java index 0b7b46449b358..77a6f8c472b5a 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1Command.java @@ -12,14 +12,13 @@ */ package org.openhab.binding.vesync.internal.dto.requests; -import javax.ws.rs.HttpMethod; +import org.eclipse.jetty.http.HttpMethod; import com.google.gson.annotations.SerializedName; /** * The {@link VeSyncRequestV1Command} is the Java class as a DTO to define the base implementation of a V1 command for - * the Vesync - * API. + * the Vesync API. * * @author David Goodyear - Initial contribution */ diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1ManagedDeviceDetails.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1ManagedDeviceDetails.java index 3d6fc580a8d28..e0f2bb8edbab1 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1ManagedDeviceDetails.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestV1ManagedDeviceDetails.java @@ -19,8 +19,7 @@ /** * The {@link VeSyncRequestV1ManagedDeviceDetails} is the Java class as a DTO to request the managed device details for - * the Vesync - * API. + * the Vesync API. * * @author David Goodyear - Initial contribution */ From c823115912f9e7b5527d47e500e2393931f9317c Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 6 Oct 2024 18:46:10 +0100 Subject: [PATCH 06/33] [veSync] Magic number removal [veSync] Magic number removal Signed-off-by: dag81 --- .../binding/vesync/internal/api/VeSyncV2ApiHelper.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java index e491f3cd8ce88..8a063d40070de 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java @@ -58,10 +58,12 @@ public class VeSyncV2ApiHelper { private final Logger logger = LoggerFactory.getLogger(VeSyncV2ApiHelper.class); - private @NonNullByDefault({}) HttpClient httpClient; + private final int RESPONSE_TIMEOUT_SEC = 5; private volatile @Nullable VeSyncUserSession loggedInSession; + private @NonNullByDefault({}) HttpClient httpClient; + private Map macLookup; public VeSyncV2ApiHelper() { @@ -177,7 +179,7 @@ private String directReqV1Authorized(final String url, final VeSyncAuthenticated request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8"); - ContentResponse response = request.timeout(5, TimeUnit.SECONDS).send(); + ContentResponse response = request.timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS).send(); if (response.getStatus() == HttpURLConnection.HTTP_OK) { VeSyncResponse commResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(), VeSyncResponse.class); @@ -229,7 +231,7 @@ private VeSyncLoginResponse processLogin(String username, String password, Strin request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8"); - ContentResponse response = request.timeout(5, TimeUnit.SECONDS).send(); + ContentResponse response = request.timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS).send(); if (response.getStatus() == HttpURLConnection.HTTP_OK) { VeSyncLoginResponse loginResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(), VeSyncLoginResponse.class); From d9fd05421ae3b4f4e72f35bec866484582583d4f Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 6 Oct 2024 18:54:35 +0100 Subject: [PATCH 07/33] [veSync] Http calls cleanup [veSync] Http calls cleanup Signed-off-by: dag81 --- .../vesync/internal/api/VeSyncV2ApiHelper.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java index 8a063d40070de..7e33d53661f8e 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java @@ -34,6 +34,7 @@ import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; import org.openhab.binding.vesync.internal.VeSyncConstants; import org.openhab.binding.vesync.internal.dto.requests.VeSyncAuthenticatedRequest; import org.openhab.binding.vesync.internal.dto.requests.VeSyncLoginCredentials; @@ -58,7 +59,7 @@ public class VeSyncV2ApiHelper { private final Logger logger = LoggerFactory.getLogger(VeSyncV2ApiHelper.class); - private final int RESPONSE_TIMEOUT_SEC = 5; + private static final int RESPONSE_TIMEOUT_SEC = 5; private volatile @Nullable VeSyncUserSession loggedInSession; @@ -169,7 +170,8 @@ public String reqV1Authorized(final String url, final VeSyncAuthenticatedRequest private String directReqV1Authorized(final String url, final VeSyncAuthenticatedRequest requestData) throws AuthenticationException { try { - Request request = httpClient.newRequest(url).method(requestData.httpMethod); + Request request = httpClient.newRequest(url).method(requestData.httpMethod).timeout(RESPONSE_TIMEOUT_SEC, + TimeUnit.SECONDS); // No headers for login request.content(new StringContentProvider(VeSyncConstants.GSON.toJson(requestData))); @@ -179,7 +181,7 @@ private String directReqV1Authorized(final String url, final VeSyncAuthenticated request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8"); - ContentResponse response = request.timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS).send(); + ContentResponse response = request.send(); if (response.getStatus() == HttpURLConnection.HTTP_OK) { VeSyncResponse commResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(), VeSyncResponse.class); @@ -223,7 +225,8 @@ public void updateBridgeData(final VeSyncBridgeHandler bridge) { private VeSyncLoginResponse processLogin(String username, String password, String timezone) throws AuthenticationException { try { - Request request = httpClient.POST(V1_LOGIN_ENDPOINT); + Request request = httpClient.newRequest(V1_LOGIN_ENDPOINT).method(HttpMethod.POST) + .timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS); // No headers for login request.content(new StringContentProvider( @@ -231,7 +234,7 @@ private VeSyncLoginResponse processLogin(String username, String password, Strin request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8"); - ContentResponse response = request.timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS).send(); + ContentResponse response = request.send(); if (response.getStatus() == HttpURLConnection.HTTP_OK) { VeSyncLoginResponse loginResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(), VeSyncLoginResponse.class); From 41d429db41a09cd8e7f7bc3bfbfacad51cea1fc6 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 6 Oct 2024 19:24:48 +0100 Subject: [PATCH 08/33] [veSync] HttpClientFactory cleanup [veSync] HttpClientFactory cleanup Signed-off-by: dag81 --- .../vesync/internal/VeSyncHandlerFactory.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java index eeb63d8f85985..4c014a1506448 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java @@ -30,6 +30,7 @@ import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @@ -46,7 +47,13 @@ public class VeSyncHandlerFactory extends BaseThingHandlerFactory implements IHt private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE, THING_TYPE_AIR_PURIFIER, THING_TYPE_AIR_HUMIDIFIER); - private @Nullable HttpClient httpClientRef = null; + private HttpClientFactory httpClientFactory; + + @Activate + public VeSyncHandlerFactory(@Reference HttpClientFactory httpClientFactory) { + super(); + this.httpClientFactory = httpClientFactory; + } @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { @@ -68,13 +75,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { return null; } - @Reference - protected void setHttpClientFactory(HttpClientFactory httpClientFactory) { - httpClientRef = httpClientFactory.getCommonHttpClient(); - } - @Override public @Nullable HttpClient getHttpClient() { - return httpClientRef; + return httpClientFactory.getCommonHttpClient(); } } From e497a9c438a6d0475fd8ba97e9ce8bd8ee6ed72b Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 6 Oct 2024 20:09:43 +0100 Subject: [PATCH 09/33] [veSync] Bridge i18n support improvements [veSync] Bridge i18n support improvements Signed-off-by: dag81 --- .../vesync/internal/VeSyncHandlerFactory.java | 13 +++++++-- .../handlers/VeSyncBridgeHandler.java | 29 ++++++++++++++++--- .../resources/OH-INF/i18n/vesync.properties | 8 +++++ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java index 4c014a1506448..c6b38b6fb59da 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java @@ -23,6 +23,8 @@ 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.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; @@ -47,12 +49,17 @@ public class VeSyncHandlerFactory extends BaseThingHandlerFactory implements IHt private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE, THING_TYPE_AIR_PURIFIER, THING_TYPE_AIR_HUMIDIFIER); - private HttpClientFactory httpClientFactory; + private final HttpClientFactory httpClientFactory; + private final TranslationProvider translationProvider; + private final LocaleProvider localeProvider; @Activate - public VeSyncHandlerFactory(@Reference HttpClientFactory httpClientFactory) { + public VeSyncHandlerFactory(@Reference HttpClientFactory httpClientFactory, + @Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) { super(); this.httpClientFactory = httpClientFactory; + this.translationProvider = translationProvider; + this.localeProvider = localeProvider; } @Override @@ -69,7 +76,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } else if (VeSyncDeviceAirHumidifierHandler.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) { return new VeSyncDeviceAirHumidifierHandler(thing); } else if (THING_TYPE_BRIDGE.equals(thingTypeUID)) { - return new VeSyncBridgeHandler((Bridge) thing, this); + return new VeSyncBridgeHandler((Bridge) thing, this, translationProvider, localeProvider); } return null; 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 b760b2e00fd51..0dfe0e334415f 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 @@ -17,6 +17,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; @@ -36,6 +37,8 @@ import org.openhab.binding.vesync.internal.dto.responses.VeSyncUserSession; import org.openhab.binding.vesync.internal.exceptions.AuthenticationException; import org.openhab.binding.vesync.internal.exceptions.DeviceUnknownException; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -46,6 +49,9 @@ import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,9 +78,22 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie private volatile int backgroundScanTime = -1; private final Object scanConfigLock = new Object(); - public VeSyncBridgeHandler(Bridge bridge, @NotNull IHttpClientProvider httpClientProvider) { + private final TranslationProvider translationProvider; + private final LocaleProvider localeProvider; + private final Bundle bundle; + + public VeSyncBridgeHandler(Bridge bridge, @NotNull IHttpClientProvider httpClientProvider, + @Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) { super(bridge); this.httpClientProvider = httpClientProvider; + this.translationProvider = translationProvider; + this.localeProvider = localeProvider; + this.bundle = FrameworkUtil.getBundle(getClass()); + } + + public String getLocalizedText(String key, @Nullable Object @Nullable... arguments) { + String result = translationProvider.getText(bundle, key, key, localeProvider.getLocale(), arguments); + return Objects.nonNull(result) ? result : key; } public ThingUID getUID() { @@ -145,7 +164,8 @@ public void runDeviceScanSequenceNoAuthErrors() { runDeviceScanSequence(); updateStatus(ThingStatus.ONLINE); } catch (AuthenticationException ae) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Check login credentials"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + getLocalizedText("bridge.offline.check-credentials")); } } @@ -211,7 +231,8 @@ public void initialize() { runDeviceScanSequence(); updateStatus(ThingStatus.ONLINE); } catch (final AuthenticationException ae) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Check login credentials"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + getLocalizedText("bridge.offline.check-credentials")); // The background scan will keep trying to authenticate in case the users credentials are updated on the // veSync servers, // to match the binding's configuration. @@ -227,7 +248,7 @@ public void dispose() { @Override public void handleCommand(ChannelUID channelUID, Command command) { - logger.warn("Handling command for VeSync bridge handler."); + logger.warn("{}", getLocalizedText("warning.bridge.unexpected-command-call")); } public void handleNewUserSession(final @Nullable VeSyncUserSession userSessionData) { diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties index de97c54680d62..0e0fad70fdf27 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties @@ -101,3 +101,11 @@ channel-type.vesync.warmLevel.label = Warm Level channel-type.vesync.warmLevel.description = Warm Level channel-type.vesync.warmModeEnabled.label = Warm Mode Enabled channel-type.vesync.warmModeEnabled.description = Indicator if the device is set to warm mist + +# bridge status messages + +bridge.offline.check-credentials = Check login credentials + +# warnings + +warning.bridge.unexpected-command-call = Handling command for VeSync bridge handler From ea5c6f90e8eb8d6ee54ff741bc87b3857ae47ae7 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 6 Oct 2024 22:13:08 +0100 Subject: [PATCH 10/33] [veSync] i18n updates [veSync] i18n updates Signed-off-by: dag81 --- .../vesync/internal/VeSyncHandlerFactory.java | 4 +- .../handlers/VeSyncBaseDeviceHandler.java | 21 ++++++- .../handlers/VeSyncBridgeHandler.java | 11 ++-- .../VeSyncDeviceAirHumidifierHandler.java | 58 ++++++++++--------- .../VeSyncDeviceAirPurifierHandler.java | 35 ++++++----- .../resources/OH-INF/i18n/vesync.properties | 12 ++++ 6 files changed, 89 insertions(+), 52 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java index c6b38b6fb59da..815d5076720eb 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java @@ -72,9 +72,9 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { final ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (VeSyncDeviceAirPurifierHandler.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) { - return new VeSyncDeviceAirPurifierHandler(thing); + return new VeSyncDeviceAirPurifierHandler(thing, translationProvider, localeProvider); } else if (VeSyncDeviceAirHumidifierHandler.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) { - return new VeSyncDeviceAirHumidifierHandler(thing); + return new VeSyncDeviceAirHumidifierHandler(thing, translationProvider, localeProvider); } else if (THING_TYPE_BRIDGE.equals(thingTypeUID)) { return new VeSyncBridgeHandler((Bridge) thing, this, translationProvider, localeProvider); } 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 a1834d119c3da..f6f9c68ae4093 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 @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -38,6 +39,8 @@ import org.openhab.binding.vesync.internal.exceptions.AuthenticationException; import org.openhab.binding.vesync.internal.exceptions.DeviceUnknownException; import org.openhab.core.cache.ExpiringCache; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; @@ -49,6 +52,9 @@ import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.builder.ThingBuilder; import org.openhab.core.types.State; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,8 +96,21 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler { @Nullable ScheduledFuture readbackPollTask = null; - public VeSyncBaseDeviceHandler(Thing thing) { + private final TranslationProvider translationProvider; + private final LocaleProvider localeProvider; + private final Bundle bundle; + + public VeSyncBaseDeviceHandler(Thing thing, @Reference TranslationProvider translationProvider, + @Reference LocaleProvider localeProvider) { super(thing); + this.translationProvider = translationProvider; + this.localeProvider = localeProvider; + this.bundle = FrameworkUtil.getBundle(getClass()); + } + + public String getLocalizedText(String key, @Nullable Object @Nullable... arguments) { + String result = translationProvider.getText(bundle, key, key, localeProvider.getLocale(), arguments); + return Objects.nonNull(result) ? result : key; } protected @Nullable Channel findChannelById(final String channelGroupId) { 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 0dfe0e334415f..e95732ab8d61f 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 @@ -68,20 +68,19 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie private static final int DEFAULT_DEVICE_SCAN_RECOVERY_INTERVAL = 60; private static final int DEFAULT_DEVICE_SCAN_DISABLED = -1; - private final Logger logger = LoggerFactory.getLogger(VeSyncBridgeHandler.class); - - private @Nullable ScheduledFuture backgroundDiscoveryPollingJob; + private volatile int backgroundScanTime = -1; protected final VeSyncV2ApiHelper api = new VeSyncV2ApiHelper(); - private IHttpClientProvider httpClientProvider; - - private volatile int backgroundScanTime = -1; + private final Logger logger = LoggerFactory.getLogger(VeSyncBridgeHandler.class); private final Object scanConfigLock = new Object(); private final TranslationProvider translationProvider; private final LocaleProvider localeProvider; private final Bundle bundle; + private @Nullable ScheduledFuture backgroundDiscoveryPollingJob; + private IHttpClientProvider httpClientProvider; + public VeSyncBridgeHandler(Bridge bridge, @NotNull IHttpClientProvider httpClientProvider, @Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) { super(bridge); 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 b6b898a2b68bc..031a9d7316443 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 @@ -33,6 +33,8 @@ import org.openhab.binding.vesync.internal.dto.responses.VeSyncV2BypassHumidifierStatus; import org.openhab.binding.vesync.internal.dto.responses.VeSyncV2Ver2BypassHumidifierStatus; import org.openhab.core.cache.ExpiringCache; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.library.items.DateTimeItem; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; @@ -46,6 +48,7 @@ import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,6 +65,8 @@ 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; + private static final int MIN_TARGET_HUMIDITY = 30; + private static final int MAX_TARGET_HUMIDITY = 80; public static final String DEV_FAMILY_CLASSIC_200S = "Classic 200S"; public static final String DEV_FAMILY_CLASSIC_300S = "Classic 300S"; @@ -121,8 +126,9 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { } }; - public VeSyncDeviceAirHumidifierHandler(Thing thing) { - super(thing); + public VeSyncDeviceAirHumidifierHandler(Thing thing, @Reference TranslationProvider translationProvider, + @Reference LocaleProvider localeProvider) { + super(thing, translationProvider, localeProvider); } @Override @@ -196,7 +202,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { } final VeSyncDeviceHumidifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily); if (devContraints == null) { - logger.warn("Could not find device family for {} during handleCommand", deviceFamily); + logger.warn("{}", getLocalizedText("warning.device.command-device-family-not-found", deviceFamily)); return; } @@ -218,19 +224,19 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { new VeSyncRequestManagedDeviceBypassV2.EnabledPayload(command.equals(OnOffType.ON))); break; case DEVICE_CHANNEL_WARM_ENABLED: - logger.warn("Warm mode API is unknown in order to send the command"); + logger.warn("{}", getLocalizedText("warning.device.warm-mode-unsupported")); break; } } else if (command instanceof QuantityType quantityCommand) { switch (channelUID.getId()) { case DEVICE_CHANNEL_CONFIG_TARGET_HUMIDITY: int targetHumidity = quantityCommand.intValue(); - if (targetHumidity < 30) { - logger.warn("Target Humidity less than 30 - adjusting to 30 as the valid API value"); - targetHumidity = 30; - } else if (targetHumidity > 80) { - logger.warn("Target Humidity greater than 80 - adjusting to 80 as the valid API value"); - targetHumidity = 80; + if (targetHumidity < MIN_TARGET_HUMIDITY) { + logger.warn("{}", getLocalizedText("warning.device.humidity-under", MIN_TARGET_HUMIDITY)); + targetHumidity = MIN_TARGET_HUMIDITY; + } else if (targetHumidity > MAX_TARGET_HUMIDITY) { + logger.warn("{}", getLocalizedText("warning.device.humidity-over", MAX_TARGET_HUMIDITY)); + targetHumidity = MAX_TARGET_HUMIDITY; } sendV2BypassControlCommand(DEVICE_SET_HUMIDITY_MODE, @@ -244,9 +250,10 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { 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); + logger.warn("{}", + getLocalizedText("warning.device.mist-level-invalid", command, + devContraints.deviceFamilyName, devContraints.targetMinMistLevel, + devContraints.targetMaxMistLevel)); targetMistLevel = targetMistLevel < devContraints.targetMinMistLevel ? devContraints.targetMinMistLevel : devContraints.targetMaxMistLevel; @@ -279,10 +286,10 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { case DEVICE_CHANNEL_WARM_LEVEL: int targetWarmMistLevel = ((QuantityType) command).intValue(); if (!devContraints.isTargetWramMistLevelSupported(targetWarmMistLevel)) { - logger.warn( - "Warm mist level command for \"{}\" is not valid ({}) API possible options {} -> {}", - command, devContraints.deviceFamilyName, devContraints.targetMinWarmMistLevel, - devContraints.targetMaxWarmMistLevel); + logger.warn("{}", + getLocalizedText("warning.device.mist-level-invalid", command, + devContraints.deviceFamilyName, devContraints.targetMinWarmMistLevel, + devContraints.targetMaxWarmMistLevel)); targetWarmMistLevel = targetWarmMistLevel < devContraints.targetMinWarmMistLevel ? devContraints.targetMinWarmMistLevel : devContraints.targetMaxWarmMistLevel; @@ -298,9 +305,8 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { switch (channelUID.getId()) { case DEVICE_CHANNEL_HUMIDIFIER_MODE: if (!devContraints.fanModes.contains(targetMode)) { - logger.warn( - "Humidifier mode command for \"{}\" is not valid in the ({}}) API possible options {}", - command, devContraints.deviceFamilyName, String.join(",", devContraints.fanModes)); + logger.warn("{}", getLocalizedText("warning.device.humidity-mode", command, + devContraints.deviceFamilyName, String.join(",", devContraints.fanModes))); return; } sendV2BypassControlCommand(DEVICE_SET_HUMIDITY_MODE, @@ -309,10 +315,8 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { break; case DEVICE_CHANNEL_AF_NIGHT_LIGHT: if (!devContraints.nightLightModes.contains(targetMode)) { - logger.warn( - "Humidifier night light command for \"{}\" is not valid in the ({}}) API possible options {}", - command, devContraints.deviceFamilyName, - String.join(",", devContraints.nightLightModes)); + logger.warn("{}", getLocalizedText("warning.device.night-light-invalid", command, + devContraints.deviceFamilyName, String.join(",", devContraints.nightLightModes))); return; } @@ -350,7 +354,7 @@ protected void pollForDeviceData(final ExpiringCache cachedResponse) { final VeSyncDeviceHumidifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily); if (devContraints == null) { - logger.warn("Could not find device family for {} during pollForDeviceData", deviceFamily); + logger.warn("{}", getLocalizedText("warning.device.poll-device-family-not-found", deviceFamily)); return; } @@ -403,7 +407,7 @@ protected void pollForDeviceData(final ExpiringCache cachedResponse) { private void parseV2Ver1Poll(final VeSyncV2BypassHumidifierStatus humidifierStatus, final @Nullable String deviceFamily) { if (!"0".equals(humidifierStatus.result.getCode())) { - logger.warn("Check Thing type has been set - API gave a unexpected response for an Air Humidifier"); + logger.warn("{}", getLocalizedText("warning.device.unexpected-resp-for-air-humidifier")); return; } @@ -444,7 +448,7 @@ private void parseV2Ver1Poll(final VeSyncV2BypassHumidifierStatus humidifierStat private void parseV2Ver2Poll(final VeSyncV2Ver2BypassHumidifierStatus humidifierStatus) { if (!"0".equals(humidifierStatus.result.getCode())) { - logger.warn("Check Thing type has been set - API gave a unexpected response for an Air Humidifier"); + logger.warn("{}", getLocalizedText("warning.device.unexpected-resp-for-air-humidifier")); return; } 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 0475f7cbe43b9..e138b70a885b6 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 @@ -40,6 +40,8 @@ import org.openhab.binding.vesync.internal.dto.responses.VeSyncV2Ver2BypassPurifierStatus; import org.openhab.binding.vesync.internal.dto.responses.v1.VeSyncV1AirPurifierDeviceDetailsResponse; import org.openhab.core.cache.ExpiringCache; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.library.items.DateTimeItem; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; @@ -53,6 +55,7 @@ import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -132,8 +135,9 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler { private final Object pollLock = new Object(); - public VeSyncDeviceAirPurifierHandler(Thing thing) { - super(thing); + public VeSyncDeviceAirPurifierHandler(Thing thing, @Reference TranslationProvider translationProvider, + @Reference LocaleProvider localeProvider) { + super(thing, translationProvider, localeProvider); } @Override @@ -218,7 +222,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { } final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_PURIFIER_MAP.get(deviceFamily); if (devContraints == null) { - logger.warn("Could not find device family for {} during handleCommand", deviceFamily); + logger.warn("{}", getLocalizedText("warning.device.command-device-family-not-found", deviceFamily)); return; } @@ -292,8 +296,8 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { final String targetFanMode = command.toString().toLowerCase(); if (!devContraints.isFanModeSupported(targetFanMode)) { - logger.warn("Fan mode command for \"{}\" is not valid in the ({}) API possible options {}", - command, devContraints.deviceFamilyName, String.join(",", devContraints.fanModes)); + logger.warn("{}", getLocalizedText("warning.device.fan-mode-invalid", command, + devContraints.deviceFamilyName, String.join(",", devContraints.fanModes))); pollForUpdate(); return; } @@ -315,10 +319,8 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { case DEVICE_CHANNEL_AF_NIGHT_LIGHT: final String targetNightLightMode = command.toString().toLowerCase(); if (!devContraints.isNightLightModeSupported(targetNightLightMode)) { - logger.warn( - "Night light mode command for \"{}\" is not valid in the ({}) API possible options {}", - command, devContraints.deviceFamilyName, - String.join(",", devContraints.nightLightModes)); + logger.warn("{}", getLocalizedText("warning.device.night-light-invalid", command, + devContraints.deviceFamilyName, String.join(",", devContraints.nightLightModes))); pollForUpdate(); return; } @@ -331,9 +333,10 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { case DEVICE_CHANNEL_FAN_SPEED_ENABLED: int requestedLevel = ((QuantityType) command).intValue(); if (!devContraints.isFanSpeedSupported(requestedLevel)) { - logger.warn("Fan speed command for \"{}\" is not valid ({}) API possible options {} -> {}", - command, devContraints.deviceFamilyName, String.valueOf(devContraints.minFanSpeed), - String.valueOf(devContraints.maxFanSpeed)); + logger.warn("{}", + getLocalizedText("warning.device.fan-speed-invalid", command, + devContraints.deviceFamilyName, String.valueOf(devContraints.minFanSpeed), + String.valueOf(devContraints.maxFanSpeed))); requestedLevel = requestedLevel < devContraints.minFanSpeed ? devContraints.minFanSpeed : devContraints.maxFanSpeed; } @@ -426,7 +429,7 @@ private void processV1AirPurifierPoll(final ExpiringCache cachedResponse } if (!"0".equals(purifierStatus.getCode())) { - logger.warn("Check Thing type has been set - API gave a unexpected response for an Air Purifier"); + logger.warn("{}", getLocalizedText("warning.device.unexpected-resp-for-air-purifier")); return; } @@ -445,7 +448,7 @@ private void processV2BypassPoll(final ExpiringCache cachedResponse) { final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_PURIFIER_MAP.get(deviceFamily); if (devContraints == null) { - logger.warn("Could not find device family for {} during handleCommand", deviceFamily); + logger.warn("{}", getLocalizedText("warning.device.command-device-family-not-found", deviceFamily)); return; } @@ -499,7 +502,7 @@ private void processV2BypassPoll(final ExpiringCache cachedResponse) { private void parseV2Ver1Poll(final VeSyncV2BypassPurifierStatus purifierStatus) { if (!"0".equals(purifierStatus.result.getCode())) { - logger.warn("Check Thing type has been set - API gave a unexpected response for an Air Purifier"); + logger.warn("{}", getLocalizedText("warning.device.unexpected-resp-for-air-purifier")); return; } @@ -541,7 +544,7 @@ private void parseV2Ver1Poll(final VeSyncV2BypassPurifierStatus purifierStatus) private void parseV2Ver2Poll(final VeSyncV2Ver2BypassPurifierStatus purifierStatus) { if (!"0".equals(purifierStatus.result.getCode())) { - logger.warn("Check Thing type has been set - API gave a unexpected response for an Air Purifier"); + logger.warn("{}", getLocalizedText("warning.device.unexpected-resp-for-air-purifier")); return; } diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties index 0e0fad70fdf27..4a640363a086d 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties @@ -109,3 +109,15 @@ bridge.offline.check-credentials = Check login credentials # warnings warning.bridge.unexpected-command-call = Handling command for VeSync bridge handler +warning.device.command-device-family-not-found = Could not find device family for {0} during handleCommand +warning.device.poll-device-family-not-found = Could not find device family for {0} during pollForDeviceData +warning.device.fan-mode-invalid = Fan mode command for "{0}" is not valid in the ({1}) API possible options {2} +warning.device.fan-speed-invalid = Fan speed command for "{0}" is not valid ({1}) API possible options {2} -> {3} +warning.device.mist-level-invalid = Mist level command for "{0}" is not valid ({1}) API possible options {2} -> {3} +warning.device.night-light-invalid = Night light mode command for "{0}" is not valid in the ({1}) API possible options {2} +warning.device.humidity-under = Target Humidity less than {0} - adjusting to {0} as the valid API value +warning.device.humidity-over = Target Humidity greater than {0} - adjusting to {0} as the valid API value +warning.device.humidity-mode = Humidifier mode command for {0} is not valid in the ({1}}) API possible options {2} +warning.device.warm-mode-unsupported = Warm mode API is unknown in order to send the command +warning.device.unexpected-resp-for-air-purifier = Check Thing type has been set - API gave a unexpected response for an Air Purifier +warning.device.unexpected-resp-for-air-humidifier = Check Thing type has been set - API gave a unexpected response for an Air Humidifier From 184aded6eed70d621959f6067633974eccd089a5 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sat, 23 Nov 2024 00:21:32 +0000 Subject: [PATCH 11/33] [veSync] unithints corrected for correct display from devices [veSync] unithints corrected for correct display from devices Signed-off-by: dag81 --- .../main/resources/OH-INF/thing/thing-types.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 2263b59a52282..e4bcf9aeffdf3 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 @@ -159,10 +159,10 @@ - Number:Dimensionless + Number:Dimensionless Indicator of the remaining filter life - + @@ -194,14 +194,14 @@ - Number:Dimensionless + Number:Dimensionless Indicator of the current fan speed - Number:Dimensionless + Number:Dimensionless Indicator of the current error code of the device @@ -215,7 +215,7 @@ - Number:Density + Number:Density Indicator of current air quality @@ -262,14 +262,14 @@ - Number:Dimensionless + Number:Dimensionless Room size (foot sq) for efficient auto mode - Number:Dimensionless + Number:Dimensionless The current number of schedules configured From 7284cbb86af205f4119b192dfb27721e95f1f902 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sat, 23 Nov 2024 00:30:34 +0000 Subject: [PATCH 12/33] [veSync] httpClient clean-up [veSync] httpClient clean-up Signed-off-by: dag81 --- .../vesync/internal/VeSyncHandlerFactory.java | 11 ++------ .../internal/api/IHttpClientProvider.java | 26 ------------------- .../handlers/VeSyncBridgeHandler.java | 11 ++++---- 3 files changed, 8 insertions(+), 40 deletions(-) delete mode 100644 bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/IHttpClientProvider.java diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java index 815d5076720eb..58c30449af5a3 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/VeSyncHandlerFactory.java @@ -18,8 +18,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; -import org.openhab.binding.vesync.internal.api.IHttpClientProvider; import org.openhab.binding.vesync.internal.handlers.VeSyncBridgeHandler; import org.openhab.binding.vesync.internal.handlers.VeSyncDeviceAirHumidifierHandler; import org.openhab.binding.vesync.internal.handlers.VeSyncDeviceAirPurifierHandler; @@ -44,7 +42,7 @@ */ @NonNullByDefault @Component(configurationPid = "binding.vesync", service = ThingHandlerFactory.class) -public class VeSyncHandlerFactory extends BaseThingHandlerFactory implements IHttpClientProvider { +public class VeSyncHandlerFactory extends BaseThingHandlerFactory { private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE, THING_TYPE_AIR_PURIFIER, THING_TYPE_AIR_HUMIDIFIER); @@ -76,14 +74,9 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } else if (VeSyncDeviceAirHumidifierHandler.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) { return new VeSyncDeviceAirHumidifierHandler(thing, translationProvider, localeProvider); } else if (THING_TYPE_BRIDGE.equals(thingTypeUID)) { - return new VeSyncBridgeHandler((Bridge) thing, this, translationProvider, localeProvider); + return new VeSyncBridgeHandler((Bridge) thing, httpClientFactory, translationProvider, localeProvider); } return null; } - - @Override - public @Nullable HttpClient getHttpClient() { - return httpClientFactory.getCommonHttpClient(); - } } diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/IHttpClientProvider.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/IHttpClientProvider.java deleted file mode 100644 index 1698126e1e49b..0000000000000 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/IHttpClientProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.api; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; - -/** - * @author David Goodyear - Initial contribution - */ -@NonNullByDefault -public interface IHttpClientProvider { - @Nullable - HttpClient getHttpClient(); -} 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 e95732ab8d61f..63ae9281a839d 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 @@ -27,8 +27,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.vesync.internal.VeSyncBridgeConfiguration; -import org.openhab.binding.vesync.internal.api.IHttpClientProvider; import org.openhab.binding.vesync.internal.api.VeSyncV2ApiHelper; import org.openhab.binding.vesync.internal.discovery.DeviceMetaDataUpdatedHandler; import org.openhab.binding.vesync.internal.discovery.VeSyncDiscoveryService; @@ -39,6 +39,7 @@ import org.openhab.binding.vesync.internal.exceptions.DeviceUnknownException; import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.TranslationProvider; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -79,12 +80,12 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie private final Bundle bundle; private @Nullable ScheduledFuture backgroundDiscoveryPollingJob; - private IHttpClientProvider httpClientProvider; + private HttpClient httpClient; - public VeSyncBridgeHandler(Bridge bridge, @NotNull IHttpClientProvider httpClientProvider, + public VeSyncBridgeHandler(Bridge bridge, @NotNull HttpClientFactory httpClientFactory, @Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) { super(bridge); - this.httpClientProvider = httpClientProvider; + this.httpClient = httpClientFactory.getCommonHttpClient(); this.translationProvider = translationProvider; this.localeProvider = localeProvider; this.bundle = FrameworkUtil.getBundle(getClass()); @@ -217,7 +218,7 @@ public Collection> getServices() { @Override public void initialize() { - api.setHttpClient(httpClientProvider.getHttpClient()); + api.setHttpClient(httpClient); VeSyncBridgeConfiguration config = getConfigAs(VeSyncBridgeConfiguration.class); From 4d9acc2846e264cff1d1dfa509eebcdc9fd9f8a8 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sat, 23 Nov 2024 00:40:44 +0000 Subject: [PATCH 13/33] [veSync] PR Feedback [veSync] PR Feedback Signed-off-by: dag81 --- bundles/org.openhab.binding.vesync/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index c56279288362c..00a550bc3d547 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -266,7 +266,6 @@ Number LoungeAHSchedulesCount "Lounge Air Humidifier Schedules Number LoungeAHErrorCode "Lounge Air Humidifier Error Code" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:errorCode" } ``` - ### Configuration (*.sitemap) #### Air Purifier Core 400S / 600S Model From 99b43aecbf7bda2b51a406207b301fee4f178688 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sat, 23 Nov 2024 00:47:45 +0000 Subject: [PATCH 14/33] [veSync] english cleanup [veSync] english cleanup Signed-off-by: dag81 --- bundles/org.openhab.binding.vesync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index 00a550bc3d547..a5d5179c54c43 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -83,7 +83,7 @@ Channel names in **bold** are read/write, everything else is read-only | airQualityPM25 | Number:Density | The air quality as represented by the Core400S | 600S, 400S, 300S, Vital 100S, Vital 200S | | µg/m³ | | errorCode | Number:Dimensionless | The error code reported by the device | 600S, 400S, 300S, Vital 100S, Vital 200S | | | | timerExpiry | DateTime | The expected expiry time of the current timer | 600S, 400S | | | -| schedulesCount | Number:Dimensionless | The number schedules configured | 600S, 400S | | | +| schedulesCount | Number:Dimensionless | The number of schedules which are configured | 600S, 400S | | | | configDisplayForever | Switch | Config: Whether the display will disable when not active | 600S, 400S, 300S | | | | configAutoRoomSize | Number:Dimensionless | Config: The room size set when auto utilises the room size | 600S, 400S, 300S | | | From 64dc88cea89376d6475c1a2a712ecc38a5a44d7c Mon Sep 17 00:00:00 2001 From: dag81 Date: Sat, 23 Nov 2024 02:28:28 +0000 Subject: [PATCH 15/33] [veSync] cleanups [veSync] cleanups Signed-off-by: dag81 --- bundles/org.openhab.binding.vesync/README.md | 5 +++-- .../vesync/internal/handlers/VeSyncBaseDeviceHandler.java | 4 ++-- .../vesync/internal/handlers/VeSyncBridgeHandler.java | 2 +- .../internal/handlers/VeSyncDeviceHumidifierMetadata.java | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index a5d5179c54c43..8467dc7cfe34a 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -83,9 +83,10 @@ Channel names in **bold** are read/write, everything else is read-only | airQualityPM25 | Number:Density | The air quality as represented by the Core400S | 600S, 400S, 300S, Vital 100S, Vital 200S | | µg/m³ | | errorCode | Number:Dimensionless | The error code reported by the device | 600S, 400S, 300S, Vital 100S, Vital 200S | | | | timerExpiry | DateTime | The expected expiry time of the current timer | 600S, 400S | | | -| schedulesCount | Number:Dimensionless | The number of schedules which are configured | 600S, 400S | | | +| schedulesCount | Number:Dimensionless | The number of schedules which are configured | 600S, 400S | | one | | configDisplayForever | Switch | Config: Whether the display will disable when not active | 600S, 400S, 300S | | | -| configAutoRoomSize | Number:Dimensionless | Config: The room size set when auto utilises the room size | 600S, 400S, 300S | | | +| configAutoMode | String | Config: The mode of operation when auto is active | 600S, 400S, 300S | | | +| configAutoRoomSize | Number:Dimensionless | Config: The room size set when auto utilises the room size | 600S, 400S, 300S | | one | ### AirHumidifier Thing 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 f6f9c68ae4093..285fab6c18a47 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 @@ -420,7 +420,7 @@ protected final String sendV2BypassControlCommand(final String method, protected final String sendV2BypassControlCommand(final String method, final VeSyncRequestManagedDeviceBypassV2.EmptyPayload payload, final boolean readbackDevice) { final String result = sendV2BypassCommand(method, payload); - if (!result.equals(EMPTY_STRING) && readbackDevice) { + if (!EMPTY_STRING.equals(result) && readbackDevice) { performReadbackPoll(); } return result; @@ -433,7 +433,7 @@ protected final String sendV1ControlCommand(final String urlPath, final VeSyncAu protected final String sendV1ControlCommand(final String urlPath, final VeSyncAuthenticatedRequest request, final boolean readbackDevice) { final String result = sendV1Command(urlPath, request); - if (!result.equals(EMPTY_STRING) && readbackDevice) { + if (!EMPTY_STRING.equals(result) && readbackDevice) { performReadbackPoll(); } return result; 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 63ae9281a839d..4e5059a954c82 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 @@ -82,7 +82,7 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie private @Nullable ScheduledFuture backgroundDiscoveryPollingJob; private HttpClient httpClient; - public VeSyncBridgeHandler(Bridge bridge, @NotNull HttpClientFactory httpClientFactory, + public VeSyncBridgeHandler(Bridge bridge, @Reference HttpClientFactory httpClientFactory, @Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) { super(bridge); this.httpClient = httpClientFactory.getCommonHttpClient(); diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java index d1494f9be42e3..eb709005c2a70 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceHumidifierMetadata.java @@ -86,7 +86,7 @@ public String getProtocolMode(final String mode) { if (!remapsAutoToHumidity) { return mode; } else { - if (mode.equals(MODE_AUTO)) { + if (MODE_AUTO.equals(mode)) { return MODE_AUTO_HUMIDITY; } return mode; From 83194c25f77b8feb8d42d504739190c58c5dd37d Mon Sep 17 00:00:00 2001 From: dag81 Date: Sat, 23 Nov 2024 13:21:09 +0000 Subject: [PATCH 16/33] [veSync] Humidifier units tuning [veSync] Humidifier units tuning Signed-off-by: dag81 --- bundles/org.openhab.binding.vesync/README.md | 38 +++++++++---------- .../resources/OH-INF/thing/thing-types.xml | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index 8467dc7cfe34a..41ef5a1b7c445 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -90,25 +90,25 @@ 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, OasisMist, OasisMist1000 | [ON, OFF] | -| **display** | Switch | Whether the display is enabled (display is shown) | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | -| waterLacking | Switch | Indicator whether the unit is lacking water | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | -| 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, OasisMist1000 | | -| **stopAtHumiditySetpoint** | Switch | Whether the unit is set to stop when the set point is reached | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | -| humidity | Number:Dimensionless | Indicator for the currently measured humidity % level | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | -| **mistLevel** | Number:Dimensionless | The current mist level set | 300S | [1...2] | -| **mistLevel** | Number:Dimensionless | The current mist level set | 200S, Dual200S, 600S, OasisMist, OasisMist1000 | [1...3] | -| **humidifierMode** | String | The current mode of operation | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [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, OasisMist1000 | [30...80] | -| warmEnabled | Switch | Indicator for warm mist mode | 600S, OasisMist | | -| **warmLevel** | Number:Dimensionless | The current warm mist level set | 600S, OasisMist | [0..3] | -| errorCode | Number:Dimensionless | The error code reported by the device | OasisMist1000 | | -| timerExpiry | DateTime | The expected expiry time of the current timer | OasisMist1000 | | -| schedulesCount | Number:Dimensionless | The number schedules configured | OasisMist1000 | | +| Channel | Type | Description | Model's Supported | Controllable Values | Unit | +|----------------------------|----------------------|---------------------------------------------------------------|------------------------------------------------------|---------------------|------| +| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | | +| **display** | Switch | Whether the display is enabled (display is shown) | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | | +| waterLacking | Switch | Indicator whether the unit is lacking water | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | | +| 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, OasisMist1000 | | | +| **stopAtHumiditySetpoint** | Switch | Whether the unit is set to stop when the set point is reached | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | | +| humidity | Number:Dimensionless | Indicator for the currently measured humidity % level | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | | +| **mistLevel** | Number:Dimensionless | The current mist level set | 300S | [1...2] | one | +| **mistLevel** | Number:Dimensionless | The current mist level set | 200S, Dual200S, 600S, OasisMist, OasisMist1000 | [1...3] | one | +| **humidifierMode** | String | The current mode of operation | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [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, OasisMist1000 | [30...80] | | +| warmEnabled | Switch | Indicator for warm mist mode | 600S, OasisMist | | | +| **warmLevel** | Number:Dimensionless | The current warm mist level set | 600S, OasisMist | [0..3] | one | +| errorCode | Number:Dimensionless | The error code reported by the device | OasisMist1000 | | one | +| timerExpiry | DateTime | The expected expiry time of the current timer | OasisMist1000 | | | +| schedulesCount | Number:Dimensionless | The number schedules configured | OasisMist1000 | | one | ## Full Example 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 e4bcf9aeffdf3..70f6ce337e9c0 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 @@ -319,7 +319,7 @@ - Number:Dimensionless + Number:Dimensionless System representation of mist level @@ -346,7 +346,7 @@ - Number:Dimensionless + Number:Dimensionless Warm Level From c94058e08b7710d902cdb7186d7e2a811c6909e7 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 24 Nov 2024 00:20:48 +0000 Subject: [PATCH 17/33] [veSync] protocol device id update [veSync] protocol device id update Signed-off-by: dag81 --- .../binding/vesync/internal/dto/requests/VeSyncRequest.java | 3 +++ .../vesync/internal/handlers/VeSyncBaseDeviceHandler.java | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java index f4d7224dcee85..04e6dc67b44f6 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java @@ -46,6 +46,9 @@ public class VeSyncRequest { @SerializedName("method") public String method; + @SerializedName("deviceId") + public String deviceId; + public VeSyncRequest() { traceId = String.valueOf(System.currentTimeMillis()); httpMethod = HttpMethod.POST; 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 285fab6c18a47..c79e2fbfc8cf7 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 @@ -100,6 +100,8 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler { private final LocaleProvider localeProvider; private final Bundle bundle; + private String deviceId = ""; + public VeSyncBaseDeviceHandler(Thing thing, @Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) { super(thing); @@ -191,6 +193,8 @@ public void updateDeviceMetaData() { newProps = getMetadataProperities(metadata); + deviceId = metadata.getUuid(); + // Refresh the device -> protocol mapping deviceLookupKey = getValidatedIdString(); @@ -485,6 +489,7 @@ protected final String sendV2BypassCommand(final String method, VeSyncRequestManagedDeviceBypassV2 readReq = new VeSyncRequestManagedDeviceBypassV2(); readReq.payload.method = method; readReq.payload.data = payload; + readReq.deviceId = deviceId; try { if (MARKER_INVALID_DEVICE_KEY.equals(deviceLookupKey)) { From 9e4276aa660058ccfb9f0e79a9a6abe1fd1fb1c6 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 24 Nov 2024 00:52:40 +0000 Subject: [PATCH 18/33] [veSync] protocol alignment to redundant data against pyvesync [veSync] protocol alignment to redundant data against pyvesync Signed-off-by: dag81 --- .../openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java | 1 + .../dto/requests/VeSyncRequestManagedDeviceBypassV2.java | 3 +++ 2 files changed, 4 insertions(+) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java index 7e33d53661f8e..fa654ba8b4344 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java @@ -157,6 +157,7 @@ public String reqV2Authorized(final String url, final String macId, final VeSync } veSyncRequestManagedDeviceBypassV2.cid = deviceData.cid; veSyncRequestManagedDeviceBypassV2.configModule = deviceData.configModule; + veSyncRequestManagedDeviceBypassV2.configModel = deviceData.configModule; veSyncRequestManagedDeviceBypassV2.deviceRegion = deviceData.deviceRegion; } return reqV1Authorized(url, requestData); diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestManagedDeviceBypassV2.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestManagedDeviceBypassV2.java index 4b3ea744b14bf..7aec7ab8844a2 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestManagedDeviceBypassV2.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequestManagedDeviceBypassV2.java @@ -34,6 +34,9 @@ public class VeSyncRequestManagedDeviceBypassV2 extends VeSyncAuthenticatedReque @SerializedName("configModule") public String configModule = ""; + @SerializedName("configModel") + public String configModel = ""; + @SerializedName("payload") public VesyncManagedDeviceBase payload = new VesyncManagedDeviceBase(); From 25610d49e80d42783405ceb5db08acd989936deb Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 24 Nov 2024 19:20:44 +0000 Subject: [PATCH 19/33] [veSync] cleanup casting uses [veSync] cleanup casting uses Signed-off-by: dag81 --- .../internal/handlers/VeSyncDeviceAirHumidifierHandler.java | 4 ++-- .../internal/handlers/VeSyncDeviceAirPurifierHandler.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 031a9d7316443..da048d20d4324 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 @@ -248,7 +248,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { new VeSyncRequestManagedDeviceBypassV2.SetTargetHumidity(targetHumidity)); break; case DEVICE_CHANNEL_MIST_LEVEL: - int targetMistLevel = ((QuantityType) command).intValue(); + int targetMistLevel = quantityCommand.intValue(); if (!devContraints.isTargetMistLevelSupported(targetMistLevel)) { logger.warn("{}", getLocalizedText("warning.device.mist-level-invalid", command, @@ -284,7 +284,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { targetMistLevel)); break; case DEVICE_CHANNEL_WARM_LEVEL: - int targetWarmMistLevel = ((QuantityType) command).intValue(); + int targetWarmMistLevel = quantityCommand.intValue(); if (!devContraints.isTargetWramMistLevelSupported(targetWarmMistLevel)) { logger.warn("{}", getLocalizedText("warning.device.mist-level-invalid", command, 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 e138b70a885b6..c989dbbf37cdc 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirPurifierHandler.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirPurifierHandler.java @@ -331,7 +331,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { } else if (command instanceof QuantityType quantityCommand) { switch (channelUID.getId()) { case DEVICE_CHANNEL_FAN_SPEED_ENABLED: - int requestedLevel = ((QuantityType) command).intValue(); + int requestedLevel = quantityCommand.intValue(); if (!devContraints.isFanSpeedSupported(requestedLevel)) { logger.warn("{}", getLocalizedText("warning.device.fan-speed-invalid", command, @@ -404,7 +404,7 @@ private void processV1AirPurifierPoll(final ExpiringCache cachedResponse logger.trace("Using cached response {}", response); } - if (response.equals(EMPTY_STRING)) { + if (EMPTY_STRING.equals(response)) { return; } From c1819150f37615f07aa0bd63d06daaac393c8ea5 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 24 Nov 2024 23:28:33 +0000 Subject: [PATCH 20/33] [veSync] air purifier update instructions pass 1 [veSync] air purifier update instructions pass 1 Signed-off-by: dag81 --- .../update/air-purifier-instructions.xml | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml new file mode 100644 index 0000000000000..ee5dd431ffcd5 --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml @@ -0,0 +1,30 @@ + + + + + + + vesync:deviceAFLightDetection + + + vesync:deviceAFLightDetected + + + Number:Dimensionless + + + + Number:Dimensionless + + + Number:Dimensionless + + + Number:Density + + + + + From 89e4976dc2395d1d243d7ef780413668ddb81b74 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 24 Nov 2024 23:33:38 +0000 Subject: [PATCH 21/33] [veSync] air purifier options attempt 1 [veSync] air purifier options attempt 1 Signed-off-by: dag81 --- .../OH-INF/update/air-purifier-instructions.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml index ee5dd431ffcd5..4081d2c9fc473 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml @@ -24,6 +24,16 @@ Number:Density + + + + + + + + + + From d1b0a77eac670ec7f11df3324fb9fe942e5efb50 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 24 Nov 2024 23:35:32 +0000 Subject: [PATCH 22/33] [veSync] air humidifier update template base [veSync] air humidifier update template base Signed-off-by: dag81 --- .../OH-INF/update/air-humidifier-instructions.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml new file mode 100644 index 0000000000000..12f2a1f5e09aa --- /dev/null +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml @@ -0,0 +1,12 @@ + + + + + + + + + + From 615fd7cdf2889ea8e9a9e72064440a44b47b8fd0 Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 24 Nov 2024 23:35:53 +0000 Subject: [PATCH 23/33] [veSync] readme updates [veSync] readme updates Signed-off-by: dag81 --- bundles/org.openhab.binding.vesync/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index 41ef5a1b7c445..73ed7af38ebe5 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -4,8 +4,8 @@ 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, OasisMist Smart Humidifier +Air Filtering models verified are Core300S, Core400S, Vital 100S. +Air Humidifier models verified are Dual 200S, Classic 300S, 600S, OasisMist Smart Humidifier ## Awaiting User Verification Models @@ -24,7 +24,7 @@ This binding supports the follow thing types: This binding was developed from the great work in the listed projects. -The only Air Filter unit it has been tested against is the Core400S unit, **I'm looking for others to confirm** my queries regarding **the Core200S and Core300S** units. +The only Air Filter unit it has been tested against are the Core400S unit and Vital 100S, **I'm looking for others to confirm** my queries regarding **the Core200S and Core300S** units. The **Classic 300S Humidifier** has been tested, and **600S with current warm mode restrictions**. ## Discovery From 536f1a8570d4f2797b80aa1fa373a961389dfdcf Mon Sep 17 00:00:00 2001 From: dag81 Date: Sun, 24 Nov 2024 23:47:18 +0000 Subject: [PATCH 24/33] [veSync] update script updates [veSync] update script updates Signed-off-by: dag81 --- .../update/air-humidifier-instructions.xml | 19 ++++++++++++++++++- .../update/air-purifier-instructions.xml | 6 ++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml index 12f2a1f5e09aa..3ddabe5f075f7 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml @@ -5,7 +5,24 @@ - + + vesync:deviceAFConfigAutoScheduleCountType + + + vesync:deviceAFTimerExpiry + + + Number:Dimensionless + + + Number:Dimensionless + + + Number:Dimensionless + + + Number:Dimensionless + diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml index 4081d2c9fc473..5f21eb24e0cc5 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml @@ -34,6 +34,12 @@ + + Number:Dimensionless + + + Number:Dimensionless + From f5e0b4f4401851f31851a119276ef15392296473 Mon Sep 17 00:00:00 2001 From: dag81 Date: Mon, 25 Nov 2024 00:00:05 +0000 Subject: [PATCH 25/33] [veSync] update script updates [veSync] update script updates Signed-off-by: dag81 --- .../resources/OH-INF/update/air-humidifier-instructions.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml index 3ddabe5f075f7..a108fd5d214d8 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml @@ -17,9 +17,6 @@ Number:Dimensionless - - Number:Dimensionless - Number:Dimensionless From f7aac5a518d48b9d146fe7ec083751fed112b2a3 Mon Sep 17 00:00:00 2001 From: dag81 Date: Tue, 26 Nov 2024 08:40:52 +0000 Subject: [PATCH 26/33] [veSync] Air purifier update modifications [veSync] Air purifier update modifications Signed-off-by: dag81 --- .../resources/OH-INF/thing/thing-types.xml | 3 +- .../update/air-purifier-instructions.xml | 36 ++++++++----------- 2 files changed, 16 insertions(+), 23 deletions(-) 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 70f6ce337e9c0..08bd20f7010df 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 @@ -12,6 +12,7 @@ + 1 @@ -66,7 +67,6 @@ - @@ -74,6 +74,7 @@ + 1 macId diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml index 5f21eb24e0cc5..d4f4eb24da074 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml @@ -11,34 +11,26 @@ vesync:deviceAFLightDetected - - Number:Dimensionless - + + vesync:airPurifierFanLevelType - - Number:Dimensionless + + vesync:airPurifierModeType - - Number:Dimensionless + + vesync:deviceFilterLifePercentageType - - Number:Density + + vesync:deviceErrorCodeType - - - - - - - - - + + vesync:deviceAFConfigAutoPrefRoomSizeType - - Number:Dimensionless + + vesync:deviceAFConfigAutoScheduleCountType - - Number:Dimensionless + + vesync:airQualityPM25 From 2816d0f840ddb52049a9f3b979e73faf2945d453 Mon Sep 17 00:00:00 2001 From: dag81 Date: Tue, 26 Nov 2024 08:50:53 +0000 Subject: [PATCH 27/33] [veSync] Air humidifier update modifications [veSync] Air humidifier update modifications Signed-off-by: dag81 --- .../OH-INF/update/air-humidifier-instructions.xml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml index a108fd5d214d8..7bb0a4239c8e6 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml @@ -11,14 +11,11 @@ vesync:deviceAFTimerExpiry - - Number:Dimensionless + + vesync:deviceMistLevelType - Number:Dimensionless - - - Number:Dimensionless + vesync:warmLevel From 27c40c67eb8af97b83d9a0f3af2cce4136f15f5a Mon Sep 17 00:00:00 2001 From: dag81 Date: Wed, 27 Nov 2024 19:31:26 +0000 Subject: [PATCH 28/33] [veSync] PR feedback updates 1 [veSync] PR feedback updates 1 Signed-off-by: dag81 --- .../internal/api/VeSyncV2ApiHelper.java | 29 +++++++++++-------- .../handlers/VeSyncBridgeHandler.java | 10 ++----- .../resources/OH-INF/thing/thing-types.xml | 2 +- .../update/air-humidifier-instructions.xml | 8 ----- .../update/air-purifier-instructions.xml | 20 ------------- 5 files changed, 21 insertions(+), 48 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java index fa654ba8b4344..f9bc237a6837c 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java @@ -63,11 +63,12 @@ public class VeSyncV2ApiHelper { private volatile @Nullable VeSyncUserSession loggedInSession; - private @NonNullByDefault({}) HttpClient httpClient; + private @Nullable HttpClient httpClient; private Map macLookup; - public VeSyncV2ApiHelper() { + public VeSyncV2ApiHelper(final HttpClient httpClient) { + this.httpClient = httpClient; macLookup = new HashMap<>(); } @@ -75,13 +76,9 @@ public VeSyncV2ApiHelper() { return macLookup; } - /** - * Sets the httpClient object to be used for API calls to Vesync. - * - * @param httpClient the client to be used. - */ - public void setHttpClient(@Nullable HttpClient httpClient) { - this.httpClient = httpClient; + public void dispose() { + loggedInSession = null; + macLookup.clear(); } public static @NotNull String calculateMd5(final @Nullable String password) { @@ -171,7 +168,11 @@ public String reqV1Authorized(final String url, final VeSyncAuthenticatedRequest private String directReqV1Authorized(final String url, final VeSyncAuthenticatedRequest requestData) throws AuthenticationException { try { - Request request = httpClient.newRequest(url).method(requestData.httpMethod).timeout(RESPONSE_TIMEOUT_SEC, + final HttpClient client = httpClient; + if (client == null) { + throw new AuthenticationException("No HTTP Client"); + } + Request request = client.newRequest(url).method(requestData.httpMethod).timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS); // No headers for login @@ -226,8 +227,12 @@ public void updateBridgeData(final VeSyncBridgeHandler bridge) { private VeSyncLoginResponse processLogin(String username, String password, String timezone) throws AuthenticationException { try { - Request request = httpClient.newRequest(V1_LOGIN_ENDPOINT).method(HttpMethod.POST) - .timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS); + final HttpClient client = httpClient; + if (client == null) { + throw new AuthenticationException("No HTTP Client"); + } + Request request = client.newRequest(V1_LOGIN_ENDPOINT).method(HttpMethod.POST).timeout(RESPONSE_TIMEOUT_SEC, + TimeUnit.SECONDS); // No headers for login request.content(new StringContentProvider( 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 4e5059a954c82..cd3b7c3900ae6 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 @@ -27,7 +27,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.vesync.internal.VeSyncBridgeConfiguration; import org.openhab.binding.vesync.internal.api.VeSyncV2ApiHelper; import org.openhab.binding.vesync.internal.discovery.DeviceMetaDataUpdatedHandler; @@ -71,7 +70,7 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie private volatile int backgroundScanTime = -1; - protected final VeSyncV2ApiHelper api = new VeSyncV2ApiHelper(); + protected final VeSyncV2ApiHelper api; private final Logger logger = LoggerFactory.getLogger(VeSyncBridgeHandler.class); private final Object scanConfigLock = new Object(); @@ -80,12 +79,11 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie private final Bundle bundle; private @Nullable ScheduledFuture backgroundDiscoveryPollingJob; - private HttpClient httpClient; public VeSyncBridgeHandler(Bridge bridge, @Reference HttpClientFactory httpClientFactory, @Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) { super(bridge); - this.httpClient = httpClientFactory.getCommonHttpClient(); + api = new VeSyncV2ApiHelper(httpClientFactory.getCommonHttpClient()); this.translationProvider = translationProvider; this.localeProvider = localeProvider; this.bundle = FrameworkUtil.getBundle(getClass()); @@ -218,8 +216,6 @@ public Collection> getServices() { @Override public void initialize() { - api.setHttpClient(httpClient); - VeSyncBridgeConfiguration config = getConfigAs(VeSyncBridgeConfiguration.class); scheduler.submit(() -> { @@ -243,7 +239,7 @@ public void initialize() { @Override public void dispose() { setBackgroundScanInterval(DEFAULT_DEVICE_SCAN_DISABLED); - api.setHttpClient(null); + api.dispose(); } @Override 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 08bd20f7010df..9006b3542f016 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 @@ -12,7 +12,6 @@ - 1 @@ -122,6 +121,7 @@ + 1 macId diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml index 7bb0a4239c8e6..fa054fa01ec75 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-humidifier-instructions.xml @@ -2,7 +2,6 @@ - @@ -11,13 +10,6 @@ vesync:deviceAFTimerExpiry - - vesync:deviceMistLevelType - - - vesync:warmLevel - - diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml index d4f4eb24da074..377a7b310d90d 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/update/air-purifier-instructions.xml @@ -2,7 +2,6 @@ - @@ -11,28 +10,9 @@ vesync:deviceAFLightDetected - - vesync:airPurifierFanLevelType - vesync:airPurifierModeType - - vesync:deviceFilterLifePercentageType - - - vesync:deviceErrorCodeType - - - vesync:deviceAFConfigAutoPrefRoomSizeType - - - vesync:deviceAFConfigAutoScheduleCountType - - - vesync:airQualityPM25 - - From 9d9fc8ceadb9cbccdb47afeeeb770aa29fd05ce9 Mon Sep 17 00:00:00 2001 From: dag81 Date: Wed, 27 Nov 2024 19:39:57 +0000 Subject: [PATCH 29/33] [veSync] PR feedback updates 1 [veSync] PR feedback updates 1 Signed-off-by: dag81 --- bundles/org.openhab.binding.vesync/README.md | 2 +- .../vesync/internal/api/VeSyncV2ApiHelper.java | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index 73ed7af38ebe5..d868cef17c68d 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -24,7 +24,7 @@ This binding supports the follow thing types: This binding was developed from the great work in the listed projects. -The only Air Filter unit it has been tested against are the Core400S unit and Vital 100S, **I'm looking for others to confirm** my queries regarding **the Core200S and Core300S** units. +The binding has been tested against the following Air Filter unit's: Core400S and Vital 100S, **I'm looking for others to confirm** my queries regarding **the Core200S and Core300S** units. The **Classic 300S Humidifier** has been tested, and **600S with current warm mode restrictions**. ## Discovery diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java index f9bc237a6837c..c64174008e673 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java @@ -168,11 +168,10 @@ public String reqV1Authorized(final String url, final VeSyncAuthenticatedRequest private String directReqV1Authorized(final String url, final VeSyncAuthenticatedRequest requestData) throws AuthenticationException { try { - final HttpClient client = httpClient; - if (client == null) { + if (httpClient == null) { throw new AuthenticationException("No HTTP Client"); } - Request request = client.newRequest(url).method(requestData.httpMethod).timeout(RESPONSE_TIMEOUT_SEC, + Request request = httpClient.newRequest(url).method(requestData.httpMethod).timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS); // No headers for login @@ -227,12 +226,11 @@ public void updateBridgeData(final VeSyncBridgeHandler bridge) { private VeSyncLoginResponse processLogin(String username, String password, String timezone) throws AuthenticationException { try { - final HttpClient client = httpClient; - if (client == null) { + if (httpClient == null) { throw new AuthenticationException("No HTTP Client"); } - Request request = client.newRequest(V1_LOGIN_ENDPOINT).method(HttpMethod.POST).timeout(RESPONSE_TIMEOUT_SEC, - TimeUnit.SECONDS); + Request request = httpClient.newRequest(V1_LOGIN_ENDPOINT).method(HttpMethod.POST) + .timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS); // No headers for login request.content(new StringContentProvider( From 6b7747417a7799b5f05048fbc52c48679da43733 Mon Sep 17 00:00:00 2001 From: dag81 Date: Wed, 27 Nov 2024 21:32:06 +0000 Subject: [PATCH 30/33] [veSync] PR feedback updates 2 [veSync] PR feedback updates 2 Signed-off-by: dag81 --- .../vesync/internal/api/VeSyncV2ApiHelper.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java index c64174008e673..a64a3c6355113 100644 --- a/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java +++ b/bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java @@ -63,7 +63,7 @@ public class VeSyncV2ApiHelper { private volatile @Nullable VeSyncUserSession loggedInSession; - private @Nullable HttpClient httpClient; + private final @Nullable HttpClient httpClient; private Map macLookup; @@ -168,10 +168,11 @@ public String reqV1Authorized(final String url, final VeSyncAuthenticatedRequest private String directReqV1Authorized(final String url, final VeSyncAuthenticatedRequest requestData) throws AuthenticationException { try { - if (httpClient == null) { + final HttpClient client = httpClient; + if (client == null) { throw new AuthenticationException("No HTTP Client"); } - Request request = httpClient.newRequest(url).method(requestData.httpMethod).timeout(RESPONSE_TIMEOUT_SEC, + Request request = client.newRequest(url).method(requestData.httpMethod).timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS); // No headers for login @@ -226,11 +227,12 @@ public void updateBridgeData(final VeSyncBridgeHandler bridge) { private VeSyncLoginResponse processLogin(String username, String password, String timezone) throws AuthenticationException { try { - if (httpClient == null) { + final HttpClient client = httpClient; + if (client == null) { throw new AuthenticationException("No HTTP Client"); } - Request request = httpClient.newRequest(V1_LOGIN_ENDPOINT).method(HttpMethod.POST) - .timeout(RESPONSE_TIMEOUT_SEC, TimeUnit.SECONDS); + Request request = client.newRequest(V1_LOGIN_ENDPOINT).method(HttpMethod.POST).timeout(RESPONSE_TIMEOUT_SEC, + TimeUnit.SECONDS); // No headers for login request.content(new StringContentProvider( From a1b1eb8fe6840b12a115cf40d1044af4bee423ce Mon Sep 17 00:00:00 2001 From: dag81 Date: Wed, 27 Nov 2024 22:37:30 +0000 Subject: [PATCH 31/33] [veSync] PR feedback updates 3 [veSync] PR feedback updates 3 Signed-off-by: dag81 --- .../internal/handlers/VeSyncDeviceAirPurifierHandler.java | 5 +++-- .../src/main/resources/OH-INF/i18n/vesync.properties | 2 +- .../src/main/resources/OH-INF/thing/thing-types.xml | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) 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 c989dbbf37cdc..dd95d4fb24ece 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 @@ -48,6 +48,7 @@ import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.ImperialUnits; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -521,8 +522,8 @@ private void parseV2Ver1Poll(final VeSyncV2BypassPurifierStatus purifierStatus) OnOffType.from(purifierStatus.result.result.configuration.displayForever)); updateState(DEVICE_CHANNEL_AF_CONFIG_AUTO_MODE_PREF, new StringType(purifierStatus.result.result.configuration.autoPreference.autoType)); - updateState(DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE, - new DecimalType(purifierStatus.result.result.configuration.autoPreference.roomSize)); + updateState(DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE, new QuantityType<>( + purifierStatus.result.result.configuration.autoPreference.roomSize, ImperialUnits.SQUARE_FOOT)); // Only 400S appears to have this JSON extension object if (purifierStatus.result.result.extension != null) { diff --git a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties index 4a640363a086d..a67c8ccaa4641 100644 --- a/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties +++ b/bundles/org.openhab.binding.vesync/src/main/resources/OH-INF/i18n/vesync.properties @@ -49,7 +49,7 @@ channel-type.vesync.airPurifierModeType.state.option.pet = Pet Auto channel-type.vesync.airQualityPM25.label = Air Quality PPM2.5 channel-type.vesync.airQualityPM25.description = Indicator of current air quality channel-type.vesync.deviceAFConfigAutoPrefRoomSizeType.label = Config: Room size -channel-type.vesync.deviceAFConfigAutoPrefRoomSizeType.description = Room size (foot sq) for efficient auto mode +channel-type.vesync.deviceAFConfigAutoPrefRoomSizeType.description = Room size for efficient auto mode channel-type.vesync.deviceAFConfigAutoPrefType.label = Config: Auto Mode channel-type.vesync.deviceAFConfigAutoPrefType.description = The operating mode when the air purifier is set to auto channel-type.vesync.deviceAFConfigAutoPrefType.state.option.default = Auto (Air Quality) 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 9006b3542f016..111ca6e4f91a5 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 @@ -263,10 +263,10 @@ - Number:Dimensionless + Number:Area - Room size (foot sq) for efficient auto mode - + Room size for efficient auto mode + From e58107bc23b69774e1f0e74065cef71e79a8b35d Mon Sep 17 00:00:00 2001 From: dag81 Date: Wed, 27 Nov 2024 23:22:42 +0000 Subject: [PATCH 32/33] [veSync] PR feedback updates 3 [veSync] PR feedback updates 3 Signed-off-by: dag81 --- bundles/org.openhab.binding.vesync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index d868cef17c68d..c9a9de9bb172b 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -86,7 +86,7 @@ Channel names in **bold** are read/write, everything else is read-only | schedulesCount | Number:Dimensionless | The number of schedules which are configured | 600S, 400S | | one | | configDisplayForever | Switch | Config: Whether the display will disable when not active | 600S, 400S, 300S | | | | configAutoMode | String | Config: The mode of operation when auto is active | 600S, 400S, 300S | | | -| configAutoRoomSize | Number:Dimensionless | Config: The room size set when auto utilises the room size | 600S, 400S, 300S | | one | +| configAutoRoomSize | Number:Area | Config: The room size set when auto utilises the room size | 600S, 400S, 300S | | | ### AirHumidifier Thing From d3abbc56d7b2c72d76f8532ad312d0c01c56949d Mon Sep 17 00:00:00 2001 From: dag81 Date: Thu, 28 Nov 2024 02:52:35 +0000 Subject: [PATCH 33/33] [veSync] MIST Model Updates [veSync] MIST Model Updates Signed-off-by: dag81 --- bundles/org.openhab.binding.vesync/README.md | 39 ++++++++++--------- .../VeSyncDeviceAirHumidifierHandler.java | 15 +++++-- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/bundles/org.openhab.binding.vesync/README.md b/bundles/org.openhab.binding.vesync/README.md index c9a9de9bb172b..9f09b4c5fc819 100644 --- a/bundles/org.openhab.binding.vesync/README.md +++ b/bundles/org.openhab.binding.vesync/README.md @@ -90,25 +90,26 @@ Channel names in **bold** are read/write, everything else is read-only ### AirHumidifier Thing -| Channel | Type | Description | Model's Supported | Controllable Values | Unit | -|----------------------------|----------------------|---------------------------------------------------------------|------------------------------------------------------|---------------------|------| -| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | | -| **display** | Switch | Whether the display is enabled (display is shown) | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | | -| waterLacking | Switch | Indicator whether the unit is lacking water | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | | -| 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, OasisMist1000 | | | -| **stopAtHumiditySetpoint** | Switch | Whether the unit is set to stop when the set point is reached | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | | -| humidity | Number:Dimensionless | Indicator for the currently measured humidity % level | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | | -| **mistLevel** | Number:Dimensionless | The current mist level set | 300S | [1...2] | one | -| **mistLevel** | Number:Dimensionless | The current mist level set | 200S, Dual200S, 600S, OasisMist, OasisMist1000 | [1...3] | one | -| **humidifierMode** | String | The current mode of operation | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [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, OasisMist1000 | [30...80] | | -| warmEnabled | Switch | Indicator for warm mist mode | 600S, OasisMist | | | -| **warmLevel** | Number:Dimensionless | The current warm mist level set | 600S, OasisMist | [0..3] | one | -| errorCode | Number:Dimensionless | The error code reported by the device | OasisMist1000 | | one | -| timerExpiry | DateTime | The expected expiry time of the current timer | OasisMist1000 | | | -| schedulesCount | Number:Dimensionless | The number schedules configured | OasisMist1000 | | one | +| Channel | Type | Description | Model's Supported | Controllable Values | Unit | +|----------------------------|----------------------|---------------------------------------------------------------|------------------------------------------------------|-----------------------|------| +| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | | +| **display** | Switch | Whether the display is enabled (display is shown) | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | | +| waterLacking | Switch | Indicator whether the unit is lacking water | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | | +| 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, OasisMist1000 | | | +| **stopAtHumiditySetpoint** | Switch | Whether the unit is set to stop when the set point is reached | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [ON, OFF] | | +| humidity | Number:Dimensionless | Indicator for the currently measured humidity % level | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | | | +| **mistLevel** | Number:Dimensionless | The current mist level set | 300S | [1...2] | one | +| **mistLevel** | Number:Dimensionless | The current mist level set | 200S, Dual200S, 600S, OasisMist, OasisMist1000 | [1...3] | one | +| **humidifierMode** | String | The current mode of operation | 200S, Dual200S, OasisMist (EU Model) | [auto, manual] | | +| **humidifierMode** | String | The current mode of operation | 300S, 600S, OasisMist1000, OasisMist (Non EU Models) | [auto, manual, sleep] | | +| **nightLightMode** | String | The night light mode | 200S, Dual200S, 300S, OasisMist (EU Model) | [on, dim, off] | | +| **humiditySetpoint** | Number:Dimensionless | Humidity % set point to reach | 200S, Dual200S, 300S, 600S, OasisMist, OasisMist1000 | [30...80] | | +| warmEnabled | Switch | Indicator for warm mist mode | 600S, OasisMist | | | +| **warmLevel** | Number:Dimensionless | The current warm mist level set | 600S, OasisMist | [0..3] | one | +| errorCode | Number:Dimensionless | The error code reported by the device | OasisMist1000 | | one | +| timerExpiry | DateTime | The expected expiry time of the current timer | OasisMist1000 | | | +| schedulesCount | Number:Dimensionless | The number schedules configured | OasisMist1000 | | one | ## Full Example 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 da048d20d4324..2daf1c36c070e 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 @@ -72,6 +72,7 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { 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_EU = "Oasis Mist EU"; public static final String DEV_FAMILY_OASIS_MIST = "Oasis Mist"; public static final String DEV_FAMILY_OASIS_MIST_1000 = "Oasis Mist 1000"; @@ -98,16 +99,20 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { 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_EU = new VeSyncDeviceHumidifierMetadata(1, + DEV_FAMILY_OASIS_MIST_EU, Collections.emptyList(), Arrays.asList("LUH-O451S-WEU"), AUTO_MAN_MODES, 1, 3, 0, + 3, false, CLASSIC_300S_NIGHT_LIGHT_MODES); + public static final VeSyncDeviceHumidifierMetadata OASIS_MIST = new VeSyncDeviceHumidifierMetadata(1, - DEV_FAMILY_OASIS_MIST, Arrays.asList("O451S"), Collections.emptyList(), AUTO_MAN_SLEEP_MODES, 1, 3, 0, 3, - true, Collections.emptyList()); + DEV_FAMILY_OASIS_MIST, Arrays.asList("0601S"), Arrays.asList("LUH-O451S-WUS", "LUH-O451S-WUSR"), + AUTO_MAN_SLEEP_MODES, 1, 3, 0, 3, true, Collections.emptyList()); public static final VeSyncDeviceHumidifierMetadata OASIS_MIST_1000 = new VeSyncDeviceHumidifierMetadata(2, DEV_FAMILY_OASIS_MIST_1000, Arrays.asList("M101S"), Collections.emptyList(), AUTO_MAN_SLEEP_MODES, 1, 3, 0, 3, false, Collections.emptyList()); public static final List SUPPORTED_MODEL_FAMILIES = Arrays.asList(LV600S, CLASSIC300S, - CLASSIC200S, DUAL200S, OASIS_MIST); + CLASSIC200S, DUAL200S, OASIS_MIST, OASIS_MIST_EU); private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirHumidifierHandler.class); @@ -122,6 +127,7 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler { put(DUAL200S.deviceFamilyName, DUAL200S); put(LV600S.deviceFamilyName, LV600S); put(OASIS_MIST.deviceFamilyName, OASIS_MIST); + put(OASIS_MIST_EU.deviceFamilyName, OASIS_MIST_EU); put(OASIS_MIST_1000.deviceFamilyName, OASIS_MIST_1000); } }; @@ -155,6 +161,9 @@ protected String[] getChannelsToRemove() { toRemove = new String[] { DEVICE_CHANNEL_AF_NIGHT_LIGHT, DEVICE_CHANNEL_AF_SCHEDULES_COUNT, DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME }; break; + case DEV_FAMILY_OASIS_MIST_EU: + toRemove = new String[] { DEVICE_CHANNEL_AF_SCHEDULES_COUNT, DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME }; + break; } } return toRemove;