UNPKG

homebridge-elero-stick

Version:

Elero funkstick for homebridge: https://github.com/ma-ku/homebridge-elero-stick

205 lines 9.12 kB
"use strict"; const elero_shutter_accessory_1 = require("./elero-shutter-accessory"); const elero_stick_1 = require("./usb/elero-stick"); const perf_hooks_1 = require("perf_hooks"); const PluginName = "homebridge-elero-stick"; const PLATFORM_NAME = "EleroStick"; const DEFAULT_UPDATEINTERVAL = 5000; const DEFAULT_MOVINGUPDATEINTERVAL = 1500; let hap; class EleroStickPlatform { constructor(log, config, api) { this.eleroAccessories = new Map(); this.channelIds = []; this.defaultUpdateInterval = 5000; this.movingUpdateInterval = 1500; this.updateInterval = 5000; this.hasCallback = false; this.log = log; this.api = api; // Dummy-Callback this.accessoriesCallback = this.dummy; this.config = this.checkConfig(config); this.name = this.config['name'] || ""; this.port = this.config['port']; // We will request an update from the stick every 5 seconds this.defaultUpdateInterval = this.config["updateInterval"] || 5000; this.movingUpdateInterval = this.config["movingUpdateInterval"] || 1500; this.updateInterval = this.defaultUpdateInterval; this.platformConfig = { defaultUpdateInterval: this.defaultUpdateInterval, movingUpdateInterval: this.movingUpdateInterval }; this.lastStatusTimestamp = perf_hooks_1.performance.now(); // This will store the actual platformAccessory instances, indexed by their uuid // this.accessories = {}; var stick = this; this.serialConnection = new elero_stick_1.EleroStick(this.port, (config.debugSerial ? this.log : undefined)); this.serialConnection.sendInterval = this.config.sendInterval || 250; if (api) { this.api = api; this.api.on('didFinishLaunching', () => { this.log.info("Requesting learned channels from stick"); this.serialConnection.on('connect', (channels) => { this.registerChannels(channels); }); this.serialConnection.on('status', (channel, state) => { stick.processState(channel, state); }); this.serialConnection.easyCheck(); this.checkChannelStates(); }); } } /** * This method is called to retrieve all accessories exposed by the platform. * The Platform can delay the response my invoking the callback at a later time, * it will delay the bridge startup though, so keep it to a minimum. * The set of exposed accessories CANNOT change over the lifetime of the plugin! */ accessories(callback) { this.accessoriesCallback = callback; this.hasCallback = true; this.publishAccessories(); } dummy(foundAccessories) { } publishAccessories() { if ((this.hasCallback) && (this.eleroAccessories.size > 0)) { this.log.debug('>> Request for accessories'); this.log.debug('>> # of Elero accessories is ' + this.eleroAccessories.size); let accessories = []; this.eleroAccessories.forEach(value => { accessories.push(value); }); this.accessoriesCallback(accessories); } } checkConfig(config) { this.log.debug("User-Config:\n" + JSON.stringify(config)); let result = { name: config['name'] || 'EleroStick', port: config['port'] || '/dev/ttyUSB0', debugSerial: config['debugSerial'], updateInterval: config['updateInterval'] || DEFAULT_UPDATEINTERVAL, movingUpdateInterval: config['movingUpdateInterval'] || DEFAULT_MOVINGUPDATEINTERVAL, sendInterval: config['sendInterval'] || 250, motors: {} }; config.motors = config.motors || []; config.motors.forEach((userConfig) => { this.log.debug("Motor-User-Config:\n" + JSON.stringify(userConfig)); let motorConfig = { type: userConfig['type'] || 'shutter', channel: userConfig['channel'], name: userConfig['name'] || "Channel " + userConfig['channel'], displayName: userConfig['displayName'] || "Channel " + userConfig['channel'], duration: userConfig['duration'] || 10000, disabled: userConfig['disabled'] || false, reverse: userConfig['reverse'] || false, stopButton: userConfig['stopButton'] || false, ventilationPositionButton: userConfig['ventilationPositionButton'] || false, intermediatePositionButton: userConfig['intermediatePositionButton'] || false, }; result.motors[motorConfig.channel] = motorConfig; }); this.log.debug("Result-Config:\n" + JSON.stringify(result)); return result; } /** * This is a callback from an easyInfo call to the EleroStick. Since other than described in the documentation, * the stick is not requesting the state for each channel sent in the command, we are requesting the state * individually for each channel and thus receive multiple responses. * * @param {int} channel * @param {int} state */ processState(channel, state) { var newTimestamp = perf_hooks_1.performance.now(); var newInterval = this.updateInterval; this.eleroAccessories.forEach(accessory => { if (accessory.channel == channel) { accessory.processState(state, newTimestamp); } }); // Find minimum monitoring time newInterval = this.defaultUpdateInterval; this.eleroAccessories.forEach(accessory => { newInterval = Math.min(newInterval, accessory.reportingInterval); }); this.updateInterval = newInterval; this.lastStatusTimestamp = newTimestamp; } channelUUID(channel) { return hap.uuid.generate(this.port + ":" + channel); } checkChannelStates() { var stick = this; if (isNaN(this.updateInterval)) { this.updateInterval = this.defaultUpdateInterval; } this.log.debug('checkChannelStates (%s ms)', this.updateInterval); stick.serialConnection.easyInfo(stick.channelIds); setTimeout(function () { stick.checkChannelStates(); }, Math.max(500, this.updateInterval)); } /** * Receive the learned channels from the linked Elero Stick * and creates the corresponding accessory for it, if not already * defined. Furthermore will all registered accessories checked * if they are still attached to a learned channel and eventually * will get set to unreachable. * * @param {int[]} channels */ registerChannels(channels) { this.log.info("Stick reported channels: ", channels); this.channelIds = channels; var stick = this; // For each channel we will generate the corresponding EleroChannel that is // then linked with a matching implementation (for now only Shutters) channels.forEach((channel) => { // Do we already have an accessory for this? var uuid = stick.channelUUID(channel); // Check if we have a corresponding accessory configuration // for that channel that we can use to complement the accessory // configuration var channelConfig = Object.assign({ type: 'shutter', channel: channel, name: 'Channel ' + channel, disabled: false }, stick.config.motors[channel]); // If this accessory is new, we will create a new instance var accessory = undefined; if (!(channelConfig.disabled || false)) { switch (channelConfig.type) { case 'shades': case 'shutter': case 'lights': case 'heating': accessory = new elero_shutter_accessory_1.EleroShutterAccessory(this.api.hap, this.log, this.platformConfig, channelConfig, uuid, this.serialConnection, channel); break; default: stick.log.error("Cannot create accessory of type '%s' for channel: %s", channelConfig.type, channel); } if (accessory) { this.eleroAccessories.set(accessory.uuid, accessory); } else { stick.log.error("Cannot add accessory for channel: %s", channel); } } else { stick.log.info("Channel: %s is disabled, no accessory will get created", channel); } }); this.publishAccessories(); } } module.exports = (api) => { hap = api.hap; api.registerPlatform(PLATFORM_NAME, EleroStickPlatform); }; //# sourceMappingURL=elero-stick-platform.js.map