bt-sensors-plugin-sk 1.1.1 → 1.2.0-beta.0.0.10

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