터치 패널 삼색 상태등과 PC 깨우기 추가

This commit is contained in:
seo committed 2026-07-30 13:04:54 +09:00
1 parent e89f1da109
commit 9388122892
2 files changed
+98 -29

No files matched your search

+97 -28
View File
@@ -1,10 +1,11 @@
#!/usr/bin/env python3
import sys
import time
import socket
from collections import deque
from pathlib import Path
from PyQt5.QtCore import QPointF, Qt, QTimer
from PyQt5.QtCore import QPointF, QProcess, Qt, QTimer
from PyQt5.QtGui import QColor, QPainter, QPainterPath, QPen
from PyQt5.QtWidgets import (
QApplication,
@@ -27,6 +28,9 @@ NET_GROUPS = {
"wifi5": ["wlan1"],
}
PC_IP = "192.168.50.73"
PC_MAC = "74:56:3C:CD:CD:79"
def read_text(path, default=""):
try:
@@ -111,6 +115,7 @@ def group_stats(names):
def disk_io():
reads = 0
writes = 0
found = False
for line in read_text("/proc/diskstats").splitlines():
parts = line.split()
if len(parts) < 14:
@@ -118,14 +123,30 @@ def disk_io():
name = parts[2]
if name.startswith(("loop", "ram", "zram", "dm-")):
continue
found = True
reads += int(parts[3])
writes += int(parts[7])
return reads, writes
return reads, writes, not found
def write_sys(path, value):
if not path:
return False
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
try:
Path(path).write_text(str(value) + "\n", encoding="utf-8")
return True
@@ -137,12 +158,12 @@ class StatusDot(QWidget):
def __init__(self, active_color):
super().__init__()
self.active_color = QColor(active_color)
self.warning = False
self.steady = False
self.pulse = 0
self.setFixedSize(15, 15)
def set_warning(self, warning=False):
self.warning = warning
def set_steady(self, steady=False):
self.steady = steady
self.update()
def blink(self):
@@ -159,10 +180,10 @@ class StatusDot(QWidget):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
base = QColor("#8fa0aa")
if self.pulse > 0:
if self.steady or self.pulse > 0:
base = self.active_color
painter.setBrush(base)
painter.setPen(QPen(QColor("#ef4444"), 1) if self.warning else Qt.NoPen)
painter.setPen(Qt.NoPen)
painter.drawEllipse(2, 2, 11, 11)
painter.setBrush(QColor(255, 255, 255, 55))
painter.drawEllipse(6, 4, 4, 4)
@@ -173,6 +194,7 @@ class StatusTile(QFrame):
super().__init__()
self.read_dot = StatusDot("#16a34a")
self.write_dot = StatusDot("#f97316")
self.error_dot = StatusDot("#dc2626")
self.title = QLabel(title)
self.title.setObjectName("statusTitle")
self.detail = QLabel("-")
@@ -180,28 +202,28 @@ class StatusTile(QFrame):
self.detail.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.setFixedHeight(58)
layout = QHBoxLayout(self)
layout.setContentsMargins(6, 6, 6, 6)
layout.setSpacing(4)
dots = QVBoxLayout()
dots.setContentsMargins(0, 0, 0, 0)
dots.setSpacing(4)
dots.addWidget(self.read_dot, 0, Qt.AlignCenter)
dots.addWidget(self.write_dot, 0, Qt.AlignCenter)
layout.addLayout(dots)
layout = QVBoxLayout(self)
layout.setContentsMargins(6, 5, 6, 5)
layout.setSpacing(2)
texts = QVBoxLayout()
texts.setContentsMargins(0, 0, 0, 0)
texts.setSpacing(1)
texts.setSpacing(0)
texts.addWidget(self.title)
texts.addWidget(self.detail)
layout.addLayout(texts, 1)
layout.addLayout(texts)
dots = QHBoxLayout()
dots.setContentsMargins(0, 0, 0, 0)
dots.setSpacing(5)
dots.addWidget(self.read_dot, 0, Qt.AlignLeft)
dots.addWidget(self.write_dot, 0, Qt.AlignLeft)
dots.addWidget(self.error_dot, 0, Qt.AlignLeft)
dots.addStretch(1)
layout.addLayout(dots)
self.setObjectName("statusTile")
def set_state(self, detail, warning=False, read_activity=False, write_activity=False):
self.read_dot.set_warning(warning)
self.write_dot.set_warning(warning)
def set_state(self, detail, error=False, read_activity=False, write_activity=False):
self.error_dot.set_steady(error)
if read_activity:
self.read_dot.blink()
if write_activity:
@@ -211,6 +233,33 @@ class StatusTile(QFrame):
def tick(self):
self.read_dot.tick()
self.write_dot.tick()
self.error_dot.tick()
class PcWakeButton(QFrame):
def __init__(self):
super().__init__()
self.setObjectName("pcWake")
self.online = False
self.dot = StatusDot("#16a34a")
self.label = QLabel("PC")
self.label.setObjectName("pcLabel")
layout = QHBoxLayout(self)
layout.setContentsMargins(5, 4, 5, 4)
layout.setSpacing(5)
layout.addWidget(self.dot)
layout.addWidget(self.label)
layout.addStretch(1)
self.setFixedHeight(28)
def set_online(self, online):
self.online = online
self.dot.set_steady(online)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and not self.online:
send_magic_packet(PC_MAC)
super().mousePressEvent(event)
class MetricCard(QFrame):
@@ -327,10 +376,17 @@ class TouchPanel(QMainWindow):
self.graph_timer.timeout.connect(self.update_graphs)
self.graph_timer.start(500)
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.pulse_timer = QTimer(self)
self.pulse_timer.timeout.connect(self.tick_pulses)
self.pulse_timer.start(70)
self.sample()
self.ping_pc()
def _build_ui(self):
root = QWidget()
@@ -348,11 +404,13 @@ class TouchPanel(QMainWindow):
title.setObjectName("sectionTitle")
left_layout.addWidget(title)
for key, label in [("io", "IO"), ("wan", "WAN"), ("lan", "LAN"), ("wifi24", "2.4G"), ("wifi5", "5G")]:
for key, label in [("io", "DISK I/O"), ("wan", "WAN"), ("lan", "LAN"), ("wifi24", "2.4G"), ("wifi5", "5G")]:
tile = StatusTile(label)
self.rows[key] = tile
left_layout.addWidget(tile)
left_layout.addStretch(1)
self.pc_button = PcWakeButton()
left_layout.addWidget(self.pc_button, 0, Qt.AlignLeft | Qt.AlignBottom)
right = QWidget()
right_layout = QVBoxLayout(right)
@@ -433,7 +491,7 @@ class TouchPanel(QMainWindow):
enable = read_int(self.fan["enable"], 2)
percent = pwm / 255 * 100 if pwm > 0 else 0
reads, writes = disk_io()
reads, writes, disk_error = disk_io()
io_read_activity = self.last_io is not None and reads != self.last_io[0]
io_write_activity = self.last_io is not None and writes != self.last_io[1]
self.last_io = (reads, writes)
@@ -458,10 +516,10 @@ class TouchPanel(QMainWindow):
self.last_net[key] = current
self.current_rates = rates
self.render(temp, rpm, pwm, percent, enable, groups, rates, activity, io_read_activity, io_write_activity)
self.render(temp, rpm, pwm, percent, enable, groups, rates, activity, io_read_activity, io_write_activity, disk_error)
def render(self, temp, rpm, pwm, percent, enable, groups, rates, activity, io_read_activity, io_write_activity):
self.rows["io"].set_state(f"{temp:.1f} C", temp >= 75, io_read_activity, io_write_activity)
def render(self, temp, rpm, pwm, percent, enable, groups, rates, activity, io_read_activity, io_write_activity, disk_error):
self.rows["io"].set_state(f"{temp:.1f} C", disk_error, io_read_activity, io_write_activity)
self.rows["wan"].set_state(self.iface_detail(groups["wan"]), self.iface_warn(groups["wan"]), activity["wan"]["read"], activity["wan"]["write"])
self.rows["lan"].set_state(self.iface_detail(groups["lan"]), self.iface_warn(groups["lan"]), activity["lan"]["read"], activity["lan"]["write"])
self.rows["wifi24"].set_state(self.iface_detail(groups["wifi24"]), self.iface_warn(groups["wifi24"]), activity["wifi24"]["read"], activity["wifi24"]["write"])
@@ -483,6 +541,15 @@ class TouchPanel(QMainWindow):
self.latest_temp = temp
self.latest_rpm = rpm
def ping_pc(self):
if self.pc_ping.state() != QProcess.NotRunning:
return
self.pc_ping.start("/usr/bin/ping", ["-c", "1", "-W", "1", PC_IP])
def pc_ping_finished(self, exit_code, exit_status):
del exit_status
self.pc_button.set_online(exit_code == 0)
def update_graphs(self):
temp = getattr(self, "latest_temp", cpu_temp())
rpm = getattr(self, "latest_rpm", read_int(self.fan["rpm"]))
@@ -545,6 +612,8 @@ def main():
#statusTile { background: #d7d9dc; border: 2px groove #f4f4f4; border-radius: 2px; }
#statusTitle { font-size: 13px; font-weight: 700; color: #111827; background: transparent; }
#statusDetail { font-size: 10px; color: #475569; background: transparent; }
#pcWake { background: transparent; border: 0; }
#pcLabel { color: #dce8f8; font-size: 13px; font-weight: 700; background: transparent; }
#metricCard, #chartCard { background: #111a2e; border: 1px solid #27364f; border-radius: 12px; }
#metricTitle { color: #8fa7c5; font-size: 13px; background: transparent; }
#metricValue { color: #f8fafc; font-size: 24px; font-weight: 800; background: transparent; }