ojousima.ruuvi_endpoints.ts
Version:
Experiments with RuuviTags and TS
108 lines (107 loc) • 4.58 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.dffeunencrypter = exports.dffeparser = void 0;
var aes_js_1 = require("aes-js");
var ruuvitagbroadcast_1 = require("./ruuvitagbroadcast");
var dataFormatStart = 0;
var dataFormatEnd = dataFormatStart + 1;
var versionStart = dataFormatEnd;
var versionEnd = versionStart + 1;
var versionInvalid = 255;
var addressStart = versionEnd;
var addressEnd = addressStart + 6;
var addressInvalid = 0xffffffffffff;
var humidityStart = addressEnd;
var humidityEnd = humidityStart + 1;
var humidityInvalid = 255;
var temperatureStart = humidityEnd;
var temperatureEnd = temperatureStart + 2;
var temperatureInvalid = -32768;
var pressureStart = temperatureEnd;
var pressureEnd = pressureStart + 2;
var pressureInvalid = 65535;
var batteryStart = pressureEnd;
var batteryEnd = batteryStart + 2;
var sequenceStart = batteryEnd;
var sequenceEnd = sequenceStart + 2;
var sequenceInvalid = 0xffff;
/*
* @brief Parse FE data
*
* Throws error if header byte is not @c 0xFE or version byte is not @c 0x02
*
* @param data complete manufacturer data to be parsed, starting from header byte
* @return Ruuvi data payload
*/
var dffeparser = function (data) {
if (data.length < addressEnd - 1 || 0xfe !== data[0]) {
throw new Error('Not DF5 data');
}
if (0x02 !== data[versionStart]) {
throw new Error('Unsupported version ' + data[versionStart].toString(16));
}
var robject = new ruuvitagbroadcast_1.RuuviTagBroadcast();
robject.dataFormat = data[dataFormatStart];
var humidity = data[humidityStart];
humidity /= 2; // scale
robject.humidityRh = humidity;
var temperatureBytes = data.slice(temperatureStart, temperatureEnd);
var temperature = temperatureBytes[0]; // Full degrees
temperature += temperatureBytes[1] / 100.0; // Decimals
if (temperature > 128) {
// Ruuvi format, sign bit + value
temperature = temperature - 128;
temperature = 0 - temperature;
}
robject.temperatureC = temperature;
var pressureBytes = data.slice(pressureStart, pressureEnd); // uint16_t pascals
var pressure = pressureBytes[0] * 256 + pressureBytes[1];
pressure += 50000; // Ruuvi format
robject.pressurePa = pressure;
var batteryBytes = data.slice(batteryStart, batteryEnd); // millivolts
var battery = batteryBytes[0] * 256 + batteryBytes[1];
robject.batteryVoltageV = battery / 1000.0;
var sequenceBytes = data.slice(sequenceStart, sequenceEnd);
var sequence = sequenceBytes[0] * 256 + sequenceBytes[1];
robject.measurementSequence = sequenceInvalid === sequence ? null : sequence;
var addressBytes = data.slice(addressStart, addressEnd);
// Sorry, but lint says so.
var address = addressBytes[0] * 256 * 256 * 256 * 256 * 256 +
addressBytes[1] * 256 * 256 * 256 * 256 +
addressBytes[2] * 256 * 256 * 256 +
addressBytes[3] * 256 * 256 +
addressBytes[4] * 256 +
addressBytes[5];
robject.mac = addressInvalid === address ? null : address;
return robject;
};
exports.dffeparser = dffeparser;
/*
* @brief Unencrypt data with the basepassword and unique password.
*
* For example data version 2 has default password of "ruuvi.com\0" + device mac.
* Convert ASCII (NOT UTF-8!) string of "ruuvi.com\0" to Uint8Array and use it as a basepw
* Add device mac as unique pw. Returns complete data in unencrypted form as Uint8Array
*
* @param data complete manufacturer data to be unencrypted, starting from header byte
* @param basepw: Base password used in encryption
* @param unitpw: Unit-specific password used in encryption.
* @return unencrypted FE data.
* @warning Be careful when converting strings to Uint8Array to avoid UTF-8 encoded values.
*/
var dffeunencrypter = function (data, basepw, unitpw) {
if (16 !== basepw.length + unitpw.length) {
throw new Error('Invalid base+unit password length. Expected 16, got ' + (basepw.length + unitpw.length).toString());
}
var key = new Uint8Array(basepw.length + unitpw.length);
key.set(basepw);
key.set(unitpw, basepw.length);
var aesEcb = new aes_js_1.ModeOfOperation.ecb(key);
// All fe data starts with header + version, followed by encrypted 16-byte payload
var encryptedBytes = data.subarray(2, 2 + 16);
// Since electronic codebook does not store state, we can
// reuse the same instance.
var decryptedBytes = aesEcb.decrypt(encryptedBytes);
return decryptedBytes;
};
exports.dffeunencrypter = dffeunencrypter;