From cb32fb04300cac3e061e40dd9dc36c6a24ce0e93 Mon Sep 17 00:00:00 2001 From: Grzegorz Date: Fri, 23 Aug 2024 16:35:03 +0200 Subject: [PATCH] release v3.1.0 --- CHANGELOG.md | 8 +++ README.md | 40 ++++++++----- index.js | 3 + package-lock.json | 18 +++--- package.json | 6 +- src/mqtt.js | 2 +- src/restful.js | 15 +++++ src/xboxdevice.js | 145 +++++++++++++++++++++++++--------------------- 8 files changed, 143 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cc3620..37d9d0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,16 @@ 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 + ### After update to v3.0.0 RESTFull and MQTT config settings need to be updated +## [3.1.0] - (23.08.2024) + +## Changes + +- add control over RESTFul POST JSON Object +- cleanup + ## [3.0.2] - (18.08.2024) ## Changes diff --git a/README.md b/README.md index cd33247..47cfd12 100644 --- a/README.md +++ b/README.md @@ -140,22 +140,32 @@ Homebridge plugin for Microsoft game Consoles. Tested with Xbox One X/S and Xbox ### RESTFul Integration -* Request: `http//homebridge_ip_address:port/path`. - * Path: `info`, `state`, `consoleslist`, `profile`, `apps`, `storages`, `status`. - * Respone as JSON object. +* POST data as a JSON Object `{Power: true}` + +| Method | URL | Path | Response | Type | +| --- | --- | --- | --- | --- | +| GET | `http//ip:port` | `info`, `state`, `consoleslist`, `profile`, `apps`, `storages`, `status`. | `{"power": true, "app": Xbox.Dashboard_8wekyb3d8bbwe!Xbox.Dashboard.Application}` | JSON object. | + +| Method | URL | Key | Value | Type | Description | +| --- | --- | --- | --- | --- | --- | +| POST | `http//ip:port` | `Power` | `true`, `false` | boolean | Power state. | +| | `http//ip:port` | `App` | `Xbox.Dashboard_8wekyb3d8bbwe!Xbox.Dashboard.Application` | string | Set app. | +| | `http//ip:port` | `RcControl` | `fastForward` | string | Send RC command. | +| | `http//ip:port` | `Volume` | `up`, `down` | string | Set volume. | +| | `http//ip:port` | `Mute` | `true`, `false` | boolean | Set mute. | ### MQTT Integration -| Direction | Topic | Message | Payload Data | -| --- | --- | --- | --- | -| Publish | `Info`, `State`, `Consoles List`, `Profile`, `Apps`, `Storages`, `Status` | `{"power": true, "app": Xbox.Dashboard_8wekyb3d8bbwe!Xbox.Dashboard.Application}` | JSON object. | -| Subscribe | `Set` | `{"Power": true}` | JSON object. | +* Subscribe data as a JSON Object `{App: "Xbox.Dashboard_8wekyb3d8bbwe!Xbox.Dashboard.Application"}` -| Subscribe | Key | Value | Type | Description | -| --- | --- | --- | --- | --- | -| Xbox | | | | | -| | `Power` | `true`, `false` | boolean | Power state. | -| | `App` | `Xbox.Dashboard_8wekyb3d8bbwe!Xbox.Dashboard.Application` | string | Set app. | -| | `RcControl` | `fastForward` | string | Send RC command. | -| | `Volume` | `up`, `down` | string | Set volume. | -| | `Mute` | `true`, `false` | boolean | Set mute. | +| Method | Topic | Message | Type | +| --- | --- | --- | --- | +| Publish | `Info`, `State`, `Consoles List`, `Profile`, `Apps`, `Storages`, `Status` | `{"power": true, "app": Xbox.Dashboard_8wekyb3d8bbwe!Xbox.Dashboard.Application}` | JSON object. | + +| Method | Topic | Key | Value | Type | Description | +| --- | --- | --- | --- | --- | --- | +| Subscribe | `Set` | `Power` | `true`, `false` | boolean | Power state. | +| | `Set` | `App` | `Xbox.Dashboard_8wekyb3d8bbwe!Xbox.Dashboard.Application` | string | Set app. | +| | `Set` | `RcControl` | `fastForward` | string | Send RC command. | +| | `Set` | `Volume` | `up`, `down` | string | Set volume. | +| | `Set` | `Mute` | `true`, `false` | boolean | Set mute. | diff --git a/index.js b/index.js index 07db9bd..12c407e 100644 --- a/index.js +++ b/index.js @@ -54,6 +54,9 @@ class XboxPlatform { .on('devInfo', (devInfo) => { log.info(devInfo); }) + .on('success', (message) => { + log.success(`Device: ${deviceHost} ${deviceName}, ${message}`); + }) .on('message', (message) => { log.info(`Device: ${deviceHost} ${deviceName}, ${message}`); }) diff --git a/package-lock.json b/package-lock.json index 16ac704..1c6cfca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "homebridge-xbox-tv", - "version": "3.0.0", + "version": "3.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "homebridge-xbox-tv", - "version": "3.0.0", + "version": "3.1.0", "license": "MIT", "dependencies": { "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", - "axios": "^1.7.4", + "axios": "^1.7.5", "elliptic": "^6.5.7", "express": "^4.19.2", "hex-to-binary": "^1.0.1", @@ -64,9 +64,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -1356,9 +1356,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "requires": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 0b9f518..af04a8e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "Xbox TV", "name": "homebridge-xbox-tv", - "version": "3.0.4", + "version": "3.1.0", "description": "Homebridge plugin to control Xbox game consoles.", "license": "MIT", "author": "grzegorz914", @@ -29,7 +29,7 @@ ], "engines": { "node": ">=18.0.0", - "homebridge": ">=1.6.0" + "homebridge": ">=1.8.0" }, "dependencies": { "@homebridge/plugin-ui-utils": "^1.0.3", @@ -40,7 +40,7 @@ "uuid": "^10.0.0", "ping": "^0.4.4", "express": "^4.19.2", - "axios": "^1.7.4" + "axios": "^1.7.5" }, "keywords": [ "homebridge", diff --git a/src/mqtt.js b/src/mqtt.js index 00ae3a9..2459847 100644 --- a/src/mqtt.js +++ b/src/mqtt.js @@ -30,7 +30,7 @@ class Mqtt extends EventEmitter { const emitDebug = config.debug ? this.emit('debug', `MQTT Received topic: ${topic}, message: ${JSON.stringify(subscribedMessage, null, 2)}`) : false; const key = Object.keys(subscribedMessage)[0]; const value = Object.values(subscribedMessage)[0]; - this.emit('subscribedMessage', key, value); + this.emit('set', key, value); } catch (error) { this.emit('error', `MQTT Parse message error: ${error}`); }; diff --git a/src/restful.js b/src/restful.js index d37994d..f50bd20 100644 --- a/src/restful.js +++ b/src/restful.js @@ -24,6 +24,7 @@ class RestFul extends EventEmitter { try { const restFul = express(); restFul.set('json spaces', 2); + restFul.use(express.json()); restFul.get('/info', (req, res) => { res.json(this.restFulData.info) }); restFul.get('/state', (req, res) => { res.json(this.restFulData.state) }); restFul.get('/consoleslist', (req, res) => { res.json(this.restFulData.consoleslist) }); @@ -32,6 +33,20 @@ class RestFul extends EventEmitter { restFul.get('/storages', (req, res) => { res.json(this.restFulData.storages) }); restFul.get('/status', (req, res) => { res.json(this.restFulData.status) }); + //post data + restFul.post('/', (req, res) => { + try { + const obj = req.body; + const emitDebug = this.restFulDebug ? this.emit('debug', `RESTFul post data: ${JSON.stringify(obj, null, 2)}`) : false; + const key = Object.keys(obj)[0]; + const value = Object.values(obj)[0]; + this.emit('set', key, value); + res.send('OK'); + } catch (error) { + this.emit('error', `RESTFul Parse object error: ${error}`); + }; + }); + restFul.listen(this.restFulPort, () => { this.emit('connected', `RESTful started on port: ${this.restFulPort}`) }); diff --git a/src/xboxdevice.js b/src/xboxdevice.js index c9a217c..8d5069f 100644 --- a/src/xboxdevice.js +++ b/src/xboxdevice.js @@ -50,15 +50,10 @@ class XboxDevice extends EventEmitter { this.volumeControlNamePrefix = device.volumeControlNamePrefix || false; this.volumeControlName = device.volumeControlName || 'Volume'; - //external integration - //restRul + //external integrations const restFul = device.restFul ?? {}; - const restFulEnabled = restFul.enable || false; this.restFulConnected = false; - - //mqtt const mqtt = device.mqtt ?? {}; - const mqttEnabled = mqtt.enable || false; this.mqttConnected = false; //accessory services @@ -324,6 +319,7 @@ class XboxDevice extends EventEmitter { }) .on('prepareAccessory', async () => { //RESTFul server + const restFulEnabled = restFul.enable || false; if (restFulEnabled) { this.restFul = new RestFul({ port: restFul.port || 3000, @@ -332,8 +328,15 @@ class XboxDevice extends EventEmitter { this.restFul.on('connected', (message) => { this.restFulConnected = true; - this.emit('message', message); + this.emit('success', message); }) + .on('set', async (key, value) => { + try { + await this.setOverExternalIntegration('RESTFul', key, value); + } catch (error) { + this.emit('warn', `RESTFul set error: ${error}`); + }; + }) .on('debug', (debug) => { this.emit('debug', debug); }) @@ -343,6 +346,7 @@ class XboxDevice extends EventEmitter { } //mqtt client + const mqttEnabled = mqtt.enable || false; if (mqttEnabled) { this.mqtt = new Mqtt({ host: mqtt.host, @@ -356,70 +360,16 @@ class XboxDevice extends EventEmitter { this.mqtt.on('connected', (message) => { this.mqttConnected = true; - this.emit('message', message); + this.emit('success', message); }) .on('subscribed', (message) => { - this.emit('message', message); + this.emit('success', message); }) - .on('subscribedMessage', async (key, value) => { + .on('set', async (key, value) => { try { - switch (key) { - case 'Power': - switch (this.webApiPowerOnOff) { - case true: - switch (value) { - case true: //off - await this.xboxWebApi.send('Power', 'WakeUp'); - break; - case false: //on - await this.xboxWebApi.send('Power', 'TurnOff'); - break; - } - break; - case false: - switch (value) { - case true: //off - await this.xboxLocalApi.powerOff(); - break; - case false: //on - await this.xboxLocalApi.powerOn(); - break; - } - } - break; - case 'App': - const payload = [{ 'oneStoreProductId': value }]; - await this.xboxWebApi.send('Shell', 'ActivateApplicationWithOneStoreProductId', payload); - break; - case 'Volume': - switch (value) { - case 'up': //Up - await this.xboxWebApi.send('Volume', 'Up'); - break; - case 'down': //Down - await this.xboxWebApi.send('Volume', 'Down'); - break; - } - break; - case 'Mute': - switch (value) { - case true: //Mute - await this.xboxWebApi.send('Audio', 'Mute'); - break; - case false: //Unmute; - await this.xboxWebApi.send('Audio', 'Unmute'); - break; - } - break; - case 'RcControl': - await this.xboxWebApi.send('Shell', 'InjectKey', [{ 'keyType': value }]); - break; - default: - this.emit('message', `MQTT Received unknown key: ${key}, value: ${value}`); - break; - }; + await this.setOverExternalIntegration('MQTT', key, value); } catch (error) { - this.emit('warn', `MQTT send error: ${error}.`); + this.emit('warn', `MQTT set error: ${error}.`); }; }) .on('debug', (debug) => { @@ -535,6 +485,69 @@ class XboxDevice extends EventEmitter { }; } + async setOverExternalIntegration(integration, key, value) { + try { + let set = false + switch (key) { + case 'Power': + switch (this.webApiPowerOnOff) { + case true: + switch (value) { + case true: //off + set = await this.xboxWebApi.send('Power', 'WakeUp'); + break; + case false: //on + set = await this.xboxWebApi.send('Power', 'TurnOff'); + break; + } + break; + case false: + switch (value) { + case true: //off + set = await this.xboxLocalApi.powerOff(); + break; + case false: //on + set = await this.xboxLocalApi.powerOn(); + break; + } + } + break; + case 'App': + const payload = [{ 'oneStoreProductId': value }]; + set = await this.xboxWebApi.send('Shell', 'ActivateApplicationWithOneStoreProductId', payload); + break; + case 'Volume': + switch (value) { + case 'up': //Up + set = await this.xboxWebApi.send('Volume', 'Up'); + break; + case 'down': //Down + set = await this.xboxWebApi.send('Volume', 'Down'); + break; + } + break; + case 'Mute': + switch (value) { + case true: //Mute + set = await this.xboxWebApi.send('Audio', 'Mute'); + break; + case false: //Unmute; + set = await this.xboxWebApi.send('Audio', 'Unmute'); + break; + } + break; + case 'RcControl': + set = await this.xboxWebApi.send('Shell', 'InjectKey', [{ 'keyType': value }]); + break; + default: + this.emit('warn', `${integration}, received key: ${key}, value: ${value}`); + break; + }; + return set; + } catch (error) { + throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error}`); + }; + } //Prepare accessory async prepareAccessory() {