美文网首页
MagicMirror² 模块开发文档

MagicMirror² 模块开发文档

作者: 上帝不会掷骰子_81 | 来源:发表于2019-10-23 23:54 被阅读0次

    MagicMirror² 模块开发文档

    原文档的拙劣翻译

    本文档介绍了开发MagicMirror²模块的方法
    目录:

    • 模块结构
      • 文件
    • 核心模块文件:modulename.js
      • 可用的模块实例属性
      • 可继承的模块方法
      • 模块实例方法
    • Node Helper:node_helper.js
      • 可用的模块实例属性
      • 可继承的模块方法
      • 模块实例方法
    • MagicMirror 辅助方法
      • 模块选择
    • MagicMirror 日志记录

    建议

    随着魔镜项目取得了巨大的关注,可用的第三方模块也越来越多。对于新用户和开发者来说,为了去了解一个模块究竟做了什么、长什么样以及它的依赖,要花费很多时间在众多的项目仓库中浏览。不幸的是,这些信息仍难以获得,除非你先安装好它。因此我们强烈建议你在README文件中包含以下信息。

    • 一张你开发的模块的高质量截图
    • 用简短的一句话描述它做了什么
    • 它调用了哪些API,包括web链接
    • API和请求是否需要密钥以及是否有用户限制

    当然,这也能帮助你更好地认识和改善你的工作。

    模块结构

    所有的模块都在modules文件夹中加载。默认模块都放在modules/default文件夹下。你的模块也应该放在modules下。注意在moduls文件下的文件会被git忽略,这样的话在升级MagicMirror² 的时候就不会丢失它们。

    一个模块可以单独放在一个文件夹下,多个模块也可以分组放在一个子文件夹下。注意模块的名字需要是唯一的,即使将名称相似的模块放在不同的文件夹中,也不能同时加载它们。

    文件

    • modulename/modulename.js -这是模块的核心文件
    • modulename/node_helper.js -这是一个可选的辅助文件,它将被node脚本加载。node helper会和模块的脚本通过socket系统进行通信。
    • modulename/public 任何在这个目录下的文件都可以通过浏览器访问/modulename/filename.ext.
    • modulename/anyfileorfolder这里放置会被核心脚本用到的一些文件。例如modulename/css/modulename.css就是用来放置样式表的一个很好路径

    模块核心文件:modulename.js

    模块将在这个脚本中被定义。要使用模块的话,这个脚本不可或缺。最简单的形式,核心文件必须包括:

    Module.register("modulename",{});
    

    当然,上面这个模块不会做任何事情,所以还是看一下最简单的一个模块:helloworld:

    //helloworld.js:
    
    Module.register("helloworld",{
        // Default module config.
        defaults: {
            text: "Hello World!"
        },
    
        // Override dom generator.
        getDom: function() {
            var wrapper = document.createElement("div");
            wrapper.innerHTML = this.config.text;
            return wrapper;
        }
    });
    

    你能够看到,Module.register()方法接受两个参数:模块的名称和一个描述模块属性的对象。

    可用的模块实例属性

    在模块被初始化之后,模块实例有一些可用的属性:

    实例属性 类型 描述
    this.name String 模块名称
    this.identifier String 模块实例的唯一标识
    this.hidden Boolean 这个代表模块现在是否被隐藏
    this.config Boolean ??? 用户在config.js中设置的配置。如果这个属性没有被用户配置覆盖的话,这个配置也包括模块的默认配置
    this.data Object data对象包括一个额外的元数据(见下)

    this.data包括以下元数据:

    • data.classes - modules的dom wrapper的class
    • data.file - 模块核心文件的名称
    • data.path - 模块所在的目录路径
    • data.header - 加到模块????
    • data.postion - 模块实例将显示的位置

    default:{}

    任何在default对象中定义的对象,都会被合并到用户的config.js中的模块配置中。这里是你设置模块默认配置的最佳位置。所有的模块配置属性都可以通过this.config.propertyName获取,这会在之后提到。

    requireVersion:

    版本:2.1.0
    定义MagicMirror框架的最低版本。如果它被设置了,系统会把这个版本号和用户的版本进行比较,如果用户版本较低,就不会运行这个模块。确保在Node helper中也设置了这个值。

    注意:因为这个检查设置是在2.1.0版本中介绍的,这个操作不会在更老的版本中执行。
    Example:

    requiresVersion: "2.1.0",
    

    可继承的模块方法

    init()

    这个放在在模块取得实例时被调用。在多数情况下,你不需要去继承这个方法。

    loaded(callback)

    版本:2.1.1
    此方法在模块被加载后调用。在配置中之后的模块现在还没有被加载。callback中的函数必须在模块被加载完后被调用。在多数情况下你不需要继承此方法。
    Example:

    loaded: function(callback) {
        this.finishLoading();
        Log.log(this.name + ' is loaded!');
        callback();
    }
    

    start()

    这个方法在所有模块都被加载好并且系统准备系统时加载。注意这个时候dom对象还没有被创建。start方法可用来定义模块其他属性。
    Example:

    start: function() {
        this.mySpecialProperty = "So much wow!";
        Log.log(this.name + ' is started!');
    }
    

    getScript()

    返回:Array
    getScript方法用来加载任何额外的脚本。这个方法需要返回一个字符串数组。如果你想放回一个module目录下的文件路径,使用this.file('file.name.js')方法。在任何情况下,加载器只会加载一次文件。它也会检查文件在vendor文件夹下是否存在。
    Example

    getScripts: function() {
        return [
            'script.js', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
            'moment.js', // this file is available in the vendor folder, so it doesn't need to be available in the module folder.
            this.file('anotherfile.js'), // this file will be loaded straight from the module folder.
            'https://code.jquery.com/jquery-2.2.3.min.js',  // this file will be loaded from the jquery servers.
        ]
    }
    

    注意:如果某个文件不能被加载,魔镜的启动会被阻塞。所以,建议不要使用外部的url。

    getStyles()

    返回:Array
    getStyle方法用来加载额外的样式文件。这个方法需要返回一个字符串数组。如果你想放回一个module目录下的文件路径,使用this.file('file.name.css')方法。在任何情况下,加载器只会加载一次文件。它也会检查文件在vendor文件夹下是否存在。

    Example

    getStyles: function() {
        return [
            'script.css', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
            'font-awesome.css', // this file is available in the vendor folder, so it doesn't need to be available in the module folder.
            this.file('anotherfile.css'), // this file will be loaded straight from the module folder.
            'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css',  // this file will be loaded from the bootstrapcdn servers.
        ]
    }
    

    注意:如果某个文件不能被加载,魔镜的启动会被阻塞。所以,建议不要使用外部的url。

    getTranslations()

    返回:Dictionary
    getTranslations方法用来请求需要加载的翻译文件。这个方法需要返回用国家代号做键名的的字典。
    如果模块没有特定的语言文件,这个函数会被忽略或者返回false
    Example:

    getTranslations: function() {
        return {
                en: "translations/en.json",
                de: "translations/de.json"
        }
    }
    

    getDom()

    返回:Dom对象
    当MagicMirror需要在屏幕上更新信息时(在它启动时,或者因为你的模块使用this.updateDom()请求刷新时),系统会调用getDom方法。这个方法需要返回一个Dom对象。
    Example:

    getDom: function() {
        var wrapper = document.createElement("div");
        wrapper.innerHTML = 'Hello world!';
        return wrapper;
    }
    

    getHeader()

    返回:String
    当MagicMirror需要在屏幕上更新信息时(在它启动时,或者因为你的模块使用this.updateDom()请求刷新时),系统会调用getHeader方法去获取模块的头部信息。这个方法要求返回一个字符串。如果这个方法没有被继承,这个方法将返回用户配置的头部信息。
    如果你想使用用户配置的头部信息,参考this.data.header
    注意:如果你没有配置默认的头部信息,没有头部信息会被显示,并且这个方法不会被调用。
    Example:

    getHeader: function() {
        return this.data.header + ' Foo Bar';
    }
    

    notificationReceived(notification, payload, sender)

    MagicMirror核心有能力给模块发送通知,甚至是一个模块可以给另一个模块发送通知。当这个函数被调用后,它有三个参数:

    • notification - String - 通知的标识
    • payload- AnyType - 通知的负载
    • sender - Module - 通知的发送方,如果这个参数是undefined,那么通知的发送方是核心系统。
      Example:
     notificationReceived: function(notification, payload, sender) {
        if (sender) {
            Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
        } else {
            Log.log(this.name + " received a system notification: " + notification);
        }
    }
    

    注意:系统会在启动时发送三个通知。这个通知可能会派上用场。

    • ALL_MODULES_STARTED - 所有模块被系统,你现在可以给其他模块发送消息。
    • DOM_OBJECTS_CREATED - 所有的Dom对象被创建,系统现在可以进行视觉上的改变。
    • MODULE_DOM_CREATED - 这个模块的Dom已经被全部加载,你现在可以访问你的Dom对象。

    socketNotificationRceived: function(notification, payload)

    当使用node_helper时,node_helper能够给你的模块发送信息,当这个模块被调用时,它有两个参数:

    • notification - String - 通知的标识
    • payload - AnyType - 通知的负载

    注意1:当node helper发送一个通知时,所有这个模块类型的模块都会收到相同的通知。
    注意2:当模块第一次使用sendSocketNotification发送信息时,socket链接就被建立。

    Example:

    socketNotificationReceived: function(notification, payload) {
        Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
    },
    

    suspend()

    当一个模块被隐藏时(使用module.hide()),suspend()方法会被调用。通过继承这个方法,你可以进行一些像中止更新计时器的操作。

    resume()

    当一个模块被显示时(使用module.show()),resume()方法会被调用。通过继承这个方法,你可以进行一些像重启更新计时器的操作。

    模块实例方法

    每个模块实例都有一些对构建模块有帮助的便捷方法。

    this.file(filename)

    filename String - 你创建路径的文件的名称
    返回 String
    如果你想在你的模块文件夹下创建文件的路径,使用file()方法。//TODO(Is method comes in handy when configuring the getScripts and getStyles methods.)

    this.update(speed)

    speed Number - 可选,运动速度 毫秒

    当你要更新模块时,调用updateDom(speed)方法。它向MagicMirror核心请求更新它的dom对象。如果你定义的speed,那么内容更新将是延时运动的,不过要内容真的有变化。
    举例,时钟模块每一秒会调用这个方法:

    ...
    start: function() {
        var self = this;
        setInterval(function() {
            self.updateDom(); // no speed defined, so it updates instantly.
        }, 1000); //perform every 1000 milliseconds.
    }
    ...
    

    this.sendNotification(notification, payload)

    notification String - 通知标识
    payload AnyType - 可选,通知负载

    如果你想给其他模块发送信息,使用sendNotification(notification, payload)。所有的模块都会通过notificationReceived方法收到信息。//TODO In that case, the sender is automatically set to the instance calling the sendNotification method.

    Example:

    this.sendNotification('MYMODULE_READY_FOR_ACTION', {foo:bar});
    

    this.sendSocketNotification(notification, payload)

    notification String - 通知标识
    payload AnyType - 可选,通知负载

    如果你想给node_helper发送通知,使用endSocketNotification(notification, payload)。只有这个模块的node_helper会收到socket通知。
    Example:

    this.sendSocketNotification('SET_CONFIG', this.config);
    

    this.hide(speed, callback, options)

    • speed Number - 可选(设置callback或option时需要),隐藏的动效速度,毫秒。
    • callback -可选,在运动结束后的回调
    • options - 可选,隐藏操作的额外选项(见下)。(2.1.0)

    你可以使用hide(speed,callback)方法来隐藏一个模块。你可以在对象实例自身上使用this.hide(),当然你也可以使用anOtherModule.hide()来隐藏其他模块。

    可配置的选择项:

    • lockString - String 当设置了lock字符,在没有正确的lockstring时模块无法被显示。这样的话可以阻止一个模块被显示。把你的模块标识(this.identifier)作为lockString的方式是好的.(见下)
      注意1:如果隐藏动效被取消,这可能因为在隐藏动画结束前调用了显示动画。回调函数不会被取消。
      注意2:如果隐藏动效被hijacked(其他方法在这个模块上调用了隐藏方法),回调函数不会被调用。
      注意3:如果dom没有被创建,hide方法不会被调用。等待DOM_OBJECTS_CREATED通知。

    this.shwo(speed,callback,options)

    • speed Number - 可选(设置callback或option时需要),显示的动效速度,毫秒。
    • callback -可选,在运动结束后的回调
    • options - 可选,显示操作的额外选项(见下)。(2.1.0)

    你可以使用show(speed,callback)方法来显示一个模块。你可以在对象实例自身上使用this.show(),当然你也可以使用anOtherModule.show()来显示其他模块。

    可选配置:

    • lockString - String 当设置了lock字符,在没有正确的lockstring时模块无法被显示。这样的话可以阻止一个模块被显示。把你的模块标识(this.identifier)作为lockString的方式是好的.(见下)
    • force - Boolean 当force标志为true时,locking机制会被覆盖。谨慎使用这个选项。

    注意1:如果显示动画被取消,这可能因为在显示动画结束前隐藏动画被调用。回调函数不会被调用。
    注意2:如果显示动效被hijacked(其他方法在这个模块上调用了显示方法),回调函数不会被调用。
    注意3:如果dom没有被创建,show方法不会被调用。等待DOM_OBJECTS_CREATED通知。

    Visibility locking

    版本:2.1.0

    Visibility locking 帮助系统阻止不想发生的隐藏/显示操作。以下面的情况为例进行介绍:
    模块B使模块A隐藏:

    moduleA.hide(0, {lockString: "module_b_identifier"});
    

    模块A现在是隐藏的,并且有一个这个的锁:

    moduleA.lockStrings == ["module_b_identifier"]
    moduleA.hidden == true
    

    模块C使模块A隐藏:

    moduleA.hide(0, {lockString: "module_c_identifier"});
    

    模块A现在是隐藏的,并且有一个这个的锁:

    moduleA.lockStrings == ["module_b_identifier", "module_c_identifier"]
    moduleA.hidden == true
    

    模块B使模块A显示:

    moduleA.show(0, {lockString: "module_b_identifier"});
    

    lockString 会从模块A的锁中去除,但因为还有一个有效的锁,所以这个模块还是会保持隐藏的状态:

    moduleA.lockStrings == ["module_c_identifier"]
    moduleA.hidden == true
    

    模块C使模块A显示:

    moduleA.show(0, {lockString: "module_c_identifier"});
    

    lockString会从模块A的锁中去除,这会时得锁为空,所以这个模块会被显示:

    moduleA.lockStrings == []
    moduleA.hidden == false
    

    注意: locking机制可以被force标志重写覆盖。

    moduleA.show(0, {force: true});
    

    这会重置lockString,模块会被展示

    moduleA.lockStrings == []
    moduleA.hidden == false
    

    谨慎使用force,查看show获取更多信息。

    this.translate(identifier)

    identifier String - 需要翻译的字符串的标识

    魔镜项目为国际化提供了便利的封装。基于用户的语言配置,你可以为你的模块提供不同语言的服务。

    如果没有找到语言包,会执行如下的回退操作:

      1. 模块语言包中的用户首选语言
      1. 核心语言包中的用户搜选语言
      1. 模块语言包中第一个被定义的语言
      1. 核心语言包中第一个被定义的语言
      1. 语言的标识

    当给你的模块添加翻译时,最好查看一下在核心翻译文件中是否存在合适的翻译。

    Example:

    this.translate("INFO") //Will return a translated string for the identifier INFO
    

    Example json file:

    {
      "INFO": "Really important information!"
    }
    

    注意:虽然JSON文件不支持注释,但MagicMirror允许在解析JSON文件之前删除注释。翻译文件中的注释可以帮助其他译者。

    this.translate(identifier,variables)

    identifier String - 需要被翻译的字符串的标识。
    variables Object - 在翻译中要用到的变量
    这种改进的,向后兼容的翻译方法和普通的翻译方法一样并且遵循以上的规则。我们推荐使用这个的格式。它允许翻译者改变翻译句子的单词顺序。
    Example:

    var timeUntilEnd = moment(event.endDate, "x").fromNow(true);
    this.translate("RUNNING", { "timeUntilEnd": timeUntilEnd) }); // Will return a translated string for the identifier RUNNING, replacing `{timeUntilEnd}` with the contents of the variable `timeUntilEnd` in the order that translator intended.
    

    Example English .json file::

    {
        "RUNNING": "Ends in {timeUntilEnd}",
    }
    

    Example Finnish .json file:

    {
        "RUNNING": "Päättyy {timeUntilEnd} päästä",
    }
    

    注意:variable对象有一个特殊的情况:fallback。它用于支持翻译文件中没有变量的旧翻译。如果你正在升级一个旧模块,并且这个模块的翻译不支持语序,那么我们推荐使用fallback
    Example

    var timeUntilEnd = moment(event.endDate, "x").fromNow(true);
    this.translate("RUNNING", {
        "fallback": this.translate("RUNNING") + " {timeUntilEnd}"
        "timeUntilEnd": timeUntilEnd
    )}); // Will return a translated string for the identifier RUNNING, replacing `{timeUntilEnd}` with the contents of the variable `timeUntilEnd` in the order that translator intended. (has a fallback)
    

    Example Swedish .json file that does not have the variable in it:

    {
        "RUNNING": "Slutar",
    }
    

    在这种情况下,translate方法不会在翻译中找到任何变量,而是寻找fallback变量,并在可能的情况下使用它来创建翻译。

    The Node Helper: node_helper.js

    node helper是一个node.js脚本,它可以通过一些后端任务来支持你的模块。对于每一个模块类型,只有一个node helper会被创建。例如:如果你的魔镜有两个日历模块,那么只有一个node helper实例。

    node_helper.js的最简单的形式必须包括:

    var NodeHelper = require("node_helper");
    module.exports = NodeHelper.create({});
    

    当然,上面的node helper不会做任何有用的事情。有了上面的信息,你应该可以让它更加复杂了。

    可用的模块实例属性

    this.name

    String
    模块的名称

    this.path

    String
    模块的路径

    this.expressApp

    Express App Instance
    这是一个express实例的链接,允许你定义额外的路由。
    Example

    start: function() {
        this.expressApp.get('/foobar', function (req, res) {
            res.send('GET request to /foobar');
        });
    }
    

    注意:默认情况下,你的模块的public文件夹的路径会被定义:

    this.expressApp.use("/" + this.name, express.static(this.path + "/public"));
    

    this.io

    Socket IO Instance
    这是一个IO实例的链接,它允许你进行一些Socket.IO操作。在多数情况下,你用不到它,因为Node Helper有一些更加方便的方法。

    requireVersion

    版本:2.1.0

    一个描述MagicMirror框架最低版本的字符串。如果它被设置了,系统会把这个版本号和用户的版本进行比较,如果用户版本较低,就不会运行这个模块。

    注意:因为这个检查设置是在2.1.0版本中介绍的,这个操作不会在更老的版本中执行。
    Example:

    requiresVersion: "2.1.0",
    

    可继承的模块方法

    init()

    当node helper被实例化后这个方法被调用,在多数情况下你不需要继承这个方法。

    start()

    这个方法在node helper被加载好并且系统准备系统时调用。start方法可以用来定义额外的模块属性。

    Example:

    start: function() {
        this.mySpecialProperty = "So much wow!";
        Log.log(this.name + ' is started!');
    }
    

    stop()

    这个方法在MagicMirror服务接收到信号指令并且开始关闭时执行。这个方法需要包括所有需要的指令去关闭已开启的连接、停止所有的子进程并且使各模块静默退出。

    Example:

    stop: function() {
        console.log("Shutting down MyModule");
        this.connection.close();
    }
    

    socketNotificationReceived: function(notification, payload)

    利用这个方法,你的node helper能够接收来自的你模块的通知。这个模块被调用时,它有两个参数:

    • notification - String - 通知的标识
    • payload - AnyType - 通知的负载

    注意: socket连接会在模块使用sendSocketNotification发送第一个信息时就创建

    Example:

    socketNotificationReceived: function(notification, payload) {
        Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
    },
    

    模块实例方法

    每个node helper都有一些能帮助你更好构建模块的便利方法。

    this.sendSocketNotification(notification,payload)

    notification String - 通知的标识
    payload AnyType - 可选 通知的负载

    如果你想给你所有的模块发送通知,使用sendSocketNotification(notification, payload),只有你模块类型的模块会接受到socket通知。

    注意: 因为所有的模块实例都会收到通知,确保正确的模块响应你的信息是你的任务。

    Example:

    this.sendSocketNotification('SET_CONFIG', this.config);
    

    MagicMirror辅助方法

    MagicMirror核心对象:MM有一些便携方法能帮助你更好的把控你的模块。大多数的MM方法都可以在你的模块实例上获取。

    模块选择

    你的模块唯一的附加方法就是获取其他模块的引用。这可以用来隐藏或显示其他模块。

    MM.getModules()

    返回 Array - 模块实例的数组

    使用MM.getModules()方法选择所有被加载的模块实例。它会返回一个现在已经被加载的模块实例的数组。返回的数组有一些筛选方法,见下。

    .withClass(classnames)

    classnames String or Array - 你想要筛选的类名称。
    返回 Array - 一个模块实例数组

    如果你想基于一个或多个类名称进行筛选,在MM。getModules()方法后使用withClass方法。它的参数可以是一个数组,或者用空格分割的字符串。

    Example:

    var modules = MM.getModules().withClass('classname');
    var modules = MM.getModules().withClass('classname1 classname2');
    var modules = MM.getModules().withClass(['classname1','classname2']);
    
    .exceptWithClass(classname)

    classnames String or Array - 你想从结果中移去的模块类名称
    返回 Array - 模块实例数组

    如果你想基于类名称从选择集合中删去一些模块,在MM。getModules()方法后使用exceptWithClass方法。它的参数可以是一个数组,或者用空格分割的字符串。

    Example:

    var modules = MM.getModules().exceptWithClass('classname');
    var modules = MM.getModules().exceptWithClass('classname1 classname2');
    var modules = MM.getModules().exceptWithClass(['classname1','classname2']);
    
    .exceptModule(module)

    module Module Object - 你想从结果中移去的模块的引用。
    返回 Array - 模块实例数组

    此处原文有误。

    如果你想筛选出除了自身的所有模块,这个方法会很有用。

    Example:

    var modules = MM.getModules().exceptModule(this);
    

    当然,你可以把以上选择器结合起来:
    Example:

    var modules = MM.getModules().withClass('classname1').exceptwithClass('classname2').exceptModule(aModule);
    
    .enumerate(callback)

    callback Function(module) - 每一个实例上的回调

    如果你想在所有选择好的模块删执行一个操作,你可以使用enumerate函数:

    MM.getModules().enumerate(function(module) {
        Log.log(module.name);
    });
    

    Example: 要隐藏除了自身的其他模块,你可以这么写:

    Module.register("modulename",{
        //...
        notificationReceived: function(notification, payload, sender) {
            if (notification === 'DOM_OBJECTS_CREATED') {
                MM.getModules().exceptModule(this).enumerate(function(module) {
                    module.hide(1000, function() {
                        //Module hidden.
                    });
                });
            }
        },
        //...
    });
    

    MagicMirror日志记录

    MagicMirror项目对log做了方便的封装。目前,这只是对原生的console.log的简单代理,但是之后可能会增加一些额外的特性。现在,log只在模块核心文件中有效(不包括node_helper)

    Example:

    Log.info('error');
    Log.log('log');
    Log.error('info');
    

    博客:blog.emx6.com

    相关文章

      网友评论

          本文标题:MagicMirror² 模块开发文档

          本文链接:https://www.haomeiwen.com/subject/lmilvctx.html