diff --git a/README.md b/README.md index d96c3b1..20cfbf8 100644 --- a/README.md +++ b/README.md @@ -485,7 +485,23 @@ I also created a nice little command line tool called [mmon][mmon-github-url] (m | | [0].default | | | X | X | | is default printer | | | [0].shared | X | | X | X | | is shared printer | -#### 12. Network related functions +#### 12. Audio + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.audio(cb) | [{...}] | X | X | X | X | | get printer information | +| | [0].id | X | | X | X | | internal id | +| | [0].name | X | | X | X | | name | +| | [0].manufacturer | X | | X | X | | manufacturer | +| | [0].revision | X | | | | | revision | +| | [0].driver | X | | | | | driver | +| | [0].default | | | X | X | | is default | +| | [0].in | | | X | X | | is input channel | +| | [0].out | | | X | X | | is output channel | +| | [0].interfaceType | X | | X | X | | interface type (PCIe, USB, HDMI, ...) | +| | [0].status | X | | X | X | | printer status (e.g. idle) | + +#### 13. Network related functions | Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | | --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | @@ -539,7 +555,8 @@ I also created a nice little command line tool called [mmon][mmon-github-url] (m | | ms | X | X | X | X | X | response time in ms | | si.inetLatency(host, cb) | : number | X | X | X | X | X | response-time (ms) to external resource
host parameter is optional (default 8.8.8.8)| -#### 13. Wifi networks +#### 14. Wifi networks + | Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | | --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | | si.wifiNetworks(cb) | [{...}] | X | | X | X | | array of available wifi networks | @@ -554,7 +571,20 @@ I also created a nice little command line tool called [mmon][mmon-github-url] (m | | [0].wpaFlags | X | | X | X | | array of WPA flags | | | [0].rsnFlags | X | | | | | array of RDN flags | -#### 14. Docker +#### 15. Bluetooth + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.bluetoothDevices(cb) | [{...}] | X | | X | X | | ... | +| | [0].device | X | | X | X | | device name | +| | [0].name | X | | X | X | | name | +| | [0].address | X | | X | X | | address | +| | [0].batteryPercent | X | | X | X | | battery level percent | +| | [0].manufacturer | X | | X | X | | manufacturer | +| | [0].type | X | | X | X | | typoe of bluetooth device | +| | [0].connected | X | | X | X | | is connected | + +#### 16. Docker | Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | | --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | @@ -652,7 +682,7 @@ I also created a nice little command line tool called [mmon][mmon-github-url] (m | | [0].command | X | X | X | X | X | command and arguments | | si.dockerAll(cb) | {...} | X | X | X | X | X | list of all containers including their stats
and processes in one single array | -#### 15. Virtual Box +#### 17. Virtual Box | Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | | --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | diff --git a/docs/audio.html b/docs/audio.html new file mode 100644 index 0000000..184563c --- /dev/null +++ b/docs/audio.html @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + systeminformation + + + + + + +
+
+ +
+
+
+
Audio
+
+

In this section you will learn how to get information about detected audio devices or interfaces. Results might differ on different platforms as not everything is available/detectable on each platform:

+

For function reference and examples we assume, that we imported systeminformation as follows:

+
const si = require('systeminformation');
+

Detected Audio Devices

+

All functions in this section return a promise or can be called with a callback function (parameter cb in the function reference)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionResult objectLinuxBSDMacWinSunComments
si.audio(cb)[{...}]XXXaudio informatiom
[0].idXXXinternal ID
[0].nameXXXaudio name
[0].manufacturerXXXmanufacturer
[0].revisionXrevision
[0].driverXdriver
[0].defaultXis default
[0].inXis input channel
[0].outXis output channel
[0].interfaceTypeXXinterface type (built-in, PCI, HDMI, USB...)
[0].statusXXstatus
+
Example
+
const si = require('systeminformation');
+si.audio().then(data => console.log(data));
+
+[
+  {
+    id: 0,
+    name: 'MacBook Microphone',
+    manufacturer: 'Apple Inc.',
+    revision: null,
+    driver: null,
+    default: true,
+    channel: 'Built-In',
+    in: true,
+    out: false,
+    status: 'online'
+  },
+  {
+    id: 1,
+    name: 'MacBook Speaker',
+    manufacturer: 'Apple Inc.',
+    revision: null,
+    driver: null,
+    default: true,
+    channel: 'Built-In',
+    in: false,
+    out: true,
+    status: 'online'
+  }
+]
+
+
+
+
+
+
+ +
+ + + + + + diff --git a/docs/bluetooth.html b/docs/bluetooth.html new file mode 100644 index 0000000..b357c39 --- /dev/null +++ b/docs/bluetooth.html @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + systeminformation + + + + + + +
+
+ +
+
+
+
Bluetooth
+
+

In this section you will learn how to get information about detected bluetooth devices. Results might differ on different platforms as not everything is available/detectable on each platform:

+

For function reference and examples we assume, that we imported systeminformation as follows:

+
const si = require('systeminformation');
+

Detected Bluetooth Devices

+

All functions in this section return a promise or can be called with a callback function (parameter cb in the function reference)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionResult objectLinuxBSDMacWinSunComments
si.bluetoothDevices(cb)[{...}]XXXbluetooth device informatiom
[0].deviceXXXdevice name
[0].nameXXXname
[0].addressXXXaddress
[0].batteryPercentXXXbattery level percent
[0].manufacturerXXXmanufacturer
[0].typeXtype of device
[0].connectedXconnected
+
Example
+
const si = require('systeminformation');
+si.bluetoothDevices().then(data => console.log(data));
+
+[
+  {
+    id: 0,
+    name: 'MacBook Microphone',
+    manufacturer: 'Apple Inc.',
+    revision: null,
+    driver: null,
+    default: true,
+    channel: 'Built-In',
+    in: true,
+    out: false,
+    status: 'online'
+  },
+  {
+    id: 1,
+    name: 'MacBook Speaker',
+    manufacturer: 'Apple Inc.',
+    revision: null,
+    driver: null,
+    default: true,
+    channel: 'Built-In',
+    in: false,
+    out: true,
+    status: 'online'
+  }
+]
+
+
+
+
+
+
+ +
+ + + + + + diff --git a/lib/audio.js b/lib/audio.js index a2ab8a4..57ebe4b 100644 --- a/lib/audio.js +++ b/lib/audio.js @@ -28,16 +28,6 @@ const _openbsd = (_platform === 'openbsd'); const _netbsd = (_platform === 'netbsd'); const _sunos = (_platform === 'sunos'); -const winPrinterStatus = { - 1: 'Other', - 2: 'Unknown', - 3: 'Idle', - 4: 'Printing', - 5: 'Warmup', - 6: 'Stopped Printing', - 7: 'Offline', -} - function getLinuxAudioPci() { let cmd = 'lspci -v 2>/dev/null' let result = []; @@ -66,13 +56,12 @@ function parseLinuxAudioPciMM(lines, audioPCI) { result.id = slotId; result.name = util.getValue(lines, 'SDevice'); - result.type = util.getValue(lines, 'Class'); + // result.type = util.getValue(lines, 'Class'); result.manufacturer = util.getValue(lines, 'SVendor'); - result.builtIn = null; - result.default = null; result.revision = util.getValue(lines, 'Rev'); result.driver = pciMatch && pciMatch.length === 1 && pciMatch[0].driver ? pciMatch[0].driver : ''; - result.channel = null; + result.default = null; + result.channel = 'PCIe'; result.in = null; result.out = null; result.status = 'online'; @@ -80,40 +69,11 @@ function parseLinuxAudioPciMM(lines, audioPCI) { return result; } -function parseLinuxLpstatPrinter(lines, id) { - const result = {}; - result.id = id - result.name = util.getValue(lines, 'Description', ':', true); - result.model = lines.length > 0 && lines[0] ? lines[0].split(' ')[0] : ''; - result.uri = null; - result.uuid = null - result.status = lines.length > 0 && lines[0] ? (lines[0].indexOf(' idle') > 0 ? 'idle' : (lines[0].indexOf(' printing') > 0 ? 'printing' : 'unknown')) : null - result.local = util.getValue(lines, 'Location', ':', true).toLowerCase().startsWith('local'); - result.default = null; - result.shared = util.getValue(lines, 'Shared', ' ').toLowerCase().startsWith('yes'); - - return result; -} - -// id -// name -// type -// manufacturer -// builtIn -// default -// revision -// driver -// (onboard) -// #channels -// in -// out -// interfaceType HDMI, Display - Port, Build -in, USB, PCIe(darwin: coreaudio_device_transport) -// status - function parseDarwinChannel(str) { let result = ''; if (str.indexOf('builtin') >= 0) { result = 'Built-In'; } + if (str.indexOf('extern') >= 0) { result = 'Audio-Jack'; } if (str.indexOf('hdmi') >= 0) { result = 'HDMI'; } if (str.indexOf('displayport') >= 0) { result = 'Display-Port'; } if (str.indexOf('usb') >= 0) { result = 'USB'; } @@ -124,14 +84,14 @@ function parseDarwinChannel(str) { function parseDarwinAudio(audioObject, id) { const result = {}; - const channelStr = (audioObject.coreaudio_device_transport || ''); + const channelStr = ((audioObject.coreaudio_device_transport || '') + ' ' + (audioObject._name || '')).toLowerCase(); result.id = id; result.name = audioObject._name result.manufacturer = audioObject.coreaudio_device_manufacturer; - result.default = !!(audioObject.coreaudio_default_audio_input_device || '') || !!(audioObject.coreaudio_default_audio_output_device || ''); result.revision = null; result.driver = null; + result.default = !!(audioObject.coreaudio_default_audio_input_device || '') || !!(audioObject.coreaudio_default_audio_output_device || ''); result.channel = parseDarwinChannel(channelStr); result.in = !!(audioObject.coreaudio_device_input || '') result.out = !!(audioObject.coreaudio_device_output || '') @@ -140,19 +100,21 @@ function parseDarwinAudio(audioObject, id) { return result; } -function parseWindowsAudio(lines, id) { +function parseWindowsAudio(lines) { const result = {}; - const status = parseInt(util.getValue(lines, 'PrinterStatus', '='), 10); + const status = util.getValue(lines, 'StatusInfo', '='); + // const description = util.getValue(lines, 'Description', '='); - result.id = id; + result.id = util.getValue(lines, 'DeviceID', '='); // PNPDeviceID?? result.name = util.getValue(lines, 'name', '='); - result.model = util.getValue(lines, 'DriverName', '='); - result.uri = null; - result.uuid = null - result.status = winPrinterStatus[status] ? winPrinterStatus[status] : null; - result.local = util.getValue(lines, 'Local', '=') === 'TRUE'; - result.default = util.getValue(lines, 'Default', '=') === 'TRUE'; - result.shared = util.getValue(lines, 'Shared', '=') === 'TRUE'; + 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; } @@ -177,33 +139,10 @@ function audio(callback) { } } } - if (result.length === 0) { - if (_linux) { - cmd = 'export LC_ALL=C; lpstat -lp 2>/dev/null; unset LC_ALL'; - // lpstat - exec(cmd, function (error, stdout) { - const parts = ('\n' + stdout.toString()).split('\nprinter '); - for (let i = 1; i < parts.length; i++) { - const printers = parseLinuxLpstatPrinter(parts[i].split('\n'), i); - result.push(printers); - } - }); - if (callback) { - callback(result); - } - resolve(result); - } else { - if (callback) { - callback(result); - } - resolve(result); - } - } else { - if (callback) { - callback(result); - } - resolve(result); + if (callback) { + callback(result); } + resolve(result); }); } if (_darwin) { @@ -229,7 +168,7 @@ function audio(callback) { }); } if (_windows) { - util.wmic('printer get /value', function (error, stdout) { + 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++) { diff --git a/lib/bluetooth.js b/lib/bluetooth.js new file mode 100644 index 0000000..b26527f --- /dev/null +++ b/lib/bluetooth.js @@ -0,0 +1,205 @@ +'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; diff --git a/lib/index.d.ts b/lib/index.d.ts index 407a633..a008d58 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -732,15 +732,25 @@ export namespace Systeminformation { interface AudioData { id: number | string; - bus: number; - deviceId: number; name: string; - type: string; - removable: boolean; - vendor: string; manufacturer: string; - maxPower: string; - serialNumber: string; + default: boolean; + revision: string; + driver: string; + in: boolean; + out: boolean; + interfaceType: string; + status: string; + } + + interface BluetoothDeviceData { + device: string; + name: string; + address: string; + batteryPercent: number; + manufacturer: string; + type: string; + connected: boolean; } // 10. "Get All at once" - functions @@ -760,7 +770,6 @@ export namespace Systeminformation { memLayout: MemLayoutData[]; diskLayout: DiskLayoutData[]; } - } export function version(): string; @@ -827,6 +836,8 @@ export function usb(cb?: (data: Systeminformation.UsbData[]) => any): Promise any): Promise; +export function bluetoothDevices(cb?: (data: Systeminformation.BlockDevicesData[]) => any): Promise; + export function getStaticData(cb?: (data: Systeminformation.StaticData) => any): Promise; export function getDynamicData(srv?: string, iface?: string, cb?: (data: any) => any): Promise; export function getAllData(srv?: string, iface?: string, cb?: (data: any) => any): Promise; diff --git a/lib/index.js b/lib/index.js index ce05876..1d92c4b 100755 --- a/lib/index.js +++ b/lib/index.js @@ -40,6 +40,7 @@ const vbox = require('./virtualbox'); const printer = require('./printer'); const usb = require('./usb'); const audio = require('./audio'); +const bluetooth = require('./bluetooth'); let _platform = process.platform; const _windows = (_platform === 'win32'); @@ -493,6 +494,7 @@ exports.printer = printer.printer; exports.usb = usb.usb; exports.audio = audio.audio; +exports.bluetoothDevices = bluetooth.bluetoothDevices; exports.getStaticData = getStaticData; exports.getDynamicData = getDynamicData; diff --git a/test/si.js b/test/si.js index 33f4489..d4f3b74 100644 --- a/test/si.js +++ b/test/si.js @@ -16,7 +16,7 @@ function test(f) { else if (f === 'f') { si.fsSize().then(data => { if (data !== null) { resolve({ data, title: 'File System' }); } else { resolve('not_supported') } }) } else if (f === 'F') { si.fsStats().then(data => { if (data !== null) { resolve({ data, title: 'FS Stats' }); } else { resolve('not_supported') } }) } else if (f === 'g') { si.graphics().then(data => { if (data !== null) { resolve({ data, title: 'Graphics' }); } else { resolve('not_supported') } }) } - else if (f === 'h') { si.bluetooth().then(data => { if (data !== null) { resolve({ data, title: 'Bluetooth' }); } else { resolve('not_supported') } }) } + else if (f === 'h') { si.bluetoothDevices().then(data => { if (data !== null) { resolve({ data, title: 'Bluetooth' }); } else { resolve('not_supported') } }) } else if (f === 'i') { si.inetLatency().then(data => { if (data !== null) { resolve({ data, title: 'Internet Latency' }); } else { resolve('not_supported') } }) } else if (f === 'I') { si.inetChecksite('www.plus-innovations.com').then(data => { if (data !== null) { resolve({ data, title: 'Internet Check Site' }); } else { resolve('not_supported') } }) } else if (f === 'l') { si.cpuCurrentspeed().then(data => { if (data !== null) { resolve({ data, title: 'CPU Current Speed' }); } else { resolve('not_supported') } }) }