No Analysis
Never interprets alert severity, never correlates with historical data, never guesses at root causes.
The Alert Monitor is the always-on sentry of the BWTS IOT pipeline. It runs on a scheduled routine (cron), continuously scanning two input channels for new alerts. When it finds one, it packages the alert into a structured payload and hands it to the IOT Manager — nothing more.
The Alert Monitor runs two discovery paths in parallel, with a third as a fallback. This ensures no alert is missed regardless of how it enters the system.
graph LR subgraph "Parallel Discovery" direction TB A["Path A\nEmail (IMAP)\ngmail_fetch.py"] B["Path B\nDashboard API\n/api/alert-instances"] end
subgraph "Fallback" C["Path C\nDB Query\ndb_query.py"] end
A -->|"BWTS-IDs from subject lines"| MERGE["Merge & Deduplicate"] B -->|"Pending alerts from API"| MERGE C -.->|"Only if A+B return empty"| MERGE
MERGE --> ENRICH["Fetch Full Unresolved Context\nfrom Dashboard API"] ENRICH --> TASK["Create Task\nfor IOT Manager"]
style A fill:#0d1b2a,stroke:#1b9aaa,stroke-width:2px,color:#fff style B fill:#0d1b2a,stroke:#1b9aaa,stroke-width:2px,color:#fff style C fill:#0d1b2a,stroke:#e94560,stroke-width:1px,color:#fff style MERGE fill:#1a1a2e,stroke:#e94560,stroke-width:2px,color:#fff style ENRICH fill:#1a1a2e,stroke:#0f3460,stroke-width:2px,color:#fff style TASK fill:#1a1a2e,stroke:#e94560,stroke-width:3px,color:#fffUses gmail_fetch.py to connect to the monitored inbox via IMAP. Scans unread messages for subjects containing BWTS alert patterns. Extracts equipment IDs and alert metadata from the subject line and body.
Strength: Catches alerts forwarded by crew or third-party monitoring systems that may not hit the dashboard.
Calls the Dashboard API to retrieve all alerts with a pending/unresolved status.
Strength: Authoritative source of truth — returns structured data with full context (timestamps, thresholds, current values).
Uses db_query.py to query the database directly. Only activated when both Path A and Path B return zero results, as a safeguard against API downtime or email delivery failures.
Strength: Last line of defence — ensures coverage even during service outages.
Poll Inbox
The scheduled routine triggers gmail_fetch.py, which connects to the configured IMAP mailbox and retrieves unread emails matching the BWTS alert pattern (subject contains alert keywords or known sender addresses).
Extract BWTS-IDs from Subject
For each matching email, the monitor parses the subject line to extract the BWTS equipment identifier (e.g., BWTS-UV-003) and the alert type. The email body is scanned for supplementary fields but is not treated as the primary data source.
Call API for Pending Alerts
In parallel with the email scan, the monitor calls the Dashboard API:
GET https://bwts-iot.vercel.app/api/alert-instancesThis returns all currently unresolved alert instances with full structured data.
Merge and Deduplicate
Results from Path A (email) and Path B (API) are merged. Deduplication uses the combination of equipment tag + alert code + timestamp window (within 5 minutes counts as the same alert). If both paths return zero results, Path C (DB fallback) is invoked.
Fetch Full Unresolved Context
For each unique alert, the monitor calls the Dashboard API again to retrieve the complete unresolved context — including current sensor values, threshold configuration, and alert severity. This enrichment step ensures the downstream payload is self-contained.
Create Task for IOT Manager
The monitor creates a LifeOSAI task assigned to the IOT Manager, containing the structured alert_monitor_v1 payload. One task per unique alert.
Mark Emails Processed
Any emails that were matched are marked as read (or moved to a “Processed” folder) to prevent duplicate processing on the next poll cycle.
Every task created for the IOT Manager follows the alert_monitor_v1 schema. The payload contains all the information the Manager needs to dispatch Phase 1 specialists.
| Field | Type | Description |
|---|---|---|
schema_version | string | Always "alert_monitor_v1" |
alert_instance_id | string | Unique identifier from the Dashboard API |
equipment_tag | string | BWTS equipment identifier (e.g., BWTS-UV-003) |
vessel_name | string | Name of the vessel where the equipment is installed |
alert_code | string | Standardised alarm code (e.g., HIGH_UV_INTENSITY) |
alert_severity | string | One of CRITICAL, WARNING, INFO |
parameter_name | string | The specific sensor parameter in violation |
current_value | number | The sensor reading that triggered the alert |
threshold_value | number | The configured threshold that was breached |
threshold_direction | string | "above" or "below" — indicates breach direction |
triggered_at | string (ISO 8601) | Timestamp when the alert was first triggered |
source_path | string | Which discovery path found this alert: "email", "api", or "db_fallback" |
raw_email_subject | string | null | Original email subject if discovered via Path A; null otherwise |
unresolved_context | object | Full context object from the Dashboard API enrichment step |
No Analysis
Never interprets alert severity, never correlates with historical data, never guesses at root causes.
No Root-Cause Work
Never queries equipment manuals, maintenance records, or past case files. That is the specialists’ job.
No Reporting
Never sends emails to stakeholders, never generates HTML reports, never communicates findings to end users.
| Tool | Script / Endpoint | Purpose |
|---|---|---|
| Email (IMAP) | gmail_fetch.py | Connect to the monitored mailbox, retrieve unread BWTS alert emails, mark as processed |
| Dashboard API | /api/alert-instances | Retrieve pending/unresolved alert instances with full structured context |
| DB Fallback | db_query.py | Direct database query when both email and API return zero results |
| Task System | LifeOSAI task creation | Create structured tasks for the IOT Manager with the alert_monitor_v1 payload |
The Alert Monitor runs as a scheduled routine (cron). The recommended polling interval depends on the operational context:
| Environment | Interval | Rationale |
|---|---|---|
| Production (active voyage) | Every 5 minutes | Minimise alert-to-response latency during operations |
| Production (port / idle) | Every 15 minutes | Reduced urgency, lower API load |
| Development / Testing | On-demand | Triggered manually to test pipeline end-to-end |