mirror of
https://github.com/thegecko/web-bluetooth-dfu.git
synced 2025-12-13 20:48:15 +08:00
Switched to using eslint
This commit is contained in:
18
.eslintrc
Normal file
18
.eslintrc
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"globals": {
|
||||
"define": true
|
||||
},
|
||||
rules: {
|
||||
"semi": ["error"],
|
||||
"indent": ["error", 4, { "SwitchCase": 1 }],
|
||||
"no-irregular-whitespace": ["error"],
|
||||
"linebreak-style": ["warn", "unix"],
|
||||
"no-undef": ["warn"],
|
||||
"no-unused-vars": ["warn"]
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,4 @@ dependencies:
|
||||
|
||||
test:
|
||||
override:
|
||||
- gulp
|
||||
- npm run gulp
|
||||
|
||||
4
dist/dfu.js
vendored
4
dist/dfu.js
vendored
@@ -230,7 +230,7 @@
|
||||
log("connected to device");
|
||||
currentServer = gattServer;
|
||||
// This delay is needed because BlueZ needs time to update it's cache.
|
||||
return new Promise(function(resolve, reject) {
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(resolve, 2000);
|
||||
});
|
||||
})
|
||||
@@ -257,7 +257,7 @@
|
||||
versionChar = characteristic;
|
||||
complete();
|
||||
})
|
||||
.catch(function(error) {
|
||||
.catch(function() {
|
||||
log("info: no version characteristic found");
|
||||
complete();
|
||||
});
|
||||
|
||||
44
dist/secure-dfu.js
vendored
44
dist/secure-dfu.js
vendored
@@ -102,13 +102,13 @@
|
||||
}
|
||||
|
||||
function createListenerFn(eventTypes) {
|
||||
return function(type, callback, capture) {
|
||||
return function(type, callback) {
|
||||
if (eventTypes.indexOf(type) < 0) return;
|
||||
if (!this.events[type]) this.events[type] = [];
|
||||
this.events[type].push(callback);
|
||||
};
|
||||
}
|
||||
function removeEventListener(type, callback, capture) {
|
||||
function removeEventListener(type, callback) {
|
||||
if (!this.events[type]) return;
|
||||
let i = this.events[type].indexOf(callback);
|
||||
if (i >= 0) this.events[type].splice(i, 1);
|
||||
@@ -136,7 +136,7 @@
|
||||
totalBytes: 0,
|
||||
currentBytes: bytes
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.requestDevice = function(hiddenDfu, filters) {
|
||||
if (!hiddenDfu && !filters) {
|
||||
@@ -194,14 +194,14 @@
|
||||
})
|
||||
.then(() => {
|
||||
this.log("sent dfu mode");
|
||||
return new Promise((resolve, reject) => {
|
||||
device.addEventListener("gattserverdisconnected", event => {
|
||||
return new Promise((resolve) => {
|
||||
device.addEventListener("gattserverdisconnected", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.update = function(device, init, firmware) {
|
||||
if (!device) throw new Error("Device not specified");
|
||||
@@ -219,17 +219,17 @@
|
||||
})
|
||||
.then(() => {
|
||||
this.log("complete, disconnecting...");
|
||||
return new Promise((resolve, reject) => {
|
||||
device.addEventListener("gattserverdisconnected", event => {
|
||||
return new Promise((resolve) => {
|
||||
device.addEventListener("gattserverdisconnected", () => {
|
||||
this.log("disconnected");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.connect = function(device) {
|
||||
device.addEventListener("gattserverdisconnected", event => {
|
||||
device.addEventListener("gattserverdisconnected", () => {
|
||||
this.controlChar = null;
|
||||
this.packetChar = null;
|
||||
});
|
||||
@@ -271,7 +271,7 @@
|
||||
.then(server => {
|
||||
this.log("connected to gatt server");
|
||||
return server.getPrimaryService(SERVICE_UUID)
|
||||
.catch(error => {
|
||||
.catch(() => {
|
||||
throw new Error("Unable to find DFU service");
|
||||
});
|
||||
})
|
||||
@@ -279,7 +279,7 @@
|
||||
this.log("found DFU service");
|
||||
return service.getCharacteristics();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.handleNotification = function(event) {
|
||||
let view = event.target.value;
|
||||
@@ -330,19 +330,19 @@
|
||||
|
||||
characteristic.writeValue(value);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.sendControl = function(operation, buffer) {
|
||||
return this.sendOperation(this.controlChar, operation, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.transferInit = function(buffer) {
|
||||
return this.transfer(buffer, "init", OPERATIONS.SELECT_COMMAND, OPERATIONS.CREATE_COMMAND);
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.transferFirmware = function(buffer) {
|
||||
return this.transfer(buffer, "firmware", OPERATIONS.SELECT_DATA, OPERATIONS.CREATE_DATA);
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.transfer = function(buffer, type, selectType, createType) {
|
||||
return this.sendControl(selectType)
|
||||
@@ -364,12 +364,12 @@
|
||||
totalBytes: buffer.byteLength,
|
||||
currentBytes: bytes
|
||||
});
|
||||
}
|
||||
};
|
||||
this.progress(0);
|
||||
|
||||
return this.transferObject(buffer, createType, maxSize, offset);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.transferObject = function(buffer, createType, maxSize, offset) {
|
||||
let start = offset - offset % maxSize;
|
||||
@@ -379,7 +379,7 @@
|
||||
view.setUint32(0, end - start, LITTLE_ENDIAN);
|
||||
|
||||
return this.sendControl(createType, view.buffer)
|
||||
.then(response => {
|
||||
.then(() => {
|
||||
let data = buffer.slice(start, end);
|
||||
return this.transferData(data, start);
|
||||
})
|
||||
@@ -406,7 +406,7 @@
|
||||
this.log("transfer complete");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.transferData = function(data, offset, start) {
|
||||
start = start || 0;
|
||||
@@ -421,7 +421,7 @@
|
||||
return this.transferData(data, offset, end);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.checkCrc = function(buffer, crc) {
|
||||
if (!this.crc32) {
|
||||
@@ -430,7 +430,7 @@
|
||||
}
|
||||
|
||||
return crc === this.crc32(new Uint8Array(buffer));
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.addEventListener = createListenerFn([ "log", "progress" ]);
|
||||
secureDfu.prototype.removeEventListener = removeEventListener;
|
||||
|
||||
265
examples/backup.html
Normal file
265
examples/backup.html
Normal file
@@ -0,0 +1,265 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web Bluetooth Secure DFU</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Raleway:400,600" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Raleway', sans-serif;
|
||||
color: #d7ecfb;
|
||||
background-color: #072b44;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
font-weight: 400;
|
||||
}
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
#drop {
|
||||
position: relative;
|
||||
margin: 40px auto;
|
||||
max-width: 680px;
|
||||
background-color: rgba(255, 255, 255, 0.10);
|
||||
padding: 120px 0px 100px;
|
||||
outline: 2px dashed #072b44;
|
||||
outline-offset: -10px;
|
||||
}
|
||||
#drop.hover {
|
||||
outline-offset: -10px;
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
#icon {
|
||||
width: 100%;
|
||||
fill: #d7ecfb;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
#file {
|
||||
width: 0.1px;
|
||||
height: 0.1px;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
#label {
|
||||
cursor: pointer;
|
||||
}
|
||||
#label:hover strong {
|
||||
color: #8bb5ba;
|
||||
}
|
||||
#update {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
visibility: hidden;
|
||||
}
|
||||
#button {
|
||||
font-size: 12px;
|
||||
color: inherit;
|
||||
margin: 20px auto;
|
||||
border: 0px;
|
||||
background-color: #1b679c;
|
||||
height: 30px;
|
||||
padding: 0 30px;
|
||||
border-radius: 4px;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
#button:hover {
|
||||
background: #2674ab;
|
||||
}
|
||||
#status {
|
||||
margin: 20px auto;
|
||||
border: 1px solid #d7ecfb;
|
||||
width: 400px;
|
||||
height: 24px;
|
||||
visibility: hidden;
|
||||
}
|
||||
#bar {
|
||||
background: #2674ab;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
}
|
||||
#transfer {
|
||||
width: 100%;
|
||||
line-height: 24px;
|
||||
margin-top: -24px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Web Bluetooth Secure Device Firmware Update</h1>
|
||||
<div id="drop">
|
||||
<svg id="icon" xmlns="http://www.w3.org/2000/svg" width="50" height="43" viewBox="0 0 50 43">
|
||||
<path d="M48.4 26.5c-.9 0-1.7.7-1.7 1.7v11.6h-43.3v-11.6c0-.9-.7-1.7-1.7-1.7s-1.7.7-1.7 1.7v13.2c0 .9.7 1.7 1.7 1.7h46.7c.9 0 1.7-.7 1.7-1.7v-13.2c0-1-.7-1.7-1.7-1.7zm-24.5 6.1c.3.3.8.5 1.2.5.4 0 .9-.2 1.2-.5l10-11.6c.7-.7.7-1.7 0-2.4s-1.7-.7-2.4 0l-7.1 8.3v-25.3c0-.9-.7-1.7-1.7-1.7s-1.7.7-1.7 1.7v25.3l-7.1-8.3c-.7-.7-1.7-.7-2.4 0s-.7 1.7 0 2.4l10 11.6z"/>
|
||||
</svg>
|
||||
|
||||
<input id="file" type="file" />
|
||||
<label id="label" for="file">
|
||||
<strong>Choose a firmware package</strong>
|
||||
<span>or drag it here</span>
|
||||
</label>
|
||||
|
||||
<div id="update">
|
||||
<button id="button">Update</button>
|
||||
</div>
|
||||
|
||||
<div id="status">
|
||||
<div id="bar"></div>
|
||||
<div id="transfer"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crc-32/1.0.2/crc32.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
|
||||
<script src="/dist/secure-dfu.js"></script>
|
||||
|
||||
<script>
|
||||
let dropEl = document.getElementById("drop");
|
||||
let fileEl = document.getElementById("file");
|
||||
let updateEl = document.getElementById("update");
|
||||
let labelEl = document.getElementById("label");
|
||||
let statusEl = document.getElementById("status");
|
||||
let transferEl = document.getElementById("transfer");
|
||||
let barEl = document.getElementById("bar");
|
||||
|
||||
let package = null;
|
||||
let manifest = null;
|
||||
|
||||
function updateStatus(state) {
|
||||
labelEl.textContent = state;
|
||||
}
|
||||
|
||||
function updateTransfer(state) {
|
||||
if (!state) {
|
||||
statusEl.style.visibility = "hidden";
|
||||
return;
|
||||
}
|
||||
updateEl.style.visibility = "hidden";
|
||||
statusEl.style.visibility = "visible";
|
||||
barEl.style.width = state.currentBytes / state.totalBytes * 100 + '%';
|
||||
transferEl.textContent = `${state.currentBytes}/${state.totalBytes} bytes written`;
|
||||
}
|
||||
|
||||
function updatePackage(file) {
|
||||
if (!file) return;
|
||||
|
||||
JSZip.loadAsync(file)
|
||||
.then(zipFile => {
|
||||
try {
|
||||
package = zipFile;
|
||||
return zipFile.file("manifest.json").async("string");
|
||||
} catch(e) {
|
||||
throw new Error("Unable to find manifest, is this a proper DFU package?");
|
||||
}
|
||||
})
|
||||
.then(content => {
|
||||
manifest = JSON.parse(content).manifest;
|
||||
updateStatus(`Package: ${file.name}`);
|
||||
updateEl.style.visibility = "visible";
|
||||
})
|
||||
.catch(error => {
|
||||
updateEl.style.visibility = "hidden";
|
||||
statusEl.style.visibility = "hidden";
|
||||
updateStatus(error);
|
||||
});
|
||||
}
|
||||
|
||||
function transfer() {
|
||||
if (!package) return;
|
||||
updateStatus(`Selecting device...`);
|
||||
updateTransfer();
|
||||
let device = null;
|
||||
|
||||
const dfu = new SecureDfu(CRC32.buf);
|
||||
dfu.addEventListener("log", event => {
|
||||
console.log(event.message);
|
||||
});
|
||||
dfu.addEventListener("progress", event => {
|
||||
updateTransfer(event);
|
||||
});
|
||||
|
||||
dfu.requestDevice()
|
||||
.then(selectedDevice => {
|
||||
device = selectedDevice;
|
||||
for (type of ["softdevice", "bootloader", "softdevice_bootloader"]) {
|
||||
if (manifest[type]) {
|
||||
return transferImage(device, dfu, manifest[type])
|
||||
.then(() => {
|
||||
if (manifest.application) {
|
||||
return new Promise((resolve, reject) => {
|
||||
device.addEventListener("gattserverdisconnected", event => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (manifest.application) {
|
||||
return transferImage(device, dfu, manifest.application);
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
updateStatus("Update complete!");
|
||||
updateTransfer();
|
||||
})
|
||||
.catch(error => {
|
||||
statusEl.style.visibility = "hidden";
|
||||
updateStatus(error);
|
||||
});
|
||||
}
|
||||
|
||||
function transferImage(device, dfu, manifest) {
|
||||
return dfu.connect(device)
|
||||
.then(() => {
|
||||
return package.file(manifest.dat_file).async("arraybuffer");
|
||||
})
|
||||
.then(data => {
|
||||
updateStatus(`Transferring init: ${manifest.dat_file}...`);
|
||||
return dfu.transferInit(data);
|
||||
})
|
||||
.then(() => {
|
||||
return package.file(manifest.bin_file).async("arraybuffer");
|
||||
})
|
||||
.then(data => {
|
||||
updateStatus(`Transferring firmware: ${manifest.bin_file}...`);
|
||||
return dfu.transferFirmware(data);
|
||||
});
|
||||
}
|
||||
|
||||
fileEl.addEventListener("change", event => {
|
||||
updatePackage(event.target.files[0]);
|
||||
});
|
||||
|
||||
dropEl.addEventListener("drop", event => {
|
||||
updatePackage(event.dataTransfer.files[0]);
|
||||
});
|
||||
|
||||
updateEl.addEventListener("click", transfer);
|
||||
|
||||
["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave", "drop"].forEach(eventName => {
|
||||
dropEl.addEventListener(eventName, event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
});
|
||||
|
||||
["dragover", "dragenter"].forEach(eventName => {
|
||||
dropEl.addEventListener(eventName, event => {
|
||||
dropEl.classList.add("hover");
|
||||
});
|
||||
});
|
||||
|
||||
["dragleave", "dragend", "drop"].forEach(eventName => {
|
||||
dropEl.addEventListener(eventName, event => {
|
||||
dropEl.classList.remove("hover");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -17,7 +17,7 @@ function logError(error) {
|
||||
}
|
||||
|
||||
function getFileName() {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
if (process.argv[2]) {
|
||||
return resolve(process.argv[2]);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ function downloadFile(url) {
|
||||
}
|
||||
|
||||
function loadFile(fileName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
var file = fs.readFileSync(fileName);
|
||||
resolve(new Uint8Array(file).buffer);
|
||||
});
|
||||
@@ -97,6 +97,7 @@ function updateFirmware(dfu, package, manifest, device, type) {
|
||||
}
|
||||
|
||||
function update() {
|
||||
var device = null;
|
||||
var dfu = null;
|
||||
var package = null;
|
||||
var manifest = null;
|
||||
@@ -144,7 +145,7 @@ function update() {
|
||||
console.log("DFU mode set");
|
||||
return bluetooth.requestDevice({
|
||||
filters: [{ services: [secureDfu.SERVICE_UUID] }],
|
||||
deviceFound: device => {
|
||||
deviceFound: () => {
|
||||
// Select first device found with correct service
|
||||
return true;
|
||||
}
|
||||
@@ -153,7 +154,7 @@ function update() {
|
||||
.then(selectedDevice => {
|
||||
device = selectedDevice;
|
||||
|
||||
for (type of ["softdevice", "bootloader", "softdevice_bootloader"]) {
|
||||
for (var type of ["softdevice", "bootloader", "softdevice_bootloader"]) {
|
||||
if (manifest[type]) {
|
||||
return updateFirmware(dfu, package, manifest[type], device, type);
|
||||
}
|
||||
|
||||
18
gulpfile.js
18
gulpfile.js
@@ -1,10 +1,14 @@
|
||||
var gulp = require('gulp');
|
||||
var jshint = require('gulp-jshint');
|
||||
var gulp = require("gulp");
|
||||
var eslint = require("gulp-eslint");
|
||||
|
||||
gulp.task('lint', function() {
|
||||
return gulp.src(['dist/*.js'])
|
||||
.pipe(jshint())
|
||||
.pipe(jshint.reporter('default'));
|
||||
gulp.task("lint", function() {
|
||||
return gulp.src([
|
||||
"dist/*.js",
|
||||
"examples/*.js"
|
||||
])
|
||||
.pipe(eslint(".eslintrc"))
|
||||
.pipe(eslint.format())
|
||||
.pipe(eslint.failOnError());
|
||||
});
|
||||
|
||||
gulp.task('default', ['lint']);
|
||||
gulp.task("default", ["lint"]);
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
"web-bluetooth"
|
||||
],
|
||||
"scripts": {
|
||||
"example": "node examples/secure_dfu_node.js"
|
||||
"example": "node examples/secure_dfu_node.js",
|
||||
"gulp": "gulp"
|
||||
},
|
||||
"dependencies": {
|
||||
"bleat": "^0.1.3"
|
||||
@@ -29,7 +30,7 @@
|
||||
"devDependencies": {
|
||||
"crc-32": "^1.0.2",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-jshint": "^1.9.0",
|
||||
"gulp-eslint": "^3.0.1",
|
||||
"jszip": "^3.1.3",
|
||||
"progress": "^2.0.0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user