osjs-hardware-provider
Version:
A service provider that adds hardware acces.
468 lines (454 loc) • 20.2 kB
JavaScript
const child_process = require('child_process');
const {EventEmitter} = require('@osjs/event-emitter');
const PulseAudioClient = require('paclient');
const si = require('systeminformation');
class ServiceProvider {
constructor(core,options={}) {
this.core = core;
this.options = options;
}
provides() {
return [
'hw'
];
}
async init() {
const pa = new PulseAudioClient();
[
'none',
'all',
'sink',
'source',
'sinkInput',
'sourceOutput',
'module',
'client',
'sampleCache',
'global',
'card'
].forEach(event => {
const cb = (...args) => {
console.log({
name: event,
arguments: args
});
core.broadcast('hw/audio/'+event,...args);
};
pa.on(event,cb);
});
[
['/hardware/battery/get',req => si.battery(),'get'],
['/hardware/cpu/get',req => si.cpu(),'get'],
['/hardware/cpu/flags',req => si.cpuFlags(),'get'],
['/hardware/cpu/speed',req => si.cpuCurrentspeed(),'get'],
['/hardware/cpu/temp',req => si.cpuTemperature(),'get'],
['/hardware/graphics/get',req => si.graphics(),'get'],
['/hardware/net/connections',req => si.networkConnections(),'get'],
['/hardware/net/interfaces',req => si.networkInterfaces(),'get'],
['/hardware/net/getDefaultInterfaceName',req => si.networkInterfaceDefault(),'get'],
['/hardware/net/stats/:iface',req => si.networkStats(req.params.iface),'get'],
['/hardware/mem/get',req => si.mem(),'get'],
['/hardware/mem/layout',req => si.memLayout(),'get'],
['/hardware/storage/devices',req => si.blockDevices(),'get'],
['/hardware/storage/fs',req => si.fsSize(),'get'],
['/hardware/storage/layout',req => si.diskLayout(),'get'],
['/hardware/storage/io',req => si.disksIO(),'get'],
['/hardware/storage/stat',req => si.fsStats(),'get'],
['/hardware/sys/get',req => si.system(),'get'],
['/hardware/sys/bios',req => si.bios(),'get'],
['/hardware/sys/baseboard',req => si.baseboard(),'get']
].forEach(([endpoint,fn,method]) => {
this.core.app[method](endpoint,(req,res) => {
if(method == 'ws') {
fn(req,res);
} else {
fn(req).then(data => {
res.json(data);
}).catch(err => {
res.json({ type: 'error', stack: err.stack, name: err.name, message: err.message });
});
}
});
});
let audio = {};
this.core.singleton('hw',() => ({
audio,
battery: { get: si.battery },
cpu: { get: si.cpu, flags: si.cpuFlags, speed: si.cpuCurrentspeed, temp: si.cpuTemperature },
graphics: { get: si.graphics },
net: {
connections: si.networkConnections,
interfaces: si.networkInterfaces,
getDefaultInterfaceName: si.networkInterfaceDefault,
stats: si.networkStats
},
mem: { get: si.mem, layout: si.memLayout },
storage: { devices: si.blockDevices, fs: si.fsSize, layout: si.diskLayout, io: si.disksIO, stat: si.fsStats },
sys: { get: si.system, bios: si.bios, baseboard: si.baseboard }
}));
pa.on('ready',() => {
pa.subscribe('all',err => {
if(err) throw err;
});
[
['/hardware/audio/getCards',req => new Promise((resolve,reject) => {
pa.getCards((err,sink) => {
if(err) return reject(err);
resolve(sink);
});
}),'get'],
['/hardware/audio/getClients',req => new Promise((resolve,reject) => {
pa.getClients((err,info) => {
if(err) return reject(err);
resolve(info);
});
}),'get'],
['/hardware/audio/getModules',req => new Promise((resolve,reject) => {
pa.getModules((err,info) => {
if(err) return reject(err);
resolve(info);
});
}),'get'],
['/hardware/audio/getSinks',req => new Promise((resolve,reject) => {
pa.getSinks((err,results) => {
if(err) return reject(err);
let list = child_process.execSync('pactl list sinks').toString().split('\n');
for(let i = 0;i < results.length;i++) {
let start = list.indexOf('Sink #'+i);
let lines = list.slice(start,list.indexOf('',start));
for(let line of lines) {
if(line.startsWith('\tVolume: ')) results[i].volume = parseInt(line.split('/')[1].split(' ').join('').replace('%',''));
}
}
resolve(results);
});
}),'get'],
['/hardware/audio/getSinkInputByIndex/:index',req => new Promise((resolve,reject) => {
pa.getSinkInputByIndex(parseInt(req.params.index),(err,sink) => {
if(err) return reject(err);
resolve(sink);
});
}),'get'],
['/hardware/audio/getSources',req => new Promise((resolve,reject) => {
pa.getSources((err,results) => {
if(err) return reject(err);
let list = child_process.execSync('pactl list sources').toString().split('\n');
for(let i = 0;i < results.length;i++) {
let start = list.indexOf('Source #'+i);
let lines = list.slice(start,list.indexOf('',start));
for(let line of lines) {
if(line.startsWith('\tVolume: ')) results[i].volume = parseInt(line.split('/')[1].split(' ').join('').replace('%',''));
}
}
resolve(results);
});
}),'get'],
['/hardware/audio/getSourceOutputByIndex/:index',req => new Promise((resolve,reject) => {
pa.getSourceOutputByIndex(parseInt(req.params.index),(err,sink) => {
if(err) return reject(err);
resolve(sink);
});
}),'get'],
['/hardware/audio/setSinkVolumes/:index',req => new Promise((resolve,reject) => {
resolve(child_process.execSync('pactl set-sink-volume '+req.params.index+' '+req.query.value));
}),'get'],
['/hardware/audio/setSourceVolumes/:index',req => new Promise((resolve,reject) => {
resolve(child_process.execSync('pactl set-source-volume '+req.params.index+' '+req.query.value));
}),'get'],
['/hardware/audio/setSinkMute/:index/:enable',req => new Promise((resolve,reject) => {
pa.setSinkMute(parseInt(req.params.index),req.params.enable == 'true',err => {
if(err) return reject(err);
resolve({ value: req.params.enable == 'true' });
});
}),'get'],
['/hardware/audio/setSourceMute/:index/:enable',req => new Promise((resolve,reject) => {
pa.setSinkMute(parseInt(req.params.index),req.params.enable == 'true',err => {
if(err) return reject(err);
resolve({ value: req.params.enable == 'true' });
});
}),'get'],
['/hardware/audio/setSinkSuspend/:index/:enable',req => new Promise((resolve,reject) => {
pa.setSinkSuspend(parseInt(req.params.index),req.params.enable == 'true',err => {
if(err) return reject(err);
resolve({ value: req.params.enable == 'true' });
});
}),'get'],
['/hardware/audio/setSourceSuspend/:index/:enable',req => new Promise((resolve,reject) => {
pa.setSourceSuspend(parseInt(req.params.index),req.params.enable == 'true',err => {
if(err) return reject(err);
resolve({ value: req.params.enable == 'true' });
});
}),'get'],
['/hardware/audio/setDefaultSinkByName/:name',req => new Promise((resolve,reject) => {
pa.setDefaultSinkByName(req.params.name,err => {
if(err) return reject(err);
resolve({ value: req.params.name });
});
}),'get'],
['/hardware/audio/setDefaultSourceByName/:name',req => new Promise((resolve,reject) => {
pa.setDefaultSourceByName(req.params.name,err => {
if(err) return reject(err);
resolve({ value: req.params.name });
});
}),'get'],
['/hardware/audio/killClientByIndex/:index',req => new Promise((resolve,reject) => {
pa.setSinkSuspend(parseInt(req.params.index),err => {
if(err) return reject(err);
resolve({ value: parseInt(req.params.index) });
});
}),'get'],
['/hardware/audio/killSinkInputByIndex/:index',req => new Promise((resolve,reject) => {
pa.killSinkInputByIndex(parseInt(req.params.index),err => {
if(err) return reject(err);
resolve({ value: parseInt(req.params.index) });
});
}),'get'],
['/hardware/audio/killSourceOutputByIndex/:index',req => new Promise((resolve,reject) => {
pa.killSourceOutputByIndex(parseInt(req.params.index),err => {
if(err) return reject(err);
resolve({ value: parseInt(req.params.index) });
});
}),'get'],
['/hardware/audio/moveSinkInput/:index/:dest',req => new Promise((resolve,reject) => {
pa.moveSinkInput(parseInt(req.params.index),parseInt(req.params.dest),err => {
if(err) return reject(err);
resolve({ index: parseInt(req.params.index), dest: parseInt(req.params.dest) });
});
}),'get'],
['/hardware/audio/moveSourceOutput/:index/:dest',req => new Promise((resolve,reject) => {
pa.moveSourceOutput(parseInt(req.params.index),parseInt(req.params.dest),err => {
if(err) return reject(err);
resolve({ index: parseInt(req.params.index), dest: parseInt(req.params.dest) });
});
}),'get'],
['/hardware/audio/setSinkPort/:index/:name',req => new Promise((resolve,reject) => {
pa.setSinkPort(parseInt(req.params.index),req.params.name,err => {
if(err) return reject(err);
resolve({ value: req.params.name });
});
}),'get'],
['/hardware/audio/setSourcePort/:index/:name',req => new Promise((resolve,reject) => {
pa.setSourcePort(parseInt(req.params.index),req.params.name,err => {
if(err) return reject(err);
resolve({ value: req.params.name });
});
}),'get'],
['/hardware/audio/setCardProfile/:index/:name',req => new Promise((resolve,reject) => {
pa.setCardProfile(parseInt(req.params.index),req.params.name,err => {
if(err) return reject(err);
resolve({ value: req.params.name });
});
}),'get'],
['/hardware/audio/updateClientProperties/:props/:mode',req => new Promise((resolve,reject) => {
pa.updateClientProperties(JSON.parse(props),req.params.mode,err => {
if(err) return reject(err);
resolve(JSON.parse(props));
});
}),'get'],
['/hardware/audio/removeClientProperties/:props',req => new Promise((resolve,reject) => {
req.params.props = req.params.props.split(',');
for(let i = 0;i < req.params.props.length;i++) req.params.props[i] = parseInt(req.params.props[i]);
pa.removeClientProperties(req.params.props,err => {
if(err) return reject(err);
resolve(req.params.props);
});
}),'get'],
['/hardware/audio/pactl',req => new Promise((resolve,reject) => {
let output = child_process.execSync('pactl '+req.query.cmd).toString();
let lines = output.split('\n');
resolve({ output, lines });
}),'get']
].forEach(([endpoint,fn,method]) => {
this.core.app[method](endpoint,(req,res) => {
if(method == 'ws') {
fn(req,res);
} else {
fn(req).then(data => {
res.json(data);
}).catch(err => {
res.json({ type: 'error', stack: err.stack, name: err.name, message: err.message });
});
}
});
});
audio = {
events: audioEvents,
getCards: cb => new Promise((resolve,reject) => {
pa.getCards((err,results) => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(results) : cb(null,results);
});
}),
getClients: cb => new Promise((resolve,reject) => {
pa.getClients((err,results) => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(results) : cb(null,results);
});
}),
getModules: cb => new Promise((resolve,reject) => {
pa.getModules((err,results) => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(results) : cb(null,results);
});
}),
getSinks: cb => new Promise((resolve,reject) => {
pa.getSinks((err,results) => {
if(err) return cb == undefined ? reject(err) : cb(err);
let list = child_process.execSync('pactl list sinks').toString().split('\n');
for(let i = 0;i < results.length;i++) {
let start = list.indexOf('Sink #'+i);
let lines = list.slice(start,list.indexOf('',start));
for(let line of lines) {
if(line.startsWith('\tVolume: ')) results[i].volume = parseInt(line.split('/')[1].split(' ').join('').replace('%',''));
}
}
cb == undefined ? resolve(results) : cb(null,results);
});
}),
getSinkInputByIndex: (index,cb) => new Promise((resolve,reject) => {
pa.getSinkInputByIndex(index,(err,results) => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(results) : cb(null,results);
});
}),
getSources: cb => new Promise((resolve,reject) => {
pa.getSources((err,results) => {
if(err) return cb == undefined ? reject(err) : cb(err);
let list = child_process.execSync('pactl list sources').toString().split('\n');
for(let i = 0;i < results.length;i++) {
let start = list.indexOf('Source #'+i);
let lines = list.slice(start,list.indexOf('',start));
for(let line of lines) {
if(line.startsWith('\tVolume: ')) results[i].volume = parseInt(line.split('/')[1].split(' ').join('').replace('%',''));
}
}
cb == undefined ? resolve(results) : cb(null,results);
});
}),
getSourceOutputByIndex: (index,cb) => new Promise((resolve,reject) => {
pa.getSourceOutputByIndex(index,(err,results) => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(results) : cb(null,results);
});
}),
setSinkVolumes: (index,volumes,cb) => new Promise((resolve,reject) => {
pa.setSinkVolumes(index,volumes,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(volumes) : cb(null,volumes);
});
}),
setSourceVolumes: (index,volumes,cb) => new Promise((resolve,reject) => {
pa.setSourceVolumes(index,volumes,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(volumes) : cb(null,volumes);
});
}),
setSinkMute: (index,enable,cb) => new Promise((resolve,reject) => {
pa.setSinkMute(index,enable,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(enable) : cb(null,enable);
});
}),
setSourceMute: (index,enable,cb) => new Promise((resolve,reject) => {
pa.setSourceMute(index,enable,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(enable) : cb(null,enable);
});
}),
setSinkSuspend: (index,enable,cb) => new Promise((resolve,reject) => {
pa.setSinkSuspend(index,enable,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(enable) : cb(null,enable);
});
}),
setSourceSuspend: (index,enable,cb) => new Promise((resolve,reject) => {
pa.setSourceSuspend(index,enable,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(enable) : cb(null,enable);
});
}),
setDefaultSinkByName: (name,cb) => new Promise((resolve,reject) => {
pa.setDefaultSinkByName(name,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(name) : cb(null,enable);
});
}),
setDefaultSourceByName: (name,cb) => new Promise((resolve,reject) => {
pa.setDefaultSourceByName(name,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(name) : cb(null,name);
});
}),
killClientByIndex: (index,cb) => new Promise((resolve,reject) => {
pa.killClientByIndex(index,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(index) : cb(null,index);
});
}),
killSinkInputByIndex: (index,cb) => new Promise((resolve,reject) => {
pa.killSinkInputByIndex(index,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(index) : cb(null,index);
});
}),
killSourceOutputByIndex: (index,cb) => new Promise((resolve,reject) => {
pa.killSourceOutputByIndex(index,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(index) : cb(null,index);
});
}),
moveSinkInput: (index,dest,cb) => new Promise((resolve,reject) => {
pa.moveSinkInput(index,dest,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve({ index, dest }) : cb(null,{ index, dest });
});
}),
moveSourceOutput: (index,dest,cb) => new Promise((resolve,reject) => {
pa.moveSourceOutput(index,dest,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve({ index, dest }) : cb(null,{ index, dest });
});
}),
setSinkPort: (index,name,cb) => new Promise((resolve,reject) => {
pa.setSinkPort(index,name,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(name) : cb(null,name);
});
}),
setSourcePort: (index,name,cb) => new Promise((resolve,reject) => {
pa.setSourcePort(index,name,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(name) : cb(null,name);
});
}),
setCardProfile: (index,name,cb) => new Promise((resolve,reject) => {
pa.setCardProfile(index,name,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(name) : cb(null,name);
});
}),
updateClientProperties: (props,mode,cb) => new Promise((resolve,reject) => {
pa.updateClientProperties(props,mode,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(props) : cb(null,props);
});
}),
removeClientProperties: (props,cb) => new Promise((resolve,reject) => {
pa.removeClientProperties(props,err => {
if(err) return cb == undefined ? reject(err) : cb(err);
cb == undefined ? resolve(props) : cb(null,props);
});
}),
pactl: (commands,cb) => new Promise((resolve,reject) => {
let output = child_process.execSync('pactl '+req.query.cmd).toString();
let lines = output.split('\n');
cb == undefined ? resolve({ output, lines }) : cb(null,{ output, lines });
})
};
});
pa.connect();
}
start() {}
destroy() {}
}
module.exports = ServiceProvider;