diff --git a/config.schema.json b/config.schema.json index a27069f..89d5211 100644 --- a/config.schema.json +++ b/config.schema.json @@ -3,6 +3,7 @@ "pluginType": "platform", "singular": true, "fixArrays": true, + "strictValidation": true, "customUi": true, "headerDisplay": "This plugin works with Xbox Game Consoles. Devices are exposed to HomeKit as separate accessories and each needs to be manually paired.", "footerDisplay": "For documentation please see [GitHub repository](https://github.com/grzegorz914/homebridge-xbox-tv).", diff --git a/index.js b/index.js index 572109c..96ab855 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ const path = require('path'); const fs = require('fs'); const XboxDevice = require('./src/xboxdevice.js'); +const ImpulseGenerator = require('./src/impulsegenerator.js'); const CONSTANTS = require('./src/constants.json'); class XboxPlatform { @@ -25,17 +26,17 @@ class XboxPlatform { api.on('didFinishLaunching', async () => { for (const device of config.devices) { const deviceName = device.name ?? false; - const deviceHost = device.host ?? false; + const host = device.host ?? false; const xboxLiveId = device.xboxLiveId ?? false; - if (!deviceName || !deviceHost || !xboxLiveId) { - log.warn(`Name: ${deviceName ? 'OK' : deviceName}, Host: ${deviceHost ? 'OK' : deviceHost}, Xbox Live ID: ${xboxLiveId ? 'OK' : xboxLiveId}, wrong or missing.`); + if (!deviceName || !host || !xboxLiveId) { + log.warn(`Name: ${deviceName ? 'OK' : deviceName}, Host: ${host ? 'OK' : host}, Xbox Live ID: ${xboxLiveId ? 'OK' : xboxLiveId}, wrong or missing.`); return; } //debug config - const debugMode = device.enableDebugMode; - const debug = debugMode ? log.info(`Device: ${deviceHost} ${deviceName}, did finish launching.`) : false; + const enableDebugMode = device.enableDebugMode; + const debug = enableDebugMode ? log.info(`Device: ${host} ${deviceName}, Did finish launching.`) : false; const config = { ...device, xboxLiveId: 'removed', @@ -46,10 +47,10 @@ class XboxPlatform { passwd: 'removed' } }; - const debug1 = debugMode ? log.info(`Device: ${deviceHost} ${deviceName}, Config: ${JSON.stringify(config, null, 2)}`) : false; + const debug1 = enableDebugMode ? log.info(`Device: ${host} ${deviceName}, Config: ${JSON.stringify(config, null, 2)}`) : false; //check files exists, if not then create it - const postFix = deviceHost.split('.').join(''); + const postFix = host.split('.').join(''); const authTokenFile = `${prefDir}/authToken_${postFix}`; const devInfoFile = `${prefDir}/devInfo_${postFix}`; const inputsFile = `${prefDir}/inputs_${postFix}`; @@ -72,7 +73,7 @@ class XboxPlatform { } }); } catch (error) { - log.error(`Device: ${deviceHost} ${deviceName}, prepare files error: ${error}`); + log.error(`Device: ${host} ${deviceName}, Prepare files error: ${error}`); return; } @@ -81,30 +82,44 @@ class XboxPlatform { const xboxDevice = new XboxDevice(api, device, authTokenFile, devInfoFile, inputsFile, inputsNamesFile, inputsTargetVisibilityFile); xboxDevice.on('publishAccessory', (accessory) => { api.publishExternalAccessories(CONSTANTS.PluginName, [accessory]); - log.success(`Device: ${deviceHost} ${deviceName}, published as external accessory.`); + log.success(`Device: ${host} ${deviceName}, Published as external accessory.`); }) .on('devInfo', (devInfo) => { log.info(devInfo); }) .on('success', (message) => { - log.success(`Device: ${deviceHost} ${deviceName}, ${message}`); + log.success(`Device: ${host} ${deviceName}, ${message}`); }) .on('message', (message) => { - log.info(`Device: ${deviceHost} ${deviceName}, ${message}`); + log.info(`Device: ${host} ${deviceName}, ${message}`); }) .on('debug', (debug) => { - log.info(`Device: ${deviceHost} ${deviceName}, debug: ${debug}`); + log.info(`Device: ${host} ${deviceName}, debug: ${debug}`); }) .on('warn', (warn) => { - log.warn(`warn: ${deviceHost} ${deviceName}, ${warn}`); + log.warn(`warn: ${host} ${deviceName}, ${warn}`); }) .on('error', (error) => { - log.error(`Device: ${deviceHost} ${deviceName}, ${error}`); + log.error(`Device: ${host} ${deviceName}, ${error}`); }); - await xboxDevice.start(); + //create impulse generator + const impulseGenerator = new ImpulseGenerator(); + impulseGenerator.on('start', async () => { + try { + await xboxDevice.start(); + impulseGenerator.stop(); + } catch (error) { + const logError = disableLogConnectError ? false : log.error(`Device: ${host} ${deviceName}, ${error}, trying again.`); + }; + }).on('state', (state) => { + const debug = enableDebugMode ? state ? log.info(`Device: ${host} ${deviceName}, Start impulse generator started.`) : log.info(`Device: ${host} ${deviceName}, Start impulse generator stopped.`) : false; + }); + + //start impulse generator + impulseGenerator.start([{ name: 'start', sampling: 45000 }]); } catch (error) { - log.error(`Device: ${deviceHost} ${deviceName}, did finish launching error: ${error}`); + log.error(`Device: ${host} ${deviceName}, Did finish launching error: ${error}`); } } }); diff --git a/package.json b/package.json index ce3f976..c6fc257 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "Xbox TV", "name": "homebridge-xbox-tv", - "version": "3.1.17", + "version": "3.1.18-beta.0", "description": "Homebridge plugin to control Xbox game consoles.", "license": "MIT", "author": "grzegorz914", diff --git a/src/impulsegenerator.js b/src/impulsegenerator.js index 8b710f0..b88e340 100644 --- a/src/impulsegenerator.js +++ b/src/impulsegenerator.js @@ -14,6 +14,8 @@ class ImpulseGenerator extends EventEmitter { this.timers = []; for (const timer of timers) { + this.emit(timer.name); + const newTimer = setInterval(() => { this.emit(timer.name); }, timer.sampling); diff --git a/src/webApi/xboxwebapi.js b/src/webApi/xboxwebapi.js index 2660f05..0bb9d01 100644 --- a/src/webApi/xboxwebapi.js +++ b/src/webApi/xboxwebapi.js @@ -46,7 +46,9 @@ class XBOXWEBAPI extends EventEmitter { } catch (error) { this.emit('error', error); }; - }).on('state', (state) => { }); + }).on('state', (state) => { + const emitState = state ? this.emit('success', `Web Api monitoring started.`) : this.emit('warn', `Web Api monitoring stopped.`); + }); } async checkAuthorization() { diff --git a/src/xboxdevice.js b/src/xboxdevice.js index b945de5..6bc7afa 100644 --- a/src/xboxdevice.js +++ b/src/xboxdevice.js @@ -157,6 +157,9 @@ class XboxDevice extends EventEmitter { }; this.power = power; }) + .on('success', (message) => { + this.emit('success', message); + }) .on('message', (message) => { this.emit('message', message); }) @@ -214,100 +217,6 @@ class XboxDevice extends EventEmitter { .setCharacteristic(Characteristic.FirmwareRevision, firmwareRevision); }; }) - .on('stateChanged', (power, volume, mute, mediaState, titleId, reference) => { - const input = this.inputsConfigured.find(input => input.reference === reference || input.titleId === titleId) ?? false; - const inputIdentifier = input ? input.identifier : this.inputIdentifier; - - //update characteristics - if (this.televisionService) { - this.televisionService - .updateCharacteristic(Characteristic.Active, power); - }; - - if (this.televisionService) { - this.televisionService - .updateCharacteristic(Characteristic.ActiveIdentifier, inputIdentifier); - }; - - if (this.speakerService) { - this.speakerService - .updateCharacteristic(Characteristic.Volume, volume) - .updateCharacteristic(Characteristic.Mute, mute); - if (this.volumeService) { - this.volumeService - .updateCharacteristic(Characteristic.Brightness, volume) - .updateCharacteristic(Characteristic.On, !mute); - }; - if (this.volumeServiceFan) { - this.volumeServiceFan - .updateCharacteristic(Characteristic.RotationSpeed, volume) - .updateCharacteristic(Characteristic.On, !mute); - }; - }; - - if (this.sensorPowerService) { - this.sensorPowerService - .updateCharacteristic(Characteristic.ContactSensorState, power); - } - - if (this.sensorInputService && reference !== this.reference) { - for (let i = 0; i < 2; i++) { - const state = power ? [true, false][i] : false; - this.sensorInputService - .updateCharacteristic(Characteristic.ContactSensorState, state); - this.sensorInputState = state; - } - } - - if (this.sensorScreenSaverService) { - const state = power ? (reference === 'Xbox.IdleScreen_8wekyb3d8bbwe!Xbox.IdleScreen.Application') : false; - this.sensorScreenSaverService - .updateCharacteristic(Characteristic.ContactSensorState, state); - this.sensorScreenSaverState = state; - } - - if (this.sensorsInputsServices) { - for (let i = 0; i < this.sensorsInputsConfiguredCount; i++) { - const sensorInput = this.sensorsInputsConfigured[i]; - const state = power ? sensorInput.reference === reference : false; - sensorInput.state = state; - const characteristicType = sensorInput.characteristicType; - this.sensorsInputsServices[i] - .updateCharacteristic(characteristicType, state); - } - } - - //buttons - if (this.buttonsServices) { - for (let i = 0; i < this.buttonsConfiguredCount; i++) { - const button = this.buttonsConfigured[i]; - const state = this.power ? button.reference === reference : false; - button.state = state; - this.buttonsServices[i] - .updateCharacteristic(Characteristic.On, state); - } - } - - this.inputIdentifier = inputIdentifier; - this.power = power; - this.reference = reference; - this.volume = volume; - this.mute = mute; - this.mediaState = mediaState; - - if (!this.disableLogInfo) { - const name = input ? input.name : reference; - const productId = input ? input.oneStoreProductId : reference; - this.emit('message', `Power: ${power ? 'ON' : 'OFF'}`); - this.emit('message', `Input Name: ${name}`); - this.emit('message', `Reference: ${reference}`); - this.emit('message', `Title Id: ${titleId}`); - this.emit('message', `Product Id: ${productId}`); - this.emit('message', `Volume: ${volume}%`); - this.emit('message', `Mute: ${mute ? 'ON' : 'OFF'}`); - this.emit('message', `Media State: ${['PLAY', 'PAUSE', 'STOPPED', 'LOADING', 'INTERRUPTED'][mediaState]}`); - }; - }) .on('externalIntegrations', () => { try { //RESTFul server @@ -411,13 +320,106 @@ class XboxDevice extends EventEmitter { //sort inputs list const sortInputsDisplayOrder = this.televisionService ? await this.displayOrder() : false; - this.startPrepareAccessory = false; } catch (error) { throw new Error(` Prepare accessory error: ${error.message || error}`); }; }) + .on('stateChanged', (power, volume, mute, mediaState, titleId, reference) => { + const input = this.inputsConfigured.find(input => input.reference === reference || input.titleId === titleId) ?? false; + const inputIdentifier = input ? input.identifier : this.inputIdentifier; + + //update characteristics + if (this.televisionService) { + this.televisionService + .updateCharacteristic(Characteristic.Active, power); + }; + + if (this.televisionService) { + this.televisionService + .updateCharacteristic(Characteristic.ActiveIdentifier, inputIdentifier); + }; + + if (this.speakerService) { + this.speakerService + .updateCharacteristic(Characteristic.Volume, volume) + .updateCharacteristic(Characteristic.Mute, mute); + if (this.volumeService) { + this.volumeService + .updateCharacteristic(Characteristic.Brightness, volume) + .updateCharacteristic(Characteristic.On, !mute); + }; + if (this.volumeServiceFan) { + this.volumeServiceFan + .updateCharacteristic(Characteristic.RotationSpeed, volume) + .updateCharacteristic(Characteristic.On, !mute); + }; + }; + + if (this.sensorPowerService) { + this.sensorPowerService + .updateCharacteristic(Characteristic.ContactSensorState, power); + } + + if (this.sensorInputService && reference !== this.reference) { + for (let i = 0; i < 2; i++) { + const state = power ? [true, false][i] : false; + this.sensorInputService + .updateCharacteristic(Characteristic.ContactSensorState, state); + this.sensorInputState = state; + } + } + + if (this.sensorScreenSaverService) { + const state = power ? (reference === 'Xbox.IdleScreen_8wekyb3d8bbwe!Xbox.IdleScreen.Application') : false; + this.sensorScreenSaverService + .updateCharacteristic(Characteristic.ContactSensorState, state); + this.sensorScreenSaverState = state; + } + + if (this.sensorsInputsServices) { + for (let i = 0; i < this.sensorsInputsConfiguredCount; i++) { + const sensorInput = this.sensorsInputsConfigured[i]; + const state = power ? sensorInput.reference === reference : false; + sensorInput.state = state; + const characteristicType = sensorInput.characteristicType; + this.sensorsInputsServices[i] + .updateCharacteristic(characteristicType, state); + } + } + + //buttons + if (this.buttonsServices) { + for (let i = 0; i < this.buttonsConfiguredCount; i++) { + const button = this.buttonsConfigured[i]; + const state = this.power ? button.reference === reference : false; + button.state = state; + this.buttonsServices[i] + .updateCharacteristic(Characteristic.On, state); + } + } + + this.inputIdentifier = inputIdentifier; + this.power = power; + this.reference = reference; + this.volume = volume; + this.mute = mute; + this.mediaState = mediaState; + + if (!this.disableLogInfo) { + const name = input ? input.name : reference; + const productId = input ? input.oneStoreProductId : reference; + this.emit('message', `Power: ${power ? 'ON' : 'OFF'}`); + this.emit('message', `Input Name: ${name}`); + this.emit('message', `Reference: ${reference}`); + this.emit('message', `Title Id: ${titleId}`); + this.emit('message', `Product Id: ${productId}`); + this.emit('message', `Volume: ${volume}%`); + this.emit('message', `Mute: ${mute ? 'ON' : 'OFF'}`); + this.emit('message', `Media State: ${['PLAY', 'PAUSE', 'STOPPED', 'LOADING', 'INTERRUPTED'][mediaState]}`); + }; + }) .on('success', (message) => { this.emit('success', message); })