TR42.ts For Obniz.js

import Obniz from 'obniz';
import ObnizPartsBleInterface, { ObnizPartsBleInfo } from 'obniz/dist/src/obniz/ObnizPartsBleInterface';
import BleRemotePeripheral from 'obniz/dist/src/obniz/libs/embeds/bleHci/bleRemotePeripheral';

/** TR42 のデータ */
export interface TR42_Data {
  /** バッテリー残量 (1:少 〜 5:多 の5段階) */
  battery: number;
  /** 摂氏 */
  temperature: number;
}

/**
 * T & D Corporation Bluetooth Thermo Reorder TR42
 * 
 * ```typescript
 * Obniz.PartsRegistrate(TR42);
 * const tr42 = Obniz.getPartsClass('TR42');
 * ```
 */
export default class TR42 implements ObnizPartsBleInterface {
  /**
   * アドバタイジングデータの対応表
   * 
   * 0x 始まりの16進数が書かれている場所は固定値。受信したアドバタイジングデータと一致することを確認する
   * -1 の場所は変動する値。コメントに内容を記している
   */
  private static advertisingData: number[] = [
    0x02,  // Length
    0x01,  // AD Type : 0x01 = Flags
    0x06,  // AD Data : 0x06 = iBeacon maybe
    0x1b,  // Length
    0xff,  // AD Type : 0xff = Manifacturer Specific Data
    0x92,  // Company ID : 0x0392
    0x03,  // Company ID : 0x0392
    -1  ,  // Serial Number : ex. 0xba → 582E1EBA
    -1  ,  // Serial Number : ex. 0x1e
    -1  ,  // Serial Number : ex. 0x2e
    -1  ,  // Serial Number : ex. 0x58
    -1  ,  // Security : 0x00 = None, 0x01 = On
    0x01,  // Format Number : 0x01 only
    -1  ,  // Measured Value : ex. 0xeb, 0x04 … 0x04eb = 1259 → (1259 - 1000) / 10 = 25.9 (℃)
    -1  ,  // Measured Value
    -1  ,  // Reserved : 0xee
    -1  ,  // Reserved : 0xee
    -1     // Battery Level : 0x01 (Low) 〜 0x05 (High)
    // Reserved : (13 or) 14 Data
    // AD Type 0x08 = Local Name (UTF-8)
  ];
  
  /**
   * パーツ情報
   * 
   * @return パーツ情報
   */
  public static info(): ObnizPartsBleInfo {
    return {
      name: 'TR42'
    };
  }
  
  /**
   * 指定のペリフェラルが TR42 かどうか
   * 
   * @param peripheral ペリフェラル
   * @return TR42 なら true
   */
  public static isDevice(peripheral: BleRemotePeripheral): boolean {
    // localName の接頭辞が 'TR42_' で始まるのが一般的だが、スキャンのタイミングによっては null の瞬間もあるので、アドバタイジングデータの固定値部分が一致するかどうかで判定する
    
    // ペリフェラルのアドバタイジングデータ長が想定より短い場合は NG
    if(this.advertisingData.length > peripheral.adv_data.length) {
      return false;
    }
    
    // ペリフェラルのアドバタイジングデータ長は想定の配列以上であることが確定したので、想定の配列を基にループする
    for(let index = 0; index < this.advertisingData.length; index++) {
      if(this.advertisingData[index] === -1) {
        continue;  // 値が変動する部分は無視する
      }
      if(this.advertisingData[index] === peripheral.adv_data[index]) {
        continue;  // 固定値部分が一致していれば続ける
      }
      return false;  // 固定値部分が想定と異なる場合は NG とする
    }
    return true;  // 全ての固定値部分が想定どおりであれば TR42 とみなす
  }
  
  /**
   * 対象のペリフェラルが TR42 の場合、アドバタイジングデータから取得できる情報を整形して返す
   * 
   * @param peripheral ペリフェラル
   * @return TR42 のデータ
   */
  public static getData(peripheral: BleRemotePeripheral): TR42_Data | null {
    if(!TR42.isDevice(peripheral)) {
      return null;
    }
    
    const data: TR42_Data = {
      battery: peripheral.adv_data[17],
      temperature: (ObnizPartsBleInterface.signed16FromBinary(peripheral.adv_data[14], peripheral.adv_data[13]) - 1000) / 10
    };
    return data;
  }
  
  /** 未使用。ペリフェラル。ObnizPartsBleInterface 仕様を守るため実装 */
  public _peripheral: BleRemotePeripheral | null = null;
  
  /** 未使用。ObnizPartsInterface 仕様を守るため実装 */
  public params: any;
  
  /** 未使用。ObnizPartsInterface 仕様を守るため、実機から取得した情報をそのまま実装 */
  public keys: string[] = [
    'device_type',
    'address_type',
    'ble_event_type',
    'rssi',
    'adv_data',
    'scan_resp'
  ];
  
  /** 未使用。ObnizPartsInterface 仕様を守るため、必須キーなしでひとまず実装 */
  public requiredKeys: string[] = [];
  
  /**
   * 未使用。ObnizPartsInterface 仕様を守るため、空で実装
   * 
   * @param _obniz Obniz
   */
  public wired(_obniz: Obniz): void {
    return;
  }
}