Skip to content

Commit ad0211c

Browse files
authored
Merge pull request #251 from mikeyyuen/master
Adding support for 42 inch smart tower fan
2 parents e0c9c55 + f5e9b38 commit ad0211c

File tree

5 files changed

+305
-4
lines changed

5 files changed

+305
-4
lines changed

.pylintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ disable=
3939
too-many-instance-attributes,
4040
too-many-lines,
4141
too-many-locals,
42+
too-many-positional-arguments,
4243
too-many-public-methods,
4344
too-many-return-statements,
4445
too-many-statements,
@@ -55,4 +56,4 @@ disable=
5556

5657
[FORMAT]
5758
expected-line-ending-format=LF
58-
max-line-length=90
59+
max-line-length=90

src/pyvesync/vesyncfan.py

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@
145145
'modes': ['manual', 'auto', 'sleep', 'off', 'turbo'],
146146
'features': ['air_quality', 'fan_rotate'],
147147
'levels': list(range(1, 4))
148+
},
149+
'SmartTowerFan': {
150+
'module': 'VeSyncTowerFan',
151+
'models': ['LTF-F422S-KEU', 'LTF-F422S-WUSR', 'LTF-F422_WJP', 'LTF-F422S-WUS'],
152+
'modes': ['normal', 'auto', 'advancedSleep', 'turbo', 'off'],
153+
'set_mode_method': 'setTowerFanMode',
154+
'features': ['fan_speed'],
155+
'levels': list(range(1, 13))
148156
}
149157
}
150158

@@ -1321,7 +1329,7 @@ def mode_toggle(self, mode: str) -> bool:
13211329

13221330
body['deviceId'] = self.cid
13231331
body['payload']['data'] = {
1324-
'workMode': mode.lower()
1332+
'workMode': mode
13251333
}
13261334

13271335
r, _ = Helpers.call_api(
@@ -1638,6 +1646,96 @@ def displayJSON(self) -> str:
16381646
return json.dumps(sup_val, indent=4)
16391647

16401648

1649+
class VeSyncTowerFan(VeSyncAirBaseV2):
1650+
"""Levoit Tower Fan Device Class."""
1651+
1652+
def __init__(self, details: Dict[str, list], manager):
1653+
"""Initialize the VeSync Base API V2 Fan Class."""
1654+
super().__init__(details, manager)
1655+
1656+
def get_details(self) -> None:
1657+
"""Build API V2 Fan details dictionary."""
1658+
head, body = self.build_api_dict('getTowerFanStatus')
1659+
r, _ = Helpers.call_api(
1660+
'/cloud/v2/deviceManaged/bypassV2',
1661+
method='post',
1662+
headers=head,
1663+
json_object=body,
1664+
)
1665+
if Helpers.nested_code_check(r) is False or not isinstance(r, dict):
1666+
logger.debug('Error getting purifier details')
1667+
self.connection_status = 'offline'
1668+
return
1669+
1670+
inner_result = r.get('result', {}).get('result')
1671+
1672+
if inner_result is not None:
1673+
self.build_purifier_dict(inner_result)
1674+
else:
1675+
self.connection_status = 'offline'
1676+
logger.debug('error in inner result dict from purifier')
1677+
if inner_result.get('configuration', {}):
1678+
self.build_config_dict(inner_result.get('configuration', {}))
1679+
1680+
def mode_toggle(self, mode: str) -> bool:
1681+
"""Set Levoit Tower Fan purifier mode.
1682+
1683+
Parameters
1684+
----------
1685+
mode : str
1686+
Mode to set purifier to, options are: auto, manual, sleep
1687+
1688+
Returns
1689+
-------
1690+
bool
1691+
"""
1692+
if mode.lower() not in [x.lower() for x in self.modes]:
1693+
logger.debug('Invalid purifier mode used - %s',
1694+
mode)
1695+
return False
1696+
1697+
if mode == 'off':
1698+
return self.turn_off()
1699+
1700+
head, body = self.build_api_dict('setTowerFanMode')
1701+
if not head and not body:
1702+
return False
1703+
1704+
body['deviceId'] = self.cid
1705+
body['payload']['data'] = {
1706+
'workMode': mode
1707+
}
1708+
1709+
r, _ = Helpers.call_api(
1710+
'/cloud/v2/deviceManaged/bypassV2',
1711+
method='post',
1712+
headers=head,
1713+
json_object=body,
1714+
)
1715+
1716+
if Helpers.code_check(r):
1717+
self.mode = mode
1718+
return True
1719+
logger.debug('Error setting purifier mode')
1720+
return False
1721+
1722+
def normal_mode(self):
1723+
"""Set mode to normal."""
1724+
return self.mode_toggle('normal')
1725+
1726+
def manual_mode(self):
1727+
"""Adapter to set mode to normal."""
1728+
return self.normal_mode()
1729+
1730+
def advanced_sleep_mode(self) -> bool:
1731+
"""Set advanced sleep mode."""
1732+
return self.mode_toggle('advancedSleep')
1733+
1734+
def sleep_mode(self) -> bool:
1735+
"""Adapter to set advanced sleep mode."""
1736+
return self.advanced_sleep_mode()
1737+
1738+
16411739
class VeSyncHumid200300S(VeSyncBaseDevice):
16421740
"""200S/300S Humidifier Class."""
16431741

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
change_fan_speed:
2+
headers:
3+
Content-Type: application/json; charset=UTF-8
4+
User-Agent: okhttp/3.12.1
5+
json_object:
6+
acceptLanguage: en
7+
accountID: sample_id
8+
appVersion: 2.8.6
9+
cid: LTF-F422S-KEU-CID
10+
configModel: ConfigModule
11+
configModule: ConfigModule
12+
debugMode: false
13+
deviceId: LTF-F422S-KEU-CID
14+
deviceRegion: US
15+
method: bypassV2
16+
payload:
17+
data:
18+
levelIdx: 0
19+
levelType: wind
20+
manualSpeedLevel: 3
21+
method: setLevel
22+
source: APP
23+
phoneBrand: SM N9005
24+
phoneOS: Android
25+
timeZone: America/New_York
26+
token: sample_tk
27+
traceId: TRACE_ID
28+
method: post
29+
url: /cloud/v2/deviceManaged/bypassV2
30+
manual_mode:
31+
headers:
32+
Content-Type: application/json; charset=UTF-8
33+
User-Agent: okhttp/3.12.1
34+
json_object:
35+
acceptLanguage: en
36+
accountID: sample_id
37+
appVersion: 2.8.6
38+
cid: LTF-F422S-KEU-CID
39+
configModel: ConfigModule
40+
configModule: ConfigModule
41+
debugMode: false
42+
deviceId: LTF-F422S-KEU-CID
43+
deviceRegion: US
44+
method: bypassV2
45+
payload:
46+
data:
47+
workMode: normal
48+
method: setTowerFanMode
49+
source: APP
50+
phoneBrand: SM N9005
51+
phoneOS: Android
52+
timeZone: America/New_York
53+
token: sample_tk
54+
traceId: TRACE_ID
55+
method: post
56+
url: /cloud/v2/deviceManaged/bypassV2
57+
sleep_mode:
58+
headers:
59+
Content-Type: application/json; charset=UTF-8
60+
User-Agent: okhttp/3.12.1
61+
json_object:
62+
acceptLanguage: en
63+
accountID: sample_id
64+
appVersion: 2.8.6
65+
cid: LTF-F422S-KEU-CID
66+
configModel: ConfigModule
67+
configModule: ConfigModule
68+
debugMode: false
69+
deviceId: LTF-F422S-KEU-CID
70+
deviceRegion: US
71+
method: bypassV2
72+
payload:
73+
data:
74+
workMode: advancedSleep
75+
method: setTowerFanMode
76+
source: APP
77+
phoneBrand: SM N9005
78+
phoneOS: Android
79+
timeZone: America/New_York
80+
token: sample_tk
81+
traceId: TRACE_ID
82+
method: post
83+
url: /cloud/v2/deviceManaged/bypassV2
84+
turn_off:
85+
headers:
86+
Content-Type: application/json; charset=UTF-8
87+
User-Agent: okhttp/3.12.1
88+
json_object:
89+
acceptLanguage: en
90+
accountID: sample_id
91+
appVersion: 2.8.6
92+
cid: LTF-F422S-KEU-CID
93+
configModel: ConfigModule
94+
configModule: ConfigModule
95+
debugMode: false
96+
deviceId: LTF-F422S-KEU-CID
97+
deviceRegion: US
98+
method: bypassV2
99+
payload:
100+
data:
101+
powerSwitch: 0
102+
switchIdx: 0
103+
method: setSwitch
104+
source: APP
105+
phoneBrand: SM N9005
106+
phoneOS: Android
107+
timeZone: America/New_York
108+
token: sample_tk
109+
traceId: TRACE_ID
110+
method: post
111+
url: /cloud/v2/deviceManaged/bypassV2
112+
turn_on:
113+
headers:
114+
Content-Type: application/json; charset=UTF-8
115+
User-Agent: okhttp/3.12.1
116+
json_object:
117+
acceptLanguage: en
118+
accountID: sample_id
119+
appVersion: 2.8.6
120+
cid: LTF-F422S-KEU-CID
121+
configModel: ConfigModule
122+
configModule: ConfigModule
123+
debugMode: false
124+
deviceId: LTF-F422S-KEU-CID
125+
deviceRegion: US
126+
method: bypassV2
127+
payload:
128+
data:
129+
powerSwitch: 1
130+
switchIdx: 0
131+
method: setSwitch
132+
source: APP
133+
phoneBrand: SM N9005
134+
phoneOS: Android
135+
timeZone: America/New_York
136+
token: sample_tk
137+
traceId: TRACE_ID
138+
method: post
139+
url: /cloud/v2/deviceManaged/bypassV2
140+
update:
141+
headers:
142+
Content-Type: application/json; charset=UTF-8
143+
User-Agent: okhttp/3.12.1
144+
json_object:
145+
acceptLanguage: en
146+
accountID: sample_id
147+
appVersion: 2.8.6
148+
cid: LTF-F422S-KEU-CID
149+
configModel: ConfigModule
150+
configModule: ConfigModule
151+
debugMode: false
152+
deviceId: LTF-F422S-KEU-CID
153+
deviceRegion: US
154+
method: bypassV2
155+
payload:
156+
data: {}
157+
method: getTowerFanStatus
158+
source: APP
159+
phoneBrand: SM N9005
160+
phoneOS: Android
161+
timeZone: America/New_York
162+
token: sample_tk
163+
traceId: TRACE_ID
164+
method: post
165+
url: /cloud/v2/deviceManaged/bypassV2

src/tests/call_json_fans.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,42 @@ class FanDetails:
335335
},
336336
}, 200)
337337

338+
details_towerfan42 = ({
339+
"traceId": Defaults.trace_id,
340+
"code": 0,
341+
"msg": "request success",
342+
"module": None,
343+
"stacktrace": None,
344+
"result": {
345+
"traceId": Defaults.trace_id,
346+
"code": 0,
347+
"result": {
348+
"powerSwitch": Defaults.bin_toggle,
349+
"workMode": "turbo",
350+
"manualSpeedLevel": 3,
351+
"fanSpeedLevel": 12,
352+
"screenState": Defaults.bin_toggle,
353+
"screenSwitch": Defaults.bin_toggle,
354+
"oscillationSwitch": Defaults.bin_toggle,
355+
"oscillationState": Defaults.bin_toggle,
356+
"muteSwitch": Defaults.bin_toggle,
357+
"muteState": Defaults.bin_toggle,
358+
"timerRemain": 0,
359+
"temperature": 750,
360+
"sleepPreference": {
361+
"sleepPreferenceType": "default",
362+
"oscillationSwitch": Defaults.bin_toggle,
363+
"initFanSpeedLevel": 0,
364+
"fallAsleepRemain": 0,
365+
"autoChangeFanLevelSwitch": Defaults.bin_toggle
366+
},
367+
"scheduleCount": 0,
368+
"displayingType": 0,
369+
"errorCode": 0,
370+
}
371+
},
372+
}, 200)
373+
338374

339375
DETAILS_RESPONSES = {
340376
"LV-PUR131S": FanDetails.details_air,
@@ -353,6 +389,7 @@ class FanDetails:
353389
"LUH-M101S-WUS": FanDetails.details_oasismist1000S,
354390
"LEH-S601S-WUS": FanDetails.details_superior6000S,
355391
"LAP-EL551S-AUS": FanDetails.details_everest,
392+
"LTF-F422S-KEU": FanDetails.details_towerfan42,
356393
}
357394

358395
FunctionResponses.default_factory = lambda: (

src/tests/test_fans.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def test_methods(self, dev_type, method):
230230

231231
# Assert request matches recored request or write new records
232232
assert_test(method_call, all_kwargs, dev_type,
233-
self.write_api, self.overwrite)
233+
self.write_api, self.overwrite)
234234

235235

236236
class TestHumidifiers(TestBase):
@@ -329,7 +329,7 @@ def test_details(self, dev_type, method):
329329
# Parse mock_api args tuple from arg, kwargs to kwargs
330330
all_kwargs = parse_args(self.mock_api)
331331

332-
# Assert request matches recored request or write new records
332+
# Assert request matches recorded request or write new records
333333
assert_test(method_call, all_kwargs, dev_type,
334334
self.write_api, self.overwrite)
335335

0 commit comments

Comments
 (0)