// ==UserScript==
// @name SOFAR Cloud 电站数据自动刷新
// @namespace codex.sofarcloud
// @version 1.0.1
// @description 定时点击指定 SOFAR Cloud 电站数据页右上角的刷新按钮
// @match https://cn.sofarcloud.com/plantDetail/plantData*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @run-at document-idle
// ==/UserScript==
(() => {
'use strict';
const TARGET_STATION_ID = '867426131215781888';
const REFRESH_ICON_HREF = '#icon-shuaxin1';
const MIN_INTERVAL_SECONDS = 5;
const DEFAULT_INTERVAL_SECONDS = 60;
if (
location.pathname !== '/plantDetail/plantData' ||
new URLSearchParams(location.search).get('stationId') !== TARGET_STATION_ID
) {
return;
}
let enabled = Boolean(GM_getValue('enabled', false));
let intervalSeconds = normalizeInterval(
GM_getValue('intervalSeconds', DEFAULT_INTERVAL_SECONDS)
);
let timerId = null;
let nextRefreshAt = 0;
let lastResult = '尚未刷新';
const panel = document.createElement('section');
panel.id = 'sofar-auto-refresh-panel';
panel.innerHTML = `
自动刷新
`;
const style = document.createElement('style');
style.textContent = `
#sofar-auto-refresh-panel {
position: fixed;
right: 18px;
bottom: 18px;
z-index: 2147483647;
width: 210px;
color: #25324b;
background: rgba(255, 255, 255, .97);
border: 1px solid #d8e1f0;
border-radius: 10px;
box-shadow: 0 8px 28px rgba(27, 53, 94, .18);
font: 14px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", sans-serif;
}
#sofar-auto-refresh-panel * { box-sizing: border-box; }
#sofar-auto-refresh-panel .sar-title {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
color: #fff;
background: #3a70d3;
border-radius: 9px 9px 0 0;
font-weight: 600;
}
#sofar-auto-refresh-panel .sar-collapse {
width: 24px;
padding: 0;
color: #fff;
background: transparent;
border: 0;
cursor: pointer;
font-size: 18px;
}
#sofar-auto-refresh-panel .sar-body { padding: 11px 12px 12px; }
#sofar-auto-refresh-panel label {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 9px;
}
#sofar-auto-refresh-panel .sar-interval {
width: 88px;
padding: 5px 7px;
border: 1px solid #cbd6e7;
border-radius: 5px;
}
#sofar-auto-refresh-panel .sar-apply,
#sofar-auto-refresh-panel .sar-refresh-now {
width: calc(50% - 4px);
padding: 6px 4px;
color: #fff;
background: #3a70d3;
border: 0;
border-radius: 5px;
cursor: pointer;
}
#sofar-auto-refresh-panel .sar-refresh-now {
margin-left: 4px;
color: #3a70d3;
background: #edf3ff;
}
#sofar-auto-refresh-panel .sar-status {
min-height: 34px;
margin-top: 9px;
color: #66748e;
font-size: 12px;
}
#sofar-auto-refresh-panel.sar-collapsed { width: 120px; }
#sofar-auto-refresh-panel.sar-collapsed .sar-title { border-radius: 9px; }
#sofar-auto-refresh-panel.sar-collapsed .sar-body { display: none; }
`;
document.documentElement.appendChild(style);
document.body.appendChild(panel);
const enabledInput = panel.querySelector('.sar-enabled');
const intervalInput = panel.querySelector('.sar-interval');
const status = panel.querySelector('.sar-status');
const collapseButton = panel.querySelector('.sar-collapse');
enabledInput.checked = enabled;
intervalInput.value = String(intervalSeconds);
panel.querySelector('.sar-apply').addEventListener('click', applySettings);
panel.querySelector('.sar-refresh-now').addEventListener('click', clickRefresh);
enabledInput.addEventListener('change', applySettings);
intervalInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') applySettings();
});
collapseButton.addEventListener('click', () => {
const collapsed = panel.classList.toggle('sar-collapsed');
collapseButton.textContent = collapsed ? '+' : '−';
collapseButton.title = collapsed ? '展开' : '收起';
});
GM_registerMenuCommand('开启/关闭自动刷新', () => {
enabledInput.checked = !enabled;
applySettings();
});
GM_registerMenuCommand('立即刷新一次', clickRefresh);
setInterval(renderStatus, 1000);
restartTimer();
renderStatus();
function normalizeInterval(value) {
const parsed = Math.floor(Number(value));
return Number.isFinite(parsed)
? Math.max(MIN_INTERVAL_SECONDS, parsed)
: DEFAULT_INTERVAL_SECONDS;
}
function applySettings() {
intervalSeconds = normalizeInterval(intervalInput.value);
enabled = enabledInput.checked;
intervalInput.value = String(intervalSeconds);
GM_setValue('enabled', enabled);
GM_setValue('intervalSeconds', intervalSeconds);
lastResult = enabled ? '设置已应用' : '自动刷新已关闭';
restartTimer();
renderStatus();
}
function restartTimer() {
if (timerId !== null) {
clearInterval(timerId);
timerId = null;
}
nextRefreshAt = 0;
if (!enabled) return;
nextRefreshAt = Date.now() + intervalSeconds * 1000;
timerId = setInterval(() => {
clickRefresh();
nextRefreshAt = Date.now() + intervalSeconds * 1000;
}, intervalSeconds * 1000);
}
function clickRefresh() {
// SOFAR Cloud 的 SVG 图标使用带命名空间的 xlink:href。
// 某些浏览器中 CSS 属性选择器无法匹配它,因此直接读取属性。
const icon = Array.from(document.querySelectorAll('use')).find((element) =>
element.getAttribute('href') === REFRESH_ICON_HREF ||
element.getAttribute('xlink:href') === REFRESH_ICON_HREF
);
const button = icon?.closest('.itemContainer');
if (!button) {
lastResult = `未找到刷新按钮(${formatTime()})`;
renderStatus();
return false;
}
button.click();
lastResult = `刷新成功(${formatTime()})`;
renderStatus();
return true;
}
function renderStatus() {
if (!enabled) {
status.textContent = lastResult;
return;
}
const remaining = Math.max(0, Math.ceil((nextRefreshAt - Date.now()) / 1000));
status.textContent = `${lastResult};下次刷新:${remaining} 秒后`;
}
function formatTime() {
return new Date().toLocaleTimeString('zh-CN', { hour12: false });
}
})();