@sailingnaturali/signalk-dsc 0.5.5 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +9 -0
  2. package/index.js +28 -40
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -212,6 +212,15 @@ broadcasts on behalf of a real, licensed MMSI). Until such hardware is common
212
212
  this stays out of scope and the plugin remains a passive receiver. If you have a
213
213
  radio that exposes a transmit interface, open an issue.
214
214
 
215
+ ## See also
216
+
217
+ DSC is the *alerting* signal — it tells you a vessel is in distress and roughly
218
+ where. For *finding* the casualty, pair this with the companion
219
+ [`@sailingnaturali/signalk-ais-distress`](https://github.com/sailingnaturali/signalk-ais-distress),
220
+ which alarms on AIS survival beacons (SART / MOB / EPIRB) and gives you a
221
+ position stream to home on. Both share the same 97x identity classes and build on
222
+ [`@sailingnaturali/signalk-distress-core`](https://github.com/sailingnaturali/signalk-distress-core).
223
+
215
224
  ## License
216
225
 
217
226
  MIT
package/index.js CHANGED
@@ -34,6 +34,7 @@ const {
34
34
  buildLogbookText,
35
35
  captureOwnShip,
36
36
  buildObservations,
37
+ createNotifier,
37
38
  unwrap,
38
39
  writeLogbookEntry,
39
40
  } = require('@sailingnaturali/signalk-distress-core');
@@ -142,37 +143,33 @@ module.exports = function makePlugin(app) {
142
143
  };
143
144
  }
144
145
 
146
+ // Notification plumbing (raise/clear/reannounce) is shared with
147
+ // signalk-ais-distress via signalk-distress-core. A non-alarming category
148
+ // (routine/unknown, not in NOTIFICATION_STATES) makes raise a no-op.
149
+ const notifier = createNotifier({
150
+ app,
151
+ pluginId: plugin.id,
152
+ pathFor: (event) => `notifications.dsc.${event.category}`,
153
+ stateFor: (event) => NOTIFICATION_STATES[event.category],
154
+ });
155
+
156
+ // Rebuild the spoken message against the current own-ship position — range
157
+ // and direction shift as we move. Terse on purpose: this string gets spoken;
158
+ // full detail lives in the resource store and the logbook entry.
159
+ function refreshMessage(event) {
160
+ event.message = buildMessage(event, messageContext(event));
161
+ return event;
162
+ }
163
+
145
164
  function notify(event) {
146
- const state = NOTIFICATION_STATES[event.category];
147
- if (!state) return;
148
- app.handleMessage(plugin.id, {
149
- updates: [
150
- {
151
- values: [
152
- {
153
- path: `notifications.dsc.${event.category}`,
154
- value: {
155
- state,
156
- method: ['visual', 'sound'],
157
- // Kept terse on purpose: this string gets spoken by the
158
- // voice pipeline. Full detail lives in the resource store
159
- // and the logbook entry.
160
- message: buildMessage(event, messageContext(event)),
161
- timestamp: event.receivedAt,
162
- },
163
- },
164
- ],
165
- },
166
- ],
167
- });
165
+ refreshMessage(event);
166
+ notifier.raise(event);
168
167
  }
169
168
 
170
169
  /** Clear an active DSC alarm: drop the live notification from our own source
171
170
  * and stamp the stored events so the restart reannounce skips them. */
172
171
  function clearCategory(category) {
173
- app.handleMessage(plugin.id, {
174
- updates: [{ values: [{ path: `notifications.dsc.${category}`, value: null }] }],
175
- });
172
+ notifier.clear(`notifications.dsc.${category}`);
176
173
  store.markCleared((e) => e.category === category, new Date().toISOString());
177
174
  }
178
175
 
@@ -420,23 +417,14 @@ module.exports = function makePlugin(app) {
420
417
  // Survive server restarts mid-incident: re-raise the newest alert per
421
418
  // category that is still fresh (a received MAYDAY must not vanish just
422
419
  // because the server bounced). Delayed so position providers are up and
423
- // the spoken message can say "N miles <direction>" instead of raw
424
- // coordinates.
420
+ // the refreshed spoken message can say "N miles <direction>" instead of raw
421
+ // coordinates. Non-alarming categories are skipped by raise's no-op path.
425
422
  reannounceTimer = setTimeout(() => {
426
423
  if (!started) return;
427
- const now = Date.now();
428
- const reannounced = new Set();
429
- const events = store.list();
430
- for (let i = events.length - 1; i >= 0; i--) {
431
- const event = events[i];
432
- if (!NOTIFICATION_STATES[event.category] || reannounced.has(event.category)) continue;
433
- if (event.clearedAt) continue; // operator-cleared: never resurrect
434
- const at = Date.parse(event.lastReceivedAt || event.receivedAt);
435
- if (now - at <= REANNOUNCE_WINDOW_MS) {
436
- notify(event);
437
- reannounced.add(event.category);
438
- }
439
- }
424
+ notifier.reannounce(store.list(), {
425
+ window: REANNOUNCE_WINDOW_MS,
426
+ prepare: refreshMessage,
427
+ });
440
428
  }, options.reannounceDelayMs ?? 30000);
441
429
  };
442
430
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingnaturali/signalk-dsc",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "Receive, log, and alert on DSC (VHF digital selective calling) calls — distress, urgency, safety, routine — from NMEA 0183 ($CDDSC/$CDDSE) and NMEA 2000 (PGN 129808).",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -49,6 +49,6 @@
49
49
  "node": ">=18"
50
50
  },
51
51
  "dependencies": {
52
- "@sailingnaturali/signalk-distress-core": "^0.2.0"
52
+ "@sailingnaturali/signalk-distress-core": "^0.3.0"
53
53
  }
54
54
  }