Compare commits
78 Commits
fb8ff229c9
...
1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
f85c89e2c4
|
|||
|
6447692d4c
|
|||
|
855eb07d89
|
|||
|
8e840c0966
|
|||
|
5ab862d703
|
|||
|
7c6f0e8a3e
|
|||
|
bda9dddd4f
|
|||
|
95709e0285
|
|||
|
4c4b896978
|
|||
|
c20c0b02cf
|
|||
|
46bd6f7161
|
|||
|
eeb90f630d
|
|||
|
bccbd61ad5
|
|||
|
2017847b9e
|
|||
|
b471d0e20f
|
|||
|
a7d5880057
|
|||
|
e5ce96adcf
|
|||
|
27403e0aed
|
|||
|
faf4e377d7
|
|||
|
ed4890818f
|
|||
|
0f61fd26c5
|
|||
|
d1927bc24e
|
|||
|
1b7719ac3d
|
|||
|
ebcfb38e41
|
|||
|
ad7402f04b
|
|||
|
e8e9547a20
|
|||
|
852fcf67f4
|
|||
|
e798f997d3
|
|||
|
d2f9809f52
|
|||
|
3e7c26e3ba
|
|||
|
e5912cd94e
|
|||
|
20c203ffa4
|
|||
|
ad464e1c0d
|
|||
|
5009b0c8d1
|
|||
|
b13787b704
|
|||
|
b155baed03
|
|||
|
41a2e30aca
|
|||
|
266044215d
|
|||
|
418df318ee
|
|||
|
896cccd7ff
|
|||
|
66cbb31dde
|
|||
|
90b8e08946
|
|||
|
4900cf6acd
|
|||
|
3e07911cac
|
|||
|
8c3dd4c2e3
|
|||
|
3bae392285
|
|||
|
b978ffa313
|
|||
|
5762bcb549
|
|||
|
836e0f70aa
|
|||
|
b0813ed082
|
|||
|
6ffc5c1e8a
|
|||
|
fdcf232f8e
|
|||
|
c65ec8dcd2
|
|||
|
3e7c9f358e
|
|||
|
6f77c15088
|
|||
|
403f01d52f
|
|||
|
0125bae4e5
|
|||
|
0a37ee00bf
|
|||
|
03038eaa00
|
|||
|
76a6d725f5
|
|||
|
1452d6eaad
|
|||
|
b800542afa
|
|||
|
678d7e89d2
|
|||
|
387f75a4d3
|
|||
|
eac14571e2
|
|||
|
2a942d8fc9
|
|||
|
e3484644d1
|
|||
|
9ca320907e
|
|||
|
572eae3306
|
|||
|
c169ccc5a8
|
|||
|
7345d5addf
|
|||
|
46e15763c8
|
|||
|
7a7f8d8ca4
|
|||
|
7893dce549
|
|||
|
0bcba44b1d
|
|||
|
420927595d
|
|||
|
c18f6d3ffe
|
|||
|
95ecb16123
|
@@ -1,3 +1,7 @@
|
|||||||
# UpdaterWebLibrary
|
# Updater Web Library
|
||||||
|
## If you are seeing this from the npm repository
|
||||||
|
you can view the source [here](https://git.emaker.limited/MicrocontrollerCD/UpdaterWebLibrary)
|
||||||
|
|
||||||
|
## Info
|
||||||
|
|
||||||
A library for the updater to be used by the user through an app or website
|
A library for the updater to be used by the user through an app or website
|
||||||
258
dist/index.cjs.js
vendored
258
dist/index.cjs.js
vendored
@@ -1,15 +1,267 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var parseXml = require('@rgrove/parse-xml');
|
||||||
|
|
||||||
|
/// <reference types="cordova-plugin-ble-central" />
|
||||||
class Updater {
|
class Updater {
|
||||||
archiveURL;
|
archiveURL;
|
||||||
feedType;
|
feedType;
|
||||||
constructor(archiveURL = "/", feedType = "rss") {
|
bleObject;
|
||||||
|
bleDeviceId;
|
||||||
|
_updaterServiceUUID = "71a4438e-fd52-4b15-b3d2-ec0e3e561900";
|
||||||
|
_updaterVersionCharactersiticUUID = "71a4438e-fd52-4b15-b3d2-ec0e3e561910";
|
||||||
|
_updaterCommandCharacterisitcUUID = "71a4438e-fd52-4b15-b3d2-ec0e3e561920";
|
||||||
|
_updateFileCharacteristicUUID = "71a4438e-fd52-4b15-b3d2-ec0e3e561930";
|
||||||
|
file;
|
||||||
|
_fileSize;
|
||||||
|
_fileProgress = 0;
|
||||||
|
_packetSize;
|
||||||
|
constructor(archiveURL = "/", feedType = "atom", bleObject, packetSize = 512) {
|
||||||
this.archiveURL = archiveURL;
|
this.archiveURL = archiveURL;
|
||||||
this.feedType = feedType;
|
this.feedType = feedType;
|
||||||
|
if (bleObject) {
|
||||||
|
this.bleObject = bleObject;
|
||||||
|
}
|
||||||
|
this._packetSize = packetSize;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
FEEDS
|
||||||
|
*/
|
||||||
|
async getRawArchive() {
|
||||||
|
const res = await fetch(`http://cors.emaker.limited/?url=${this.archiveURL}.${this.feedType}`, {
|
||||||
|
// "mode": "cors"
|
||||||
|
});
|
||||||
|
const text = await res.text();
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
// atom feeds
|
||||||
|
atomGetVersionDetails(entry) {
|
||||||
|
let outEntry = { title: "", date: new Date, link: "", html: "" };
|
||||||
|
entry.children.forEach((elm) => {
|
||||||
|
let element = elm;
|
||||||
|
if (element.name == "title") {
|
||||||
|
outEntry.title = element.children[0].text;
|
||||||
|
}
|
||||||
|
else if (element.name == "updated") {
|
||||||
|
outEntry.date = new Date(element.children[0].text);
|
||||||
|
}
|
||||||
|
else if (element.name == "link") {
|
||||||
|
outEntry.link = element.attributes["href"];
|
||||||
|
}
|
||||||
|
else if (element.name == "content") {
|
||||||
|
outEntry.html = element.children[0].text;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return outEntry;
|
||||||
|
}
|
||||||
|
async atomGetArchive() {
|
||||||
|
const rawArchive = await this.getRawArchive();
|
||||||
|
const releaseNotes = parseXml.parseXml(rawArchive);
|
||||||
|
const output = [];
|
||||||
|
// console.dir(releaseNotes)
|
||||||
|
releaseNotes.children[0].children.forEach((elm) => {
|
||||||
|
if (elm.type == "element") {
|
||||||
|
const element = elm;
|
||||||
|
if (element.name == "entry") {
|
||||||
|
output.push(this.atomGetVersionDetails(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
// rss feeds
|
||||||
|
rssGetVersionDetails(entry) {
|
||||||
|
let outEntry = { title: "", date: new Date, link: "", html: "" };
|
||||||
|
entry.children.forEach((elm) => {
|
||||||
|
let element = elm;
|
||||||
|
if (element.name == "title") {
|
||||||
|
outEntry.title = element.children[0].text;
|
||||||
|
}
|
||||||
|
else if (element.name == "pubDate") {
|
||||||
|
outEntry.date = element.children[0].text;
|
||||||
|
}
|
||||||
|
else if (element.name == "link") {
|
||||||
|
outEntry.link = element.children[0].text;
|
||||||
|
}
|
||||||
|
else if (element.name == "content") {
|
||||||
|
outEntry.html = element.children[0].text;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return outEntry;
|
||||||
|
}
|
||||||
|
async rssGetArchive() {
|
||||||
|
const rawArchive = await this.getRawArchive();
|
||||||
|
const releaseNotes = parseXml.parseXml(rawArchive);
|
||||||
|
const output = [];
|
||||||
|
releaseNotes.children[0].children[1].children.forEach((elm) => {
|
||||||
|
if (elm.type == "element") {
|
||||||
|
const element = elm;
|
||||||
|
if (element.name == "item") {
|
||||||
|
output.push(this.rssGetVersionDetails(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
async getArchive() {
|
async getArchive() {
|
||||||
const res = await fetch(`${this.archiveURL}.${this.feedType}`);
|
if (this.feedType == "atom") {
|
||||||
console.log(res);
|
return this.atomGetArchive();
|
||||||
|
}
|
||||||
|
else if (this.feedType == "rss") {
|
||||||
|
return this.rssGetArchive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
BLUETOOTH
|
||||||
|
*/
|
||||||
|
setDeviceId(id) {
|
||||||
|
this.bleDeviceId = id;
|
||||||
|
}
|
||||||
|
bytesToString(buffer) {
|
||||||
|
return String.fromCharCode.apply(null, new Uint8Array(buffer));
|
||||||
|
}
|
||||||
|
async readVersionNumber() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.bleObject.read(this.bleDeviceId, this._updaterServiceUUID, this._updaterVersionCharactersiticUUID, (rawData) => {
|
||||||
|
resolve(this.bytesToString(rawData));
|
||||||
|
}, (error) => {
|
||||||
|
reject(`Error: ${error}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async getLatestVersion() {
|
||||||
|
let feed = await this.getArchive();
|
||||||
|
let newestDate = feed[0].date;
|
||||||
|
let i = 0;
|
||||||
|
feed.forEach((item, index) => {
|
||||||
|
if (item.date > newestDate) {
|
||||||
|
newestDate = item.date;
|
||||||
|
i = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return feed[i].title;
|
||||||
|
}
|
||||||
|
async checkForUpdate() {
|
||||||
|
// read device value
|
||||||
|
const deviceVersion = await this.readVersionNumber();
|
||||||
|
// compare with latest version
|
||||||
|
const latestVersion = await this.getLatestVersion();
|
||||||
|
if (deviceVersion != latestVersion) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// update
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
async getBoardVersion() {
|
||||||
|
return await this.readVersionNumber();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
FILE FLASHING
|
||||||
|
*/
|
||||||
|
async getFirmware(version) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://cors.emaker.limited/?url=${this.archiveURL}/download/${version.title}/firmware.bin`);
|
||||||
|
let buf = await res.arrayBuffer();
|
||||||
|
this.file = new Int8Array(buf);
|
||||||
|
this._fileSize = this.file.byteLength;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getFileSize() {
|
||||||
|
return this._fileSize;
|
||||||
|
}
|
||||||
|
async sendNextPacket() {
|
||||||
|
let packet = this.file.slice(this._fileProgress, this._fileProgress + this._packetSize);
|
||||||
|
// this._fileProgress += this._packetSize;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.bleObject.writeWithoutResponse(this.bleDeviceId, this._updaterServiceUUID, this._updateFileCharacteristicUUID, packet.buffer, () => {
|
||||||
|
console.log(`Sent: ${packet}, progress: ${this._fileProgress}`);
|
||||||
|
resolve(true);
|
||||||
|
}, (error) => {
|
||||||
|
this._fileProgress -= this._packetSize;
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async sendEndCmd(agree) {
|
||||||
|
const buffer = new ArrayBuffer(1);
|
||||||
|
let view = new Int8Array(buffer);
|
||||||
|
view[0] = agree ? 3 : 4;
|
||||||
|
await this.bleObject.withPromises.write(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// start and autoconnect before you run this function, so that you can have an "uninterrupted" connection when the board reboots
|
||||||
|
async flashFirmware(progressCallback) {
|
||||||
|
// write filesize to board
|
||||||
|
// await notify from cmd - ready
|
||||||
|
// send a packet
|
||||||
|
// check for error
|
||||||
|
// write file length
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
// set mtu
|
||||||
|
this._packetSize = await this.bleObject.withPromises.requestMtu(this.bleDeviceId, this._packetSize);
|
||||||
|
// start notify
|
||||||
|
this.bleObject.startNotification(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, (rawData) => {
|
||||||
|
let dataView = new Uint8Array(rawData);
|
||||||
|
console.log(dataView);
|
||||||
|
if (dataView[0] == 1 && dataView.length == 1) {
|
||||||
|
// send file
|
||||||
|
this.sendNextPacket();
|
||||||
|
progressCallback(`MTU: ${this._packetSize}; Sending (${Math.floor((this._fileProgress * 100) / this._fileSize)}%), ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
}
|
||||||
|
else if (dataView[0] == 2 && dataView.length == 1) {
|
||||||
|
// done logic
|
||||||
|
console.log(`progress >= filesize: ${this._fileProgress} vs ${this._fileSize}`);
|
||||||
|
if (this._fileProgress >= (this._fileSize - this._packetSize)) {
|
||||||
|
console.log("true");
|
||||||
|
// send agree
|
||||||
|
this.sendEndCmd(true);
|
||||||
|
progressCallback(`Complete!`);
|
||||||
|
this.bleObject.stopNotification(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, () => {
|
||||||
|
// success
|
||||||
|
resolve(true);
|
||||||
|
}, (error) => {
|
||||||
|
reject("Error: Failed to stop notify");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("False");
|
||||||
|
// send disagree
|
||||||
|
this.sendEndCmd(false);
|
||||||
|
progressCallback(`Error, starting over: ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
this._fileProgress = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dataView[0] == 15 && dataView.length == 1) {
|
||||||
|
// error cmd
|
||||||
|
progressCallback(`Error on remote: ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
reject("Error on remote");
|
||||||
|
}
|
||||||
|
else if (dataView[0] == 0 && dataView.length == 1) {
|
||||||
|
// ignore no command
|
||||||
|
progressCallback(`Board is on`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// should be the file progress
|
||||||
|
let fileProgressView = new Uint32Array(rawData);
|
||||||
|
this._fileProgress = (dataView[3] << 24) |
|
||||||
|
(dataView[2] << 16) |
|
||||||
|
(dataView[1] << 8) | dataView[0];
|
||||||
|
this.sendNextPacket();
|
||||||
|
progressCallback(`Recieved progress ${fileProgressView[0]}; Sending (${Math.floor((this._fileProgress * 100) / this._fileSize)}%), ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
}
|
||||||
|
}, (error) => {
|
||||||
|
reject("Error: Failed to start notify");
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
const buffer = new ArrayBuffer(4);
|
||||||
|
let view = new Int32Array(buffer);
|
||||||
|
view[0] = this._fileSize;
|
||||||
|
this.bleObject.withPromises.write(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, buffer);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
36
dist/index.d.ts
vendored
36
dist/index.d.ts
vendored
@@ -1,6 +1,38 @@
|
|||||||
|
export type versionNotes = {
|
||||||
|
title: string;
|
||||||
|
date: Date | string;
|
||||||
|
link: string;
|
||||||
|
html: string;
|
||||||
|
};
|
||||||
export default class Updater {
|
export default class Updater {
|
||||||
archiveURL: string;
|
archiveURL: string;
|
||||||
feedType: string;
|
feedType: string;
|
||||||
constructor(archiveURL?: string, feedType?: string);
|
bleObject: BLECentralPlugin.BLECentralPluginStatic;
|
||||||
getArchive(): Promise<void>;
|
protected bleDeviceId: string;
|
||||||
|
private readonly _updaterServiceUUID;
|
||||||
|
private readonly _updaterVersionCharactersiticUUID;
|
||||||
|
private readonly _updaterCommandCharacterisitcUUID;
|
||||||
|
private readonly _updateFileCharacteristicUUID;
|
||||||
|
file: Int8Array;
|
||||||
|
private _fileSize;
|
||||||
|
private _fileProgress;
|
||||||
|
private _packetSize;
|
||||||
|
constructor(archiveURL?: string, feedType?: string, bleObject?: BLECentralPlugin.BLECentralPluginStatic, packetSize?: number);
|
||||||
|
private getRawArchive;
|
||||||
|
private atomGetVersionDetails;
|
||||||
|
private atomGetArchive;
|
||||||
|
private rssGetVersionDetails;
|
||||||
|
private rssGetArchive;
|
||||||
|
getArchive(): Promise<versionNotes[]>;
|
||||||
|
setDeviceId(id: string): void;
|
||||||
|
private bytesToString;
|
||||||
|
private readVersionNumber;
|
||||||
|
private getLatestVersion;
|
||||||
|
checkForUpdate(): Promise<boolean>;
|
||||||
|
getBoardVersion(): Promise<string>;
|
||||||
|
getFirmware(version: versionNotes): Promise<boolean>;
|
||||||
|
getFileSize(): number;
|
||||||
|
private sendNextPacket;
|
||||||
|
private sendEndCmd;
|
||||||
|
flashFirmware(progressCallback: (message: string) => void): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|||||||
258
dist/index.es.js
vendored
258
dist/index.es.js
vendored
@@ -1,13 +1,265 @@
|
|||||||
|
import { parseXml } from '@rgrove/parse-xml';
|
||||||
|
|
||||||
|
/// <reference types="cordova-plugin-ble-central" />
|
||||||
class Updater {
|
class Updater {
|
||||||
archiveURL;
|
archiveURL;
|
||||||
feedType;
|
feedType;
|
||||||
constructor(archiveURL = "/", feedType = "rss") {
|
bleObject;
|
||||||
|
bleDeviceId;
|
||||||
|
_updaterServiceUUID = "71a4438e-fd52-4b15-b3d2-ec0e3e561900";
|
||||||
|
_updaterVersionCharactersiticUUID = "71a4438e-fd52-4b15-b3d2-ec0e3e561910";
|
||||||
|
_updaterCommandCharacterisitcUUID = "71a4438e-fd52-4b15-b3d2-ec0e3e561920";
|
||||||
|
_updateFileCharacteristicUUID = "71a4438e-fd52-4b15-b3d2-ec0e3e561930";
|
||||||
|
file;
|
||||||
|
_fileSize;
|
||||||
|
_fileProgress = 0;
|
||||||
|
_packetSize;
|
||||||
|
constructor(archiveURL = "/", feedType = "atom", bleObject, packetSize = 512) {
|
||||||
this.archiveURL = archiveURL;
|
this.archiveURL = archiveURL;
|
||||||
this.feedType = feedType;
|
this.feedType = feedType;
|
||||||
|
if (bleObject) {
|
||||||
|
this.bleObject = bleObject;
|
||||||
|
}
|
||||||
|
this._packetSize = packetSize;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
FEEDS
|
||||||
|
*/
|
||||||
|
async getRawArchive() {
|
||||||
|
const res = await fetch(`http://cors.emaker.limited/?url=${this.archiveURL}.${this.feedType}`, {
|
||||||
|
// "mode": "cors"
|
||||||
|
});
|
||||||
|
const text = await res.text();
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
// atom feeds
|
||||||
|
atomGetVersionDetails(entry) {
|
||||||
|
let outEntry = { title: "", date: new Date, link: "", html: "" };
|
||||||
|
entry.children.forEach((elm) => {
|
||||||
|
let element = elm;
|
||||||
|
if (element.name == "title") {
|
||||||
|
outEntry.title = element.children[0].text;
|
||||||
|
}
|
||||||
|
else if (element.name == "updated") {
|
||||||
|
outEntry.date = new Date(element.children[0].text);
|
||||||
|
}
|
||||||
|
else if (element.name == "link") {
|
||||||
|
outEntry.link = element.attributes["href"];
|
||||||
|
}
|
||||||
|
else if (element.name == "content") {
|
||||||
|
outEntry.html = element.children[0].text;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return outEntry;
|
||||||
|
}
|
||||||
|
async atomGetArchive() {
|
||||||
|
const rawArchive = await this.getRawArchive();
|
||||||
|
const releaseNotes = parseXml(rawArchive);
|
||||||
|
const output = [];
|
||||||
|
// console.dir(releaseNotes)
|
||||||
|
releaseNotes.children[0].children.forEach((elm) => {
|
||||||
|
if (elm.type == "element") {
|
||||||
|
const element = elm;
|
||||||
|
if (element.name == "entry") {
|
||||||
|
output.push(this.atomGetVersionDetails(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
// rss feeds
|
||||||
|
rssGetVersionDetails(entry) {
|
||||||
|
let outEntry = { title: "", date: new Date, link: "", html: "" };
|
||||||
|
entry.children.forEach((elm) => {
|
||||||
|
let element = elm;
|
||||||
|
if (element.name == "title") {
|
||||||
|
outEntry.title = element.children[0].text;
|
||||||
|
}
|
||||||
|
else if (element.name == "pubDate") {
|
||||||
|
outEntry.date = element.children[0].text;
|
||||||
|
}
|
||||||
|
else if (element.name == "link") {
|
||||||
|
outEntry.link = element.children[0].text;
|
||||||
|
}
|
||||||
|
else if (element.name == "content") {
|
||||||
|
outEntry.html = element.children[0].text;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return outEntry;
|
||||||
|
}
|
||||||
|
async rssGetArchive() {
|
||||||
|
const rawArchive = await this.getRawArchive();
|
||||||
|
const releaseNotes = parseXml(rawArchive);
|
||||||
|
const output = [];
|
||||||
|
releaseNotes.children[0].children[1].children.forEach((elm) => {
|
||||||
|
if (elm.type == "element") {
|
||||||
|
const element = elm;
|
||||||
|
if (element.name == "item") {
|
||||||
|
output.push(this.rssGetVersionDetails(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
async getArchive() {
|
async getArchive() {
|
||||||
const res = await fetch(`${this.archiveURL}.${this.feedType}`);
|
if (this.feedType == "atom") {
|
||||||
console.log(res);
|
return this.atomGetArchive();
|
||||||
|
}
|
||||||
|
else if (this.feedType == "rss") {
|
||||||
|
return this.rssGetArchive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
BLUETOOTH
|
||||||
|
*/
|
||||||
|
setDeviceId(id) {
|
||||||
|
this.bleDeviceId = id;
|
||||||
|
}
|
||||||
|
bytesToString(buffer) {
|
||||||
|
return String.fromCharCode.apply(null, new Uint8Array(buffer));
|
||||||
|
}
|
||||||
|
async readVersionNumber() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.bleObject.read(this.bleDeviceId, this._updaterServiceUUID, this._updaterVersionCharactersiticUUID, (rawData) => {
|
||||||
|
resolve(this.bytesToString(rawData));
|
||||||
|
}, (error) => {
|
||||||
|
reject(`Error: ${error}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async getLatestVersion() {
|
||||||
|
let feed = await this.getArchive();
|
||||||
|
let newestDate = feed[0].date;
|
||||||
|
let i = 0;
|
||||||
|
feed.forEach((item, index) => {
|
||||||
|
if (item.date > newestDate) {
|
||||||
|
newestDate = item.date;
|
||||||
|
i = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return feed[i].title;
|
||||||
|
}
|
||||||
|
async checkForUpdate() {
|
||||||
|
// read device value
|
||||||
|
const deviceVersion = await this.readVersionNumber();
|
||||||
|
// compare with latest version
|
||||||
|
const latestVersion = await this.getLatestVersion();
|
||||||
|
if (deviceVersion != latestVersion) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// update
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
async getBoardVersion() {
|
||||||
|
return await this.readVersionNumber();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
FILE FLASHING
|
||||||
|
*/
|
||||||
|
async getFirmware(version) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://cors.emaker.limited/?url=${this.archiveURL}/download/${version.title}/firmware.bin`);
|
||||||
|
let buf = await res.arrayBuffer();
|
||||||
|
this.file = new Int8Array(buf);
|
||||||
|
this._fileSize = this.file.byteLength;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getFileSize() {
|
||||||
|
return this._fileSize;
|
||||||
|
}
|
||||||
|
async sendNextPacket() {
|
||||||
|
let packet = this.file.slice(this._fileProgress, this._fileProgress + this._packetSize);
|
||||||
|
// this._fileProgress += this._packetSize;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.bleObject.writeWithoutResponse(this.bleDeviceId, this._updaterServiceUUID, this._updateFileCharacteristicUUID, packet.buffer, () => {
|
||||||
|
console.log(`Sent: ${packet}, progress: ${this._fileProgress}`);
|
||||||
|
resolve(true);
|
||||||
|
}, (error) => {
|
||||||
|
this._fileProgress -= this._packetSize;
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async sendEndCmd(agree) {
|
||||||
|
const buffer = new ArrayBuffer(1);
|
||||||
|
let view = new Int8Array(buffer);
|
||||||
|
view[0] = agree ? 3 : 4;
|
||||||
|
await this.bleObject.withPromises.write(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// start and autoconnect before you run this function, so that you can have an "uninterrupted" connection when the board reboots
|
||||||
|
async flashFirmware(progressCallback) {
|
||||||
|
// write filesize to board
|
||||||
|
// await notify from cmd - ready
|
||||||
|
// send a packet
|
||||||
|
// check for error
|
||||||
|
// write file length
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
// set mtu
|
||||||
|
this._packetSize = await this.bleObject.withPromises.requestMtu(this.bleDeviceId, this._packetSize);
|
||||||
|
// start notify
|
||||||
|
this.bleObject.startNotification(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, (rawData) => {
|
||||||
|
let dataView = new Uint8Array(rawData);
|
||||||
|
console.log(dataView);
|
||||||
|
if (dataView[0] == 1 && dataView.length == 1) {
|
||||||
|
// send file
|
||||||
|
this.sendNextPacket();
|
||||||
|
progressCallback(`MTU: ${this._packetSize}; Sending (${Math.floor((this._fileProgress * 100) / this._fileSize)}%), ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
}
|
||||||
|
else if (dataView[0] == 2 && dataView.length == 1) {
|
||||||
|
// done logic
|
||||||
|
console.log(`progress >= filesize: ${this._fileProgress} vs ${this._fileSize}`);
|
||||||
|
if (this._fileProgress >= (this._fileSize - this._packetSize)) {
|
||||||
|
console.log("true");
|
||||||
|
// send agree
|
||||||
|
this.sendEndCmd(true);
|
||||||
|
progressCallback(`Complete!`);
|
||||||
|
this.bleObject.stopNotification(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, () => {
|
||||||
|
// success
|
||||||
|
resolve(true);
|
||||||
|
}, (error) => {
|
||||||
|
reject("Error: Failed to stop notify");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("False");
|
||||||
|
// send disagree
|
||||||
|
this.sendEndCmd(false);
|
||||||
|
progressCallback(`Error, starting over: ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
this._fileProgress = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dataView[0] == 15 && dataView.length == 1) {
|
||||||
|
// error cmd
|
||||||
|
progressCallback(`Error on remote: ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
reject("Error on remote");
|
||||||
|
}
|
||||||
|
else if (dataView[0] == 0 && dataView.length == 1) {
|
||||||
|
// ignore no command
|
||||||
|
progressCallback(`Board is on`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// should be the file progress
|
||||||
|
let fileProgressView = new Uint32Array(rawData);
|
||||||
|
this._fileProgress = (dataView[3] << 24) |
|
||||||
|
(dataView[2] << 16) |
|
||||||
|
(dataView[1] << 8) | dataView[0];
|
||||||
|
this.sendNextPacket();
|
||||||
|
progressCallback(`Recieved progress ${fileProgressView[0]}; Sending (${Math.floor((this._fileProgress * 100) / this._fileSize)}%), ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
}
|
||||||
|
}, (error) => {
|
||||||
|
reject("Error: Failed to start notify");
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
const buffer = new ArrayBuffer(4);
|
||||||
|
let view = new Int32Array(buffer);
|
||||||
|
view[0] = this._fileSize;
|
||||||
|
this.bleObject.withPromises.write(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, buffer);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
package.json
19
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "updaterweblibrary",
|
"name": "updaterweblibrary",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"description": "OTA Updater App frontend library",
|
"description": "OTA Updater App frontend library",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -9,13 +9,13 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "chopster44",
|
"author": "chopster44",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"main": "./dist/cjs/index.js",
|
"main": "./dist/index.cjs.js",
|
||||||
"module": "./dist/esm/index.js",
|
"module": "./dist/index.es.js",
|
||||||
"types": "./dist/types/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"require": "./dist/cjs/index.js",
|
"require": "./dist/index.cjs.js",
|
||||||
"import": "./dist/esm/index.js"
|
"import": "./dist/index.es.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@@ -24,7 +24,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build:types": "tsc -p tsconfig.json --emitDeclarationOnly",
|
"build:types": "tsc -p tsconfig.json --emitDeclarationOnly",
|
||||||
"build": "rm -rf ./dist && npm run build:types && rollup -c",
|
"build": "rm -rf ./dist && npm run build:types && rollup -c",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "vitest",
|
||||||
|
"version": "npm run build && git add -A ./dist"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-typescript": "^12.1.4",
|
"@rollup/plugin-typescript": "^12.1.4",
|
||||||
@@ -32,5 +33,9 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@rgrove/parse-xml": "^4.2.0",
|
||||||
|
"cordova-plugin-ble-central": "^2.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
302
src/index.ts
302
src/index.ts
@@ -1,14 +1,308 @@
|
|||||||
|
/// <reference types="cordova-plugin-ble-central" />
|
||||||
|
import { parseXml, XmlDocument, XmlElement, XmlText } from "@rgrove/parse-xml";
|
||||||
|
|
||||||
|
export type versionNotes = {
|
||||||
|
title: string;
|
||||||
|
date: Date | string;
|
||||||
|
link: string;
|
||||||
|
html: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default class Updater {
|
export default class Updater {
|
||||||
public archiveURL: string;
|
public archiveURL: string;
|
||||||
public feedType: string;
|
public feedType: string;
|
||||||
constructor(archiveURL: string = "/", feedType: string = "rss") {
|
|
||||||
|
public bleObject: BLECentralPlugin.BLECentralPluginStatic;
|
||||||
|
protected bleDeviceId: string;
|
||||||
|
|
||||||
|
private readonly _updaterServiceUUID: string = "71a4438e-fd52-4b15-b3d2-ec0e3e561900";
|
||||||
|
private readonly _updaterVersionCharactersiticUUID: string = "71a4438e-fd52-4b15-b3d2-ec0e3e561910";
|
||||||
|
private readonly _updaterCommandCharacterisitcUUID: string = "71a4438e-fd52-4b15-b3d2-ec0e3e561920";
|
||||||
|
private readonly _updateFileCharacteristicUUID: string = "71a4438e-fd52-4b15-b3d2-ec0e3e561930";
|
||||||
|
|
||||||
|
public file: Int8Array;
|
||||||
|
private _fileSize: number;
|
||||||
|
private _fileProgress: number = 0;
|
||||||
|
|
||||||
|
private _packetSize: number;
|
||||||
|
|
||||||
|
constructor(archiveURL: string = "/", feedType: string = "atom", bleObject?: BLECentralPlugin.BLECentralPluginStatic, packetSize: number = 512) {
|
||||||
this.archiveURL = archiveURL;
|
this.archiveURL = archiveURL;
|
||||||
this.feedType = feedType;
|
this.feedType = feedType;
|
||||||
|
if (bleObject) {
|
||||||
|
this.bleObject = bleObject;
|
||||||
|
}
|
||||||
|
this._packetSize = packetSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getArchive(): Promise<void> {
|
/*
|
||||||
const res = await fetch(`${this.archiveURL}.${this.feedType}`);
|
FEEDS
|
||||||
console.log(res);
|
*/
|
||||||
|
|
||||||
|
private async getRawArchive(): Promise<string> {
|
||||||
|
const res = await fetch(`http://cors.emaker.limited/?url=${this.archiveURL}.${this.feedType}`, {
|
||||||
|
// "mode": "cors"
|
||||||
|
});
|
||||||
|
const text = await res.text();
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// atom feeds
|
||||||
|
private atomGetVersionDetails(entry: XmlElement): versionNotes {
|
||||||
|
let outEntry: versionNotes = { title: "", date: new Date, link: "", html: "" };
|
||||||
|
entry.children.forEach((elm) => {
|
||||||
|
let element = elm as XmlElement;
|
||||||
|
if (element.name == "title") {
|
||||||
|
outEntry.title = (element.children[0] as XmlText).text;
|
||||||
|
}
|
||||||
|
else if (element.name == "updated") {
|
||||||
|
outEntry.date = new Date((element.children[0] as XmlText).text);
|
||||||
|
}
|
||||||
|
else if (element.name == "link") {
|
||||||
|
outEntry.link = element.attributes["href"];
|
||||||
|
} else if (element.name == "content") {
|
||||||
|
outEntry.html = (element.children[0] as XmlText).text;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return outEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async atomGetArchive(): Promise<versionNotes[]> {
|
||||||
|
const rawArchive: string = await this.getRawArchive();
|
||||||
|
const releaseNotes: XmlDocument = parseXml(rawArchive);
|
||||||
|
|
||||||
|
const output: versionNotes[] = [];
|
||||||
|
// console.dir(releaseNotes)
|
||||||
|
(releaseNotes.children[0] as XmlElement).children.forEach((elm) => {
|
||||||
|
if (elm.type == "element") {
|
||||||
|
const element = elm as XmlElement;
|
||||||
|
if (element.name == "entry") {
|
||||||
|
output.push(this.atomGetVersionDetails(element))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rss feeds
|
||||||
|
private rssGetVersionDetails(entry: XmlElement): versionNotes {
|
||||||
|
let outEntry: versionNotes = { title: "", date: new Date, link: "", html: "" };
|
||||||
|
entry.children.forEach((elm) => {
|
||||||
|
let element = elm as XmlElement;
|
||||||
|
if (element.name == "title") {
|
||||||
|
outEntry.title = (element.children[0] as XmlText).text;
|
||||||
|
}
|
||||||
|
else if (element.name == "pubDate") {
|
||||||
|
outEntry.date = (element.children[0] as XmlText).text;
|
||||||
|
}
|
||||||
|
else if (element.name == "link") {
|
||||||
|
outEntry.link = (element.children[0] as XmlText).text;
|
||||||
|
} else if (element.name == "content") {
|
||||||
|
outEntry.html = (element.children[0] as XmlText).text;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return outEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async rssGetArchive(): Promise<versionNotes[]> {
|
||||||
|
const rawArchive: string = await this.getRawArchive();
|
||||||
|
const releaseNotes: XmlDocument = parseXml(rawArchive);
|
||||||
|
|
||||||
|
const output: versionNotes[] = [];
|
||||||
|
((releaseNotes.children[0] as XmlElement).children[1] as XmlElement).children.forEach((elm) => {
|
||||||
|
if (elm.type == "element") {
|
||||||
|
const element = elm as XmlElement;
|
||||||
|
if (element.name == "item") {
|
||||||
|
output.push(this.rssGetVersionDetails(element))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getArchive(): Promise<versionNotes[]> {
|
||||||
|
if (this.feedType == "atom") {
|
||||||
|
return this.atomGetArchive()
|
||||||
|
} else if (this.feedType == "rss") {
|
||||||
|
return this.rssGetArchive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
BLUETOOTH
|
||||||
|
*/
|
||||||
|
|
||||||
|
public setDeviceId(id: string): void {
|
||||||
|
this.bleDeviceId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bytesToString(buffer: ArrayBuffer): string {
|
||||||
|
return String.fromCharCode.apply(null, new Uint8Array(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async readVersionNumber(): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.bleObject.read(
|
||||||
|
this.bleDeviceId,
|
||||||
|
this._updaterServiceUUID,
|
||||||
|
this._updaterVersionCharactersiticUUID,
|
||||||
|
(rawData: ArrayBuffer) => {
|
||||||
|
resolve(this.bytesToString(rawData));
|
||||||
|
},
|
||||||
|
(error: string) => {
|
||||||
|
reject(`Error: ${error}`);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getLatestVersion(): Promise<string> {
|
||||||
|
let feed: versionNotes[] = await this.getArchive();
|
||||||
|
let newestDate: Date = feed[0].date as Date;
|
||||||
|
let i: number = 0;
|
||||||
|
feed.forEach((item: versionNotes, index: number) => {
|
||||||
|
if (item.date > newestDate) {
|
||||||
|
newestDate = item.date as Date;
|
||||||
|
i = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return feed[i].title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async checkForUpdate(): Promise<boolean> {
|
||||||
|
// read device value
|
||||||
|
const deviceVersion = await this.readVersionNumber();
|
||||||
|
// compare with latest version
|
||||||
|
const latestVersion = await this.getLatestVersion();
|
||||||
|
if (deviceVersion != latestVersion) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// update
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getBoardVersion(): Promise<string> {
|
||||||
|
return await this.readVersionNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FILE FLASHING
|
||||||
|
*/
|
||||||
|
|
||||||
|
public async getFirmware(version: versionNotes): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://cors.emaker.limited/?url=${this.archiveURL}/download/${version.title}/firmware.bin`);
|
||||||
|
let buf = await res.arrayBuffer();
|
||||||
|
this.file = new Int8Array(buf);
|
||||||
|
this._fileSize = this.file.byteLength;
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFileSize(): number {
|
||||||
|
return this._fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendNextPacket(): Promise<boolean> {
|
||||||
|
let packet = this.file.slice(this._fileProgress, this._fileProgress+this._packetSize);
|
||||||
|
// this._fileProgress += this._packetSize;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.bleObject.writeWithoutResponse(this.bleDeviceId, this._updaterServiceUUID, this._updateFileCharacteristicUUID, packet.buffer,
|
||||||
|
() => {
|
||||||
|
console.log(`Sent: ${packet}, progress: ${this._fileProgress}`);
|
||||||
|
resolve(true);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this._fileProgress -= this._packetSize;
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendEndCmd(agree: boolean): Promise<void> {
|
||||||
|
const buffer = new ArrayBuffer(1);
|
||||||
|
let view = new Int8Array(buffer);
|
||||||
|
view[0] = agree ? 3 : 4;
|
||||||
|
await this.bleObject.withPromises.write(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start and autoconnect before you run this function, so that you can have an "uninterrupted" connection when the board reboots
|
||||||
|
public async flashFirmware(progressCallback: (message: string) => void): Promise<boolean> {
|
||||||
|
// write filesize to board
|
||||||
|
// await notify from cmd - ready
|
||||||
|
// send a packet
|
||||||
|
// check for error
|
||||||
|
|
||||||
|
// write file length
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
// set mtu
|
||||||
|
this._packetSize = await this.bleObject.withPromises.requestMtu(this.bleDeviceId, this._packetSize);
|
||||||
|
// start notify
|
||||||
|
this.bleObject.startNotification(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID,
|
||||||
|
(rawData: ArrayBuffer): void => {
|
||||||
|
let dataView = new Uint8Array(rawData);
|
||||||
|
console.log(dataView);
|
||||||
|
if (dataView[0] == 1 && dataView.length == 1) {
|
||||||
|
// send file
|
||||||
|
this.sendNextPacket();
|
||||||
|
progressCallback(`MTU: ${this._packetSize}; Sending (${Math.floor((this._fileProgress *100)/this._fileSize)}%), ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
} else if (dataView[0] == 2 && dataView.length == 1) {
|
||||||
|
// done logic
|
||||||
|
console.log(`progress >= filesize: ${this._fileProgress} vs ${this._fileSize}`);
|
||||||
|
if (this._fileProgress >= (this._fileSize - this._packetSize)) {
|
||||||
|
console.log("true");
|
||||||
|
// send agree
|
||||||
|
this.sendEndCmd(true);
|
||||||
|
progressCallback(`Complete!`);
|
||||||
|
this.bleObject.stopNotification(this.bleDeviceId,
|
||||||
|
this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID,
|
||||||
|
() => {
|
||||||
|
// success
|
||||||
|
resolve(true);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
reject("Error: Failed to stop notify");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log("False");
|
||||||
|
// send disagree
|
||||||
|
this.sendEndCmd(false);
|
||||||
|
progressCallback(`Error, starting over: ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
this._fileProgress = 0;
|
||||||
|
}
|
||||||
|
} else if (dataView[0] == 15 && dataView.length == 1) {
|
||||||
|
// error cmd
|
||||||
|
progressCallback(`Error on remote: ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
reject("Error on remote");
|
||||||
|
} else if (dataView[0] == 0 && dataView.length == 1) {
|
||||||
|
// ignore no command
|
||||||
|
progressCallback(`Board is on`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// should be the file progress
|
||||||
|
let fileProgressView = new Uint32Array(rawData);
|
||||||
|
this._fileProgress = (dataView[3] << 24) |
|
||||||
|
(dataView[2] << 16) |
|
||||||
|
(dataView[1] << 8) | dataView[0];
|
||||||
|
this.sendNextPacket();
|
||||||
|
progressCallback(`Recieved progress ${fileProgressView[0]}; Sending (${Math.floor((this._fileProgress *100)/this._fileSize)}%), ${this._fileProgress} / ${this._fileSize}`);
|
||||||
|
}
|
||||||
|
}, (error) => { reject("Error: Failed to start notify");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const buffer = new ArrayBuffer(4)
|
||||||
|
let view = new Int32Array(buffer);
|
||||||
|
view[0] = this._fileSize;
|
||||||
|
this.bleObject.withPromises.write(this.bleDeviceId, this._updaterServiceUUID, this._updaterCommandCharacterisitcUUID, buffer);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
37
test/archive.test.ts
Normal file
37
test/archive.test.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import Updater, { versionNotes } from "../src/index.ts";
|
||||||
|
|
||||||
|
describe("retrieving data", () => {
|
||||||
|
test("get an atom feed from gitea", async (): Promise<void> => {
|
||||||
|
const updater = new Updater("https://git.emaker.limited/MicrocontrollerCD/SoftwareRelease/releases", "atom");
|
||||||
|
const res = await updater.getArchive();
|
||||||
|
expect(res).not.toBe(null);
|
||||||
|
expect(res.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("get an rss feed from gitea", async (): Promise<void> => {
|
||||||
|
const updater = new Updater("https://git.emaker.limited/MicrocontrollerCD/SoftwareRelease/releases", "rss");
|
||||||
|
const res = await updater.getArchive();
|
||||||
|
expect(res).not.toBe(null);
|
||||||
|
expect(res.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("get an atom feed from github", async (): Promise<void> => {
|
||||||
|
const updater = new Updater("https://github.com/chopster44/Phaser_3_pong/releases", "atom");
|
||||||
|
const res = await updater.getArchive();
|
||||||
|
expect(res).not.toBe(null);
|
||||||
|
expect(res.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("data validation", () => {
|
||||||
|
const updater = new Updater("https://git.emaker.limited/MicrocontrollerCD/SoftwareRelease/releases", "atom");
|
||||||
|
test("date checked is not current date", async () => {
|
||||||
|
const res: versionNotes[] = await updater.getArchive();
|
||||||
|
const date: Date = res[0].date as Date;
|
||||||
|
const today: Date = new Date;
|
||||||
|
console.log(date);
|
||||||
|
console.log(today)
|
||||||
|
expect(date.getUTCSeconds()).not.toBe(today.getUTCSeconds())
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationDir": "./dist",
|
"declarationDir": "./dist",
|
||||||
"paths": {
|
"paths": {
|
||||||
"rollup/parseAst": ["./node_modules/rollup/dist/parseAst"]
|
"rollup/parseAst": ["./node_modules/rollup/dist/parseAst"],
|
||||||
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
|
|||||||
10
vitest.config.ts
Normal file
10
vitest.config.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user