diff --git a/cli/zpinfo.js b/cli/zpinfo.js index 149e011..f22bc67 100755 --- a/cli/zpinfo.js +++ b/cli/zpinfo.js @@ -5,6 +5,28 @@ // // Homebridge plugin for Sonos ZonePlayer. +// TODO: implement commands like `ph` +// +// zp [-hVn] [-t timeout] -H hostname command [argument ...] +// discover [-v] +// description [-s] +// eventlog [-dn] [DeviceService ...] +// play [...] +// pause +// stop +// join [zone] +// leave +// volume [volume] +// mute [true|false] +// bass [bass] +// treble [treble] +// loudness [true|false] +// balance [balance] +// nightSound [true|false] +// speechEnhancement [true|false] +// led [true|false] +// locked [true|false] + 'use strict' const chalk = require('chalk') @@ -15,7 +37,7 @@ const packageJson = require('../package.json') const b = chalk.bold const u = chalk.underline -const usage = `${b('zpinfo')} [${b('-hVlnSs')}] [${b('-t')} ${u('timeout')}] ${u('ip')}` +const usage = `${b('zpinfo')} [${b('-hVdnSs')}] [${b('-t')} ${u('timeout')}] ${u('hostname')} ...` const help = `Sonos ZonePlayer information. Print the device description of a Sonos ZonePlayer as JSON. @@ -45,8 +67,17 @@ Parameters: ${b('-t')} ${u('timeout')}, ${b('--timeout=')}${u('timeout')} Wait for ${u('timeout')} seconds instead of default ${b('15')}. - ${u('ip')} - IPv4 address of the zoneplayer.` + ${u('hostname')} + Hostname or IPv4 address of the zoneplayer.` + +const unsupportedServices = [ + 'ConnectionManager', // No useful information. + 'ContentDirectory', // Not supported by homebridge-zp. + 'MusicServices', // Not supported by homebridge-zp. + 'QPlay', // Doesn't support SUBSCRIBE. + 'Queue', // Not supported by homebridge-zp. + 'SystemProperties' // No useful information. +] class Main extends homebridgeLib.CommandLineTool { constructor () { @@ -70,8 +101,10 @@ class Main extends homebridgeLib.CommandLineTool { parser.option('t', 'timeout', (value, key) => { this.options.timeout = homebridgeLib.OptionParser.toInt(value, 1, 60, true) }) - // parser.parameter('hostname', (value) => { this.options.hostname = value }) - parser.remaining((value) => { this.options.hostnames = value }) + parser.parameter('hostname', (value) => { this.options.hostnames = [value] }) + parser.remaining((value) => { + this.options.hostnames = this.options.hostnames.concat(value) + }) parser.parse() } @@ -84,7 +117,7 @@ class Main extends homebridgeLib.CommandLineTool { this.error(error) } } - process.exit(0) + setImmediate(() => { process.exit(0) }) } async main () { @@ -94,7 +127,7 @@ class Main extends homebridgeLib.CommandLineTool { const jsonOptions = { noWhiteSpace: this.options.noWhiteSpace } const jsonFormatter = new homebridgeLib.JsonFormatter(jsonOptions) - if (this.options.mode) { + if (this.options.mode != null) { this.setOptions({ mode: this.options.mode }) process.on('SIGINT', () => { this.shutdown('SIGINT') }) process.on('SIGTERM', () => { this.shutdown('SIGTERM') }) @@ -109,38 +142,82 @@ class Main extends homebridgeLib.CommandLineTool { } for (const hostname of this.options.hostnames) { - const zpClientOptions = { - hostname: hostname, - timeout: this.options.timeout - } - const zpClient = new ZpClient(zpClientOptions) - this.clients.push(zpClient) - const description = await zpClient.deviceDescription() - if (this.options.mode) { + try { + const zpClient = new ZpClient({ + hostname: hostname, + timeout: this.options.timeout + }) zpClient.on('error', (error) => { - this.error('%s: %s', zpClient.ip, error) + this.error('%s: %s', hostname, error.message) }) zpClient.on('event', (device, service, event) => { - // this.log('%s: %s %s event', zpClient.ip, device, service) + // this.log('%s: %s %s event', hostname, device, service) this.log( - '%s: %s %s event: %s', zpClient.ip, + '%s: %s %s event: %s', hostname, device, service, jsonFormatter.format(event) ) }) - await zpClient.open(this.zpListener) - } else { - if (this.options.scdp) { - const devices = [description.device].concat(description.device.deviceList) - for (const device of devices) { + await zpClient.init() + this.clients.push(zpClient) + const description = await zpClient.get() + if (this.options.mode) { + await zpClient.open(this.zpListener) + const deviceList = [description.device] + .concat(description.device.deviceList) + for (const device of deviceList) { for (const service of device.serviceList) { - service.scpd = await zpClient.deviceDescription(service.scpdUrl) + const serviceName = service.serviceId.split(':')[3] + if (unsupportedServices.includes(serviceName)) { + continue + } + try { + await zpClient.subscribe(service.eventSubUrl) + } catch (error) { + this.error(error) + } + } + } + } else { + if (this.options.scdp) { + const devices = [description.device] + .concat(description.device.deviceList) + for (const device of devices) { + for (const service of device.serviceList) { + service.scpd = await zpClient.get(service.scpdUrl) + } } } + this.print(jsonFormatter.format(description)) + // this.print('zoneAttributes: %s', jsonFormatter.format(await zpClient.getZoneAttributes())) + // this.print('zoneInfo: %s', jsonFormatter.format(await zpClient.getZoneInfo())) + // this.print('zoneGroupAttributes: %s', jsonFormatter.format(await zpClient.getZoneGroupAttributes())) + // this.print('zoneGroupState: %s', jsonFormatter.format(await zpClient.getZoneGroupState())) + // this.print('alarms: %s', jsonFormatter.format(await zpClient.listAlarms())) + // this.print( + // '%s: %s (%s) %s%s', hostname, zpClient.modelName, + // zpClient.modelNumber, zpClient.zoneName, + // zpClient.channel == null ? '' : ' [' + zpClient.channel + ']' + // ) + // if (zpClient.type !== 'sattellite') { + // this.print(' volume: %s', jsonFormatter.format(await zpClient.getVolume())) + // this.print(' mute: %s', jsonFormatter.format(await zpClient.getMute())) + // this.print(' bass: %s', jsonFormatter.format(await zpClient.getBass())) + // this.print(' treble: %s', jsonFormatter.format(await zpClient.getTreble())) + // this.print(' loudness: %s', jsonFormatter.format(await zpClient.getLoudness())) + // } + // if (zpClient.balance) { + // this.print(' balance: %s', jsonFormatter.format(await zpClient.getBalance())) + // } + // if (zpClient.tvIn) { + // this.print(' night sound: %s', jsonFormatter.format(await zpClient.getNightSound())) + // this.print(' speech enhancement: %s', jsonFormatter.format(await zpClient.getSpeechEnhancement())) + // } + // this.print(' led: %s', jsonFormatter.format(await zpClient.getLedState())) + // this.print(' locked: %s', jsonFormatter.format(await zpClient.getButtonLockState())) + // this.print() } - this.print(jsonFormatter.format(description)) - // this.print('alarms: %s', jsonFormatter.format(await zpClient.listAlarms())) - // this.print('volume: %s', jsonFormatter.format(await zpClient.getVolume())) - // this.print('balance: %s', jsonFormatter.format(await zpClient.getBalance())) + } catch (error) { + this.error('%s: %s', hostname, error.message) } } } catch (error) {