pycoway 1.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.
pycoway-1.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 RobertD502
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.
pycoway-1.3.0/PKG-INFO ADDED
@@ -0,0 +1,282 @@
1
+ Metadata-Version: 2.4
2
+ Name: pycoway
3
+ Version: 1.3.0
4
+ Summary: An asynchronous Python library for Coway IoCare Air Purifiers
5
+ Author: Antonio112009
6
+ Maintainer: Antonio112009
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/antonio112009/pycoway
9
+ Project-URL: Bug Reports, https://github.com/antonio112009/pycoway/issues
10
+ Project-URL: Source, https://github.com/antonio112009/pycoway/
11
+ Project-URL: Upstream, https://github.com/RobertD502/cowayaio
12
+ Keywords: coway,iocare,iocare api,coway api,airmega
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: OS Independent
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: aiohttp>=3.9.0
24
+ Requires-Dist: beautifulsoup4>=4.12.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
28
+ Requires-Dist: aioresponses>=0.7; extra == "dev"
29
+ Requires-Dist: ruff; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # pycoway
33
+
34
+ [![CI](https://github.com/Antonio112009/pycoway/actions/workflows/ci.yml/badge.svg)](https://github.com/Antonio112009/pycoway/actions/workflows/ci.yml)
35
+ [![PyPI](https://img.shields.io/pypi/v/pycoway?color=blue&label=pypi)](https://pypi.org/project/pycoway/)
36
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
37
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
38
+ [![Version](https://img.shields.io/github/v/release/Antonio112009/pycoway?display_name=tag&sort=semver&color=orange&label=version)](https://github.com/Antonio112009/pycoway/releases/latest)
39
+
40
+ `pycoway` is an asynchronous Python client for the [Coway IoCare](https://iocare.com/) API. It is designed for AIRMEGA air purifiers and exposes both state retrieval and device control through a typed, `asyncio`-friendly interface.
41
+
42
+ > Based on [RobertD502/cowayaio](https://github.com/RobertD502/cowayaio) with active maintenance, typed models, tests, CI, and automated releases.
43
+
44
+ ## Features
45
+
46
+ - Async API built on [aiohttp](https://docs.aiohttp.org/)
47
+ - Typed dataclass models for purifier state
48
+ - Device control: power, fan speed, light, timers, modes, button lock, and more
49
+ - Air-quality readings: PM2.5, PM10, CO2, VOC, AQI
50
+ - Filter health monitoring: pre-filter, MAX2, and odor filter
51
+ - Automatic token and session management
52
+ - Full test coverage with GitHub Actions CI
53
+ - Automated semantic version bumping, GitHub releases, and PyPI publishing
54
+
55
+ ## Requirements
56
+
57
+ - Python 3.11 or newer
58
+ - A Coway IoCare account with at least one registered purifier
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install pycoway
64
+ ```
65
+
66
+ For local development:
67
+
68
+ ```bash
69
+ git clone https://github.com/Antonio112009/pycoway.git
70
+ cd pycoway
71
+ pip install -e ".[dev]"
72
+ ```
73
+
74
+ ## Quick Start
75
+
76
+ ```python
77
+ import asyncio
78
+
79
+ from pycoway import CowayClient
80
+
81
+
82
+ async def main() -> None:
83
+ async with CowayClient("email@example.com", "password") as client:
84
+ await client.login()
85
+ data = await client.async_get_purifiers_data()
86
+
87
+ for device_id, purifier in data.purifiers.items():
88
+ print(f"{purifier.device_attr.name} ({device_id})")
89
+ print(f" Power: {'On' if purifier.is_on else 'Off'}")
90
+ print(f" Fan Speed: {purifier.fan_speed}")
91
+ print(f" PM2.5: {purifier.particulate_matter_2_5}")
92
+ print(f" AQI: {purifier.air_quality_index}")
93
+
94
+
95
+ asyncio.run(main())
96
+ ```
97
+
98
+ ## Device Control
99
+
100
+ Every control method accepts the `device_attr` from a `CowayPurifier` instance:
101
+
102
+ ```python
103
+ import asyncio
104
+
105
+ from pycoway import CowayClient, LightMode
106
+
107
+
108
+ async def control_first_purifier() -> None:
109
+ async with CowayClient("email@example.com", "password") as client:
110
+ await client.login()
111
+ data = await client.async_get_purifiers_data()
112
+
113
+ purifier = next(iter(data.purifiers.values()))
114
+ attr = purifier.device_attr
115
+
116
+ await client.async_set_power(attr, is_on=True)
117
+ await client.async_set_auto_mode(attr)
118
+ await client.async_set_fan_speed(attr, speed="2")
119
+ await client.async_set_light(attr, light_on=True)
120
+ await client.async_set_light_mode(attr, LightMode.AQI_OFF)
121
+ await client.async_set_timer(attr, time="120")
122
+
123
+
124
+ asyncio.run(control_first_purifier())
125
+ ```
126
+
127
+ ### Available Control Methods
128
+
129
+ | Method | Parameters | Description |
130
+ |---|---|---|
131
+ | `async_set_power()` | `is_on: bool` | Turn purifier on or off |
132
+ | `async_set_auto_mode()` | — | Switch to auto mode |
133
+ | `async_set_night_mode()` | — | Switch to night mode |
134
+ | `async_set_eco_mode()` | — | Switch to eco mode (AP-1512HHS only) |
135
+ | `async_set_rapid_mode()` | — | Switch to rapid mode (250s only) |
136
+ | `async_set_fan_speed()` | `speed: str` | Set fan speed: `"1"`, `"2"`, or `"3"` |
137
+ | `async_set_light()` | `light_on: bool` | Toggle light on/off (not for 250s) |
138
+ | `async_set_light_mode()` | `light_mode: LightMode` | Set light mode for advanced models |
139
+ | `async_set_timer()` | `time: str` | Off timer in minutes: `"0"`, `"60"`, `"120"`, `"240"`, `"480"` |
140
+ | `async_set_smart_mode_sensitivity()` | `sensitivity: str` | `"1"` sensitive, `"2"` moderate, `"3"` insensitive |
141
+ | `async_set_button_lock()` | `value: str` | `"1"` lock, `"0"` unlock |
142
+ | `async_change_prefilter_setting()` | `value: int` | Wash frequency: `2`, `3`, or `4` weeks |
143
+
144
+ ## Data Model
145
+
146
+ `async_get_purifiers_data()` returns a `PurifierData` dataclass containing a `purifiers` dictionary keyed by device ID.
147
+
148
+ Each `CowayPurifier` includes:
149
+
150
+ ### Device Identity
151
+
152
+ | Field | Type | Description |
153
+ |---|---|---|
154
+ | `device_attr` | `DeviceAttributes` | Device ID, model, name, place ID |
155
+ | `mcu_version` | `str \| None` | Firmware version |
156
+ | `network_status` | `bool \| None` | Network connectivity |
157
+
158
+ ### Control State
159
+
160
+ | Field | Type | Description |
161
+ |---|---|---|
162
+ | `is_on` | `bool \| None` | Power state |
163
+ | `auto_mode` | `bool \| None` | Auto mode |
164
+ | `auto_eco_mode` | `bool \| None` | Auto eco mode |
165
+ | `eco_mode` | `bool \| None` | Eco mode |
166
+ | `night_mode` | `bool \| None` | Night mode |
167
+ | `rapid_mode` | `bool \| None` | Rapid mode |
168
+ | `fan_speed` | `int \| None` | Fan speed level |
169
+ | `light_on` | `bool \| None` | Light state |
170
+ | `light_mode` | `int \| None` | Device-specific light mode |
171
+ | `button_lock` | `int \| None` | Button lock state |
172
+ | `smart_mode_sensitivity` | `int \| None` | Smart mode sensitivity level |
173
+ | `timer` | `str \| None` | Configured off timer |
174
+ | `timer_remaining` | `int \| None` | Remaining timer (minutes) |
175
+
176
+ ### Air Quality
177
+
178
+ | Field | Type | Description |
179
+ |---|---|---|
180
+ | `particulate_matter_2_5` | `int \| None` | PM2.5 (μg/m³) |
181
+ | `particulate_matter_10` | `int \| None` | PM10 (μg/m³) |
182
+ | `carbon_dioxide` | `int \| None` | CO₂ (ppm) |
183
+ | `volatile_organic_compounds` | `int \| None` | VOC level |
184
+ | `air_quality_index` | `int \| None` | AQI value |
185
+ | `aq_grade` | `int \| None` | Air quality grade |
186
+ | `lux_sensor` | `int \| None` | Ambient light sensor |
187
+
188
+ ### Filter Health
189
+
190
+ | Field | Type | Description |
191
+ |---|---|---|
192
+ | `pre_filter_pct` | `int \| None` | Pre-filter remaining (%) |
193
+ | `pre_filter_change_frequency` | `int \| None` | Wash frequency (weeks) |
194
+ | `max2_pct` | `int \| None` | MAX2 filter remaining (%) |
195
+ | `odor_filter_pct` | `int \| None` | Odor filter remaining (%) |
196
+
197
+ For the complete schema, see [`src/pycoway/devices/models.py`](src/pycoway/devices/models.py).
198
+
199
+ ## Exceptions
200
+
201
+ All exceptions inherit from `CowayError`:
202
+
203
+ ```python
204
+ from pycoway import AuthError, CowayError, PasswordExpired
205
+ ```
206
+
207
+ | Exception | Description |
208
+ |---|---|
209
+ | `CowayError` | Base exception for all library errors |
210
+ | `AuthError` | Authentication failed |
211
+ | `PasswordExpired` | Coway requires a password change |
212
+ | `ServerMaintenance` | Coway API is under maintenance |
213
+ | `RateLimited` | Coway temporarily blocked the account |
214
+ | `NoPlaces` | No places configured in the IoCare account |
215
+ | `NoPurifiers` | No air purifiers found |
216
+
217
+ ## Migrating from cowayaio
218
+
219
+ If you're switching from the original `cowayaio` package:
220
+
221
+ ```bash
222
+ pip uninstall cowayaio
223
+ pip install pycoway
224
+ ```
225
+
226
+ Update your imports:
227
+
228
+ ```python
229
+ # Before
230
+ from cowayaio import CowayClient
231
+
232
+ # After
233
+ from pycoway import CowayClient
234
+ ```
235
+
236
+ ## Development
237
+
238
+ ```bash
239
+ git clone https://github.com/Antonio112009/pycoway.git
240
+ cd pycoway
241
+ pip install -e ".[dev]"
242
+ pytest
243
+ ruff check .
244
+ ruff format --check .
245
+ ```
246
+
247
+ Feature work should branch from `development`, and pull requests merge into `development` first. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full workflow.
248
+
249
+ ## Release Flow
250
+
251
+ - PRs from `development` to `main` trigger the release workflow when merged
252
+ - The workflow bumps `src/pycoway/__version__.py`
253
+ - PRs to `main` must have exactly one version label: `patch`, `minor`, or `major`
254
+ - A git tag and GitHub release are created automatically
255
+ - The package is published to PyPI automatically
256
+
257
+ ## Project Structure
258
+
259
+ ```text
260
+ src/pycoway/
261
+ ├── __init__.py # Public API exports
262
+ ├── __version__.py # Version string
263
+ ├── client.py # Public CowayClient entry point
264
+ ├── constants.py # API constants
265
+ ├── enums.py # Enumerations
266
+ ├── exceptions.py # Public exception hierarchy
267
+ ├── py.typed # PEP 561 marker
268
+ ├── account/
269
+ │ ├── auth.py # Authentication (login, token refresh)
270
+ │ └── maintenance.py # Server maintenance checks
271
+ ├── devices/
272
+ │ ├── control.py # Purifier control commands
273
+ │ ├── data.py # Data fetching (purifiers, filters, air quality)
274
+ │ ├── models.py # Dataclasses (CowayPurifier, PurifierData)
275
+ │ └── parser.py # HTML/JSON response parsing
276
+ └── transport/
277
+ └── http.py # HTTP base client with session management
278
+ ```
279
+
280
+ ## License
281
+
282
+ [MIT](LICENSE), originally authored by [RobertD502](https://github.com/RobertD502)
@@ -0,0 +1,251 @@
1
+ # pycoway
2
+
3
+ [![CI](https://github.com/Antonio112009/pycoway/actions/workflows/ci.yml/badge.svg)](https://github.com/Antonio112009/pycoway/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/pycoway?color=blue&label=pypi)](https://pypi.org/project/pycoway/)
5
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
7
+ [![Version](https://img.shields.io/github/v/release/Antonio112009/pycoway?display_name=tag&sort=semver&color=orange&label=version)](https://github.com/Antonio112009/pycoway/releases/latest)
8
+
9
+ `pycoway` is an asynchronous Python client for the [Coway IoCare](https://iocare.com/) API. It is designed for AIRMEGA air purifiers and exposes both state retrieval and device control through a typed, `asyncio`-friendly interface.
10
+
11
+ > Based on [RobertD502/cowayaio](https://github.com/RobertD502/cowayaio) with active maintenance, typed models, tests, CI, and automated releases.
12
+
13
+ ## Features
14
+
15
+ - Async API built on [aiohttp](https://docs.aiohttp.org/)
16
+ - Typed dataclass models for purifier state
17
+ - Device control: power, fan speed, light, timers, modes, button lock, and more
18
+ - Air-quality readings: PM2.5, PM10, CO2, VOC, AQI
19
+ - Filter health monitoring: pre-filter, MAX2, and odor filter
20
+ - Automatic token and session management
21
+ - Full test coverage with GitHub Actions CI
22
+ - Automated semantic version bumping, GitHub releases, and PyPI publishing
23
+
24
+ ## Requirements
25
+
26
+ - Python 3.11 or newer
27
+ - A Coway IoCare account with at least one registered purifier
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install pycoway
33
+ ```
34
+
35
+ For local development:
36
+
37
+ ```bash
38
+ git clone https://github.com/Antonio112009/pycoway.git
39
+ cd pycoway
40
+ pip install -e ".[dev]"
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ import asyncio
47
+
48
+ from pycoway import CowayClient
49
+
50
+
51
+ async def main() -> None:
52
+ async with CowayClient("email@example.com", "password") as client:
53
+ await client.login()
54
+ data = await client.async_get_purifiers_data()
55
+
56
+ for device_id, purifier in data.purifiers.items():
57
+ print(f"{purifier.device_attr.name} ({device_id})")
58
+ print(f" Power: {'On' if purifier.is_on else 'Off'}")
59
+ print(f" Fan Speed: {purifier.fan_speed}")
60
+ print(f" PM2.5: {purifier.particulate_matter_2_5}")
61
+ print(f" AQI: {purifier.air_quality_index}")
62
+
63
+
64
+ asyncio.run(main())
65
+ ```
66
+
67
+ ## Device Control
68
+
69
+ Every control method accepts the `device_attr` from a `CowayPurifier` instance:
70
+
71
+ ```python
72
+ import asyncio
73
+
74
+ from pycoway import CowayClient, LightMode
75
+
76
+
77
+ async def control_first_purifier() -> None:
78
+ async with CowayClient("email@example.com", "password") as client:
79
+ await client.login()
80
+ data = await client.async_get_purifiers_data()
81
+
82
+ purifier = next(iter(data.purifiers.values()))
83
+ attr = purifier.device_attr
84
+
85
+ await client.async_set_power(attr, is_on=True)
86
+ await client.async_set_auto_mode(attr)
87
+ await client.async_set_fan_speed(attr, speed="2")
88
+ await client.async_set_light(attr, light_on=True)
89
+ await client.async_set_light_mode(attr, LightMode.AQI_OFF)
90
+ await client.async_set_timer(attr, time="120")
91
+
92
+
93
+ asyncio.run(control_first_purifier())
94
+ ```
95
+
96
+ ### Available Control Methods
97
+
98
+ | Method | Parameters | Description |
99
+ |---|---|---|
100
+ | `async_set_power()` | `is_on: bool` | Turn purifier on or off |
101
+ | `async_set_auto_mode()` | — | Switch to auto mode |
102
+ | `async_set_night_mode()` | — | Switch to night mode |
103
+ | `async_set_eco_mode()` | — | Switch to eco mode (AP-1512HHS only) |
104
+ | `async_set_rapid_mode()` | — | Switch to rapid mode (250s only) |
105
+ | `async_set_fan_speed()` | `speed: str` | Set fan speed: `"1"`, `"2"`, or `"3"` |
106
+ | `async_set_light()` | `light_on: bool` | Toggle light on/off (not for 250s) |
107
+ | `async_set_light_mode()` | `light_mode: LightMode` | Set light mode for advanced models |
108
+ | `async_set_timer()` | `time: str` | Off timer in minutes: `"0"`, `"60"`, `"120"`, `"240"`, `"480"` |
109
+ | `async_set_smart_mode_sensitivity()` | `sensitivity: str` | `"1"` sensitive, `"2"` moderate, `"3"` insensitive |
110
+ | `async_set_button_lock()` | `value: str` | `"1"` lock, `"0"` unlock |
111
+ | `async_change_prefilter_setting()` | `value: int` | Wash frequency: `2`, `3`, or `4` weeks |
112
+
113
+ ## Data Model
114
+
115
+ `async_get_purifiers_data()` returns a `PurifierData` dataclass containing a `purifiers` dictionary keyed by device ID.
116
+
117
+ Each `CowayPurifier` includes:
118
+
119
+ ### Device Identity
120
+
121
+ | Field | Type | Description |
122
+ |---|---|---|
123
+ | `device_attr` | `DeviceAttributes` | Device ID, model, name, place ID |
124
+ | `mcu_version` | `str \| None` | Firmware version |
125
+ | `network_status` | `bool \| None` | Network connectivity |
126
+
127
+ ### Control State
128
+
129
+ | Field | Type | Description |
130
+ |---|---|---|
131
+ | `is_on` | `bool \| None` | Power state |
132
+ | `auto_mode` | `bool \| None` | Auto mode |
133
+ | `auto_eco_mode` | `bool \| None` | Auto eco mode |
134
+ | `eco_mode` | `bool \| None` | Eco mode |
135
+ | `night_mode` | `bool \| None` | Night mode |
136
+ | `rapid_mode` | `bool \| None` | Rapid mode |
137
+ | `fan_speed` | `int \| None` | Fan speed level |
138
+ | `light_on` | `bool \| None` | Light state |
139
+ | `light_mode` | `int \| None` | Device-specific light mode |
140
+ | `button_lock` | `int \| None` | Button lock state |
141
+ | `smart_mode_sensitivity` | `int \| None` | Smart mode sensitivity level |
142
+ | `timer` | `str \| None` | Configured off timer |
143
+ | `timer_remaining` | `int \| None` | Remaining timer (minutes) |
144
+
145
+ ### Air Quality
146
+
147
+ | Field | Type | Description |
148
+ |---|---|---|
149
+ | `particulate_matter_2_5` | `int \| None` | PM2.5 (μg/m³) |
150
+ | `particulate_matter_10` | `int \| None` | PM10 (μg/m³) |
151
+ | `carbon_dioxide` | `int \| None` | CO₂ (ppm) |
152
+ | `volatile_organic_compounds` | `int \| None` | VOC level |
153
+ | `air_quality_index` | `int \| None` | AQI value |
154
+ | `aq_grade` | `int \| None` | Air quality grade |
155
+ | `lux_sensor` | `int \| None` | Ambient light sensor |
156
+
157
+ ### Filter Health
158
+
159
+ | Field | Type | Description |
160
+ |---|---|---|
161
+ | `pre_filter_pct` | `int \| None` | Pre-filter remaining (%) |
162
+ | `pre_filter_change_frequency` | `int \| None` | Wash frequency (weeks) |
163
+ | `max2_pct` | `int \| None` | MAX2 filter remaining (%) |
164
+ | `odor_filter_pct` | `int \| None` | Odor filter remaining (%) |
165
+
166
+ For the complete schema, see [`src/pycoway/devices/models.py`](src/pycoway/devices/models.py).
167
+
168
+ ## Exceptions
169
+
170
+ All exceptions inherit from `CowayError`:
171
+
172
+ ```python
173
+ from pycoway import AuthError, CowayError, PasswordExpired
174
+ ```
175
+
176
+ | Exception | Description |
177
+ |---|---|
178
+ | `CowayError` | Base exception for all library errors |
179
+ | `AuthError` | Authentication failed |
180
+ | `PasswordExpired` | Coway requires a password change |
181
+ | `ServerMaintenance` | Coway API is under maintenance |
182
+ | `RateLimited` | Coway temporarily blocked the account |
183
+ | `NoPlaces` | No places configured in the IoCare account |
184
+ | `NoPurifiers` | No air purifiers found |
185
+
186
+ ## Migrating from cowayaio
187
+
188
+ If you're switching from the original `cowayaio` package:
189
+
190
+ ```bash
191
+ pip uninstall cowayaio
192
+ pip install pycoway
193
+ ```
194
+
195
+ Update your imports:
196
+
197
+ ```python
198
+ # Before
199
+ from cowayaio import CowayClient
200
+
201
+ # After
202
+ from pycoway import CowayClient
203
+ ```
204
+
205
+ ## Development
206
+
207
+ ```bash
208
+ git clone https://github.com/Antonio112009/pycoway.git
209
+ cd pycoway
210
+ pip install -e ".[dev]"
211
+ pytest
212
+ ruff check .
213
+ ruff format --check .
214
+ ```
215
+
216
+ Feature work should branch from `development`, and pull requests merge into `development` first. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full workflow.
217
+
218
+ ## Release Flow
219
+
220
+ - PRs from `development` to `main` trigger the release workflow when merged
221
+ - The workflow bumps `src/pycoway/__version__.py`
222
+ - PRs to `main` must have exactly one version label: `patch`, `minor`, or `major`
223
+ - A git tag and GitHub release are created automatically
224
+ - The package is published to PyPI automatically
225
+
226
+ ## Project Structure
227
+
228
+ ```text
229
+ src/pycoway/
230
+ ├── __init__.py # Public API exports
231
+ ├── __version__.py # Version string
232
+ ├── client.py # Public CowayClient entry point
233
+ ├── constants.py # API constants
234
+ ├── enums.py # Enumerations
235
+ ├── exceptions.py # Public exception hierarchy
236
+ ├── py.typed # PEP 561 marker
237
+ ├── account/
238
+ │ ├── auth.py # Authentication (login, token refresh)
239
+ │ └── maintenance.py # Server maintenance checks
240
+ ├── devices/
241
+ │ ├── control.py # Purifier control commands
242
+ │ ├── data.py # Data fetching (purifiers, filters, air quality)
243
+ │ ├── models.py # Dataclasses (CowayPurifier, PurifierData)
244
+ │ └── parser.py # HTML/JSON response parsing
245
+ └── transport/
246
+ └── http.py # HTTP base client with session management
247
+ ```
248
+
249
+ ## License
250
+
251
+ [MIT](LICENSE), originally authored by [RobertD502](https://github.com/RobertD502)
@@ -0,0 +1,78 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pycoway"
7
+ dynamic = ["version"]
8
+ description = "An asynchronous Python library for Coway IoCare Air Purifiers"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.11"
12
+ authors = [
13
+ { name = "Antonio112009" },
14
+ ]
15
+ maintainers = [
16
+ { name = "Antonio112009" },
17
+ ]
18
+ keywords = ["coway", "iocare", "iocare api", "coway api", "airmega"]
19
+ classifiers = [
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ "License :: OSI Approved :: MIT License",
26
+ "Operating System :: OS Independent",
27
+ ]
28
+ dependencies = [
29
+ "aiohttp>=3.9.0",
30
+ "beautifulsoup4>=4.12.0",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ dev = [
35
+ "pytest>=8.0",
36
+ "pytest-asyncio>=0.24",
37
+ "aioresponses>=0.7",
38
+ "ruff",
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/antonio112009/pycoway"
43
+ "Bug Reports" = "https://github.com/antonio112009/pycoway/issues"
44
+ Source = "https://github.com/antonio112009/pycoway/"
45
+ Upstream = "https://github.com/RobertD502/cowayaio"
46
+
47
+ [tool.setuptools.dynamic]
48
+ version = { attr = "pycoway.__version__.__version__" }
49
+
50
+ [tool.setuptools]
51
+ package-dir = {"" = "src"}
52
+
53
+ [tool.setuptools.packages.find]
54
+ where = ["src"]
55
+ include = ["pycoway*"]
56
+
57
+ [tool.setuptools.package-data]
58
+ pycoway = ["py.typed"]
59
+
60
+ [tool.ruff]
61
+ target-version = "py311"
62
+ line-length = 100
63
+
64
+ [tool.ruff.lint]
65
+ select = [
66
+ "E", # pycodestyle errors
67
+ "W", # pycodestyle warnings
68
+ "F", # pyflakes
69
+ "I", # isort
70
+ "UP", # pyupgrade
71
+ "B", # flake8-bugbear
72
+ "SIM", # flake8-simplify
73
+ "TCH", # flake8-type-checking
74
+ ]
75
+
76
+ [tool.pytest.ini_options]
77
+ asyncio_mode = "auto"
78
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,31 @@
1
+ """Init file for pycoway."""
2
+
3
+ from .__version__ import __version__
4
+ from .client import CowayClient
5
+ from .constants import LightMode
6
+ from .devices.models import CowayPurifier, DeviceAttributes, PurifierData
7
+ from .exceptions import (
8
+ AuthError,
9
+ CowayError,
10
+ NoPlaces,
11
+ NoPurifiers,
12
+ PasswordExpired,
13
+ RateLimited,
14
+ ServerMaintenance,
15
+ )
16
+
17
+ __all__ = [
18
+ "AuthError",
19
+ "CowayClient",
20
+ "CowayError",
21
+ "CowayPurifier",
22
+ "DeviceAttributes",
23
+ "LightMode",
24
+ "NoPlaces",
25
+ "NoPurifiers",
26
+ "PasswordExpired",
27
+ "PurifierData",
28
+ "RateLimited",
29
+ "ServerMaintenance",
30
+ "__version__",
31
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "1.3.0"
@@ -0,0 +1 @@
1
+ """Account and authentication functionality for pycoway."""