From 916e08371bfc716a363f29eaf37d6d19851d1b2d Mon Sep 17 00:00:00 2001 From: Grzegorz Date: Sun, 27 Feb 2022 16:41:48 +0100 Subject: [PATCH] bump --- CHANGELOG.md | 9 + README.md | 23 +- config.schema.json | 141 +++++++- index.js | 65 ++-- package-lock.json | 741 +++++++++++++++++++++++++++++++++++++++- package.json | 3 +- sample-config.json | 12 +- src/mqtt.js | 58 ++++ src/packet/message.js | 4 +- src/packet/packer.js | 2 +- src/packet/structure.js | 56 +-- src/smartglass.js | 314 +++++++++-------- 12 files changed, 1193 insertions(+), 235 deletions(-) create mode 100644 src/mqtt.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 6debf67..4bf2b23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### NOTE!!! ## After update to 2.x.x the plugin settings (xboxLiveId) need to be updated. +## [2.2.0] - (27.02.2022) +## Added +- MQTT Client, publish all device data +- possibility to set custom command for Info button in RC + +## Changes +- update dependencies +- code refactor + ## [2.1.3] - (28.01.2022) ### Fixed - offset out of range diff --git a/README.md b/README.md index 6372f01..e89f34e 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ Homebridge plugin for Microsoft game Consoles. Tested with Xbox One X/S and Xbox * Siri can be used for all functions, some times need create legacy buttons/switches/sensors. * Automations can be used for all functions, some times need create legacy buttons/switches/sensors. * Record Game DVR with additional button. +* MQTT Client publisch all available data from device.

Accessory tile in the HomeKit app @@ -98,7 +99,7 @@ Install and use [Homebridge Config UI X](https://github.com/oznu/homebridge-conf | `disableLogDeviceInfo` | If enabled, add ability to disable log device info by every connections device to the network. | | `enableDebugMode` | If enabled, deep log will be present in homebridge console. | | `volumeControl` | Here select what a additional volume control mode You want to use (None, Slider, Fan), not yet implemented. | -| `switchInfoMenu` | If enabled, `I` button change its behaviour in RC app between Menu and INFO. | +| `infoButtonCommand` | Here select the function of `I` button in RC app. | | `getInputsFromDevice`| If enabled, apps will be loaded from device, only available if `webApiControl` enabled. | | `filterGames` | If enabled, Games will be hidden and not displayed in the inputs list, only available if `webApiControl` enabled. | | `filterApps` | If enabled, Apps will be hidden and not displayed in the inputs list, only available if `webApiControl` enabled. | @@ -112,6 +113,14 @@ Install and use [Homebridge Config UI X](https://github.com/oznu/homebridge-conf | `buttons.command` | Here select button control mode or command, `Reboot` and `Switch App/Game`- only possible if `webApiControl` enabled. | | `buttons.oneStoreProductId` | Here set *Input oneStoreProductId*, only possible if `webApiControl` enabled.| | `buttons.displayType` | Here select display type in HomeKit app, possible `Switch`, `Button` - selectable in HomeKit app as Light, Fan, Outlet.| +| `enableMqtt` | If enabled, MQTT Broker will start automatically and publish all awailable PV installation data. | +| `mqttHost` | Here set the *IP Address* or *Hostname* for MQTT Broker.) | +| `mqttPort` | Here set the *Port* for MQTT Broker, default 1883.) | +| `mqttPrefix` | Here set the *Prefix* for *Topic* or leave empty.) | +| `mqttAuth` | If enabled, MQTT Broker will use authorization credentials. | +| `mqttUser` | Here set the MQTT Broker user. | +| `mqttPasswd` | Here set the MQTT Broker password. | +| `mqttDebug` | If enabled, deep log will be present in homebridge console for MQTT. | | `reference`, `oneStoreProductId` | If web Api enabled then all available in `./homebridge/xboxTv/inputs_xxxxxx` file. | *Example Config: @@ -134,7 +143,7 @@ Install and use [Homebridge Config UI X](https://github.com/oznu/homebridge-conf "disableLogDeviceInfo": false, "enableDebugMode": false, "volumeControl": 0, - "switchInfoMenu": false, + "infoButtonCommand": "nexus", "getInputsFromDevice": false, "filterGames": false, "filterApps": false, @@ -176,7 +185,15 @@ Install and use [Homebridge Config UI X](https://github.com/oznu/homebridge-conf "oneStoreProductId": "oneStoreProductId", "displayType": 0 }, - ] + ], + "enableMqtt": false, + "mqttHost": "192.168.1.33", + "mqttPort": 1883, + "mqttPrefix": "home/xbox", + "mqttAuth": false, + "mqttUser": "user", + "mqttPass": "password", + "mqttDebug": false } ] } diff --git a/config.schema.json b/config.schema.json index 8536a33..eaab1f4 100644 --- a/config.schema.json +++ b/config.schema.json @@ -70,35 +70,35 @@ "required": false }, "disableLogInfo": { - "title": "Disable log info", + "title": "Disable Log Info", "type": "boolean", "default": false, "description": "This disable logging values and states on every it change.", "required": false - }, - "disableLogDeviceInfo": { - "title": "Disable log device info", + }, + "disableLogDeviceInfo": { + "title": "Disable Log Device Info", "type": "boolean", "default": false, "description": "This disable log device info by every connections device to the network.", "required": false - }, - "enableDebugMode": { - "title": "Enable Debug Mode", + }, + "enableDebugMode": { + "title": "Debug", "type": "boolean", "default": false, "description": "This enable debug mode.", "required": false - }, + }, "webApiControl": { - "title": "Enable Web Api", + "title": "Web Api", "type": "boolean", "default": false, "description": "This enable possibility to control Xbox over Web Api.", "required": false }, "volumeControl": { - "title": "Volume Control Mode", + "title": "Volume Control", "type": "integer", "minimum": 0, "maximum": 2, @@ -125,12 +125,24 @@ ], "required": true }, - "switchInfoMenu": { - "title": "Switch Info/Menu", - "type": "boolean", - "default": false, - "description": "This toggle behaviour for *I* button in RC app and *PowerModeSelection* in settings.", - "required": false + "infoButtonCommand": { + "title": "Info Button", + "type": "string", + "oneOf": [{ + "title": "View", + "enum": [ + "view" + ] + }, + { + "title": "Nexus", + "enum": [ + "nexus" + ] + } + ], + "description": "Here select the function of info button in RC.", + "required": true }, "getInputsFromDevice": { "title": "Load Inputs From Device", @@ -532,6 +544,84 @@ } } } + }, + "enableMqtt": { + "title": "Enable", + "type": "boolean", + "default": false, + "required": false, + "description": "This enable MQTT client." + }, + "mqttHost": { + "title": "IP/Hostname", + "type": "string", + "placeholder": "ip or hostname", + "required": true, + "format": "hostname", + "description": "Here set the IP/Hostname of MQTT Broker.", + "condition": { + "functionBody": "return model.devices[arrayIndices].enableMqtt === true;" + } + }, + "mqttPort": { + "title": "Port", + "type": "integer", + "placeholder": 1883, + "required": true, + "description": "Here set the port of MQTT Broker.", + "condition": { + "functionBody": "return model.devices[arrayIndices].enableMqtt === true;" + } + }, + "mqttPrefix": { + "title": "Prefix", + "type": "string", + "placeholder": "home/xbox", + "required": false, + "description": "Here set the prefix.", + "condition": { + "functionBody": "return model.devices[arrayIndices].enableMqtt === true;" + } + }, + "mqttAuth": { + "title": "Authorization", + "type": "boolean", + "default": false, + "required": false, + "description": "This enable authorization for MQTT Broker.", + "condition": { + "functionBody": "return model.devices[arrayIndices].enableMqtt === true;" + } + }, + "mqttUser": { + "title": "User", + "type": "string", + "placeholder": "user", + "required": false, + "description": "Here set the user of MQTT Broker.", + "condition": { + "functionBody": "return model.devices[arrayIndices].enableMqtt === true && model.devices[arrayIndices].mqttAuth === true;" + } + }, + "mqttPasswd": { + "title": "Password", + "type": "string", + "placeholder": "password", + "required": false, + "description": "Here set the password of MQTT Broker.", + "condition": { + "functionBody": "return model.devices[arrayIndices].enableMqtt === true && model.devices[arrayIndices].mqttAuth === true;" + } + }, + "mqttDebug": { + "title": "Debug", + "type": "boolean", + "default": false, + "required": false, + "description": "This enable debug mode for MQTT.", + "condition": { + "functionBody": "return model.devices[arrayIndices].enableMqtt === true;" + } } } } @@ -593,6 +683,23 @@ ] }] }, + { + "key": "devices[]", + "type": "section", + "title": "MQTT Client", + "expandable": true, + "expanded": false, + "items": [ + "devices[].enableMqtt", + "devices[].mqttHost", + "devices[].mqttPort", + "devices[].mqttPrefix", + "devices[].mqttAuth", + "devices[].mqttUser", + "devices[].mqttPasswd", + "devices[].mqttDebug" + ] + }, { "key": "devices[]", "type": "section", @@ -620,7 +727,7 @@ "devices[].enableDebugMode", "devices[].disableLogInfo", "devices[].disableLogDeviceInfo", - "devices[].switchInfoMenu", + "devices[].infoButtonCommand", "devices[].volumeControl" ] } diff --git a/index.js b/index.js index 547ac5d..499ce37 100644 --- a/index.js +++ b/index.js @@ -6,9 +6,7 @@ const fsPromises = fs.promises; const XboxWebApi = require('xbox-webapi'); const Smartglass = require('./src/smartglass'); -const { - format -} = require('path'); +const mqttClient = require('./src/mqtt.js'); const PLUGIN_NAME = 'homebridge-xbox-tv'; const PLATFORM_NAME = 'XboxTv'; @@ -202,12 +200,11 @@ class xboxTvDevice { this.userToken = config.userToken; this.userHash = config.userHash; this.xboxWebApiToken = config.xboxWebApiToken || ''; - this.webApiControl = config.webApiControl || false; this.disableLogInfo = config.disableLogInfo || false; this.disableLogDeviceInfo = config.disableLogDeviceInfo || false; this.enableDebugMode = config.enableDebugMode || false; this.volumeControl = config.volumeControl || 0; - this.switchInfoMenu = config.switchInfoMenu || false; + this.infoButtonCommand = config.infoButtonCommand || 'nexus'; this.getInputsFromDevice = config.getInputsFromDevice || false; this.filterGames = config.filterGames || false; this.filterApps = config.filterApps || false; @@ -215,6 +212,14 @@ class xboxTvDevice { this.filterDlc = config.filterDlc || false; this.inputs = config.inputs || []; this.buttons = config.buttons || []; + this.enableMqtt = config.enableMqtt || false; + this.mqttHost = config.mqttHost; + this.mqttPort = config.mqttPort || 1883; + this.mqttPrefix = config.mqttPrefix; + this.mqttAuth = config.mqttAuth || false; + this.mqttUser = config.mqttUser; + this.mqttPasswd = config.mqttPasswd; + this.mqttDebug = config.mqttDebug || false; //add configured inputs to the default inputs const inputsArr = new Array(); @@ -255,9 +260,6 @@ class xboxTvDevice { this.volume = 0; this.muteState = true; this.mediaState = 0; - - this.setStartInput = false; - this.startInputIdentifier = 0; this.inputIdentifier = 0; this.pictureMode = 0; @@ -297,6 +299,36 @@ class xboxTvDevice { fs.writeFileSync(this.inputsTargetVisibilityFile, ''); } + //mqtt client + this.mqttClient = new mqttClient({ + enabled: this.enableMqtt, + host: this.mqttHost, + port: this.mqttPort, + prefix: this.mqttPrefix, + topic: this.name, + auth: this.mqttAuth, + user: this.mqttUser, + passwd: this.mqttPasswd, + debug: this.mqttDebug + }); + + this.mqttClient.on('connected', (message) => { + this.log('Device: %s %s, %s', this.host, this.name, message); + }) + .on('error', (error) => { + this.log('Device: %s %s, %s', this.host, this.name, error); + }) + .on('debug', (message) => { + this.log('Device: %s %s, debug: %s', this.host, this.name, message); + }) + .on('message', (message) => { + this.log('Device: %s %s, %s', this.host, this.name, message); + }) + .on('disconnected', (message) => { + this.log('Device: %s %s, %s', this.host, this.name, message); + }); + + //web api client this.xboxWebApi = XboxWebApi({ clientId: this.clientId, clientSecret: this.clientSecret, @@ -305,6 +337,7 @@ class xboxTvDevice { }); const checkAuthorizationState = this.webApiControl ? this.getAuthorizationState() : false + //xbox client this.xbox = new Smartglass({ host: this.host, xboxLiveId: this.xboxLiveId, @@ -365,13 +398,6 @@ class xboxTvDevice { this.televisionService .updateCharacteristic(Characteristic.Active, powerState) .updateCharacteristic(Characteristic.ActiveIdentifier, inputIdentifier); - - if (this.setStartInput) { - setTimeout(() => { - this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, this.startInputIdentifier); - this.setStartInput = false; - }, 1200); - } }; if (this.speakerService) { @@ -396,6 +422,9 @@ class xboxTvDevice { this.mediaState = mediaState; this.inputIdentifier = inputIdentifier; }) + .on('mqtt', (topic, message) => { + this.mqttClient.send(topic, message); + }) .on('disconnected', (message) => { const stopInterval = this.webApiControl ? clearInterval(this.checkAuthorizationState) : false; this.log('Device: %s %s, %s', this.host, this.name, message); @@ -753,14 +782,12 @@ class xboxTvDevice { const setTelevision = (inputOneStoreProductId === 'Television'); const setApp = ((inputOneStoreProductId != undefined && inputOneStoreProductId != '0') && !setDashboard && !setTelevision); try { - const setInput = (this.powerState && this.webApiEnabled) ? setApp ? await this.xboxWebApi.getProvider('smartglass').launchApp(this.xboxLiveId, inputOneStoreProductId) : setDashboard ? await this.xboxWebApi.getProvider('smartglass').launchDashboard(this.xboxLiveId) : setTelevision ? await this.xboxWebApi.getProvider('smartglass').launchOneGuide(this.xboxLiveId) : false : false; + const setInput = (this.webApiEnabled) ? setApp ? await this.xboxWebApi.getProvider('smartglass').launchApp(this.xboxLiveId, inputOneStoreProductId) : setDashboard ? await this.xboxWebApi.getProvider('smartglass').launchDashboard(this.xboxLiveId) : setTelevision ? await this.xboxWebApi.getProvider('smartglass').launchOneGuide(this.xboxLiveId) : false : false; const logInfo = this.disableLogInfo ? false : this.log('Device: %s %s, set Input successful, input: %s, reference: %s, product Id: %s', this.host, accessoryName, inputName, inputReference, inputOneStoreProductId); this.inputIdentifier = inputIdentifier; } catch (error) { this.log.error('Device: %s %s, set Input error: %s', this.host, accessoryName, error); }; - this.setStartInput = !this.powerState; - this.startInputIdentifier = inputIdentifier; }); this.televisionService.getCharacteristic(Characteristic.RemoteKey) @@ -816,7 +843,7 @@ class xboxTvDevice { channelName = 'systemMedia'; break; case Characteristic.RemoteKey.INFORMATION: - command = this.switchInfoMenu ? 'nexus' : 'view'; + command = this.infoButtonCommand; channelName = 'systemInput'; break; }; diff --git a/package-lock.json b/package-lock.json index 8d083a5..9e5e6f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "homebridge-xbox-tv", - "version": "2.1.3", + "version": "2.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "2.1.3", + "version": "2.2.0", "license": "MIT", "dependencies": { "@homebridge/plugin-ui-utils": ">=0.0.19", + "async-mqtt": "^2.6.2", "elliptic": "^6.5.4", "hex-to-binary": ">=1.0.1", "jsrsasign": ">=10.5.1", @@ -44,6 +45,49 @@ "version": "1.1.1", "license": "MIT" }, + "node_modules/async-mqtt": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async-mqtt/-/async-mqtt-2.6.2.tgz", + "integrity": "sha512-541G08kFaIZLdNdHEo4lNmUPpOn1Yc9VdcB6pw99VfknPIZFOu2qwjQM1edxkK8KWbJUHcr7x3yf3RAbAFSPJQ==", + "license": "MIT", + "dependencies": { + "mqtt": "^4.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bn.js": { "version": "4.11.9", "license": "MIT" @@ -78,10 +122,47 @@ "version": "2.0.0", "license": "MIT" }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/brorand": { "version": "1.1.0", "license": "MIT" }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, "node_modules/bytes": { "version": "3.1.0", "license": "MIT", @@ -89,6 +170,34 @@ "node": ">= 0.8" } }, + "node_modules/commist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", + "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", + "dependencies": { + "leven": "^2.1.0", + "minimist": "^1.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/content-disposition": { "version": "0.5.3", "license": "MIT", @@ -143,6 +252,17 @@ "version": "1.0.4", "license": "MIT" }, + "node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" @@ -169,6 +289,14 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/escape-html": { "version": "1.0.3", "license": "MIT" @@ -271,6 +399,30 @@ "node": ">= 0.6" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/hash.js": { "version": "1.1.7", "license": "MIT", @@ -279,6 +431,15 @@ "minimalistic-assert": "^1.0.1" } }, + "node_modules/help-me": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", + "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", + "dependencies": { + "glob": "^7.1.6", + "readable-stream": "^3.6.0" + } + }, "node_modules/hex-to-binary": { "version": "1.0.1", "license": "MIT" @@ -320,6 +481,34 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "license": "ISC" @@ -331,6 +520,11 @@ "node": ">= 0.10" } }, + "node_modules/js-sdsl": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-2.1.4.tgz", + "integrity": "sha512-/Ew+CJWHNddr7sjwgxaVeIORIH4AMVC9dy0hPf540ZGMVgS9d3ajwuVdyhDt6/QUvT8ATjR3yuYBKsS79F+H4A==" + }, "node_modules/jsrsasign": { "version": "10.5.1", "license": "MIT", @@ -338,6 +532,25 @@ "url": "https://github.com/kjur/jsrsasign#donations" } }, + "node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/media-typer": { "version": "0.3.0", "license": "MIT", @@ -391,6 +604,64 @@ "version": "1.0.1", "license": "MIT" }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mqtt": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.6.tgz", + "integrity": "sha512-1dgQbkbh1Bba9iAGDNIrhSZ4nLDjbhmNHjOEvsmKI1Bb+2orj0gHwjqUKJ5CKUMKBYbkQYRM1fy+N1/2iZOj5w==", + "dependencies": { + "commist": "^1.0.0", + "concat-stream": "^2.0.0", + "debug": "^4.1.1", + "duplexify": "^4.1.1", + "help-me": "^3.0.0", + "inherits": "^2.0.3", + "lru-cache": "^6.0.0", + "minimist": "^1.2.5", + "mqtt-packet": "^6.8.0", + "number-allocator": "^1.0.9", + "pump": "^3.0.0", + "readable-stream": "^3.6.0", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^3.1.0", + "ws": "^7.5.5", + "xtend": "^4.0.2" + }, + "bin": { + "mqtt": "bin/mqtt.js", + "mqtt_pub": "bin/pub.js", + "mqtt_sub": "bin/sub.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", + "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", + "dependencies": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + } + }, "node_modules/ms": { "version": "2.1.2", "license": "MIT" @@ -402,6 +673,15 @@ "node": ">= 0.6" } }, + "node_modules/number-allocator": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.9.tgz", + "integrity": "sha512-sIIF0dZKMs3roPUD7rLreH8H3x47QKV9dHZ+PeSnH24gL0CxKxz/823woGZC0hLBSb2Ar/rOOeHiNbnPBum/Mw==", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "^2.1.2" + } + }, "node_modules/on-finished": { "version": "2.3.0", "license": "MIT", @@ -412,6 +692,14 @@ "node": ">= 0.8" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "license": "MIT", @@ -419,10 +707,23 @@ "node": ">= 0.8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "license": "MIT" }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/proxy-addr": { "version": "2.0.6", "license": "MIT", @@ -434,6 +735,15 @@ "node": ">= 0.10" } }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/qs": { "version": "6.7.0", "license": "BSD-3-Clause", @@ -461,6 +771,29 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, "node_modules/safe-buffer": { "version": "5.1.2", "license": "MIT" @@ -523,6 +856,14 @@ "version": "1.1.1", "license": "ISC" }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, "node_modules/statuses": { "version": "1.5.0", "license": "MIT", @@ -530,6 +871,38 @@ "node": ">= 0.6" } }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/toidentifier": { "version": "1.0.0", "license": "MIT", @@ -548,6 +921,11 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, "node_modules/unpipe": { "version": "1.0.0", "license": "MIT", @@ -555,6 +933,11 @@ "node": ">= 0.8" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "node_modules/utils-merge": { "version": "1.0.1", "license": "MIT", @@ -584,6 +967,31 @@ "node": ">= 0.8" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xbox-webapi": { "version": "1.2.0", "license": "MIT", @@ -592,6 +1000,19 @@ "express": "^4.17.1", "uuid4": "^2.0.2" } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } }, "dependencies": { @@ -608,6 +1029,34 @@ "array-flatten": { "version": "1.1.1" }, + "async-mqtt": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async-mqtt/-/async-mqtt-2.6.2.tgz", + "integrity": "sha512-541G08kFaIZLdNdHEo4lNmUPpOn1Yc9VdcB6pw99VfknPIZFOu2qwjQM1edxkK8KWbJUHcr7x3yf3RAbAFSPJQ==", + "requires": { + "mqtt": "^4.1.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "bn.js": { "version": "4.11.9" }, @@ -637,12 +1086,60 @@ } } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "brorand": { "version": "1.1.0" }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, "bytes": { "version": "3.1.0" }, + "commist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", + "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", + "requires": { + "leven": "^2.1.0", + "minimist": "^1.1.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "content-disposition": { "version": "0.5.3", "requires": { @@ -670,6 +1167,17 @@ "destroy": { "version": "1.0.4" }, + "duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, "ee-first": { "version": "1.1.1" }, @@ -690,6 +1198,14 @@ "encodeurl": { "version": "1.0.2" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "escape-html": { "version": "1.0.3" }, @@ -771,6 +1287,24 @@ "fresh": { "version": "0.5.2" }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "hash.js": { "version": "1.1.7", "requires": { @@ -778,6 +1312,15 @@ "minimalistic-assert": "^1.0.1" } }, + "help-me": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", + "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", + "requires": { + "glob": "^7.1.6", + "readable-stream": "^3.6.0" + } + }, "hex-to-binary": { "version": "1.0.1" }, @@ -810,15 +1353,47 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.4" }, "ipaddr.js": { "version": "1.9.1" }, + "js-sdsl": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-2.1.4.tgz", + "integrity": "sha512-/Ew+CJWHNddr7sjwgxaVeIORIH4AMVC9dy0hPf540ZGMVgS9d3ajwuVdyhDt6/QUvT8ATjR3yuYBKsS79F+H4A==" + }, "jsrsasign": { "version": "10.5.1" }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, "media-typer": { "version": "0.3.0" }, @@ -846,24 +1421,98 @@ "minimalistic-crypto-utils": { "version": "1.0.1" }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mqtt": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.6.tgz", + "integrity": "sha512-1dgQbkbh1Bba9iAGDNIrhSZ4nLDjbhmNHjOEvsmKI1Bb+2orj0gHwjqUKJ5CKUMKBYbkQYRM1fy+N1/2iZOj5w==", + "requires": { + "commist": "^1.0.0", + "concat-stream": "^2.0.0", + "debug": "^4.1.1", + "duplexify": "^4.1.1", + "help-me": "^3.0.0", + "inherits": "^2.0.3", + "lru-cache": "^6.0.0", + "minimist": "^1.2.5", + "mqtt-packet": "^6.8.0", + "number-allocator": "^1.0.9", + "pump": "^3.0.0", + "readable-stream": "^3.6.0", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^3.1.0", + "ws": "^7.5.5", + "xtend": "^4.0.2" + } + }, + "mqtt-packet": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", + "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", + "requires": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + } + }, "ms": { "version": "2.1.2" }, "negotiator": { "version": "0.6.2" }, + "number-allocator": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.9.tgz", + "integrity": "sha512-sIIF0dZKMs3roPUD7rLreH8H3x47QKV9dHZ+PeSnH24gL0CxKxz/823woGZC0hLBSb2Ar/rOOeHiNbnPBum/Mw==", + "requires": { + "debug": "^4.3.1", + "js-sdsl": "^2.1.2" + } + }, "on-finished": { "version": "2.3.0", "requires": { "ee-first": "1.1.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, "parseurl": { "version": "1.3.3" }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, "path-to-regexp": { "version": "0.1.7" }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "proxy-addr": { "version": "2.0.6", "requires": { @@ -871,6 +1520,15 @@ "ipaddr.js": "1.9.1" } }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "qs": { "version": "6.7.0" }, @@ -886,6 +1544,26 @@ "unpipe": "1.0.0" } }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, "safe-buffer": { "version": "5.1.2" }, @@ -938,9 +1616,37 @@ "setprototypeof": { "version": "1.1.1" }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "requires": { + "readable-stream": "^3.0.0" + } + }, "statuses": { "version": "1.5.0" }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "toidentifier": { "version": "1.0.0" }, @@ -951,9 +1657,19 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, "unpipe": { "version": "1.0.0" }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "utils-merge": { "version": "1.0.1" }, @@ -969,6 +1685,17 @@ "vary": { "version": "1.1.2" }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "requires": {} + }, "xbox-webapi": { "version": "1.2.0", "requires": { @@ -976,6 +1703,16 @@ "express": "^4.17.1", "uuid4": "^2.0.2" } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index 86229e0..1ad2d3a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "Xbox TV", "name": "homebridge-xbox-tv", - "version": "2.1.3", + "version": "2.2.0", "description": "Homebridge plugin (https://github.com/homebridge/homebridge) to control Xbox game consoles.", "license": "MIT", "author": "grzegorz914", @@ -34,6 +34,7 @@ }, "dependencies": { "@homebridge/plugin-ui-utils": ">=0.0.19", + "async-mqtt": "^2.6.2", "elliptic": "^6.5.4", "hex-to-binary": ">=1.0.1", "jsrsasign": ">=10.5.1", diff --git a/sample-config.json b/sample-config.json index cf15683..9561d1f 100644 --- a/sample-config.json +++ b/sample-config.json @@ -30,7 +30,7 @@ "disableLogDeviceInfo": false, "enableDebugMode": false, "volumeControl": 0, - "switchInfoMenu": false, + "infoButtonCommand": "nexus", "getInputsFromDevice": false, "filterGames": false, "filterApps": false, @@ -82,7 +82,15 @@ "oneStoreProductId": "oneStoreProductId", "displayType": 0 } - ] + ], + "enableMqtt": false, + "mqttHost": "192.168.1.33", + "mqttPort": 1883, + "mqttPrefix": "home/denon", + "mqttAuth": false, + "mqttUser": "user", + "mqttPass": "password", + "mqttDebug": false }] }] } \ No newline at end of file diff --git a/src/mqtt.js b/src/mqtt.js new file mode 100644 index 0000000..d9500a2 --- /dev/null +++ b/src/mqtt.js @@ -0,0 +1,58 @@ +"use strict"; +const MQTT = require("async-mqtt"); +const EventEmitter = require('events'); + +class MQTTCLIENT extends EventEmitter { + constructor(config) { + super(); + this.mqttEnabled = config.enabled; + this.mqttHost = config.host; + this.mqttPort = config.port; + this.mqttPrefix = config.prefix; + this.mqttTopic = config.topic; + this.mqttAuth = config.auth; + this.mqttUser = config.user; + this.mqttPasswd = config.passwd; + this.isConnected = false; + + const run = this.mqttEnabled ? this.connect() : false; + }; + + async connect() { + try { + const options = { + username: this.mqttUser, + password: this.mqttPasswd + } + const url = `mqtt://${this.mqttHost}:${this.mqttPort}`; + this.mqttClient = await MQTT.connectAsync(url, options); + + this.isConnected = true; + this.emit('connected', 'MQTT Connected.'); + } catch (error) { + this.isConnected = false; + this.emit('error', error); + }; + }; + + async send(topic, message) { + if (!this.isConnected) { + return + }; + + try { + const fullTopic = `${this.mqttPrefix}/${this.mqttTopic}/${topic}`; + await this.mqttClient.publish(fullTopic, message); + this.emit('debug', `MQTT publish: ${fullTopic}: ${message}`); + } catch (error) { + await this.mqttClient.end(); + this.isConnected = false; + this.emit('disconnected', 'MQTT Disconnected, trying to reconnect.'); + + setTimeout(() => { + this.connect(); + }, 5000); + }; + }; +}; +module.exports = MQTTCLIENT; \ No newline at end of file diff --git a/src/packet/message.js b/src/packet/message.js index b2cbcc4..7ace31d 100644 --- a/src/packet/message.js +++ b/src/packet/message.js @@ -104,10 +104,10 @@ class MESSAGE { value: value, length: length, pack(packetStructure) { - return packetStructure.writeBytes(this.setFlags(this.value)); + return packetStructure.writeBytes(setFlags(this.value)); }, unpack(packetStructure) { - this.value = this.readFlags(packetStructure.readBytes(this.length)); + this.value = readFlags(packetStructure.readBytes(this.length)); return this.value; } } diff --git a/src/packet/packer.js b/src/packet/packer.js index d707326..07028c4 100644 --- a/src/packet/packer.js +++ b/src/packet/packer.js @@ -13,7 +13,7 @@ const Types = { class PACKER { constructor(type) { const packetType = type.slice(0, 2).toString('hex'); - let structure = ''; + this.packetStructure = ''; if (packetType in Types) { const packetValue = type; type = Types[packetType]; diff --git a/src/packet/structure.js b/src/packet/structure.js index 48bdea9..307ca7d 100644 --- a/src/packet/structure.js +++ b/src/packet/structure.js @@ -1,6 +1,7 @@ class STRUCTURE { constructor(packet) { this.packet = (packet == undefined) ? Buffer.from('') : packet; + this.totalLength = this.packet.length; this.offset = 0; }; @@ -9,15 +10,14 @@ class STRUCTURE { }; writeSGString(data) { - const lengthBuffer = Buffer.allocUnsafe(2); - lengthBuffer.writeUInt16BE(data.length, 0); - - const dataBuffer = Buffer.from(data + '\x00'); - this.add(Buffer.concat([ - lengthBuffer, - dataBuffer + const bufferLength = Buffer.allocUnsafe(2); + bufferLength.writeUInt16BE(data.length, 0); + const buffer = Buffer.from(data + '\x00'); + const packet = this.add(Buffer.concat([ + bufferLength, + buffer ])); - return this; + return packet; }; readSGString() { @@ -28,9 +28,9 @@ class STRUCTURE { }; writeBytes(data, type) { - const dataBuffer = Buffer.from(data, type); - this.add(dataBuffer); - return this; + const buffer = Buffer.from(data, type); + const packet = this.add(buffer); + return packet; }; readBytes(count = false) { @@ -38,7 +38,7 @@ class STRUCTURE { if (count == false) { data = this.packet.slice(this.offset); - this.offset = this.packet.length;; + this.offset = this.totalLength; } else { data = this.packet.slice(this.offset, this.offset + count); this.offset = (this.offset + count); @@ -47,10 +47,10 @@ class STRUCTURE { }; writeUInt8(data) { - const tempBuffer = Buffer.allocUnsafe(1); - tempBuffer.writeUInt8(data, 0); - this.add(tempBuffer); - return this; + const buffer = Buffer.allocUnsafe(1); + buffer.writeUInt8(data, 0); + const packet = this.add(buffer); + return packet; }; readUInt8() { @@ -60,10 +60,10 @@ class STRUCTURE { }; writeUInt16(data) { - const tempBuffer = Buffer.allocUnsafe(2); - tempBuffer.writeUInt16BE(data, 0); - this.add(tempBuffer); - return this; + const buffer = Buffer.allocUnsafe(2); + buffer.writeUInt16BE(data, 0); + const packet = this.add(buffer); + return packet; }; readUInt16() { @@ -73,10 +73,10 @@ class STRUCTURE { }; writeUInt32(data) { - const tempBuffer = Buffer.allocUnsafe(4); - tempBuffer.writeUInt32BE(data, 0); - this.add(tempBuffer); - return this; + const buffer = Buffer.allocUnsafe(4); + buffer.writeUInt32BE(data, 0); + const packet = this.add(buffer); + return packet; }; readUInt32() { @@ -86,10 +86,10 @@ class STRUCTURE { }; writeInt32(data) { - const tempBuffer = Buffer.allocUnsafe(4); - tempBuffer.writeInt32BE(data, 0); - this.add(tempBuffer); - return this; + const buffer = Buffer.allocUnsafe(4); + buffer.writeInt32BE(data, 0); + const packet = this.add(buffer); + return packet; }; readInt32() { diff --git a/src/smartglass.js b/src/smartglass.js index 2046264..9cd821f 100644 --- a/src/smartglass.js +++ b/src/smartglass.js @@ -5,7 +5,6 @@ const EOL = require('os').EOL; const jsrsasign = require('jsrsasign'); const EventEmitter = require('events').EventEmitter; const Packer = require('./packet/packer'); -const Structure = require('./packet/structure'); const SGCrypto = require('./sgcrypto'); const systemMediaCommands = { @@ -71,7 +70,6 @@ class SMARTGLASS extends EventEmitter { this.userHash = config.uhs; this.crypto = new SGCrypto(); - this.structure = new Structure(); this.isConnected = false; this.isAuthorized = false; this.fragments = {}; @@ -95,107 +93,6 @@ class SMARTGLASS extends EventEmitter { this.channelRequestId = null; this.message = {}; - //dgram socket - this.socket = new dgram.createSocket('udp4'); - this.socket.on('error', (error) => { - this.emit('error', `Socket error: ${error}`); - this.socket.close(); - }) - .on('message', (message, remote) => { - this.emit('debug', `Received message from: ${remote.address}:${remote.port}`); - this.messageReceivedTime = (new Date().getTime()) / 1000; - message = new Packer(message); - if (message.structure == false) { - return; - }; - this.response = message.unpack(this); - - if (this.response.packetDecoded.type != 'd00d') { - this.function = this.response.name; - } else { - if (this.response.packetDecoded.targetParticipantId != this.participantId) { - this.emit('debug', 'Participant id does not match. Ignoring packet.'); - return; - }; - this.function = message.structure.packetDecoded.name; - }; - - if (this.function == 'json') { - const jsonMessage = JSON.parse(this.response.packetDecoded.protectedPayload.json) - - // Check if JSON is fragmented - if (jsonMessage.datagramId != undefined) { - this.emit('debug', `Json message is fragmented: ${jsonMessage.datagramId}`); - if (this.fragments[jsonMessage.datagramId] == undefined) { - // Prepare buffer for JSON - this.fragments[jsonMessage.datagramId] = { - - getValue() { - let buffer = Buffer.from(''); - for (let partial in this.partials) { - buffer = Buffer.concat([ - buffer, - Buffer.from(this.partials[partial]) - ]) - }; - const bufferDecoded = Buffer(buffer.toString(), 'base64'); - return bufferDecoded; - }, - isValid() { - const json = this.getValue(); - let isValid = false; - try { - JSON.parse(json.toString()); - isValid = true; - } catch (error) { - isValid = false; - this.emit('error', `Valid packet error: ${error}`); - }; - return isValid; - }, - partials: {} - }; - }; - - this.fragments[jsonMessage.datagramId].partials[jsonMessage.fragmentOffset] = jsonMessage.fragmentData; - if (this.fragments[jsonMessage.datagramId].isValid()) { - this.emit('debug', 'Json completed fragmented packet.'); - this.response.packetDecoded.protectedPayload.json = this.fragments[jsonMessage.datagramId].getValue().toString(); - this.fragments[jsonMessage.datagramId] = undefined; - }; - this.function = 'jsonFragment'; - }; - }; - - if (this.function == 'status') { - const decodedMessage = JSON.stringify(this.response.packetDecoded.protectedPayload); - if (this.message === decodedMessage) { - this.emit('debug', 'Received unchanged status message.'); - return; - }; - this.message = decodedMessage; - }; - - this.emit('debug', `Received event type: ${this.function}`); - this.emit(this.function, this.response); - }) - .on('listening', () => { - const address = this.socket.address(); - this.emit('debug', `Server listening: ${address.address}:${address.port}, start discovering.`); - - // Start discovery - this.startDiscovery(); - }) - .on('close', () => { - clearInterval(this.discovery); - this.emit('debug', 'Socket closed.'); - - setTimeout(() => { - this.connect(); - }, 5000); - }) - .bind(); - //EventEmmiter this.on('discoveryResponse', (message) => { clearInterval(this.boot); @@ -333,6 +230,7 @@ class SMARTGLASS extends EventEmitter { const inputReference = appsArray[appsCount - 1].reference; const mediaState = 0; this.emit('stateChanged', power, titleId, inputReference, volume, mute, mediaState); + this.emit('mqtt', 'State', JSON.stringify(decodedMessage, null, 2)); }; }) .on('channelResponse', (message) => { @@ -467,13 +365,127 @@ class SMARTGLASS extends EventEmitter { .on('jsonFragment', (message) => { this.emit('debug', `Json fragment: ${message}`); }); + + this.connect(); + }; + + connect() { + this.socket = new dgram.createSocket('udp4'); + this.socket.on('error', (error) => { + this.emit('error', `Socket error: ${error}`); + clearInterval(this.checkConnection); + clearInterval(this.discovery); + this.socket.close(); + }) + .on('message', (message, remote) => { + this.emit('debug', `Received message from: ${remote.address}:${remote.port}`); + this.messageReceivedTime = (new Date().getTime()) / 1000; + message = new Packer(message); + if (message.structure == false) { + return; + }; + this.response = message.unpack(this); + + if (this.response.packetDecoded.type != 'd00d') { + this.function = this.response.name; + } else { + if (this.response.packetDecoded.targetParticipantId != this.participantId) { + this.emit('debug', 'Participant id does not match. Ignoring packet.'); + return; + }; + this.function = message.structure.packetDecoded.name; + }; + + if (this.function == 'json') { + const jsonMessage = JSON.parse(this.response.packetDecoded.protectedPayload.json) + + // Check if JSON is fragmented + if (jsonMessage.datagramId != undefined) { + this.emit('debug', `Json message is fragmented: ${jsonMessage.datagramId}`); + if (this.fragments[jsonMessage.datagramId] == undefined) { + // Prepare buffer for JSON + this.fragments[jsonMessage.datagramId] = { + + getValue() { + let buffer = Buffer.from(''); + for (let partial in this.partials) { + buffer = Buffer.concat([ + buffer, + Buffer.from(this.partials[partial]) + ]) + }; + const bufferDecoded = Buffer(buffer.toString(), 'base64'); + return bufferDecoded; + }, + isValid() { + const json = this.getValue(); + let isValid = false; + try { + JSON.parse(json.toString()); + isValid = true; + } catch (error) { + isValid = false; + this.emit('error', `Valid packet error: ${error}`); + }; + return isValid; + }, + partials: {} + }; + }; + + this.fragments[jsonMessage.datagramId].partials[jsonMessage.fragmentOffset] = jsonMessage.fragmentData; + if (this.fragments[jsonMessage.datagramId].isValid()) { + this.emit('debug', 'Json completed fragmented packet.'); + this.response.packetDecoded.protectedPayload.json = this.fragments[jsonMessage.datagramId].getValue().toString(); + this.fragments[jsonMessage.datagramId] = undefined; + }; + this.function = 'jsonFragment'; + }; + }; + + if (this.function == 'status') { + const decodedMessage = JSON.stringify(this.response.packetDecoded.protectedPayload); + if (this.message === decodedMessage) { + this.emit('debug', 'Received unchanged status message.'); + return; + }; + this.message = decodedMessage; + }; + + this.emit('debug', `Received event type: ${this.function}`); + this.emit(this.function, this.response); + }) + .on('listening', () => { + const address = this.socket.address(); + this.emit('debug', `Server start listening: ${address.address}:${address.port}.`); + + // Start discovery + this.startDiscovery(); + }) + .on('close', () => { + this.emit('debug', 'Socket closed.'); + + this.isConnected = false; + this.requestNum = 0; + this.channelTargetId = null; + this.channelRequestId = null; + this.emit('stateChanged', false, 0, 0, 0, true, 0); + this.emit('disconnected', 'Disconnected.'); + + //reconnect + this.reconnect(); + }) + .bind(); + }; + + reconnect() { + this.connect(); }; startDiscovery() { this.emit('debug', 'Start discovery.'); this.discovery = setInterval(() => { if (!this.isConnected) { - this.structure.setOffset(0); const discoveryPacket = new Packer('simple.discoveryRequest'); const message = discoveryPacket.pack(); this.sendSocketMessage(message); @@ -481,6 +493,21 @@ class SMARTGLASS extends EventEmitter { }, 5000); }; + getRequestNum() { + this.requestNum++; + this.emit('debug', `Request number set to: ${this.requestNum}`); + }; + + sendSocketMessage(message) { + const messageLength = message.length; + this.socket.send(message, 0, messageLength, 5050, this.host, (error, bytes) => { + if (error) { + this.emit('error', `Socket send message error: ${error}`); + }; + this.emit('debug', `Socket send ${bytes} bytes.`); + }); + }; + powerOn() { return new Promise((resolve, reject) => { if (!this.isConnected) { @@ -488,7 +515,6 @@ class SMARTGLASS extends EventEmitter { const powerOnStartTime = (new Date().getTime()) / 1000; this.boot = setInterval(() => { - this.structure.setOffset(0); const powerOn = new Packer('simple.powerOn'); powerOn.set('liveId', this.xboxLiveId); const message = powerOn.pack(); @@ -497,6 +523,7 @@ class SMARTGLASS extends EventEmitter { const lastPowerOnTime = (Math.trunc(((new Date().getTime()) / 1000) - powerOnStartTime)); if (lastPowerOnTime > 15) { clearInterval(this.boot) + this.emit('stateChanged', false, 0, 0, 0, true, 0); this.emit('disconnected', 'Power On failed, please try again.'); }; }, 500); @@ -513,29 +540,6 @@ class SMARTGLASS extends EventEmitter { }); }; - powerOff() { - return new Promise((resolve, reject) => { - if (this.isConnected) { - this.emit('message', 'Send power Off.'); - - const powerOff = new Packer('message.powerOff'); - powerOff.set('liveId', this.xboxLiveId); - const message = powerOff.pack(this); - this.sendSocketMessage(message); - - setTimeout(() => { - this.disconnect(); - resolve(true); - }, 3500); - } else { - reject({ - status: 'error', - error: 'Already disconnected.' - }); - }; - }); - }; - recordGameDvr() { return new Promise((resolve, reject) => { if (this.isConnected && this.isAuthorized) { @@ -588,23 +592,33 @@ class SMARTGLASS extends EventEmitter { }); }; + powerOff() { + return new Promise((resolve, reject) => { + if (this.isConnected) { + this.emit('message', 'Send power Off.'); + const powerOff = new Packer('message.powerOff'); + powerOff.set('liveId', this.xboxLiveId); + const message = powerOff.pack(this); + this.sendSocketMessage(message); - sendSocketMessage(message) { - if (this.socket) { - const messageLength = message.length; - this.socket.send(message, 0, messageLength, 5050, this.host, (error, bytes) => { - if (error) { - this.emit('error', `Socket send message error: ${error}`); - }; - this.emit('debug', `Socket send ${bytes} bytes.`); - }); - }; + setTimeout(() => { + this.disconnect(); + resolve(true); + }, 3500); + } else { + reject({ + status: 'error', + error: 'Already disconnected.' + }); + }; + }); }; disconnect() { this.emit('debug', 'Disconnecting...'); clearInterval(this.checkConnection); + clearInterval(this.discovery); const disconnect = new Packer('message.disconnect'); disconnect.set('reason', 4); @@ -612,30 +626,10 @@ class SMARTGLASS extends EventEmitter { const message = disconnect.pack(this); this.sendSocketMessage(message); + //colose socket setTimeout(() => { - this.structure.setOffset(0); - this.isConnected = false; - this.requestNum = 0; - this.channelTargetId = null; - this.channelRequestId = null; - this.emit('stateChanged', false, 0, 0, 0, true, 0); - this.emit('disconnected', 'Disconnected.'); - - // Start discovery - this.startDiscovery(); + this.socket.close(); }, 3500); }; - - getRequestNum() { - this.requestNum++; - this.emit('debug', `Request number set to: ${this.requestNum}`); - }; - - connect() { - if (!this.socket) { - this.socket = new dgram.createSocket('udp4'); - this.socket.bind(); - }; - }; }; module.exports = SMARTGLASS; \ No newline at end of file