Greasy Fork is available in English.
在极客页面中,点击设备列表,调用API获取设备和规则列表,并生成设备规则映射并显示在当前页面上
当前为
// ==UserScript==
// @name 米家极客版油猴插件
// @namespace http://tampermonkey.net/
// @version v0.7
// @description 在极客页面中,点击设备列表,调用API获取设备和规则列表,并生成设备规则映射并显示在当前页面上
// @author 王丰,sk163
// @license MIT
// @match http://*/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// ==/UserScript==
(async () => {
const callAPI = (api, params) => {
return new Promise(res => editor.gateway.callAPI(api, params, res));
};
let selectCardIds = '';
const executeScript = async () => {
// 检查是否已经存在结果容器,避免重复生成
if (document.getElementById('device-rule-map')) {
return;
}
if (typeof editor === 'undefined' || typeof editor.gateway === 'undefined' || typeof editor.gateway.callAPI === 'undefined') {
console.error('editor.gateway.callAPI 方法未定义。请确保在正确的环境中运行此脚本。');
return;
}
try {
const devListResponse = await callAPI('getDevList');
const devList = devListResponse.devList;
const ruleList = await callAPI('getGraphList');
let devRuleMap = {};
for (const rule of ruleList) {
const content = await callAPI('getGraph', {id: rule.id});
const dids = new Set(content.nodes.map(n => n.props?.did).filter(d => d !== undefined));
const cards = new Set(content.nodes.map(n => {
return (n.props && n.cfg) ? {did: n.props.did, oriId: n.cfg.oriId} : undefined;
}).filter(d => d !== undefined));
dids.forEach(d => {
devRuleMap[d] = devRuleMap[d] ?? [];
const cardIds = Array.from(cards)
.filter(card => card.did === d)
.map(card => card.oriId).join(',');
const tempDevRule={
ruleId:rule.id,
cardIds:cardIds,
totalCardNum:cards.size
};
devRuleMap[d].push(tempDevRule);
});
}
const result = Object.fromEntries(Object.entries(devRuleMap).map(([k, v]) => [
devList[k]?.name ?? `did: ${k}`,
v.map(r => {
return ({
id: r.ruleId,
cardIds: r.cardIds,
totalCardNum: r.totalCardNum,
name: ruleList.find(rr => rr.id === r.ruleId).userData.name
});
})
]));
// 创建结果容器
const container = document.createElement('div');
container.id = 'device-rule-map';
container.style.position = 'fixed';
container.style.top = '10px';
container.style.right = '10px';
container.style.width = '800px';
container.style.height = '600px';
container.style.overflowY = 'scroll';
container.style.backgroundColor = 'white';
container.style.border = '1px solid #ccc';
container.style.paddingTop = '50px';
container.style.zIndex = 10000;
container.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)';
// 创建固定的顶部栏
const topBar = document.createElement('div');
topBar.style.position = 'fixed';
topBar.style.top = '0';
topBar.style.right = '10px';
topBar.style.width = '800px';
topBar.style.height = '50px';
topBar.style.backgroundColor = 'white';
topBar.style.zIndex = 10001;
topBar.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)';
topBar.style.display = 'flex';
topBar.style.justifyContent = 'space-between';
topBar.style.alignItems = 'center';
topBar.style.padding = '0 10px';
// 创建标题
const title = document.createElement('h1');
title.style.margin = '0';
title.textContent = '设备规则映射结果';
// 创建按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.gap = '10px';
// 创建折叠按钮
const collapseButton = document.createElement('button');
collapseButton.textContent = '折叠';
collapseButton.onclick = () => {
if (container.style.height === '600px') {
container.style.height = '50px';
collapseButton.textContent = '展开';
} else {
container.style.height = '600px';
collapseButton.textContent = '折叠';
}
};
// 创建关闭按钮
const closeButton = document.createElement('button');
closeButton.textContent = '关闭';
closeButton.onclick = () => document.body.removeChild(container);
buttonContainer.appendChild(collapseButton);
buttonContainer.appendChild(closeButton);
topBar.appendChild(title);
topBar.appendChild(buttonContainer);
// 创建表格
const table = document.createElement('table');
table.border = '1';
table.cellSpacing = '0';
table.cellPadding = '5';
table.style.width = '100%';
// 表头
const thead = document.createElement('thead');
const headerRow = document.createElement('tr');
const deviceHeader = document.createElement('th');
const ruleHeader = document.createElement('th');
let deviceSortOrder = 'asc'; // 初始排序顺序
let ruleSortOrder = 'asc'; // 初始排序顺序
const updateSortMarkers = () => {
deviceHeader.innerHTML = `设备 ${deviceSortOrder === 'asc' ? '⬆️' : '⬇️'}`;
ruleHeader.innerHTML = `规则 ${ruleSortOrder === 'asc' ? '⬆️' : '⬇️'}`;
};
deviceHeader.textContent = '设备';
ruleHeader.textContent = '规则';
// 添加点击事件进行排序
deviceHeader.onclick = () => {
deviceSortOrder = deviceSortOrder === 'asc' ? 'desc' : 'asc';
sortTable(0, deviceSortOrder);
updateSortMarkers();
};
ruleHeader.onclick = () => {
ruleSortOrder = ruleSortOrder === 'asc' ? 'desc' : 'asc';
sortTable(1, ruleSortOrder);
updateSortMarkers();
};
headerRow.appendChild(deviceHeader);
headerRow.appendChild(ruleHeader);
thead.appendChild(headerRow);
table.appendChild(thead);
// 表体
const tbody = document.createElement('tbody');
Object.entries(result).forEach(([device, rules]) => {
const row = document.createElement('tr');
const deviceCell = document.createElement('td');
deviceCell.textContent = device;
const ruleCell = document.createElement('td');
// 获取当前主机名
const host = window.location.host;
// 创建规则链接
rules.forEach(rule => {
const link = document.createElement('a');
link.href = `http://${host}/#/graph/${rule.id}`;
link.target = '_self';
link.textContent = rule.name+"["+rule.cardIds.split(',').length+"/"+rule.totalCardNum+"]";
link.onclick = () => {
window.location.hash = '#/';
selectCardIds = rule.cardIds;
};
ruleCell.appendChild(link);
ruleCell.appendChild(document.createTextNode(', ')); // 添加逗号分隔符
});
ruleCell.removeChild(ruleCell.lastChild);
row.appendChild(deviceCell);
row.appendChild(ruleCell);
tbody.appendChild(row);
});
table.appendChild(tbody);
container.appendChild(topBar);
container.appendChild(table);
document.body.appendChild(container);
// 排序函数
function sortTable(columnIndex, sortOrder) {
const rows = Array.from(tbody.rows);
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[columnIndex].textContent;
const bText = b.cells[columnIndex].textContent;
if (sortOrder === 'asc') {
return aText.localeCompare(bText);
} else {
return bText.localeCompare(aText);
}
});
// 清空并重新添加排序后的行
tbody.innerHTML = '';
sortedRows.forEach(row => tbody.appendChild(row));
}
// 初始化排序标记
updateSortMarkers();
} catch (error) {
console.error('调用 API 时出错:', error);
}
};
const selectDevices = async () => {
await sleep(1000);
const cardIds = selectCardIds.split(',');
for (const cardId of cardIds) {
if (cardId.trim() !== '') {
let targetElement = document.querySelector("#" + cardId.trim() + " > div > div");
if (targetElement) {
targetElement.style.backgroundColor = '#F08080';
}
}
}
selectCardIds = '';
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
};
function isMiJiaJiKePage(){
return document.title==="米家自动化极客版";
}
// 检查初始的 hash 值
if (isMiJiaJiKePage() && window.location.hash === '#/device') {
executeScript();
}
// 监听 hash 值变化
window.addEventListener('hashchange', () => {
if (isMiJiaJiKePage() && window.location.hash === '#/device') {
executeScript();
}
if (isMiJiaJiKePage() && window.location.hash.match(/^#\/graph\/.*/g) ) {
selectDevices();
}
});
})();