#MagicMirror²模块开发文档
本文档介绍了开发自己的MagicMirror²模块的方法。
目录:
模块结构
核心模块文件:modulename.js
Node Helper:node_helper.js
可用的模块实例属性
子类可模块方法
模块实例方法
MagicMirror助手方法
模块选择
MagicMirror Logger
随着MagicMirror的广泛普及,可用模块的数量也越来越多。对于新用户和开发人员来说,浏览各种存储库以查明某些模块究竟是什么,它的外观以及它所依赖的内容非常耗时。遗憾的是,这些信息很少可用,也不需要先安装即可轻松获得。
因此我们强烈建议您在README文件中包含以下信息。
当然,这也有助于您获得更好的工作认可和反馈。
所有模块都加载到modules
文件夹中。默认模块在modules / default
文件夹中组合在一起。您的模块应该放在modules
的子目录下。请注意,您在modules
文件夹中创建的任何文件或文件夹都将被git忽略,允许您升级MagicMirror²而不会丢失文件。
模块可以放在一个文件夹中。或者可以将多个模块分组到子文件夹中。请注意,模块的名称必须是唯一的。即使具有相似名称的模块放在不同的文件夹中,也无法同时加载它们。
/ modulename / filename.ext
上的浏览器加入。这是将定义模块的脚本。需要此脚本才能使用该模块。在它最简单的形式中,核心模块文件必须包含:
Module.register( “模块名”,{});
当然,上面的模块不会做任何花哨的事情,所以最好看一个最简单的模块:** helloworld **:
//helloworld.js:
Module.register( “HelloWorld” 的,{
//默认模块配置
默认值:{
文字:“Hello World!”
},
//覆盖dom生成器。
getDom:function(){
var wrapper = document.createElement(“div”);
wrapper.innerHTML = this.config.text;
返回包装;
}
});
如您所见,Module.register()
方法有两个参数:模块的名称和具有模块属性的对象。
初始化模块后,模块实例具有一些可用的模块属性:
| 实例属性| 输入| 说明|
|:----------------- |:---- |:-----------
| this.name
| 字符串| 模块的名称。|
| this.identifier
| 字符串| 这是模块实例的唯一标识符。|
| this.hidden
| 布尔| 这表示模块当前是否隐藏(淡出)。|
| this.config
| 布尔| 用户的config.js
文件中设置的模块实例的配置。如果用户配置未覆盖这些属性,则此配置还将包含模块的默认值。|
| this.data
| 对象| 数据对象包含有关模块实例的其他元数据。(见下文)|
this.data
数据对象包含以下元数据:
data.classes
- 添加到模块dom包装器的类。data.file
- 核心模块文件的文件名。data.path
- 模块文件夹的路径。data.header
- 添加到模块的标头。data.position
- 实例显示的位置。defaults:{}
defaults对象中定义的任何属性都将与用户的config.js文件中定义的模块配置合并。这是设置模块配置默认值的最佳位置。可以使用this.config.propertyName
访问任何模块配置属性,但稍后会详细介绍。
requiresVersion:
在版本中引入:2.1.0。
一个字符串,用于定义MagicMirror框架的最低版本。如果已设置,系统会将所需版本与用户版本进行比较。如果用户的版本已过期,则不会运行该模块。确保还在Node助手中设置此值。
注意: 由于此检查是在2.1.0版本中引入的,因此不会在旧版本中运行此检查。如果您在模块上收到问题报告,请记住这一点。
例:
要求版本:“2.1.0”,
init()
在模块实例化时调用此方法。在大多数情况下,您不需要子类化此方法。
loaded(callback)
在版本中引入:2.1.1。
加载模块时调用此方法。配置中的后续模块尚未加载。模块加载完成后,必须调用callback
函数。在大多数情况下,您不需要子类化此方法。
例:
loaded:function(callback){
this.finishLoading();
Log.log(this.name +'已加载!');
打回来();
}
start()
加载所有模块并且系统准备好启动时调用此方法。请记住,尚未创建模块的dom对象。start方法是定义任何其他模块属性的理想场所:
例:
start:function(){
this.mySpecialProperty =“太多哇!”;
Log.log(this.name +'已启动!');
}
getScripts()
应该返回:数组
调用getScripts方法以请求任何需要加载的其他脚本。因此,此方法应返回包含字符串的数组。如果要返回模块文件夹中文件的完整路径,请使用this.file('filename.js')
方法。在所有情况下,加载器只会加载一次文件。它甚至会检查文件是否在默认供应商文件夹中可用。
例:
getScripts:function(){
返回[
'script.js',//将尝试从vendor文件夹加载它,否则它将从模块文件夹加载。
'moment.js',//此文件在供应商文件夹中可用,因此不需要在模块文件夹中可用。
this.file('anotherfile.js'),//这个文件将直接从模块文件夹加载。
'https://code.jquery.com/jquery-2.2.3.min.js',//此文件将从jquery服务器加载。
]
}
**注意:**如果无法加载文件,镜像的启动将停止。因此建议不要使用任何外部网址。
getStyles()
应该返回:数组
调用getStyles方法以请求任何需要加载的其他样式表。因此,此方法应返回包含字符串的数组。如果要返回模块文件夹中文件的完整路径,请使用this.file('filename.css')
方法。在所有情况下,加载器只会加载一次文件。它甚至会检查文件是否在默认供应商文件夹中可用。
例:
getStyles:function(){
返回[
'script.css',//将尝试从vendor文件夹加载它,否则它将从模块文件夹加载。
'font-awesome.css',//此文件在供应商文件夹中可用,因此不需要在模块文件夹中提供。
this.file('anotherfile.css'),//这个文件将直接从模块文件夹加载。
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css',//此文件将从bootstrapcdn服务器加载。
]
}
**注意:**如果无法加载文件,镜像的启动将停止。因此建议不要使用任何外部网址。
getTranslations()
应该返回:词典
调用getTranslations方法以请求需要加载的转换文件。因此,此方法应返回包含要加载的文件的字典,由国家/地区的短名称标识。
如果模块没有任何模块特定的转换,则可以省略该函数或返回“false”。
例:
getTranslations:function(){
返回{
EN:“translations / en.json”,
de:“translations / de.json”
}
}
getDom()
应该返回: Dom对象
每当MagicMirror需要更新屏幕上的信息时(因为它启动,或者因为你的模块使用this.updateDom()
来请求刷新),系统会调用getDom方法。因此,此方法应返回一个dom对象。
例:
getDom:function(){
var wrapper = document.createElement(“div”);
wrapper.innerHTML ='Hello world!';
返回包装;
}
getHeader()
**应该返回:**字符串
每当MagicMirror需要更新屏幕上的信息时(因为它启动,或者因为你的模块使用this.updateDom()
来请求刷新),系统会调用getHeader方法来检索模块的头。因此,此方法应返回一个字符串。如果此方法不是子类,则此函数将返回用户配置的标头。
如果要使用原始用户的已配置标头,请参阅this.data.header
。
**注意:**如果用户未配置默认标头,则不会显示标头,因此不会调用此方法。
例:
getHeader:function(){
返回this.data.header +'Foo Bar';
}
notificationReceived(通知,有效负载,发送者)
该MagicMirror核心能够向模块发送通知。甚至更好:模块可以向其他模块发送通知。调用此模块时,它有3个参数:
notification
- String - 通知标识符。payload
- AnyType - 通知的有效负载。sender
- Module - 通知的发送者。如果这个参数是“未定义的”,则通知的发送者是核心系统。例:
notificationReceived:function(notification,payload,sender){
if(sender){
Log.log(this.name +“收到模块通知:”+发件人+“来自发件人:”+ sender.name);
} else {
Log.log(this.name +“收到系统通知:”+通知);
}
}
**注意:**系统在启动时发送三个通知。这些通知可以派上用场!
ALL_MODULES_STARTED
- 所有模块都已启动。您现在可以向其他模块发送通知。DOM_OBJECTS_CREATED
- 创建所有dom对象。系统现在可以执行视觉更改。MODULE_DOM_CREATED
- 该模块的dom已满载。您现在可以访问模块的dom对象。socketNotificationReceived:function(notification,payload)
使用node_helper时,节点帮助程序可以发送模块通知。调用此模块时,它有2个参数:
notification
- String - 通知标识符。payload
- AnyType - 通知的有效负载。**注1:**当节点助手发送通知时,该模块类型的所有模块都会收到相同的通知。点击
**注2:**一旦模块使用[sendSocketNotification](thissendsocketnotificationnotification-payload)发送第一条消息,就建立套接字连接。
例:
socketNotificationReceived:function(notification,payload){
Log.log(this.name +“收到套接字通知:”+ notification +“ - Payload:”+ payload);
},
suspend()
当一个模块被隐藏时(使用module.hide()
方法),将调用suspend()
方法。通过子类化此方法,您可以执行暂停更新计时器等任务。
resume()
当请求显示模块时(使用module.show()
方法),将调用resume()
方法。通过子类化此方法,您可以执行重新启动更新计时器的任务。
每个模块实例都有一些方便的方法,可以帮助您构建模块。
this.file(filename)
*** filename * String ** - 要为其创建路径的文件的名称。
返回字符串
如果要在模块文件夹中创建文件的路径,请使用file()
方法。它返回作为属性给出的文件名的路径。在配置[getScripts](#getscripts)和[getStyles](#getstyles)方法时,方法是否派上用场。
this.updateDom(speed)
速度号码 - 可选。动画速度,以毫秒为单位。
每当需要更新模块时,请调用updateDom(speed)
方法。它请求MagicMirror核心更新其dom对象。如果您定义速度,则内容更新将被设置动画,但前提是内容确实会更改。
例如:时钟模块每秒调用此方法:
...
start:function(){
var self = this;
setInterval(function(){
self.updateDom(); //没有定义速度,所以它立即更新。
},1000); //每1000毫秒执行一次。
},
...
this.sendNotification(notification,payload)
*** notification * String ** - 通知标识符。
**有效载荷 AnyType ** - 可选。通知负载。
如果要向所有其他模块发送通知,请使用sendNotification(notification,payload)
。所有其他模块将通过[notificationReceived](#notificationreceivednotification-payload-sender)方法接收消息。在这种情况下,发件人会自动设置为调用sendNotification方法的实例。
例:
this.sendNotification('MYMODULE_READY_FOR_ACTION',{foo:bar});
####this.sendSocketNotification(notification,payload)
*** notification * String ** - 通知标识符。
**有效载荷 AnyType ** - 可选。通知负载。
如果要向node_helper发送通知,请使用sendSocketNotification(notification,payload)
。只有此模块的node_helper才会收到套接字通知。
例:
this.sendSocketNotification('SET_CONFIG',this.config);
####this.hide(speed,callback,options)
*** speed * Number ** - 可选(设置回调或选项时需要),隐藏动画的速度(以毫秒为单位)。
*** callback * Function ** - 可选,隐藏动画结束后的回调。
*** options * Function ** - 可选,具有隐藏操作附加选项的对象(见下文)。(在版本中引入:2.1.0。)
要隐藏模块,可以调用hide(speed,callback)
方法。您可以使用this.hide()
在模块实例本身上调用hide方法,但当然您也可以使用anOtherModule.hide()
隐藏另一个模块。
可配置的选项:
lockString
- String - 设置锁定字符串时,如果没有传递正确的锁定字符串,则无法显示模块。这种方式(多个)模块可以阻止模块显示。将模块标识符用作locksString:this.identifier
被认为是最佳实践。请参阅下面的* visibility locking *。注1: 如果隐藏动画被取消,例如因为在隐藏动画完成之前调用了show方法,则不会调用回调。
注2: 如果隐藏动画被劫持(其他方法调用隐藏在同一模块上),则不会调用回调。
注3: 如果尚未创建dom,则隐藏方法将不起作用。等待DOM_OBJECTS_CREATED
[通知](#notificationreceivednotification-payload-sender)。
this.show(speed,callback,options)
*** speed * Number ** - 可选(设置回调或选项时需要),show动画的速度,以毫秒为单位。
*** callback * Function ** - 可选,show动画结束后的回调。
*** options * Function ** - 可选,具有show动作的附加选项的Object(见下文)。(在版本中引入:2.1.0。)
要显示模块,可以调用show(speed,callback)
方法。您可以使用this.show()
在模块实例本身上调用show方法,但当然您也可以使用anOtherModule.show()
来显示另一个模块。
可配置的选项:
lockString
- String - 设置锁定字符串时,如果没有传递正确的锁定字符串,则无法显示模块。这种方式(多个)模块可以阻止模块显示。请参阅下面的* visibility locking *。force
- Boolean - 当强制标记设置为“true”时,锁定机制将被覆盖。请谨慎使用此选项。最好的做法是让force选项的使用是可配置的。请参阅下面的* visibility locking *。**注1:**如果取消节目动画,例如因为在节目动画播放完成之前调用了hide方法,则不会调用回调。
**注2:**如果节目动画被劫持(其他方法调用显示在同一模块上),则不会调用回调。
**注3:**如果尚未创建dom,show方法将不起作用。等待DOM_OBJECTS_CREATED
[通知](#notificationreceivednotification-payload-sender)。
(在版本中引入:2.1.0。)
Visiblity锁定有助于模块系统防止不必要的隐藏/显示操作。以下方案解释了该概念:
模块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将从moduleA的locks数组中删除,但由于仍有其他锁定字符串可用,因此模块保持隐藏状态:
moduleA.lockStrings == [“module_c_identifier”]
moduleA.hidden == true
模块C要求模块A显示:
moduleA.show(0,{lockString:“module_c_identifier”});
lockString将从moduleA的locks数组中删除,因为这将导致一个空的锁数组,该模块将是可见的:
moduleA.lockStrings == []
moduleA.hidden == false
**注意:**使用强制标记可以覆盖锁定机制:
moduleA.show(0,{force:true});
这将重置lockstring数组,并将显示模块。
moduleA.lockStrings == []
moduleA.hidden == false
请谨慎使用此“强制”方法。有关更多信息,请参阅show()
方法。
this.translate(identifier)
*** identifier * String ** - 应翻译的字符串的标识符。
魔镜包含一个便利包装l18n
。您可以使用它根据用户的“语言”配置自动为模块提供不同的翻译。
如果未找到翻译,则将使用后备。回退顺序如下:
当添加翻译到你的模块,这是一个好主意,看看如果apropriate翻译是在[核心翻译文件]已经上市(https://github.com/MichMich/MagicMirror/tree/master/translations)。这样,您的模块就可以从现有的翻译中受益。
例:
this.translate(“INFO”)//将返回标识符INFO的翻译字符串
示例json文件:
{
“信息”:“非常重要的信息!”
}
**注意:**虽然JSON文件中官方不支持注释,但MagicMirror通过在解析JSON文件之前剥离注释来允许它。翻译文件中的评论可以帮助其他翻译人员。
this.translate(标识符,变量)
*** identifier * String ** - 应翻译的字符串的标识符。
*** variables * Object ** - 要在翻译中使用的变量对象。
这种改进的和向后兼容的处理翻译的方式与正常翻译功能类似,并遵循上述规则。建议使用这种新格式进行各种翻译。它允许翻译人员改变要翻译的句子中的单词顺序。
例:
var timeUntilEnd = moment(event.endDate,“x”)。fromNow(true);
this.translate(“RUNNING”,{“timeUntilEnd”:timeUntilEnd)}); //将返回标识符RUNNING的翻译字符串,将“{timeUntilEnd}”替换为变量`timeUntilEnd`的内容,按照翻译者的意图。
示例英文.json文件:
{
“RUNNING”:“结束于{timeUntilEnd}”,
}
示例芬兰语.json文件:
{
“RUNNING”:“Päättyy{timeUntilEnd}päästä”,
}
注意: * variables * Object有一个特殊情况叫做’fallback`。它用于支持没有变量的翻译文件中的旧翻译。如果要升级具有不支持单词顺序的翻译的旧模块,建议使用后备布局。
例:
var timeUntilEnd = moment(event.endDate,“x”)。fromNow(true);
this.translate(“RUNNING”,{
“后备”:this.translate(“RUNNING”)+“{timeUntilEnd}”
“timeUntilEnd”:timeUntilEnd
)}); //将返回标识符RUNNING的翻译字符串,将“{timeUntilEnd}”替换为变量`timeUntilEnd`的内容,按照翻译者的意图。(有后备)
示例瑞典.json文件中没有变量:
{
“奔跑”:“Slutar”,
}
在这种情况下,translate
-function将不会在翻译中找到任何变量,将查找fallback
变量并在可能的情况下使用它来创建翻译。
节点助手是一个Node.js脚本,它能够执行一些后端任务来支持您的模块。对于每种模块类型,只会创建一个节点帮助程序实例。例如:如果您的MagicMirror使用两个日历模块,则只会实例化一个日历节点帮助程序。
**注意:**因为每个模块类型只有一个节点助手,所以模块中没有可用的默认配置。您需要将所需的配置从模块发送到节点帮助程序。
在它最简单的形式中,node_helper.js文件必须包含:
var NodeHelper = require(“node_helper”);
module.exports = NodeHelper.create({});
当然,上面的助手不会做任何有用的事情。因此,根据上述信息,您应该能够使它更复杂一些。
this.name
串
模块的名称
this.path
串
模块的路径
this.expressApp
** Express App Instance **
这是Express实例的链接。它将允许您定义额外的路线。
例:
start:function(){
this.expressApp.get('/ foobar',function(req,res){
res.send('GET request to / foobar');
});
}
**注意:**默认情况下,将创建模块公用文件夹的公共路径:
this.expressApp.use(“/”+ this.name,express.static(this.path +“/ public”));
this.io
套接字IO实例
这是IO实例的链接。它将允许你做一些Socket.IO魔术。在大多数情况下,您不需要这样做,因为Node Helper有一些方便的方法来简化这一过程。
requiresVersion:
在版本中引入:2.1.0。
一个字符串,用于定义MagicMirror框架的最低版本。如果已设置,系统会将所需版本与用户版本进行比较。如果用户的版本已过期,则不会运行该模块。
**注意:**由于此检查是在2.1.0版本中引入的,因此不会在旧版本中运行此检查。如果您在模块上收到问题报告,请记住这一点。
例:
要求版本:“2.1.0”,
init()
在实例化节点帮助程序时调用此方法。在大多数情况下,您不需要子类化此方法。
start()
加载所有节点助手并且系统准备好启动时,将调用此方法。start方法是定义任何其他模块属性的理想场所:
例:
start:function(){
this.mySpecialProperty =“太多哇!”;
Log.log(this.name +'已启动!');
}
stop()
当MagicMirror服务器收到SIGINT
命令并正在关闭时,将调用此方法。此方法应包括关闭任何打开的连接所需的任何命令,停止任何子进程并正常退出模块。
例:
stop:function(){
console.log(“关闭MyModule”);
this.connection.close();
}
socketNotificationReceived:function(notification,payload)
使用此方法,您的节点帮助程序可以从模块接收通知。调用此方法时,它有2个参数:
notification
- String - 通知标识符。payload
- AnyType - 通知的有效负载。**注意:**一旦模块使用[sendSocketNotification](thissendsocketnotificationnotification-payload)发送第一条消息,就建立套接字连接。
例:
socketNotificationReceived:function(notification,payload){
Log.log(this.name +“收到套接字通知:”+ notification +“ - Payload:”+ payload);
},
每个节点助手都有一些方便的方法,可以帮助您构建模块。
this.sendSocketNotification(notification,payload)
*** notification * String ** - 通知标识符。
**有效载荷 AnyType ** - 可选。通知负载。
如果要向所有模块发送通知,请使用sendSocketNotification(notification,payload)
。只有模块类型的模块才会收到套接字通知。
**注意:**由于模块的所有实例都将收到通知,因此确保正确的模块响应您的消息是您的任务。
例:
this.sendSocketNotification('SET_CONFIG',this.config);
核心Magic Mirror对象:MM
有一些方便的方法可以帮助你控制你和其他模块。大多数MM
方法都可以通过Module实例上的便捷方法获得。
您的模块唯一可用的附加方法是检索对其他模块的引用的功能。这可以用于隐藏和显示其他模块。
MM.getModules()
返回数组 - 包含模块实例的数组。
要选择所有当前加载的模块实例,请运行MM.getModules()
方法。它将返回一个包含所有当前加载的模块实例的数组。返回的数组有很多过滤方法。有关详细信息,请参见下文
**注意:**如果尚未启动所有模块,则此方法返回空数组。等待ALL_MODULES_STARTED
[通知](#notificationreceivednotification-payload-sender)。
。withClass(classnames)
*** classnames * String或Array ** - 要过滤的类名。
返回数组 - 包含模块实例的数组。
如果要根据一个或多个类名进行选择,请对MM.getModules()
方法的结果使用withClass方法。withClass(classname)
方法的参数可以是数组,也可以是空格分隔的字符串。
例子:
var modules = MM.getModules()。withClass('classname');
var modules = MM.getModules()。withClass('classname1 classname2');
var modules = MM.getModules()。withClass(['classname1','classname2']);
。exceptWithClass(classnames)
*** classnames * String或Array ** - 要从结果中删除的模块的类名。
返回数组 - 包含模块实例的数组。
如果要从基于类名的选择中删除某些模块,请对MM.getModules()
方法的结果使用exceptWithClass方法。exceptWithClass(classname)
方法的参数可以是数组,也可以是空格分隔的字符串。
例子:
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 ** - 对要从结果中删除的模块的引用。
返回数组 - 包含模块实例的数组。
如果要从基于类名的选择中删除特定模块实例,请对MM.getModules()
方法的结果使用exceptWithClass方法。如果要选择除模块实例之外的所有模块实例,这将非常有用。
例子:
var modules = MM.getModules()。exceptModule(this);
当然,您可以组合以上所有过滤器:
例:
var modules = MM.getModules()。withClass('classname1')。exceptwithClass('classname2')。exceptModule(aModule);
。。enumerate(callback)
回调函数(模块) - 在每个实例上运行回调。
如果要对所有选定的模块执行操作,可以使用enumerate
函数:
MM.getModules()。enumerate(function(module){
Log.log(module.name);
});
例:
要隐藏除模块实例之外的所有模块,您可以编写如下内容:
Module.register( “模块名”,{
// ...
notificationReceived:function(notification,payload,sender){
if(notification ==='DOM_OBJECTS_CREATED'){
MM.getModules()。exceptModule(this).enumerate(function(module){
module.hide(1000,function(){
//模块隐藏
});
});
}
},
// ...
});
Magic Mirror包含一个用于记录的便利包装器。目前,此记录器是原始console.log
方法的简单代理。但它可能会在未来获得更多功能。Logger目前仅在核心模块文件中可用(不在node_helper中)。
例子:
Log.info( '错误');
Log.log( '登录');
Log.error( '信息');