advlib-ble-manufacturers
Version:
Wireless advertising packet decoding library for Bluetooth Low Energy manufacturer-specific data. We believe in an open Internet of Things.
655 lines (563 loc) • 22 kB
JavaScript
/**
* Copyright reelyActive 2021-2025
* We believe in an open Internet of Things
*/
const utils = require('./utils');
const MIN_DATA_LENGTH_BYTES = 1;
const MSE_MIN_FRAME_LENGTH = 7;
const S3_STATIC_FRAME_LENGTH = 20;
const S3_TEMPERATURE_HUMIDITY_FRAME_LENGTH = 16;
const S4_STATIC_FRAME_LENGTH = 18;
const S4_DOOR_ALARM_FRAME_LENGTH = 15;
const FRAME_VERSION_OFFSET = 1;
const FIRMWARE_VERSION_OFFSET = 2;
const HARDWARE_VERSION_OFFSET = 5;
const BATTERY_PERCENTAGE_OFFSET = 2;
const DOOR_MAGNET_ALARM_OFFSET = 3;
const ANTI_DISASSEMBLY_ALARM_OFFSET = 4;
const ALARM_TRIGGER_OFFSET = 5;
const S3_TEMPERATURE_OFFSET = 3;
const S3_HUMIDITY_OFFSET = 5;
const S3_STATIC_MAC_OFFSET = 12;
const S3_TEMPERATURE_HUMIDITY_MAC_OFFSET = 7;
const S4_STATIC_MAC_OFFSET = 10;
const S4_DOOR_ALARM_MAC_OFFSET = 7;
const V3_00_FRAME_LENGTH = 24;
const V3_05_FRAME_LENGTH = 24;
const V3_18_FRAME_LENGTH = 24;
const V3_1B_FRAME_LENGTH = 24;
const V3_1E_FRAME_LENGTH = 24;
const V3_SUBFRAME_VERSION_OFFSET = 2;
const V3_00_MAC_OFFSET = 2;
const V3_00_BATTERY_PERCENTAGE_OFFSET = 8;
const V3_00_FIRMWARE_VERSION_OFFSET = 9;
const V3_00_SALT_OFFSET = 20;
const V3_00_DIGITAL_SIGNATURE_OFFSET = 22;
const V3_03_MIN_FRAME_LENGTH = 6;
const V3_03_POST_BLOCK_LENGTH = 4;
const V3_05_DEVICE_INFO_OFFSET = 3;
const V3_05_TEMPERATURE_OFFSET = 5;
const V3_05_HUMIDITY_OFFSET = 7;
const V3_05_NAME_OFFSET = 9;
const V3_05_MARK_OFFSET = 17;
const V3_05_SALT_OFFSET = 20;
const V3_05_DIGITAL_SIGNATURE_OFFSET = 22;
const V3_05_NAME_LENGTH = 8;
const V3_05_DEVICE_INFO_TEMPERATURE_ONLY_MASK = 0x02;
const V3_18_SERIAL_NUMBER_OFFSET = 3;
const V3_18_NUMBER_OF_ENTRIES_OFFSET = 4;
const V3_18_NUMBER_OF_EXITS_OFFSET = 6;
const V3_18_NUMBER_OF_PEOPLE_OFFSET = 4;
const V3_18_SALT_OFFSET = 20;
const V3_18_DIGITAL_SIGNATURE_OFFSET = 22;
const V3_1B_SUBVERSION_OFFSET = 2;
const V3_1B_PRODUCT_ID_OFFSET = 3;
const V3_1B_BLOCKS_OFFSET = 3;
const V3_1B_STATIC_MAC_OFFSET = 6;
const V3_1B_BATTERY_VOLTAGE_OFFSET = 12;
const V3_1B_TEMPERATURE_OFFSET = 14;
const V3_1B_TX_POWER_OFFSET = 17;
const V3_1B_ENCRYPTION_MASK = 0x80;
const V3_1B_SUBVERSION_MASK = 0x7f;
const V3_1E_DISTANCE_CYCLE_OFFSET = 3;
const V3_1E_DISTANCE_OFFSET = 4;
const V3_1E_FLAGS_OFFSET = 6;
const V3_1E_OCCUPANCY_DETECTION_CYCLE_OFFSET = 7;
const V3_1E_TAMPER_CYCLE_OFFSET = 8;
const V3_1E_SALT_OFFSET = 20;
const V3_1E_DIGITAL_SIGNATURE_OFFSET = 22;
const V3_1E_LOW_BATTERY_MASK = 0x08;
const V3_1E_OCCUPANCY_DETECTION_MASK = 0x04;
const V3_1E_MOTION_DETECTION_MASK = 0x02;
const V3_1E_CONTACT_MASK = 0x01;
const IDENTIFIER_TYPE_EUI_48 = 2;
const MINEW_URI = "https://sniffypedia.org/Organization/Shenzhen_Minew_Technologies_Co_Ltd/";
const MSE_PRODUCT_URIS = new Map([
[ 0x01, "https://sniffypedia.org/Product/Minew_MSE01/" ],
[ 0x02, "https://sniffypedia.org/Product/Minew_MSE02/" ]
]);
const V3_PRODUCT_URIS = new Map([
[ 0x007, "https://sniffypedia.org/Product/Minew_MTB02/" ]
]);
const V3_COMBINATION_BLOCKS = new Map([
[ 0x03, { length: 7, process: processV3CombinationBlock03 } ],
[ 0x1f, { length: 5, process: processV3CombinationBlock1F } ],
[ 0x21, { length: 2, process: processV3CombinationBlock21 } ],
[ 0x22, { length: 2, process: processV3CombinationBlock22 } ]
]);
const V3_SENSOR_BLOCKS = new Map([
[ 0x01, { length: 6, process: processV3SensorBlock01 } ],
[ 0x02, { length: 7, process: processV3SensorBlock02 } ],
[ 0x03, { length: 3, process: processV3SensorBlock03 } ]
]);
/**
* Process Minew manufacturer-specific data.
* @param {Object} data The manufacturer data as a hexadecimal-string or Buffer.
* @return {Object} The processed Minew data as JSON.
*/
function process(data) {
let buf = utils.convertToBuffer(data);
if((buf === null) || (buf.length < MIN_DATA_LENGTH_BYTES)) {
return null;
}
let frameType = buf.readUInt8();
switch(frameType) {
case 0x51:
return processMSE(buf);
case 0xa3:
return processS3(buf);
case 0xa4:
return processS4(buf);
case 0xca:
return processConnectV3(buf);
}
return null;
}
/**
* Process Minew MSE sensor data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processMSE(data) {
if(data.length < MSE_MIN_FRAME_LENGTH) {
return null;
}
let model = data.readUInt8(1);
let txCycle = data.readUInt8(2);
let batteryPercentage = data.readUInt8(3);
let sensorStatus = data.readUInt8(4);
let isButtonPressed = [ (sensorStatus & 0x20) === 0x20,
(sensorStatus & 0x40) === 0x40 ];
let isTamperDetected = [ (sensorStatus & 0x01) === 0x01 ];
let isMotionDetected = [ (sensorStatus & 0x02) === 0x02 ];
let energyPercentage = 100 * data.readUInt16LE(5) / 4095;
let uri = MSE_PRODUCT_URIS.has(model) ? MSE_PRODUCT_URIS.get(model) :
MINEW_URI;
// TODO: process optional relay data
return { batteryPercentage: batteryPercentage,
isButtonPressed: isButtonPressed,
isTamperDetected: isTamperDetected,
isMotionDetected: isMotionDetected,
levelPercentage: energyPercentage,
txCycle: txCycle, uri: uri };
}
/**
* Process Minew S3 data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processS3(data) {
let frameVersion = data.readUInt8(FRAME_VERSION_OFFSET);
if((frameVersion === 1) && // Static information frame
(data.length === S3_STATIC_FRAME_LENGTH)) {
let firmwareVersion = data.readUInt8(FIRMWARE_VERSION_OFFSET) + '.' +
data.readUInt8(FIRMWARE_VERSION_OFFSET + 1) + '.' +
data.readUInt8(FIRMWARE_VERSION_OFFSET + 2);
let hardwareVersion = data.readUInt16BE(HARDWARE_VERSION_OFFSET).toString();
let macSignature = toMacSignature(data, S3_STATIC_MAC_OFFSET);
return {
deviceIds: [ macSignature ],
firmwareVersion: firmwareVersion,
hardwareVersion: hardwareVersion,
uri: MINEW_URI
};
}
else if((frameVersion === 3) && // Temperature humidity frame
(data.length === S3_TEMPERATURE_HUMIDITY_FRAME_LENGTH)) {
let batteryPercentage = data.readUInt8(BATTERY_PERCENTAGE_OFFSET);
let temperature = utils.parseSigned88(data.subarray(S3_TEMPERATURE_OFFSET));
let relativeHumidity =
utils.parseSigned88(data.subarray(S3_HUMIDITY_OFFSET));
let macSignature = toMacSignature(data, S3_TEMPERATURE_HUMIDITY_MAC_OFFSET);
return {
batteryPercentage: batteryPercentage,
deviceIds: [ macSignature ],
relativeHumidity: relativeHumidity,
temperature: temperature,
uri: MINEW_URI
};
}
return null;
}
/**
* Process Minew S4 data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processS4(data) {
let frameVersion = data.readUInt8(FRAME_VERSION_OFFSET);
if((frameVersion === 0) && // Static information frame
(data.length === S4_STATIC_FRAME_LENGTH)) {
let firmwareVersion = data.readUInt8(FIRMWARE_VERSION_OFFSET) + '.' +
data.readUInt8(FIRMWARE_VERSION_OFFSET + 1) + '.' +
data.readUInt8(FIRMWARE_VERSION_OFFSET + 2);
let hardwareVersion = data.readUInt16BE(HARDWARE_VERSION_OFFSET).toString();
let macSignature = toMacSignature(data, S4_STATIC_MAC_OFFSET);
return {
deviceIds: [ macSignature ],
firmwareVersion: firmwareVersion,
hardwareVersion: hardwareVersion,
uri: MINEW_URI
};
}
else if((frameVersion === 1) && // Door alarm frame
(data.length === S4_DOOR_ALARM_FRAME_LENGTH)) {
let batteryPercentage = data.readUInt8(BATTERY_PERCENTAGE_OFFSET);
let isDoorMagnetAlarm = (data.readUInt8(DOOR_MAGNET_ALARM_OFFSET) > 0);
let isAntiDisassemblyAlarm = (data.readUInt8(
ANTI_DISASSEMBLY_ALARM_OFFSET) > 0);
let isCurrentAlarmTrigger = (data.readUInt8(ALARM_TRIGGER_OFFSET) > 0);
let macSignature = toMacSignature(data, S4_DOOR_ALARM_MAC_OFFSET);
return {
batteryPercentage: batteryPercentage,
deviceIds: [ macSignature ],
isContactDetected: [ !isDoorMagnetAlarm ],
isTamperDetected: [ !isAntiDisassemblyAlarm ],
uri: MINEW_URI
};
}
return null;
}
/**
* Process Minew Connect V3 data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processConnectV3(data) {
let frameVersion = data.readUInt8(FRAME_VERSION_OFFSET);
switch(frameVersion) {
case 0x00:
return processConnectV3Type00(data);
case 0x03:
return processConnectV3Type03(data);
case 0x05:
return processConnectV3Type05(data);
case 0x18:
return processConnectV3Type18(data);
case 0x1b:
return processConnectV3Type1b(data);
case 0x1e:
return processConnectV3Type1e(data);
}
return null;
}
/**
* Process Minew Connect V3 type 0x00 device information frame data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processConnectV3Type00(data) {
if(data.length !== V3_00_FRAME_LENGTH) {
return null;
}
let macSignature = toMacSignature(data, V3_00_MAC_OFFSET);
let batteryPercentage = data.readUInt8(V3_00_BATTERY_PERCENTAGE_OFFSET);
let firmwareVersionBinary = data.readUInt16BE(V3_00_FIRMWARE_VERSION_OFFSET);
let firmwareVersion = ((firmwareVersionBinary >> 13) & 0x0003) + '.' +
((firmwareVersionBinary >> 7) & 0x003f) + '.' +
(firmwareVersionBinary & 0x007f);
let salt = data.readUInt16BE(V3_00_SALT_OFFSET);
let checksum = data.readUInt16BE(V3_00_DIGITAL_SIGNATURE_OFFSET);
return {
batteryPercentage: batteryPercentage,
deviceIds: [ macSignature ],
encrypted: { salt: salt, checksum: checksum, method: "minew-connect-v3" },
firmwareVersion: firmwareVersion,
uri: MINEW_URI
};
}
/**
* Process Minew Connect V3 type 0x03 combination frame data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processConnectV3Type03(data) {
if(data.length < V3_03_MIN_FRAME_LENGTH) {
return null;
}
let sensorData = { uri: MINEW_URI };
let currentBlockOffset = V3_SUBFRAME_VERSION_OFFSET;
while(currentBlockOffset < (data.length - V3_03_POST_BLOCK_LENGTH)) {
let currentBlockId = data.readUInt8(currentBlockOffset);
if(V3_COMBINATION_BLOCKS.has(currentBlockId)) {
let currentBlockParameters = V3_COMBINATION_BLOCKS.get(currentBlockId);
let currentBlockLength = currentBlockParameters.length;
let currentBlock = data.subarray(currentBlockOffset + 1,
currentBlockOffset + currentBlockLength);
let blockData = currentBlockParameters.process(currentBlock);
Object.assign(sensorData, blockData);
currentBlockOffset += currentBlockLength;
}
else {
return sensorData;
}
}
if(currentBlockOffset === (data.length - V3_03_POST_BLOCK_LENGTH)) {
sensorData.encrypted = {
salt: data.readUInt16BE(currentBlockOffset),
checksum: data.readUInt16BE(currentBlockOffset + 2),
method: "minew-connect-v3"
};
}
return sensorData;
}
/**
* Process Minew Connect V3 type 0x05 temperature/humidity frame data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processConnectV3Type05(data) {
if(data.length !== V3_05_FRAME_LENGTH) {
return null;
}
let subframeVersion = data.readUInt8(V3_SUBFRAME_VERSION_OFFSET);
let deviceInfo = data.readUInt8(V3_05_DEVICE_INFO_OFFSET);
let temperature =
utils.parseSigned88(data.subarray(V3_05_TEMPERATURE_OFFSET));
let relativeHumidity =
utils.parseSigned88(data.subarray(V3_05_HUMIDITY_OFFSET));
let name = data.toString('utf8', V3_05_NAME_OFFSET, V3_05_NAME_OFFSET +
V3_05_NAME_LENGTH).replace(/\0/g, '');
let salt = data.readUInt16BE(V3_05_SALT_OFFSET);
let checksum = data.readUInt16BE(V3_05_DIGITAL_SIGNATURE_OFFSET);
let sensorData = {
encrypted: { salt: salt, checksum: checksum, method: "minew-connect-v3" },
name: name,
temperature: temperature,
uri: MINEW_URI
};
if(!(deviceInfo & V3_05_DEVICE_INFO_TEMPERATURE_ONLY_MASK)) {
sensorData.relativeHumidity = relativeHumidity;
}
if(subframeVersion > 0) {
let isMarked = (data.readUInt8(V3_05_MARK_OFFSET) === 0x01);
sensorData.isMarked = [ isMarked ];
}
return sensorData;
}
/**
* Process Minew Connect V3 type 18 radar monitor frame data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processConnectV3Type18(data) {
if(data.length !== V3_18_FRAME_LENGTH) {
return null;
}
let subframeVersion = data.readUInt8(V3_SUBFRAME_VERSION_OFFSET);
let sensorData = { uri: MINEW_URI };
if(subframeVersion === 0x00) { // Human traffic monitoring
let serialNumber = data.readUInt8(V3_18_SERIAL_NUMBER_OFFSET);
let numberOfEntries = data.readUInt16LE(V3_18_NUMBER_OF_ENTRIES_OFFSET);
let numberOfExits = data.readUInt16LE(V3_18_NUMBER_OF_EXITS_OFFSET);
sensorData.passageCounts = [ numberOfEntries, numberOfExits ];
sensorData.passageCountsCycle = serialNumber;
sensorData.encrypted = {
salt: data.readUInt16BE(V3_18_SALT_OFFSET),
checksum: data.readUInt16BE(V3_18_DIGITAL_SIGNATURE_OFFSET),
method: "minew-connect-v3"
};
}
else if(subframeVersion === 0x01) { // Person coordinate info
let serialNumber = data.readUInt8(V3_18_SERIAL_NUMBER_OFFSET);
let statusByte = data.readUInt8(V3_18_NUMBER_OF_PEOPLE_OFFSET);
let packetIndex = statusByte & 0x30;
sensorData.numberOfOccupants = statusByte & 0x0f;
sensorData.numberOfOccupantsCycle = serialNumber;
// TODO: nearest?
sensorData.encrypted = {
salt: data.readUInt16BE(V3_18_SALT_OFFSET),
checksum: data.readUInt16BE(V3_18_DIGITAL_SIGNATURE_OFFSET),
method: "minew-connect-v3"
};
}
else {
return null;
}
return sensorData;
}
/**
* Process Minew Connect V3 type 0x1b temperature frame data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processConnectV3Type1b(data) {
if(data.length < V3_1B_SUBVERSION_OFFSET) {
return null;
}
let isEncrypted = (data.readUInt8(V3_1B_SUBVERSION_OFFSET) &
V3_1B_ENCRYPTION_MASK) === V3_1B_ENCRYPTION_MASK;
let subVersion = data.readUInt8(V3_1B_SUBVERSION_OFFSET) &
V3_1B_SUBVERSION_MASK;
if(isEncrypted) {
return {}; // TODO: extract data for decryption
}
if(subVersion === 0x01) {
let productId = data.readUInt16BE(V3_1B_PRODUCT_ID_OFFSET);
let macSignature = toMacSignature(data, V3_1B_STATIC_MAC_OFFSET);
let batteryVoltage = data.readUInt16BE(V3_1B_BATTERY_VOLTAGE_OFFSET) / 1000;
let temperature = utils.parseSigned88(data.subarray(
V3_1B_TEMPERATURE_OFFSET));
let txPower = data.readInt8(V3_1B_TX_POWER_OFFSET);
let uri = V3_PRODUCT_URIS.has(productId) ? V3_PRODUCT_URIS.get(productId) :
MINEW_URI;
return { batteryVoltage: batteryVoltage,
temperature: temperature,
txPower: txPower,
deviceIds: [ macSignature ],
uri: uri };
}
else if(subVersion === 0x02) {
return processV3SensorBlocks(data.subarray(V3_1B_BLOCKS_OFFSET));
}
return { uri: uri };
}
/**
* Process Minew Connect V3 type 0x1e ToF monitor frame data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processConnectV3Type1e(data) {
if(data.length !== V3_1E_FRAME_LENGTH) {
return null;
}
let distanceCycle = data.readUInt8(V3_1E_DISTANCE_CYCLE_OFFSET);
let distance = data.readUInt16LE(V3_1E_DISTANCE_OFFSET) / 1000;
let flags = data.readUInt8(V3_1E_FLAGS_OFFSET);
let isBatteryLow = ((flags & V3_1E_LOW_BATTERY_MASK) !== 0);
let isOccupancyDetected = [ (flags & V3_1E_OCCUPANCY_DETECTION_MASK) !== 0 ];
let isMotionDetected = [ (flags & V3_1E_MOTION_DETECTION_MASK) !== 0 ];
let isTamperDetected = [ (flags & V3_1E_CONTACT_MASK) !== 0 ];
let isOccupancyDetectedCycle = data.readUInt8(
V3_1E_OCCUPANCY_DETECTION_CYCLE_OFFSET);
let isTamperDetectedCycle = data.readUInt8(V3_1E_TAMPER_CYCLE_OFFSET);
let salt = data.readUInt16BE(V3_1E_SALT_OFFSET);
let checksum = data.readUInt16BE(V3_1E_DIGITAL_SIGNATURE_OFFSET);
let sensorData = {
distance: distance,
distanceCycle: distanceCycle,
encrypted: { salt: salt, checksum: checksum, method: "minew-connect-v3" },
isBatteryLow: isBatteryLow,
isTamperDetected: isTamperDetected,
isMotionDetected: isMotionDetected,
isOccupancyDetected: isOccupancyDetected,
isOccupancyDetectedCycle: isOccupancyDetectedCycle,
isTamperDetectedCycle: isTamperDetectedCycle,
uri: MINEW_URI
};
return sensorData;
}
/**
* Process Minew Connect V3 combination block type 0x03 acceleration data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processV3CombinationBlock03(data) {
return {
acceleration: [
utils.parseSigned88(data.subarray(0, 2)),
utils.parseSigned88(data.subarray(2, 4)),
utils.parseSigned88(data.subarray(4, 6))
]
};
}
/**
* Process Minew Connect V3 combination block type 0x1f door contact data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processV3CombinationBlock1F(data) {
let doorSensorStatus = data.readUInt8();
return { isContactDetected: [ (doorSensorStatus & 0x80) === 0x00 ],
isTamperDetected: [ (doorSensorStatus & 0x40) === 0x40 ],
isContactDetectedCycle: data.readUInt8(1),
isTamperDetectedCycle: data.readUInt8(3) };
}
/**
* Process Minew Connect V3 combination block type 0x21 battery data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processV3CombinationBlock21(data) {
return { batteryPercentage: data.readUInt8() };
}
/**
* Process Minew Connect V3 combination block type 0x22 tamper data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processV3CombinationBlock22(data) {
let isTriggered = (data.readUInt8() > 0);
return { isContactDetected: [ !isTriggered ] };
}
/**
* Process Minew Connect V3 blocks data.
* @param {Object} data The raw V3 blocks data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processV3SensorBlocks(data) {
let sensorData = { uri: MINEW_URI };
let currentBlockOffset = 0;
while(currentBlockOffset < data.length) {
let currentBlockId = data.readUInt8(currentBlockOffset);
if(V3_SENSOR_BLOCKS.has(currentBlockId)) {
let currentBlockParameters = V3_SENSOR_BLOCKS.get(currentBlockId);
let currentBlockLength = currentBlockParameters.length;
let currentBlock = data.subarray(currentBlockOffset + 1,
currentBlockOffset + currentBlockLength);
let blockData = currentBlockParameters.process(currentBlock);
Object.assign(sensorData, blockData);
currentBlockOffset += currentBlockLength;
}
else {
return sensorData;
}
}
return sensorData;
}
/**
* Process Minew Connect V3 sensor block type 0x01 temperature & humidity data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processV3SensorBlock01(data) {
let dataFormat = data.readUInt8();
if(dataFormat === 0x01) {
let temperature = (175 * data.readUInt16BE(1) / 65535) - 45;
let relativeHumidity = (125 * data.readUInt16BE(3) / 65535) - 6;
return { temperature: temperature, relativeHumidity: relativeHumidity };
}
return {};
}
/**
* Process Minew Connect V3 sensor block type 0x02 MAC data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processV3SensorBlock02(data) {
return { deviceIds: [ toMacSignature(data, 0) ] };
}
/**
* Process Minew Connect V3 sensor block type 0x03 battery data.
* @param {Object} data The raw manufacturer data as a Buffer.
* @return {Object} The processed beacon data as JSON.
*/
function processV3SensorBlock03(data) {
return { batteryVoltage: data.readUInt16BE() / 1000 };
}
/**
* Convert the given 48-bit data into a MAC address signature.
* @param {Buffer} data The buffer containing the little-endian MAC address.
* @param {Integer} index The index to the start of the MAC address.
* @return {String} The MAC address signature.
*/
function toMacSignature(data, index) {
return data.toString('hex', index + 5, index + 6) +
data.toString('hex', index + 4, index + 5) +
data.toString('hex', index + 3, index + 4) +
data.toString('hex', index + 2, index + 3) +
data.toString('hex', index + 1, index + 2) +
data.toString('hex', index + 0, index + 1) +
'/' + IDENTIFIER_TYPE_EUI_48;
}
module.exports.process = process;