pycaldera 0.1.dev0__tar.gz → 0.1.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 (33) hide show
  1. pycaldera-0.1.2/CHANGELOG.rst +58 -0
  2. pycaldera-0.1.2/PKG-INFO +287 -0
  3. pycaldera-0.1.2/README.md +215 -0
  4. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pycaldera/__init__.py +10 -2
  5. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pycaldera/__meta__.py +2 -1
  6. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pycaldera/async_client.py +224 -43
  7. pycaldera-0.1.2/pycaldera/client.py +274 -0
  8. pycaldera-0.1.2/pycaldera/const.py +38 -0
  9. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pycaldera/models.py +80 -12
  10. pycaldera-0.1.2/pycaldera.egg-info/PKG-INFO +287 -0
  11. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pycaldera.egg-info/SOURCES.txt +5 -1
  12. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pycaldera.egg-info/requires.txt +6 -16
  13. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pyproject.toml +1 -1
  14. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/requirements-docs.txt +1 -1
  15. pycaldera-0.1.2/requirements-test.txt +4 -0
  16. pycaldera-0.1.2/requirements.txt +2 -0
  17. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/tests/test_async_client.py +181 -5
  18. pycaldera-0.1.2/tests/test_client.py +199 -0
  19. pycaldera-0.1.2/tests/test_models.py +100 -0
  20. pycaldera-0.1.dev0/CHANGELOG.rst +0 -18
  21. pycaldera-0.1.dev0/PKG-INFO +0 -198
  22. pycaldera-0.1.dev0/README.md +0 -117
  23. pycaldera-0.1.dev0/pycaldera.egg-info/PKG-INFO +0 -198
  24. pycaldera-0.1.dev0/requirements-test.txt +0 -3
  25. pycaldera-0.1.dev0/requirements.txt +0 -13
  26. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/LICENSE +0 -0
  27. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/MANIFEST.in +0 -0
  28. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pycaldera/exceptions.py +0 -0
  29. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pycaldera.egg-info/dependency_links.txt +0 -0
  30. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/pycaldera.egg-info/top_level.txt +0 -0
  31. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/requirements-dev.txt +0 -0
  32. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/setup.cfg +0 -0
  33. {pycaldera-0.1.dev0 → pycaldera-0.1.2}/setup.py +0 -0
@@ -0,0 +1,58 @@
1
+ Changelog
2
+ =========
3
+
4
+ All notable changes to pycaldera will be documented here.
5
+
6
+ The format is based on `Keep a Changelog`_, and this project adheres to `Semantic Versioning`_.
7
+
8
+ .. _Keep a Changelog: https://keepachangelog.com/en/1.0.0/
9
+ .. _Semantic Versioning: https://semver.org/spec/v2.0.0.html
10
+
11
+ Categories for changes are: Added, Changed, Deprecated, Removed, Fixed, Security.
12
+
13
+
14
+ Version `0.1.1 <https://github.com/mwatson2/pycaldera/tree/v0.1.1>`__
15
+ ---------------------------------------------------------------------
16
+
17
+ Release date: 2026-04-08.
18
+
19
+ Added
20
+ ~~~~~
21
+ - ``SpaResponseDato.water_temperature`` and ``SpaResponseDato.set_temperature``
22
+ convenience properties that read the current and target temperatures from
23
+ the embedded ``isConnectedData.liveSettings.rows[0]`` payload. Both return
24
+ ``None`` if no live-settings row is available.
25
+ - Unit tests for the synchronous ``CalderaClient`` covering ``get_spa_status``,
26
+ ``get_live_settings``, ``set_pump``, ``set_temp_lock``, ``set_spa_lock`` and
27
+ the ``close()`` paths.
28
+
29
+ Fixed
30
+ ~~~~~
31
+ - README quickstart examples referenced ``status.ctrl_head_water_temperature``,
32
+ which is not a field on the spa-status response. They now use the new
33
+ ``status.water_temperature`` property and actually work.
34
+ - ``requirements.txt`` no longer pins development tooling (``black``,
35
+ ``isort``, ``mypy``, ``pylint``, ``pytest`` and friends) as runtime
36
+ dependencies. Plain ``pip install pycaldera`` now installs only ``aiohttp``
37
+ and ``pydantic``.
38
+ - ``requirements-test.txt`` now contains the async test plugins
39
+ (``pytest-asyncio``, ``pytest-aiohttp``) that the test suite actually
40
+ requires, so the ``[test]`` extra is self-sufficient.
41
+
42
+ Added (release tooling)
43
+ ~~~~~~~~~~~~~~~~~~~~~~~
44
+ - GitHub Actions ``release`` workflow that triggers on ``v*`` tags, runs the
45
+ test suite, builds sdist + wheel and publishes to PyPI via
46
+ `trusted publishing <https://docs.pypi.org/trusted-publishers/>`__
47
+ (no API tokens stored in the repository).
48
+
49
+
50
+ Version `0.1.0 <https://github.com/mwatson2/pycaldera/tree/v0.1.0>`__
51
+ ---------------------------------------------------------------------
52
+
53
+ Release date: 2026-03-13.
54
+
55
+ Initial release. Async and synchronous clients for the Caldera Connected Spa
56
+ cloud API, with support for authentication, status / live-settings retrieval,
57
+ temperature / pump / light / lock control, and an acknowledgment-polling
58
+ helper for temperature changes.
@@ -0,0 +1,287 @@
1
+ Metadata-Version: 2.4
2
+ Name: pycaldera
3
+ Version: 0.1.2
4
+ Summary: Unofficial Python client for Caldera Spa API
5
+ Home-page: https://github.com/mwatson2/pycaldera
6
+ Author: Mark Watson
7
+ Author-email: markwatson@cantab.net
8
+ License: MIT
9
+ Classifier: Natural Language :: English
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: aiohttp>=3.8.0
21
+ Requires-Dist: pydantic>=2.0.0
22
+ Provides-Extra: docs
23
+ Requires-Dist: myst-parser; extra == "docs"
24
+ Requires-Dist: pypandoc>=1.15; extra == "docs"
25
+ Requires-Dist: readthedocs-sphinx-search; python_version >= "3.6" and extra == "docs"
26
+ Requires-Dist: sphinx<6,>=3.5.4; extra == "docs"
27
+ Requires-Dist: sphinx-autobuild; extra == "docs"
28
+ Requires-Dist: sphinx_book_theme; extra == "docs"
29
+ Requires-Dist: watchdog<1.0.0; python_version < "3.6" and extra == "docs"
30
+ Provides-Extra: test
31
+ Requires-Dist: pytest>=7.0.0; extra == "test"
32
+ Requires-Dist: pytest-aiohttp>=1.0.0; extra == "test"
33
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
34
+ Requires-Dist: pytest-cov>=4.0.0; extra == "test"
35
+ Provides-Extra: dev
36
+ Requires-Dist: black>=23.0.0; extra == "dev"
37
+ Requires-Dist: isort>=5.12.0; extra == "dev"
38
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
39
+ Requires-Dist: pylint>=2.17.0; extra == "dev"
40
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
41
+ Requires-Dist: pytest-aiohttp>=1.0.0; extra == "dev"
42
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
43
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
44
+ Provides-Extra: all
45
+ Requires-Dist: black>=23.0.0; extra == "all"
46
+ Requires-Dist: isort>=5.12.0; extra == "all"
47
+ Requires-Dist: mypy>=1.0.0; extra == "all"
48
+ Requires-Dist: myst-parser; extra == "all"
49
+ Requires-Dist: pylint>=2.17.0; extra == "all"
50
+ Requires-Dist: pypandoc>=1.15; extra == "all"
51
+ Requires-Dist: pytest-aiohttp>=1.0.0; extra == "all"
52
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
53
+ Requires-Dist: pytest-cov>=4.0.0; extra == "all"
54
+ Requires-Dist: pytest>=7.0.0; extra == "all"
55
+ Requires-Dist: readthedocs-sphinx-search; python_version >= "3.6" and extra == "all"
56
+ Requires-Dist: sphinx-autobuild; extra == "all"
57
+ Requires-Dist: sphinx<6,>=3.5.4; extra == "all"
58
+ Requires-Dist: sphinx_book_theme; extra == "all"
59
+ Requires-Dist: watchdog<1.0.0; python_version < "3.6" and extra == "all"
60
+ Dynamic: author
61
+ Dynamic: author-email
62
+ Dynamic: classifier
63
+ Dynamic: description
64
+ Dynamic: description-content-type
65
+ Dynamic: home-page
66
+ Dynamic: license
67
+ Dynamic: license-file
68
+ Dynamic: provides-extra
69
+ Dynamic: requires-dist
70
+ Dynamic: requires-python
71
+ Dynamic: summary
72
+
73
+ # pycaldera
74
+
75
+ Python client library for controlling Caldera spas via their cloud API.
76
+
77
+ ## Installation
78
+
79
+ ```bash
80
+ pip install pycaldera
81
+ ```
82
+
83
+ ## Usage
84
+
85
+ ### Asynchronous API
86
+
87
+ ```python
88
+ import asyncio
89
+ from pycaldera import AsyncCalderaClient, PUMP_OFF, PUMP_LOW, PUMP_HIGH
90
+
91
+
92
+ async def main():
93
+ async with AsyncCalderaClient("email@example.com", "password") as spa:
94
+ # Get current spa status
95
+ status = await spa.get_spa_status()
96
+ print(f"Current temperature: {status.water_temperature}°F")
97
+
98
+ # Get detailed live settings
99
+ settings = await spa.get_live_settings()
100
+ print(f"Target temperature: {settings.ctrl_head_set_temperature}°F")
101
+
102
+ # Control the spa
103
+ await spa.set_temperature(102) # Set temperature to 102°F
104
+ await spa.set_pump(1, PUMP_HIGH) # Set pump 1 to high speed
105
+ await spa.set_lights(True) # Turn on the lights
106
+
107
+
108
+ asyncio.run(main())
109
+ ```
110
+
111
+ ### Synchronous API
112
+
113
+ For simpler use cases, a synchronous wrapper is also available:
114
+
115
+ ```python
116
+ from pycaldera import CalderaClient, PUMP_OFF, PUMP_LOW, PUMP_HIGH
117
+
118
+ with CalderaClient("email@example.com", "password") as spa:
119
+ # Get current spa status
120
+ status = spa.get_spa_status()
121
+ print(f"Current temperature: {status.water_temperature}°F")
122
+
123
+ # Get detailed live settings
124
+ settings = spa.get_live_settings()
125
+ print(f"Target temperature: {settings.ctrl_head_set_temperature}°F")
126
+
127
+ # Control the spa
128
+ spa.set_temperature(102) # Set temperature to 102°F
129
+ spa.set_pump(1, PUMP_HIGH) # Set pump 1 to high speed
130
+ spa.set_lights(True) # Turn on the lights
131
+ ```
132
+
133
+ Both clients provide identical functionality, with the synchronous client simply wrapping the async one for convenience.
134
+
135
+ ## API Reference
136
+
137
+ ### AsyncCalderaClient
138
+
139
+ The main async client class for interacting with the spa. All operations must be performed within an async context manager:
140
+
141
+ ```python
142
+ async with AsyncCalderaClient(
143
+ email="email@example.com",
144
+ password="password",
145
+ timeout=10.0, # Optional: request timeout in seconds
146
+ debug=False, # Optional: enable debug logging
147
+ ) as spa:
148
+ # All spa operations must be inside this block
149
+ await spa.get_spa_status()
150
+ await spa.set_temperature(102)
151
+ # etc...
152
+ ```
153
+
154
+ ### CalderaClient
155
+
156
+ A synchronous wrapper around AsyncCalderaClient that provides the same functionality without requiring async/await:
157
+
158
+ ```python
159
+ with CalderaClient(
160
+ email="email@example.com",
161
+ password="password",
162
+ timeout=10.0, # Optional: request timeout in seconds
163
+ debug=False, # Optional: enable debug logging
164
+ ) as spa:
165
+ # All spa operations can be called synchronously
166
+ spa.get_spa_status()
167
+ spa.set_temperature(102)
168
+ # etc...
169
+ ```
170
+
171
+ ### Error Handling
172
+
173
+ All operations can raise these base exceptions:
174
+ - `AuthenticationError`: When authentication fails or token expires
175
+ - `ConnectionError`: When network connection fails or API is unreachable
176
+ - `SpaControlError`: When the API returns an error response
177
+
178
+ ### Temperature Control
179
+
180
+ ```python
181
+ async with spa as client:
182
+ # Set temperature (80-104°F or 26.5-40°C)
183
+ try:
184
+ # Basic temperature setting
185
+ await client.set_temperature(102) # Fahrenheit
186
+ await client.set_temperature(39, "C") # Celsius
187
+
188
+ # Wait for spa to acknowledge the temperature change
189
+ await client.set_temperature(102, wait_for_ack=True)
190
+
191
+ # Control polling behavior when waiting for acknowledgment
192
+ await client.set_temperature(
193
+ 102,
194
+ wait_for_ack=True,
195
+ polling_interval=5.0, # Check every 5 seconds
196
+ polling_timeout=120.0, # Time out after 2 minutes
197
+ )
198
+
199
+ # Manually wait for temperature acknowledgment
200
+ settings = await client.wait_for_temperature_ack(
201
+ expected_temp=102, # Expected temperature in Fahrenheit
202
+ interval=5.0, # Check every 5 seconds
203
+ timeout=120.0, # Time out after 2 minutes
204
+ )
205
+ except InvalidParameterError:
206
+ # Raised when temperature is outside valid range
207
+ # (80-104°F or 26.5-40°C)
208
+ pass
209
+ except SpaControlError:
210
+ # Raised when polling times out waiting for acknowledgment
211
+ pass
212
+ ```
213
+
214
+ ### Pump Control
215
+
216
+ ```python
217
+ async with spa as client:
218
+ try:
219
+ await client.set_pump(1, PUMP_HIGH) # Set pump 1 to high speed
220
+ await client.set_pump(2, PUMP_LOW) # Set pump 2 to low speed
221
+ await client.set_pump(3, PUMP_OFF) # Turn off pump 3
222
+ except InvalidParameterError:
223
+ # Raised when:
224
+ # - pump_number is not 1, 2, or 3
225
+ # - speed is not PUMP_OFF (0), PUMP_LOW (1), or PUMP_HIGH (2)
226
+ pass
227
+ ```
228
+
229
+ ### Light Control
230
+
231
+ ```python
232
+ async with spa as client:
233
+ try:
234
+ await client.set_lights(True) # Turn lights on
235
+ await client.set_lights(False) # Turn lights off
236
+ except SpaControlError:
237
+ # Raised when light control fails
238
+ pass
239
+ ```
240
+
241
+ ### Status & Settings
242
+
243
+ ```python
244
+ async with spa as client:
245
+ try:
246
+ # Get basic spa status
247
+ status = await client.get_spa_status()
248
+ print(f"Online: {status.status == 'ONLINE'}")
249
+
250
+ # Get detailed live settings
251
+ settings = await client.get_live_settings()
252
+ print(f"Target temp: {settings.ctrl_head_set_temperature}°F")
253
+ except ConnectionError:
254
+ # Raised when spa is offline or unreachable
255
+ pass
256
+ except SpaControlError:
257
+ # Raised when API returns invalid data
258
+ pass
259
+
260
+ ## Development
261
+
262
+ 1. Clone the repository
263
+ 2. Create a virtual environment:
264
+ ```bash
265
+ python -m venv venv
266
+ source venv/bin/activate # or `venv\Scripts\activate` on Windows
267
+ ```
268
+ 3. Install development dependencies:
269
+ ```bash
270
+ pip install -r requirements-dev.txt
271
+ ```
272
+ 4. Install pre-commit hooks:
273
+ ```bash
274
+ pre-commit install
275
+ ```
276
+
277
+ The pre-commit hooks will run automatically on git commit, checking:
278
+ - Code formatting (Black)
279
+ - Import sorting (isort)
280
+ - Type checking (mypy)
281
+ - Linting (pylint, ruff)
282
+ - YAML/TOML syntax
283
+ - Trailing whitespace and file endings
284
+
285
+ ## License
286
+
287
+ MIT License - see LICENSE file for details.
@@ -0,0 +1,215 @@
1
+ # pycaldera
2
+
3
+ Python client library for controlling Caldera spas via their cloud API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install pycaldera
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Asynchronous API
14
+
15
+ ```python
16
+ import asyncio
17
+ from pycaldera import AsyncCalderaClient, PUMP_OFF, PUMP_LOW, PUMP_HIGH
18
+
19
+
20
+ async def main():
21
+ async with AsyncCalderaClient("email@example.com", "password") as spa:
22
+ # Get current spa status
23
+ status = await spa.get_spa_status()
24
+ print(f"Current temperature: {status.water_temperature}°F")
25
+
26
+ # Get detailed live settings
27
+ settings = await spa.get_live_settings()
28
+ print(f"Target temperature: {settings.ctrl_head_set_temperature}°F")
29
+
30
+ # Control the spa
31
+ await spa.set_temperature(102) # Set temperature to 102°F
32
+ await spa.set_pump(1, PUMP_HIGH) # Set pump 1 to high speed
33
+ await spa.set_lights(True) # Turn on the lights
34
+
35
+
36
+ asyncio.run(main())
37
+ ```
38
+
39
+ ### Synchronous API
40
+
41
+ For simpler use cases, a synchronous wrapper is also available:
42
+
43
+ ```python
44
+ from pycaldera import CalderaClient, PUMP_OFF, PUMP_LOW, PUMP_HIGH
45
+
46
+ with CalderaClient("email@example.com", "password") as spa:
47
+ # Get current spa status
48
+ status = spa.get_spa_status()
49
+ print(f"Current temperature: {status.water_temperature}°F")
50
+
51
+ # Get detailed live settings
52
+ settings = spa.get_live_settings()
53
+ print(f"Target temperature: {settings.ctrl_head_set_temperature}°F")
54
+
55
+ # Control the spa
56
+ spa.set_temperature(102) # Set temperature to 102°F
57
+ spa.set_pump(1, PUMP_HIGH) # Set pump 1 to high speed
58
+ spa.set_lights(True) # Turn on the lights
59
+ ```
60
+
61
+ Both clients provide identical functionality, with the synchronous client simply wrapping the async one for convenience.
62
+
63
+ ## API Reference
64
+
65
+ ### AsyncCalderaClient
66
+
67
+ The main async client class for interacting with the spa. All operations must be performed within an async context manager:
68
+
69
+ ```python
70
+ async with AsyncCalderaClient(
71
+ email="email@example.com",
72
+ password="password",
73
+ timeout=10.0, # Optional: request timeout in seconds
74
+ debug=False, # Optional: enable debug logging
75
+ ) as spa:
76
+ # All spa operations must be inside this block
77
+ await spa.get_spa_status()
78
+ await spa.set_temperature(102)
79
+ # etc...
80
+ ```
81
+
82
+ ### CalderaClient
83
+
84
+ A synchronous wrapper around AsyncCalderaClient that provides the same functionality without requiring async/await:
85
+
86
+ ```python
87
+ with CalderaClient(
88
+ email="email@example.com",
89
+ password="password",
90
+ timeout=10.0, # Optional: request timeout in seconds
91
+ debug=False, # Optional: enable debug logging
92
+ ) as spa:
93
+ # All spa operations can be called synchronously
94
+ spa.get_spa_status()
95
+ spa.set_temperature(102)
96
+ # etc...
97
+ ```
98
+
99
+ ### Error Handling
100
+
101
+ All operations can raise these base exceptions:
102
+ - `AuthenticationError`: When authentication fails or token expires
103
+ - `ConnectionError`: When network connection fails or API is unreachable
104
+ - `SpaControlError`: When the API returns an error response
105
+
106
+ ### Temperature Control
107
+
108
+ ```python
109
+ async with spa as client:
110
+ # Set temperature (80-104°F or 26.5-40°C)
111
+ try:
112
+ # Basic temperature setting
113
+ await client.set_temperature(102) # Fahrenheit
114
+ await client.set_temperature(39, "C") # Celsius
115
+
116
+ # Wait for spa to acknowledge the temperature change
117
+ await client.set_temperature(102, wait_for_ack=True)
118
+
119
+ # Control polling behavior when waiting for acknowledgment
120
+ await client.set_temperature(
121
+ 102,
122
+ wait_for_ack=True,
123
+ polling_interval=5.0, # Check every 5 seconds
124
+ polling_timeout=120.0, # Time out after 2 minutes
125
+ )
126
+
127
+ # Manually wait for temperature acknowledgment
128
+ settings = await client.wait_for_temperature_ack(
129
+ expected_temp=102, # Expected temperature in Fahrenheit
130
+ interval=5.0, # Check every 5 seconds
131
+ timeout=120.0, # Time out after 2 minutes
132
+ )
133
+ except InvalidParameterError:
134
+ # Raised when temperature is outside valid range
135
+ # (80-104°F or 26.5-40°C)
136
+ pass
137
+ except SpaControlError:
138
+ # Raised when polling times out waiting for acknowledgment
139
+ pass
140
+ ```
141
+
142
+ ### Pump Control
143
+
144
+ ```python
145
+ async with spa as client:
146
+ try:
147
+ await client.set_pump(1, PUMP_HIGH) # Set pump 1 to high speed
148
+ await client.set_pump(2, PUMP_LOW) # Set pump 2 to low speed
149
+ await client.set_pump(3, PUMP_OFF) # Turn off pump 3
150
+ except InvalidParameterError:
151
+ # Raised when:
152
+ # - pump_number is not 1, 2, or 3
153
+ # - speed is not PUMP_OFF (0), PUMP_LOW (1), or PUMP_HIGH (2)
154
+ pass
155
+ ```
156
+
157
+ ### Light Control
158
+
159
+ ```python
160
+ async with spa as client:
161
+ try:
162
+ await client.set_lights(True) # Turn lights on
163
+ await client.set_lights(False) # Turn lights off
164
+ except SpaControlError:
165
+ # Raised when light control fails
166
+ pass
167
+ ```
168
+
169
+ ### Status & Settings
170
+
171
+ ```python
172
+ async with spa as client:
173
+ try:
174
+ # Get basic spa status
175
+ status = await client.get_spa_status()
176
+ print(f"Online: {status.status == 'ONLINE'}")
177
+
178
+ # Get detailed live settings
179
+ settings = await client.get_live_settings()
180
+ print(f"Target temp: {settings.ctrl_head_set_temperature}°F")
181
+ except ConnectionError:
182
+ # Raised when spa is offline or unreachable
183
+ pass
184
+ except SpaControlError:
185
+ # Raised when API returns invalid data
186
+ pass
187
+
188
+ ## Development
189
+
190
+ 1. Clone the repository
191
+ 2. Create a virtual environment:
192
+ ```bash
193
+ python -m venv venv
194
+ source venv/bin/activate # or `venv\Scripts\activate` on Windows
195
+ ```
196
+ 3. Install development dependencies:
197
+ ```bash
198
+ pip install -r requirements-dev.txt
199
+ ```
200
+ 4. Install pre-commit hooks:
201
+ ```bash
202
+ pre-commit install
203
+ ```
204
+
205
+ The pre-commit hooks will run automatically on git commit, checking:
206
+ - Code formatting (Black)
207
+ - Import sorting (isort)
208
+ - Type checking (mypy)
209
+ - Linting (pylint, ruff)
210
+ - YAML/TOML syntax
211
+ - Trailing whitespace and file endings
212
+
213
+ ## License
214
+
215
+ MIT License - see LICENSE file for details.
@@ -1,6 +1,9 @@
1
1
  """Python client for Caldera Spa Connexion API."""
2
2
 
3
+ from .__meta__ import __version__
3
4
  from .async_client import AsyncCalderaClient
5
+ from .client import CalderaClient
6
+ from .const import PUMP_HIGH, PUMP_LOW, PUMP_OFF
4
7
  from .exceptions import (
5
8
  AuthenticationError,
6
9
  CalderaError,
@@ -8,15 +11,20 @@ from .exceptions import (
8
11
  InvalidParameterError,
9
12
  SpaControlError,
10
13
  )
11
- from .models import LiveSettings
14
+ from .models import LiveSettings, PumpInfo
12
15
 
13
16
  __all__ = [
14
17
  "AsyncCalderaClient",
18
+ "CalderaClient",
15
19
  "LiveSettings",
20
+ "PumpInfo",
21
+ "PUMP_OFF",
22
+ "PUMP_LOW",
23
+ "PUMP_HIGH",
16
24
  "CalderaError",
17
25
  "AuthenticationError",
18
26
  "ConnectionError",
19
27
  "SpaControlError",
20
28
  "InvalidParameterError",
29
+ "__version__",
21
30
  ]
22
- __version__ = "0.1.0"
@@ -4,7 +4,8 @@ name = "pycaldera"
4
4
  path = name.lower().replace("-", "_").replace(" ", "_")
5
5
  # Your version number should follow https://python.org/dev/peps/pep-0440 and
6
6
  # https://semver.org
7
- version = "0.1.dev0"
7
+ version = "0.1.2"
8
+ __version__ = version
8
9
  author = "Mark Watson"
9
10
  author_email = "markwatson@cantab.net"
10
11
  description = "Unofficial Python client for Caldera Spa API" # One-liner