Closed mStirner closed 1 month ago
class.scene.js
const Joi = require("joi");
const mongodb = require("mongodb");
const { EventEmitter } = require("events");
const { setTimeout } = require("timers/promises");
const Makro = require("./class.makro.js");
const Trigger = require("./class.trigger.js");
const Item = require("../../system/component/class.item.js");
module.exports = class Scene extends Item {
constructor(obj) {
super(obj);
// removed for #356
//Object.assign(this, obj);
//this._id = String(obj._id);
this.makros = obj.makros.map((makro) => {
return new Makro(makro);
});
this.triggers = obj.triggers.map((data) => {
let trigger = new Trigger(data);
trigger.signal.on("fire", () => {
this.trigger();
});
return trigger;
});
Object.defineProperty(this, "running", {
value: false,
enumerable: false,
configurable: false,
writable: true
});
Object.defineProperty(this, "aborted", {
value: false,
enumerable: false,
configurable: false,
writable: true
});
Object.defineProperty(this, "index", {
value: 0,
enumerable: false,
configurable: false,
writable: true
});
Object.defineProperty(this, "finished", {
value: false,
enumerable: false,
configurable: false,
writable: true
});
Object.defineProperty(this, "_ac", {
value: null,
enumerable: false,
configurable: false,
writable: true
});
this.events = new EventEmitter();
}
static schema() {
return Joi.object({
_id: Joi.string().pattern(/^[0-9a-fA-F]{24}$/).default(() => {
return String(new mongodb.ObjectId());
}),
name: Joi.string().required(),
makros: Joi.array().items(Makro.schema()).default([]),
triggers: Joi.array().items(Trigger.schema()).default([]),
visible: Joi.boolean().default(true),
icon: Joi.string().allow(null).default(null)
});
}
static validate(data) {
return Scene.schema().validate(data);
}
trigger() {
this.events.emit("started", this);
let ac = new AbortController();
this._ac = ac;
// wrap this in a custom method
// that returns the state?
// `getStates()` or so...
this.running = true;
this.aborted = false;
this.finished = false;
this.index = 0;
let init = this.makros.filter(({
// enabled is per default "true"
// when a marko should be disabled
// this has explicit to be set to false
enabled = true
}) => {
// execute only enabled makros
return enabled;
}).map((makro) => {
// bind scope to method
return makro.execute.bind(makro);
}).reduce((acc, cur, i) => {
return (result) => {
return acc(result, this._ac.signal).then(async (r) => {
if (this.aborted) {
return Promise.reject("Aborted!");
} else {
// NOTE: Intended to be a workaround for #329 & #312
// But the general idea of this is not bad
await setTimeout(Number(process.env.SCENES_MAKRO_DELAY));
// represents the current index of makro
// e.g. timer takes 90min to finish,
// index = timer makro in `makros` array
this.index = i;
this.events.emit("makro", cur);
return cur(r, this._ac.signal);
}
}).catch((err) => {
console.log("Catched", i, err);
return Promise.reject(err);
});
};
});
return init(true, this._ac).then((result) => {
console.log("Makro stack done", result);
this.finished = true;
this.events.emit("finished", this);
}).catch((err) => {
console.log("Makro stack aborted", err);
this.finished = false;
this.events.emit("aborted", this);
}).finally(() => {
console.log("Finaly");
this.running = false;
});
}
abort() {
console.log("Aborted called");
this._ac.abort();
this.running = false;
this.aborted = true;
this.finished = false;
}
};
Would be nice when you can react upon scenes executions steps.
Also would be nice to have timestamps in
/state
routes.