pyvesync 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. pyvesync/__init__.py +5 -0
  2. pyvesync/base_devices/__init__.py +35 -0
  3. pyvesync/base_devices/bulb_base.py +330 -0
  4. pyvesync/base_devices/fan_base.py +392 -0
  5. pyvesync/base_devices/fryer_base.py +68 -0
  6. pyvesync/base_devices/humidifier_base.py +462 -0
  7. pyvesync/base_devices/outlet_base.py +327 -0
  8. pyvesync/base_devices/purifier_base.py +525 -0
  9. pyvesync/base_devices/switch_base.py +271 -0
  10. pyvesync/base_devices/thermostat_base.py +283 -0
  11. pyvesync/base_devices/vesyncbasedevice.py +572 -0
  12. pyvesync/const.py +825 -0
  13. pyvesync/device_container.py +373 -0
  14. pyvesync/device_map.py +1172 -0
  15. pyvesync/devices/__init__.py +1 -0
  16. pyvesync/devices/vesyncbulb.py +1126 -0
  17. pyvesync/devices/vesyncfan.py +233 -0
  18. pyvesync/devices/vesynchumidifier.py +808 -0
  19. pyvesync/devices/vesynckitchen.py +651 -0
  20. pyvesync/devices/vesyncoutlet.py +954 -0
  21. pyvesync/devices/vesyncpurifier.py +990 -0
  22. pyvesync/devices/vesyncswitch.py +415 -0
  23. pyvesync/devices/vesyncthermostat.py +171 -0
  24. pyvesync/models/__init__.py +11 -0
  25. pyvesync/models/base_models.py +129 -0
  26. pyvesync/models/bulb_models.py +197 -0
  27. pyvesync/models/bypass_models.py +289 -0
  28. pyvesync/models/fan_models.py +47 -0
  29. pyvesync/models/fryer_models.py +34 -0
  30. pyvesync/models/home_models.py +169 -0
  31. pyvesync/models/humidifier_models.py +202 -0
  32. pyvesync/models/outlet_models.py +220 -0
  33. pyvesync/models/purifier_models.py +261 -0
  34. pyvesync/models/switch_models.py +101 -0
  35. pyvesync/models/thermostat_models.py +134 -0
  36. pyvesync/models/vesync_models.py +439 -0
  37. pyvesync/utils/__init__.py +1 -0
  38. pyvesync/utils/colors.py +250 -0
  39. pyvesync/utils/device_mixins.py +362 -0
  40. pyvesync/utils/enum_utils.py +26 -0
  41. pyvesync/utils/errors.py +867 -0
  42. pyvesync/utils/helpers.py +700 -0
  43. pyvesync/utils/logs.py +525 -0
  44. pyvesync/vesync.py +638 -0
  45. pyvesync/vesynchome.py +152 -0
  46. pyvesync-3.0.0.dist-info/METADATA +567 -0
  47. pyvesync-3.0.0.dist-info/RECORD +50 -0
  48. pyvesync-3.0.0.dist-info/WHEEL +5 -0
  49. pyvesync-3.0.0.dist-info/licenses/LICENSE +21 -0
  50. pyvesync-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,129 @@
1
+ """Base data models for API requests and response.
2
+
3
+ These models are used to define the structure of the requests and
4
+ responses from the API. They use Mashumaro for serialization and
5
+ deserialization. The `DataClassConfigMixin` class sets default options
6
+ for `orjson` and `Mashumaro`.
7
+
8
+ Note:
9
+ Dataclasses should follow the naming convention of
10
+ Request/Response + API Name. Internal models can have any descriptive
11
+ name.
12
+
13
+
14
+ All models should inherit `ResponseBaseModel` or `RequestBaseModel`. Use
15
+ [pyvesync.const][pyvesync.const] to set default values and import here.
16
+
17
+ Attributes are inherited from the [const][pyvesync.const] module for
18
+ default values.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from dataclasses import dataclass
24
+ from time import time
25
+ from typing import ClassVar
26
+
27
+ import orjson
28
+ from mashumaro.config import BaseConfig
29
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
30
+
31
+ from pyvesync.const import (
32
+ APP_ID,
33
+ APP_VERSION,
34
+ BYPASS_HEADER_UA,
35
+ CLIENT_TYPE,
36
+ DEFAULT_LANGUAGE,
37
+ DEFAULT_REGION,
38
+ DEFAULT_TZ,
39
+ MOBILE_ID,
40
+ PHONE_BRAND,
41
+ PHONE_OS,
42
+ TERMINAL_ID,
43
+ USER_TYPE,
44
+ )
45
+
46
+ RequestHeaders = {
47
+ 'User-Agent': BYPASS_HEADER_UA,
48
+ 'Content-Type': 'application/json; charset=UTF-8',
49
+ }
50
+
51
+
52
+ class BaseModelConfig(BaseConfig):
53
+ """Base config for dataclasses."""
54
+
55
+ orjson_options = orjson.OPT_NON_STR_KEYS
56
+
57
+
58
+ @dataclass
59
+ class RequestBaseModel(DataClassORJSONMixin):
60
+ """Base request model for API requests.
61
+
62
+ Forbids extra keys in the request JSON.
63
+ """
64
+
65
+ class Config(BaseModelConfig):
66
+ """orjson config for dataclasses."""
67
+
68
+ forbid_extra_keys = True
69
+ orjson_options = orjson.OPT_NON_STR_KEYS
70
+
71
+
72
+ @dataclass
73
+ class ResponseBaseModel(DataClassORJSONMixin):
74
+ """Base response model for API responses.
75
+
76
+ Allows extra keys in the response model and
77
+ non-string keys in the JSON.
78
+ """
79
+
80
+ class Config(BaseConfig):
81
+ """Config for dataclasses."""
82
+
83
+ orjson_options = orjson.OPT_NON_STR_KEYS
84
+ forbid_extra_keys = False
85
+
86
+
87
+ @dataclass
88
+ class DefaultValues:
89
+ """Default request fields.
90
+
91
+ Attributes for the default values of the request fields
92
+ and static methods for preparing calculated fields.
93
+ """
94
+
95
+ _call_number: ClassVar[int] = 0
96
+ userType: str = USER_TYPE
97
+ appVersion: str = APP_VERSION
98
+ clientType: str = CLIENT_TYPE
99
+ appId: str = APP_ID
100
+ phoneBrand: str = PHONE_BRAND
101
+ phoneOS: str = PHONE_OS
102
+ mobileId: str = MOBILE_ID
103
+ deviceRegion: str = DEFAULT_REGION
104
+ countryCode: str = DEFAULT_REGION
105
+ userCountryCode: str = DEFAULT_REGION
106
+ acceptLanguage: str = DEFAULT_LANGUAGE
107
+ timeZone: str = DEFAULT_TZ
108
+ terminalId: str = TERMINAL_ID
109
+ debugMode: bool = False
110
+
111
+ @staticmethod
112
+ def traceId() -> str:
113
+ """Trace ID CSRF token."""
114
+ return str(int(time()))
115
+
116
+ @staticmethod
117
+ def newTraceId() -> str:
118
+ """Generate a new trace ID."""
119
+ DefaultValues._call_number += 1
120
+ return f'APP{TERMINAL_ID[-5:-1]}{int(time())}-{DefaultValues._call_number:0>5}'
121
+
122
+
123
+ @dataclass
124
+ class ResponseCodeModel(ResponseBaseModel):
125
+ """Model for the 'result' field in response."""
126
+
127
+ traceId: str
128
+ code: int
129
+ msg: str | None
@@ -0,0 +1,197 @@
1
+ """Models for VeSync Bulb API responses and requests.
2
+
3
+ These models are used to serialize and deserialize the JSON responses from the
4
+ VeSync API. The models are used in the VeSync API class methods to provide
5
+ type hints and data validation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from typing import Self, TypedDict
12
+
13
+ from mashumaro import field_options
14
+ from mashumaro.config import BaseConfig
15
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
16
+
17
+ from pyvesync.models.base_models import ResponseBaseModel, ResponseCodeModel
18
+ from pyvesync.models.bypass_models import BypassV2InnerResult, RequestBypassV1
19
+
20
+
21
+ @dataclass
22
+ class JSONCMD(DataClassORJSONMixin):
23
+ """Tunable Bulb JSON CMD dict."""
24
+
25
+ light: None | JSONCMDLight = None
26
+ getLightStatus: None | str = None
27
+
28
+ class Config(BaseConfig):
29
+ """Configure the JSONCMD model."""
30
+
31
+ omit_none = True
32
+
33
+
34
+ @dataclass
35
+ class JSONCMDLight(DataClassORJSONMixin):
36
+ """Light JSON CMD dict."""
37
+
38
+ action: str
39
+ brightness: int | None = None
40
+ colorTempe: int | None = None
41
+
42
+ class Config(BaseConfig):
43
+ """Configure the JSONCMDLight model."""
44
+
45
+ omit_none = True
46
+
47
+
48
+ @dataclass
49
+ class RequestESL100Detail(RequestBypassV1):
50
+ """Request model for Etekcity bulb details."""
51
+
52
+
53
+ @dataclass
54
+ class RequestESL100Status(RequestBypassV1):
55
+ """Request model for Etekcity bulb details."""
56
+
57
+ status: str
58
+
59
+
60
+ @dataclass
61
+ class RequestESL100Brightness(RequestBypassV1):
62
+ """Request model for Etekcity bulb details."""
63
+
64
+ status: str
65
+ brightNess: int
66
+
67
+
68
+ @dataclass
69
+ class RequestESL100CWBase(RequestBypassV1):
70
+ """Request model for ESL100CW bulb."""
71
+
72
+ jsonCmd: JSONCMD
73
+
74
+
75
+ @dataclass
76
+ class ResponseESL100Detail(ResponseCodeModel):
77
+ """Response model for Etekcity bulb details."""
78
+
79
+ traceId: str
80
+ code: int
81
+ msg: str | None
82
+ result: ResponseESL100DetailResult
83
+
84
+
85
+ @dataclass
86
+ class ResponseESL100DetailResult(ResponseBaseModel):
87
+ """ESL100 Dimmable Bulb Device Detail Response."""
88
+
89
+ deviceName: str | None
90
+ name: str | None
91
+ brightness: int | None = field(metadata=field_options(alias='brightNess'))
92
+ activeTime: int | None
93
+ deviceStatus: str = 'off'
94
+ connectionStatus: str = 'offline'
95
+
96
+ @classmethod
97
+ def __post_deserialize__( # type: ignore[override]
98
+ cls, obj: Self
99
+ ) -> Self:
100
+ """Set values depending on color or white mode."""
101
+ if obj.brightness is None:
102
+ obj.brightness = 0
103
+ if obj.activeTime is None:
104
+ obj.activeTime = 0
105
+ return obj
106
+
107
+
108
+ @dataclass
109
+ class ResponseESL100CWDetail(ResponseCodeModel):
110
+ """Response model for Etekcity bulb details."""
111
+
112
+ result: ResponseESL100CWDetailResult
113
+
114
+
115
+ @dataclass
116
+ class ResponseESL100CWLight(ResponseBaseModel):
117
+ """ESL100CW Tunable Bulb Device Detail Response."""
118
+
119
+ brightness: int | None
120
+ action: str = 'on'
121
+ colorTempe: int = 0
122
+
123
+
124
+ @dataclass
125
+ class ResponseESL100CWDetailResult(ResponseBaseModel):
126
+ """Result model for ESL100CW Tunable bulb details."""
127
+
128
+ light: ResponseESL100CWLight
129
+
130
+
131
+ @dataclass
132
+ class ResponseESL100MCStatus(ResponseCodeModel):
133
+ """Response model for Etekcity bulb status."""
134
+
135
+ result: ResponseESL100MCOuterResult
136
+
137
+
138
+ @dataclass
139
+ class ResponseESL100MCOuterResult:
140
+ """ESL100MC Multi-Color Bulb Status Response."""
141
+
142
+ traceId: str
143
+ code: int
144
+ result: ResponseESL100MCResult
145
+
146
+
147
+ @dataclass
148
+ class ResponseESL100MCResult(BypassV2InnerResult):
149
+ """ESL100MC Multi-Color Bulb Status Response."""
150
+
151
+ colorMode: str
152
+ action: str
153
+ brightness: int = 0
154
+ red: int = 0
155
+ green: int = 0
156
+ blue: int = 0
157
+
158
+
159
+ @dataclass
160
+ class ResponseValcenoStatus(ResponseCodeModel):
161
+ """Response model for Valceno bulb status."""
162
+
163
+ result: ResponseValcenoOuterResult
164
+
165
+
166
+ @dataclass
167
+ class ResponseValcenoOuterResult(ResponseBaseModel):
168
+ """Valceno Bulb Status Response."""
169
+
170
+ result: ResponseValcenoStatusResult
171
+ traceId: str = ''
172
+ code: int = 0
173
+
174
+
175
+ @dataclass
176
+ class ResponseValcenoStatusResult(ResponseBaseModel):
177
+ """Valceno Bulb Status Result."""
178
+
179
+ colorMode: str = ''
180
+ colorTemp: int = 0
181
+ brightness: int = 0
182
+ hue: int = 0
183
+ saturation: int = 0
184
+ value: int = 0
185
+ enabled: str = 'off'
186
+
187
+
188
+ class ValcenoStatusPayload(TypedDict):
189
+ """Typed Dict for setting Valceno bulb status."""
190
+
191
+ colorMode: str
192
+ colorTemp: int | str
193
+ brightness: int | str
194
+ hue: int | str
195
+ saturation: int | str
196
+ value: int | str
197
+ force: int
@@ -0,0 +1,289 @@
1
+ """Request Models for Bypass V2 Endpoints.
2
+
3
+ API calls to bypassV2 endpoints have similar request structures. These models are used
4
+ to serialize and deserialize the JSON requests for the bypassV2 endpoints.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass, field
10
+
11
+ import orjson
12
+ from mashumaro import pass_through
13
+ from mashumaro.config import BaseConfig
14
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
15
+
16
+ from pyvesync.models.base_models import (
17
+ BaseModelConfig,
18
+ RequestBaseModel,
19
+ ResponseBaseModel,
20
+ ResponseCodeModel,
21
+ )
22
+
23
+
24
+ @dataclass
25
+ class RequestBypassV2(RequestBaseModel):
26
+ """Bypass V2 Status Request Dict.
27
+
28
+ This is the bypassV2 request model for API calls
29
+ that use the `configModel` and `deviceId` fields.
30
+ """
31
+
32
+ acceptLanguage: str
33
+ accountID: str
34
+ appVersion: str
35
+ cid: str
36
+ configModule: str
37
+ debugMode: bool
38
+ method: str
39
+ phoneBrand: str
40
+ phoneOS: str
41
+ traceId: str
42
+ timeZone: str
43
+ token: str
44
+ userCountryCode: str
45
+ deviceId: str
46
+ configModel: str
47
+ payload: BypassV2RequestPayload
48
+
49
+
50
+ @dataclass
51
+ class BypassV2RequestPayload(RequestBaseModel):
52
+ """Generic Bypass V2 Payload Request model."""
53
+
54
+ data: dict
55
+ method: str
56
+ source: str = 'APP'
57
+
58
+
59
+ @dataclass
60
+ class RequestBypassV1(RequestBaseModel):
61
+ """Bypass V1 Status Request Dict.
62
+
63
+ This is the bypassV1 request model for API calls
64
+ that use the `configModel` and `deviceId` fields.
65
+ """
66
+
67
+ acceptLanguage: str
68
+ accountID: str
69
+ appVersion: str
70
+ cid: str
71
+ configModule: str
72
+ debugMode: bool
73
+ method: str
74
+ phoneBrand: str
75
+ phoneOS: str
76
+ traceId: str
77
+ timeZone: str
78
+ token: str
79
+ userCountryCode: str
80
+ uuid: str
81
+ deviceId: str
82
+ configModel: str
83
+
84
+ class Config(BaseConfig): # type: ignore[override]
85
+ """Configure omit None value keys."""
86
+
87
+ omit_none = True
88
+ orjson_options = orjson.OPT_NON_STR_KEYS
89
+ forbid_extra_keys = True
90
+
91
+
92
+ @dataclass
93
+ class ResponseBypassV1(ResponseCodeModel):
94
+ """Bypass V1 Response Dict."""
95
+
96
+ result: BypassV1Result | None = field(
97
+ default=None,
98
+ metadata={
99
+ 'serialize': pass_through,
100
+ 'deserialize': pass_through,
101
+ },
102
+ )
103
+
104
+
105
+ @dataclass
106
+ class BypassV1Result(DataClassORJSONMixin):
107
+ """Bypass V1 Response Dict."""
108
+
109
+
110
+ @dataclass
111
+ class ResponseBypassV2(ResponseCodeModel):
112
+ """Bypass V2 Response Dict.
113
+
114
+ This is the bypassV2 response model for API calls
115
+ that use the `configModel` and `deviceId` fields.
116
+ """
117
+
118
+ result: BypassV2OuterResult | None = None
119
+
120
+
121
+ @dataclass
122
+ class BypassV2InnerResult(DataClassORJSONMixin):
123
+ """Inner Bypass V2 Result Data Model."""
124
+
125
+ class Config(BaseModelConfig):
126
+ """Configure the Outer Result model."""
127
+
128
+ allow_deserialization_not_by_alias = True
129
+
130
+
131
+ @dataclass
132
+ class BypassV2OuterResult(DataClassORJSONMixin):
133
+ """Bypass V2 Outer Result Data Model."""
134
+
135
+ code: int
136
+ result: BypassV2InnerResult | None = field(
137
+ default=None,
138
+ metadata={
139
+ 'serialize': pass_through,
140
+ 'deserialize': pass_through,
141
+ },
142
+ )
143
+
144
+ class Config(BaseModelConfig):
145
+ """Configure the Outer Result model."""
146
+
147
+ allow_deserialization_not_by_alias = True
148
+
149
+
150
+ @dataclass
151
+ class BypassV2ResultError(DataClassORJSONMixin):
152
+ """Bypass V2 Result Error Data Model."""
153
+
154
+ msg: str
155
+
156
+
157
+ @dataclass
158
+ class ResultV2GetTimer(BypassV2InnerResult):
159
+ """Inner result for Bypass V2 GetTimer method."""
160
+
161
+ timers: list[TimerItemV2] | None = None
162
+
163
+
164
+ @dataclass
165
+ class ResultV2SetTimer(BypassV2InnerResult):
166
+ """Result for Bypass V2 SetTimer method."""
167
+
168
+ id: int
169
+
170
+
171
+ # Bypass V1 Timer Models
172
+
173
+
174
+ @dataclass
175
+ class TimeItemV1(ResponseBaseModel):
176
+ """Data model for Bypass V1 Timers."""
177
+
178
+ timerID: str
179
+ counterTime: str
180
+ action: str
181
+ status: str
182
+ resetTime: str
183
+ uuid: str
184
+
185
+
186
+ @dataclass
187
+ class TimerItemV1(ResponseBaseModel):
188
+ """Data model for Bypass V1 Timers."""
189
+
190
+ timerID: str
191
+ counterTimer: str
192
+ action: str
193
+ status: str
194
+ resetTime: str
195
+
196
+
197
+ @dataclass
198
+ class TimerItemV2(ResponseBaseModel):
199
+ """Data model for Bypass V2 Timers."""
200
+
201
+ id: int
202
+ remain: int
203
+ action: str
204
+ total: int
205
+
206
+
207
+ @dataclass
208
+ class ResultV1SetTimer(BypassV1Result):
209
+ """Result model for setting Bypass V1 API timer."""
210
+
211
+ timerID: str
212
+ conflictTimerIds: list[str] | None = None
213
+
214
+
215
+ @dataclass
216
+ class RequestV1ClearTimer(RequestBypassV1):
217
+ """Request model for clearing Bypass V1 API outlet timer."""
218
+
219
+ timerId: str
220
+ status: str | None = None
221
+
222
+
223
+ @dataclass
224
+ class ResultV1GetTimerList(BypassV1Result):
225
+ """Get timers result for v1 API."""
226
+
227
+ timers: list[TimeItemV1] | list[TimerItemV1] | TimerItemV1 | None = None
228
+
229
+
230
+ @dataclass
231
+ class RequestV1SetTime(RequestBypassV1):
232
+ """Request model for setting timer with counterTime."""
233
+
234
+ counterTime: str
235
+ action: str
236
+ status: str | None = None
237
+ switchNo: int | None = None
238
+
239
+
240
+ @dataclass
241
+ class RequestV1GetTimer(RequestBypassV1):
242
+ """Request model for getting timers from v1 API."""
243
+
244
+ switchNo: str | None = None
245
+
246
+
247
+ @dataclass
248
+ class RequestV1SetTimer(RequestBypassV1):
249
+ """Request model for timer with counterTimer key.
250
+
251
+ Attributes:
252
+ counterTimer (str): The timer value in seconds.
253
+ action (str): The action to perform (e.g., "on", "off").
254
+ switchNo (int | None): The switch number for the timer.
255
+ """
256
+
257
+ counterTimer: str
258
+ action: str
259
+ switchNo: int | None = None
260
+
261
+
262
+ class TimerModels:
263
+ """Class holding all common timer models.
264
+
265
+ Attributes:
266
+ ResultV2GetTimer (ResultV2GetTimer): Result model for Bypass V2 GetTimer method.
267
+ ResultV2SetTimer (ResultV2SetTimer): Result model for Bypass V2 SetTimer method.
268
+ ResultV1SetTimer (ResultV1SetTimer): Result model V1 API for setting timer.
269
+ ResultV1GetTimer (ResultV1GetTimerList): Get timers result for v1 API.
270
+ TimeItemV1 (TimeItemV1): Data model for Bypass V1 Timers.
271
+ TimerItemV1 (TimerItemV1): Data model for Bypass V1 Timers.
272
+ TimerItemV2 (TimerItemV2): Data model for Bypass V2 Timers.
273
+ RequestV1ClearTimer (RequestV1ClearTimer): Model for deleting timer.
274
+ RequestV1SetTimer (RequestV1SetTimer): Model for timer with counterTimer key.
275
+ RequestV1GetTimer (RequestV1GetTimer): Model for getting timers from v1 API.
276
+ RequestV1SetTime (RequestV1SetTime): Model for setting timer with counterTime key.
277
+ """
278
+
279
+ ResultV2GetTimer = ResultV2GetTimer
280
+ ResultV2SetTimer = ResultV2SetTimer
281
+ ResultV1SetTimer = ResultV1SetTimer
282
+ ResultV1GetTimer = ResultV1GetTimerList
283
+ TimeItemV1 = TimeItemV1
284
+ TimerItemV1 = TimerItemV1
285
+ TimerItemV2 = TimerItemV2
286
+ RequestV1ClearTimer = RequestV1ClearTimer
287
+ RequestV1SetTimer = RequestV1SetTimer
288
+ RequestV1GetTimer = RequestV1GetTimer
289
+ RequestV1SetTime = RequestV1SetTime
@@ -0,0 +1,47 @@
1
+ """Data models for VeSync Fans.
2
+
3
+ These models inherit from `ResponseBaseModel` and `RequestBaseModel` from the
4
+ `base_models` module.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+
11
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
12
+
13
+ from pyvesync.models.bypass_models import (
14
+ BypassV2InnerResult,
15
+ )
16
+
17
+
18
+ @dataclass
19
+ class TowerFanResult(BypassV2InnerResult):
20
+ """Tower Fan Result Model."""
21
+
22
+ powerSwitch: int
23
+ workMode: str
24
+ manualSpeedLevel: int
25
+ fanSpeedLevel: int
26
+ screenState: int
27
+ screenSwitch: int
28
+ oscillationSwitch: int
29
+ oscillationState: int
30
+ muteSwitch: int
31
+ muteState: int
32
+ timerRemain: int
33
+ temperature: int
34
+ errorCode: int
35
+ scheduleCount: int
36
+ displayingType: int | None = None
37
+ sleepPreference: FanSleepPreferences | None = None
38
+
39
+
40
+ @dataclass
41
+ class FanSleepPreferences(DataClassORJSONMixin):
42
+ """Fan Sleep Preferences."""
43
+
44
+ sleepPreferenceType: str
45
+ oscillationSwitch: int
46
+ fallAsleepRemain: int
47
+ autoChangeFanLevelSwitch: int
@@ -0,0 +1,34 @@
1
+ """Data models for VeSync air fryers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from pyvesync.models.base_models import ResponseBaseModel
8
+
9
+
10
+ @dataclass
11
+ class ResultFryerDetails(ResponseBaseModel):
12
+ """Result model for air fryer details."""
13
+
14
+ returnStatus: FryerCookingReturnStatus | FryerBaseReturnStatus | None = None
15
+
16
+
17
+ @dataclass
18
+ class FryerCookingReturnStatus(ResponseBaseModel):
19
+ """Result returnStatus model for air fryer status."""
20
+
21
+ currentTemp: int
22
+ cookSetTemp: int
23
+ mode: str
24
+ cookSetTime: int
25
+ cookLastTime: int
26
+ cookStatus: str
27
+ tempUnit: str
28
+
29
+
30
+ @dataclass
31
+ class FryerBaseReturnStatus(ResponseBaseModel):
32
+ """Result returnStatus model for air fryer status."""
33
+
34
+ cookStatus: str