mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[veSync] 131 and Vital Purifiers base support (#15296)
* [veSync] Device support enhancements Device support enhancements Signed-off-by: David Goodyear <david.goodyear@gmail.com>
This commit is contained in:
parent
fd7fd8a84d
commit
b00a44aa76
@ -4,13 +4,13 @@ Its current support is for the Air Purifiers & Humidifer's branded as Levoit whi
|
|||||||
|
|
||||||
## Verified Models
|
## Verified Models
|
||||||
|
|
||||||
Air Filtering models supported are Core300S, Core400S.
|
Air Filtering models verified are Core300S, Core400S, Vital 100S.
|
||||||
Air Humidifier models supported are Dual 200S, Classic 300S, 600S, OasisMist Smart Humidifier
|
Air Humidifier models verified are Dual 200S, Classic 300S, 600S, OasisMist Smart Humidifier
|
||||||
|
|
||||||
## Awaiting User Verification Models
|
## Awaiting User Verification Models
|
||||||
|
|
||||||
Air Filtering models supported are Core200S and Core600S.
|
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)
|
Air Humidifier Classic 200S (Same as 300S without the nightlight from initial checks), OasisMist 1000 Smart Humidifier
|
||||||
|
|
||||||
## Supported Things
|
## Supported Things
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ This binding supports the follow thing types:
|
|||||||
|
|
||||||
This binding was developed from the great work in the listed projects.
|
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 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**.
|
The **Classic 300S Humidifier** has been tested, and **600S with current warm mode restrictions**.
|
||||||
|
|
||||||
## Discovery
|
## Discovery
|
||||||
@ -66,43 +66,50 @@ Channel names in **bold** are read/write, everything else is read-only
|
|||||||
|
|
||||||
### AirPurifier Thing
|
### AirPurifier Thing
|
||||||
|
|
||||||
| Channel | Type | Description | Model's Supported | Controllable Values |
|
| Channel | Type | Description | Model's Supported | Controllable Values | Unit |
|
||||||
|----------------------|----------------------|------------------------------------------------------------|-------------------|-----------------------|
|
|----------------------|----------------------|------------------------------------------------------------|------------------------------------------------|----------------------------|-------|
|
||||||
| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 600S, 400S, 300S | [ON, OFF] |
|
| **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 | [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) | 600S, 400S, 300S | [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 | 600S, 400S | [auto, manual, sleep] |
|
| **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 | 200S, 300S, | [manual, sleep] | |
|
||||||
| **manualFanSpeed** | Number:Dimensionless | The speed of the fan when in manual mode | 600S, 400S | [1...4] |
|
| **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 | 300S | [1...3] |
|
| **manualFanSpeed** | Number:Dimensionless | The speed of the fan when in manual mode | 600S, 400S | [1...4] | |
|
||||||
| **nightLightMode** | String | The night lights mode | 200S, 300S | [on, dim, off] |
|
| **manualFanSpeed** | Number:Dimensionless | The speed of the fan when in manual mode | 131S, 300S | [1...3] | |
|
||||||
| filterLifePercentage | Number:Dimensionless | The remaining filter life as a percentage | 600S, 400S, 300S | |
|
| **manualFanSpeed** | Number:Dimensionless | The speed of the fan when in manual mode | Vital 100S,Vital 200S | [1...5] | |
|
||||||
| airQuality | Number:Dimensionless | The air quality as represented by the Core200S / Core300S | 600S, 400S, 300S | |
|
| **nightLightMode** | String | The night lights mode | 200S, 300S | [on, dim, off] | |
|
||||||
| airQualityPM25 | Number:Density | The air quality as represented by the Core400S | 600S, 400S, 300S | |
|
| filterLifePercentage | Number:Dimensionless | The remaining filter life as a percentage | 131S, 600S, 400S, 300S, Vital 100S, Vital 200S | | |
|
||||||
| errorCode | Number:Dimensionless | The error code reported by the device | 600S, 400S, 300S | |
|
| airQuality | Number:Dimensionless | The air quality as represented by the Core200S / Core300S | 131S, 600S, 400S, 300S, Vital 100S, Vital 200S | | |
|
||||||
| timerExpiry | DateTime | The expected expiry time of the current timer | 600S, 400S | |
|
| airQualityPM25 | Number:Density | The air quality as represented by the Core400S | 600S, 400S, 300S, Vital 100S, Vital 200S | | µg/m³ |
|
||||||
| schedulesCount | Number:Dimensionless | The number schedules configured | 600S, 400S | |
|
| errorCode | Number:Dimensionless | The error code reported by the device | 600S, 400S, 300S, Vital 100S, Vital 200S | | |
|
||||||
| configDisplayForever | Switch | Config: Whether the display will disable when not active | 600S, 400S, 300S | |
|
| timerExpiry | DateTime | The expected expiry time of the current timer | 600S, 400S | | |
|
||||||
| configAutoMode | String | Config: The mode of operation when auto is active | 600S, 400S, 300S | |
|
| schedulesCount | Number:Dimensionless | The number of schedules which are configured | 600S, 400S | | one |
|
||||||
| configAutoRoomSize | Number:Dimensionless | Config: The room size set when auto utilises the room size | 600S, 400S, 300S | |
|
| 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:Area | Config: The room size set when auto utilises the room size | 600S, 400S, 300S | | |
|
||||||
|
|
||||||
### AirHumidifier Thing
|
### AirHumidifier Thing
|
||||||
|
|
||||||
| Channel | Type | Description | Model's Supported | Controllable Values |
|
| Channel | Type | Description | Model's Supported | Controllable Values | Unit |
|
||||||
|----------------------------|----------------------|---------------------------------------------------------------|---------------------------------------|---------------------|
|
|----------------------------|----------------------|---------------------------------------------------------------|------------------------------------------------------|-----------------------|------|
|
||||||
| **enabled** | Switch | Whether the hardware device is enabled (Switched on) | 200S, Dual200S, 300S, 600S, OasisMist | [ON, OFF] |
|
| **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 | [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 | |
|
| 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 | |
|
| 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 | |
|
| 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 | [ON, OFF] |
|
| **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 | |
|
| 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 | 300S | [1...2] | one |
|
||||||
| **mistLevel** | Number:Dimensionless | The current mist level set | 200S, Dual200S, 600S, OasisMist | [1...3] |
|
| **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 | [auto, sleep] |
|
| **humidifierMode** | String | The current mode of operation | 200S, Dual200S, OasisMist (EU Model) | [auto, manual] | |
|
||||||
| **nightLightMode** | String | The night light mode | 200S, Dual200S, 300S | [on, dim, off] |
|
| **humidifierMode** | String | The current mode of operation | 300S, 600S, OasisMist1000, OasisMist (Non EU Models) | [auto, manual, sleep] | |
|
||||||
| **humiditySetpoint** | Number:Dimensionless | Humidity % set point to reach | 200S, Dual200S, 300S, 600S, OasisMist | [30...80] |
|
| **nightLightMode** | String | The night light mode | 200S, Dual200S, 300S, OasisMist (EU Model) | [on, dim, off] | |
|
||||||
| warmEnabled | Switch | Indicator for warm mist mode | 600S, OasisMist | |
|
| **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
|
## Full Example
|
||||||
|
|
||||||
@ -129,7 +136,7 @@ Switch LoungeAPControlsLock "Lounge Air Purifier Controls
|
|||||||
Number:Dimensionless LoungeAPFilterRemainingUse "Lounge Air Purifier Filter Remaining [%.0f %unit%]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:filterLifePercentage" }
|
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" }
|
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 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" }
|
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" }
|
String LoungeAPAutoMode "Lounge Air Purifier Auto Mode" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoMode" }
|
||||||
Number LoungeAPAutoRoomSize "Lounge Air Purifier Auto Room Size [%.0f% sqft]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoRoomSize" }
|
Number LoungeAPAutoRoomSize "Lounge Air Purifier Auto Room Size [%.0f% sqft]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoRoomSize" }
|
||||||
@ -137,7 +144,7 @@ DateTime LoungeAPTimerExpiry "Lounge Air Purifier Timer Ex
|
|||||||
Number LoungeAPSchedulesCount "Lounge Air Purifier Schedules Count" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:schedulesCount" }
|
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
|
```java
|
||||||
Switch LoungeAPPower "Lounge Air Purifier Power" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:enabled" }
|
Switch LoungeAPPower "Lounge Air Purifier Power" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:enabled" }
|
||||||
@ -147,7 +154,7 @@ Switch LoungeAPControlsLock "Lounge Air Purifier Controls
|
|||||||
Number:Dimensionless LoungeAPFilterRemainingUse "Lounge Air Purifier Filter Remaining [%.0f %unit%]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:filterLifePercentage" }
|
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" }
|
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 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" }
|
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" }
|
String LoungeAPAutoMode "Lounge Air Purifier Auto Mode" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoMode" }
|
||||||
Number LoungeAPAutoRoomSize "Lounge Air Purifier Auto Room Size [%.0f% sqft]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoRoomSize" }
|
Number LoungeAPAutoRoomSize "Lounge Air Purifier Auto Room Size [%.0f% sqft]" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:configAutoRoomSize" }
|
||||||
@ -155,6 +162,30 @@ DateTime LoungeAPTimerExpiry "Lounge Air Purifier Timer Ex
|
|||||||
Number LoungeAPSchedulesCount "Lounge Air Purifier Schedules Count" { channel="vesync:airPurifier:vesyncServers:loungeAirFilter:schedulesCount" }
|
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
|
#### Air Humidifier Classic 200S / Dual 200S Model
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ -199,6 +230,7 @@ Number:Dimensionless LoungeAHHumidity "Lounge Air Humidifier Measured H
|
|||||||
Switch LoungeAHTargetStop "Lounge Air Humidifier Stop at target" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:stopAtTargetLevel" }
|
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 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 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
|
#### Air Humidifier Oasis Mist Smart Model
|
||||||
@ -214,6 +246,26 @@ Number:Dimensionless LoungeAHHumidity "Lounge Air Humidifier Measured H
|
|||||||
Switch LoungeAHTargetStop "Lounge Air Humidifier Stop at target" { channel="vesync:airHumidifier:vesyncServers:loungeHumidifier:stopAtTargetLevel" }
|
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 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 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)
|
### Configuration (*.sitemap)
|
||||||
@ -234,7 +286,7 @@ Frame {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Air Purifier Core 200S/300S Model
|
#### Air Purifier Core 200S / 300S Model
|
||||||
|
|
||||||
```perl
|
```perl
|
||||||
Frame {
|
Frame {
|
||||||
@ -251,6 +303,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
|
#### Air Humidifier Classic 200S / Dual 200S Model
|
||||||
|
|
||||||
```perl
|
```perl
|
||||||
@ -282,7 +375,7 @@ Frame {
|
|||||||
Text icon="none" item=LoungeAHHumidity
|
Text icon="none" item=LoungeAHHumidity
|
||||||
Switch item=LoungeAHTargetStop
|
Switch item=LoungeAHTargetStop
|
||||||
Slider item=LoungeAHTarget minValue=30 maxValue=80
|
Slider item=LoungeAHTarget minValue=30 maxValue=80
|
||||||
Slider item=LoungeAHMistLevel minValue=1 maxValue=3
|
Slider item=LoungeAHMistLevel minValue=0 maxValue=3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -292,7 +385,7 @@ Frame {
|
|||||||
Frame {
|
Frame {
|
||||||
Switch item=LoungeAHPower
|
Switch item=LoungeAHPower
|
||||||
Switch item=LoungeAHDisplay
|
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=LoungeAHWaterLacking
|
||||||
Text icon="none" item=LoungeAHHighHumidity
|
Text icon="none" item=LoungeAHHighHumidity
|
||||||
Text icon="none" item=LoungeAHWaterTankRemoved
|
Text icon="none" item=LoungeAHWaterTankRemoved
|
||||||
@ -300,6 +393,7 @@ Frame {
|
|||||||
Switch item=LoungeAHTargetStop
|
Switch item=LoungeAHTargetStop
|
||||||
Slider item=LoungeAHTarget minValue=30 maxValue=80
|
Slider item=LoungeAHTarget minValue=30 maxValue=80
|
||||||
Slider item=LoungeAHMistLevel minValue=1 maxValue=3
|
Slider item=LoungeAHMistLevel minValue=1 maxValue=3
|
||||||
|
Slider item=LoungeAHWarmMistLevel minValue=0 maxValue=3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -317,6 +411,27 @@ Frame {
|
|||||||
Switch item=LoungeAHTargetStop
|
Switch item=LoungeAHTargetStop
|
||||||
Slider item=LoungeAHTarget minValue=30 maxValue=80
|
Slider item=LoungeAHTarget minValue=30 maxValue=80
|
||||||
Slider item=LoungeAHMistLevel minValue=1 maxValue=3
|
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]"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ public class VeSyncConstants {
|
|||||||
|
|
||||||
public static final Gson GSON = new GsonBuilder()
|
public static final Gson GSON = new GsonBuilder()
|
||||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setPrettyPrinting()
|
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setPrettyPrinting()
|
||||||
.disableHtmlEscaping().serializeNulls().create();
|
.disableHtmlEscaping().create();
|
||||||
|
|
||||||
private static final String BINDING_ID = "vesync";
|
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_CONFIG_AUTO_ROOM_SIZE = "configAutoRoomSize";
|
||||||
public static final String DEVICE_CHANNEL_AF_SCHEDULES_COUNT = "schedulesCount";
|
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_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
|
// Humidity related channels
|
||||||
public static final String DEVICE_CHANNEL_WATER_LACKS = "waterLacking";
|
public static final String DEVICE_CHANNEL_WATER_LACKS = "waterLacking";
|
||||||
|
@ -18,11 +18,11 @@ import java.util.Set;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
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.VeSyncBridgeHandler;
|
||||||
import org.openhab.binding.vesync.internal.handlers.VeSyncDeviceAirHumidifierHandler;
|
import org.openhab.binding.vesync.internal.handlers.VeSyncDeviceAirHumidifierHandler;
|
||||||
import org.openhab.binding.vesync.internal.handlers.VeSyncDeviceAirPurifierHandler;
|
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.io.net.http.HttpClientFactory;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
@ -30,6 +30,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
|||||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
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.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
@ -41,12 +42,23 @@ import org.osgi.service.component.annotations.Reference;
|
|||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
@Component(configurationPid = "binding.vesync", service = ThingHandlerFactory.class)
|
@Component(configurationPid = "binding.vesync", service = ThingHandlerFactory.class)
|
||||||
public class VeSyncHandlerFactory extends BaseThingHandlerFactory implements IHttpClientProvider {
|
public class VeSyncHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE,
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE,
|
||||||
THING_TYPE_AIR_PURIFIER, THING_TYPE_AIR_HUMIDIFIER);
|
THING_TYPE_AIR_PURIFIER, THING_TYPE_AIR_HUMIDIFIER);
|
||||||
|
|
||||||
private @Nullable HttpClient httpClientRef = null;
|
private final HttpClientFactory httpClientFactory;
|
||||||
|
private final TranslationProvider translationProvider;
|
||||||
|
private final LocaleProvider localeProvider;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public VeSyncHandlerFactory(@Reference HttpClientFactory httpClientFactory,
|
||||||
|
@Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) {
|
||||||
|
super();
|
||||||
|
this.httpClientFactory = httpClientFactory;
|
||||||
|
this.translationProvider = translationProvider;
|
||||||
|
this.localeProvider = localeProvider;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
@ -58,23 +70,13 @@ public class VeSyncHandlerFactory extends BaseThingHandlerFactory implements IHt
|
|||||||
final ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
final ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||||
|
|
||||||
if (VeSyncDeviceAirPurifierHandler.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
|
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)) {
|
} 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)) {
|
} else if (THING_TYPE_BRIDGE.equals(thingTypeUID)) {
|
||||||
return new VeSyncBridgeHandler((Bridge) thing, this);
|
return new VeSyncBridgeHandler((Bridge) thing, httpClientFactory, translationProvider, localeProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Reference
|
|
||||||
protected void setHttpClientFactory(HttpClientFactory httpClientFactory) {
|
|
||||||
httpClientRef = httpClientFactory.getCommonHttpClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable HttpClient getHttpClient() {
|
|
||||||
return httpClientRef;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
|
||||||
}
|
|
@ -34,6 +34,7 @@ import org.eclipse.jetty.client.api.ContentResponse;
|
|||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
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.VeSyncConstants;
|
||||||
import org.openhab.binding.vesync.internal.dto.requests.VeSyncAuthenticatedRequest;
|
import org.openhab.binding.vesync.internal.dto.requests.VeSyncAuthenticatedRequest;
|
||||||
import org.openhab.binding.vesync.internal.dto.requests.VeSyncLoginCredentials;
|
import org.openhab.binding.vesync.internal.dto.requests.VeSyncLoginCredentials;
|
||||||
@ -58,13 +59,16 @@ public class VeSyncV2ApiHelper {
|
|||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(VeSyncV2ApiHelper.class);
|
private final Logger logger = LoggerFactory.getLogger(VeSyncV2ApiHelper.class);
|
||||||
|
|
||||||
private @NonNullByDefault({}) HttpClient httpClient;
|
private static final int RESPONSE_TIMEOUT_SEC = 5;
|
||||||
|
|
||||||
private volatile @Nullable VeSyncUserSession loggedInSession;
|
private volatile @Nullable VeSyncUserSession loggedInSession;
|
||||||
|
|
||||||
|
private final @Nullable HttpClient httpClient;
|
||||||
|
|
||||||
private Map<String, @NotNull VeSyncManagedDeviceBase> macLookup;
|
private Map<String, @NotNull VeSyncManagedDeviceBase> macLookup;
|
||||||
|
|
||||||
public VeSyncV2ApiHelper() {
|
public VeSyncV2ApiHelper(final HttpClient httpClient) {
|
||||||
|
this.httpClient = httpClient;
|
||||||
macLookup = new HashMap<>();
|
macLookup = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,13 +76,9 @@ public class VeSyncV2ApiHelper {
|
|||||||
return macLookup;
|
return macLookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void dispose() {
|
||||||
* Sets the httpClient object to be used for API calls to Vesync.
|
loggedInSession = null;
|
||||||
*
|
macLookup.clear();
|
||||||
* @param httpClient the client to be used.
|
|
||||||
*/
|
|
||||||
public void setHttpClient(@Nullable HttpClient httpClient) {
|
|
||||||
this.httpClient = httpClient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull String calculateMd5(final @Nullable String password) {
|
public static @NotNull String calculateMd5(final @Nullable String password) {
|
||||||
@ -154,6 +154,7 @@ public class VeSyncV2ApiHelper {
|
|||||||
}
|
}
|
||||||
veSyncRequestManagedDeviceBypassV2.cid = deviceData.cid;
|
veSyncRequestManagedDeviceBypassV2.cid = deviceData.cid;
|
||||||
veSyncRequestManagedDeviceBypassV2.configModule = deviceData.configModule;
|
veSyncRequestManagedDeviceBypassV2.configModule = deviceData.configModule;
|
||||||
|
veSyncRequestManagedDeviceBypassV2.configModel = deviceData.configModule;
|
||||||
veSyncRequestManagedDeviceBypassV2.deviceRegion = deviceData.deviceRegion;
|
veSyncRequestManagedDeviceBypassV2.deviceRegion = deviceData.deviceRegion;
|
||||||
}
|
}
|
||||||
return reqV1Authorized(url, requestData);
|
return reqV1Authorized(url, requestData);
|
||||||
@ -167,16 +168,22 @@ public class VeSyncV2ApiHelper {
|
|||||||
private String directReqV1Authorized(final String url, final VeSyncAuthenticatedRequest requestData)
|
private String directReqV1Authorized(final String url, final VeSyncAuthenticatedRequest requestData)
|
||||||
throws AuthenticationException {
|
throws AuthenticationException {
|
||||||
try {
|
try {
|
||||||
Request request = httpClient.POST(url);
|
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
|
// No headers for login
|
||||||
request.content(new StringContentProvider(VeSyncConstants.GSON.toJson(requestData)));
|
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");
|
request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8");
|
||||||
|
|
||||||
ContentResponse response = request.timeout(5, TimeUnit.SECONDS).send();
|
ContentResponse response = request.send();
|
||||||
if (response.getStatus() == HttpURLConnection.HTTP_OK) {
|
if (response.getStatus() == HttpURLConnection.HTTP_OK) {
|
||||||
VeSyncResponse commResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(),
|
VeSyncResponse commResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(),
|
||||||
VeSyncResponse.class);
|
VeSyncResponse.class);
|
||||||
@ -220,7 +227,12 @@ public class VeSyncV2ApiHelper {
|
|||||||
private VeSyncLoginResponse processLogin(String username, String password, String timezone)
|
private VeSyncLoginResponse processLogin(String username, String password, String timezone)
|
||||||
throws AuthenticationException {
|
throws AuthenticationException {
|
||||||
try {
|
try {
|
||||||
Request request = httpClient.POST(V1_LOGIN_ENDPOINT);
|
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
|
// No headers for login
|
||||||
request.content(new StringContentProvider(
|
request.content(new StringContentProvider(
|
||||||
@ -228,7 +240,7 @@ public class VeSyncV2ApiHelper {
|
|||||||
|
|
||||||
request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8");
|
request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8");
|
||||||
|
|
||||||
ContentResponse response = request.timeout(5, TimeUnit.SECONDS).send();
|
ContentResponse response = request.send();
|
||||||
if (response.getStatus() == HttpURLConnection.HTTP_OK) {
|
if (response.getStatus() == HttpURLConnection.HTTP_OK) {
|
||||||
VeSyncLoginResponse loginResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(),
|
VeSyncLoginResponse loginResponse = VeSyncConstants.GSON.fromJson(response.getContentAsString(),
|
||||||
VeSyncLoginResponse.class);
|
VeSyncLoginResponse.class);
|
||||||
|
@ -23,6 +23,8 @@ public interface VeSyncProtocolConstants {
|
|||||||
String MODE_AUTO = "auto";
|
String MODE_AUTO = "auto";
|
||||||
String MODE_MANUAL = "manual";
|
String MODE_MANUAL = "manual";
|
||||||
String MODE_SLEEP = "sleep";
|
String MODE_SLEEP = "sleep";
|
||||||
|
String MODE_PET = "pet";
|
||||||
|
String MODE_AUTO_HUMIDITY = "humidity";
|
||||||
|
|
||||||
String MODE_ON = "on";
|
String MODE_ON = "on";
|
||||||
String MODE_DIM = "dim";
|
String MODE_DIM = "dim";
|
||||||
@ -42,6 +44,7 @@ public interface VeSyncProtocolConstants {
|
|||||||
String DEVICE_GET_HUMIDIFIER_STATUS = "getHumidifierStatus";
|
String DEVICE_GET_HUMIDIFIER_STATUS = "getHumidifierStatus";
|
||||||
|
|
||||||
String DEVICE_LEVEL_TYPE_MIST = "mist";
|
String DEVICE_LEVEL_TYPE_MIST = "mist";
|
||||||
|
String DEVICE_LEVEL_TYPE_WARM_MIST = "warm";
|
||||||
|
|
||||||
// Air Purifier Commands
|
// Air Purifier Commands
|
||||||
String DEVICE_SET_PURIFIER_MODE = "setPurifierMode";
|
String DEVICE_SET_PURIFIER_MODE = "setPurifierMode";
|
||||||
@ -49,12 +52,16 @@ public interface VeSyncProtocolConstants {
|
|||||||
String DEVICE_SET_NIGHT_LIGHT = "setNightLight";
|
String DEVICE_SET_NIGHT_LIGHT = "setNightLight";
|
||||||
String DEVICE_GET_PURIFIER_STATUS = "getPurifierStatus";
|
String DEVICE_GET_PURIFIER_STATUS = "getPurifierStatus";
|
||||||
String DEVICE_LEVEL_TYPE_WIND = "wind";
|
String DEVICE_LEVEL_TYPE_WIND = "wind";
|
||||||
|
String DEVICE_SET_LIGHT_DETECTION = "setLightDetection";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base URL for AUTHENTICATION REQUESTS
|
* Base URL for AUTHENTICATION REQUESTS
|
||||||
*/
|
*/
|
||||||
String PROTOCOL = "https";
|
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_LOGIN_ENDPOINT = HOST_ENDPOINT + "/v1/user/login";
|
||||||
String V1_MANAGED_DEVICES_ENDPOINT = HOST_ENDPOINT + "/v1/deviceManaged/devices";
|
String V1_MANAGED_DEVICES_ENDPOINT = HOST_ENDPOINT + "/v1/deviceManaged/devices";
|
||||||
String V2_BYPASS_ENDPOINT = HOST_ENDPOINT + "/v2/deviceManaged/bypassV2";
|
String V2_BYPASS_ENDPOINT = HOST_ENDPOINT + "/v2/deviceManaged/bypassV2";
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.vesync.internal.dto.requests;
|
package org.openhab.binding.vesync.internal.dto.requests;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,6 +23,8 @@ import com.google.gson.annotations.SerializedName;
|
|||||||
*/
|
*/
|
||||||
public class VeSyncRequest {
|
public class VeSyncRequest {
|
||||||
|
|
||||||
|
public transient HttpMethod httpMethod;
|
||||||
|
|
||||||
@SerializedName("timeZone")
|
@SerializedName("timeZone")
|
||||||
public String timeZone = "America/New_York";
|
public String timeZone = "America/New_York";
|
||||||
|
|
||||||
@ -42,7 +46,11 @@ public class VeSyncRequest {
|
|||||||
@SerializedName("method")
|
@SerializedName("method")
|
||||||
public String method;
|
public String method;
|
||||||
|
|
||||||
|
@SerializedName("deviceId")
|
||||||
|
public String deviceId;
|
||||||
|
|
||||||
public VeSyncRequest() {
|
public VeSyncRequest() {
|
||||||
traceId = String.valueOf(System.currentTimeMillis());
|
traceId = String.valueOf(System.currentTimeMillis());
|
||||||
|
httpMethod = HttpMethod.POST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,9 @@ public class VeSyncRequestManagedDeviceBypassV2 extends VeSyncAuthenticatedReque
|
|||||||
@SerializedName("configModule")
|
@SerializedName("configModule")
|
||||||
public String configModule = "";
|
public String configModule = "";
|
||||||
|
|
||||||
|
@SerializedName("configModel")
|
||||||
|
public String configModel = "";
|
||||||
|
|
||||||
@SerializedName("payload")
|
@SerializedName("payload")
|
||||||
public VesyncManagedDeviceBase payload = new VesyncManagedDeviceBase();
|
public VesyncManagedDeviceBase payload = new VesyncManagedDeviceBase();
|
||||||
|
|
||||||
@ -55,6 +58,75 @@ public class VeSyncRequestManagedDeviceBypassV2 extends VeSyncAuthenticatedReque
|
|||||||
public static class EmptyPayload {
|
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 static class SetSwitchPayload extends EmptyPayload {
|
||||||
|
|
||||||
public SetSwitchPayload(final boolean enabled, final int id) {
|
public SetSwitchPayload(final boolean enabled, final int id) {
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* 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.dto.requests;
|
||||||
|
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
@ -18,8 +18,8 @@ import org.openhab.binding.vesync.internal.exceptions.AuthenticationException;
|
|||||||
import com.google.gson.annotations.SerializedName;
|
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
|
||||||
* API.
|
* the Vesync API.
|
||||||
*
|
*
|
||||||
* @author David Goodyear - Initial contribution
|
* @author David Goodyear - Initial contribution
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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.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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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.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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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.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;
|
||||||
|
}
|
||||||
|
}
|
@ -16,8 +16,7 @@ import com.google.gson.annotations.SerializedName;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link VeSyncV2BypassPurifierStatus} is a Java class used as a DTO to hold the Vesync's API's common response
|
* The {@link VeSyncV2BypassPurifierStatus} is a Java class used as a DTO to hold the Vesync's API's common response
|
||||||
* data,
|
* data, in regards to an Air Purifier device.
|
||||||
* in regards to an Air Purifier device.
|
|
||||||
*
|
*
|
||||||
* @author David Goodyear - Initial contribution
|
* @author David Goodyear - Initial contribution
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* 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.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* 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.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -53,6 +53,29 @@ public class VeSyncV1AirPurifierDeviceDetailsResponse extends VeSyncResponse {
|
|||||||
return mode;
|
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")
|
@SerializedName("deviceName")
|
||||||
public String deviceName;
|
public String deviceName;
|
||||||
|
|
||||||
|
@ -16,14 +16,9 @@ import static org.openhab.binding.vesync.internal.VeSyncConstants.*;
|
|||||||
import static org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolConstants.V2_BYPASS_ENDPOINT;
|
import static org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolConstants.V2_BYPASS_ENDPOINT;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@ -32,11 +27,14 @@ import org.eclipse.jdt.annotation.Nullable;
|
|||||||
import org.openhab.binding.vesync.internal.VeSyncBridgeConfiguration;
|
import org.openhab.binding.vesync.internal.VeSyncBridgeConfiguration;
|
||||||
import org.openhab.binding.vesync.internal.VeSyncDeviceConfiguration;
|
import org.openhab.binding.vesync.internal.VeSyncDeviceConfiguration;
|
||||||
import org.openhab.binding.vesync.internal.dto.requests.VeSyncAuthenticatedRequest;
|
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.requests.VeSyncRequestManagedDeviceBypassV2;
|
||||||
import org.openhab.binding.vesync.internal.dto.responses.VeSyncManagedDeviceBase;
|
import org.openhab.binding.vesync.internal.dto.responses.VeSyncManagedDeviceBase;
|
||||||
import org.openhab.binding.vesync.internal.exceptions.AuthenticationException;
|
import org.openhab.binding.vesync.internal.exceptions.AuthenticationException;
|
||||||
import org.openhab.binding.vesync.internal.exceptions.DeviceUnknownException;
|
import org.openhab.binding.vesync.internal.exceptions.DeviceUnknownException;
|
||||||
import org.openhab.core.cache.ExpiringCache;
|
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.Bridge;
|
||||||
import org.openhab.core.thing.Channel;
|
import org.openhab.core.thing.Channel;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
@ -47,6 +45,10 @@ import org.openhab.core.thing.binding.BaseThingHandler;
|
|||||||
import org.openhab.core.thing.binding.BridgeHandler;
|
import org.openhab.core.thing.binding.BridgeHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -88,8 +90,23 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler {
|
|||||||
@Nullable
|
@Nullable
|
||||||
ScheduledFuture<?> readbackPollTask = null;
|
ScheduledFuture<?> readbackPollTask = null;
|
||||||
|
|
||||||
public VeSyncBaseDeviceHandler(Thing thing) {
|
private final TranslationProvider translationProvider;
|
||||||
|
private final LocaleProvider localeProvider;
|
||||||
|
private final Bundle bundle;
|
||||||
|
|
||||||
|
private String deviceId = "";
|
||||||
|
|
||||||
|
public VeSyncBaseDeviceHandler(Thing thing, @Reference TranslationProvider translationProvider,
|
||||||
|
@Reference LocaleProvider localeProvider) {
|
||||||
super(thing);
|
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) {
|
protected @Nullable Channel findChannelById(final String channelGroupId) {
|
||||||
@ -151,12 +168,7 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler {
|
|||||||
if (bridgeHandler instanceof VeSyncBridgeHandler veSyncBridgeHandler) {
|
if (bridgeHandler instanceof VeSyncBridgeHandler veSyncBridgeHandler) {
|
||||||
@Nullable
|
@Nullable
|
||||||
VeSyncManagedDeviceBase metadata = veSyncBridgeHandler.api.getMacLookupMap().get(deviceLookupKey);
|
VeSyncManagedDeviceBase metadata = veSyncBridgeHandler.api.getMacLookupMap().get(deviceLookupKey);
|
||||||
|
return metadata != null && "online".equals(metadata.connectionStatus);
|
||||||
if (metadata == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ("online".equals(metadata.connectionStatus));
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -175,6 +187,8 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler {
|
|||||||
|
|
||||||
newProps = getMetadataProperities(metadata);
|
newProps = getMetadataProperities(metadata);
|
||||||
|
|
||||||
|
deviceId = metadata.getUuid();
|
||||||
|
|
||||||
// Refresh the device -> protocol mapping
|
// Refresh the device -> protocol mapping
|
||||||
deviceLookupKey = getValidatedIdString();
|
deviceLookupKey = getValidatedIdString();
|
||||||
|
|
||||||
@ -404,13 +418,26 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler {
|
|||||||
protected final String sendV2BypassControlCommand(final String method,
|
protected final String sendV2BypassControlCommand(final String method,
|
||||||
final VeSyncRequestManagedDeviceBypassV2.EmptyPayload payload, final boolean readbackDevice) {
|
final VeSyncRequestManagedDeviceBypassV2.EmptyPayload payload, final boolean readbackDevice) {
|
||||||
final String result = sendV2BypassCommand(method, payload);
|
final String result = sendV2BypassCommand(method, payload);
|
||||||
if (!result.equals(EMPTY_STRING) && readbackDevice) {
|
if (!EMPTY_STRING.equals(result) && readbackDevice) {
|
||||||
performReadbackPoll();
|
performReadbackPoll();
|
||||||
}
|
}
|
||||||
return result;
|
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 (!EMPTY_STRING.equals(result) && readbackDevice) {
|
||||||
|
performReadbackPoll();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String sendV1Command(final String urlPath, final VeSyncAuthenticatedRequest request) {
|
||||||
if (ThingStatus.OFFLINE.equals(this.thing.getStatus())) {
|
if (ThingStatus.OFFLINE.equals(this.thing.getStatus())) {
|
||||||
logger.debug("Command blocked as device is offline");
|
logger.debug("Command blocked as device is offline");
|
||||||
return EMPTY_STRING;
|
return EMPTY_STRING;
|
||||||
@ -422,6 +449,7 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler {
|
|||||||
}
|
}
|
||||||
VeSyncClient client = getVeSyncClient();
|
VeSyncClient client = getVeSyncClient();
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
|
final String url = VeSyncProtocolConstants.SERVER_ENDPOINT + "/" + urlPath;
|
||||||
return client.reqV2Authorized(url, deviceLookupKey, request);
|
return client.reqV2Authorized(url, deviceLookupKey, request);
|
||||||
} else {
|
} else {
|
||||||
throw new DeviceUnknownException("Missing client");
|
throw new DeviceUnknownException("Missing client");
|
||||||
@ -455,6 +483,7 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler {
|
|||||||
VeSyncRequestManagedDeviceBypassV2 readReq = new VeSyncRequestManagedDeviceBypassV2();
|
VeSyncRequestManagedDeviceBypassV2 readReq = new VeSyncRequestManagedDeviceBypassV2();
|
||||||
readReq.payload.method = method;
|
readReq.payload.method = method;
|
||||||
readReq.payload.data = payload;
|
readReq.payload.data = payload;
|
||||||
|
readReq.deviceId = deviceId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (MARKER_INVALID_DEVICE_KEY.equals(deviceLookupKey)) {
|
if (MARKER_INVALID_DEVICE_KEY.equals(deviceLookupKey)) {
|
||||||
@ -526,17 +555,26 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler {
|
|||||||
|
|
||||||
public static VeSyncDeviceMetadata getDeviceFamilyMetadata(final @Nullable String deviceType,
|
public static VeSyncDeviceMetadata getDeviceFamilyMetadata(final @Nullable String deviceType,
|
||||||
final String deviceProtocolPrefix, final List<VeSyncDeviceMetadata> metadata) {
|
final String deviceProtocolPrefix, final List<VeSyncDeviceMetadata> metadata) {
|
||||||
|
|
||||||
if (deviceType == null) {
|
if (deviceType == null) {
|
||||||
return UNKNOWN;
|
return UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First look for a direct ID match, if no matches are found scan for the matches based on the generation ID.
|
||||||
|
final Optional<VeSyncDeviceMetadata> directIdMatch = metadata.stream()
|
||||||
|
.filter(x -> x.nonStandardIds.contains(deviceType)).findFirst();
|
||||||
|
if (directIdMatch.isPresent()) {
|
||||||
|
return directIdMatch.get();
|
||||||
|
}
|
||||||
|
|
||||||
final String[] idParts = deviceType.split("-");
|
final String[] idParts = deviceType.split("-");
|
||||||
if (idParts.length == 3) {
|
if (idParts.length == 3) {
|
||||||
if (!deviceProtocolPrefix.equals(idParts[0])) {
|
if (!deviceProtocolPrefix.equals(idParts[0])) {
|
||||||
return UNKNOWN;
|
return UNKNOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<VeSyncDeviceMetadata> foundMatch = metadata.stream()
|
final List<VeSyncDeviceMetadata> foundMatch = metadata.stream().filter(x -> x.deviceTypeIdMatches(idParts))
|
||||||
.filter(x -> x.deviceTypeIdMatches(deviceType, idParts)).collect(Collectors.toList());
|
.toList();
|
||||||
if (foundMatch.size() == 1) {
|
if (foundMatch.size() == 1) {
|
||||||
return foundMatch.get(0);
|
return foundMatch.get(0);
|
||||||
} else {
|
} else {
|
||||||
@ -547,4 +585,15 @@ public abstract class VeSyncBaseDeviceHandler extends BaseThingHandler {
|
|||||||
public VeSyncDeviceMetadata getDeviceFamilyMetadata(final @Nullable String deviceType) {
|
public VeSyncDeviceMetadata getDeviceFamilyMetadata(final @Nullable String deviceType) {
|
||||||
return getDeviceFamilyMetadata(deviceType, getDeviceFamilyProtocolPrefix(), getSupportedDeviceMetadata());
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import static org.openhab.binding.vesync.internal.VeSyncConstants.*;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
@ -27,7 +28,6 @@ import javax.validation.constraints.NotNull;
|
|||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.binding.vesync.internal.VeSyncBridgeConfiguration;
|
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.api.VeSyncV2ApiHelper;
|
||||||
import org.openhab.binding.vesync.internal.discovery.DeviceMetaDataUpdatedHandler;
|
import org.openhab.binding.vesync.internal.discovery.DeviceMetaDataUpdatedHandler;
|
||||||
import org.openhab.binding.vesync.internal.discovery.VeSyncDiscoveryService;
|
import org.openhab.binding.vesync.internal.discovery.VeSyncDiscoveryService;
|
||||||
@ -36,6 +36,9 @@ import org.openhab.binding.vesync.internal.dto.responses.VeSyncManagedDeviceBase
|
|||||||
import org.openhab.binding.vesync.internal.dto.responses.VeSyncUserSession;
|
import org.openhab.binding.vesync.internal.dto.responses.VeSyncUserSession;
|
||||||
import org.openhab.binding.vesync.internal.exceptions.AuthenticationException;
|
import org.openhab.binding.vesync.internal.exceptions.AuthenticationException;
|
||||||
import org.openhab.binding.vesync.internal.exceptions.DeviceUnknownException;
|
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.Bridge;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
@ -46,6 +49,9 @@ import org.openhab.core.thing.binding.BaseBridgeHandler;
|
|||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
import org.openhab.core.types.Command;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -62,19 +68,30 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie
|
|||||||
private static final int DEFAULT_DEVICE_SCAN_RECOVERY_INTERVAL = 60;
|
private static final int DEFAULT_DEVICE_SCAN_RECOVERY_INTERVAL = 60;
|
||||||
private static final int DEFAULT_DEVICE_SCAN_DISABLED = -1;
|
private static final int DEFAULT_DEVICE_SCAN_DISABLED = -1;
|
||||||
|
|
||||||
|
private volatile int backgroundScanTime = -1;
|
||||||
|
|
||||||
|
protected final VeSyncV2ApiHelper api;
|
||||||
private final Logger logger = LoggerFactory.getLogger(VeSyncBridgeHandler.class);
|
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 @Nullable ScheduledFuture<?> backgroundDiscoveryPollingJob;
|
||||||
|
|
||||||
protected final VeSyncV2ApiHelper api = new VeSyncV2ApiHelper();
|
public VeSyncBridgeHandler(Bridge bridge, @Reference HttpClientFactory httpClientFactory,
|
||||||
private IHttpClientProvider httpClientProvider;
|
@Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) {
|
||||||
|
|
||||||
private volatile int backgroundScanTime = -1;
|
|
||||||
private final Object scanConfigLock = new Object();
|
|
||||||
|
|
||||||
public VeSyncBridgeHandler(Bridge bridge, @NotNull IHttpClientProvider httpClientProvider) {
|
|
||||||
super(bridge);
|
super(bridge);
|
||||||
this.httpClientProvider = httpClientProvider;
|
api = new VeSyncV2ApiHelper(httpClientFactory.getCommonHttpClient());
|
||||||
|
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() {
|
public ThingUID getUID() {
|
||||||
@ -145,7 +162,8 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie
|
|||||||
runDeviceScanSequence();
|
runDeviceScanSequence();
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
} catch (AuthenticationException ae) {
|
} catch (AuthenticationException ae) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Check login credentials");
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
|
getLocalizedText("bridge.offline.check-credentials"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,8 +216,6 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
api.setHttpClient(httpClientProvider.getHttpClient());
|
|
||||||
|
|
||||||
VeSyncBridgeConfiguration config = getConfigAs(VeSyncBridgeConfiguration.class);
|
VeSyncBridgeConfiguration config = getConfigAs(VeSyncBridgeConfiguration.class);
|
||||||
|
|
||||||
scheduler.submit(() -> {
|
scheduler.submit(() -> {
|
||||||
@ -211,7 +227,8 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie
|
|||||||
runDeviceScanSequence();
|
runDeviceScanSequence();
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
} catch (final AuthenticationException ae) {
|
} 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
|
// The background scan will keep trying to authenticate in case the users credentials are updated on the
|
||||||
// veSync servers,
|
// veSync servers,
|
||||||
// to match the binding's configuration.
|
// to match the binding's configuration.
|
||||||
@ -222,12 +239,12 @@ public class VeSyncBridgeHandler extends BaseBridgeHandler implements VeSyncClie
|
|||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
setBackgroundScanInterval(DEFAULT_DEVICE_SCAN_DISABLED);
|
setBackgroundScanInterval(DEFAULT_DEVICE_SCAN_DISABLED);
|
||||||
api.setHttpClient(null);
|
api.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
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) {
|
public void handleNewUserSession(final @Nullable VeSyncUserSession userSessionData) {
|
||||||
|
@ -15,17 +15,28 @@ package org.openhab.binding.vesync.internal.handlers;
|
|||||||
import static org.openhab.binding.vesync.internal.VeSyncConstants.*;
|
import static org.openhab.binding.vesync.internal.VeSyncConstants.*;
|
||||||
import static org.openhab.binding.vesync.internal.dto.requests.VeSyncProtocolConstants.*;
|
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.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
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.VeSyncBridgeConfiguration;
|
||||||
import org.openhab.binding.vesync.internal.VeSyncConstants;
|
import org.openhab.binding.vesync.internal.VeSyncConstants;
|
||||||
import org.openhab.binding.vesync.internal.dto.requests.VeSyncRequestManagedDeviceBypassV2;
|
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.VeSyncV2BypassHumidifierStatus;
|
||||||
|
import org.openhab.binding.vesync.internal.dto.responses.VeSyncV2Ver2BypassHumidifierStatus;
|
||||||
import org.openhab.core.cache.ExpiringCache;
|
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;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
import org.openhab.core.library.types.QuantityType;
|
import org.openhab.core.library.types.QuantityType;
|
||||||
@ -37,6 +48,7 @@ import org.openhab.core.thing.ThingStatus;
|
|||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
import org.openhab.core.types.RefreshType;
|
import org.openhab.core.types.RefreshType;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -47,38 +59,60 @@ import org.slf4j.LoggerFactory;
|
|||||||
* @author David Goodyear - Initial contribution
|
* @author David Goodyear - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
|
@SuppressWarnings("serial")
|
||||||
public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
||||||
|
|
||||||
public static final String DEV_TYPE_FAMILY_AIR_HUMIDIFIER = "LUH";
|
public static final String DEV_TYPE_FAMILY_AIR_HUMIDIFIER = "LUH";
|
||||||
|
|
||||||
public static final int DEFAULT_AIR_PURIFIER_POLL_RATE = 120;
|
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_200S = "Classic 200S";
|
||||||
public static final String DEV_FAMILY_CLASSIC_300S = "Classic 300S";
|
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_DUAL_200S = "Dual 200S";
|
||||||
public static final String DEV_FAMILY_600S = "600S";
|
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 = "Oasis Mist";
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata CLASSIC200S = new VeSyncDeviceMetadata(DEV_FAMILY_CLASSIC_200S,
|
public static final String DEV_FAMILY_OASIS_MIST_1000 = "Oasis Mist 1000";
|
||||||
Collections.emptyList(), List.of("Classic200S"));
|
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata CLASSIC300S = new VeSyncDeviceMetadata(DEV_FAMILY_CLASSIC_300S,
|
private static final List<String> AUTO_MAN_SLEEP_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP);
|
||||||
Arrays.asList("A601S"), List.of("Classic300S"));
|
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata DUAL200S = new VeSyncDeviceMetadata(DEV_FAMILY_DUAL_200S,
|
private static final List<String> AUTO_MAN_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL);
|
||||||
Arrays.asList("D301S"), List.of("Dual200S"));
|
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata LV600S = new VeSyncDeviceMetadata(DEV_FAMILY_600S, Arrays.asList("A602S"),
|
private static final List<String> CLASSIC_300S_NIGHT_LIGHT_MODES = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF);
|
||||||
|
|
||||||
|
public static final VeSyncDeviceHumidifierMetadata CLASSIC200S = new VeSyncDeviceHumidifierMetadata(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());
|
Collections.emptyList());
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata OASIS_MIST = new VeSyncDeviceMetadata(DEV_FAMILY_OASIS_MIST,
|
public static final VeSyncDeviceHumidifierMetadata LV600S = new VeSyncDeviceHumidifierMetadata(1, DEV_FAMILY_600S,
|
||||||
Arrays.asList("O451S"), Collections.emptyList());
|
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", "O601S"), 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<VeSyncDeviceMetadata> SUPPORTED_MODEL_FAMILIES = Arrays.asList(LV600S, CLASSIC300S,
|
public static final List<VeSyncDeviceMetadata> SUPPORTED_MODEL_FAMILIES = Arrays.asList(LV600S, CLASSIC300S,
|
||||||
CLASSIC200S, DUAL200S, OASIS_MIST);
|
CLASSIC200S, DUAL200S, OASIS_MIST, OASIS_MIST_EU);
|
||||||
|
|
||||||
private static final List<String> CLASSIC_300S_600S_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP);
|
|
||||||
private static final List<String> CLASSIC_300S_NIGHT_LIGHT_MODES = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF);
|
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirHumidifierHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirHumidifierHandler.class);
|
||||||
|
|
||||||
@ -86,8 +120,21 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
|
|
||||||
private final Object pollLock = new Object();
|
private final Object pollLock = new Object();
|
||||||
|
|
||||||
public VeSyncDeviceAirHumidifierHandler(Thing thing) {
|
public static final Map<String, VeSyncDeviceHumidifierMetadata> DEV_FAMILY_HUMIDIFER_MAP = new HashMap<String, VeSyncDeviceHumidifierMetadata>() {
|
||||||
super(thing);
|
{
|
||||||
|
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_EU.deviceFamilyName, OASIS_MIST_EU);
|
||||||
|
put(OASIS_MIST_1000.deviceFamilyName, OASIS_MIST_1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public VeSyncDeviceAirHumidifierHandler(Thing thing, @Reference TranslationProvider translationProvider,
|
||||||
|
@Reference LocaleProvider localeProvider) {
|
||||||
|
super(thing, translationProvider, localeProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -97,15 +144,25 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
if (deviceFamily != null) {
|
if (deviceFamily != null) {
|
||||||
switch (deviceFamily) {
|
switch (deviceFamily) {
|
||||||
case DEV_FAMILY_CLASSIC_300S:
|
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;
|
break;
|
||||||
case DEV_FAMILY_DUAL_200S:
|
case DEV_FAMILY_DUAL_200S:
|
||||||
case DEV_FAMILY_CLASSIC_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,
|
toRemove = new String[] { DEVICE_CHANNEL_WARM_ENABLED, DEVICE_CHANNEL_WARM_LEVEL,
|
||||||
DEVICE_CHANNEL_AF_NIGHT_LIGHT };
|
DEVICE_CHANNEL_AF_NIGHT_LIGHT };
|
||||||
break;
|
break;
|
||||||
case DEV_FAMILY_OASIS_MIST:
|
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;
|
||||||
|
case DEV_FAMILY_OASIS_MIST_EU:
|
||||||
|
toRemove = new String[] { DEVICE_CHANNEL_AF_SCHEDULES_COUNT, DEVICE_CHANNEL_AF_AUTO_OFF_CALC_TIME };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,6 +209,11 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
if (deviceFamily == null) {
|
if (deviceFamily == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final VeSyncDeviceHumidifierMetadata devContraints = DEV_FAMILY_HUMIDIFER_MAP.get(deviceFamily);
|
||||||
|
if (devContraints == null) {
|
||||||
|
logger.warn("{}", getLocalizedText("warning.device.command-device-family-not-found", deviceFamily));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
scheduler.submit(() -> {
|
scheduler.submit(() -> {
|
||||||
|
|
||||||
@ -171,47 +233,44 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
new VeSyncRequestManagedDeviceBypassV2.EnabledPayload(command.equals(OnOffType.ON)));
|
new VeSyncRequestManagedDeviceBypassV2.EnabledPayload(command.equals(OnOffType.ON)));
|
||||||
break;
|
break;
|
||||||
case DEVICE_CHANNEL_WARM_ENABLED:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
} else if (command instanceof QuantityType quantityCommand) {
|
} else if (command instanceof QuantityType quantityCommand) {
|
||||||
switch (channelUID.getId()) {
|
switch (channelUID.getId()) {
|
||||||
case DEVICE_CHANNEL_CONFIG_TARGET_HUMIDITY:
|
case DEVICE_CHANNEL_CONFIG_TARGET_HUMIDITY:
|
||||||
int targetHumidity = quantityCommand.intValue();
|
int targetHumidity = quantityCommand.intValue();
|
||||||
if (targetHumidity < 30) {
|
if (targetHumidity < MIN_TARGET_HUMIDITY) {
|
||||||
logger.warn("Target Humidity less than 30 - adjusting to 30 as the valid API value");
|
logger.warn("{}", getLocalizedText("warning.device.humidity-under", MIN_TARGET_HUMIDITY));
|
||||||
targetHumidity = 30;
|
targetHumidity = MIN_TARGET_HUMIDITY;
|
||||||
} else if (targetHumidity > 80) {
|
} else if (targetHumidity > MAX_TARGET_HUMIDITY) {
|
||||||
logger.warn("Target Humidity greater than 80 - adjusting to 80 as the valid API value");
|
logger.warn("{}", getLocalizedText("warning.device.humidity-over", MAX_TARGET_HUMIDITY));
|
||||||
targetHumidity = 80;
|
targetHumidity = MAX_TARGET_HUMIDITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendV2BypassControlCommand(DEVICE_SET_HUMIDITY_MODE,
|
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,
|
sendV2BypassControlCommand(DEVICE_SET_TARGET_HUMIDITY_MODE,
|
||||||
new VeSyncRequestManagedDeviceBypassV2.SetTargetHumidity(targetHumidity));
|
new VeSyncRequestManagedDeviceBypassV2.SetTargetHumidity(targetHumidity));
|
||||||
break;
|
break;
|
||||||
case DEVICE_CHANNEL_MIST_LEVEL:
|
case DEVICE_CHANNEL_MIST_LEVEL:
|
||||||
int targetMistLevel = quantityCommand.intValue();
|
int targetMistLevel = quantityCommand.intValue();
|
||||||
|
if (!devContraints.isTargetMistLevelSupported(targetMistLevel)) {
|
||||||
|
logger.warn("{}",
|
||||||
|
getLocalizedText("warning.device.mist-level-invalid", 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
|
// 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.
|
// be simplified, originally devices mapped 1/5/9 to 1/2/3.
|
||||||
if (DEV_FAMILY_DUAL_200S.equals(deviceFamily)) {
|
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;
|
|
||||||
}
|
|
||||||
// Re-map to what appears to be bitwise encoding of the states
|
// Re-map to what appears to be bitwise encoding of the states
|
||||||
switch (targetMistLevel) {
|
switch (targetMistLevel) {
|
||||||
case 1:
|
case 1:
|
||||||
@ -234,33 +293,42 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
targetMistLevel));
|
targetMistLevel));
|
||||||
break;
|
break;
|
||||||
case DEVICE_CHANNEL_WARM_LEVEL:
|
case DEVICE_CHANNEL_WARM_LEVEL:
|
||||||
logger.warn("Warm level API is unknown in order to send the command");
|
int targetWarmMistLevel = quantityCommand.intValue();
|
||||||
|
if (!devContraints.isTargetWramMistLevelSupported(targetWarmMistLevel)) {
|
||||||
|
logger.warn("{}",
|
||||||
|
getLocalizedText("warning.device.mist-level-invalid", 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;
|
break;
|
||||||
}
|
}
|
||||||
} else if (command instanceof StringType) {
|
} else if (command instanceof StringType) {
|
||||||
final String targetMode = command.toString().toLowerCase();
|
final String targetMode = command.toString().toLowerCase();
|
||||||
switch (channelUID.getId()) {
|
switch (channelUID.getId()) {
|
||||||
case DEVICE_CHANNEL_HUMIDIFIER_MODE:
|
case DEVICE_CHANNEL_HUMIDIFIER_MODE:
|
||||||
if (!CLASSIC_300S_600S_MODES.contains(targetMode)) {
|
if (!devContraints.fanModes.contains(targetMode)) {
|
||||||
logger.warn(
|
logger.warn("{}", getLocalizedText("warning.device.humidity-mode", command,
|
||||||
"Humidifier mode command for \"{}\" is not valid in the (Classic300S/600S) API possible options {}",
|
devContraints.deviceFamilyName, String.join(",", devContraints.fanModes)));
|
||||||
command, String.join(",", CLASSIC_300S_NIGHT_LIGHT_MODES));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sendV2BypassControlCommand(DEVICE_SET_HUMIDITY_MODE,
|
sendV2BypassControlCommand(DEVICE_SET_HUMIDITY_MODE,
|
||||||
new VeSyncRequestManagedDeviceBypassV2.SetMode(targetMode));
|
new VeSyncRequestManagedDeviceBypassV2.SetMode(
|
||||||
|
devContraints.getProtocolMode(targetMode)));
|
||||||
break;
|
break;
|
||||||
case DEVICE_CHANNEL_AF_NIGHT_LIGHT:
|
case DEVICE_CHANNEL_AF_NIGHT_LIGHT:
|
||||||
if (!DEV_FAMILY_CLASSIC_300S.equals(deviceFamily) && !DEV_FAMILY_600S.equals(deviceFamily)) {
|
if (!devContraints.nightLightModes.contains(targetMode)) {
|
||||||
logger.warn("Humidifier night light is not valid for your device ({}})", deviceFamily);
|
logger.warn("{}", getLocalizedText("warning.device.night-light-invalid", command,
|
||||||
return;
|
devContraints.deviceFamilyName, String.join(",", devContraints.nightLightModes)));
|
||||||
}
|
|
||||||
if (!CLASSIC_300S_NIGHT_LIGHT_MODES.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));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int targetValue;
|
int targetValue;
|
||||||
switch (targetMode) {
|
switch (targetMode) {
|
||||||
case MODE_OFF:
|
case MODE_OFF:
|
||||||
@ -289,7 +357,16 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
@Override
|
@Override
|
||||||
protected void pollForDeviceData(final ExpiringCache<String> cachedResponse) {
|
protected void pollForDeviceData(final ExpiringCache<String> cachedResponse) {
|
||||||
String response;
|
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("{}", getLocalizedText("warning.device.poll-device-family-not-found", deviceFamily));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (pollLock) {
|
synchronized (pollLock) {
|
||||||
response = cachedResponse.getValue();
|
response = cachedResponse.getValue();
|
||||||
boolean cachedDataUsed = response != null;
|
boolean cachedDataUsed = response != null;
|
||||||
@ -305,7 +382,11 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
return;
|
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) {
|
if (humidifierStatus == null) {
|
||||||
return;
|
return;
|
||||||
@ -325,13 +406,20 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
updateStatus(ThingStatus.ONLINE);
|
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())) {
|
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("{}", getLocalizedText("warning.device.unexpected-resp-for-air-humidifier"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY);
|
|
||||||
|
|
||||||
updateState(DEVICE_CHANNEL_ENABLED, OnOffType.from(humidifierStatus.result.result.enabled));
|
updateState(DEVICE_CHANNEL_ENABLED, OnOffType.from(humidifierStatus.result.result.enabled));
|
||||||
updateState(DEVICE_CHANNEL_DISPLAY_ENABLED, OnOffType.from(humidifierStatus.result.result.display));
|
updateState(DEVICE_CHANNEL_DISPLAY_ENABLED, OnOffType.from(humidifierStatus.result.result.display));
|
||||||
updateState(DEVICE_CHANNEL_WATER_LACKS, OnOffType.from(humidifierStatus.result.result.waterLacks));
|
updateState(DEVICE_CHANNEL_WATER_LACKS, OnOffType.from(humidifierStatus.result.result.waterLacks));
|
||||||
@ -342,6 +430,10 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
updateState(DEVICE_CHANNEL_HUMIDITY,
|
updateState(DEVICE_CHANNEL_HUMIDITY,
|
||||||
new QuantityType<>(humidifierStatus.result.result.humidity, Units.PERCENT));
|
new QuantityType<>(humidifierStatus.result.result.humidity, Units.PERCENT));
|
||||||
updateState(DEVICE_CHANNEL_MIST_LEVEL, new DecimalType(humidifierStatus.result.result.mistLevel));
|
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));
|
updateState(DEVICE_CHANNEL_HUMIDIFIER_MODE, new StringType(humidifierStatus.result.result.mode));
|
||||||
|
|
||||||
// Only the 300S supports nightlight currently of tested devices.
|
// Only the 300S supports nightlight currently of tested devices.
|
||||||
@ -354,12 +446,43 @@ public class VeSyncDeviceAirHumidifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
} else {
|
} else {
|
||||||
updateState(DEVICE_CHANNEL_AF_NIGHT_LIGHT, new StringType(MODE_DIM));
|
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_ENABLED, OnOffType.from(humidifierStatus.result.result.warnEnabled));
|
||||||
updateState(DEVICE_CHANNEL_WARM_LEVEL, new DecimalType(humidifierStatus.result.result.warmLevel));
|
updateState(DEVICE_CHANNEL_WARM_LEVEL, new DecimalType(humidifierStatus.result.result.warmLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState(DEVICE_CHANNEL_CONFIG_TARGET_HUMIDITY,
|
updateState(DEVICE_CHANNEL_CONFIG_TARGET_HUMIDITY,
|
||||||
new QuantityType<>(humidifierStatus.result.result.configuration.autoTargetHumidity, Units.PERCENT));
|
new QuantityType<>(humidifierStatus.result.result.configuration.autoTargetHumidity, Units.PERCENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parseV2Ver2Poll(final VeSyncV2Ver2BypassHumidifierStatus humidifierStatus) {
|
||||||
|
if (!"0".equals(humidifierStatus.result.getCode())) {
|
||||||
|
logger.warn("{}", getLocalizedText("warning.device.unexpected-resp-for-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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,11 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@ -29,15 +32,23 @@ import org.openhab.binding.vesync.internal.VeSyncBridgeConfiguration;
|
|||||||
import org.openhab.binding.vesync.internal.VeSyncConstants;
|
import org.openhab.binding.vesync.internal.VeSyncConstants;
|
||||||
import org.openhab.binding.vesync.internal.dto.requests.VeSyncRequestManagedDeviceBypassV2;
|
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.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.VeSyncV2BypassPurifierStatus;
|
||||||
|
import org.openhab.binding.vesync.internal.dto.responses.VeSyncV2Ver2BypassPurifierStatus;
|
||||||
import org.openhab.binding.vesync.internal.dto.responses.v1.VeSyncV1AirPurifierDeviceDetailsResponse;
|
import org.openhab.binding.vesync.internal.dto.responses.v1.VeSyncV1AirPurifierDeviceDetailsResponse;
|
||||||
import org.openhab.core.cache.ExpiringCache;
|
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.items.DateTimeItem;
|
||||||
import org.openhab.core.library.types.DateTimeType;
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
import org.openhab.core.library.types.QuantityType;
|
import org.openhab.core.library.types.QuantityType;
|
||||||
import org.openhab.core.library.types.StringType;
|
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.library.unit.Units;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
@ -45,6 +56,7 @@ import org.openhab.core.thing.ThingStatus;
|
|||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
import org.openhab.core.types.RefreshType;
|
import org.openhab.core.types.RefreshType;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -55,6 +67,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
* @author David Goodyear - Initial contribution
|
* @author David Goodyear - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
|
@SuppressWarnings("serial")
|
||||||
public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
||||||
|
|
||||||
public static final String DEV_TYPE_FAMILY_AIR_PURIFIER = "LAP";
|
public static final String DEV_TYPE_FAMILY_AIR_PURIFIER = "LAP";
|
||||||
@ -68,27 +81,54 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
|
|
||||||
public static final String DEV_FAMILY_PUR_131S = "131S";
|
public static final String DEV_FAMILY_PUR_131S = "131S";
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata CORE200S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_200S,
|
public static final String DEV_FAMILY_VITAL_100S = "V100S";
|
||||||
Arrays.asList("C201S", "C202S"), List.of("Core200S"));
|
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata CORE300S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_300S,
|
public static final String DEV_FAMILY_VITAL_200S = "V200S";
|
||||||
List.of("C301S", "C302S"), List.of("Core300S"));
|
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata CORE400S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_400S, List.of("C401S"),
|
private static final List<String> FAN_MODES_WITH_PET = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP, MODE_PET);
|
||||||
List.of("Core400S"));
|
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata CORE600S = new VeSyncDeviceMetadata(DEV_FAMILY_CORE_600S, List.of("C601S"),
|
private static final List<String> FAN_MODES_NO_PET = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP);
|
||||||
List.of("Core600S"));
|
private static final List<String> FAN_MODES_MAN_SLEEP = Arrays.asList(MODE_MANUAL, MODE_SLEEP);
|
||||||
|
private static final List<String> NIGHT_LIGHTS = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF);
|
||||||
|
|
||||||
public static final VeSyncDeviceMetadata PUR131S = new VeSyncDeviceMetadata(DEV_FAMILY_PUR_131S,
|
private static final List<String> NO_NIGHT_LIGHTS = Collections.emptyList();
|
||||||
Collections.emptyList(), Arrays.asList("LV-PUR131S", "LV-RH131S"));
|
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<VeSyncDeviceMetadata> SUPPORTED_MODEL_FAMILIES = Arrays.asList(CORE600S, CORE400S,
|
public static final VeSyncDevicePurifierMetadata CORE300S = new VeSyncDevicePurifierMetadata(1,
|
||||||
CORE300S, CORE200S, PUR131S);
|
DEV_FAMILY_CORE_300S, List.of("C301S", "C302S"), List.of("Core300S"), FAN_MODES_MAN_SLEEP, 1, 3,
|
||||||
|
NIGHT_LIGHTS);
|
||||||
|
|
||||||
private static final List<String> CORE_400S600S_FAN_MODES = Arrays.asList(MODE_AUTO, MODE_MANUAL, MODE_SLEEP);
|
public static final VeSyncDevicePurifierMetadata CORE400S = new VeSyncDevicePurifierMetadata(1,
|
||||||
private static final List<String> CORE_200S300S_FAN_MODES = Arrays.asList(MODE_MANUAL, MODE_SLEEP);
|
DEV_FAMILY_CORE_400S, List.of("C401S"), List.of("Core400S"), FAN_MODES_NO_PET, 1, 4, NO_NIGHT_LIGHTS);
|
||||||
private static final List<String> CORE_200S300S_NIGHT_LIGHT_MODES = Arrays.asList(MODE_ON, MODE_DIM, MODE_OFF);
|
|
||||||
|
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<String, VeSyncDevicePurifierMetadata> DEV_FAMILY_PURIFIER_MAP = new HashMap<String, VeSyncDevicePurifierMetadata>() {
|
||||||
|
{
|
||||||
|
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<VeSyncDeviceMetadata> SUPPORTED_MODEL_FAMILIES = DEV_FAMILY_PURIFIER_MAP.values().stream()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirPurifierHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(VeSyncDeviceAirPurifierHandler.class);
|
||||||
|
|
||||||
@ -96,8 +136,9 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
|
|
||||||
private final Object pollLock = new Object();
|
private final Object pollLock = new Object();
|
||||||
|
|
||||||
public VeSyncDeviceAirPurifierHandler(Thing thing) {
|
public VeSyncDeviceAirPurifierHandler(Thing thing, @Reference TranslationProvider translationProvider,
|
||||||
super(thing);
|
@Reference LocaleProvider localeProvider) {
|
||||||
|
super(thing, translationProvider, localeProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -114,16 +155,28 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
switch (deviceFamily) {
|
switch (deviceFamily) {
|
||||||
case DEV_FAMILY_CORE_600S:
|
case DEV_FAMILY_CORE_600S:
|
||||||
case DEV_FAMILY_CORE_400S:
|
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;
|
break;
|
||||||
case DEV_FAMILY_PUR_131S:
|
case DEV_FAMILY_PUR_131S:
|
||||||
toRemove = new String[] { DEVICE_CHANNEL_AF_NIGHT_LIGHT, DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE,
|
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_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_AIRQUALITY_PM25, DEVICE_CHANNEL_AF_SCHEDULES_COUNT,
|
||||||
DEVICE_CHANNEL_AF_SCHEDULES_COUNT, DEVICE_CHANNEL_AF_CONFIG_DISPLAY_FOREVER };
|
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;
|
break;
|
||||||
default:
|
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;
|
return toRemove;
|
||||||
@ -164,111 +217,152 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
if (deviceFamily == null) {
|
if (deviceFamily == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final String deviceUuid = getThing().getProperties().get(DEVICE_PROP_DEVICE_UUID);
|
||||||
|
if (deviceUuid == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_PURIFIER_MAP.get(deviceFamily);
|
||||||
|
if (devContraints == null) {
|
||||||
|
logger.warn("{}", getLocalizedText("warning.device.command-device-family-not-found", deviceFamily));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
scheduler.submit(() -> {
|
scheduler.submit(() -> {
|
||||||
|
|
||||||
if (command instanceof OnOffType) {
|
if (command instanceof OnOffType) {
|
||||||
switch (channelUID.getId()) {
|
switch (channelUID.getId()) {
|
||||||
case DEVICE_CHANNEL_ENABLED:
|
case DEVICE_CHANNEL_ENABLED:
|
||||||
sendV2BypassControlCommand(DEVICE_SET_SWITCH,
|
switch (deviceFamily) {
|
||||||
new VeSyncRequestManagedDeviceBypassV2.SetSwitchPayload(command.equals(OnOffType.ON),
|
case DEV_FAMILY_VITAL_100S:
|
||||||
0));
|
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;
|
break;
|
||||||
case DEVICE_CHANNEL_DISPLAY_ENABLED:
|
case DEVICE_CHANNEL_DISPLAY_ENABLED:
|
||||||
sendV2BypassControlCommand(DEVICE_SET_DISPLAY,
|
switch (deviceFamily) {
|
||||||
new VeSyncRequestManagedDeviceBypassV2.SetState(command.equals(OnOffType.ON)));
|
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;
|
break;
|
||||||
case DEVICE_CHANNEL_CHILD_LOCK_ENABLED:
|
case DEVICE_CHANNEL_CHILD_LOCK_ENABLED:
|
||||||
sendV2BypassControlCommand(DEVICE_SET_CHILD_LOCK,
|
switch (deviceFamily) {
|
||||||
new VeSyncRequestManagedDeviceBypassV2.SetChildLock(command.equals(OnOffType.ON)));
|
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;
|
break;
|
||||||
}
|
}
|
||||||
} else if (command instanceof StringType) {
|
} else if (command instanceof StringType) {
|
||||||
switch (channelUID.getId()) {
|
switch (channelUID.getId()) {
|
||||||
case DEVICE_CHANNEL_FAN_MODE_ENABLED:
|
case DEVICE_CHANNEL_FAN_MODE_ENABLED:
|
||||||
final String targetFanMode = command.toString().toLowerCase();
|
final String targetFanMode = command.toString().toLowerCase();
|
||||||
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;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendV2BypassControlCommand(DEVICE_SET_PURIFIER_MODE,
|
if (!devContraints.isFanModeSupported(targetFanMode)) {
|
||||||
new VeSyncRequestManagedDeviceBypassV2.SetMode(targetFanMode));
|
logger.warn("{}", getLocalizedText("warning.device.fan-mode-invalid", command,
|
||||||
|
devContraints.deviceFamilyName, String.join(",", devContraints.fanModes)));
|
||||||
|
pollForUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (deviceFamily) {
|
||||||
|
case DEV_FAMILY_VITAL_100S:
|
||||||
|
case DEV_FAMILY_VITAL_200S:
|
||||||
|
sendV2BypassControlCommand(DEVICE_SET_PURIFIER_MODE,
|
||||||
|
new VeSyncRequestManagedDeviceBypassV2.SetWorkModePayload(targetFanMode));
|
||||||
|
break;
|
||||||
|
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));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DEVICE_CHANNEL_AF_NIGHT_LIGHT:
|
case DEVICE_CHANNEL_AF_NIGHT_LIGHT:
|
||||||
final String targetNightLightMode = command.toString().toLowerCase();
|
final String targetNightLightMode = command.toString().toLowerCase();
|
||||||
switch (deviceFamily) {
|
if (!devContraints.isNightLightModeSupported(targetNightLightMode)) {
|
||||||
case DEV_FAMILY_CORE_600S:
|
logger.warn("{}", getLocalizedText("warning.device.night-light-invalid", command,
|
||||||
case DEV_FAMILY_CORE_400S:
|
devContraints.deviceFamilyName, String.join(",", devContraints.nightLightModes)));
|
||||||
logger.warn("Core400S API does not support night light");
|
pollForUpdate();
|
||||||
return;
|
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;
|
|
||||||
}
|
}
|
||||||
|
sendV2BypassControlCommand(DEVICE_SET_NIGHT_LIGHT,
|
||||||
|
new VeSyncRequestManagedDeviceBypassV2.SetNightLight(targetNightLightMode));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (command instanceof QuantityType quantityCommand) {
|
} else if (command instanceof QuantityType quantityCommand) {
|
||||||
switch (channelUID.getId()) {
|
switch (channelUID.getId()) {
|
||||||
case DEVICE_CHANNEL_FAN_SPEED_ENABLED:
|
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();
|
int requestedLevel = quantityCommand.intValue();
|
||||||
if (requestedLevel < 1) {
|
if (!devContraints.isFanSpeedSupported(requestedLevel)) {
|
||||||
logger.warn("Fan speed command less than 1 - adjusting to 1 as the valid API value");
|
logger.warn("{}",
|
||||||
requestedLevel = 1;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (deviceFamily) {
|
switch (deviceFamily) {
|
||||||
case DEV_FAMILY_CORE_600S:
|
case DEV_FAMILY_VITAL_100S:
|
||||||
case DEV_FAMILY_CORE_400S:
|
case DEV_FAMILY_VITAL_200S:
|
||||||
if (requestedLevel > 4) {
|
sendV2BypassControlCommand(DEVICE_SET_PURIFIER_MODE,
|
||||||
logger.warn(
|
new VeSyncRequestManagedDeviceBypassV2.SetWorkModePayload(MODE_MANUAL));
|
||||||
"Fan speed command greater than 4 - adjusting to 4 as the valid (Core400S) API value");
|
sendV2BypassControlCommand(DEVICE_SET_LEVEL,
|
||||||
requestedLevel = 4;
|
new VeSyncRequestManagedDeviceBypassV2.SetManualSpeedLevelPayload(
|
||||||
}
|
requestedLevel));
|
||||||
break;
|
break;
|
||||||
case DEV_FAMILY_CORE_200S:
|
case DEV_FAMILY_PUR_131S:
|
||||||
case DEV_FAMILY_CORE_300S:
|
sendV1ControlCommand("131airPurifier/v1/device/updateMode",
|
||||||
if (requestedLevel > 3) {
|
new VeSyncRequestV1SetMode(deviceUuid, MODE_MANUAL), false);
|
||||||
logger.warn(
|
sendV1ControlCommand("131airPurifier/v1/device/updateSpeed",
|
||||||
"Fan speed command greater than 3 - adjusting to 3 as the valid (Core200S/Core300S) API value");
|
new VeSyncRequestV1SetLevel(deviceUuid, requestedLevel));
|
||||||
requestedLevel = 3;
|
|
||||||
}
|
|
||||||
break;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
} else if (command instanceof RefreshType) {
|
} else if (command instanceof RefreshType) {
|
||||||
@ -285,17 +379,10 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
if (deviceFamily == null) {
|
if (deviceFamily == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!DEV_FAMILY_PUR_131S.equals(deviceFamily)) {
|
||||||
switch (deviceFamily) {
|
processV2BypassPoll(cachedResponse);
|
||||||
case DEV_FAMILY_CORE_600S:
|
} else {
|
||||||
case DEV_FAMILY_CORE_400S:
|
processV1AirPurifierPoll(cachedResponse);
|
||||||
case DEV_FAMILY_CORE_300S:
|
|
||||||
case DEV_FAMILY_CORE_200S:
|
|
||||||
processV2BypassPoll(cachedResponse);
|
|
||||||
break;
|
|
||||||
case DEV_FAMILY_PUR_131S:
|
|
||||||
processV1AirPurifierPoll(cachedResponse);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,13 +399,13 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
boolean cachedDataUsed = response != null;
|
boolean cachedDataUsed = response != null;
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
logger.trace("Requesting fresh response");
|
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));
|
new VeSyncRequestV1ManagedDeviceDetails(deviceUuid));
|
||||||
} else {
|
} else {
|
||||||
logger.trace("Using cached response {}", response);
|
logger.trace("Using cached response {}", response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.equals(EMPTY_STRING)) {
|
if (EMPTY_STRING.equals(response)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,7 +430,7 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!"0".equals(purifierStatus.getCode())) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,11 +440,22 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
updateState(DEVICE_CHANNEL_FAN_SPEED_ENABLED, new DecimalType(String.valueOf(purifierStatus.getLevel())));
|
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_DISPLAY_ENABLED, OnOffType.from(MODE_ON.equals(purifierStatus.getScreenStatus())));
|
||||||
updateState(DEVICE_CHANNEL_AIRQUALITY_BASIC, new DecimalType(purifierStatus.getAirQuality()));
|
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<String> cachedResponse) {
|
private void processV2BypassPoll(final ExpiringCache<String> cachedResponse) {
|
||||||
|
final String deviceFamily = getThing().getProperties().get(DEVICE_PROP_DEVICE_FAMILY);
|
||||||
|
|
||||||
|
final VeSyncDevicePurifierMetadata devContraints = DEV_FAMILY_PURIFIER_MAP.get(deviceFamily);
|
||||||
|
if (devContraints == null) {
|
||||||
|
logger.warn("{}", getLocalizedText("warning.device.command-device-family-not-found", deviceFamily));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String response;
|
String response;
|
||||||
VeSyncV2BypassPurifierStatus purifierStatus;
|
VeSyncResponse purifierStatus = null;
|
||||||
|
|
||||||
synchronized (pollLock) {
|
synchronized (pollLock) {
|
||||||
response = cachedResponse.getValue();
|
response = cachedResponse.getValue();
|
||||||
boolean cachedDataUsed = response != null;
|
boolean cachedDataUsed = response != null;
|
||||||
@ -372,8 +470,11 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
if (response.equals(EMPTY_STRING)) {
|
if (response.equals(EMPTY_STRING)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (devContraints.protocolV2Version == 2) {
|
||||||
purifierStatus = VeSyncConstants.GSON.fromJson(response, VeSyncV2BypassPurifierStatus.class);
|
purifierStatus = VeSyncConstants.GSON.fromJson(response, VeSyncV2Ver2BypassPurifierStatus.class);
|
||||||
|
} else {
|
||||||
|
purifierStatus = VeSyncConstants.GSON.fromJson(response, VeSyncV2BypassPurifierStatus.class);
|
||||||
|
}
|
||||||
|
|
||||||
if (purifierStatus == null) {
|
if (purifierStatus == null) {
|
||||||
return;
|
return;
|
||||||
@ -393,8 +494,16 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
updateStatus(ThingStatus.ONLINE);
|
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())) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,15 +518,12 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
updateState(DEVICE_CHANNEL_AIRQUALITY_BASIC, new DecimalType(purifierStatus.result.result.airQuality));
|
updateState(DEVICE_CHANNEL_AIRQUALITY_BASIC, new DecimalType(purifierStatus.result.result.airQuality));
|
||||||
updateState(DEVICE_CHANNEL_AIRQUALITY_PM25,
|
updateState(DEVICE_CHANNEL_AIRQUALITY_PM25,
|
||||||
new QuantityType<>(purifierStatus.result.result.airQualityValue, Units.MICROGRAM_PER_CUBICMETRE));
|
new QuantityType<>(purifierStatus.result.result.airQualityValue, Units.MICROGRAM_PER_CUBICMETRE));
|
||||||
|
|
||||||
updateState(DEVICE_CHANNEL_AF_CONFIG_DISPLAY_FOREVER,
|
updateState(DEVICE_CHANNEL_AF_CONFIG_DISPLAY_FOREVER,
|
||||||
OnOffType.from(purifierStatus.result.result.configuration.displayForever));
|
OnOffType.from(purifierStatus.result.result.configuration.displayForever));
|
||||||
|
|
||||||
updateState(DEVICE_CHANNEL_AF_CONFIG_AUTO_MODE_PREF,
|
updateState(DEVICE_CHANNEL_AF_CONFIG_AUTO_MODE_PREF,
|
||||||
new StringType(purifierStatus.result.result.configuration.autoPreference.autoType));
|
new StringType(purifierStatus.result.result.configuration.autoPreference.autoType));
|
||||||
|
updateState(DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE, new QuantityType<>(
|
||||||
updateState(DEVICE_CHANNEL_AF_CONFIG_AUTO_ROOM_SIZE,
|
purifierStatus.result.result.configuration.autoPreference.roomSize, ImperialUnits.SQUARE_FOOT));
|
||||||
new DecimalType(purifierStatus.result.result.configuration.autoPreference.roomSize));
|
|
||||||
|
|
||||||
// Only 400S appears to have this JSON extension object
|
// Only 400S appears to have this JSON extension object
|
||||||
if (purifierStatus.result.result.extension != null) {
|
if (purifierStatus.result.result.extension != null) {
|
||||||
@ -436,4 +542,28 @@ public class VeSyncDeviceAirPurifierHandler extends VeSyncBaseDeviceHandler {
|
|||||||
updateState(DEVICE_CHANNEL_AF_NIGHT_LIGHT, new DecimalType(purifierStatus.result.result.nightLight));
|
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("{}", getLocalizedText("warning.device.unexpected-resp-for-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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* 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.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<String> deviceGenerations, final List<String> nonStandardIds, final List<String> fanModes,
|
||||||
|
final int targetMinMistLevel, final int targetMaxMistLevel, final int targetMinWarmMistLevel,
|
||||||
|
final int targetMaxWarmMistLevel, final boolean remapsAutoToHumidity, List<String> nightLightModes) {
|
||||||
|
super(deviceFamilyName, deviceGenerations, nonStandardIds);
|
||||||
|
this.fanModes = fanModes;
|
||||||
|
this.targetMinMistLevel = targetMinMistLevel;
|
||||||
|
this.targetMaxMistLevel = targetMaxMistLevel;
|
||||||
|
this.targetMinWarmMistLevel = targetMinWarmMistLevel;
|
||||||
|
this.targetMaxWarmMistLevel = targetMaxWarmMistLevel;
|
||||||
|
this.remapsAutoToHumidity = remapsAutoToHumidity;
|
||||||
|
this.nightLightModes = nightLightModes;
|
||||||
|
this.protocolV2Version = v2version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int protocolV2Version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fan modes supported by this generation of device
|
||||||
|
*/
|
||||||
|
public final List<String> fanModes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum target mist level supported
|
||||||
|
*/
|
||||||
|
public final int targetMinMistLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum target mist level supported
|
||||||
|
*/
|
||||||
|
public final int targetMaxMistLevel;
|
||||||
|
|
||||||
|
public final boolean isTargetMistLevelSupported(final int target) {
|
||||||
|
return target >= targetMinMistLevel && target <= targetMaxMistLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum target mist level supported
|
||||||
|
*/
|
||||||
|
public final int targetMinWarmMistLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum target mist level supported
|
||||||
|
*/
|
||||||
|
public final int targetMaxWarmMistLevel;
|
||||||
|
|
||||||
|
public final boolean isTargetWramMistLevelSupported(final int target) {
|
||||||
|
return target >= targetMinWarmMistLevel && target <= targetMaxWarmMistLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores whether auto in openhab is humidity mode in the protocol
|
||||||
|
*/
|
||||||
|
public final boolean remapsAutoToHumidity;
|
||||||
|
|
||||||
|
public String getProtocolMode(final String mode) {
|
||||||
|
if (!remapsAutoToHumidity) {
|
||||||
|
return mode;
|
||||||
|
} else {
|
||||||
|
if (MODE_AUTO.equals(mode)) {
|
||||||
|
return MODE_AUTO_HUMIDITY;
|
||||||
|
}
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> nightLightModes;
|
||||||
|
}
|
@ -50,14 +50,8 @@ public class VeSyncDeviceMetadata {
|
|||||||
*/
|
*/
|
||||||
public final List<String> nonStandardIds;
|
public final List<String> nonStandardIds;
|
||||||
|
|
||||||
public boolean deviceTypeIdMatches(final String deviceType, final String[] deviceTypeSegments) {
|
public boolean deviceTypeIdMatches(final String[] deviceTypeSegments) {
|
||||||
if (nonStandardIds.contains(deviceType)) {
|
return (deviceTypeSegments.length == 3 && deviceGenerations.contains(deviceTypeSegments[1]));
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (deviceTypeSegments.length == 3) {
|
|
||||||
return deviceGenerations.contains(deviceTypeSegments[1]);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDeviceFamilyName() {
|
public String getDeviceFamilyName() {
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* 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.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<String> deviceGenerations, final List<String> nonStandardIds, final List<String> fanModes,
|
||||||
|
final int minFanSpeed, final int maxFanSpeed, final List<String> 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<String> 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<String> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -45,10 +45,11 @@ channel-type.vesync.airPurifierModeType.description = The operating mode the air
|
|||||||
channel-type.vesync.airPurifierModeType.state.option.auto = Auto
|
channel-type.vesync.airPurifierModeType.state.option.auto = Auto
|
||||||
channel-type.vesync.airPurifierModeType.state.option.manual = Manual Fan Control
|
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.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.label = Air Quality PPM2.5
|
||||||
channel-type.vesync.airQualityPM25.description = Indicator of current air quality
|
channel-type.vesync.airQualityPM25.description = Indicator of current air quality
|
||||||
channel-type.vesync.deviceAFConfigAutoPrefRoomSizeType.label = Config: Room size
|
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.label = Config: Auto Mode
|
||||||
channel-type.vesync.deviceAFConfigAutoPrefType.description = The operating mode when the air purifier is set to auto
|
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)
|
channel-type.vesync.deviceAFConfigAutoPrefType.state.option.default = Auto (Air Quality)
|
||||||
@ -58,6 +59,10 @@ channel-type.vesync.deviceAFConfigAutoScheduleCountType.label = Config: Schedule
|
|||||||
channel-type.vesync.deviceAFConfigAutoScheduleCountType.description = The current number of schedules configured
|
channel-type.vesync.deviceAFConfigAutoScheduleCountType.description = The current number of schedules configured
|
||||||
channel-type.vesync.deviceAFConfigDisplayForever.label = Config: Display Forever
|
channel-type.vesync.deviceAFConfigDisplayForever.label = Config: Display Forever
|
||||||
channel-type.vesync.deviceAFConfigDisplayForever.description = Configuration: If the devices display is enabled 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.label = Night Light
|
||||||
channel-type.vesync.deviceAFNightLight.description = The operating mode of the night light functionality
|
channel-type.vesync.deviceAFNightLight.description = The operating mode of the night light functionality
|
||||||
channel-type.vesync.deviceAFNightLight.state.option.on = On
|
channel-type.vesync.deviceAFNightLight.state.option.on = On
|
||||||
@ -96,3 +101,23 @@ channel-type.vesync.warmLevel.label = Warm Level
|
|||||||
channel-type.vesync.warmLevel.description = Warm Level
|
channel-type.vesync.warmLevel.description = Warm Level
|
||||||
channel-type.vesync.warmModeEnabled.label = Warm Mode Enabled
|
channel-type.vesync.warmModeEnabled.label = Warm Mode Enabled
|
||||||
channel-type.vesync.warmModeEnabled.description = Indicator if the device is set to warm mist
|
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
|
||||||
|
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
|
||||||
|
@ -64,6 +64,8 @@
|
|||||||
<channel id="configAutoRoomSize" typeId="deviceAFConfigAutoPrefRoomSizeType"/>
|
<channel id="configAutoRoomSize" typeId="deviceAFConfigAutoPrefRoomSizeType"/>
|
||||||
<channel id="schedulesCount" typeId="deviceAFConfigAutoScheduleCountType"/>
|
<channel id="schedulesCount" typeId="deviceAFConfigAutoScheduleCountType"/>
|
||||||
<channel id="nightLightMode" typeId="deviceAFNightLight"/>
|
<channel id="nightLightMode" typeId="deviceAFNightLight"/>
|
||||||
|
<channel id="lightDetection" typeId="deviceAFLightDetection"/>
|
||||||
|
<channel id="lightDetected" typeId="deviceAFLightDetected"/>
|
||||||
</channels>
|
</channels>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@ -71,6 +73,7 @@
|
|||||||
<property name="Device Type"/>
|
<property name="Device Type"/>
|
||||||
<property name="MAC Id"/>
|
<property name="MAC Id"/>
|
||||||
<property name="Device Family"/>
|
<property name="Device Family"/>
|
||||||
|
<property name="thingTypeVersion">1</property>
|
||||||
</properties>
|
</properties>
|
||||||
<representation-property>macId</representation-property>
|
<representation-property>macId</representation-property>
|
||||||
|
|
||||||
@ -109,6 +112,8 @@
|
|||||||
<channel id="humiditySetpoint" typeId="deviceConfigTargetHumidity"/>
|
<channel id="humiditySetpoint" typeId="deviceConfigTargetHumidity"/>
|
||||||
<channel id="warmEnabled" typeId="warmModeEnabled"/>
|
<channel id="warmEnabled" typeId="warmModeEnabled"/>
|
||||||
<channel id="warmLevel" typeId="warmLevel"/>
|
<channel id="warmLevel" typeId="warmLevel"/>
|
||||||
|
<channel id="schedulesCount" typeId="deviceAFConfigAutoScheduleCountType"/>
|
||||||
|
<channel id="timerExpiry" typeId="deviceAFTimerExpiry"/>
|
||||||
</channels>
|
</channels>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@ -116,6 +121,7 @@
|
|||||||
<property name="Device Type"/>
|
<property name="Device Type"/>
|
||||||
<property name="MAC Id"/>
|
<property name="MAC Id"/>
|
||||||
<property name="Device Family"/>
|
<property name="Device Family"/>
|
||||||
|
<property name="thingTypeVersion">1</property>
|
||||||
</properties>
|
</properties>
|
||||||
<representation-property>macId</representation-property>
|
<representation-property>macId</representation-property>
|
||||||
|
|
||||||
@ -154,10 +160,10 @@
|
|||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="deviceFilterLifePercentageType">
|
<channel-type id="deviceFilterLifePercentageType">
|
||||||
<item-type>Number:Dimensionless</item-type>
|
<item-type unitHint="%">Number:Dimensionless</item-type>
|
||||||
<label>Filter Life Remaining</label>
|
<label>Filter Life Remaining</label>
|
||||||
<description>Indicator of the remaining filter life</description>
|
<description>Indicator of the remaining filter life</description>
|
||||||
<state readOnly="true" pattern="%.0f %%"/>
|
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="airPurifierModeType">
|
<channel-type id="airPurifierModeType">
|
||||||
@ -169,6 +175,7 @@
|
|||||||
<option value="auto">Auto</option>
|
<option value="auto">Auto</option>
|
||||||
<option value="manual">Manual Fan Control</option>
|
<option value="manual">Manual Fan Control</option>
|
||||||
<option value="sleep">Sleeping Auto</option>
|
<option value="sleep">Sleeping Auto</option>
|
||||||
|
<option value="pet">Pet Auto</option>
|
||||||
</options>
|
</options>
|
||||||
</state>
|
</state>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
@ -188,14 +195,14 @@
|
|||||||
|
|
||||||
|
|
||||||
<channel-type id="airPurifierFanLevelType">
|
<channel-type id="airPurifierFanLevelType">
|
||||||
<item-type>Number:Dimensionless</item-type>
|
<item-type unitHint="one">Number:Dimensionless</item-type>
|
||||||
<label>Fan Speed</label>
|
<label>Fan Speed</label>
|
||||||
<description>Indicator of the current fan speed</description>
|
<description>Indicator of the current fan speed</description>
|
||||||
<state readOnly="true" pattern="%.0f"/>
|
<state readOnly="true" pattern="%.0f"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="deviceErrorCodeType">
|
<channel-type id="deviceErrorCodeType">
|
||||||
<item-type>Number:Dimensionless</item-type>
|
<item-type unitHint="one">Number:Dimensionless</item-type>
|
||||||
<label>Device Error Code</label>
|
<label>Device Error Code</label>
|
||||||
<description>Indicator of the current error code of the device</description>
|
<description>Indicator of the current error code of the device</description>
|
||||||
<state readOnly="true" pattern="%.0f"/>
|
<state readOnly="true" pattern="%.0f"/>
|
||||||
@ -209,7 +216,7 @@
|
|||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="airQualityPM25">
|
<channel-type id="airQualityPM25">
|
||||||
<item-type>Number:Density</item-type>
|
<item-type unitHint="µg/m³">Number:Density</item-type>
|
||||||
<label>Air Quality PPM2.5</label>
|
<label>Air Quality PPM2.5</label>
|
||||||
<description>Indicator of current air quality</description>
|
<description>Indicator of current air quality</description>
|
||||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||||
@ -221,6 +228,20 @@
|
|||||||
<description>Configuration: If the devices display is enabled forever</description>
|
<description>Configuration: If the devices display is enabled forever</description>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="deviceAFLightDetection">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Light Detection</label>
|
||||||
|
<description>If the devices light detection is enabled</description>
|
||||||
|
<state readOnly="false"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="deviceAFLightDetected">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Light Detected</label>
|
||||||
|
<description>Indicator if the device detects light</description>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="deviceAFConfigAutoPrefType">
|
<channel-type id="deviceAFConfigAutoPrefType">
|
||||||
<item-type>String</item-type>
|
<item-type>String</item-type>
|
||||||
<label>Config: Auto Mode</label>
|
<label>Config: Auto Mode</label>
|
||||||
@ -242,14 +263,14 @@
|
|||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="deviceAFConfigAutoPrefRoomSizeType">
|
<channel-type id="deviceAFConfigAutoPrefRoomSizeType">
|
||||||
<item-type>Number:Dimensionless</item-type>
|
<item-type unitHint="ft2">Number:Area</item-type>
|
||||||
<label>Config: Room size</label>
|
<label>Config: Room size</label>
|
||||||
<description>Room size (foot sq) for efficient auto mode</description>
|
<description>Room size for efficient auto mode</description>
|
||||||
<state readOnly="true" pattern="%.0f sq ft"/>
|
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="deviceAFConfigAutoScheduleCountType">
|
<channel-type id="deviceAFConfigAutoScheduleCountType">
|
||||||
<item-type>Number:Dimensionless</item-type>
|
<item-type unitHint="one">Number:Dimensionless</item-type>
|
||||||
<label>Config: Schedules Count</label>
|
<label>Config: Schedules Count</label>
|
||||||
<description>The current number of schedules configured</description>
|
<description>The current number of schedules configured</description>
|
||||||
<state readOnly="true" pattern="%.0f"/>
|
<state readOnly="true" pattern="%.0f"/>
|
||||||
@ -299,7 +320,7 @@
|
|||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="deviceMistLevelType">
|
<channel-type id="deviceMistLevelType">
|
||||||
<item-type>Number:Dimensionless</item-type>
|
<item-type unitHint="one">Number:Dimensionless</item-type>
|
||||||
<label>Mist Level</label>
|
<label>Mist Level</label>
|
||||||
<description>System representation of mist level</description>
|
<description>System representation of mist level</description>
|
||||||
<state readOnly="false" pattern="%.0f"/>
|
<state readOnly="false" pattern="%.0f"/>
|
||||||
@ -326,11 +347,10 @@
|
|||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="warmLevel">
|
<channel-type id="warmLevel">
|
||||||
<item-type>Number:Dimensionless</item-type>
|
<item-type unitHint="one">Number:Dimensionless</item-type>
|
||||||
<label>Warm Level</label>
|
<label>Warm Level</label>
|
||||||
<description>Warm Level</description>
|
<description>Warm Level</description>
|
||||||
<state readOnly="false" pattern="%.0f"/>
|
<state readOnly="false" pattern="%.0f"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
|
|
||||||
</thing:thing-descriptions>
|
</thing:thing-descriptions>
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
|
||||||
|
<thing-type uid="vesync:airHumidifier">
|
||||||
|
<instruction-set targetVersion="1">
|
||||||
|
<add-channel id="schedulesCount">
|
||||||
|
<type>vesync:deviceAFConfigAutoScheduleCountType</type>
|
||||||
|
</add-channel>
|
||||||
|
<add-channel id="timerExpiry">
|
||||||
|
<type>vesync:deviceAFTimerExpiry</type>
|
||||||
|
</add-channel>
|
||||||
|
</instruction-set>
|
||||||
|
</thing-type>
|
||||||
|
</update:update-descriptions>
|
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
|
||||||
|
<thing-type uid="vesync:airPurifier">
|
||||||
|
<instruction-set targetVersion="1">
|
||||||
|
<add-channel id="lightDetection">
|
||||||
|
<type>vesync:deviceAFLightDetection</type>
|
||||||
|
</add-channel>
|
||||||
|
<add-channel id="lightDetected">
|
||||||
|
<type>vesync:deviceAFLightDetected</type>
|
||||||
|
</add-channel>
|
||||||
|
<update-channel id="fanMode">
|
||||||
|
<type>vesync:airPurifierModeType</type>
|
||||||
|
</update-channel>
|
||||||
|
</instruction-set>
|
||||||
|
</thing-type>
|
||||||
|
</update:update-descriptions>
|
Loading…
Reference in New Issue
Block a user