@meri-imperiumi/signalk-meshtastic 1.2.1 → 1.2.3
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/README.md +8 -1
- package/package.json +1 -1
- package/plugin/commands/waypoint.js +1 -1
- package/plugin/index.js +44 -15
- package/plugin/telemetry.js +11 -8
package/README.md
CHANGED
|
@@ -91,12 +91,19 @@ Metrics used:
|
|
|
91
91
|
|
|
92
92
|
## Changes
|
|
93
93
|
|
|
94
|
+
* 1.2.3 (2024-10-15)
|
|
95
|
+
- Nodes that haven't been seen in last two days are no longer registered to Signal K data structure
|
|
96
|
+
- Added safeties for various non-numeric telemetry and coordinate values
|
|
97
|
+
* 1.2.2 (2025-10-01)
|
|
98
|
+
- Set "last seen" timestamp of nodes based on packet payloads, not the time they're received
|
|
99
|
+
- Send timestamp with telemetry
|
|
100
|
+
- Fixed issue with persising node-to-vessel matches from `DE <callsign>`
|
|
94
101
|
* 1.2.1 (2025-09-28)
|
|
95
102
|
- Fixed issue with Signal K servers that don't have navigation.position set
|
|
96
103
|
* 1.2.0 (2025-09-28)
|
|
104
|
+
- Support for Node.js older than 22.x, for example as seen in Venus OS Large
|
|
97
105
|
- Safety for nodes in DB that don't have a "last seen" timestamp
|
|
98
106
|
- Made connection status notifications clearer
|
|
99
|
-
- Attempt at support for Node.js older than 22.x
|
|
100
107
|
* 1.1.2 (2025-09-25)
|
|
101
108
|
- Added support for the new roles from Meshtastic 2.7 (`ROUTER_LATE` and `CLIENT_BASE`)
|
|
102
109
|
- Fixed issue with sending a bell with alerts that have sound enabled
|
package/package.json
CHANGED
|
@@ -47,7 +47,7 @@ module.exports = {
|
|
|
47
47
|
latitudeI: Math.floor(waypointVessel.navigation.position.value.latitude / 1e-7),
|
|
48
48
|
longitudeI: Math.floor(waypointVessel.navigation.position.value.longitude / 1e-7),
|
|
49
49
|
expire: Math.floor((new Date().getTime() / 1000) + (length * 60 * 60)),
|
|
50
|
-
name: waypointVessel.name,
|
|
50
|
+
name: waypointVessel.name || waypointVessel.mmsi,
|
|
51
51
|
description: `AIS vessel ${waypointVessel.mmsi}`,
|
|
52
52
|
icon: vesselIcon(waypointVessel),
|
|
53
53
|
});
|
package/plugin/index.js
CHANGED
|
@@ -101,7 +101,7 @@ function nodeToSignalK(app, node, nodeInfo, settings) {
|
|
|
101
101
|
},
|
|
102
102
|
];
|
|
103
103
|
|
|
104
|
-
if (nodeInfo.position) {
|
|
104
|
+
if (nodeInfo.position && Number.isFinite(nodeInfo.position.latitudeI)) {
|
|
105
105
|
values.push({
|
|
106
106
|
path: 'navigation.position',
|
|
107
107
|
value: {
|
|
@@ -278,6 +278,7 @@ module.exports = (app) => {
|
|
|
278
278
|
return;
|
|
279
279
|
}
|
|
280
280
|
const telemetryMessage = create(Protobuf.Telemetry.TelemetrySchema, {
|
|
281
|
+
time: Math.floor(new Date().getTime() / 1000),
|
|
281
282
|
variant: {
|
|
282
283
|
case: 'environmentMetrics',
|
|
283
284
|
value: create(Protobuf.Telemetry.EnvironmentMetricsSchema, values),
|
|
@@ -310,6 +311,13 @@ module.exports = (app) => {
|
|
|
310
311
|
}, 60000 * minutes);
|
|
311
312
|
}
|
|
312
313
|
|
|
314
|
+
function writeNodeDb() {
|
|
315
|
+
writeFile(nodeDbFile, JSON.stringify(nodes, null, 2), 'utf-8')
|
|
316
|
+
.catch((e) => {
|
|
317
|
+
app.error(`Failed to store node DB: ${e.message}`);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
313
321
|
function setConnectionStatus() {
|
|
314
322
|
setWatchdog();
|
|
315
323
|
const now = new Date();
|
|
@@ -553,22 +561,27 @@ module.exports = (app) => {
|
|
|
553
561
|
nodes[nodeInfo.num].publicKey = Buffer.from(nodeInfo.user.publicKey).toString('base64');
|
|
554
562
|
}
|
|
555
563
|
nodes[nodeInfo.num].seen = new Date(nodeInfo.lastHeard * 1000);
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
564
|
+
if (nodes[nodeInfo.num].seen > Date.now() - (1000 * 60 * 60 * 24 * 2)) {
|
|
565
|
+
// Node seen less than a two days ago, register with SK
|
|
566
|
+
const ctx = nodeToSignalK(app, nodes[nodeInfo.num], nodeInfo, settings);
|
|
567
|
+
if (ctx && ctx.indexOf('vessels.urn:mrn:imo:mmsi:') === 0) {
|
|
568
|
+
// We have an MMSI match, store it
|
|
569
|
+
nodes[nodeInfo.num].mmsi = ctx.split(':').at(-1);
|
|
570
|
+
}
|
|
560
571
|
}
|
|
561
572
|
setConnectionStatus();
|
|
562
|
-
|
|
563
|
-
.catch((e) => {
|
|
564
|
-
app.error(`Failed to store node DB: ${e.message}`);
|
|
565
|
-
});
|
|
573
|
+
writeNodeDb();
|
|
566
574
|
}),
|
|
567
575
|
device.events.onMeshPacket.subscribe((packet) => {
|
|
568
576
|
if (!nodes[packet.from]) {
|
|
569
577
|
nodes[packet.from] = {};
|
|
570
578
|
}
|
|
571
|
-
|
|
579
|
+
if (packet.rxTime) {
|
|
580
|
+
const packetDate = new Date(packet.rxTime * 1000);
|
|
581
|
+
if (packetDate > nodes[packet.from].seen) {
|
|
582
|
+
nodes[packet.from].seen = packetDate;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
572
585
|
setConnectionStatus();
|
|
573
586
|
}),
|
|
574
587
|
device.events.onMessagePacket.subscribe((message) => {
|
|
@@ -604,7 +617,12 @@ module.exports = (app) => {
|
|
|
604
617
|
// Unknown node
|
|
605
618
|
return;
|
|
606
619
|
}
|
|
607
|
-
|
|
620
|
+
if (packet.data && packet.data.time) {
|
|
621
|
+
const telemetryDate = new Date(packet.data.time * 1000);
|
|
622
|
+
if (telemetryDate > nodes[packet.from].seen) {
|
|
623
|
+
nodes[packet.from].seen = telemetryDate;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
608
626
|
setConnectionStatus();
|
|
609
627
|
const context = getNodeContext(app, nodes[packet.from], packet.from, settings);
|
|
610
628
|
if (!context) {
|
|
@@ -697,7 +715,12 @@ module.exports = (app) => {
|
|
|
697
715
|
// Unknown node
|
|
698
716
|
return;
|
|
699
717
|
}
|
|
700
|
-
|
|
718
|
+
if (position.data && position.data.time) {
|
|
719
|
+
const positionDate = new Date(position.data.time * 1000);
|
|
720
|
+
if (positionDate > nodes[position.from].seen) {
|
|
721
|
+
nodes[position.from].seen = positionDate;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
701
724
|
setConnectionStatus();
|
|
702
725
|
const context = getNodeContext(app, nodes[position.from], position.from, settings);
|
|
703
726
|
if (!context) {
|
|
@@ -734,7 +757,7 @@ module.exports = (app) => {
|
|
|
734
757
|
},
|
|
735
758
|
{
|
|
736
759
|
path: 'navigation.gnss.antennaAltitude',
|
|
737
|
-
value: position.data.altitude,
|
|
760
|
+
value: position.data.altitude || 0,
|
|
738
761
|
},
|
|
739
762
|
];
|
|
740
763
|
app.handleMessage('signalk-meshtastic', {
|
|
@@ -750,6 +773,11 @@ module.exports = (app) => {
|
|
|
750
773
|
},
|
|
751
774
|
],
|
|
752
775
|
});
|
|
776
|
+
if (context && context.indexOf('vessels.urn:mrn:imo:mmsi:') === 0) {
|
|
777
|
+
// We have an MMSI match, store it
|
|
778
|
+
nodes[position.from].mmsi = context.split(':').at(-1);
|
|
779
|
+
}
|
|
780
|
+
writeNodeDb();
|
|
753
781
|
}),
|
|
754
782
|
);
|
|
755
783
|
|
|
@@ -822,7 +850,8 @@ module.exports = (app) => {
|
|
|
822
850
|
// Not connected to Meshtastic yet
|
|
823
851
|
return;
|
|
824
852
|
}
|
|
825
|
-
if (v.value.latitude
|
|
853
|
+
if (!Number.isFinite(v.value.latitude)
|
|
854
|
+
|| !Number.isFinite(v.value.longitude)) {
|
|
826
855
|
// No position
|
|
827
856
|
return;
|
|
828
857
|
}
|
|
@@ -886,7 +915,7 @@ module.exports = (app) => {
|
|
|
886
915
|
}
|
|
887
916
|
}
|
|
888
917
|
}
|
|
889
|
-
if (!mobPosition || !mobPosition.latitude) {
|
|
918
|
+
if (!mobPosition || !Number.isFinite(mobPosition.latitude)) {
|
|
890
919
|
return;
|
|
891
920
|
}
|
|
892
921
|
const setWaypointMessage = create(Protobuf.Mesh.WaypointSchema, {
|
package/plugin/telemetry.js
CHANGED
|
@@ -14,16 +14,16 @@ class Telemetry {
|
|
|
14
14
|
|
|
15
15
|
toMeshtastic() {
|
|
16
16
|
const values = {};
|
|
17
|
-
if (this.data['environment.outside.temperature']) {
|
|
17
|
+
if (Number.isFinite(this.data['environment.outside.temperature'])) {
|
|
18
18
|
values.temperature = this.data['environment.outside.temperature'] - 273.15;
|
|
19
19
|
}
|
|
20
|
-
if (this.data['environment.outside.relativeHumidity']) {
|
|
20
|
+
if (Number.isFinite(this.data['environment.outside.relativeHumidity'])) {
|
|
21
21
|
values.relativeHumidity = this.data['environment.outside.relativeHumidity'] * 100;
|
|
22
22
|
}
|
|
23
|
-
if (this.data['environment.outside.pressure']) {
|
|
23
|
+
if (Number.isFinite(this.data['environment.outside.pressure'])) {
|
|
24
24
|
values.barometricPressure = this.data['environment.outside.pressure'] / 100;
|
|
25
25
|
}
|
|
26
|
-
if (this.data['environment.wind.directionTrue']) {
|
|
26
|
+
if (Number.isFinite(this.data['environment.wind.directionTrue'])) {
|
|
27
27
|
values.windDirection = Math.floor(this.data['environment.wind.directionTrue'] * (180 / Math.PI));
|
|
28
28
|
}
|
|
29
29
|
if (this.data['environment.wind.speedOverGround'] && this.data['environment.wind.speedOverGround'].length) {
|
|
@@ -38,16 +38,16 @@ class Telemetry {
|
|
|
38
38
|
// Clear wind history
|
|
39
39
|
this.data['environment.wind.speedOverGround'] = [];
|
|
40
40
|
}
|
|
41
|
-
if (this.data['electrical.batteries.house.voltage']) {
|
|
41
|
+
if (Number.isFinite(this.data['electrical.batteries.house.voltage'])) {
|
|
42
42
|
values.voltage = this.data['electrical.batteries.house.voltage'];
|
|
43
43
|
}
|
|
44
|
-
if (this.data['electrical.batteries.house.current']) {
|
|
44
|
+
if (Number.isFinite(this.data['electrical.batteries.house.current'])) {
|
|
45
45
|
values.current = this.data['electrical.batteries.house.current'] * 1000;
|
|
46
46
|
}
|
|
47
|
-
if (this.data['navigation.anchor.distanceFromBow']) {
|
|
47
|
+
if (Number.isFinite(this.data['navigation.anchor.distanceFromBow'])) {
|
|
48
48
|
// Using distance is a bit silly here as the unit is mm, but what can we do
|
|
49
49
|
values.distance = this.data['navigation.anchor.distanceFromBow'] * 1000;
|
|
50
|
-
} else if (this.data['environment.depth.belowSurface']) {
|
|
50
|
+
} else if (Number.isFinite(this.data['environment.depth.belowSurface'])) {
|
|
51
51
|
// If not anchored, report depth as distance. Still mm.
|
|
52
52
|
values.distance = this.data['environment.depth.belowSurface'] * 1000;
|
|
53
53
|
}
|
|
@@ -55,6 +55,9 @@ class Telemetry {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
updateWindSpeed(windSpeed) {
|
|
58
|
+
if (!Number.isFinite(windSpeed)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
58
61
|
if (!this.data['environment.wind.speedOverGround']) {
|
|
59
62
|
this.data['environment.wind.speedOverGround'] = [];
|
|
60
63
|
}
|