mirror of
https://github.com/Koenkk/zigbee-OTA.git
synced 2026-06-24 13:18:14 +00:00
First firmware.
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
# zigbee-OTA
|
# zigbee-OTA
|
||||||
A collection of Zigbee OTA files
|
A collection of Zigbee OTA files.
|
||||||
|
|||||||
Binary file not shown.
10
index.json
Normal file
10
index.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileVersion": 1107326256,
|
||||||
|
"fileSize": 256632,
|
||||||
|
"manufacturerCode": 4107,
|
||||||
|
"imageType": 261,
|
||||||
|
"url": "TODO",
|
||||||
|
"path": "images/Hue/WhiteLamp-Atmel-Target_0105_5.130.1.30000_0012.sbl-ota"
|
||||||
|
}
|
||||||
|
]
|
||||||
58
lib/ota.js
Normal file
58
lib/ota.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const assert = require('assert');
|
||||||
|
const upgradeFileIdentifier = Buffer.from([0x1E, 0xF1, 0xEE, 0x0B]);
|
||||||
|
|
||||||
|
function parseSubElement(buffer, position) {
|
||||||
|
const tagID = buffer.readUInt16LE(position);
|
||||||
|
const length = buffer.readUInt32LE(position + 2);
|
||||||
|
const data = buffer.slice(position + 6, position + 6 + length);
|
||||||
|
return {tagID, length, data};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseImage(buffer) {
|
||||||
|
const header = {
|
||||||
|
otaUpgradeFileIdentifier: buffer.subarray(0, 4),
|
||||||
|
otaHeaderVersion: buffer.readUInt16LE(4),
|
||||||
|
otaHeaderLength: buffer.readUInt16LE(6),
|
||||||
|
otaHeaderFieldControl: buffer.readUInt16LE(8),
|
||||||
|
manufacturerCode: buffer.readUInt16LE(10),
|
||||||
|
imageType: buffer.readUInt16LE(12),
|
||||||
|
fileVersion: buffer.readUInt32LE(14),
|
||||||
|
zigbeeStackVersion: buffer.readUInt16LE(18),
|
||||||
|
otaHeaderString: buffer.toString('utf8', 20, 52),
|
||||||
|
totalImageSize: buffer.readUInt32LE(52),
|
||||||
|
};
|
||||||
|
let headerPos = 56;
|
||||||
|
if (header.otaHeaderFieldControl & 1) {
|
||||||
|
header.securityCredentialVersion = buffer.readUInt8(headerPos);
|
||||||
|
headerPos += 1;
|
||||||
|
}
|
||||||
|
if (header.otaHeaderFieldControl & 2) {
|
||||||
|
header.upgradeFileDestination = buffer.subarray(headerPos, headerPos + 8);
|
||||||
|
headerPos += 8;
|
||||||
|
}
|
||||||
|
if (header.otaHeaderFieldControl & 4) {
|
||||||
|
header.minimumHardwareVersion = buffer.readUInt16LE(headerPos);
|
||||||
|
headerPos += 2;
|
||||||
|
header.maximumHardwareVersion = buffer.readUInt16LE(headerPos);
|
||||||
|
headerPos += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw = buffer.slice(0, header.totalImageSize);
|
||||||
|
|
||||||
|
assert(Buffer.compare(header.otaUpgradeFileIdentifier, upgradeFileIdentifier) === 0, 'Not an OTA file');
|
||||||
|
|
||||||
|
let position = header.otaHeaderLength;
|
||||||
|
const elements = [];
|
||||||
|
while (position < header.totalImageSize) {
|
||||||
|
const element = parseSubElement(buffer, position);
|
||||||
|
elements.push(element);
|
||||||
|
position += element.data.length + 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(position === header.totalImageSize, 'Size mismatch');
|
||||||
|
return {header, elements, raw};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
parseImage
|
||||||
|
};
|
||||||
58
scripts/add.js
Normal file
58
scripts/add.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const ota = require('../lib/ota');
|
||||||
|
const filename = process.argv[2];
|
||||||
|
|
||||||
|
const manufacturerNameLookup = {
|
||||||
|
4107: 'Hue',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
throw new Error('Please provide a filename');
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = path.resolve(filename);
|
||||||
|
if (!fs.existsSync(file)) {
|
||||||
|
throw new Error(`${file} does not exist`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = fs.readFileSync(file);
|
||||||
|
const parsed = ota.parseImage(buffer);
|
||||||
|
|
||||||
|
if (!manufacturerNameLookup[parsed.header.manufacturerCode]) {
|
||||||
|
throw new Error(`${parsed.header.manufacturerCode} not in manufacturerNameLookup (please add it)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const manufacturerName = manufacturerNameLookup[parsed.header.manufacturerCode];
|
||||||
|
const indexJSON = JSON.parse(fs.readFileSync('index.json'));
|
||||||
|
const destination = path.join('images', manufacturerName, path.basename(file));
|
||||||
|
|
||||||
|
const entry = {
|
||||||
|
fileVersion: parsed.header.fileVersion,
|
||||||
|
fileSize: parsed.header.totalImageSize,
|
||||||
|
manufacturerCode: parsed.header.manufacturerCode,
|
||||||
|
imageType: parsed.header.imageType,
|
||||||
|
url: 'TODO',
|
||||||
|
path: destination,
|
||||||
|
};
|
||||||
|
|
||||||
|
const index = indexJSON.findIndex((i) => {
|
||||||
|
return i.manufacturerCode === entry.manufacturerCode && i.imageType === entry.imageType
|
||||||
|
});
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
console.log(`Updated existing entry (${JSON.stringify(entry)})`);
|
||||||
|
indexJSON[index] = entry;
|
||||||
|
fs.unlinkSync(entry.path)
|
||||||
|
} else {
|
||||||
|
console.log(`Added new entry (${JSON.stringify(entry)})`);
|
||||||
|
indexJSON.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(path.dirname(destination))) {
|
||||||
|
fs.mkdirSync(path.dirname(destination));
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.copyFileSync(file, destination);
|
||||||
|
|
||||||
|
fs.writeFileSync('index.json', JSON.stringify(indexJSON, null, ' '));
|
||||||
Reference in New Issue
Block a user