UNPKG

magichome-platform

Version:

discover, control, and receive status for magichome devices

146 lines (145 loc) 6.42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AnimationManager = void 0; const animationLoop_1 = require("./animationLoop"); class AnimationManager { constructor(controllers, animationBlueprints, STEP_INTERVAL_MS = 20) { this.startTime = 0; this.ticksActive = false; this.lightMap = new Map(); controllers.forEach((controller) => this.lightMap.set(controller.id, { id: controller.id, controller })); this.STEP_INTERVAL_MS = STEP_INTERVAL_MS; this.animationLoops = []; this.animationBlueprints = animationBlueprints.sort((a, b) => a.priority - b.priority); this.animationBlueprints.forEach(animationBlueprint => this.generateAnimationLoopFromBlueprint(animationBlueprint)); this.numActiveAnimations = 0; } static getInstance(controllers, animationBlueprints, STEP_INTERVAL_MS = 20) { if (this.instance === null && controllers && animationBlueprints) { this.instance = new AnimationManager(controllers, animationBlueprints, STEP_INTERVAL_MS); } return this.instance; } isAnimationLoopActiveByName(animationName) { const animation = this.animationLoops.find(animation => animation.name === animationName); if (!animation) return false; return animation.isActive; } activateAnimationLoopByName(animationName) { if (Array.isArray(animationName)) { animationName.forEach(animation => this.activateAnimationLoopByName(animation)); return; } const animation = this.animationLoops.find(animation => animation.name === animationName); if (!animation) return; animation.isActive = true; this.numActiveAnimations++; if (!this.ticksActive) { this.startTicks(); } } deactivateAnimationLoopByName(animationName) { if (Array.isArray(animationName)) { animationName.forEach(animation => this.deactivateAnimationLoopByName(animation)); return; } const animation = this.animationLoops.find(animation => animation.name === animationName); if (!animation) return; animation.isActive = false; this.numActiveAnimations--; if (this.numActiveAnimations <= 0) { this.stopTicks(); } } generateAnimationLoopFromBlueprint(animationBlueprint) { const assignedLightsIds = Array.from(this.lightMap.values()) .filter(light => light.controller.getAnimationList().includes(animationBlueprint.name)) .map(light => light.id); const animation = new animationLoop_1.AnimationLoop(animationBlueprint, assignedLightsIds, this.STEP_INTERVAL_MS); this.animationLoops.push(animation); return animation; } addLightToAnimationLoop(controller, animationBlueprint) { if (Array.isArray(animationBlueprint)) { animationBlueprint.forEach(animation => this.addLightToAnimationLoop(controller, animation)); return; } let animation = this.animationLoops.find(animation => animation.name === animationBlueprint.name); if (!animation) { animation = this.generateAnimationLoopFromBlueprint(animationBlueprint); } animation.addLightToAnimationLoop(controller.id); controller.appendAnimationList(animationBlueprint.name); this.lightMap.set(controller.id, { id: controller.id, controller }); } removeLightFromAnimationLoop(controller, animationBlueprint) { if (Array.isArray(animationBlueprint)) { animationBlueprint.forEach(animation => this.removeLightFromAnimationLoop(controller, animation)); return; } const animation = this.animationLoops.find(animation => animation.name === animationBlueprint.name); if (!animation) return; animation.removeLightFromAnimationLoop(controller.id); controller.removeAnimationFromList(animationBlueprint.name); this.lightMap.delete(controller.id); } startTicks() { this.ticksActive = true; this.startTime = this.getCurrentTime(); this.tickAnimations(); } stopTicks() { this.ticksActive = false; } tickAnimations() { if (!this.ticksActive) return; this.animationLoops.forEach(animation => { if (!animation.isActive) return; animation.tickAllActiveLoops(); }); this.tickLights(); const currentTime = this.getCurrentTime(); const elapsedTime = currentTime - this.startTime; this.startTime = currentTime; const timeoutDuration = elapsedTime >= this.STEP_INTERVAL_MS ? this.STEP_INTERVAL_MS : this.STEP_INTERVAL_MS - elapsedTime; setTimeout(this.tickAnimations.bind(this), timeoutDuration); } tickLights() { this.lightMap.forEach((light) => { if (light.controller.manuallyControlled) return; const nextAnimation = this.getNextAnimation(light); const currentStep = nextAnimation ? nextAnimation.getAnimationStep(light.id) : this.getDefaultStep(); light.controller.setLEDColorAnimation(this.roundStepValues(currentStep)); }); } getCurrentTime() { const hrtime = process.hrtime(); return hrtime[0] * 1000 + hrtime[1] / 1e6; } getNextAnimation(light) { return this.animationLoops.find(animation => light.controller.hasAnimation(animation.name) && this.isValidAnimationStep(animation, light.id)); } isValidAnimationStep(animation, lightId) { const step = animation.getAnimationStep(lightId); return step !== undefined && Math.max(...Object.values(step)) > 0; } getDefaultStep() { return { red: 0, green: 0, blue: 0, warmWhite: 0, coldWhite: 0 }; } roundStepValues(step) { const roundedStep = { ...step }; for (const key in roundedStep) { roundedStep[key] = Math.ceil(roundedStep[key]); } return roundedStep; } } exports.AnimationManager = AnimationManager; AnimationManager.instance = null;