obniz
Version:
obniz sdk for javascript
631 lines (630 loc) • 16.5 kB
JavaScript
"use strict";
/**
* @packageDocumentation
* @module Parts.RS_BTWATTCH2
*/
/* eslint rulesdir/non-ascii: 0 */
Object.defineProperty(exports, "__esModule", { value: true });
/** RS_BTWATTCH2 management class RS_BTWATTCH2を管理するクラス */
class RS_BTWATTCH2 {
/**
* Constructor.
*
* If you want to change the RTC auto-configuration option from the default,
*
* set it as an argument at this time.
*
* コンストラクタ
*
* RTC自動設定オプションをデフォルトから変更する場合は、このタイミングで引数に設定
*
* @param peripheral instance of BleRemotePeripheral BleRemotePeripheralのインスタンス
*
* @param options set auto RTC or not RTC自動設定の有無
*/
constructor(peripheral, options) {
this.keys = ['rtcAutoset'];
this.requiredKeys = [];
// parsing
this._totalSize = -1;
this._received = [];
this._waitings = [];
if (peripheral && !RS_BTWATTCH2.isDevice(peripheral)) {
throw new Error('peripheral is not RS_BTWATTCH2');
}
this._peripheral = peripheral;
this.params = options || {};
}
static info() {
return {
name: 'RS_BTWATTCH2',
};
}
/**
* Verify that the received peripheral is from the RS_BTWATTCH2
*
* 受け取ったPeripheralがRS_BTWATTCH2のものかどうか確認する
*
* @param peripheral instance of BleRemotePeripheral BleRemotePeripheralのインスタンス
*
* @returns Whether it is the RS_BTWATTCH2
*
* RS_BTWATTCH2かどうか
*/
static isDevice(peripheral) {
return (peripheral.localName &&
(peripheral.localName.indexOf('BTWATTCH2_') >= 0 ||
peripheral.localName.indexOf('btwattch2_') >= 0));
}
wired(obniz) {
// do nothing.
}
/**
* Check if device is under pairing mode(over 3 seconds button pressing)
*
* デバイスがペアリングモード中であることを検出する (3秒間本体のボタンを押してください)
*
* @returns Whether there is a device under pairing mode
*
* ペアリングモード中のデバイスがあるかどうか
*/
isPairingMode() {
return this._peripheral.localName.indexOf('BTWATTCH2_') < 0;
}
/**
* Get the pairing key under pairing mode
*
* ペアリングモード中にペアリングキーを取得
*
* @returns pairing key ペアリングキー
*/
async firstPairingWait() {
if (!this._peripheral) {
throw new Error('No Peripheral Found');
}
if (this.isPairingMode() === false) {
throw new Error(`peripheral is not pairing mode. Press Pairing Button on device over 3 seconds. LED will start blinking then it is under pairing mode.`);
}
this._peripheral.ondisconnect = (reason) => {
if (typeof this.ondisconnect === 'function') {
this.ondisconnect(reason);
}
};
try {
let gotKeys;
await this._peripheral.connectWait({
pairingOption: {
onPairedCallback: (keys) => {
gotKeys = keys;
},
onPairingFailed: (e) => {
throw e;
},
},
});
if (!gotKeys) {
const keys = await this._peripheral.pairingWait();
gotKeys = keys;
}
await this._peripheral.disconnectWait();
return gotKeys;
}
catch (e) {
try {
if (this._peripheral.connected) {
await this._peripheral.disconnectWait();
}
}
catch (disconErr) {
// ignore when disconnection failed.
console.log(disconErr);
}
throw e;
}
}
/**
* Connect to the target device with pairing key
*
* ペアリングキーを用いてデバイスと接続
*
* @param keys pairing key ペアリングキー
*/
async connectWait(keys) {
if (!keys) {
throw new Error(`You should get keys before. call firstPairingWait() to get keys and provide this`);
}
if (this.isPairingMode()) {
throw new Error(`peripheral is pairing mode. Unplug and plug it again to change to normal mode.`);
}
await this._peripheral.connectWait();
await this._peripheral.pairingWait({
keys,
});
this._peripheral.ondisconnect = (reason) => {
if (typeof this.ondisconnect === 'function') {
this.ondisconnect(reason);
}
};
const service = this._peripheral.getService('6e400001b5a3f393e0a9e50e24dcca9e');
if (!service) {
throw new Error(`no service found`);
}
this._rxFromTargetCharacteristic = service.getCharacteristic('6e400003b5a3f393e0a9e50e24dcca9e');
this._txToTargetCharacteristic = service.getCharacteristic('6e400002b5a3f393e0a9e50e24dcca9e');
try {
await this._rxFromTargetCharacteristic.registerNotifyWait((data) => {
this._pushData(data);
});
if (this.params.rtcAutoset !== false) {
await this.setRTCWait();
}
}
catch (e) {
try {
if (this._peripheral.connected) {
await this._peripheral.disconnectWait();
}
}
catch (disconErr) {
// ignore when disconnection failed.
console.log(disconErr);
}
throw e;
}
}
/**
* Disconnect from the device
*
* デバイスとの接続を切断
*/
async disconnectWait() {
await this._peripheral.disconnectWait();
}
/**
* @deprecated Please use {@linkplain setRTCWait}
*
* {@linkplain setRTCWait} の使用を推奨
*
* @param date
*/
setRTC(date) {
return this.setRTCWait(date);
}
/**
* Set device RTC (date)
*
* デバイスのRTC(日時)の設定
*
* @param date instance of Date Dateのインスタンス
*/
async setRTCWait(date) {
if (!date) {
date = new Date();
}
const ret = await this._transactionWait([
0x01,
date.getSeconds(),
date.getMinutes(),
date.getHours(),
date.getDate(),
date.getMonth(),
date.getFullYear() - 1900,
]);
if (ret.length !== 2) {
throw new Error(`communication error`);
}
if (ret[1] !== 0x00) {
throw new Error(`set rtc failed`);
}
}
/**
* Set relay ON/OFF
*
* リレーのON/OFFを設定する
*
* @param isOn set relay or not
*
* リレーを設定するかどうか
*/
async setPowerStateWait(isOn) {
const ret = await this._transactionWait([0xa7, isOn ? 0x01 : 0x00]);
if (ret.length !== 3) {
throw new Error(`communication error`);
}
if (ret[1] === 0x01) {
throw new Error(`set power failed`);
}
}
/**
* Get current relay state
*
* 現在のリレーの状態を取得
*
* @returns the relay in set or not
*
* リレーが設定されているかどうか
*/
async getPowerStateWait() {
return (await this.getRealTimeDataWait()).powerState;
}
/**
* Get realtime measurement data(voltage[Vrms]・electric current[Irms]・electric power[Wa]) and relay state
*
* リアルタイム計測データ(電圧[Vrms]・電流[Irms]・電力[Wa])とリレーの状態を取得
*
* @returns received realtime measurement data and relay state
*
* 受けとったリアルタイム計測データとリレーの状態
*/
async getRealTimeDataWait() {
const ret = await this._transactionWait([0x08]);
if (ret.length !== 27) {
throw new Error(`communication error`);
}
if (ret[1] !== 0x00) {
throw new Error(`get data failed`);
}
const obj = {};
// Relay
obj.powerState = ret[26] === 0x01;
// measured
let vrms = ((ret[2] + ret[3]) << (8 * 1)) +
(ret[4] << (8 * 2)) +
(ret[5] << (8 * 3)) +
(ret[6] << (8 * 4)) +
(ret[7] << (8 * 5));
vrms /= Math.pow(2, 24);
obj.vrms = vrms;
let irms = ((ret[8] + ret[9]) << (8 * 1)) +
(ret[10] << (8 * 2)) +
(ret[11] << (8 * 3)) +
(ret[12] << (8 * 4)) +
(ret[13] << (8 * 5));
irms /= Math.pow(2, 30);
obj.irms = irms;
let wa = ((ret[14] + ret[15]) << (8 * 1)) +
(ret[16] << (8 * 2)) +
(ret[17] << (8 * 3)) +
(ret[18] << (8 * 4)) +
(ret[19] << (8 * 5));
wa /= Math.pow(2, 24);
obj.wa = wa;
obj.date = new Date(1900 + ret[25], ret[24], ret[23], ret[22], ret[21], ret[20], 0);
return obj;
}
_pushData(data) {
if (this._waitings.length === 0) {
return;
}
if (this._totalSize === -1) {
if (data[0] !== 0xaa) {
return;
}
this._totalSize = (data[1] << 8) + data[2]; // Not found in maker's docs
this._received = [];
data = data.splice(3);
}
this._received.push(...data);
if (this._received.length === this._totalSize + 1) {
this._received.pop(); // => CRC
this._onReceived(this._received);
this._received = [];
this._totalSize = -1;
}
}
_onReceived(data) {
const one = this._waitings.shift();
if (!one) {
return;
}
if (one.command !== data[0]) {
one.reject(new Error(`received command does not matched`));
return;
}
one.resolve(data);
}
async _transactionWait(data) {
let timeoutFunc = null;
const timeout = setTimeout(() => {
if (timeoutFunc)
timeoutFunc('Timed out for waiting');
}, 30 * 1000);
try {
const waitData = new Promise((resolve, reject) => {
timeoutFunc = reject;
this._waitings.push({
command: data[0],
resolve: (received) => {
clearTimeout(timeout);
resolve(received);
},
reject: (e) => {
clearTimeout(timeout);
reject(e);
},
});
});
const send = this._createData(data);
await this._txToTargetCharacteristic.writeWait(send);
return await waitData;
}
catch (e) {
clearTimeout(timeout);
throw e;
}
}
_createData(data) {
const cmd = Buffer.alloc(data.length + 4);
cmd.writeUInt8(0xaa, 0);
cmd.writeUInt16BE(data.length, 1);
Buffer.from(data).copy(cmd, 3);
cmd.writeUInt8(this._GetCRC8(data), data.length + 3);
return [...cmd];
}
_GetCRC8(arr) {
const CRC8Table = [
0x00,
0x85,
0x8f,
0x0a,
0x9b,
0x1e,
0x14,
0x91,
0xb3,
0x36,
0x3c,
0xb9,
0x28,
0xad,
0xa7,
0x22,
0xe3,
0x66,
0x6c,
0xe9,
0x78,
0xfd,
0xf7,
0x72,
0x50,
0xd5,
0xdf,
0x5a,
0xcb,
0x4e,
0x44,
0xc1,
0x43,
0xc6,
0xcc,
0x49,
0xd8,
0x5d,
0x57,
0xd2,
0xf0,
0x75,
0x7f,
0xfa,
0x6b,
0xee,
0xe4,
0x61,
0xa0,
0x25,
0x2f,
0xaa,
0x3b,
0xbe,
0xb4,
0x31,
0x13,
0x96,
0x9c,
0x19,
0x88,
0x0d,
0x07,
0x82,
0x86,
0x03,
0x09,
0x8c,
0x1d,
0x98,
0x92,
0x17,
0x35,
0xb0,
0xba,
0x3f,
0xae,
0x2b,
0x21,
0xa4,
0x65,
0xe0,
0xea,
0x6f,
0xfe,
0x7b,
0x71,
0xf4,
0xd6,
0x53,
0x59,
0xdc,
0x4d,
0xc8,
0xc2,
0x47,
0xc5,
0x40,
0x4a,
0xcf,
0x5e,
0xdb,
0xd1,
0x54,
0x76,
0xf3,
0xf9,
0x7c,
0xed,
0x68,
0x62,
0xe7,
0x26,
0xa3,
0xa9,
0x2c,
0xbd,
0x38,
0x32,
0xb7,
0x95,
0x10,
0x1a,
0x9f,
0x0e,
0x8b,
0x81,
0x04,
0x89,
0x0c,
0x06,
0x83,
0x12,
0x97,
0x9d,
0x18,
0x3a,
0xbf,
0xb5,
0x30,
0xa1,
0x24,
0x2e,
0xab,
0x6a,
0xef,
0xe5,
0x60,
0xf1,
0x74,
0x7e,
0xfb,
0xd9,
0x5c,
0x56,
0xd3,
0x42,
0xc7,
0xcd,
0x48,
0xca,
0x4f,
0x45,
0xc0,
0x51,
0xd4,
0xde,
0x5b,
0x79,
0xfc,
0xf6,
0x73,
0xe2,
0x67,
0x6d,
0xe8,
0x29,
0xac,
0xa6,
0x23,
0xb2,
0x37,
0x3d,
0xb8,
0x9a,
0x1f,
0x15,
0x90,
0x01,
0x84,
0x8e,
0x0b,
0x0f,
0x8a,
0x80,
0x05,
0x94,
0x11,
0x1b,
0x9e,
0xbc,
0x39,
0x33,
0xb6,
0x27,
0xa2,
0xa8,
0x2d,
0xec,
0x69,
0x63,
0xe6,
0x77,
0xf2,
0xf8,
0x7d,
0x5f,
0xda,
0xd0,
0x55,
0xc4,
0x41,
0x4b,
0xce,
0x4c,
0xc9,
0xc3,
0x46,
0xd7,
0x52,
0x58,
0xdd,
0xff,
0x7a,
0x70,
0xf5,
0x64,
0xe1,
0xeb,
0x6e,
0xaf,
0x2a,
0x20,
0xa5,
0x34,
0xb1,
0xbb,
0x3e,
0x1c,
0x99,
0x93,
0x16,
0x87,
0x02,
0x08,
0x8d,
];
let crc8 = 0x00;
for (const data of arr) {
crc8 = CRC8Table[(crc8 ^ (data & 0xff)) & 0xff];
}
return crc8;
}
}
exports.default = RS_BTWATTCH2;