The companion app turns an absolute https `clickAction` into a bare ACTION_VIEW intent, which Android 12+ hands to the default browser — a Chrome-minted WebAPK never has verified links for its host, so every notify/ask tap opened a browser tab next to the installed app. New optional `pwa_package` / `pwa_origin` config: a notification URL on the PWA's origin is sent as an `intent:` URI pinned to the WebAPK's package (path + query kept), which the app launches by package. Other hosts stay plain links; empty package = previous behaviour. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
103 lines
5.2 KiB
Python
103 lines
5.2 KiB
Python
"""Constants for the AI Agent notifications integration."""
|
|
|
|
DOMAIN = "ai_agent"
|
|
|
|
CONF_WEBHOOK_ID = "webhook_id"
|
|
CONF_NOTIFY_TARGET = "notify_target"
|
|
CONF_CALLBACK_URL = "callback_url"
|
|
# Optional "<domain>.<service>" called for the *voice* leg of a notify — the
|
|
# homelab points it at `script.ai_agent_phone_notify`, which rings the AI desk
|
|
# phone (services/phone). Empty = screen notifications only.
|
|
CONF_PHONE_SERVICE = "phone_service"
|
|
# Optional: the Android package of the ai-agent PWA installed from Chrome (a
|
|
# WebAPK, `org.chromium.webapk.<hash>` — read it off chrome://webapks). When
|
|
# set, a notification URL on `pwa_origin` is sent as an `intent:` URI that
|
|
# names that package, so tapping the push opens the installed app instead of
|
|
# a browser tab. Empty = plain https URLs (browser tab).
|
|
CONF_PWA_PACKAGE = "pwa_package"
|
|
CONF_PWA_ORIGIN = "pwa_origin"
|
|
|
|
DEFAULT_WEBHOOK_ID = "ai_agent"
|
|
DEFAULT_NOTIFY_TARGET = "mobile_app_pixel_9"
|
|
# The ai-agent backend's host-published port (HA runs on the host network).
|
|
DEFAULT_CALLBACK_URL = "http://127.0.0.1:8096"
|
|
DEFAULT_PHONE_SERVICE = ""
|
|
DEFAULT_PWA_PACKAGE = ""
|
|
DEFAULT_PWA_ORIGIN = "https://ai-agent.lab.gabvdl.xyz"
|
|
|
|
# Payload fields handed to the phone service. `spoken` is the phone-only line
|
|
# notify-done sends; `title`/`message` are what the phone reads when there is
|
|
# none; `type` picks the jingle; `audio` is an optional custom clip.
|
|
PHONE_FIELDS = ("spoken", "type", "title", "message", "audio")
|
|
|
|
# Bus events fired for every handled payload, so HA automations can react to
|
|
# agent activity (flash a light on an error, log asks, …) without re-parsing
|
|
# the webhook.
|
|
EVENT_NOTIFY = "ai_agent_notify"
|
|
EVENT_ASK = "ai_agent_ask"
|
|
|
|
# Asks get their own Android notification channel so they buzz distinctly.
|
|
# NOTE: Android locks a channel's importance/vibration when the channel is
|
|
# first created — to re-tune, rename the channel (or clear the companion app's
|
|
# storage).
|
|
ASK_CHANNEL = "Claude · Ask"
|
|
ASK_IMPORTANCE = "max"
|
|
# Vibration pattern ". .. .._" in morse-ish groups (ms: delay, on, off, on, …):
|
|
# dot = 100ms buzz, dash = 450ms buzz,
|
|
# gap inside a group = 120ms, gap between groups = 350ms.
|
|
# ".": 100
|
|
# "..": 100 120 100
|
|
# ".._": 100 120 100 120 450
|
|
ASK_VIBRATION = "0, 100, 350, 100, 120, 100, 350, 100, 120, 100, 120, 450"
|
|
ASK_ICON = "mdi:comment-question"
|
|
ASK_COLOR = "#7c3aed"
|
|
|
|
# Per-type icon / color / channel / importance / vibration defaults, mirroring
|
|
# the notify-done skill's notify.sh map. Explicit fields in the webhook payload
|
|
# always win over these.
|
|
TYPE_STYLES = {
|
|
"deploy": ("mdi:rocket-launch", "#7c3aed", "Claude · Deploy", "high",
|
|
"0, 200, 100, 200, 100, 400"),
|
|
"code": ("mdi:code-tags", "#2563eb", "Claude · Code", "default", "0, 120"),
|
|
"config": ("mdi:code-tags", "#2563eb", "Claude · Code", "default", "0, 120"),
|
|
"build": ("mdi:hammer-wrench", "#d97706", "Claude · Build", "high",
|
|
"0, 150, 80, 150"),
|
|
"git": ("mdi:source-branch", "#ea580c", "Claude · Git", "default", "0, 100"),
|
|
"commit": ("mdi:source-branch", "#ea580c", "Claude · Git", "default", "0, 100"),
|
|
"service": ("mdi:docker", "#0ea5e9", "Claude · Service", "default", "0, 150"),
|
|
"container": ("mdi:docker", "#0ea5e9", "Claude · Service", "default", "0, 150"),
|
|
"ai": ("mdi:robot", "#9333ea", "Claude · AI", "high",
|
|
"0, 100, 50, 100, 50, 100"),
|
|
"mcp": ("mdi:robot", "#9333ea", "Claude · AI", "high",
|
|
"0, 100, 50, 100, 50, 100"),
|
|
"music": ("mdi:music", "#16a34a", "Claude · Music", "default", "0, 120"),
|
|
"download": ("mdi:music", "#16a34a", "Claude · Music", "default", "0, 120"),
|
|
"media": ("mdi:movie-open", "#db2777", "Claude · Media", "default", "0, 120"),
|
|
"jellyfin": ("mdi:movie-open", "#db2777", "Claude · Media", "default", "0, 120"),
|
|
"research": ("mdi:book-search-outline", "#0d9488", "Claude · Research",
|
|
"high", "0, 150, 80, 150"),
|
|
"logs": ("mdi:chart-line", "#65a30d", "Claude · Logs", "default", "0, 100"),
|
|
"metrics": ("mdi:chart-line", "#65a30d", "Claude · Logs", "default", "0, 100"),
|
|
"dns": ("mdi:dns", "#0891b2", "Claude · DNS", "default", "0, 100"),
|
|
"auth": ("mdi:shield-lock", "#dc2626", "Claude · Security", "max",
|
|
"0, 300, 150, 300, 150, 300"),
|
|
"security": ("mdi:shield-lock", "#dc2626", "Claude · Security", "max",
|
|
"0, 300, 150, 300, 150, 300"),
|
|
"mail": ("mdi:email", "#4f46e5", "Claude · Mail", "high", "0, 120"),
|
|
"success": ("mdi:check-circle", "#16a34a", "Claude · Success", "high",
|
|
"0, 150, 75, 150"),
|
|
"done": ("mdi:check-circle", "#16a34a", "Claude · Success", "high",
|
|
"0, 150, 75, 150"),
|
|
"backup": ("mdi:cloud-upload", "#0d9488", "Claude · Backup", "default",
|
|
"0, 120"),
|
|
"error": ("mdi:alert-circle", "#dc2626", "Claude · Error", "max",
|
|
"0, 400, 150, 400, 150, 400"),
|
|
"fail": ("mdi:alert-circle", "#dc2626", "Claude · Error", "max",
|
|
"0, 400, 150, 400, 150, 400"),
|
|
"warn": ("mdi:alert", "#d97706", "Claude · Warning", "high",
|
|
"0, 300, 150, 300"),
|
|
"warning": ("mdi:alert", "#d97706", "Claude · Warning", "high",
|
|
"0, 300, 150, 300"),
|
|
}
|
|
DEFAULT_STYLE = ("mdi:bell", "#6b7280", "Claude Code", "high", "0, 250")
|