'use strict'; // @ts-check // ================================================================================== // audio.js // ---------------------------------------------------------------------------------- // Description: System Information - library // for Node.js // Copyright: (c) 2014 - 2021 // Author: Sebastian Hildebrandt // ---------------------------------------------------------------------------------- // License: MIT // ================================================================================== // 17. bluetooth // ---------------------------------------------------------------------------------- const exec = require('child_process').exec; const execSync = require('child_process').execSync; const util = require('./util'); // const fs = require('fs'); let _platform = process.platform; const _linux = (_platform === 'linux'); const _darwin = (_platform === 'darwin'); const _windows = (_platform === 'win32'); const _freebsd = (_platform === 'freebsd'); const _openbsd = (_platform === 'openbsd'); const _netbsd = (_platform === 'netbsd'); const _sunos = (_platform === 'sunos'); function getLinuxAudioPci() { let cmd = 'lspci -v 2>/dev/null' let result = []; try { const parts = execSync(cmd).toString().split('\n\n'); for (let i = 0; i < parts.length; i++) { const lines = parts[i].split('\n'); if (lines && lines.length && lines[0].toLowerCase().indexOf('audio') >= 0) { const audio = {}; audio.slotId = lines[0].split(' ')[0]; audio.driver = util.getValue(lines, 'Kernel driver in use', ':', true) || util.getValue(lines, 'Kernel modules', ':', true) result.push(audio); } } return result; } catch (e) { return result; } } // device("device_services") // name-- > Key // address-- > "device_addr" // batteryPercent-- > "device_batteryPercent" // manufacturer-- > "device_manufacturer" // type(keyboard, ...)-- > ("device_majorClassOfDevice_string"), "device_minorClassOfDevice_string" // connected function parseLinuxAudioPciMM(lines, audioPCI) { const result = {}; const slotId = util.getValue(lines, 'Slot'); const pciMatch = audioPCI.filter(function (item) { return item.slotId === slotId }) result.id = slotId; result.name = util.getValue(lines, 'SDevice'); // result.type = util.getValue(lines, 'Class'); result.manufacturer = util.getValue(lines, 'SVendor'); result.revision = util.getValue(lines, 'Rev'); result.driver = pciMatch && pciMatch.length === 1 && pciMatch[0].driver ? pciMatch[0].driver : ''; result.default = null; result.channel = 'PCIe'; result.in = null; result.out = null; result.status = 'online'; return result; } function parseDarwinBluetoothTyoe(str) { let result = ''; if (str.indexOf('keyboard') >= 0) { result = 'Keyboard'; } if (str.indexOf('mouse') >= 0) { result = 'Mouse'; } if (str.indexOf('speaker') >= 0) { result = 'Speaker'; } if (str.indexOf('headset') >= 0) { result = 'Headset'; } // to be continued ... return result; } function parseDarwinBluetoothDevices(bluetoothObject) { const result = {}; const typeStr = ((bluetoothObject.device_minorClassOfDevice_string || bluetoothObject.device_majorClassOfDevice_string || '') + (bluetoothObject.device_name || '')).toLowerCase(); result.device = bluetoothObject.device_services || ''; result.name = bluetoothObject.device_name || ''; result.manufacturer = bluetoothObject.device_manufacturer || ''; result.address = bluetoothObject.device_addr || ''; result.batteryPercent = bluetoothObject.device_batteryPercent || null; result.tyoe = parseDarwinBluetoothTyoe(typeStr); result.connected = bluetoothObject.device_isconnected === 'attrib_Yes'|| false; return result; } function parseWindowsAudio(lines) { const result = {}; const status = util.getValue(lines, 'StatusInfo', '='); // const description = util.getValue(lines, 'Description', '='); result.id = util.getValue(lines, 'DeviceID', '='); // PNPDeviceID?? result.name = util.getValue(lines, 'name', '='); result.manufacturer = util.getValue(lines, 'manufacturer', '='); result.revision = null; result.driver = null result.default = null result.in = null result.out = null result.interfaceType = null result.status = status return result; } function bluetoothDevices(callback) { return new Promise((resolve) => { process.nextTick(() => { let result = []; if (_linux || _freebsd || _openbsd || _netbsd) { let cmd = 'lspci -vmm 2>/dev/null' exec(cmd, function (error, stdout) { // PCI if (!error) { const audioPCI = getLinuxAudioPci(); const parts = stdout.toString().split('\n\n'); for (let i = 0; i < parts.length; i++) { const lines = parts[i].split('\n'); if (util.getValue(lines, 'class', ':', true).toLowerCase().indexOf('audio') >= 0) { const audio = parseLinuxAudioPciMM(lines, audioPCI); result.push(audio); } } } if (callback) { callback(result); } resolve(result); }); } if (_darwin) { let cmd = 'system_profiler SPBluetoothDataType -json' exec(cmd, function (error, stdout) { if (!error) { try { const outObj = JSON.parse(stdout.toString()); if (outObj.SPBluetoothDataType && outObj.SPBluetoothDataType.length && outObj.SPBluetoothDataType[0] && outObj.SPBluetoothDataType[0]['device_title'] && outObj.SPBluetoothDataType[0]['device_title'].length) { for (let i = 0; i < outObj.SPBluetoothDataType[0]['device_title'].length; i++) { const obj = outObj.SPBluetoothDataType[0]['device_title'][i]; const objKey = Object.keys(obj); if (objKey && objKey.length === 1) { const innerObject = obj[objKey[0]]; innerObject.device_name = objKey[0]; const bluetoothDevice = parseDarwinBluetoothDevices(innerObject); result.push(bluetoothDevice); } } } } catch (e) { util.noop() } } if (callback) { callback(result); } resolve(result); }); } if (_windows) { util.wmic('path Win32_SoundDevice get /value', function (error, stdout) { if (!error) { const parts = stdout.toString().split(/\n\s*\n/); for (let i = 0; i < parts.length; i++) { result.push(parseWindowsAudio(parts[i].split('\n'), i)) } } if (callback) { callback(result); } resolve(result); }); } if (_sunos) { resolve(null); } }); }); } exports.bluetoothDevices = bluetoothDevices;