magichome-platform
Version:
discover, control, and receive status for magichome devices
146 lines (145 loc) • 6.42 kB
JavaScript
"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;