@calebcall/camera-ui-notify 0.5.6 → 0.6.0

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ All notable changes to **Notify** are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.0] - 2026-08-01
9
+
10
+ ### Added
11
+
12
+ - **Grafana backend** with three delivery modes selected by a **Mode** field:
13
+ - **Annotations** — a point-in-time, organization-wide annotation via `POST /api/annotations`,
14
+ tagged `camera.ui` / `camera:<id>` / `severity:<level>` plus any extra tags, so dashboards can
15
+ surface camera events through a tag-filtered annotation query.
16
+ - **Alerts** — a firing alert via `POST /api/alertmanager/grafana/api/v2/alerts`, routed by your
17
+ existing notification policies. `endsAt` is `startsAt + grafana_ttl` (default 300s) so Grafana
18
+ auto-resolves it with no second request, and a unique `event_id` label keeps Alertmanager from
19
+ deduplicating two detections on the same camera into one alert.
20
+ - **IRM** — one alert group per event via a Grafana IRM / OnCall inbound webhook, using the
21
+ formatted-webhook field set. The only Grafana mode that renders the snapshot, and only when the
22
+ publisher supplied a hosted `ImageURL`.
23
+
24
+ The integration URL for IRM embeds its own token, so it is a masked field and is stripped from
25
+ every error message, including transport failures.
26
+
8
27
  ## [0.5.6] - 2026-08-01
9
28
 
10
29
  ### Changed
package/README.md CHANGED
@@ -3,7 +3,8 @@
3
3
  <p align="center">
4
4
  A fully-local, multi-backend <a href="https://github.com/seydx/camera.ui">camera.ui</a> notifier
5
5
  plugin — delivers notifications to a service you control (<a href="https://ntfy.sh">ntfy</a>,
6
- <a href="https://gotify.net">Gotify</a>, Pushover, Telegram, Discord, or a generic webhook).
6
+ <a href="https://gotify.net">Gotify</a>, Pushover, Telegram, Discord,
7
+ <a href="https://grafana.com">Grafana</a>, or a generic webhook).
7
8
  </p>
8
9
 
9
10
  ---
@@ -32,7 +33,7 @@ Adding a new backend later is **one new file** — `src/backend/<name>.go` imple
32
33
 
33
34
  ## Backends (v1)
34
35
 
35
- Severity is mapped consistently across backends via `backend.PriorityScale`, which spreads camera.ui's four severity levels (`info` → `warn` → `error` → `critical`) evenly across each backend's native priority range, `info` at the low end and `critical` at the high end.
36
+ Severity is mapped consistently across backends via `backend.PriorityScale`, which spreads camera.ui's four severity levels (`info` → `warn` → `error` → `critical`) evenly across each backend's native priority range, `info` at the low end and `critical` at the high end. Grafana is the exception: no Grafana surface has a numeric priority, so severity travels verbatim as a label/tag.
36
37
 
37
38
  ### ntfy
38
39
 
@@ -102,8 +103,44 @@ Delivers to a channel via a Discord [webhook](https://support.discord.com/hc/en-
102
103
 
103
104
  Delivery: a rich embed (title, body, severity color — blue/yellow/red) with the snapshot **image** attached; an absolute deep link makes the title a link.
104
105
 
106
+ ### Grafana
107
+
108
+ Delivers to a [Grafana](https://grafana.com) instance. Grafana is not one ingest endpoint, so a
109
+ **Mode** field selects which surface receives the notification.
110
+
111
+ | Field | Required | Mode | Notes |
112
+ | ----------------------- | -------- | ----------------------- | --------------------------------------------------------- |
113
+ | `grafana_mode` | yes | — | `annotations`, `alerts`, or `irm`. Defaults to `annotations`. |
114
+ | `grafana_server` | yes | annotations, alerts | Base URL of the Grafana instance. Trailing `/` trimmed. |
115
+ | `grafana_token` | yes | annotations, alerts | Service-account token, sent as `Authorization: Bearer <token>`. |
116
+ | `grafana_tags` | no | annotations | Comma-separated extra tags. |
117
+ | `grafana_alertname` | no | alerts | `alertname` label. Defaults to `CameraUINotification`. |
118
+ | `grafana_ttl` | no | alerts | Seconds before Grafana auto-resolves the alert. Default `300`, minimum `30`. |
119
+ | `grafana_irm_url` | yes | irm | Inbound webhook URL of an IRM / OnCall integration. The token is in the URL, so it is masked and kept out of every error message. |
120
+
121
+ **Annotations** — `POST {server}/api/annotations` with a point-in-time, organization-wide
122
+ annotation tagged `camera.ui`, `camera:<id>`, `severity:<level>`, plus your extra tags. Surface it
123
+ on a dashboard with an annotation query filtered on the `camera.ui` tag; that survives dashboard
124
+ renames, which pinning to a dashboard UID would not. The tooltip text carries the title, the body,
125
+ and — when `base_url` is set — a link back to camera.ui.
126
+
127
+ **Alerts** — `POST {server}/api/alertmanager/grafana/api/v2/alerts`, so your existing notification
128
+ policies route the event. `endsAt` is `startsAt + grafana_ttl`, which lets Grafana auto-resolve the
129
+ alert without a second request. Labels are `alertname`, `source=camera.ui`, `severity` (camera.ui's
130
+ own four levels, verbatim), `camera`, and a unique `event_id` — the last of these matters, because
131
+ Alertmanager deduplicates on the label set and without it two detections on one camera inside the
132
+ TTL window would collapse into a single alert. The absolute deep link becomes `generatorURL`,
133
+ which Grafana shows as **Source**.
134
+
135
+ **IRM** — `POST {integration URL}` using Grafana IRM / OnCall's formatted-webhook fields
136
+ (`alert_uid`, `title`, `message`, `image_url`, `link_to_upstream_details`, `state=alerting`), one
137
+ alert group per event.
138
+
105
139
  > **Images:** ntfy, Pushover, Telegram, and Discord all render the detection snapshot. Gotify is
106
140
  > text + link only (it needs a hosted image URL, which this fully-local plugin doesn't provide).
141
+ > Grafana renders one only in IRM mode, and only when the publisher supplied a hosted `ImageURL` —
142
+ > annotations have no image field at all, and alerts carry the URL as an `image_url` annotation
143
+ > that Grafana itself won't render but downstream notification templates can use.
107
144
 
108
145
  > **Secrets in logs:** transport failures never log the bot token / webhook URL / other
109
146
  > URL-embedded secret — request URLs are redacted from delivery errors.
@@ -113,7 +150,7 @@ Delivery: a rich embed (title, body, severity color — blue/yellow/red) with th
113
150
  There is no "add device" flow. Instead, configure the plugin itself:
114
151
 
115
152
  1. Open the **Notify** plugin's page in camera.ui (Plugins → Notify).
116
- 2. In its settings, pick a **Service** (`ntfy`, `Gotify`, `Generic webhook`, `Pushover`, `Telegram`, or `Discord`) from the dropdown built from the registered backends.
153
+ 2. In its settings, pick a **Service** (`ntfy`, `Gotify`, `Generic webhook`, `Pushover`, `Telegram`, `Discord`, or `Grafana`) from the dropdown built from the registered backends.
117
154
  3. Fill in that service's fields — only the selected service's fields are shown; the rest are condition-gated out.
118
155
  4. Save. The config is validated (`ParseTarget`) the next time a notification is dispatched; `getDevices` then synthesizes one delivery target from it, and notifications from any publisher are delivered there.
119
156
 
package/bundle.zip CHANGED
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "Notify",
3
3
  "name": "@calebcall/camera-ui-notify",
4
- "version": "0.5.6",
4
+ "version": "0.6.0",
5
5
  "description": "A fully-local, multi-backend camera.ui notifier plugin: delivers notifications to ntfy, Gotify, or a generic webhook, entirely on your own hardware with no cloud dependency.",
6
6
  "author": "calebcall (https://github.com/calebcall/camera-ui-notify)",
7
7
  "main": "./main.go",
@@ -48,14 +48,14 @@
48
48
  "protocolLevel": 1
49
49
  },
50
50
  "optionalDependencies": {
51
- "@calebcall/camera-ui-notify-darwin-arm64": "0.5.6",
52
- "@calebcall/camera-ui-notify-darwin-amd64": "0.5.6",
53
- "@calebcall/camera-ui-notify-linux-amd64": "0.5.6",
54
- "@calebcall/camera-ui-notify-linux-arm64": "0.5.6",
55
- "@calebcall/camera-ui-notify-windows-amd64": "0.5.6",
56
- "@calebcall/camera-ui-notify-windows-arm64": "0.5.6",
57
- "@calebcall/camera-ui-notify-linux-amd64-musl": "0.5.6",
58
- "@calebcall/camera-ui-notify-linux-arm64-musl": "0.5.6"
51
+ "@calebcall/camera-ui-notify-darwin-arm64": "0.6.0",
52
+ "@calebcall/camera-ui-notify-darwin-amd64": "0.6.0",
53
+ "@calebcall/camera-ui-notify-linux-amd64": "0.6.0",
54
+ "@calebcall/camera-ui-notify-linux-arm64": "0.6.0",
55
+ "@calebcall/camera-ui-notify-windows-amd64": "0.6.0",
56
+ "@calebcall/camera-ui-notify-windows-arm64": "0.6.0",
57
+ "@calebcall/camera-ui-notify-linux-amd64-musl": "0.6.0",
58
+ "@calebcall/camera-ui-notify-linux-arm64-musl": "0.6.0"
59
59
  },
60
60
  "files": [
61
61
  "bundle.zip",