@meri-imperiumi/signalk-mob-notifier 1.0.0
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.
- package/.eslintrc.json +6 -0
- package/.github/workflows/publish.yml +30 -0
- package/.github/workflows/test.yml +21 -0
- package/README.md +10 -0
- package/package.json +34 -0
- package/plugin/index.js +157 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Publish Node.js Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v3.0.2
|
|
13
|
+
- uses: actions/setup-node@v3.1.1
|
|
14
|
+
with:
|
|
15
|
+
node-version: 22
|
|
16
|
+
- run: npm install
|
|
17
|
+
- run: npm test
|
|
18
|
+
|
|
19
|
+
publish-npm:
|
|
20
|
+
needs: build
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v3.0.2
|
|
24
|
+
- uses: actions/setup-node@v3.1.1
|
|
25
|
+
with:
|
|
26
|
+
node-version: 22
|
|
27
|
+
registry-url: https://registry.npmjs.org/
|
|
28
|
+
- run: npm publish
|
|
29
|
+
env:
|
|
30
|
+
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Node CI
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
name: Run test suite
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
strategy:
|
|
10
|
+
matrix:
|
|
11
|
+
node-version: [22.x]
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v3.0.2
|
|
14
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
15
|
+
uses: actions/setup-node@v3.1.1
|
|
16
|
+
with:
|
|
17
|
+
node-version: ${{ matrix.node-version }}
|
|
18
|
+
- run: npm install
|
|
19
|
+
- run: npm test
|
|
20
|
+
env:
|
|
21
|
+
CI: true
|
package/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Signal K MOB notifier plugin
|
|
2
|
+
============================
|
|
3
|
+
|
|
4
|
+
This plugin detects if Signal K has seen an AIS MOB (MMSI starting with `972`) or SART (MMSI starting with `970`) beacon. If a beacon is detected, a [Signal K notification](https://signalk.org/specification/1.5.0/doc/notifications.html) will be raised. With the notification there are various ways to alert the crew:
|
|
5
|
+
|
|
6
|
+
* Audio with plugins like [signalk-audio-notifications](https://github.com/meri-imperiumi/signalk-audio-notifications) and an attached speaker
|
|
7
|
+
* Meshtastic message with [signalk-meshtastic](https://github.com/meri-imperiumi/signalk-meshtastic)
|
|
8
|
+
* Visual with apps like Freeboard
|
|
9
|
+
|
|
10
|
+
Please note that this plugin aims to aid with noticing and locating crew overboard. It is not a replacement for "proper" alerting mechanisms via dedicated alert buzzers, chartplotters, or a VHF radio.
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meri-imperiumi/signalk-mob-notifier",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create Signal K notifications for discovered AIS MOB and SART beacons",
|
|
5
|
+
"main": "plugin/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "eslint ."
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git://github.com/meri-imperiumi/signalk-mob-notifier.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"signalk-node-server-plugin",
|
|
15
|
+
"signalk-category-utility"
|
|
16
|
+
],
|
|
17
|
+
"author": "Henri Bergius <henri.bergius@iki.fi>",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/meri-imperiumi/signalk-mob-notifier/issues"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/meri-imperiumi/signalk-mob-notifier#readme",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"eslint": "^8.57.1",
|
|
28
|
+
"eslint-config-airbnb-base": "^15.0.0",
|
|
29
|
+
"eslint-plugin-import": "^2.32.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"where": "^0.4.2"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/plugin/index.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const { Point } = require('where');
|
|
2
|
+
|
|
3
|
+
function longDirection(direction) {
|
|
4
|
+
switch (direction) {
|
|
5
|
+
case 'N': {
|
|
6
|
+
return 'north';
|
|
7
|
+
}
|
|
8
|
+
case 'NE': {
|
|
9
|
+
return 'northeast';
|
|
10
|
+
}
|
|
11
|
+
case 'E': {
|
|
12
|
+
return 'east';
|
|
13
|
+
}
|
|
14
|
+
case 'SE': {
|
|
15
|
+
return 'southeast';
|
|
16
|
+
}
|
|
17
|
+
case 'S': {
|
|
18
|
+
return 'south';
|
|
19
|
+
}
|
|
20
|
+
case 'SW': {
|
|
21
|
+
return 'southwest';
|
|
22
|
+
}
|
|
23
|
+
case 'W': {
|
|
24
|
+
return 'west';
|
|
25
|
+
}
|
|
26
|
+
case 'NW': {
|
|
27
|
+
return 'northwest';
|
|
28
|
+
}
|
|
29
|
+
default: {
|
|
30
|
+
return 'unknown';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = (app) => {
|
|
36
|
+
const plugin = {};
|
|
37
|
+
let interval;
|
|
38
|
+
const notified = [];
|
|
39
|
+
plugin.id = 'signalk-mob-notifier';
|
|
40
|
+
plugin.name = 'MOB Beacon Notifier';
|
|
41
|
+
plugin.description = 'Create notifications for discovered AIS MOB and SART beacons';
|
|
42
|
+
|
|
43
|
+
plugin.start = (settings) => {
|
|
44
|
+
interval = setInterval(() => {
|
|
45
|
+
// Check context for MOBs
|
|
46
|
+
const mobs = Object.keys(app.signalk.vessels)
|
|
47
|
+
.filter((context) => {
|
|
48
|
+
const mmsi = context.split(':').at(-1);
|
|
49
|
+
if (mmsi.indexOf('972') === 0) {
|
|
50
|
+
// MOB beacon seen!
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (mmsi.indexOf('970') === 0) {
|
|
54
|
+
// SART beacon seen!
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Clear notifications as needed
|
|
61
|
+
notified
|
|
62
|
+
.filter((mmsi) => {
|
|
63
|
+
if (mobs.indexOf(mmsi) !== -1) {
|
|
64
|
+
// This beacon is still active
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
})
|
|
69
|
+
.forEach((mmsi) => {
|
|
70
|
+
app.handleMessage('signalk-meshtastic', {
|
|
71
|
+
context: `vessels.${app.selfId}`,
|
|
72
|
+
updates: [
|
|
73
|
+
{
|
|
74
|
+
source: {
|
|
75
|
+
label: plugin.id,
|
|
76
|
+
},
|
|
77
|
+
timestamp: (new Date().toISOString()),
|
|
78
|
+
values: [
|
|
79
|
+
{
|
|
80
|
+
path: `notifications.mob.${mmsi}`,
|
|
81
|
+
value: null,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
notified.splice(notified.indexOf(mmsi), 1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (mobs.length === 0) {
|
|
91
|
+
// TODO: Clear notifications
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
mobs.forEach((mmsi) => {
|
|
95
|
+
let message = 'Crew Overboard Beacon detected';
|
|
96
|
+
if (mmsi.indexOf('970') === 0) {
|
|
97
|
+
message = 'Search and Rescue Transponder Beacon detected';
|
|
98
|
+
}
|
|
99
|
+
// For each MOB get direction and range
|
|
100
|
+
const ownPosition = app.getSelfPath('navigation.position');
|
|
101
|
+
const mobPosition = app.getPath(`vessels.urn:mrn:imo:mmsi:${mmsi}.navigation.position`);
|
|
102
|
+
if (ownPosition && mobPosition) {
|
|
103
|
+
const me = new Point(ownPosition.latitude, ownPosition.longitude);
|
|
104
|
+
const they = new Point(mobPosition.latitude, mobPosition.longitude);
|
|
105
|
+
const distance = me.distanceTo(they, 'K') * 1000; // In meters
|
|
106
|
+
const direction = longDirection(me.directionTo(they));
|
|
107
|
+
message = `${message} ${distance} meters to ${direction}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Raise notification
|
|
111
|
+
app.handleMessage('signalk-meshtastic', {
|
|
112
|
+
context: `vessels.${app.selfId}`,
|
|
113
|
+
updates: [
|
|
114
|
+
{
|
|
115
|
+
source: {
|
|
116
|
+
label: plugin.id,
|
|
117
|
+
},
|
|
118
|
+
timestamp: (new Date().toISOString()),
|
|
119
|
+
values: [
|
|
120
|
+
{
|
|
121
|
+
path: `notifications.mob.${mmsi}`,
|
|
122
|
+
value: {
|
|
123
|
+
message,
|
|
124
|
+
state: 'emergency',
|
|
125
|
+
method: ['visual', 'sound'],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
notified.push(mmsi);
|
|
133
|
+
});
|
|
134
|
+
}, settings.interval * 1000);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
plugin.stop = () => {
|
|
138
|
+
if (!interval) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
clearInterval(interval);
|
|
142
|
+
interval = undefined;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
plugin.schema = {
|
|
146
|
+
type: 'object',
|
|
147
|
+
properties: {
|
|
148
|
+
interval: {
|
|
149
|
+
type: 'integer',
|
|
150
|
+
default: 5,
|
|
151
|
+
title: 'How often to check for MOB Beacons (in seconds)?',
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return plugin;
|
|
157
|
+
};
|