Add OctoEverywhere webhook to Notify.Events automation

LAN-mode OctoEverywhere webhooks post fixed JSON that Notify.Events can't
parse. This local-only webhook automation translates important events
into titled Notify.Events alerts, passing the OctoEverywhere channel token
per message (the notify_events platform ignores per-platform api_key).

Requires notify_event_octoeverywhere_token in secrets.yaml on the host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Joshua King
2026-11-04 12:23:14 -05:00
co-authored by Claude Opus 5.5
parent 9eee3374b2
commit 7831b35af2
@@ -0,0 +1,100 @@
### OctoEverywhere -> Notify.Events ###
# OctoEverywhere (LAN mode) POSTs fixed JSON to this webhook. Notify.Events can't
# parse that JSON itself, so this automation turns it into a readable alert.
#
# Token: the notify_events platform ignores per-platform api_key and always uses
# the global notify_events token (Cats). Passing `token` inside `data` overrides
# it per message, same pattern as script.notify_events_alerts.
# Requires `notify_event_octoeverywhere_token` in secrets.yaml (the Home Assistant
# source token from the Notify.Events "OctoEverywhere" channel).
#
# Event types: https://docs.octoeverywhere.com/webhook-notifications/event-types/
# Forwarded ("important only"): 1-5, 7-12, 16. Skipped: 6 Progress, 13, 14, 15.
- id: '1793812953197'
alias: OctoEverywhere to Notify.Events
description: Turns OctoEverywhere webhook JSON into readable Notify.Events alerts
mode: queued
max: 10
triggers:
- trigger: webhook
webhook_id: octoeverywhere-7301a739d954078c5a5c5fb3
allowed_methods:
- POST
local_only: true
actions:
# HA only fills trigger.json when the request's Content-Type contains "json".
# Otherwise the body is dropped and p is empty, which the safety net catches.
- variables:
p: "{{ trigger.json | default({}, true) }}"
ev: "{{ p.EventType | default(0) | int(0) }}"
wanted: [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 16]
names:
"1": Print started
"2": Print complete
"3": Print failed
"4": Print paused
"5": Print resumed
"6": Print progress
"7": Gadget possible failure
"8": Gadget paused the print
"9": Printer error
"10": First layer complete
"11": Filament change required
"12": Needs your attention
"13": OctoEverywhere daily notification limit reached
"14": Third layer complete
"15": Bed cool down complete
"16": Test notification
# Safety net: body wasn't readable JSON, so say so instead of failing silently
- if:
- condition: template
value_template: "{{ not p }}"
then:
- action: notify.notify_events_alerts
data:
message: OctoEverywhere sent a webhook that Home Assistant could not read as JSON. Check this automation's trace.
data:
token: !secret notify_event_octoeverywhere_token
title: OctoEverywhere webhook problem
level: warning
- stop: Body was not JSON
# Drop events we don't want
- condition: template
value_template: "{{ ev in wanted }}"
# Build the message
- variables:
printer: "{{ p.PrinterName | default('Printer', true) }}"
event_name: "{{ names[ev | string] | default('Event ' ~ ev, true) }}"
level: "{{ 'error' if ev in [3, 8, 9] else 'warning' if ev in [4, 7, 11, 12] else 'success' if ev == 2 else 'info' }}"
priority: "{{ 'high' if ev in [3, 4, 7, 8, 9, 11, 12] else 'normal' }}"
images: "{{ [{'url': p.SnapshotUrl, 'name': 'snapshot.jpg'}] if p.SnapshotUrl else [] }}"
body: >-
{%- set parts = [] -%}
{%- if p.FileName %}{% set parts = parts + ['<b>File:</b> ' ~ p.FileName] %}{% endif -%}
{%- if p.Progress is defined and ev not in [1, 2] %}{% set parts = parts + ['<b>Progress:</b> ' ~ p.Progress ~ '%'] %}{% endif -%}
{%- if p.TimeRemainingSec is defined and (p.TimeRemainingSec | int(0)) > 0 and ev not in [2, 3] -%}
{%- set s = p.TimeRemainingSec | int(0) -%}
{%- set parts = parts + ['<b>Time left:</b> ' ~ (s // 3600) ~ 'h ' ~ ((s % 3600) // 60) ~ 'm'] -%}
{%- endif -%}
{%- if p.DurationSec is defined and ev in [2, 3] -%}
{%- set d = p.DurationSec | int(0) -%}
{%- set parts = parts + ['<b>Ran for:</b> ' ~ (d // 3600) ~ 'h ' ~ ((d % 3600) // 60) ~ 'm'] -%}
{%- endif -%}
{%- if p.Error %}{% set parts = parts + ['<b>Error:</b> ' ~ p.Error] %}{% endif -%}
{%- if p.QuickViewUrl %}{% set parts = parts + ['<a href="' ~ p.QuickViewUrl ~ '">Open printer</a>'] %}{% endif -%}
{{ parts | join('<br>') }}
# Send it. If a snapshot ever breaks delivery, remove the images line.
- action: notify.notify_events_alerts
data:
message: "{{ body if body else event_name }}"
data:
token: !secret notify_event_octoeverywhere_token
title: "{{ printer }}: {{ event_name }}"
level: "{{ level }}"
priority: "{{ priority }}"
images: "{{ images }}"