为插件开发人员实施了官方API,开发人员可以在此API的基础上开发自己的第三方插件。
实际上看了下实现原理,新增了一个文本文件代替nvram,存储插件的配置信息。对比koolshare实现的小型数据库,相对简单些。
更好地与路由器集成。这包括到十个不同的页面,可以在其中的任何位置添加webui,以及用于您的专用存储库设置,可以通过您的自定义网页。有关更多信息,请参见Wiki:
https://github.com/RMerl/asuswrt-merlin/wiki/Addons-API
下面是机翻说明:
第三方插件
介绍
从384.15开始,Asuswrt-Merlin现在支持第三方插件的Web集成。作为新标签,可以在webui的任何现有部分上最多添加十个自定义页面。
还有一个专门的插件存储设置,与nvram分开,因此不受其限制(即,您可以创建新设置而不必重新编译固件映像)。
地点
该/ JFFS /插件/目录的标准位置。您应该在此处创建一个目录来存储所有文件。不要在此目录的根目录下添加文件。
所有插件的用户定义设置将存储在/jffs/addons/custom_settings.txt中。
自定义页面
固件最多支持五个自定义页面。这些页面应在启动时通过脚本安装,理想情况下应从services-start调用该脚本。您应该将自定义页面与安装脚本一起放在/ jffs / addons / my_addon /文件夹中。这是安装脚本的示例,该脚本还将在“工具”部分的新标签页中插入您的页面。
#!/bin/sh
source /usr/sbin/helper.sh
# Locate the first available mount point
MyPage=$(get_webui_page)
if [ $MyPage = "none" ]
then
logger "MyPage" "Unable to install Mypage"
exit 5
fi
logger "MyPage" "Mounting MyPage as $MyPage"
# Copy custom page
cp /jffs/addons/my_addon/MyPage.asp /www/user/$MyPage
# Copy menuTree (if no other script has done it yet) so we can modify it
if [ ! -f /tmp/menuTree.js ]
then
cp /www/require/modules/menuTree.js /tmp/
mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js
fi
# Set correct return URL within your copied page
sed -i "s/MyPage.asp/$MyPage/g" /www/user/$MyPage
# Insert link at the end of the Tools menu. Match partial string, since tabname can change between builds (if using an AS tag)
sed -i "/url: \"Tools_OtherSettings.asp\", tabName:/a {url: \"$MyPage\", tabName: \"My Page\"}," /tmp/menuTree.js
# sed and binding mounts don't work well together, so remount modified file
umount /www/require/modules/menuTree.js && mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js
自定义设置
由于不重建固件就无法动态添加新的nvram设置,并且由于新的Broadcom HND平台的大小限制(新变量的最大限制为最大100个字节),因此为插件实现了新的设置存储库。该/jffs/addons/custom_settings.txt文件将包含所有这些第三方设置。每行将包含一个设置,定义如下:
setting1 My First Setting
setting2 My Second Setting
变量名称应限制为字母数字,破折号(-)和下划线(_)字符。名称的最大长度为29个字符。
从理论上讲,变量内容应允许任何7位ASCII可打印字符。内容的大小限制为2999个字符。如果您需要更复杂的值(例如带有回车符的字符串),建议使用base64编码,或者将数据移动到单独的文件中(如果太大或太复杂)。请注意,如果需要从网页进行读取访问,则可以将这些文件写入/ www / user /文件夹。在这种情况下,请确保使用唯一名称(例如,重用已建立的名称空间),或将数据存储在子目录中。
整个存储库的最大总大小为8 KB。由于这些大小限制,请尽量避免在其中存储大量数据-如果需要,请使用单独的文件。如果更大,则由Web服务器处理时设置将被截断。
您应该定义一个名称空间,以确保轻松识别您的设置,因为所有插件都将共享同一存储库。看起来像这样:
my_addon_version 3.0.2
my_addon_state enabled
diversion_version 2.0
diversion_whitelist /jffs/addons/diversion/my-whitelist.txt
在页面内,您可以通过在页面开始处插入以下内容来作为Javascript对象访问这些设置:
var custom_settings = <% get_custom_settings(); %>;
在HTML部分中,定义输入字段,就像使用nvram设置处理字段一样:
<tr>
<th>Diversion path</th>
<td>
<input type="text" maxlength="100" class="input_25_table" id="diversion_whitelist" autocorrect="off" autocapitalize="off">
</td>
</tr>
还定义一个隐藏的amng_custom输入字段,将在应用设置时将其用于发送回设置:
<input type="hidden" name="amng_custom" id="amng_custom" value="">
然后,在initial()函数中,从对象中检索值,并将其分配给您的字段:
if (custom_settings.diversion_whitelist == undefined)
document.getElementById('diversion_whitelist').value = "/jffs/addons/diversion/diversion-white.txt";
else
document.getElementById('diversion_whitelist').value = custom_settings.diversion_whitelist;
在用户单击“应用”按钮时调用的apply()函数中,检索字段值,将其存储回对象中,将对象转换为字符串并将其分配给隐藏的amng_custom字段,然后提交表单:
function applySettings(){
/* Retrieve value from input fields, and store in object */
custom_settings.diversion_whitelist = document.getElementById('diversion_whitelist').value;
/* Store object as a string in the amng_custom hidden input field */
document.getElementById('amng_custom').value = JSON.stringify(custom_settings);
/* Apply */
showLoading();
document.form.submit();
}
客户服务
您可能需要在路由器上运行一些脚本以对那些设置更改进行操作。服务事件脚本就是这样做的地方,因为即使对于路由器无法识别的事件,它也将运行,从而允许您设计自己的服务处理程序。
在页面中,配置服务重新启动。例如,我们称其为“ myservice”:
<input type="hidden" name="action_script" value="restart_myservice">
创建一个/jffs/addons/my_addon/myservice.sh脚本,其中包含以下内容:
#!/bin/sh
TYPE=$1
EVENT=$2
if [ "$EVENT" = "myservice" -a "$TYPE" = "restart" ]
then
logger -t "myservice" "Restarting my service..."
# do your stuff here, access /jffs/addons/custom_settings.txt, etc...
fi
在/ jffs / scripts / service-event中,调用您的脚本:
#!/bin/sh
### MyService start
/jffs/addons/my_addon/myservice.sh $*
### MyService end
注意:避免将复杂的脚本添加到系统/ jffs / scripts / *脚本中。而是将您的脚本放在/ jffs / addons / my_addon /文件夹中,然后从相关的系统脚本中调用该脚本。这样,您可以轻松开发一个可以查找这些开始/结束行的卸载程序,并删除整个块而不会影响其他已安装的插件。
自定义页面示例
这是一个简单的示例页面,您可以将其用作起点。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta HTTP-EQUIV="Pragma" CONTENT="no-cache">
<meta HTTP-EQUIV="Expires" CONTENT="-1">
<link rel="shortcut icon" href="images/favicon.png">
<link rel="icon" href="images/favicon.png">
<title>Test page</title>
<link rel="stylesheet" type="text/css" href="index_style.css">
<link rel="stylesheet" type="text/css" href="form_style.css">
<script language="JavaScript" type="text/javascript" src="/state.js"></script>
<script language="JavaScript" type="text/javascript" src="/general.js"></script>
<script language="JavaScript" type="text/javascript" src="/popup.js"></script>
<script language="JavaScript" type="text/javascript" src="/help.js"></script>
<script type="text/javascript" language="JavaScript" src="/validator.js"></script>
<script>
var custom_settings = <% get_custom_settings(); %>;
function initial(){
show_menu();
if (custom_settings.diversion_path == undefined)
document.getElementById('diversion_path').value = "/tmp/default";
else
document.getElementById('diversion_path').value = custom_settings.diversion_path;
}
function applySettings(){
/* Retrieve value from input fields, and store in object */
custom_settings.diversion_path = document.getElementById('diversion_path').value;
/* Store object as a string in the amng_custom hidden input field */
document.getElementById('amng_custom').value = JSON.stringify(custom_settings);
/* Apply */
showLoading();
document.form.submit();
}
</script>
</head>
<body onload="initial();" class="bg">
<div id="TopBanner"></div>
<div id="Loading" class="popup_bg"></div>
<iframe name="hidden_frame" id="hidden_frame" src="" width="0" height="0" frameborder="0"></iframe>
<form method="post" name="form" action="start_apply.htm" target="hidden_frame">
<input type="hidden" name="current_page" value="MyPage.asp">
<input type="hidden" name="next_page" value="MyPage.asp">
<input type="hidden" name="group_id" value="">
<input type="hidden" name="modified" value="0">
<input type="hidden" name="action_mode" value="apply">
<input type="hidden" name="action_wait" value="5">
<input type="hidden" name="first_time" value="">
<input type="hidden" name="action_script" value="">
<input type="hidden" name="preferred_lang" id="preferred_lang" value="<% nvram_get("preferred_lang"); %>">
<input type="hidden" name="firmver" value="<% nvram_get("firmver"); %>">
<input type="hidden" name="amng_custom" id="amng_custom" value="">
<table class="content" align="center" cellpadding="0" cellspacing="0">
<tr>
<td width="17"> </td>
<td valign="top" width="202">
<div id="mainMenu"></div>
<div id="subMenu"></div>
</td>
<td valign="top">
<div id="tabMenu" class="submenuBlock"></div>
<table width="98%" border="0" align="left" cellpadding="0" cellspacing="0">
<tr>
<td align="left" valign="top">
<table width="760px" border="0" cellpadding="5" cellspacing="0" bordercolor="#6b8fa3" class="FormTitle" id="FormTitle">
<tr>
<td bgcolor="#4D595D" colspan="3" valign="top">
<div> </div>
<div class="formfonttitle">Test page - Custom settings</div>
<div style="margin:10px 0 10px 5px;" class="splitLine"></div>
<div class="formfontdesc"><#1838#></div>
<table width="100%" border="1" align="center" cellpadding="4" cellspacing="0" bordercolor="#6b8fa3" class="FormTable">
<tr>
<th>Diversion path</th>
<td>
<input type="text" maxlength="100" class="input_25_table" id="diversion_path" autocorrect="off" autocapitalize="off">
</td>
</tr>
</table>
<div class="apply_gen">
<input name="button" type="button" class="button_gen" onclick="applySettings();" value="Apply"/>
</div>
</form>
<div>
<table class="apply_gen">
<tr class="apply_gen" valign="top">
</tr>
</table>
</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td width="10" align="center" valign="top"></td>
</tr>
</table>
<div id="footer"></div>
</body>
</html>
老哥,天天看你博客,终于更新了
我的评论审核通过了吗 现在疫情真严重 作者也要保重啊
期待大佬的新固件
这是好消息