UNPKG

soundtouch

Version:
387 lines (317 loc) 11.3 kB
var http = require('http'); var parser = require('./utils/xmltojson'); var request = require('request'); var WebSocketClient = require('websocket').client; var KEYS = require('./utils/types').Keys; var SOURCES = require('./utils/types').Source; /** * * Device should at least contain * { * name: 'Kitchen', * ip: '', * mac_address: '' * } * * @param device * @constructor */ var SoundTouchAPI = function(device) { device.url = "http://" + device.ip + ":" + device.port; device.ws_url = "ws://" + device.ip + ":" + '8080'; this.device = device; this.name = device.name; this.socket = { source: undefined }; }; SoundTouchAPI.prototype.getDevice = function() { return this.device; }; SoundTouchAPI.prototype.getMetaData = function() { return this.device; }; SoundTouchAPI.prototype.getNowPlaying = function(handler) { this._getForDevice("now_playing", handler); }; SoundTouchAPI.prototype.getTrackInfo = function(handler) { this._getForDevice("trackInfo", handler); }; SoundTouchAPI.prototype.getPresets = function(handler) { this._getForDevice("presets", handler); }; SoundTouchAPI.prototype.getSources = function(handler) { this._getForDevice("sources", handler); }; SoundTouchAPI.prototype.getInfo = function(handler) { this._getForDevice("info", handler); }; SoundTouchAPI.prototype.isAlive = function(handler) { this.getNowPlaying(function(json){ if (json == undefined) { handler(false); return; } var isAlive = json.nowPlaying.source != SOURCES.STANDBY; if (isAlive) { isAlive = json.nowPlaying.playStatus == 'PLAY_STATE'; } handler(isAlive); }); }; SoundTouchAPI.prototype.isPoweredOn = function(handler) { this.getNowPlaying(function(json){ if (json == undefined) { handler(false); return; } var isAlive = json.nowPlaying.source != SOURCES.STANDBY; handler(isAlive); }); }; SoundTouchAPI.prototype.getVolume = function(handler) { this._getForDevice("volume", handler); }; SoundTouchAPI.prototype.setVolume = function(volume, handler) { var data = "<volume>" + volume + "</volume>"; this._setForDevice("volume", data, handler); }; SoundTouchAPI.prototype.select = function(source, type, sourceAccount, location, handler) { if (source == undefined) { throw new Error("Source is not optional, provide a source from the SOURCES list."); } var data = '<ContentItem source="' + source + '" type="' + type + '" sourceAccount="' + sourceAccount + '" location="' + location + '">' + '<itemName>' + 'Select using API' + '</itemName>' + '</ContentItem>'; this._setForDevice("select", data, handler); }; SoundTouchAPI.prototype.setName = function(name, handler) { var data = "<name>" + name + "</name>"; this._setForDevice("name", data, handler); }; SoundTouchAPI.prototype.play = function(handler) { this.pressKey(KEYS.PLAY, handler); }; SoundTouchAPI.prototype.stop = function(handler) { this.pressKey(KEYS.STOP, handler); }; SoundTouchAPI.prototype.pause = function(handler) { this.pressKey(KEYS.PAUSE, handler); }; SoundTouchAPI.prototype.playPause = function(handler) { this.pressKey(KEYS.PLAY_PAUSE, handler); }; SoundTouchAPI.prototype.power = function(handler) { this.pressKey(KEYS.POWER, handler); }; SoundTouchAPI.prototype.powerOn = function(handler) { var api = this; api.isPoweredOn(function(isPoweredOn) { if (!isPoweredOn) { api.pressKey(KEYS.POWER, function(json) { handler(true); }); } else { handler(false); } }); }; SoundTouchAPI.prototype.powerOnWithVolume = function(volume, handler) { var api = this; api.isPoweredOn(function(isPoweredOn) { if (!isPoweredOn) { api.pressKey(KEYS.POWER, function(json) { api.setVolume(volume, function(json) { handler(true); }); }); } else { handler(false); } }); }; SoundTouchAPI.prototype.powerOff = function(handler) { var api = this; api.isPoweredOn(function(isPoweredOn) { if (isPoweredOn) { api.pressKey(KEYS.POWER, function(json) { handler(true); }); } else { handler(false); } }); }; /** * Select a specific preset (this also turns on the speaker if it was disabled) * * @param presetNumber 1, 2, 3, 4, 5 or 6 * @param handler function (required) */ SoundTouchAPI.prototype.setPreset = function(presetNumber, handler) { this.pressKey("PRESET_" + presetNumber, handler); }; SoundTouchAPI.prototype.pressKey = function(key, handler) { var press = "<key state=\"press\" sender=\"Gabbo\">" + key + "</key>"; var release = "<key state=\"release\" sender=\"Gabbo\">" + key + "</key>"; var api = this; api._setForDevice("key", press, function(json) { api._setForDevice("key", release, handler); }); }; SoundTouchAPI.prototype.getZone = function(handler) { this._getForDevice("getZone", handler); }; SoundTouchAPI.prototype.setZone = function(members, handler) { this._zones('setZone', members, handler); }; SoundTouchAPI.prototype.addZoneSlave = function(members, handler) { this._zones('addZoneSlave', members, handler); }; SoundTouchAPI.prototype.removeZoneSlave = function(members, handler) { this._zones('removeZoneSlave', members, handler); }; SoundTouchAPI.prototype._zones = function(action, members, handler) { var item = {}; // the below line looked like it might have been a copy/paste error from discovery.js? master is undefined here. // item.master = master; var data = '<zone master="' + this.getDevice().txtRecord.MAC + '" senderIPAddress="127.0.0.1">'; for (var i in members) { var member = members[i]; if (i == 0) { item.slaves = []; item.slaves.push(member); data += '<member>' + member + '</member>'; } else { item.slaves.push(member); data += '<member>' + member + '</member>'; } } data += '</zone>'; this._setForDevice(action, data, function(json) { handler(json, item); }); }; /* ****** WEB SOCKETS *********** */ SoundTouchAPI.prototype.socketStart = function(successCallback, errorCallback) { if (this.client != undefined) { return; } this.client = new WebSocketClient(); var api = this; this.client.on('connect', function(connection) { if (successCallback != undefined) successCallback(); connection.on('error', function(error) { if (errorCallback != undefined) errorCallback(error.toString()); }); connection.on('close', function() { api.client = undefined; }); connection.on('message', function(message) { if (message.type === 'utf8') { var json = parser.convert(message.utf8Data); api.socketUpdate(json.updates); } }); }); this.client.on('connectFailed', function(error) { if (errorCallback != undefined) errorCallback(error.toString()); }); this.client.connect(this.getMetaData().ws_url, 'gabbo'); }; SoundTouchAPI.prototype.socketUpdate = function(json) { if (json == undefined) { console.log("Update response is empty"); return; } if (json.nowPlayingUpdated != undefined) { if (this.socket.nowPlayingUpdatedListener != undefined) { this.socket.nowPlayingUpdatedListener(json.nowPlayingUpdated); } //special listener: Powered On // Powered Off var source = json.nowPlayingUpdated.nowPlaying.source; if (this.socket.source != source) { this.socket.source = source; if (this.socket.poweredListener != undefined) { this.socket.poweredListener(source != SOURCES.STANDBY, json.nowPlayingUpdated.nowPlaying); } } //special listener: Playing // Not Playing var playStatus = json.nowPlayingUpdated.nowPlaying.playStatus; if (this.socket.playStatus != playStatus) { this.socket.playStatus = playStatus; if (this.socket.isPlayingListener != undefined) { this.socket.isPlayingListener(playStatus == 'PLAY_STATE', json.nowPlayingUpdated.nowPlaying); } } } else if (json.volumeUpdated != undefined) { this.socket.volume = json.volumeUpdated.volume.actualvolume; if (this.socket.volumeUpdatedListener != undefined) { this.socket.volumeUpdatedListener(json.volumeUpdated.volume.actualvolume, json.volumeUpdated); } } else if (json.connectionStateUpdated != undefined) { if (this.socket.connectionStateUpdatedListener != undefined) { this.socket.connectionStateUpdatedListener(json.connectionStateUpdated); } } else if (json.nowSelectionUpdated != undefined) { if (this.socket.nowSelectionUpdatedListener != undefined) { this.socket.nowSelectionUpdatedListener(json.nowSelectionUpdated); } } else if (json.recentsUpdated != undefined) { if (this.socket.recentsUpdatedListener != undefined) { this.socket.recentsUpdatedListener(json.recentsUpdated); } } else { console.log("Other update", json); } }; SoundTouchAPI.prototype.setNowPlayingUpdatedListener = function(handler) { this.socket.nowPlayingUpdatedListener = handler; }; SoundTouchAPI.prototype.setPoweredListener = function(handler) { this.socket.poweredListener = handler; }; SoundTouchAPI.prototype.setIsPlayingListener = function(handler) { this.socket.isPlayingListener = handler; }; SoundTouchAPI.prototype.setVolumeUpdatedListener = function(handler) { this.socket.volumeUpdatedListener = handler; }; SoundTouchAPI.prototype.setConnectionStateUpdatedListener = function(handler) { this.socket.connectionStateUpdatedListener = handler; }; SoundTouchAPI.prototype.setNowSelectionUpdatedListener = function(handler) { this.socket.nowSelectionUpdatedListener = handler; }; SoundTouchAPI.prototype.setRecentsUpdatedListener = function(handler) { this.socket.recentsUpdatedListener = handler; }; /* ****** UTILITY METHODS *********** */ SoundTouchAPI.prototype._getForDevice = function (action, callback) { var device = this.getMetaData(); http.get(device.url + "/" + action, function(response) { parser.convertResponse(response, function(json) { callback(json); }); }) .on('error', function(e) { console.error("Got error: " + e.message); throw new Error(e.message); }); }; SoundTouchAPI.prototype._setForDevice = function (action, data, handler) { var device = this.getDevice(); var options = { url: device.url + '/' + action, form: data }; request.post(options, function(err, httpResponse, body) { var json = parser.convert(body); handler(json); }); }; module.exports = SoundTouchAPI;