bt-sensors-plugin-sk 1.1.0-beta.2.1.3.1 → 1.1.0-beta.2.1.3.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/package.json
CHANGED
package/sensor_classes/ATC.js
CHANGED
|
@@ -47,7 +47,7 @@ class ATC extends BTSensor{
|
|
|
47
47
|
this.addMetadatum('batteryStrength', 'ratio', 'sensor battery strength',
|
|
48
48
|
(buff)=>{return ((buff.readUInt8(9))/100)})
|
|
49
49
|
this.addMetadatum('temp','K', 'temperature',
|
|
50
|
-
(buff)=>{return parseFloat((273.15+(buff.readInt16BE(6))/
|
|
50
|
+
(buff)=>{return parseFloat((273.15+(buff.readInt16BE(6))/10).toFixed(2))})
|
|
51
51
|
this.addMetadatum('humidity','ratio', 'humidity',
|
|
52
52
|
(buff)=>{return ((buff.readUInt8(8))/100)})
|
|
53
53
|
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/*
|
|
2
|
+
"""Parser for Gmopeka_iot BLE advertisements.
|
|
3
|
+
|
|
4
|
+
Thanks to https://github.com/spbrogan/mopeka_pro_check for
|
|
5
|
+
help decoding the advertisements.
|
|
6
|
+
|
|
7
|
+
MIT License applies.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from bluetooth_data_tools import short_address
|
|
15
|
+
from bluetooth_sensor_state_data import BluetoothData
|
|
16
|
+
from home_assistant_bluetooth import BluetoothServiceInfo
|
|
17
|
+
from sensor_state_data import (
|
|
18
|
+
BinarySensorDeviceClass,
|
|
19
|
+
SensorDeviceClass,
|
|
20
|
+
SensorLibrary,
|
|
21
|
+
Units,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from .models import MediumType
|
|
25
|
+
|
|
26
|
+
_LOGGER = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# converting sensor value to height
|
|
30
|
+
MOPEKA_TANK_LEVEL_COEFFICIENTS = {
|
|
31
|
+
MediumType.PROPANE: (0.573045, -0.002822, -0.00000535),
|
|
32
|
+
MediumType.AIR: (0.153096, 0.000327, -0.000000294),
|
|
33
|
+
MediumType.FRESH_WATER: (0.600592, 0.003124, -0.00001368),
|
|
34
|
+
MediumType.WASTE_WATER: (0.600592, 0.003124, -0.00001368),
|
|
35
|
+
MediumType.LIVE_WELL: (0.600592, 0.003124, -0.00001368),
|
|
36
|
+
MediumType.BLACK_WATER: (0.600592, 0.003124, -0.00001368),
|
|
37
|
+
MediumType.RAW_WATER: (0.600592, 0.003124, -0.00001368),
|
|
38
|
+
MediumType.GASOLINE: (0.7373417462, -0.001978229885, 0.00000202162),
|
|
39
|
+
MediumType.DIESEL: (0.7373417462, -0.001978229885, 0.00000202162),
|
|
40
|
+
MediumType.LNG: (0.7373417462, -0.001978229885, 0.00000202162),
|
|
41
|
+
MediumType.OIL: (0.7373417462, -0.001978229885, 0.00000202162),
|
|
42
|
+
MediumType.HYDRAULIC_OIL: (0.7373417462, -0.001978229885, 0.00000202162),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
MOPEKA_MANUFACTURER = 89
|
|
46
|
+
MOKPEKA_PRO_SERVICE_UUID = "0000fee5-0000-1000-8000-00805f9b34fb"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class MopekaDevice:
|
|
51
|
+
model: str
|
|
52
|
+
name: str
|
|
53
|
+
adv_length: int
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
DEVICE_TYPES = {
|
|
57
|
+
0x3: MopekaDevice("M1017", "Pro Check", 10),
|
|
58
|
+
0x4: MopekaDevice("Pro-200", "Pro-200", 10),
|
|
59
|
+
0x5: MopekaDevice("Pro H20", "Pro Check H2O", 10),
|
|
60
|
+
0x6: MopekaDevice("M1017", "Lippert BottleCheck", 10),
|
|
61
|
+
0x8: MopekaDevice("M1015", "Pro Plus", 10),
|
|
62
|
+
0x9: MopekaDevice("M1015", "Pro Plus with Cellular", 10),
|
|
63
|
+
0xA: MopekaDevice("TD40/TD200", "TD40/TD200", 10),
|
|
64
|
+
0xB: MopekaDevice("TD40/TD200", "TD40/TD200 with Cellular", 10),
|
|
65
|
+
0xC: MopekaDevice("M1017", "Pro Check Universal", 10),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def hex(data: bytes) -> str:
|
|
70
|
+
"""Return a string object containing two hexadecimal digits for each byte in the instance."""
|
|
71
|
+
return "b'{}'".format("".join(f"\\x{b:02x}" for b in data)) # noqa: E231
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def battery_to_voltage(battery: int) -> float:
|
|
75
|
+
"""Convert battery value to voltage"""
|
|
76
|
+
return battery / 32.0
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def battery_to_percentage(battery: int) -> float:
|
|
80
|
+
"""Convert battery value to percentage."""
|
|
81
|
+
return round(max(0, min(100, (((battery / 32.0) - 2.2) / 0.65) * 100)), 1)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def temp_to_celsius(temp: int) -> int:
|
|
85
|
+
"""Convert temperature value to celsius."""
|
|
86
|
+
return temp - 40
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def tank_level_to_mm(tank_level: int) -> int:
|
|
90
|
+
"""Convert tank level value to mm."""
|
|
91
|
+
return tank_level * 10
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def tank_level_and_temp_to_mm(
|
|
95
|
+
tank_level: int, temp: int, medium: MediumType = MediumType.PROPANE
|
|
96
|
+
) -> int:
|
|
97
|
+
"""Get the tank level in mm for a given fluid type."""
|
|
98
|
+
coefs = MOPEKA_TANK_LEVEL_COEFFICIENTS[medium]
|
|
99
|
+
return int(tank_level * (coefs[0] + (coefs[1] * temp) + (coefs[2] * (temp**2))))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class MopekaIOTBluetoothDeviceData(BluetoothData):
|
|
103
|
+
"""Data for Mopeka IOT BLE sensors."""
|
|
104
|
+
|
|
105
|
+
def __init__(self, medium_type: MediumType = MediumType.PROPANE) -> None:
|
|
106
|
+
super().__init__()
|
|
107
|
+
self._medium_type = medium_type
|
|
108
|
+
|
|
109
|
+
def _start_update(self, service_info: BluetoothServiceInfo) -> None:
|
|
110
|
+
"""Update from BLE advertisement data."""
|
|
111
|
+
_LOGGER.debug(
|
|
112
|
+
"Parsing Mopeka IOT BLE advertisement data: %s, MediumType is: %s",
|
|
113
|
+
service_info,
|
|
114
|
+
self._medium_type,
|
|
115
|
+
)
|
|
116
|
+
manufacturer_data = service_info.manufacturer_data
|
|
117
|
+
service_uuids = service_info.service_uuids
|
|
118
|
+
address = service_info.address
|
|
119
|
+
if (
|
|
120
|
+
MOPEKA_MANUFACTURER not in manufacturer_data
|
|
121
|
+
or MOKPEKA_PRO_SERVICE_UUID not in service_uuids
|
|
122
|
+
):
|
|
123
|
+
_LOGGER.debug("Not a Mopeka IOT BLE advertisement: %s", service_info)
|
|
124
|
+
return
|
|
125
|
+
data = manufacturer_data[MOPEKA_MANUFACTURER]
|
|
126
|
+
model_num = data[0]
|
|
127
|
+
if not (device_type := DEVICE_TYPES.get(model_num)):
|
|
128
|
+
_LOGGER.debug("Unsupported Mopeka IOT BLE advertisement: %s", service_info)
|
|
129
|
+
return
|
|
130
|
+
adv_length = device_type.adv_length
|
|
131
|
+
if len(data) != adv_length:
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
self.set_device_manufacturer("Mopeka IOT")
|
|
135
|
+
self.set_device_type(device_type.model)
|
|
136
|
+
self.set_device_name(f"{device_type.name} {short_address(address)}")
|
|
137
|
+
battery = data[1]
|
|
138
|
+
battery_voltage = battery_to_voltage(battery)
|
|
139
|
+
battery_percentage = battery_to_percentage(battery)
|
|
140
|
+
button_pressed = bool(data[2] & 0x80 > 0)
|
|
141
|
+
temp = data[2] & 0x7F
|
|
142
|
+
temp_celsius = temp_to_celsius(temp)
|
|
143
|
+
tank_level = ((int(data[4]) << 8) + data[3]) & 0x3FFF
|
|
144
|
+
tank_level_mm = tank_level_and_temp_to_mm(tank_level, temp, self._medium_type)
|
|
145
|
+
reading_quality = data[4] >> 6
|
|
146
|
+
accelerometer_x = data[8]
|
|
147
|
+
accelerometer_y = data[9]
|
|
148
|
+
|
|
149
|
+
self.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, temp_celsius)
|
|
150
|
+
self.update_predefined_sensor(
|
|
151
|
+
SensorLibrary.BATTERY__PERCENTAGE, battery_percentage
|
|
152
|
+
)
|
|
153
|
+
self.update_predefined_sensor(
|
|
154
|
+
SensorLibrary.VOLTAGE__ELECTRIC_POTENTIAL_VOLT,
|
|
155
|
+
battery_voltage,
|
|
156
|
+
name="Battery Voltage",
|
|
157
|
+
key="battery_voltage",
|
|
158
|
+
)
|
|
159
|
+
self.update_predefined_binary_sensor(
|
|
160
|
+
BinarySensorDeviceClass.OCCUPANCY,
|
|
161
|
+
button_pressed,
|
|
162
|
+
key="button_pressed",
|
|
163
|
+
name="Button pressed",
|
|
164
|
+
)
|
|
165
|
+
self.update_sensor(
|
|
166
|
+
"tank_level",
|
|
167
|
+
Units.LENGTH_MILLIMETERS,
|
|
168
|
+
tank_level_mm if reading_quality >= 1 else None,
|
|
169
|
+
SensorDeviceClass.DISTANCE,
|
|
170
|
+
"Tank Level",
|
|
171
|
+
)
|
|
172
|
+
self.update_sensor(
|
|
173
|
+
"accelerometer_x",
|
|
174
|
+
None,
|
|
175
|
+
accelerometer_x,
|
|
176
|
+
None,
|
|
177
|
+
"Position X",
|
|
178
|
+
)
|
|
179
|
+
self.update_sensor(
|
|
180
|
+
"accelerometer_y",
|
|
181
|
+
None,
|
|
182
|
+
accelerometer_y,
|
|
183
|
+
None,
|
|
184
|
+
"Position Y",
|
|
185
|
+
)
|
|
186
|
+
self.update_sensor(
|
|
187
|
+
"reading_quality_raw",
|
|
188
|
+
None,
|
|
189
|
+
reading_quality,
|
|
190
|
+
None,
|
|
191
|
+
"Reading quality raw",
|
|
192
|
+
)
|
|
193
|
+
self.update_sensor(
|
|
194
|
+
"reading_quality",
|
|
195
|
+
Units.PERCENTAGE,
|
|
196
|
+
round(reading_quality / 3 * 100),
|
|
197
|
+
None,
|
|
198
|
+
"Reading quality",
|
|
199
|
+
)
|
|
200
|
+
# Reading stars = (3-reading_quality) * "★" + (reading_quality * "⭐")
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
class MopekaDevice{
|
|
204
|
+
constructor (ID, name, lengthOfAd = 10){
|
|
205
|
+
this.ID=ID
|
|
206
|
+
this.name=name
|
|
207
|
+
this.lengthOfAd=lengthOfAd
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
MopekaDevices = new Map()
|
|
211
|
+
MopekaDevices.set()
|
|
212
|
+
.set (0x0, new MopekaDevice("XXXX","Unknown Mopeka device"))
|
|
213
|
+
.set (0x3, new MopekaDevice("M1017", "Pro Check"))
|
|
214
|
+
.set (0x4, new MopekaDevice("Pro-200", "Pro-200"))
|
|
215
|
+
.set (0x5, new MopekaDevice("Pro H20", "Pro Check H2O"))
|
|
216
|
+
.set (0x6, new MopekaDevice("M1017", "Lippert BottleCheck"))
|
|
217
|
+
.set (0x8, new MopekaDevice("M1015", "Pro Plus"))
|
|
218
|
+
.set (0x9, new MopekaDevice("M1015", "Pro Plus with Cellular"))
|
|
219
|
+
.set (0xA, new MopekaDevice("TD40/TD200", "TD40/TD200"))
|
|
220
|
+
.set (0xB, new MopekaDevice("TD40/TD200", "TD40/TD200 with Cellular"))
|
|
221
|
+
.set (0xC, new MopekaDevice("M1017", "Pro Check Universal"))
|
|
222
|
+
|
|
223
|
+
const Media={
|
|
224
|
+
PROPANE: {coefficients: [0.573045, -0.002822, -0.00000535]},
|
|
225
|
+
AIR: {coefficients: [0.153096, 0.000327, -0.000000294]},
|
|
226
|
+
FRESH_WATER: {coefficients: [0.600592, 0.003124, -0.00001368]},
|
|
227
|
+
WASTE_WATER: {coefficients: [0.600592, 0.003124, -0.00001368]},
|
|
228
|
+
LIVE_WELL: {coefficients: [0.600592, 0.003124, -0.00001368]},
|
|
229
|
+
BLACK_WATER: {coefficients: [0.600592, 0.003124, -0.00001368]},
|
|
230
|
+
RAW_WATER: {coefficients: [0.600592, 0.003124, -0.00001368]},
|
|
231
|
+
GASOLINE: {coefficients: [0.7373417462, -0.001978229885, 0.00000202162]},
|
|
232
|
+
DIESEL: {coefficients: [0.7373417462, -0.001978229885, 0.00000202162]},
|
|
233
|
+
LNG: {coefficients: [0.7373417462, -0.001978229885, 0.00000202162]},
|
|
234
|
+
OIL: {coefficients: [0.7373417462, -0.001978229885, 0.00000202162]},
|
|
235
|
+
HYDRAULIC_OIL: {coefficients: [0.7373417462, -0.001978229885, 0.00000202162]}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
const BTSensor = require("../BTSensor");
|
|
240
|
+
class MopekaTankSensor extends BTSensor{
|
|
241
|
+
static serviceID = "0000fee5-0000-1000-8000-00805f9b34fb"
|
|
242
|
+
static serviceID16 = 0xFEE5
|
|
243
|
+
|
|
244
|
+
static manufacturerID = 0x0059
|
|
245
|
+
static async identify(device){
|
|
246
|
+
try{
|
|
247
|
+
if (await this.getManufacturerID(device)==this.manufacturerID ){
|
|
248
|
+
const uuids = await this.getDeviceProp(device, 'UUIDs')
|
|
249
|
+
if (uuids != null && uuids.length>0) {
|
|
250
|
+
if (uuids.includes(this.serviceID))
|
|
251
|
+
return this
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
} catch (e){
|
|
256
|
+
this.debug.log(e)
|
|
257
|
+
}
|
|
258
|
+
return null
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async init(){
|
|
262
|
+
await super.init()
|
|
263
|
+
const md = this.valueIfVariant(this.getManufacturerData(this.constructor.manufacturerID))
|
|
264
|
+
this.modelID = md[0]
|
|
265
|
+
this.initMetadata()
|
|
266
|
+
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
getMedium(){
|
|
270
|
+
return Media[this?.medium??'PROPANE']
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
_tankLevel( rawLevel ){
|
|
274
|
+
const coefs= this.getMedium().coefficients
|
|
275
|
+
return rawLevel * (coefs[0] + (coefs[1] * this.temp) + (coefs[2] * (this.temp^2)))
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
initMetadata(){
|
|
279
|
+
const md = this.addMetadatum("medium","","type of liquid in tank")
|
|
280
|
+
md.isParam=true
|
|
281
|
+
md.enum=Object.keys(Media)
|
|
282
|
+
|
|
283
|
+
this.addMetadatum("battVolt","V","sensor battery in volts",
|
|
284
|
+
((buffer)=>{
|
|
285
|
+
this.battVolt = (buffer.readUInt8(1)/32)
|
|
286
|
+
return this.battVolt
|
|
287
|
+
}).bind(this)
|
|
288
|
+
)
|
|
289
|
+
this.addMetadatum("battStrength","ratio","sensor battery strength",
|
|
290
|
+
(buffer)=>{ return Math.max(0, Math.min(100, (((this.battVolt / 32.0) - 2.2) / 0.65))) }
|
|
291
|
+
)
|
|
292
|
+
this.addMetadatum("temp","K","temperature",
|
|
293
|
+
((buffer)=>{
|
|
294
|
+
this.temp = parseFloat(((buffer.readUInt8(2)&0x7F)+233.15).toFixed(2))
|
|
295
|
+
return this.temp
|
|
296
|
+
}).bind(this)
|
|
297
|
+
)
|
|
298
|
+
this.addMetadatum("tankLevel","m","tank level",
|
|
299
|
+
(buffer)=>{ return this._tankLevel(((buffer.readUInt16LE(3))&0x3FFF)*10)/1000}
|
|
300
|
+
)
|
|
301
|
+
this.addMetadatum("readingQuality","","quality of read",
|
|
302
|
+
(buffer)=>{ return buffer.readUInt8(4)>>6}
|
|
303
|
+
)
|
|
304
|
+
this.addMetadatum("accX","Mg","acceleration on X-axis",
|
|
305
|
+
(buffer)=>{ return buffer.readUInt8(8)}
|
|
306
|
+
)
|
|
307
|
+
this.addMetadatum("accY","Mg","acceleration on Y-axis",
|
|
308
|
+
(buffer)=>{ return buffer.readUInt8(9)}
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
propertiesChanged(props){
|
|
313
|
+
super.propertiesChanged(props)
|
|
314
|
+
if (props.ManufacturerData)
|
|
315
|
+
this.emitValuesFrom( this.getManufacturerData(this.constructor.manufacturerID) )
|
|
316
|
+
}
|
|
317
|
+
getName(){
|
|
318
|
+
if (this.name)
|
|
319
|
+
return this.name
|
|
320
|
+
|
|
321
|
+
const _name = MopekaDevices.get(this?.modelID??0x0).name
|
|
322
|
+
return _name?_name:MopekaDevices.get(0x0).name
|
|
323
|
+
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
module.exports=MopekaTankSensor
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const BTSensor = require("../BTSensor");
|
|
2
|
+
class UltrasonicWindMeter extends BTSensor{
|
|
3
|
+
static async identify(device){
|
|
4
|
+
try{
|
|
5
|
+
const uuids = await this.getDeviceProp(device,'UUIDs')
|
|
6
|
+
const name = await this.getDeviceProp(device,"Name")
|
|
7
|
+
if (name == 'ULTRASONIC'){
|
|
8
|
+
return this
|
|
9
|
+
}
|
|
10
|
+
} catch (e){
|
|
11
|
+
this.debug(e)
|
|
12
|
+
return null
|
|
13
|
+
}
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
hasGATT(){
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
emitGatt(){
|
|
20
|
+
this.battCharacteristic.readValue()
|
|
21
|
+
.then((buffer)=>
|
|
22
|
+
this.emitData("batt", buffer)
|
|
23
|
+
)
|
|
24
|
+
this.awsCharacteristic.readValue()
|
|
25
|
+
.then((buffer)=>
|
|
26
|
+
this.emit("aws", buffer)
|
|
27
|
+
|
|
28
|
+
)
|
|
29
|
+
this.awaCharacteristic.readValue()
|
|
30
|
+
.then((buffer)=>
|
|
31
|
+
this.emit("awa", buffer)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
initGATTConnection(){
|
|
37
|
+
return new Promise((resolve,reject )=>{ this.device.connect().then(async ()=>{
|
|
38
|
+
if (!this.gattServer) {
|
|
39
|
+
this.gattServer = await this.device.gatt()
|
|
40
|
+
this.battService = await this.gattServer.getPrimaryService("0000180f-0000-1000-8000-00805f9b34fb")
|
|
41
|
+
this.battCharacteristic = await this.battService.getCharacteristic("00002a19-0000-1000-8000-00805f9b34fb")
|
|
42
|
+
this.envService = await this.gattServer.getPrimaryService("0000181a-0000-1000-8000-00805f9b34fb")
|
|
43
|
+
this.awsCharacteristic = await this.envService.getCharacteristic("00002a72-0000-1000-8000-00805f9b34fb")
|
|
44
|
+
this.awaCharacteristic = await this.envService.getCharacteristic("00002a73-0000-1000-8000-00805f9b34fb") }
|
|
45
|
+
resolve(this)
|
|
46
|
+
}) .catch((e)=>{ reject(e.message) }) })
|
|
47
|
+
}
|
|
48
|
+
initGATTNotifications() {
|
|
49
|
+
Promise.resolve(this.battCharacteristic.startNotifications().then(()=>{
|
|
50
|
+
this.battCharacteristic.on('valuechanged', buffer => {
|
|
51
|
+
this.emitData("batt",buffer)
|
|
52
|
+
})
|
|
53
|
+
}))
|
|
54
|
+
Promise.resolve(this.awaCharacteristic.startNotifications().then(()=>{
|
|
55
|
+
this.awaCharacteristic.on('valuechanged', buffer => {
|
|
56
|
+
this.emitData("awa", buffer)
|
|
57
|
+
})
|
|
58
|
+
}))
|
|
59
|
+
Promise.resolve(this.awsCharacteristic.startNotifications().then(()=>{
|
|
60
|
+
this.awsCharacteristic.on('valuechanged', buffer => {
|
|
61
|
+
this.emitData("aws", buffer)
|
|
62
|
+
})
|
|
63
|
+
}))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async stopListening(){
|
|
67
|
+
super.stopListening()
|
|
68
|
+
if (this.battCharacteristic && await this.battCharacteristic.isNotifying()) {
|
|
69
|
+
await this.battCharacteristic.stopNotifications()
|
|
70
|
+
this.battCharacteristic=null
|
|
71
|
+
}
|
|
72
|
+
if (this.awaCharacteristic && await this.awaCharacteristic.isNotifying()) {
|
|
73
|
+
await this.awaCharacteristic.stopNotifications()
|
|
74
|
+
this.awaCharacteristic=null
|
|
75
|
+
}
|
|
76
|
+
if (this.awsCharacteristic && await this.awsCharacteristic.isNotifying()) {
|
|
77
|
+
await this.awsCharacteristic.stopNotifications()
|
|
78
|
+
this.awsCharacteristic=null
|
|
79
|
+
}
|
|
80
|
+
if (await this.device.isConnected()){
|
|
81
|
+
await this.device.disconnect()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
module.exports=UltrasonicWindMeter
|
|
@@ -18,7 +18,7 @@ const BLACKLISTED = require("../BlackListedDevice.js");
|
|
|
18
18
|
const md = await this.getDeviceProp(device,'ManufacturerData')
|
|
19
19
|
if (!md) return null
|
|
20
20
|
const data = md[0x2e1]
|
|
21
|
-
if (data.value[0]==0x2) { //VE.Smart is on
|
|
21
|
+
if (data && data.value[0]==0x2) { //VE.Smart is on
|
|
22
22
|
return BLACKLISTED
|
|
23
23
|
}
|
|
24
24
|
if (data && data.value[0]==0x10 && data.value[4]==mode)
|
|
@@ -181,7 +181,7 @@ class XiaomiMiBeacon extends BTSensor{
|
|
|
181
181
|
this.emit("temp",(dec.readInt16LE(3)/10)+273.15)
|
|
182
182
|
break
|
|
183
183
|
case 0x06:
|
|
184
|
-
this.emit("humidity",(dec.readInt16LE(3)/
|
|
184
|
+
this.emit("humidity",(dec.readInt16LE(3)/1000))
|
|
185
185
|
break
|
|
186
186
|
default:
|
|
187
187
|
throw new Error(`${this.getNameAndAddress()} unable to parse decrypted service data (${dec})`)
|