bt-sensors-plugin-sk 1.3.0 → 1.3.2
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 +4 -0
- package/README.md +12 -1
- package/index.js +1 -1
- package/package.json +1 -1
- package/public/images/SensorPush.webp +0 -0
- package/sensor_classes/GobiusCTankMeter.js +1 -1
- package/sensor_classes/JBDBMS.js +31 -5
- package/sensor_classes/SensorPush.js +123 -0
- package/sensor_classes/ShellySBHT003C.js +2 -2
- package/sensor_classes/ShellySBMO003Z.js +3 -17
- package/sensor_classes/ShenzhenLiOnBMS.js +23 -6
- package/sensor_classes/Victron/VictronConstants.js +699 -103
- package/sensor_classes/Victron/VictronSensor.js +27 -2
- package/sensor_classes/VictronBatteryMonitor.js +5 -7
- package/sensor_classes/VictronDCDCConverter.js +1 -1
- package/sensor_classes/VictronDCEnergyMeter.js +2 -3
- package/sensor_classes/VictronInverter.js +2 -3
- package/sensor_classes/VictronLynxSmartBMS.js +1 -1
- package/sensor_classes/VictronOrionXS.js +3 -1
- package/sensor_classes/VictronSmartBatteryProtect.js +9 -2
- package/testGATT.js +24 -0
package/BTSensor.js
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# Bluetooth Sensors for [Signal K](http://www.signalk.org)
|
|
2
2
|
|
|
3
3
|
## WHAT'S NEW
|
|
4
|
+
# Version 1.3.2
|
|
5
|
+
|
|
6
|
+
- Victron Alarm Reason improvements
|
|
7
|
+
- VictronOrionXS offReason text implementation
|
|
8
|
+
- Shelly Blu H&T description changes
|
|
9
|
+
|
|
10
|
+
# Version 1.3.1
|
|
11
|
+
|
|
12
|
+
- JBD Protection status
|
|
13
|
+
- SensorPush devices (untested)
|
|
4
14
|
|
|
5
15
|
# Version 1.3.0
|
|
6
16
|
|
|
@@ -112,7 +122,7 @@ It's pretty easy to write and deploy your own sensor class for any currently uns
|
|
|
112
122
|
|[Jikong](https://jikongbms.com/)| https://jikongbms.com/product/ |
|
|
113
123
|
|[Junctek](https://www.junteks.com)|[Junctek BMS](https://www.junteks.com/pages/product/index) |
|
|
114
124
|
|[Remoran](https://remoran.eu)| [Remoran Wave.3](https://remoran.eu/wave.html)|
|
|
115
|
-
|[AC DC Systems](https://marinedcac.com) | [Bank Manager] hybrid (Pb and Li) charger
|
|
125
|
+
|[AC DC Systems](https://marinedcac.com) | [Bank Manager](https://marinedcac.com/pages/bankmanager) hybrid (Pb and Li) charger|
|
|
116
126
|
|[Ective](https://ective.de/)| Also Topband(?), Skanbatt and others |
|
|
117
127
|
|
|
118
128
|
### Environmental
|
|
@@ -128,6 +138,7 @@ It's pretty easy to write and deploy your own sensor class for any currently uns
|
|
|
128
138
|
|[Govee](http://www.govee.com)| Govee H50xx and H510x Temperature and humidity sensors |
|
|
129
139
|
|[BTHome](https://bthome.io/)| NOTE: Framework for IOT sensor devices. |
|
|
130
140
|
|[Inkbird](https://inkbird.com/)| TH-2 Temp and Humidity Sensor |
|
|
141
|
+
|[SensorPush](https://www.sensorpush.com/)| Temperature, Humidity and Atmospheric Pressure sensor|
|
|
131
142
|
|
|
132
143
|
|
|
133
144
|
### Tanks
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bt-sensors-plugin-sk",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Bluetooth Sensors for Signalk - see https://www.npmjs.com/package/bt-sensors-plugin-sk#supported-sensors for a list of supported sensors",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
Binary file
|
package/sensor_classes/JBDBMS.js
CHANGED
|
@@ -62,15 +62,41 @@ class JBDBMS extends BTSensor {
|
|
|
62
62
|
.read=(buffer)=>{return buffer.readUInt16BE(12)}
|
|
63
63
|
|
|
64
64
|
this.addMetadatum('protectionStatus', '', 'Protection Status',
|
|
65
|
-
(buffer)=>{
|
|
65
|
+
(buffer)=>{
|
|
66
|
+
const bits = buffer.readUInt16BE(20).toString(2)
|
|
67
|
+
return {
|
|
68
|
+
singleCellOvervolt: bits[0]=='1',
|
|
69
|
+
singleCellUndervolt: bits[1]=='1',
|
|
70
|
+
packOvervolt: (bits[2]=='1'),
|
|
71
|
+
packUndervolt: bits[3]=='1',
|
|
72
|
+
chargeOvertemp:bits[4]=='1',
|
|
73
|
+
chargeUndertemp:bits[5]=='1',
|
|
74
|
+
dischargeOvertemp:bits[6]=='1',
|
|
75
|
+
dischargeUndertemp:bits[7]=='1',
|
|
76
|
+
chargeOvercurrent:bits[8]=='1',
|
|
77
|
+
dischargeOvercurrent:bits[9]=='1',
|
|
78
|
+
shortCircut:bits[10]=='1',
|
|
79
|
+
frontEndDetectionICError:bits[11]=='1',
|
|
80
|
+
softwareLockMOS:bits[12]=='1',
|
|
81
|
+
}
|
|
82
|
+
})
|
|
66
83
|
.default="electrical.batteries.{batteryID}.protectionStatus"
|
|
67
84
|
|
|
68
85
|
this.addDefaultPath('SOC','electrical.batteries.capacity.stateOfCharge')
|
|
69
86
|
.read=(buffer)=>{return buffer.readUInt8(23)/100}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
this.addMetadatum('FET', '', 'FET On/Off Status',
|
|
90
|
+
(buffer)=>{return buffer.readUInt8(24) !=0 } )
|
|
91
|
+
.default="electrical.batteries.{batteryID}.FETStatus"
|
|
92
|
+
|
|
93
|
+
this.addMetadatum('FETCharging', '', 'FET Status Charging',
|
|
94
|
+
(buffer)=>{return (buffer.readUInt8(24) & 0x1) !=0 })
|
|
95
|
+
.default="electrical.batteries.{batteryID}.FETStatus.charging"
|
|
96
|
+
|
|
97
|
+
this.addMetadatum('FETDischarging', '', 'FET Status Discharging',
|
|
98
|
+
(buffer)=>{return (buffer.readUInt8(24) & 0x2) !=0 })
|
|
99
|
+
.default="electrical.batteries.{batteryID}.FETStatus.discharging"
|
|
74
100
|
|
|
75
101
|
await this.deviceConnect()
|
|
76
102
|
const gattServer = await this.device.gatt()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const BTSensor = require("../BTSensor");
|
|
2
|
+
class SensorPush extends BTSensor{
|
|
3
|
+
static Domain = BTSensor.SensorDomains.environmental
|
|
4
|
+
|
|
5
|
+
static async identify(device){
|
|
6
|
+
|
|
7
|
+
const name = await this.getDeviceProp(device,"Name")
|
|
8
|
+
const regex= /^SensorPush\s(HT|HTP)\s[0-9a-fA-F]{4}$/
|
|
9
|
+
if (name && name.match(regex))
|
|
10
|
+
return this
|
|
11
|
+
else
|
|
12
|
+
return null
|
|
13
|
+
}
|
|
14
|
+
static ImageFile="SensorPush.webp"
|
|
15
|
+
static Manufacturer="Cousins & Sears - Creative Technologists - Brooklyn, NY USA"
|
|
16
|
+
static ServiceUUID = "EF090000-11D6-42BA-93B8-9DD7EC090AB0"
|
|
17
|
+
static Characteristics =
|
|
18
|
+
{
|
|
19
|
+
tx: "EF090003-11D6-42BA-93B8-9DD7EC090AA9",
|
|
20
|
+
adv: "EF090005-11D6-42BA-93B8-9DD7EC090AA9",
|
|
21
|
+
batt: "EF090007-11D6-42BA-93B8-9DD7EC090AA9",
|
|
22
|
+
LED: "EF09000C-11D6-42BA-93B8-9DD7EC090AA9",
|
|
23
|
+
temp: "EF090080-11D6-42BA-93B8-9DD7EC090AA9",
|
|
24
|
+
hum: "EF090081-11D6-42BA-93B8-9DD7EC090AA9",
|
|
25
|
+
bar: "EF090082-11D6-42BA-93B8-9DD7EC090AA9"
|
|
26
|
+
}
|
|
27
|
+
pollFreq=30
|
|
28
|
+
hasGATT(){
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
31
|
+
usingGATT(){
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async emitGATT(){
|
|
37
|
+
this.characteristics.temp.writeValue(0x01000000).then(async ()=>{
|
|
38
|
+
this.emitData("temp", await this.characteristics.temp.readValue())
|
|
39
|
+
this.emitData("hum", await this.characteristics.temp.readValue())
|
|
40
|
+
})
|
|
41
|
+
this.characteristics.bar.writeValue(0x01000000).then(async ()=>{
|
|
42
|
+
this.emitData("bar", await this.characteristics.bar.readValue())
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
initSchema(){
|
|
47
|
+
super.initSchema()
|
|
48
|
+
this.getGATTParams()["useGATT"].default=true
|
|
49
|
+
|
|
50
|
+
this.addDefaultParam("zone")
|
|
51
|
+
|
|
52
|
+
this.addParameter(
|
|
53
|
+
"tx",
|
|
54
|
+
{
|
|
55
|
+
title:'transmission rate in db',
|
|
56
|
+
type: 'number',
|
|
57
|
+
enum: [-21, -18, -15, -12, -9, -6, -3, 0, 1, 2, 3, 4, 5],
|
|
58
|
+
default: -3,
|
|
59
|
+
isRequired: true
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
this.addParameter(
|
|
63
|
+
"adv",
|
|
64
|
+
{
|
|
65
|
+
title:'advertising interval in ms',
|
|
66
|
+
type: 'number',
|
|
67
|
+
default: 1285,
|
|
68
|
+
minimum: Math.round((32*625)/1000),
|
|
69
|
+
maximum: Math.round((32767*625)/1000),
|
|
70
|
+
isRequired: true
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
this.addParameter(
|
|
75
|
+
"LED",
|
|
76
|
+
{
|
|
77
|
+
type: 'number',
|
|
78
|
+
title:'LED flashes per second (0=off, 1-127, 128=once every second',
|
|
79
|
+
default: 0,
|
|
80
|
+
minimum: 0,
|
|
81
|
+
maximum: 128,
|
|
82
|
+
isRequired: true
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
this.addDefaultPath("batt","sensors.batteryVoltage")
|
|
87
|
+
.read=(buffer)=>{ return buffer.readUInt16LE()/100}
|
|
88
|
+
|
|
89
|
+
this.addDefaultPath("temp","environment.temperature")
|
|
90
|
+
.read=(buffer)=>{ return buffer.readInt32E()/100}
|
|
91
|
+
|
|
92
|
+
this.addDefaultPath("humidity","environment.humidity")
|
|
93
|
+
.read=(buffer)=>{ return buffer.readInt32E()/10000}
|
|
94
|
+
|
|
95
|
+
this.addDefaultPath("pressure","environment.pressure")
|
|
96
|
+
.read=(buffer)=>{ return buffer.readInt32E()/100}
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async initGATTConnection(isReconnecting){
|
|
101
|
+
await super.initGATTConnection(isReconnecting)
|
|
102
|
+
const gattServer = await this.getGATTServer()
|
|
103
|
+
const service = await gattServer.getPrimaryService(this.constructor.ServiceUUID)
|
|
104
|
+
this.characteristics={}
|
|
105
|
+
for (const c in this.constructor.Characteristics) {
|
|
106
|
+
this.characteristics[c] = await service.getCharacteristic(this.constructor.Characteristics[c])
|
|
107
|
+
}
|
|
108
|
+
if (this.tx) this.characteristics.tx.writeValueWithoutResponse(this.tx)
|
|
109
|
+
if (this.LED) this.characteristics.LED.writeValueWithoutResponse(this.LED)
|
|
110
|
+
if (this.adv) this.characteristics.tx.writeValueWithoutResponse(Math.round((this.adv/625)*1000))
|
|
111
|
+
}
|
|
112
|
+
async initGATTNotifications() {
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async deactivateGATT(){
|
|
117
|
+
for (const c in this.characteristics) {
|
|
118
|
+
await this.stopGATTNotifications(this.characteristics[c])
|
|
119
|
+
}
|
|
120
|
+
await super.deactivateGATT()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
module.exports=SensorPush
|
|
@@ -20,10 +20,10 @@ class ShellySBHT003C extends AbstractBTHomeSensor {
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
getTextDescription(){
|
|
23
|
-
return `NOTE: Device must be paired with SignalK server machine
|
|
23
|
+
return `NOTE: Device must be paired with SignalK server machine or you won't see updates (see: <a href=https://shelly-api-docs.shelly.cloud/docs-ble/common#pairing target="_blank">https://shelly-api-docs.shelly.cloud/docs-ble/common#pairing</a> ). For more information about the sensor go here: <a href=https://us.shelly.com/blogs/documentation/shelly-blu-h-t target="_blank">Shelly Blu H&T</a>.<br><br>Alternatively you can edit your /etc/bluetooth/main.conf file and set "TemporaryTimeout = 90" in the [General] section, then restart the bluetooth service.`
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
initSchema() {
|
|
26
|
+
initSchema() {
|
|
27
27
|
super.initSchema()
|
|
28
28
|
this.addDefaultParam("zone")
|
|
29
29
|
|
|
@@ -20,11 +20,11 @@ class ShellySBMO003Z extends AbstractBTHomeSensor {
|
|
|
20
20
|
* @type {string}
|
|
21
21
|
*/
|
|
22
22
|
static LOCAL_NAME="Shelly BLU Motion";
|
|
23
|
-
static ImageFile="ShellyBLUMotion.webp"
|
|
23
|
+
static ImageFile="ShellyBLUMotion.webp"
|
|
24
24
|
|
|
25
25
|
getTextDescription(){
|
|
26
|
-
return
|
|
27
|
-
}
|
|
26
|
+
return `${!this.isPaired()?"NOTE: Device must be paired with SignalK server machine to operate properly (see: <a href=https://shelly-api-docs.shelly.cloud/docs-ble/common#pairing target=\"_blank\">https://shelly-api-docs.shelly.cloud/docs-ble/common#pairing</a> ":"Device is paired."}).<br><br>For more information about the sensor click here: <a href=https://us.shelly.com/products/shelly-blu-motion target="_blank">Shelly Blu Motion</a>.`
|
|
27
|
+
}
|
|
28
28
|
|
|
29
29
|
initSchema() {
|
|
30
30
|
super.initSchema()
|
|
@@ -62,20 +62,6 @@ class ShellySBMO003Z extends AbstractBTHomeSensor {
|
|
|
62
62
|
.default="sensors.{macAndName}.button"
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
/*
|
|
66
|
-
this.addMetadatum(
|
|
67
|
-
"packetID",
|
|
68
|
-
null,
|
|
69
|
-
"packetID from sensor",
|
|
70
|
-
this.parsePacketID.bind(this)
|
|
71
|
-
)
|
|
72
|
-
.default="sensors.{macAndName}.packetID"
|
|
73
|
-
*/
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
getState() {
|
|
77
|
-
|
|
78
|
-
return super.getState()
|
|
79
65
|
}
|
|
80
66
|
}
|
|
81
67
|
|
|
@@ -18,6 +18,14 @@ const ProtectionStatus = {
|
|
|
18
18
|
0x4000: "Short Circuit Protection"
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const BatteryState = {
|
|
22
|
+
0: "Discharging/Idle",
|
|
23
|
+
1: "Charging",
|
|
24
|
+
2: "Discharging",
|
|
25
|
+
4: "Charge Disabled"
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
class ShenzhenLiONBMS extends BTSensor{
|
|
22
30
|
static Domain = BTSensor.SensorDomains.electrical
|
|
23
31
|
static ImageFile = "LiTimeLiFePo4Battery.avif"
|
|
@@ -91,6 +99,9 @@ class ShenzhenLiONBMS extends BTSensor{
|
|
|
91
99
|
this.addMetadatum(`cell${cellNum+1}Voltage`,'V', `cell #${cellNum+1} voltage`,
|
|
92
100
|
(buff)=>{return buff.readInt16LE(16+(cellNum*2)) /1000})
|
|
93
101
|
.default=`electrical.batteries.{batteryID}.cells.${cellNum+1}.voltage`
|
|
102
|
+
|
|
103
|
+
this.addMetadatum(`cell${cellNum+1}Balancing`,'', `cell #${cellNum+1} balance state (true=balancing)`)
|
|
104
|
+
.default=`electrical.batteries.{batteryID}.cells.${cellNum+1}.balancing`
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
this.addDefaultPath('current','electrical.batteries.current')
|
|
@@ -119,19 +130,15 @@ class ShenzhenLiONBMS extends BTSensor{
|
|
|
119
130
|
.default="electrical.batteries.{batteryID}.balanceMemoryActive"
|
|
120
131
|
|
|
121
132
|
this.addMetadatum('protectionState','', 'protection state',
|
|
122
|
-
(buff)=>{return buff.
|
|
133
|
+
(buff)=>{return ProtectionStatus[buff.readUInt16LE(76)]??"Normal"})
|
|
123
134
|
.default="electrical.batteries.{batteryID}.protectionState"
|
|
124
135
|
|
|
125
136
|
this.addMetadatum('failureState','', 'failure state',
|
|
126
137
|
(buff)=>{return buff.slice(80,84).reverse().join("").slice(-3)})
|
|
127
138
|
.default="electrical.batteries.{batteryID}.failureState"
|
|
128
|
-
|
|
129
|
-
this.addMetadatum('balanceState','', '1 = cell at offset is balancing',
|
|
130
|
-
(buff)=>{return buff.slice(84,88).reverse().join("")})
|
|
131
|
-
.default="electrical.batteries.{batteryID}.balanceState"
|
|
132
139
|
|
|
133
140
|
this.addMetadatum('batteryState','', 'charge disabled = "0004", charging = "0001" (when charging active app will show estimated time untill fully charged), discharging/idle: "0000", unkown = "0002"',
|
|
134
|
-
(buff)=>{return buff.
|
|
141
|
+
(buff)=>{return (BatteryState[buff.readUInt16LE(88)])??"Unknown"})
|
|
135
142
|
.default="electrical.batteries.{batteryID}.batteryState"
|
|
136
143
|
|
|
137
144
|
this.addMetadatum('dischargeCount','', 'discharge count',
|
|
@@ -148,6 +155,16 @@ class ShenzhenLiONBMS extends BTSensor{
|
|
|
148
155
|
this.getJSONSchema().properties.params.required=["batteryID", "numberOfCells" ]
|
|
149
156
|
}
|
|
150
157
|
|
|
158
|
+
emitValuesFrom(buffer){
|
|
159
|
+
super.emitValuesFrom(buffer)
|
|
160
|
+
const balanceState= buffer.slice(84,88).reverse().join("")
|
|
161
|
+
|
|
162
|
+
for(let cellNum=0; cellNum < this?.numberOfCells??4; cellNum++) {
|
|
163
|
+
this.emit(`cell${cellNum+1}Balancing`, balanceState[cellNum]==='1')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
151
168
|
async initGATTConnection(isReconnecting){
|
|
152
169
|
|
|
153
170
|
await super.initGATTConnection(isReconnecting)
|