bt-sensors-plugin-sk 1.2.5-1 → 1.2.6-beta
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 +74 -38
- package/DistanceManager.js +249 -0
- package/Mixin.js +19 -0
- package/OutOfRangeDevice.js +46 -0
- package/README.md +14 -0
- package/classLoader.js +7 -2
- package/connectUUID.exp +26 -0
- package/index.js +49 -3
- package/package.json +8 -6
- package/public/159.js +1 -1
- package/public/540.js +1 -1
- package/public/681.js +3 -9
- package/public/681.js.LICENSE.txt +2 -0
- package/public/764.js +1 -0
- package/public/images/ATC.jpeg +0 -0
- package/public/images/Aranet4.webp +0 -0
- package/public/images/BP108B.webp +0 -0
- package/public/images/GoveeH5074.jpg +0 -0
- package/public/images/GoveeH5075.webp +0 -0
- package/public/images/GoveeH510x.jpg +0 -0
- package/public/images/InkbirdTH3.webp +0 -0
- package/public/images/JBDBMS.webp +0 -0
- package/public/images/Junctek.webp +0 -0
- package/public/images/KilovaultHLXPlus.jpg +0 -0
- package/public/images/LancolVoltageMeter.webp +0 -0
- package/public/images/LiTimeLiFePo4Battery.avif +0 -0
- package/public/images/MercurySmartcraft.jpg +0 -0
- package/public/images/MopekaTankSensor.jpg +0 -0
- package/public/images/RemoranWave3.jpeg +0 -0
- package/public/images/RenogyInverter.jpg +0 -0
- package/public/images/RenogyRoverClient.jpg +0 -0
- package/public/images/RenogySmartLiFePo4Battery.webp +0 -0
- package/public/images/RuuviTag.jpg +0 -0
- package/public/images/ShellyBLUHT.webp +0 -0
- package/public/images/ShellyBLUMotion.webp +0 -0
- package/public/images/ShellyBluDoorWindow.webp +0 -0
- package/public/images/Skanbatt.jpg +0 -0
- package/public/images/SmartBatteryProtect.webp +0 -0
- package/public/images/SmartBatterySense.webp +0 -0
- package/public/images/SwitchBotMeterPlus.webp +0 -0
- package/public/images/SwitchBotTH.webp +0 -0
- package/public/images/TopbandBattery.webp +0 -0
- package/public/images/Ultrasonic.jpg +0 -0
- package/public/images/VictronBlueSmartACCharger.jpg +0 -0
- package/public/images/VictronBlueSolarMPPT.jpeg +0 -0
- package/public/images/VictronCerboGX.webp +0 -0
- package/public/images/VictronInverterRS.webp +0 -0
- package/public/images/VictronLynxSmartBMS.webp +0 -0
- package/public/images/VictronMultiPlus-II.webp +0 -0
- package/public/images/VictronOrionTrIsolated.webp +0 -0
- package/public/images/VictronOrionTrNonIsolated.webp +0 -0
- package/public/images/VictronPhoenixInverter.webp +0 -0
- package/public/images/VictronPhoenixSmart1600.webp +0 -0
- package/public/images/VictronSmartBatteryProtect.jpg +0 -0
- package/public/images/VictronSmartIP43.webp +0 -0
- package/public/images/VictronSmartLithiumBattery.jpg +0 -0
- package/public/images/VictronSmartSolarMPPT.webp +0 -0
- package/public/images/VictronVEBus.webp +0 -0
- package/public/images/iBeacon.jpg +0 -0
- package/public/main.js +1 -1
- package/public/remoteEntry.js +1 -1
- package/readUUID.exp +23 -0
- package/sensor_classes/ATC.js +3 -2
- package/sensor_classes/Aranet2.js +3 -1
- package/sensor_classes/Aranet4.js +1 -2
- package/sensor_classes/BankManager.js +1 -1
- package/sensor_classes/Beacon/AbstractBeaconMixin.js +85 -0
- package/sensor_classes/Beacon/Eddystone.js +77 -0
- package/sensor_classes/Beacon/iBeacon.js +58 -0
- package/sensor_classes/EctiveBMS.js +270 -0
- package/sensor_classes/FeasyComBeacon.js +68 -0
- package/sensor_classes/GobiusCTankMeter.js +4 -3
- package/sensor_classes/GoveeH5074.js +2 -0
- package/sensor_classes/GoveeH5075.js +2 -0
- package/sensor_classes/GoveeH510x.js +1 -0
- package/sensor_classes/Inkbird.js +1 -0
- package/sensor_classes/JBDBMS.js +1 -0
- package/sensor_classes/Junctek.js +14 -6
- package/sensor_classes/KilovaultHLXPlus.js +1 -0
- package/sensor_classes/LancolVoltageMeter.js +2 -0
- package/sensor_classes/MercurySmartcraft.js +1 -0
- package/sensor_classes/MopekaTankSensor.js +3 -200
- package/sensor_classes/RemoranWave3.js +2 -0
- package/sensor_classes/Renogy/RenogySensor.js +1 -0
- package/sensor_classes/RenogyBattery.js +3 -4
- package/sensor_classes/RenogyInverter.js +3 -6
- package/sensor_classes/RenogyRoverClient.js +3 -0
- package/sensor_classes/RuuviTag.js +6 -3
- package/sensor_classes/ShellySBDW002C.js +3 -1
- package/sensor_classes/ShellySBHT003C.js +7 -0
- package/sensor_classes/ShellySBMO003Z.js +3 -2
- package/sensor_classes/ShenzhenLiOnBMS.js +4 -0
- package/sensor_classes/SwitchBotMeterPlus.js +1 -1
- package/sensor_classes/SwitchBotTH.js +2 -1
- package/sensor_classes/UNKNOWN.js +2 -1
- package/sensor_classes/UltrasonicWindMeter.js +3 -0
- package/sensor_classes/Victron/VictronConstants.js +2 -0
- package/sensor_classes/Victron/VictronIdentifier.js +24 -0
- package/sensor_classes/Victron/VictronSensor.js +43 -54
- package/sensor_classes/VictronACCharger.js +1 -6
- package/sensor_classes/VictronBatteryMonitor.js +37 -26
- package/sensor_classes/VictronDCDCConverter.js +1 -4
- package/sensor_classes/VictronDCEnergyMeter.js +1 -4
- package/sensor_classes/VictronGXDevice.js +1 -4
- package/sensor_classes/VictronInverter.js +2 -3
- package/sensor_classes/VictronInverterRS.js +2 -4
- package/sensor_classes/VictronLynxSmartBMS.js +1 -4
- package/sensor_classes/VictronOrionXS.js +1 -3
- package/sensor_classes/VictronSmartBatteryProtect.js +1 -4
- package/sensor_classes/VictronSmartLithium.js +1 -4
- package/sensor_classes/VictronSolarCharger.js +1 -3
- package/sensor_classes/VictronVEBus.js +1 -4
- package/sensor_classes/XiaomiMiBeacon.js +5 -2
- package/sensor_classes/iBeaconSensor.js +40 -0
- package/src/components/PluginConfigurationPanel.js +53 -17
- package/Screenshot 2025-06-12 at 9.33.57/342/200/257AM.png +0 -0
- package/bt-sensors-plugin-sk copy.json +0 -170
- package/bt-sensors-plugin-sk.json.bak +0 -121
- package/diff.txt +0 -2860
- package/public/847.js +0 -1
- package/sensor_classes/IBeacon.js +0 -45
- package/vsl_patch_17_06_25.patch +0 -13
- /package/public/images/{Aranet2_HOME_F_900x900_90OVA5J.original.webp → Aranet2.webp} +0 -0
- /package/public/images/{Bank Manager All-in-onewc.webp → BankManager.webp} +0 -0
- /package/public/images/{Gobius_C.png → GobiusCTankMeter.png} +0 -0
- /package/public/images/{Victron-SmartShunt.jpg → VictronSmartShunt.jpg} +0 -0
- /package/public/images/{smartsolarMPPT7515.png → VictronSmartSolarMPPT7515.png} +0 -0
|
@@ -1,204 +1,4 @@
|
|
|
1
|
-
/*
|
|
2
|
-
"""Parser for Gmopeka_iot BLE advertisements.
|
|
3
1
|
|
|
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
2
|
|
|
203
3
|
class MopekaDevice{
|
|
204
4
|
|
|
@@ -246,6 +46,9 @@ class MopekaTankSensor extends BTSensor{
|
|
|
246
46
|
static serviceID16 = 0xFEE5
|
|
247
47
|
|
|
248
48
|
static manufacturerID = 0x0059
|
|
49
|
+
static Manufacturer = "Mopeka Products LLC"
|
|
50
|
+
|
|
51
|
+
static ImageFile="MopekaTankSensor.jpg"
|
|
249
52
|
static async identify(device){
|
|
250
53
|
if (await this.getManufacturerID(device)==this.manufacturerID ){
|
|
251
54
|
const uuids = await this.getDeviceProp(device, 'UUIDs')
|
|
@@ -33,11 +33,13 @@ const states= ["Charging Needed", "Charging", "Floating", "Idle"]
|
|
|
33
33
|
const BTSensor = require("../BTSensor");
|
|
34
34
|
class RemoranWave3 extends BTSensor{
|
|
35
35
|
static Domain = BTSensor.SensorDomains.electrical
|
|
36
|
+
static ImageFile = "RemoranWave3.jpeg"
|
|
36
37
|
serviceUUID = "81d08df0-c0f8-422a-9d9d-e4379bb1ea3b"
|
|
37
38
|
info1CharUUID = "62c91222-fafe-4f6e-95f0-afc02bd19f2e"
|
|
38
39
|
info2CharUUID = "f5d12d34-4390-486c-b906-24ea8906af71"
|
|
39
40
|
eventUUID = "f12a8e25-59f7-42f2-b7ae-ba96fb25c13c"
|
|
40
41
|
|
|
42
|
+
|
|
41
43
|
static async identify(device){
|
|
42
44
|
|
|
43
45
|
const name = await this.getDeviceProp(device,"Name")
|
|
@@ -13,6 +13,7 @@ class RenogySensor extends BTSensor{
|
|
|
13
13
|
static WRITE_CHAR_UUID = "0000ffd1-0000-1000-8000-00805f9b34fb"
|
|
14
14
|
static READ_FUNC = 3
|
|
15
15
|
static WRITE_FUNC = 6
|
|
16
|
+
static Manufacturer = "Renogy USA"
|
|
16
17
|
constructor(device,config,gattConfig){
|
|
17
18
|
super(device,config,gattConfig)
|
|
18
19
|
}
|
|
@@ -5,13 +5,12 @@
|
|
|
5
5
|
const RenogySensor = require("./Renogy/RenogySensor.js");
|
|
6
6
|
class RenogyBattery extends RenogySensor {
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
static ImageFile="RenogySmartLiFePo4Battery.webp"
|
|
9
9
|
|
|
10
10
|
async init(){
|
|
11
|
-
await super.init()
|
|
12
11
|
this.numberOfCells = await this.retrieveNumberOfCells()
|
|
13
12
|
this.deviceID = await this.retrieveDeviceID()
|
|
14
|
-
|
|
13
|
+
await super.init()
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
async getAllEmitterFunctions(){
|
|
@@ -19,7 +18,7 @@ class RenogyBattery extends RenogySensor {
|
|
|
19
18
|
this.getAndEmitCellTemperatures.bind(this),
|
|
20
19
|
this.getAndEmitCellVoltages.bind(this)]
|
|
21
20
|
}
|
|
22
|
-
|
|
21
|
+
initSchema(){
|
|
23
22
|
|
|
24
23
|
this.addMetadatum('numberOfCells','', 'number of cells')
|
|
25
24
|
this.addDefaultPath('current','electrical.batteries.current')
|
|
@@ -6,13 +6,10 @@ const RenogySensor = require("./Renogy/RenogySensor.js");
|
|
|
6
6
|
const RC=require("./Renogy/RenogyConstants.js")
|
|
7
7
|
class RenogyInverter extends RenogySensor {
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
async init(){
|
|
11
|
-
await super.init()
|
|
12
|
-
this.initMetadata()
|
|
13
|
-
}
|
|
9
|
+
static ImageFile = "RenogyInverter.jpg"
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
initSchema(){
|
|
12
|
+
super.initSchema()
|
|
16
13
|
this.addMetadatum('batteryType', '', "battery type")
|
|
17
14
|
|
|
18
15
|
this.addMetadatum('ueiVoltage','V','UEI Voltage',
|
|
@@ -6,6 +6,9 @@ const RenogySensor = require("./Renogy/RenogySensor.js");
|
|
|
6
6
|
const RC=require("./Renogy/RenogyConstants.js")
|
|
7
7
|
|
|
8
8
|
class RenogyRoverClient extends RenogySensor {
|
|
9
|
+
|
|
10
|
+
static ImageFile = "RenogyRoverClient.jpg"
|
|
11
|
+
|
|
9
12
|
/*
|
|
10
13
|
"batteryType": "electrical.charger.battery.type",
|
|
11
14
|
"batteryPercentage": "electrical.charger.battery.charge",
|
|
@@ -2,6 +2,7 @@ const BTSensor = require("../BTSensor");
|
|
|
2
2
|
class RuuviTag extends BTSensor{
|
|
3
3
|
static manufacturerID = 0x0499
|
|
4
4
|
static Domain = BTSensor.SensorDomains.environmental
|
|
5
|
+
static ImageFile = "RuuviTag.jpg"
|
|
5
6
|
static async identify(device){
|
|
6
7
|
if (await this.getManufacturerID(device)==this.manufacturerID)
|
|
7
8
|
return this
|
|
@@ -14,9 +15,11 @@ class RuuviTag extends BTSensor{
|
|
|
14
15
|
this.addDefaultParam("zone")
|
|
15
16
|
|
|
16
17
|
const md = this.valueIfVariant(this.getManufacturerData(this.constructor.manufacturerID))
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this['_initModeV'+this.mode]
|
|
18
|
+
if (md) {
|
|
19
|
+
this.mode = md[0]
|
|
20
|
+
if (this['_initModeV'+this.mode])
|
|
21
|
+
this['_initModeV'+this.mode]()
|
|
22
|
+
}
|
|
20
23
|
else
|
|
21
24
|
throw new Error("Unrecognized Ruuvitag data mode "+md[0])
|
|
22
25
|
|
|
@@ -16,7 +16,9 @@ class ShellySBDW002C extends AbstractBTHomeSensor {
|
|
|
16
16
|
static SHORTENED_LOCAL_NAME = "SBDW-002C";
|
|
17
17
|
static LOCAL_NAME = "Shelly BLU Door/Window";
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
static ImageFile="ShellyBluDoorWindow.webp"
|
|
20
|
+
|
|
21
|
+
getTextDescription() {
|
|
20
22
|
return `For more information about the sensor go here: <a href=https://us.shelly.com/products/shelly-blu-door-window-white target="_blank">Shelly Blu Door/Window</a>.`;
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -15,6 +15,13 @@ class ShellySBHT003C extends AbstractBTHomeSensor {
|
|
|
15
15
|
*/
|
|
16
16
|
static SHORTENED_LOCAL_NAME = "SBHT-003C";
|
|
17
17
|
static LOCAL_NAME = "TBD"
|
|
18
|
+
|
|
19
|
+
static ImageFile="ShellyBLUHT.webp"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
getTextDescription(){
|
|
23
|
+
return `NOTE: Device must be paired with SignalK server machine to operate properly. For more information about the sensor go here: <a href=https://us.shelly.com/products/shelly-blu-h-t-black target="_blank">Shelly Blu H&T</a>.`
|
|
24
|
+
}
|
|
18
25
|
|
|
19
26
|
initSchema() {
|
|
20
27
|
super.initSchema()
|
|
@@ -20,8 +20,9 @@ class ShellySBMO003Z extends AbstractBTHomeSensor {
|
|
|
20
20
|
* @type {string}
|
|
21
21
|
*/
|
|
22
22
|
static LOCAL_NAME="Shelly BLU Motion";
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
static ImageFile="ShellyBLUMotion.webp"
|
|
24
|
+
|
|
25
|
+
getTextDescription(){
|
|
25
26
|
return `NOTE: Device must be paired with SignalK server machine to operate properly. For more information about the sensor go here: <a href=https://us.shelly.com/products/shelly-blu-motion target="_blank">Shelly Blu Motion</a>.`
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -20,6 +20,7 @@ const ProtectionStatus = {
|
|
|
20
20
|
|
|
21
21
|
class ShenzhenLiONBMS extends BTSensor{
|
|
22
22
|
static Domain = BTSensor.SensorDomains.electrical
|
|
23
|
+
static ImageFile = "LiTimeLiFePo4Battery.avif"
|
|
23
24
|
static Commands = {
|
|
24
25
|
product_registration: new Uint8Array([0x00, 0x00, 0x04, 0x01, 0x01, 0x55, 0xAA, 0x05]),
|
|
25
26
|
disconnect_registration: new Uint8Array([0x00, 0x00, 0x04, 0x01, 0x02, 0x55, 0xAA, 0x06]),
|
|
@@ -34,6 +35,9 @@ class ShenzhenLiONBMS extends BTSensor{
|
|
|
34
35
|
static async identify(device){
|
|
35
36
|
return null
|
|
36
37
|
}
|
|
38
|
+
static DisplayName(){
|
|
39
|
+
"Shenzhen LiTime BMS Batteries: Redodo, Litime, PowerQueen and others"
|
|
40
|
+
}
|
|
37
41
|
async sendCommand(cmd){
|
|
38
42
|
await this.txCharacteristic.writeValueWithResponse( Buffer.from(cmd))
|
|
39
43
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const BTSensor = require("../BTSensor");
|
|
2
2
|
class SwitchBotMeterPlus extends BTSensor{
|
|
3
3
|
static Domain = BTSensor.SensorDomains.environmental
|
|
4
|
-
|
|
4
|
+
static ImageFile = "SwitchBotMeterPlus.webp"
|
|
5
5
|
constructor(device, config={}){
|
|
6
6
|
super(device,config)
|
|
7
7
|
if (config.parser){
|
|
@@ -2,7 +2,8 @@ const BTSensor = require("../BTSensor");
|
|
|
2
2
|
|
|
3
3
|
class SwitchBotTH extends BTSensor {
|
|
4
4
|
static Domain = BTSensor.SensorDomains.environmental
|
|
5
|
-
|
|
5
|
+
static ImageFile = "SwitchBotTH.webp"
|
|
6
|
+
|
|
6
7
|
static async test(){
|
|
7
8
|
|
|
8
9
|
const p = {[this.ID.toString()]: [0xd8, 0x35, 0x34, 0x38, 0x4f, 0x70, 0x07, 0x02, 0x04, 0x96, 0x2c, 0x00]}
|
|
@@ -13,7 +13,8 @@ class UNKNOWN extends BTSensor{
|
|
|
13
13
|
"sensorClass",
|
|
14
14
|
{
|
|
15
15
|
title: "Sensor Class",
|
|
16
|
-
enum:Array.from(this.constructor.classMap.keys())
|
|
16
|
+
enum: Array.from(this.constructor.classMap.keys()),
|
|
17
|
+
enumNames: (Array.from(this.constructor.classMap.values()).map((cls)=>{return cls.DisplayName()}))
|
|
17
18
|
}
|
|
18
19
|
)
|
|
19
20
|
this.listen()
|
|
@@ -110,6 +110,8 @@ const Images = require('./VictronImages.js')
|
|
|
110
110
|
0xA06F: "BlueSolar MPPT 150|45 rev2",
|
|
111
111
|
0xA070: "BlueSolar MPPT 150|60 rev2",
|
|
112
112
|
0xA071: "BlueSolar MPPT 150|70 rev2",
|
|
113
|
+
0xA0EB: "Smart Lithium Battery 12.8v 200AH",
|
|
114
|
+
0xA0EA: "Smart Lithium Battery 25.6v 200AH",
|
|
113
115
|
0xA0F0: "Smart Lithium Battery 4 cells 12v 330AH",
|
|
114
116
|
0xA0EE: "Smart Lithium Battery",
|
|
115
117
|
0xA102: "SmartSolar MPPT VE.Can 150/70",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
class VictronIdentifier{
|
|
3
|
+
static identify( md ){
|
|
4
|
+
if (md.length>=5 && md[0]==0x10){
|
|
5
|
+
switch (md[4]) {
|
|
6
|
+
case 0x1: return require("../VictronSolarCharger")
|
|
7
|
+
case 0x2: return require("../VictronBatteryMonitor")
|
|
8
|
+
case 0x3: return require("../VictronInverter")
|
|
9
|
+
case 0x4: return require("../VictronDCDCConverter")
|
|
10
|
+
case 0x5: return require("../VictronSmartLithium")
|
|
11
|
+
case 0x6: return require("../VictronInverterRS")
|
|
12
|
+
case 0x7: return require("../VictronGXDevice")
|
|
13
|
+
case 0x8: return require("../VictronACCharger")
|
|
14
|
+
case 0x9: return require("../VictronSmartBatteryProtect")
|
|
15
|
+
case 0xA: return require("../VictronLynxSmartBMS")
|
|
16
|
+
case 0xC: return require("../VictronVEBus")
|
|
17
|
+
case 0xD: return require("../VictronDCEnergyMeter");
|
|
18
|
+
case 0xF: return require("../VictronOrionXS")
|
|
19
|
+
default: return null
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
module.exports=VictronIdentifier
|
|
@@ -3,71 +3,62 @@ const Images = require('./VictronImages.js')
|
|
|
3
3
|
|
|
4
4
|
const BTSensor = require("../../BTSensor.js");
|
|
5
5
|
const crypto = require('node:crypto');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
setTimeout(() => {
|
|
9
|
-
resolve(x);
|
|
10
|
-
}, x);
|
|
11
|
-
});
|
|
12
|
-
}
|
|
6
|
+
const VictronIdentifier = require('./VictronIdentifier.js');
|
|
7
|
+
|
|
13
8
|
class VictronSensor extends BTSensor{
|
|
14
9
|
static Domain = BTSensor.SensorDomains.electrical
|
|
15
|
-
|
|
10
|
+
static ManufacturerID = 0x2e1
|
|
16
11
|
constructor(device,config,gattConfig){
|
|
17
12
|
super(device,config,gattConfig)
|
|
18
13
|
|
|
19
|
-
if(device.modelID)
|
|
20
|
-
this.modelID=device.modelID
|
|
21
|
-
|
|
14
|
+
if (device.modelID)
|
|
15
|
+
this.modelID=device.modelID
|
|
22
16
|
}
|
|
17
|
+
|
|
23
18
|
|
|
24
|
-
static
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
var md = await this.getDeviceProp(device,'ManufacturerData')
|
|
30
|
-
if (md==undefined || !Object.hasOwn(md,0x2e1))
|
|
31
|
-
return null
|
|
32
|
-
else {
|
|
33
|
-
|
|
34
|
-
if (md[0x2e1].value[0]==0x10) {
|
|
35
|
-
if (md[0x2e1].value[4]==mode) {
|
|
36
|
-
device.modelID=this.getModelID(md)
|
|
37
|
-
return this
|
|
38
|
-
}
|
|
39
|
-
else
|
|
40
|
-
return null
|
|
41
|
-
}
|
|
19
|
+
static async getDataPacket(device, md){
|
|
20
|
+
if (md && md[this.ManufacturerID]?.value[0]==0x10)
|
|
21
|
+
return md[this.ManufacturerID].value
|
|
22
|
+
|
|
23
|
+
device.helper._prepare()
|
|
42
24
|
|
|
43
|
-
|
|
44
|
-
device.helper._prepare()
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
45
26
|
device.helper.on("PropertiesChanged",
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
27
|
+
(props)=> {
|
|
28
|
+
if (Object.hasOwn(props,'ManufacturerData')){
|
|
29
|
+
const md = props['ManufacturerData'].value
|
|
30
|
+
if(md[this.ManufacturerID].value[0]==0x10) {
|
|
31
|
+
device.helper.removeListeners()
|
|
32
|
+
resolve(md[this.ManufacturerID].value)
|
|
50
33
|
}
|
|
34
|
+
}
|
|
51
35
|
})
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
device.modelID=this.getModelID(md)
|
|
58
|
-
return this
|
|
59
|
-
}
|
|
60
|
-
else
|
|
61
|
-
return null
|
|
62
|
-
}
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static getModelID(md) {
|
|
40
|
+
return md[this.ManufacturerID]?.value.readUInt16LE(2)??-1
|
|
63
41
|
}
|
|
64
42
|
|
|
65
|
-
async
|
|
43
|
+
static async identify(device){
|
|
44
|
+
|
|
45
|
+
var md = await this.getDeviceProp(device,'ManufacturerData')
|
|
46
|
+
if (md==undefined || !Object.hasOwn(md,this.ManufacturerID))
|
|
47
|
+
return null
|
|
48
|
+
const data=await this.getDataPacket(device,md)
|
|
49
|
+
if (data) {
|
|
50
|
+
device.modelID=this.getModelID(md)
|
|
51
|
+
return VictronIdentifier.identify(data)
|
|
52
|
+
}
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
async init(){
|
|
66
56
|
await super.init()
|
|
67
57
|
this.addParameter(
|
|
68
58
|
"encryptionKey",
|
|
69
59
|
{
|
|
70
|
-
title:"Encryption Key"
|
|
60
|
+
title:"Encryption Key",
|
|
61
|
+
isRequired: true
|
|
71
62
|
}
|
|
72
63
|
)
|
|
73
64
|
}
|
|
@@ -109,7 +100,7 @@ function sleep(x) {
|
|
|
109
100
|
}
|
|
110
101
|
getModelID(){
|
|
111
102
|
if (!this.modelID ||this.modelID==-1)
|
|
112
|
-
this.modelID=this.getManufacturerData(
|
|
103
|
+
this.modelID=this.getManufacturerData(this.constructor.ManufacturerID)?.readUInt16LE(2)??-1
|
|
113
104
|
|
|
114
105
|
return this.modelID
|
|
115
106
|
}
|
|
@@ -122,7 +113,7 @@ function sleep(x) {
|
|
|
122
113
|
if (this.usingGATT()) return
|
|
123
114
|
if (!props.hasOwnProperty("ManufacturerData")) return
|
|
124
115
|
try{
|
|
125
|
-
const md = this.getManufacturerData(
|
|
116
|
+
const md = this.getManufacturerData(this.constructor.ManufacturerID)
|
|
126
117
|
if (md && md.length && md[0]==0x10){
|
|
127
118
|
const decData=this.decrypt(md)
|
|
128
119
|
this.emitValuesFrom(decData)
|
|
@@ -145,13 +136,11 @@ function sleep(x) {
|
|
|
145
136
|
return Images.generic
|
|
146
137
|
}
|
|
147
138
|
|
|
148
|
-
|
|
139
|
+
getTextDescription(){
|
|
149
140
|
//return `<img src="https://www.victronenergy.com/_next/image?url=https%3A%2F%2Fwww.victronenergy.com%2Fupload%2Fproducts%2FSmartShunt%2520500_nw.png&w=1080&q=70"" height="150" align=”top” ></img>`
|
|
150
141
|
|
|
151
142
|
|
|
152
|
-
return
|
|
153
|
-
margin: 20px;" ></img>
|
|
154
|
-
To get the encryption key for your device, follow the instructions <a href=https://communityarchive.victronenergy.com/questions/187303/victron-bluetooth-advertising-protocol.html target="_victron_encrypt">here</a>`
|
|
143
|
+
return `To get the encryption key for your device, follow the instructions <a href=https://communityarchive.victronenergy.com/questions/187303/victron-bluetooth-advertising-protocol.html target="_victron_encrypt">here</a>`
|
|
155
144
|
}
|
|
156
145
|
|
|
157
146
|
prepareConfig(config){
|
|
@@ -23,12 +23,7 @@ const VC = require("./Victron/VictronConstants.js")
|
|
|
23
23
|
const BitReader = require("./_BitReader")
|
|
24
24
|
|
|
25
25
|
class VictronACCharger extends VictronSensor{
|
|
26
|
-
|
|
27
|
-
static async identify(device){
|
|
28
|
-
return await this.identifyMode(device, 0x08)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
static ImageFile = "VictronBlueSmartACCharger.jpg"
|
|
32
27
|
initSchema(){
|
|
33
28
|
super.initSchema()
|
|
34
29
|
this.addDefaultParam("id")
|