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.
Files changed (127) hide show
  1. package/BTSensor.js +74 -38
  2. package/DistanceManager.js +249 -0
  3. package/Mixin.js +19 -0
  4. package/OutOfRangeDevice.js +46 -0
  5. package/README.md +14 -0
  6. package/classLoader.js +7 -2
  7. package/connectUUID.exp +26 -0
  8. package/index.js +49 -3
  9. package/package.json +8 -6
  10. package/public/159.js +1 -1
  11. package/public/540.js +1 -1
  12. package/public/681.js +3 -9
  13. package/public/681.js.LICENSE.txt +2 -0
  14. package/public/764.js +1 -0
  15. package/public/images/ATC.jpeg +0 -0
  16. package/public/images/Aranet4.webp +0 -0
  17. package/public/images/BP108B.webp +0 -0
  18. package/public/images/GoveeH5074.jpg +0 -0
  19. package/public/images/GoveeH5075.webp +0 -0
  20. package/public/images/GoveeH510x.jpg +0 -0
  21. package/public/images/InkbirdTH3.webp +0 -0
  22. package/public/images/JBDBMS.webp +0 -0
  23. package/public/images/Junctek.webp +0 -0
  24. package/public/images/KilovaultHLXPlus.jpg +0 -0
  25. package/public/images/LancolVoltageMeter.webp +0 -0
  26. package/public/images/LiTimeLiFePo4Battery.avif +0 -0
  27. package/public/images/MercurySmartcraft.jpg +0 -0
  28. package/public/images/MopekaTankSensor.jpg +0 -0
  29. package/public/images/RemoranWave3.jpeg +0 -0
  30. package/public/images/RenogyInverter.jpg +0 -0
  31. package/public/images/RenogyRoverClient.jpg +0 -0
  32. package/public/images/RenogySmartLiFePo4Battery.webp +0 -0
  33. package/public/images/RuuviTag.jpg +0 -0
  34. package/public/images/ShellyBLUHT.webp +0 -0
  35. package/public/images/ShellyBLUMotion.webp +0 -0
  36. package/public/images/ShellyBluDoorWindow.webp +0 -0
  37. package/public/images/Skanbatt.jpg +0 -0
  38. package/public/images/SmartBatteryProtect.webp +0 -0
  39. package/public/images/SmartBatterySense.webp +0 -0
  40. package/public/images/SwitchBotMeterPlus.webp +0 -0
  41. package/public/images/SwitchBotTH.webp +0 -0
  42. package/public/images/TopbandBattery.webp +0 -0
  43. package/public/images/Ultrasonic.jpg +0 -0
  44. package/public/images/VictronBlueSmartACCharger.jpg +0 -0
  45. package/public/images/VictronBlueSolarMPPT.jpeg +0 -0
  46. package/public/images/VictronCerboGX.webp +0 -0
  47. package/public/images/VictronInverterRS.webp +0 -0
  48. package/public/images/VictronLynxSmartBMS.webp +0 -0
  49. package/public/images/VictronMultiPlus-II.webp +0 -0
  50. package/public/images/VictronOrionTrIsolated.webp +0 -0
  51. package/public/images/VictronOrionTrNonIsolated.webp +0 -0
  52. package/public/images/VictronPhoenixInverter.webp +0 -0
  53. package/public/images/VictronPhoenixSmart1600.webp +0 -0
  54. package/public/images/VictronSmartBatteryProtect.jpg +0 -0
  55. package/public/images/VictronSmartIP43.webp +0 -0
  56. package/public/images/VictronSmartLithiumBattery.jpg +0 -0
  57. package/public/images/VictronSmartSolarMPPT.webp +0 -0
  58. package/public/images/VictronVEBus.webp +0 -0
  59. package/public/images/iBeacon.jpg +0 -0
  60. package/public/main.js +1 -1
  61. package/public/remoteEntry.js +1 -1
  62. package/readUUID.exp +23 -0
  63. package/sensor_classes/ATC.js +3 -2
  64. package/sensor_classes/Aranet2.js +3 -1
  65. package/sensor_classes/Aranet4.js +1 -2
  66. package/sensor_classes/BankManager.js +1 -1
  67. package/sensor_classes/Beacon/AbstractBeaconMixin.js +85 -0
  68. package/sensor_classes/Beacon/Eddystone.js +77 -0
  69. package/sensor_classes/Beacon/iBeacon.js +58 -0
  70. package/sensor_classes/EctiveBMS.js +270 -0
  71. package/sensor_classes/FeasyComBeacon.js +68 -0
  72. package/sensor_classes/GobiusCTankMeter.js +4 -3
  73. package/sensor_classes/GoveeH5074.js +2 -0
  74. package/sensor_classes/GoveeH5075.js +2 -0
  75. package/sensor_classes/GoveeH510x.js +1 -0
  76. package/sensor_classes/Inkbird.js +1 -0
  77. package/sensor_classes/JBDBMS.js +1 -0
  78. package/sensor_classes/Junctek.js +14 -6
  79. package/sensor_classes/KilovaultHLXPlus.js +1 -0
  80. package/sensor_classes/LancolVoltageMeter.js +2 -0
  81. package/sensor_classes/MercurySmartcraft.js +1 -0
  82. package/sensor_classes/MopekaTankSensor.js +3 -200
  83. package/sensor_classes/RemoranWave3.js +2 -0
  84. package/sensor_classes/Renogy/RenogySensor.js +1 -0
  85. package/sensor_classes/RenogyBattery.js +3 -4
  86. package/sensor_classes/RenogyInverter.js +3 -6
  87. package/sensor_classes/RenogyRoverClient.js +3 -0
  88. package/sensor_classes/RuuviTag.js +6 -3
  89. package/sensor_classes/ShellySBDW002C.js +3 -1
  90. package/sensor_classes/ShellySBHT003C.js +7 -0
  91. package/sensor_classes/ShellySBMO003Z.js +3 -2
  92. package/sensor_classes/ShenzhenLiOnBMS.js +4 -0
  93. package/sensor_classes/SwitchBotMeterPlus.js +1 -1
  94. package/sensor_classes/SwitchBotTH.js +2 -1
  95. package/sensor_classes/UNKNOWN.js +2 -1
  96. package/sensor_classes/UltrasonicWindMeter.js +3 -0
  97. package/sensor_classes/Victron/VictronConstants.js +2 -0
  98. package/sensor_classes/Victron/VictronIdentifier.js +24 -0
  99. package/sensor_classes/Victron/VictronSensor.js +43 -54
  100. package/sensor_classes/VictronACCharger.js +1 -6
  101. package/sensor_classes/VictronBatteryMonitor.js +37 -26
  102. package/sensor_classes/VictronDCDCConverter.js +1 -4
  103. package/sensor_classes/VictronDCEnergyMeter.js +1 -4
  104. package/sensor_classes/VictronGXDevice.js +1 -4
  105. package/sensor_classes/VictronInverter.js +2 -3
  106. package/sensor_classes/VictronInverterRS.js +2 -4
  107. package/sensor_classes/VictronLynxSmartBMS.js +1 -4
  108. package/sensor_classes/VictronOrionXS.js +1 -3
  109. package/sensor_classes/VictronSmartBatteryProtect.js +1 -4
  110. package/sensor_classes/VictronSmartLithium.js +1 -4
  111. package/sensor_classes/VictronSolarCharger.js +1 -3
  112. package/sensor_classes/VictronVEBus.js +1 -4
  113. package/sensor_classes/XiaomiMiBeacon.js +5 -2
  114. package/sensor_classes/iBeaconSensor.js +40 -0
  115. package/src/components/PluginConfigurationPanel.js +53 -17
  116. package/Screenshot 2025-06-12 at 9.33.57/342/200/257AM.png +0 -0
  117. package/bt-sensors-plugin-sk copy.json +0 -170
  118. package/bt-sensors-plugin-sk.json.bak +0 -121
  119. package/diff.txt +0 -2860
  120. package/public/847.js +0 -1
  121. package/sensor_classes/IBeacon.js +0 -45
  122. package/vsl_patch_17_06_25.patch +0 -13
  123. /package/public/images/{Aranet2_HOME_F_900x900_90OVA5J.original.webp → Aranet2.webp} +0 -0
  124. /package/public/images/{Bank Manager All-in-onewc.webp → BankManager.webp} +0 -0
  125. /package/public/images/{Gobius_C.png → GobiusCTankMeter.png} +0 -0
  126. /package/public/images/{Victron-SmartShunt.jpg → VictronSmartShunt.jpg} +0 -0
  127. /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
- this.initMetadata()
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
- initMetadata(){
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
- initMetadata(){
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
- this.mode = md[0]
18
- if (this['_initModeV'+this.mode])
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
- getDescription() {
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
- getDescription(){
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()
@@ -11,6 +11,9 @@ class UltrasonicWindMeter extends BTSensor{
11
11
  else
12
12
  return null
13
13
  }
14
+ static ImageFile="Ultrasonic.jpg"
15
+ static Manufacturer="Calypso Instruments EMEA, S.L."
16
+
14
17
  hasGATT(){
15
18
  return true
16
19
  }
@@ -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
- function sleep(x) {
7
- return new Promise((resolve) => {
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 getModelID(md) {
25
- return md[0x2e1]?.value.readUInt16LE(2)??-1
26
- }
27
- static async identifyMode(device, mode){
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
- var hasDataPacket=false
44
- device.helper._prepare()
25
+ return new Promise((resolve, reject) => {
45
26
  device.helper.on("PropertiesChanged",
46
- (props)=> {
47
- if (Object.hasOwn(props,'ManufacturerData')){
48
- md = props['ManufacturerData'].value
49
- hasDataPacket=md[0x2e1].value[0]==0x10
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
- while (!hasDataPacket) {
53
- await sleep(500)
54
- }
55
- device.helper.removeListeners()
56
- if (md[0x2e1].value[4]==mode) {
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 init(){
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(0x2e1)?.readUInt16LE(2)??-1
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(0x2e1)
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
- getDescription(){
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 `<img src="../bt-sensors-plugin-sk/images/${this.getImage()}" width="200" style="float: left;
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")