node-hca
Version:
Node.js client for HCA
263 lines (199 loc) • 8.19 kB
JavaScript
;
var util = require('util'),
EventEmitter = require('events').EventEmitter,
message = require('../Messaging/message'),
itemType = require('./itemType'),
Item = require('./Item');
(function () {
var self,
connection,
designIndex = 0,
itemSubTypeIndex = 0,
items = [];
var Design = function (conn) {
if (!conn) {
throw Error('A Connection instance is required.');
return;
}
EventEmitter.call(this); // Superclass constructor.
self = this;
connection = conn;
this.topics = {
designReceived: 'Design:Received',
designUpdated: 'Design:Updated',
designUpdatedId: 'Design:Updated:{id}'
};
};
util.inherits(Design, EventEmitter);
Design.prototype.init = function () {
// Reset initial values in case of a reconnect.
designIndex = 0;
itemSubTypeIndex = 0;
items.length = 0;
// Remove event listeners
connection.removeListener(connection.topics.messageReceived, onMessageReceived);
var params = ['HCAApp', 'GetDesign', '0'];
connection.on('GetDesign', onChunk);
connection.send(params);
};
Design.prototype.getItemCount = function () {
return items.length;
};
Design.prototype.getItem = function (itemId) {
var item = items.filter(function (obj) {
return obj.id == parseInt(itemId);
});
return item[0];
};
Design.prototype.getItems = function () {
return items;
};
var onChunk = function (response) {
if (response.code == -1) { // end of design.
connection.removeListener('GetDesign', onChunk);
connection.on(connection.topics.messageReceived, onMessageReceived);
// [Un]comment the following lines to toggle retrieval of item sub type.
// NOTE: getItemSubType is an expensive operation.
// self.emit(self.topics.designReceived, null); // !!!
getItemSubType(); // !!!
return;
}
decodeData(response.data);
};
var onMessageReceived = function (response) {
if (response.command !== 'Update')
return;
updateItem(response);
};
var decodeData = function (data) {
var items2 = message.decode(data[0]);
for (var i = 0; i < items2.length; i++) {
var itemParams = message.decode(items2[i]);
var existingItem = self.getItem(itemParams[0]);
// Do not allow duplicate entries.
if (existingItem)
continue;
addItem(itemParams);
}
designIndex++;
// Get next chunk.
var params = ['HCAApp', 'GetDesign', designIndex.toString()];
connection.send(params);
};
var addItem = function (itemParams) {
var item = new Item();
item.id = parseInt(itemParams[0]);
item.name = itemParams[1];
item.iconName = itemParams[2];
item.popupName = itemParams[3];
item.state = parseInt(itemParams[4]);
item.suspend = parseInt(itemParams[5]);
item.type = parseInt(itemParams[6]);
item.rockerCount = parseInt(itemParams[7]);
item.buttonCount = parseInt(itemParams[8]);
var currentIndex = 9;
// *** NOTE: DO NOT change order of the following item properties! ***
// *** Parameter count and position/index may vary based on parameter data above. ***
if (item.rockerCount > 0) {
for (var rocker = 0; rocker < item.rockerCount; rocker++) {
item.rockerInfo.push(itemParams[currentIndex++]);
};
}
if (item.buttonCount > 0) {
for (var button = 0; button < item.buttonCount; button++) {
item.buttonInfo.push(itemParams[currentIndex++]);
};
}
if (item.buttonCount > 0) {
for (var buttonState = 0; buttonState < item.buttonCount; buttonState++) {
item.buttonState.push(parseInt(itemParams[currentIndex++]));
};
}
item.shortTapAction = parseInt(itemParams[currentIndex++]);
item.longTapAction = parseInt(itemParams[currentIndex++]);
item.folderName = itemParams[currentIndex++];
item.currentIconName = itemParams[currentIndex++];
item.currentIconLabel = itemParams[currentIndex++];
item.currentIconRepresentation = parseInt(itemParams[currentIndex++]);
item.wattage = parseInt(itemParams[currentIndex++]);
item.noShow = parseInt(itemParams[currentIndex++]);
item.voiceAssistantName = itemParams[currentIndex++];
// v14
item.errorState = parseInt(itemParams[currentIndex++]);
item.uid = parseInt(itemParams[currentIndex++]);
item.manufacturer = itemParams[currentIndex++];
item.model = itemParams[currentIndex++];
item.tag = itemParams[currentIndex++];
item.isDimmable = parseInt(itemParams[currentIndex++]) > 0;
item.kind = parseInt(itemParams[currentIndex++]);
// Custom propeties
item.voiceAssistantEnabled = Boolean(item.voiceAssistantName && item.voiceAssistantName.length > 0);
items.push(item);
return item;
};
var updateItem = function (response) {
var data = response.data,
id = parseInt(data[0]),
item = self.getItem(id);
if (!(item instanceof Item))
return;
item.state = parseInt(data[1]);
item.suspend = parseInt(data[2]);
item.buttonCount = parseInt(data[3]);
const hasButtons = item.buttonCount > 0;
var currentIndex = 4;
// *** NOTE: DO NOT change order of the following item properties! ***
// *** Parameter count and position/index may vary based on parameter data above. ***
if (hasButtons) {
for (var buttonState = 0; buttonState < item.buttonCount; buttonState++) {
item.buttonState.push(parseInt(data[currentIndex++]));
};
}
item.currentIconName = data[currentIndex++];
item.currentIconLabel = data[currentIndex++];
item.currentIconRepresentation = parseInt(data[currentIndex++]);
currentIndex++; // item.timestamp = parseInt(data[currentIndex++]);
item.wattage = parseInt(data[currentIndex++]);
// v14
item.errorState = parseInt(data[currentIndex++]);
self.emit(self.topics.designUpdated);
self.emit(self.topics.designUpdatedId.replace('{id}', id), item);
};
// ** HACK ***
// HCA doesn't make it easy to asynchronously get this data,
// as it doesn't return the ID of the item requested in the return data.
// So we'll need to issue synchronous (blocking) requests.
var getItemSubType = function () {
var item = items[itemSubTypeIndex];
if (!item)
return;
var command =
item.type === itemType.device ? "Device.Kind" :
item.type === itemType.controller ? "Controller.Kind" :
null;
if (command) {
var params = ['HCAObject', command, item.id];
connection.once(command, function (hcaResponse) {
var currentItem = items[itemSubTypeIndex];
if (currentItem && (hcaResponse.code === 0)) {
currentItem.subType = parseInt(hcaResponse.data[0]);
}
itemSubTypeIndex++;
if (itemSubTypeIndex <= (self.getItemCount() - 1)) {
getItemSubType();
} else {
self.emit(self.topics.designReceived, null);
}
});
connection.send(params);
} else {
itemSubTypeIndex++;
if (itemSubTypeIndex <= (self.getItemCount() - 1)) {
getItemSubType();
} else {
self.emit(self.topics.designReceived, null);
}
}
};
module.exports = Design;
})();