diff --git a/automations/05_printers_and_workshop/05c_octoeverywhere_notify_events.yaml b/automations/05_printers_and_workshop/05c_octoeverywhere_notify_events.yaml
new file mode 100644
index 0000000..e241fd1
--- /dev/null
+++ b/automations/05_printers_and_workshop/05c_octoeverywhere_notify_events.yaml
@@ -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 + ['File: ' ~ p.FileName] %}{% endif -%}
+ {%- if p.Progress is defined and ev not in [1, 2] %}{% set parts = parts + ['Progress: ' ~ 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 + ['Time left: ' ~ (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 + ['Ran for: ' ~ (d // 3600) ~ 'h ' ~ ((d % 3600) // 60) ~ 'm'] -%}
+ {%- endif -%}
+ {%- if p.Error %}{% set parts = parts + ['Error: ' ~ p.Error] %}{% endif -%}
+ {%- if p.QuickViewUrl %}{% set parts = parts + ['Open printer'] %}{% endif -%}
+ {{ parts | join('
') }}
+
+ # 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 }}"