bt-sensors-plugin-sk 1.1.0-beta.2.2.1.5 → 1.1.0-beta.2.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 +13 -1
- package/index.js +84 -23
- package/package.json +4 -3
- package/sensor_classes/BTHome/AbstractBTHomeSensor.js +199 -0
- package/sensor_classes/BTHome/BTHomeServiceData.js +2289 -0
- package/sensor_classes/BlackListedDevice.js +25 -23
- package/sensor_classes/IBeacon.js +42 -0
- package/sensor_classes/JBDBMS.js +170 -0
- package/sensor_classes/LancolVoltageMeter.js +39 -0
- package/sensor_classes/RenogyBattery.js +4 -4
- package/sensor_classes/RenogyInverter.js +6 -6
- package/sensor_classes/ShellySBHT003C.js +109 -0
- package/sensor_classes/VictronBatteryMonitor.js +3 -3
- package/sensor_classes/XiaomiMiBeacon.js +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Bluetooth Sensors for [Signal K](http://www.signalk.org)
|
|
2
2
|
|
|
3
|
+
## BETA 2.2.3
|
|
4
|
+
### What's New
|
|
5
|
+
Support for [JBD/Jiabaida/Xiaoxiang Battery management systems](https://jiabaida-bms.com/), IBeacon and clone devices courtesy of [Arjen R](https://github.com/ArjenR).
|
|
6
|
+
|
|
7
|
+
### What's New for Developers
|
|
8
|
+
Runtime loading of external device modules. See (https://github.com/naugehyde/bt-sensors-plugin-sk/discussions/26)
|
|
9
|
+
## BETA 2.2.2
|
|
10
|
+
### What's New
|
|
11
|
+
Support for Lancol Battery Meters, Kilovault HLX+ smart batteries courtesy of [sdlee1963](https://github.com/sdlee1963) and baseline support for BTHome devices as well as support for the ShellySBHT003C enviromental sensor courtesy of [Sebastian Haas](https://github.com/sebastianhaas)
|
|
12
|
+
|
|
13
|
+
Fixed incorrect reporting of Victron Battery Monitor -> aux temperature values.
|
|
14
|
+
|
|
3
15
|
## BETA 2.2.1
|
|
4
16
|
|
|
5
17
|
### What's New
|
|
@@ -24,7 +36,7 @@ On the plugin config page, You will need to select the device from the Device dr
|
|
|
24
36
|
|
|
25
37
|
BT Sensors Plugin for Signalk is a lightweight BLE (Bluetooth Low Energy) framework for listening and connecting to Bluetooth sensors on your boat and sending deltas to Signalk paths with the values the sensors reports. <br>
|
|
26
38
|
|
|
27
|
-
The Plugin currently supports every documented Victron device (AC Charger, Battery Monitor, DC-DC Converter, DC Energy Meter, GX Device, Inverter, Inverter RS, Lynx Smart BMS, Orion XS, Smart Battery Protect, Smart Lithium and VE Bus), Xiaomi devices, [ATC devices](https://github.com/atc1441/ATC_MiThermometer), RuuviTags and Inkbird thermometers.
|
|
39
|
+
The Plugin currently supports every documented Victron device (AC Charger, Battery Monitor, DC-DC Converter, DC Energy Meter, GX Device, Inverter, Inverter RS, Lynx Smart BMS, Orion XS, Smart Battery Protect, Smart Lithium and VE Bus), [Lancol Battery Meters ](https://www.lancol.com/product/12v-bluetooth-4-0-battery-tester-micro-10-c/), [Kilovault HLX+ smart batteries ](https://sunwatts.com/content/manual/KiloVault_HLX_PLUS_Datasheet_06252021%20%281%29.pdf?srsltid=AfmBOooY-cGnC_Qm6V1T9Vg5oZzBCJurS0AOGoWqWeyy-dwz2vA-l1Jb), Xiaomi devices, [ATC devices](https://github.com/atc1441/ATC_MiThermometer), RuuviTags, Renogy BMS, Ultrasonic Wind Meters, SwitchBotTH and Meterplus, Aranet 2/4 environmental sensors, Govee 50/51xx sensors, and Inkbird thermometers.
|
|
28
40
|
|
|
29
41
|
A typical use case is a Bluetooth thermometer like the Xiaomi LYWSD03MMC, an inexpensive Bluetooth thermometer that runs on a 3V watch battery that can report the current temperature and humidity in your refrigerator or cabin or wherever you want to stick it (no judgement.) <br>
|
|
30
42
|
|
package/index.js
CHANGED
|
@@ -10,7 +10,7 @@ const BTSensor = require('./BTSensor.js')
|
|
|
10
10
|
const BLACKLISTED = require('./sensor_classes/BlackListedDevice.js')
|
|
11
11
|
|
|
12
12
|
class MissingSensor {
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
|
|
15
15
|
constructor(config){
|
|
16
16
|
this.Metadatum = BTSensor.Metadatum
|
|
@@ -53,8 +53,10 @@ class MissingSensor {
|
|
|
53
53
|
stopListening(){}
|
|
54
54
|
listen(){}
|
|
55
55
|
}
|
|
56
|
-
module.exports =
|
|
57
|
-
|
|
56
|
+
module.exports = function (app) {
|
|
57
|
+
var adapterID = 'hci0'
|
|
58
|
+
|
|
59
|
+
|
|
58
60
|
var deviceConfigs
|
|
59
61
|
var starts=0
|
|
60
62
|
var classMap
|
|
@@ -66,6 +68,8 @@ module.exports = function (app) {
|
|
|
66
68
|
plugin.name = 'BT Sensors plugin';
|
|
67
69
|
plugin.description = 'Plugin to communicate with and update paths to BLE Sensors in Signalk';
|
|
68
70
|
|
|
71
|
+
|
|
72
|
+
|
|
69
73
|
//Try and load utilities-sk NOTE: should be installed from App Store--
|
|
70
74
|
//But there's a fail safe because I'm a reasonable man.
|
|
71
75
|
|
|
@@ -162,10 +166,19 @@ module.exports = function (app) {
|
|
|
162
166
|
}
|
|
163
167
|
|
|
164
168
|
function loadClassMap() {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
+
import(app.config.appPath+"/lib/modules.js").then( (modulesjs)=>{
|
|
170
|
+
const _classMap = utilities_sk.loadClasses(path.join(__dirname, 'sensor_classes'))
|
|
171
|
+
classMap = new Map([..._classMap].filter(([k, v]) => !k.startsWith("_") ))
|
|
172
|
+
const { default:defaultExport} = modulesjs
|
|
173
|
+
const modules = defaultExport.modulesWithKeyword(app.config, "signalk-bt-sensor-class")
|
|
174
|
+
modules.forEach((module)=>{
|
|
175
|
+
module.metadata.classFiles.forEach((classFile)=>{
|
|
176
|
+
const cls = require(module.location+module.module+"/"+classFile);
|
|
177
|
+
classMap.set(cls.name, cls);
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
classMap.get('UNKNOWN').classMap=new Map([...classMap].sort().filter(([k, v]) => !v.isSystem )) // share the classMap with Unknown for configuration purposes
|
|
181
|
+
})
|
|
169
182
|
}
|
|
170
183
|
|
|
171
184
|
app.debug(`Loading plugin ${packageInfo.version}`)
|
|
@@ -338,20 +351,28 @@ module.exports = function (app) {
|
|
|
338
351
|
async function startScanner() {
|
|
339
352
|
|
|
340
353
|
app.debug("Starting scan...");
|
|
341
|
-
|
|
342
|
-
|
|
354
|
+
//Use adapter.helper directly to get around Adapter::startDiscovery()
|
|
355
|
+
//filter options which can cause issues with Device::Connect()
|
|
356
|
+
//turning off Discovery
|
|
357
|
+
try{ await adapter.helper.callMethod('StartDiscovery') } catch (error){
|
|
358
|
+
app.debug(error)
|
|
359
|
+
}
|
|
360
|
+
|
|
343
361
|
}
|
|
344
362
|
|
|
345
363
|
const sensorMap=new Map()
|
|
346
|
-
var adapter
|
|
347
364
|
|
|
348
365
|
plugin.started=false
|
|
349
366
|
|
|
350
367
|
loadClassMap()
|
|
351
368
|
var discoveryIntervalID
|
|
352
|
-
|
|
369
|
+
var adapter
|
|
370
|
+
var adapterPower
|
|
353
371
|
plugin.start = async function (options, restartPlugin) {
|
|
354
|
-
|
|
372
|
+
plugin.started=true
|
|
373
|
+
|
|
374
|
+
var adapterID=options.adapter
|
|
375
|
+
|
|
355
376
|
function getDeviceConfig(mac){
|
|
356
377
|
return deviceConfigs.find((p)=>p.mac_address==mac)
|
|
357
378
|
}
|
|
@@ -381,7 +402,8 @@ module.exports = function (app) {
|
|
|
381
402
|
const msg =`Sensor at ${deviceConfig.mac_address} unavailable. Reason: ${error}`
|
|
382
403
|
app.debug(msg)
|
|
383
404
|
app.debug(error)
|
|
384
|
-
|
|
405
|
+
if (deviceConfig.active)
|
|
406
|
+
app.setPluginError(msg)
|
|
385
407
|
deviceConfig.sensor=new MissingSensor(deviceConfig)
|
|
386
408
|
addSensorToList(deviceConfig.sensor) //add sensor to list with known options
|
|
387
409
|
|
|
@@ -420,25 +442,63 @@ module.exports = function (app) {
|
|
|
420
442
|
discoveryIntervalID = setInterval( findDevices, discoveryInterval*1000, discoveryTimeout)
|
|
421
443
|
}
|
|
422
444
|
|
|
423
|
-
|
|
445
|
+
|
|
446
|
+
if (!adapterID || adapterID=="")
|
|
447
|
+
adapterID = "hci0"
|
|
448
|
+
//Check if Adapter has changed since last start()
|
|
449
|
+
if (adapter) {
|
|
450
|
+
const n = await adapter.getName()
|
|
451
|
+
if (n!=adapterID) {
|
|
452
|
+
adapter.helper.removeAllListeners()
|
|
453
|
+
adapter=null
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
//Connect to adapter
|
|
457
|
+
|
|
458
|
+
if (!adapter){
|
|
459
|
+
app.debug(`Connecting to bluetooth adapter ${adapterID}`);
|
|
460
|
+
|
|
461
|
+
adapter = await bluetooth.getAdapter(adapterID)
|
|
462
|
+
|
|
463
|
+
//Set up DBUS listener to monitor Powered status of current adapter
|
|
464
|
+
|
|
465
|
+
await adapter.helper._prepare()
|
|
466
|
+
adapter.helper._propsProxy.on('PropertiesChanged', async (iface,changedProps,invalidated) => {
|
|
467
|
+
if (Object.hasOwn(changedProps,"Powered")){
|
|
468
|
+
if (changedProps.Powered.value==false) {
|
|
469
|
+
if (plugin.started){ //only call stop() if plugin is started
|
|
470
|
+
app.setPluginStatus(`Bluetooth Adapter ${adapterID} turned off. Plugin disabled.`)
|
|
471
|
+
await plugin.stop()
|
|
472
|
+
adapterPower=false
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
if (!adapterPower) { //only call start() once
|
|
476
|
+
adapterPower=true
|
|
477
|
+
await plugin.start(options,restartPlugin)
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
})
|
|
482
|
+
if (!await adapter.isPowered()) {
|
|
483
|
+
app.debug(`Bluetooth Adapter ${adapterID} not powered on.`)
|
|
484
|
+
app.setPluginError(`Bluetooth Adapter ${adapterID} not powered on.`)
|
|
485
|
+
adapterPower=false
|
|
486
|
+
await plugin.stop()
|
|
487
|
+
return
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
adapterPower=true
|
|
491
|
+
|
|
424
492
|
plugin.uiSchema.peripherals['ui:disabled']=false
|
|
425
493
|
sensorMap.clear()
|
|
426
494
|
deviceConfigs=options?.peripherals??[]
|
|
427
495
|
|
|
428
|
-
|
|
429
|
-
|
|
430
496
|
if (plugin.stopped) {
|
|
431
497
|
await sleep(5000) //Make sure plugin.stop() completes first
|
|
432
498
|
//plugin.start is called asynchronously for some reason
|
|
433
499
|
//and does not wait for plugin.stop to complete
|
|
434
500
|
plugin.stopped=false
|
|
435
501
|
}
|
|
436
|
-
var adapterID=options.adapter
|
|
437
|
-
|
|
438
|
-
if (!adapterID || adapterID==="")
|
|
439
|
-
adapterID = "hci0"
|
|
440
|
-
|
|
441
|
-
app.debug(`Connecting to bluetooth adapter ${adapterID}`);
|
|
442
502
|
|
|
443
503
|
const activeAdapters = await bluetooth.activeAdapters()
|
|
444
504
|
plugin.schema.properties.adapter.enum=[]
|
|
@@ -450,7 +510,7 @@ module.exports = function (app) {
|
|
|
450
510
|
|
|
451
511
|
plugin.uiSchema.adapter={'ui:disabled': (activeAdapters.length==1)}
|
|
452
512
|
|
|
453
|
-
|
|
513
|
+
|
|
454
514
|
await startScanner()
|
|
455
515
|
if (starts>0){
|
|
456
516
|
app.debug(`Plugin ${packageInfo.version} restarting...`);
|
|
@@ -479,6 +539,7 @@ module.exports = function (app) {
|
|
|
479
539
|
plugin.stop = async function () {
|
|
480
540
|
app.debug("Stopping plugin")
|
|
481
541
|
plugin.stopped=true
|
|
542
|
+
plugin.started=false
|
|
482
543
|
plugin.uiSchema.peripherals['ui:disabled']=true
|
|
483
544
|
if ((sensorMap)){
|
|
484
545
|
plugin.schema.properties.peripherals.items.properties.mac_address.enum=[]
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bt-sensors-plugin-sk",
|
|
3
|
-
"version": "1.1.0-beta.2.2.
|
|
3
|
+
"version": "1.1.0-beta.2.2.3",
|
|
4
4
|
"description": "Bluetooth Sensors for Signalk -- support for Victron devices, RuuviTag, Xiaomi, ATC and Inkbird, Ultrasonic wind meters, Mopeka tank readers, Renogy Battery and Solar Controllers, Aranet4 environment sensors, SwitchBot temp and humidity sensors, KilovaultHLXPlus smart batteries, and Govee GVH51xx temp sensors",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"dbus-next": "^0.10.2",
|
|
8
|
-
"node-ble": "^1.
|
|
9
|
-
"int24":"^0.0.1"
|
|
8
|
+
"node-ble": "^1.13.0",
|
|
9
|
+
"int24":"^0.0.1",
|
|
10
|
+
"kaitai-struct": "^0.10.0"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
13
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
const BTSensor = require("../../BTSensor");
|
|
2
|
+
const BTHomeServiceData = require("./BTHomeServiceData");
|
|
3
|
+
const KaitaiStream = require("kaitai-struct/KaitaiStream");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base class for sensors publishing BTHome data.
|
|
7
|
+
*
|
|
8
|
+
* BTHome is an open standard for broadcasting sensor data and button presses over Bluetooth LE.
|
|
9
|
+
*
|
|
10
|
+
* @abstract
|
|
11
|
+
* @see https://bthome.io/
|
|
12
|
+
*/
|
|
13
|
+
class AbstractBTHomeSensor extends BTSensor {
|
|
14
|
+
/**
|
|
15
|
+
* Offset from Celsius to Kelvin, used to convert between these units.
|
|
16
|
+
* 273.15 degrees Kelvin correspond to 0 degrees Celsius.
|
|
17
|
+
*/
|
|
18
|
+
static KELVIN_OFFSET = 273.15;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* BTHome Service identifier
|
|
22
|
+
*
|
|
23
|
+
* @type {string} The Service UUID
|
|
24
|
+
* @see https://bthome.io/images/License_Statement_-_BTHOME.pdf
|
|
25
|
+
*/
|
|
26
|
+
static BTHOME_SERVICE_ID = "0000fcd2-0000-1000-8000-00805f9b34fb";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns measurement data for the given object ID from the given BTHomeData.
|
|
30
|
+
*
|
|
31
|
+
* @param btHomeData {BTHomeServiceData.BthomeServiceData}
|
|
32
|
+
* @param objectId {number}
|
|
33
|
+
* @return {any|null} Returns the measurement data for the given object ID, or `null`, if the BTHomeData does not
|
|
34
|
+
* contain the measurement.
|
|
35
|
+
*/
|
|
36
|
+
static getSensorDataByObjectId(btHomeData, objectId) {
|
|
37
|
+
return btHomeData.measurement.find((m) => m.objectId === objectId)?.data;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Returns whether the specified device's advertisement contains BTHome service data or not.
|
|
42
|
+
*
|
|
43
|
+
* This method should be included in the {@link BTSensor#identify} method of inheriting sensor classes.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* static async identify(device) {
|
|
47
|
+
* if (await this.hasBtHomeServiceData(device)) {
|
|
48
|
+
* // Additional checks to distinguish from other BTHome devices
|
|
49
|
+
* return YourSensorClass;
|
|
50
|
+
* }
|
|
51
|
+
* return null;
|
|
52
|
+
* }
|
|
53
|
+
* @see BTSensor#identify
|
|
54
|
+
* @param device The Bluetooth device to check for BTHome data.
|
|
55
|
+
* @returns {Promise<boolean>} Returns `true`, if the device exposes BTHome service data, or `false`,
|
|
56
|
+
* if not.
|
|
57
|
+
*/
|
|
58
|
+
static async hasBtHomeServiceData(device) {
|
|
59
|
+
const serviceData = await AbstractBTHomeSensor.getDeviceProp(
|
|
60
|
+
device,
|
|
61
|
+
"ServiceData",
|
|
62
|
+
);
|
|
63
|
+
if (serviceData) {
|
|
64
|
+
if (AbstractBTHomeSensor.BTHOME_SERVICE_ID in serviceData) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns whether the specified device has a given name.
|
|
73
|
+
*
|
|
74
|
+
* This method can be included in the {@link BTSensor#identify} method of inheriting sensor classes.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* static async identify(device) {
|
|
78
|
+
* if (await this.hasName(device)) {
|
|
79
|
+
* // Additional checks, if required
|
|
80
|
+
* return YourSensorClass;
|
|
81
|
+
* }
|
|
82
|
+
* return null;
|
|
83
|
+
* }
|
|
84
|
+
* @see BTSensor#identify
|
|
85
|
+
* @param device The Bluetooth device to check the name.
|
|
86
|
+
* @param name {string} The name to be checked for.
|
|
87
|
+
* @returns {Promise<boolean>} Returns `true`, if the device exposes BTHome service data, or `false`,
|
|
88
|
+
* if not.
|
|
89
|
+
*/
|
|
90
|
+
static async hasName(device, name) {
|
|
91
|
+
const deviceName = await AbstractBTHomeSensor.getDeviceProp(device, "Name");
|
|
92
|
+
if (deviceName) {
|
|
93
|
+
if (deviceName === name) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Extracts battery level from the given BTHome data.
|
|
102
|
+
*
|
|
103
|
+
* @param btHomeData {BTHomeServiceData.BthomeServiceData} The BTHome data provided by the device.
|
|
104
|
+
* @returns {number|null} The device's battery level as ratio (0‒1).
|
|
105
|
+
*/
|
|
106
|
+
static parseBatteryLevel(btHomeData) {
|
|
107
|
+
const batteryLevel = AbstractBTHomeSensor.getSensorDataByObjectId(
|
|
108
|
+
btHomeData,
|
|
109
|
+
BTHomeServiceData.BthomeObjectId.SENSOR_BATTERY,
|
|
110
|
+
)?.battery;
|
|
111
|
+
if (batteryLevel) {
|
|
112
|
+
return Number.parseFloat((batteryLevel / 100.0).toFixed(2));
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Extracts temperature from the given BTHome data and converts it to Kelvin for Signal-K.
|
|
119
|
+
*
|
|
120
|
+
* @param btHomeData {BTHomeServiceData.BthomeServiceData} The BTHome data provided by the device.
|
|
121
|
+
* @returns {number|null} The temperature in Kelvin.
|
|
122
|
+
*/
|
|
123
|
+
static parseTemperature(btHomeData) {
|
|
124
|
+
const tempCelsius = AbstractBTHomeSensor.getSensorDataByObjectId(
|
|
125
|
+
btHomeData,
|
|
126
|
+
BTHomeServiceData.BthomeObjectId.SENSOR_TEMPERATURE_0_1,
|
|
127
|
+
)?.temperature;
|
|
128
|
+
if (tempCelsius) {
|
|
129
|
+
return Number.parseFloat(
|
|
130
|
+
(AbstractBTHomeSensor.KELVIN_OFFSET + tempCelsius).toFixed(2),
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Extracts humidity from the given BTHome data.
|
|
138
|
+
*
|
|
139
|
+
* @param btHomeData {BTHomeServiceData.BthomeServiceData} The BTHome data provided by the device.
|
|
140
|
+
* @returns {number|null} The relative humidity as ratio (0‒1).
|
|
141
|
+
*/
|
|
142
|
+
static parseHumidity(btHomeData) {
|
|
143
|
+
const humidity = AbstractBTHomeSensor.getSensorDataByObjectId(
|
|
144
|
+
btHomeData,
|
|
145
|
+
BTHomeServiceData.BthomeObjectId.SENSOR_HUMIDITY,
|
|
146
|
+
)?.humidity;
|
|
147
|
+
if (humidity) {
|
|
148
|
+
return Number.parseFloat((humidity / 100.0).toFixed(2));
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Extracts button press event from the given BTHome data.
|
|
155
|
+
*
|
|
156
|
+
* @param btHomeData {BTHomeServiceData.BthomeServiceData} The BTHome data provided by the device.
|
|
157
|
+
* @returns {BTHomeServiceData.ButtonEventType|null} The device's button press state.
|
|
158
|
+
*/
|
|
159
|
+
static parseButton(btHomeData) {
|
|
160
|
+
const buttonEvent = AbstractBTHomeSensor.getSensorDataByObjectId(
|
|
161
|
+
btHomeData,
|
|
162
|
+
BTHomeServiceData.BthomeObjectId.EVENT_BUTTON,
|
|
163
|
+
)?.event;
|
|
164
|
+
if (buttonEvent) {
|
|
165
|
+
return buttonEvent;
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
propertiesChanged(props) {
|
|
171
|
+
super.propertiesChanged(props);
|
|
172
|
+
|
|
173
|
+
// Make sure the advertisement contains service data, which is not the case for, e.g., RSSI events
|
|
174
|
+
if (!Object.hasOwn(props, "ServiceData")) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Retrieve BTHome data
|
|
179
|
+
const buffer = this.getServiceData(AbstractBTHomeSensor.BTHOME_SERVICE_ID);
|
|
180
|
+
if (!buffer) {
|
|
181
|
+
this.debug(
|
|
182
|
+
`ServiceData does not contain BTHome service, which is unexpected for ${this.getDisplayName()}`,
|
|
183
|
+
);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
// Parse ServiceData according to the BTHome v2 format (https://bthome.io/format/) and emit sensor data
|
|
189
|
+
const btHomeData = new BTHomeServiceData(new KaitaiStream(buffer));
|
|
190
|
+
this.emitValuesFrom(btHomeData);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`Unable to parse BTHome data for ${this.getDisplayName()}`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = AbstractBTHomeSensor;
|