터치 패널 상태 카드 재배치
This commit is contained in:
1 parent
d10682708e
commit
637739386e
2 files changed
+252
-168
No files matched your search
+244
-166
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import time
|
||||
import socket
|
||||
import os
|
||||
import shutil
|
||||
from collections import deque
|
||||
from pathlib import Path
|
||||
|
||||
@@ -27,8 +28,13 @@ NET_GROUPS = {
|
||||
"wifi5": ["wlan1"],
|
||||
}
|
||||
|
||||
PC_IP = "192.168.50.73"
|
||||
PC_MAC = "74:56:3C:CD:CD:79"
|
||||
RPI_SERVICE_UNITS = [
|
||||
("rpi_docker", "docker.service"),
|
||||
("rpi_nginx", "nginx.service"),
|
||||
("rpi_mariadb", "mariadb.service"),
|
||||
("rpi_control", "control-websocket.service"),
|
||||
("rpi_fanpolicy", "fanpanel-apply.service"),
|
||||
]
|
||||
SYNOLOGY_PING_HOST = "chaegeon.com"
|
||||
SYNOLOGY_SSH = [
|
||||
"/usr/bin/ssh",
|
||||
@@ -66,6 +72,32 @@ while :; do
|
||||
$3 == "sata2" {printf "SATA2_READS=%s\nSATA2_WRITES=%s\n", $6, $10}
|
||||
$3 == "sata4" {printf "SATA4_READS=%s\nSATA4_WRITES=%s\n", $6, $10}
|
||||
' /proc/diskstats
|
||||
awk '{printf "LOAD1=%s\nLOAD5=%s\nLOAD15=%s\n", $1, $2, $3}' /proc/loadavg
|
||||
awk '
|
||||
/MemTotal:/ {mem_total=$2}
|
||||
/MemAvailable:/ {mem_avail=$2}
|
||||
/SwapTotal:/ {swap_total=$2}
|
||||
/SwapFree:/ {swap_free=$2}
|
||||
END {
|
||||
if (mem_total > 0) printf "MEM_USED_PCT=%.0f\n", (mem_total - mem_avail) * 100 / mem_total
|
||||
if (swap_total > 0) printf "SWAP_USED_PCT=%.0f\n", (swap_total - swap_free) * 100 / swap_total
|
||||
else printf "SWAP_USED_PCT=0\n"
|
||||
}
|
||||
' /proc/meminfo
|
||||
awk '{printf "UPTIME=%d\n", $1}' /proc/uptime
|
||||
df -P / /volume1 /volume2 2>/dev/null | awk '
|
||||
NR > 1 {
|
||||
key = ""
|
||||
if ($6 == "/") key = "ROOT"
|
||||
if ($6 == "/volume1") key = "VOL1"
|
||||
if ($6 == "/volume2") key = "VOL2"
|
||||
if (key != "") {
|
||||
pct = $5
|
||||
gsub("%", "", pct)
|
||||
printf "%s_FS_PCT=%s\n", key, pct
|
||||
}
|
||||
}
|
||||
'
|
||||
echo END=1
|
||||
sleep 0.1
|
||||
done
|
||||
@@ -97,6 +129,7 @@ DISK_TEMP_CACHE_FILE = "/run/control-rpi-disk-temp"
|
||||
DISK_TEMP_CACHE_MAX_AGE_SECONDS = 30
|
||||
RPI_THROTTLE_INTERVAL_MS = SAMPLE_INTERVAL_MS
|
||||
SYNOLOGY_HEALTH_INTERVAL_MS = FAN_INTERVAL_MS
|
||||
RPI_SERVICE_INTERVAL_MS = 5000
|
||||
|
||||
|
||||
def byte_rate_unit(values):
|
||||
@@ -136,6 +169,61 @@ def format_temp_rpm_caption(temp, rpm):
|
||||
return f"{temp:5.1f} C / {int(rpm):5d} rpm"
|
||||
|
||||
|
||||
def format_uptime(seconds):
|
||||
seconds = max(0, int(float(seconds or 0)))
|
||||
days = seconds // 86400
|
||||
hours = (seconds % 86400) // 3600
|
||||
minutes = (seconds % 3600) // 60
|
||||
if days > 0:
|
||||
return f"{days}d {hours}h"
|
||||
if hours > 0:
|
||||
return f"{hours}h {minutes}m"
|
||||
return f"{minutes}m"
|
||||
|
||||
|
||||
def load_average():
|
||||
parts = read_text("/proc/loadavg").split()
|
||||
try:
|
||||
return float(parts[0])
|
||||
except (IndexError, ValueError):
|
||||
return 0.0
|
||||
|
||||
|
||||
def memory_used_percent(swap=False):
|
||||
values = {}
|
||||
for line in read_text("/proc/meminfo").splitlines():
|
||||
if ":" not in line:
|
||||
continue
|
||||
key, rest = line.split(":", 1)
|
||||
parts = rest.strip().split()
|
||||
if parts and parts[0].isdigit():
|
||||
values[key] = int(parts[0])
|
||||
|
||||
if swap:
|
||||
total = values.get("SwapTotal", 0)
|
||||
free = values.get("SwapFree", 0)
|
||||
return 0.0 if total <= 0 else (total - free) * 100 / total
|
||||
|
||||
total = values.get("MemTotal", 0)
|
||||
available = values.get("MemAvailable", 0)
|
||||
return 0.0 if total <= 0 else (total - available) * 100 / total
|
||||
|
||||
|
||||
def disk_used_percent(path):
|
||||
try:
|
||||
usage = shutil.disk_usage(path)
|
||||
except OSError:
|
||||
return None
|
||||
return usage.used * 100 / usage.total if usage.total > 0 else None
|
||||
|
||||
|
||||
def uptime_seconds():
|
||||
try:
|
||||
return float(read_text("/proc/uptime").split()[0])
|
||||
except (IndexError, ValueError):
|
||||
return 0.0
|
||||
|
||||
|
||||
def read_text(path, default=""):
|
||||
try:
|
||||
return Path(path).read_text(encoding="utf-8").strip()
|
||||
@@ -252,21 +340,6 @@ def disk_io():
|
||||
return reads, writes, not found
|
||||
|
||||
|
||||
def send_magic_packet(mac):
|
||||
clean = mac.replace(":", "").replace("-", "")
|
||||
if len(clean) != 12:
|
||||
return False
|
||||
packet = bytes.fromhex("ff" * 6 + clean * 16)
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
for target in ["192.168.50.255", "255.255.255.255"]:
|
||||
sock.sendto(packet, (target, 9))
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
class StatusDot(QWidget):
|
||||
def __init__(self, active_color):
|
||||
super().__init__()
|
||||
@@ -389,72 +462,6 @@ class StatusTile(QFrame):
|
||||
self.error_dot.set_steady(error_on)
|
||||
|
||||
|
||||
class PcWakeButton(StatusTile):
|
||||
def __init__(self):
|
||||
super().__init__("PC")
|
||||
self.online = False
|
||||
self.last_wake_at = 0.0
|
||||
self.wait_until = 0.0
|
||||
self.error_until = 0.0
|
||||
self.wait_phase = False
|
||||
self.blink_timer = QTimer(self)
|
||||
self.blink_timer.timeout.connect(self.tick_waiting)
|
||||
self.blink_timer.start(250)
|
||||
|
||||
def set_online(self, online):
|
||||
self.online = online
|
||||
self.read_dot.set_steady(online)
|
||||
now = time.monotonic()
|
||||
self.error_dot.set_steady(now < self.error_until)
|
||||
if online:
|
||||
self.wait_until = 0.0
|
||||
self.wait_phase = False
|
||||
self.write_dot.set_steady(False)
|
||||
self.detail.setText("켜짐")
|
||||
elif now < self.wait_until:
|
||||
remaining = int(self.wait_until - now)
|
||||
self.detail.setText(f"대기 {remaining}s")
|
||||
else:
|
||||
self.write_dot.set_steady(False)
|
||||
self.detail.setText("꺼짐")
|
||||
|
||||
def tick_waiting(self):
|
||||
now = time.monotonic()
|
||||
if not self.online and now < self.wait_until:
|
||||
self.wait_phase = not self.wait_phase
|
||||
self.write_dot.set_steady(self.wait_phase)
|
||||
if not self.online and self.wait_until > 0 and now >= self.wait_until:
|
||||
self.wait_until = 0.0
|
||||
self.wait_phase = False
|
||||
self.write_dot.set_steady(False)
|
||||
self.error_until = now + 60
|
||||
self.error_dot.set_steady(True)
|
||||
self.detail.setText("실패")
|
||||
if self.error_until > 0 and now >= self.error_until:
|
||||
self.error_until = 0.0
|
||||
self.error_dot.set_steady(False)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.LeftButton:
|
||||
now = time.monotonic()
|
||||
if not self.online and now >= self.wait_until:
|
||||
self.last_wake_at = now
|
||||
self.wait_until = now + 60
|
||||
self.wait_phase = True
|
||||
self.write_dot.set_steady(True)
|
||||
self.error_until = 0.0
|
||||
self.error_dot.set_steady(False)
|
||||
self.detail.setText("깨우는 중")
|
||||
if not send_magic_packet(PC_MAC):
|
||||
self.wait_until = 0.0
|
||||
self.error_until = now + 5
|
||||
self.error_dot.set_steady(True)
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def tick(self):
|
||||
self.error_dot.tick()
|
||||
|
||||
|
||||
class Sparkline(QFrame):
|
||||
def __init__(self, title, color="#38bdf8", second_color=None, y_unit="", right_unit="", auto_byte_unit=False, max_points=120):
|
||||
super().__init__()
|
||||
@@ -603,16 +610,16 @@ class TouchPanel(QMainWindow):
|
||||
self.fan_timer.timeout.connect(self.update_fan_status)
|
||||
self.fan_timer.start(FAN_INTERVAL_MS)
|
||||
|
||||
self.pc_ping = QProcess(self)
|
||||
self.pc_ping.finished.connect(self.pc_ping_finished)
|
||||
self.pc_timer = QTimer(self)
|
||||
self.pc_timer.timeout.connect(self.ping_pc)
|
||||
self.pc_timer.start(1000)
|
||||
|
||||
self.disk_temp_timer = QTimer(self)
|
||||
self.disk_temp_timer.timeout.connect(self.update_disk_temp)
|
||||
self.disk_temp_timer.start(DISK_TEMP_INTERVAL_MS)
|
||||
|
||||
self.rpi_services_proc = QProcess(self)
|
||||
self.rpi_services_proc.finished.connect(self.rpi_services_finished)
|
||||
self.rpi_services_timer = QTimer(self)
|
||||
self.rpi_services_timer.timeout.connect(self.update_rpi_services)
|
||||
self.rpi_services_timer.start(RPI_SERVICE_INTERVAL_MS)
|
||||
|
||||
self.synology_proc = QProcess(self)
|
||||
self.synology_proc.readyReadStandardOutput.connect(self.synology_status_ready)
|
||||
self.synology_proc.finished.connect(self.synology_status_finished)
|
||||
@@ -646,8 +653,8 @@ class TouchPanel(QMainWindow):
|
||||
self.pulse_timer.start(25)
|
||||
self.sample()
|
||||
self.update_fan_status()
|
||||
self.ping_pc()
|
||||
self.update_disk_temp()
|
||||
self.update_rpi_services()
|
||||
self.update_synology_status()
|
||||
self.update_synology_health()
|
||||
self.ping_synology()
|
||||
@@ -660,85 +667,71 @@ class TouchPanel(QMainWindow):
|
||||
layout.setContentsMargins(4, 8, 10, 8)
|
||||
layout.setSpacing(8)
|
||||
|
||||
left = QFrame()
|
||||
left.setObjectName("statusPanel")
|
||||
left.setFixedWidth(398)
|
||||
left_layout = QVBoxLayout(left)
|
||||
left_layout.setContentsMargins(6, 8, 6, 8)
|
||||
left_layout.setSpacing(6)
|
||||
title = QLabel("상태")
|
||||
title.setObjectName("sectionTitle")
|
||||
left_layout.addWidget(title)
|
||||
|
||||
status_grid = QGridLayout()
|
||||
status_grid.setContentsMargins(0, 0, 0, 0)
|
||||
status_grid.setHorizontalSpacing(6)
|
||||
status_grid.setVerticalSpacing(6)
|
||||
pi_title = QLabel("RASPBERRY")
|
||||
pi_title.setObjectName("statusGroupTitle")
|
||||
synology_title = QLabel("SYNOLOGY")
|
||||
synology_title.setObjectName("statusGroupTitle")
|
||||
extra_title = QLabel("CHECK")
|
||||
extra_title.setObjectName("statusGroupTitle")
|
||||
status_grid.addWidget(pi_title, 0, 0)
|
||||
status_grid.addWidget(synology_title, 0, 1)
|
||||
status_grid.addWidget(extra_title, 0, 2)
|
||||
|
||||
status_rows = [
|
||||
(("io", "SSD I/O"), ("synology_ping", "PING"), ("rpi_temp", "RPI TEMP")),
|
||||
(("local", "LOCAL"), ("synology_sata1", "SATA1 I/O"), ("rpi_fan", "RPI FAN")),
|
||||
(("wan", "UPLINK"), ("synology_sata2", "SATA2 I/O"), ("rpi_throttle", "RPI THROT")),
|
||||
(("lan", "ETH1"), ("synology_sata4", "SATA4 I/O"), ("synology_temp", "SYNO TEMP")),
|
||||
(("wifi5", "5G"), ("synology_local", "LOCAL"), ("synology_fan", "SYNO FAN")),
|
||||
(("wifi24", "2.4G"), ("synology_wan", "UPLINK"), ("synology_guard", "SYNO GUARD")),
|
||||
rpi_rows = [
|
||||
(("io", "SSD I/O"), ("rpi_temp", "TEMP"), ("rpi_fan", "FAN")),
|
||||
(("rpi_throttle", "THROT"), ("rpi_load", "LOAD"), ("rpi_mem", "MEM")),
|
||||
(("rpi_root", "ROOT"), ("rpi_uptime", "UPTIME"), ("local", "LOCAL")),
|
||||
(("wan", "UPLINK"), ("lan", "ETH1"), ("wifi5", "5G")),
|
||||
(("wifi24", "2.4G"), ("rpi_docker", "DOCKER"), ("rpi_nginx", "NGINX")),
|
||||
(("rpi_mariadb", "MARIADB"), ("rpi_control", "CONTROL"), ("rpi_fanpolicy", "FANPOL")),
|
||||
]
|
||||
synology_rows = [
|
||||
(("synology_ping", "PING"), ("synology_temp", "TEMP"), ("synology_fan", "FAN")),
|
||||
(("synology_guard", "GUARD"), ("synology_pwm", "PWM"), ("synology_load", "LOAD")),
|
||||
(("synology_mem", "MEM"), ("synology_swap", "SWAP"), ("synology_uptime", "UPTIME")),
|
||||
(("synology_root", "ROOT"), ("synology_vol1", "VOL1"), ("synology_vol2", "VOL2")),
|
||||
(("synology_local", "LOCAL"), ("synology_wan", "UPLINK"), ("synology_ssh", "SSH")),
|
||||
(("synology_sata1", "SATA1 I/O"), ("synology_sata2", "SATA2 I/O"), ("synology_sata4", "SATA4 I/O")),
|
||||
]
|
||||
for index, (pi_row, synology_row, extra_row) in enumerate(status_rows, 1):
|
||||
key, label = pi_row
|
||||
tile = StatusTile(label)
|
||||
self.rows[key] = tile
|
||||
status_grid.addWidget(tile, index, 0)
|
||||
self.add_status_grid_cell(status_grid, index, 1, synology_row)
|
||||
self.add_status_grid_cell(status_grid, index, 2, extra_row)
|
||||
left_layout.addLayout(status_grid)
|
||||
left_layout.addStretch(1)
|
||||
self.pc_button = PcWakeButton()
|
||||
left_layout.addWidget(self.pc_button)
|
||||
|
||||
right = QWidget()
|
||||
right_layout = QVBoxLayout(right)
|
||||
right_layout.setContentsMargins(0, 0, 0, 0)
|
||||
right_layout.setSpacing(8)
|
||||
left = self.build_status_panel("RASPBERRY", rpi_rows, fixed_width=398, bottom_blank=True)
|
||||
right = self.build_status_panel("SYNOLOGY", synology_rows)
|
||||
|
||||
header = QHBoxLayout()
|
||||
heading = QLabel("기록")
|
||||
heading.setObjectName("sectionTitle")
|
||||
self.clock = QLabel("--:--:--")
|
||||
self.clock.setObjectName("clock")
|
||||
self.status_text = QLabel("")
|
||||
self.status_text.setObjectName("statusText")
|
||||
header.addWidget(heading)
|
||||
header.addStretch(1)
|
||||
header.addWidget(self.status_text)
|
||||
header.addWidget(self.clock)
|
||||
right_layout.addLayout(header)
|
||||
|
||||
chart_grid = QGridLayout()
|
||||
chart_grid.setHorizontalSpacing(8)
|
||||
chart_grid.setVerticalSpacing(8)
|
||||
for index, (key, chart) in enumerate([
|
||||
("rpi_temp", Sparkline("라즈베리파이 온도 / RPM", "#f97316", "#38bdf8", "C", "rpm", max_points=TEMP_GRAPH_POINTS)),
|
||||
("rpi_wan", Sparkline("라즈베리파이 UPLINK 트래픽", "#f97316", "#22c55e", auto_byte_unit=True, max_points=WAN_GRAPH_POINTS)),
|
||||
("synology_temp", Sparkline("시놀로지 온도 / RPM", "#f97316", "#38bdf8", "C", "rpm", max_points=TEMP_GRAPH_POINTS)),
|
||||
("synology_wan", Sparkline("시놀로지 UPLINK 트래픽", "#f97316", "#22c55e", auto_byte_unit=True, max_points=WAN_GRAPH_POINTS)),
|
||||
]):
|
||||
self.charts[key] = chart
|
||||
chart_grid.addWidget(chart, index, 0)
|
||||
right_layout.addLayout(chart_grid, 1)
|
||||
right.layout().insertLayout(1, header)
|
||||
|
||||
layout.addWidget(left)
|
||||
layout.addWidget(right, 1)
|
||||
self.setCentralWidget(root)
|
||||
|
||||
def build_status_panel(self, title_text, rows, fixed_width=None, bottom_blank=False):
|
||||
panel = QFrame()
|
||||
panel.setObjectName("statusPanel")
|
||||
if fixed_width:
|
||||
panel.setFixedWidth(fixed_width)
|
||||
panel_layout = QVBoxLayout(panel)
|
||||
panel_layout.setContentsMargins(6, 8, 6, 8)
|
||||
panel_layout.setSpacing(6)
|
||||
|
||||
title = QLabel(title_text)
|
||||
title.setObjectName("sectionTitle")
|
||||
panel_layout.addWidget(title)
|
||||
|
||||
grid = QGridLayout()
|
||||
grid.setContentsMargins(0, 0, 0, 0)
|
||||
grid.setHorizontalSpacing(6)
|
||||
grid.setVerticalSpacing(6)
|
||||
for row_index, row in enumerate(rows):
|
||||
for column_index, spec in enumerate(row):
|
||||
self.add_status_grid_cell(grid, row_index, column_index, spec)
|
||||
panel_layout.addLayout(grid)
|
||||
panel_layout.addStretch(1)
|
||||
|
||||
if bottom_blank:
|
||||
blank = QWidget()
|
||||
blank.setFixedHeight(48)
|
||||
blank.setObjectName("statusEmpty")
|
||||
panel_layout.addWidget(blank)
|
||||
|
||||
return panel
|
||||
|
||||
def add_status_grid_cell(self, grid, row, column, spec):
|
||||
if spec is None:
|
||||
spacer = QWidget()
|
||||
@@ -795,7 +788,7 @@ class TouchPanel(QMainWindow):
|
||||
self.render_status(groups, rates, activity, io_read_activity, io_write_activity, disk_error)
|
||||
|
||||
def render_status(self, groups, rates, activity, io_read_activity, io_write_activity, disk_error):
|
||||
disk_temp_text = f"{self.disk_temp:.1f} C" if self.disk_temp is not None else "- C"
|
||||
disk_temp_text = f"{self.disk_temp:.1f} C" if self.disk_temp is not None else ""
|
||||
self.rows["io"].set_state(disk_temp_text, disk_error, io_read_activity, io_write_activity)
|
||||
self.rows["local"].set_state(self.iface_detail(groups["local"], "lo"), activity["local"]["error"], activity["local"]["read"], activity["local"]["write"])
|
||||
self.rows["wan"].set_state(self.iface_detail(groups["wan"], self.wan_iface), activity["wan"]["error"], activity["wan"]["read"], activity["wan"]["write"])
|
||||
@@ -810,20 +803,38 @@ class TouchPanel(QMainWindow):
|
||||
self.latest_rpm = rpm
|
||||
|
||||
def update_dashboard(self):
|
||||
rpm = getattr(self, "latest_rpm", read_int(self.fan["rpm"]))
|
||||
temp = getattr(self, "latest_temp", 0.0)
|
||||
|
||||
self.clock.setText(time.strftime("%H:%M:%S"))
|
||||
self.update_temp_graphs(temp, rpm)
|
||||
self.render_rpi_system()
|
||||
|
||||
def ping_pc(self):
|
||||
if self.pc_ping.state() != QProcess.NotRunning:
|
||||
def render_rpi_system(self):
|
||||
cores = max(1, os.cpu_count() or 1)
|
||||
load = load_average()
|
||||
self.rows["rpi_load"].set_state(
|
||||
f"{load:.2f}",
|
||||
read_steady=load < cores * 0.7,
|
||||
write_steady=cores * 0.7 <= load < cores * 1.2,
|
||||
error_steady=load >= cores * 1.2,
|
||||
)
|
||||
self.set_percent_tile("rpi_mem", memory_used_percent(), warn=80, critical=92)
|
||||
root_used = disk_used_percent("/")
|
||||
self.set_percent_tile("rpi_root", root_used, warn=80, critical=92)
|
||||
self.rows["rpi_uptime"].set_state(format_uptime(uptime_seconds()), read_steady=True, write_steady=False, error_steady=False)
|
||||
|
||||
def update_rpi_services(self):
|
||||
if self.rpi_services_proc.state() != QProcess.NotRunning:
|
||||
return
|
||||
self.pc_ping.start("/usr/bin/ping", ["-c", "1", "-W", "1", PC_IP])
|
||||
units = [unit for _key, unit in RPI_SERVICE_UNITS]
|
||||
self.rpi_services_proc.start("/usr/bin/systemctl", ["is-active"] + units)
|
||||
|
||||
def pc_ping_finished(self, exit_code, exit_status):
|
||||
del exit_status
|
||||
self.pc_button.set_online(exit_code == 0)
|
||||
def rpi_services_finished(self, exit_code, exit_status):
|
||||
del exit_code, exit_status
|
||||
output = bytes(self.rpi_services_proc.readAllStandardOutput()).decode("utf-8", "ignore")
|
||||
states = output.splitlines()
|
||||
for index, (key, _unit) in enumerate(RPI_SERVICE_UNITS):
|
||||
state = states[index].strip() if index < len(states) else "unknown"
|
||||
active = state == "active"
|
||||
detail = "OK" if active else state.upper()[:6]
|
||||
self.rows[key].set_state(detail, read_steady=active, write_steady=False, error_steady=not active)
|
||||
|
||||
def ping_synology(self):
|
||||
if self.synology_ping_proc.state() != QProcess.NotRunning:
|
||||
@@ -958,7 +969,23 @@ class TouchPanel(QMainWindow):
|
||||
self.set_synology_offline()
|
||||
|
||||
def set_synology_offline(self):
|
||||
for key in ["synology_sata1", "synology_sata2", "synology_sata4", "synology_wan", "synology_local", "synology_guard"]:
|
||||
for key in [
|
||||
"synology_sata1",
|
||||
"synology_sata2",
|
||||
"synology_sata4",
|
||||
"synology_wan",
|
||||
"synology_local",
|
||||
"synology_guard",
|
||||
"synology_pwm",
|
||||
"synology_load",
|
||||
"synology_mem",
|
||||
"synology_swap",
|
||||
"synology_uptime",
|
||||
"synology_root",
|
||||
"synology_vol1",
|
||||
"synology_vol2",
|
||||
"synology_ssh",
|
||||
]:
|
||||
self.rows[key].set_state("-", read_steady=False, write_steady=False, error_steady=True)
|
||||
|
||||
def render_synology_status(self, values):
|
||||
@@ -988,6 +1015,8 @@ class TouchPanel(QMainWindow):
|
||||
int(values.get("LOCAL_RX_DROP", "0") or 0) + int(values.get("LOCAL_TX_DROP", "0") or 0),
|
||||
"lo",
|
||||
)
|
||||
self.render_synology_system(values)
|
||||
self.rows["synology_ssh"].set_state("OK", read_steady=True, write_steady=False, error_steady=False)
|
||||
|
||||
def set_synology_io_tile(self, key, reads, writes, detail):
|
||||
previous = self.last_synology.get(key)
|
||||
@@ -1039,6 +1068,30 @@ class TouchPanel(QMainWindow):
|
||||
self.rows["synology_guard"].set_state("FULL", read_steady=False, write_steady=True, error_steady=False)
|
||||
else:
|
||||
self.rows["synology_guard"].set_state("AUTO", read_steady=True, write_steady=False, error_steady=False)
|
||||
self.rows["synology_pwm"].set_state(
|
||||
f"{round(pwm * 100 / 255):.0f}%",
|
||||
read_steady=0 < pwm < 170,
|
||||
write_steady=170 <= pwm <= 255,
|
||||
error_steady=pwm <= 0 or enable != 5,
|
||||
)
|
||||
|
||||
def render_synology_system(self, values):
|
||||
try:
|
||||
load = float(values.get("LOAD1", "0") or 0)
|
||||
except ValueError:
|
||||
load = 0.0
|
||||
self.rows["synology_load"].set_state(
|
||||
f"{load:.2f}",
|
||||
read_steady=load < 2.8,
|
||||
write_steady=2.8 <= load < 4.8,
|
||||
error_steady=load >= 4.8,
|
||||
)
|
||||
self.set_percent_tile("synology_mem", self.float_value(values.get("MEM_USED_PCT")), warn=80, critical=92)
|
||||
self.set_percent_tile("synology_swap", self.float_value(values.get("SWAP_USED_PCT")), warn=50, critical=80)
|
||||
self.set_percent_tile("synology_root", self.float_value(values.get("ROOT_FS_PCT")), warn=80, critical=92)
|
||||
self.set_percent_tile("synology_vol1", self.float_value(values.get("VOL1_FS_PCT")), warn=80, critical=92)
|
||||
self.set_percent_tile("synology_vol2", self.float_value(values.get("VOL2_FS_PCT")), warn=80, critical=92)
|
||||
self.rows["synology_uptime"].set_state(format_uptime(self.float_value(values.get("UPTIME"))), read_steady=True, write_steady=False, error_steady=False)
|
||||
|
||||
def render_fan_tile(self, key, rpm):
|
||||
detail = f"{int(rpm):,} rpm" if rpm > 0 else "0 rpm"
|
||||
@@ -1077,6 +1130,8 @@ class TouchPanel(QMainWindow):
|
||||
self.update_synology_wan_graph()
|
||||
|
||||
def update_temp_graphs(self, rpi_temp, rpi_rpm):
|
||||
if "rpi_temp" not in self.charts or "synology_temp" not in self.charts:
|
||||
return
|
||||
synology_temp = getattr(self, "synology_cpu_temp", 0.0)
|
||||
synology_rpm = getattr(self, "synology_fan_rpm", 0)
|
||||
|
||||
@@ -1097,6 +1152,8 @@ class TouchPanel(QMainWindow):
|
||||
)
|
||||
|
||||
def update_wan_graph(self):
|
||||
if "rpi_wan" not in self.charts:
|
||||
return
|
||||
wan = self.current_rates.get("wan", {"rx": 0.0, "tx": 0.0})
|
||||
self.net_history["wan"]["rx"].append(wan["rx"])
|
||||
self.net_history["wan"]["tx"].append(wan["tx"])
|
||||
@@ -1107,6 +1164,8 @@ class TouchPanel(QMainWindow):
|
||||
)
|
||||
|
||||
def update_synology_wan_graph(self):
|
||||
if "synology_wan" not in self.charts:
|
||||
return
|
||||
wan = self.current_synology_rates.get("wan", {"rx": 0.0, "tx": 0.0})
|
||||
self.net_history["synology_wan"]["rx"].append(wan["rx"])
|
||||
self.net_history["synology_wan"]["tx"].append(wan["tx"])
|
||||
@@ -1132,6 +1191,25 @@ class TouchPanel(QMainWindow):
|
||||
del normal_label
|
||||
return f"D {drops}"
|
||||
|
||||
@staticmethod
|
||||
def float_value(value):
|
||||
try:
|
||||
return float(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
def set_percent_tile(self, key, percent, warn=80, critical=92):
|
||||
if percent is None:
|
||||
self.rows[key].set_state("-", read_steady=False, write_steady=False, error_steady=True)
|
||||
return
|
||||
detail = f"{percent:.0f}%"
|
||||
self.rows[key].set_state(
|
||||
detail,
|
||||
read_steady=0 <= percent < warn,
|
||||
write_steady=warn <= percent < critical,
|
||||
error_steady=percent >= critical,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def iface_warn(row):
|
||||
return row["rx_errors"] > 0 or row["tx_errors"] > 0
|
||||
|
||||
Reference in new issue
Block a user