@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 ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "airbnb-base",
3
+ "parserOptions": {
4
+ "ecmaVersion": 11
5
+ }
6
+ }
@@ -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
+ }
@@ -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
+ };