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.
@@ -0,0 +1,49 @@
1
+ /*
2
+ Smart Battery Protect
3
+ Start
4
+ bit
5
+ Nr of
6
+ bits Meaning Units Range NA
7
+ value Remark
8
+ 8 8 Device state 0 .. 0xFE 0xFF VE_REG_DEVICE_STATE
9
+ 16 8 Output state 0 .. 0xFE 0xFF VE_REG_DC_OUTPUT_STATUS
10
+ 24 8 Error code 0 .. 0xFE 0xFF VE_REG_CHR_ERROR_CODE
11
+ 32 16 Alarm reason 0 .. 0xFFFF - VE_REG_ALARM_REASON
12
+ 48 16 Warning reason 0 .. 0xFFFF - VE_REG_WARNING_REASON
13
+ 64 16 Input voltage 0.01 V 327.68 .. 327.66 V 0x7FFF VE_REG_DC_CHANNEL1_VOLTAGE
14
+ 80 16 Output voltage 0.01 V 0 .. 655.34 V 0xFFFF VE_REG_DC_OUTPUT_VOLTAGE
15
+ 96 32 Off reason 0 .. 0xFFFFFFFF - VE_REG_DEVICE_OFF_REASON_2
16
+ */
17
+
18
+ const VictronSensor = require ("./Victron/VictronSensor.js")
19
+ const VC = require("./Victron/VictronConstants.js")
20
+ class VictronSmartBatteryProtect extends VictronSensor{
21
+
22
+ static async identify(device){
23
+ return await this.identifyMode(device, 0x09)
24
+ }
25
+
26
+ static {
27
+ this.metadata = new Map(super.getMetadata())
28
+
29
+ this.addMetadatum('deviceState','', 'device state',
30
+ (buff)=>{return VC.OperationMode.get(buff.readUInt8(1))})
31
+ this.addMetadatum('outputStatus','', 'output status', //TODO
32
+ (buff)=>{return (buff.readUInt8(2))})
33
+
34
+ this.addMetadatum('chargerError','', 'charger error',
35
+ (buff)=>{return VC.ChargerError.get(buff.readUInt8(3))})
36
+ this.addMetadatum('alarmReason','', 'alarm reason',
37
+ (buff)=>{return VC.AlarmReason.get(buff.readUInt16LE(4))})
38
+ this.addMetadatum('warningReason','', 'warning reason', //TODO
39
+ (buff)=>{return (buff.readUInt16LE(5))})
40
+ this.addMetadatum('channel1Voltage','V', 'channel one voltage',
41
+ (buff)=>{return this.NaNif(buff.readInt16LE(7),0x7FFF)/100})
42
+ this.addMetadatum('outputVoltage','V', 'output voltage',
43
+ (buff)=>{return this.NaNif(buff.readUInt16LE(9),0xFFFF)/100})
44
+ this.addMetadatum('offReason','', 'off reason',
45
+ (buff)=>{return VC.OffReasons.get(buff.readUInt16LE(11))}) //TODO
46
+ }
47
+
48
+ }
49
+ module.exports=VictronSmartBatteryProtect
@@ -0,0 +1,66 @@
1
+ /*
2
+ SmartLithium (0x05)
3
+ Start Bit Nr of Bits Meaning Units Range NA Value Remark
4
+ 32 32 BMS flags 0..0xFFFFFFFF VE_REG_BMS_FLAGs
5
+ 64 16 SmartLithium error 0..0xFFFF VE_REG_SMART_LITHIUM_ERROR_FLAGS
6
+ 80 7 Cell 1 0.01V 2.60..3.86 V 0x7F VE_REG_BATTERY_CELL_VOLTAGE*
7
+ 87 7 Cell 2 0.01V 2.60..3.86 V 0x7F VE_REG_BATTERY_CELL_VOLTAGE*
8
+ 94 7 Cell 3 0.01V 2.60..3.86 V 0x7F VE_REG_BATTERY_CELL_VOLTAGE*
9
+ 101 7 Cell 4 0.01V 2.60..3.86 V 0x7F VE_REG_BATTERY_CELL_VOLTAGE*
10
+ 108 7 Cell 5 0.01V 2.60..3.86 V 0x7F VE_REG_BATTERY_CELL_VOLTAGE*
11
+ 115 7 Cell 6 0.01V 2.60..3.86 V 0x7F VE_REG_BATTERY_CELL_VOLTAGE*
12
+ 122 7 Cell 7 0.01V 2.60..3.86 V 0x7F VE_REG_BATTERY_CELL_VOLTAGE*
13
+ 129 7 Cell 8 0.01V 2.60..3.86 V 0x7F VE_REG_BATTERY_CELL_VOLTAGE*
14
+ 136 12 Battery voltage 0.01V 0..40.94 V 0x0FFF VE_REG_DC_CHANNEL1_VOLTAGE
15
+ 148 4 Balancer status 0..15 0x0F VE_REG_BALANCER_STATUS
16
+ 152 7 Battery temperature 1°C -40..86 °C 0x7F VE_REG_BAT_TEMPERATURE Temperature = Record value - 40
17
+ 159 1 Unused
18
+ VE_REG_BATTERY_CELL_VOLTAGE 0x00 ( 0) when cell voltage < 2.61V 0x01 ( 1) when cell voltage == 2.61V 0x7D (125) when cell voltage == 3.85V 0x7E (126) when cell voltage > 3.85 0x7F (127) when cell voltage is not available / unknown
19
+ */const VictronSensor = require ("./Victron/VictronSensor.js")
20
+ const VC = require("./Victron/VictronConstants.js")
21
+
22
+ class VictronSmartLithium extends VictronSensor{
23
+
24
+ static async identify(device){
25
+ return await this.identifyMode(device, 0x05)
26
+ }
27
+
28
+ static {
29
+
30
+ function _toCellVoltage(val){
31
+ return val==0x7F?NaN:2.6+(val/100)
32
+ }
33
+ this.metadata = new Map(super.getMetadata())
34
+
35
+ this.addMetadatum('bmsFlags','', 'BMS Flags',
36
+ (buff)=>{return buff.readUInt32BE(0)})
37
+ /
38
+ this.addMetadatum('smartLithiumErrors','', 'Smart Lithium Errors Flags',
39
+ (buff)=>{return buff.readUInt16BE(3)})
40
+ this.addMetadatum('cell1Voltage','V', 'cell #1 voltage',
41
+ (buff)=>{return _toCellVoltage((buff.readUInt8(5))>>1)})
42
+ this.addMetadatum('cell2Voltage','V', 'cell #2 voltage',
43
+ (buff)=>{return _toCellVoltage((buff.subarray(5,7).readUInt16BE())&0x01fe>>2)})
44
+ this.addMetadatum('cell3Voltage','V', 'cell #3 voltage',
45
+ (buff)=>{return _toCellVoltage(((buff.subarray(6,8).readUInt16BE())&0x03fe)>>3)})
46
+ this.addMetadatum('cell4Voltage','V', 'cell #4 voltage',
47
+ (buff)=>{return _toCellVoltage(((buff.subarray(7,9).readUInt16BE())&0x07fe)>>4)})
48
+ this.addMetadatum('cell5Voltage','V', 'cell #5 voltage',
49
+ (buff)=>{return _toCellVoltage(((buff.subarray(8,10).readUInt16BE())&0x0ffe)>>5)})
50
+ this.addMetadatum('cell6Voltage','V', 'cell #6 voltage',
51
+ (buff)=>{return _toCellVoltage(((buff.subarray(9,11).readUInt16BE())&0x1ffe)>>6)})
52
+ this.addMetadatum('cell7Voltage','V', 'cell #7 voltage',
53
+ (buff)=>{return _toCellVoltage(((buff.subarray(10,12).readUInt16BE())&0x3ffe)>>7)})
54
+ this.addMetadatum('cell8Voltage','V', 'cell #8 voltage',
55
+ (buff)=>{return _toCellVoltage((buff.readUInt8(11))&0x7f)})
56
+ this.addMetadatum('batteryVoltage','V', 'battery voltage',
57
+ (buff)=>{return this.NaNif((buff.readUInt16BE(14)>>4),0xFFF)/100})
58
+ this.addMetadatum('balancerStatus','', 'balancer status', //TODO
59
+ (buff)=>{return this.NaNif((buff.readUInt16BE(14)&0xf),0xF)})
60
+ this.addMetadatum('batteryTemp','K', 'battery temperature',
61
+ (buff)=>{return this.NaNif((buff.readInt8(15)>>1),0x7F)+233.15})
62
+
63
+
64
+ }
65
+ }
66
+ module.exports=VictronSmartLithium
@@ -0,0 +1,29 @@
1
+ const VictronSensor = require ("./Victron/VictronSensor.js")
2
+ const VC = require("./Victron/VictronConstants.js")
3
+ class VictronSolarCharger extends VictronSensor{
4
+
5
+ static async identify(device){
6
+ return await this.identifyMode(device, 0x01)
7
+ }
8
+
9
+ static {
10
+ this.metadata = new Map(super.getMetadata())
11
+
12
+ this.addMetadatum('chargeState','', 'charge state',
13
+ (buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
14
+ this.addMetadatum('chargerError','', 'charger error',
15
+ (buff)=>{return VC.ChargerError.get(buff.readUInt8(1))})
16
+ this.addMetadatum('voltage','V', 'charger battery voltage',
17
+ (buff)=>{return this.NaNif(buff.readInt16LE(2),0x7FFF)/100})
18
+ this.addMetadatum('current','A','charger battery current',
19
+ (buff)=>{return this.NaNif(buff.readInt16LE(4),0x7FFF)/10})
20
+ this.addMetadatum('yield','Wh', 'yield today in Watt-hours',
21
+ (buff)=>{return this.NaNif(buff.readUInt16LE(6),0xFFFF)*10})
22
+ this.addMetadatum('solarPower','W', 'solar power',
23
+ (buff)=>{return this.NaNif(buff.readUInt16LE(8),0xFFFF)})
24
+ this.addMetadatum('externalDeviceLoad','A', 'external device load',
25
+ (buff)=>{return this.NaNif(buff.readUInt16BE(10)>>7,0x1FF)})
26
+ }
27
+
28
+ }
29
+ module.exports=VictronSolarCharger
@@ -0,0 +1,47 @@
1
+ const VictronSensor = require ("./Victron/VictronSensor.js")
2
+ const VC = require("./Victron/VictronConstants.js")
3
+ const int24 = require('int24')
4
+ AC_IN_STATE=["AC in 1","AC in 2","NOT CONNECTED", "NA"]
5
+ ALARM_STATE=["None","warning", "alarm","NA"]
6
+ class VictronVEBus extends VictronSensor{
7
+
8
+ static async identify(device){
9
+ return await this.identifyMode(device, 0x0C)
10
+ }
11
+
12
+ static {
13
+ this.metadata = new Map(super.getMetadata())
14
+
15
+ this.addMetadatum('chargeState','', 'charge state',
16
+ (buff)=>{return VC.OperationMode.get(buff.readUInt8(0))})
17
+
18
+ this.addMetadatum('veBusError','', 'VE bus error',
19
+ (buff)=>{return buff.readUInt8(1)}) //TODO
20
+
21
+ this.addMetadatum('current','A','charger battery current',
22
+ (buff)=>{return this.NaNif(buff.readInt16LE(2),0x7FFF)/10})
23
+
24
+ this.addMetadatum('voltage','V', 'charger battery voltage',
25
+ (buff)=>{return this.NaNif(buff.readUInt16LE(4) & 0x3FFF,0x3FFF)/100})
26
+
27
+ this.addMetadatum('acInState','', 'AC in state',
28
+ (buff)=>{return AC_IN_STATE[readUInt8(5)>>6]})
29
+
30
+ this.addMetadatum('acInPower','W','AC power IN in watts',
31
+ (buff)=>{return this.NaNif(int24.readInt24LE(buff,6)>>5,0x3FFFF)*(raw & 0x40000)?-1:1})
32
+
33
+ this.addMetadatum('acOutPower','W','AC power OUT in watts',
34
+ (buff)=>{return this.NaNif((int24.readInt24LE(buff,8) & 0x1FFFF) >> 2,0x3FFFF) * (raw & 0b1)?-1:1})
35
+
36
+ this.addMetadatum('alarm','','alarm state 0=no alarm, 1=warning, 2=alarm',
37
+ (buff)=>{return ALARM_STATE[buff.readUInt8(10)&0x3] })
38
+
39
+ this.addMetadatum('batteryTemperature','K', 'battery temperature',
40
+ (buff)=>{return this.NaNif((buff.readInt8(10)&0x1FF)>>1,0x7F) +233.15})
41
+
42
+ this.addMetadatum('soc', 'ratio', 'state of charge',
43
+ (buff)=>{return this.NaNif((buff.readInt16BE(10)&0x1FF)>>2,0x7F)/100})
44
+
45
+ }
46
+ }
47
+ module.exports=VictronVEBus
@@ -0,0 +1,232 @@
1
+ const { throws } = require("assert");
2
+ const BTSensor = require("../BTSensor");
3
+
4
+ const crypto = require('crypto');
5
+ const { isGeneratorFunction } = require("util/types");
6
+
7
+ const DEVICE_TYPES = new Map([
8
+ [0x0C3C, { name: "Alarm Clock", model: "CGC1" }],
9
+ [0x0576, { name: "3-in-1 Alarm Clock", model: "CGD1" }],
10
+ [0x066F, { name: "Temperature/Humidity Sensor", model: "CGDK2" }],
11
+ [0x0347, { name: "Temperature/Humidity Sensor", model: "CGG1" }],
12
+ [0x0B48, { name: "Temperature/Humidity Sensor", model: "CGG1-ENCRYPTED" }],
13
+ [0x03D6, { name: "Door/Window Sensor", model: "CGH1" }],
14
+ [0x0A83, { name: "Motion/Light Sensor", model: "CGPR1" }],
15
+ [0x03BC, { name: "Grow Care Garden", model: "GCLS002" }],
16
+ [0x0098, { name: "Plant Sensor", model: "HHCCJCY01" }],
17
+ [0x015D, { name: "Smart Flower Pot", model: "HHCCPOT002" }],
18
+ [0x02DF, { name: "Formaldehyde Sensor", model: "JQJCY01YM" }],
19
+ [0x0997, { name: "Smoke Detector", model: "JTYJGD03MI" }],
20
+ [0x1568, { name: "Switch (single button)", model: "K9B-1BTN" }],
21
+ [0x1569, { name: "Switch (double button)", model: "K9B-2BTN" }],
22
+ [0x0DFD, { name: "Switch (triple button)", model: "K9B-3BTN" }],
23
+ [0x1C10, { name: "Switch (single button)", model: "K9BB-1BTN" }],
24
+ [0x1889, { name: "Door/Window Sensor", model: "MS1BB(MI)" }],
25
+ [0x2AEB, { name: "Motion Sensor", model: "HS1BB(MI)" }],
26
+ [0x3F0F, { name: "Flood and Rain Sensor", model: "RS1BB(MI)" }],
27
+ [0x01AA, { name: "Temperature/Humidity Sensor", model: "LYWSDCGQ" }],
28
+ [0x045B, { name: "Temperature/Humidity Sensor", model: "LYWSD02" }],
29
+ [0x16E4, { name: "Temperature/Humidity Sensor", model: "LYWSD02MMC" }],
30
+ [0x2542, { name: "Temperature/Humidity Sensor", model: "LYWSD02MMC" }],
31
+ [0x055B, { name: "Temperature/Humidity Sensor", model: "LYWSD03MMC" }],
32
+ [0x2832, { name: "Temperature/Humidity Sensor", model: "MJWSD05MMC" }],
33
+ [0x098B, { name: "Door/Window Sensor", model: "MCCGQ02HL" }],
34
+ [0x06D3, { name: "Alarm Clock", model: "MHO-C303" }],
35
+ [0x0387, { name: "Temperature/Humidity Sensor", model: "MHO-C401" }],
36
+ [0x07F6, { name: "Nightlight", model: "MJYD02YL" }],
37
+ [0x04E9, { name: "Door Lock", model: "MJZNMSQ01YD" }],
38
+ [0x00DB, { name: "Baby Thermometer", model: "MMC-T201-1" }],
39
+ [0x0391, { name: "Body Thermometer", model: "MMC-W505" }],
40
+ [0x03DD, { name: "Nightlight", model: "MUE4094RT" }],
41
+ [0x0489, { name: "Smart Toothbrush", model: "M1S-T500" }],
42
+ [0x0806, { name: "Smart Toothbrush", model: "T700" }],
43
+ [0x1790, { name: "Smart Toothbrush", model: "T700" }],
44
+ [0x0A8D, { name: "Motion Sensor", model: "RTCGQ02LM" }],
45
+ [0x3531, { name: "Motion Sensor", model: "XMPIRO2SXS" }],
46
+ [0x4C60, { name: "Motion Sensor", model: "XMPIRO2GSXS" }],
47
+ [0x4683, { name: "Occupancy Sensor", model: "XMOSB01XS" }],
48
+ [0x0863, { name: "Flood Detector", model: "SJWS01LM" }],
49
+ [0x045C, { name: "Smart Kettle", model: "V-SK152" }],
50
+ [0x040A, { name: "Mosquito Repellent", model: "WX08ZM" }],
51
+ [0x04E1, { name: "Magic Cube", model: "XMMF01JQD" }],
52
+ [0x1203, { name: "Thermometer", model: "XMWSDJ04MMC" }],
53
+ [0x1949, { name: "Switch (double button)", model: "XMWXKG01YL" }],
54
+ [0x2387, { name: "Button", model: "XMWXKG01LM" }],
55
+ [0x098C, { name: "Door Lock", model: "XMZNMST02YD" }],
56
+ [0x0784, { name: "Door Lock", model: "XMZNMS04LM" }],
57
+ [0x0E39, { name: "Door Lock", model: "XMZNMS08LM" }],
58
+ [0x07BF, { name: "Wireless Switch", model: "YLAI003" }],
59
+ [0x38BB, { name: "Wireless Switch", model: "PTX_YK1_QMIMB" }],
60
+ [0x0153, { name: "Remote Control", model: "YLYK01YL" }],
61
+ [0x068E, { name: "Fan Remote Control", model: "YLYK01YL-FANCL" }],
62
+ [0x04E6, { name: "Ventilator Fan Remote Control", model: "YLYK01YL-VENFAN" }],
63
+ [0x03BF, { name: "Bathroom Heater Remote", model: "YLYB01YL-BHFRC" }],
64
+ [0x03B6, { name: "Dimmer Switch", model: "YLKG07YL/YLKG08YL" }],
65
+ [0x0083, { name: "Smart Kettle", model: "YM-K1501" }],
66
+ [0x0113, { name: "Smart Kettle", model: "YM-K1501EU" }],
67
+ [0x069E, { name: "Door Lock", model: "ZNMS16LM" }],
68
+ [0x069F, { name: "Door Lock", model: "ZNMS17LM" }],
69
+ [0x0380, { name: "Door Lock", model: "DSL-C08" }],
70
+ [0x11C2, { name: "Door Lock", model: "Lockin-SV40" }],
71
+ [0x0DE7, { name: "Odor Eliminator", model: "SU001-T" }]
72
+ ]);
73
+
74
+ class XiaomiMiBeacon extends BTSensor{
75
+
76
+ constructor(device, config, gattConfig){
77
+ super(device, config, gattConfig)
78
+ this.encryptionKey = config?.encryptionKey
79
+ }
80
+ static SERVICE_MIBEACON = "0000fe95-0000-1000-8000-00805f9b34fb"
81
+
82
+ static async identify(device){
83
+ try{
84
+ const sd = await this.getDeviceProp(device, 'ServiceData')
85
+ if (sd == null || sd.length==0) return null
86
+ const keys = Object.keys(sd)
87
+ if (keys[0]==this.SERVICE_MIBEACON) return this
88
+ } catch (e){
89
+ console.log(e)
90
+ return null
91
+ }
92
+ return null
93
+ }
94
+ static {
95
+ this.metadata = new Map(super.getMetadata())
96
+ //3985f4ebc032f276cc316f1f6ecea085
97
+ //8a1dadfa832fef54e9c1d190
98
+
99
+ const md = this.addMetadatum("encryptionKey", "", "encryptionKey (AKA bindKey) for decryption")
100
+ md.isParam=true
101
+ this.addMetadatum('temp','K', 'temperature',
102
+ (buff,offset)=>{return ((buff.readInt16LE(offset))/100) + 273.1})
103
+ this.addMetadatum('humidity','ratio', 'humidity',
104
+ (buff,offset)=>{return ((buff.readUInt8(offset))/100)})
105
+ this.addMetadatum('voltage', 'V', 'sensor battery voltage',
106
+ (buff,offset)=>{return ((buff.readUInt16LE(offset))/1000)})
107
+
108
+ }
109
+ GATTwarning = "WARNING: Xiaomi GATT connections are known to be unreliable on Debian distributions with Bluez 5.55 and up (earlier BlueZ versions are untested). Using GATT on the Xiaomi may put the system Bluetooth stack into an inconsistent state disrupting and disabling other plugin Bluetooth connections. If by some chance you're successful using GATT with the Xiaomi, let the plugin developer know your configuration. Refer to the plugin documentation for getting the Xiamoi bindKey for non-GATT connections and more information on Xiaomi GATT issues."
110
+
111
+ emitValues(buffer){
112
+ this.emitData("temp", buffer, 0)
113
+ this.emitData("humidity", buffer,2)
114
+ this.emitData("voltage",buffer,3);
115
+ }
116
+ getManufacturer(){
117
+ return "Xiaomi Inc."
118
+ }
119
+ getGATTDescription() {
120
+ return this.GATTwarning
121
+ }
122
+ initGATT(){
123
+ this.debug(this.GATTwarning.toUpperCase())
124
+ return new Promise((resolve,reject )=>{
125
+ this.device.connect().then(async ()=>{
126
+ if (!this.gattServer) {
127
+ this.gattServer = await this.device.gatt()
128
+ this.gattService = await this.gattServer.getPrimaryService("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6")
129
+ this.gattCharacteristic = await this.gattService.getCharacteristic("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6")
130
+ }
131
+ resolve(this)
132
+ })
133
+ .catch((e)=>{
134
+ reject(e.message)
135
+ })
136
+ })
137
+ }
138
+
139
+ emitGATT(){
140
+ this.gattCharacteristic.readValue()
141
+ .then((buffer)=>this.emitValues(buffer))
142
+ }
143
+
144
+ async initGATTNotifications() {
145
+ Promise.resolve(this.gattCharacteristic.startNotifications().then(()=>{
146
+ this.gattCharacteristic.on('valuechanged', buffer => {
147
+ this.emitValues(buffer)
148
+ })
149
+ }))
150
+ }
151
+
152
+ decryptV2and3(data){
153
+ const encryptedPayload = data.subarray(-4);
154
+ const xiaomi_mac = data.subarray(5,11)
155
+ const nonce = Buffer.concat([data.subarray(0, 5), data.subarray(-4,-1), xiaomi_mac.subarray(-1)]);
156
+ const cipher = crypto.createDecipheriv('aes-128-ccm', Buffer.from(this.encryptionKey,"hex"), nonce, { authTagLength: 4});
157
+ cipher.setAAD(Buffer.from('11', 'hex'), { plaintextLength: encryptedPayload.length });
158
+
159
+ return cipher.update(encryptedPayload)
160
+ }
161
+ decryptV4and5(data){
162
+ const encryptedPayload = data.subarray(11,-7);
163
+ const xiaomi_mac = data.subarray(5,11)
164
+ const nonce = Buffer.concat([xiaomi_mac, data.subarray(2, 5), data.subarray(-7,-4)]);
165
+ const cipher = crypto.createDecipheriv('aes-128-ccm', Buffer.from(this.encryptionKey,"hex"), nonce, { authTagLength: 4});
166
+ cipher.setAAD(Buffer.from('11', 'hex'), { plaintextLength: encryptedPayload.length });
167
+ cipher.setAuthTag(data.subarray(-4))
168
+ return cipher.update(encryptedPayload)
169
+ }
170
+
171
+ canUseGATT(){
172
+ return true
173
+ }
174
+
175
+ propertiesChanged(props){
176
+ super.propertiesChanged(props)
177
+ if (this.usingGATT()) return
178
+ const data = this.getServiceData(this.constructor.SERVICE_MIBEACON)
179
+ var dec
180
+ if (this.encryptionVersion >= 4) {
181
+ dec = this.decryptV4and5(data)
182
+ } else {
183
+ if(this.encryptionVersion>=2){
184
+ dec=this.decryptV2and3(data)
185
+ }
186
+ }
187
+ if (dec.length==0)
188
+ throw new Error(`${this.getNameAndAddress()} received empty decrypted packet. Check that the bind/encryption key in config is correct.`)
189
+
190
+ switch(dec[0]){
191
+ case 0x04:
192
+ this.emit("temp",(dec.readInt16LE(3)/10)+273.15)
193
+ break
194
+ case 0x06:
195
+ this.emit("humidity",(dec.readInt16LE(3)/10))
196
+ break
197
+ default:
198
+ throw new Error(`${this.getNameAndAddress()} unable to parse decrypted service data (${dec})`)
199
+
200
+ }
201
+ }
202
+
203
+ async init(){
204
+ await super.init()
205
+ const data = this.getServiceData(this.constructor.SERVICE_MIBEACON)
206
+ const frameControl = data[0] + (data[1] << 8)
207
+ this.deviceID = data[2] + (data[3] << 8)
208
+ this.isEncrypted = (frameControl >> 3) & 1
209
+ this.encryptionVersion = frameControl >> 12
210
+ }
211
+
212
+ getName(){
213
+ const dt = DEVICE_TYPES.get(this.deviceID)
214
+ return this?.name??`${dt.name} ${dt.model}`
215
+ }
216
+
217
+ async disconnectGattCharacteristic(){
218
+ if (this.gattCharacteristic && await this.gattCharacteristic.isNotifying()) {
219
+ await this.gattCharacteristic.stopNotifications()
220
+ this.gattCharacteristic=null
221
+ }
222
+ }
223
+ async disconnect(){
224
+ super.disconnect()
225
+ await this.disconnectGattCharacteristic()
226
+
227
+ if (await this.device.isConnected()){
228
+ await this.device.disconnect()
229
+ }
230
+ }
231
+ }
232
+ module.exports=XiaomiMiBeacon
package/test.js ADDED
@@ -0,0 +1,32 @@
1
+ const {createBluetooth} = require('node-ble')
2
+ const {bluetooth, destroy} = createBluetooth()
3
+ const { argv } = require('node:process');
4
+
5
+
6
+ async function main(){
7
+
8
+ const adapter = await bluetooth.defaultAdapter()
9
+
10
+ try{await adapter.startDiscovery()} catch{}
11
+ const device = await adapter.waitDevice(argv[2])
12
+ var trials=0
13
+ await device.connect()
14
+ const gatt = await device.gatt()
15
+ await device.disconnect()
16
+
17
+ async function search(){
18
+ await device.connect()
19
+
20
+ console.log(`GATT trial #${++trials}...`)
21
+ const s = await gatt.getPrimaryService("65970000-4bda-4c1e-af4b-551c4cf74769")
22
+ const c = await s.getCharacteristic('6597ffff-4bda-4c1e-af4b-551c4cf74769')
23
+ await c.readValue()
24
+ await device.disconnect()
25
+ }
26
+
27
+ setInterval(() => {
28
+ search()
29
+ },argv[3]);
30
+ search()
31
+ }
32
+ main()
@@ -1,39 +0,0 @@
1
- const BTSensor = require("../BTSensor");
2
-
3
- class LYWSD03MMC extends BTSensor{
4
-
5
- static needsScannerOn(){
6
- return false
7
- }
8
- static metadata = new Map()
9
- .set('temp',{unit:'K', description: 'temperature'})
10
- .set('humidity',{unit:'ratio', description: 'humidity'})
11
- .set('voltage',{unit:'V', description: 'sensor battery voltage'})
12
-
13
- constructor(device){
14
- super(device)
15
- }
16
-
17
- emitValues(buffer){
18
- this.emit("temp",((buffer.readInt16LE(0))/100) + 273.1);
19
- this.emit("humidity",buffer.readUInt8(2)/100);
20
- this.emit("voltage",buffer.readUInt16LE(3)/1000);
21
- }
22
-
23
- async connect() {
24
- await this.device.connect()
25
- var gattServer = await this.device.gatt()
26
- var gattService = await gattServer.getPrimaryService("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6")
27
- var gattCharacteristic = await gattService.getCharacteristic("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6")
28
- this.emitValues(await gattCharacteristic.readValue())
29
- await gattCharacteristic.startNotifications();
30
- gattCharacteristic.on('valuechanged', buffer => {
31
- this.emitValues(buffer)
32
- })
33
- }
34
- async disconnect(){
35
- super.disconnect()
36
- await this.device.disconnect()
37
- }
38
- }
39
- module.exports=LYWSD03MMC