UNPKG

thing-it-device-enocean-ip

Version:

[thing-it-node] Device Plugin for EnOcean IP products.

323 lines (271 loc) 9.63 kB
module.exports = { metadata: { family: 'enocean-ip', plugin: 'gateway', label: 'EnOcean IP Gateway', manufacturer: 'e.g. Digital Concepts', discoverable: true, tangible: false, additionalSoftware: [], actorTypes: [], sensorTypes: [], services: [], state: [{ label: "Gateway status", id: "gatewayStatus", type: { id: "string" } }], configuration: [{ label: "Host", id: "host", type: { id: "string" }, defaultValue: "0.0.0.0" }, { label: "Port", id: "port", type: { id: "integer" }, defaultValue: 8080 }, { label: "Admin Account", id: "adminAccount", type: { id: "string" } }, { label: "Admin Password", id: "adminPassword", type: { id: "password" } }] }, create: function () { return new Gateway(); }, discovery: function () { return new GatewayDiscovery(); } }; var q = require('q'); /** * * @constructor */ function GatewayDiscovery() { /** * * @param options */ GatewayDiscovery.prototype.start = function () { if (this.isSimulated()) { this.timer = setInterval(function () { }.bind(this), 20000); } else { this.logLevel = 'debug'; } }; /** * * @param options */ GatewayDiscovery.prototype.stop = function () { if (this.timer) { clearInterval(this.timer); } }; } /** * * @constructor */ function Gateway() { /** * */ Gateway.prototype.start = function () { var deferred = q.defer(); if (this.isSimulated()) { this.logDebug("Starting Gateway in simulated mode."); deferred.resolve(); } else { this.adapter = new Adapter().initialize(this.configuration.host, this.configuration.port, this, this.configuration.adminAccount, this.configuration.adminPassword); deferred.resolve(); } return deferred.promise; }; /** * */ Gateway.prototype.stop = function () { var deferred = q.defer(); if (this.isSimulated()) { this.logDebug("Stopping Gateway in simulated mode."); } else { this.adapter = null; } deferred.resolve(); return deferred.promise; }; /** * */ Gateway.prototype.getState = function () { return {}; }; /** * */ Gateway.prototype.setState = function () { }; } function endsWith(string, suffix) { return string.indexOf(suffix, this.length - suffix.length) !== -1; } function Adapter() { Adapter.prototype.initialize = function (host, port, logger, account, password) { const CONNECT_RETRY_TIME = 30000; this.logger = logger; this.request = require('request-json'); this.client = this.request.createClient('http://' + account + ':' + password + '@' + host + ':' + port + '/'); this.listeners = []; let message = ''; // this.logger.state.gatewayStatus = 'Not connected'; // this.logger.publishStateChange({gatewayStatus: this.logger.state.gatewayStatus}); // // this.logger.operationalState = {status: 'PENDING', message: 'Starting...'}; // this.logger.publishOperationalStateChange(); this.connectInterval = setInterval(() => { if (!this.connected && !this.connectInProgress) { //Remove old client this.client = null; this.client = this.request.createClient('http://' + account + ':' + password + '@' + host + ':' + port + '/'); // Initialize Stream this.logger.logDebug("Trying to connect to " + host + ":" + port); this.connectInProgress = true; this.client.get('devices/stream', (error, response, body) => { if (error) { this.connected = false; this.connectInProgress = false; this.logger.logDebug("Connection to " + host + ":" + port + " not successful: " + error.code); this.logger.state.gatewayStatus = error.code; this.logger.publishStateChange({gatewayStatus: this.logger.state.gatewayStatus}); this.logger.operationalState = {status: 'ERROR', message: error.code}; this.logger.publishOperationalStateChange(); } if (response) { this.connected = false; this.connectInProgress = false; if (response.statusMessage !== this.logger.state.gatewayStatus) { this.logger.logDebug('Response: ', response.statusMessage); this.logger.state.gatewayStatus = response.statusMessage; this.logger.publishStateChange({gatewayStatus: this.logger.state.gatewayStatus}); this.logger.operationalState = {status: 'ERROR', message: response.statusMessage}; this.logger.publishOperationalStateChange(); } } if (body) { //TODO HANDLE STATES // this.logger.logDebug('Authorization body: ', body); } }).on('connect', (data) => { this.connected = true; this.connectInProgress = false; this.logger.state.gatewayStatus = "connected"; this.logger.publishStateChange({gatewayStatus: this.logger.state.gatewayStatus}); }).on('data', (chunk) => { if (!this.connected){ this.logger.state.gatewayStatus = "connected"; this.logger.publishStateChange({gatewayStatus: this.logger.state.gatewayStatus}); this.logger.operationalState = {status: 'OK', message: "connected"}; this.logger.publishOperationalStateChange(); } this.connected = true; this.connectInProgress = false; message += chunk.toString('utf8'); if (message.endsWith('\r\n\r\n')) { message = JSON.parse(message); if (message.telegram) { //console.log('##################################### Telegram recieved. ', message.telegram); for (var n in this.listeners) { this.listeners[n](message.telegram); } } message = ''; } }).on('end', () => { this.connected = false; }).on('close', (data) => { this.connected = false; this.logger.logDebug("Connection closed by Enocean Gateway. Trying to reconnect to: " + host); this.logger.state.gatewayStatus = 'Not connected'; this.logger.publishStateChange({gatewayStatus: this.logger.state.gatewayStatus}); this.logger.operationalState = {status: 'PENDING', message: 'Connection closed'}; this.logger.publishOperationalStateChange(); }).on('error', () => { this.connected = false; }); } }, CONNECT_RETRY_TIME); return this; }; /** * */ Adapter.prototype.getDeviceState = function (deviceId) { var deferred = q.defer(); this.client.get('devices/' + deviceId + '/state', function (err, res, body) { if (err) { deferred.reject(err); } else { deferred.resolve(body); } }); return deferred.promise; }; /** * */ Adapter.prototype.setDeviceState = function (deviceId, functions) { var deferred = q.defer(); this.client.put('devices/' + deviceId + '/state', { state: { functions: functions } }, function (err, res, body) { if (err) { deferred.reject(err); } else { //TODO usefull result debug here deferred.resolve(body); } }); return deferred.promise; }; /** * */ Adapter.prototype.invokeDeviceService = function (deviceId, serviceId) { var deferred = q.defer(); this.client.post('devices/' + deviceId + '/services/' + serviceId, function (err, res, body) { if (err) { deferred.reject(err); } else { //TODO usefull result debug here deferred.resolve(body); } }); return deferred.promise; }; /** * */ Adapter.prototype.registerListener = function (callback) { this.listeners.push(callback); }; }