pyvesync 3.4.1__tar.gz → 3.4.2__tar.gz

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 (60) hide show
  1. {pyvesync-3.4.1/src/pyvesync.egg-info → pyvesync-3.4.2}/PKG-INFO +115 -131
  2. {pyvesync-3.4.1 → pyvesync-3.4.2}/README.md +114 -130
  3. {pyvesync-3.4.1 → pyvesync-3.4.2}/pyproject.toml +1 -1
  4. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/auth.py +43 -28
  5. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/vesync.py +20 -2
  6. {pyvesync-3.4.1 → pyvesync-3.4.2/src/pyvesync.egg-info}/PKG-INFO +115 -131
  7. {pyvesync-3.4.1 → pyvesync-3.4.2}/LICENSE +0 -0
  8. {pyvesync-3.4.1 → pyvesync-3.4.2}/MANIFEST.in +0 -0
  9. {pyvesync-3.4.1 → pyvesync-3.4.2}/requirements.txt +0 -0
  10. {pyvesync-3.4.1 → pyvesync-3.4.2}/setup.cfg +0 -0
  11. {pyvesync-3.4.1 → pyvesync-3.4.2}/setup.py +0 -0
  12. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/__init__.py +0 -0
  13. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/__init__.py +0 -0
  14. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/bulb_base.py +0 -0
  15. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/fan_base.py +0 -0
  16. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/fryer_base.py +0 -0
  17. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/humidifier_base.py +0 -0
  18. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/outlet_base.py +0 -0
  19. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/purifier_base.py +0 -0
  20. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/switch_base.py +0 -0
  21. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/thermostat_base.py +0 -0
  22. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/base_devices/vesyncbasedevice.py +0 -0
  23. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/const.py +0 -0
  24. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/device_container.py +0 -0
  25. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/device_map.py +0 -0
  26. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/devices/__init__.py +0 -0
  27. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/devices/vesyncbulb.py +0 -0
  28. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/devices/vesyncfan.py +0 -0
  29. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/devices/vesynchumidifier.py +0 -0
  30. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/devices/vesynckitchen.py +0 -0
  31. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/devices/vesyncoutlet.py +0 -0
  32. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/devices/vesyncpurifier.py +0 -0
  33. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/devices/vesyncswitch.py +0 -0
  34. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/devices/vesyncthermostat.py +0 -0
  35. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/__init__.py +0 -0
  36. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/base_models.py +0 -0
  37. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/bulb_models.py +0 -0
  38. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/bypass_models.py +0 -0
  39. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/fan_models.py +0 -0
  40. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/fryer_models.py +0 -0
  41. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/home_models.py +0 -0
  42. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/humidifier_models.py +0 -0
  43. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/outlet_models.py +0 -0
  44. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/purifier_models.py +0 -0
  45. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/switch_models.py +0 -0
  46. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/thermostat_models.py +0 -0
  47. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/models/vesync_models.py +0 -0
  48. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/py.typed +0 -0
  49. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/utils/__init__.py +0 -0
  50. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/utils/colors.py +0 -0
  51. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/utils/device_mixins.py +0 -0
  52. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/utils/enum_utils.py +0 -0
  53. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/utils/errors.py +0 -0
  54. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/utils/helpers.py +0 -0
  55. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/utils/logs.py +0 -0
  56. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync/vesynchome.py +0 -0
  57. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync.egg-info/SOURCES.txt +0 -0
  58. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync.egg-info/dependency_links.txt +0 -0
  59. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync.egg-info/requires.txt +0 -0
  60. {pyvesync-3.4.1 → pyvesync-3.4.2}/src/pyvesync.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyvesync
3
- Version: 3.4.1
3
+ Version: 3.4.2
4
4
  Summary: pyvesync is a library to manage Etekcity Devices, Cosori Air Fryers, and Levoit Air Purifiers run on the VeSync app.
5
5
  Author: Mark Perdue
6
6
  Author-email: Joe Trabulsy <webdjoe@gmail.com>
@@ -46,7 +46,7 @@ Requires-Dist: griffe-inherited-docstrings; extra == "docs"
46
46
  Requires-Dist: griffe_warnings_deprecated; extra == "docs"
47
47
  Dynamic: license-file
48
48
 
49
- # pyvesync [![build status](https://img.shields.io/pypi/v/pyvesync.svg)](https://pypi.python.org/pypi/pyvesync) [![Build Status](https://dev.azure.com/webdjoe/pyvesync/_apis/build/status/webdjoe.pyvesync?branchName=master)](https://dev.azure.com/webdjoe/pyvesync/_build/latest?definitionId=4&branchName=master) [![Open Source? Yes!](https://badgen.net/badge/Open%20Source%20%3F/Yes%21/blue?icon=github)](https://github.com/Naereen/badges/) [![PyPI license](https://img.shields.io/pypi/l/ansicolortags.svg)](https://pypi.python.org/pypi/ansicolortags/) <!-- omit in toc -->
49
+ # pyvesync [![build status](https://img.shields.io/pypi/v/pyvesync.svg)](https://pypi.python.org/pypi/pyvesync) [![Open Source? Yes!](https://badgen.net/badge/Open%20Source%20%3F/Yes%21/blue?icon=github)](https://github.com/Naereen/badges/) [![PyPI license](https://img.shields.io/pypi/l/ansicolortags.svg)](https://pypi.python.org/pypi/ansicolortags/) <!-- omit in toc -->
50
50
 
51
51
  pyvesync is a library to manage VeSync compatible [smart home devices](#supported-devices)
52
52
 
@@ -83,7 +83,6 @@ Some of the changes are:
83
83
  - Implemented custom exceptions and error (code) handling for API responses.
84
84
  - `const` module to hold all library constants
85
85
  - Built the `DeviceMap` class to hold the mapping and features of devices.
86
- - COMING SOON: Use API to pull device modes and operating features.
87
86
 
88
87
  See [pyvesync V3](https://webdjoe.github.io/pyvesync/latest/pyvesync3/) for more information on the changes.
89
88
 
@@ -94,7 +93,7 @@ Library is now asynchronous, using aiohttp as a replacement for requests. The `p
94
93
  ```python
95
94
  import asyncio
96
95
  import aiohttp
97
- from pyvesync.vesync import VeSync
96
+ from pyvesync import VeSync
98
97
 
99
98
  async def main():
100
99
  async with VeSync(
@@ -119,7 +118,7 @@ async def main():
119
118
  if not manager.enabled:
120
119
  print("Not logged in.")
121
120
  return
122
- await manager.get_devices() # Instantiates supported devices in device list, automatically called by login, only needed if you would like updates
121
+ await manager.get_devices() # Instantiates supported devices in device list
123
122
  await manager.update() # Updates the state of all devices
124
123
 
125
124
  # manager.devices is a DeviceContainer object
@@ -140,8 +139,8 @@ async def main():
140
139
 
141
140
  # State of object held in `device.state` attribute
142
141
  print(outlet.state)
143
- state_json = outlet.dumps() # Returns JSON string of device state
144
- state_bytes = orjson.dumps(outlet.state) # Returns bytes of device state
142
+ state_json = outlet.to_json(state=True) # Returns JSON string of device state
143
+ state_bytes = outlet.to_jsonb(state=True) # Returns JSON bytes of device state
145
144
 
146
145
  # to view the response information of the last API call
147
146
  print(outlet.last_response)
@@ -153,7 +152,7 @@ async def main():
153
152
  session = aiohttp.ClientSession()
154
153
 
155
154
  async def main():
156
- async with VeSync("user", "password", session=session):
155
+ async with VeSync("user", "password", session=session) as manager:
157
156
  await manager.login()
158
157
  await manager.update()
159
158
 
@@ -166,7 +165,7 @@ if __name__ == "__main__":
166
165
  If using `async with` is not ideal, the `__aenter__()` and `__aexit__()` methods need to be called manually:
167
166
 
168
167
  ```python
169
- manager = VeSync(user, password)
168
+ manager = VeSync("user", "password")
170
169
 
171
170
  await manager.__aenter__()
172
171
 
@@ -175,7 +174,7 @@ await manager.__aenter__()
175
174
  await manager.__aexit__(None, None, None)
176
175
  ```
177
176
 
178
- pvesync will close the `ClientSession` that was created by the library on `__aexit__`. If a session is passed in as an argument, the library does not close it. If a session is passed in and not closed, aiohttp will generate an error on exit:
177
+ pyvesync will close the `ClientSession` that was created by the library on `__aexit__`. If a session is passed in as an argument, the library does not close it. If a session is passed in and not closed, aiohttp will generate an error on exit:
179
178
 
180
179
  ```text
181
180
  2025-02-16 14:41:07 - ERROR - asyncio - Unclosed client session
@@ -190,9 +189,9 @@ The VeSync signature is:
190
189
  VeSync(
191
190
  username: str,
192
191
  password: str,
193
- country_code: str = DEFAULT_COUNTRY_CODE, # US
192
+ country_code: str = DEFAULT_REGION, # "US"
194
193
  session: ClientSession | None = None,
195
- time_zone: str = DEFAULT_TZ # America/New_York,
194
+ time_zone: str = DEFAULT_TZ, # "America/New_York"
196
195
  redact: bool = True,
197
196
  )
198
197
  ```
@@ -209,6 +208,7 @@ There is a new nomenclature for product types that defines the device class. The
209
208
  5. `humidifier` - Humidifiers (not air purifiers)
210
209
  6. `bulb` - Light bulbs (not dimmers or switches)
211
210
  7. `airfryer` - Air fryers
211
+ 8. `thermostat` - Thermostats
212
212
 
213
213
  See [Supported Devices](#supported-devices) for a complete list of supported devices and models.
214
214
 
@@ -218,22 +218,22 @@ Exceptions are no longer caught by the library and must be handled by the user.
218
218
 
219
219
  Errors that occur at the aiohttp level are raised automatically and propagated to the user. That means exceptions raised by aiohttp that inherit from `aiohttp.ClientError` are propagated.
220
220
 
221
- When the connection to the VeSync API succeeds but returns an error code that prevents the library from functioning a custom exception inherited from `pyvesync.logs.VeSyncError` is raised.
221
+ When the connection to the VeSync API succeeds but returns an error code that prevents the library from functioning a custom exception inherited from `pyvesync.utils.errors.VeSyncError` is raised.
222
222
 
223
223
  Custom Exceptions raised by all API calls:
224
224
 
225
- - `pyvesync.logs.VeSyncServerError` - The API connected and returned a code indicated there is a server-side error.
226
- - `pyvesync.logs.VeSyncRateLimitError` - The API's rate limit has been exceeded.
227
- - `pyvesync.logs.VeSyncAPIStatusCodeError` - The API returned a non-200 status code.
228
- - `pyvesync.logs.VeSyncAPIResponseError` - The response from the API was not in an expected format.
225
+ - `pyvesync.utils.errors.VeSyncServerError` - The API connected and returned a code indicated there is a server-side error.
226
+ - `pyvesync.utils.errors.VeSyncRateLimitError` - The API's rate limit has been exceeded.
227
+ - `pyvesync.utils.errors.VeSyncAPIStatusCodeError` - The API returned a non-200 status code.
228
+ - `pyvesync.utils.errors.VeSyncAPIResponseError` - The response from the API was not in an expected format.
229
229
 
230
230
  Login API Exceptions
231
231
 
232
- - `pyvesync.logs.VeSyncLoginError` - The username or password is incorrect.
232
+ - `pyvesync.utils.errors.VeSyncLoginError` - The username or password is incorrect.
233
233
 
234
- See [errors](https://webdjoe.github.io/pyvesync/latest/development/utils/errors) documentation for a complete list of error codes and exceptions.
234
+ See the [errors](https://webdjoe.github.io/pyvesync/latest/development/utils/errors/) documentation for a complete list of error codes and exceptions.
235
235
 
236
- The [raise_api_errors()](https://webdjoe.github.io/pyvesync/latest/development/utils/errors/#pyvesync.utils.errors.raise_api_errors) function is called for every API call and checks for general response errors. It can raise the following exceptions:
236
+ The `raise_api_errors()` function is called for every API call and checks for general response errors. It can raise the following exceptions:
237
237
 
238
238
  - `VeSyncServerError` - The API connected and returned a code indicated there is a server-side error.
239
239
  - `VeSyncRateLimitError` - The API's rate limit has been exceeded.
@@ -257,12 +257,14 @@ pip install pyvesync
257
257
 
258
258
  ### Etekcity Outlets
259
259
 
260
- 1. Voltson Smart WiFi Outlet- Round (7A model ESW01-USA)
260
+ 1. Voltson Smart WiFi Outlet - Round (7A model ESW01-USA)
261
261
  2. Voltson Smart WiFi Outlet - Round (10A model ESW01-EU)
262
- 3. Voltson Smart Wifi Outlet - Round (10A model ESW03-USA)
263
- 4. Voltson Smart Wifi Outlet - Round (10A model ESW10-USA)
262
+ 3. Voltson Smart WiFi Outlet - Round (10A model ESW03-USA)
263
+ 4. Voltson Smart WiFi Outlet - Round (10A model ESW10-USA)
264
264
  5. Voltson Smart WiFi Outlet - Rectangle (15A model ESW15-USA)
265
265
  6. Two Plug Outdoor Outlet (ESO15-TB) (Each plug is a separate `VeSyncOutlet` object, energy readings are for both plugs combined)
266
+ 7. BSDOG / Greensun Smart Outlet Series (BSDOG01, BSDOG02, WYSMTOD16A, WM-PLUG and more)
267
+ 8. WHOPLUG / Greensun Smart Outlet
266
268
 
267
269
  <!--SUPPORTED OUTLETS END-->
268
270
 
@@ -271,46 +273,54 @@ pip install pyvesync
271
273
  ### Wall Switches
272
274
 
273
275
  1. Etekcity Smart WiFi Light Switch (model ESWL01)
274
- 2. Etekcity Wifi Dimmer Switch (ESD16)
276
+ 2. Etekcity WiFi 3-Way Switch (model ESWL03)
277
+ 3. Etekcity WiFi Dimmer Switch (model ESWD16)
275
278
 
276
279
  <!--SUPPORTED SWITCHES END-->
277
280
 
278
281
  ### Levoit Air Purifiers
279
282
 
280
283
  1. LV-PUR131S
281
- 2. Core 200S
282
- 3. Core 300S
283
- 4. Core 400S
284
- 5. Core 600S
285
- 6. Vital 100S
286
- 7. Vital 200S
284
+ 2. LV-RH131S
285
+ 3. Core 200S
286
+ 4. Core 300S
287
+ 5. Core 400S
288
+ 6. Core 600S
289
+ 7. Vital 100S / 200S
287
290
  8. Everest Air
291
+ 9. Sprout Air Purifier
288
292
 
289
- ### Etekcity Bulbs
293
+ ### Etekcity / Valceno Bulbs
290
294
 
291
295
  1. Soft White Dimmable Smart Bulb (ESL100)
292
296
  2. Cool to Soft White Tunable Dimmable Bulb (ESL100CW)
293
-
294
- ### Valceno Bulbs
295
-
296
- 1. Valceno Multicolor Bulb (XYD0001)
297
+ 3. Multicolor Dimmable Bulb (ESL100MC)
298
+ 4. Valceno Multicolor Bulb (XYD0001)
297
299
 
298
300
  ### Levoit Humidifiers
299
301
 
300
- 1. Dual 200S
301
- 2. Classic 300S
302
- 3. LV600S
303
- 4. OasisMist 450S
304
- 5. OasisMist 600S
302
+ 1. Classic 200S
303
+ 2. Dual 200S
304
+ 3. Classic 300S
305
+ 4. LV600S / LV603S
306
+ 5. OasisMist 450S / 600S
305
307
  6. OasisMist 1000S
308
+ 7. Superior 6000S
309
+ 8. Sprout Humidifier
306
310
 
307
- ### Cosori Air Fryer
311
+ ### Levoit Fans
308
312
 
309
- 1. Cosori 3.7 and 5.8 Quart Air Fryer
313
+ 1. 42 in. Tower Fan (LTF-F422S Series)
314
+ 2. Pedestal Fan (LPF-R432S Series)
310
315
 
311
- ### Fans
316
+ ### Cosori Air Fryers
312
317
 
313
- 1. 42 in. Tower Fan
318
+ 1. Cosori 3.7 Quart Air Fryer (CS137-AF)
319
+ 2. Cosori 5.8 Quart Air Fryer (CS158-AF)
320
+
321
+ ### Thermostats
322
+
323
+ 1. Aura Thermostat (LTM-A401S-WUS)
314
324
 
315
325
  <!--SUPPORTED DEVICES END-->
316
326
 
@@ -319,10 +329,6 @@ pip install pyvesync
319
329
  ```python
320
330
  import asyncio
321
331
  from pyvesync import VeSync
322
- from pyvesync.logs import VeSyncLoginError
323
-
324
- # VeSync is an asynchronous context manager
325
- # VeSync(username, password, redact=True, session=None)
326
332
 
327
333
  async def main():
328
334
  async with VeSync("user", "password") as manager:
@@ -332,14 +338,14 @@ async def main():
332
338
  # Acts as a set of device instances
333
339
  device_container = manager.devices
334
340
 
335
- outlets = device_container.outlets # List of outlet instances
341
+ outlets = device_container.outlets # List of outlet instances
336
342
  outlet = outlets[0]
337
343
  await outlet.update()
338
344
  await outlet.turn_off()
339
345
  outlet.display()
340
346
 
341
- # Iterate of entire device list
342
- for devices in device_container:
347
+ # Iterate over entire device list
348
+ for device in device_container:
343
349
  device.display()
344
350
 
345
351
 
@@ -347,15 +353,11 @@ if __name__ == "__main__":
347
353
  asyncio.run(main())
348
354
  ```
349
355
 
350
- If you want to reuse your token and account_id between runs. The `VeSync.auth` object holds the credentials and helper methods to save and load credentials. See the [Authentication Documentation](https://webdjoe.github.io/pyvesync/latest/authentication) for more details.
356
+ If you want to reuse your token and account_id between runs. The `VeSync.auth` object holds the credentials and helper methods to save and load credentials. See the [Authentication Documentation](https://webdjoe.github.io/pyvesync/latest/authentication) for more details.
351
357
 
352
358
  ```python
353
359
  import asyncio
354
360
  from pyvesync import VeSync
355
- from pyvesync.logs import VeSyncLoginError
356
-
357
- # VeSync is an asynchronous context manager
358
- # VeSync(username, password, redact=True, session=None)
359
361
 
360
362
  async def main():
361
363
  async with VeSync("user", "password") as manager:
@@ -367,32 +369,37 @@ async def main():
367
369
  await manager.load_credentials_from_file("/path/to/token_file")
368
370
 
369
371
  # Or credentials can be passed directly
370
- manager.set_credentials("your_token", "your_account_id")
371
-
372
- # No login needed
372
+ manager.set_credentials(
373
+ token="your_token",
374
+ account_id="your_account_id",
375
+ country_code="US",
376
+ region="US",
377
+ )
378
+
379
+ # No login needed if token is valid
373
380
  # await manager.login()
374
381
 
375
382
  # To store credentials to a file after login
376
- await manager.save_credentials() # Saves to default location ~/.vesync_token
377
- # or pass a file path
378
383
  await manager.save_credentials("/path/to/token_file")
379
384
 
380
- # Output Credentials as JSON String
381
- await manager.output_credentials()
385
+ # Output credentials as JSON string
386
+ print(manager.output_credentials_json())
387
+ # Output credentials as dictionary
388
+ print(manager.output_credentials_dict())
382
389
 
383
390
  await manager.update()
384
391
 
385
392
  # Acts as a set of device instances
386
393
  device_container = manager.devices
387
394
 
388
- outlets = device_container.outlets # List of outlet instances
395
+ outlets = device_container.outlets # List of outlet instances
389
396
  outlet = outlets[0]
390
397
  await outlet.update()
391
398
  await outlet.turn_off()
392
399
  outlet.display()
393
400
 
394
- # Iterate of entire device list
395
- for devices in device_container:
401
+ # Iterate over entire device list
402
+ for device in device_container:
396
403
  device.display()
397
404
 
398
405
 
@@ -417,7 +424,7 @@ manager.devices.bulbs = [VeSyncBulbInstances]
417
424
  manager.devices.air_purifiers = [VeSyncPurifierInstances]
418
425
  manager.devices.humidifiers = [VeSyncHumidifierInstances]
419
426
  manager.devices.air_fryers = [VeSyncAirFryerInstances]
420
- managers.devices.thermostats = [VeSyncThermostatInstances]
427
+ manager.devices.thermostats = [VeSyncThermostatInstances]
421
428
 
422
429
  # Get device by device name
423
430
  dev_name = "My Device"
@@ -446,8 +453,7 @@ This is an example of debug mode with redact enabled:
446
453
  ```python
447
454
  import logging
448
455
  import asyncio
449
- import aiohttp
450
- from pyvesync.vesync import VeSync
456
+ from pyvesync import VeSync
451
457
 
452
458
  logger = logging.getLogger("pyvesync")
453
459
  logger.setLevel(logging.DEBUG)
@@ -460,7 +466,7 @@ async def main():
460
466
  await manager.login()
461
467
  await manager.update()
462
468
 
463
- outlet = manager.outlets[0]
469
+ outlet = manager.devices.outlets[0]
464
470
  await outlet.update()
465
471
  await outlet.turn_off()
466
472
  outlet.display()
@@ -475,17 +481,16 @@ if __name__ == "__main__":
475
481
  To log to a file, use the `log_to_file()` method of the `VeSync` class. Pass the file path as an argument.
476
482
 
477
483
  ```python
478
-
479
484
  import asyncio
480
485
  from pyvesync import VeSync
481
486
 
482
487
  async def main():
483
488
  async with VeSync("user", "password") as manager:
484
- manager.log_to_file("pyvesync.log", level=logging.DEBUG, stdout=True) # stdout argument prints log to console as well
489
+ manager.log_to_file("pyvesync.log", stdout=True) # stdout argument prints log to console as well
485
490
  await manager.login()
486
491
  await manager.update()
487
492
 
488
- outlet = manager.outlets[0]
493
+ outlet = manager.devices.outlets[0]
489
494
  await outlet.update()
490
495
  await outlet.turn_off()
491
496
  outlet.display()
@@ -497,13 +502,13 @@ if __name__ == "__main__":
497
502
 
498
503
  ## Feature Requests
499
504
 
500
- Before filing an issue to request a new feature or device, please ensure that you will take the time to test the feature throuroughly. New features cannot be simply tested on Home Assistant. A separate integration must be created which is not part of this library. In order to test a new feature, clone the branch and install into a new virtual environment.
505
+ Before filing an issue to request a new feature or device, please ensure that you will take the time to test the feature thoroughly. New features cannot be simply tested on Home Assistant. A separate integration must be created which is not part of this library. In order to test a new feature, clone the branch and install into a new virtual environment.
501
506
 
502
507
  ```bash
503
508
  mkdir python_test && cd python_test
504
509
 
505
510
  # Check Python version is 3.11 or higher
506
- python3 --version # or python --version or python3.8 --version
511
+ python3 --version # or python --version
507
512
  # Create a new venv
508
513
  python3 -m venv pyvesync-venv
509
514
  # Activate the venv on linux
@@ -521,14 +526,11 @@ pip install git+https://github.com/webdjoe/pyvesync.git@refs/pull/PR_NUMBER/head
521
526
 
522
527
  Test functionality with a script, please adjust methods and logging statements to the device you are testing.
523
528
 
524
- See `[testing_scripts](./testing_scripts/README.md)` for a fully functioning test script for all devices.
529
+ See [testing_scripts](./testing_scripts/README.md) for a fully functioning test script for all devices.
525
530
 
526
531
  ```python
527
532
  import asyncio
528
- import sys
529
533
  import logging
530
- import json
531
- from functool import chain
532
534
  from pyvesync import VeSync
533
535
 
534
536
  vs_logger = logging.getLogger("pyvesync")
@@ -540,65 +542,47 @@ logger.setLevel(logging.DEBUG)
540
542
  USERNAME = "YOUR USERNAME"
541
543
  PASSWORD = "YOUR PASSWORD"
542
544
 
543
- DEVICE_NAME = "Device" # Device to test
545
+ DEVICE_NAME = "Device" # Device to test
544
546
 
545
547
  async def test_device():
546
548
  # Instantiate VeSync class and login
547
- async with VeSync(USERNAME, PASSWORD, redact=True) as manager:
548
- await manager.login()
549
-
550
- # Pull and update devices
551
- await manager.update()
552
-
553
- for dev in manager.devices:
554
- # Print all device info
555
- logger.debug(dev.device_name + "\n")
556
- logger.debug(dev.display())
557
-
558
- # Find correct device
559
- if dev.device_name.lower() != DEVICE_NAME.lower():
560
- logger.debug("%s is not %s, continuing", self.device_name, DEVICE_NAME)
561
- continue
562
-
563
- logger.debug('--------------%s-----------------' % dev.device_name)
564
- logger.debug(dev.display())
565
- logger.debug(dev.displayJSON())
566
- # Test all device methods and functionality
567
- # Test Properties
568
- logger.debug("Fan is on - %s", dev.is_on)
569
- logger.debug("Modes - %s", dev.modes)
570
- logger.debug("Fan Level - %s", dev.fan_level)
571
- logger.debug("Fan Air Quality - %s", dev.air_quality)
572
- logger.debug("Screen Status - %s", dev.screen_status)
573
-
574
- logger.debug("Turning on")
575
- await fan.turn_on()
576
- logger.debug("Device is on %s", dev.is_on)
577
-
578
- logger.debug("Turning off")
579
- await fan.turn_off()
580
- logger.debug("Device is on %s", dev.is_on)
581
-
582
- logger.debug("Sleep mode")
583
- fan.sleep_mode()
584
- logger.debug("Current mode - %s", dev.details['mode'])
585
-
586
- fan.auto_mode()
587
-
588
- logger.debug("Set Fan Speed - %s", dev.set_fan_speed)
589
- logger.debug("Current Fan Level - %s", dev.fan_level)
590
- logger.debug("Current mode - %s", dev.mode)
591
-
592
- # Display all device info
593
- logger.debug(dev.display(state=True))
594
- logger.debug(dev.to_json(state=True, indent=True))
595
- dev_dict = dev.to_dict(state=True)
549
+ async with VeSync(USERNAME, PASSWORD, redact=True) as manager:
550
+ await manager.login()
551
+
552
+ # Pull and update devices
553
+ await manager.update()
554
+
555
+ for dev in manager.devices:
556
+ # Print all device info
557
+ logger.debug(dev.device_name)
558
+ dev.display()
559
+
560
+ # Find correct device
561
+ if dev.device_name.lower() != DEVICE_NAME.lower():
562
+ logger.debug("%s is not %s, continuing", dev.device_name, DEVICE_NAME)
563
+ continue
564
+
565
+ logger.debug('-------------- %s -----------------', dev.device_name)
566
+ dev.display()
567
+
568
+ # Test device methods
569
+ logger.debug("Device is on: %s", dev.is_on)
570
+
571
+ logger.debug("Turning on")
572
+ await dev.turn_on()
573
+ logger.debug("Device is on: %s", dev.is_on)
574
+
575
+ logger.debug("Turning off")
576
+ await dev.turn_off()
577
+ logger.debug("Device is on: %s", dev.is_on)
578
+
579
+ # Display all device info as JSON
580
+ logger.debug(dev.to_json(state=True, indent=True))
581
+ dev_dict = dev.to_dict(state=True)
596
582
 
597
583
  if __name__ == "__main__":
598
584
  logger.debug("Testing device")
599
585
  asyncio.run(test_device())
600
- ...
601
-
602
586
  ```
603
587
 
604
588
  ## Device Requests