// ==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 }); } })();