From 061df19143f655752d83711d7a1d82d93a0551ad Mon Sep 17 00:00:00 2001 From: Grzegorz Date: Sun, 11 Apr 2021 12:43:36 +0200 Subject: [PATCH] bump 1.5.0 ## [1.5.0] - 2021-04-11 ## Changes - added control over Web Api - code rebuild --- CHANGELOG.md | 5 + LICENSE | 2 +- README.md | 45 +- config.schema.json | 75 ++- index.js | 784 +++++++++++++++++++------------ package-lock.json | 1115 +++++++++++++++++++++++++++++++++++++++++++- package.json | 9 +- sample-config.json | 28 ++ 8 files changed, 1730 insertions(+), 333 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e086f4..cda1a0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.5.0] - 2021-04-11 +## Changes +- added control over Web Api +- code rebuild + ## [1.4.0] - 2021-02-19 ## Changes - code rebuild, use Characteristic.onSet diff --git a/LICENSE b/LICENSE index bd680a0..f168b5c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Grzegorz Kaczor +Copyright (c) 2021 Grzegorz Kaczor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ef5c240..2d1914f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![GitHub pull requests](https://img.shields.io/github/issues-pr/grzegorz914/homebridge-xbox-tv.svg)](https://github.com/grzegorz914/homebridge-xbox-tv/pulls) [![GitHub issues](https://img.shields.io/github/issues/grzegorz914/homebridge-xbox-tv.svg)](https://github.com/grzegorz914/homebridge-xbox-tv/issues) -Homebridge plugin for Microsoft game consoles. Tested with Xbox One X and Xbox Series X. +Homebridge plugin for Microsoft game consoles. Tested with Xbox One X/S and Xbox Series X. @@ -45,7 +45,7 @@ Homebridge plugin for Microsoft game consoles. Tested with Xbox One X and Xbox S * Remote/media control is possible after you go to the RC app on iOS or iPadOS. * Speaker control is possible after you go to RC app on iOS or iPadOS as a `Speaker Service`. * Legacy volume and mute control is possible through extra `lightbulb` dimmer slider or using Siri `Volume Service`, not working with the current API. -* Applications and games can only be read from the device, switching apps or games does not work with the current API. +* Apps, inputs and games can be controled and switched if `xboxWebApiEnabled` and the console is authenticated. In other case the current apps, inputs, games can be displayed. * Siri control.

@@ -64,17 +64,28 @@ Homebridge plugin for Microsoft game consoles. Tested with Xbox One X and Xbox S 2. Console need to allow connect from any 3rd app. *Allow Connections from any device* should be enabled. * Profile & system > Settings > Devices & connections > Remote features > Xbox app preferences. +## Configuration and enable web API control +1. After enable `xboxWebApiEnabled` option, restart the plugin and go to Homebridge console log. +2. Open the authentication URL and login to Your XboxLive account, next accept permision for this app. +3. After accept permiosion for this app copy the part after `?code=` from the response URL and paste it in to the `xboxWebApiToken` in plugin config, save and restart the plugin again, done. + Install and use [Homebridge Config UI X](https://github.com/oznu/homebridge-config-ui-x) plugin to configure this plugin (strongly recomended). The sample configuration can be edited and used manually as an alternative. See the `sample-config.json` file in this repository for an example or copy the example below into your config.json file, making the apporpriate changes before saving it. Be sure to always make a backup copy of your config.json file before making any changes to it. | Key | Description | | --- | --- | | `xboxliveid` | on your console select Profile & system > Settings > System > Console info, listed as **Xbox Live device ID**. *You can only find the Xbox Live device ID in Settings on your console, this is different from your console serial number* | +| `clientID` | Optional free-form for future use | +| `clientSecret` | Optional free-form for future use | +| `xboxWebApiToken` | Required if `xboxWebApiEnabled` enabled.| +| `xboxWebApiEnabled` | Optional, if enabled the console can be controlled using Web Api | | `refreshInterval` | Set the data refresh time in seconds, default is every 5 seconds | | `volumeControl`| Select what a additional volume control mode You want to use (None, Slider, Fan) | | `switchInfoMenu`| If `true` then the `I` button will toggle its behaviour in the Apple Remote in Control Center and `PowerModeSelection` in settings | | `disableLogInfo`| If `true` then disable log info, all values and state will not be displayed in Homebridge log console | | `inputs` | Configure apps/inputs which will be published to and appear in HomeKit app in the device tile as inputs list | | `buttons` | same as inputs but appear in HomeKit.app as extra tile | -| `reference` | open log in homebridge, open app on console and look in the log | +| `reference` | Required to identify current running app, open homebridge console and look in the log or if web Api enabled then all available in `/var/lib/homebridge/xboxTv/installedApps` file. | +| `referenceId` | Optiona, required if U want to use web api and switch inputs/apps, all available data in `/var/lib/homebridge/xboxTv/installedApps` file. | +| `type` | Optional choice from available options | | `manufacturer` | Optional free-form informational data that will be displayed in the Home.app if it is filled in | | `modelName` | Optional free-form informational data that will be displayed in the Home.app if it is filled in | | `serialNumber` | Optional free-form informational data that will be displayed in the Home.app if it is filled in | @@ -92,7 +103,11 @@ Install and use [Homebridge Config UI X](https://github.com/oznu/homebridge-conf "name": "Xbox One", "host": "192.168.1.6", "xboxliveid": "FD0000000000", + "xboxWebApiToken": "M.R5_BAU.be1c3729-8ae5-d62b-5abd-4323c9c96383", + "clientID": "", + "clientSecret": "", "refreshInterval": 5, + "xboxWebApiEnabled": false, "disableLogInfo": false, "volumeControl": 0, "switchInfoMenu": false, @@ -100,111 +115,133 @@ Install and use [Homebridge Config UI X](https://github.com/oznu/homebridge-conf { "name": "Dashboard", "reference": "Xbox.Dashboard_8wekyb3d8bbwe!Xbox.Dashboard.Application", + "referenceId": "", "type": "HOME_SCREEN" }, { "name": "Settings", "reference": "Microsoft.Xbox.Settings_8wekyb3d8bbwe!Xbox.Settings.Application", + "referenceId": "", "type": "OTHER" }, { "name": "Accessory", "reference": "Microsoft.XboxDevices_8wekyb3d8bbwe!App", + "referenceId": "", "type": "OTHER" }, { "name": "Spotify", "reference": "SpotifyAB.SpotifyMusic-forXbox_zpdnekdrzrea0!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "YouTube", "reference": "GoogleInc.YouTube_yfg5n0ztvskxp!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Netflix", "reference": "4DF9E0F8.Netflix_mcm4njqhnhss8!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Telewizja", "reference": "Microsoft.Xbox.LiveTV_8wekyb3d8bbwe!Microsoft.Xbox.LiveTV.Application", + "referenceId": "", "type": "HDMI" }, { "name": "Sklep", "reference": "Microsoft.WindowsStore_8wekyb3d8bbwe!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Microsoft Edge", "reference": "Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge", + "referenceId": "", "type": "APPLICATION" }, { "name": "Airserver", "reference": "F3F176BD.53203526D8F6C_p8qzvses5c8me!AirServer", + "referenceId": "", "type": "APPLICATION" }, { "name": "Gears of War 5", "reference": "Microsoft.HalifaxBaseGame_8wekyb3d8bbwe!HalifaxGameShip", + "referenceId": "", "type": "APPLICATION" }, { "name": "Fortnite", "reference": "Fortnite_d5xxtpggmzx6p!AppFortnite", + "referenceId": "", "type": "APPLICATION" }, { "name": "Minecraft", "reference": "Microsoft.MinecraftUWPConsole_8wekyb3d8bbwe!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Plex", "reference": "CAF9E577.PlexforXbox_aam28m9va5cke!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Bluray", "reference": "Microsoft.BlurayPlayer_8wekyb3d8bbwe!Xbox.BlurayPlayer.Application", + "referenceId": "", "type": "APPLICATION" }, { "name": "COD WII", "reference": "shg2SubmissionENFR_ht1qfjb0gaftw!S2Boot", + "referenceId": "", "type": "APPLICATION" }, { "name": "COD WZ", "reference": "iw8Submission-EN-FR_ht1qfjb0gaftw!iw8", + "referenceId": "", "type": "APPLICATION" }, { "name": "GTA V", "reference": "GTA-V_vesz1v3mcwykm!GTAV", + "referenceId": "", "type": "APPLICATION" }, { "name": "All4", "reference": "CHANNELFOURTELEVISIONCOMP.All4_e1252dwpj85a4!vstest.executionengine.universal.App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Amazon Prime", "reference": "AmazonVideo.AmazonVideoUK_pwbj9vvecjh7j!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Disney", "reference": "Disney.37853FC22B2CE_6rarf9sa4v8jt!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "BBC iPlayer", "reference": "BBCMobileApps.BBCIPLAYER_wzgfedwv7gft2!App", + "referenceId": "", "type": "APPLICATION" } ], @@ -212,10 +249,12 @@ Install and use [Homebridge Config UI X](https://github.com/oznu/homebridge-conf { "name": "Disney", "reference": "Disney.37853FC22B2CE_6rarf9sa4v8jt!App", + "referenceId": "" }, { "name": "BBC iPlayer", "reference": "BBCMobileApps.BBCIPLAYER_wzgfedwv7gft2!App", + "referenceId": "" }, ], "manufacturer": "Microsoft Corporation", diff --git a/config.schema.json b/config.schema.json index 5844ff0..cd7f82a 100644 --- a/config.schema.json +++ b/config.schema.json @@ -2,6 +2,7 @@ "pluginAlias": "XboxTv", "pluginType": "platform", "singular": true, + "customUi": false, "headerDisplay": "This plugin works with Xbox game consoles and are exposed to HomeKit as separate accessories and each needs to be manually paired.\n\n1. Open the Home app on your device.\n2. Tap the Home tab, then tap .\n3. Tap *Add Accessory*, and select *I Don't Have a Code or Cannot Scan*.\n4. Enter the Homebridge PIN, this can be found under the QR code in Homebridge UI or your Homebridge logs, alternatively you can select *Use Camera* and scan the QR code again.", "footerDisplay": "This plugin works with Xbox game console which is available [here](https://github.com/grzegorz914/homebridge-xbox-tv).", "schema": { @@ -26,11 +27,32 @@ "required": true, "format": "hostname" }, + "clientID": { + "title": "Web Api ID", + "type": "string", + "placeholder": "5e5ead27-ed60-482d-b3fc-702b28a97404", + "description": "Optional free-form for future use.", + "required": false + }, + "clientSecret": { + "title": "Web Api Secret", + "type": "string", + "placeholder": "Web Api Secret", + "description": "Optional free-form for future use.", + "required": false + }, "xboxliveid": { - "title": "Xbox Live ID", + "title": "Live ID", "type": "string", - "default": "FD00000000000000", "placeholder": "FD00000000000000", + "description": "Here put the Xbox Live ID", + "required": true + }, + "xboxWebApiToken": { + "title": "Web Api Token", + "type": "string", + "placeholder": "", + "description": "Here put the reponse Token (value after ?code=) from the authentication URL.", "required": true }, "refreshInterval": { @@ -48,6 +70,13 @@ "required": false, "description": "This disable log info, all values and state will not be displayed in Homebridge log console." }, + "xboxWebApiEnabled": { + "title": "Enable Web Api", + "type": "boolean", + "default": false, + "required": false, + "description": "This enable possibility to control Xbox over Web Api." + }, "volumeControl": { "title": "Volume control mode", "type": "integer", @@ -91,17 +120,23 @@ "type": "object", "properties": { "name": { - "title": "Input name", + "title": "Name", "type": "string", "description": "Here set Your own name.", "required": true }, "reference": { - "title": "Input reference", + "title": "Reference", "type": "string", "description": "Here set reference.", "required": true }, + "referenceId": { + "title": "Reference ID", + "type": "string", + "description": "Here set reference ID if You use web api and want to switch apps", + "required": false + }, "type": { "title": "Input type", "type": "string", @@ -174,7 +209,7 @@ ] } ], - "required": true + "required": false } } } @@ -197,6 +232,12 @@ "type": "string", "description": "Here set the reference.", "required": true + }, + "referenceId": { + "title": "Reference ID", + "type": "string", + "description": "Here set reference ID if You use web api and want to switch apps.", + "required": false } } } @@ -204,7 +245,6 @@ "manufacturer": { "name": "Manufacturer", "type": "string", - "default": "Microsoft Corporation", "placeholder": "Manufacturer", "description": "Set the manufacturer name.", "required": false @@ -243,8 +283,7 @@ "items": [ "devices[].name", "devices[].host", - "devices[].xboxliveid", - "devices[].volumeControl", + "devices[].xboxWebApiEnabled", { "key": "devices[]", "type": "section", @@ -260,6 +299,7 @@ "items": [ "devices[].inputs[].name", "devices[].inputs[].reference", + "devices[].inputs[].referenceId", "devices[].inputs[].type" ] } @@ -279,11 +319,25 @@ "buttonText": "Add button", "items": [ "devices[].buttons[].name", - "devices[].buttons[].reference" + "devices[].buttons[].reference", + "devices[].buttons[].referenceId" ] } ] }, + { + "key": "devices[]", + "type": "section", + "title": "Xbox Live and Web Api", + "expandable": true, + "expanded": false, + "items": [ + "devices[].xboxliveid", + "devices[].clientID", + "devices[].clientSecret", + "devices[].xboxWebApiToken" + ] + }, { "key": "devices[]", "type": "section", @@ -292,8 +346,9 @@ "expanded": false, "items": [ "devices[].disableLogInfo", + "devices[].switchInfoMenu", "devices[].refreshInterval", - "devices[].switchInfoMenu" + "devices[].volumeControl" ] }, { diff --git a/index.js b/index.js index 282db77..668d89f 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ const fs = require('fs'); const fsPromises = require('fs').promises; const path = require('path'); +const XboxWebApi = require('xbox-webapi'); const Smartglass = require('xbox-smartglass-core-node'); const SystemInputChannel = require('xbox-smartglass-core-node/src/channels/systeminput'); const SystemMediaChannel = require('xbox-smartglass-core-node/src/channels/systemmedia'); @@ -12,6 +13,8 @@ const TvRemoteChannel = require('xbox-smartglass-core-node/src/channels/tvremote const PLUGIN_NAME = 'homebridge-xbox-tv'; const PLATFORM_NAME = 'XboxTv'; +const CONSOLES_NAME = { 'XboxSeriesX': 'Xbox Series X', 'XboxSeriesS': 'Xbox Series S', 'XboxOne': 'Xbox One', 'XboxOneS': 'Xbox One S', 'XboxOneX': 'Xbox One X' }; + let Accessory, Characteristic, Service, Categories, UUID; module.exports = (api) => { @@ -41,9 +44,11 @@ class xboxTvPlatform { this.log.debug('didFinishLaunching'); for (let i = 0; i < this.devices.length; i++) { const device = this.devices[i]; + const deviceName = device.name; if (!device.name) { this.log.warn('Device Name Missing') } else { + this.log.info('Adding new accessory:', deviceName); new xboxTvDevice(this.log, device, this.api); } } @@ -70,7 +75,11 @@ class xboxTvDevice { //device configuration this.name = config.name; this.host = config.host; + this.clientID = config.clientID || '5e5ead27-ed60-482d-b3fc-702b28a97404'; + this.clientSecret = config.clientSecret || false; this.xboxliveid = config.xboxliveid; + this.xboxWebApiToken = config.xboxWebApiToken; + this.xboxWebApiEnabled = config.xboxWebApiEnabled || false; this.refreshInterval = config.refreshInterval || 5; this.disableLogInfo = config.disableLogInfo; this.volumeControl = config.volumeControl || 0; @@ -78,7 +87,7 @@ class xboxTvDevice { this.inputs = config.inputs || []; this.buttons = config.buttons || []; - //device info + //device this.manufacturer = config.manufacturer || 'Microsoft'; this.modelName = config.modelName || 'Model Name'; this.serialNumber = config.serialNumber || 'Serial Number'; @@ -86,34 +95,43 @@ class xboxTvDevice { //setup variables this.inputsService = new Array(); - this.inputsReference = new Array(); this.inputsName = new Array(); + this.inputsReference = new Array(); + this.inputsReferenceId = new Array(); this.inputsType = new Array(); + this.installedAppsName = new Array(); + this.installedAppsAumId = new Array(); + this.installedAppsTitleId = new Array(); this.buttonsService = new Array(); - this.buttonsReference = new Array(); this.buttonsName = new Array(); - this.checkDeviceInfo = true; + this.buttonsReference = new Array(); + this.buttonsReferenceId = new Array(); + this.webApiEnabled = this.xboxWebApiEnabled ? this.webApiEnabled : false; + this.checkDeviceInfo = false; + this.checkDeviceState = false; this.setStartInput = false; this.currentPowerState = false; this.currentMuteState = false; this.currentVolume = 0; this.currentInputName = ''; this.currentInputReference = ''; + this.currentInputReferenceId = ''; this.currentInputIdentifier = 0; this.setStartInputIdentifier = 0; this.currentMediaState = false; this.inputsLength = this.inputs.length; this.buttonsLength = this.buttons.length; this.prefDir = path.join(api.user.storagePath(), 'xboxTv'); - this.devConfigurationFile = this.prefDir + '/' + 'Configurationo_' + this.host.split('.').join(''); - this.devHeadendInfoFile = this.prefDir + '/' + 'HeadendInfo_' + this.host.split('.').join(''); - this.devLiveTVInfoFile = this.prefDir + '/' + 'LiveTVInfo_' + this.host.split('.').join(''); - this.devTunerLineupsFile = this.prefDir + '/' + 'TunerLineups_' + this.host.split('.').join(''); - this.devAppChannelLineupsFile = this.prefDir + '/' + 'AppChannelLineups_' + this.host.split('.').join(''); - this.targetVisibilityInputsFile = this.prefDir + '/' + 'targetVisibilityInputs_' + this.host.split('.').join(''); this.devInfoFile = this.prefDir + '/' + 'devInfo_' + this.host.split('.').join(''); - this.customInputsFile = this.prefDir + '/' + 'customInputs_' + this.host.split('.').join(''); + this.authTokenFile = this.prefDir + '/' + 'ApiToken_' + this.host.split('.').join('') + '.json'; + this.installedAppsFile = this.prefDir + '/' + 'installedApps_' + this.host.split('.').join(''); + this.targetVisibilityInputsFile = this.prefDir + '/' + 'targetVisibilityInputs_' + this.host.split('.').join(''); + this.customInputsNameFile = this.prefDir + '/' + 'customInputs_' + this.host.split('.').join(''); this.xbox = Smartglass(); + this.xboxWebApi = XboxWebApi({ + clientId: this.clientID, + clientSecret: this.clientSecret + }); //check if prefs directory ends with a /, if not then add it if (this.prefDir.endsWith('/') === false) { @@ -124,166 +142,297 @@ class xboxTvDevice { fsPromises.mkdir(this.prefDir); } //check if the files exists, if not then create it - if (fs.existsSync(this.devConfigurationFile) === false) { - fsPromises.writeFile(this.devConfigurationFile, ''); - } - //check if the files exists, if not then create it - if (fs.existsSync(this.devHeadendInfoFile) === false) { - fsPromises.writeFile(this.devHeadendInfoFile, ''); - } - //check if the files exists, if not then create it - if (fs.existsSync(this.devTunerLineupsFile) === false) { - fsPromises.writeFile(this.devTunerLineupsFile, ''); + if (fs.existsSync(this.devInfoFile) === false) { + fsPromises.writeFile(this.devInfoFile, ''); } //check if the files exists, if not then create it - if (fs.existsSync(this.devTunerLineupsFile) === false) { - fsPromises.writeFile(this.devTunerLineupsFile, ''); + if (fs.existsSync(this.authTokenFile) === false) { + fsPromises.writeFile(this.authTokenFile, ''); } //check if the files exists, if not then create it - if (fs.existsSync(this.devAppChannelLineupsFile) === false) { - fsPromises.writeFile(this.devAppChannelLineupsFile, ''); + if (fs.existsSync(this.installedAppsFile) === false) { + fsPromises.writeFile(this.installedAppsFile, ''); } //check if the files exists, if not then create it - if (fs.existsSync(this.customInputsFile) === false) { - fsPromises.writeFile(this.customInputsFile, ''); + if (fs.existsSync(this.customInputsNameFile) === false) { + fsPromises.writeFile(this.customInputsNameFile, ''); } //check if the files exists, if not then create it if (fs.existsSync(this.targetVisibilityInputsFile) === false) { fsPromises.writeFile(this.targetVisibilityInputsFile, ''); } - //check if the files exists, if not then create it - if (fs.existsSync(this.devInfoFile) === false) { - fsPromises.writeFile(this.devInfoFile, ''); - } //Check net state setInterval(function () { if (!this.xbox._connection_status) { this.xbox = Smartglass(); - this.xbox.connect(this.host).then(response => { + this.xbox.connect(this.host).then(() => { this.xbox.addManager('system_input', SystemInputChannel()); this.xbox.addManager('system_media', SystemMediaChannel()); this.xbox.addManager('tv_remote', TvRemoteChannel()); - this.currentPowerState = true; this.checkDeviceInfo = true; + this.currentPowerState = true; + + this.log('Device: %s %s, connected.', this.host, this.name); + this.getDeviceInfo(); }).catch(error => { - this.log.debug('Device: %s %s, state Offline.', this.host, this.name); this.currentPowerState = false; - if (this.televisionService) { - this.televisionService.updateCharacteristic(Characteristic.Active, 0); - } }); - } else { - this.currentPowerState = true; + if (this.checkDeviceState) { + this.log('Device: %s %s, disconnected.', this.host, this.name); + } + this.currentPowerState = false; + this.checkDeviceState = false; if (this.televisionService) { - this.televisionService.updateCharacteristic(Characteristic.Active, 1); + this.televisionService + .updateCharacteristic(Characteristic.Active, false); } } - if (this.currentPowerState && this.checkDeviceInfo) { - this.getDeviceInfo(); + if (this.checkDeviceState) { + this.updateDeviceState(); } }.bind(this), this.refreshInterval * 1000); + const getWebApiToken = this.xboxWebApiEnabled ? this.getWebApiToken() : false; this.prepareAccessory(); } + getWebApiToken() { + this.log.debug('Device: %s %s, preparing web api.', this.host, this.name); + this.xboxWebApi._authentication._tokensFile = this.authTokenFile; + this.xboxWebApi.isAuthenticated().then(() => { + this.log('Device: %s %s, authenticated and web api enabled.', this.host, this.name); + this.webApiEnabled = true; + + //get installed apps from xbox live account + this.xboxWebApi.getProvider('smartglass').getInstalledApps(this.xboxliveid).then((response) => { + this.log.debug('Device: %s %s, debug status: %s, result: %s', this.host, this.name, response.status, response.result); + this.installedAppsArr = new Array(); + const installedApps = response.result; + const installedAppsLength = installedApps.length; + + for (let i = 0; i < installedAppsLength; i++) { + const oneStoreProductId = installedApps[i].oneStoreProductId; + const titleId = (installedApps[i].titleId).toString(); + const aumid = installedApps[i].aumid; + const lastActiveTime = installedApps[i].lastActiveTime; + const isGame = (installedApps[i].isGame === true); + const name = installedApps[i].name; + const contentType = installedApps[i].contentType; + const instanceId = installedApps[i].instanceId; + const storageDeviceId = installedApps[i].storageDeviceId; + const uniqueId = installedApps[i].uniqueId; + const legacyProductId = installedApps[i].legacyProductId; + const version = installedApps[i].version; + const sizeInBytes = installedApps[i].sizeInBytes; + const installTime = installedApps[i].installTime; + const updateTime = installedApps[i].updateTime; + const parentId = installedApps[i].parentId; + + this.installedAppsName.push(name); + this.installedAppsAumId.push(aumid); + this.installedAppsTitleId.push(titleId); + const obj = { 'name': name, 'reference': aumid, 'referenceId': titleId }; + this.installedAppsArr.push(obj); + } + + const objApps = JSON.stringify(this.installedAppsArr, null, 2); + const writeInstalledApps = fs.writeFileSync(this.installedAppsFile, objApps); + this.log.debug('Device: %s %s, debug writeInstalledApps: %s', this.host, this.name, objApps); + }).catch((error) => { + this.log.error('Device: %s %s, getInstalledApps error: %s', this.host, this.name, error); + }); + }).catch(() => { + this.log('Device: %s %s, open the authentication URL and login to Your XboxLive account, next accept permision for this app, Open this URL: %s:', this.host, this.name, this.xboxWebApi._authentication.generateAuthorizationUrl()); + this.log('Device: %s %s, after accept permiosion for this app copy the part after the (?code=) from the response URL and paste it in to plugin config Settings >> Xbox Live and Web Api >> Web Api Token, save and restart the plugin again, done.', this.host, this.name); + this.log.debug('Device: %s %s, current used Web Api Token:', this.host, this.name, this.xboxWebApiToken); + + if (this.xboxWebApiToken !== undefined) { + this.log.debug('Device: %s %s, trying to authenticate with Web Api Token...', this.host, this.name, this.xboxWebApiToken); + this.xboxWebApi._authentication.getTokenRequest(this.xboxWebApiToken).then((data) => { + this.log('Device: %s %s, web api enabled.', this.host, this.name); + this.log.debug('Device: %s %s, get oauth Web Api Token:', this.host, this.name, data); + + this.xboxWebApi._authentication._tokens.oauth = data; + this.xboxWebApi._authentication.saveTokens(); + this.webApiEnabled = true; + + }).catch((error) => { + this.log('Device: %s %s, web api disabled, error: %s:', this.host, this.name, error); + this.webApiEnabled = false; + }); + } else { + this.log('Device: %s %s, web api disabled, token undefined.', this.host, this.name); + this.webApiEnabled = false; + } + }); + } + getDeviceInfo() { - this.log.debug('Device: %s %s, requesting Device Info.', this.host, this.name); - try { - const manufacturer = this.manufacturer; - const modelName = this.modelName; - const serialNumber = this.serialNumber; - const firmwareRevision = this.firmwareRevision; - - const obj = { 'manufacturer': manufacturer, 'modelName': modelName, 'serialNumber': serialNumber, 'firmwareRevision': firmwareRevision }; - const devInfo = JSON.stringify(obj, null, 2); - const writeDevInfoFile = fsPromises.writeFile(this.devInfoFile, devInfo); - this.log.debug('Device: %s %s, saved Device Info successful: %s', this.host, this.name, devInfo); - - if (!this.disableLogInfo) { - this.log('Device: %s %s, state: Online.', this.host, this.name); + this.log.debug('Device: %s %s, requesting device info.', this.host, this.name); + if (this.checkDeviceInfo) { + this.xboxInfo = new Array(); + } + if (this.webApiEnabled) { + this.xboxWebApi.getProvider('smartglass').getConsoleStatus(this.xboxliveid).then((result) => { + this.log('Device: %s %s, debug getConsoleStatus, result:', result); + + const id = result.id; + const name = result.name; + const locale = result.locale; + const consoleType = CONSOLES_NAME[result.consoleType]; + const powerState = (result.powerState === 'On'); + const playback = (result.playbackState !== 'Stopped'); + const loginState = result.loginState; + const focusAppAumid = result.focusAppAumid; + const isTvConfigured = (result.isTvConfigured === true); + const digitalAssistantRemoteControlEnabled = (result.digitalAssistantRemoteControlEnabled === true); + const consoleStreamingEnabled = (result.consoleStreamingEnabled === true); + const remoteManagementEnabled = (result.remoteManagementEnabled === true); + + if (this.checkDeviceInfo) { + const manufacturer = this.manufacturer; + const modelName = consoleType; + const serialNumber = id; + + this.xboxInfo.push(manufacturer); + this.xboxInfo.push(modelName); + this.xboxInfo.push(serialNumber); + } + + }).catch((error) => { + if (error.errorCode === 'XboxDataNotFound') { + this.log.error('Device: %s %s, with liveid: %s, getConsoleStatus error: %s.', this.host, this.name, this.xboxliveid, error); + } + }); + } + this.xbox.on('_on_console_status', (response, config, smartglass) => { + this.log.debug('Device %s %s, debug _on_console_status response: %s, config: %s, smartglass: %s', this.host, this.name, response.packet_decoded.protected_payload, config, smartglass); + + const devInfoAndApps = response.packet_decoded.protected_payload; + const devConfig = config; + const devNetConfig = smartglass; + + const live_tv_provider = devInfoAndApps.live_tv_provider; + const major_version = devInfoAndApps.major_version; + const minor_version = devInfoAndApps.minor_version; + const build_number = devInfoAndApps.build_number; + const locale = devInfoAndApps.locale; + + const ip = devConfig._ip; + const certificate = devConfig._certificate; + const iv = devConfig._iv; + const liveid = devConfig._liveid; + const is_authenticated = devConfig._is_authenticated; + const participantid = devConfig._participantid; + const connection_status = devConfig._connection_status; + const request_num = devConfig._request_num; + const target_participant_id = devConfig._target_participant_id; + const source_participant_id = devConfig._source_participant_id; + const fragments = devConfig._fragments; + + const address = devNetConfig.address; + const family = devNetConfig.family; + const port = devNetConfig.port; + const size = devNetConfig.size; + + if (this.checkDeviceInfo) { + const manufacturer = this.webApiEnabled ? this.devInfo[0] : this.manufacturer;; + const modelName = this.webApiEnabled ? this.devInfo[1] : this.modelName; + const serialNumber = this.webApiEnabled ? this.devInfo[2] : liveid; + const firmwareRevision = Object.assign(major_version, minor_version, build_number); + + const pushManufacturer = this.webApiEnabled ? this.xboxInfo.push(manufacturer) : false; + const pushModelName = this.webApiEnabled ? this.xboxInfo.push(modelName) : false; + const pushSerialNumber = this.webApiEnabled ? this.xboxInfo.push(serialNumber) : false; + this.xboxInfo.push(firmwareRevision); + + + const obj = { 'manufacturer': manufacturer, 'modelName': modelName, 'serialNumber': serialNumber, 'firmwareRevision': firmwareRevision }; + const devInfo = JSON.stringify(obj, null, 2); + const writeDevInfo = fsPromises.writeFile(this.devInfoFile, devInfo); + this.log('Device: %s %s, debug writeDevInfo: %s', this.host, this.name, devInfo); + + this.log('-------- %s --------', this.name); + this.log('Manufacturer: %s', manufacturer); + this.log('Model: %s', modelName); + this.log('Serialnr: %s', serialNumber); + this.log('Firmware: %s', firmwareRevision); + this.log('----------------------------------'); } - this.log('-------- %s --------', this.name); - this.log('Manufacturer: %s', manufacturer); - this.log('Model: %s', modelName); - this.log('Serialnr: %s', serialNumber); - this.log('Firmware: %s', firmwareRevision); - this.log('----------------------------------'); + + this.devInfoAndApps = devInfoAndApps; + this.devConfig = devConfig; + this.devNetConfig = devNetConfig; this.checkDeviceInfo = false; this.updateDeviceState(); - } catch (error) { - this.log.error('Device: %s %s, get device info eror: %s, device offline, trying to reconnect', this.host, this.name, error); - this.checkDeviceInfo = true; - } + }, function (error) { + this.log.error('Device: %s %s, update device state error: %s', this.host, this.name, error); + }); } updateDeviceState() { - this.log.debug('Device: %s %s, requesting Device state.', this.host, this.name); - if (this.currentPowerState) { - this.xbox.on('_on_console_status', (response, config, smartglass) => { - this.log.debug('Device %s %s, get device status data: %s', this.host, this.name, response); - if (response.packet_decoded.protected_payload.apps[0] !== undefined) { - const powerState = this.currentPowerState; - const inputReference = response.packet_decoded.protected_payload.apps[0].aum_id; - const inputIdentifier = (this.inputsReference.indexOf(inputReference) >= 0) ? this.inputsReference.indexOf(inputReference) : 0; - const inputName = this.inputsName[inputIdentifier]; - const volume = this.currentVolume; - const mute = powerState ? this.currentMuteState : true; - - if (this.televisionService) { - if (powerState) { - this.televisionService - .updateCharacteristic(Characteristic.Active, true) - .updateCharacteristic(Characteristic.ActiveIdentifier, inputIdentifier); - - } + this.log.debug('Device: %s %s, update device state.', this.host, this.name); + const devInfoAndApps = this.devInfoAndApps; + const devConfig = this.devConfig; + this.log('Device: %s %s, debug devInfoAndApps: %s, devConfig: %s', this.host, this.name, devInfoAndApps, devConfig); + + if (devInfoAndApps.apps[0] !== undefined) { + const currentMediaState = this.xbox.getManager('system_media').getState(); + this.log('Device: %s %s, debug currentMediaState: %s', this.host, this.name, currentMediaState); + + const powerState = devConfig._connection_status; + const inputReference = devInfoAndApps.apps[0].aum_id; + const inputIdentifier = this.setStartInput ? this.setStartInputIdentifier : (this.inputsReference.indexOf(inputReference) >= 0) ? this.inputsReference.indexOf(inputReference) : 0; + const inputInstalledAppsIdentifier = (this.webApiEnabled && (this.installedAppsAumId.indexOf(inputReference) !== undefined)) ? this.installedAppsAumId.indexOf(inputReference) : false; + const inputReferenceId = (inputInstalledAppsIdentifier !== false) ? this.installedAppsTitleId[inputInstalledAppsIdentifier] : this.inputsReferenceId[inputIdentifier]; + const inputName = (inputInstalledAppsIdentifier !== false) ? this.installedAppsName[inputInstalledAppsIdentifier] : this.inputsName[inputIdentifier]; + const volume = this.currentVolume; + const mute = powerState ? this.currentMuteState : true; + + if (this.televisionService) { + if (powerState) { + this.televisionService + .updateCharacteristic(Characteristic.Active, true) + this.currentPowerState = true; + } - if (!powerState) { - this.televisionService - .updateCharacteristic(Characteristic.Active, false); - } + if (!powerState) { + this.televisionService + .updateCharacteristic(Characteristic.Active, false) + this.currentPowerState = false; + } - if (this.setStartInput) { - this.televisionService - .setCharacteristic(Characteristic.ActiveIdentifier, this.setStartInputIdentifier); - if (this.setStartInputIdentifier === inputIdentifier) { - this.setStartInput = false; - } - } - } + this.televisionService.updateCharacteristic(Characteristic.ActiveIdentifier, inputIdentifier); + this.setStatrtInput = (this.setStatrtInput && this.setStartInputIdentifier === inputIdentifier) ? false : true; - if (this.speakerService) { - this.speakerService - .updateCharacteristic(Characteristic.Volume, volume) - .updateCharacteristic(Characteristic.Mute, mute); - if (this.volumeService && this.volumeControl == 1) { - this.volumeService - .updateCharacteristic(Characteristic.Brightness, volume) - .updateCharacteristic(Characteristic.On, !mute); - } - if (this.volumeServiceFan && this.volumeControl == 2) { - this.volumeServiceFan - .updateCharacteristic(Characteristic.RotationSpeed, volume) - .updateCharacteristic(Characteristic.On, !mute); - } - } + this.currentInputName = inputName; + this.currentInputReference = inputReference; + this.currentInputReferenceId = inputReferenceId; + this.currentInputIdentifier = inputIdentifier; + } - this.currentPowerState = powerState; - this.currentInputName = inputName; - this.currentInputReference = inputReference; - this.currentInputIdentifier = inputIdentifier; - this.currentVolume = volume; - this.currentMuteState = mute; + if (this.speakerService) { + this.speakerService + .updateCharacteristic(Characteristic.Volume, volume) + .updateCharacteristic(Characteristic.Mute, mute); + if (this.volumeService && this.volumeControl === 1) { + this.volumeService + .updateCharacteristic(Characteristic.Brightness, volume) + .updateCharacteristic(Characteristic.On, !mute); } - - const currentMediaState = this.xbox.getManager('system_media').getState(); - this.currentMediaState = (currentMediaState.title_id !== 0); - this.log.debug('Device: %s %s, get current media state: %s', this.host, this.name, this.currentMediaState); - }, function (error) { - this.log.error('Device: %s %s, update device state error: %s', this.host, this.name, error); - }); + if (this.volumeServiceFan && this.volumeControl === 2) { + this.volumeServiceFan + .updateCharacteristic(Characteristic.RotationSpeed, volume) + .updateCharacteristic(Characteristic.On, !mute); + } + this.currentVolume = volume; + this.currentMuteState = mute; + } + this.currentMediaState = currentMediaState; } + this.checkDeviceState = true; } //Prepare accessory @@ -298,8 +447,8 @@ class xboxTvDevice { this.log.debug('prepareInformationService'); try { const readDevInfo = await fsPromises.readFile(this.devInfoFile); - const devInfo = (readDevInfo.modelName !== undefined) ? JSON.parse(readDevInfo) : { 'manufacturer': this.manufacturer, 'modelName': this.modelName, 'serialNumber': this.serialNumber, 'firmwareRevision': this.firmwareRevision }; - this.log.debug('Device: %s %s, read devInfo: %s', this.host, accessoryName, devInfo); + const devInfo = (readDevInfo !== undefined) ? JSON.parse(readDevInfo) : { 'manufacturer': this.manufacturer, 'modelName': this.modelName, 'serialNumber': this.serialNumber, 'firmwareRevision': this.firmwareRevision }; + this.log.debug('Device: %s %s, debug devInfo: %s', this.host, accessoryName, devInfo); const manufacturer = devInfo.manufacturer; const modelName = devInfo.modelName; @@ -307,16 +456,15 @@ class xboxTvDevice { const firmwareRevision = devInfo.firmwareRevision; accessory.removeService(accessory.getService(Service.AccessoryInformation)); - const informationService = new Service.AccessoryInformation(); + const informationService = new Service.AccessoryInformation(accessoryName); informationService - .setCharacteristic(Characteristic.Name, accessoryName) .setCharacteristic(Characteristic.Manufacturer, manufacturer) .setCharacteristic(Characteristic.Model, modelName) .setCharacteristic(Characteristic.SerialNumber, serialNumber) .setCharacteristic(Characteristic.FirmwareRevision, firmwareRevision); accessory.addService(informationService); } catch (error) { - this.log.error('Device: %s %s, prepareInformationService error: %s', this.host, accessoryName, error); + this.log.debug('Device: %s %s, debug prepareInformationService error: %s', this.host, accessoryName, error); }; @@ -328,32 +476,50 @@ class xboxTvDevice { this.televisionService.getCharacteristic(Characteristic.Active) .onGet(async () => { - const state = this.currentPowerState ? 1 : 0; + const state = this.currentPowerState ? true : false; if (!this.disableLogInfo) { - this.log('Device: %s %s, get current Power state successfull, state: %s', this.host, accessoryName, state ? 'ON' : 'OFF'); + this.log('Device: %s %s, get current Power state successful, state: %s', this.host, accessoryName, state ? 'ON' : 'OFF'); } return state; }) .onSet(async (state) => { - if (state && !this.currentPowerState) { + if (state && (state !== !this.currentPowerState)) { const xbox = Smartglass(); - xbox.powerOn({ live_id: this.xboxliveid, tries: 10, ip: this.host }).then(response => { + const setPowerOn = this.xboxWebApiEnabled ? this.xboxWebApi.getProvider('smartglass').powerOn(this.xboxliveid).then(() => { + if (!this.disableLogInfo) { + this.log('Device: %s %s, web api set power ON state successful', this.host, accessoryName); + } + }).catch((error) => { + this.log.error('Device: %s %s, web api set power ON, error: %s', this.host, accessoryName, error); + this.currentPowerState = false; + this.televisionService + .updateCharacteristic(Characteristic.Active, false); + }) : xbox.powerOn({ live_id: this.xboxliveid, tries: 15, ip: this.host }).then(() => { if (!this.disableLogInfo) { - this.log('Device: %s %s, set new Power state successful: %s, %s', this.host, accessoryName, 'ON', response); + this.log('Device: %s %s, set power ON successful', this.host, accessoryName); } }).catch(error => { - this.log.error('Device: %s %s, booting failed, error: %s', this.host, accessoryName, error); + this.log.error('Device: %s %s, set power ON, error: %s', this.host, accessoryName, error); + this.currentPowerState = false; + this.televisionService + .updateCharacteristic(Characteristic.Active, false); }); } else { - if (!state && this.currentPowerState) { - this.xbox.powerOff().then(response => { + if (!state && (state !== this.currentPowerState)) { + const setPowerOff = this.xboxWebApiEnabled ? this.xboxWebApi.getProvider('smartglass').powerOff(this.xboxliveid).then(() => { if (!this.disableLogInfo) { - this.log('Device: %s %s, set new Power state successful, new state: %s, %s', this.host, accessoryName, 'OFF', response); + this.log('Device: %s %s, web api set power OFF successful', this.host, accessoryName); + } + this.currentPowerState = false; + }).catch((error) => { + this.log.error('Device: %s %s, set power OFF error: %s', this.host, accessoryName, error); + }) : this.xbox.powerOff().then(() => { + if (!this.disableLogInfo) { + this.log('Device: %s %s, set power OFF successful', this.host, accessoryName); } this.currentPowerState = false; - this.checkDeviceInfo = true; }).catch(error => { - this.log.error('Device: %s %s, set new Power state error: %s', this.host, accessoryName, error); + this.log.error('Device: %s %s, set power OFF error: %s', this.host, accessoryName, error); }); } } @@ -361,122 +527,117 @@ class xboxTvDevice { this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) .onGet(async () => { + const inputIdentifier = this.currentInputIdentifier; const inputName = this.currentInputName; const inputReference = this.currentInputReference; - const inputIdentifier = this.currentInputIdentifier; - if (!this.disableLogInfo) { - this.log('Device: %s %s, get current Input successful: %s %s', this.host, accessoryName, inputName, inputReference); + const inputReferenceId = this.currentInputReferenceId; + if (!this.disableLogInfo && this.currentPowerState) { + this.log('Device: %s %s, get current App successful, name: %s, reference: %s, referenceId: %s', this.host, accessoryName, inputName, inputReference, inputReferenceId); } return inputIdentifier; }) .onSet(async (inputIdentifier) => { - try { - const inputName = this.inputsName[inputIdentifier]; - const inputReference = (this.inputsReference[inputIdentifier] !== undefined) ? this.inputsReference[inputIdentifier] : 0; + const inputReference = (this.inputsReference[inputIdentifier] !== undefined) ? this.inputsReference[inputIdentifier] : 0; + const inputInstalledAppsIdentifier = (this.webApiEnabled && (this.installedAppsAumId.indexOf(inputReference) !== undefined)) ? this.installedAppsAumId.indexOf(inputReference) : false; + const inputReferenceId = (inputInstalledAppsIdentifier !== false) ? this.installedAppsTitleId[inputInstalledAppsIdentifier] : this.inputsReferenceId[inputIdentifier]; + const inputName = (inputInstalledAppsIdentifier !== false) ? this.installedAppsName[inputInstalledAppsIdentifier] : this.inputsName[inputIdentifier]; + const setInput = this.webApiEnabled ? this.xboxWebApi.getProvider('smartglass').launchApp(this.xboxliveid, inputReferenceId).then(() => { if (!this.disableLogInfo) { - this.log('Device: %s %s, set new App successful, new App reference: %s %s', this.host, accessoryName, inputName, inputReference); + this.log('Device: %s %s, set new App successful, name: %s, reference: %s, referenceId: %s', this.host, accessoryName, inputName, inputReference, inputReferenceId); } - this.setStartInputIdentifier = this.currentPowerState ? this.currentInputIdentifier : inputIdentifier; - this.setStartInput = this.currentPowerState ? false : true; - } catch (error) { - this.log.error('Device: %s %s, can not set new Input. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }; + }).catch((error) => { + this.log.error('Device: %s %s, set new App error:', this.host, accessoryName, error); + }) : false; + this.setStartInputIdentifier = this.currentPowerState ? this.currentInputIdentifier : inputIdentifier; + this.setStartInput = this.currentPowerState ? false : true; }); this.televisionService.getCharacteristic(Characteristic.RemoteKey) .onSet(async (command) => { - try { - let type; - switch (command) { - case Characteristic.RemoteKey.REWIND: - command = 'rewind'; - type = 'system_media'; - break; - case Characteristic.RemoteKey.FAST_FORWARD: - command = 'fast_forward'; - type = 'system_media'; - break; - case Characteristic.RemoteKey.NEXT_TRACK: - command = 'next_track'; - type = 'system_media'; - break; - case Characteristic.RemoteKey.PREVIOUS_TRACK: - command = 'prev_track'; - type = 'system_media'; - break; - case Characteristic.RemoteKey.ARROW_UP: - command = 'up'; - type = 'system_input'; - break; - case Characteristic.RemoteKey.ARROW_DOWN: - command = 'down'; - type = 'system_input'; - break; - case Characteristic.RemoteKey.ARROW_LEFT: - command = 'left'; - type = 'system_input'; - break; - case Characteristic.RemoteKey.ARROW_RIGHT: - command = 'right'; - type = 'system_input'; - break; - case Characteristic.RemoteKey.SELECT: - command = 'a'; - type = 'system_input'; - break; - case Characteristic.RemoteKey.BACK: - command = 'b'; - type = 'system_input'; - break; - case Characteristic.RemoteKey.EXIT: - command = 'nexus'; - type = 'system_input'; - break; - case Characteristic.RemoteKey.PLAY_PAUSE: - command = 'playpause'; - type = 'system_media'; - break; - case Characteristic.RemoteKey.INFORMATION: - command = this.switchInfoMenu ? 'nexus' : 'view'; - type = 'system_input'; - break; + let type; + switch (command) { + case Characteristic.RemoteKey.REWIND: + command = 'rewind'; + type = 'system_media'; + break; + case Characteristic.RemoteKey.FAST_FORWARD: + command = 'fast_forward'; + type = 'system_media'; + break; + case Characteristic.RemoteKey.NEXT_TRACK: + command = 'next_track'; + type = 'system_media'; + break; + case Characteristic.RemoteKey.PREVIOUS_TRACK: + command = 'prev_track'; + type = 'system_media'; + break; + case Characteristic.RemoteKey.ARROW_UP: + command = 'up'; + type = 'system_input'; + break; + case Characteristic.RemoteKey.ARROW_DOWN: + command = 'down'; + type = 'system_input'; + break; + case Characteristic.RemoteKey.ARROW_LEFT: + command = 'left'; + type = 'system_input'; + break; + case Characteristic.RemoteKey.ARROW_RIGHT: + command = 'right'; + type = 'system_input'; + break; + case Characteristic.RemoteKey.SELECT: + command = 'a'; + type = 'system_input'; + break; + case Characteristic.RemoteKey.BACK: + command = 'b'; + type = 'system_input'; + break; + case Characteristic.RemoteKey.EXIT: + command = 'nexus'; + type = 'system_input'; + break; + case Characteristic.RemoteKey.PLAY_PAUSE: + command = 'playpause'; + type = 'system_media'; + break; + case Characteristic.RemoteKey.INFORMATION: + command = this.switchInfoMenu ? 'nexus' : 'view'; + type = 'system_input'; + break; + } + this.xbox.getManager(type).sendCommand(command).then(() => { + if (!this.disableLogInfo) { + this.log('Device: %s %s, setRemoteKey successful, command: %s', this.host, accessoryName, command); } - this.xbox.getManager(type).sendCommand(command).then(response => { - if (!this.disableLogInfo) { - this.log('Device: %s %s, setRemoteKey successful, command: %s', this.host, accessoryName, command); - } - }).catch(error => { - this.log.error('Device: %s %s, can not setRemoteKey command. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }); - } catch (error) { - this.log.error('Device: %s %s, can not setRemoteKey command. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }; + }).catch(error => { + this.log.error('Device: %s %s, can not setRemoteKey command, error: %s', this.host, accessoryName, error); + }); }); this.televisionService.getCharacteristic(Characteristic.PowerModeSelection) .onSet(async (command) => { - try { - let type; - switch (command) { - case Characteristic.PowerModeSelection.SHOW: - command = this.switchInfoMenu ? 'nexus' : 'menu'; - type = 'system_input'; - break; - case Characteristic.PowerModeSelection.HIDE: - command = 'b'; - type = 'system_input';; - break; + let type; + switch (command) { + case Characteristic.PowerModeSelection.SHOW: + command = this.switchInfoMenu ? 'nexus' : 'menu'; + type = 'system_input'; + break; + case Characteristic.PowerModeSelection.HIDE: + command = 'b'; + type = 'system_input';; + break; + } + this.xbox.getManager(type).sendCommand(command).then(() => { + if (!this.disableLogInfo) { + this.log('Device: %s %s, setPowerModeSelection successful, command: %s', this.host, accessoryName, command); } - this.xbox.getManager(type).sendCommand(command).then(response => { - if (!this.disableLogInfo) { - this.log('Device: %s %s, setPowerModeSelection successful, command: %s', this.host, accessoryName, command); - } - }).catch(error => { - this.log.error('Device: %s %s, can not setPowerModeSelection command. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }); - } catch (error) { - this.log.error('Device: %s %s, can not setPowerModeSelection command. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }; + }).catch(error => { + this.log.error('Device: %s %s, can not setPowerModeSelection command, error: %s', this.host, accessoryName, error); + }); }); accessory.addService(this.televisionService); @@ -488,74 +649,73 @@ class xboxTvDevice { .setCharacteristic(Characteristic.VolumeControlType, Characteristic.VolumeControlType.ABSOLUTE); this.speakerService.getCharacteristic(Characteristic.VolumeSelector) .onSet(async (command) => { - try { - let type; - switch (command) { - case Characteristic.VolumeSelector.INCREMENT: - command = 'btn.vol_up'; - type = 'tv_remote'; - break; - case Characteristic.VolumeSelector.DECREMENT: - command = 'btn.vol_down'; - type = 'tv_remote'; - break; + switch (command) { + case Characteristic.VolumeSelector.INCREMENT: + command = this.webApiEnabled ? 'Up' : 'btn.vol_up'; + break; + case Characteristic.VolumeSelector.DECREMENT: + command = this.webApiEnabled ? 'Down' : 'btn.vol_down'; + break; + } + const setVolume = this.webApiEnabled ? this.xboxWebApi.getProvider('smartglass')._sendCommand(this.xboxliveid, 'Audio', 'Volume', [{ + 'direction': (command), 'amount': 1, + }]).then(() => { + if (!this.disableLogInfo) { + this.log('Device: %s %s, setVolumeSelector successful, command: %s', this.host, accessoryName, command); } - this.xbox.getManager(type).sendIrCommand(command).then(response => { - if (!this.disableLogInfo) { - this.log('Device: %s %s, setVolumeSelector successful, command: %s', this.host, accessoryName, command); - } - }).catch(error => { - this.log.error('Device: %s %s, can not setVolumeSelector command. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }); - } catch (error) { - this.log.error('Device: %s %s, can not setVolumeSelector command. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }; + }).catch((error) => { + this.log.error('Device: %s %s, can not setVolumeSelector command, error: %s', this.host, accessoryName, error); + }) : this.xbox.getManager('tv_remote').sendIrCommand(command).then(() => { + if (!this.disableLogInfo) { + this.log('Device: %s %s, setVolumeSelector successful, command: %s', this.host, accessoryName, command); + } + }).catch(error => { + this.log.error('Device: %s %s, can not setVolumeSelector command, error: %s', this.host, accessoryName, error); + }); }); this.speakerService.getCharacteristic(Characteristic.Volume) .onGet(async () => { const volume = this.currentVolume; - if (!this.disableLogInfo) { + if (!this.disableLogInfo && this.currentPowerState) { this.log('Device: %s %s, get current Volume level successful: %s', this.host, accessoryName, volume); } return volume; }) .onSet(async (volume) => { - try { - if (volume == 0 || volume == 100) { - volume = this.currentVolume; - } - if (!this.disableLogInfo) { - this.log('Device: %s %s, set new Volume level successful: %s', this.host, accessoryName, volume); - } - } catch (error) { - this.log.error('Device: %s %s, can not set new Volume level. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }; + if (volume == 0 || volume == 100) { + volume = this.currentVolume; + } + if (!this.disableLogInfo) { + this.log('Device: %s %s, set new Volume level successful: %s', this.host, accessoryName, volume); + } }); this.speakerService.getCharacteristic(Characteristic.Mute) .onGet(async () => { const state = this.currentPowerState ? this.currentMuteState : true; - if (!this.disableLogInfo) { + if (!this.disableLogInfo && this.currentPowerState) { this.log('Device: %s %s, get current Mute state successful: %s', this.host, accessoryName, state ? 'ON' : 'OFF'); } return state; }) .onSet(async (state) => { - if (this.currentPowerState && state !== this.currentMuteState) { - try { - const command = 'btn.vol_mute'; - const type = 'tv_remote'; - this.xbox.getManager(type).sendIrCommand(command).then(response => { - if (!this.disableLogInfo) { - this.log('Device: %s %s, set new Mute state successful: %s', this.host, accessoryName, state ? 'ON' : 'OFF'); - } - }).catch(error => { - this.log.error('Device: %s %s, can not set new Mute state. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }); - } catch (error) { - this.log.error('Device: %s %s, can not set new Mute state. Might be due to a wrong settings in config, error: %s', this.host, accessoryName, error); - }; + if (this.currentPowerState && (state !== this.currentMuteState)) { + const command = 'btn.vol_mute'; + const type = 'tv_remote'; + const setMute = this.webApiEnabled ? this.xboxWebApi.getProvider('smartglass').mute(this.xboxliveid).then(() => { + if (!this.disableLogInfo) { + this.log('Device: %s %s, set new Mute state successful: %s', this.host, accessoryName, state ? 'ON' : 'OFF'); + } + }).catch((error) => { + this.log.error('Device: %s %s, can not set new Mute state, error: %s', this.host, accessoryName, error); + }) : this.xbox.getManager(type).sendIrCommand(command).then(() => { + if (!this.disableLogInfo) { + this.log('Device: %s %s, set new Mute state successful: %s', this.host, accessoryName, state ? 'ON' : 'OFF'); + } + }).catch(error => { + this.log.error('Device: %s %s, can not set new Mute state, error: %s', this.host, accessoryName, error); + }); }; }); @@ -613,19 +773,22 @@ class xboxTvDevice { this.log.debug('prepareInputsService'); const inputs = this.inputs; - const savedNames = ((fs.readFileSync(this.customInputsFile)).length > 0) ? JSON.parse(fs.readFileSync(this.customInputsFile)) : {}; - this.log.debug('Device: %s %s, read savedNames: %s', this.host, accessoryName, savedNames) + const savedNames = ((fs.readFileSync(this.customInputsNameFile)).length > 0) ? JSON.parse(fs.readFileSync(this.customInputsNameFile)) : {}; + this.log.debug('Device: %s %s, read saved Apps name: %s', this.host, accessoryName, savedNames) const savedTargetVisibility = ((fs.readFileSync(this.targetVisibilityInputsFile)).length > 0) ? JSON.parse(fs.readFileSync(this.targetVisibilityInputsFile)) : {}; - this.log.debug('Device: %s %s, read savedTargetVisibility: %s', this.host, accessoryName, savedTargetVisibility); - + this.log.debug('Device: %s %s, read target state successful: %s', this.host, accessoryName, savedTargetVisibility); //check possible inputs count const inputsLength = (inputs.length > 96) ? 96 : inputs.length; for (let i = 0; i < inputsLength; i++) { //get input reference - const inputReference = inputs[i].reference; + const inputReference = (inputs[i].reference !== undefined) ? inputs[i].reference : 'undefined'; + + //get input reference Id + const inputInstalledAppsIdentifier = (this.webApiEnabled && (this.installedAppsAumId.indexOf(inputReference) !== undefined)) ? this.installedAppsAumId.indexOf(inputReference) : false; + const inputReferenceId = (inputInstalledAppsIdentifier !== false) ? this.installedAppsTitleId[inputInstalledAppsIdentifier] : inputs[i].referenceId; //get input name const inputName = (savedNames[inputReference] !== undefined) ? savedNames[inputReference] : (inputs[i].name !== undefined) ? inputs[i].name : inputs[i].reference; @@ -655,13 +818,14 @@ class xboxTvDevice { try { let newName = savedNames; newName[inputReference] = name; - await fsPromises.writeFile(this.customInputsFile, JSON.stringify(newName, null, 2)); - this.log.debug('Device: %s %s, saved new Input successful, savedNames: %s', this.host, accessoryName, JSON.stringify(newName, null, 2)); + const newCustomName = JSON.stringify(newName); + const writeNewCustomName = await fsPromises.writeFile(this.customInputsNameFile, newCustomName); + this.log.debug('Device: %s %s, saved new App name successful, name: %s', this.host, accessoryName, newCustomName); if (!this.disableLogInfo) { - this.log('Device: %s %s, new Input name saved successful, name: %s reference: %s', this.host, accessoryName, name, inputReference); + this.log('Device: %s %s, new App name saved successful, name: %s reference: %s', this.host, accessoryName, name, inputReference); } } catch (error) { - this.log.error('Device: %s %s, new Input name saved failed, error: %s', this.host, accessoryName, error); + this.log.error('Device: %s %s, new App name saved error: %s', this.host, accessoryName, error); } }); @@ -670,7 +834,7 @@ class xboxTvDevice { .onGet(async () => { const state = targetVisibility; if (!this.disableLogInfo) { - this.log('Device: %s %s, Input: %s, get target visibility state: %s', this.host, accessoryName, inputName, state ? 'HIDEN' : 'SHOWN'); + this.log('Device: %s %s, App: %s, target visibility state: %s', this.host, accessoryName, inputName, state ? 'HIDEN' : 'SHOWN'); } return state; }) @@ -678,18 +842,20 @@ class xboxTvDevice { try { let newState = savedTargetVisibility; newState[inputReference] = state; - await fsPromises.writeFile(this.targetVisibilityInputsFile, JSON.stringify(newState, null, 2)); - this.log.debug('Device: %s %s, Input: %s, saved target visibility state: %s', this.host, accessoryName, inputName, JSON.stringify(newState, null, 2)); + const newTargetVisibility = JSON.stringify(newState); + const writeNewTargetVisibility = await fsPromises.writeFile(this.targetVisibilityInputsFile, newTargetVisibility); + this.log.debug('Device: %s %s, App: %s, saved new target visibility state: %s', this.host, accessoryName, inputName, newTargetVisibility); if (!this.disableLogInfo) { - this.log('Device: %s %s, Input: %s, saved target visibility state: %s', this.host, accessoryName, inputName, state ? 'HIDEN' : 'SHOWN'); + this.log('Device: %s %s, App: %s, saved new target visibility state: %s', this.host, accessoryName, inputName, state ? 'HIDEN' : 'SHOWN'); } inputService.setCharacteristic(Characteristic.CurrentVisibilityState, state); } catch (error) { - this.log.error('Device: %s %s, Input: %s, saved target visibility state error: %s', this.host, accessoryName, error); + this.log.error('Device: %s %s, App: %s, saved new target visibility error: %s', this.host, accessoryName, error); } }); this.inputsReference.push(inputReference); + this.inputsReferenceId.push(inputReferenceId); this.inputsName.push(inputName); this.inputsType.push(inputType); @@ -706,6 +872,8 @@ class xboxTvDevice { const buttonsLength = ((inputs.length + buttons.length) > 96) ? 96 - inputs.length : buttons.length; for (let i = 0; i < buttonsLength; i++) { const buttonReference = buttons[i].reference; + const buttonInstalledAppsIdentifier = (this.webApiEnabled && (this.installedAppsAumId.indexOf(buttonReference) !== undefined)) ? this.installedAppsAumId.indexOf(buttonReference) : false; + const buttonReferenceId = (buttonInstalledAppsIdentifier !== false) ? this.installedAppsTitleId[buttonInstalledAppsIdentifier] : buttons[i].referenceId; const buttonName = (buttons[i].name !== undefined) ? buttons[i].name : buttons[i].reference; const buttonService = new Service.Switch(accessoryName + ' ' + buttonName, 'buttonService' + i); buttonService.getCharacteristic(Characteristic.On) @@ -718,22 +886,17 @@ class xboxTvDevice { }) .onSet(async (state) => { if (state && this.currentPowerState) { - try { - const response = await axios.get(this.url + '/api/zap?sRef=' + buttonReference); + const setInput = this.webApiEnabled ? this.xboxWebApi.getProvider('smartglass').launchApp(this.xboxliveid, buttonReferenceId).then(() => { if (!this.disableLogInfo) { - this.log('Device: %s %s, set new Channel successful: %s %s', this.host, accessoryName, buttonName, buttonReference); + this.log('Device: %s %s, set new App successful, name: %s reference: %s', this.host, accessoryName, buttonName, buttonReference); } + }).catch((error) => { + this.log.error('Device: %s %s, set new App error: %s', this.host, accessoryName, error); setTimeout(() => { buttonService .updateCharacteristic(Characteristic.On, false); }, 350); - } catch (error) { - this.log.error('Device: %s %s, can not set new Channel. Might be due to a wrong settings in config, error: %s.', this.host, accessoryName, error); - setTimeout(() => { - buttonService - .updateCharacteristic(Characteristic.On, false); - }, 350); - }; + }) : false; } else { setTimeout(() => { buttonService @@ -742,6 +905,7 @@ class xboxTvDevice { } }); this.buttonsReference.push(buttonReference); + this.buttonsReferenceId.push(buttonReferenceId); this.buttonsName.push(buttonName); this.buttonsService.push(buttonService) diff --git a/package-lock.json b/package-lock.json index 7d28dc1..ac6a1db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,773 @@ { "name": "homebridge-xbox-tv", - "version": "1.4.5", - "lockfileVersion": 1, + "version": "1.5.0-beta.118", + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "version": "1.5.0-beta.118", + "license": "MIT", + "dependencies": { + "@homebridge/plugin-ui-utils": "^0.0.19", + "xbox-smartglass-core-node": "^0.6.8", + "xbox-webapi": "^1.1.1" + }, + "engines": { + "homebridge": ">=1.3.0", + "node": ">=12.0.0" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/GrzegorzKaczor" + } + }, + "node_modules/@homebridge/plugin-ui-utils": { + "version": "0.0.19", + "resolved": "https://registry.npmjs.org/@homebridge/plugin-ui-utils/-/plugin-ui-utils-0.0.19.tgz", + "integrity": "sha512-axFX7lN2Yd7bz/6SlD7dzq5sY/N9+XYuLHPukuiyHQRDtNMRL1uDqJhOx6RVaN2tYDHB75h7YxRQWP7U44Mgzg==" + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hex-to-binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hex-to-binary/-/hex-to-binary-1.0.1.tgz", + "integrity": "sha1-YcevAW/CK86pcE2fLpo46MfbuFQ=" + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/jsrsasign": { + "version": "8.0.24", + "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.24.tgz", + "integrity": "sha512-u45jAyusqUpyGbFc2IbHoeE4rSkoBWQgLe/w99temHenX+GyCz4nflU5sjK7ajU1ffZTezl6le7u43Yjr/lkQg==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "dependencies": { + "mime-db": "1.47.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/uuid-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.1.0.tgz", + "integrity": "sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==" + }, + "node_modules/uuid4": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.2.tgz", + "integrity": "sha512-TzsQS8sN1B2m9WojyNp0X/3JL8J2RScnrAJnooNPL6lq3lA02/XdoWysyUgI6rAif0DzkkWk51N6OggujPy2RA==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/xbox-smartglass-core-node": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/xbox-smartglass-core-node/-/xbox-smartglass-core-node-0.6.8.tgz", + "integrity": "sha512-+xu1IRgA56VJN5vZ+7pdM8V6uD/vfQJPuCVL2PBxTR4DSgxElGrTWLEs4cvynwGtOizqEBg9otK6ZMPXkM6XPQ==", + "license": "MIT", + "dependencies": { + "commander": "^2.20.3", + "debug": "^4.1.1", + "elliptic": "^6.5.2", + "hex-to-binary": "^1.0.1", + "jsrsasign": "^8.0.13", + "uuid": "^3.4.0", + "uuid-parse": "^1.1.0" + } + }, + "node_modules/xbox-webapi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/xbox-webapi/-/xbox-webapi-1.1.1.tgz", + "integrity": "sha512-qTJ7IsUjL3n1rC2WobUNW3/D9mlSLj2JdrzGEgkIaXNCog8WGK1/+TaNbs8xk+Pdrg/IZqAIfBZJe1NzqMFEIw==", + "dependencies": { + "debug": "^4.2.0", + "express": "^4.17.1", + "uuid4": "^2.0.2" + } + } + }, "dependencies": { + "@homebridge/plugin-ui-utils": { + "version": "0.0.19", + "resolved": "https://registry.npmjs.org/@homebridge/plugin-ui-utils/-/plugin-ui-utils-0.0.19.tgz", + "integrity": "sha512-axFX7lN2Yd7bz/6SlD7dzq5sY/N9+XYuLHPukuiyHQRDtNMRL1uDqJhOx6RVaN2tYDHB75h7YxRQWP7U44Mgzg==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -27,6 +776,21 @@ "ms": "2.1.2" } }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, "elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -41,6 +805,112 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, "hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -65,16 +935,81 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, "jsrsasign": { "version": "8.0.24", "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.24.tgz", "integrity": "sha512-u45jAyusqUpyGbFc2IbHoeE4rSkoBWQgLe/w99temHenX+GyCz4nflU5sjK7ajU1ffZTezl6le7u43Yjr/lkQg==" }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" + }, + "mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "requires": { + "mime-db": "1.47.0" + } + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -90,6 +1025,156 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -100,10 +1185,20 @@ "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.1.0.tgz", "integrity": "sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==" }, + "uuid4": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.2.tgz", + "integrity": "sha512-TzsQS8sN1B2m9WojyNp0X/3JL8J2RScnrAJnooNPL6lq3lA02/XdoWysyUgI6rAif0DzkkWk51N6OggujPy2RA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, "xbox-smartglass-core-node": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/xbox-smartglass-core-node/-/xbox-smartglass-core-node-0.6.7.tgz", - "integrity": "sha512-8aVlohzTYZ4csuoXM61pL9ct1f/K9bDq9JfK6ZpDvGyNQOe5vRhF19dgEsznpqrj7xv1nmP0CTKr9Fl30c/L3w==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/xbox-smartglass-core-node/-/xbox-smartglass-core-node-0.6.8.tgz", + "integrity": "sha512-+xu1IRgA56VJN5vZ+7pdM8V6uD/vfQJPuCVL2PBxTR4DSgxElGrTWLEs4cvynwGtOizqEBg9otK6ZMPXkM6XPQ==", "requires": { "commander": "^2.20.3", "debug": "^4.1.1", @@ -113,6 +1208,16 @@ "uuid": "^3.4.0", "uuid-parse": "^1.1.0" } + }, + "xbox-webapi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/xbox-webapi/-/xbox-webapi-1.1.1.tgz", + "integrity": "sha512-qTJ7IsUjL3n1rC2WobUNW3/D9mlSLj2JdrzGEgkIaXNCog8WGK1/+TaNbs8xk+Pdrg/IZqAIfBZJe1NzqMFEIw==", + "requires": { + "debug": "^4.2.0", + "express": "^4.17.1", + "uuid4": "^2.0.2" + } } } } diff --git a/package.json b/package.json index 43ac645..fb3382c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "Xbox TV", "name": "homebridge-xbox-tv", - "version": "1.4.32", + "version": "1.5.0", "description": "Homebridge plugin (https://github.com/homebridge/homebridge) to control Xbox game consoles.", "license": "MIT", "author": "grzegorz914", @@ -31,7 +31,9 @@ "homebridge": ">=1.3.0" }, "dependencies": { - "xbox-smartglass-core-node": "^0.6.7" + "@homebridge/plugin-ui-utils": "^0.0.19", + "xbox-smartglass-core-node": "^0.6.8", + "xbox-webapi": "^1.1.1" }, "keywords": [ "homebridge", @@ -41,8 +43,7 @@ "XboxTv" ], "contributors": [], - "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" } -} \ No newline at end of file +} diff --git a/sample-config.json b/sample-config.json index 7584c80..52cff75 100644 --- a/sample-config.json +++ b/sample-config.json @@ -22,7 +22,11 @@ "name": "Game Console", "host": "192.168.1.6", "xboxliveid": "FD0000000000", + "xboxWebApiToken": "M.R5_BAU.be1c3729-8ae5-d62b-5abd-4323c9c96383", + "clientID": "", + "clientSecret": "", "refreshInterval": 5, + "xboxWebApiEnabled": false, "disableLogInfo": false, "volumeControl": 0, "switchInfoMenu": false, @@ -30,111 +34,133 @@ { "name": "Dashboard", "reference": "Xbox.Dashboard_8wekyb3d8bbwe!Xbox.Dashboard.Application", + "referenceId": "", "type": "HOME_SCREEN" }, { "name": "Settings", "reference": "Microsoft.Xbox.Settings_8wekyb3d8bbwe!Xbox.Settings.Application", + "referenceId": "", "type": "OTHER" }, { "name": "Accessory", "reference": "Microsoft.XboxDevices_8wekyb3d8bbwe!App", + "referenceId": "", "type": "OTHER" }, { "name": "Spotify", "reference": "SpotifyAB.SpotifyMusic-forXbox_zpdnekdrzrea0!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "YouTube", "reference": "GoogleInc.YouTube_yfg5n0ztvskxp!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Netflix", "reference": "4DF9E0F8.Netflix_mcm4njqhnhss8!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Telewizja", "reference": "Microsoft.Xbox.LiveTV_8wekyb3d8bbwe!Microsoft.Xbox.LiveTV.Application", + "referenceId": "", "type": "HDMI" }, { "name": "Sklep", "reference": "Microsoft.WindowsStore_8wekyb3d8bbwe!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Microsoft Edge", "reference": "Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge", + "referenceId": "", "type": "APPLICATION" }, { "name": "Airserver", "reference": "F3F176BD.53203526D8F6C_p8qzvses5c8me!AirServer", + "referenceId": "", "type": "APPLICATION" }, { "name": "Gears of War 5", "reference": "Microsoft.HalifaxBaseGame_8wekyb3d8bbwe!HalifaxGameShip", + "referenceId": "", "type": "APPLICATION" }, { "name": "Fortnite", "reference": "Fortnite_d5xxtpggmzx6p!AppFortnite", + "referenceId": "", "type": "APPLICATION" }, { "name": "Minecraft", "reference": "Microsoft.MinecraftUWPConsole_8wekyb3d8bbwe!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Plex", "reference": "CAF9E577.PlexforXbox_aam28m9va5cke!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Bluray", "reference": "Microsoft.BlurayPlayer_8wekyb3d8bbwe!Xbox.BlurayPlayer.Application", + "referenceId": "", "type": "APPLICATION" }, { "name": "COD WII", "reference": "shg2SubmissionENFR_ht1qfjb0gaftw!S2Boot", + "referenceId": "", "type": "APPLICATION" }, { "name": "COD WZ", "reference": "iw8Submission-EN-FR_ht1qfjb0gaftw!iw8", + "referenceId": "", "type": "APPLICATION" }, { "name": "GTA V", "reference": "GTA-V_vesz1v3mcwykm!GTAV", + "referenceId": "", "type": "APPLICATION" }, { "name": "All4", "reference": "CHANNELFOURTELEVISIONCOMP.All4_e1252dwpj85a4!vstest.executionengine.universal.App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Amazon Prime", "reference": "AmazonVideo.AmazonVideoUK_pwbj9vvecjh7j!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "Disney", "reference": "Disney.37853FC22B2CE_6rarf9sa4v8jt!App", + "referenceId": "", "type": "APPLICATION" }, { "name": "BBC iPlayer", "reference": "BBCMobileApps.BBCIPLAYER_wzgfedwv7gft2!App", + "referenceId": "", "type": "APPLICATION" } ], @@ -142,10 +168,12 @@ { "name": "Disney", "reference": "Disney.37853FC22B2CE_6rarf9sa4v8jt!App", + "referenceId": "" }, { "name": "BBC iPlayer", "reference": "BBCMobileApps.BBCIPLAYER_wzgfedwv7gft2!App", + "referenceId": "" } ], "manufacturer": "Microsoft Corporation",