// ==UserScript==
// @name 小米路由器PC端设备实时网速
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 小米路由器PC端后台管理界面设备实时网速
// @author 过去终究是个回忆
// @license MIT
// @match http://192.168.31.1/cgi-bin/luci/*/web/home*
// @require https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/dayjs/1.10.8/dayjs.min.js
// @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js
// @require https://unpkg.com/[email protected]/dist/ajaxhook.min.js
// @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/element-ui/2.15.7/index.min.js
// @grant GM_getResourceText
// @grant GM_addStyle
// @grant GM_addStyle
// @grant unsafeWindow
// @resource element-ui https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/element-ui/2.15.7/theme-chalk/index.min.css
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
GM_addStyle(GM_getResourceText('element-ui'))
GM_addStyle(`
@font-face{font-family:element-icons;src:url(https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/element-ui/2.15.7/theme-chalk/fonts/element-icons.woff) format("woff"),url(https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/element-ui/2.15.7/theme-chalk/fonts/element-icons.ttf) format("truetype");font-weight:400;font-display:"auto";font-style:normal}
#net-speed {
float: left;
margin-top: 40px;
width: 100%;
}
.demo-table-expand {
font-size: 0;
}
.speed-line {
display: flex;
justify-content: space-between;
align-items: center;
}
.speed-line div[role="progressbar"] {
width: 300px;
}
`)
const maxEachDataStorage = 100
const updateDevDataEvent = new Event('updateDevData')
const initData = {
history: {},
tableData: []
}
window.ah.proxy({
//请求成功后进入
onResponse: (response, handler) => {
if (response.config.url.includes('/api/misystem/status')) {
const data = JSON.parse(response.response)
initData.tableData = data.dev
data.dev.forEach(item => {
if (!initData.history[item.mac]) {
initData.history[item.mac] = []
}
const each = {
...item,
dateSecond: Date.now(),
combinedSpeed: item.upspeed + item.downspeed,
}
if (initData.history[item.mac].length === maxEachDataStorage) {
initData.history[item.mac][maxEachDataStorage] = each
} else if (initData.history[item.mac].length < maxEachDataStorage) {
initData.history[item.mac].push(each)
} else if (initData.history[item.mac].length > maxEachDataStorage) {
initData.history[item.mac].splice(maxEachDataStorage - 1, initData.history.length, each)
}
})
document.dispatchEvent(updateDevDataEvent)
}
handler.next(response)
}
}, unsafeWindow)
window.onload = function () {
const container = document.createElement('div')
container.id = 'net-speed'
console.log('net-speed starting...')
const target = document.querySelector('#bd>.mod-routerstatus.nav-tab-content')
target.appendChild(container)
new Vue({
el: container,
data() {
return {
dialogTableTitle: 'dialogTableTitle',
dialogTableVisible: false,
currentRow: {},
history: {},
tableData: [],
}
},
created() {
this.history = initData.history
this.tableData = initData.tableData
document.addEventListener("updateDevData", () => {
this.history = initData.history
this.tableData = initData.tableData
})
},
methods: {
byteFormat: byteFormat,
secondToDate: secondToDate,
speedFormat(number, precision, isarray) {
return this.byteFormat(number, precision, isarray) + '/S'
},
speedFormatter(row, column, cellValue) {
return this.speedFormat(cellValue, 100)
},
dataFormatter(row, column, cellValue) {
return this.byteFormat(cellValue, 100)
},
secondFormatter(row, column, cellValue) {
return this.secondToDate(cellValue)
},
dateTimeFormatter(row, column, cellValue) {
return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
},
showHistoryData(row) {
this.dialogTableVisible = true
this.dialogTableTitle = `${row.devname}(${row.mac}) 历史数据`
this.currentRow = row
console.log(this.history)
}
},
template: `
<div id="net-speed">
<el-table
v-loading="!tableData.length"
:data="tableData"
:default-sort="{prop: 'combinedSpeed', order: 'ascending'}"
border
fit
max-height="500px"
style="width: 100%;"
>
<el-table-column
prop="devname"
label="设备"
sortable
width="200"
/>
<el-table-column
prop="mac"
label="MAC"
sortable
width="148"
/>
<el-table-column
prop="combinedSpeed"
label="网速"
width="540"
sortable
sort-by="combinedSpeed"
>
<template slot-scope="scope">
<p class="speed-line">上传速度: <el-progress :percentage="scope.row.upspeed / scope.row.maxuploadspeed * 100" :show-text="false" color="#2673bf"></el-progress>{{speedFormat(scope.row.upspeed, 100)}}</p>
<p class="speed-line">下载速度: <el-progress :percentage="scope.row.downspeed / scope.row.maxdownloadspeed * 100" :show-text="false" color="#33cc33"></el-progress>{{speedFormat(scope.row.downspeed, 100)}}</p>
</template>
</el-table-column>
<el-table-column
prop="maxuploadspeed"
label="历史最大上传速度"
width="158"
sortable
:formatter="speedFormatter"
/>
<el-table-column
prop="maxdownloadspeed"
label="历史最大下载速度"
width="158"
sortable
:formatter="speedFormatter"
/>
<el-table-column
prop="upload"
label="上传流量"
width="102"
sortable
:formatter="dataFormatter"
/>
<el-table-column
prop="download"
label="下载流量"
width="102"
sortable
:formatter="dataFormatter"
/>
<el-table-column
prop="online"
label="在线时间"
width="140"
sortable
:formatter="secondFormatter"
/>
<el-table-column
prop="devname"
label="操作"
fixed="right"
>
<template slot-scope="scope">
<el-button type="text" size="small" @click="showHistoryData(scope.row)">历史数据</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog :title="dialogTableTitle" :visible.sync="dialogTableVisible">
<el-table
:data="this.history[this.currentRow.mac]"
:default-sort="{prop: 'dateSecond', order: 'descending'}"
border
fit
max-height="500px"
style="width: 100%;"
>
<el-table-column
property="dateSecond"
sortable
label="时间"
width="164"
:formatter="dateTimeFormatter"
/>
<el-table-column
label="网速"
sortable
min-width="540"
sort-by="combinedSpeed"
>
<template slot-scope="scope">
<p class="speed-line">上传速度: <el-progress :percentage="scope.row.upspeed / scope.row.maxuploadspeed * 100" :show-text="false" color="#2673bf"></el-progress>{{speedFormat(scope.row.upspeed, 100)}}</p>
<p class="speed-line">下载速度: <el-progress :percentage="scope.row.downspeed / scope.row.maxdownloadspeed * 100" :show-text="false" color="#33cc33"></el-progress>{{speedFormat(scope.row.downspeed, 100)}}</p>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
`
})
}
})();