bt-sensors-plugin-sk 1.1.0 → 1.2.0-beta.0.0.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 +285 -107
- package/README.md +8 -10
- package/definitions.json +941 -0
- package/index.js +419 -317
- package/package.json +52 -6
- package/plugin_defaults.json +57 -0
- package/public/159.js +2 -0
- package/public/159.js.LICENSE.txt +14 -0
- package/public/30.js +8 -0
- package/public/30.js.LICENSE.txt +65 -0
- package/public/540.js +2 -0
- package/public/540.js.LICENSE.txt +8 -0
- package/public/893.js +1 -0
- package/public/main.js +1 -0
- package/public/remoteEntry.js +1 -0
- package/sensor_classes/ATC.js +31 -20
- package/sensor_classes/BlackListedDevice.js +4 -0
- package/sensor_classes/DEVELOPMENT.md +1 -6
- package/sensor_classes/GoveeH510x.js +3 -3
- package/sensor_classes/IBeacon.js +2 -3
- package/sensor_classes/Inkbird.js +4 -4
- package/sensor_classes/JBDBMS.js +3 -3
- package/sensor_classes/KilovaultHLXPlus.js +1 -1
- package/sensor_classes/MopekaTankSensor.js +13 -6
- package/sensor_classes/Renogy/RenogySensor.js +15 -6
- package/sensor_classes/UNKNOWN.js +8 -4
- package/sensor_classes/UltrasonicWindMeter.js +1 -1
- package/sensor_classes/Victron/VictronSensor.js +6 -2
- package/sensor_classes/VictronBatteryMonitor.js +9 -7
- package/sensor_classes/VictronDCEnergyMeter.js +1 -1
- package/sensor_classes/VictronInverter.js +1 -1
- package/sensor_classes/VictronInverterRS.js +1 -1
- package/sensor_classes/VictronOrionXS.js +1 -1
- package/sensor_classes/XiaomiMiBeacon.js +18 -9
- package/spec/electrical.json +688 -0
- package/spec/environment.json +401 -0
- package/spec/sensors.json +39 -0
- package/spec/tanks.json +115 -0
- package/src/components/PluginConfigurationPanel.js +368 -0
- package/src/index.js +0 -0
- package/webpack.config.js +71 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import Form from '@rjsf/core' ;
|
|
2
|
+
import validator from '@rjsf/validator-ajv8';
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import {useEffect, useState} from 'react'
|
|
5
|
+
|
|
6
|
+
import {Button, Grid} from '@material-ui/core';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import { SignalCellular0Bar, SignalCellular1Bar, SignalCellular2Bar, SignalCellular3Bar, SignalCellular4Bar, SignalCellularConnectedNoInternet0Bar } from '@material-ui/icons';
|
|
10
|
+
|
|
11
|
+
const log = (type) => console.log.bind(console, type);
|
|
12
|
+
|
|
13
|
+
import ListGroup from 'react-bootstrap/ListGroup';
|
|
14
|
+
import { ListGroupItem } from 'react-bootstrap';
|
|
15
|
+
|
|
16
|
+
import ProgressBar from 'react-bootstrap/ProgressBar';
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
var _sensorMap
|
|
20
|
+
|
|
21
|
+
export default (props) => {
|
|
22
|
+
|
|
23
|
+
const hideSubmit= {
|
|
24
|
+
'ui:submitButtonOptions': {
|
|
25
|
+
props: {
|
|
26
|
+
disabled: false,
|
|
27
|
+
className: 'btn btn-info',
|
|
28
|
+
},
|
|
29
|
+
norender: true,
|
|
30
|
+
submitText: 'Submit',
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
const [baseSchema, setBaseSchema] = useState({})
|
|
34
|
+
const [baseData, setBaseData] = useState({})
|
|
35
|
+
|
|
36
|
+
const [schema, setSchema] = useState({})
|
|
37
|
+
const [ uiSchema, setUISchema] = useState(hideSubmit )
|
|
38
|
+
const [sensorList, setSensorList] = useState([])
|
|
39
|
+
|
|
40
|
+
const [sensorData, setSensorData] = useState()
|
|
41
|
+
const [sensorMap, setSensorMap ] = useState(new Map() )
|
|
42
|
+
|
|
43
|
+
const [progress, setProgress ] = useState({
|
|
44
|
+
"progress":0, "maxTimeout": 100,
|
|
45
|
+
"deviceCount":0,
|
|
46
|
+
"totalDevices":0})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
const [pluginState, setPluginState ] = useState("unknown")
|
|
50
|
+
const [error, setError ] = useState()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
function sendJSONData(cmd, data){
|
|
54
|
+
|
|
55
|
+
console.log(`sending ${cmd}`)
|
|
56
|
+
console.log(data)
|
|
57
|
+
const headers = new Headers();
|
|
58
|
+
headers.append("Content-Type", "application/json");
|
|
59
|
+
return fetch(`/plugins/bt-sensors-plugin-sk/${cmd}`, {
|
|
60
|
+
credentials: 'include',
|
|
61
|
+
method: 'POST',
|
|
62
|
+
body: JSON.stringify(data),
|
|
63
|
+
headers:headers
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function fetchJSONData(path){
|
|
68
|
+
console.log(`fetching ${path}`)
|
|
69
|
+
|
|
70
|
+
return fetch(`/plugins/bt-sensors-plugin-sk/${path}`, {
|
|
71
|
+
credentials: 'include'
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function getSensorData(){
|
|
77
|
+
|
|
78
|
+
const response = await fetchJSONData("sensors")
|
|
79
|
+
if (response.status!=200){
|
|
80
|
+
throw new Error(`Unable get sensor data: ${response.statusText} (${response.status}) `)
|
|
81
|
+
}
|
|
82
|
+
const json = await response.json()
|
|
83
|
+
console.log(json)
|
|
84
|
+
return json
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function getBaseData(){
|
|
89
|
+
|
|
90
|
+
const response = await fetchJSONData("base")
|
|
91
|
+
if (response.status!=200){
|
|
92
|
+
throw new Error(`Unable get base data: ${response.statusText} (${response.status}) `)
|
|
93
|
+
}
|
|
94
|
+
const json = await response.json()
|
|
95
|
+
console.log(json)
|
|
96
|
+
return json
|
|
97
|
+
}
|
|
98
|
+
async function getProgress(){
|
|
99
|
+
|
|
100
|
+
const response = await fetchJSONData("progress")
|
|
101
|
+
if (response.status!=200){
|
|
102
|
+
throw new Error(`Unable get progres: ${response.statusText} (${response.status}) `)
|
|
103
|
+
}
|
|
104
|
+
const json = await response.json()
|
|
105
|
+
console.log(json)
|
|
106
|
+
return json
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function updateSensorData(data){
|
|
110
|
+
sendJSONData("sendSensorData", data).then((response)=>{
|
|
111
|
+
if (response.status != 200) {
|
|
112
|
+
throw new Error(response.statusText)
|
|
113
|
+
}
|
|
114
|
+
debugger
|
|
115
|
+
sensorMap.get(data.mac_address).config = data
|
|
116
|
+
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function undoChanges(mac) {
|
|
121
|
+
setSensorData( sensorMap.get(mac).config )
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function removeSensorData(mac){
|
|
125
|
+
try{
|
|
126
|
+
|
|
127
|
+
sendJSONData("removeSensorData", {mac_address:mac} ).then((response)=>{
|
|
128
|
+
if (response.status != 200) {
|
|
129
|
+
throw new Error(response.statusText)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
_sensorMap.delete(mac)
|
|
134
|
+
|
|
135
|
+
setSensorMap(new Map(_sensorMap))
|
|
136
|
+
setSchema( {} )
|
|
137
|
+
} catch {(e)=>
|
|
138
|
+
setError( new Error(`Couldn't remove ${mac}: ${e}`))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
function updateBaseData(data){
|
|
145
|
+
sendJSONData("sendBaseData", data).then( (response )=>{
|
|
146
|
+
if (response.status != 200) {
|
|
147
|
+
setError(new Error(`Unable to update base data: ${response.statusText} (${response.status})`))
|
|
148
|
+
} else {
|
|
149
|
+
getProgress().then((json)=>{
|
|
150
|
+
setProgress(json)
|
|
151
|
+
}).catch((e)=>{
|
|
152
|
+
setError(e)
|
|
153
|
+
})
|
|
154
|
+
refreshSensors()
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
function refreshSensors(){
|
|
161
|
+
console.log('refreshing sensor map')
|
|
162
|
+
|
|
163
|
+
getSensorData().then((sensors)=>{
|
|
164
|
+
setSensorMap (new Map(sensors.map((sensor)=>[sensor.info.mac,sensor])));
|
|
165
|
+
})
|
|
166
|
+
.catch((e)=>{
|
|
167
|
+
setError(e)
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
useEffect(()=>{
|
|
173
|
+
|
|
174
|
+
fetchJSONData("sendPluginState").then( async (response)=> {
|
|
175
|
+
const json = await response.json()
|
|
176
|
+
setPluginState(json.state)
|
|
177
|
+
console.log("Setting up eventsource")
|
|
178
|
+
const eventSource = new EventSource("/plugins/bt-sensors-plugin-sk/sse")
|
|
179
|
+
|
|
180
|
+
eventSource.addEventListener("newsensor", (event) => {
|
|
181
|
+
let json = JSON.parse(event.data)
|
|
182
|
+
|
|
183
|
+
if (!_sensorMap.has(json.info.mac)) {
|
|
184
|
+
console.log(`New sensor: ${json.info.mac}`)
|
|
185
|
+
setSensorMap(new Map(_sensorMap.set(json.info.mac, json)))
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
eventSource.addEventListener("sensorchanged", (event) => {
|
|
190
|
+
let json = JSON.parse(event.data)
|
|
191
|
+
|
|
192
|
+
if (_sensorMap.has(json.mac)) {
|
|
193
|
+
let sensor = _sensorMap.get(json.mac)
|
|
194
|
+
Object.assign(sensor.info, json )
|
|
195
|
+
setSensorMap(new Map ( _sensorMap ))
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
eventSource.addEventListener("progress", (event) => {
|
|
199
|
+
const json = JSON.parse(event.data)
|
|
200
|
+
setProgress(json)
|
|
201
|
+
console.log(json)
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
eventSource.addEventListener("pluginstate", (event) => {
|
|
205
|
+
const json = JSON.parse(event.data)
|
|
206
|
+
setPluginState(json.state)
|
|
207
|
+
});
|
|
208
|
+
return () => eventSource.close();
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
.catch( (e) => {
|
|
212
|
+
setError(e)
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
},[])
|
|
216
|
+
|
|
217
|
+
useEffect(()=>{
|
|
218
|
+
if (pluginState=="started"){
|
|
219
|
+
refreshSensors()
|
|
220
|
+
|
|
221
|
+
getBaseData().then((json) => {
|
|
222
|
+
setBaseSchema(json.schema);
|
|
223
|
+
setBaseData(json.data);
|
|
224
|
+
}).catch((e)=>{
|
|
225
|
+
setError(e)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
getProgress().then((json)=>{
|
|
229
|
+
setProgress(json)
|
|
230
|
+
}).catch((e)=>{
|
|
231
|
+
setError(e)
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
} else{
|
|
235
|
+
setSensorMap(new Map())
|
|
236
|
+
setBaseSchema({})
|
|
237
|
+
setBaseData({})
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
},[pluginState])
|
|
241
|
+
|
|
242
|
+
useEffect(()=>{
|
|
243
|
+
console.log(error)
|
|
244
|
+
},[error])
|
|
245
|
+
|
|
246
|
+
function confirmDelete(mac){
|
|
247
|
+
const result = window.confirm(`Delete configuration for ${mac}?`)
|
|
248
|
+
if (result)
|
|
249
|
+
removeSensorData(mac)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function signalStrengthIcon(sensor){
|
|
253
|
+
if (sensor.info.lastContactDelta ==null || sensor.info.lastContactDelta > sensor.config.discoveryTimeout)
|
|
254
|
+
return <SignalCellularConnectedNoInternet0Bar/>
|
|
255
|
+
|
|
256
|
+
if (sensor.info.signalStrength > 80)
|
|
257
|
+
return <SignalCellular4Bar/>
|
|
258
|
+
|
|
259
|
+
if (sensor.info.signalStrength > 60)
|
|
260
|
+
return <SignalCellular3Bar/>
|
|
261
|
+
|
|
262
|
+
if (sensor.info.signalStrength > 40)
|
|
263
|
+
return <SignalCellular2Bar/>
|
|
264
|
+
|
|
265
|
+
if (sensor.info.signalStrength > 20)
|
|
266
|
+
return <SignalCellular1Bar/>
|
|
267
|
+
|
|
268
|
+
return <SignalCellular0Bar/>
|
|
269
|
+
|
|
270
|
+
}
|
|
271
|
+
useEffect(()=>{
|
|
272
|
+
_sensorMap = sensorMap
|
|
273
|
+
|
|
274
|
+
setSensorList(
|
|
275
|
+
|
|
276
|
+
Array.from(sensorMap.entries()).map((entry) => {
|
|
277
|
+
const sensor = sensorMap.get(entry[0]);
|
|
278
|
+
const config= sensor.config
|
|
279
|
+
const hasConfig = Object.keys(config).length>0;
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
return <ListGroupItem action
|
|
283
|
+
onClick={()=>{
|
|
284
|
+
if (sensor){
|
|
285
|
+
config.mac_address=entry[0]
|
|
286
|
+
setSchema(sensor.schema)
|
|
287
|
+
setSensorData(config)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}>
|
|
291
|
+
<div class="d-flex justify-content-between align-items-center" style={hasConfig?{fontWeight:"normal"}:{fontStyle:"italic"}}>
|
|
292
|
+
{`${sensor.info.name} MAC: ${sensor.info.mac} RSSI: ${ifNullNaN(sensor.info.RSSI)}` }
|
|
293
|
+
<div class="d-flex justify-content-between ">
|
|
294
|
+
{
|
|
295
|
+
signalStrengthIcon(sensor)
|
|
296
|
+
}
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
</ListGroupItem>
|
|
300
|
+
})
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
},[sensorMap]
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
function ifNullNaN(value){
|
|
307
|
+
return value==null? NaN : value
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
if (pluginState=="stopped")
|
|
312
|
+
return (<h1 >Enable plugin to see configuration</h1>)
|
|
313
|
+
else
|
|
314
|
+
return(
|
|
315
|
+
<div>
|
|
316
|
+
{error?<h2 style="color: red;">{error.message}</h2>:""}
|
|
317
|
+
<Form
|
|
318
|
+
schema={baseSchema}
|
|
319
|
+
validator={validator}
|
|
320
|
+
onChange={(e) => setBaseData(e.formData)}
|
|
321
|
+
onSubmit={ ({ formData }, e) => {setSensorData(null); updateBaseData(formData) }}
|
|
322
|
+
onError={log('errors')}
|
|
323
|
+
formData={baseData}
|
|
324
|
+
/>
|
|
325
|
+
<p></p>
|
|
326
|
+
<p></p>
|
|
327
|
+
{ (progress.deviceCount<progress.totalDevices)?
|
|
328
|
+
<ProgressBar max={progress.maxTimeout}
|
|
329
|
+
now={progress.progress}
|
|
330
|
+
/>:""
|
|
331
|
+
}
|
|
332
|
+
<h2>{`${sensorMap.size>0?" Bluetooth Devices click to configure":""}`}</h2>
|
|
333
|
+
<p></p>
|
|
334
|
+
<div style={{paddingBottom: 20}} class="d-flex flex-wrap justify-content-start align-items-start">
|
|
335
|
+
<ListGroup style={{ maxHeight: '300px', overflowY: 'auto' }}>
|
|
336
|
+
{sensorList}
|
|
337
|
+
</ListGroup>
|
|
338
|
+
<div style= {{ paddingLeft: 10, paddingTop: 10, display: (Object.keys(schema).length==0)? "none" :"" }} >
|
|
339
|
+
<Form
|
|
340
|
+
schema={schema}
|
|
341
|
+
validator={validator}
|
|
342
|
+
uiSchema={uiSchema}
|
|
343
|
+
onChange={(e) => setSensorData(e.formData)}
|
|
344
|
+
onSubmit={({ formData }, e) => {
|
|
345
|
+
console.log(formData)
|
|
346
|
+
updateSensorData(formData)
|
|
347
|
+
setSchema({})
|
|
348
|
+
alert("Changes saved")
|
|
349
|
+
}}
|
|
350
|
+
onError={log('errors')}
|
|
351
|
+
formData={sensorData}>
|
|
352
|
+
<div>
|
|
353
|
+
<Grid direction="row" style={{spacing:5}}>
|
|
354
|
+
<Button type='submit' color="primary" variant="contained">Save</Button>
|
|
355
|
+
<Button variant="contained" onClick={()=>{undoChanges(sensorData.mac_address)}}>Undo</Button>
|
|
356
|
+
<Button variant="contained" color="secondary" onClick={(e)=>confirmDelete(sensorData.mac_address)}>Delete</Button>
|
|
357
|
+
|
|
358
|
+
</Grid>
|
|
359
|
+
</div>
|
|
360
|
+
</Form>
|
|
361
|
+
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
}
|
package/src/index.js
ADDED
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const { ModuleFederationPlugin } = require('webpack').container;
|
|
5
|
+
const { WatchIgnorePlugin } = require('webpack')
|
|
6
|
+
|
|
7
|
+
require('@signalk/server-admin-ui-dependencies')
|
|
8
|
+
|
|
9
|
+
const packageJson = require('./package')
|
|
10
|
+
|
|
11
|
+
console.log(packageJson.name.replace(/[-@/]/g, '_'))
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
entry: './src/index',
|
|
15
|
+
mode: 'development',
|
|
16
|
+
output: {
|
|
17
|
+
path: path.resolve(__dirname, 'public')
|
|
18
|
+
},
|
|
19
|
+
module: {
|
|
20
|
+
rules: [
|
|
21
|
+
{
|
|
22
|
+
test: /\.js?$/,
|
|
23
|
+
loader: 'babel-loader',
|
|
24
|
+
exclude: /node_modules/,
|
|
25
|
+
options: {
|
|
26
|
+
presets: ['@babel/preset-react'],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
test: /\.jsx?$/,
|
|
31
|
+
loader: 'babel-loader',
|
|
32
|
+
exclude: /node_modules/,
|
|
33
|
+
options: {
|
|
34
|
+
presets: ['@babel/preset-react'],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
test: /\.css$/,
|
|
39
|
+
use: [
|
|
40
|
+
'style-loader',
|
|
41
|
+
'css-loader'
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
test: /\.(png|svg|jpg|gif)$/,
|
|
46
|
+
loader:
|
|
47
|
+
'file-loader',
|
|
48
|
+
options: {
|
|
49
|
+
name: '[path][name].[ext]',
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
plugins: [
|
|
55
|
+
new ModuleFederationPlugin({
|
|
56
|
+
name: 'Bluetooth Sensors for Signalk',
|
|
57
|
+
library: { type: 'var', name: packageJson.name.replace(/[-@/]/g, '_') },
|
|
58
|
+
filename: 'remoteEntry.js',
|
|
59
|
+
exposes: {
|
|
60
|
+
'./PluginConfigurationPanel': './src/components/PluginConfigurationPanel',
|
|
61
|
+
},
|
|
62
|
+
shared: [{ react: { singleton: false, strictVersion:true } }],
|
|
63
|
+
}),
|
|
64
|
+
new WatchIgnorePlugin({
|
|
65
|
+
paths: [path.resolve(__dirname, 'public/')]
|
|
66
|
+
})
|
|
67
|
+
// new HtmlWebpackPlugin({
|
|
68
|
+
// template: './public/index.html',
|
|
69
|
+
// }),
|
|
70
|
+
],
|
|
71
|
+
};
|