mirror of https://github.com/espruino/BangleApps
bthrm - Move caching of characteristics out of the lib into the settings app
parent
47af3b482a
commit
4e696ee36e
|
@ -15,8 +15,7 @@
|
||||||
"custom_fallbackTimeout": 10,
|
"custom_fallbackTimeout": 10,
|
||||||
"gracePeriodNotification": 0,
|
"gracePeriodNotification": 0,
|
||||||
"gracePeriodConnect": 0,
|
"gracePeriodConnect": 0,
|
||||||
"gracePeriodService": 0,
|
|
||||||
"gracePeriodRequest": 0,
|
"gracePeriodRequest": 0,
|
||||||
"bonding": false,
|
"bonding": false,
|
||||||
"active": true
|
"active": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,55 +16,20 @@ exports.enable = () => {
|
||||||
|
|
||||||
log("Settings: ", settings);
|
log("Settings: ", settings);
|
||||||
|
|
||||||
if (settings.enabled){
|
if (settings.enabled && settings.cache){
|
||||||
|
|
||||||
var clearCache = function() {
|
log("Start");
|
||||||
return require('Storage').erase("bthrm.cache.json");
|
|
||||||
};
|
|
||||||
|
|
||||||
var getCache = function() {
|
|
||||||
var cache = require('Storage').readJSON("bthrm.cache.json", true) || {};
|
|
||||||
if (settings.btid && settings.btid === cache.id) return cache;
|
|
||||||
clearCache();
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
var addNotificationHandler = function(characteristic) {
|
var addNotificationHandler = function(characteristic) {
|
||||||
log("Setting notification handler"/*supportedCharacteristics[characteristic.uuid].handler*/);
|
log("Setting notification handler"/*supportedCharacteristics[characteristic.uuid].handler*/);
|
||||||
characteristic.on('characteristicvaluechanged', (ev) => supportedCharacteristics[characteristic.uuid].handler(ev.target.value));
|
characteristic.on('characteristicvaluechanged', (ev) => supportedCharacteristics[characteristic.uuid].handler(ev.target.value));
|
||||||
};
|
};
|
||||||
|
|
||||||
var writeCache = function(cache) {
|
|
||||||
var oldCache = getCache();
|
|
||||||
if (oldCache !== cache) {
|
|
||||||
log("Writing cache");
|
|
||||||
require('Storage').writeJSON("bthrm.cache.json", cache);
|
|
||||||
} else {
|
|
||||||
log("No changes, don't write cache");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var characteristicsToCache = function(characteristics) {
|
|
||||||
log("Cache characteristics");
|
|
||||||
var cache = getCache();
|
|
||||||
if (!cache.characteristics) cache.characteristics = {};
|
|
||||||
for (var c of characteristics){
|
|
||||||
//"handle_value":16,"handle_decl":15
|
|
||||||
log("Saving handle " + c.handle_value + " for characteristic: ", c);
|
|
||||||
cache.characteristics[c.uuid] = {
|
|
||||||
"handle": c.handle_value,
|
|
||||||
"uuid": c.uuid,
|
|
||||||
"notify": c.properties.notify,
|
|
||||||
"read": c.properties.read
|
|
||||||
};
|
|
||||||
}
|
|
||||||
writeCache(cache);
|
|
||||||
};
|
|
||||||
|
|
||||||
var characteristicsFromCache = function(device) {
|
var characteristicsFromCache = function(device) {
|
||||||
var service = { device : device }; // fake a BluetoothRemoteGATTService
|
var service = { device : device }; // fake a BluetoothRemoteGATTService
|
||||||
log("Read cached characteristics");
|
log("Read cached characteristics");
|
||||||
var cache = getCache();
|
var cache = settings.cache;
|
||||||
if (!cache.characteristics) return [];
|
if (!cache.characteristics) return [];
|
||||||
var restored = [];
|
var restored = [];
|
||||||
for (var c in cache.characteristics){
|
for (var c in cache.characteristics){
|
||||||
|
@ -84,18 +49,6 @@ exports.enable = () => {
|
||||||
return restored;
|
return restored;
|
||||||
};
|
};
|
||||||
|
|
||||||
log("Start");
|
|
||||||
|
|
||||||
var lastReceivedData={
|
|
||||||
};
|
|
||||||
|
|
||||||
var supportedServices = [
|
|
||||||
"0x180d", // Heart Rate
|
|
||||||
"0x180f", // Battery
|
|
||||||
];
|
|
||||||
|
|
||||||
var bpmTimeout;
|
|
||||||
|
|
||||||
var supportedCharacteristics = {
|
var supportedCharacteristics = {
|
||||||
"0x2a37": {
|
"0x2a37": {
|
||||||
//Heart rate measurement
|
//Heart rate measurement
|
||||||
|
@ -177,6 +130,7 @@ exports.enable = () => {
|
||||||
//Body sensor location
|
//Body sensor location
|
||||||
handler: function(dv){
|
handler: function(dv){
|
||||||
if (!lastReceivedData["0x180d"]) lastReceivedData["0x180d"] = {};
|
if (!lastReceivedData["0x180d"]) lastReceivedData["0x180d"] = {};
|
||||||
|
log("Got location", dv);
|
||||||
lastReceivedData["0x180d"]["0x2a38"] = parseInt(dv.buffer, 10);
|
lastReceivedData["0x180d"]["0x2a38"] = parseInt(dv.buffer, 10);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -189,6 +143,11 @@ exports.enable = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var lastReceivedData={
|
||||||
|
};
|
||||||
|
|
||||||
|
var bpmTimeout;
|
||||||
|
|
||||||
var device;
|
var device;
|
||||||
var gatt;
|
var gatt;
|
||||||
var characteristics = [];
|
var characteristics = [];
|
||||||
|
@ -198,11 +157,6 @@ exports.enable = () => {
|
||||||
var maxRetryTime = 60000;
|
var maxRetryTime = 60000;
|
||||||
var retryTime = initialRetryTime;
|
var retryTime = initialRetryTime;
|
||||||
|
|
||||||
var connectSettings = {
|
|
||||||
minInterval: 7.5,
|
|
||||||
maxInterval: 1500
|
|
||||||
};
|
|
||||||
|
|
||||||
var waitingPromise = function(timeout) {
|
var waitingPromise = function(timeout) {
|
||||||
return new Promise(function(resolve){
|
return new Promise(function(resolve){
|
||||||
log("Start waiting for " + timeout);
|
log("Start waiting for " + timeout);
|
||||||
|
@ -416,22 +370,13 @@ exports.enable = () => {
|
||||||
|
|
||||||
promise = promise.then(()=>{
|
promise = promise.then(()=>{
|
||||||
gatt = device.gatt;
|
gatt = device.gatt;
|
||||||
|
|
||||||
let cache = getCache();
|
|
||||||
if (device.id !== cache.id){
|
|
||||||
log("Device ID changed from " + cache.id + " to " + device.id + ", clearing cache");
|
|
||||||
clearCache();
|
|
||||||
var newCache = getCache();
|
|
||||||
newCache.id = device.id;
|
|
||||||
writeCache(newCache);
|
|
||||||
}
|
|
||||||
return Promise.resolve(gatt);
|
return Promise.resolve(gatt);
|
||||||
});
|
});
|
||||||
|
|
||||||
promise = promise.then((gatt)=>{
|
promise = promise.then((gatt)=>{
|
||||||
if (!gatt.connected){
|
if (!gatt.connected){
|
||||||
log("Connecting...");
|
log("Connecting...");
|
||||||
var connectPromise = gatt.connect(connectSettings).then(function() {
|
var connectPromise = gatt.connect().then(function() {
|
||||||
log("Connected.");
|
log("Connected.");
|
||||||
});
|
});
|
||||||
if (settings.gracePeriodConnect){
|
if (settings.gracePeriodConnect){
|
||||||
|
@ -447,63 +392,20 @@ exports.enable = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (settings.bonding){
|
|
||||||
promise = promise.then(() => {
|
|
||||||
log(JSON.stringify(gatt.getSecurityStatus()));
|
|
||||||
if (gatt.getSecurityStatus()['bonded']) {
|
|
||||||
log("Already bonded");
|
|
||||||
return Promise.resolve();
|
|
||||||
} else {
|
|
||||||
log("Start bonding");
|
|
||||||
return gatt.startBonding()
|
|
||||||
.then(() => log("Security status after bonding" + gatt.getSecurityStatus()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
promise = promise.then(()=>{
|
promise = promise.then(()=>{
|
||||||
if (!characteristics || characteristics.length == 0){
|
if (!characteristics || characteristics.length == 0){
|
||||||
characteristics = characteristicsFromCache(device);
|
characteristics = characteristicsFromCache(device);
|
||||||
}
|
}
|
||||||
});
|
let characteristicsPromise = Promise.resolve();
|
||||||
|
|
||||||
promise = promise.then(()=>{
|
|
||||||
var characteristicsPromise = Promise.resolve();
|
|
||||||
if (characteristics.length == 0){
|
|
||||||
characteristicsPromise = characteristicsPromise.then(()=>{
|
|
||||||
log("Getting services");
|
|
||||||
return gatt.getPrimaryServices();
|
|
||||||
});
|
|
||||||
|
|
||||||
characteristicsPromise = characteristicsPromise.then((services)=>{
|
|
||||||
log("Got services", services);
|
|
||||||
var result = Promise.resolve();
|
|
||||||
for (var service of services){
|
|
||||||
if (!(supportedServices.includes(service.uuid))) continue;
|
|
||||||
log("Supporting service", service.uuid);
|
|
||||||
result = attachServicePromise(result, service);
|
|
||||||
}
|
|
||||||
if (settings.gracePeriodService){
|
|
||||||
log("Add " + settings.gracePeriodService + "ms grace period after services");
|
|
||||||
result = result.then(()=>{
|
|
||||||
log("Wait after services");
|
|
||||||
return waitingPromise(settings.gracePeriodService);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
for (var characteristic of characteristics){
|
for (var characteristic of characteristics){
|
||||||
characteristicsPromise = attachCharacteristicPromise(characteristicsPromise, characteristic, true);
|
characteristicsPromise = attachCharacteristicPromise(characteristicsPromise, characteristic, true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return characteristicsPromise;
|
return characteristicsPromise;
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise.then(()=>{
|
return promise.then(()=>{
|
||||||
log("Connection established, waiting for notifications");
|
log("Connection established, waiting for notifications");
|
||||||
characteristicsToCache(characteristics);
|
|
||||||
clearRetryTimeout(true);
|
clearRetryTimeout(true);
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
characteristics = [];
|
characteristics = [];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
(function(back) {
|
(function(back) {
|
||||||
function writeSettings(key, value) {
|
function writeSettings(key, value) {
|
||||||
var s = require('Storage').readJSON(FILE, true) || {};
|
let s = require('Storage').readJSON(FILE, true) || {};
|
||||||
s[key] = value;
|
s[key] = value;
|
||||||
require('Storage').writeJSON(FILE, s);
|
require('Storage').writeJSON(FILE, s);
|
||||||
readSettings();
|
readSettings();
|
||||||
|
@ -13,10 +13,16 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var FILE="bthrm.json";
|
let FILE="bthrm.json";
|
||||||
var settings;
|
let settings;
|
||||||
readSettings();
|
readSettings();
|
||||||
|
|
||||||
|
let log = ()=>{};
|
||||||
|
if (settings.debuglog)
|
||||||
|
log = print;
|
||||||
|
|
||||||
|
const bthrm = require("bthrm");
|
||||||
|
|
||||||
function applyCustomSettings(){
|
function applyCustomSettings(){
|
||||||
writeSettings("enabled",true);
|
writeSettings("enabled",true);
|
||||||
writeSettings("replace",settings.custom_replace);
|
writeSettings("replace",settings.custom_replace);
|
||||||
|
@ -26,7 +32,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildMainMenu(){
|
function buildMainMenu(){
|
||||||
var mainmenu = {
|
let mainmenu = {
|
||||||
'': { 'title': 'Bluetooth HRM' },
|
'': { 'title': 'Bluetooth HRM' },
|
||||||
'< Back': back,
|
'< Back': back,
|
||||||
'Mode': {
|
'Mode': {
|
||||||
|
@ -63,12 +69,13 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
if (settings.btname || settings.btid){
|
if (settings.btname || settings.btid){
|
||||||
var name = "Clear " + (settings.btname || settings.btid);
|
let name = "Clear " + (settings.btname || settings.btid);
|
||||||
mainmenu[name] = function() {
|
mainmenu[name] = function() {
|
||||||
E.showPrompt("Clear current device?").then((r)=>{
|
E.showPrompt("Clear current device?").then((r)=>{
|
||||||
if (r) {
|
if (r) {
|
||||||
writeSettings("btname",undefined);
|
writeSettings("btname",undefined);
|
||||||
writeSettings("btid",undefined);
|
writeSettings("btid",undefined);
|
||||||
|
writeSettings("cache", undefined);
|
||||||
}
|
}
|
||||||
E.showMenu(buildMainMenu());
|
E.showMenu(buildMainMenu());
|
||||||
});
|
});
|
||||||
|
@ -81,7 +88,7 @@
|
||||||
return mainmenu;
|
return mainmenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
var submenu_debug = {
|
let submenu_debug = {
|
||||||
'' : { title: "Debug"},
|
'' : { title: "Debug"},
|
||||||
'< Back': function() { E.showMenu(buildMainMenu()); },
|
'< Back': function() { E.showMenu(buildMainMenu()); },
|
||||||
'Alert on disconnect': {
|
'Alert on disconnect': {
|
||||||
|
@ -111,11 +118,137 @@
|
||||||
'Grace periods': function() { E.showMenu(submenu_grace); }
|
'Grace periods': function() { E.showMenu(submenu_grace); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let supportedServices = [
|
||||||
|
"0x180d", // Heart Rate
|
||||||
|
"0x180f", // Battery
|
||||||
|
];
|
||||||
|
|
||||||
|
let supportedCharacteristics = [
|
||||||
|
"0x2a37", // Heart Rate
|
||||||
|
"0x2a38", // Body sensor location
|
||||||
|
"0x2a19", // Battery
|
||||||
|
];
|
||||||
|
|
||||||
|
var characteristicsToCache = function(characteristics, deviceId) {
|
||||||
|
log("Cache characteristics");
|
||||||
|
let cache = {
|
||||||
|
id: deviceId
|
||||||
|
};
|
||||||
|
if (!cache.characteristics) cache.characteristics = {};
|
||||||
|
for (var c of characteristics){
|
||||||
|
//"handle_value":16,"handle_decl":15
|
||||||
|
log("Saving handle " + c.handle_value + " for characteristic: ", c.uuid);
|
||||||
|
cache.characteristics[c.uuid] = {
|
||||||
|
"handle": c.handle_value,
|
||||||
|
"uuid": c.uuid,
|
||||||
|
"notify": c.properties.notify,
|
||||||
|
"read": c.properties.read
|
||||||
|
};
|
||||||
|
}
|
||||||
|
writeSettings("cache", cache);
|
||||||
|
};
|
||||||
|
|
||||||
|
let createCharacteristicPromise = function(newCharacteristic) {
|
||||||
|
log("Create characteristic promise", newCharacteristic.uuid);
|
||||||
|
return Promise.resolve().then(()=>log("Handled characteristic", newCharacteristic.uuid));
|
||||||
|
};
|
||||||
|
|
||||||
|
let attachCharacteristicPromise = function(promise, characteristic) {
|
||||||
|
return promise.then(()=>{
|
||||||
|
log("Handling characteristic:", characteristic.uuid);
|
||||||
|
return createCharacteristicPromise(characteristic);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let characteristics;
|
||||||
|
|
||||||
|
let createCharacteristicsPromise = function(newCharacteristics) {
|
||||||
|
log("Create characteristics promise ", newCharacteristics.length);
|
||||||
|
let result = Promise.resolve();
|
||||||
|
for (let c of newCharacteristics){
|
||||||
|
if (!supportedCharacteristics.includes(c.uuid)) continue;
|
||||||
|
log("Supporting characteristic", c.uuid);
|
||||||
|
characteristics.push(c);
|
||||||
|
|
||||||
|
result = attachCharacteristicPromise(result, c);
|
||||||
|
}
|
||||||
|
return result.then(()=>log("Handled characteristics"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let createServicePromise = function(service) {
|
||||||
|
log("Create service promise", service.uuid);
|
||||||
|
let result = Promise.resolve();
|
||||||
|
result = result.then(()=>{
|
||||||
|
log("Handling service", service.uuid);
|
||||||
|
return service.getCharacteristics().then((c)=>createCharacteristicsPromise(c));
|
||||||
|
});
|
||||||
|
return result.then(()=>log("Handled service", service.uuid));
|
||||||
|
};
|
||||||
|
|
||||||
|
let attachServicePromise = function(promise, service) {
|
||||||
|
return promise.then(()=>createServicePromise(service));
|
||||||
|
};
|
||||||
|
|
||||||
|
function cacheDevice(deviceId){
|
||||||
|
let promise;
|
||||||
|
let filters;
|
||||||
|
let gatt;
|
||||||
|
characteristics = [];
|
||||||
|
filters = [{ id: deviceId }];
|
||||||
|
|
||||||
|
log("Requesting device with filters", filters);
|
||||||
|
promise = NRF.requestDevice({ filters: filters, active: settings.active });
|
||||||
|
|
||||||
|
promise = promise.then((d)=>{
|
||||||
|
log("Got device", d);
|
||||||
|
gatt = d.gatt;
|
||||||
|
log("Connecting...");
|
||||||
|
return gatt.connect().then(function() {
|
||||||
|
log("Connected.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (settings.bonding){
|
||||||
|
promise = promise.then(() => {
|
||||||
|
log(JSON.stringify(gatt.getSecurityStatus()));
|
||||||
|
if (gatt.getSecurityStatus().bonded) {
|
||||||
|
log("Already bonded");
|
||||||
|
return Promise.resolve();
|
||||||
|
} else {
|
||||||
|
log("Start bonding");
|
||||||
|
return gatt.startBonding()
|
||||||
|
.then(() => log("Security status after bonding" + gatt.getSecurityStatus()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
promise = promise.then(()=>{
|
||||||
|
log("Getting services");
|
||||||
|
return gatt.getPrimaryServices();
|
||||||
|
});
|
||||||
|
|
||||||
|
promise = promise.then((services)=>{
|
||||||
|
log("Got services", services.length);
|
||||||
|
let result = Promise.resolve();
|
||||||
|
for (let service of services){
|
||||||
|
if (!(supportedServices.includes(service.uuid))) continue;
|
||||||
|
log("Supporting service", service.uuid);
|
||||||
|
result = attachServicePromise(result, service);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise.then(()=>{
|
||||||
|
log("Connection established, saving cache");
|
||||||
|
characteristicsToCache(characteristics, deviceId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function createMenuFromScan(){
|
function createMenuFromScan(){
|
||||||
E.showMenu();
|
E.showMenu();
|
||||||
E.showMessage("Scanning for 4 seconds");
|
E.showMessage("Scanning for 4 seconds");
|
||||||
|
|
||||||
var submenu_scan = {
|
let submenu_scan = {
|
||||||
'< Back': function() { E.showMenu(buildMainMenu()); }
|
'< Back': function() { E.showMenu(buildMainMenu()); }
|
||||||
};
|
};
|
||||||
NRF.findDevices(function(devices) {
|
NRF.findDevices(function(devices) {
|
||||||
|
@ -126,27 +259,47 @@
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
devices.forEach((d) => {
|
devices.forEach((d) => {
|
||||||
print("Found device", d);
|
log("Found device", d);
|
||||||
var shown = (d.name || d.id.substr(0, 17));
|
let shown = (d.name || d.id.substr(0, 17));
|
||||||
submenu_scan[shown] = function () {
|
submenu_scan[shown] = function () {
|
||||||
E.showPrompt("Set " + shown + "?").then((r) => {
|
E.showPrompt("Set " + shown + "?").then((r) => {
|
||||||
if (r) {
|
if (r) {
|
||||||
|
E.showMessage("Connecting...");
|
||||||
|
let count = 0;
|
||||||
|
const successHandler = ()=>{
|
||||||
|
E.showAlert("Success").then(()=>{
|
||||||
writeSettings("btid", d.id);
|
writeSettings("btid", d.id);
|
||||||
// Store the name for displaying later. Will connect by ID
|
// Store the name for displaying later. Will connect by ID
|
||||||
if (d.name) {
|
if (d.name) {
|
||||||
writeSettings("btname", d.name);
|
writeSettings("btname", d.name);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
E.showMenu(buildMainMenu());
|
E.showMenu(buildMainMenu());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const errorHandler = (e)=>{
|
||||||
|
count++;
|
||||||
|
log("ERROR", e);
|
||||||
|
if (count <= 10){
|
||||||
|
E.showMessage("Error during caching, Retry " + count + "/10", e);
|
||||||
|
return cacheDevice(d.id).then(successHandler).catch(errorHandler);
|
||||||
|
} else {
|
||||||
|
E.showAlert("Error during caching", e).then(()=>{
|
||||||
|
E.showMenu(buildMainMenu());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return cacheDevice(d.id).then(successHandler).catch(errorHandler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
E.showMenu(submenu_scan);
|
E.showMenu(submenu_scan);
|
||||||
}, { timeout: 4000, active: true, filters: [{services: [ "180d" ]}]});
|
}, { timeout: 4000, active: true, filters: [{services: [ "180d" ]}]});
|
||||||
}
|
}
|
||||||
|
|
||||||
var submenu_custom = {
|
let submenu_custom = {
|
||||||
'' : { title: "Custom mode"},
|
'' : { title: "Custom mode"},
|
||||||
'< Back': function() { E.showMenu(buildMainMenu()); },
|
'< Back': function() { E.showMenu(buildMainMenu()); },
|
||||||
'Replace HRM': {
|
'Replace HRM': {
|
||||||
|
@ -183,7 +336,7 @@
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var submenu_grace = {
|
let submenu_grace = {
|
||||||
'' : { title: "Grace periods"},
|
'' : { title: "Grace periods"},
|
||||||
'< Back': function() { E.showMenu(submenu_debug); },
|
'< Back': function() { E.showMenu(submenu_debug); },
|
||||||
'Request': {
|
'Request': {
|
||||||
|
@ -215,16 +368,6 @@
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
writeSettings("gracePeriodNotification",v);
|
writeSettings("gracePeriodNotification",v);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
'Service': {
|
|
||||||
value: settings.gracePeriodService,
|
|
||||||
min: 0,
|
|
||||||
max: 3000,
|
|
||||||
step: 100,
|
|
||||||
format: v=>v+"ms",
|
|
||||||
onchange: v => {
|
|
||||||
writeSettings("gracePeriodService",v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue