libdyson-rest 0.3.0__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.
@@ -0,0 +1,21 @@
1
+ [flake8]
2
+ max-line-length = 120
3
+ extend-ignore =
4
+ # E203: whitespace before ':' (conflicts with black)
5
+ E203,
6
+ # W503: line break before binary operator (conflicts with black)
7
+ W503,
8
+ exclude =
9
+ .git,
10
+ __pycache__,
11
+ .venv,
12
+ venv,
13
+ build,
14
+ dist,
15
+ .tox,
16
+ .eggs,
17
+ *.egg-info
18
+ per-file-ignores =
19
+ # Allow unused imports in __init__.py files
20
+ __init__.py:F401
21
+ max-complexity = 10
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Christopher Gray
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,34 @@
1
+ # Include the README and license files
2
+ include README.md
3
+ include LICENSE
4
+
5
+ # Include requirements files
6
+ include requirements.txt
7
+ include requirements-dev.txt
8
+
9
+ # Include configuration files
10
+ include pyproject.toml
11
+ include .flake8
12
+
13
+ # Include the source code
14
+ recursive-include src *.py
15
+
16
+ # Include type stubs if any
17
+ recursive-include src *.pyi
18
+
19
+ # Exclude development and build files
20
+ exclude .gitignore
21
+ exclude .pre-commit-config.yaml
22
+ recursive-exclude * __pycache__
23
+ recursive-exclude * *.py[co]
24
+ recursive-exclude * *.so
25
+ recursive-exclude * *.dylib
26
+ recursive-exclude .venv *
27
+ recursive-exclude venv *
28
+ recursive-exclude build *
29
+ recursive-exclude dist *
30
+ recursive-exclude *.egg-info *
31
+ recursive-exclude tests *
32
+ recursive-exclude examples *
33
+ recursive-exclude .git *
34
+ recursive-exclude .github *
@@ -0,0 +1,604 @@
1
+ Metadata-Version: 2.4
2
+ Name: libdyson-rest
3
+ Version: 0.3.0
4
+ Summary: Python library for interacting with Dyson devices through their official REST API
5
+ Author-email: Chris Gray <cmgrayb@outlook.com>
6
+ Maintainer-email: Chris Gray <cmgrayb@outlook.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/cmgrayb/libdyson-rest
9
+ Project-URL: Repository, https://github.com/cmgrayb/libdyson-rest
10
+ Project-URL: Issues, https://github.com/cmgrayb/libdyson-rest/issues
11
+ Project-URL: Documentation, https://github.com/cmgrayb/libdyson-rest#readme
12
+ Project-URL: Changelog, https://github.com/cmgrayb/libdyson-rest/releases
13
+ Keywords: dyson,air-purifier,fan,iot,rest-api,mqtt,home-automation
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Home Automation
23
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: requests>=2.28.0
29
+ Requires-Dist: cryptography>=3.4.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: black==25.1.0; extra == "dev"
32
+ Requires-Dist: flake8==7.3.0; extra == "dev"
33
+ Requires-Dist: isort==6.0.1; extra == "dev"
34
+ Requires-Dist: pytest==8.4.1; extra == "dev"
35
+ Requires-Dist: pytest-cov==6.2.1; extra == "dev"
36
+ Requires-Dist: mypy==1.17.1; extra == "dev"
37
+ Requires-Dist: types-requests==2.32.4.20250809; extra == "dev"
38
+ Requires-Dist: types-cryptography==3.3.23.2; extra == "dev"
39
+ Requires-Dist: bandit[toml]==1.8.0; extra == "dev"
40
+ Dynamic: license-file
41
+
42
+ # libdyson-rest
43
+
44
+ [![PyPI version](https://badge.fury.io/py/libdyson-rest.svg)](https://badge.fury.io/py/libdyson-rest)
45
+ [![Python](https://img.shields.io/pypi/pyversions/libdyson-rest.svg)](https://pypi.org/project/libdyson-rest/)
46
+ [![License](https://img.shields.io/pypi/l/libdyson-rest.svg)](https://github.com/cmgrayb/libdyson-rest/blob/main/LICENSE)
47
+
48
+ A Python library for interacting with Dyson devices through their official REST API.
49
+
50
+ ## Features
51
+
52
+ - **Official API Compliance**: Implements the complete Dyson App API as documented in their OpenAPI specification
53
+ - **Two-Step Authentication**: Secure login process with OTP codes
54
+ - **Complete Device Management**: List devices, get device details, and retrieve IoT credentials
55
+ - **MQTT Connection Support**: Extract both cloud (AWS IoT) and local MQTT connection parameters
56
+ - **Password Decryption**: Decrypt local MQTT broker credentials for direct device communication
57
+ - **Token-Based Authentication**: Store and reuse authentication tokens for repeated API calls
58
+ - **Type-Safe Models**: Comprehensive data models with proper type hints
59
+ - **Error Handling**: Detailed exception hierarchy for robust error handling
60
+ - **Context Manager Support**: Automatic resource cleanup
61
+
62
+ ## Installation
63
+
64
+ Install from PyPI:
65
+
66
+ ```bash
67
+ pip install libdyson-rest
68
+ ```
69
+
70
+ Or install from source:
71
+
72
+ ```bash
73
+ git clone https://github.com/cmgrayb/libdyson-rest.git
74
+ cd libdyson-rest
75
+ pip install -e .
76
+ ```
77
+
78
+ ## Quick Start
79
+
80
+ ```python
81
+ from libdyson_rest import DysonClient
82
+
83
+ # Initialize the client
84
+ client = DysonClient(
85
+ email="your@email.com",
86
+ password="your_password",
87
+ country="US", # ISO 3166-1 alpha-2 country code
88
+ culture="en-US" # IETF language code
89
+ )
90
+
91
+ # Two-step authentication process
92
+ try:
93
+ # Step 1: Begin login process
94
+ challenge = client.begin_login()
95
+ print(f"Challenge ID: {challenge.challenge_id}")
96
+ print("Check your email for an OTP code")
97
+
98
+ # Step 2: Complete login with OTP code
99
+ otp_code = input("Enter OTP code: ")
100
+ login_info = client.complete_login(str(challenge.challenge_id), otp_code)
101
+ print(f"Logged in! Account: {login_info.account}")
102
+
103
+ # Get devices
104
+ devices = client.get_devices()
105
+ for device in devices:
106
+ print(f"Device: {device.name} ({device.serial_number})")
107
+ print(f" Type: {device.type}")
108
+ print(f" Category: {device.category.value}")
109
+
110
+ # Get IoT credentials for connected devices
111
+ if device.connection_category.value != "nonConnected":
112
+ iot_data = client.get_iot_credentials(device.serial_number)
113
+ print(f" IoT Endpoint: {iot_data.endpoint}")
114
+
115
+ finally:
116
+ client.close()
117
+ ```
118
+
119
+ ## Authentication Flow
120
+
121
+ The Dyson API uses a secure two-step authentication process:
122
+
123
+ ### 1. API Provisioning (Automatic)
124
+ ```python
125
+ version = client.provision() # Called automatically
126
+ ```
127
+
128
+ ### 2. User Status Check (Optional)
129
+ ```python
130
+ user_status = client.get_user_status()
131
+ print(f"Account status: {user_status.account_status.value}")
132
+ ```
133
+
134
+ ### 3. Begin Login Process
135
+ ```python
136
+ challenge = client.begin_login()
137
+ # This triggers an OTP code to be sent to your email
138
+ ```
139
+
140
+ ### 4. Complete Login with OTP
141
+ ```python
142
+ login_info = client.complete_login(
143
+ challenge_id=str(challenge.challenge_id),
144
+ otp_code="123456" # From your email
145
+ )
146
+ ```
147
+
148
+ ### 5. Authenticated API Calls
149
+ ```python
150
+ devices = client.get_devices()
151
+ iot_data = client.get_iot_credentials("device_serial")
152
+ ```
153
+
154
+ ## API Reference
155
+
156
+ ### DysonClient
157
+
158
+ #### Constructor
159
+ ```python
160
+ DysonClient(
161
+ email: Optional[str] = None,
162
+ password: Optional[str] = None,
163
+ country: str = "US",
164
+ culture: str = "en-US",
165
+ timeout: int = 30,
166
+ user_agent: str = "android client"
167
+ )
168
+ ```
169
+
170
+ #### Core Methods
171
+
172
+ ##### Authentication
173
+ - `provision() -> str`: Required initial API call
174
+ - `get_user_status(email=None) -> UserStatus`: Check account status
175
+ - `begin_login(email=None) -> LoginChallenge`: Start login process
176
+ - `complete_login(challenge_id, otp_code, email=None, password=None) -> LoginInformation`: Complete authentication
177
+ - `authenticate(otp_code=None) -> bool`: Convenience method for full auth flow
178
+
179
+ ##### Device Management
180
+ - `get_devices() -> List[Device]`: List all account devices
181
+ - `get_iot_credentials(serial_number) -> IoTData`: Get AWS IoT connection info
182
+
183
+ ##### Session Management
184
+ - `close() -> None`: Close session and clear state
185
+ - `__enter__()` and `__exit__()`: Context manager support
186
+
187
+ ### Data Models
188
+
189
+ #### Device
190
+ ```python
191
+ @dataclass
192
+ class Device:
193
+ category: DeviceCategory # ec, flrc, hc, light, robot, wearable
194
+ connection_category: ConnectionCategory # lecAndWifi, lecOnly, nonConnected, wifiOnly
195
+ model: str
196
+ name: str
197
+ serial_number: str
198
+ type: str
199
+ variant: Optional[str] = None
200
+ connected_configuration: Optional[ConnectedConfiguration] = None
201
+ ```
202
+
203
+ #### DeviceCategory (Enum)
204
+ - `ENVIRONMENT_CLEANER = "ec"` - Air filters, purifiers
205
+ - `FLOOR_CLEANER = "flrc"` - Vacuum cleaners
206
+ - `HAIR_CARE = "hc"` - Hair dryers, stylers
207
+ - `LIGHT = "light"` - Lighting products
208
+ - `ROBOT = "robot"` - Robot vacuums
209
+ - `WEARABLE = "wearable"` - Wearable devices
210
+
211
+ #### ConnectionCategory (Enum)
212
+ - `LEC_AND_WIFI = "lecAndWifi"` - Bluetooth and Wi-Fi
213
+ - `LEC_ONLY = "lecOnly"` - Bluetooth only
214
+ - `NON_CONNECTED = "nonConnected"` - No connectivity
215
+ - `WIFI_ONLY = "wifiOnly"` - Wi-Fi only
216
+
217
+ #### LoginInformation
218
+ ```python
219
+ @dataclass
220
+ class LoginInformation:
221
+ account: UUID # Account ID
222
+ token: str # Bearer token for API calls
223
+ token_type: TokenType # Always "Bearer"
224
+ ```
225
+
226
+ #### IoTData
227
+ ```python
228
+ @dataclass
229
+ class IoTData:
230
+ endpoint: str # AWS IoT endpoint
231
+ iot_credentials: IoTCredentials # Connection credentials
232
+ ```
233
+
234
+ ### Exception Hierarchy
235
+
236
+ ```
237
+ DysonAPIError (base)
238
+ ├── DysonConnectionError # Network/connection issues
239
+ ├── DysonAuthError # Authentication failures
240
+ ├── DysonDeviceError # Device operation failures
241
+ └── DysonValidationError # Input validation errors
242
+ ```
243
+
244
+ ## Advanced Usage
245
+
246
+ ### Using Context Manager
247
+ ```python
248
+ with DysonClient(email="your@email.com", password="password") as client:
249
+ # Authentication
250
+ challenge = client.begin_login()
251
+ otp = input("Enter OTP: ")
252
+ client.complete_login(str(challenge.challenge_id), otp)
253
+
254
+ # API calls
255
+ devices = client.get_devices()
256
+ # Client automatically closed on exit
257
+ ```
258
+
259
+ ### Error Handling
260
+ ```python
261
+ from libdyson_rest import DysonAuthError, DysonConnectionError, DysonAPIError
262
+
263
+ try:
264
+ client = DysonClient(email="user@example.com", password="pass")
265
+ challenge = client.begin_login()
266
+
267
+ except DysonAuthError as e:
268
+ print(f"Authentication failed: {e}")
269
+ except DysonConnectionError as e:
270
+ print(f"Network error: {e}")
271
+ except DysonAPIError as e:
272
+ print(f"API error: {e}")
273
+ ```
274
+
275
+ ### Manual Authentication Steps
276
+ ```python
277
+ client = DysonClient(email="user@example.com", password="password")
278
+
279
+ # Step 1: Provision (required)
280
+ version = client.provision()
281
+ print(f"API version: {version}")
282
+
283
+ # Step 2: Check user status
284
+ user_status = client.get_user_status()
285
+ print(f"Account active: {user_status.account_status.value == 'ACTIVE'}")
286
+
287
+ # Step 3: Begin login
288
+ challenge = client.begin_login()
289
+ print("Check email for OTP")
290
+
291
+ # Step 4: Complete login
292
+ otp = input("OTP: ")
293
+ login_info = client.complete_login(str(challenge.challenge_id), otp)
294
+ print(f"Bearer token: {login_info.token[:10]}...")
295
+
296
+ # Step 5: Use authenticated endpoints
297
+ devices = client.get_devices()
298
+ ```
299
+
300
+ ## Configuration
301
+
302
+ ### Environment Variables
303
+ - `DYSON_EMAIL`: Default email address
304
+ - `DYSON_PASSWORD`: Default password
305
+ - `DYSON_COUNTRY`: Default country code (default: "US")
306
+ - `DYSON_CULTURE`: Default culture/locale (default: "en-US")
307
+ - `DYSON_TIMEOUT`: Request timeout in seconds (default: "30")
308
+
309
+ ### Country and Culture Codes
310
+ - **Country**: 2-letter uppercase ISO 3166-1 alpha-2 codes (e.g., "US", "GB", "DE")
311
+ - **Culture**: 5-character IETF language codes (e.g., "en-US", "en-GB", "de-DE")
312
+
313
+ ## API Compliance
314
+
315
+ This library implements the complete Dyson App API as documented in their OpenAPI specification:
316
+ - Authentication endpoints (`/v3/userregistration/email/*`)
317
+ - Device management (`/v3/manifest`)
318
+ - IoT credentials (`/v2/authorize/iot-credentials`)
319
+ - Provisioning (`/v1/provisioningservice/application/Android/version`)
320
+
321
+ ## Requirements
322
+
323
+ - Python 3.8+
324
+ - `requests` - HTTP client library
325
+ - `dataclasses` - Data model support (Python 3.8+)
326
+
327
+ ## Contributing
328
+
329
+ Contributions are welcome! Please ensure all changes maintain compatibility with the official Dyson OpenAPI specification.
330
+
331
+ ## Versioning & Releases
332
+
333
+ This project follows **PEP 440** versioning (not semantic versioning). Here's how versions are distributed:
334
+
335
+ ### Version Patterns
336
+
337
+ | Pattern | Example | Distribution | Purpose |
338
+ |---------|---------|--------------|---------|
339
+ | **Alpha** | `0.3.0a1`, `0.3.0alpha1` | TestPyPI | Internal testing only |
340
+ | **Dev** | `0.3.0.dev1` | TestPyPI | Development builds |
341
+ | **Beta** | `0.3.0b1`, `0.3.0beta1` | **PyPI** | Public beta testing |
342
+ | **RC** | `0.3.0rc1` | **PyPI** | Release candidates |
343
+ | **Stable** | `0.3.0` | **PyPI** | Production releases |
344
+ | **Patch** | `0.3.0.post1` | **PyPI** | Post-release patches |
345
+
346
+ ### Installation
347
+
348
+ ```bash
349
+ # Install stable release
350
+ pip install libdyson-rest
351
+
352
+ # Install latest beta (includes rc, beta versions)
353
+ pip install --pre libdyson-rest
354
+
355
+ # Install specific version
356
+ pip install libdyson-rest==0.3.0b1
357
+
358
+ # Install from TestPyPI (alpha/dev versions)
359
+ pip install -i https://test.pypi.org/simple/ libdyson-rest==0.3.0a1
360
+ ```
361
+
362
+ ### For Beta Testers
363
+
364
+ Want to help test new features? Install pre-release versions:
365
+
366
+ ```bash
367
+ pip install --pre libdyson-rest
368
+ ```
369
+
370
+ This will install the latest beta or release candidate, giving you access to new features before stable release.
371
+
372
+ ## License
373
+
374
+ This project is licensed under the MIT License - see the LICENSE file for details.
375
+
376
+ ## Disclaimer
377
+
378
+ This is an unofficial library. Dyson is a trademark of Dyson Ltd. This library is not affiliated with, endorsed by, or sponsored by Dyson Ltd.
379
+
380
+ ## OpenAPI Specification
381
+
382
+ This library is based on the community-documented Dyson App API OpenAPI specification. The specification can be found at:
383
+ https://raw.githubusercontent.com/libdyson-wg/appapi/refs/heads/main/openapi.yaml
384
+
385
+ This project is created to further the efforts of others in the community in interacting with the
386
+ Dyson devices they have purchased to better integrate them into their smart homes.
387
+
388
+ At this time, this library is PURELY EXPERIMENTAL and should not be used without carefully examining
389
+ the code before doing so. **USE AT YOUR OWN RISK**
390
+
391
+ ## Features
392
+
393
+ - Clean, intuitive API for Dyson device interaction
394
+ - Full type hints support
395
+ - Comprehensive error handling
396
+ - Async/sync support
397
+ - Built-in authentication handling
398
+ - Extensive test coverage
399
+
400
+ ## Installation
401
+
402
+ ### From Source (Development)
403
+
404
+ ```bash
405
+ # Clone the repository
406
+ git clone https://github.com/cmgrayb/libdyson-rest.git
407
+ cd libdyson-rest
408
+
409
+ # Create and activate virtual environment
410
+ python -m venv .venv
411
+
412
+ # Windows
413
+ .venv\Scripts\activate
414
+
415
+ # Linux/Mac
416
+ source .venv/bin/activate
417
+
418
+ # Install development dependencies
419
+ pip install -r requirements-dev.txt
420
+ ```
421
+
422
+ ## Quick Start
423
+
424
+ ```python
425
+ from libdyson_rest import DysonClient
426
+
427
+ # Initialize the client
428
+ client = DysonClient(
429
+ email="your_email@example.com",
430
+ password="your_password",
431
+ country="US"
432
+ )
433
+
434
+ # Authenticate with Dyson API
435
+ client.authenticate()
436
+
437
+ # Get your devices
438
+ devices = client.get_devices()
439
+ for device in devices:
440
+ print(f"Device: {device['name']} ({device['serial']})")
441
+
442
+ # Always close the client when done
443
+ client.close()
444
+
445
+ # Or use as context manager
446
+ with DysonClient(email="email@example.com", password="password") as client:
447
+ client.authenticate()
448
+ devices = client.get_devices()
449
+ # Client is automatically closed
450
+ ```
451
+
452
+ ## Development
453
+
454
+ This project uses several tools to maintain code quality:
455
+
456
+ - **Black**: Code formatting (120 character line length)
457
+ - **Flake8**: Linting and style checking
458
+ - **isort**: Import sorting
459
+ - **MyPy**: Type checking
460
+ - **Pytest**: Testing framework
461
+ - **Pre-commit**: Git hooks
462
+
463
+ ### Setting up Development Environment
464
+
465
+ 1. **Create virtual environment and install dependencies:**
466
+ ```bash
467
+ python -m venv .venv
468
+ .venv\Scripts\activate # Windows
469
+ pip install -r requirements-dev.txt
470
+ ```
471
+
472
+ 2. **Install pre-commit hooks:**
473
+ ```bash
474
+ pre-commit install
475
+ ```
476
+
477
+ ### VSCode Tasks
478
+
479
+ This project includes VSCode tasks for common development operations:
480
+
481
+ - **Setup Dev Environment**: Create venv and install dependencies
482
+ - **Format Code**: Run Black formatter
483
+ - **Lint Code**: Run Flake8 linter
484
+ - **Sort Imports**: Run isort
485
+ - **Type Check**: Run MyPy type checker
486
+ - **Run Tests**: Execute pytest with coverage
487
+ - **Check All**: Run all quality checks in sequence
488
+
489
+ Access these via `Ctrl+Shift+P` → "Tasks: Run Task"
490
+
491
+ ### Code Quality Commands
492
+
493
+ ```bash
494
+ # Format code
495
+ black .
496
+
497
+ # Sort imports
498
+ isort .
499
+
500
+ # Lint code
501
+ flake8 .
502
+
503
+ # Type check
504
+ mypy src/libdyson_rest
505
+
506
+ # Run tests
507
+ pytest
508
+
509
+ # Run all checks
510
+ black . && isort . && flake8 . && mypy src/libdyson_rest && pytest
511
+ ```
512
+
513
+ ### Testing
514
+
515
+ Run tests with coverage:
516
+
517
+ ```bash
518
+ # All tests
519
+ pytest
520
+
521
+ # Unit tests only
522
+ pytest tests/unit/
523
+
524
+ # Integration tests only
525
+ pytest tests/integration/
526
+
527
+ # With coverage report
528
+ pytest --cov=src/libdyson_rest --cov-report=html
529
+ ```
530
+
531
+ ## Project Structure
532
+
533
+ ```
534
+ libdyson-rest/
535
+ ├── src/
536
+ │ └── libdyson_rest/ # Main library code
537
+ │ ├── __init__.py
538
+ │ ├── client.py # Main API client
539
+ │ ├── exceptions.py # Custom exceptions
540
+ │ ├── models/ # Data models
541
+ │ └── utils/ # Utility functions
542
+ ├── tests/
543
+ │ ├── unit/ # Unit tests
544
+ │ └── integration/ # Integration tests
545
+ ├── .vscode/
546
+ │ └── tasks.json # VSCode tasks
547
+ ├── requirements.txt # Production dependencies
548
+ ├── requirements-dev.txt # Development dependencies
549
+ ├── pyproject.toml # Project configuration
550
+ ├── .flake8 # Flake8 configuration
551
+ ├── .pre-commit-config.yaml # Pre-commit hooks
552
+ └── README.md
553
+ ```
554
+
555
+ ## Configuration Files
556
+
557
+ - **pyproject.toml**: Main project configuration (Black, isort, pytest, mypy)
558
+ - **.flake8**: Flake8 linting configuration
559
+ - **.pre-commit-config.yaml**: Git pre-commit hooks
560
+ - **requirements.txt**: Production dependencies
561
+ - **requirements-dev.txt**: Development dependencies
562
+
563
+ ## Publishing to PyPI
564
+
565
+ This package is automatically published to PyPI using GitHub Actions. For detailed publishing instructions, see [PUBLISHING.md](PUBLISHING.md).
566
+
567
+ ### Quick Publishing
568
+
569
+ - **Test Release**: GitHub Actions → Run workflow → TestPyPI
570
+ - **Production Release**: Create a GitHub release with version tag (e.g., `v0.2.0`)
571
+ - **Local Build**: `python .github/scripts/publish_to_pypi.py --check`
572
+
573
+ The package is available on PyPI as `libdyson-rest`.
574
+
575
+ ## Contributing
576
+
577
+ 1. Fork the repository
578
+ 2. Create a feature branch: `git checkout -b feature-name`
579
+ 3. Make your changes following the coding standards
580
+ 4. Run all quality checks: ensure Black, Flake8, isort, MyPy, and tests pass
581
+ 5. Commit your changes: `git commit -am 'Add feature'`
582
+ 6. Push to the branch: `git push origin feature-name`
583
+ 7. Create a Pull Request
584
+
585
+ All PRs must pass the full test suite and code quality checks.
586
+
587
+ ## License
588
+
589
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
590
+
591
+ ## Security
592
+
593
+ - No hardcoded credentials or sensitive data
594
+ - Use environment variables for configuration
595
+ - All user inputs are validated
596
+ - API responses are sanitized
597
+
598
+ ## Roadmap
599
+
600
+ - [ ] Complete API endpoint coverage
601
+ - [ ] Asynchronous client support
602
+ - [ ] WebSocket real-time updates
603
+ - [ ] Command-line interface
604
+ - [ ] Docker container support