@noforeignland/signalk-to-noforeignland 1.1.0-beta.3 → 1.2.0-beta.1

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.
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Cleanup script for old plugin versions
5
- * This runs as a standalone script during npm postinstall
6
- * NOT a class - just a simple executable script
7
- */
8
-
9
- const fs = require('fs');
10
- const path = require('path');
11
-
12
- // Detect SignalK directory (standard ~/.signalk or Victron Cerbo /data/conf/signalk)
13
- function getSignalKDir() {
14
- const victronPath = '/data/conf/signalk';
15
- const homePath = path.join(process.env.HOME || '', '.signalk');
16
-
17
- // Check Victron path first
18
- if (fs.existsSync(victronPath)) {
19
- return victronPath;
20
- }
21
-
22
- return homePath;
23
- }
24
-
25
- const signalkDir = getSignalKDir();
26
- console.log(`Using SignalK directory: ${signalkDir}`);
27
-
28
- // 1. Migration (only for signalk-to-noforeignland config)
29
- try {
30
- const configPath = path.join(signalkDir, 'plugin-config-data');
31
- const oldConfig = path.join(configPath, 'signalk-to-noforeignland.json');
32
- const newConfig = path.join(configPath, '@noforeignland-signalk-to-noforeignland.json');
33
-
34
- if (fs.existsSync(oldConfig) && !fs.existsSync(newConfig)) {
35
- fs.copyFileSync(oldConfig, newConfig);
36
- fs.copyFileSync(oldConfig, `${oldConfig}.backup`);
37
- console.log('✓ Configuration migrated');
38
- }
39
- } catch (e) {
40
- console.warn('Could not migrate config:', e.message);
41
- }
42
-
43
- // 2. Uninstall old plugins (with delay so npm can finish)
44
- setTimeout(() => {
45
- try {
46
- const oldPlugins = [
47
- { dir: path.join(signalkDir, 'node_modules', 'signalk-to-noforeignland'), name: 'signalk-to-noforeignland' },
48
- { dir: path.join(signalkDir, 'node_modules', 'signalk-to-nfl'), name: 'signalk-to-nfl' }
49
- ];
50
-
51
- let removedAny = false;
52
- const failedPlugins = [];
53
-
54
- for (const plugin of oldPlugins) {
55
- if (fs.existsSync(plugin.dir)) {
56
- try {
57
- console.log(`Removing old plugin "${plugin.name}"...`);
58
- // Direct deletion is safer than npm uninstall during installation
59
- fs.rmSync(plugin.dir, { recursive: true, force: true });
60
- console.log(`✓ Old plugin "${plugin.name}" removed`);
61
- removedAny = true;
62
- } catch (e) {
63
- console.warn(`Could not remove "${plugin.name}":`, e.message);
64
- failedPlugins.push(plugin.name);
65
- }
66
- }
67
- }
68
-
69
- if (failedPlugins.length > 0) {
70
- const uninstallCmd = failedPlugins.join(' ');
71
- console.warn(`Please run manually: npm uninstall ${uninstallCmd}`);
72
- }
73
-
74
- if (!removedAny && failedPlugins.length === 0) {
75
- console.log('No old plugins found - already clean!');
76
- }
77
- } catch (e) {
78
- console.warn('Error during cleanup:', e.message);
79
- }
80
- }, 2000); // Wait 2 seconds for npm to finish
@@ -1,116 +0,0 @@
1
- How-to install the latest beta or dev tree (unstable) on your device?
2
-
3
- This guide assumes you have a default install with default folders.
4
- This is written for a Victron Cerbo GX jump to the section you need want to go to.
5
- It is recommended to enable the Debug Log for this plugin in Server -> Plugin Config before updating.
6
-
7
-
8
- # Pre-requirements
9
- On the Cerbo GUI enable SSH access and set a afmin password
10
- Windows User: Download putty (for ssh) and Winscp (for secure fie copy)
11
-
12
- ## On GUI v1:
13
- 1. Click "Menu" (bottom right)
14
- 2. Settings > General
15
- 3. Set root password
16
- 4. Enable "SSH on LAN"
17
-
18
- ## On GUI v2:
19
- 1. Settings -> General
20
- 2. Set root password
21
- 3. Enable "SSH on LAN"
22
-
23
-
24
- # Backup old data
25
-
26
- ## BEST + SLOW: Full signalk copy to local computer
27
- 1. Connect your Cerbo with WinSCP
28
- 2. Copy the folder "/data/conf/signalk" to your local PC
29
-
30
- ## Alternative Cerbo has a SD Card connected:
31
- 1. ssh to the Cerbo
32
- ```
33
- df -h
34
- # showns for me that /dev/mmcblk0p1 has 29GB free and is mounted to /run/media/mmcblk0p1
35
- # This is a SD card that is manually inserted.
36
-
37
- mount
38
- # confirms this for me with output:
39
- # /dev/mmcblk0p1 on /run/media/mmcblk0p1 type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)
40
-
41
- cp -r /data/conf/signalk /run/media/mmcblk0p1/signalk-backup
42
- # copies the files for you, it will take some time, as the CPU and IO are not the fastest
43
-
44
- ls –la /run/media/mmcblk0p1/signalk-backup
45
- # Verify that folder and files exist.
46
- ```
47
-
48
-
49
- # npmjs beta on Cerbo
50
-
51
- ## Install latest beta
52
- 1. SSH to the Cerbo:
53
- ```
54
- cd /data/conf/signalk
55
-
56
- npm i @noforeignland/signalk-to-noforeignland@beta
57
-
58
- # reset owner properly, else package belongs to root
59
- chown -R signalk:signalk /data/conf/signalk/*
60
-
61
- ```
62
-
63
- 2. Restart Server & Check logs
64
- ```
65
- svc -t /service/signalk-server
66
- ```
67
-
68
-
69
- ## Install specific beta version f.e. 1.0.1-beta.5
70
- 1. SSH to the Cerbo:
71
- ```
72
- cd /data/conf/signalk
73
-
74
- # Here use the beta version you want to install
75
- npm i @noforeignland/signalk-to-noforeignland@1.0.1-beta.5
76
-
77
- # reset owner properly, else package belongs to root
78
- chown -R signalk:signalk /data/conf/signalk/*
79
-
80
- ```
81
- 2. Restart Server & Check logs
82
- ```
83
- svc -t /service/signalk-server
84
- ```
85
-
86
-
87
- ----------------------------------------
88
-
89
-
90
- # dev tree (unstable) install - NOT RECOMMENDED
91
-
92
- 1. Backup as above
93
-
94
- 2. Get new files from repo (main for latest)
95
-
96
- ```
97
- cd ~
98
- mkdir dev
99
- cd dev
100
- wget https://github.com/noforeignland/nfl-signalk/archive/refs/heads/main.zip
101
- unzip main.zip
102
- cd nfl-signalk-main/
103
- npm pack
104
-
105
- cd /data/conf/signalk
106
- npm install ~/dev/nfl-signalk-main/<your_npm_pack.tgz>
107
-
108
- # reset owner properly, else package belongs to root
109
- chown -R signalk:signalk /data/conf/signalk/*
110
-
111
- ```
112
-
113
- 3. Restart Server & Check logs
114
- ```
115
- svc -t /service/signalk-server
116
- ```
@@ -1,64 +0,0 @@
1
- How-to install the latest beta or dev tree (unstable) on your device?
2
-
3
- This guide assumes you have a default install with default folders.
4
- This is written for a RPI, jump to the section you need want to go to.
5
- It is recommended to enable the Debug Log for this plugin in Server -> Plugin Config before updating.
6
-
7
-
8
- # Backup old data
9
- ```
10
- cp -a ~/.signalk ~/signalk-backup
11
- ```
12
-
13
- # npmjs beta on RPI
14
-
15
- ## Install latest beta
16
- ```
17
- cd ~.signalk
18
-
19
- npm i @noforeignland/signalk-to-noforeignland@beta
20
-
21
- sudo systemctl restart signalk.service && sudo journalctl -u signalk.service -f
22
- ```
23
-
24
- ## Install a specific beta
25
- ```
26
- cd ~.signalk
27
-
28
- # Here use the beta version you want to install
29
- npm i @noforeignland/signalk-to-noforeignland@1.0.1-beta.5
30
-
31
- sudo systemctl restart signalk.service && sudo journalctl -u signalk.service -f
32
- ```
33
-
34
- --------------------------
35
-
36
- # dev tree (unstable) - NOT RECOMMENDED
37
- 1. Backup old data
38
-
39
- ```
40
- cd ~
41
- cp -a .signalk/ signalk-backup
42
- ```
43
-
44
- 2. Get new files from repo (main for latest)
45
-
46
- ```
47
- cd ~
48
- mkdir dev
49
- cd dev
50
- wget https://github.com/noforeignland/nfl-signalk/archive/refs/heads/main.zip
51
- unzip main.zip
52
- cd nfl-signalk-main/
53
- npm pack
54
-
55
- cd ~/.signalk
56
- npm install ~/dev/nfl-signalk-main/<your_npm_pack.tgz>
57
- ```
58
-
59
- 3. Restart Server & Check logs
60
-
61
- ```
62
- sudo systemctl restart signalk.service && sudo journalctl -u signalk.service -f
63
- ```
64
-
package/doc/dev DELETED
File without changes
package/index.js DELETED
@@ -1,304 +0,0 @@
1
- const CronJob = require('cron').CronJob;
2
-
3
- // Import all modules
4
- const ConfigManager = require('./lib/ConfigManager');
5
- const PluginCleanup = require('./lib/PluginCleanup');
6
- const TrackMigration = require('./lib/TrackMigration');
7
- const TrackLogger = require('./lib/TrackLogger');
8
- const TrackSender = require('./lib/TrackSender');
9
- const HealthMonitor = require('./lib/HealthMonitor');
10
- const DataPathEmitter = require('./lib/DataPathEmitter');
11
- const DirectoryUtils = require('./lib/DirectoryUtils');
12
-
13
- class SignalkToNoforeignland {
14
- constructor(app) {
15
- this.app = app;
16
- this.pluginId = 'signalk-to-noforeignland';
17
- this.pluginName = 'Signal K to Noforeignland';
18
-
19
- // Runtime state
20
- this.options = {};
21
- this.upSince = null;
22
- this.cron = null;
23
- this.lastSuccessfulTransfer = null;
24
-
25
- // Module instances
26
- this.configManager = new ConfigManager(app);
27
- this.pluginCleanup = new PluginCleanup(app);
28
- this.dataPathEmitter = new DataPathEmitter(app, this.pluginId);
29
- this.trackLogger = null;
30
- this.trackSender = null;
31
- this.healthMonitor = null;
32
- this.trackMigration = null;
33
- }
34
-
35
- /**
36
- * Get plugin schema
37
- */
38
- getSchema() {
39
- return ConfigManager.getSchema(this.pluginName);
40
- }
41
-
42
- /**
43
- * Get plugin object for SignalK
44
- */
45
- getPluginObject() {
46
- return {
47
- id: this.pluginId,
48
- name: this.pluginName,
49
- description: 'SignalK track logger to noforeignland.com',
50
- schema: this.getSchema(),
51
- start: this.start.bind(this),
52
- stop: this.stop.bind(this)
53
- };
54
- }
55
-
56
- /**
57
- * Start the plugin
58
- */
59
- async start(options = {}, restartPlugin) {
60
- try {
61
- // 1. Migrate old config structure if needed
62
- const { options: migratedOptions, migrated } = await this.configManager.migrateOldConfig(options);
63
- options = migratedOptions;
64
-
65
- // 2. Flatten config and apply defaults
66
- this.options = this.configManager.flattenConfig(options);
67
-
68
- // 3. Validate API key
69
- try {
70
- this.configManager.validateApiKey(this.options);
71
- } catch (err) {
72
- this.app.debug(err.message);
73
- this.setPluginError(err.message);
74
- this.stop();
75
- return;
76
- }
77
-
78
- // 4. Resolve track directory path
79
- const dataDirPath = this.app.getDataDirPath();
80
- this.options.trackDir = this.configManager.resolveTrackDir(this.options, dataDirPath);
81
-
82
- // 5. Create track directory
83
- try {
84
- DirectoryUtils.createDir(this.options.trackDir, this.app);
85
- } catch (err) {
86
- this.setPluginError(err.message);
87
- this.stop();
88
- return;
89
- }
90
-
91
- // 6. Cleanup old plugin versions (with callback handling)
92
- this.pluginCleanup.cleanup()
93
- .then((result) => {
94
- if (result === 'all_removed') {
95
- this.app.debug('Old plugins successfully cleaned up');
96
- // Update status if plugin started successfully
97
- if (this.options.boatApiKey) {
98
- this.setPluginStatus('Started (old plugins cleaned up)');
99
- }
100
- }
101
- })
102
- .catch(err => {
103
- this.app.debug('Error in cleanupOldPlugin:', err.message);
104
- });
105
-
106
- // 7. Migrate old track files
107
- this.trackMigration = new TrackMigration(this.app, this.options.trackDir);
108
- await this.trackMigration.migrate();
109
-
110
- // 8. Initialize modules
111
- this.trackLogger = new TrackLogger(this.app, this.options, this.options.trackDir);
112
- this.trackSender = new TrackSender(this.app, this.options, this.options.trackDir);
113
- this.healthMonitor = new HealthMonitor(this.app, this.options);
114
-
115
- // 9. Update startup time and randomize CRON
116
- this.upSince = new Date().getTime();
117
- this.options = this.configManager.randomizeCron(this.options);
118
-
119
- this.app.debug('Setting CRON to', this.options.apiCron);
120
- this.app.debug('trackFrequency is set to', this.options.trackFrequency, 'seconds');
121
- this.app.debug('track logger started, now logging to', this.options.trackDir);
122
-
123
- // 10. Start logging
124
- this.trackLogger.startLogging((lastPosition) => {
125
- this.handleSavePoint(lastPosition);
126
- });
127
-
128
- // 11. Start CRON job for sending data
129
- this.cron = new CronJob(this.options.apiCron, this.interval.bind(this));
130
- this.cron.start();
131
-
132
- // 12. Start health monitoring
133
- this.healthMonitor.start(
134
- () => this.trackLogger.getLastPositionReceived(),
135
- () => this.trackLogger.getAutoSelectedSource(),
136
- (errorMsg) => this.setPluginError(errorMsg),
137
- () => this.handleHealthy()
138
- );
139
-
140
- // 13. Set plugin status
141
- this.setPluginStatus(`Started${migrated ? ' (config migrated)' : ''}`);
142
-
143
- } catch (err) {
144
- this.app.debug('Error during plugin start:', err.message);
145
- this.setPluginError(`Failed to start: ${err.message}`);
146
- this.stop();
147
- }
148
- }
149
-
150
- /**
151
- * Handle savepoint event
152
- */
153
- handleSavePoint(lastPosition) {
154
- const now = new Date();
155
- this.dataPathEmitter.emitSavepoint();
156
-
157
- // Update plugin status
158
- const activeSource = this.options.filterSource || this.trackLogger.getAutoSelectedSource() || '';
159
- const sourcePrefix = activeSource ? `${activeSource} | ` : '';
160
- const saveTime = now.toISOString();
161
- const transferTime = this.lastSuccessfulTransfer
162
- ? this.lastSuccessfulTransfer.toISOString()
163
- : 'None since start';
164
-
165
- this.setPluginStatus(`Save: ${saveTime} | Transfer: ${transferTime} | ${sourcePrefix}`);
166
- }
167
-
168
- /**
169
- * Handle healthy status from health monitor
170
- */
171
- handleHealthy() {
172
- // Clear error if position health is OK
173
- if (this.dataPathEmitter.getError()) {
174
- const lastPosition = this.trackLogger.getLastPosition();
175
- const activeSource = this.options.filterSource || this.trackLogger.getAutoSelectedSource() || '';
176
- const sourcePrefix = activeSource ? `${activeSource} | ` : '';
177
- const saveTime = lastPosition
178
- ? new Date(lastPosition.currentTime).toISOString()
179
- : 'None since start';
180
- const transferTime = this.lastSuccessfulTransfer
181
- ? this.lastSuccessfulTransfer.toISOString()
182
- : 'None since start';
183
-
184
- this.setPluginStatus(`Save: ${saveTime} | Transfer: ${transferTime} | ${sourcePrefix}`);
185
- }
186
- }
187
-
188
- /**
189
- * CRON interval - check and send data
190
- */
191
- async interval() {
192
- try {
193
- // Check if boat is moving
194
- const lastPosition = this.trackLogger.getLastPosition();
195
- const boatMoving = this.trackSender.isBoatMoving(lastPosition, this.upSince);
196
- if (!boatMoving) {
197
- return;
198
- }
199
-
200
- // Check if we have track data
201
- const hasTrack = await this.trackSender.hasTrackData();
202
- if (!hasTrack) {
203
- return;
204
- }
205
-
206
- // Test internet connection
207
- const hasInternet = await this.trackSender.testInternet();
208
- if (!hasInternet) {
209
- const errorMsg = 'No internet connection detected. Unable to send tracking data to NFL. DNS lookups failed - check your internet connection.';
210
- this.app.debug(errorMsg);
211
- this.setPluginError(errorMsg);
212
- return;
213
- }
214
-
215
- // Send track data
216
- const success = await this.trackSender.sendTrack();
217
-
218
- if (success) {
219
- this.lastSuccessfulTransfer = new Date();
220
- this.dataPathEmitter.emitApiTransfer(this.lastSuccessfulTransfer);
221
-
222
- // Update status
223
- const activeSource = this.options.filterSource || this.trackLogger.getAutoSelectedSource() || '';
224
- const sourcePrefix = activeSource ? `${activeSource} | ` : '';
225
- const saveTime = lastPosition
226
- ? new Date(lastPosition.currentTime).toISOString()
227
- : 'None since start';
228
- const transferTime = this.lastSuccessfulTransfer.toISOString();
229
-
230
- this.setPluginStatus(`Save: ${saveTime} | Transfer: ${transferTime} | ${sourcePrefix}`);
231
- }
232
-
233
- } catch (err) {
234
- this.app.debug('Error during send interval:', err.message);
235
- this.setPluginError(`Failed to send track - ${err.message}`);
236
- }
237
- }
238
-
239
- /**
240
- * Stop the plugin
241
- */
242
- stop() {
243
- this.app.debug('plugin stopped');
244
-
245
- // Stop CRON job
246
- if (this.cron) {
247
- this.cron.stop();
248
- this.cron = undefined;
249
- }
250
-
251
- // Stop track logger
252
- if (this.trackLogger) {
253
- this.trackLogger.stop();
254
- }
255
-
256
- // Stop health monitor
257
- if (this.healthMonitor) {
258
- this.healthMonitor.stop();
259
- }
260
-
261
- this.app.setPluginStatus('Plugin stopped');
262
- }
263
-
264
- /**
265
- * Set plugin status and update data paths
266
- */
267
- setPluginStatus(status) {
268
- this.dataPathEmitter.clearError();
269
- this.app.setPluginStatus(status);
270
-
271
- const lastPosition = this.trackLogger ? this.trackLogger.getLastPosition() : null;
272
- const autoSelectedSource = this.trackLogger ? this.trackLogger.getAutoSelectedSource() : null;
273
-
274
- this.dataPathEmitter.updateStatusPaths(
275
- this.options,
276
- lastPosition,
277
- this.lastSuccessfulTransfer,
278
- autoSelectedSource
279
- );
280
- }
281
-
282
- /**
283
- * Set plugin error and update data paths
284
- */
285
- setPluginError(error) {
286
- this.dataPathEmitter.setError(error);
287
- this.app.setPluginError(error);
288
-
289
- const lastPosition = this.trackLogger ? this.trackLogger.getLastPosition() : null;
290
- const autoSelectedSource = this.trackLogger ? this.trackLogger.getAutoSelectedSource() : null;
291
-
292
- this.dataPathEmitter.updateStatusPaths(
293
- this.options,
294
- lastPosition,
295
- this.lastSuccessfulTransfer,
296
- autoSelectedSource
297
- );
298
- }
299
- }
300
-
301
- module.exports = function (app) {
302
- const instance = new SignalkToNoforeignland(app);
303
- return instance.getPluginObject();
304
- };