bt-sensors-plugin-sk 1.3.4 → 1.3.6

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 CHANGED
@@ -87,7 +87,11 @@ class BTSensor extends EventEmitter {
87
87
  tanks: { name: "tanks", description: "Sensors that measure level in tanks (gas, propane, water etc.) "},
88
88
  beacons: { name: "beacons", description: "iBeacon/Eddystone sensor tags"}
89
89
  }
90
+ static batteryStrengthTag="batteryStrength"
91
+
90
92
  static Domain = this.SensorDomains.unknown
93
+
94
+ _currentValues = {}
91
95
  /**
92
96
  *
93
97
  * @param {module:node-ble/Device} device
@@ -315,7 +319,11 @@ class BTSensor extends EventEmitter {
315
319
  type: "integer", default:30,
316
320
  minimum: 10,
317
321
  maximum: 600 },
318
-
322
+ noContactThreshhold: {title: "If no contact (in seconds), raise warning. Set to 0 to disable",
323
+ type: "integer",
324
+ minimum: 0,
325
+ maximum: 600,
326
+ default: 2*(this?.discoveryTimeout??30) },
319
327
  params:{
320
328
  title:`Device parameters`,
321
329
  type:"object",
@@ -955,6 +963,19 @@ class BTSensor extends EventEmitter {
955
963
  throw new Error("Subclass must implement ::emitGATT function")
956
964
  }
957
965
 
966
+ getCurrentValue(tag){
967
+ return this._currentValues[tag]
968
+ }
969
+
970
+ setCurrentValue(tag,value){
971
+ this._currentValues[tag]=value
972
+ }
973
+
974
+ emit(tag, value){
975
+ super.emit(tag, value)
976
+ this.setCurrentValue(tag,value)
977
+ }
978
+
958
979
  emitData(tag, buffer, ...args){
959
980
  const md = this.getPath(tag)
960
981
  if (md && md.read)
@@ -1048,7 +1069,7 @@ class BTSensor extends EventEmitter {
1048
1069
  this._app.handleMessage(id,
1049
1070
  {
1050
1071
  updates:
1051
- [{ meta: [{path: this.preparePath(path), value: { units: pathMeta?.unit }}]}]
1072
+ [{ meta: [{path: this.preparePath(path), value: { units: pathMeta?.unit, zones:pathMeta?.zones } }]}]
1052
1073
  })
1053
1074
  }
1054
1075
  })
@@ -1062,11 +1083,7 @@ class BTSensor extends EventEmitter {
1062
1083
  if (!(path === undefined)) {
1063
1084
  let preparedPath = this.preparePath(path)
1064
1085
  this.on(tag, (val)=>{
1065
- /* if (pathMeta.notify){
1066
- this._app.notify(tag, val, id )
1067
- } else {*/
1068
- this.updatePath(preparedPath,val, id, source)
1069
- //}
1086
+ this.updatePath(preparedPath,val, id, source)
1070
1087
  })
1071
1088
  }
1072
1089
  })
@@ -1075,7 +1092,10 @@ class BTSensor extends EventEmitter {
1075
1092
  this._app.handleMessage(id, {updates: [ { $source: source, values: [ { path: path, value: val }] } ] })
1076
1093
  }
1077
1094
  elapsedTimeSinceLastContact(){
1078
- return (Date.now()-this?._lastContact??Date.now())/1000
1095
+ if (this.device instanceof OutOfRangeDevice)
1096
+ return Infinity
1097
+ else
1098
+ return (Date.now()-this?._lastContact??Date.now())/1000
1079
1099
  }
1080
1100
 
1081
1101
  prepareConfig(config){
@@ -1116,7 +1136,83 @@ class BTSensor extends EventEmitter {
1116
1136
  return resultString || str; // Return original string if no replacements were made
1117
1137
  }
1118
1138
 
1139
+ notifyUnableToCommunicate(){
1140
+ this._app.handleMessage('bt-sensors-plugin-sk',
1141
+ {
1142
+ updates: [{
1143
+ $source: this.getName(),
1144
+ timestamp: (new Date()).toISOString(),
1145
+ values: [{
1146
+ path: `notifications.sensors.${this.macAndName()}`,
1147
+ value: {
1148
+ state: "warn",
1149
+ message: "Unable to communicate with sensor",
1150
+ method: ["visual", "sound"]
1151
+ }
1152
+ }]
1153
+ }]
1154
+ }
1155
+
1156
+ );
1157
+ }
1119
1158
 
1159
+ clearUnableToCommunicate(){
1160
+ this._app.handleMessage('bt-sensors-plugin-sk',
1161
+ {
1162
+ updates: [{
1163
+ $source: this.getName(),
1164
+ timestamp: (new Date()).toISOString(),
1165
+ values: [{
1166
+ path: `notifications.sensors.${this.macAndName()}`,
1167
+ value: null
1168
+ }]
1169
+ }]
1170
+ }
1171
+
1172
+ );
1173
+ }
1174
+ notifyNoContact(){
1175
+ this._app.handleMessage('bt-sensors-plugin-sk',
1176
+ {
1177
+ updates: [{
1178
+ $source: this.getName(),
1179
+ timestamp: (new Date()).toISOString(),
1180
+ values: [{
1181
+ path: `notifications.sensors.${this.macAndName()}`,
1182
+ value: {
1183
+ state: "warn",
1184
+ message: `No contact with sensor for ${this.elapsedTimeSinceLastContact()} seconds`,
1185
+ method: ["visual", "sound"]
1186
+ }
1187
+ }]
1188
+ }]
1189
+ }
1190
+
1191
+ );
1192
+ }
1193
+ clearNoContact(){
1194
+ this._app.handleMessage('bt-sensors-plugin-sk',
1195
+ {
1196
+ updates: [{
1197
+ $source: this.getName(),
1198
+ timestamp: (new Date()).toISOString(),
1199
+ values: [{
1200
+ path: `notifications.sensors.${this.macAndName()}`,
1201
+ value: {
1202
+ state: "normal",
1203
+ message: `Communication with sensor normal`,
1204
+ method: []
1205
+ }
1206
+ }]
1207
+ }]
1208
+ }
1209
+
1210
+ );
1211
+ }
1212
+
1213
+ getBatteryStrength(){
1214
+ return this.getCurrentValue(this.constructor.batteryStrengthTag)
1215
+ }
1120
1216
  }
1121
1217
 
1122
- module.exports = BTSensor
1218
+ module.exports = BTSensor
package/README.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## WHAT'S NEW
4
4
 
5
+ # Version 1.3.6
6
+
7
+ - New sensor parameter: no contact threshhold.
8
+ - Notification if no contact threshhold exceeded for sensor
9
+ - Smaller install footprint (moved rjsf dependencies to dev)
10
+
11
+ # Version 1.3.5
12
+
13
+ - Battery strength notification for supported sensors
14
+ - Battery strength value/icon displayed on config page
15
+ - MicrotikTag TG-BT5 environment sensor support
5
16
 
6
17
  # Version 1.3.4
7
18
 
@@ -157,6 +168,7 @@ It's pretty easy to write and deploy your own sensor class for any currently uns
157
168
  |[BTHome](https://bthome.io/)| NOTE: Framework for IOT sensor devices. |
158
169
  |[Inkbird](https://inkbird.com/)| TH-2 Temp and Humidity Sensor |
159
170
  |[SensorPush](https://www.sensorpush.com/)| Temperature, Humidity and Atmospheric Pressure sensor|
171
+ |[MicrotikTag](https://mikrotik.com/products/group/iot-products)| TG-BT5 Temperature and Humidity and sensor|
160
172
 
161
173
 
162
174
  ### Tanks
@@ -0,0 +1,14 @@
1
+ {
2
+ "delay":100,
3
+ "data":
4
+ {
5
+ "0x96": [
6
+ "55 aa eb 90 10 e0 ac d0 00 00 28 a0 00 00 5a a0 00 00 42 e0 00 00 78 d0 00 00 50 00 00 00 79 d0 00 00 50 a0 00 00 7a d0 00 00 16 d0 00 00 ba 90 00 00 20 bf 20 00 30 00 00 00 3c 00 00 00 20 bf 20 00 2c 10 00 00 3c 00 00 00 1e 00 00 00 d0 70 00 00 bc 20 00 00 58 20 00 00 bc 20 00 00 58 20 00 00 9c ff ff ff 00 00 00 00 20 30 00 00 bc 20 00 00 40 00 00 00 10 00 00 00 10 00 00 00 10 00 00 00 f0 ba 40 00 50 ","00 00 00 5c d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 20 a1 70 00 90 18 f6 00 18 fe ff ff ff 1f a9 6f e0 00 dd e2 00 7b" ],
7
+ "0x97":[
8
+ "55 aa eb 90 00 3 c4 4a 4b 5f 42 32 41 38 53 32 30 50 00 0 00 0 00 00 00 31 31 2e 58 57 00 00 00 31 31 2e 32 36 31 00 00 68 96 c1 03 18 00 00 00 48 42 31 5f 42 34 00 00 00 00 00 00 00 00 00 00 31 32 33 34 00 00 00 00 00 00 00 00 00 00 00 00 32 33 30 38 31 34 00 00 33 30 32 32 38 34 34 32 36 37 00 30 30 30 30 00 49 6e 70 75 74 20 55 73 65 72 64 61 74 61 00 00 31 32 33 33 32 31 00 00 00 00",
9
+ "00 00 00 00 00 00 49 6e 70 75 74 20 55 73 65 72 64 61 74 61 00 00",
10
+ "7c f8 ff ff 1f 0d 00 00 00 00 00 00 90 0f 00 00 00 00 c0 d8 03 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
11
+ "00 00 00 00 00 00 00 00 00 fe 2f 00 00 00 00 00 00 00 00 00 00 bf"
12
+ ]
13
+ }
14
+ }
package/diff.txt CHANGED
@@ -1209,7 +1209,7 @@ index 0000000..473e8f1
1209
1209
  + })*/
1210
1210
  + }
1211
1211
  + initGATTConnection(){
1212
- + return new Promise((resolve,reject )=>{ this.deviceConnect().then(async ()=>{
1212
+ + return new Promise((resolve,reject )=>{ this.).then(async ()=>{
1213
1213
  + if (!this.gattServer) {
1214
1214
  + this.gattServer = await this.device.gatt()
1215
1215
  + this.battService = await this.gattServer.getPrimaryService("0000fff0-0000-1000-8000-00805f9b34fb")
package/index.js CHANGED
@@ -77,6 +77,9 @@ class MissingSensor {
77
77
  getRSSI(){
78
78
  return NaN
79
79
  }
80
+ getBatteryStrength(){
81
+ return NaN
82
+ }
80
83
  getState(){
81
84
  return "OUT_OF_RANGE"
82
85
  }
@@ -386,6 +389,7 @@ module.exports = function (app) {
386
389
  debugLog: sensor.getDebugLog(),
387
390
  RSSI: sensor.getRSSI(),
388
391
  signalStrength: sensor.getSignalStrength(),
392
+ batteryStrength: sensor.getBatteryStrength(),
389
393
  connected: sensor.isConnected(),
390
394
  lastContactDelta: sensor.elapsedTimeSinceLastContact()
391
395
  }
@@ -457,9 +461,12 @@ module.exports = function (app) {
457
461
  sensor.on("debug", ()=>{
458
462
  updateSensor(sensor)
459
463
  })
464
+ sensor.on(sensor.constructor.batteryStrengthTag,()=>{
465
+ updateSensor(sensor)
466
+ })
460
467
  sensor._lastRSSI=-1*Infinity
461
468
  sensor.on("RSSI",(()=>{
462
- if (Date.now()-sensor._lastRSSI > 30000) { //only update RSSI on client every 30 seconds
469
+ if (Date.now()-sensor._lastRSSI > 10000) { //only update RSSI on client every 10 seconds
463
470
 
464
471
  sensor._lastRSSI=Date.now()
465
472
 
@@ -502,8 +509,10 @@ module.exports = function (app) {
502
509
  device.once("deviceFound",async (device)=>{
503
510
  s.device=device
504
511
  s.listen()
505
- if (config.active)
512
+ if (config.active) {
513
+ s.clearUnableToCommunicate()
506
514
  await s.activate(config, plugin)
515
+ }
507
516
  else {
508
517
  s.unsetError()
509
518
  s.setState("DORMANT")
@@ -516,15 +525,18 @@ module.exports = function (app) {
516
525
  }
517
526
  if (startNumber == starts ) {
518
527
  const errorTxt = `Unable to communicate with device ${deviceNameAndAddress(config)} Reason: ${e?.message??e}`
519
- if(s)
520
- {
521
- s.setError(errorTxt)
522
- } else {
523
- if (config.active)
528
+
529
+ if (config.active) {
530
+ if(s) {
531
+ s.setError(errorTxt)
532
+ s.notifyUnableToCommunicate()
533
+ }
534
+ else
524
535
  plugin.setError(errorTxt)
525
536
  }
537
+ plugin.debug(errorTxt)
526
538
  plugin.debug(e)
527
-
539
+
528
540
  reject( e?.message??e )
529
541
  }
530
542
  })})
@@ -776,6 +788,14 @@ module.exports = function (app) {
776
788
  if (lc > dt) {
777
789
  updateSensor(sensor)
778
790
  }
791
+ if (sensor.noContactThreshhold && lc>sensor.noContactThreshold){
792
+ if (sensor.isActive())
793
+ sensor.notifyNoContact()
794
+ }
795
+ else{
796
+ if (sensor.isActive())
797
+ sensor.clearNoContact()
798
+ }
779
799
  })
780
800
  if (sensorMap.size && options.inactivityTimeout && lastContactDelta > options.inactivityTimeout)
781
801
  {
package/package.json CHANGED
@@ -1,13 +1,10 @@
1
1
  {
2
2
  "name": "bt-sensors-plugin-sk",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
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": {
7
- "@rjsf/bootstrap-4": "^5.24.12",
8
- "@rjsf/core": "^5.24.11",
9
- "@rjsf/utils": "^5.24.11",
10
- "@rjsf/validator-ajv8": "^5.24.11",
7
+
11
8
  "better-sse": "^0.14.1",
12
9
  "buffer": "^6.0.3",
13
10
  "@jellybrick/dbus-next": "^0.10.3",
@@ -18,6 +15,10 @@
18
15
  "lru-cache": "^11.1.0"
19
16
  },
20
17
  "devDependencies": {
18
+ "@rjsf/bootstrap-4": "^5.24.12",
19
+ "@rjsf/core": "^5.24.11",
20
+ "@rjsf/utils": "^5.24.11",
21
+ "@rjsf/validator-ajv8": "^5.24.11",
21
22
  "@babel/core": "^7.11.6",
22
23
  "@babel/preset-react": "^7.10.4",
23
24
  "@fortawesome/fontawesome-free": "^5.15.1",
@@ -26,10 +27,10 @@
26
27
  "@fortawesome/react-fontawesome": "^0.2.2",
27
28
  "@material-ui/core": "^4.12.4",
28
29
  "@material-ui/icons": "^4.11.3",
30
+ "react-battery-gauge": "^1.0.7",
29
31
  "babel-loader": "^8.1.0",
30
32
  "chai": "^4.2.0",
31
33
  "css-loader": "^5.0.0",
32
- "d3": "^6.2.0",
33
34
  "file-loader": "^6.1.1",
34
35
  "font-awesome": "^4.7.0",
35
36
  "mocha": "^8.0.1",
@@ -167,6 +167,19 @@
167
167
  "title": "Battery Strength",
168
168
  "unit":"ratio",
169
169
  "default":"sensors.{macAndName}.battery.strength",
170
+ "zones":[
171
+ { "upper": 0.5,
172
+ "lower": 0.3,
173
+ "state": "alert",
174
+ "message": "Battery low - change soon"
175
+ },
176
+ {
177
+ "upper": 0.3,
178
+ "lower": 0,
179
+ "state": "warn",
180
+ "message": "Battery very low - change now"
181
+ }
182
+ ],
170
183
  "description":"Sensor battery strength"
171
184
  },
172
185
 
@@ -180,4 +193,4 @@
180
193
 
181
194
  }
182
195
 
183
- }
196
+ }
package/public/143.js ADDED
@@ -0,0 +1 @@
1
+ (self.webpackChunkbt_sensors_plugin_sk=self.webpackChunkbt_sensors_plugin_sk||[]).push([[143],{9337:()=>{},62995:(e,t,n)=>{"use strict";n.r(t),n.d(t,{BTConfig:()=>$,default:()=>D});var a=n(73490),s=n(74810),r=n(40334),o=n(86528),i=n.n(o),c=n(11363),l=n(97493),u=n(27606),m=n(82096),d=n(55877),f=n(3768),g=n(71431),h=n(39676),p=n(47041),E=n(86038),y=n(95027),w=n(26067),S=n(43540),v=n(38250),b=n(31008),A=n(20455),C=n(23399);const k=e=>console.log.bind(console,e);function $(e){const t=(0,m.A)((e=>({root:{"& > *":{margin:e.spacing(1)}}}))),[n,$]=(0,o.useState)({}),[D,_]=(0,o.useState)({}),[x,T]=(0,o.useState)({}),[N,O]=(0,o.useState)({"ui:options":{label:!1},paths:{enableMarkdownInDescription:!0},title:{"ui:widget":"hidden"}}),[j,M]=(0,o.useState)(),[U,R]=(0,o.useState)(!1),[B,J]=(0,o.useState)(!0),[L,I]=(0,o.useState)(new Map),[P,W]=(0,o.useState)({progress:0,maxTimeout:100,deviceCount:0,totalDevices:0}),[z,K]=(0,o.useState)("unknown"),[F,G]=(0,o.useState)(),[H,Y]=(0,o.useState)(!1),[q,Q]=(0,o.useState)(""),V=t();function X(e,t){const n=new Headers;return n.append("Content-Type","application/json"),fetch(`/plugins/bt-sensors-plugin-sk/${e}`,{credentials:"include",method:"POST",body:JSON.stringify(t),headers:n})}async function Z(e,t={}){let n;try{const a=Object.keys(t).length?"?"+new URLSearchParams(t).toString():"";n=await fetch(`/plugins/bt-sensors-plugin-sk/${e}${a}`,{credentials:"include",method:"GET"})}catch(e){n={status:500,statusText:e.toString()}}return n}function ee(e){return Object.keys(e.configCopy).length>0}function te(e){const t=ee(e);return i().createElement(A.A,{action:!0,onClick:()=>{e.config.mac_address=e.info.mac,T(e.schema),M(e.config)}},i().createElement("div",{class:"d-flex justify-content-between"},i().createElement("div",{class:"d-flex",style:t?{fontWeight:"normal"}:{fontStyle:"italic"}},`${e._changesMade?"*":""}${e.info.name} MAC: ${e.info.mac} RSSI: ${n=e.info.RSSI,null==n?NaN:n} ${function(e){const t=e.info?.batteryStrength;return t?`BATT: ${100*t}%`:""}(e)}`,function(e){const t=e.info.batteryStrength;return void 0===t?"":i().createElement(w.A,{size:22,orientation:"vertical",customization:{batteryBody:{fill:"silver",strokeColor:"silver",strokeWidth:2},batteryCap:{fill:"silver",strokeColor:"silver",cornerRadius:3,strokeWidth:0,capToBodyRatio:.4},readingText:{lightContrastColor:"purple",darkContrastColor:"yellow",lowBatteryColor:"red",fontFamily:"Arial",fontSize:16}},value:100*t})}(e)),i().createElement("div",{class:"d-flex "},`${e.info.state} ${e.info.error?" (ERROR)":""}`,function(e){return e.info.connected?i().createElement(d.A,null):null==e.info.lastContactDelta||e.info.lastContactDelta>e.config.discoveryTimeout?i().createElement(f.A,null):e.info.signalStrength>80?i().createElement(g.A,null):e.info.signalStrength>60?i().createElement(h.A,null):e.info.signalStrength>40?i().createElement(p.A,null):e.info.signalStrength>20?i().createElement(E.A,null):i().createElement(y.A,null)}(e))));var n}function ne(e){window.open(e,"_blank","noreferrer")}return(0,o.useEffect)((()=>{let e=null;return Z("getPluginState").then((async t=>{if(404==t.status)throw K("unknown"),new Error("unable to get plugin state");const n=await t.json();e=new EventSource("/plugins/bt-sensors-plugin-sk/sse",{withCredentials:!0}),e.addEventListener("newsensor",(e=>{!function(e){let t=JSON.parse(e.data);console.log(`New sensor: ${t.info.mac}`),I((e=>(e.set(t.info.mac,t),new Map(e))))}(e)})),e.addEventListener("sensorchanged",(e=>{!function(e){console.log("sensorchanged");const t=JSON.parse(e.data);console.log(t),I((e=>{const n=e.get(t.mac);return n&&Object.assign(n.info,t),new Map(e)}))}(e)})),e.addEventListener("progress",(e=>{const t=JSON.parse(e.data);W(t)})),e.addEventListener("pluginstate",(e=>{const t=JSON.parse(e.data);K(t.state)})),K(n.state),(async()=>{const e=await async function(){const e=await Z("getSensors");if(200!=e.status)throw new Error(`Unable get sensor data: ${e.statusText} (${e.status}) `);return await e.json()}();I(new Map(e.map((e=>[e.info.mac,e]))))})()})).catch((e=>{G(e.message)})),()=>{console.log("Closing connection to SSE"),e.close()}}),[]),(0,o.useEffect)((()=>{if(!U)return;if(!j||!L)return;const e=L.get(j.mac_address);e&&x&&j&&Object.hasOwn(j,"params")&&"UNKNOWN"==e.info.class&&j.params.sensorClass&&"UNKNOWN"!=j.params.sensorClass&&(J(!1),Q(`Please wait. Fetching schema for ${j.params.sensorClass}...`),async function(e,t){const n=await Z("getSensorInfo",{mac_address:e,class:t});if(200!=n.status)throw new Error(`Unable get sensor info: ${n.statusText} (${n.status}) `);return await n.json()}(j.mac_address,j.params.sensorClass).then((e=>{T(e.schema)})).catch((e=>{alert(e.message)})).finally((()=>{Q(""),J(!0),R(!1)})))}),[U]),(0,o.useEffect)((()=>{Y(""!=q)}),[q]),(0,o.useEffect)((()=>{"started"==z?(async function(){const e=await Z("getBaseData");if(200!=e.status)throw new Error(`Unable to get base data: ${e.statusText} (${e.status}) `);const t=await e.json();return t.schema.htmlDescription=i().createElement("div",null,(0,r.Ay)(t.schema.htmlDescription),i().createElement("p",null)),t}().then((e=>{$(e.schema),_(e.data)})).catch((e=>{G(e.message)})),async function(){const e=await Z("getProgress");if(200!=e.status)throw new Error(`Unable to get progress: ${e.statusText} (${e.status}) `);return await e.json()}().then((e=>{W(e)})).catch((e=>{G(e.message)}))):($({}),_({}))}),[z]),"stopped"==z||"unknown"==z?i().createElement("h3",null,"Enable plugin to see configuration"):i().createElement("div",null,i().createElement(c.A,{anchorOrigin:{horizontal:"center",vertical:"bottom"},onClose:()=>Y(!1),open:H,message:q,key:"snackbar"}),i().createElement("div",{className:V.root},i().createElement(l.A,{variant:"contained",onClick:()=>{ne("https://github.com/naugehyde/bt-sensors-plugin-sk/tree/1.2.0-beta#configuration")}},"Documentation"),i().createElement(l.A,{variant:"contained",onClick:()=>{ne("https://github.com/naugehyde/bt-sensors-plugin-sk/issues/new/choose")}},"Report Issue"),i().createElement(l.A,{variant:"contained",onClick:()=>{ne("https://discord.com/channels/1170433917761892493/1295425963466952725")}},"Discord Thread"),i().createElement("p",null),i().createElement("p",null)),i().createElement("hr",{style:{width:"100%",height:"1px",color:"gray","background-color":"gray","text-align":"left","margin-left":0}}),F?i().createElement("h2",{style:{color:"red"}},F):"",i().createElement(a.Ay,{schema:n,validator:s.Ay,uiSchema:{"ui:field":"LayoutGridField","ui:layoutGrid":{"ui:row":[{"ui:row":{className:"row",children:[{"ui:columns":{className:"col-xs-4",children:["adapter","transport","duplicateData","discoveryTimeout","discoveryInterval"]}}]}}]}},onChange:e=>_(e.formData),onSubmit:({formData:e},t)=>{var n;n=e,I(new Map),X("updateBaseData",n).then((e=>{200!=e.status&&G(`Unable to update base data: ${e.statusText} (${e.status})`)})),T({})},onError:k("errors"),formData:D}),i().createElement("hr",{style:{width:"100%",height:"1px",color:"gray","background-color":"gray","text-align":"left","margin-left":0}}),i().createElement("p",null),i().createElement("p",null),P.deviceCount<P.totalDevices?i().createElement(C.A,{max:P.maxTimeout,now:P.progress}):"",i().createElement("p",null),i().createElement(v.A,{defaultActiveKey:"_configured",id:"domain-tabs",className:"mb-3"},function(){const e=[...new Set([...L.entries()].map((e=>e[1].info.domain)))].sort(),t=Array.from(L.entries()).filter((e=>ee(e[1])));let n={};return n._configured=0==t.length?"Select a device from its domain tab (Electrical etc.) and configure it.":t.map((e=>te(L.get(e[0])))),e.forEach((e=>{var t;n[e]=(t=e,Array.from(L.entries()).filter((e=>e[1].info.domain===t))).map((e=>te(L.get(e[0]))))})),Object.keys(n).map((e=>function(e,t){let n=e.slice("_"===e.charAt(0)?1:0);return i().createElement(b.A,{eventKey:e,title:`${n.charAt(0).toUpperCase()}${n.slice(1)}${"string"==typeof t?"":" ("+t.length+")"}`},i().createElement(S.A,{style:{maxHeight:"300px",overflowY:"auto"}},t))}(e,n[e])))}()),i().createElement("div",{style:{paddingLeft:10,paddingTop:10,display:0==Object.keys(x).length?"none":""}},i().createElement(u.A,{container:!0,direction:"column",style:{spacing:5}},i().createElement(u.A,{item:!0},i().createElement("h2",null,x?.title),i().createElement("p",null)),i().createElement(u.A,{item:!0},(0,r.Ay)(x?.htmlDescription))),i().createElement("fieldset",{disabled:!B},i().createElement(a.Ay,{schema:x,validator:s.Ay,uiSchema:N,onChange:(e,t)=>{const n=L.get(e.formData.mac_address);n&&(n._changesMade=!0,n.config=e.formData,M(e.formData)),"root_params_sensorClass"==t&&R(!0)},onSubmit:({formData:e},t)=>{var n;X("updateSensorData",n=e).then((e=>{if(200!=e.status)throw new Error(e.statusText);I((e=>(e.delete(n.mac_address),new Map(e)))),T({})})),alert("Changes saved")},onError:k("errors"),formData:j},i().createElement("div",{className:V.root},i().createElement(l.A,{type:"submit",color:"primary",variant:"contained"},"Save"),i().createElement(l.A,{variant:"contained",onClick:()=>{var e;e=j.mac_address,L.get(e)._changesMade=!1,L.get(e).config=JSON.parse(JSON.stringify(L.get(e).configCopy)),M(L.get(e).config)}},"Undo"),i().createElement(l.A,{variant:"contained",color:"secondary",onClick:e=>function(e){const t=L.get(e);(!ee(t)||window.confirm(`Delete configuration for ${t.info.name}?`))&&function(e){try{X("removeSensorData",{mac_address:e}).then((e=>{if(200!=e.status)throw new Error(e.statusText)})),I((t=>(t.delete(e),new Map(t)))),T({})}catch{}}(e)}(j.mac_address)},"Delete"))))))}const D=$}}]);