├── .gitignore ├── ExtPlaneJs.js ├── LICENSE ├── README.md ├── client.js ├── config.json ├── example.js ├── package.json └── test ├── mocha.opts └── unit └── ExtPlaneJs.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .history 3 | package-lock.json -------------------------------------------------------------------------------- /ExtPlaneJs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * 5 | * @name: ExtPlaneJs 6 | * @author: Wade Wildbore - Bluu Interactive 7 | * @description: ExtPlane TCP Connector for NodeJS 8 | * @see: https://github.com/vranki/ExtPlane - For more information 9 | */ 10 | var EventEmitter = require('events').EventEmitter; 11 | var client = require('./client'); 12 | var util = require('util'); 13 | var async = require('async'); 14 | var base64 = require('base-64'); 15 | 16 | function ExtPlaneJs(config){ 17 | 18 | // Ref to &this 19 | var self = this; 20 | 21 | // CTOR 22 | EventEmitter.call(this); 23 | 24 | // ExtPlaneJs Config 25 | this.config = config; 26 | 27 | // ExtPlane TCP Client 28 | this.client = client(this.config); 29 | 30 | // Couple the TCP clients on data event to ExtPlaneJs 31 | this.client.on('data', function(data){ 32 | self.emit('data', data); 33 | }); 34 | 35 | /** 36 | * 37 | * Loaded 38 | * 39 | */ 40 | this.on('loaded', function(){ 41 | if(this.config.debug) console.log('ExtPlane Ready!'); 42 | }); 43 | 44 | /** 45 | * 46 | * Data 47 | * 48 | * @param {string} data 49 | */ 50 | this.on('data', function(data){ 51 | 52 | // log incoming TCP stream 53 | //console.log(data.toString()); 54 | 55 | if(data.toString().includes('EXTPLANE')) 56 | { 57 | // loaded.. 58 | return this.emit('loaded'); 59 | } 60 | 61 | // emit parse 62 | return this.emit('parse', data.toString()); 63 | 64 | }); 65 | 66 | /** 67 | * 68 | * Parse 69 | * 70 | * Either emit single data-ref events, or broadcast all data-ref events on one handler 71 | * 72 | * @param {string} data 73 | */ 74 | this.on('parse', function(data){ 75 | 76 | // 77 | var commands = data.trim().split('\n'); 78 | 79 | async.each(commands, function(cmd, cb){ 80 | 81 | self.parseDataRef(cmd, cb); 82 | 83 | }, function(err){ 84 | if(err && this.config.debug) console.log(err); 85 | }); 86 | 87 | }); 88 | 89 | /** 90 | * 91 | * Parse an Individual Data Ref 92 | * 93 | * @param {string} data_ref - The data_ref response 94 | * @param {function} cb - The done callback 95 | */ 96 | this.parseDataRef = function(data_ref, cb){ 97 | 98 | var params = data_ref.split(' '); 99 | 100 | // if not data-ref output, break 101 | if(params[0][0] !== 'u') 102 | return false; 103 | 104 | // data-ref 105 | data_ref = params[1]; 106 | 107 | var type = params[0].substring(1); 108 | var value = this.parseValue(type, params[2]); 109 | 110 | // emit - data_ref, value or 'data-ref', data_ref, value 111 | !this.config.broadcast ? this.emit(data_ref, data_ref, value) : this.emit('data-ref', data_ref, value); 112 | 113 | // done 114 | return cb(null); 115 | }; 116 | 117 | /** 118 | * 119 | * Parse ExtPlanes TCP Text Based Response 120 | * 121 | * Override type value conversions from ExtPlanes response 122 | * 123 | * @param {string} - type 124 | * @param {string} - value 125 | */ 126 | this.parseValue = function(type, value){ 127 | 128 | switch(type) 129 | { 130 | // int 131 | case 'i': 132 | return parseInt(value); 133 | break; 134 | 135 | // float 136 | case 'f': 137 | return parseFloat(value); 138 | break; 139 | 140 | // int array 141 | case 'ia': 142 | return JSON.parse(value); 143 | break; 144 | 145 | // float array 146 | case 'fa': 147 | return JSON.parse(value); 148 | break; 149 | 150 | // base64 data 151 | case 'b': 152 | return base64.decode(value); 153 | break; 154 | 155 | default: 156 | return value; 157 | break; 158 | 159 | } 160 | 161 | }; 162 | 163 | }; 164 | 165 | util.inherits(ExtPlaneJs, EventEmitter); 166 | 167 | module.exports = ExtPlaneJs; 168 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is released under the MIT license: 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExtPlaneJs # 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![NPM version][npm-downloads]][npm-url] 5 | [![Dependency Status][daviddm-image]][daviddm-url] 6 | 7 | 8 | A JavaScript Client Library for NodeJs that uses the [ExtPlane plugin](https://github.com/vranki/ExtPlane). 9 | 10 | Inspired by [ExtPlaneInterface for Java](https://github.com/pau662/ExtPlaneInterface) and [ExtPlaneNet for C#](https://github.com/swemaniac/ExtPlaneNet) 11 | 12 | ## Prerequisites 13 | 1. You need X-Plane (demo works fine) with the [ExtPlane plugin](https://github.com/vranki/ExtPlane) installed. 14 | 2. [NodeJs](https://nodejs.org) >= 4.0.0 15 | 16 | 17 | ## Installation 18 | Install via NPM 19 | 20 | ``` 21 | $ npm install extplanejs 22 | ``` 23 | 24 | 25 | ### Connecting to X-Plane 26 | 27 | Make sure X-Plane is started and the plugin is installed and active. 28 | 29 | ### ExtPlaneJs Config 30 | Either modify the config.json or modify the config object passed into ExtPlaneJs 31 | 32 | ``` 33 | { 34 | "host": "127.0.0.1", 35 | "port": 51000, 36 | "broadcast": false, 37 | "debug": true 38 | } 39 | 40 | ``` 41 | 42 | ### Running the example connector 43 | 44 | 1. Open your terminal 45 | 2. cd to ExtPlaneJs 46 | 3. Install Node.js Dependencies `npm install` 47 | 4. Run `npm start` 48 | 49 | Watch your terminal as your X-Plane information is streamed on-screen 50 | 51 | ### Running the tests 52 | 53 | 1. Make sure X-Plane is running and in a current flight. Not on the QuickFlight screen. 54 | 2. Run `npm test` 55 | 56 | 57 | # Example 58 | 59 | ``` 60 | var ExtPlaneJs = require('extplanejs'); 61 | 62 | var ExtPlane = new ExtPlaneJs({ 63 | host: '127.0.0.1', 64 | port: 51000, 65 | broadcast: true 66 | }); 67 | 68 | ExtPlane.on('loaded', function(){ 69 | 70 | ExtPlane.client.interval(0.33); 71 | 72 | // Subscribe to the airspeed 73 | ExtPlane.client.subscribe('sim/cockpit2/gauges/indicators/airspeed_kts_pilot'); 74 | 75 | // Handle all data-ref changes 76 | ExtPlane.on('data-ref', function(data_ref, value){ 77 | console.log(data_ref+' - '+value); 78 | }); 79 | 80 | }); 81 | 82 | ``` 83 | 84 | # ExtPlaneJS API 85 | 86 | ### ExtPlaneJs(config) 87 | Instantiate ExtPlaneJs and try connect 88 | 89 | ### ExtPlaneJs.on(event) 90 | Assign an event handler to the specific event 91 | 92 | ### ExtPlaneJs.client.* 93 | Access the client API from ExtPlaneJs 94 | 95 | ### Events 96 | 97 | - `loaded` - No parameters 98 | - `data-ref` (data_ref, value) - Receive all data-ref events over one handler. Broadcast: true 99 | - `sim/cockpit2/gauges/indicators/airspeed_kts_pilot` (data_ref, value) - Receive individual data-ref events. Broadcast: false 100 | 101 | 102 | 103 | # Client API 104 | 105 | ### client.key(key_id) 106 | Press a key 107 | 108 | ### client.once(cmd) 109 | Execute a command 110 | 111 | ### client.begin(cmd) 112 | Begin a command 113 | 114 | ### client.end(cmd) 115 | End a command 116 | 117 | ### client.button(button_id) 118 | Press a button 119 | 120 | ### client.release(button_id) 121 | Release a button 122 | 123 | ### client.set(data_ref, value) 124 | Set a data refs value 125 | 126 | ### client.subscribe(data_ref, [accuracy]) 127 | Subscribe to a data ref 128 | 129 | ### client.unsubscribe(data_ref) 130 | Unsubscribe to a data ref 131 | 132 | ### client.interval(value) 133 | Override the data update interval 134 | 135 | ### client.disconnect() 136 | Send the disconnect command 137 | 138 | 139 | # More information ? 140 | 141 | See the example connector source in example.js for example subscriptions, methods available and event handlers for data refs 142 | 143 | 144 | # License 145 | MIT License - Copyright © Bluu Interactive 146 | 147 | [![Bluu](http://www.bluu.co.nz/async_js/img/wings.png)](http://www.bluu.co.nz) 148 | 149 | [npm-image]: https://img.shields.io/npm/v/extplanejs.svg 150 | [npm-url]: https://www.npmjs.com/package/extplanejs 151 | [daviddm-image]: http://img.shields.io/david/wadeos/extplanejs.svg 152 | [daviddm-url]: https://david-dm.org/wadeos/extplanejs 153 | [npm-downloads]: https://img.shields.io/npm/dt/extplanejs.svg 154 | -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var net = require('net'); 4 | 5 | /** 6 | * 7 | * @name: ExtPlaneJs 8 | * @author: Wade Wildbore - Bluu Interactive 9 | * @description: ExtPlane TCP Connector for NodeJS 10 | * @see: https://github.com/vranki/ExtPlane - For more information 11 | */ 12 | module.exports = function(config){ 13 | 14 | /** 15 | * Socket Connect 16 | */ 17 | var client = net.connect(config, function(){ 18 | if(config.debug) console.log('Connected to '+config.host+':'+config.port); 19 | }); 20 | 21 | /** 22 | * 23 | * On Socket End 24 | */ 25 | client.on('end', function(){ 26 | if(config.debug) console.log('disconnected from server'); 27 | }); 28 | 29 | /** 30 | * On Socket Error 31 | */ 32 | client.on('error', function(error) { 33 | if(config.debug) console.log('An error occurred!', error); 34 | }); 35 | 36 | /** 37 | * 38 | * Key Press 39 | * 40 | * @param {string} key_id - XPlane key ID 41 | */ 42 | client.key = function(key_id){ 43 | this.write('key '+key_id+'\r\n'); 44 | }; 45 | 46 | /** 47 | * 48 | * CMD Once 49 | * 50 | * @param {string} cmd - The command 51 | */ 52 | client.cmd = function(cmd){ 53 | this.write('cmd once '+cmd+'\r\n'); 54 | }; 55 | 56 | /** 57 | * 58 | * CMD Begin 59 | * 60 | * @param {string} cmd - The command 61 | */ 62 | client.begin = function(cmd){ 63 | this.write('cmd begin '+cmd+'\r\n'); 64 | }; 65 | 66 | /** 67 | * 68 | * CMD End 69 | * 70 | * @param {string} cmd - The command 71 | */ 72 | client.end = function(cmd){ 73 | this.write('cmd end '+cmd+'\r\n'); 74 | }; 75 | 76 | /** 77 | * 78 | * Button Press 79 | * 80 | * @param {string} button_id - XPlane button ID 81 | */ 82 | client.button = function(button_id){ 83 | this.write('but '+button_id+'\r\n'); 84 | }; 85 | 86 | /** 87 | * 88 | * Release Button 89 | * 90 | * @param {string} button_id - XPlane button ID 91 | */ 92 | client.release = function(button_id){ 93 | this.write('rel '+button_id+'\r\n'); 94 | }; 95 | 96 | /** 97 | * 98 | * Set a Data Ref to a specific value 99 | * 100 | * @param {string} data_ref - the XPlane Data Ref 101 | * @param {string} value - ExtPlane Values 102 | */ 103 | client.set = function(data_ref, value){ 104 | this.write('set '+data_ref+' '+value+'\r\n'); 105 | }; 106 | 107 | /** 108 | * 109 | * Subscribe to the XPlane Data Ref 110 | * 111 | * @param {string} data_ref - the XPlane Data Ref 112 | * @param {float} accuracy 113 | */ 114 | client.subscribe = function(data_ref, accuracy){ 115 | this.write('sub '+data_ref+(accuracy !== undefined ? ' '+accuracy : '')+'\r\n'); 116 | }; 117 | 118 | /** 119 | * 120 | * Unsubscribe from the XPlane Data Ref 121 | * 122 | * @param {string} data_ref - the XPlane Data Ref 123 | */ 124 | client.unsubscribe = function(data_ref){ 125 | this.write('unsub '+data_ref+'\r\n'); 126 | }; 127 | 128 | /** 129 | * 130 | * How often ExtPlane should update its data from X-Plane, in seconds. Use as high value as possible here for best performance. For example 0.16 would mean 60Hz, 0.33 = 30Hz, 0.1 = 10Hz etc.. Must be a positive float. Default is 0.33. 131 | * 132 | * @param {float} value 133 | */ 134 | client.interval = function(value){ 135 | this.write('extplane-set update_interval '+value+'\r\n'); 136 | } 137 | 138 | /** 139 | * 140 | * Disconnect from ExtPlane and then close the TCP socket 141 | */ 142 | client.disconnect = function(){ 143 | this.write('disconnect'+'\r\n'); 144 | this.end(); 145 | }; 146 | 147 | return client; 148 | 149 | }; 150 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "127.0.0.1", 3 | "port": 51000, 4 | "broadcast": false, 5 | "debug": false 6 | } 7 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @name: ExtPlaneJs 4 | * @author: Wade Wildbore - Bluu Interactive 5 | * @description: ExtPlane TCP Connector for NodeJS 6 | * @see: https://github.com/vranki/ExtPlane - For more information 7 | */ 8 | var ExtPlaneEmitter = require('./ExtPlaneJs'); 9 | var ExtPlane = new ExtPlaneEmitter(require('./config.json')); 10 | 11 | // Loaded CB 12 | ExtPlane.on('loaded', function(){ 13 | 14 | this.client.subscribe('sim/flightmodel/engine/ENGN_thro'); 15 | //this.client.unsubscribe('sim/flightmodel/engine/ENGN_thro'); 16 | 17 | 18 | // See: http://www.xsquawkbox.net/xpsdk/mediawiki/XPLMUtilities - For key and button ids 19 | 20 | // this.client.subscribe('sim/flightmodel/position/local_y', 100); 21 | // this.client.subscribe('sim/cockpit/electrical/night_vision_on'); 22 | // this.client.subscribe('sim/flightmodel/position/q'); 23 | this.client.subscribe('sim/cockpit2/engine/indicators/N1_percent'); 24 | 25 | 26 | //this.client.write('sub sim/flightmodel/engine/ENGN_thro'); 27 | //this.client.write('sub sim/cockpit/electrical/night_vision_on'); 28 | //this.client.write('sub sim/flightmodel/position/local_y 100'); 29 | //this.client.write('set sim/flightmodel/engine/ENGN_thro [1,0]'); 30 | //this.client.write('set sim/flightmodel/engine/ENGN_thro [0,0]'); 31 | 32 | //this.client.set('data_ref', 'value'); 33 | //this.client.key('key_id'); 34 | //this.client.button('button_id'); 35 | //this.client.release('button_id'); 36 | //this.client.disconnect(); 37 | 38 | }); 39 | 40 | // Listen on invidiual data-ref events - broadcast = false 41 | ExtPlane.on('sim/flightmodel/engine/ENGN_thro', function(data_ref, value){ 42 | console.log(data_ref+' - '+value); 43 | }); 44 | 45 | // Listen for all data-ref events - broadcast = true 46 | ExtPlane.on('data-ref', function(data_ref, value){ 47 | console.log(data_ref+' - '+value); 48 | }); 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extplanejs", 3 | "version": "0.1.4", 4 | "description": "A JavaScript Client Library for NodeJs that uses the ExtPlane-Plugin", 5 | "keywords": [ 6 | "xplane", 7 | "x-plane", 8 | "extplane", 9 | "extplanejs" 10 | ], 11 | "main": "ExtPlaneJs.js", 12 | "scripts": { 13 | "test": "mocha --reporter spec", 14 | "start": "node ./example.js" 15 | }, 16 | "author": "wadedos", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/wadeos/ExtPlaneJs" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/wadeos/ExtPlaneJs/issues" 23 | }, 24 | "engines": { 25 | "node": ">= 4.0.0" 26 | }, 27 | "dependencies": { 28 | "async": "^3.1.0", 29 | "base-64": "^0.1.0" 30 | }, 31 | "devDependencies": { 32 | "assert": "^1.3.0", 33 | "mocha": "^5.2.0" 34 | }, 35 | "license": "MIT" 36 | } 37 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --recursive 2 | --colors 3 | --timeout 90000 4 | -------------------------------------------------------------------------------- /test/unit/ExtPlaneJs.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var ExtPlaneEmitter, ExtPlane; 4 | 5 | describe('ExtPlaneJs', function(){ 6 | 7 | it('Connect to ExtPlane', function(done){ 8 | 9 | ExtPlaneEmitter = require('../../ExtPlaneJs'); 10 | ExtPlane = new ExtPlaneEmitter(require('../../config.json')); 11 | 12 | ExtPlane.on('loaded', function(){ 13 | 14 | this.client.subscribe('sim/flightmodel/engine/ENGN_thro'); 15 | 16 | assert.ok(ExtPlane.client); 17 | 18 | done(null); 19 | 20 | }); 21 | 22 | }); 23 | 24 | 25 | it('Subcribe to Engine Throttle Data Ref', function(done){ 26 | 27 | ExtPlane.on('sim/flightmodel/engine/ENGN_thro', function(data_ref, value){ 28 | 29 | assert.equal(data_ref, 'sim/flightmodel/engine/ENGN_thro'); 30 | assert.equal(typeof value, 'object'); 31 | done(null); 32 | 33 | }); 34 | 35 | }); 36 | 37 | 38 | it('Close Connection', function(done){ 39 | ExtPlane.client.destroy(); 40 | done(null); 41 | }); 42 | 43 | }); 44 | --------------------------------------------------------------------------------