quilt-hp-python 0.1.1__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.
Files changed (53) hide show
  1. quilt_hp/__init__.py +22 -0
  2. quilt_hp/_paths.py +26 -0
  3. quilt_hp/_proto/__init__.py +0 -0
  4. quilt_hp/_proto/quilt_device_pairing_pb2.py +56 -0
  5. quilt_hp/_proto/quilt_device_pairing_pb2.pyi +317 -0
  6. quilt_hp/_proto/quilt_device_pairing_pb2_grpc.py +24 -0
  7. quilt_hp/_proto/quilt_hds_pb2.py +292 -0
  8. quilt_hp/_proto/quilt_hds_pb2.pyi +3947 -0
  9. quilt_hp/_proto/quilt_hds_pb2_grpc.py +1732 -0
  10. quilt_hp/_proto/quilt_notifier_pb2.py +55 -0
  11. quilt_hp/_proto/quilt_notifier_pb2.pyi +258 -0
  12. quilt_hp/_proto/quilt_notifier_pb2_grpc.py +97 -0
  13. quilt_hp/_proto/quilt_services_pb2.py +171 -0
  14. quilt_hp/_proto/quilt_services_pb2.pyi +1320 -0
  15. quilt_hp/_proto/quilt_services_pb2_grpc.py +1188 -0
  16. quilt_hp/_proto/quilt_system_pb2.py +53 -0
  17. quilt_hp/_proto/quilt_system_pb2.pyi +164 -0
  18. quilt_hp/_proto/quilt_system_pb2_grpc.py +270 -0
  19. quilt_hp/auth.py +244 -0
  20. quilt_hp/cli/__init__.py +1 -0
  21. quilt_hp/cli/main.py +770 -0
  22. quilt_hp/cli/settings.py +123 -0
  23. quilt_hp/cli/store.py +105 -0
  24. quilt_hp/cli/tui.py +2677 -0
  25. quilt_hp/client.py +616 -0
  26. quilt_hp/const.py +57 -0
  27. quilt_hp/exceptions.py +23 -0
  28. quilt_hp/models/__init__.py +85 -0
  29. quilt_hp/models/comfort.py +47 -0
  30. quilt_hp/models/controller.py +135 -0
  31. quilt_hp/models/energy.py +31 -0
  32. quilt_hp/models/enums.py +298 -0
  33. quilt_hp/models/indoor_unit.py +412 -0
  34. quilt_hp/models/outdoor_unit.py +71 -0
  35. quilt_hp/models/qsm.py +105 -0
  36. quilt_hp/models/schedule.py +98 -0
  37. quilt_hp/models/sensor.py +92 -0
  38. quilt_hp/models/software_update.py +74 -0
  39. quilt_hp/models/space.py +177 -0
  40. quilt_hp/models/system.py +451 -0
  41. quilt_hp/py.typed +1 -0
  42. quilt_hp/services/__init__.py +1 -0
  43. quilt_hp/services/hds.py +480 -0
  44. quilt_hp/services/streaming.py +561 -0
  45. quilt_hp/services/system.py +95 -0
  46. quilt_hp/services/user.py +143 -0
  47. quilt_hp/tokens.py +119 -0
  48. quilt_hp/transport.py +192 -0
  49. quilt_hp_python-0.1.1.dist-info/METADATA +172 -0
  50. quilt_hp_python-0.1.1.dist-info/RECORD +53 -0
  51. quilt_hp_python-0.1.1.dist-info/WHEEL +4 -0
  52. quilt_hp_python-0.1.1.dist-info/entry_points.txt +2 -0
  53. quilt_hp_python-0.1.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,85 @@
1
+ """Pythonic data models wrapping protobuf messages."""
2
+
3
+ from quilt_hp.models.comfort import ComfortSetting
4
+ from quilt_hp.models.controller import Controller
5
+ from quilt_hp.models.energy import EnergyBucket, SpaceEnergyMetrics
6
+ from quilt_hp.models.enums import (
7
+ BoostMode,
8
+ ComfortSettingOverride,
9
+ ComfortSettingType,
10
+ ConditionState,
11
+ FallbackControlCommand,
12
+ FanSpeed,
13
+ HvacControllerType,
14
+ HVACMode,
15
+ HVACState,
16
+ LedAnimation,
17
+ LightPreset,
18
+ LouverAngle,
19
+ LouverMode,
20
+ OccupancyMode,
21
+ OccupancyState,
22
+ RemoteSensorControlMode,
23
+ SafetyHeatingMode,
24
+ )
25
+ from quilt_hp.models.indoor_unit import (
26
+ IndoorUnit,
27
+ IndoorUnitCommands,
28
+ IndoorUnitControls,
29
+ IndoorUnitSettings,
30
+ IndoorUnitState,
31
+ )
32
+ from quilt_hp.models.outdoor_unit import OutdoorUnit
33
+ from quilt_hp.models.schedule import ScheduleDay, ScheduleEvent, ScheduleWeek
34
+ from quilt_hp.models.sensor import ControllerRemoteSensor, RemoteSensor
35
+ from quilt_hp.models.software_update import SoftwareUpdateInfo
36
+ from quilt_hp.models.space import (
37
+ Space,
38
+ SpaceControls,
39
+ SpaceSettings,
40
+ SpaceState,
41
+ )
42
+ from quilt_hp.models.system import Location, SystemInfo, SystemSnapshot
43
+
44
+ __all__ = [
45
+ "BoostMode",
46
+ "ComfortSetting",
47
+ "ComfortSettingOverride",
48
+ "ComfortSettingType",
49
+ "ConditionState",
50
+ "Controller",
51
+ "ControllerRemoteSensor",
52
+ "EnergyBucket",
53
+ "FallbackControlCommand",
54
+ "FanSpeed",
55
+ "HVACMode",
56
+ "HVACState",
57
+ "HvacControllerType",
58
+ "IndoorUnit",
59
+ "IndoorUnitCommands",
60
+ "IndoorUnitControls",
61
+ "IndoorUnitSettings",
62
+ "IndoorUnitState",
63
+ "LedAnimation",
64
+ "LightPreset",
65
+ "Location",
66
+ "LouverAngle",
67
+ "LouverMode",
68
+ "OccupancyMode",
69
+ "OccupancyState",
70
+ "OutdoorUnit",
71
+ "RemoteSensor",
72
+ "RemoteSensorControlMode",
73
+ "SafetyHeatingMode",
74
+ "ScheduleDay",
75
+ "ScheduleEvent",
76
+ "ScheduleWeek",
77
+ "SoftwareUpdateInfo",
78
+ "Space",
79
+ "SpaceControls",
80
+ "SpaceEnergyMetrics",
81
+ "SpaceSettings",
82
+ "SpaceState",
83
+ "SystemInfo",
84
+ "SystemSnapshot",
85
+ ]
@@ -0,0 +1,47 @@
1
+ """Comfort setting (named preset) model."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from quilt_hp.models.enums import (
8
+ ComfortSettingType,
9
+ FanSpeed,
10
+ HVACMode,
11
+ LouverMode,
12
+ )
13
+
14
+
15
+ @dataclass(slots=True)
16
+ class ComfortSetting:
17
+ """A named comfort preset (Active, Sleep, Away, etc.)."""
18
+
19
+ id: str
20
+ system_id: str
21
+ space_id: str
22
+ name: str
23
+ type: ComfortSettingType
24
+ hvac_mode: HVACMode
25
+ heating_setpoint_c: float
26
+ cooling_setpoint_c: float
27
+ fan_speed: FanSpeed
28
+ louver_mode: LouverMode = LouverMode.UNSPECIFIED
29
+ louver_fixed_position: float = 0.0 # degrees, used when louver_mode=FIXED
30
+
31
+ @classmethod
32
+ def from_proto(cls, proto: object) -> ComfortSetting:
33
+ """Construct from a protobuf ComfortSetting message."""
34
+ a = proto.attributes # type: ignore[attr-defined]
35
+ return cls(
36
+ id=proto.header.object_id, # type: ignore[attr-defined]
37
+ system_id=proto.header.system_id, # type: ignore[attr-defined]
38
+ space_id=proto.relationships.space_id, # type: ignore[attr-defined]
39
+ name=a.name,
40
+ type=ComfortSettingType(a.type),
41
+ hvac_mode=HVACMode(a.hvac_mode),
42
+ heating_setpoint_c=a.heating_temperature_setpoint_c,
43
+ cooling_setpoint_c=a.cooling_temperature_setpoint_c,
44
+ fan_speed=FanSpeed.from_wire(a.fan_speed_mode, a.fan_speed_percent),
45
+ louver_mode=LouverMode(a.louver_mode) if a.louver_mode else LouverMode.UNSPECIFIED,
46
+ louver_fixed_position=a.louver_fixed_position,
47
+ )
@@ -0,0 +1,135 @@
1
+ """Controller (Quilt Dial thermostat) model."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from datetime import UTC, datetime
7
+ from typing import Any, cast
8
+
9
+ from quilt_hp.models.enums import RemoteSensorControlMode
10
+ from quilt_hp.models.qsm import WifiInfo
11
+
12
+ _ONLINE_THRESHOLD_S = 5 * 60 # 5 minutes, matching KMP IS_ONLINE_THRESHOLD_MINUTES
13
+
14
+
15
+ @dataclass(slots=True)
16
+ class Controller:
17
+ """A Quilt controller (Dial thermostat)."""
18
+
19
+ id: str
20
+ system_id: str
21
+ space_id: str
22
+ name: str
23
+ raw_thermistor_c: float # ambient_temperature_c from raw Dial thermistor
24
+ pcb_temperature_a_c: float # temperature_f3 — PCB temp A (~30–50°C)
25
+ pcb_temperature_b_c: float # temperature_f4 — PCB temp B (hotter component, ~45–52°C)
26
+ calibrated_ambient_c: float # temperature_f5 — calibrated ext ambient sent to IDU
27
+ wifi_ssid: str | None
28
+ wifi_ip: str | None
29
+ wifi_signal_dbm: int | None
30
+ wifi_freq_mhz: int | None = None # e.g. 5745 → 5 GHz; 2437 → 2.4 GHz
31
+ wifi_last_seen: datetime | None = (
32
+ None # WifiState.updated_ts — when the dial last checked in over WiFi
33
+ )
34
+ ap_wifi: WifiInfo | None = None # AP-mode interface (device provisioning)
35
+ p2p_wifi: WifiInfo | None = None # peer-to-peer / Wi-Fi Direct
36
+ remote_sensor_mode: RemoteSensorControlMode = RemoteSensorControlMode.UNSPECIFIED
37
+ software_update_info_id: str | None = None
38
+ firmware_update_info_id: str | None = None
39
+ serial_number: str | None = None # ControllerHardware.attributes.serial_number
40
+ model_sku: str | None = None # ControllerHardware.attributes.model_sku
41
+ firmware_version: str | None = None # ControllerHardware.attributes.firmware_version
42
+ state_updated_at: datetime | None = None # ControllerState.updated_ts (field 1)
43
+
44
+ @property
45
+ def ambient_temperature_c(self) -> float:
46
+ """Calibrated ambient temperature used for system control.
47
+
48
+ Use this for display and logic. See also ``raw_thermistor_c`` for the
49
+ uncorrected on-chip reading (biased high by self-heating).
50
+ """
51
+ return self.calibrated_ambient_c
52
+
53
+ @property
54
+ def wifi_band(self) -> str | None:
55
+ """'5 GHz' or '2.4 GHz' based on frequency, or None if unknown."""
56
+ if self.wifi_freq_mhz is None:
57
+ return None
58
+ return "5 GHz" if self.wifi_freq_mhz > 5000 else "2.4 GHz"
59
+
60
+ @property
61
+ def is_online(self) -> bool:
62
+ """True if the controller is known to be online.
63
+
64
+ Uses ``ControllerState.updated_ts`` if available, with a 5-minute
65
+ threshold matching KMP ``IS_ONLINE_THRESHOLD_MINUTES = 5``.
66
+
67
+ The server does not currently send ``ControllerState.updated_ts``
68
+ (confirmed from wire captures — field 1 always absent). When no
69
+ timestamp is available we assume the controller is online; we only
70
+ report offline when we have positive evidence of a stale timestamp.
71
+ """
72
+ if self.state_updated_at is None:
73
+ return True # no timestamp → unknown → assume online (fail-open)
74
+ age = (datetime.now(tz=UTC) - self.state_updated_at).total_seconds()
75
+ return age < _ONLINE_THRESHOLD_S
76
+
77
+ @classmethod
78
+ def from_proto(cls, proto: object, hw_map: dict[str, object] | None = None) -> Controller:
79
+ """Construct from a protobuf Controller message.
80
+
81
+ ``hw_map`` maps hardware_id → ControllerHardware proto, built once from
82
+ ``HomeDatastoreSystem.controller_hardware`` and passed in at snapshot
83
+ load time. Stream diffs won't have it; fields default to None.
84
+ """
85
+ p = cast("Any", proto)
86
+ w = p.hosted_wifi_state
87
+ ts = p.state.updated_ts
88
+ updated_at: datetime | None = None
89
+ if ts.seconds != 0:
90
+ updated_at = datetime.fromtimestamp(ts.seconds, tz=UTC)
91
+
92
+ wifi_last_seen: datetime | None = None
93
+ if w.updated_ts.seconds != 0:
94
+ wifi_last_seen = datetime.fromtimestamp(w.updated_ts.seconds, tz=UTC)
95
+
96
+ def _wifi(wstate: object) -> WifiInfo | None:
97
+ info = WifiInfo.from_proto(wstate)
98
+ return info if info.connected else None
99
+
100
+ serial: str | None = None
101
+ model_sku: str | None = None
102
+ fw_ver: str | None = None
103
+ if hw_map:
104
+ hw_id = p.relationships.hardware_id
105
+ hw = hw_map.get(hw_id)
106
+ if hw is not None:
107
+ a = cast("Any", hw).attributes
108
+ serial = a.serial_number or None
109
+ model_sku = a.model_sku or None
110
+ fw_ver = a.firmware_version or None
111
+
112
+ return cls(
113
+ id=p.header.object_id,
114
+ system_id=p.header.system_id,
115
+ space_id=p.relationships.space_id,
116
+ name=p.settings.name,
117
+ raw_thermistor_c=p.state.ambient_temperature_c,
118
+ pcb_temperature_a_c=p.state.temperature_f3,
119
+ pcb_temperature_b_c=p.state.temperature_f4,
120
+ calibrated_ambient_c=p.state.temperature_f5,
121
+ wifi_ssid=w.ssid or None,
122
+ wifi_ip=w.ipv4_address or None,
123
+ wifi_signal_dbm=w.signal_level_dbm or None,
124
+ wifi_freq_mhz=w.frequency_mhz or None,
125
+ wifi_last_seen=wifi_last_seen,
126
+ ap_wifi=_wifi(p.ap_wifi_state),
127
+ p2p_wifi=_wifi(p.p2p_wifi_state),
128
+ remote_sensor_mode=RemoteSensorControlMode(p.controls.remote_sensor_control_mode),
129
+ software_update_info_id=p.relationships.software_update_info_id or None,
130
+ firmware_update_info_id=p.relationships.firmware_update_info_id or None,
131
+ serial_number=serial,
132
+ model_sku=model_sku,
133
+ firmware_version=fw_ver,
134
+ state_updated_at=updated_at,
135
+ )
@@ -0,0 +1,31 @@
1
+ """Energy metrics models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from datetime import datetime
10
+
11
+
12
+ @dataclass(slots=True)
13
+ class EnergyBucket:
14
+ """One hourly energy measurement slot."""
15
+
16
+ start_time: datetime
17
+ energy_kwh: float
18
+ status: int # 0=UNSPECIFIED, 1=COMPLETE, 2=INCOMPLETE
19
+
20
+
21
+ @dataclass(slots=True)
22
+ class SpaceEnergyMetrics:
23
+ """Hourly energy buckets for one space over a time range."""
24
+
25
+ space_id: str
26
+ buckets: list[EnergyBucket]
27
+
28
+ @property
29
+ def total_kwh(self) -> float:
30
+ """Sum of all bucket energy values."""
31
+ return sum(b.energy_kwh for b in self.buckets)
@@ -0,0 +1,298 @@
1
+ """Pythonic enums mirroring the Quilt protobuf enum values."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import IntEnum
6
+
7
+
8
+ class HVACMode(IntEnum):
9
+ """HVAC operating mode."""
10
+
11
+ UNSPECIFIED = 0
12
+ STANDBY = 1
13
+ COOL = 2
14
+ HEAT = 3
15
+ AUTO = 4
16
+ FAN = 5
17
+ FALLBACK_AUTO = 6
18
+ FALLBACK_OFF = 7
19
+
20
+ def __str__(self) -> str:
21
+ return self.name
22
+
23
+
24
+ class HVACState(IntEnum):
25
+ """Current HVAC state (what the system is actually doing)."""
26
+
27
+ UNSPECIFIED = 0
28
+ STANDBY = 1
29
+ COOL = 2
30
+ HEAT = 3
31
+ DRIFT = 4
32
+ FAN = 5
33
+ COOL_DEFERRED = 6
34
+ HEAT_DEFERRED = 7
35
+ FAN_DEFERRED = 8
36
+ COOL_PREPARING = 9
37
+ HEAT_PREPARING = 10
38
+
39
+ def __str__(self) -> str:
40
+ return self.name
41
+
42
+
43
+ class FanSpeed(IntEnum):
44
+ """Discrete fan speed labels (maps to mode + percent on the wire)."""
45
+
46
+ AUTO = 0
47
+ QUIET = 1
48
+ LOW = 2
49
+ MEDIUM = 3
50
+ HIGH = 4
51
+ BLAST = 5
52
+
53
+ def __str__(self) -> str:
54
+ return self.name
55
+
56
+ def to_wire(self) -> tuple[int, float]:
57
+ """Return (fan_speed_mode, fan_speed_percent) for the wire protocol."""
58
+ _MAP: dict[FanSpeed, tuple[int, float]] = {
59
+ FanSpeed.AUTO: (1, 0.0), # FAN_SPEED_MODE_AUTO
60
+ FanSpeed.QUIET: (2, 0.20), # FAN_SPEED_MODE_SETPOINT
61
+ FanSpeed.LOW: (2, 0.40),
62
+ FanSpeed.MEDIUM: (2, 0.60),
63
+ FanSpeed.HIGH: (2, 0.80),
64
+ FanSpeed.BLAST: (2, 1.00),
65
+ }
66
+ return _MAP[self]
67
+
68
+ @classmethod
69
+ def from_wire(cls, mode: int, percent: float) -> FanSpeed:
70
+ """Decode wire fan_speed_mode/fan_speed_percent to FanSpeed label."""
71
+ if mode != 2: # FAN_SPEED_MODE_SETPOINT
72
+ return cls.AUTO
73
+ if percent <= 0.21:
74
+ return cls.QUIET
75
+ if percent <= 0.41:
76
+ return cls.LOW
77
+ if percent <= 0.61:
78
+ return cls.MEDIUM
79
+ if percent <= 0.81:
80
+ return cls.HIGH
81
+ return cls.BLAST
82
+
83
+
84
+ class LouverMode(IntEnum):
85
+ """Indoor unit louver mode."""
86
+
87
+ UNSPECIFIED = 0
88
+ CLOSED = 1
89
+ SWEEP = 2
90
+ FIXED = 3
91
+ AUTO = 4
92
+
93
+ def __str__(self) -> str:
94
+ return self.name
95
+
96
+
97
+ class LouverAngle(IntEnum):
98
+ """Discrete louver angle positions (when mode=FIXED)."""
99
+
100
+ ANGLE1 = 1
101
+ ANGLE2 = 2
102
+ ANGLE3 = 3
103
+ ANGLE4 = 4
104
+ ANGLE5 = 5
105
+
106
+ def to_wire(self) -> float:
107
+ """Return the louver_fixed_position float for the wire."""
108
+ return {1: 0.20, 2: 0.40, 3: 0.60, 4: 0.80, 5: 1.00}[self.value]
109
+
110
+ @classmethod
111
+ def from_wire(cls, position: float) -> LouverAngle:
112
+ """Decode a wire louver_fixed_position to a LouverAngle."""
113
+ if position <= 0.21:
114
+ return cls.ANGLE1
115
+ if position <= 0.41:
116
+ return cls.ANGLE2
117
+ if position <= 0.61:
118
+ return cls.ANGLE3
119
+ if position <= 0.81:
120
+ return cls.ANGLE4
121
+ return cls.ANGLE5
122
+
123
+
124
+ class LightPreset(IntEnum):
125
+ """Built-in LED color presets (RGBW packed int32)."""
126
+
127
+ DAYLIGHT = 0x000000FF
128
+ WARM = 0xFF460064
129
+ SUNSET = 0xFF460024
130
+ SKY = 0x009CFF54
131
+
132
+ def __str__(self) -> str:
133
+ return self.name
134
+
135
+
136
+ class LedAnimation(IntEnum):
137
+ """Indoor unit LED animation modes.
138
+
139
+ Note: NONE = 1 (not 0). UNSPECIFIED = 0 means the field was absent from
140
+ the wire diff; NONE means the server explicitly set "no animation" (solid
141
+ colour). Callers should treat UNSPECIFIED the same as NONE for display.
142
+ """
143
+
144
+ UNSPECIFIED = 0
145
+ NONE = 1
146
+ SPARKLE_FADE = 2
147
+ TWINKLE_FADE = 3
148
+ DANCE = 4
149
+ CHASE = 5
150
+
151
+ def __str__(self) -> str:
152
+ return self.name
153
+
154
+
155
+ class ComfortSettingType(IntEnum):
156
+ """Type of comfort preset."""
157
+
158
+ UNSPECIFIED = 0
159
+ ACTIVE = 1
160
+ SLEEP = 2
161
+ AWAY = 3
162
+ STANDBY = 4
163
+ CUSTOM = 5
164
+
165
+ def __str__(self) -> str:
166
+ return self.name
167
+
168
+
169
+ class OccupancyMode(IntEnum):
170
+ """Space-level auto-away/return occupancy mode."""
171
+
172
+ UNSPECIFIED = 0
173
+ DISABLED = 1
174
+ ENABLED = 2
175
+
176
+ def __str__(self) -> str:
177
+ return self.name
178
+
179
+
180
+ class SafetyHeatingMode(IntEnum):
181
+ """Freeze protection mode for a space."""
182
+
183
+ UNSPECIFIED = 0 # treated as ENABLED by the device
184
+ DISABLED = 1
185
+ ENABLED = 2
186
+
187
+ def __str__(self) -> str:
188
+ return self.name
189
+
190
+
191
+ class OccupancyState(IntEnum):
192
+ """Occupancy detection state."""
193
+
194
+ UNSPECIFIED = 0
195
+ UNDETECTED = 1
196
+ DETECTED = 2
197
+
198
+ def __str__(self) -> str:
199
+ return self.name
200
+
201
+
202
+ class Presence(IntEnum):
203
+ """Raw radar sensor presence detection."""
204
+
205
+ UNSPECIFIED = 0
206
+ UNDETECTED = 1
207
+ DETECTED = 2
208
+
209
+ def __str__(self) -> str:
210
+ return self.name
211
+
212
+
213
+ class LightState(IntEnum):
214
+ """Explicit LED on/off state (field 13 of IndoorUnitControls).
215
+
216
+ Sent when the ``mobile_led_scheduling_enabled`` Statsig gate is on.
217
+ When state=OFF, brightness is **preserved** server-side (not zeroed) so
218
+ toggling back on restores the prior brightness. When UNSPECIFIED, fall
219
+ back to brightness-based detection.
220
+ """
221
+
222
+ UNSPECIFIED = 0
223
+ ON = 1
224
+ OFF = 2
225
+
226
+ def __str__(self) -> str:
227
+ return self.name
228
+
229
+
230
+ class ConditionState(IntEnum):
231
+ """Fault condition state."""
232
+
233
+ UNSPECIFIED = 0
234
+ INACTIVE = 1
235
+ ACTIVE = 2
236
+
237
+ def __str__(self) -> str:
238
+ return self.name
239
+
240
+
241
+ class HvacControllerType(IntEnum):
242
+ """Algorithm variant used by the controller to drive the IDU."""
243
+
244
+ UNSPECIFIED = 0
245
+ PASS_THROUGH_TEMPERATURE = 1
246
+ INTEGRAL_TEMPERATURE_V1 = 2
247
+ INTEGRAL_TEMPERATURE_V2 = 3
248
+
249
+ def __str__(self) -> str:
250
+ return self.name
251
+
252
+
253
+ class BoostMode(IntEnum):
254
+ """Boost (turbo) mode override for a space."""
255
+
256
+ UNSPECIFIED = 0
257
+ OFF = 1
258
+ ON = 2
259
+
260
+ def __str__(self) -> str:
261
+ return self.name
262
+
263
+
264
+ class ComfortSettingOverride(IntEnum):
265
+ """Why the active comfort setting differs from the schedule."""
266
+
267
+ UNSPECIFIED = 0
268
+ NONE = 1
269
+ UNTIL_NEXT_SCHEDULE = 2
270
+ INDEFINITE = 3
271
+ SCHEDULE = 4
272
+ UNOCCUPIED = 5
273
+ OCCUPIED = 6
274
+
275
+ def __str__(self) -> str:
276
+ return self.name
277
+
278
+
279
+ class FallbackControlCommand(IntEnum):
280
+ """Command sent when the Dial loses cloud connectivity."""
281
+
282
+ UNSPECIFIED = 0
283
+ COMPLETE = 1
284
+ EXIT = 2
285
+
286
+ def __str__(self) -> str:
287
+ return self.name
288
+
289
+
290
+ class RemoteSensorControlMode(IntEnum):
291
+ """Whether the Dial acts as the zone temperature sensor."""
292
+
293
+ UNSPECIFIED = 0
294
+ DISABLED = 1
295
+ ENABLED = 2
296
+
297
+ def __str__(self) -> str:
298
+ return self.name