Skip to content

Commit 5d7e37d

Browse files
committed
feat: add timer live activity DX wrappers
1 parent 48900f1 commit 5d7e37d

3 files changed

Lines changed: 107 additions & 2 deletions

File tree

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,11 @@ activitysmith.notifications.send(
139139

140140
## Live Activities
141141

142-
There are five types of Live Activities:
142+
There are six types of Live Activities:
143143

144144
- `stats`: best for showing business numbers side by side, such as revenue, sales, new users, conversion, refunds, or any other value you want visible at a glance
145145
- `metrics`: best for live percentage values that change often, like server CPU, memory usage, disk usage, or error rate
146+
- `timer`: best for countdowns and elapsed timers, like benchmark runs, cooking timers, workouts, soak windows, and long-running jobs
146147
- `segmented_progress`: best for anything that moves through clear stages, like deployments, onboarding flows, backups, ETL pipelines, migrations, and AI agent runs
147148
- `progress`: best for tracking real-time progress with percentage, like tasks, backups, migrations, syncs, or uploads
148149
- `alert`: best for status updates, such as feature adoption, reactivation, onboarding blockers, incidents, escalations, and other operational states
@@ -250,6 +251,25 @@ activitysmith.live_activities.stream(
250251
)
251252
```
252253

254+
#### Timer
255+
256+
Use `duration_seconds` for a countdown. Omit `counts_down` for the default countdown behavior, or set `counts_down=False` for an elapsed timer.
257+
258+
```python
259+
activitysmith.live_activities.stream(
260+
"benchmark-run",
261+
content_state=content_state(
262+
title="Benchmark Run",
263+
subtitle="sampling performance",
264+
type="timer",
265+
duration_seconds=300,
266+
color="cyan",
267+
),
268+
)
269+
```
270+
271+
Later updates can change the title, subtitle, color, icon, or action without resetting the timer. Include `duration_seconds` again only when you want to restart the timer window from that update.
272+
253273
#### Alert
254274

255275
<p align="center">
@@ -382,7 +402,7 @@ Add more context to Live Activities with icons and badges.
382402

383403
#### Icon
384404

385-
Supported Live Activity types: `stats`, `metrics`, `progress`, `segmented_progress`, and `alert`.
405+
Supported Live Activity types: `stats`, `metrics`, `progress`, `segmented_progress`, `alert`, and `timer`.
386406

387407
<p align="center">
388408
<img src="https://cdn.activitysmith.com/features/metrics-live-activity-with-icon.png" alt="Metrics Live Activity with an SF Symbol icon on the iPhone Lock Screen" width="680" />

activitysmith/client.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ def content_state(
166166
percentage: int | float | None = None,
167167
value: int | float | None = None,
168168
upper_limit: int | float | None = None,
169+
duration_seconds: int | float | None = None,
170+
counts_down: bool | None = None,
169171
color: str | None = None,
170172
step_color: str | None = None,
171173
auto_dismiss_seconds: int | None = None,
@@ -185,6 +187,8 @@ def content_state(
185187
"percentage": percentage,
186188
"value": value,
187189
"upper_limit": upper_limit,
190+
"duration_seconds": duration_seconds,
191+
"counts_down": counts_down,
188192
"color": color,
189193
"step_color": step_color,
190194
"auto_dismiss_seconds": auto_dismiss_seconds,
@@ -265,6 +269,8 @@ def _build_live_activity_request(
265269
percentage: Any | None = None,
266270
value: Any | None = None,
267271
upper_limit: Any | None = None,
272+
duration_seconds: Any | None = None,
273+
counts_down: Any | None = None,
268274
color: Any | None = None,
269275
step_color: Any | None = None,
270276
auto_dismiss_seconds: Any | None = None,
@@ -288,6 +294,8 @@ def _build_live_activity_request(
288294
"percentage": percentage,
289295
"value": value,
290296
"upper_limit": upper_limit,
297+
"duration_seconds": duration_seconds,
298+
"counts_down": counts_down,
291299
"color": color,
292300
"step_color": step_color,
293301
"auto_dismiss_seconds": auto_dismiss_seconds,
@@ -387,6 +395,7 @@ class LiveActivitiesResource:
387395
TYPE_METRICS = "metrics"
388396
TYPE_STATS = "stats"
389397
TYPE_ALERT = "alert"
398+
TYPE_TIMER = "timer"
390399

391400
def __init__(self, api: LiveActivitiesApi) -> None:
392401
self._api = api
@@ -434,6 +443,8 @@ def start(
434443
percentage: Any | None = None,
435444
value: Any | None = None,
436445
upper_limit: Any | None = None,
446+
duration_seconds: Any | None = None,
447+
counts_down: Any | None = None,
437448
color: Any | None = None,
438449
step_color: Any | None = None,
439450
action: Any | None = None,
@@ -456,6 +467,8 @@ def start(
456467
percentage=percentage,
457468
value=value,
458469
upper_limit=upper_limit,
470+
duration_seconds=duration_seconds,
471+
counts_down=counts_down,
459472
color=color,
460473
step_color=step_color,
461474
action=action,
@@ -485,6 +498,8 @@ def update(
485498
percentage: Any | None = None,
486499
value: Any | None = None,
487500
upper_limit: Any | None = None,
501+
duration_seconds: Any | None = None,
502+
counts_down: Any | None = None,
488503
color: Any | None = None,
489504
step_color: Any | None = None,
490505
action: Any | None = None,
@@ -505,6 +520,8 @@ def update(
505520
percentage=percentage,
506521
value=value,
507522
upper_limit=upper_limit,
523+
duration_seconds=duration_seconds,
524+
counts_down=counts_down,
508525
color=color,
509526
step_color=step_color,
510527
action=action,
@@ -529,6 +546,8 @@ def end(
529546
percentage: Any | None = None,
530547
value: Any | None = None,
531548
upper_limit: Any | None = None,
549+
duration_seconds: Any | None = None,
550+
counts_down: Any | None = None,
532551
color: Any | None = None,
533552
step_color: Any | None = None,
534553
auto_dismiss_minutes: Any | None = None,
@@ -550,6 +569,8 @@ def end(
550569
percentage=percentage,
551570
value=value,
552571
upper_limit=upper_limit,
572+
duration_seconds=duration_seconds,
573+
counts_down=counts_down,
553574
color=color,
554575
step_color=step_color,
555576
auto_dismiss_minutes=auto_dismiss_minutes,
@@ -575,6 +596,8 @@ def stream(
575596
percentage: Any | None = None,
576597
value: Any | None = None,
577598
upper_limit: Any | None = None,
599+
duration_seconds: Any | None = None,
600+
counts_down: Any | None = None,
578601
color: Any | None = None,
579602
step_color: Any | None = None,
580603
action: Any | None = None,
@@ -597,6 +620,8 @@ def stream(
597620
percentage=percentage,
598621
value=value,
599622
upper_limit=upper_limit,
623+
duration_seconds=duration_seconds,
624+
counts_down=counts_down,
600625
color=color,
601626
step_color=step_color,
602627
action=action,
@@ -627,6 +652,8 @@ def end_stream(
627652
percentage: Any | None = None,
628653
value: Any | None = None,
629654
upper_limit: Any | None = None,
655+
duration_seconds: Any | None = None,
656+
counts_down: Any | None = None,
630657
color: Any | None = None,
631658
step_color: Any | None = None,
632659
auto_dismiss_minutes: Any | None = None,
@@ -648,6 +675,8 @@ def end_stream(
648675
percentage=percentage,
649676
value=value,
650677
upper_limit=upper_limit,
678+
duration_seconds=duration_seconds,
679+
counts_down=counts_down,
651680
color=color,
652681
step_color=step_color,
653682
auto_dismiss_minutes=auto_dismiss_minutes,

tests/test_resources.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,62 @@ def test_live_activities_support_progress_payloads(monkeypatch):
305305
]
306306

307307

308+
def test_live_activities_support_timer_payloads(monkeypatch):
309+
monkeypatch.setattr(client_module, "PushNotificationsApi", FakePushNotificationsApi)
310+
monkeypatch.setattr(client_module, "LiveActivitiesApi", FakeLiveActivitiesApi)
311+
monkeypatch.setattr(client_module, "MetricsApi", FakeMetricsApi)
312+
313+
client = ActivitySmith(api_key="x")
314+
315+
client.live_activities.start(
316+
title="Benchmark Run",
317+
subtitle="sampling performance",
318+
type=client.live_activities.TYPE_TIMER,
319+
duration_seconds=300,
320+
counts_down=True,
321+
color="cyan",
322+
)
323+
client.live_activities.update(
324+
activity_id="act-1",
325+
title="Benchmark Run",
326+
type=client.live_activities.TYPE_TIMER,
327+
subtitle="complete",
328+
color="cyan",
329+
)
330+
331+
assert client.live_activities._api.calls == [
332+
(
333+
"start",
334+
{
335+
"live_activity_start_request": {
336+
"content_state": {
337+
"title": "Benchmark Run",
338+
"subtitle": "sampling performance",
339+
"type": "timer",
340+
"duration_seconds": 300,
341+
"counts_down": True,
342+
"color": "cyan",
343+
}
344+
}
345+
},
346+
),
347+
(
348+
"update",
349+
{
350+
"live_activity_update_request": {
351+
"activity_id": "act-1",
352+
"content_state": {
353+
"title": "Benchmark Run",
354+
"type": "timer",
355+
"subtitle": "complete",
356+
"color": "cyan",
357+
},
358+
}
359+
},
360+
),
361+
]
362+
363+
308364
def test_live_activities_support_stats_payloads(monkeypatch):
309365
monkeypatch.setattr(client_module, "PushNotificationsApi", FakePushNotificationsApi)
310366
monkeypatch.setattr(client_module, "LiveActivitiesApi", FakeLiveActivitiesApi)

0 commit comments

Comments
 (0)