Night Sky Pi Module: NSP Ntfy (MQTT → ntfy.sh)
The first Night Sky Pi module is up and running. It is deliberately small so the focus stays on the module pattern rather than fighting complexity on day one. The module is called NSP Ntfy and its only job is to listen on one or more MQTT topics and forward messages to phones via ntfy.sh. That proves the shape of a module that cooperates with the Night Sky Pi core without needing special casing in the core itself.
The code is available at: https://github.com/joe-mccarthy/nsp-ntfy
Why this module exists
Night Sky Pi’s core produces useful events during an observation window: capture started, per-frame status, packaging finished, and so on. Glancing at logs or a dashboard is fine, but a small push when something important happens is better. NSP Ntfy bridges that gap. It reads messages on configured MQTT topics and mirrors them to an ntfy topic that your phone is already subscribed to.
Two choices guide the design:
- Keep MQTT local. The broker listens on the loopback interface for security. That means any module that consumes MQTT should run on the same machine as the Night Sky Pi core.
- Keep the module self contained. Configuration and logging live with the module. It relies on the core only for shared facts like broker address and whether MQTT is enabled.
Configuration: two files
The module reads two configs at startup:
- Module config (NSP Ntfy): declares the MQTT topics to subscribe to and how to forward each one to ntfy.sh.
- Night Sky Pi core config: supplies shared settings like the MQTT broker details and whether MQTT is enabled.
Here is the module configuration structure:
|
|
- configurations: an array so one module instance can forward multiple MQTT topics to different ntfy topics.
- ntfy.options: optional extras for presentation. Use
title
, a numericpriority
, andtags
(emoji names work). - logging: the module chooses its own log file name and log level. Rotation and placement are handled by the Night Sky Pi logging setup, so the module only needs to state its preference.
The message schema is simple: the MQTT message payload must be valid JSON containing a field named notification
. The module takes the value of that field as the body text for the push. Keeping the schema tiny avoids coupling the notifier to the full structure of every upstream message.
Runtime behaviour
When the service starts it:
- Loads the module config and the core config.
- Verifies that MQTT is enabled in the core configuration. If not enabled, the module exits cleanly rather than retrying forever.
- Connects to the local MQTT broker using the core’s connection settings.
- Subscribes to each
mqtt_topic
listed in the module config. - For every message received on a subscribed topic, parses the payload, extracts
notification
, applies the optionaloptions
(title, priority, tags), and posts to ntfy.sh under the configured ntfy topic.
That is the entire loop. No extra queues, no external state, and nothing that the core needs to know about beyond “MQTT on, topics published.”
What this enables today
- Immediate alerts for observation lifecycle events without opening a dashboard.
- Multiple routes from one running instance by adding more entries to
configurations
. - Predictable logs that live alongside the rest of Night Sky Pi’s logs, with the module able to pick its own file name and verbosity.
Limits and expectations
The goal is small and reliable rather than feature rich:
- The module assumes the MQTT broker is local to the device. That matches the security decision in the core to bind on loopback.
- Messages are expected to be well formed JSON with a
notification
field. If the field is missing or the JSON is invalid, the module will not attempt to deliver a push. - The module does not alter the timing or content of the core’s events. It acts as a mirror for pushes, nothing more.
How it fits the module pattern
Night Sky Pi modules should be single purpose processes that read from and write to well understood interfaces. NSP Ntfy does exactly that. It depends on MQTT being available, consumes a topic, and produces a push to ntfy. This keeps failure modes narrow and makes it easy to swap it out later if a different notification channel is preferred.
Early results
Running alongside the core, the service has been reliable for routine alerts like “capture started,” periodic progress notes, and “packaging complete.” Pushes arrive quickly and have proven more useful than polling logs, especially during long sessions where the UI is not open.
This is a small step, but it confirms the module shape that future pieces will follow.
Thanks for reading. If you have an elegant pattern for mapping many MQTT topics to notification channels without growing the config file, share it so others can benefit.