python-hotspring 2.0.1__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hotspring/__init__.py +12 -0
- hotspring/const.py +104 -2
- hotspring/exceptions.py +15 -0
- hotspring/hotspring.py +351 -70
- hotspring/models.py +356 -112
- {python_hotspring-2.0.1.dist-info → python_hotspring-3.0.0.dist-info}/METADATA +8 -9
- python_hotspring-3.0.0.dist-info/RECORD +10 -0
- python_hotspring-2.0.1.dist-info/RECORD +0 -10
- {python_hotspring-2.0.1.dist-info → python_hotspring-3.0.0.dist-info}/WHEEL +0 -0
- {python_hotspring-2.0.1.dist-info → python_hotspring-3.0.0.dist-info}/licenses/LICENSE +0 -0
hotspring/__init__.py
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from .const import (
|
|
4
4
|
BrightnessLevel,
|
|
5
|
+
DeviceType,
|
|
6
|
+
EnergySavingMode,
|
|
5
7
|
HeatingMode,
|
|
6
8
|
JetSpeed,
|
|
9
|
+
JetSpeedType,
|
|
7
10
|
LightColor,
|
|
8
11
|
LightWheelMode,
|
|
9
12
|
SpaBrand,
|
|
@@ -15,7 +18,9 @@ from .exceptions import (
|
|
|
15
18
|
HotSpringConnectionError,
|
|
16
19
|
HotSpringConnectionTimeoutError,
|
|
17
20
|
HotSpringError,
|
|
21
|
+
HotSpringInvalidDeviceError,
|
|
18
22
|
HotSpringNotReadyError,
|
|
23
|
+
HotSpringSNADetectedError,
|
|
19
24
|
)
|
|
20
25
|
from .hotspring import HotSpring
|
|
21
26
|
from .models import (
|
|
@@ -32,6 +37,7 @@ from .models import (
|
|
|
32
37
|
Spa,
|
|
33
38
|
SpaInfo,
|
|
34
39
|
SpaLock,
|
|
40
|
+
SpaTestData,
|
|
35
41
|
Versions,
|
|
36
42
|
WaterCare,
|
|
37
43
|
)
|
|
@@ -41,8 +47,10 @@ __all__ = [
|
|
|
41
47
|
"BrightnessLevel",
|
|
42
48
|
"CleanCycle",
|
|
43
49
|
"ConnectionStatus",
|
|
50
|
+
"DeviceType",
|
|
44
51
|
"Diagnostics",
|
|
45
52
|
"EnergySaving",
|
|
53
|
+
"EnergySavingMode",
|
|
46
54
|
"FreshWaterIQ",
|
|
47
55
|
"Heater",
|
|
48
56
|
"HeatingMode",
|
|
@@ -51,9 +59,12 @@ __all__ = [
|
|
|
51
59
|
"HotSpringConnectionError",
|
|
52
60
|
"HotSpringConnectionTimeoutError",
|
|
53
61
|
"HotSpringError",
|
|
62
|
+
"HotSpringInvalidDeviceError",
|
|
54
63
|
"HotSpringNotReadyError",
|
|
64
|
+
"HotSpringSNADetectedError",
|
|
55
65
|
"Jet",
|
|
56
66
|
"JetSpeed",
|
|
67
|
+
"JetSpeedType",
|
|
57
68
|
"LightColor",
|
|
58
69
|
"LightWheelMode",
|
|
59
70
|
"LightZone",
|
|
@@ -63,6 +74,7 @@ __all__ = [
|
|
|
63
74
|
"SpaFailureState",
|
|
64
75
|
"SpaInfo",
|
|
65
76
|
"SpaLock",
|
|
77
|
+
"SpaTestData",
|
|
66
78
|
"TemperatureUnit",
|
|
67
79
|
"Versions",
|
|
68
80
|
"WaterCare",
|
hotspring/const.py
CHANGED
|
@@ -75,6 +75,35 @@ class JetSpeed(Enum):
|
|
|
75
75
|
_JET_SPEED_MAP: dict[str, JetSpeed] = {s.value: s for s in JetSpeed}
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
class JetSpeedType(Enum):
|
|
79
|
+
"""Configured speed capability of a jet pump."""
|
|
80
|
+
|
|
81
|
+
UNKNOWN = "unknown"
|
|
82
|
+
SINGLE_SPEED = "singleSpeed"
|
|
83
|
+
DUAL_SPEED = "dualSpeed"
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def build(cls, value: str | None) -> JetSpeedType:
|
|
87
|
+
"""Parse a raw API string into a JetSpeedType.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
----
|
|
91
|
+
value: The raw speed configuration string from the API, or None.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
-------
|
|
95
|
+
The matching JetSpeedType, or JetSpeedType.UNKNOWN for
|
|
96
|
+
unrecognized values.
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
if value is None:
|
|
100
|
+
return cls.UNKNOWN
|
|
101
|
+
return _JET_SPEED_TYPE_MAP.get(value, cls.UNKNOWN)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
_JET_SPEED_TYPE_MAP: dict[str, JetSpeedType] = {t.value: t for t in JetSpeedType}
|
|
105
|
+
|
|
106
|
+
|
|
78
107
|
class LightColor(Enum):
|
|
79
108
|
"""Color setting for a spa light zone.
|
|
80
109
|
|
|
@@ -153,12 +182,13 @@ class BrightnessLevel(Enum):
|
|
|
153
182
|
"""
|
|
154
183
|
|
|
155
184
|
UNKNOWN = "unknown"
|
|
185
|
+
AUTO = "auto"
|
|
156
186
|
LEVEL_1 = "brightness_level_1"
|
|
157
187
|
LEVEL_2 = "brightness_level_2"
|
|
158
188
|
LEVEL_3 = "brightness_level_3"
|
|
159
189
|
|
|
160
190
|
@classmethod
|
|
161
|
-
def build(cls, value: str | None) -> BrightnessLevel:
|
|
191
|
+
def build(cls, value: str | int | None) -> BrightnessLevel:
|
|
162
192
|
"""Parse a raw API string into a BrightnessLevel.
|
|
163
193
|
|
|
164
194
|
Args:
|
|
@@ -173,10 +203,49 @@ class BrightnessLevel(Enum):
|
|
|
173
203
|
"""
|
|
174
204
|
if value is None:
|
|
175
205
|
return cls.UNKNOWN
|
|
176
|
-
|
|
206
|
+
val_str = str(value).strip().lower()
|
|
207
|
+
return _BRIGHTNESS_MAP.get(val_str, cls.UNKNOWN)
|
|
177
208
|
|
|
178
209
|
|
|
179
210
|
_BRIGHTNESS_MAP: dict[str, BrightnessLevel] = {b.value: b for b in BrightnessLevel}
|
|
211
|
+
_BRIGHTNESS_MAP.update(
|
|
212
|
+
{
|
|
213
|
+
"1": BrightnessLevel.LEVEL_1,
|
|
214
|
+
"2": BrightnessLevel.LEVEL_2,
|
|
215
|
+
"3": BrightnessLevel.LEVEL_3,
|
|
216
|
+
"auto": BrightnessLevel.AUTO,
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
_BRIGHTNESS_TO_WIRE: dict[BrightnessLevel, str] = {
|
|
221
|
+
BrightnessLevel.LEVEL_1: "1",
|
|
222
|
+
BrightnessLevel.LEVEL_2: "2",
|
|
223
|
+
BrightnessLevel.LEVEL_3: "3",
|
|
224
|
+
BrightnessLevel.AUTO: "auto",
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
MAX_ENERGY_SAVING_SCHEDULES = 2
|
|
228
|
+
VALID_ENERGY_SAVING_SCHEDULE_IDS = (1, 2)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class EnergySavingMode(Enum):
|
|
232
|
+
"""Energy saving schedule mode."""
|
|
233
|
+
|
|
234
|
+
UNKNOWN = "unknown"
|
|
235
|
+
OFF = "off"
|
|
236
|
+
ON = "on"
|
|
237
|
+
|
|
238
|
+
@classmethod
|
|
239
|
+
def build(cls, value: object) -> EnergySavingMode:
|
|
240
|
+
"""Parse raw mode from API into EnergySavingMode."""
|
|
241
|
+
if value is None:
|
|
242
|
+
return cls.UNKNOWN
|
|
243
|
+
val_str = str(value).strip().lower()
|
|
244
|
+
if val_str in ("1", "on", "enable", "true"):
|
|
245
|
+
return cls.ON
|
|
246
|
+
if val_str in ("0", "off", "disable", "false"):
|
|
247
|
+
return cls.OFF
|
|
248
|
+
return cls.UNKNOWN
|
|
180
249
|
|
|
181
250
|
|
|
182
251
|
class TemperatureUnit(Enum):
|
|
@@ -408,3 +477,36 @@ def resolve_spa_model(
|
|
|
408
477
|
model_name = SPA_MODEL_MAP.get((brand_id, collection_id, model_id), "Unknown")
|
|
409
478
|
|
|
410
479
|
return (brand, collection, model_name)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
class DeviceType(Enum):
|
|
483
|
+
"""Connected Spa kit adapter device type.
|
|
484
|
+
|
|
485
|
+
The Connected Spa Kit 2 consists of two modules:
|
|
486
|
+
- HNA (Home Network Adapter): Connected to home network, acts as API bridge.
|
|
487
|
+
- SNA (Spa Network Adapter): Located in the tub, physically wired to controller.
|
|
488
|
+
"""
|
|
489
|
+
|
|
490
|
+
UNKNOWN = "unknown"
|
|
491
|
+
HNA = "hna"
|
|
492
|
+
SNA = "sna"
|
|
493
|
+
|
|
494
|
+
@classmethod
|
|
495
|
+
def build(cls, value: str | None) -> DeviceType:
|
|
496
|
+
"""Parse a raw string into a DeviceType.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
----
|
|
500
|
+
value: The raw device type string, or None.
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
-------
|
|
504
|
+
The matching DeviceType, or DeviceType.UNKNOWN for unrecognized values.
|
|
505
|
+
|
|
506
|
+
"""
|
|
507
|
+
if value is None:
|
|
508
|
+
return cls.UNKNOWN
|
|
509
|
+
return _DEVICE_TYPE_MAP.get(value.lower(), cls.UNKNOWN)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
_DEVICE_TYPE_MAP: dict[str, DeviceType] = {d.value: d for d in DeviceType}
|
hotspring/exceptions.py
CHANGED
|
@@ -26,3 +26,18 @@ class HotSpringCommandError(HotSpringError):
|
|
|
26
26
|
|
|
27
27
|
Raised when a command sent to the spa is rejected or fails.
|
|
28
28
|
"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class HotSpringInvalidDeviceError(HotSpringError):
|
|
32
|
+
"""Hot Spring invalid device exception.
|
|
33
|
+
|
|
34
|
+
Raised when attempting to connect to or command an unsupported device.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class HotSpringSNADetectedError(HotSpringInvalidDeviceError):
|
|
39
|
+
"""Hot Spring SNA detected exception.
|
|
40
|
+
|
|
41
|
+
Raised when connecting to a Spa Network Adapter (SNA) instead of
|
|
42
|
+
the Home Network Adapter (HNA). The HNA must be used as the API bridge.
|
|
43
|
+
"""
|