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.
- quilt_hp/__init__.py +22 -0
- quilt_hp/_paths.py +26 -0
- quilt_hp/_proto/__init__.py +0 -0
- quilt_hp/_proto/quilt_device_pairing_pb2.py +56 -0
- quilt_hp/_proto/quilt_device_pairing_pb2.pyi +317 -0
- quilt_hp/_proto/quilt_device_pairing_pb2_grpc.py +24 -0
- quilt_hp/_proto/quilt_hds_pb2.py +292 -0
- quilt_hp/_proto/quilt_hds_pb2.pyi +3947 -0
- quilt_hp/_proto/quilt_hds_pb2_grpc.py +1732 -0
- quilt_hp/_proto/quilt_notifier_pb2.py +55 -0
- quilt_hp/_proto/quilt_notifier_pb2.pyi +258 -0
- quilt_hp/_proto/quilt_notifier_pb2_grpc.py +97 -0
- quilt_hp/_proto/quilt_services_pb2.py +171 -0
- quilt_hp/_proto/quilt_services_pb2.pyi +1320 -0
- quilt_hp/_proto/quilt_services_pb2_grpc.py +1188 -0
- quilt_hp/_proto/quilt_system_pb2.py +53 -0
- quilt_hp/_proto/quilt_system_pb2.pyi +164 -0
- quilt_hp/_proto/quilt_system_pb2_grpc.py +270 -0
- quilt_hp/auth.py +244 -0
- quilt_hp/cli/__init__.py +1 -0
- quilt_hp/cli/main.py +770 -0
- quilt_hp/cli/settings.py +123 -0
- quilt_hp/cli/store.py +105 -0
- quilt_hp/cli/tui.py +2677 -0
- quilt_hp/client.py +616 -0
- quilt_hp/const.py +57 -0
- quilt_hp/exceptions.py +23 -0
- quilt_hp/models/__init__.py +85 -0
- quilt_hp/models/comfort.py +47 -0
- quilt_hp/models/controller.py +135 -0
- quilt_hp/models/energy.py +31 -0
- quilt_hp/models/enums.py +298 -0
- quilt_hp/models/indoor_unit.py +412 -0
- quilt_hp/models/outdoor_unit.py +71 -0
- quilt_hp/models/qsm.py +105 -0
- quilt_hp/models/schedule.py +98 -0
- quilt_hp/models/sensor.py +92 -0
- quilt_hp/models/software_update.py +74 -0
- quilt_hp/models/space.py +177 -0
- quilt_hp/models/system.py +451 -0
- quilt_hp/py.typed +1 -0
- quilt_hp/services/__init__.py +1 -0
- quilt_hp/services/hds.py +480 -0
- quilt_hp/services/streaming.py +561 -0
- quilt_hp/services/system.py +95 -0
- quilt_hp/services/user.py +143 -0
- quilt_hp/tokens.py +119 -0
- quilt_hp/transport.py +192 -0
- quilt_hp_python-0.1.1.dist-info/METADATA +172 -0
- quilt_hp_python-0.1.1.dist-info/RECORD +53 -0
- quilt_hp_python-0.1.1.dist-info/WHEEL +4 -0
- quilt_hp_python-0.1.1.dist-info/entry_points.txt +2 -0
- quilt_hp_python-0.1.1.dist-info/licenses/LICENSE +21 -0
quilt_hp/services/hds.py
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
"""HomeDatastoreService.
|
|
2
|
+
|
|
3
|
+
Async CRUD for spaces, IDUs, comfort settings, and schedules.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
from collections.abc import Callable, Sequence
|
|
10
|
+
from typing import Protocol, cast
|
|
11
|
+
|
|
12
|
+
import grpc.aio
|
|
13
|
+
from google.protobuf.timestamp_pb2 import Timestamp
|
|
14
|
+
|
|
15
|
+
from quilt_hp._proto import quilt_hds_pb2 as hds
|
|
16
|
+
from quilt_hp._proto import quilt_hds_pb2_grpc as hds_grpc
|
|
17
|
+
from quilt_hp.exceptions import QuiltError, QuiltNotFoundError
|
|
18
|
+
from quilt_hp.models.comfort import ComfortSetting
|
|
19
|
+
from quilt_hp.models.enums import FanSpeed, HVACMode, LouverMode
|
|
20
|
+
from quilt_hp.models.indoor_unit import IndoorUnit
|
|
21
|
+
from quilt_hp.models.schedule import ScheduleDay, ScheduleEvent, ScheduleWeek, ScheduleWeekDay
|
|
22
|
+
from quilt_hp.models.space import Space
|
|
23
|
+
from quilt_hp.models.system import SystemSnapshot
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _HomeDatastoreServiceStub(Protocol):
|
|
27
|
+
async def GetHomeDatastoreSystem(
|
|
28
|
+
self, request: hds.GetHomeDatastoreSystemRequest
|
|
29
|
+
) -> object: ...
|
|
30
|
+
async def UpdateSpace(self, request: hds.UpdateSpaceRequest) -> object: ...
|
|
31
|
+
async def UpdateIndoorUnit(self, request: hds.UpdateIndoorUnitRequest) -> object: ...
|
|
32
|
+
async def UpdateComfortSetting(self, request: hds.UpdateComfortSettingRequest) -> object: ...
|
|
33
|
+
async def CreateScheduleDay(self, request: hds.CreateScheduleDayRequest) -> object: ...
|
|
34
|
+
async def CreateScheduleWeek(self, request: hds.CreateScheduleWeekRequest) -> object: ...
|
|
35
|
+
async def UpdateScheduleWeek(self, request: hds.UpdateScheduleWeekRequest) -> object: ...
|
|
36
|
+
async def DeleteScheduleDay(self, request: hds.DeleteScheduleDayRequest) -> object: ...
|
|
37
|
+
async def UpdateScheduleDay(self, request: hds.UpdateScheduleDayRequest) -> object: ...
|
|
38
|
+
async def DeleteScheduleWeek(self, request: hds.DeleteScheduleWeekRequest) -> object: ...
|
|
39
|
+
async def UpdateLocation(self, request: hds.UpdateLocationRequest) -> object: ...
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _now_ts() -> Timestamp:
|
|
43
|
+
ts = Timestamp()
|
|
44
|
+
ts.FromSeconds(int(time.time()))
|
|
45
|
+
return ts
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _to_wire_schedule_event(event: ScheduleEvent | hds.ScheduleEvent) -> hds.ScheduleEvent:
|
|
49
|
+
if isinstance(event, hds.ScheduleEvent):
|
|
50
|
+
return event
|
|
51
|
+
return hds.ScheduleEvent(
|
|
52
|
+
start_s=event.start_s,
|
|
53
|
+
comfort_setting_id=event.comfort_setting_id,
|
|
54
|
+
hvac_mode=cast("hds.HVACMode.ValueType", event.hvac_mode),
|
|
55
|
+
heating_temperature_setpoint_c=event.heating_setpoint_c,
|
|
56
|
+
cooling_temperature_setpoint_c=event.cooling_setpoint_c,
|
|
57
|
+
precondition=event.precondition,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _to_wire_schedule_week_day(
|
|
62
|
+
day: ScheduleWeekDay | hds.ScheduleWeekDay,
|
|
63
|
+
) -> hds.ScheduleWeekDay:
|
|
64
|
+
if isinstance(day, hds.ScheduleWeekDay):
|
|
65
|
+
return day
|
|
66
|
+
return hds.ScheduleWeekDay(
|
|
67
|
+
weekday=cast("hds.Weekday.ValueType", day.weekday),
|
|
68
|
+
day_id=day.day_id,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class HomeDatastoreService:
|
|
73
|
+
"""Async wrapper for HomeDatastoreService gRPC methods."""
|
|
74
|
+
|
|
75
|
+
def __init__(self, channel: grpc.aio.Channel) -> None:
|
|
76
|
+
factory = cast(
|
|
77
|
+
"Callable[[grpc.aio.Channel], _HomeDatastoreServiceStub]",
|
|
78
|
+
hds_grpc.HomeDatastoreServiceStub,
|
|
79
|
+
)
|
|
80
|
+
self._stub: _HomeDatastoreServiceStub = factory(channel)
|
|
81
|
+
|
|
82
|
+
async def get_system(self, system_id: str) -> SystemSnapshot:
|
|
83
|
+
"""Fetch a full system snapshot."""
|
|
84
|
+
try:
|
|
85
|
+
snap = await self._stub.GetHomeDatastoreSystem(
|
|
86
|
+
hds.GetHomeDatastoreSystemRequest(system_id=system_id)
|
|
87
|
+
)
|
|
88
|
+
except grpc.aio.AioRpcError as exc:
|
|
89
|
+
if exc.code() == grpc.StatusCode.NOT_FOUND:
|
|
90
|
+
raise QuiltNotFoundError(f"System {system_id} not found") from exc
|
|
91
|
+
raise QuiltError(f"GetHomeDatastoreSystem failed: {exc.details()}") from exc
|
|
92
|
+
return SystemSnapshot.from_proto(snap)
|
|
93
|
+
|
|
94
|
+
async def update_space(
|
|
95
|
+
self,
|
|
96
|
+
snapshot_space: Space,
|
|
97
|
+
*,
|
|
98
|
+
mode: HVACMode | None = None,
|
|
99
|
+
heat_setpoint_c: float | None = None,
|
|
100
|
+
cool_setpoint_c: float | None = None,
|
|
101
|
+
) -> Space:
|
|
102
|
+
"""Update a space's HVAC mode and/or setpoints.
|
|
103
|
+
|
|
104
|
+
Follows the app's setpoint routing rules:
|
|
105
|
+
- temperature_setpoint_c = mode-relevant setpoint
|
|
106
|
+
- Both heating/cooling setpoints are always sent
|
|
107
|
+
|
|
108
|
+
STANDBY semantics:
|
|
109
|
+
When mode=STANDBY is explicitly requested the comfort-setting association
|
|
110
|
+
is cleared (empty id, override=NONE). This produces plain OFF —
|
|
111
|
+
the room stays off regardless of occupancy. Sending the existing
|
|
112
|
+
comfort_setting_id when going to STANDBY would preserve an AWAY
|
|
113
|
+
setting and the room would still reactivate on occupancy.
|
|
114
|
+
"""
|
|
115
|
+
c = snapshot_space.controls
|
|
116
|
+
mode_enum = mode if mode is not None else c.hvac_mode
|
|
117
|
+
mode_val = mode_enum.value
|
|
118
|
+
heat = heat_setpoint_c if heat_setpoint_c is not None else c.heating_setpoint_c
|
|
119
|
+
cool = cool_setpoint_c if cool_setpoint_c is not None else c.cooling_setpoint_c
|
|
120
|
+
|
|
121
|
+
# Mode-relevant setpoint routing (from KX.java)
|
|
122
|
+
temp_setpoint = heat if mode_enum == HVACMode.HEAT else cool
|
|
123
|
+
|
|
124
|
+
# AUTO mode: enforce cool - heat >= 2.5°C
|
|
125
|
+
if mode_enum == HVACMode.AUTO and cool - heat < 2.5:
|
|
126
|
+
cool = heat + 2.5
|
|
127
|
+
|
|
128
|
+
# Setting to STANDBY explicitly means "turn off" — clear the comfort
|
|
129
|
+
# setting so occupancy cannot reactivate the room (i.e. not AWAY mode).
|
|
130
|
+
if mode_enum == HVACMode.STANDBY:
|
|
131
|
+
cs_id = ""
|
|
132
|
+
cs_override = hds.COMFORT_SETTING_OVERRIDE_NONE
|
|
133
|
+
else:
|
|
134
|
+
cs_id = c.comfort_setting_id
|
|
135
|
+
cs_override = hds.COMFORT_SETTING_OVERRIDE_UNTIL_NEXT_SCHEDULE
|
|
136
|
+
|
|
137
|
+
diff = hds.Space(
|
|
138
|
+
header=hds.EntityMetadata(
|
|
139
|
+
object_id=snapshot_space.id,
|
|
140
|
+
system_id=snapshot_space.system_id,
|
|
141
|
+
),
|
|
142
|
+
controls=hds.SpaceControls(
|
|
143
|
+
hvac_mode=cast("hds.HVACMode.ValueType", mode_val),
|
|
144
|
+
temperature_setpoint_c=temp_setpoint,
|
|
145
|
+
heating_temperature_setpoint_c=heat,
|
|
146
|
+
cooling_temperature_setpoint_c=cool,
|
|
147
|
+
updated_ts=_now_ts(),
|
|
148
|
+
comfort_setting_override=cs_override,
|
|
149
|
+
comfort_setting_id_string=cs_id,
|
|
150
|
+
),
|
|
151
|
+
)
|
|
152
|
+
try:
|
|
153
|
+
result = await self._stub.UpdateSpace(hds.UpdateSpaceRequest(diff=diff))
|
|
154
|
+
except grpc.aio.AioRpcError as exc:
|
|
155
|
+
raise QuiltError(f"UpdateSpace failed: {exc.details()}") from exc
|
|
156
|
+
return Space.from_proto(result)
|
|
157
|
+
|
|
158
|
+
async def update_space_settings(
|
|
159
|
+
self,
|
|
160
|
+
snapshot_space: Space,
|
|
161
|
+
*,
|
|
162
|
+
unoccupied_timeout_s: float | None = None,
|
|
163
|
+
occupied_timeout_s: float | None = None,
|
|
164
|
+
) -> Space:
|
|
165
|
+
"""Update a space's auto-away / auto-return timeouts.
|
|
166
|
+
|
|
167
|
+
Sends a sparse UpdateSpace diff containing only the ``settings`` block.
|
|
168
|
+
All current settings fields are echoed back to avoid the server clearing
|
|
169
|
+
fields that are absent from a partial proto (name, timezone, etc.).
|
|
170
|
+
"""
|
|
171
|
+
s = snapshot_space.settings
|
|
172
|
+
diff = hds.Space(
|
|
173
|
+
header=hds.EntityMetadata(
|
|
174
|
+
object_id=snapshot_space.id,
|
|
175
|
+
system_id=snapshot_space.system_id,
|
|
176
|
+
),
|
|
177
|
+
settings=hds.SpaceSettings(
|
|
178
|
+
name=s.name,
|
|
179
|
+
timezone=s.timezone,
|
|
180
|
+
occupied_timeout_s=(
|
|
181
|
+
occupied_timeout_s if occupied_timeout_s is not None else s.occupied_timeout_s
|
|
182
|
+
),
|
|
183
|
+
unoccupied_timeout_s=(
|
|
184
|
+
unoccupied_timeout_s
|
|
185
|
+
if unoccupied_timeout_s is not None
|
|
186
|
+
else s.unoccupied_timeout_s
|
|
187
|
+
),
|
|
188
|
+
occupancy=cast("hds.OccupancyMode.ValueType", s.occupancy_mode.value),
|
|
189
|
+
safety_heating=cast("hds.SafetyHeatingMode.ValueType", s.safety_heating.value),
|
|
190
|
+
updated_ts=_now_ts(),
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
try:
|
|
194
|
+
result = await self._stub.UpdateSpace(hds.UpdateSpaceRequest(diff=diff))
|
|
195
|
+
except grpc.aio.AioRpcError as exc:
|
|
196
|
+
raise QuiltError(f"UpdateSpace settings failed: {exc.details()}") from exc
|
|
197
|
+
return Space.from_proto(result)
|
|
198
|
+
|
|
199
|
+
async def update_indoor_unit(
|
|
200
|
+
self,
|
|
201
|
+
idu: IndoorUnit,
|
|
202
|
+
*,
|
|
203
|
+
fan_speed: FanSpeed | None = None,
|
|
204
|
+
louver_mode: LouverMode | None = None,
|
|
205
|
+
louver_position: float | None = None,
|
|
206
|
+
led_color_code: int | None = None,
|
|
207
|
+
led_brightness: float | None = None,
|
|
208
|
+
led_animation: int | None = None,
|
|
209
|
+
) -> IndoorUnit:
|
|
210
|
+
"""Update indoor unit controls (fan, louver, LED)."""
|
|
211
|
+
c = idu.controls
|
|
212
|
+
fan_mode_val, fan_pct = (
|
|
213
|
+
fan_speed.to_wire() if fan_speed is not None else c.fan_speed.to_wire()
|
|
214
|
+
)
|
|
215
|
+
diff = hds.IndoorUnit(
|
|
216
|
+
header=hds.EntityMetadata(
|
|
217
|
+
object_id=idu.id,
|
|
218
|
+
system_id=idu.system_id,
|
|
219
|
+
),
|
|
220
|
+
controls=hds.IndoorUnitControls(
|
|
221
|
+
updated_ts=_now_ts(),
|
|
222
|
+
fan_speed_mode=cast("hds.FanSpeedMode.ValueType", fan_mode_val),
|
|
223
|
+
fan_speed_percent=fan_pct,
|
|
224
|
+
louver_mode=cast(
|
|
225
|
+
"hds.IndoorUnitLouverMode.ValueType",
|
|
226
|
+
louver_mode.value if louver_mode is not None else c.louver_mode.value,
|
|
227
|
+
),
|
|
228
|
+
louver_fixed_position=(
|
|
229
|
+
louver_position if louver_position is not None else c.louver_fixed_position
|
|
230
|
+
),
|
|
231
|
+
led_color_code=led_color_code if led_color_code is not None else c.led_color_code,
|
|
232
|
+
led_color_brightness_percent=(
|
|
233
|
+
led_brightness if led_brightness is not None else c.led_brightness
|
|
234
|
+
),
|
|
235
|
+
led_animation=cast(
|
|
236
|
+
"hds.LightAnimation.ValueType",
|
|
237
|
+
led_animation if led_animation is not None else c.led_animation,
|
|
238
|
+
),
|
|
239
|
+
),
|
|
240
|
+
)
|
|
241
|
+
try:
|
|
242
|
+
result = await self._stub.UpdateIndoorUnit(hds.UpdateIndoorUnitRequest(diff=diff))
|
|
243
|
+
except grpc.aio.AioRpcError as exc:
|
|
244
|
+
raise QuiltError(f"UpdateIndoorUnit failed: {exc.details()}") from exc
|
|
245
|
+
return IndoorUnit.from_proto(result)
|
|
246
|
+
|
|
247
|
+
async def update_indoor_unit_settings(
|
|
248
|
+
self,
|
|
249
|
+
idu: IndoorUnit,
|
|
250
|
+
*,
|
|
251
|
+
fence_left_m: float | None = None,
|
|
252
|
+
fence_right_m: float | None = None,
|
|
253
|
+
fence_forward_m: float | None = None,
|
|
254
|
+
radar_height_m: float | None = None,
|
|
255
|
+
light_brightness_default: float | None = None,
|
|
256
|
+
) -> IndoorUnit:
|
|
257
|
+
"""Update indoor unit settings.
|
|
258
|
+
|
|
259
|
+
Includes presence fence geometry and default brightness.
|
|
260
|
+
"""
|
|
261
|
+
st = idu.settings
|
|
262
|
+
diff = hds.IndoorUnit(
|
|
263
|
+
header=hds.EntityMetadata(
|
|
264
|
+
object_id=idu.id,
|
|
265
|
+
system_id=idu.system_id,
|
|
266
|
+
),
|
|
267
|
+
settings=hds.IndoorUnitSettings(
|
|
268
|
+
updated_ts=_now_ts(),
|
|
269
|
+
name=st.name,
|
|
270
|
+
description=st.description,
|
|
271
|
+
light_brightness_default_percent=(
|
|
272
|
+
light_brightness_default
|
|
273
|
+
if light_brightness_default is not None
|
|
274
|
+
else st.light_brightness_default_percent
|
|
275
|
+
),
|
|
276
|
+
presence_fence_left_m=(
|
|
277
|
+
fence_left_m if fence_left_m is not None else st.presence_fence_left_m
|
|
278
|
+
),
|
|
279
|
+
presence_fence_right_m=(
|
|
280
|
+
fence_right_m if fence_right_m is not None else st.presence_fence_right_m
|
|
281
|
+
),
|
|
282
|
+
presence_fence_forward_m=(
|
|
283
|
+
fence_forward_m if fence_forward_m is not None else st.presence_fence_forward_m
|
|
284
|
+
),
|
|
285
|
+
radar_sensor_distance_from_floor_m=(
|
|
286
|
+
radar_height_m
|
|
287
|
+
if radar_height_m is not None
|
|
288
|
+
else st.radar_sensor_distance_from_floor_m
|
|
289
|
+
),
|
|
290
|
+
),
|
|
291
|
+
)
|
|
292
|
+
try:
|
|
293
|
+
result = await self._stub.UpdateIndoorUnit(hds.UpdateIndoorUnitRequest(diff=diff))
|
|
294
|
+
except grpc.aio.AioRpcError as exc:
|
|
295
|
+
raise QuiltError(f"UpdateIndoorUnit settings failed: {exc.details()}") from exc
|
|
296
|
+
return IndoorUnit.from_proto(result)
|
|
297
|
+
|
|
298
|
+
async def update_comfort_setting(
|
|
299
|
+
self,
|
|
300
|
+
setting: ComfortSetting,
|
|
301
|
+
*,
|
|
302
|
+
name: str | None = None,
|
|
303
|
+
hvac_mode: HVACMode | None = None,
|
|
304
|
+
heat_setpoint_c: float | None = None,
|
|
305
|
+
cool_setpoint_c: float | None = None,
|
|
306
|
+
fan_speed: FanSpeed | None = None,
|
|
307
|
+
) -> ComfortSetting:
|
|
308
|
+
"""Update a comfort setting preset."""
|
|
309
|
+
fan_mode_val, fan_pct = (
|
|
310
|
+
fan_speed.to_wire() if fan_speed is not None else setting.fan_speed.to_wire()
|
|
311
|
+
)
|
|
312
|
+
diff = hds.ComfortSetting(
|
|
313
|
+
header=hds.EntityMetadata(
|
|
314
|
+
object_id=setting.id,
|
|
315
|
+
system_id=setting.system_id,
|
|
316
|
+
),
|
|
317
|
+
attributes=hds.ComfortSettingAttributes(
|
|
318
|
+
updated_ts=_now_ts(),
|
|
319
|
+
name=name if name is not None else setting.name,
|
|
320
|
+
heating_temperature_setpoint_c=(
|
|
321
|
+
heat_setpoint_c if heat_setpoint_c is not None else setting.heating_setpoint_c
|
|
322
|
+
),
|
|
323
|
+
cooling_temperature_setpoint_c=(
|
|
324
|
+
cool_setpoint_c if cool_setpoint_c is not None else setting.cooling_setpoint_c
|
|
325
|
+
),
|
|
326
|
+
hvac_mode=cast(
|
|
327
|
+
"hds.HVACMode.ValueType",
|
|
328
|
+
hvac_mode.value if hvac_mode is not None else setting.hvac_mode.value,
|
|
329
|
+
),
|
|
330
|
+
fan_speed_mode=cast("hds.FanSpeedMode.ValueType", fan_mode_val),
|
|
331
|
+
fan_speed_percent=fan_pct,
|
|
332
|
+
type=cast("hds.ComfortSettingType.ValueType", setting.type.value),
|
|
333
|
+
),
|
|
334
|
+
)
|
|
335
|
+
try:
|
|
336
|
+
result = await self._stub.UpdateComfortSetting(
|
|
337
|
+
hds.UpdateComfortSettingRequest(comfort_setting=diff)
|
|
338
|
+
)
|
|
339
|
+
except grpc.aio.AioRpcError as exc:
|
|
340
|
+
raise QuiltError(f"UpdateComfortSetting failed: {exc.details()}") from exc
|
|
341
|
+
return ComfortSetting.from_proto(result)
|
|
342
|
+
|
|
343
|
+
async def create_schedule_day(
|
|
344
|
+
self,
|
|
345
|
+
system_id: str,
|
|
346
|
+
space_id: str,
|
|
347
|
+
name: str,
|
|
348
|
+
events: Sequence[ScheduleEvent | hds.ScheduleEvent],
|
|
349
|
+
) -> ScheduleDay:
|
|
350
|
+
"""Create a new schedule day program for a space."""
|
|
351
|
+
wire_events = [_to_wire_schedule_event(event) for event in events]
|
|
352
|
+
diff = hds.ScheduleDay(
|
|
353
|
+
header=hds.EntityMetadata(system_id=system_id),
|
|
354
|
+
attributes=hds.ScheduleDayAttributes(name=name),
|
|
355
|
+
relationships=hds.ScheduleDayRelationships(space_id=space_id),
|
|
356
|
+
events=wire_events,
|
|
357
|
+
)
|
|
358
|
+
try:
|
|
359
|
+
result = await self._stub.CreateScheduleDay(
|
|
360
|
+
hds.CreateScheduleDayRequest(schedule_day=diff)
|
|
361
|
+
)
|
|
362
|
+
except grpc.aio.AioRpcError as exc:
|
|
363
|
+
raise QuiltError(f"CreateScheduleDay failed: {exc.details()}") from exc
|
|
364
|
+
return ScheduleDay.from_proto(result)
|
|
365
|
+
|
|
366
|
+
async def create_schedule_week(
|
|
367
|
+
self,
|
|
368
|
+
system_id: str,
|
|
369
|
+
space_id: str,
|
|
370
|
+
days: Sequence[ScheduleWeekDay | hds.ScheduleWeekDay] | None = None,
|
|
371
|
+
) -> ScheduleWeek:
|
|
372
|
+
"""Create a new schedule week for a space."""
|
|
373
|
+
wire_days = [_to_wire_schedule_week_day(day) for day in (days or [])]
|
|
374
|
+
diff = hds.ScheduleWeek(
|
|
375
|
+
header=hds.EntityMetadata(system_id=system_id),
|
|
376
|
+
relationships=hds.ScheduleWeekRelationships(space_id=space_id),
|
|
377
|
+
days=wire_days,
|
|
378
|
+
)
|
|
379
|
+
try:
|
|
380
|
+
result = await self._stub.CreateScheduleWeek(
|
|
381
|
+
hds.CreateScheduleWeekRequest(schedule_week=diff)
|
|
382
|
+
)
|
|
383
|
+
except grpc.aio.AioRpcError as exc:
|
|
384
|
+
raise QuiltError(f"CreateScheduleWeek failed: {exc.details()}") from exc
|
|
385
|
+
return ScheduleWeek.from_proto(result)
|
|
386
|
+
|
|
387
|
+
async def update_schedule_week(
|
|
388
|
+
self,
|
|
389
|
+
schedule_week_id: str,
|
|
390
|
+
system_id: str,
|
|
391
|
+
space_id: str,
|
|
392
|
+
days: Sequence[ScheduleWeekDay | hds.ScheduleWeekDay],
|
|
393
|
+
) -> ScheduleWeek:
|
|
394
|
+
"""Update an existing schedule week."""
|
|
395
|
+
wire_days = [_to_wire_schedule_week_day(day) for day in days]
|
|
396
|
+
diff = hds.ScheduleWeek(
|
|
397
|
+
header=hds.EntityMetadata(
|
|
398
|
+
object_id=schedule_week_id,
|
|
399
|
+
system_id=system_id,
|
|
400
|
+
),
|
|
401
|
+
relationships=hds.ScheduleWeekRelationships(space_id=space_id),
|
|
402
|
+
days=wire_days,
|
|
403
|
+
)
|
|
404
|
+
try:
|
|
405
|
+
result = await self._stub.UpdateScheduleWeek(
|
|
406
|
+
hds.UpdateScheduleWeekRequest(schedule_week=diff)
|
|
407
|
+
)
|
|
408
|
+
except grpc.aio.AioRpcError as exc:
|
|
409
|
+
raise QuiltError(f"UpdateScheduleWeek failed: {exc.details()}") from exc
|
|
410
|
+
return ScheduleWeek.from_proto(result)
|
|
411
|
+
|
|
412
|
+
async def delete_schedule_day(self, schedule_day_id: str) -> None:
|
|
413
|
+
"""Delete a schedule day program."""
|
|
414
|
+
try:
|
|
415
|
+
await self._stub.DeleteScheduleDay(
|
|
416
|
+
hds.DeleteScheduleDayRequest(schedule_day_id=schedule_day_id)
|
|
417
|
+
)
|
|
418
|
+
except grpc.aio.AioRpcError as exc:
|
|
419
|
+
raise QuiltError(f"DeleteScheduleDay failed: {exc.details()}") from exc
|
|
420
|
+
|
|
421
|
+
async def update_schedule_day(
|
|
422
|
+
self,
|
|
423
|
+
schedule_day_id: str,
|
|
424
|
+
system_id: str,
|
|
425
|
+
space_id: str,
|
|
426
|
+
name: str | None = None,
|
|
427
|
+
events: Sequence[ScheduleEvent | hds.ScheduleEvent] | None = None,
|
|
428
|
+
) -> ScheduleDay:
|
|
429
|
+
"""Update an existing schedule day (name and/or events)."""
|
|
430
|
+
diff = hds.ScheduleDay(
|
|
431
|
+
header=hds.EntityMetadata(
|
|
432
|
+
object_id=schedule_day_id,
|
|
433
|
+
system_id=system_id,
|
|
434
|
+
),
|
|
435
|
+
relationships=hds.ScheduleDayRelationships(space_id=space_id),
|
|
436
|
+
)
|
|
437
|
+
if name is not None:
|
|
438
|
+
diff.attributes.CopyFrom(hds.ScheduleDayAttributes(name=name))
|
|
439
|
+
if events is not None:
|
|
440
|
+
diff.events.extend(_to_wire_schedule_event(event) for event in events)
|
|
441
|
+
try:
|
|
442
|
+
result = await self._stub.UpdateScheduleDay(
|
|
443
|
+
hds.UpdateScheduleDayRequest(schedule_day=diff)
|
|
444
|
+
)
|
|
445
|
+
except grpc.aio.AioRpcError as exc:
|
|
446
|
+
raise QuiltError(f"UpdateScheduleDay failed: {exc.details()}") from exc
|
|
447
|
+
return ScheduleDay.from_proto(result)
|
|
448
|
+
|
|
449
|
+
async def delete_schedule_week(self, schedule_week_id: str) -> None:
|
|
450
|
+
"""Delete a schedule week."""
|
|
451
|
+
try:
|
|
452
|
+
await self._stub.DeleteScheduleWeek(
|
|
453
|
+
hds.DeleteScheduleWeekRequest(schedule_week_id=schedule_week_id)
|
|
454
|
+
)
|
|
455
|
+
except grpc.aio.AioRpcError as exc:
|
|
456
|
+
raise QuiltError(f"DeleteScheduleWeek failed: {exc.details()}") from exc
|
|
457
|
+
|
|
458
|
+
async def update_location_schedule_execution(
|
|
459
|
+
self,
|
|
460
|
+
location_id: str,
|
|
461
|
+
system_id: str,
|
|
462
|
+
paused: bool,
|
|
463
|
+
) -> None:
|
|
464
|
+
"""Pause or resume all schedules for a location (global switch).
|
|
465
|
+
|
|
466
|
+
Args:
|
|
467
|
+
paused: True to pause all schedules, False to resume.
|
|
468
|
+
"""
|
|
469
|
+
execution = hds.SCHEDULE_EXECUTION_PAUSED if paused else hds.SCHEDULE_EXECUTION_RUNNING
|
|
470
|
+
diff = hds.Location(
|
|
471
|
+
header=hds.EntityMetadata(
|
|
472
|
+
object_id=location_id,
|
|
473
|
+
system_id=system_id,
|
|
474
|
+
),
|
|
475
|
+
controls=hds.LocationControls(schedule_execution=execution),
|
|
476
|
+
)
|
|
477
|
+
try:
|
|
478
|
+
await self._stub.UpdateLocation(hds.UpdateLocationRequest(location=diff))
|
|
479
|
+
except grpc.aio.AioRpcError as exc:
|
|
480
|
+
raise QuiltError(f"UpdateLocation failed: {exc.details()}") from exc
|