import json import bluetooth import threading from kivy.config import Config import time import sys # Kivy 앱 설정 Config.set('graphics', 'fullscreen', '1') Config.set('graphics', 'borderless', '1') Config.set('graphics', 'resizable', '0') Config.set('graphics', 'width', '480') Config.set('graphics', 'height', '320') Config.set('graphics', 'show_cursor', '0') # SERVER_MAC = '8C:E9:EE:C9:33:4D' SERVER_MAC = '00:72:EE:DE:15:79' PORT = 5 from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.gridlayout import GridLayout from kivy.uix.label import Label from kivy.uix.image import Image from kivy.uix.behaviors import ButtonBehavior from kivy.uix.button import Button from kivy.core.window import Window from kivy.graphics import Color, RoundedRectangle, Line from kivy.clock import Clock class DashboardApp(App): def __init__(self, **kwargs): super().__init__(**kwargs) self.theme_mode = 'dark' # 기본 테마 def build(self): self.client_sock = None self.is_connected = False self.status_label = Label(text="Connecting...", font_size=20, color=(1, 1, 0, 1)) root = BoxLayout(orientation='vertical', padding=10) # 타이틀 바 + 테마 버튼 title_bar = BoxLayout(orientation='horizontal', size_hint_y=None, height=40) self.title_label = Label(text="MK's GalaxyBook5 Stream Deck", font_size=25, halign='left', valign='middle') theme_btn = Button(text='dark', size_hint_x=None, width=60) theme_btn.bind(on_press=self.toggle_theme) title_bar.add_widget(self.title_label) title_bar.add_widget(theme_btn) root.add_widget(title_bar) grid = GridLayout(cols=4, rows=2, spacing=10, padding=5) try: with open("cmders.json", "r", encoding="utf-8") as f: self.cmders = json.load(f) except FileNotFoundError: self.cmders = [] print("cmders.json 파일을 찾을 수 없습니다. 기본값으로 실행합니다.") for item in self.cmders: icon_path = item["icons_path"] if item["icons_path"] else "icons/default.png" cmd = item["cmd"] btn = IconButton(icon_path=icon_path, cmd=cmd, app_instance=self) grid.add_widget(btn) root.add_widget(grid) self.status_label.text = "Attempting to connect..." self.status_label.size_hint_y = None self.status_label.height = 30 root.add_widget(self.status_label) print("[DEBUG] 초기 연결 시도 시작...") self.connect_scheduled_event = Clock.schedule_once(self.connect_to_server, 1) self.apply_theme() # 초기 테마 적용 return root def toggle_theme(self, instance): self.theme_mode = 'dark' if self.theme_mode == 'light' else 'light' instance.text = 'Light' if self.theme_mode == 'light' else 'Dark' print(f"[DEBUG] 테마 변경됨: {self.theme_mode}") self.apply_theme() def apply_theme(self): if self.theme_mode == 'dark': Window.clearcolor = (0.1, 0.1, 0.1, 1) self.status_label.color = (0.8, 0.8, 0.2, 1) self.title_label.color = (1, 1, 1, 1) # 밝은 글씨 else: Window.clearcolor = (1, 1, 1, 1) self.status_label.color = (0.2, 0.2, 0.2, 1) self.title_label.color = (0, 0, 0, 1) # 어두운 글씨 def connect_to_server(self, dt=0): if self.client_sock: self.client_sock.close() self.client_sock = None self.is_connected = False self.update_status_label("Connecting...") print(f"[DEBUG] 연결 시도: MAC={SERVER_MAC}, PORT={PORT}") try: self.client_sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM) self.client_sock.connect((SERVER_MAC, PORT)) self.is_connected = True self.update_status_label("Connected!") print("[DEBUG] 연결 성공!") Clock.schedule_interval(self.get_system_status, 5) except bluetooth.btcommon.BluetoothError as e: self.update_status_label(f"Connection Failed: {e}") print(f"[DEBUG] 연결 실패: {e}. 5초 후 재시도...") if self.client_sock: self.client_sock.close() self.client_sock = None Clock.schedule_once(self.connect_to_server, 5) def get_system_status(self, dt): if not self.is_connected or not self.client_sock: print("[DEBUG] 연결이 끊어져 상태 업데이트 중단.") Clock.unschedule(self.get_system_status) self.connect_to_server() return try: self.client_sock.send("get_status".encode('utf-8')) data = self.client_sock.recv(1024) status_text = data.decode('utf-8') self.update_status_label(status_text) except bluetooth.btcommon.BluetoothError as e: self.is_connected = False self.update_status_label(f"Connection Lost: {e}") print(f"[DEBUG] 통신 오류: {e}. 재연결 시도...") Clock.unschedule(self.get_system_status) self.connect_to_server() def update_status_label(self, text): self.status_label.text = text if "Connected" in text or "CPU" in text: self.status_label.color = (0, 1, 0, 1) else: self.status_label.color = (1, 0, 0, 1) def send_command(self, command): if not self.is_connected or not self.client_sock: self.update_status_label("Not connected. Cannot send command.") print("Not connected to server.") return try: print(f"[DEBUG] '{command}' 명령 전송 시도...") self.client_sock.send(command.encode('utf-8')) data = self.client_sock.recv(1024) response_text = data.decode('utf-8') self.update_status_label(f"Response: {response_text}") print(f"[DEBUG] 서버 응답: {response_text}") except bluetooth.btcommon.BluetoothError as e: self.is_connected = False self.update_status_label(f"Send failed: {e}") print(f"[DEBUG] 전송 오류: {e}. 연결 끊김...") Clock.unschedule(self.get_system_status) self.connect_to_server() class IconButton(ButtonBehavior, BoxLayout): def __init__(self, icon_path, cmd, app_instance, **kwargs): super().__init__(orientation='vertical', **kwargs) self.cmd = cmd self.app_instance = app_instance with self.canvas.before: Color(0.5, 0.5, 0.5, 1) self.rect = RoundedRectangle(radius=[20], pos=self.pos, size=self.size) with self.canvas.after: Color(0.6, 0.6, 0.6, 1) self.border = Line(rectangle=(self.x, self.y, self.width, self.height), width=1) self.bind(pos=self.update_graphics, size=self.update_graphics) self.add_widget(Image(source=icon_path, size_hint=(1, 1), allow_stretch=True, keep_ratio=False)) def update_graphics(self, *args): self.rect.pos = self.pos self.rect.size = self.size self.border.rectangle = (self.x, self.y, self.width, self.height) def on_press(self): print(f"[DEBUG] 버튼 클릭됨: {self.cmd}") self.app_instance.send_command(self.cmd) if __name__ == "__main__": DashboardApp().run()