bt-sensors-plugin-sk 1.1.0 → 1.2.0-beta.0.0.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.
- package/BTSensor.js +285 -107
- package/README.md +8 -10
- package/definitions.json +941 -0
- package/index.js +419 -317
- package/package.json +52 -6
- package/plugin_defaults.json +57 -0
- package/public/159.js +2 -0
- package/public/159.js.LICENSE.txt +14 -0
- package/public/30.js +8 -0
- package/public/30.js.LICENSE.txt +65 -0
- package/public/540.js +2 -0
- package/public/540.js.LICENSE.txt +8 -0
- package/public/893.js +1 -0
- package/public/main.js +1 -0
- package/public/remoteEntry.js +1 -0
- package/sensor_classes/ATC.js +31 -20
- package/sensor_classes/BlackListedDevice.js +4 -0
- package/sensor_classes/DEVELOPMENT.md +1 -6
- package/sensor_classes/GoveeH510x.js +3 -3
- package/sensor_classes/IBeacon.js +2 -3
- package/sensor_classes/Inkbird.js +4 -4
- package/sensor_classes/JBDBMS.js +3 -3
- package/sensor_classes/KilovaultHLXPlus.js +1 -1
- package/sensor_classes/MopekaTankSensor.js +13 -6
- package/sensor_classes/Renogy/RenogySensor.js +15 -6
- package/sensor_classes/UNKNOWN.js +8 -4
- package/sensor_classes/UltrasonicWindMeter.js +1 -1
- package/sensor_classes/Victron/VictronSensor.js +6 -2
- package/sensor_classes/VictronBatteryMonitor.js +9 -7
- package/sensor_classes/VictronDCEnergyMeter.js +1 -1
- package/sensor_classes/VictronInverter.js +1 -1
- package/sensor_classes/VictronInverterRS.js +1 -1
- package/sensor_classes/VictronOrionXS.js +1 -1
- package/sensor_classes/XiaomiMiBeacon.js +18 -9
- package/spec/electrical.json +688 -0
- package/spec/environment.json +401 -0
- package/spec/sensors.json +39 -0
- package/spec/tanks.json +115 -0
- package/src/components/PluginConfigurationPanel.js +368 -0
- package/src/index.js +0 -0
- package/webpack.config.js +71 -0
package/index.js
CHANGED
|
@@ -5,37 +5,53 @@ const semver = require('semver')
|
|
|
5
5
|
const packageInfo = require("./package.json")
|
|
6
6
|
|
|
7
7
|
const {createBluetooth} = require('node-ble')
|
|
8
|
-
const {
|
|
8
|
+
const {Variant} = require('dbus-next')
|
|
9
9
|
const {bluetooth, destroy} = createBluetooth()
|
|
10
10
|
|
|
11
11
|
const BTSensor = require('./BTSensor.js')
|
|
12
12
|
const BLACKLISTED = require('./sensor_classes/BlackListedDevice.js')
|
|
13
|
+
const { createChannel, createSession } = require("better-sse");
|
|
14
|
+
const { clearTimeout } = require('timers')
|
|
13
15
|
|
|
14
16
|
class MissingSensor {
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
constructor(config){
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
|
|
20
|
+
this.config=config
|
|
21
|
+
this.addPath=BTSensor.prototype.addPath.bind(this)
|
|
22
|
+
this.addParameter=BTSensor.prototype.addParameter.bind(this)
|
|
23
|
+
|
|
24
|
+
this.getJSONSchema = BTSensor.prototype.getJSONSchema.bind(this)
|
|
25
|
+
this.initSchema = BTSensor.prototype.initSchema.bind(this)
|
|
26
|
+
|
|
27
|
+
this.initSchema()
|
|
22
28
|
|
|
23
|
-
this.metadata= new Map()
|
|
24
29
|
var keys = Object.keys(config?.paths??{})
|
|
25
|
-
|
|
30
|
+
|
|
26
31
|
keys.forEach((key)=>{
|
|
27
|
-
this.
|
|
32
|
+
this.addPath(key,
|
|
33
|
+
{ type: config.paths[key]?.type??'string',
|
|
34
|
+
title: config.paths[key].title} )
|
|
28
35
|
} )
|
|
29
36
|
keys = Object.keys(config?.params??{})
|
|
30
37
|
keys.forEach((key)=>{
|
|
31
|
-
this.
|
|
38
|
+
this.addParameter(key,
|
|
39
|
+
{ type: config.params[key]?.type??'string',
|
|
40
|
+
title: config.params[key].title
|
|
41
|
+
})
|
|
32
42
|
this[key]=config.params[key]
|
|
33
43
|
})
|
|
34
44
|
this.mac_address = config.mac_address
|
|
35
45
|
|
|
36
46
|
}
|
|
37
47
|
hasGATT(){
|
|
38
|
-
return
|
|
48
|
+
return this.config.gattParams
|
|
49
|
+
}
|
|
50
|
+
initGATTConnection(){
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
getGATTDescription(){
|
|
54
|
+
return ""
|
|
39
55
|
}
|
|
40
56
|
getMetadata(){
|
|
41
57
|
return this.metadata
|
|
@@ -47,13 +63,26 @@ class MissingSensor {
|
|
|
47
63
|
return ""
|
|
48
64
|
}
|
|
49
65
|
getName(){
|
|
50
|
-
return this?.name??"Unknown device"
|
|
66
|
+
return `${this?.name??"Unknown device"} (OUT OF RANGE)`
|
|
51
67
|
}
|
|
52
68
|
getDisplayName(){
|
|
53
|
-
return `
|
|
69
|
+
return `(${this.getName()} ${this.getMacAddress()})`
|
|
70
|
+
}
|
|
71
|
+
getRSSI(){
|
|
72
|
+
return NaN
|
|
54
73
|
}
|
|
55
74
|
stopListening(){}
|
|
56
75
|
listen(){}
|
|
76
|
+
isActive(){
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
elapsedTimeSinceLastContact(){
|
|
80
|
+
return NaN
|
|
81
|
+
}
|
|
82
|
+
getSignalStrength(){
|
|
83
|
+
return NaN
|
|
84
|
+
}
|
|
85
|
+
|
|
57
86
|
}
|
|
58
87
|
module.exports = function (app) {
|
|
59
88
|
var adapterID = 'hci0'
|
|
@@ -69,8 +98,6 @@ module.exports = function (app) {
|
|
|
69
98
|
plugin.id = 'bt-sensors-plugin-sk';
|
|
70
99
|
plugin.name = 'BT Sensors plugin';
|
|
71
100
|
plugin.description = 'Plugin to communicate with and update paths to BLE Sensors in Signalk';
|
|
72
|
-
|
|
73
|
-
|
|
74
101
|
|
|
75
102
|
//Try and load utilities-sk NOTE: should be installed from App Store--
|
|
76
103
|
//But there's a fail safe because I'm a reasonable man.
|
|
@@ -98,82 +125,13 @@ module.exports = function (app) {
|
|
|
98
125
|
}, x);
|
|
99
126
|
});
|
|
100
127
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
for (var [clsName, cls] of classMap) {
|
|
104
|
-
if (clsName.startsWith("_")) continue
|
|
105
|
-
const c = await cls.identify(device)
|
|
106
|
-
if (c) {
|
|
107
|
-
c.debug=app.debug
|
|
108
|
-
const sensor = new c(device,config?.params, config?.gattParams)
|
|
109
|
-
sensor.debug=app.debug
|
|
110
|
-
await sensor.init()
|
|
111
|
-
app.debug(`instantiated ${await BTSensor.getDeviceProp(device,"Address")}`)
|
|
112
|
-
|
|
113
|
-
return sensor
|
|
114
|
-
}
|
|
115
|
-
}} catch(error){
|
|
116
|
-
const msg = `Unable to instantiate ${await BTSensor.getDeviceProp(device,"Address")}: ${error.message} `
|
|
117
|
-
app.debug(msg)
|
|
118
|
-
app.debug(error)
|
|
119
|
-
app.setPluginError(msg)
|
|
120
|
-
}
|
|
121
|
-
//if we're here ain't got no class for the device
|
|
122
|
-
var sensor
|
|
123
|
-
if (config.params?.sensorClass){
|
|
124
|
-
const c = classMap.get(config.params.sensorClass)
|
|
125
|
-
c.debug=app.debug
|
|
126
|
-
sensor = new c(device,config?.params, config?.gattParams)
|
|
127
|
-
sensor.debug=app.debug
|
|
128
|
-
await sensor.init()
|
|
129
|
-
} else{
|
|
130
|
-
sensor = new (classMap.get('UNKNOWN'))(device)
|
|
131
|
-
await sensor.init()
|
|
132
|
-
}
|
|
133
|
-
return sensor
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function createPaths(config){
|
|
137
|
-
config.sensor.getMetadata().forEach((metadatum, tag)=>{
|
|
138
|
-
if ((!(metadatum?.isParam)??false)){ //param metadata is passed to the sensor at
|
|
139
|
-
//create time through the constructor, and isn't a
|
|
140
|
-
//a value you want to see in a path
|
|
141
|
-
|
|
142
|
-
const path = config.paths[tag]
|
|
143
|
-
if (!(path===undefined))
|
|
144
|
-
app.handleMessage(plugin.id,
|
|
145
|
-
{
|
|
146
|
-
updates:
|
|
147
|
-
[{ meta: [{path: path, value: { units: metadatum?.unit }}]}]
|
|
148
|
-
})
|
|
149
|
-
}
|
|
150
|
-
})
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function initPaths(deviceConfig){
|
|
154
|
-
deviceConfig.sensor.getMetadata().forEach((metadatum, tag)=>{
|
|
155
|
-
const path = deviceConfig.paths[tag];
|
|
156
|
-
if (!(path === undefined))
|
|
157
|
-
deviceConfig.sensor.on(tag, (val)=>{
|
|
158
|
-
if (metadatum.notify){
|
|
159
|
-
app.notify( tag, val, plugin.id )
|
|
160
|
-
} else {
|
|
161
|
-
updatePath(path,val)
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
function updatePath(path, val){
|
|
167
|
-
app.handleMessage(plugin.id, {updates: [ { values: [ {path: path, value: val }] } ] })
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function loadClassMap() {
|
|
128
|
+
|
|
129
|
+
function loadClassMap() {
|
|
171
130
|
const _classMap = utilities_sk.loadClasses(path.join(__dirname, 'sensor_classes'))
|
|
172
131
|
classMap = new Map([..._classMap].filter(([k, v]) => !k.startsWith("_") ))
|
|
173
132
|
const libPath = app.config.appPath +(
|
|
174
|
-
semver.gt(app.config.version,"2.13.5")?"dist":"lib"
|
|
133
|
+
semver.gt(app.config.version,"2.13.5")?"dist":"lib"
|
|
175
134
|
)
|
|
176
|
-
//+ app.config.version
|
|
177
135
|
import(libPath+"/modules.js").then( (modulesjs)=>{
|
|
178
136
|
const { default:defaultExport} = modulesjs
|
|
179
137
|
const modules = defaultExport.modulesWithKeyword(app.config, "signalk-bt-sensor-class")
|
|
@@ -191,265 +149,370 @@ module.exports = function (app) {
|
|
|
191
149
|
|
|
192
150
|
plugin.schema = {
|
|
193
151
|
type: "object",
|
|
194
|
-
description: "NOTE: \n 1) Plugin must be enabled to configure your sensors. \n"+
|
|
195
|
-
"2) You will have to wait until the scanner has found your device before seeing your device's config fields and saving the configuration. \n"+
|
|
196
|
-
"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"+
|
|
197
|
-
"4) If you submit and get errors it may be because the configured devices have not yet all been discovered.",
|
|
198
152
|
required:["adapter","discoveryTimeout", "discoveryInterval"],
|
|
199
153
|
properties: {
|
|
200
154
|
adapter: {title: "Bluetooth adapter",
|
|
201
155
|
type: "string", default: "hci0"},
|
|
202
156
|
transport: {title: "Transport ",
|
|
203
|
-
|
|
204
|
-
|
|
157
|
+
type: "string", enum: ["auto","le","bredr"], default: "le", enumNames:["Auto", "LE-Bluetooth Low Energy", "BR/EDR Bluetooth basic rate/enhanced data rate"]},
|
|
205
158
|
discoveryTimeout: {title: "Default device discovery timeout (in seconds)",
|
|
206
159
|
type: "integer", default: 30,
|
|
207
160
|
minimum: 10,
|
|
208
161
|
maximum: 3600
|
|
209
162
|
},
|
|
210
|
-
|
|
211
163
|
discoveryInterval: {title: "Scan for new devices interval (in seconds-- 0 for no new device scanning)",
|
|
212
164
|
type: "integer",
|
|
213
165
|
default: 10,
|
|
214
|
-
minimum: 0
|
|
215
|
-
multipleOf: 10
|
|
166
|
+
minimum: 0
|
|
216
167
|
},
|
|
217
|
-
peripherals:
|
|
218
|
-
{ type: "array", title: "Sensors", items:{
|
|
219
|
-
title: "", type: "object",
|
|
220
|
-
required:["mac_address", "discoveryTimeout"],
|
|
221
|
-
properties:{
|
|
222
|
-
active: {title: "Active", type: "boolean", default: true },
|
|
223
|
-
mac_address: {title: "Bluetooth Sensor", type: "string" },
|
|
224
|
-
discoveryTimeout: {title: "Device discovery timeout (in seconds)",
|
|
225
|
-
type: "integer", default:30,
|
|
226
|
-
minimum: 10,
|
|
227
|
-
maximum: 600
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
168
|
}
|
|
234
169
|
}
|
|
235
|
-
const UI_SCHEMA =
|
|
236
|
-
{ "peripherals": {
|
|
237
|
-
'ui:disabled': !plugin.started
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
plugin.uiSchema=UI_SCHEMA
|
|
241
|
-
|
|
242
|
-
function updateSensorDisplayName(sensor){
|
|
243
|
-
const mac_address = sensor.getMacAddress()
|
|
244
|
-
const displayName = sensor.getDisplayName()
|
|
245
|
-
|
|
246
|
-
const mac_addresses_list = plugin.schema.properties.peripherals.items.properties.
|
|
247
|
-
mac_address.enum
|
|
248
|
-
const mac_addresses_names = plugin.schema.properties.peripherals.items.properties.
|
|
249
|
-
mac_address.enumNames
|
|
250
|
-
|
|
251
|
-
var index = mac_addresses_list.indexOf(mac_address)
|
|
252
|
-
if (index!=-1)
|
|
253
|
-
mac_addresses_names[index]= displayName
|
|
254
|
-
}
|
|
255
170
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const oneOf = plugin.schema.properties.peripherals.items.dependencies.mac_address.oneOf
|
|
260
|
-
const mac_address = sensor.getMacAddress()
|
|
261
|
-
|
|
262
|
-
const i = mac_addresses_list.indexOf(mac_address)
|
|
263
|
-
if (i<0) return // n'existe pas
|
|
171
|
+
const sensorMap=new Map()
|
|
172
|
+
|
|
173
|
+
plugin.started=false
|
|
264
174
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
175
|
+
loadClassMap()
|
|
176
|
+
var discoveryIntervalID, progressID, progressTimeoutID, deviceHealthID
|
|
177
|
+
var adapter
|
|
178
|
+
var adapterPower
|
|
179
|
+
const channel = createChannel()
|
|
268
180
|
|
|
181
|
+
plugin.registerWithRouter = function(router) {
|
|
182
|
+
router.get('/sendPluginState', async (req, res) => {
|
|
183
|
+
|
|
184
|
+
res.status(200).json({
|
|
185
|
+
"state":(plugin.started?"started":"stopped")
|
|
186
|
+
})
|
|
187
|
+
});
|
|
188
|
+
router.get("/sse", async (req, res) => {
|
|
189
|
+
const session = await createSession(req, res);
|
|
190
|
+
channel.register(session)
|
|
191
|
+
});
|
|
192
|
+
|
|
269
193
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
194
|
+
|
|
195
|
+
plugin.start = async function (options, restartPlugin) {
|
|
196
|
+
plugin.started=true
|
|
197
|
+
var adapterID=options.adapter
|
|
198
|
+
var foundConfiguredDevices=0
|
|
199
|
+
plugin.registerWithRouter = function(router) {
|
|
200
|
+
|
|
201
|
+
router.post('/sendSensorData', async (req, res) => {
|
|
202
|
+
app.debug(req.body)
|
|
203
|
+
const i = deviceConfigs.findIndex((p)=>p.mac_address==req.body.mac_address)
|
|
204
|
+
if (i<0){
|
|
205
|
+
if (!options.peripherals){
|
|
206
|
+
if (!options.hasOwnProperty("peripherals"))
|
|
207
|
+
options.peripherals=[]
|
|
208
|
+
|
|
209
|
+
options.peripherals=[]
|
|
210
|
+
}
|
|
211
|
+
options.peripherals.push(req.body)
|
|
212
|
+
} else {
|
|
213
|
+
options.peripherals[i] = req.body
|
|
214
|
+
}
|
|
215
|
+
app.savePluginOptions(
|
|
216
|
+
options, async () => {
|
|
217
|
+
app.debug('Plugin options saved')
|
|
218
|
+
res.status(200).json({message: "Sensor updated"})
|
|
219
|
+
const sensor = sensorMap.get(req.body.mac_address)
|
|
220
|
+
if (sensor) {
|
|
221
|
+
removeSensorFromList(sensor)
|
|
222
|
+
if (sensor.isActive()) {
|
|
223
|
+
sensor.stopListening().then(()=>
|
|
224
|
+
initConfiguredDevice(req.body)
|
|
225
|
+
)
|
|
226
|
+
} else {
|
|
227
|
+
initConfiguredDevice(req.body)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
});
|
|
235
|
+
router.post('/removeSensorData', async (req, res) => {
|
|
236
|
+
app.debug(req.body)
|
|
237
|
+
const i = deviceConfigs.findIndex((p)=>p.mac_address==req.body.mac_address)
|
|
238
|
+
if (i>=0){
|
|
239
|
+
deviceConfigs.splice(i,1)
|
|
240
|
+
}
|
|
241
|
+
if (sensorMap.has(req.body.mac_address))
|
|
242
|
+
sensorMap.delete(req.body.mac_address)
|
|
243
|
+
app.savePluginOptions(
|
|
244
|
+
options, async () => {
|
|
245
|
+
app.debug('Plugin options saved')
|
|
246
|
+
res.status(200).json({message: "Sensor updated"})
|
|
247
|
+
channel.broadcast({},"resetSensors")
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
router.post('/sendBaseData', async (req, res) => {
|
|
254
|
+
|
|
255
|
+
app.debug(req.body)
|
|
256
|
+
Object.assign(options,req.body)
|
|
257
|
+
app.savePluginOptions(
|
|
258
|
+
options, () => {
|
|
259
|
+
app.debug('Plugin options saved')
|
|
260
|
+
res.status(200).json({message: "Plugin updated"})
|
|
261
|
+
channel.broadcast({},"pluginRestarted")
|
|
262
|
+
restartPlugin(options)
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
});
|
|
273
266
|
|
|
274
|
-
if(plugin.schema.properties.peripherals.items.properties.mac_address.enum==undefined) {
|
|
275
|
-
plugin.schema.properties.peripherals.items.properties.mac_address.enum=[]
|
|
276
|
-
plugin.schema.properties.peripherals.items.properties.mac_address.enumNames=[]
|
|
277
|
-
}
|
|
278
|
-
const mac_addresses_names = plugin.schema.properties.peripherals.items.properties.mac_address.enumNames
|
|
279
|
-
const mac_addresses_list = plugin.schema.properties.peripherals.items.properties.mac_address.enum
|
|
280
|
-
const mac_address = sensor.getMacAddress()
|
|
281
|
-
const displayName = sensor.getDisplayName()
|
|
282
|
-
|
|
283
|
-
var index = mac_addresses_list.indexOf(mac_address)
|
|
284
|
-
if (index==-1)
|
|
285
|
-
index = mac_addresses_list.push(mac_address)-1
|
|
286
|
-
mac_addresses_names[index]= displayName
|
|
287
|
-
var oneOf = {properties:{mac_address:{enum:[mac_address]}}}
|
|
288
267
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
268
|
+
router.get('/base', (req, res) => {
|
|
269
|
+
|
|
270
|
+
res.status(200).json(
|
|
271
|
+
{
|
|
272
|
+
schema: plugin.schema,
|
|
273
|
+
data: {
|
|
274
|
+
adapter: options.adapter,
|
|
275
|
+
transport: options.transport,
|
|
276
|
+
discoveryTimeout: options.discoveryTimeout,
|
|
277
|
+
discoveryInterval: options.discoveryInterval
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
})
|
|
282
|
+
router.get('/sensors', (req, res) => {
|
|
283
|
+
app.debug("Sending sensors")
|
|
284
|
+
const t = sensorsToJSON()
|
|
285
|
+
res.status(200).json(t)
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
router.get('/progress', (req, res) => {
|
|
289
|
+
app.debug("Sending progress")
|
|
290
|
+
const json = {"progress":foundConfiguredDevices/deviceConfigs.length, "maxTimeout": 1,
|
|
291
|
+
"deviceCount":foundConfiguredDevices,
|
|
292
|
+
"totalDevices": deviceConfigs.length}
|
|
293
|
+
res.status(200).json(json)
|
|
294
|
+
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
router.get('/sendPluginState', async (req, res) => {
|
|
298
|
+
|
|
299
|
+
res.status(200).json({
|
|
300
|
+
"state":(plugin.started?"started":"stopped")
|
|
301
|
+
})
|
|
302
|
+
});
|
|
303
|
+
router.get("/sse", async (req, res) => {
|
|
304
|
+
const session = await createSession(req, res);
|
|
305
|
+
channel.register(session)
|
|
306
|
+
});
|
|
307
|
+
|
|
298
308
|
|
|
299
|
-
|
|
309
|
+
};
|
|
300
310
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
sensor.getGATTParamMetadata().forEach((metadatum,tag)=>{
|
|
308
|
-
oneOf.properties.gattParams.properties[tag]=metadatum.asJSONSchema()
|
|
309
|
-
})
|
|
311
|
+
function sensorsToJSON(){
|
|
312
|
+
return Array.from(
|
|
313
|
+
Array.from(sensorMap.values()).filter((s)=>!(s instanceof BLACKLISTED) ).map(
|
|
314
|
+
sensorToJSON
|
|
315
|
+
))
|
|
310
316
|
}
|
|
311
317
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
318
|
+
function getSensorInfo(sensor){
|
|
319
|
+
if (sensor.getName()=="vent")
|
|
320
|
+
debugger
|
|
321
|
+
return { mac: sensor.getMacAddress(),
|
|
322
|
+
name: sensor.getName(),
|
|
323
|
+
RSSI: sensor.getRSSI(),
|
|
324
|
+
signalStrength: sensor.getSignalStrength(),
|
|
325
|
+
lastContactDelta: sensor. elapsedTimeSinceLastContact()
|
|
326
|
+
}
|
|
317
327
|
}
|
|
318
|
-
sensor.getPathMetadata().forEach((metadatum,tag)=>{
|
|
319
|
-
oneOf.properties.paths.properties[tag]=metadatum.asJSONSchema()
|
|
320
|
-
})
|
|
321
|
-
plugin.schema.properties.peripherals.items.dependencies.mac_address.oneOf.push(oneOf)
|
|
322
|
-
//plugin.schema.properties.peripherals.items.title=sensor.getName()
|
|
323
328
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
329
|
+
function sensorToJSON(sensor){
|
|
330
|
+
const config = getDeviceConfig(sensor.getMacAddress())
|
|
331
|
+
return {
|
|
332
|
+
info: getSensorInfo(sensor),
|
|
333
|
+
schema: sensor.getJSONSchema(),
|
|
334
|
+
config: config?config:{}
|
|
335
|
+
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async function startScanner(transport) {
|
|
332
339
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
340
|
+
app.debug("Starting scan...");
|
|
341
|
+
//Use adapter.helper directly to get around Adapter::startDiscovery()
|
|
342
|
+
//filter options which can cause issues with Device::Connect()
|
|
343
|
+
//turning off Discovery
|
|
344
|
+
//try {await adapter.startDiscovery()}
|
|
345
|
+
const _transport = transport?plugin.schema.properties.transport.default:transport
|
|
346
|
+
try{
|
|
347
|
+
if (_transport) {
|
|
348
|
+
app.debug(`Setting Bluetooth transport option to ${_transport}`)
|
|
349
|
+
await adapter.helper.callMethod('SetDiscoveryFilter', {
|
|
350
|
+
Transport: new Variant('s', _transport)
|
|
351
|
+
})
|
|
352
|
+
}
|
|
353
|
+
adapter._transport=_transport
|
|
354
|
+
await adapter.helper.callMethod('StartDiscovery')
|
|
355
|
+
}
|
|
356
|
+
catch (error){
|
|
357
|
+
app.debug(error)
|
|
348
358
|
}
|
|
349
|
-
|
|
350
|
-
.catch((e)=>{
|
|
351
|
-
if (s)
|
|
352
|
-
s.stopListening()
|
|
353
|
-
|
|
354
|
-
app.debug(`Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`)
|
|
355
|
-
app.debug(e)
|
|
356
|
-
reject( e?.message??e )
|
|
357
|
-
})})
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
async function startScanner(transport) {
|
|
361
|
-
|
|
362
|
-
app.debug("Starting scan...");
|
|
363
|
-
//Use adapter.helper directly to get around Adapter::startDiscovery()
|
|
364
|
-
//filter options which can cause issues with Device::Connect()
|
|
365
|
-
//turning off Discovery
|
|
366
|
-
//try {await adapter.startDiscovery()}
|
|
367
|
-
try{
|
|
368
|
-
if (transport) {
|
|
369
|
-
app.debug(`Setting Bluetooth transport option to ${transport}`)
|
|
370
|
-
await adapter.helper.callMethod('SetDiscoveryFilter', {
|
|
371
|
-
Transport: new Variant('s', transport)
|
|
372
|
-
})
|
|
373
|
-
}
|
|
374
|
-
await adapter.helper.callMethod('StartDiscovery')
|
|
375
|
-
}
|
|
376
|
-
catch (error){
|
|
377
|
-
app.debug(error)
|
|
359
|
+
|
|
378
360
|
}
|
|
379
361
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
plugin.started=false
|
|
362
|
+
function updateSensor(sensor){
|
|
363
|
+
channel.broadcast(getSensorInfo(sensor), "sensorchanged")
|
|
364
|
+
}
|
|
385
365
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
plugin.start = async function (options, restartPlugin) {
|
|
391
|
-
plugin.started=true
|
|
366
|
+
function removeSensorFromList(sensor){
|
|
367
|
+
sensorMap.delete(sensor.getMacAddress())
|
|
368
|
+
channel.broadcast({mac:sensor.getMacAddress()},"removesensor")
|
|
369
|
+
}
|
|
392
370
|
|
|
393
|
-
|
|
371
|
+
async function addSensorToList(sensor){
|
|
372
|
+
app.debug(`adding sensor to list ${sensor.getMacAddress()}`)
|
|
373
|
+
if (sensorMap.has(sensor.getMacAddress()) )
|
|
374
|
+
debugger
|
|
375
|
+
sensorMap.set(sensor.getMacAddress(),sensor)
|
|
376
|
+
channel.broadcast(sensorToJSON(sensor),"newsensor");
|
|
377
|
+
}
|
|
378
|
+
function deviceNameAndAddress(config){
|
|
379
|
+
return `${config?.name??""}${config.name?" at ":""}${config.mac_address}`
|
|
380
|
+
}
|
|
394
381
|
|
|
382
|
+
async function createSensor(adapter, config) {
|
|
383
|
+
return new Promise( ( resolve, reject )=>{
|
|
384
|
+
var s
|
|
385
|
+
const startNumber=starts
|
|
386
|
+
//app.debug(`Waiting on ${deviceNameAndAddress(config)}`)
|
|
387
|
+
adapter.waitDevice(config.mac_address,(config?.discoveryTimeout??30)*1000)
|
|
388
|
+
.then(async (device)=> {
|
|
389
|
+
if (startNumber != starts ) {
|
|
390
|
+
debugger
|
|
391
|
+
return
|
|
392
|
+
}
|
|
393
|
+
//app.debug(`Found ${config.mac_address}`)
|
|
394
|
+
s = await instantiateSensor(device,config)
|
|
395
|
+
|
|
396
|
+
if (s instanceof BLACKLISTED)
|
|
397
|
+
reject ( `Device is blacklisted (${s.reasonForBlacklisting()}).`)
|
|
398
|
+
else{
|
|
399
|
+
addSensorToList(s)
|
|
400
|
+
s.on("RSSI",(()=>{
|
|
401
|
+
updateSensor(s)
|
|
402
|
+
}))
|
|
403
|
+
resolve(s)
|
|
404
|
+
}
|
|
405
|
+
})
|
|
406
|
+
.catch((e)=>{
|
|
407
|
+
if (s)
|
|
408
|
+
s.stopListening()
|
|
409
|
+
if (startNumber == starts ) {
|
|
410
|
+
app.debug(`Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`)
|
|
411
|
+
app.debug(e)
|
|
412
|
+
reject( e?.message??e )
|
|
413
|
+
}
|
|
414
|
+
})})
|
|
415
|
+
}
|
|
395
416
|
function getDeviceConfig(mac){
|
|
396
417
|
return deviceConfigs.find((p)=>p.mac_address==mac)
|
|
397
418
|
}
|
|
419
|
+
async function instantiateSensor(device,config){
|
|
420
|
+
try{
|
|
421
|
+
for (var [clsName, cls] of classMap) {
|
|
422
|
+
if (clsName.startsWith("_")) continue
|
|
423
|
+
const c = await cls.identify(device)
|
|
424
|
+
if (c) {
|
|
425
|
+
c.debug=app.debug
|
|
426
|
+
const sensor = new c(device,config?.params, config?.gattParams)
|
|
427
|
+
sensor.debug=app.debug
|
|
428
|
+
sensor.app=app
|
|
429
|
+
sensor._adapter=adapter //HACK!
|
|
430
|
+
|
|
431
|
+
await sensor.init()
|
|
432
|
+
//app.debug(`instantiated ${await BTSensor.getDeviceProp(device,"Address")}`)
|
|
433
|
+
|
|
434
|
+
return sensor
|
|
435
|
+
}
|
|
436
|
+
}} catch(error){
|
|
437
|
+
const msg = `Unable to instantiate ${await BTSensor.getDeviceProp(device,"Address")}: ${error.message} `
|
|
438
|
+
app.debug(msg)
|
|
439
|
+
app.debug(error)
|
|
440
|
+
app.setPluginError(msg)
|
|
441
|
+
}
|
|
442
|
+
//if we're here ain't got no class for the device
|
|
443
|
+
var sensor
|
|
444
|
+
if (config.params?.sensorClass){
|
|
445
|
+
var c = classMap.get(config.params.sensorClass)
|
|
446
|
+
} else{
|
|
447
|
+
c = classMap.get('UNKNOWN')
|
|
448
|
+
}
|
|
449
|
+
c.debug=app.debug
|
|
450
|
+
sensor = new c(device,config?.params, config?.gattParams)
|
|
451
|
+
sensor.debug=app.debug
|
|
452
|
+
sensor.app=app
|
|
398
453
|
|
|
454
|
+
await sensor.init()
|
|
455
|
+
return sensor
|
|
456
|
+
}
|
|
457
|
+
|
|
399
458
|
function initConfiguredDevice(deviceConfig){
|
|
459
|
+
const startNumber=starts
|
|
400
460
|
app.setPluginStatus(`Initializing ${deviceNameAndAddress(deviceConfig)}`);
|
|
401
461
|
if (!deviceConfig.discoveryTimeout)
|
|
402
462
|
deviceConfig.discoveryTimeout = options.discoveryTimeout
|
|
403
463
|
createSensor(adapter, deviceConfig).then((sensor)=>{
|
|
404
|
-
|
|
464
|
+
if (startNumber != starts ) {
|
|
465
|
+
return
|
|
466
|
+
}
|
|
405
467
|
if (deviceConfig.active) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
initPaths(deviceConfig)
|
|
409
|
-
}
|
|
410
|
-
const result = Promise.resolve(deviceConfig.sensor.listen())
|
|
411
|
-
result.then(() => {
|
|
412
|
-
app.debug(`Listening for changes from ${deviceConfig.sensor.getDisplayName()}`);
|
|
413
|
-
app.setPluginStatus(`Initial scan complete. Listening to ${++found} sensors.`);
|
|
414
|
-
})
|
|
415
|
-
|
|
468
|
+
app.setPluginStatus(`Listening to ${++foundConfiguredDevices} sensors.`);
|
|
469
|
+
sensor.activate(deviceConfig, plugin)
|
|
416
470
|
}
|
|
417
|
-
|
|
471
|
+
|
|
418
472
|
})
|
|
419
473
|
.catch((error)=>
|
|
420
474
|
{
|
|
475
|
+
if (deviceConfig.unconfigured) return
|
|
476
|
+
if (startNumber != starts ) {
|
|
477
|
+
return
|
|
478
|
+
}
|
|
421
479
|
const msg =`Sensor at ${deviceConfig.mac_address} unavailable. Reason: ${error}`
|
|
422
480
|
app.debug(msg)
|
|
423
481
|
app.debug(error)
|
|
424
482
|
if (deviceConfig.active)
|
|
425
483
|
app.setPluginError(msg)
|
|
426
|
-
|
|
427
|
-
|
|
484
|
+
const sensor=new MissingSensor(deviceConfig)
|
|
485
|
+
++foundConfiguredDevices
|
|
486
|
+
|
|
487
|
+
addSensorToList(sensor) //add sensor to list with known options
|
|
428
488
|
|
|
429
489
|
})
|
|
430
490
|
}
|
|
431
491
|
function findDevices (discoveryTimeout) {
|
|
492
|
+
const startNumber = starts
|
|
432
493
|
app.setPluginStatus("Scanning for new Bluetooth devices...");
|
|
433
494
|
|
|
434
495
|
adapter.devices().then( (macs)=>{
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
createSensor(adapter,
|
|
447
|
-
{mac_address:_mac, discoveryTimeout: discoveryTimeout*1000})
|
|
448
|
-
.then((s)=>
|
|
449
|
-
app.setPluginStatus(`Found ${s.getDisplayName()}.`))
|
|
450
|
-
.catch((e)=>
|
|
451
|
-
app.debug(`Device at ${_mac} unavailable. Reason: ${e}`))
|
|
496
|
+
if (startNumber != starts ) {
|
|
497
|
+
return
|
|
498
|
+
}
|
|
499
|
+
for (const mac of macs) {
|
|
500
|
+
var deviceConfig = getDeviceConfig(mac)
|
|
501
|
+
const sensor = sensorMap.get(mac)
|
|
502
|
+
|
|
503
|
+
if (sensor) {
|
|
504
|
+
if (sensor instanceof MissingSensor){
|
|
505
|
+
removeSensorFromList(sensor)
|
|
506
|
+
initConfiguredDevice(deviceConfig)
|
|
452
507
|
}
|
|
508
|
+
} else {
|
|
509
|
+
|
|
510
|
+
if (!deviceConfig) {
|
|
511
|
+
deviceConfig = {mac_address: mac,
|
|
512
|
+
discoveryTimeout: discoveryTimeout*1000,
|
|
513
|
+
active: false, unconfigured: true}
|
|
514
|
+
initConfiguredDevice(deviceConfig)
|
|
515
|
+
}
|
|
453
516
|
}
|
|
454
517
|
}
|
|
455
518
|
})
|
|
@@ -458,9 +521,10 @@ module.exports = function (app) {
|
|
|
458
521
|
function findDeviceLoop(discoveryTimeout, discoveryInterval, immediate=true ){
|
|
459
522
|
if (immediate)
|
|
460
523
|
findDevices(discoveryTimeout)
|
|
461
|
-
discoveryIntervalID =
|
|
524
|
+
discoveryIntervalID =
|
|
525
|
+
setInterval( findDevices, discoveryInterval*1000, discoveryTimeout)
|
|
462
526
|
}
|
|
463
|
-
|
|
527
|
+
|
|
464
528
|
|
|
465
529
|
if (!adapterID || adapterID=="")
|
|
466
530
|
adapterID = "hci0"
|
|
@@ -483,18 +547,15 @@ module.exports = function (app) {
|
|
|
483
547
|
|
|
484
548
|
await adapter.helper._prepare()
|
|
485
549
|
adapter.helper._propsProxy.on('PropertiesChanged', async (iface,changedProps,invalidated) => {
|
|
550
|
+
app.debug(changedProps)
|
|
486
551
|
if (Object.hasOwn(changedProps,"Powered")){
|
|
487
552
|
if (changedProps.Powered.value==false) {
|
|
488
553
|
if (plugin.started){ //only call stop() if plugin is started
|
|
489
554
|
app.setPluginStatus(`Bluetooth Adapter ${adapterID} turned off. Plugin disabled.`)
|
|
490
555
|
await plugin.stop()
|
|
491
|
-
adapterPower=false
|
|
492
556
|
}
|
|
493
557
|
} else {
|
|
494
|
-
|
|
495
|
-
adapterPower=true
|
|
496
|
-
await plugin.start(options,restartPlugin)
|
|
497
|
-
}
|
|
558
|
+
await restartPlugin(options)
|
|
498
559
|
}
|
|
499
560
|
}
|
|
500
561
|
})
|
|
@@ -508,12 +569,13 @@ module.exports = function (app) {
|
|
|
508
569
|
}
|
|
509
570
|
adapterPower=true
|
|
510
571
|
|
|
511
|
-
plugin.uiSchema.peripherals['ui:disabled']=false
|
|
512
572
|
sensorMap.clear()
|
|
573
|
+
if (channel)
|
|
574
|
+
channel.broadcast({state:"started"},"pluginstate")
|
|
513
575
|
deviceConfigs=options?.peripherals??[]
|
|
514
576
|
|
|
515
577
|
if (plugin.stopped) {
|
|
516
|
-
await sleep(5000) //Make sure plugin.stop() completes first
|
|
578
|
+
//await sleep(5000) //Make sure plugin.stop() completes first
|
|
517
579
|
//plugin.start is called asynchronously for some reason
|
|
518
580
|
//and does not wait for plugin.stop to complete
|
|
519
581
|
plugin.stopped=false
|
|
@@ -526,15 +588,10 @@ module.exports = function (app) {
|
|
|
526
588
|
plugin.schema.properties.adapter.enum.push(a.adapter)
|
|
527
589
|
plugin.schema.properties.adapter.enumNames.push(`${a.adapter} @ ${ await a.getAddress()} (${await a.getName()})`)
|
|
528
590
|
}
|
|
529
|
-
|
|
530
|
-
plugin.uiSchema.adapter={'ui:disabled': (activeAdapters.length==1)}
|
|
531
|
-
|
|
532
591
|
|
|
533
592
|
await startScanner(options.transport)
|
|
534
593
|
if (starts>0){
|
|
535
594
|
app.debug(`Plugin ${packageInfo.version} restarting...`);
|
|
536
|
-
if (plugin.schema.properties.peripherals.items.dependencies)
|
|
537
|
-
plugin.schema.properties.peripherals.items.dependencies.mac_address.oneOf=[]
|
|
538
595
|
} else {
|
|
539
596
|
app.debug(`Plugin ${packageInfo.version} started` )
|
|
540
597
|
|
|
@@ -542,43 +599,87 @@ module.exports = function (app) {
|
|
|
542
599
|
starts++
|
|
543
600
|
if (!await adapter.isDiscovering())
|
|
544
601
|
try{
|
|
545
|
-
await
|
|
602
|
+
await startScanner()
|
|
546
603
|
} catch (e){
|
|
547
604
|
app.debug(`Error starting scan: ${e.message}`)
|
|
548
605
|
}
|
|
549
606
|
if (!(deviceConfigs===undefined)){
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
607
|
+
const maxTimeout=Math.max(...deviceConfigs.map((dc)=>dc?.discoveryTimeout??options.discoveryTimeout))
|
|
608
|
+
var progress = 0
|
|
609
|
+
if (progressID==null)
|
|
610
|
+
progressID = setInterval(()=>{
|
|
611
|
+
channel.broadcast({"progress":++progress, "maxTimeout": maxTimeout, "deviceCount":foundConfiguredDevices, "totalDevices": deviceConfigs.length},"progress")
|
|
612
|
+
if ( foundConfiguredDevices==deviceConfigs.length){
|
|
613
|
+
app.debug("progress complete")
|
|
614
|
+
progressID,progressTimeoutID = null
|
|
615
|
+
clearTimeout(progressTimeoutID)
|
|
616
|
+
clearInterval(progressID)
|
|
617
|
+
progressID = null
|
|
618
|
+
}
|
|
619
|
+
},1000);
|
|
620
|
+
if (progressTimeoutID==null)
|
|
621
|
+
progressTimeoutID = setTimeout(()=> {
|
|
622
|
+
app.debug("progress timed out ")
|
|
623
|
+
if (progressID) {
|
|
624
|
+
|
|
625
|
+
clearInterval(progressID);
|
|
626
|
+
progressID=null
|
|
627
|
+
channel.broadcast({"progress":maxTimeout, "maxTimeout": maxTimeout, "deviceCount":foundConfiguredDevices, "totalDevices": deviceConfigs.length},"progress")
|
|
628
|
+
}
|
|
629
|
+
}, (maxTimeout+1)*1000);
|
|
630
|
+
|
|
631
|
+
for (const config of deviceConfigs) {
|
|
632
|
+
initConfiguredDevice(config)
|
|
553
633
|
}
|
|
554
634
|
}
|
|
635
|
+
const minTimeout=Math.min(...deviceConfigs.map((dc)=>dc?.discoveryTimeout??options.discoveryTimeout))
|
|
636
|
+
|
|
637
|
+
deviceHealthID = setInterval( ()=> {
|
|
638
|
+
sensorMap.forEach((sensor)=>{
|
|
639
|
+
const config = getDeviceConfig(sensor.getMacAddress())
|
|
640
|
+
const dt = config?.discoveryTimeout??options.discoveryTimeout
|
|
641
|
+
if (sensor.elapsedTimeSinceLastContact()> dt)
|
|
642
|
+
channel.broadcast(getSensorInfo(sensor), "sensorchanged")
|
|
643
|
+
})
|
|
644
|
+
}, (minTimeout==Infinity)?(options?.discoveryTimeout??plugin.schema.properties.discoveryTimeout.default):minTimeout)
|
|
645
|
+
|
|
646
|
+
if (!options.hasOwnProperty("discoveryInterval" )) //no config -- first run
|
|
647
|
+
options.discoveryInterval = plugin.schema.properties.discoveryInterval.default
|
|
648
|
+
|
|
555
649
|
if (options.discoveryInterval && !discoveryIntervalID)
|
|
556
|
-
findDeviceLoop(options.discoveryTimeout,
|
|
650
|
+
findDeviceLoop(options?.discoveryTimeout??plugin.schema.properties.discoveryTimeout.default,
|
|
651
|
+
options.discoveryInterval)
|
|
557
652
|
}
|
|
558
653
|
plugin.stop = async function () {
|
|
559
654
|
app.debug("Stopping plugin")
|
|
560
655
|
plugin.stopped=true
|
|
561
656
|
plugin.started=false
|
|
562
|
-
|
|
657
|
+
channel.broadcast({state:"stopped"},"pluginstate")
|
|
658
|
+
if (discoveryIntervalID) {
|
|
659
|
+
clearInterval(discoveryIntervalID)
|
|
660
|
+
discoveryIntervalID=null
|
|
661
|
+
}
|
|
662
|
+
if (progressID) {
|
|
663
|
+
clearInterval(progressID)
|
|
664
|
+
progressID=null
|
|
665
|
+
}
|
|
666
|
+
if (progressTimeoutID) {
|
|
667
|
+
clearTimeout(progressTimeoutID)
|
|
668
|
+
progressTimeoutID=null
|
|
669
|
+
}
|
|
670
|
+
|
|
563
671
|
if ((sensorMap)){
|
|
564
|
-
|
|
565
|
-
plugin.schema.properties.peripherals.items.properties.mac_address.enumNames=[]
|
|
566
|
-
sensorMap.forEach(async (sensor, mac)=> {
|
|
672
|
+
for await (const sensorEntry of sensorMap.entries()) {
|
|
567
673
|
try{
|
|
568
|
-
await
|
|
569
|
-
app.debug(`No longer listening to ${
|
|
674
|
+
await sensorEntry[1].stopListening()
|
|
675
|
+
app.debug(`No longer listening to ${sensorEntry[0]}`)
|
|
570
676
|
}
|
|
571
677
|
catch (e){
|
|
572
|
-
app.debug(`Error stopping listening to ${
|
|
678
|
+
app.debug(`Error stopping listening to ${sensorEntry[0]}: ${e.message}`)
|
|
573
679
|
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
if (discoveryIntervalID) {
|
|
578
|
-
clearInterval(discoveryIntervalID)
|
|
579
|
-
discoveryIntervalID=null
|
|
680
|
+
}
|
|
580
681
|
}
|
|
581
|
-
|
|
682
|
+
sensorMap.clear()
|
|
582
683
|
if (adapter && await adapter.isDiscovering())
|
|
583
684
|
try{
|
|
584
685
|
await adapter.stopDiscovery()
|
|
@@ -586,9 +687,10 @@ module.exports = function (app) {
|
|
|
586
687
|
} catch (e){
|
|
587
688
|
app.debug(`Error stopping scan: ${e.message}`)
|
|
588
689
|
}
|
|
690
|
+
|
|
589
691
|
app.debug('BT Sensors plugin stopped')
|
|
590
692
|
|
|
591
693
|
}
|
|
592
|
-
|
|
694
|
+
|
|
593
695
|
return plugin;
|
|
594
696
|
}
|