小米路由器增强脚本

哎!

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         小米路由器增强脚本
// @namespace    https://blog.iccfish.com/
// @version      0.3.0
// @description  哎!
// @author       木鱼([email protected])
// @include      /http:\/\/.*?\/cgi-bin\/luci\/;stok.*/
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==

function boot() {
	let pathName = location.pathname;

	if (/\/web\/home/.test(pathName)) {
		initHomePage();
	}
}

let token,
	jQuery,
	uw = unsafeWindow;

function getToken() {
	if (!token) token = /;stok=([\da-f]+)/.exec(location.href) && RegExp.$1;

	return token;
}

function initHomePage() {
	var s = document.createElement("style");
	s.id = "blog.iccfish.com";
	s.textContent =
		".device-speed { float: right; }\
	.up-speed, .down-speed { display: inline-block;  padding-left: 16px; border: 1px solid #ff4c00; color: #ff4c00; position: relative; --percentage: 0; width: 110px; text-align: center; font-size: 90%; }\
	.up-speed:after, .down-speed:after { 	content: '🔼'; position: absolute; left: 0; top: 0; line-height: 150%; }\
	.up-speed:before, .down-speed:before { content: ' '; position: absolute; left: 0; top: 0; bottom: 0; background: rgba(255,0,0,0.25); width: var(--percentage); }\
	.down-speed { color:#0059fa; border-color:#0059fa; }\
	.down-speed:after { content: '🔽'; }\
	.down-speed:before { background-color: rgb(0 68 255 / 25%); }\
	";
	document.head.appendChild(s);
	// 替换模板
	let devicesItemTmpl = document.querySelector("#tmpldevicesitem");
	devicesItemTmpl.innerHTML =
		'\
	<tr class="device-item" data-mac="{$mac}">\
	<td>\
	<img class="dev-icon" width="60" src="{$devices_icon}" onerror="this.src=\'/img/device_list_error.png\'">\
	<div class="dev-info">\
	<div class="name">{$name} &nbsp;&nbsp;{if($isself)}<span class="muted">|&nbsp;本机</span>{/if}</div>\
	<ul class="devnetinfo clearfix">\
	<li><span class="k">已连接:</span> <span class="v online-time">{$online}</span></li>\
	<li>{for(var i=0, len=$ip.length; i<len; i++)}<p data-ip="{$ip[i]}"><span class="k">IP地址:</span> <span class="v">{$ip[i]}</span></p>{/for}</li>\
	<li><span class="k">MAC地址:</span> <span class="v">{$mac}</span></li>\
	</ul>\
	</div>\
	</td>\
	{if($d_is_ap != 8)}<td class="option">{$option}</td>{/if}\
	{if($d_is_ap == 8)}<td class="option_d01"></td>{/if}\
	{if($hasDisk)}<td class="option2">{$option2}</td>{/if}\
	</tr>';

	// 处理数据
	let lock;

	function showSpeed(list) {
		let needFullReload = false;

		let totalUpload = 0,
			totalDownload = 0;
		list.filter(item => item.statistics).forEach(item => {
			totalDownload += +item.statistics.downspeed;
			totalUpload += +item.statistics.upspeed;
		});

		list.forEach(item => {
			if (item.statistics) {
				let mac = item.mac;
				let tr = uw.$(`tr.device-item[data-mac='${mac}']`);
				if (!tr) {
					needFullReload = true;
					return;
				}

				let title = tr.find("div.name");
				let upspeed = uw.byteFormat(+item.statistics.upspeed, 100) + "/S";
				let downspeed = uw.byteFormat(+item.statistics.downspeed, 100) + "/S";
				let online = jQuery.secondToDate(+item.statistics.online);
				let ups = title.find(".up-speed");

				const pu = totalUpload ? Math.round((+item.statistics.upspeed * 10000) / totalUpload) / 100 : 0;
				const pd = totalDownload ? Math.round((+item.statistics.downspeed * 10000) / totalDownload) / 100 : 0;

				if (ups.length) {
					ups.html(`${upspeed} | ${pu}%`)[0].style.setProperty("--percentage", pu + "%");
					title
						.find(".down-speed")
						.html(`${downspeed} | ${pd}%`)[0]
						.style.setProperty("--percentage", pd + "%");
				} else {
					let speedTmpl = `<sub class='device-speed'><span class='up-speed' style="--percentage: ${pu}%;">${upspeed} | ${pu}%</span> <span class='down-speed' style="--percentage: ${pd}%;">${downspeed} | ${pd}%</span></sub>`;
					title.append(speedTmpl);
				}
				tr.find(".online-time").html(online);
			}
		});

		let total = jQuery("div.total-speed");
		if (!total.length) {
			jQuery("#bd").prepend("<div class=\"total-speed\" style='padding: 10px 0;margin-bottom: -40px;font-size: 130%;color: #0a6f15;'>总速度:🔼<span style='color:#ff4c00;' class='up'>--</span> 🔽<span style='color:#0059fa;' class='down'>--</span></div>");
			total = jQuery("div.total-speed");
		}
		//debugger;
		total.find(".up").html(uw.byteFormat(totalUpload, 100) + "/S");
		total.find(".down").html(uw.byteFormat(totalDownload, 100) + "/S");

		if (location.hash !== "#devices") return;
		if (needFullReload) {
			console.log("发现新设备,需要完全重新加载.");
			jQuery.pub("devices:getlist");
		} else {
			setTimeout(refreshSpeed, 1000);
		}
	}

	function refreshSpeed() {
		if (lock) return;
		lock = true;

		let api = `/cgi-bin/luci/;stok=${getToken()}/api/misystem/devicelist`;
		jQuery
			.getJSON(api, {})
			.done(function (data) {
				if (data.code !== 0) return;

				showSpeed(data.list);
			})
			.fail(function () {
				setTimeout(refreshSpeed, 1000);
			})
			.always(function () {
				lock = false;
			});
	}

	jQuery(document).ajaxComplete(function (e, xhr, setting) {
		if (/misystem\/devicelist/.test(setting.url)) {
			let data;
			try {
				data = JSON.parse(xhr.responseText);
			} catch (e) {
				console.log(`invalid reponse: ${e}.`);
				return;
			}

			showSpeed(data.list);
		}
	});
}

(function () {
	let val, inited;

	Object.defineProperty(unsafeWindow, "jQuery", {
		get: function () {
			return val;
		},
		set: function (v) {
			val = v;
			jQuery = v;
			if (!inited) {
				inited = true;
				boot();
			}
		},
	});
})();