@canboat/canboatjs 1.20.2 → 1.22.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/README.md CHANGED
@@ -205,9 +205,9 @@ Output: `!PDGY,127245,255,/Pj/f/9///8=`
205
205
  ## Generate YDGW-02 format from canboat json
206
206
 
207
207
  ```js
208
- const pgnToYdwgRawFormat = require("./index").pgnToYdwgRawFormat;
208
+ const pgnToYdgwRawFormat = require("./index").pgnToYdgwRawFormat;
209
209
 
210
- const array = pgnToYdwgRawFormat({
210
+ const array = pgnToYdgwRawFormat({
211
211
  src: 127,
212
212
  prio: 3,
213
213
  dst: 255,
package/index.js CHANGED
@@ -34,7 +34,8 @@ module.exports = {
34
34
  Venus: require('./lib/venus'),
35
35
  VenusMQTT: require('./lib/venus-mqtt'),
36
36
  discover: require('./lib/discovery'),
37
- SimpleCan: require('./lib/simpleCan')
37
+ SimpleCan: require('./lib/simpleCan'),
38
+ addCustomPgn: require('./lib/pgns').addCustomPgn
38
39
  }
39
40
 
40
41
  try {
package/ios.js CHANGED
@@ -1,6 +1,15 @@
1
1
 
2
2
  const FromPgn = require('./lib/fromPgn').Parser
3
- const { pgnToYdgwRawFormat, actisenseToYdgwRawFormat, pgnToPCDIN, actisenseToPCDIN} = require('./lib/toPgn')
3
+ const {
4
+ pgnToYdgwRawFormat,
5
+ actisenseToYdgwRawFormat,
6
+ pgnToPCDIN,
7
+ actisenseToPCDIN,
8
+ pgnToiKonvertSerialFormat,
9
+ actisenseToiKonvert,
10
+ pgnToMXPGN,
11
+ actisenseToMXPGN
12
+ } = require('./lib/toPgn')
4
13
 
5
14
  const parser = new FromPgn({})
6
15
 
@@ -19,6 +28,8 @@ global.pgnToPCDIN = pgnToPCDIN
19
28
  global.actisenseToPCDIN = actisenseToPCDIN
20
29
  global.pgnToMXPGN = pgnToMXPGN
21
30
  global.actisenseToMXPGN = actisenseToMXPGN
31
+ global.pgnToiKonvertSerialFormat = pgnToiKonvertSerialFormat
32
+ global.actisenseToiKonvert = actisenseToiKonvert
22
33
 
23
34
  global.parsePCDIN = (pcdin) => {
24
35
  return parser.parseN2KOver0183(pcdin)
@@ -30,6 +41,19 @@ global.parseMXPGN = (mxpgn) => {
30
41
 
31
42
  global.parseHelmSmart = global.parsePCDIN
32
43
 
44
+ global.isN2KOver0183 = (msg) => {
45
+ return parser.isN2KOver0183(msg)
46
+ }
47
+ global.parseN2KOver0183 = (msg) => {
48
+ return parser.parseN2KOver0183(msg)
49
+ }
50
+ global.parsePDGY = (pdgy) => {
51
+ if ( !pdgy.startsWith('!PDGY') ) {
52
+ return
53
+ }
54
+ return parser.parsePDGY(pdgy)
55
+ }
56
+
33
57
  //global.toPgn: require('./lib/toPgn').toPgn,
34
58
 
35
59
  //console.log(global.parseYDGW02('06:06:39.801 R 09F10DCC 00 FF FF 7F FF 7F FF FF'))
package/lib/canbus.js CHANGED
@@ -120,7 +120,7 @@ function CanbusStream (options) {
120
120
  })
121
121
  } else {
122
122
  try {
123
- this.channel = socketcan.createRawChannel(canDevice);
123
+ this.channel = socketcan.createRawChannelWithOptions(canDevice, { non_block_send: true} );
124
124
  this.channel.addListener('onMessage', (msg) => {
125
125
  var pgn = parseCanId(msg.id)
126
126
 
package/lib/candevice.js CHANGED
@@ -177,7 +177,7 @@ function handleGroupFunction(device, n2kMsg) {
177
177
  const acknowledgement = {
178
178
  pgn: 126208,
179
179
  dst: n2kMsg.src,
180
- "Function Code": 1,
180
+ "Function Code": 2,
181
181
  "PGN": n2kMsg.fields.PGN,
182
182
  "PGN error code": 4,
183
183
  "Transmission interval/Priority error code": 0,
@@ -195,7 +195,7 @@ function handleGroupFunction(device, n2kMsg) {
195
195
  const acknowledgement = {
196
196
  pgn: 126208,
197
197
  dst: n2kMsg.src,
198
- "Function Code": 1,
198
+ "Function Code": 2,
199
199
  "PGN": n2kMsg.fields.PGN,
200
200
  "PGN error code": 4,
201
201
  "Transmission interval/Priority error code": 0,
package/lib/codes.test.js CHANGED
@@ -4,6 +4,7 @@ describe('getManufacturerCode', () => {
4
4
  test('Return mfg number from name string', () => {
5
5
  expect(getManufacturerCode('Furuno')).toBe(1855)
6
6
  expect(getManufacturerCode('Yacht Devices')).toBe(717)
7
+ expect(getManufacturerCode('TJC Micro')).toBe(963)
7
8
  })
8
9
  })
9
10
 
@@ -11,5 +12,6 @@ describe('getManufacturerName', () => {
11
12
  test('Return name string from mfg number', () => {
12
13
  expect(getManufacturerName(1855)).toBe('Furuno')
13
14
  expect(getManufacturerName(717)).toBe('Yacht Devices')
15
+ expect(getManufacturerName(963)).toBe('TJC Micro')
14
16
  })
15
17
  })
@@ -136,6 +136,7 @@
136
136
  "Teleflex Marine (SeaStar Solutions)": 1850,
137
137
  "Thrane and Thrane": 351,
138
138
  "Tides Marine": 797,
139
+ "TJC Micro": 963,
139
140
  "Tohatsu Co, JP": 431,
140
141
  "Transas USA": 518,
141
142
  "Trimble": 1856,
package/lib/fromPgn.js CHANGED
@@ -19,7 +19,7 @@ const trace = require('debug')('canboatjs:fromPgn:trace')
19
19
  const EventEmitter = require('events')
20
20
  const pkg = require('../package.json')
21
21
  const _ = require('lodash')
22
- const { pgns } = require('./pgns')
22
+ const { pgns, getCustomPgn, addCustomPgn } = require('./pgns')
23
23
  const BitStream = require('bit-buffer').BitStream
24
24
  const BitView = require('bit-buffer').BitView
25
25
  const Int64LE = require('int64-buffer').Int64LE
@@ -29,7 +29,7 @@ const { parse: parseDate } = require('date-fns')
29
29
  const { getPGNFromCanId } = require('./utilities')
30
30
  const { getIndustryName, getManufacturerName } = require('./codes')
31
31
  const { parseCanId } = require('./canId')
32
- const { parseN2kString, parseYDRAW, isN2KOver0183, parseN2KOver0183 } = require('./stringMsg')
32
+ const { parseN2kString, parseYDRAW, isN2KOver0183, parseN2KOver0183, parsePDGY } = require('./stringMsg')
33
33
 
34
34
  var fieldTypeReaders = {}
35
35
  var fieldTypePostProcessors = {}
@@ -66,6 +66,19 @@ class Parser extends EventEmitter {
66
66
  this.format = _.isUndefined(this.options.format) ? -1 : this.options.format
67
67
  this.devices = {}
68
68
  this.mixedFormat = this.options.mixedFormat || false
69
+
70
+ if ( this.options.onPropertyValues ) {
71
+ this.options.onPropertyValues('canboat-custom-pgns', values => {
72
+ values.filter(v => v != null).forEach(pv => {
73
+ pv.value.PGNs.forEach(pgn => {
74
+ if ( !addCustomPgn(pgn, pv.value.callback) ) {
75
+ this.emit('warning', pgn, `pgn ${pgn.PGN} can't be overwritten`)
76
+ }
77
+ debug('registered custom pgn %d by %s', pgn.PGN, pv.setter)
78
+ })
79
+ })
80
+ })
81
+ }
69
82
  }
70
83
 
71
84
  _parse(pgn, bs, len, coalesced, cb, sourceString) {
@@ -74,12 +87,18 @@ class Parser extends EventEmitter {
74
87
  return undefined
75
88
  }
76
89
 
90
+ const customPgns = getCustomPgn(pgn.pgn)
77
91
  let pgnList = pgns[pgn.pgn]
78
- if (!pgnList) {
92
+
93
+ if (!pgnList && !customPgns ) {
79
94
  this.emit('warning', pgn, `no conversion found for pgn ${JSON.stringify(pgn)}`)
80
95
  return undefined
81
96
  }
82
97
 
98
+ if ( customPgns ) {
99
+ pgnList = [ ...customPgns.definitions, ...(pgnList||[]) ]
100
+ }
101
+
83
102
  let pgnData = pgnList[0]
84
103
  let couldBeMulti = false;
85
104
 
@@ -92,14 +111,13 @@ class Parser extends EventEmitter {
92
111
  }
93
112
 
94
113
  trace(`${pgn.pgn} ${len} ${pgnData.Length} ${pgnData.RepeatingFields} ${couldBeMulti}`)
95
- if ( len > 0x8 || (this.format == FORMAT_FAST && !this.mixedFormat) ) {
96
- //if ( (len > 0x8 || pgnData.Type === 'Fast') && coalesced ) {
114
+ if ( coalesced || len > 0x8 || (this.format == FORMAT_FAST && !this.mixedFormat) ) {
97
115
  this.format = FORMAT_FAST
98
116
  if ( sourceString ) {
99
117
  pgn.input = [ sourceString ]
100
118
  }
101
- } else if ( pgnData.Length > 0x8 || (len == 0x8 && (pgnData.RepeatingFields || couldBeMulti))) {
102
- //} else if ( pgnData.Type === 'Fast' ) {
119
+ //} else if ( pgnData.Length > 0x8 || (len == 0x8 && (pgnData.RepeatingFields || couldBeMulti))) {
120
+ } else if ( pgnData.Type === 'Fast' ) {
103
121
  //partial packet
104
122
  this.format = FORMAT_PLAIN
105
123
 
@@ -133,13 +151,10 @@ class Parser extends EventEmitter {
133
151
  packet.buffer.copy(newBuf)
134
152
  }
135
153
  packet.buffer = newBuf
136
- }
154
+ }
137
155
  bs.view.buffer.copy(packet.buffer, 0, FASTPACKET_BUCKET_0_OFFSET, 8)
138
156
  trace(`${pgn.pgn} targetStart: 0 sourceStart: ${FASTPACKET_BUCKET_0_OFFSET}`)
139
157
  } else {
140
- if ( sourceString ) {
141
- packet.src.push(sourceString)
142
- }
143
158
  if (packet.lastPacket + 1 != packetIndex) {
144
159
  debug(`PGN ${pgn.pgn} malformed packet for ${pgn.src} received; expected ${packet.lastPacket+1} but got ${packetIndex}`)
145
160
  cb && cb(`Could not parse ${JSON.stringify(pgn)}`)
@@ -189,7 +204,11 @@ class Parser extends EventEmitter {
189
204
  if ( pgnList.length == 0 ) {
190
205
  //this.emit('warning', pgn, `no conversion found for pgn`)
191
206
  trace('warning no conversion found for pgn %j', pgn)
192
- return
207
+ const ts = _.get(pgn, 'timestamp', new Date())
208
+ pgn.timestamp = _.isDate(ts) ? ts.toISOString() : ts
209
+ this.emit('pgn', pgn)
210
+ cb && cb(undefined, pgn)
211
+ return pgn
193
212
  }
194
213
  pgnData = pgnList[0]
195
214
  fields = pgnData.Fields
@@ -230,6 +249,9 @@ class Parser extends EventEmitter {
230
249
  pgn.timestamp = _.isDate(ts) ? ts.toISOString() : ts
231
250
  this.emit('pgn', pgn)
232
251
  cb && cb(undefined, pgn)
252
+ if ( pgnData.callback ) {
253
+ pgnData.callback(pgn)
254
+ }
233
255
  return pgn
234
256
  } catch ( error ) {
235
257
  this.emit('error', pgn, error)
@@ -326,6 +348,33 @@ class Parser extends EventEmitter {
326
348
  return undefined
327
349
  }
328
350
 
351
+ parsePDGY(pgn_data, cb) {
352
+ if ( pgn_data[0] != '!') {
353
+ return
354
+ }
355
+ try {
356
+ const { coalesced, data, error, len, ...pgn } = parsePDGY(pgn_data)
357
+ if (error) {
358
+ cb && cb(error)
359
+ this.emit('error', pgn, error)
360
+ return
361
+ }
362
+
363
+ const bs = new BitStream(data)
364
+ delete pgn.format
365
+ delete pgn.type
366
+ delete pgn.prefix
367
+ const res = this._parse(pgn, bs, len || data.length, coalesced, cb, pgn_data)
368
+ if (res) {
369
+ debug('parsed pgn %j', pgn)
370
+ }
371
+ return res
372
+ } catch ( error ) {
373
+ cb && cb(error)
374
+ this.emit('error', pgn_data, error)
375
+ }
376
+ }
377
+
329
378
  parseString (pgn_data, cb) {
330
379
  try {
331
380
  const { coalesced, data, error, len, ...pgn } = parseN2kString(pgn_data)
@@ -467,17 +516,26 @@ function readField(options, runPostProcessor, pgn, field, bs) {
467
516
  if ( field.Resolution && typeof value === 'number' ) {
468
517
  var resolution = field.Resolution
469
518
 
519
+ if ( _.isString(resolution) ) {
520
+ resolution = Number.parseFloat(resolution)
521
+ }
522
+
470
523
  value = (value * resolution)
524
+
525
+ let precision = 0;
526
+ for (let r = resolution; (r > 0.0) && (r < 1.0); r = r * 10.0)
527
+ {
528
+ precision++;
529
+ }
530
+
531
+ value = Number.parseFloat(value.toFixed(precision))
471
532
 
533
+ /*
472
534
  if ( resolution === 3.125e-8 ) {
473
535
  //yes. hack.
474
536
  resolution = "0.0000000001"
475
537
  }
476
-
477
- if ( _.isString(resolution) &&
478
- resolution.indexOf('.') != -1 ) {
479
- value = Number.parseFloat(value.toFixed(resolution.length-2))
480
- }
538
+ */
481
539
  }
482
540
 
483
541
  if (field.EnumValues &&
@@ -655,7 +713,9 @@ fieldTypeReaders['ASCII string starting with length byte'] = (pgn, field, bs) =>
655
713
  fieldTypeReaders["String with start/stop byte"] = (pgn, field, bs) => {
656
714
  var len
657
715
  var first = bs.readUint8()
658
- if ( first == 0x02 ) {
716
+ if ( first == 0xff ) { // no name, stop reading
717
+ return ''
718
+ } else if ( first == 0x02 ) {
659
719
  var buf = Buffer.alloc(255)
660
720
  var c
661
721
  var idx = 0
package/lib/ikonvert.js CHANGED
@@ -82,6 +82,7 @@ function iKonvertStream (options) {
82
82
  })
83
83
 
84
84
  this.isSetup = false
85
+ //this.cansend = true
85
86
  this.state = 0
86
87
  this.setupCommands = this.getSetupCommands()
87
88
  this.expecting = false
@@ -116,7 +117,7 @@ iKonvertStream.prototype.sendPGN = function (pgn) {
116
117
  iKonvertStream.prototype.sendActisensePGN = function (msg) {
117
118
  if ( this.cansend ) {
118
119
  if ( !this.parser ) {
119
- this.parser = new Parser()
120
+ this.parser = new Parser(this.options)
120
121
 
121
122
  let that = this
122
123
  this.parser.on('error', (pgn, error) => {
package/lib/pgns.js CHANGED
@@ -2,6 +2,7 @@ const { flow, first, isArray, isEmpty, propertyOf } = require('lodash/fp')
2
2
  const pgns = require('@canboat/pgns')
3
3
  const pgnsIK = require('@canboat/pgns/pgns-ik')
4
4
  const pgnsNGT = require('@canboat/pgns/pgns-ngt')
5
+ const _ = require('lodash')
5
6
 
6
7
  function organizePGNs() {
7
8
  const res = {}
@@ -36,9 +37,30 @@ function organizePGNs() {
36
37
 
37
38
  const organizedPGNs = organizePGNs()
38
39
  const getPgn = pgn => organizedPGNs[pgn]
40
+ const customPgns = {}
39
41
 
40
42
  module.exports = {
41
43
  getPgn,
42
44
  getPgn0: flow(getPgn, first),
43
45
  pgns: organizedPGNs,
46
+ addCustomPgn: (pgn) => {
47
+ if ( !customPgns[pgn.PGN] ) {
48
+ customPgns[pgn.PGN] = {
49
+ definitions: [],
50
+ callbacks: []
51
+ }
52
+ }
53
+
54
+ customPgns[pgn.PGN].definitions.push(pgn)
55
+ /*
56
+ if ( pgn.calllback ) {
57
+ customPgns[pgn.PGN].callbacks.push()
58
+ }
59
+ */
60
+
61
+ return customPgns[pgn.PGN]
62
+ },
63
+ getCustomPgn: (pgnNum) => {
64
+ return customPgns[pgnNum]
65
+ }
44
66
  }
package/lib/simpleCan.js CHANGED
@@ -27,9 +27,9 @@ SimpleCan.prototype.start = function () {
27
27
 
28
28
  pgn.timestamp = new Date().toISOString()
29
29
  if ( this.plainText ) {
30
- messageCb(binToActisense(pgn, msg.data, msg.data.length))
30
+ this.messageCb(binToActisense(pgn, msg.data, msg.data.length))
31
31
  } else {
32
- messageCb({ pgn, length: msg.data.length, data: msg.data })
32
+ this.messageCb({ pgn, length: msg.data.length, data: msg.data })
33
33
  }
34
34
  })
35
35
  }
package/lib/stringMsg.js CHANGED
@@ -49,7 +49,7 @@ exports.parseActisense = (input) => {
49
49
  buildCanId(prio, pgn, dst, src),
50
50
  'Actisense',
51
51
  Buffer.from(data.join(''), 'hex'),
52
- { len: Number(len), timestamp, coalesced: true },
52
+ { len: Number(len), timestamp },
53
53
  )
54
54
  }
55
55
  exports.encodeActisense = ({
@@ -153,12 +153,21 @@ exports.encodeMXPGN = ({ prefix = '$MXPGN', pgn, prio, src, data }) => {
153
153
  exports.isPDGY = startsWith('!PDGY,')
154
154
  exports.parsePDGY = (input) => {
155
155
  const parts = input.split(',')
156
- if ( parts.length != 7 ) return buildErr('iKonvert', 'Invalid parts.', input)
157
- const [ prefix, pgn, prio, src, dst, timer, data ] = parts
158
- return buildMsg(
159
- buildCanId(prio, pgn, dst, src), 'PDGY', Buffer.from(data, 'base64'),
160
- { timer: Number(timer), prefix },
161
- )
156
+ if ( parts.length === 7 ) {
157
+ const [ prefix, pgn, prio, src, dst, timer, data ] = parts
158
+ return buildMsg(
159
+ buildCanId(prio, pgn, dst, src), 'PDGY', Buffer.from(data, 'base64'),
160
+ { timer: Number(timer), prefix, coalesced: true },
161
+ )
162
+ } else if ( parts.length === 4 ) {
163
+ const [ prefix, pgn, dst, data ] = parts
164
+ return buildMsg(
165
+ buildCanId(0, pgn, dst, 0), 'PDGY', Buffer.from(data, 'base64'),
166
+ { coalesced: true }
167
+ )
168
+ } else {
169
+ return buildErr('iKonvert', 'Invalid parts.', input)
170
+ }
162
171
  }
163
172
  exports.encodePDGY = ({ prefix = '!PDGY', pgn, data, dst = 255}) => (
164
173
  [ prefix, pgn, dst, data.toString('base64')].join(',')
@@ -65,7 +65,6 @@ describe('parseActisense', () => {
65
65
  test('basic msg', () => {
66
66
  const msg = '2016-04-09T16:41:09.078Z,3,127257,17,255,8,00,ff,7f,52,00,21,fe,ff'
67
67
  expect(parseActisense(msg)).toEqual({
68
- coalesced: true,
69
68
  data: Buffer.from('00ff7f520021feff', 'hex'),
70
69
  dst: 255,
71
70
  len: 8,
@@ -99,6 +98,7 @@ describe('parsePDGY', () => {
99
98
  prio: 3,
100
99
  src: 2,
101
100
  timer: 0.563,
101
+ coalesced: true
102
102
  })
103
103
  })
104
104
  test('long msg', () => {
@@ -111,6 +111,7 @@ describe('parsePDGY', () => {
111
111
  prio: 3,
112
112
  src: 2,
113
113
  timer: 483.236,
114
+ coalesced: true
114
115
  })
115
116
  })
116
117
  })
package/lib/toPgn.js CHANGED
@@ -15,13 +15,13 @@
15
15
  */
16
16
 
17
17
  const { getField } = require('./fromPgn')
18
- const { pgns } = require('./pgns')
18
+ const { pgns, getCustomPgn } = require('./pgns')
19
19
  const _ = require('lodash')
20
20
  const BitStream = require('bit-buffer').BitStream
21
21
  const Int64LE = require('int64-buffer').Int64LE
22
22
  const Uint64LE = require('int64-buffer').Uint64LE
23
23
  const { getPlainPGNs } = require('./utilities')
24
- const { encodeActisense, encodeYDRAW, parseActisense, encodePCDIN, encodeMXPGN } = require('./stringMsg')
24
+ const { encodeActisense, encodeYDRAW, parseActisense, encodePCDIN, encodeMXPGN, encodePDGY } = require('./stringMsg')
25
25
  const { encodeCanId } = require('./canId')
26
26
  const { getIndustryCode, getManufacturerCode } = require('./codes')
27
27
  const debug = require('debug')('canboatjs:toPgn')
@@ -35,12 +35,17 @@ var fieldTypeMappers = {}
35
35
  var lengthsOff = { 129029: 45, 127257:8, 127258:8, 127251:8 }
36
36
 
37
37
  function toPgn(data) {
38
- var pgnList = pgns[data.pgn]
39
- if (!pgnList) {
38
+ const customPgns = getCustomPgn(data.pgn)
39
+ let pgnList = pgns[data.pgn]
40
+ if (!pgnList && !customPgns ) {
40
41
  debug("no pgn found: " + data.pgn)
41
42
  return
42
43
  }
43
-
44
+
45
+ if ( customPgns ) {
46
+ pgnList = [ ...customPgns.definitions, ...(pgnList || []) ]
47
+ }
48
+
44
49
  const pgn_number = data.pgn
45
50
  var pgnData = pgnList[0]
46
51
 
@@ -309,6 +314,7 @@ function pgnToMXPGN(info) {
309
314
  const actisenseToYdgwRawFormat = _.flow(parseActisense, encodeYDRAW)
310
315
  const actisenseToPCDIN = _.flow(parseActisense, encodePCDIN)
311
316
  const actisenseToMXPGN = _.flow(parseActisense, encodeMXPGN)
317
+ const actisenseToiKonvert = _.flow(parseActisense, encodePDGY)
312
318
 
313
319
  fieldTypeWriters['ASCII text'] = (pgn, field, value, bs) => {
314
320
 
@@ -429,3 +435,4 @@ module.exports.pgnToPCDIN = pgnToPCDIN
429
435
  module.exports.actisenseToPCDIN = actisenseToPCDIN
430
436
  module.exports.pgnToMXPGN = pgnToMXPGN
431
437
  module.exports.actisenseToMXPGN = actisenseToMXPGN
438
+ module.exports.actisenseToiKonvert = actisenseToiKonvert
package/lib/ydgw02.js CHANGED
@@ -40,7 +40,7 @@ function Ydgw02Stream (options, type) {
40
40
  this.fromPgn = new FromPgn(options)
41
41
 
42
42
  this.fromPgn.on('warning', (pgn, warning) => {
43
- debug(`[warning] ${pgn.pgn} ${warning}`)
43
+ //debug(`[warning] ${pgn.pgn} ${warning}`)
44
44
  })
45
45
 
46
46
  this.fromPgn.on('error', (pgn, error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canboat/canboatjs",
3
- "version": "1.20.2",
3
+ "version": "1.22.0",
4
4
  "description": "Native javascript version of canboat",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -63,6 +63,8 @@
63
63
  },
64
64
  "devDependencies": {
65
65
  "@signalk/github-create-release": "^1.0.1",
66
+ "@signalk/server-api": "^1.39.0",
67
+ "baconjs": "^1.0.1",
66
68
  "chai": "^4.1.2",
67
69
  "chai-json-equal": "0.0.1",
68
70
  "chai-string": "^1.5.0",
@@ -74,7 +76,7 @@
74
76
  "webpack-cli": "^3.3.10"
75
77
  },
76
78
  "optionalDependencies": {
77
- "serialport": "^7.1.5",
79
+ "serialport": "^9.0.7",
78
80
  "socketcan": "^2.2.2"
79
81
  },
80
82
  "repository": {