juno.notifications

Local notifications via UNUserNotificationCenter.

Schedule alert / sound / badge notifications to fire after a delay, list what’s currently pending, cancel one or all of them. The authorization prompt is handled with a single blocking native call — no Python-side polling — because Apple’s UNUserNotificationCenter.requestAuthorization is bounded (callback always fires once with the user’s decision, even if the status was already determined).

Typical usage:

from juno import notifications

if not notifications.is_authorized():
    notifications.request_authorization()
if not notifications.is_authorized():
    raise SystemExit("notification permission needed")

identifier = notifications.schedule(
    "Time to drink water",
    title="Hydration",
    delay=60.0,
)
pending = notifications.get_pending()
notifications.cancel(identifier)

Limitations:

  • provisional (iOS 12+ quiet-delivery) and ephemeral (iOS 14+ App Clip) authorization states are reported via authorization_status() but is_authorized() returns False for both. Branch on the status string if you need to distinguish.

  • Only the time-interval trigger is exposed (delay= seconds from now). Calendar / location triggers, attachments, custom sound files, and notification categories are not yet wrapped.

  • Already-delivered notifications (visible in Notification Center) are not surfaced or cancellable through this module.

  • Stop is responsive while the iOS authorization prompt is on screen — pressing Stop raises KeyboardInterrupt.

class juno.notifications.AuthorizationStatus

Bases: object

String constants returned by authorization_status().

class juno.notifications.Notification(identifier, title, body, seconds_from_now, has_badge, badge)

Bases: object

A single pending local notification.

seconds_from_now is the remaining time until the trigger fires, computed at the moment get_pending() was called. For a brief window during scheduling and just before fire it can be slightly off; treat it as approximate. For non-time- interval triggers (calendar, location — neither of which this module schedules but which may exist if the app schedules them by other means) it’s 0.0.

Parameters:
juno.notifications.authorization_status()

Return the current notification authorization status.

Returns:

One of the AuthorizationStatus string constants.

Return type:

str

juno.notifications.is_authorized()

Return True if the app has full notification authorization.

provisional and ephemeral return False — they don’t grant the standard alert / badge / sound surface this module schedules into.

Return type:

bool

juno.notifications.request_authorization(*, alert=True, sound=True, badge=True)

Request notification authorization with the given options.

On the first call after a fresh install this presents the iOS permission prompt and blocks until the user picks Allow / Don’t Allow. Subsequent calls return immediately with the cached decision. Stop is responsive throughout — the wait is interruptible by KeyboardInterrupt.

Parameters:
  • alert (bool) – Whether to request the alert presentation option (banner / lock-screen notification UI).

  • sound (bool) – Whether to request the sound option.

  • badge (bool) – Whether to request the badge option (red dot on the app icon).

Returns:

Resolved authorization status, one of the AuthorizationStatus constants.

Raises:
  • TypeError – If any of the option arguments is not a bool.

  • ValueError – If all three options are False (the system rejects an empty option set).

Return type:

str

juno.notifications.schedule(message, *, title='', delay, sound=True, badge=None)

Schedule a local notification.

Parameters:
  • message (str) – The notification body text. Required.

  • title (str) – Optional notification title (shown above the body). Empty string omits the title row.

  • delay (float) – Seconds from now to fire. Must be > 0.

  • sound (bool) – Whether to play the system default sound when the notification fires.

  • badge (int | None) – Badge value to set on the app icon when the notification fires. None means “don’t change the badge”; an int (typically 1) sets it.

Returns:

The system-assigned identifier (a UUID string). Pass it to cancel() to remove the notification before it fires.

Raises:
  • TypeError – If any argument has the wrong type.

  • ValueError – If message is empty, delay is not positive, or badge is negative.

  • PermissionError – If notification authorization is denied, restricted, or only provisional / ephemeral.

  • RuntimeError – If the system rejected the scheduling request.

Return type:

str

juno.notifications.cancel(identifier)

Cancel a pending notification by identifier.

Parameters:

identifier (str) – The string returned from an earlier schedule().

Returns:

True if the notification was actually pending and was cancelled, False if no such pending notification existed (already fired, never scheduled, or already cancelled).

Raises:

TypeError – If identifier is not a string.

Return type:

bool

juno.notifications.cancel_all()

Cancel every pending notification this app has scheduled.

Already-delivered notifications (visible in Notification Center) are not affected.

Return type:

None

juno.notifications.get_pending()

Return every pending notification this app has scheduled.

Returns:

A list of Notification records, possibly empty.

Raises:

RuntimeError – If the system query failed.

Return type:

list[Notification]