impl turnon/off and notifications
This commit is contained in:
parent
5d90bceb99
commit
c21d7e1243
3 changed files with 167 additions and 3 deletions
108
plejd/ble.js
108
plejd/ble.js
|
|
@ -3,7 +3,6 @@ const crypto = require('crypto');
|
|||
const xor = require('buffer-xor');
|
||||
const _ = require('lodash');
|
||||
const EventEmitter = require('events');
|
||||
const sleep = require('sleep');
|
||||
|
||||
let debug = 'console';
|
||||
|
||||
|
|
@ -47,8 +46,11 @@ class PlejdService extends EventEmitter {
|
|||
this.devices = {};
|
||||
// Keeps track of the currently connected device
|
||||
this.device = null;
|
||||
this.deviceAddress = null;
|
||||
this.deviceIdx = 0;
|
||||
|
||||
this.writeQueue = [];
|
||||
|
||||
// Holds a reference to all characteristics
|
||||
this.characteristicState = STATE_UNINITIALIZED;
|
||||
this.characteristics = {
|
||||
|
|
@ -62,6 +64,27 @@ class PlejdService extends EventEmitter {
|
|||
this.wireEvents();
|
||||
}
|
||||
|
||||
turnOn(id, brightness) {
|
||||
logger('turning on ' + id + ' at brightness ' + brightness);
|
||||
|
||||
var payload;
|
||||
if (!brightness) {
|
||||
payload = Buffer.from((id).toString(16).padStart(2, '0') + '0110009701', 'hex');
|
||||
} else {
|
||||
brightness = brightness << 8 | brightness;
|
||||
payload = Buffer.from((id).toString(16).padStart(2, '0') + '0110009801' + (brightness).toString(16).padStart(4, '0'), 'hex');
|
||||
}
|
||||
|
||||
this.write(payload);
|
||||
}
|
||||
|
||||
turnOff(id) {
|
||||
logger('turning off ' + id);
|
||||
|
||||
var payload = Buffer.from((id).toString(16).padStart(2, '0') + '0110009700', 'hex');
|
||||
this.write(payload);
|
||||
}
|
||||
|
||||
scan() {
|
||||
logger('scan()');
|
||||
|
||||
|
|
@ -72,7 +95,7 @@ class PlejdService extends EventEmitter {
|
|||
|
||||
this.state = STATE_SCANNING;
|
||||
noble.startScanning([PLEJD_SERVICE]);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
noble.stopScanning();
|
||||
this.state = STATE_IDLE;
|
||||
|
|
@ -112,6 +135,14 @@ class PlejdService extends EventEmitter {
|
|||
return;
|
||||
}
|
||||
|
||||
this.deviceAddress = this._reverseBuffer(
|
||||
Buffer.from(
|
||||
String(this.device.address)
|
||||
.replace(/\-/g, '')
|
||||
.replace(/\:/g, ''), 'hex'
|
||||
)
|
||||
);
|
||||
|
||||
logger('connecting to ' + this.device.id + ' with addr ' + this.device.address + ' and rssi ' + this.device.rssi);
|
||||
setTimeout(() => {
|
||||
if (self.state !== STATE_CONNECTED && self.state !== STATE_AUTHENTICATED) {
|
||||
|
|
@ -141,7 +172,12 @@ class PlejdService extends EventEmitter {
|
|||
return;
|
||||
}
|
||||
|
||||
clearInterval(this.pingRef);
|
||||
|
||||
this.unsubscribeCharacteristics();
|
||||
this.device.disconnect();
|
||||
|
||||
this.state = STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
authenticate() {
|
||||
|
|
@ -179,6 +215,22 @@ class PlejdService extends EventEmitter {
|
|||
});
|
||||
}
|
||||
|
||||
write(data) {
|
||||
if (this.state !== STATE_AUTHENTICATED) {
|
||||
logger('error: not connected.');
|
||||
this.writeQueue.push(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
const encryptedData = this._encryptDecrypt(this.cryptoKey, this.deviceAddress, data);
|
||||
this.characteristics.data.write(encryptedData, false);
|
||||
|
||||
let writeData;
|
||||
while ((writeData = this.writeQueue.shift()) !== undefined) {
|
||||
this.characteristics.data.write(this._encryptDecrypt(this.cryptoKey, this.deviceAddress, writeData), false);
|
||||
}
|
||||
}
|
||||
|
||||
onAuthenticated() {
|
||||
// Start ping
|
||||
logger('onAuthenticated()');
|
||||
|
|
@ -194,6 +246,9 @@ class PlejdService extends EventEmitter {
|
|||
logger('ping');
|
||||
this.ping();
|
||||
}
|
||||
else if (this.state === STATE_DISCONNECTED) {
|
||||
console.log('warning: device disconnected, stop ping.');
|
||||
}
|
||||
else {
|
||||
console.log('error: ping failed, not connected.');
|
||||
}
|
||||
|
|
@ -210,7 +265,9 @@ class PlejdService extends EventEmitter {
|
|||
logger('stopping ping and reconnecting.');
|
||||
clearInterval(this.pingRef);
|
||||
|
||||
this.unsubscribeCharacteristics();
|
||||
this.state = STATE_DISCONNECTED;
|
||||
|
||||
this.connect(this.device.id);
|
||||
}
|
||||
|
||||
|
|
@ -322,6 +379,10 @@ class PlejdService extends EventEmitter {
|
|||
&& self.characteristics.ping) {
|
||||
|
||||
self.characteristicState = STATE_INITIALIZED;
|
||||
|
||||
// subscribe to notifications
|
||||
this.subscribeCharacteristics();
|
||||
|
||||
self.emit('deviceCharacteristicsComplete', self.device);
|
||||
}
|
||||
});
|
||||
|
|
@ -367,6 +428,28 @@ class PlejdService extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
onLastDataUpdated(data, isNotification) {
|
||||
const decoded = this._encryptDecrypt(this.cryptoKey, this.deviceAddress, data);
|
||||
|
||||
let state = 0;
|
||||
let dim = 0;
|
||||
let device = parseInt(decoded[0], 10);
|
||||
|
||||
if (decoded.toString('hex', 3, 5) === '00c8' || decoded.toString('hex', 3, 5) === '0098') {
|
||||
state = parseInt(decoded.toString('hex', 5, 6), 10);
|
||||
dim = parseInt(decoded.toString('hex', 6, 8), 16) >> 8;
|
||||
|
||||
logger('d: ' + device + ' got state+dim update: ' + state + ' - ' + dim);
|
||||
this.emit('dimChanged', device, state, dim);
|
||||
}
|
||||
else if (decoded.toString('hex', 3, 5) === '0097') {
|
||||
state = parseInt(decoded.toString('hex', 5, 6), 10);
|
||||
|
||||
logger('d: ' + device + ' got state update: ' + state);
|
||||
this.emit('stateChanged', device, state);
|
||||
}
|
||||
}
|
||||
|
||||
wireEvents() {
|
||||
logger('wireEvents()');
|
||||
const self = this;
|
||||
|
|
@ -383,6 +466,27 @@ class PlejdService extends EventEmitter {
|
|||
this.on('pingSuccess', this.onPingSuccess.bind(self));
|
||||
}
|
||||
|
||||
subscribeCharacteristics() {
|
||||
if (this.characteristics.lastData) {
|
||||
this.characteristics.lastData.subscribe((err) => {
|
||||
if (err) {
|
||||
console.log('error: could not subscribe to event.');
|
||||
}
|
||||
});
|
||||
this.characteristics.lastData.on('data', this.onLastDataUpdated.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribeCharacteristics() {
|
||||
if (this.characteristics.lastData) {
|
||||
this.characteristics.lastData.unsubscribe((err) => {
|
||||
if (err) {
|
||||
console.log('error: could not unsubscribe from event.');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_createChallengeResponse(key, challenge) {
|
||||
const intermediate = crypto.createHash('sha256').update(xor(key, challenge)).digest();
|
||||
const part1 = intermediate.subarray(0, 16);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue