/* * WinJS Contrib v2.1.0.6 * licensed under MIT license (see http://opensource.org/licenses/MIT) * sources available at https://github.com/gleborgne/winjscontrib */ var WinJSContrib = WinJSContrib || {}; WinJSContrib.UPnP = WinJSContrib.UPnP || {}; (function (global) { WinJSContrib.UPnP.deviceTypes = { ContentDirectory1: 'urn:schemas-upnp-org:service:ContentDirectory:1', ContentDirectory2: 'urn:schemas-upnp-org:service:ContentDirectory:2', ContentDirectory3: 'urn:schemas-upnp-org:service:ContentDirectory:3', ContentDirectory4: 'urn:schemas-upnp-org:service:ContentDirectory:4', MediaRenderer1: 'urn:schemas-upnp-org:device:MediaRenderer:1', MediaRenderer2: 'urn:schemas-upnp-org:device:MediaRenderer:2', MediaRenderer3: 'urn:schemas-upnp-org:device:MediaRenderer:3', MediaRenderer4: 'urn:schemas-upnp-org:device:MediaRenderer:4', MediaServer1: 'urn:schemas-upnp-org:device:MediaServer:1', MediaServer2: 'urn:schemas-upnp-org:device:MediaServer:2', MediaServer3: 'urn:schemas-upnp-org:device:MediaServer:3', MediaServer4: 'urn:schemas-upnp-org:device:MediaServer:4', AVTransport1:'urn:schemas-upnp-org:service:AVTransport:1', AVTransport2: 'urn:schemas-upnp-org:service:AVTransport:2', ConnectionManager1: 'urn:schemas-upnp-org:service:ConnectionManager:1', ConnectionManager2: 'urn:schemas-upnp-org:service:ConnectionManager:2', ConnectionManager3: 'urn:schemas-upnp-org:service:ConnectionManager:3' }; WinJSContrib.UPnP.devicesDiscovery = function devicesDiscovery(filter, callback, timeout) { var hostName; try { hostName = new Windows.Networking.HostName("239.255.255.250"); } catch (ex) { error({ message: "Invalid host name." }); return; } var devices = new WinJS.Binding.List(); var discovery = { devices: devices, hasError: null, promise: null, }; function exists(url) { var matches = devices.filter(function (d) { return (url == d.deviceDescriptionUrl); }); return matches.length > 0; } function discover(filter) { return new WinJS.Promise(function (complete, error) { var clientSocket = new Windows.Networking.Sockets.DatagramSocket(); function onsuccess(err) { try { if (clientSocket) clientSocket.close(); } catch (ex) { } complete(devices); } function onerror(err) { try { if (clientSocket) clientSocket.close(); } catch (ex) { } error(err); } clientSocket.onmessagereceived = function (args) { var e = args; var reader = args.getDataReader(); var count = reader.unconsumedBufferLength; var data = reader.readString(count); var completed = false; if (data) { var items = data.split('\n'); items.forEach(function (item) { if (!completed && item.toLowerCase().indexOf('location:') == 0) { var url = item.substr(9, item.length - 9); completed = true; if (!exists(url)) { WinJS.xhr({ url: url }).then(function (r) { var rawdevice = xmlToJson(r.responseXML); var device = rawdevice.root.device; device.deviceDescriptionUrl = url; device.rootUrl = url.substr(0, url.indexOf('/', 8)); if (!exists(url)) { devices.push(new WinJSContrib.UPnP.UPnPDevice(device)); } if (callback) callback(device, url); }, function (err) { console.error('calling device failed'); console.error(err); }); } } }); } } clientSocket.getOutputStreamAsync(hostName, "1900").then(function (outputStream) { var message = "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "ST:" + (filter || "upnp:rootdevice") + "\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:3\r\n\r\n"; var writer = new Windows.Storage.Streams.DataWriter(outputStream); writer.unicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8; writer.writeString(message); return writer.storeAsync(); }).then(function () { return WinJS.Promise.timeout(timeout || 20000); }).then(onsuccess, onerror); }).then(function (res) { discovery.hasError = false; }, function (err) { discovery.hasError = true; discovery.error = err; }); } var promises = []; if (!filter || (typeof filter == 'string')) { promises.push(discover(filter)); } else if (filter && typeof filter != 'string' && filter.length) { for (var i = 0 ; i < filter.length; i++) { promises.push(discover(filter[i])); } } discovery.promise = WinJS.Promise.join(promises); return discovery; } function endsWith(text, search) { return text.indexOf(search) == text.length - search.length; } WinJSContrib.UPnP.getContentType = function (url, extension) { var lowerUrl = url.toLowerCase(); if (extension == "mp3" || endsWith(lowerUrl, ".mp3")) return "audio/mp3"; if (extension == "mp4" || endsWith(lowerUrl, ".mp4")) return "video/mp4"; if (extension == "avi" || endsWith(lowerUrl, ".avi")) return "video/avi"; if (extension == "mkv" || endsWith(lowerUrl, ".mkv")) return "video/x-matroska"; } function xmlToJson(xml) { // Create the return object var obj = {}; if (xml.nodeType == 1) { // element // do attributes if (xml.attributes.length > 0) { obj["@attributes"] = {}; for (var j = 0; j < xml.attributes.length; j++) { var attribute = xml.attributes.item(j); obj["@attributes"][attribute.nodeName] = attribute.nodeValue; } } } else if (xml.nodeType == 3) { // text obj = xml.nodeValue; } // do children if (xml.hasChildNodes()) { var properties = 0; for (var i = 0; i < xml.childNodes.length; i++) { var item = xml.childNodes.item(i); var nodeName = item.nodeName; if (typeof (obj[nodeName]) == "undefined") { var tmp = xmlToJson(item); var colonIdx = nodeName.indexOf(':'); if (colonIdx > 0) { var clearNode = nodeName.substr(colonIdx + 1, nodeName.length - colonIdx - 1); } if (clearNode && !obj[clearNode]) { obj[clearNode] = tmp; } else { obj[nodeName] = tmp; } properties++; } else { if (typeof (obj[nodeName].push) == "undefined") { var old = obj[nodeName]; obj[nodeName] = []; obj[nodeName].push(old); } obj[nodeName].push(xmlToJson(item)); properties++; } } if (obj['#text'] && typeof obj['#text'] == 'string') { obj['#text'] = obj['#text'].trim(); } if (properties == 1 && obj['#text'] && typeof obj['#text'] == 'string') { obj = obj['#text']; } } for (var e in obj["@attributes"]) { if (!obj[e]) obj[e] = obj["@attributes"][e]; } return obj; }; /** * UPnP device * @class */ WinJSContrib.UPnP.UPnPDevice = function (deviceObj) { for (var p in deviceObj) { this[p] = deviceObj[p]; } } WinJSContrib.UPnP.UPnPDevice.prototype.getService = function (serviceType) { var device = this; var service = null; if (typeof serviceType == 'string') serviceType = [serviceType]; if (device.serviceList && device.serviceList.service) { service = device.serviceList.service.filter(function (s) { return serviceType.indexOf(s.serviceType) >= 0; })[0]; if (!service && device.deviceList) { device.deviceList.device.forEach(function (childdevice) { var childService = childdevice.serviceList.service.filter(function (s) { return serviceType.indexOf(s.serviceType) >= 0; })[0]; if (childService) service = childService; }); } } if (service) { service.rootUrl = device.rootUrl; service.url = device.rootUrl + service.controlURL; } return service; } WinJSContrib.UPnP.UPnPDevice.prototype.getAVTransport = function () { var service = this.getService([WinJSContrib.UPnP.deviceTypes.AVTransport1, WinJSContrib.UPnP.deviceTypes.AVTransport2]); if (service) { return new WinJSContrib.UPnP.UPnPAVTransport(service); } } WinJSContrib.UPnP.UPnPDevice.prototype.getContentDirectory = function () { var service = this.getService([WinJSContrib.UPnP.deviceTypes.ContentDirectory1, WinJSContrib.UPnP.deviceTypes.ContentDirectory2, WinJSContrib.UPnP.deviceTypes.ContentDirectory3, WinJSContrib.UPnP.deviceTypes.ContentDirectory4]); if (service) { return new WinJSContrib.UPnP.UPnPContentDirectory(service); } } WinJSContrib.UPnP.UPnPDevice.prototype.getConnectionManager = function () { var service = this.getService([WinJSContrib.UPnP.deviceTypes.ConnectionManager1, WinJSContrib.UPnP.deviceTypes.ConnectionManager2, WinJSContrib.UPnP.deviceTypes.ConnectionManager3]); if (service) { return new WinJSContrib.UPnP.UPnPConnectionManager(service); } } /** * Code largely from https://github.com/richtr/plug.play.js */ WinJSContrib.UPnP.UPnPService = function (serviceObj, opts) { this.options = opts || { debug: false }; this.svc = serviceObj; // API stub this.action = function () { }; //if (!this.svc || !(this.svc instanceof NetworkService)) { // this.debugLog('First argument provided in constructor MUST be a valid NetworkService object'); // return this; //} //if (!this.svc.type || this.svc.type.indexOf('upnp:') !== 0) { // this.debugLog('First argument provided in constructor MUST be a _UPnP_ NetworkService object'); // return this; //} //this.svcType = this.svc.type.replace('upnp:', ''); this.svcType = this.svc.serviceType; this.svcUrl = this.svc.url; var self = this; // Full API this.action = function (upnpAction, upnpParameters, callback) { return new WinJS.Promise(function (promiseComplete, promiseError) { // Handle .action( name, callback ) if (!callback && Object.prototype.toString.call(upnpParameters) == '[object Function]') { callback = upnpParameters; upnpParameters = {}; } // Generate a callback stub if a Function has not been provided if (!callback || Object.prototype.toString.call(callback) !== '[object Function]') { callback = function (e, response) { }; } // Create a UPnP XML Request message var svcMsg = self.createRequest(self.svcType, upnpAction, upnpParameters); // Send the UPnP XML Request message to the current service self.sendRequest(self.svcType, upnpAction, self.svcUrl, svcMsg, function (e, xmlResponse) { if (e !== null) { callback.apply(self, [e, null]); promiseError(e); return; } // Parse UPnP XML Response message self.handleResponse(upnpAction, xmlResponse.responseXML || "", upnpParameters, function (e, upnpResponse) { // Fire callback callback.apply(self, [e, upnpResponse]); // Fire promise if (e !== null) { promiseError(e); } else { promiseComplete(upnpResponse); } }); }); }); } }; WinJSContrib.UPnP.UPnPService.prototype.constructor = WinJSContrib.UPnP.UPnPService; // UPnP XML MESSAGING TEMPLATES WinJSContrib.UPnP.UPnPService.prototype.requestTemplate = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" " + "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" + "\t<s:Body>\n" + "\t\t<u:{ACTION_NAME} xmlns:u=\"{ACTION_TYPE}\">\n" + "{ACTION_VARS}" + "\t\t</u:{ACTION_NAME}>\n" + "\t</s:Body>\n" + "</s:Envelope>\n"; // UPNP PRIMITIVE VARIABLE TYPE CHECKERS // from http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf WinJSContrib.UPnP.UPnPService.prototype.types = { "i1": function (val, allowedValues, toNative) { var i8 = new Int8Array(1); i8[0] = val; return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(i8[0], allowedValues); }, "i2": function (val, allowedValues, toNative) { var i16 = new Int16Array(1); i16[0] = val; return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(i16[0], allowedValues); }, "i4": function (val, allowedValues, toNative) { var i32 = new Int32Array(1); i32[0] = val; return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(i32[0], allowedValues); }, "ui1": function (val, allowedValues, toNative) { var ui8 = new Uint8Array(1); ui8[0] = val; return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(ui8[0], allowedValues); }, "ui2": function (val, allowedValues, toNative) { var ui16 = new Uint16Array(1); ui16[0] = val; return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(ui16[0], allowedValues); }, "ui4": function (val, allowedValues, toNative) { var ui32 = new Uint32Array(1); ui32[0] = val; return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(ui32[0], allowedValues); }, "int": function (val, allowedValues, toNative) { if (val === undefined || val === null || isNaN(val)) { val = 0; } else if (typeof val !== 'number') { if (val === true) { val = 1; } else { val = parseInt(val + "", 10); } } if (!toNative) { val = expandExponential(val + ""); } if (isNaN(val)) val = 0; return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(val, allowedValues); }, "r4": function (val, allowedValues, toNative) { var f32 = new Float32Array(1); f32[0] = val; if (!toNative) { return f32[0] ? expandExponential(f32[0] + "") : "0"; } return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(f32[0], allowedValues); }, "r8": function (val, allowedValues, toNative) { var f64 = new Float64Array(1); f64[0] = val; if (!toNative) { return f64[0] ? expandExponential(f64[0] + "") : "0"; } return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(f64[0], allowedValues); }, "number": function (val, allowedValues, toNative) { return WinJSContrib.UPnP.UPnPService.prototype.types['r8'](val, allowedValues, toNative); }, "fixed_14_4": function (val, allowedValues, toNative) { return WinJSContrib.UPnP.UPnPService.prototype.types['float'](val, allowedValues, toNative); }, "float": function (val, allowedValues, toNative) { var _float = parseFloat(val); if (!toNative) { return _float ? expandExponential(_float + "") : "0"; } return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(_float, allowedValues); }, "char": function (val, allowedValues, toNative) { if (val === undefined || val === null || val === "" || (val + "") == "NaN" || val instanceof RegExp) { val = ""; } else if (val === false || val === true || val == "true" || val == "yes" || val == "false" || val == "no") { val = (val === true || val == "true" || val == "yes") ? "1" : "0"; } else { val = val.toString(); } if (val.length > 0) { val = WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(val, allowedValues); return val.charAt(0); } return val; }, "string": function (val, allowedValues, toNative) { if (val === undefined || val === null || val === "" || (val + "") == "NaN" || val instanceof RegExp) { val = ""; } val = val + ""; val = WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(val, allowedValues); return val; }, "xmlObj": function (val, allowedValues, toNative) { if (val === undefined || val === null || val === "" || (val + "") == "NaN" || val instanceof RegExp) { val = ""; } val = val + ""; val = WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(val, allowedValues); try { var s = ''; var currentPos = val.indexOf('&'); while (currentPos > 0) { var idx = val.indexOf(';', currentPos); if (idx < 0 || (idx - currentPos) > 5) { val = val.substr(0, currentPos) + "&" + val.substr(currentPos + 1, val.length - (currentPos + 1)); } currentPos = val.indexOf('&', currentPos + 1); } var xml = parseXML(val); return xmlToJson(xml); } catch (exception) { console.error(exception); return val; } }, "date": function (val, allowedValues, toNative) { return WinJSContrib.UPnP.UPnPService.prototype.types['dateTime'](val, allowedValues, toNative); }, "dateTime": function (val, allowedValues, toNative) { if (toNative) { // Parse to ECMA Date object if (Object.prototype.toString.call(val) == '[object String]') { val = new Date(parseDate(val)); if (isNaN(val)) val = ""; } else if (Object.prototype.toString.call(val) == '[object Date]') { val = val; } } else { // Parse to ISO-8601 string if (Object.prototype.toString.call(val) == '[object String]') { val = val + ""; } else if (Object.prototype.toString.call(val) == '[object Date]') { if (val.toString() == "Invalid Date") { // opera specific functionality val = ""; } else { val = val.toISOString(); } } } val = WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(val, allowedValues); return val; }, "dateTime_tz": function (val, allowedValues, toNative) { return WinJSContrib.UPnP.UPnPService.prototype.types['dateTime'](val, allowedValues, toNative); }, "time": function (val, allowedValues, toNative) { return WinJSContrib.UPnP.UPnPService.prototype.types['dateTime'](val, allowedValues, toNative); }, "time_tz": function (val, allowedValues, toNative) { return WinJSContrib.UPnP.UPnPService.prototype.types['dateTime'](val, allowedValues, toNative); }, "boolean": function (val, allowedValues, toNative) { if (val == 'false' || val == 'f' || val == 'no' || val == 'n' || val instanceof RegExp || val <= 0 || !val) { if (toNative) { val = false; } else { val = "0"; } } else { if (toNative) { val = true; } else { val = "1"; } } return val; }, "bin_base64": function (val, allowedValues, toNative) { if (val === undefined || val === null || val === "" || (val + "") == "NaN" || val instanceof RegExp) { val = ""; } if (val === true || val === false) { val = val === true ? "1" : "0"; } if (toNative) { // convert to string val = atob(val + ""); } else { // convert to base64 val = btoa(val + ""); } return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(val, allowedValues); }, "bin_hex": function (val, allowedValues, toNative) { if (val === undefined || val === null || val === "" || (val + "") == "NaN" || val instanceof RegExp) { val = ""; } if (val === true || val === false) { val = val === true ? "1" : "0"; } if (toNative) { // convert to string val = htoa(val + ""); } else { // convert to hex val = atoh(val + ""); } return WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues(val, allowedValues); }, "uri": function (val, allowedValues, toNative) { if (toNative) { val = decodeURI(val + ""); } return WinJSContrib.UPnP.UPnPService.prototype.types['string'](val, allowedValues); // No URI syntax checking }, "uuid": function (val, allowedValues, toNative) { return WinJSContrib.UPnP.UPnPService.prototype.types['string'](val, allowedValues); // No UUID syntax checking } }; // Override .types toString function for (var type in WinJSContrib.UPnP.UPnPService.prototype.types) { WinJSContrib.UPnP.UPnPService.prototype.types[type]._name = type; WinJSContrib.UPnP.UPnPService.prototype.types[type]["toString"] = function () { return this._name; }; } WinJSContrib.UPnP.UPnPService.prototype.createRequest = function (upnpServiceType, upnpActionName, parameters) { parameters = this.formatParameters(parameters); this.debugLog('Creating UPnP XML Control Message for [' + upnpActionName + '] with parameters:'); this.debugLog(JSON ? JSON.stringify(parameters) : null); // Build basic part of XML request var svcMsg = this.requestTemplate.replace(/\{ACTION_TYPE\}/g, encodeXML(trim(upnpServiceType))); svcMsg = svcMsg.replace(/\{ACTION_NAME\}/g, encodeXML(trim(upnpActionName))); var svcInParams = parameters['request'], svcInParams_xml = ""; if (svcInParams) { // Generate XML parameters syntax for (var svcParam in svcInParams) { // Perform type checking sanitization if (svcInParams[svcParam]['type'] !== undefined && svcInParams[svcParam]['type'] !== null) { // get the primitive sanitization function var typeCheck = this.types[svcInParams[svcParam]['type']]; if (typeCheck) { svcInParams[svcParam]['value'] = typeCheck(svcInParams[svcParam]['value'], svcInParams[svcParam]['allowedValueList'], false); } } if (svcInParams[svcParam]['value'] === null || svcInParams[svcParam]['value'] === undefined) { svcInParams[svcParam]['value'] = ""; // pass through empty value } svcInParams_xml += "\t\t\t<" + svcParam + ">" + encodeXML(trim(svcInParams[svcParam]['value'] + "")) + "</" + svcParam + ">\n"; } } // Add ACTION_VARS, if any svcMsg = svcMsg.replace(/\{ACTION_VARS\}/g, svcInParams_xml); // DEBUG this.debugLog('[REQUEST] (SOAPAction: ' + upnpServiceType + '#' + upnpActionName + ') ' + svcMsg); return svcMsg; }; WinJSContrib.UPnP.UPnPService.prototype.sendRequest = function (upnpServiceType, upnpActionName, upnpEndpointURL, upnpRequestXML, onUPnPResponse) { var soapaction = '\"' + upnpServiceType.replace(/[\"\#]/g, '') + '#' + upnpActionName.replace(/[\"\#]/g, '') + '\"' var httpClient = new Windows.Web.Http.HttpClient(); var uri = new Windows.Foundation.Uri(upnpEndpointURL); var request = new Windows.Web.Http.HttpRequestMessage(Windows.Web.Http.HttpMethod.post, uri); request.headers.append('SOAPACTION', soapaction); //request.headers.append('Content-Type', 'text/xml; charset="utf-8";'); request.content = new Windows.Web.Http.HttpStringContent(upnpRequestXML, Windows.Storage.Streams.UnicodeEncoding.utf8, "text/xml"); httpClient.sendRequestAsync(request).then(function (r) { var content = r.content.readAsStringAsync().then(function (text) { var xml = parseXML(text); var r = { status: 200, responseText: text, responseXML: xml }; onUPnPResponse.apply(this, [null, r]); }); }, function (err) { onUPnPResponse.apply(undefined, [new UPnPError('XmlHttpRequest failed'), null]); }); return this; try { var xhr = new XMLHttpRequest(); xhr.open('POST', upnpEndpointURL); xhr.setRequestHeader('Content-Type', 'text/xml; charset="utf-8";'); xhr.setRequestHeader('action', soapaction); xhr.setRequestHeader('zurgl', soapaction); xhr.setRequestHeader('SOAPAction', soapaction); xhr.onabort = xhr.onerror = function () { onUPnPResponse.apply(undefined, [new UPnPError('XmlHttpRequest failed'), null]); //xhrManager.release(xhr); }; var self = this; xhr.onreadystatechange = function () { if (this.readyState < 4) return; if (this.status !== 200) { onUPnPResponse.apply(undefined, [new UPnPError('XmlHttpRequest expected 200 OK. Received ' + this.status + ' response.'), null]); self.debugLog("[ERROR-RESPONSE] " + this.responseText); //xhrManager.release(xhr); return; } self.debugLog("[SUCCESS-RESPONSE] " + this.responseText); onUPnPResponse.apply(this, [null, this]); //xhrManager.release(xhr); }; xhr.send(upnpRequestXML); } catch (e) { onUPnPResponse.apply(undefined, [new UPnPError(e), null]); } return this; // allow method chaining }; WinJSContrib.UPnP.UPnPService.prototype.handleResponse = function (upnpActionName, upnpResponseXML, parameters, onUPnPResolved) { parameters = this.formatParameters(parameters); var svcOutParams = parameters['response']; var responseJSON = {}; // Process all XML response variables var responseContainer = upnpResponseXML.getElementsByTagName ? upnpResponseXML.getElementsByTagName(upnpActionName + "Response") : null; // If we don't have any response variables, try again with the namespace attached (required for lib to work in Firefox) if (responseContainer === null || responseContainer.length <= 0) { responseContainer = upnpResponseXML.getElementsByTagName ? upnpResponseXML.getElementsByTagName("u:" + upnpActionName + "Response") : null; } if (responseContainer !== null && responseContainer.length > 0) { var responseVars = responseContainer[0].childNodes; if (responseVars && responseVars.length > 0) { for (var i = 0, l = responseVars.length; i < l; i++) { var varName = responseVars[i].nodeName; var varVal = responseVars[i].firstChild ? trim(decodeXML(responseVars[i].firstChild.nodeValue + "")) : null; // Perform type checking sanitization if requested if (svcOutParams[varName] && svcOutParams[varName]['type'] !== undefined && svcOutParams[varName]['type'] !== null) { // get the primitive sanitization function var typeCheck = this.types[svcOutParams[varName]['type']]; if (typeCheck) { varVal = typeCheck(varVal, svcOutParams[varName]['allowedValueList'] || null, true); } } responseJSON[varName] = (varVal !== undefined && varVal !== null) ? varVal : ""; } } } // Fire callback with the parsed action response data onUPnPResolved.apply(undefined, [null, { data: responseJSON }]); return this; // allow method chaining }; WinJSContrib.UPnP.UPnPService.prototype.formatParameter = function (parameter) { var param = {}; if (parameter['type'] !== undefined || parameter['value'] !== undefined || parameter['allowedValueList'] !== undefined) { param = parameter; if (parameter['allowedValueList'] !== undefined && typeof parameter['allowedValueList'] !== 'array') { parameter['allowedValueList'] = []; } } else { param = { value: parameter }; } // check type is valid, otherwise discard requested type if (param['type'] !== undefined && param['type'] !== null) { if (!this.types[(param['type'] + "")]) { delete param['type']; } } return param; }; // Normalize the input parameters argument WinJSContrib.UPnP.UPnPService.prototype.formatParameters = function (parameters) { if (parameters === undefined || parameters === null) { parameters = {}; } if (!parameters['request']) { parameters['request'] = {}; } if (!parameters['response']) { parameters['response'] = {}; } for (var param in parameters) { if (param === 'request' || param === 'response') { for (var processParam in parameters[param]) { parameters[param][processParam] = this.formatParameter(parameters[param][processParam]); } } else { // append as 'request' parameter and remove from root object parameters['request'][param] = this.formatParameter(parameters[param]); delete parameters[param]; } } return parameters; }; // ALLOWED VALUE LIST CHECKER WinJSContrib.UPnP.UPnPService.prototype.checkAllowedValues = function (val, allowedValueList) { // Check against allowedValues if provided if (val && allowedValueList && Object.prototype.toString.call(allowedValueList) == '[object Array]') { var matchFound = false; for (var i = 0, l = allowedValueList.length; i < l; i++) { if (val == allowedValueList[i]) { matchFound = true; break; } } if (!matchFound) { return ""; } } return val; }; // UPnP DEBUGGER WinJSContrib.UPnP.UPnPService.prototype.debugLog = function (msg, level) { if ((this.options && this.options.debug)) { console[level || 'log']("Plug.UPnP: " + msg); } }; // UPnP ERROR OBJECT var UPnPError = function (description) { return { 'description': description }; }; function parseXML(text) { return (new DOMParser()).parseFromString(text, "text/xml"); } // *** CUSTOM TYPE FUNCTIONS *** // BASE64 FUNCTIONS // (forked from https://github.com/dankogai/js-base64/blob/master/base64.js) var b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; var b64tab = (function (bin) { var t = {}; for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i; return t; })(b64chars); var cb_encode = function (ccc) { var padlen = [0, 2, 1][ccc.length % 3], ord = ccc.charCodeAt(0) << 16 | ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8) | ((ccc.length > 2 ? ccc.charCodeAt(2) : 0)), chars = [ b64chars.charAt(ord >>> 18), b64chars.charAt((ord >>> 12) & 63), padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63), padlen >= 1 ? '=' : b64chars.charAt(ord & 63) ]; return chars.join(''); }; var cb_decode = function (cccc) { var len = cccc.length, padlen = len % 4, n = (len > 0 ? b64tab[cccc.charAt(0)] << 18 : 0) | (len > 1 ? b64tab[cccc.charAt(1)] << 12 : 0) | (len > 2 ? b64tab[cccc.charAt(2)] << 6 : 0) | (len > 3 ? b64tab[cccc.charAt(3)] : 0), chars = [ String.fromCharCode(n >>> 16), String.fromCharCode((n >>> 8) & 0xff), String.fromCharCode(n & 0xff) ]; chars.length -= [0, 0, 2, 1][padlen]; return chars.join(''); }; var btoa = global.btoa || function (b) { return b.replace(/[\s\S]{1,3}/g, cb_encode); }; var atob = global.atob || function (a) { return a.replace(/[\s\S]{1,4}/g, cb_decode); }; // HEX FUNCTIONS var hexChars = '0123456789abcdef'; var hextab = (function (bin) { var t = {}; for (var i = 0, l = bin.length; i < l; i++) t[i] = bin.charAt(i); return t; })(hexChars); function toHex(n) { var result = '' var start = true; for (var i = 32; i > 0;) { i -= 4; var digit = (n >> i) & 0xf; if (!start || digit != 0) { start = false; result += hextab[digit]; } } return (result == '' ? '0' : result); } function pad(str, len, pad) { var result = str; for (var i = str.length; i < len; i++) { result = pad + result; } return result; } function ntos(n) { n = n.toString(16); if (n.length == 1) n = "0" + n; n = "%" + n; return unescape(n); } function htoa(str) { str = str.toUpperCase().replace(new RegExp("s/[^0-9A-Z]//g")); var result = ""; var nextchar = ""; for (var i = 0; i < str.length; i++) { nextchar += str.charAt(i); if (nextchar.length == 2) { result += ntos(parseInt(nextchar, 16)); nextchar = ""; } } return result; } function atoh(str) { var result = ""; for (var i = 0; i < str.length; i++) { result += pad(toHex(str.charCodeAt(i) & 0xff), 2, '0'); } return result; } // NUMBER FUNCTIONS function expandExponential(str) { return str.replace(/^([+-])?(\d+).?(\d*)[eE]([-+]?\d+)$/, function (x, s, n, f, c) { var l = +c < 0, i = n.length + +c, x = (l ? n : f).length, c = ((c = Math.abs(c)) >= x ? c - x + l : 0), z = (new Array(c + 1)).join("0"), r = n + f; return (s || "") + (l ? r = z + r : r += z).substr(0, i += l ? z.length : 0) + (i < r.length ? "." + r.substr(i) : ""); }); }; // STRING FUNCTIONS var trim = function (str) { if (String.prototype.trim) { return str.trim(); } else { return str.replace(/^\s+|\s+$/g, ""); } }; var encodeXML = function (str) { return str.replace(/\&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/'/g, ''') .replace(/"/g, '"'); }; var decodeXML = function (str) { return str.replace(/\"/g, '"') .replace(/\&\#39;/g, '\'') .replace(/\>/g, '>') .replace(/\</g, '<') .replace(/\&/g, '&'); }; /** * Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601> * © 2011 Colin Snover <http://zetafleet.com> * Released under MIT license. * * 2012 - Modifications to accept times without dates - by Rich Tibbett */ var parseDate = function (date) { var timestamp, struct, minutesOffset = 0, numericKeys = [1, 4, 5, 6, 7, 10, 11]; if (typeof date !== 'string') date += ""; if ((struct = /^((\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:[T\s]?(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{4}))?)?$/.exec(date))) { struct = struct.slice(1, struct.length); // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC for (var i = 0, k; (k = numericKeys[i]) ; ++i) { struct[k] = +struct[k] || 0; } // allow undefined days and months struct[2] = (+struct[2] || 1) - 1; struct[3] = +struct[3] || 1; if (struct[8] !== 'Z' && struct[9] !== undefined) { minutesOffset = ((struct[10] / 100) - ((struct[10] / 100) % 1)) * 60 + (struct[10] % 100); if (struct[9] === '+') { minutesOffset = 0 - minutesOffset; } } timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); } else { timestamp = Date.parse ? Date.parse(date) : NaN; } return timestamp; }; var allowedContentDirectoryTypes = { 'urn:schemas-upnp-org:service:ContentDirectory:1': true, 'urn:schemas-upnp-org:service:ContentDirectory:2': true, 'urn:schemas-upnp-org:service:ContentDirectory:3': true, 'urn:schemas-upnp-org:service:ContentDirectory:4': true }; WinJSContrib.UPnP.UPnPContentDirectory = function (serviceObj, opts) { WinJSContrib.UPnP.UPnPService.apply(this, [serviceObj, opts]); this.upnpType = serviceObj.serviceType; //if (serviceObj.type.indexOf('upnp:') === 0) { // this.upnpType = this.upnpType.replace('upnp:', ''); //} if (!allowedContentDirectoryTypes[this.upnpType]) { console.error("Provided service is not a UPnP Media Server Service"); return; } // *** ContentDirectory:1 methods this.getSystemUpdateId = function (callback) { return this.action('GetSystemUpdateID', { request: {}, response: { Id: { type: this.types.ui4 } } }, callback); }; this.getSearchCapabilities = function (callback) { return this.action('GetSearchCapabilities', { request: {}, response: { SearchCaps: { type: this.types.string } } }, callback); }; this.getSortCapabilities = function (callback) { return this.action('GetSortCapabilities', { request: {}, response: { SortCaps: { type: this.types.string } } }, callback); }; this.browse = function (objectId, browseFlag, filter, startingIndex, requestedCount, sortCriteria, callback) { return this.action('Browse', { request: { ObjectID: { value: objectId || '0', type: this.types.string }, BrowseFlag: { value: browseFlag || 'BrowseDirectChildren', type: this.types.string }, Filter: { value: filter || '*', type: this.types.string }, StartingIndex: { value: startingIndex || '0', type: this.types.ui4 }, RequestedCount: { value: requestedCount || '0', type: this.types.ui4 }, SortCriteria: { value: sortCriteria, type: this.types.string } }, response: { Result: { type: this.types.xmlObj }, NumberReturned: { type: this.types.ui4 }, TotalMatches: { type: this.types.ui4 }, UpdateID: { type: this.types.ui4 } } }, callback); }; this.search = function (objectId, browseFlag, filter, startingIndex, requestedCount, sortCriteria, callback) { return this.action('Search', { request: { ObjectID: { value: objectId || '0', type: this.types.string }, BrowseFlag: { value: browseFlag || 'BrowseDirectChildren', type: this.types.string }, Filter: { value: filter || '*', type: this.types.string }, StartingIndex: { value: startingIndex || '0', type: this.types.ui4 }, RequestedCount: { value: requestedCount || '0', type: this.types.ui4 }, SortCriteria: { value: sortCriteria, type: this.types.string } }, response: { Result: { type: this.types.xmlObj }, NumberReturned: { type: this.types.ui4 }, TotalMatches: { type: this.types.ui4 }, UpdateID: { type: this.types.ui4 } } }, callback); }; // *** ContentDirectory:2 methods if (this.upnpType === 'urn:schemas-upnp-org:service:ContentDirectory:2') { // TODO: GetFeatureList, GetSortExtensionCapabilities, MoveObject } // *** ContentDirectory:3 methods if (this.upnpType === 'urn:schemas-upnp-org:service:ContentDirectory:3') { // TODO } // *** ContentDirectory:4 methods if (this.upnpType === 'urn:schemas-upnp-org:service:ContentDirectory:4') { // TODO } }; WinJSContrib.UPnP.UPnPContentDirectory.prototype = Object.create(WinJSContrib.UPnP.UPnPService.prototype); var allowedConnectionManagerTypes = { 'urn:schemas-upnp-org:service:ConnectionManager:1': true, 'urn:schemas-upnp-org:service:ConnectionManager:2': true, 'urn:schemas-upnp-org:service:ConnectionManager:3': true }; WinJSContrib.UPnP.UPnPConnectionManager = function (serviceObj, opts) { WinJSContrib.UPnP.UPnPService.apply(this, [serviceObj, opts]); this.upnpType = serviceObj.serviceType; //if (serviceObj.type.indexOf('upnp:') === 0) { // this.upnpType = this.upnpType.replace('upnp:', ''); //} if (!allowedConnectionManagerTypes[this.upnpType]) { console.error("Provided service is not a UPnP Service"); return; } // *** ConnectionManager:1 methods this.getCurrentConnectionIDs = function (callback) { return this.action('GetCurrentConnectionIDs', { request: {}, response: { ConnectionIDs: { type: this.types.string } } }, callback); }; this.getCurrentConnectionInfo = function (connectionId, callback) { return this.action('GetCurrentConnectionInfo', { request: { ConnectionID: { value: connectionId, type: this.types.ui4 } }, response: { RcsID: { type: this.types.i4 }, AVTransportID: { type: this.types.i4 }, ProtocolInfo: { type: this.types.string }, PeerConnectionManager: { type: this.types.string }, PeerConnectionID: { type: this.types.i4 }, Direction: { type: this.types.string }, Status: { type: this.types.string } } }, callback); }; this.getProtocolInfo = function (callback) { return this.action('GetProtocolInfo', { request: {}, response: { Source: { type: this.types.string }, Sink: { type: this.types.string } } }, callback); }; // *** ConnectionManager:2 methods if (this.upnpType === 'urn:schemas-upnp-org:service:ConnectionManager:2') { // TODO } }; WinJSContrib.UPnP.UPnPConnectionManager.prototype = Object.create(WinJSContrib.UPnP.UPnPService.prototype); var allowedAVTransportTypes = { 'urn:schemas-upnp-org:service:AVTransport:1': true, 'urn:schemas-upnp-org:service:AVTransport:2': true }; WinJSContrib.UPnP.UPnPAVTransport = function (serviceObj, opts) { WinJSContrib.UPnP.UPnPService.apply(this, [serviceObj, opts]); this.upnpType = serviceObj.serviceType; //if (serviceObj.type.indexOf('upnp:') === 0) { // this.upnpType = this.upnpType.replace('upnp:', ''); //} if (!allowedAVTransportTypes[this.upnpType]) { console.error("Provided service is not a UPnP Media Renderer Service"); return; } // *** AVTransport:1 methods this.getDeviceCapabilities = function (instanceId, callback) { return this.action('GetDeviceCapabilities', { request: { InstanceID: { value: instanceId, type: this.types.ui4 } }, response: { PlayMedia: { type: this.types.string }, RecMedia: { type: this.types.string }, RecQualityModes: { type: this.types.string } } }, callback); }; this.getMediaInfo = function (instanceId, callback) { return this.action('GetMediaInfo', { request: { InstanceID: { value: instanceId, type: this.types.ui4 } }, response: { NrTracks: { type: this.types.ui4 }, MediaDuration: { type: this.types.string }, CurrentURI: { type: this.types.string }, CurrentURIMetaData: { type: this.types.string }, NextURI: { type: this.types.string }, NextURIMetaData: { type: this.types.string }, PlayMedium: { type: this.types.string }, RecordMedium: { type: this.types.string }, WriteStatus: { type: this.types.string } } }, callback); }; this.getPositionInfo = function (instanceId, callback) { return this.action('GetPositionInfo', { request: { InstanceID: { value: instanceId, type: this.types.ui4 } }, response: { Track: { type: this.types.ui4 }, TrackDuration: { type: this.types.string }, TrackMetaData: { type: this.types.xmlObj }, TrackURI: { type: this.types.string }, RelTime: { type: this.types.string }, AbsTime: { type: this.types.string }, RelCount: { type: this.types.i4 }, AbsCount: { type: this.types.i4 } } }, callback); }; this.getTransportInfo = function (instanceId, callback) { return this.action('GetTransportInfo', { request: { InstanceID: { value: instanceId, type: this.types.ui4 } }, response: { CurrentTransportState: { type: this.types.string }, CurrentTransportStatus: { type: this.types.string }, CurrentSpeed: { type: this.types.string } } }, callback); }; this.getTransportSettings = function (instanceId, callback) { return this.action('GetTransportSettings', { request: { InstanceID: { value: instanceId, type: this.types.ui4 } }, response: { PlayMode: { type: this.types.ui4 }, RecQualityMode: { type: this.types.string } } }, callback); }; this.setAVTransportURI = function (instanceId, currentURI, currentURIMetaData, callback) { return this.action('SetAVTransportURI', { request: { InstanceID: { value: instanceId, type: this.types.ui4 }, CurrentURI: { value: currentURI, type: this.types.string }, CurrentURIMetaData: { value: currentURIMetaData, type: this.types.string } }, response: {} }, callback); }; this.play = function (instanceId, speed, callback) { return this.action('Play', { request: { InstanceID: { value: instanceId, type: this.types.ui4 }, Speed: { value: speed || '1', type: this.types.string } }, response: {} }, callback); }; this.pause = function (instanceId, callback) { return this.action('Pause', { request: { InstanceID: { value: instanceId, type: this.types.ui4 } }, response: {} }, callback); }; this.stop = function (instanceId, callback) { return this.action('Stop', { request: { InstanceID: { value: instanceId, type: this.types.ui4 } }, response: {} }, callback); }; this.next = function (instanceId, callback) { return this.action('Next', { request: { InstanceID: { value: instanceId, type: this.types.ui4 } }, response: {} }, callback); }; this.previous = function (instanceId, callback) { return this.action('Previous', { request: { InstanceID: { value: instanceId, type: this.types.ui4 } }, response: {} }, callback); }; this.seek = function (instanceId, unit, target, callback) { return this.action('Seek', { request: { InstanceID: { value: instanceId, type: this.types.ui4 }, Unit: { value: unit, type: this.types.string }, Target: { value: target, type: this.types.string } }, response: {} }, callback); }; // *** AVTransport:2 methods if (this.upnpType === 'urn:schemas-upnp-org:service:AVTransport:2') { // TODO } }; WinJSContrib.UPnP.UPnPAVTransport.prototype = Object.create(WinJSContrib.UPnP.UPnPService.prototype); var allowedRenderingControlTypes = { 'urn:schemas-upnp-org:service:RenderingControl:1': true, 'urn:schemas-upnp-org:service:RenderingControl:2': true, 'urn:schemas-upnp-org:service:RenderingControl:3': true }; WinJSContrib.UPnP.UPnPRenderingControl = function (serviceObj, opts) { WinJSContrib.UPnP.UPnPService.apply(this, [serviceObj, opts]); this.upnpType = serviceObj.serviceType; //if (serviceObj.type.indexOf('upnp:') === 0) { // this.upnpType = this.upnpType.replace('upnp:', ''); //} if (!allowedRenderingControlTypes[this.upnpType]) { console.error("Provided service is not a UPnP Media Renderer Service"); return; } // *** RenderingControl:1 methods this.listPresets = function (instanceId, callback) { return this.action('ListPresets', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentPresetNameList: { type: this.types.string } } }, callback); }; this.selectPreset = function (instanceId, presetName, callback) { return this.action('SelectPreset', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, PresetName: { value: presetName || "FactoryDefaults", type: this.types.string } } }, callback); }; this.getBrightness = function (instanceId, callback) { return this.action('GetBrightness', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentBrightness: { type: this.types.ui2 } } }, callback); }; this.setBrightness = function (instanceId, desiredBrightness, callback) { return this.action('SetBrightness', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredBrightness: { value: desiredBrightness, type: this.types.ui2 } } }, callback); }; this.getContrast = function (instanceId, callback) { return this.action('GetContrast', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentContrast: { type: this.types.ui2 } } }, callback); }; this.setContrast = function (instanceId, desiredContrast, callback) { return this.action('SetContrast', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredContrast: { value: desiredContrast, type: this.types.ui2 } } }, callback); }; this.getSharpness = function (instanceId, callback) { return this.action('GetSharpness', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentSharpness: { type: this.types.ui2 } } }, callback); }; this.setSharpness = function (instanceId, desiredSharpness, callback) { return this.action('SetSharpness', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredSharpness: { value: desiredSharpness, type: this.types.ui2 } } }, callback); }; this.getRedVideoGain = function (instanceId, callback) { return this.action('GetRedVideoGain', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentRedVideoGain: { type: this.types.ui2 } } }, callback); }; this.setRedVideoGain = function (instanceId, desiredRedVideoGain, callback) { return this.action('SetRedVideoGain', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredRedVideoGain: { value: desiredRedVideoGain, type: this.types.ui2 } } }, callback); }; this.getGreenVideoGain = function (instanceId, callback) { return this.action('GetGreenVideoGain', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentGreenVideoGain: { type: this.types.ui2 } } }, callback); }; this.setGreenVideoGain = function (instanceId, desiredGreenVideoGain, callback) { return this.action('SetGreenVideoGain', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredGreenVideoGain: { value: desiredGreenVideoGain, type: this.types.ui2 } } }, callback); }; this.getBlueVideoGain = function (instanceId, callback) { return this.action('GetBlueVideoGain', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentBlueVideoGain: { type: this.types.ui2 } } }, callback); }; this.setBlueVideoGain = function (instanceId, desiredBlueVideoGain, callback) { return this.action('SetBlueVideoGain', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredBlueVideoGain: { value: desiredBlueVideoGain, type: this.types.ui2 } } }, callback); }; this.getRedVideoBlackLevel = function (instanceId, callback) { return this.action('GetRedVideoBlackLevel', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentRedVideoBlackLevel: { type: this.types.ui2 } } }, callback); }; this.setRedVideoBlackLevel = function (instanceId, desiredRedVideoBlackLevel, callback) { return this.action('SetRedVideoBlackLevel', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredRedVideoBlackLevel: { value: desiredRedVideoBlackLevel, type: this.types.ui2 } } }, callback); }; this.getGreenVideoBlackLevel = function (instanceId, callback) { return this.action('GetGreenVideoBlackLevel', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentGreenVideoBlackLevel: { type: this.types.ui2 } } }, callback); }; this.setGreenVideoBlackLevel = function (instanceId, desiredGreenVideoBlackLevel, callback) { return this.action('SetGreenVideoBlackLevel', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredGreenVideoBlackLevel: { value: desiredGreenVideoBlackLevel, type: this.types.ui2 } } }, callback); }; this.getBlueVideoBlackLevel = function (instanceId, callback) { return this.action('GetBlueVideoBlackLevel', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentBlueVideoBlackLevel: { type: this.types.ui2 } } }, callback); }; this.setBlueVideoBlackLevel = function (instanceId, desiredBlueVideoBlackLevel, callback) { return this.action('SetBlueVideoBlackLevel', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredBlueVideoBlackLevel: { value: desiredBlueVideoBlackLevel, type: this.types.ui2 } } }, callback); }; this.getBlueVideoBlackLevel = function (instanceId, callback) { return this.action('GetBlueVideoBlackLevel', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentBlueVideoBlackLevel: { type: this.types.ui2 } } }, callback); }; this.getColorTemperature = function (instanceId, callback) { return this.action('GetColorTemperature', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentColorTemperature: { type: this.types.ui2 } } }, callback); }; this.setColorTemperature = function (instanceId, desiredColorTemperature, callback) { return this.action('SetColorTemperature', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredColorTemperature: { value: desiredColorTemperature, type: this.types.ui2 } } }, callback); }; this.getHorizontalKeystone = function (instanceId, callback) { return this.action('GetHorizontalKeystone', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentHorizontalKeystone: { type: this.types.i2 } } }, callback); }; this.setHorizontalKeystone = function (instanceId, desiredHorizontalKeystone, callback) { return this.action('SetHorizontalKeystone', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredHorizontalKeystone: { value: desiredHorizontalKeystone, type: this.types.i2 } } }, callback); }; this.getVerticalKeystone = function (instanceId, callback) { return this.action('GetVerticalKeystone', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentVerticalKeystone: { type: this.types.i2 } } }, callback); }; this.setVerticalKeystone = function (instanceId, desiredVerticalKeystone, callback) { return this.action('SetVerticalKeystone', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredVerticalKeystone: { value: desiredVerticalKeystone, type: this.types.i2 } } }, callback); }; this.getMute = function (instanceId, channel, callback) { return this.action('GetMute', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, Channel: { value: channel || 'Master', type: this.types.string } }, response: { CurrentMute: { type: this.types.boolean } } }, callback); }; this.setMute = function (instanceId, channel, desiredMute, callback) { return this.action('SetMute', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, Channel: { value: channel || 'Master', type: this.types.string }, DesiredMute: { value: desiredMute, type: this.types.boolean } } }, callback); }; this.getVolume = function (instanceId, channel, callback) { return this.action('GetVolume', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, Channel: { value: channel || 'Master', type: this.types.string } }, response: { CurrentVolume: { type: this.types.ui2 } } }, callback); }; this.setVolume = function (instanceId, channel, desiredVolume, callback) { return this.action('SetVolume', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, Channel: { value: channel || 'Master', type: this.types.string }, DesiredVolume: { value: desiredVolume, type: this.types.ui2 } } }, callback); }; this.getVolumeDB = function (instanceId, channel, callback) { return this.action('GetVolumeDB', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, Channel: { value: channel || 'Master', type: this.types.string } }, response: { CurrentVolume: { type: this.types.i2 } } }, callback); }; this.setVolumeDB = function (instanceId, channel, desiredVolume, callback) { return this.action('SetVolumeDB', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, Channel: { value: channel || 'Master', type: this.types.string }, DesiredVolume: { value: desiredVolume, type: this.types.i2 } } }, callback); }; this.getVolumeDBRange = function (instanceId, channel, callback) { return this.action('GetVolumeDBRange', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, Channel: { value: channel || 'Master', type: this.types.string } }, response: { MinValue: { type: this.types.i2 }, MaxValue: { type: this.types.i2 }, } }, callback); }; this.getLoudness = function (instanceId, channel, callback) { return this.action('GetLoudness', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, Channel: { value: channel || 'Master', type: this.types.string } }, response: { CurrentLoudness: { type: this.types.boolean } } }, callback); }; this.setLoudness = function (instanceId, channel, desiredLoudness, callback) { return this.action('SetLoudness', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, Channel: { value: channel || 'Master', type: this.types.string }, DesiredLoudness: { value: desiredLoudness, type: this.types.boolean } } }, callback); }; // *** RenderingControl:2 methods if (this.upnpType === 'urn:schemas-upnp-org:service:RenderingControl:2' || this.upnpType === 'urn:schemas-upnp-org:service:RenderingControl:3') { this.getStateVariables = function (instanceId, stateVariableList, callback) { return this.action('GetStateVariables', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, StateVariableList: { value: stateVariableList, type: this.types.string } }, response: { StateVariableValuePairs: { type: this.types.string } } }, callback); }; this.setStateVariables = function (instanceId, renderingControlUDN, serviceType, serviceId, stateVariableValuePairs, callback) { return this.action('SetStateVariables', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, RenderingControlUDN: { value: renderingControlUDN, type: this.types.string }, ServiceType: { value: serviceType, type: this.types.string }, ServiceId: { value: serviceId, type: this.types.string }, StateVariableValuePairs: { value: stateVariableValuePairs, type: this.types.string } }, response: { StateVariableList: { type: this.types.string } } }, callback); }; } // *** RenderingControl:3 methods if (this.upnpType === 'urn:schemas-upnp-org:service:RenderingControl:3') { this.getAllowedTransforms = function (instanceId, callback) { return this.action('GetAllowedTransforms', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentAllowedTransformSettings: { type: this.types.string } } }, callback); }; this.getTransforms = function (instanceId, callback) { return this.action('GetTransforms', { request: { InstanceId: { value: instanceId, type: this.types.ui4 } }, response: { CurrentTransformValues: { type: this.types.string } } }, callback); }; this.setTransforms = function (instanceId, desiredTransformValues, callback) { return this.action('SetTransforms', { request: { InstanceId: { value: instanceId, type: this.types.ui4 }, DesiredTransformValues: { value: desiredTransformValues, type: this.types.string } } }, callback); }; this.getAllAvailableTransforms = function (instanceId, callback) { return this.action('GetAllAvailableTransforms', { response: { AllAllowedTransformSettings: { type: this.types.string } } }, callback); }; this.getAllowedDefaultTransforms = function (instanceId, callback) { return this.action('GetAllowedDefaultTransforms', { response: { AllowedDefaultTransformSettings: { type: this.types.string } } }, callback); }; this.getDefaultTransforms = function (instanceId, callback) { return this.action('GetDefaultTransforms', { response: { CurrentDefaultTransformSettings: { type: this.types.string } } }, callback); }; this.setDefaultTransforms = function (instanceId, desiredDefaultTransformSettings, callback) { return this.action('SetDefaultTransforms', { request: { DesiredDefaultTransformSettings: { value: desiredDefaultTransformSettings, type: this.types.string } } }, callback); }; } }; WinJSContrib.UPnP.UPnPRenderingControl.prototype = Object.create(WinJSContrib.UPnP.UPnPService.prototype); })(window);