@sailingnaturali/signalk-dsc 0.5.4 → 0.5.6

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 (2) hide show
  1. package/index.js +36 -59
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -34,7 +34,9 @@ const {
34
34
  buildLogbookText,
35
35
  captureOwnShip,
36
36
  buildObservations,
37
+ createNotifier,
37
38
  unwrap,
39
+ writeLogbookEntry,
38
40
  } = require('@sailingnaturali/signalk-distress-core');
39
41
 
40
42
  const DSC_PGN = 129808;
@@ -141,37 +143,33 @@ module.exports = function makePlugin(app) {
141
143
  };
142
144
  }
143
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
+
144
164
  function notify(event) {
145
- const state = NOTIFICATION_STATES[event.category];
146
- if (!state) return;
147
- app.handleMessage(plugin.id, {
148
- updates: [
149
- {
150
- values: [
151
- {
152
- path: `notifications.dsc.${event.category}`,
153
- value: {
154
- state,
155
- method: ['visual', 'sound'],
156
- // Kept terse on purpose: this string gets spoken by the
157
- // voice pipeline. Full detail lives in the resource store
158
- // and the logbook entry.
159
- message: buildMessage(event, messageContext(event)),
160
- timestamp: event.receivedAt,
161
- },
162
- },
163
- ],
164
- },
165
- ],
166
- });
165
+ refreshMessage(event);
166
+ notifier.raise(event);
167
167
  }
168
168
 
169
169
  /** Clear an active DSC alarm: drop the live notification from our own source
170
170
  * and stamp the stored events so the restart reannounce skips them. */
171
171
  function clearCategory(category) {
172
- app.handleMessage(plugin.id, {
173
- updates: [{ values: [{ path: `notifications.dsc.${category}`, value: null }] }],
174
- });
172
+ notifier.clear(`notifications.dsc.${category}`);
175
173
  store.markCleared((e) => e.category === category, new Date().toISOString());
176
174
  }
177
175
 
@@ -185,26 +183,14 @@ module.exports = function makePlugin(app) {
185
183
  }
186
184
 
187
185
  async function postLogbook(event) {
188
- const observations = buildObservations(event.ownShip);
189
- const res = await fetch(options.logbookUrl, {
190
- method: 'POST',
191
- headers: {
192
- 'Content-Type': 'application/json',
193
- // signalk-server's auth gate reads the Authorization header; the
194
- // logbook plugin reads the author from the JAUTHENTICATION cookie.
195
- Authorization: `Bearer ${options.logbookToken}`,
196
- Cookie: `JAUTHENTICATION=${options.logbookToken}`,
197
- },
198
- body: JSON.stringify({
199
- text: buildLogbookText(event, messageContext(event)),
200
- ago: 0,
201
- category: 'radio',
202
- // DSC is received on VHF channel 70 by definition.
203
- vhf: '70',
204
- ...(observations ? { observations } : {}),
205
- }),
186
+ await writeLogbookEntry({
187
+ url: options.logbookUrl,
188
+ token: options.logbookToken,
189
+ text: buildLogbookText(event, messageContext(event)),
190
+ observations: buildObservations(event.ownShip),
191
+ // DSC is received on VHF channel 70 by definition.
192
+ extra: { vhf: '70' },
206
193
  });
207
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
208
194
  }
209
195
 
210
196
  /** Store a normalized call, alarm on it, and log it. Returns the stored event. */
@@ -431,23 +417,14 @@ module.exports = function makePlugin(app) {
431
417
  // Survive server restarts mid-incident: re-raise the newest alert per
432
418
  // category that is still fresh (a received MAYDAY must not vanish just
433
419
  // because the server bounced). Delayed so position providers are up and
434
- // the spoken message can say "N miles <direction>" instead of raw
435
- // 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.
436
422
  reannounceTimer = setTimeout(() => {
437
423
  if (!started) return;
438
- const now = Date.now();
439
- const reannounced = new Set();
440
- const events = store.list();
441
- for (let i = events.length - 1; i >= 0; i--) {
442
- const event = events[i];
443
- if (!NOTIFICATION_STATES[event.category] || reannounced.has(event.category)) continue;
444
- if (event.clearedAt) continue; // operator-cleared: never resurrect
445
- const at = Date.parse(event.lastReceivedAt || event.receivedAt);
446
- if (now - at <= REANNOUNCE_WINDOW_MS) {
447
- notify(event);
448
- reannounced.add(event.category);
449
- }
450
- }
424
+ notifier.reannounce(store.list(), {
425
+ window: REANNOUNCE_WINDOW_MS,
426
+ prepare: refreshMessage,
427
+ });
451
428
  }, options.reannounceDelayMs ?? 30000);
452
429
  };
453
430
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingnaturali/signalk-dsc",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
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.1.0"
52
+ "@sailingnaturali/signalk-distress-core": "^0.3.0"
53
53
  }
54
54
  }