diff --git a/baidudrive/Makefile b/baidudrive/Makefile index e08e4075..37ac6339 100644 --- a/baidudrive/Makefile +++ b/baidudrive/Makefile @@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk PKG_ARCH_BAIDUDRIVE:=$(ARCH) PKG_NAME:=baidudrive -PKG_VERSION:=baidudrive-runtime-v1.0.4 -PKG_RELEASE:=5 +PKG_VERSION:=linkease-runtime-v1.7.5 +PKG_RELEASE:=6 PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/baidudrive-runtime-v$(PKG_VERSION)/ PKG_HASH:=skip diff --git a/dae/Makefile b/dae/Makefile index 39c2ea5a..2b7beb7b 100644 --- a/dae/Makefile +++ b/dae/Makefile @@ -5,10 +5,10 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dae -PKG_VERSION:=2026.08.03 -PKG_RELEASE:=30 +PKG_VERSION:=2026.08.05 +PKG_RELEASE:=31 -PKG_SOURCE:=dae-src-2026.08.03-354bb6787b4e.tar.gz +PKG_SOURCE:=dae-src-2026.08.05-03ce5c39b38c.tar.gz PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_HASH:=skip diff --git a/daed/Makefile b/daed/Makefile index 8b94e6ae..292c904d 100644 --- a/daed/Makefile +++ b/daed/Makefile @@ -5,10 +5,10 @@ include $(TOPDIR)/rules.mk PKG_NAME:=daed -PKG_VERSION:=2026.08.03 -PKG_RELEASE:=41 +PKG_VERSION:=2026.08.05 +PKG_RELEASE:=42 -PKG_SOURCE:=daed-src-2026.08.03-5f7939adc12a.tar.gz +PKG_SOURCE:=daed-src-2026.08.05-ebb45ff28107.tar.gz PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_HASH:=skip diff --git a/dnsproxy/Makefile b/dnsproxy/Makefile index ebff35d7..7d5bb34a 100644 --- a/dnsproxy/Makefile +++ b/dnsproxy/Makefile @@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dnsproxy -PKG_VERSION:=0.83.1 -PKG_RELEASE:=14 +PKG_VERSION:=0.83.2 +PKG_RELEASE:=15 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/AdguardTeam/dnsproxy/tar.gz/v$(PKG_VERSION)? diff --git a/gowebdav/Makefile b/gowebdav/Makefile index cd5a19f6..3f34a641 100644 --- a/gowebdav/Makefile +++ b/gowebdav/Makefile @@ -6,8 +6,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=gowebdav -PKG_VERSION:=5.14.1 -PKG_RELEASE:=18 +PKG_VERSION:=5.14.2 +PKG_RELEASE:=19 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/hacdias/webdav/tar.gz/v$(PKG_VERSION)? diff --git a/images/screenshot_19-5-2025_82421.jpeg b/images/screenshot_19-5-2025_82421.jpeg new file mode 100644 index 00000000..bf03b562 Binary files /dev/null and b/images/screenshot_19-5-2025_82421.jpeg differ diff --git a/images/screenshot_19-5-2025_82438.jpeg b/images/screenshot_19-5-2025_82438.jpeg new file mode 100644 index 00000000..97927360 Binary files /dev/null and b/images/screenshot_19-5-2025_82438.jpeg differ diff --git a/images/screenshot_19-5-2025_82457.jpeg b/images/screenshot_19-5-2025_82457.jpeg new file mode 100644 index 00000000..2d72fd8b Binary files /dev/null and b/images/screenshot_19-5-2025_82457.jpeg differ diff --git a/luci-app-accesscontrol-plus/Makefile b/luci-app-accesscontrol-plus/Makefile index 5578e262..c67f3b67 100644 --- a/luci-app-accesscontrol-plus/Makefile +++ b/luci-app-accesscontrol-plus/Makefile @@ -12,7 +12,7 @@ LUCI_PKGARCH:=all PKG_NAME:=luci-app-accesscontrol-plus PKG_VERSION:=1 -PKG_RELEASE:=1 +PKG_RELEASE:=2 include $(TOPDIR)/feeds/luci/luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-accesscontrol-plus/luasrc/controller/miaplus.lua b/luci-app-accesscontrol-plus/luasrc/controller/miaplus.lua index b6dd744f..ae5454a0 100644 --- a/luci-app-accesscontrol-plus/luasrc/controller/miaplus.lua +++ b/luci-app-accesscontrol-plus/luasrc/controller/miaplus.lua @@ -15,7 +15,7 @@ end function act_status() local e = {} - e.running = luci.sys.call("iptables -L INPUT |grep MIAPLUS >/dev/null") == 0 + e.running = luci.sys.call("iptables -t filter -S MIAPLUS >/dev/null 2>&1") == 0 luci.http.prepare_content("application/json") luci.http.write_json(e) end diff --git a/luci-app-accesscontrol-plus/luasrc/model/cbi/advanced.lua b/luci-app-accesscontrol-plus/luasrc/model/cbi/advanced.lua index 4244fecb..346da7a2 100644 --- a/luci-app-accesscontrol-plus/luasrc/model/cbi/advanced.lua +++ b/luci-app-accesscontrol-plus/luasrc/model/cbi/advanced.lua @@ -1,6 +1,10 @@ local ds = require "luci.dispatcher" a = Map("miaplus") +a.apply_on_parse = true +function a.on_apply(self) + luci.sys.call("/etc/init.d/miaplus restart >/dev/null 2>&1 &") +end t = a:section(TypedSection, "templates") t.template = "cbi/tblsection" diff --git a/luci-app-accesscontrol-plus/luasrc/model/cbi/base.lua b/luci-app-accesscontrol-plus/luasrc/model/cbi/base.lua index af0849d8..d53e4342 100644 --- a/luci-app-accesscontrol-plus/luasrc/model/cbi/base.lua +++ b/luci-app-accesscontrol-plus/luasrc/model/cbi/base.lua @@ -3,6 +3,10 @@ local uci = luci.model.uci.cursor() a = Map("miaplus") a.title = translate("Internet Access Schedule Control Plus") a.description = translate("Access Schedule Control Description") +a.apply_on_parse = true +function a.on_apply(self) + luci.sys.call("/etc/init.d/miaplus restart >/dev/null 2>&1 &") +end a:section(SimpleSection).template = "miaplus/miaplus_status" diff --git a/luci-app-accesscontrol-plus/luasrc/model/cbi/template.lua b/luci-app-accesscontrol-plus/luasrc/model/cbi/template.lua index 618f6946..d931e842 100644 --- a/luci-app-accesscontrol-plus/luasrc/model/cbi/template.lua +++ b/luci-app-accesscontrol-plus/luasrc/model/cbi/template.lua @@ -1,4 +1,8 @@ a = Map("miaplus") +a.apply_on_parse = true +function a.on_apply(self) + luci.sys.call("/etc/init.d/miaplus restart >/dev/null 2>&1 &") +end local section = arg[1] diff --git a/luci-app-accesscontrol-plus/po/zh_Hans/miaplus.po b/luci-app-accesscontrol-plus/po/zh_Hans/miaplus.po index ad851754..2e88d6b8 100644 --- a/luci-app-accesscontrol-plus/po/zh_Hans/miaplus.po +++ b/luci-app-accesscontrol-plus/po/zh_Hans/miaplus.po @@ -1,9 +1,15 @@ msgid "Internet Access Schedule Control Plus" -msgstr "上网时间控制Plus" +msgstr "上网时间控制增强版" msgid "Access Schedule Control Description" msgstr "设置客户端访问互联网的时间" +msgid "Base Setting" +msgstr "基础设置" + +msgid "Advance Setting" +msgstr "高级设置" + msgid "General switch" msgstr "开启/关闭" @@ -19,6 +25,9 @@ msgstr "严格模式" msgid "Strict Mode will degrade CPU performance, but it can achieve better results" msgstr "严格模式会损耗部分CPU资源,但可以按照时间规则立即拦截数据包,效果更好" +msgid "IPV6 Enabled" +msgstr "启用 IPV6" + msgid "Client Rules" msgstr "客户端规则" diff --git a/luci-app-accesscontrol-plus/root/etc/init.d/miaplus b/luci-app-accesscontrol-plus/root/etc/init.d/miaplus index c0426b46..03d370b4 100755 --- a/luci-app-accesscontrol-plus/root/etc/init.d/miaplus +++ b/luci-app-accesscontrol-plus/root/etc/init.d/miaplus @@ -10,6 +10,8 @@ START=30 CONFIG=miaplus +. /lib/functions.sh + uci_export_section_name() { local ret=$(uci -n export $CONFIG | grep "config $1" | awk '{print $3}' | awk -F\' '{print $2}') echo ${ret:=$2} @@ -50,23 +52,18 @@ uci_get_mac_by_template(){ } add_rules(){ - local sections=$(uci_export_section_name templates '') - if [ -n "$sections" ]; then - for s in $sections - do - local enable=$(uci_get_by_name $s enable '' $i) - local title=$(uci_get_by_name $s title '' $i) - if [ -z $enable ]; then - break - fi - if [ "$enable" == "1" ]; then - add_rule $s - fi - done - fi + config_load "$CONFIG" + config_foreach add_template_rule templates +} +add_template_rule(){ + local section="$1" + local enable + + config_get enable "$section" enable 0 + [ "$enable" = "1" ] && add_rule "$section" } add_rule(){ - ipv6enable=$(uci get miaplus.@basic[0].ipv6enable) + ipv6enable=$(uci -q get miaplus.@basic[0].ipv6enable) local macaddrs=$(uci_get_mac_by_template $1 '') if [ -z "$macaddrs" ]; then exit 0 @@ -74,7 +71,7 @@ add_rule(){ for macaddr in $macaddrs do iptables -t filter -A MIAPLUS -m mac --mac-source $macaddr -j DROP - [ $ipv6enable -eq 1 ] && ip6tables -t filter -A MIAPLUS -m mac --mac-source $macaddr -j DROP + [ "${ipv6enable:-0}" -eq 1 ] && ip6tables -t filter -A MIAPLUS -m mac --mac-source $macaddr -j DROP done for i in $(seq 0 100) do @@ -103,7 +100,7 @@ add_rule(){ for macaddr in $macaddrs do iptables -t filter -I MIAPLUS -m mac --mac-source $macaddr -m time --kerneltz --timestart $timeon --timestop $timeoff --weekdays $Z1$Z2$Z3$Z4$Z5$Z6$Z7 -j ACCEPT - [ $ipv6enable -eq 1 ] && ip6tables -t filter -I MIAPLUS -m mac --mac-source $macaddr -m time --kerneltz --timestart $timeon --timestop $timeoff --weekdays $Z1$Z2$Z3$Z4$Z5$Z6$Z7 -j ACCEPT + [ "${ipv6enable:-0}" -eq 1 ] && ip6tables -t filter -I MIAPLUS -m mac --mac-source $macaddr -m time --kerneltz --timestart $timeon --timestop $timeoff --weekdays $Z1$Z2$Z3$Z4$Z5$Z6$Z7 -j ACCEPT done fi for n in $(seq 1 7) @@ -126,27 +123,36 @@ del_rule(){ start(){ stop - enable=$(uci get miaplus.@basic[0].enable) - [ $enable -eq 0 ] && exit 0 + enable=$(uci -q get miaplus.@basic[0].enable) + [ "${enable:-0}" -eq 0 ] && exit 0 iptables -t filter -N MIAPLUS iptables -I INPUT -p udp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS iptables -I INPUT -p tcp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control" iptables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control" - strict=$(uci get miaplus.@basic[0].strict) - [ $strict -eq 1 ] && iptables -t filter -I FORWARD -m comment --comment "Rule For Control" -j MIAPLUS + strict=$(uci -q get miaplus.@basic[0].strict) + [ "${strict:-0}" -eq 1 ] && iptables -t filter -I FORWARD -m comment --comment "Rule For Control" -j MIAPLUS - ipv6enable=$(uci get miaplus.@basic[0].ipv6enable) - if [ "$ipv6enable" -eq 1 ]; then + ipv6enable=$(uci -q get miaplus.@basic[0].ipv6enable) + if [ "${ipv6enable:-0}" -eq 1 ]; then ip6tables -t filter -N MIAPLUS ip6tables -I INPUT -p udp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS ip6tables -I INPUT -p tcp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS ip6tables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control" ip6tables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control" - [ $strict -eq 1 ] && ip6tables -t filter -I FORWARD -m comment --comment "Rule For Control" -j MIAPLUS + [ "${strict:-0}" -eq 1 ] && ip6tables -t filter -I FORWARD -m comment --comment "Rule For Control" -j MIAPLUS fi add_rules } +status(){ + if iptables -t filter -S MIAPLUS >/dev/null 2>&1; then + echo "running" + return 0 + fi + + echo "not running" + return 1 +} stop(){ iptables -t filter -D FORWARD -m comment --comment "Rule For Control" -j MIAPLUS 2>/dev/null iptables -D INPUT -p udp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS 2>/dev/null diff --git a/luci-app-fancontrol/Makefile b/luci-app-fancontrol/Makefile index d3228cbf..8514cdae 100644 --- a/luci-app-fancontrol/Makefile +++ b/luci-app-fancontrol/Makefile @@ -15,7 +15,7 @@ PKG_MAINTAINER:=JiaY-shi PKG_NAME:=luci-app-fancontrol PKG_VERSION:=2.0.0 -PKG_RELEASE:=2 +PKG_RELEASE:=3 include $(TOPDIR)/feeds/luci/luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-fancontrol/htdocs/luci-static/resources/view/fancontrol.css b/luci-app-fancontrol/htdocs/luci-static/resources/view/fancontrol.css index 76e3a15c..fa77bb07 100644 --- a/luci-app-fancontrol/htdocs/luci-static/resources/view/fancontrol.css +++ b/luci-app-fancontrol/htdocs/luci-static/resources/view/fancontrol.css @@ -70,24 +70,38 @@ text-overflow: ellipsis; } -.fancontrol-level { - height: 4px; - margin-top: .35rem; - overflow: hidden; - background: rgba(127, 127, 127, .2); - border-radius: 3px; +.fancontrol-chart-wrap { + height: 72px; + margin-top: .4rem; } -.fancontrol-level > div { +.fancontrol-chart { + display: block; + width: 100%; height: 100%; - background: var(--primary-color-medium, #1976d2); - border-radius: 3px; - transition: width .25s ease-in; + color: var(--text-color-medium, #666); + background: rgba(127, 127, 127, .025); + border: 1px solid rgba(127, 127, 127, .24); +} + +.fancontrol-chart-temperature { + --fancontrol-chart-line: #c74b45; + --fancontrol-chart-fill: rgba(199, 75, 69, .2); +} + +.fancontrol-chart-pwm { + --fancontrol-chart-line: #1976d2; + --fancontrol-chart-fill: rgba(25, 118, 210, .2); +} + +.fancontrol-chart-rpm { + --fancontrol-chart-line: #2f8a57; + --fancontrol-chart-fill: rgba(47, 138, 87, .2); } .fancontrol-details { display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .3rem 1rem; padding: .42rem .7rem; background: rgba(127, 127, 127, .04); @@ -102,10 +116,6 @@ min-width: 0; } -.fancontrol-detail:last-child { - grid-column: 1 / -1; -} - .fancontrol-detail-value { min-width: 0; overflow-wrap: anywhere; @@ -133,9 +143,6 @@ grid-template-columns: 1fr; } - .fancontrol-detail:last-child { - grid-column: auto; - } } @media (max-width: 420px) { diff --git a/luci-app-fancontrol/htdocs/luci-static/resources/view/fancontrol.js b/luci-app-fancontrol/htdocs/luci-static/resources/view/fancontrol.js index 887c366e..ba92d465 100644 --- a/luci-app-fancontrol/htdocs/luci-static/resources/view/fancontrol.js +++ b/luci-app-fancontrol/htdocs/luci-static/resources/view/fancontrol.js @@ -14,6 +14,11 @@ const callServiceList = rpc.declare({ expect: { '': {} } }); +const HISTORY_WINDOW_MS = 2 * 60 * 1000; +const TIME_GRID_INTERVAL_MS = 10 * 1000; +const TIME_LABEL_INTERVAL_MS = 30 * 1000; +const VALUE_GRID_DIVISIONS = 4; + function serviceRunning(result) { const instances = result?.fancontrol?.instances || {}; @@ -35,20 +40,173 @@ function parseStatus(text) { return status; } -function statusMetric(title, value, unit, level) { +function validStatusNumber(value) { + const number = Number(value); + + return Number.isFinite(number) && number >= 0 ? number : null; +} + +function appendHistory(history, running, status) { + const now = Date.now(); + const temperature = validStatusNumber(status.temperature_mc); + const pwm = validStatusNumber(status.pwm); + const rpm = validStatusNumber(status.rpm); + + history.push({ + time: now, + temperature: running && temperature != null ? temperature / 1000 : null, + pwm: running && pwm != null ? pwm * 100 / 255 : null, + rpm: running ? rpm : null + }); + + while (history.length && history[0].time < now - HISTORY_WINDOW_MS) + history.shift(); +} + +function chartScale(history, key, minimumMax, step) { + let maximum = minimumMax; + + history.forEach(function(sample) { + if (sample[key] != null) + maximum = Math.max(maximum, sample[key]); + }); + + return Math.ceil(maximum / step) * step; +} + +function drawChart(canvas, history, key, options) { + const style = window.getComputedStyle(canvas); + const width = Math.max(canvas.clientWidth, 1); + const height = Math.max(canvas.clientHeight, 1); + const ratio = Math.min(window.devicePixelRatio || 1, 2); + const context = canvas.getContext('2d'); + const padding = { left: 28, right: 5, top: 6, bottom: 14 }; + const plotWidth = Math.max(width - padding.left - padding.right, 1); + const plotHeight = Math.max(height - padding.top - padding.bottom, 1); + const plotBottom = padding.top + plotHeight; + const now = history.length ? history[history.length - 1].time : Date.now(); + const start = now - HISTORY_WINDOW_MS; + const maximum = chartScale(history, key, options.minimumMax, options.step); + const lineColor = style.getPropertyValue('--fancontrol-chart-line').trim() || '#1976d2'; + const fillColor = style.getPropertyValue('--fancontrol-chart-fill').trim() || 'rgba(25, 118, 210, .2)'; + const labelColor = style.color || '#666'; + const gridColor = 'rgba(127, 127, 127, .2)'; + let segment = []; + + canvas.width = Math.round(width * ratio); + canvas.height = Math.round(height * ratio); + context.setTransform(ratio, 0, 0, ratio, 0, 0); + context.clearRect(0, 0, width, height); + + context.strokeStyle = gridColor; + context.lineWidth = 1; + context.beginPath(); + for (let elapsed = 0; elapsed <= HISTORY_WINDOW_MS; elapsed += TIME_GRID_INTERVAL_MS) { + const x = padding.left + plotWidth * elapsed / HISTORY_WINDOW_MS; + + context.moveTo(x, padding.top); + context.lineTo(x, plotBottom); + } + for (let division = 0; division <= VALUE_GRID_DIVISIONS; division++) { + const y = padding.top + plotHeight * division / VALUE_GRID_DIVISIONS; + + context.moveTo(padding.left, y); + context.lineTo(padding.left + plotWidth, y); + } + context.stroke(); + + context.fillStyle = labelColor; + context.font = '9px sans-serif'; + context.textAlign = 'right'; + context.textBaseline = 'top'; + context.fillText(options.format(maximum), padding.left - 4, padding.top - 1); + context.textBaseline = 'bottom'; + context.fillText(options.format(0), padding.left - 4, plotBottom + 1); + context.textBaseline = 'middle'; + context.fillText(options.format(maximum / 2), padding.left - 4, + padding.top + plotHeight / 2); + + context.textBaseline = 'bottom'; + for (let elapsed = 0; elapsed <= HISTORY_WINDOW_MS; elapsed += TIME_LABEL_INTERVAL_MS) { + const x = padding.left + plotWidth * elapsed / HISTORY_WINDOW_MS; + const remaining = (HISTORY_WINDOW_MS - elapsed) / 1000; + + context.textAlign = elapsed === 0 ? 'left' : + elapsed === HISTORY_WINDOW_MS ? 'right' : 'center'; + context.fillText(remaining ? '-%ds'.format(remaining) : '0', x, height); + } + + function point(sample) { + return { + x: padding.left + (sample.time - start) / HISTORY_WINDOW_MS * plotWidth, + y: plotBottom - sample[key] / maximum * plotHeight + }; + } + + function drawSegment() { + if (!segment.length) + return; + + if (segment.length > 1) { + context.beginPath(); + context.moveTo(segment[0].x, plotBottom); + segment.forEach(function(item) { context.lineTo(item.x, item.y); }); + context.lineTo(segment[segment.length - 1].x, plotBottom); + context.closePath(); + context.fillStyle = fillColor; + context.fill(); + + context.beginPath(); + segment.forEach(function(item, index) { + if (index) + context.lineTo(item.x, item.y); + else + context.moveTo(item.x, item.y); + }); + context.strokeStyle = lineColor; + context.lineWidth = 1.5; + context.lineJoin = 'round'; + context.stroke(); + } else { + context.beginPath(); + context.arc(segment[0].x, segment[0].y, 2, 0, Math.PI * 2); + context.fillStyle = lineColor; + context.fill(); + } + + segment = []; + } + + history.forEach(function(sample) { + if (sample[key] == null) { + drawSegment(); + return; + } + + segment.push(point(sample)); + }); + drawSegment(); +} + +function statusMetric(title, value, unit, chartClass) { + const canvas = E('canvas', { + 'class': 'fancontrol-chart fancontrol-chart-%s'.format(chartClass), + 'role': 'img', + 'aria-label': title + }); const children = [ E('span', { 'class': 'fancontrol-metric-label' }, title), E('div', { 'class': 'fancontrol-metric-reading' }, [ E('span', { 'class': 'fancontrol-metric-value' }, value), unit ? E('span', { 'class': 'fancontrol-metric-unit' }, unit) : '' - ]) + ]), + E('div', { 'class': 'fancontrol-chart-wrap' }, canvas) ]; - if (level != null) - children.push(E('div', { 'class': 'fancontrol-level', 'aria-hidden': 'true' }, - E('div', { 'style': 'width:%d%%'.format(level) }))); - - return E('div', { 'class': 'fancontrol-metric' }, children); + return { + node: E('div', { 'class': 'fancontrol-metric' }, children), + canvas: canvas + }; } function statusDetail(title, value) { @@ -58,7 +216,7 @@ function statusDetail(title, value) { ]); } -function renderStatus(node, running, status) { +function renderStatus(node, running, status, history) { const temp = Number(status.temperature_mc); const pwm = Number(status.pwm); const rpm = Number(status.rpm); @@ -69,6 +227,12 @@ function renderStatus(node, running, status) { const fault = status.error ? E('span', { 'class': 'label warning' }, status.error) : E('span', { 'class': 'fancontrol-ok' }, _('None')); + const temperatureMetric = statusMetric(_('Temperature'), + tempValid ? '%.1f'.format(temp / 1000) : '-', tempValid ? '°C' : '', 'temperature'); + const pwmMetric = statusMetric(_('PWM output'), pwmValid ? String(pwm) : '-', + pwmValid ? '/ 255 · %d%%'.format(pwmPercent) : '', 'pwm'); + const rpmMetric = statusMetric(_('Fan speed'), rpmValid ? String(rpm) : '-', + rpmValid ? 'RPM' : '', 'rpm'); dom.content(node, E('div', { 'class': 'fancontrol-status' }, [ E('div', { 'class': 'fancontrol-status-head' }, [ @@ -77,11 +241,9 @@ function renderStatus(node, running, status) { running ? _('Running') : _('Stopped')) ]), E('div', { 'class': 'fancontrol-metrics' }, [ - statusMetric(_('Temperature'), tempValid ? '%.1f'.format(temp / 1000) : '-', - tempValid ? '°C' : ''), - statusMetric(_('PWM output'), pwmValid ? String(pwm) : '-', - pwmValid ? '/ 255 · %d%%'.format(pwmPercent) : '', pwmPercent), - statusMetric(_('Fan speed'), rpmValid ? String(rpm) : '-', rpmValid ? 'RPM' : '') + temperatureMetric.node, + pwmMetric.node, + rpmMetric.node ]), E('div', { 'class': 'fancontrol-details' }, [ statusDetail(_('Temperature input'), status.thermal_file || '-'), @@ -89,6 +251,22 @@ function renderStatus(node, running, status) { statusDetail(_('Fault'), fault) ]) ])); + + drawChart(temperatureMetric.canvas, history, 'temperature', { + minimumMax: 100, + step: 10, + format: function(value) { return '%d°'.format(value); } + }); + drawChart(pwmMetric.canvas, history, 'pwm', { + minimumMax: 100, + step: 100, + format: function(value) { return '%d%%'.format(value); } + }); + drawChart(rpmMetric.canvas, history, 'rpm', { + minimumMax: 1000, + step: 500, + format: function(value) { return String(value); } + }); } function validateAutoPath(sectionId, value) { @@ -114,6 +292,7 @@ return view.extend({ s.anonymous = true; s.addremove = false; s.render = function() { + const history = []; const node = E('div', { 'class': 'cbi-section fancontrol-runtime' }, [ E('link', { 'rel': 'stylesheet', @@ -128,7 +307,11 @@ return view.extend({ L.resolveDefault(callServiceList('fancontrol'), {}), L.resolveDefault(fs.read_direct('/var/run/fancontrol.status'), '') ]).then(function(result) { - renderStatus(statusNode, serviceRunning(result[0]), parseStatus(result[1])); + const running = serviceRunning(result[0]); + const status = parseStatus(result[1]); + + appendHistory(history, running, status); + renderStatus(statusNode, running, status, history); }); }, 5); diff --git a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js index 430fbb6e..193a7360 100644 --- a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js +++ b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js @@ -890,14 +890,6 @@ return view.extend({ so.depends('type', 'zerotier'); so.modalonly = true; - so = ss.taboption('field_general', form.ListValue, 'zerotier_ipstack', _('IP stack')); - so.value('', _('Keep default')); - so.value('auto', _('Auto')); - so.value('gvisor', _('gVisor')); - so.value('mips', _('mihomo IP stack (MIPS)')); - so.depends('type', 'zerotier'); - so.modalonly = true; - /* WireGuard fields */ so = ss.taboption('field_general', hm.GenValue, 'wireguard_private_key', _('Private key'), _('WireGuard requires base64-encoded private keys.')); @@ -1048,6 +1040,14 @@ return view.extend({ so.modalonly = true; /* Extra fields */ + so = ss.taboption('field_general', form.ListValue, 'ipstack', _('IP stack')); + so.value('', _('Keep default')); + so.value('auto', _('Auto')); + so.value('gvisor', _('gVisor')); + so.value('mips', _('mihomo IP stack (MIPS)')); + so.depends({type: /^(zerotier|wireguard|masque|openvpn)$/}); + so.modalonly = true; + so = ss.taboption('field_general', form.ListValue, 'congestion_controller', _('Congestion controller')); so.default = hm.congestion_controller[0][0]; hm.congestion_controller.forEach((res) => { @@ -1055,7 +1055,7 @@ return view.extend({ }) so.depends({type: /^(tuic|shadowquic|trusttunnel)$/}); so.depends({type: 'masque', masque_network: /^(|h3-l4proxy)$/}); - so.depends({type: 'zerotier', zerotier_ipstack: /^(auto|mips)$/}); // not empty not gvisor + so.depends({ipstack: /^(auto|mips)$/}); // not empty not gvisor so.modalonly = true; so = ss.taboption('field_general', form.ListValue, 'bbr_profile', _('BBR profile')); diff --git a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node index 515c6ffa..078a2c78 100755 --- a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node +++ b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node @@ -163,6 +163,14 @@ migrate() { config_list_foreach "$1" masque_dns add_list "$CONF" "$1" endpoint_dns uci_remove "$CONF" "$1" masque_dns fi + + # zerotier_ipstack -> ipstack + if isDefined "$1" zerotier_ipstack; then + local zerotier_ipstack + config_get zerotier_ipstack "$1" zerotier_ipstack "" + uci_remove "$CONF" "$1" zerotier_ipstack + uci_set "$CONF" "$1" ipstack "$zerotier_ipstack" + fi } config_foreach migrate node diff --git a/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc b/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc index cd19da27..7d6b96af 100644 --- a/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc +++ b/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc @@ -652,10 +652,6 @@ uci.foreach(uciconf, ucinode, (cfg) => { return orbits; })[0] }), - "ip-stack": cfg.zerotier_ipstack ? { - mode: cfg.zerotier_ipstack, - "congestion-controller": replace(cfg.congestion_controller, 'new_reno', 'reno') - } : null, /* WireGuard */ "pre-shared-key": cfg.wireguard_pre_shared_key, @@ -680,10 +676,14 @@ uci.foreach(uciconf, ucinode, (cfg) => { "host-key": cfg.ssh_host_key, /* Extra fields */ + "ip-stack": cfg.ipstack ? { + mode: cfg.ipstack, + "congestion-controller": replace(cfg.congestion_controller, 'new_reno', 'reno') + } : null, "udp-over-stream": strToBool(cfg.shadowquic_udp_over_stream || cfg.tuic_udp_over_stream), "heartbeat-interval": strToInt(cfg.tuic_heartbeat) || null, "keep-alive-interval": strToInt(cfg.shadowquic_heartbeat) || null, - "congestion-controller": cfg.type in ['zerotier'] ? null : cfg.congestion_controller, + "congestion-controller": cfg.type in ['zerotier', 'wireguard', 'masque', 'openvpn'] ? null : cfg.congestion_controller, "bbr-profile": cfg.bbr_profile, "max-open-streams": strToInt(cfg.max_open_streams) || null, diff --git a/luci-theme-aurora/Makefile b/luci-theme-aurora/Makefile index 214b0885..b1dca675 100644 --- a/luci-theme-aurora/Makefile +++ b/luci-theme-aurora/Makefile @@ -9,7 +9,7 @@ LUCI_TITLE:=Aurora Theme (A modern browser theme built with Vite and Tailwind CS LUCI_DEPENDS:=+luci-base PKG_VERSION:=1.1.13 -PKG_RELEASE:=64 +PKG_RELEASE:=65 PKG_LICENSE:=Apache-2.0 LUCI_MINIFY_CSS:= diff --git a/luci-theme-aurora/README.md b/luci-theme-aurora/README.md index 3d122cb6..b3b339c6 100644 --- a/luci-theme-aurora/README.md +++ b/luci-theme-aurora/README.md @@ -64,6 +64,8 @@ wget -qO- https://openwrt.eamonxg.fun/install.sh | sh That is the whole installation — the script adds the feed and installs whatever you tick from the list it shows. Upgrade later with the usual commands: `apk update && apk upgrade luci-theme-aurora`, or `opkg update && opkg upgrade luci-theme-aurora`. Details: [openwrt.eamonxg.fun](https://openwrt.eamonxg.fun/). +> **apk**: if this package was previously installed from a downloaded `.apk` file, that pinned it in `/etc/apk/world` and `apk upgrade` will silently do nothing — success reported, version unchanged. Run `apk add luci-theme-aurora` once (the name, no path) to clear the pin. The script above already does this for you. + ### Via a GitHub release OpenWrt 25.12+ and snapshots use `apk`; other versions use `opkg`: diff --git a/luci-theme-aurora/README_zh.md b/luci-theme-aurora/README_zh.md index 9ac92cb8..74c53a8b 100644 --- a/luci-theme-aurora/README_zh.md +++ b/luci-theme-aurora/README_zh.md @@ -64,6 +64,8 @@ wget -qO- https://openwrt.eamonxg.fun/install.sh | sh 这就是全部安装步骤——脚本会添加软件源,并安装您从列表中勾选的软件包。之后用常规命令升级即可:`apk update && apk upgrade luci-theme-aurora`,或 `opkg update && opkg upgrade luci-theme-aurora`。详细信息见 [openwrt.eamonxg.fun](https://openwrt.eamonxg.fun/)。 +> **apk**:若此前是从下载的 `.apk` 文件安装的,该文件会把软件包钉死在 `/etc/apk/world` 里,此后 `apk upgrade` 会静默地什么都不做——报告成功,版本却没变。执行一次 `apk add luci-theme-aurora`(只写包名,不带路径)即可解除。上面的脚本已自动处理这一步。 + ### 通过 GitHub Release OpenWrt 25.12+ 和 Snapshot 版本使用 `apk`;其他版本使用 `opkg`: diff --git a/luci-theme-footstrap/Makefile b/luci-theme-footstrap/Makefile index 10242b03..98ae39f5 100644 --- a/luci-theme-footstrap/Makefile +++ b/luci-theme-footstrap/Makefile @@ -17,7 +17,7 @@ LUCI_NAME:=luci-theme-footstrap FOOTSTRAP_VERSION?= ifneq ($(FOOTSTRAP_VERSION),) PKG_VERSION:=$(FOOTSTRAP_VERSION) -PKG_RELEASE:=5 +PKG_RELEASE:=6 endif LUCI_TITLE:=Footstrap Theme diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-sheets.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-sheets.js index 17dd5b6a..04d61ea3 100644 --- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-sheets.js +++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-sheets.js @@ -519,10 +519,14 @@ function silence(el) { * A foreign sheet is injected by ONE page and has no business painting any other. Before this, an * invasive sheet made the whole document spent (documentPoisoned) and the SPA fell back to a full * load on the way OUT — correct, and paid by ORDINARY pages: `luci-app-filemanager`, a stock app, - * calls its `insertCss()` at MODULE EVAL and lands two