UNPKG

node-hca

Version:

Node.js client for HCA

263 lines (199 loc) 8.19 kB
'use strict'; 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; })();