Files
ISR/ROADMAP.md
T

5.4 KiB
Raw Blame History

ISR Roadmap

notify.py — NTFY Loudness Notifications

Context

Street ambience recorder. Goal: detect notable audio events (speech, thunder, sustained unusual sounds) in hourly recording files and push a notification via a self-hosted NTFY server. Generic short events (car horn, passing vehicle) should be filtered out by a minimum section duration.

Design decisions

Topic Decision
Detection RMS + minimum section duration filter (KISS — no FFT for now)
Timing Configurable: immediate / daily / both
Config [notify] section in existing config.ini
Code structure notify.py imports analyze_wav / analyze_flac from web.py (DRY)
Source name Included in notification body; configurable display name per source

Config additions (config.example.ini)

Add a [notify] section to config.ini:

[notify]
enabled              = true
ntfy_url             = https://ntfy.example.com/mytopic   ; full URL incl. topic
mode                 = immediate   ; immediate | daily | both
daily_time           = 08:00       ; HH:MM — used in daily and both modes
debounce_minutes     = 60          ; immediate mode: suppress repeat notifications within this window
min_section_duration = 2.0         ; seconds — sections shorter than this are ignored (filters car horns etc.)
min_sections         = 1           ; number of qualifying sections required to trigger a notification
loudness_threshold   = 0.05        ; RMS 01, same scale as web.py analysis threshold

Per recording source, add an optional display_name:

[radio1]
type         = stream
url          = http://icecast.example.com:8000/live
display_name = Street mic north   ; shown in notification; defaults to section name [radio1]

Notification format

Title:  ISR — Notable audio · Street mic north
Body:   radio1_20260427_0300.wav
        3 notable sections (≥ 2.0 s each)
        → 00:12  00:18
        → 01:45  01:52
        → 47:03  47:11
        Peak RMS: 0.312

Daily digest example:

Title:  ISR Daily Digest · 2026-04-27
Body:   Street mic north — 4 files with notable events
        03:00 file · 3 sections (peak 0.312)
        07:00 file · 1 section  (peak 0.091)
        14:00 file · 2 sections (peak 0.204)
        21:00 file · 1 section  (peak 0.178)

Implementation plan

Phase 1 — Core

  1. config.example.ini — add [notify] section and display_name key to source section examples (as shown above).

  2. notify.py — file watcher

    • Polls recordings/status.json every 30 s.
    • Tracks which files were in active on the previous poll.
    • When a file disappears from active it was just closed → queues it for analysis.
    • Skips files with extensions that cannot be analysed (anything other than .wav / .flac).
  3. notify.py — analysis + filter

    • Imports analyze_wav / analyze_flac from web.py.
    • Applies loudness_threshold from [notify] config.
    • Filters resulting sections to those with duration ≥ min_section_duration.
    • Counts filtered sections against min_sections threshold.
  4. notify.py — NTFY HTTP POST

    • Plain urllib POST to ntfy_url (no extra dependencies).
    • Sets Title and message body as described above.
    • Logs success / failure to stdout.

Phase 2 — Cadence modes

  1. Immediate mode with debounce

    • Fires right after the file closes and analysis passes.
    • Persists last-notification timestamp per source to a small notify_state.json in the recordings directory.
    • Suppresses sending if last notification for that source was within debounce_minutes.
  2. Daily digest mode

    • Appends qualifying events to notify_log.jsonl in the recordings directory (one JSON line per event: timestamp, source, filename, sections, peak RMS).
    • On each poll checks whether daily_time has passed today and no digest has been sent yet (tracked in notify_state.json).
    • Reads all undigested entries from notify_log.jsonl, groups by display_name, sends one notification per source with notable activity.
    • Marks entries as digested.
  3. Both mode

    • Immediate path: only fires when peak RMS exceeds a second, higher threshold (alarm_threshold, default 0.3; add to [notify] config).
    • Daily digest path: fires for everything that passes min_sections.

Phase 3 — Integration

  1. Docker — optional notify service in docker-compose.yml:

    notify:
      build: .
      command: python notify.py
      volumes:
        - ./recordings:/app/recordings
        - ./config.ini:/app/config.ini:ro
      restart: unless-stopped
    
  2. README — new section documenting notify.py usage, config keys, and Docker setup.


Open questions (decide before implementing)

  • Log rotation: notify_log.jsonl grows indefinitely. Options: cap at N days (configurable), cap at N MB, or leave cleanup to the user. No decision made yet.
  • Multiple NTFY topics per source: current design uses one global topic. If per-source topics are ever needed, ntfy_url could be moved to the source section and override the global one.
  • FFT / frequency analysis (future): distinguishing thunder (low rumble, 50200 Hz) from speech (3003000 Hz) from vehicles would reduce false positives further. Deferred — requires numpy and adds meaningful complexity.