hap-controller
Version:
Library to implement a HAP (HomeKit) controller
177 lines • 5.05 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Watcher = void 0;
exports.uuidToNobleUuid = uuidToNobleUuid;
exports.nobleUuidToUuid = nobleUuidToUuid;
exports.bufferToValue = bufferToValue;
exports.valueToBuffer = valueToBuffer;
/**
* Convert a proper UUID to noble's format.
*
* @param {string} uuid - UUID to convert
* @returns {string} UUID
*/
function uuidToNobleUuid(uuid) {
return uuid.toLowerCase().replace(/-/g, '');
}
/**
* Convert a UUID in noble's format to a proper UUID.
*
* @param {string} uuid - UUID to convert
* @returns {string} UUID
*/
function nobleUuidToUuid(uuid) {
uuid = uuid.toUpperCase();
if (uuid.length !== 32) {
return uuid;
}
const parts = [
uuid.substring(0, 8),
uuid.substring(8, 12),
uuid.substring(12, 16),
uuid.substring(16, 20),
uuid.substring(20, 32),
];
return parts.join('-');
}
/**
* Unpack a HAP value from a buffer.
*
* @param {Buffer} buffer - Buffer to unpack
* @param {string} format - HAP data format
* @returns {*} Unpacked value.
*/
function bufferToValue(buffer, format) {
switch (format) {
case 'bool':
return buffer.readUInt8(0) !== 0;
case 'uint8':
return buffer.readUInt8(0);
case 'uint16':
return buffer.readUInt16LE(0);
case 'uint32':
return buffer.readUInt32LE(0);
case 'uint64':
return buffer.readUInt32LE(0) || buffer.readUInt32LE(4) << 32;
case 'int':
return buffer.readInt32LE(0);
case 'float':
return buffer.readFloatLE(0);
case 'string':
return buffer.toString();
case 'data':
return buffer.toString('base64');
default:
throw new Error(`Unknown format type: ${format}`);
}
}
/**
* Pack a HAP value into a buffer.
*
* @param {*} value - Value to pack
* @param {string} format - HAP data format
* @returns {Buffer} Packed buffer
*/
function valueToBuffer(value, format) {
switch (format) {
case 'bool':
return Buffer.from([value ? 1 : 0]);
case 'uint8':
return Buffer.from([value & 0xff]);
case 'uint16': {
const b = Buffer.alloc(2);
b.writeUInt16LE(value, 0);
return b;
}
case 'uint32': {
const b = Buffer.alloc(4);
b.writeUInt32LE(value, 0);
return b;
}
case 'uint64': {
const b = Buffer.alloc(8);
b.writeUInt32LE(value & 0xffffffff, 0);
b.writeUInt32LE(value >> 32, 4);
return b;
}
case 'int': {
const b = Buffer.alloc(4);
b.writeInt32LE(value);
return b;
}
case 'float': {
const b = Buffer.alloc(4);
b.writeFloatLE(value);
return b;
}
case 'string':
return Buffer.from(value);
case 'data':
if (typeof value === 'string') {
return Buffer.from(value, 'base64');
}
return value;
default:
throw new Error(`Unknown format type: ${format}`);
}
}
/**
* This should be used when doing any communication with a BLE device, since
* noble doesn't provide any timeout functionality.
*/
class Watcher {
/**
* Initialize the Watcher object.
*
* @param {Object} peripheral - The noble peripheral object
* @param {Promise} watch - The Promise to set a timeout on
* @param {number?} timeout - Timeout
*/
constructor(peripheral, watch, timeout = 30000) {
this.rejected = false;
this.stopped = false;
this.peripheral = peripheral;
this.reject = this._reject.bind(this);
this.peripheral.once('disconnect', this.reject);
const watchPromise = watch.finally(() => this.stop());
const timeoutPromise = new Promise((_resolve, reject) => {
this.rejectFn = reject;
this.timer = setTimeout(() => {
this._reject('Timeout');
}, timeout);
});
this.promise = Promise.race([watchPromise, timeoutPromise]);
}
/**
* Get the promise associated with this watcher.
*
* @returns {Promise} The promise.
*/
getPromise() {
return this.promise;
}
/**
* Call the reject function with the provided reason.
*
* @param {string?} reason - Reject reason
*/
_reject(reason = 'Disconnected') {
if (this.rejected || this.stopped) {
return;
}
this.rejected = true;
this.rejectFn(reason);
}
/**
* Stop the watcher.
*/
stop() {
this.stopped = true;
if (this.timer) {
clearTimeout(this.timer);
}
this.peripheral.removeListener('disconnect', this.reject);
}
}
exports.Watcher = Watcher;
//# sourceMappingURL=gatt-utils.js.map