Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "events" & "timestamps" for scenes #496

Open
mStirner opened this issue Sep 14, 2024 · 1 comment
Open

Add "events" & "timestamps" for scenes #496

mStirner opened this issue Sep 14, 2024 · 1 comment

Comments

@mStirner
Copy link
Member

Would be nice when you can react upon scenes executions steps.

  • started
  • stopped
  • aborted
  • marko step

Also would be nice to have timestamps in /state routes.

  • started
  • finished
  • aborted
@mStirner
Copy link
Member Author

Draft:

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;

    }

};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant