diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index bc00500..2ba13bc 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -22,6 +22,7 @@ RUN apt-get update \ # # Verify git and needed tools are installed && apt-get -y install git iproute2 procps \ + && apt-get -y install libdbus-1-dev libglib2.0-dev \ # # Remove outdated yarn from /opt and install via package # so it can be easily updated via apt-get upgrade yarn @@ -53,5 +54,7 @@ RUN apt-get update \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* +RUN npm install -g node-gyp + # Switch back to dialog for any ad-hoc use of apt-get ENV DEBIAN_FRONTEND=dialog diff --git a/plejd/ble.bluez.js b/plejd/ble.bluez.js new file mode 100644 index 0000000..9238673 --- /dev/null +++ b/plejd/ble.bluez.js @@ -0,0 +1,766 @@ +const dbus = require('dbus'); +const crypto = require('crypto'); +const xor = require('buffer-xor'); +const _ = require('lodash'); +const EventEmitter = require('events'); + +let debug = ''; + +const getLogger = () => { + const consoleLogger = msg => console.log('plejd', msg); + if (debug === 'console') { + return consoleLogger; + } + + // > /dev/null + return _.noop; +}; + +const logger = getLogger(); + +// UUIDs +const PLEJD_SERVICE = "31ba000160854726be45040c957391b5" +const DATA_UUID = "31ba000460854726be45040c957391b5" +const LAST_DATA_UUID = "31ba000560854726be45040c957391b5" +const AUTH_UUID = "31ba000960854726be45040c957391b5" +const PING_UUID = "31ba000a60854726be45040c957391b5" + +const STATE_IDLE = 'idle'; +const STATE_SCANNING = 'scanning'; +const STATE_CONNECTING = 'connecting'; +const STATE_CONNECTED = 'connected'; +const STATE_AUTHENTICATED = 'authenticated'; +const STATE_DISCONNECTED = 'disconnected'; +const STATE_UNINITIALIZED = 'uninitialized'; +const STATE_INITIALIZED = 'initialized'; + +const BLE_CMD_DIM_CHANGE = '00c8'; +const BLE_CMD_DIM2_CHANGE = '0098'; +const BLE_CMD_STATE_CHANGE = '0097'; +const BLE_CMD_SCENE_TRIG = '0021'; + +const BLUEZ_SERVICE_NAME = 'org.bluez'; +const DBUS_OBJECT_MANAGER = 'org.freedesktop.DBus.ObjectManager'; +const DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'; + +const BLUEZ_ADAPTER_ID = 'org.bluez.Adapter1'; +const BLUEZ_DEVICE_ID = 'org.bluez.Device1'; +const GATT_SERVICE_ID = 'org.bluez.GattService1'; +const GATT_CHRC_ID = 'org.bluez.GattCharacteristic1'; + +class PlejdService extends EventEmitter { + constructor(cryptoKey, keepAlive = false) { + super(); + + this.cryptoKey = Buffer.from(cryptoKey.replace(/-/g, ''), 'hex'); + + // Keeps track of the current state + this.state = STATE_IDLE; + // Keeps track of discovered devices + this.devices = {}; + // Keeps track of the currently connected device + this.device = null; + this.deviceAddress = null; + this.deviceIdx = 0; + + this.writeQueue = []; + + this.plejdDevices = {}; + this.connectEventHooked = false; + + // Holds a reference to all characteristics + this.characteristics = { + data: null, + lastData: null, + auth: null, + ping: null + }; + + this.bus = dbus.registerService('system', null); + + logger('wiring events and waiting for BLE interface to power up.'); + this.wireEvents(); + } + + async init() { + if (this.objectManager) { + this.objectManager.removeAllListeners(); + } + + this.objectManager = await this.bus.getInterface(BLUEZ_SERVICE_NAME, '/', DBUS_OBJECT_MANAGER); + + this.objectManager.on('InterfacesAdded', this.onInterfacesAdded.bind(this)); + this.objectManager.on('InterfacesRemoved', this.onInterfacesRemoved.bind(this)); + this.objectManager.GetManagedObjects((err, objs) => { + Object.keys(objs).forEach((k) => this.onInterfacesAdded(k, objs[k])) + }); + } + + async onInterfacesAdded(path, interfaces) { + const [adapter, dev, service, characteristic] = path.split('/').slice(3); + + if ('org.bluez.Adapter1' in interfaces) { + console.log('has org.bluez.Adapter1'); + + const props = interfaces['org.bluez.Adapter1']; + console.log(props); + this.adapter_props = props; + } + + if ('org.bluez.Device1' in interfaces) { + console.log('has org.bluez.Device1'); + + const props = interfaces['org.bluez.Device1']; + console.log(props); + this.devices[props.Address] = path; + this.emit('device', props.Address, props); + } + + if ('org.bluez.GattService1' in interfaces) { + console.log('has org.bluez.GattService1'); + + const props = interfaces['org.bluez.GattService1']; + console.log(props); + + const iface = await this.getInterface('org.bluez', path, 'org.bluez.GattService1'); + const service = new Service(iface); + + const dev = await this.getDevice(props.Device); + + if (this._path[path]) { + // Characteristic was registered before service, copy them over + service.characteristics = this._path[path].characteristics || {}; + } + + dev.services[props.UUID] = service; + this._path[path] = service; + } + + if ('org.bluez.GattCharacteristic1' in interfaces) { + console.log('has org.bluez.GattCharacteristic1'); + const props = interfaces['org.bluez.GattCharacteristic1']; + console.log(props); + + const iface = await this.getInterface('org.bluez', path, 'org.bluez.GattCharacteristic1'); + const changed = await this.getInterface('org.bluez', path, 'org.freedesktop.DBus.Properties'); + const characteristic = new Characteristic(iface, changed); + + if (this._path[path]) { + // Descriptor was registered before characteristic, copy them over + characteristic.descriptors = this._path[path].descriptors || {}; + } + + if (!this._path[props.Service]) { + // service not jet created + this._path[props.Service] = { characteristics: {} }; + } + this._path[props.Service].characteristics[props.UUID] = characteristic; + this._path[path] = characteristic; + } + } + + async onInterfacesRemoved(path, interfaces) { + const [adapter, dev, service, characteristic] = path.split('/').slice(3); + } + + updateSettings(settings) { + if (settings.debug) { + debug = 'console'; + } + else { + debug = ''; + } + } + + turnOn(id, command) { + logger('turning on ' + id + ' at brightness ' + (!command.brightness ? 255 : command.brightness)); + const brightness = command.brightness ? command.brightness : 0; + + if (command.transition) { + // we have a transition time, split the target brightness + // into pieces spread of the transition time + const steps = command.transition * 2; + const brightnessStep = brightness / steps; + + let i = 0; + const transitionRef = setInterval(() => { + let currentBrightness = parseInt((brightnessStep * i) + 1); + if (currentBrightness > 254) { + currentBrightness = 254; + } + + this._turnOn(id, currentBrightness); + + if (i >= steps) { + clearInterval(transitionRef); + } + + i++; + }, 500); + } + else { + this._turnOn(id, brightness); + } + } + + _turnOn(id, brightness) { + var payload; + if (!brightness || brightness === 0) { + logger('no brightness specified, setting to previous known.'); + payload = Buffer.from((id).toString(16).padStart(2, '0') + '0110009701', 'hex'); + } else { + logger('brightness is ' + brightness); + 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, command) { + logger('turning off ' + id); + + if (command.transition) { + // we have a transition time, split the target brightness (which will be 0) + // into pieces spread of the transition time + const initialBrightness = this.plejdDevices[id] ? this.plejdDevices[id].dim : 250; + const steps = command.transition * 2; + const brightnessStep = initialBrightness / steps; + let currentBrightness = initialBrightness; + + let i = 0; + const transitionRef = setInterval(() => { + currentBrightness = parseInt(initialBrightness - (brightnessStep * i)); + if (currentBrightness <= 0 || i >= steps) { + clearInterval(transitionRef); + + // finally, we turn it off + this._turnOff(id); + return; + } + + this._turnOn(id, currentBrightness); + + i++; + }, 500); + } + else { + this._turnOff(id); + } + } + + _turnOff(id) { + var payload = Buffer.from((id).toString(16).padStart(2, '0') + '0110009700', 'hex'); + this.write(payload); + } + + scan() { + console.log('scan()'); + + if (this.state === STATE_SCANNING) { + console.log('error: already scanning, please wait.'); + return; + } + + // this.state = STATE_SCANNING; + // noble.startScanning([PLEJD_SERVICE]); + + // setTimeout(() => { + // noble.stopScanning(); + // this.state = STATE_IDLE; + + // const foundDeviceCount = Object.values(this.devices).length; + // console.log('scan completed, found ' + foundDeviceCount + ' device(s).'); + + // if (foundDeviceCount == 0) { + // console.log('warning: no devices found. will not do anything else.'); + // } + // else { + // this.emit('scanComplete', this.devices); + // } + // }, 5000); + } + + connect(uuid = null) { + const self = this; + if (this.state === STATE_CONNECTING) { + console.log('warning: currently connecting to a device, please wait...'); + return; + } + + // if (!uuid) { + // this.device = Object.values(this.devices)[this.deviceIdx]; + // } + // else { + // this.device = this.devices[uuid]; + // if (!this.device) { + // console.log('error: could not find a device with uuid: ' + uuid); + // return; + // } + // } + + // if (!this.device) { + // console.log('error: reached end of device list. cannot continue.'); + // return; + // } + + // this.deviceAddress = this._reverseBuffer( + // Buffer.from( + // String(this.device.address) + // .replace(/\-/g, '') + // .replace(/\:/g, ''), 'hex' + // ) + // ); + + // console.log('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) { + // if (self.deviceIdx < Object.keys(self.devices).length) { + // logger('connection timed out after 10 s. cleaning up and trying next.'); + + // self.disconnect(); + + // self.deviceIdx++; + // self.connect(); + // } + // } + // }, 10 * 1000); + + // this.state = STATE_CONNECTING; + + // if (!this.connectEventHooked) { + // this.device.once('connect', (state) => { + // self.onDeviceConnected(state); + // this.connectEventHooked = false; + // }); + // this.connectEventHooked = true; + // } + + // this.device.connect(); + } + + disconnect() { + console.log('disconnect()'); + + clearInterval(this.pingRef); + + // if (this.device) { + // this.device.removeAllListeners('servicesDiscover'); + // this.device.removeAllListeners('connect'); + // this.device.removeAllListeners('disconnect'); + // } + // if (this.characteristics.auth) { + // this.characteristics.auth.removeAllListeners('read'); + // this.characteristics.auth.removeAllListeners('write'); + // } + // if (this.characteristics.data) { + // this.characteristics.data.removeAllListeners('read'); + // this.characteristics.data.removeAllListeners('write'); + // } + // if (this.characteristics.lastData) { + // this.characteristics.lastData.removeAllListeners('read'); + // this.characteristics.lastData.removeAllListeners('write'); + // } + // if (this.characteristics.ping) { + // this.characteristics.ping.removeAllListeners('read'); + // this.characteristics.ping.removeAllListeners('write'); + // } + + + this.connectEventHooked = false; + + // this.unsubscribeCharacteristics(); + // this.device.disconnect(); + + this.state = STATE_DISCONNECTED; + } + + authenticate() { + console.log('authenticate()'); + const self = this; + + if (this.state !== STATE_CONNECTED) { + console.log('error: need to be connected and not previously authenticated (new connection).'); + return; + } + + // this.characteristics.auth.write(Buffer.from([0]), false, (err) => { + // if (err) { + // console.log('error: failed to authenticate: ' + err); + // return; + // } + + // self.characteristics.auth.read((err, data) => { + // if (err) { + // console.log('error: failed to read auth response: ' + err); + // return; + // } + + // var resp = self._createChallengeResponse(self.cryptoKey, data); + // self.characteristics.auth.write(resp, false, (err) => { + // if (err) { + // console.log('error: failed to challenge: ' + err); + // return; + // } + + // self.state = STATE_AUTHENTICATED; + // self.emit('authenticated'); + // }); + // }) + // }); + } + + write(data) { + if (this.state !== STATE_AUTHENTICATED) { + logger('error: not connected.'); + this.writeQueue.push(data); + return false; + } + + // try { + // 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); + // } + // } + // catch (error) { + // console.log('error: writing to plejd: ' + error); + // console.log('will reconnect and try again.'); + // this.writeQueue.push(data); + + // this.disconnect(); + // this.connect(); + // } + } + + onAuthenticated() { + // Start ping + logger('onAuthenticated()'); + this.startPing(); + } + + startPing() { + console.log('startPing()'); + clearInterval(this.pingRef); + + this.pingRef = setInterval(async () => { + if (this.state === STATE_AUTHENTICATED) { + 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.'); + } + }, 3000); + } + + onPingSuccess(nr) { + logger('pong: ' + nr); + } + + onPingFailed(error) { + logger('onPingFailed(' + error + ')'); + + logger('ping failed, reconnecting.'); + this.disconnect(); + this.connect(); + // clearInterval(this.pingRef); + + // this.unsubscribeCharacteristics(); + // this.state = STATE_DISCONNECTED; + // this.characteristicState = STATE_UNINITIALIZED; + + // this.connect(this.device.id); + } + + ping() { + logger('ping()'); + + if (this.state !== STATE_AUTHENTICATED) { + console.log('error: needs to be authenticated before pinging.'); + return; + } + + const self = this; + var ping = crypto.randomBytes(1); + + // try { + // this.characteristics.ping.write(ping, false, (err) => { + // if (err) { + // console.log('error: unable to send ping: ' + err); + // self.emit('pingFailed'); + // return; + // } + + // this.characteristics.ping.read((err, data) => { + // if (err) { + // console.log('error: unable to read ping: ' + err); + // self.emit('pingFailed'); + // return; + // } + + // if (((ping[0] + 1) & 0xff) !== data[0]) { + // self.emit('pingFailed'); + // return; + // } + // else { + // self.emit('pingSuccess', data[0]); + // } + // }); + // }); + // } + // catch (error) { + // console.log('error: writing to plejd: ' + error); + // self.emit('pingFailed', error); + // } + } + + onDeviceConnected(err) { + console.log('onDeviceConnected()'); + const self = this; + + // if (err) { + // console.log('error: failed to connect to device: ' + err + '. picking next.'); + // this.deviceIdx++; + // this.disconnect(); + // this.connect(); + // return; + // } + + // this.state = STATE_CONNECTED; + + // if (this.characteristicState === STATE_UNINITIALIZED) { + // // We need to discover the characteristics + // logger('discovering services and characteristics'); + + // setTimeout(() => { + // if (this.characteristicState === STATE_UNINITIALIZED) { + // console.log('error: discovering characteristics timed out. trying next device.'); + // self.deviceIdx++; + // self.disconnect(); + // self.connect(); + // } + // }, 5000); + + // this.device.discoverSomeServicesAndCharacteristics([PLEJD_SERVICE], [], async (err, services, characteristics) => { + // if (err) { + // console.log('error: failed to discover services: ' + err); + // return; + // } + + // if (self.state !== STATE_CONNECTED || self.characteristicState !== STATE_UNINITIALIZED) { + // // in case our time out triggered before we got here. + // console.log('warning: found characteristics in invalid state. ignoring.'); + // return; + // } + + // logger('found ' + characteristics.length + ' characteristic(s).'); + + // characteristics.forEach((ch) => { + // if (DATA_UUID == ch.uuid) { + // logger('found DATA characteristic.'); + // self.characteristics.data = ch; + // } + // else if (LAST_DATA_UUID == ch.uuid) { + // logger('found LAST_DATA characteristic.'); + // self.characteristics.lastData = ch; + // } + // else if (AUTH_UUID == ch.uuid) { + // logger('found AUTH characteristic.'); + // self.characteristics.auth = ch; + // } + // else if (PING_UUID == ch.uuid) { + // logger('found PING characteristic.'); + // self.characteristics.ping = ch; + // } + // }); + + // if (self.characteristics.data + // && self.characteristics.lastData + // && self.characteristics.auth + // && self.characteristics.ping) { + + // self.characteristicState = STATE_INITIALIZED; + + // // subscribe to notifications + // this.subscribeCharacteristics(); + + // self.emit('deviceCharacteristicsComplete', self.device); + // } + // }); + // } + } + + onDeviceCharacteristicsComplete(device) { + logger('onDeviceCharacteristicsComplete(' + device.id + ')'); + this.authenticate(); + } + + onDeviceDiscovered(device) { + logger('onDeviceDiscovered(' + device.id + ')'); + if (device.advertisement.localName === 'P mesh') { + logger('device is P mesh'); + this.devices[device.id] = device; + } + } + + onDeviceDisconnected() { + logger('onDeviceDisconnected()'); + this.disconnect(); + + if (!this.device) { + console.log('warning: reconnect will not be performed.'); + return; + } + + // we just want to reconnect + // this.connect(this.device.id); + } + + onDeviceScanComplete() { + console.log('onDeviceScanComplete()'); + console.log('trying to connect to the mesh network.'); + this.connect(); + } + + // onInterfaceStateChanged(state) { + // console.log('onInterfaceStateChanged(' + state + ')'); + + // if (state === 'poweredOn') { + // this.scan(); + // } + // else { + // noble.stopScanning(); + // } + // } + + 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.length < 5) { + // ignore the notification since too small + return; + } + + const cmd = decoded.toString('hex', 3, 5); + + if (debug) { + logger('raw event received: ' + decoded.toString('hex')); + } + + if (cmd === BLE_CMD_DIM_CHANGE || cmd === BLE_CMD_DIM2_CHANGE) { + 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('stateChanged', device, { state: state, brightness: dim }); + } + else if (cmd === BLE_CMD_STATE_CHANGE) { + state = parseInt(decoded.toString('hex', 5, 6), 10); + + logger('d: ' + device + ' got state update: ' + state); + this.emit('stateChanged', device, { state: state }); + } + else if (cmd === BLE_CMD_SCENE_TRIG) { + const scene = parseInt(decoded.toString('hex', 5, 6), 10); + this.emit('sceneTriggered', device, scene); + } + + this.plejdDevices[device] = { + state: state, + dim: dim + }; + } + + wireEvents() { + console.log('wireEvents()'); + const self = this; + + // noble.on('stateChange', this.onInterfaceStateChanged.bind(self)); + // noble.on('discover', this.onDeviceDiscovered.bind(self)); + // noble.on('disconnect', this.onDeviceDisconnected.bind(self)); + + this.on('scanComplete', this.onDeviceScanComplete.bind(this)); + this.on('deviceCharacteristicsComplete', this.onDeviceCharacteristicsComplete.bind(self)); + this.on('authenticated', this.onAuthenticated.bind(self)); + this.on('pingFailed', this.onPingFailed.bind(self)); + 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) { + // try { + // this.characteristics.lastData.unsubscribe((err) => { + // if (err) { + // console.log('error: could not unsubscribe from event.'); + // } + // }); + // } + // catch (error) { + // console.log('warning: could not unsubscribe from lastData, probably already disconnected: ' + error); + // } + // } + } + + _createChallengeResponse(key, challenge) { + const intermediate = crypto.createHash('sha256').update(xor(key, challenge)).digest(); + const part1 = intermediate.subarray(0, 16); + const part2 = intermediate.subarray(16); + + const resp = xor(part1, part2); + + return resp; + } + + _encryptDecrypt(key, addr, data) { + var buf = Buffer.concat([addr, addr, addr.subarray(0, 4)]); + + var cipher = crypto.createCipheriv("aes-128-ecb", key, ''); + cipher.setAutoPadding(false); + + var ct = cipher.update(buf).toString('hex'); + ct += cipher.final().toString('hex'); + ct = Buffer.from(ct, 'hex'); + + var output = ""; + for (var i = 0, length = data.length; i < length; i++) { + output += String.fromCharCode(data[i] ^ ct[i % 16]); + } + + return Buffer.from(output, 'ascii'); + } + + _reverseBuffer(src) { + var buffer = Buffer.allocUnsafe(src.length) + + for (var i = 0, j = src.length - 1; i <= j; ++i, --j) { + buffer[i] = src[j] + buffer[j] = src[i] + } + + return buffer + } +} + +module.exports = PlejdService; \ No newline at end of file diff --git a/plejd/package-lock.json b/plejd/package-lock.json index d1315fa..baa320c 100644 --- a/plejd/package-lock.json +++ b/plejd/package-lock.json @@ -13,22 +13,6 @@ "usb": "^1.6.0" } }, - "@icanos/noble": { - "version": "1.9.2-6", - "resolved": "https://registry.npmjs.org/@icanos/noble/-/noble-1.9.2-6.tgz", - "integrity": "sha512-+NxEW7nNEueqX8MknTdno3AZeFode56tzN+dMDW0TJjW96XG822DhoHmHeQZRylTd74r/8M5c4Sb9x/m45yiPw==", - "requires": { - "@abandonware/bluetooth-hci-socket": "^0.5.3-3", - "debug": "^4.1.1", - "napi-thread-safe-callback": "0.0.6", - "node-addon-api": "^1.1.0" - } - }, - "@types/zen-observable": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz", - "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -139,11 +123,6 @@ "readable-stream": "> 1.0.0 < 3.0.0" } }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, "chownr": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", @@ -205,6 +184,14 @@ "type": "^1.0.1" } }, + "dbus": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/dbus/-/dbus-1.0.5.tgz", + "integrity": "sha512-itMup/0lcjnwV4DULJtI8q37Nky713KE2b1QM4+W/0zlzAsMNJZURBtyENtnJc6BlHouqbur++PXWeEKDuGulg==", + "requires": { + "nan": "^2.13.2" + } + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -321,11 +308,6 @@ "ext": "^1.1.2" } }, - "esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" - }, "event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -565,14 +547,6 @@ "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" }, - "is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "requires": { - "symbol-observable": "^1.1.0" - } - }, "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", @@ -730,11 +704,6 @@ "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==", "optional": true }, - "napi-thread-safe-callback": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/napi-thread-safe-callback/-/napi-thread-safe-callback-0.0.6.tgz", - "integrity": "sha512-X7uHCOCdY4u0yamDxDrv3jF2NtYc8A1nvPzBQgvpoSX+WB3jAe2cVNsY448V1ucq7Whf9Wdy02HEUoLW5rJKWg==" - }, "needle": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", @@ -769,11 +738,6 @@ "semver": "^5.4.1" } }, - "node-addon-api": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.1.tgz", - "integrity": "sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ==" - }, "node-pre-gyp": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", @@ -841,11 +805,6 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, - "observable-fns": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.4.0.tgz", - "integrity": "sha512-2BFtEqza7sjLpgImAmagHK97mBwh3+bkwAZS/qF/4n2S8RzKsbdsdOczRBh+Piz7QgQZRAjTzI5vtxtOUgU+cQ==" - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1118,11 +1077,6 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, "tar": { "version": "4.4.13", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", @@ -1175,20 +1129,6 @@ } } }, - "threads": { - "version": "1.0.0-beta.9", - "resolved": "https://registry.npmjs.org/threads/-/threads-1.0.0-beta.9.tgz", - "integrity": "sha512-ZjpQvqA78p+y4jtlhnQsKc8V9AwUvrWwOhy9FkFKWO24JHKte3oWllmjvUw896YqrZymsJvqJwlbUHV1CpVtKw==", - "requires": { - "@types/zen-observable": "^0.8.0", - "callsites": "^3.1.0", - "debug": "^4.1.1", - "is-observable": "^1.1.0", - "observable-fns": "^0.4.0", - "tiny-worker": ">= 2", - "zen-observable": "^0.8.14" - } - }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -1207,14 +1147,6 @@ "xtend": "~4.0.0" } }, - "tiny-worker": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", - "integrity": "sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==", - "requires": { - "esm": "^3.2.25" - } - }, "to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", @@ -1337,11 +1269,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "zen-observable": { - "version": "0.8.15", - "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", - "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" } } } diff --git a/plejd/package.json b/plejd/package.json index 166653b..cf4a6c6 100644 --- a/plejd/package.json +++ b/plejd/package.json @@ -1,7 +1,7 @@ { "dependencies": { "@abandonware/bluetooth-hci-socket": "0.5.3-3", - "@icanos/noble": "^1.9.2-9", + "dbus": "^1.0.5", "axios": "^0.19.0", "buffer-xor": "^2.0.2", "fs": "0.0.1-security", @@ -10,4 +10,4 @@ "mqtt": "^3.0.0", "sleep": "^6.1.0" } -} \ No newline at end of file +}