node-lox-ws-api
Version:
Web Socket Loxone™ API
233 lines (215 loc) • 8.66 kB
JavaScript
const WebSocketClient = require('websocket').client;
const MessageHeader = require('./Message/Header.js');
const MessageText = require('./Message/Text.js');
const MessageFile = require('./Message/File.js');
const MessageEventValue = require('./Message/EventValue.js');
const MessageEventText = require('./Message/EventText.js');
const MessageEventDaytimer = require('./Message/EventDaytimer.js');
const MessageEventWeather = require('./Message/EventWeather.js');
/*
Events:
close
close_failed
connect
connect_failed
connection_error
send
message_header
message_text
message_file
message_event_table_values
message_event_table_text
message_event_table_daytimer
message_event_table_weather
message_invalid
keepalive
*/
var Connection = function (host, keepalive) {
this._host = host;
this._keepalive = (keepalive === undefined ? 120000 : keepalive);
this._keepalive_interval = undefined;
this._keepalive_time = undefined;
this._ws_url = 'ws://'+host+'/ws/rfc6455';
this._ws = {
'client': new WebSocketClient(),
'connection': undefined,
};
this._connection_data = {};
this.register_client();
};
Connection.prototype.__proto__ = require('events').EventEmitter.prototype;
Connection.prototype.connect = function() {
this._ws.client.connect(this._ws_url, 'remotecontrol');
};
Connection.prototype.close = function() {
if (this._ws.connection !== undefined){
clearInterval(this._keepalive_interval);
this._ws.connection.close();
}else{
this.emit('close_failed');
}
};
Connection.prototype.register_client = function() {
var that = this;
this._ws.client.on('connectFailed', function(error) {
console.log("WebSocket error: ", this._ws.connection.closeDescription, this._ws.connection.closeReasonCode);
let reason = that._ws.connection.closeDescription + ' (' + that._ws.connection.closeReasonCode + ')';
that.emit('connect_failed', error, reason);
}.bind(this));
this._ws.client.on('connect', function(connection) {
that.register_connection(connection);
that.emit('connect');
});
};
Connection.prototype.register_connection = function(connection) {
var that = this;
this._ws.connection = connection;
this._connection_data = {
'message_state': 'header',
'status_update_subscription': false,
};
this._keepalive_interval = setInterval( function() {
that._keepalive_time = new Date();
if (that._ws.connection !== undefined){
that._ws.connection.sendUTF('keepalive');
} else {
clearInterval(that._keepalive_interval);
}
}, this._keepalive);
/*
this._ws.connection.on('close', function() {
that._ws.connection = undefined;
clearInterval(that._keepalive_interval);
if (that._connection_data.last_header && that._connection_data.last_header.identifier == '5'){
that.emit('close', true);
}else{
that.emit('close', false);
}
});
this._ws.connection.on('error', function(error) {
clearInterval(that._keepalive_interval);
that.emit('connection_error', error);
});
*/
this._ws.connection.on('close', function(_reason, _desc) {
console.log("Websocket Connection closed: ", _reason, _desc);
let reason = _desc + ' (' + _reason + ')';
that._ws.connection = undefined;
clearInterval(that._keepalive_interval);
if (that._connection_data.last_header && that._connection_data.last_header.identifier == '5'){
that.emit('close', true, reason);
}else{
that.emit('close', false, reason);
}
});
this._ws.connection.on('error', function(error) {
console.log("WebSocket error: ", this._ws.connection.closeDescription, this._ws.connection.closeReasonCode);
clearInterval(that._keepalive_interval);
let reason = this._ws.connection.closeDescription + ' (' + this._ws.connection.closeReasonCode + ')';
that.emit('connection_error', error, reason);
}.bind(this));
this._ws.connection.on('message', function(message) {
that.handle_message(message);
});
};
Connection.prototype.send = function(message) {
if (this._ws.connection !== undefined){
this._connection_data.last_request = message;
this._ws.connection.sendUTF(message);
this.emit('send', message);
}
};
Connection.prototype.handle_message = function(message) {
var that = this;
this.emit('handle_message', message);
switch(this._connection_data.message_state) {
case 'header':
if (message.type !== 'binary'){
this.emit('message_invalid', message);
break;
}
this._connection_data.last_header = new MessageHeader(message.binaryData);
this._connection_data.message_state = this._connection_data.last_header.next_state();
if (this._connection_data.last_header.identifier == 6){
this.emit('keepalive', new Date().getTime() - this._keepalive_time.getTime());
}
this.emit('message_header', this._connection_data.last_header);
break;
case 'text':
if (message.type === 'binary'){
this.emit('message_invalid', message);
break;
}
this.emit('message_text', new MessageText(message.utf8Data));
this._connection_data.message_state = 'header';
break;
case 'binary_file':
this.emit('message_file', new MessageFile(message, that._connection_data.last_request));
this._connection_data.message_state = 'header';
break;
case 'etable_values':
if (message.type !== 'binary'){
this.emit('message_invalid', message);
break;
}
var event_table = [];
var buffer_index = 0;
while (buffer_index < message.binaryData.length) {
var event_item = new MessageEventValue(message.binaryData, buffer_index);
event_table.push(event_item);
buffer_index+=event_item.data_length;
}
this.emit('message_event_table_values', event_table);
this._connection_data.message_state = 'header';
break;
case 'etable_text':
if (message.type !== 'binary'){
this.emit('message_invalid', message);
break;
}
var event_table = [];
var buffer_index = 0;
while (buffer_index < message.binaryData.length) {
var event_item = new MessageEventText(message.binaryData, buffer_index);
event_table.push(event_item);
buffer_index+=event_item.data_length;
}
this.emit('message_event_table_text', event_table);
this._connection_data.message_state = 'header';
break;
case 'etable_daytimer':
if (message.type !== 'binary'){
this.emit('message_invalid', message);
break;
}
var event_table = [];
var buffer_index = 0;
while (buffer_index < message.binaryData.length) {
var event_item = new MessageEventDaytimer(message.binaryData, buffer_index);
event_table.push(event_item);
buffer_index+=event_item.data_length;
}
this.emit('message_event_table_daytimer', event_table);
this._connection_data.message_state = 'header';
break;
case 'etable_weather':
if (message.type !== 'binary'){
this.emit('message_invalid', message);
break;
}
var event_table = [];
var buffer_index = 0;
while (buffer_index < message.binaryData.length) {
var event_item = new MessageEventWeather(message.binaryData, buffer_index);
event_table.push(event_item);
buffer_index+=event_item.data_length;
}
this.emit('message_event_table_weather', event_table);
this._connection_data.message_state = 'header';
break;
default:
this.emit('unknown_message_state', message);
this._connection_data.message_state = 'header';
}
};
module.exports = Connection;