thing-it-device-enocean-ip
Version:
[thing-it-node] Device Plugin for EnOcean IP products.
323 lines (271 loc) • 9.63 kB
JavaScript
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);
};
}