@noforeignland/signalk-to-noforeignland 1.1.0 → 1.2.0-beta.2

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,127 +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
- # dev tree (unstable) install - NOT RECOMMENDED
88
-
89
- 1. Backup as above
90
-
91
- 2. Get new files from repo (main for latest)
92
-
93
- ```
94
- cd ~
95
- mkdir dev
96
- cd dev
97
- wget https://github.com/noforeignland/nfl-signalk/archive/refs/heads/main.zip
98
- unzip main.zip
99
- cd nfl-signalk-main/
100
- npm pack
101
-
102
- cd /data/conf/signalk
103
- npm install ~/dev/nfl-signalk-main/<your_npm_pack.tgz>
104
-
105
- # reset owner properly, else package belongs to root
106
- chown -R signalk:signalk /data/conf/signalk/*
107
-
108
- ```
109
-
110
- 3. Restart Server & Check logs
111
- ```
112
- svc -t /service/signalk-server
113
- ```
114
-
115
- # Manual Fixes
116
-
117
- ## WARNING: found multiple copies of plugin with id signalk-to-noforeignland at /data/conf/signalk/node_modules/ and /data/conf/signalk/node_modules/
118
-
119
- ```
120
- cd /data/conf/signalk && npm uninstall signalk-to-noforeignland signalk-to-nfl
121
- ```
122
-
123
- and than
124
-
125
- ```
126
- svc -t /service/signalk-server
127
- ```
@@ -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/index.js DELETED
@@ -1,295 +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
- // Send track data (has built-in retry logic)
207
- const success = await this.trackSender.sendTrack();
208
-
209
- if (success) {
210
- this.lastSuccessfulTransfer = new Date();
211
- this.dataPathEmitter.emitApiTransfer(this.lastSuccessfulTransfer);
212
-
213
- // Update status
214
- const activeSource = this.options.filterSource || this.trackLogger.getAutoSelectedSource() || '';
215
- const sourcePrefix = activeSource ? `${activeSource} | ` : '';
216
- const saveTime = lastPosition
217
- ? new Date(lastPosition.currentTime).toISOString()
218
- : 'None since start';
219
- const transferTime = this.lastSuccessfulTransfer.toISOString();
220
-
221
- this.setPluginStatus(`Save: ${saveTime} | Transfer: ${transferTime} | ${sourcePrefix}`);
222
- }
223
-
224
- } catch (err) {
225
- this.app.debug('Error during send interval:', err.message);
226
- this.setPluginError(`Failed to send track - ${err.message}`);
227
- }
228
- }
229
-
230
- /**
231
- * Stop the plugin
232
- */
233
- stop() {
234
- this.app.debug('plugin stopped');
235
-
236
- // Stop CRON job
237
- if (this.cron) {
238
- this.cron.stop();
239
- this.cron = undefined;
240
- }
241
-
242
- // Stop track logger
243
- if (this.trackLogger) {
244
- this.trackLogger.stop();
245
- }
246
-
247
- // Stop health monitor
248
- if (this.healthMonitor) {
249
- this.healthMonitor.stop();
250
- }
251
-
252
- this.app.setPluginStatus('Plugin stopped');
253
- }
254
-
255
- /**
256
- * Set plugin status and update data paths
257
- */
258
- setPluginStatus(status) {
259
- this.dataPathEmitter.clearError();
260
- this.app.setPluginStatus(status);
261
-
262
- const lastPosition = this.trackLogger ? this.trackLogger.getLastPosition() : null;
263
- const autoSelectedSource = this.trackLogger ? this.trackLogger.getAutoSelectedSource() : null;
264
-
265
- this.dataPathEmitter.updateStatusPaths(
266
- this.options,
267
- lastPosition,
268
- this.lastSuccessfulTransfer,
269
- autoSelectedSource
270
- );
271
- }
272
-
273
- /**
274
- * Set plugin error and update data paths
275
- */
276
- setPluginError(error) {
277
- this.dataPathEmitter.setError(error);
278
- this.app.setPluginError(error);
279
-
280
- const lastPosition = this.trackLogger ? this.trackLogger.getLastPosition() : null;
281
- const autoSelectedSource = this.trackLogger ? this.trackLogger.getAutoSelectedSource() : null;
282
-
283
- this.dataPathEmitter.updateStatusPaths(
284
- this.options,
285
- lastPosition,
286
- this.lastSuccessfulTransfer,
287
- autoSelectedSource
288
- );
289
- }
290
- }
291
-
292
- module.exports = function (app) {
293
- const instance = new SignalkToNoforeignland(app);
294
- return instance.getPluginObject();
295
- };
@@ -1,216 +0,0 @@
1
- const path = require('path');
2
-
3
- const defaultTracksDir = 'nfl-track';
4
-
5
- class ConfigManager {
6
- constructor(app) {
7
- this.app = app;
8
- }
9
-
10
- /**
11
- * Migrate old flat config structure to new grouped structure
12
- */
13
- async migrateOldConfig(options) {
14
- if (options.boatApiKey && !options.mandatory) {
15
- this.app.debug('Migrating old configuration to new grouped structure');
16
-
17
- const migratedOptions = {
18
- mandatory: {
19
- boatApiKey: options.boatApiKey
20
- },
21
- advanced: {
22
- minMove: options.minMove !== undefined ? options.minMove : 50,
23
- minSpeed: options.minSpeed !== undefined ? options.minSpeed : 1.5,
24
- sendWhileMoving: options.sendWhileMoving !== undefined ? options.sendWhileMoving : true,
25
- ping_api_every_24h: options.ping_api_every_24h !== undefined ? options.ping_api_every_24h : true
26
- },
27
- expert: {
28
- filterSource: options.filterSource,
29
- trackDir: options.trackDir,
30
- keepFiles: options.keepFiles !== undefined ? options.keepFiles : false,
31
- trackFrequency: options.trackFrequency !== undefined ? options.trackFrequency : 60,
32
- internetTestTimeout: options.internetTestTimeout !== undefined ? options.internetTestTimeout : 2000,
33
- apiCron: options.apiCron || '*/10 * * * *',
34
- apiTimeout: options.apiTimeout !== undefined ? options.apiTimeout : 30
35
- }
36
- };
37
-
38
- try {
39
- this.app.debug('Saving migrated configuration...');
40
- await this.app.savePluginOptions(migratedOptions, () => {
41
- this.app.debug('Configuration successfully migrated and saved');
42
- });
43
- return { options: migratedOptions, migrated: true };
44
- } catch (err) {
45
- this.app.debug('Failed to save migrated configuration:', err.message);
46
- return { options: migratedOptions, migrated: true };
47
- }
48
- }
49
-
50
- return { options, migrated: false };
51
- }
52
-
53
- /**
54
- * Flatten nested config structure and apply defaults
55
- */
56
- flattenConfig(options) {
57
- return {
58
- boatApiKey: options.mandatory?.boatApiKey,
59
- minMove: options.advanced?.minMove !== undefined ? options.advanced.minMove : 80,
60
- minSpeed: options.advanced?.minSpeed !== undefined ? options.advanced.minSpeed : 1.5,
61
- sendWhileMoving: options.advanced?.sendWhileMoving !== undefined ? options.advanced.sendWhileMoving : true,
62
- ping_api_every_24h: options.advanced?.ping_api_every_24h !== undefined ? options.advanced.ping_api_every_24h : true,
63
- filterSource: options.expert?.filterSource,
64
- trackDir: options.expert?.trackDir || defaultTracksDir,
65
- keepFiles: options.expert?.keepFiles !== undefined ? options.expert.keepFiles : false,
66
- trackFrequency: options.expert?.trackFrequency !== undefined ? options.expert.trackFrequency : 60,
67
- internetTestTimeout: options.expert?.internetTestTimeout !== undefined ? options.expert.internetTestTimeout : 2000,
68
- apiCron: options.expert?.apiCron || '*/10 * * * *',
69
- apiTimeout: options.expert?.apiTimeout !== undefined ? options.expert.apiTimeout : 30
70
- };
71
- }
72
-
73
- /**
74
- * Validate boat API key
75
- */
76
- validateApiKey(config) {
77
- if (!config.boatApiKey || config.boatApiKey.trim() === '') {
78
- throw new Error(
79
- 'No boat API key configured. Please set your API key in plugin settings ' +
80
- '(Mandatory Settings > Boat API key). You can find your API key at ' +
81
- 'noforeignland.com under Account > Settings > Boat tracking > API Key.'
82
- );
83
- }
84
- }
85
-
86
- /**
87
- * Resolve track directory path (absolute or relative to data dir)
88
- */
89
- resolveTrackDir(config, dataDirPath) {
90
- if (!path.isAbsolute(config.trackDir)) {
91
- return path.join(dataDirPath, config.trackDir);
92
- }
93
- return config.trackDir;
94
- }
95
-
96
- /**
97
- * Randomize CRON schedule to avoid all instances running at same time
98
- */
99
- randomizeCron(config) {
100
- if (!config.apiCron || config.apiCron === '*/10 * * * *') {
101
- const startMinute = Math.floor(Math.random() * 10);
102
- const startSecond = Math.floor(Math.random() * 60);
103
- config.apiCron = `${startSecond} ${startMinute}/10 * * * *`;
104
- }
105
- return config;
106
- }
107
-
108
- /**
109
- * Get the full schema for plugin configuration
110
- */
111
- static getSchema(pluginName) {
112
- return {
113
- title: pluginName,
114
- description: 'Some parameters need for use',
115
- type: 'object',
116
- properties: {
117
- // Mandatory Settings Group
118
- mandatory: {
119
- type: 'object',
120
- title: 'Mandatory Settings',
121
- properties: {
122
- boatApiKey: {
123
- type: 'string',
124
- title: 'Boat API Key',
125
- description: 'Boat API Key from noforeignland.com. Can be found in Account > Settings > Boat tracking > API Key.'
126
- }
127
- }
128
- },
129
-
130
- // Advanced Settings Group
131
- advanced: {
132
- type: 'object',
133
- title: 'Advanced Settings',
134
- properties: {
135
- minMove: {
136
- type: 'number',
137
- title: 'Minimum boat move to log in meters',
138
- description: 'To keep file sizes small we only log positions if a move larger than this size (if set to 0 will log every move)',
139
- default: 80
140
- },
141
- minSpeed: {
142
- type: 'number',
143
- title: 'Minimum boat speed to log in knots',
144
- description: 'To keep file sizes small we only log positions if boat speed goes above this value to minimize recording position on anchor or mooring (if set to 0 will log every move)',
145
- default: 1.5
146
- },
147
- sendWhileMoving: {
148
- type: 'boolean',
149
- title: 'Attempt sending location while moving',
150
- description: 'Should the plugin attempt to send tracking data to NFL while detecting the vessel is moving or only when stopped?',
151
- default: true
152
- },
153
- ping_api_every_24h: {
154
- type: 'boolean',
155
- title: 'Force a send every 24 hours',
156
- description: 'Keeps your boat active on NFL in your current location even if you do not move',
157
- default: true
158
- }
159
- }
160
- },
161
-
162
- // Expert Settings Group
163
- expert: {
164
- type: 'object',
165
- title: 'Expert Settings',
166
- properties: {
167
- filterSource: {
168
- type: 'string',
169
- title: 'Position source device',
170
- description: 'EMPTY DEFAULT IS FINE - Set this value to the name of a source if you want to only use the position given by that source.'
171
- },
172
- trackDir: {
173
- type: 'string',
174
- title: 'Directory to cache tracks',
175
- description: 'EMPTY DEFAULT IS FINE - Path to store track data. Relative paths are stored in plugin data directory. Absolute paths can point anywhere.\nDefault: nfl-track'
176
- },
177
- keepFiles: {
178
- type: 'boolean',
179
- title: 'Keep track files on disk',
180
- description: 'If you have a lot of hard drive space you can keep the track files for logging purposes.',
181
- default: false
182
- },
183
- trackFrequency: {
184
- type: 'integer',
185
- title: 'Position tracking frequency in seconds',
186
- description: 'To keep file sizes small we only log positions once in a while (unless you set this value to 0)',
187
- default: 60
188
- },
189
- apiCron: {
190
- type: 'string',
191
- title: 'Send attempt CRON',
192
- description: 'We send the tracking data to NFL once in a while, you can set the schedule with this setting.\nCRON format: https://crontab.guru/',
193
- default: '*/10 * * * *'
194
- },
195
- internetTestTimeout: {
196
- type: 'number',
197
- title: 'Timeout for testing internet connection in ms',
198
- description: 'Set this number higher for slower computers and internet connections',
199
- default: 2000
200
- },
201
- apiTimeout: {
202
- type: 'integer',
203
- title: 'API request timeout in seconds',
204
- description: 'Timeout for sending data to NFL API. Increase for slow connections.',
205
- default: 30,
206
- minimum: 10,
207
- maximum: 180
208
- }
209
- }
210
- }
211
- }
212
- };
213
- }
214
- }
215
-
216
- module.exports = ConfigManager;