<commit_before>// Part of <https://miracle.systems/p/h5.bluetooth.hci.inode> licensed under <MIT>

'use strict';

const btHci = require('h5.bluetooth.hci');

const AdvertisingReportEventType = btHci.AdvertisingReportEventType;
const AdvertisingReportAddressType = btHci.AdvertisingReportAddressType;
const EirDataType = btHci.EirDataType;

/**
 * @enum {number}
 */
const DeviceModel = exports.DeviceModel = {
  Beacon: 0x80,
  EnergyMeter: 0x82,
  ControlId: 0x88,
  Nav: 0x89,
  CareSensor1: 0x91,
  CareSensor2: 0x92,
  CareSensor3: 0x93,
  CareSensor4: 0x94,
  CareSensor5: 0x95,
  CareSensor6: 0x96,
  CareSensorT: 0x9A,
  CareSensorHT: 0x9B,
  ControlPoint: 0xB2,
  CareRelay: 0xB3,
  TransceiverUart: 0xB5,
  TransceiverUsb: 0xB6,
  Gsm: 0xB7
};

/**
 * @param {Object<EirDataType, function(Buffer, INodeDeviceMsd)>} eirDataTypeDecoders
 */
exports.registerManufacturerSpecificDataDecoder = function(eirDataTypeDecoders)
{
  const originalDecoder = eirDataTypeDecoders[EirDataType.ManufacturerSpecificData];

  eirDataTypeDecoders[EirDataType.ManufacturerSpecificData] = function(buffer, msd)
  {
    const deviceModel = buffer[1];
    const deviceModelDecoder = exports.msdDecoders[deviceModel];

    if (deviceModelDecoder)
    {
      deviceModelDecoder(buffer, msd);
    }
    else if (originalDecoder)
    {
      originalDecoder(buffer, msd);
    }
  };
};

/**
 * @param {number} gsmTime
 * @param {Buffer} gsmData
 * @returns {Array<AdvertisingReport>}
 */
exports.decodeGsmData = function(gsmTime, gsmData)
{
  const reports = [];

  for (let i = 0; i < gsmData.length;)
  {
    const recordType = gsmData[i++];
    const recordLength = gsmData[i++];
    const recordData = gsmData.slice(i, i + recordLength);

    if (recordData.length !== recordLength)
    {
      break;
    }

    i += recordLength;

    if (recordLength < 24)
    {
      continue;
    }

    tryDecodeGsmDataRecord(gsmTime, recordType, recordData, reports);
  }

  return reports;
};

/**
 * @type {Object<DeviceModel, function(Buffer, INodeDeviceMsd)>}
 */
exports.gsmDecoders = {
  [DeviceModel.EnergyMeter]: function(buffer, msd)
  {
    msd.modelLabel = 'iNode Energy Meter';

    decodeRtto(buffer, 0, msd);
    decodeAlarms(buffer, -1, -1, msd);
    decodeEnergyMeter(buffer, 24, msd);
  },
  [DeviceModel.CareSensor1]: function(buffer, msd)
  {
    msd.modelLabel = 'iNode Care Sensor #1';<commit_msg>Fixed the Energy Meter GSM decoder reading invalid values for the extra properties (week day, light level...) instead of using the default ones.<commit_after>// Part of <https://miracle.systems/p/h5.bluetooth.hci.inode> licensed under <MIT>

'use strict';

const btHci = require('h5.bluetooth.hci');

const AdvertisingReportEventType = btHci.AdvertisingReportEventType;
const AdvertisingReportAddressType = btHci.AdvertisingReportAddressType;
const EirDataType = btHci.EirDataType;

/**
 * @enum {number}
 */
const DeviceModel = exports.DeviceModel = {
  Beacon: 0x80,
  EnergyMeter: 0x82,
  ControlId: 0x88,
  Nav: 0x89,
  CareSensor1: 0x91,
  CareSensor2: 0x92,
  CareSensor3: 0x93,
  CareSensor4: 0x94,
  CareSensor5: 0x95,
  CareSensor6: 0x96,
  CareSensorT: 0x9A,
  CareSensorHT: 0x9B,
  ControlPoint: 0xB2,
  CareRelay: 0xB3,
  TransceiverUart: 0xB5,
  TransceiverUsb: 0xB6,
  Gsm: 0xB7
};

/**
 * @param {Object<EirDataType, function(Buffer, INodeDeviceMsd)>} eirDataTypeDecoders
 */
exports.registerManufacturerSpecificDataDecoder = function(eirDataTypeDecoders)
{
  const originalDecoder = eirDataTypeDecoders[EirDataType.ManufacturerSpecificData];

  eirDataTypeDecoders[EirDataType.ManufacturerSpecificData] = function(buffer, msd)
  {
    const deviceModel = buffer[1];
    const deviceModelDecoder = exports.msdDecoders[deviceModel];

    if (deviceModelDecoder)
    {
      deviceModelDecoder(buffer, msd);
    }
    else if (originalDecoder)
    {
      originalDecoder(buffer, msd);
    }
  };
};

/**
 * @param {number} gsmTime
 * @param {Buffer} gsmData
 * @returns {Array<AdvertisingReport>}
 */
exports.decodeGsmData = function(gsmTime, gsmData)
{
  const reports = [];

  for (let i = 0; i < gsmData.length;)
  {
    const recordType = gsmData[i++];
    const recordLength = gsmData[i++];
    const recordData = gsmData.slice(i, i + recordLength);

    if (recordData.length !== recordLength)
    {
      break;
    }

    i += recordLength;

    if (recordLength < 24)
    {
      continue;
    }

    tryDecodeGsmDataRecord(gsmTime, recordType, recordData, reports);
  }

  return reports;
};

/**
 * @type {Object<DeviceModel, function(Buffer, INodeDeviceMsd)>}
 */
exports.gsmDecoders = {
  [DeviceModel.EnergyMeter]: function(buffer, msd)
  {
    msd.modelLabel = 'iNode Energy Meter';

    decodeRtto(buffer, 0, msd);
    decodeAlarms(buffer, -1, -1, msd);
    decodeEnergyMeter(buffer.slice(0, -6), 24, msd);
  },
  [DeviceModel.CareSensor1]: function(buffer, msd)
  {
    msd.modelLabel = 'iNode Care Sensor #1';
{'fraction_non_alphanumeric': 0.1227217496962333, 'fraction_numerical': 0.025921425678412314, 'mean_word_length': 4.070841889117043, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5469401', 'n_tokens_mistral': 1851, 'n_tokens_neox': 1786, 'n_words': 350}