Documentation

Triggers CLI commands

Take screens over from CAP emergency alerts and show overlays posted by Home Assistant, sensors or any HTTP client: create, test and rotate triggers.

Every command here runs as described in the CLI reference: global flags, JSON output, exit codes, revisions and retries.

CAP alert triggers

A trigger lets an emergency notification system, such as Everbridge, Rave, Alertus or an IPAWS relay, take screens over with CAP 1.2 alerts. Each trigger has its own URL, username and password, which you enter in the sending system as its CAP HTTP POST destination with HTTP Basic authentication. An organization has at most 20 triggers across its projects.

Triggers come in two kinds. A CAP trigger, described here, takes an emergency system's alerts. An overlay trigger shows what any sender posts, such as Home Assistant, a sensor hub or an alerting tool, as a bar over what plays or as a takeover; triggers create --kind overlay makes one, and triggers update, triggers clear and triggers rotate-secret serve both kinds, while triggers deliveries reads an overlay trigger's log.

A trigger targets a tag, resolved when each alert arrives, or up to 500 screen ids. Each Alert becomes a takeover of the trigger's own playlist: a full-screen card in the colour of its severity, with the event, headline, description and instruction. An Exercise adds an EXERCISE banner. The takeover holds until the alert's <expires>, else the trigger's --for default (1 minute to 7 days, 1 hour unless set), and never past 7 days. An Update redraws the card, and a Cancel or AllClear gives every screen back to what it played before. A new Alert replaces the one showing. Test, System and Draft messages, Ack and Error messages and expired alerts are ignored, and a repeated sender and identifier is a duplicate. A screen needs a default playlist to be taken over; other screens are skipped.

The password, srtg_…, is shown only by triggers create and triggers rotate-secret, with the warning trigger_password_shown_once. The CLI keeps it out of its configuration, operation log and recovery state. The URL and username are not secret.

Problem codeExitMeaning
trigger_limit_reached5The organization already has 20 triggers. Nothing was created. Delete one first.
resource_conflict5triggers test or triggers delete while an alert is active. next is triggers clear.
not_found4No trigger has this id in the project.
revision_conflict6The --expect-rev revision was stale. Run triggers show, then retry.

triggers create#

Create a trigger and receive its URL, username and password.

triggers create --name NAME [--kind cap|overlay] (--tag TAG | --screen-id ID...) [--for DURATION] [--pull FILE]
    [--show bar|takeover] [--edge bottom|top] [--style FILE] [--map FILE] [--signing]
    [--verify VENDOR [--verify-secret-file FILE] [--verify-header NAME] [--verify-encoding hex|base64]
        [--verify-timestamp-header NAME] [--verify-tolerance SECONDS]]
    [--handshake VENDOR]
ParameterTypeRequiredDefaultNotes
--name NAMEstringyesnoneThe trigger's name, 1 to 80 characters without control characters.
--kind KINDcap or overlaynocapcap takes an emergency system's CAP 1.2 alerts. overlay takes a few words from any sender and needs --show.
--tag TAGstringone of --tag or --screen-idnoneEvery active screen with this tag when an alert arrives.
--screen-id IDstringone of --tag or --screen-idnoneA scr_ screen id. Repeat it, or list ids comma-separated; at most 500.
--for DURATIONdurationno1h (overlay 10m)How long an alert without <expires>, or an overlay post without a duration, holds the screens, such as 30m, 2h or 1d; 1 minute to 7 days for a CAP trigger, 10 seconds to 7 days for an overlay trigger.
--pull FILEpathnononeA CAP trigger: a JSON object, from a file or - for stdin, that also has the platform read CAP alert sources for the trigger. Its members are the params of the cap-alert card, listed below.
--show SHOWbar or takeoverwith --kind overlaynonebar draws a strip over whatever plays on the target screens. takeover plays the alert full screen.
--edge EDGEbottom or topnobottomThe edge a bar takes. Only with --show bar.
--style FILEpathnononeA JSON object, from a file or - for stdin, of the text card's style: theme, accent, background, backgroundColor, textColor and align. At most 64 KiB.
--map FILEpathnononeA JSON template, from a file or - for stdin, that builds the overlay post from the body a sender posts; see overlay triggers. At most 64 KiB.
--signingflagnooffAlso require a ScreenRig-Signature header on every post, and print the signing secret once. Not with --verify.
--verify VENDORstringnononeAn overlay trigger: accept a post that carries this vendor's own signature in place of the srtg_ password, which keeps working beside it. github, stripe, shopify, slack, linear, vercel, asana or generic_hmac.
--verify-secret-file FILEpathwith --verify, except asananoneThe vendor's secret, from a file or - for stdin, never an argument: the webhook secret GitHub, Stripe, Shopify, Slack, Linear or Vercel shows, or a generic_hmac sender's. asana takes none: its handshake delivers it.
--verify-header NAMEstringwith --verify generic_hmacnoneThe header that carries the signature.
--verify-encoding ENCODINGhex or base64nohexWith --verify generic_hmac: how the signature is written. It may begin with sha256=.
--verify-timestamp-header NAMEstringnononeWith --verify generic_hmac: a header that carries the Unix time in seconds, or in milliseconds as 13 digits. The signed text is then <timestamp>.<body>.
--verify-tolerance SECONDSintegerno300With --verify-timestamp-header: how far the timestamp may sit from the server's clock, 1 to 86400.
--handshake VENDORstringnononeAn overlay trigger: answer the setup challenge of microsoft_graph, monday, trello or azure_event_grid. Slack and Asana answer theirs through --verify.
screenrig triggers create --name "Campus alerts" --tag Campus
screenrig triggers create --name "Campus alerts" --tag Campus --pull pull.json
screenrig triggers create --name "Lobby sensors" --kind overlay --show bar --tag Lobby
screenrig triggers create --name "Doorbell" --kind overlay --show takeover --tag Lobby --for 30s
screenrig triggers create --name "Payments" --kind overlay --show bar --tag Lobby --verify stripe --verify-secret-file stripe.secret --map stripe-map.json
screenrig triggers create --name "Boards" --kind overlay --show bar --tag Lobby --handshake trello

The envelope is the trigger with data.url (https://api.screenrig.ai/api/triggers/<id>/cap, or .../overlay for an overlay trigger), data.username (the trigger id) and data.password. Enter them in the sending system now; an overlay trigger's sender needs the url and the password (Authorization: Bearer). With --signing the envelope also carries data.signing_secret, shown once, with the warning trigger_signing_secret_shown_once. After an interrupted or ambiguous create, rerun the identical command within 24 hours: it reuses the saved idempotency key, and the server returns the same trigger and password instead of creating another.

--pull adds sources the platform reads beside the posted URL, which keeps working. The object holds sources (up to 5 http or https URLs of CAP 1.0 to 1.2 alerts or ATOM/RSS indexes), publicAlerts, location (name, latitude, longitude, and geocodes as [{name, value}]), minSeverity, maxSeverity, minUrgency, minCertainty, events, excludeEvents, senders, statuses, scopes, languages and areaUnknown; feeds get cap takes the same filters as flags. location is required unless sources are given, and with no sources the public feeds covering location are read. Each source is fetched once a minute. The most severe matching alert takes the screens over like a posted one, and when the sources show none a pulled alert ends; a stale or failing source never ends an alert. For example {"location": {"latitude": 49.25, "longitude": -123}, "minSeverity": "moderate"}.

The overlay options (--show, --edge, --style, --map, --signing, --verify with its companions, and --handshake) need --kind overlay, and --pull needs a CAP trigger. --style, --map and --verify-secret-file each read a file, and only one of them can read - (stdin).

--verify VENDOR lets a vendor's own webhook post straight to data.url: a post that carries the vendor's valid signature in its own header is accepted without data.password, which keeps working beside it. The vendor's secret is stored sealed and read back only as overlay.verify.secret, with set and its last four characters. asana takes no secret: its handshake delivers one, and events answer secret_pending until then. generic_hmac needs --verify-header and takes --verify-encoding, --verify-timestamp-header and --verify-tolerance. --handshake VENDOR answers that vendor's setup challenge before any credential is asked; it changes no alert, is never billed and shows in triggers deliveries as outcome handshake. The vendors, their headers and the settings to enter at each are listed under vendor signatures and handshakes.

triggers list#

List this project's triggers and their latest alerts, oldest first.

triggers list
screenrig triggers list

--human prints ID, KIND, LABEL, TARGET, ALERT and LAST_RECEIVED; for an overlay trigger ALERT says what it draws and how many alerts it holds. A list never carries a password or a signing secret.

triggers show#

Inspect a trigger and its latest alert.

triggers show <id>
ParameterTypeRequiredDefaultNotes
<id>stringyesnoneThe trg_ trigger id.
screenrig triggers show trg_01EXAMPLETRIGGER00

data.alert is the latest alert: its CAP fields, state active or ended (with ended_reason cancelled, all_clear, cleared or expired), until, and screens with targeted, taken_over, cleared, skipped, failed and pending. pending is true while screens are still being written, and failed screens are retried. data.playlist_id is the trigger's alert playlist, and screen show reports an alert as a takeover of it. For an overlay trigger data.overlay holds show, edge, enabled, signing, verify and handshake while set, card_id, alerts (the active alerts, newest first; the first shows), screens with targeted, holding, taken_over, removed, edge_taken, skipped and failed, and pending.

triggers test#

Show a 60-second exercise alert on the trigger's screens.

triggers test <id>
ParameterTypeRequiredDefaultNotes
<id>stringyesnoneThe trg_ trigger id.
screenrig triggers test trg_01EXAMPLETRIGGER00

The envelope is the receipt: data.outcome shown and data.alert. The answer arrives before every screen is written; triggers show reports the counts. An overlay trigger shows the alert screenrig-test (label Test, title Test overlay) for 60 seconds, and the receipt is the one a post gets (data.alert_id, data.until, data.active). Both kinds refuse a test while an alert shows.

triggers update#

Change a trigger without changing its URL or secret.

triggers update <id> [--name NAME] [--tag TAG | --screen-id ID...] [--for DURATION] [--pull FILE | --no-pull]
    [--edge bottom|top] [--style FILE | --clear-style] [--map FILE | --clear-map] [--enable | --disable]
    [--signing | --no-signing]
    [--verify VENDOR [--verify-secret-file FILE] [--verify-header NAME] [--verify-encoding hex|base64]
        [--verify-timestamp-header NAME] [--verify-tolerance SECONDS] | --clear-verify]
    [--handshake VENDOR | --clear-handshake] [--expect-rev REVISION]
ParameterTypeRequiredDefaultNotes
<id>stringyesnoneThe trg_ trigger id.
--name NAMEstringnounchangedRename the trigger.
--tag TAG, --screen-id IDstringnounchangedA new target, as in triggers create.
--for DURATIONdurationnounchangedA new hold for alerts that name no end: 1 minute to 7 days for a CAP trigger, 10 seconds to 7 days for an overlay trigger.
--pull FILE, --no-pullpath, flagnounchangedA CAP trigger: replace its CAP sources, or stop reading them.
--edge EDGEbottom or topnounchangedAn overlay bar: move it to this edge.
--style FILE, --clear-stylepath, flagnounchangedAn overlay trigger: replace the whole style, or remove it.
--map FILE, --clear-mappath, flagnounchangedAn overlay trigger: replace the template, or remove it so the posted object is the post.
--enable, --disableflagnounchangedAn overlay trigger: a disabled trigger answers every post with the 404 a wrong secret gets.
--signing, --no-signingflagnounchangedAn overlay trigger: turn the signature check on, printing the signing secret once, or off. Not with --verify.
--verify VENDORstringnounchangedAn overlay trigger: replace the vendor verification, as in triggers create. Without --verify-secret-file it keeps the stored secret when the vendor stays the same; a different vendor needs its own.
--verify-secret-file FILEpathnounchangedWith --verify: the vendor's secret, from a file or - for stdin.
--verify-header NAMEstringnounchangedWith --verify generic_hmac: the header that carries the signature.
--verify-encoding ENCODINGhex or base64nounchangedWith --verify generic_hmac: how the signature is written.
--verify-timestamp-header NAMEstringnounchangedWith --verify generic_hmac: a header that carries the Unix time.
--verify-tolerance SECONDSintegernounchangedWith --verify-timestamp-header: how far the timestamp may sit from the server's clock, 1 to 86400.
--clear-verifyflagnooffAn overlay trigger: stop accepting a vendor's signature and forget its secret.
--handshake VENDORstringnounchangedAn overlay trigger: answer the setup challenge of microsoft_graph, monday, trello or azure_event_grid.
--clear-handshakeflagnooffAn overlay trigger: stop answering the vendor handshake.
--expect-rev REVISIONstringnononeOptional revision precondition.
screenrig triggers update trg_01EXAMPLETRIGGER00 --tag Lobby
screenrig triggers update trg_01EXAMPLETRIGGER00 --edge top --style style.json
screenrig triggers update trg_01EXAMPLETRIGGER00 --map map.json --signing
screenrig triggers update trg_01EXAMPLETRIGGER00 --verify github --verify-secret-file github.secret
screenrig triggers update trg_01EXAMPLETRIGGER00 --clear-verify

At least one option besides --expect-rev is required. A bar re-places itself on the new target screens and edge; a takeover trigger's new target applies from the next alert that starts. A trigger that already signs keeps its signing secret when --signing is repeated; triggers rotate-secret --signing replaces it. --clear-verify removes the vendor verification and forgets its secret, and --clear-handshake stops answering the vendor handshake. After an interrupted or ambiguous update, rerun the identical command: it reuses the saved idempotency key.

triggers deliveries#

Read an overlay trigger's log of posts: why each one was shown, ignored or refused.

triggers deliveries <id> [--limit N]
ParameterTypeRequiredDefaultNotes
<id>stringyesnoneThe trg_ trigger id.
--limit Nintegerno100How many deliveries, newest first; 1 to 100.
screenrig triggers deliveries trg_01EXAMPLETRIGGER00 --limit 20

data.items holds the posts of the last seven days: at, outcome (shown, updated, unchanged, ended, ignored, refused, invalid or handshake), reason for a refused or invalid post (credentials, signature, signature_missing, signature_invalid, timestamp_missing, timestamp_stale, secret_pending, handshake_invalid, disabled, body, mapping, field, rate_limited or internal), detail (the member or mapping path that was wrong), status, bytes, files (the file parts of a multipart post, which are ignored), alert_id and request_id. signature is a failed ScreenRig-Signature; the other signature, timestamp and handshake reasons are a vendor's, as the table under vendor signatures and handshakes explains. The log never keeps a body, a header or a credential. A wrong secret, a bad signature and a disabled trigger all answer the sender the same 404, so this log is where to read which it was. A CAP trigger has no deliveries.

triggers clear#

End the trigger's alert now.

triggers clear <id> [--alert-id ID]
ParameterTypeRequiredDefaultNotes
<id>stringyesnoneThe trg_ trigger id.
--alert-id IDstringnononeAn overlay trigger: end only this alert. Without it every alert ends.
screenrig triggers clear trg_01EXAMPLETRIGGER00
screenrig triggers clear trg_01EXAMPLETRIGGER00 --alert-id front-door

The alert ends with ended_reason cleared, and its screens go back to what they played before. A screen whose takeover something else has replaced since is left alone. With no active alert, nothing changes. On an overlay trigger a clear of an alert that is not active changes nothing, and the last alert ending lowers the bar or gives the takeover's screens back.

triggers rotate-secret#

Replace a trigger's password.

triggers rotate-secret <id> [--signing] [--expect-rev REVISION]
ParameterTypeRequiredDefaultNotes
<id>stringyesnoneThe trg_ trigger id.
--signingflagnooffAn overlay trigger that signs: replace the signing secret instead of the password.
--expect-rev REVISIONstringnononeOptional revision precondition.
screenrig triggers rotate-secret trg_01EXAMPLETRIGGER00

The envelope carries the new password in data.password. The old password stops working at once, so enter the new one in the sending system. With --signing the envelope carries the new data.signing_secret instead, shown once; the old one stops working at once, and a trigger that does not sign answers invalid_request. After an interrupted or ambiguous rotation, rerun the identical command within 24 hours: it reuses the saved idempotency key, so the server returns the rotation that already happened instead of rotating again.

triggers delete#

Delete a trigger. Its URL stops accepting alerts and posts at once; an overlay trigger's bars come off its screens and its card is deleted. A trigger showing an alert is refused with resource_conflict: run triggers clear first.

triggers delete <id> [--expect-rev REVISION]
ParameterTypeRequiredDefaultNotes
<id>stringyesnoneThe trg_ trigger id.
--expect-rev REVISIONstringnononeOptional revision precondition.
screenrig triggers delete trg_01EXAMPLETRIGGER00

Overlay triggers

An overlay trigger shows what an outside system posts. Home Assistant, a door or motion sensor hub, a camera system or an alerting tool sends a few words with one HTTP POST, and the trigger's screens show them until they expire or the sender clears them. It is a trigger like a CAP trigger: triggers create --kind overlay makes it, and it counts toward the same limit of 20 per organization.

--show bar draws a strip over whatever plays on every target screen, on the bottom edge or, with --edge top, the top. The strip slides in when an alert starts and out when the last one ends; the playlist, its pages and its timing are untouched. A screen holds one bar per edge, so an overlay trigger whose edge a screen's other overlay trigger already holds does not show there (triggers show counts those screens as edge_taken), and a page that has its own strip with presence on that edge keeps it. A corner or chip card on that edge moves in past the bar's height whether or not the bar is showing. No bar draws on an ad slot page or while a takeover plays. --show takeover plays the alert full screen on target screens that have a default playlist from the first alert until the last one ends. A takeover trigger never replaces a takeover that is already on a screen, such as an operator's or an emergency alert, and a CAP alert replaces it.

A tag target is resolved when an alert starts; a screen tagged later gets the bar with the next alert that starts. Screens without a default playlist are skipped by a takeover. A screen that is offline catches up when it reconnects.

The sender posts JSON, a form or multipart/form-data (file parts are ignored) to data.url with Authorization: Bearer SECRET, or HTTP Basic with the trigger id as the username and the secret as the password. The secret never goes in the URL. A post has these members; an unknown member is refused naming it.

MemberTypeNotes
alert_idstringThe sender's id for the alert, 1 to 200 characters. The same id updates that alert instead of stacking another. Absent is one alert called default. On a clear, absent ends every alert.
labelstringA short label above the title, at most 40 characters.
titlestringAt most 120 characters.
messagestringThe main text, at most 400 characters.
notestringA quiet line at the foot, at most 120 characters.
durationnumber or stringHow long this post holds its alert, from now: seconds from 10 to 604800, or 10s, 5m, 2h, 1d, or until_cleared. Absent takes the trigger's --for (10 minutes unless set).
clearboolean or stringtrue (or yes, y, 1, x) ends the alert; false (or no, n, 0, empty) raises it. A clear ignores the other members.
statestringA state word from the sender. off, closed, clear, cleared, resolved, ok, normal, inactive and ended, in any case, end the alert; any other state raises it.

A raise needs at least one of label, title, message and note. Text longer than its limit is refused, and duration outside 10 seconds to 7 days is refused, both with invalid_request naming the member. Every raise or update holds its alert for duration from that post, so a motion sensor that posts every few seconds keeps the bar up and the bar leaves duration after the last post. A trigger shows its newest alert; ending it shows the one before it again. It holds ten alerts, and an eleventh replaces the oldest. An alert ends within about ten seconds of its time. A clear sent twice ends the alert once, and a clear of an alert that is not active changes nothing.

The answer is a receipt: outcome (shown, updated, unchanged, ended or ignored), alert_id, until (null for an alert held until cleared) and active, the alerts the trigger holds after the post. An Idempotency-Key header of 8 to 200 characters answers a sender's retry with the first receipt for 24 hours. A trigger takes 120 posts a minute. A post does not count toward the agent request cap, and a clear is never refused, so an alert can always be taken off the screens.

curl -X POST https://api.screenrig.ai/api/triggers/trg_01EXAMPLETRIGGER00/overlay \
  -H "Authorization: Bearer srtg_YOUR_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"alert_id":"front-door","title":"Front door","message":"The front door is open","duration":"10m"}'

A wrong address, a wrong secret, a disabled trigger, a missing or bad signature and every GET answer the same 404 not_found, so a sender or a link previewer learns nothing about which it was. triggers deliveries shows the project why, for the last seven days, with no post body. --style FILE sets the text card's theme, accent, background, backgroundColor, textColor and align; use one trigger per look.

--map FILE builds the post from the body an alerting tool sends, with the template language of card webhooks. A string with {{body.title}} is text, a string that is exactly one placeholder keeps its type, {"$text": "{{body.message}}", "$max": 400} cuts text, and a path starts with body or query. This template maps an alert from a monitoring tool and clears it when its status is resolved:

{"alert_id": "{{body.groupKey}}", "title": "{{body.title}}", "message": {"$text": "{{body.message}}", "$max": 400}, "state": "{{body.status}}"}

--signing also requires ScreenRig-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">, keyed with data.signing_secret (whsec_…, shown once), with t within five minutes of the server's clock. A sender may carry two v1 values while it rotates the secret.

body='{"alert_id":"front-door","title":"Front door","message":"The front door is open"}'
t=$(date +%s)
sig=$(printf '%s.%s' "$t" "$body" | openssl dgst -sha256 -hmac "$SIGNING_SECRET" -hex | sed 's/^.* //')
curl -X POST "$URL" -H "Authorization: Bearer $SECRET" -H "ScreenRig-Signature: t=$t,v1=$sig" \
  -H "Content-Type: application/json" -d "$body"

An alerting tool or a vendor that signs its own posts can have its signature verified in place of the secret, and a vendor's setup handshake answered, as for card webhooks: triggers create and triggers update take --verify VENDOR with --verify-secret-file FILE, and --handshake VENDOR, and triggers update takes --clear-verify and --clear-handshake to remove each. The vendors, their headers and the generic_hmac options are the ones listed there. A post that carries the vendor's valid signature is accepted without the trigger's srtg_ password, which keeps working beside it, --verify excludes --signing, and a handshake changes no alert. triggers deliveries shows why a signature failed.

A Home Assistant rest_command posts to the trigger. Keep the secret in secrets.yaml, and give each automation the alert it raises or clears:

# secrets.yaml
screenrig_lobby_overlay: "Bearer srtg_YOUR_SECRET"

# configuration.yaml
rest_command:
  screenrig_lobby:
    url: "https://api.screenrig.ai/api/triggers/trg_01EXAMPLETRIGGER00/overlay"
    method: post
    headers:
      Authorization: !secret screenrig_lobby_overlay
    content_type: "application/json"
    payload: >-
      {{ {"alert_id": alert_id | default(""), "title": title | default(""),
          "message": message | default(""), "duration": duration | default("10m"),
          "clear": clear | default(false)} | tojson }}

# automations.yaml
- alias: Front door open on the lobby screens
  triggers:
    - trigger: state
      entity_id: binary_sensor.front_door
      to: "on"
  actions:
    - action: rest_command.screenrig_lobby
      data: {alert_id: front-door, title: Front door, message: The front door is open, duration: until_cleared}
- alias: Front door closed
  triggers:
    - trigger: state
      entity_id: binary_sensor.front_door
      to: "off"
  actions:
    - action: rest_command.screenrig_lobby
      data: {alert_id: front-door, clear: true}
- alias: Driveway motion
  triggers:
    - trigger: state
      entity_id: binary_sensor.driveway_motion
      to: "on"
  actions:
    - action: rest_command.screenrig_lobby
      data: {alert_id: driveway, title: Driveway, message: Motion at the driveway, duration: 2m}

Two facts make this work. Each motion post holds the bar for 2 minutes from that post, so it stays while motion continues and leaves 2 minutes after the last event. And Home Assistant renders the {{ }} template before it posts, so the trigger receives plain JSON; the trigger's own --map template is a separate step that runs every post through it, so leave --map off for a sender that already posts the overlay members, as Home Assistant does here, or give that sender its own trigger.

Other command groups