bt-sensors-plugin-sk 1.0.3 → 1.1.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.
package/index.js CHANGED
@@ -5,20 +5,58 @@ const {createBluetooth} = require('node-ble')
5
5
  const {bluetooth, destroy} = createBluetooth()
6
6
 
7
7
  const BTSensor = require('./BTSensor.js')
8
+ const BLACKLISTED = require('./sensor_classes/BlackListedDevice.js')
8
9
 
9
- const Device = require('../node-ble/src/Device.js')
10
+ class MissingSensor {
11
+
10
12
 
11
- Device.prototype.getUUIDs=async function() {
12
- return this.helper.prop('UUIDs')
13
- }
13
+ constructor(config){
14
+ this.Metadatum = BTSensor.Metadatum
15
+ this.addMetadatum=BTSensor.prototype.addMetadatum
16
+ this.getPathMetadata = BTSensor.prototype.getPathMetadata
17
+ this.getParamMetadata = BTSensor.prototype.getParamMetadata
14
18
 
19
+ this.metadata= new Map()
20
+ var keys = Object.keys(config?.paths??{})
21
+ this.addMetadatum.bind(this)
22
+ keys.forEach((key)=>{
23
+ this.addMetadatum(key, config.paths[key]?.type??'string', config.paths[key].description )
24
+ } )
25
+ keys = Object.keys(config?.params??{})
26
+ keys.forEach((key)=>{
27
+ this.addMetadatum(key, config.params[key]?.type??'string', config.params[key].description ).isParam=true
28
+ this[key]=config.params[key]
29
+ })
30
+ this.mac_address = config.mac_address
31
+
32
+ }
33
+ canUseGATT(){
34
+ return false
35
+ }
36
+ getMetadata(){
37
+ return this.metadata
38
+ }
39
+ getMacAddress(){
40
+ return this.mac_address
41
+ }
42
+ getDescription(){
43
+ return ""
44
+ }
45
+ getName(){
46
+ return this?.name??"Unknown device"
47
+ }
48
+ getDisplayName(){
49
+ return `OUT OF RANGE DEVICE (${this.getName()} ${this.getMacAddress()})`
50
+ }
51
+ disconnect(){}
52
+ connect(){}
53
+ }
15
54
  module.exports = function (app) {
16
- const discoveryTimeout = 30
17
55
  const adapterID = 'hci0'
18
-
19
- var peripherals=[]
56
+ var deviceConfigs
20
57
  var starts=0
21
58
  var classMap
59
+
22
60
  var utilities_sk
23
61
 
24
62
  var plugin = {};
@@ -29,233 +67,403 @@ module.exports = function (app) {
29
67
  //Try and load utilities-sk NOTE: should be installed from App Store--
30
68
  //But there's a fail safe because I'm a reasonable man.
31
69
 
32
- try{
33
- utilities_sk = require('../utilities-sk/utilities.js')
70
+ utilities_sk = {
71
+ loadClasses: function(dir, ext='.js')
72
+ {
73
+ const classMap = new Map()
74
+ const classFiles = fs.readdirSync(dir)
75
+ .filter(file => file.endsWith(ext));
76
+
77
+ classFiles.forEach(file => {
78
+ const filePath = path.join(dir, file);
79
+ const cls = require(filePath);
80
+ classMap.set(cls.name, cls);
81
+ })
82
+ return classMap
83
+ }
34
84
  }
35
- catch (error){
36
- try {
37
- utilities_sk = require('utilities-sk/utilities.js')
38
- } catch(error){
39
- console.log(`${plugin.id} Plugin utilities-sk not found. Please install.`)
40
- utilities_sk = {
41
- loadClasses: function(dir, ext='.js')
42
- {
43
- const classMap = new Map()
44
- const classFiles = fs.readdirSync(dir)
45
- .filter(file => file.endsWith(ext));
85
+
86
+ function sleep(x) {
87
+ return new Promise((resolve) => {
88
+ setTimeout(() => {
89
+ resolve(x);
90
+ }, x);
91
+ });
92
+ }
93
+ async function instantiateSensor(device,config){
94
+ try{
95
+ for (var [clsName, cls] of classMap) {
96
+ const c = await cls.identify(device)
97
+ if (c) {
46
98
 
47
- classFiles.forEach(file => {
48
- const filePath = path.join(dir, file);
49
- const cls = require(filePath);
50
- classMap.set(cls.name, cls);
51
- })
52
- return classMap
53
- }
54
- }
99
+ if (c.name.startsWith("_")) continue
100
+ c.debug=app.debug
101
+ c.debug.bind(c)
102
+ const sensor = new c(device,config?.params, config?.gattParams)
103
+ sensor.debug=app.debug
104
+ await sensor.init()
105
+ return sensor
106
+ }
107
+ }} catch(error){
108
+ const msg = `Unable to instantiate ${await BTSensor.getDeviceProp(device,"Address")}: ${error.message} `
109
+ app.debug(msg)
110
+ app.debug(error)
111
+ app.setPluginError(msg)
55
112
  }
113
+ //if we're here ain't got no class for the device
114
+ const sensor = new (classMap.get('UNKNOWN'))(device)
115
+ await sensor.init()
116
+ return sensor
56
117
  }
57
- function createPaths(sensorClass, peripheral){
58
118
 
59
- for (const tag of sensorClass.metadataTags()) {
60
- const path = peripheral[tag]
61
- if (!(path===undefined))
62
- app.handleMessage(plugin.id,
63
- {
64
- updates:
65
- [{ meta: [{path: path, value: { units: sensorClass.unitFor(tag) }}]}]
66
- }
67
- )
68
- }
69
- }
119
+ function createPaths(config){
120
+ config.sensor.getMetadata().forEach((metadatum, tag)=>{
121
+ if ((!(metadatum?.isParam)??false)){ //param metadata is passed to the sensor at
122
+ //create time through the constructor, and isn't a
123
+ //a value you want to see in a path
124
+
125
+ const path = config.paths[tag]
126
+ if (!(path===undefined))
127
+ app.handleMessage(plugin.id,
128
+ {
129
+ updates:
130
+ [{ meta: [{path: path, value: { units: metadatum?.unit }}]}]
131
+ })
132
+ }
133
+ })
134
+ }
70
135
 
136
+ function initPaths(deviceConfig){
137
+ deviceConfig.sensor.getMetadata().forEach((metadatum, tag)=>{
138
+ const path = deviceConfig.paths[tag];
139
+ if (!(path === undefined))
140
+ deviceConfig.sensor.on(tag, (val)=>{
141
+ if (metadatum.notify){
142
+ app.notify( tag, val, plugin.id )
143
+ } else {
144
+ updatePath(path,val)
145
+ }
146
+ })
147
+ })
148
+ }
71
149
  function updatePath(path, val){
72
150
  app.handleMessage(plugin.id, {updates: [ { values: [ {path: path, value: val }] } ] })
73
151
  }
74
152
 
75
153
  function loadClassMap() {
76
154
  classMap = utilities_sk.loadClasses(path.join(__dirname, 'sensor_classes'))
77
-
78
155
  }
79
156
 
80
157
  app.debug('Loading plugin')
81
158
 
82
- plugin.uiSchema = {
83
- peripherals: {
84
- 'ui:disabled': true
85
- }
86
- }
87
-
88
159
  plugin.schema = {
89
160
  type: "object",
90
- description: "",
161
+ description: "NOTE: \n 1) Plugin must be enabled to configure your sensors. \n"+
162
+ "2) You will have to wait until the scanner has found and connected to your device before seeing your device's config fields and saving the configuration. \n"+
163
+ "3) To refresh the list of available devices and their configurations, just open and close the config screen by clicking on the arrow symbol in the config's top bar. \n"+
164
+ "4) If you submit and get errors it may be because the configured devices have not yet all been discovered.",
165
+ required:["discoveryTimeout", "discoveryInterval"],
91
166
  properties: {
92
- adapter: {title: "Bluetooth Adapter", type: "string", enum:[], default:'hci0' },
93
- discoveryTimeout: {title: "Initial scan timeout (in seconds)", type: "number",default: 45 },
167
+ discoveryTimeout: {title: "Default device discovery timeout (in seconds)",
168
+ type: "integer", default: 30,
169
+ minimum: 10,
170
+ maximum: 3600
171
+ },
172
+ discoveryInterval: {title: "Scan for new devices interval (in seconds-- 0 for no new device scanning)",
173
+ type: "integer",
174
+ default: 60,
175
+ minimum: 0,
176
+ multipleOf: 10
177
+ },
94
178
  peripherals:
95
179
  { type: "array", title: "Sensors", items:{
96
- title: "", type: "object",
180
+ title: "", type: "object",
181
+ required:["mac_address", "discoveryTimeout"],
97
182
  properties:{
98
- active: {title: "Active", type: "boolean", default: true },
99
- mac_address: {title: "Bluetooth Device", enum: [], enumNames:[], type: "string" },
100
- BT_class: {title: "Bluetooth sensor class", type: "string", enum: []},
101
- discoveryTimeout: {title: "Discovery timeout (in seconds)", type: "number", default:30},
102
- }, dependencies:{BT_class:{oneOf:[]}}
183
+ active: {title: "Active", type: "boolean", default: true },
184
+ mac_address: {title: "Bluetooth Sensor", type: "string" },
185
+ discoveryTimeout: {title: "Device discovery timeout (in seconds)",
186
+ type: "integer", default:30,
187
+ minimum: 10,
188
+ maximum: 600
189
+ }
190
+ }
103
191
 
104
192
  }
105
193
  }
106
194
  }
107
195
  }
108
-
109
- function setDeviceNameInList(device,name){
110
- const deviceNamesList = plugin.schema.properties.peripherals.items.properties.
111
- mac_address.enumNames
112
- const deviceList = plugin.schema.properties.peripherals.items.properties.
196
+ const UI_SCHEMA =
197
+ { "peripherals": {
198
+ 'ui:disabled': !plugin.started
199
+ }
200
+ }
201
+ plugin.uiSchema=UI_SCHEMA
202
+
203
+ function updateSensorDisplayName(sensor){
204
+ const mac_address = sensor.getMacAddress()
205
+ const displayName = sensor.getDisplayName()
206
+
207
+ const mac_addresses_list = plugin.schema.properties.peripherals.items.properties.
113
208
  mac_address.enum
209
+ const mac_addresses_names = plugin.schema.properties.peripherals.items.properties.
210
+ mac_address.enumNames
114
211
 
115
- deviceNamesList[deviceList.indexOf(device)]=`${name} (${device})`
212
+ var index = mac_addresses_list.indexOf(mac_address)
213
+ if (index!=-1)
214
+ mac_addresses_names[index]= displayName
116
215
  }
117
216
 
118
- function addDeviceToList( device, name ){
119
- const devices = plugin.schema.properties.peripherals.items.properties.
120
- mac_address.enum
217
+ function removeSensorFromList(sensor){
218
+ const mac_addresses_list = plugin.schema.properties.peripherals.items.properties.mac_address.enum
219
+ const mac_addresses_names = plugin.schema.properties.peripherals.items.properties.mac_address.enumNames
220
+ const oneOf = plugin.schema.properties.peripherals.items.dependencies.mac_address.oneOf
221
+ const mac_address = sensor.getMacAddress()
222
+
223
+ const i = mac_addresses_list.indexOf(mac_address)
224
+ if (i<0) return // n'existe pas
225
+
226
+ mac_addresses_list.splice(i,1)
227
+ mac_addresses_names.splice(i,1)
228
+ oneOf.splice(oneOf.findIndex((p)=>p.properties.mac_address.enum[0]==mac_address),1)
121
229
 
122
- if (!devices.includes(device)) {
123
- devices.push(device)
124
- if (!(name===undefined))
125
- setDeviceNameInList(device,name)
126
- }
127
230
  }
128
- async function getDeviceName(device){
129
- var dn = "UNKNOWN"
130
- try{
131
- dn = await device.getName()
231
+ function addSensorToList(sensor){
232
+ if (!plugin.schema.properties.peripherals.items.dependencies)
233
+ plugin.schema.properties.peripherals.items.dependencies={mac_address:{oneOf:[]}}
234
+
235
+ if(plugin.schema.properties.peripherals.items.properties.mac_address.enum==undefined) {
236
+ plugin.schema.properties.peripherals.items.properties.mac_address.enum=[]
237
+ plugin.schema.properties.peripherals.items.properties.mac_address.enumNames=[]
132
238
  }
133
- catch (error) {}
134
- return dn
135
- }
239
+ const mac_addresses_names = plugin.schema.properties.peripherals.items.properties.mac_address.enumNames
240
+ const mac_addresses_list = plugin.schema.properties.peripherals.items.properties.mac_address.enum
241
+ const mac_address = sensor.getMacAddress()
242
+ const displayName = sensor.getDisplayName()
136
243
 
137
- function updateAdapters(){
138
- bluetooth.adapters().then((adapters)=>
139
- {plugin.schema.properties.adapter.enum = adapters}
140
- )
141
- }
142
- function updateClassProperties(){
143
- plugin.schema.properties.peripherals.items.properties.BT_class.enum=[...classMap.keys()]
144
- plugin.schema.properties.peripherals.items.dependencies.BT_class.oneOf=[]
145
- classMap.forEach(( cls, className )=>{
146
- var oneOf = {properties:{BT_class:{enum:[className]}}}
147
- cls.metadata.forEach((metadatum,tag)=>{
148
- oneOf.properties[tag]={type:'string', title: "Path for "+metadatum.description}
244
+ var index = mac_addresses_list.indexOf(mac_address)
245
+ if (index==-1)
246
+ index = mac_addresses_list.push(mac_address)-1
247
+ mac_addresses_names[index]= displayName
248
+ var oneOf = {properties:{mac_address:{enum:[mac_address]}}}
249
+
250
+ oneOf.properties.params={
251
+ title:`Device parameters`,
252
+ description: sensor.getDescription(),
253
+ type:"object",
254
+ properties:{}
255
+ }
256
+ sensor.getParamMetadata().forEach((metadatum,tag)=>{
257
+ oneOf.properties.params.properties[tag]=metadatum.asJSONSchema()
258
+ })
259
+
260
+ if (sensor.canUseGATT()){
261
+
262
+ oneOf.properties.gattParams={
263
+ title:`GATT Specific device parameters`,
264
+ description: sensor.getGATTDescription(),
265
+ type:"object",
266
+ properties:{}
267
+ }
268
+ sensor.getGATTParamMetadata().forEach((metadatum,tag)=>{
269
+ oneOf.properties.gattParams.properties[tag]=metadatum.asJSONSchema()
149
270
  })
271
+ }
150
272
 
151
- plugin.schema.properties.peripherals.items.dependencies.BT_class.oneOf.push(oneOf)
273
+ oneOf.properties.paths={
274
+ title:"Signalk Paths",
275
+ description: `Signalk paths to be updated when ${sensor.getName()}'s values change`,
276
+ type:"object",
277
+ properties:{}
278
+ }
279
+ sensor.getPathMetadata().forEach((metadatum,tag)=>{
280
+ oneOf.properties.paths.properties[tag]=metadatum.asJSONSchema()
152
281
  })
282
+ plugin.schema.properties.peripherals.items.dependencies.mac_address.oneOf.push(oneOf)
283
+ //plugin.schema.properties.peripherals.items.title=sensor.getName()
284
+
153
285
  }
154
- function startScanner(){
155
- bluetooth.getAdapter(app.settings?.btAdapter??adapterID).then(async (adapter) => {
156
- app.debug("Starting scan...");
157
- try {
158
- await adapter.startDiscovery()
159
- }
160
- catch (error) {
286
+ function deviceNameAndAddress(config){
287
+ return `${config?.name??""}${config.name?" at ":""}${config.mac_address}`
288
+ }
289
+
290
+ async function createSensor(adapter, config) {
291
+ return new Promise( ( resolve, reject )=>{
292
+ var s
293
+
294
+ app.debug(`Waiting on ${deviceNameAndAddress(config)}`)
295
+ adapter.waitDevice(config.mac_address,config.discoveryTimeout*1000)
296
+ .then(async (device)=> {
297
+ app.debug(`Found ${config.mac_address}`)
298
+ s = await instantiateSensor(device,config)
299
+ sensorMap.set(config.mac_address,s)
300
+
301
+ if (s instanceof BLACKLISTED)
302
+ reject ( `Device is blacklisted (${s.reasonForBlacklisting()}).`)
303
+ else{
304
+ addSensorToList(s)
305
+ s.on("RSSI",(()=>{
306
+ updateSensorDisplayName(s)
307
+ }))
308
+ resolve(s)
161
309
  }
162
- plugin.schema.description='Scanning for Bluetooth devices...'
163
- setTimeout( () => {
164
- adapter.devices().then((devices)=>{
165
- app.debug(`Found: ${util.inspect(devices)}`)
166
- devices.forEach( (device) => {
167
- adapter.waitDevice(device,discoveryTimeout*1000).then((d)=>{
168
- getDeviceName(d).then((dn)=>{
169
- addDeviceToList(device, dn )
170
- })
171
- })
172
- .catch ((e)=> {
173
- app.debug(e)
174
- })
175
- })
176
- plugin.schema.description=`Scan complete. Found ${devices.length} Bluetooth devices.`
177
- })
178
- plugin.uiSchema.peripherals['ui:disabled']=false
179
- }, app.settings?.btDiscoveryTimeout ?? discoveryTimeout * 1000)
180
310
  })
311
+ .catch((e)=>{
312
+ if (s)
313
+ s.disconnect()
314
+ app.debug(`Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`)
315
+ reject( e?.message??e )
316
+ })})
181
317
  }
318
+
319
+ async function startScanner() {
320
+
321
+ app.debug("Starting scan...");
322
+ try{ await adapter.stopDiscovery() } catch(error){}
323
+ try{ await adapter.startDiscovery() } catch(error){}
324
+ }
325
+
326
+ const sensorMap=new Map()
327
+ var adapter
328
+
329
+ plugin.started=false
330
+
182
331
  loadClassMap()
183
- updateAdapters()
184
- updateClassProperties()
185
- startScanner()
332
+ var discoveryIntervalID
333
+
186
334
  plugin.start = async function (options, restartPlugin) {
187
- if (starts>0){
188
- app.debug('Plugin restarted');
189
- plugin.uiSchema.peripherals['ui:disabled']=true
190
- loadClassMap()
191
- updateClassProperties()
192
- startScanner()
193
- } else {
194
- app.debug('Plugin started');
195
- }
196
- starts++
197
- const adapter = await bluetooth.getAdapter(options?.adapter??app.settings?.btAdapter??adapterID)
198
- peripherals=options.peripherals
199
- if (!(peripherals===undefined)){
200
- var found = 0
201
- for (const peripheral of peripherals) {
202
-
203
- addDeviceToList(peripheral.mac_address)
204
- app.setPluginStatus(`Waiting on ${peripheral.mac_address}`);
205
- adapter.waitDevice(peripheral.mac_address,1000*peripheral.discoveryTimeout).then(async (device)=>
206
- {
207
335
 
208
- setDeviceNameInList(peripheral.mac_address, await getDeviceName(device))
336
+ function getDeviceConfig(mac){
337
+ return deviceConfigs.find((p)=>p.mac_address==mac)
338
+ }
209
339
 
210
- if (peripheral.active) {
340
+ function initConfiguredDevice(deviceConfig){
341
+ app.setPluginStatus(`Initializing ${deviceNameAndAddress(deviceConfig)}`);
342
+ if (!deviceConfig.discoveryTimeout)
343
+ deviceConfig.discoveryTimeout = options.discoveryTimeout
344
+ createSensor(adapter, deviceConfig).then((sensor)=>{
345
+ deviceConfig.sensor=sensor
346
+ if (deviceConfig.active) {
347
+ createPaths(deviceConfig)
348
+ initPaths(deviceConfig)
349
+ const result = Promise.resolve(deviceConfig.sensor.connect())
350
+ result.then(() => {
351
+ app.debug(`Connected to ${deviceConfig.sensor.getDisplayName()}`);
352
+ app.setPluginStatus(`Initial scan complete. Connected to ${++found} sensors.`);
353
+ })
354
+ }
211
355
 
212
- var sensorClass = classMap.get(peripheral.BT_class)
213
- if (!sensorClass)
214
- throw new Error(`File for Class ${peripheral.BT_class} not found.`)
215
- createPaths(sensorClass, peripheral)
356
+ })
357
+ .catch((error)=>
358
+ {
359
+ const msg =`Sensor at ${deviceConfig.mac_address} unavailable. Reason: ${error}`
360
+ app.debug(msg)
361
+ app.debug(error)
362
+ app.setPluginError(msg)
363
+ deviceConfig.sensor=new MissingSensor(deviceConfig)
364
+ addSensorToList(deviceConfig.sensor) //add sensor to list with known options
365
+ })
366
+ }
367
+ function findDevices (discoveryTimeout) {
368
+ app.setPluginStatus("Scanning for new Bluetooth devices...");
369
+
370
+ adapter.devices().then( (macs)=>{
371
+ for (var mac of macs) {
372
+ const deviceConfig = getDeviceConfig(mac)
373
+ const _mac = mac
216
374
 
217
- peripheral.sensor = new sensorClass(device);
218
- await peripheral.sensor.connect();
219
- for (const tag of sensorClass.metadataTags()){
220
- const path = peripheral[tag];
221
- if (!(path === undefined))
222
- peripheral.sensor.on(tag, (val)=>{
223
- updatePath(path,val)
224
- })
375
+ if (deviceConfig && deviceConfig.sensor instanceof MissingSensor){
376
+ removeSensorFromList(deviceConfig.sensor)
377
+ initConfiguredDevice(deviceConfig)
378
+ } else
379
+ {
380
+ if (!sensorMap.has(_mac)) {
381
+ if (deviceConfig) continue;
382
+ createSensor(adapter,
383
+ {mac_address:_mac, discoveryTimeout: discoveryTimeout*1000})
384
+ .then((s)=>
385
+ app.setPluginStatus(`Found ${s.getDisplayName()}.`))
386
+ .catch((e)=>
387
+ app.debug(`Device at ${_mac} unavailable. Reason: ${e}`))
225
388
  }
226
- app.debug('Device: '+peripheral.mac_address+' connected.')
227
- app.setPluginStatus(`Connected to ${found++} sensors.`);
228
389
  }
229
- })
230
- .catch ((e)=> {
231
- if (peripheral.sensor)
232
- peripheral.sensor.disconnect()
233
- app.debug("Unable to connect to device " + peripheral.mac_address +". Reason: "+ e.message )
234
- })
235
- .finally( ()=>{
236
- app.setPluginStatus(`Connected to ${found} sensors.`);
237
390
  }
238
- )
239
- }
391
+ })
240
392
  }
241
393
 
242
- }
243
- plugin.stop = async function () {
244
- var adapter = await bluetooth.getAdapter(app.settings?.btAdapter??adapterID)
394
+ function findDeviceLoop(discoveryTimeout, discoveryInterval, immediate=true ){
395
+ if (immediate)
396
+ findDevices(discoveryTimeout)
397
+ discoveryIntervalID = setInterval( findDevices, discoveryInterval*1000, discoveryTimeout)
398
+ }
399
+
400
+ plugin.started=true
401
+ plugin.uiSchema.peripherals['ui:disabled']=false
402
+ sensorMap.clear()
403
+ deviceConfigs=options?.peripherals??[]
245
404
 
246
- if (adapter && await adapter.isDiscovering()){
247
- try{await adapter.stopDiscovery()} catch (e){
248
- app.debug(e.message)
405
+ if (plugin.stopped) {
406
+ await sleep(5000) //Make sure plugin.stop() completes first
407
+ //plugin.start is called asynchronously for some reason
408
+ //and does not wait for plugin.stop to complete
409
+ plugin.stopped=false
410
+ }
411
+ adapter = await bluetooth.getAdapter(app.settings?.btAdapter??adapterID)
412
+ await startScanner()
413
+ if (starts>0){
414
+ app.debug('Plugin restarting...');
415
+ if (plugin.schema.properties.peripherals.items.dependencies)
416
+ plugin.schema.properties.peripherals.items.dependencies.mac_address.oneOf=[]
417
+ } else {
418
+ app.debug('Plugin build 2024.10.08 started');
419
+
420
+ }
421
+ starts++
422
+ if (!await adapter.isDiscovering())
423
+ try{
424
+ await adapter.startDiscovery()
425
+ } catch (e){
426
+ app.debug(`Error starting scan: ${e.message}`)
427
+ }
428
+ if (!(deviceConfigs===undefined)){
429
+ var found = 0
430
+ for (const deviceConfig of deviceConfigs) {
431
+ initConfiguredDevice(deviceConfig)
249
432
  }
250
433
  }
251
- if (!(peripherals === undefined)){
252
- for (p of peripherals) {
253
- if (p.sensor)
254
- p.sensor.disconnect()
255
- }
434
+ if (options.discoveryInterval && !discoveryIntervalID)
435
+ findDeviceLoop(options.discoveryTimeout, options.discoveryInterval)
436
+ }
437
+ plugin.stop = async function () {
438
+ app.debug("Stopping plugin")
439
+ plugin.stopped=true
440
+ plugin.uiSchema.peripherals['ui:disabled']=true
441
+ if ((sensorMap)){
442
+ sensorMap.forEach(async (sensor, mac)=> {
443
+ try{
444
+ await sensor.disconnect()
445
+ app.debug(`Disconnected from ${mac}`)
446
+ }
447
+ catch (e){
448
+ app.debug(`Error disconnecting from ${mac}: ${e.message}`)
449
+ }
450
+ })
451
+ }
452
+
453
+ if (discoveryIntervalID) {
454
+ clearInterval(discoveryIntervalID)
455
+ discoveryIntervalID=null
256
456
  }
257
- //destroy();
258
- app.debug('BT Sensors plugin stopped');
457
+
458
+ if (adapter && await adapter.isDiscovering())
459
+ try{
460
+ await adapter.stopDiscovery()
461
+ app.debug('Scan stopped')
462
+ } catch (e){
463
+ app.debug(`Error stopping scan: ${e.message}`)
464
+ }
465
+ app.debug('BT Sensors plugin stopped')
466
+
259
467
  }
260
468
 
261
469
  return plugin;
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "bt-sensors-plugin-sk",
3
- "version": "1.0.3",
4
- "description": "Bluetooth Sensors for Signalk",
3
+ "version": "1.1.0-beta.1",
4
+ "description": "Bluetooth Sensors for Signalk - Beta 20241012",
5
5
  "main": "index.js",
6
6
  "dependencies": {
7
7
  "dbus-next": "^0.10.2",
8
- "node-ble": "^1.9.0"
8
+ "node-ble": "^1.9.0",
9
+ "int24":"^0.0.1"
9
10
  },
10
11
  "scripts": {
11
12
  "test": "echo \"Error: no test specified\" && exit 1"