diff --git a/rpiKivyDash/cmders.json b/rpiKivyDash/cmders.json deleted file mode 100644 index 37ac0d4..0000000 --- a/rpiKivyDash/cmders.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - {"id": 1, "icons_path": "icons/calc.png", "cmd": "calc"}, - {"id": 2, "icons_path": "icons/notepad.png", "cmd": "notepad"}, - {"id": 3, "icons_path": "icons/gemini.png", "cmd": "gemini"}, - {"id": 4, "icons_path": "", "cmd": "copilot"}, - {"id": 5, "icons_path": "", "cmd": "calc"}, - {"id": 6, "icons_path": "", "cmd": "calc"}, - {"id": 7, "icons_path": "", "cmd": "calc"}, - {"id": 8, "icons_path": "", "cmd": "calc"} -] diff --git a/rpiKivyStreamDeck/.README.md.swp b/rpiKivyStreamDeck/.README.md.swp new file mode 100644 index 0000000..58497dc Binary files /dev/null and b/rpiKivyStreamDeck/.README.md.swp differ diff --git a/rpiKivyStreamDeck/check_display_restart.sh b/rpiKivyStreamDeck/check_display_restart.sh new file mode 100755 index 0000000..4e0756b --- /dev/null +++ b/rpiKivyStreamDeck/check_display_restart.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +export DISPLAY=:0 +export XAUTHORITY=/home/mkpark/.Xauthority + +# 화면 연결 상태 확인 +if xrandr | grep " connected" | grep -v "disconnected"; then + echo "Display is connected. Restarting service..." + systemctl restart kivy-dashboard.service +else + echo "Display not ready yet." +fi diff --git a/rpiKivyStreamDeck/cmders.json b/rpiKivyStreamDeck/cmders.json new file mode 100644 index 0000000..94b056a --- /dev/null +++ b/rpiKivyStreamDeck/cmders.json @@ -0,0 +1,10 @@ +[ + {"id": 1, "icons_path": "icons/youtube.png", "cmd": "youtube"}, + {"id": 2, "icons_path": "icons/copilot.png", "cmd": "copilot"}, + {"id": 3, "icons_path": "icons/gemini.png", "cmd": "gemini"}, + {"id": 4, "icons_path": "icons/chatgpt.png", "cmd": "chatgpt"}, + {"id": 5, "icons_path": "icons/gitea.png", "cmd": "gitea"}, + {"id": 6, "icons_path": "icons/redmine.png", "cmd": "redmine"}, + {"id": 7, "icons_path": "icons/github.png", "cmd": "github"}, + {"id": 8, "icons_path": "icons/taskmgr.png", "cmd": "taskmgr"} +] diff --git a/rpiKivyStreamDeck/icons/calc.png b/rpiKivyStreamDeck/icons/calc.png new file mode 100644 index 0000000..d7497b5 Binary files /dev/null and b/rpiKivyStreamDeck/icons/calc.png differ diff --git a/rpiKivyStreamDeck/icons/chatgpt.png b/rpiKivyStreamDeck/icons/chatgpt.png new file mode 100644 index 0000000..b35710d Binary files /dev/null and b/rpiKivyStreamDeck/icons/chatgpt.png differ diff --git a/rpiKivyStreamDeck/icons/copilot.png b/rpiKivyStreamDeck/icons/copilot.png new file mode 100644 index 0000000..55c76aa Binary files /dev/null and b/rpiKivyStreamDeck/icons/copilot.png differ diff --git a/rpiKivyStreamDeck/icons/gemini.png b/rpiKivyStreamDeck/icons/gemini.png new file mode 100644 index 0000000..029f8ca Binary files /dev/null and b/rpiKivyStreamDeck/icons/gemini.png differ diff --git a/rpiKivyStreamDeck/icons/gitea.png b/rpiKivyStreamDeck/icons/gitea.png new file mode 100644 index 0000000..a678c19 Binary files /dev/null and b/rpiKivyStreamDeck/icons/gitea.png differ diff --git a/rpiKivyStreamDeck/icons/github.png b/rpiKivyStreamDeck/icons/github.png new file mode 100644 index 0000000..b34b665 Binary files /dev/null and b/rpiKivyStreamDeck/icons/github.png differ diff --git a/rpiKivyStreamDeck/icons/redmine.png b/rpiKivyStreamDeck/icons/redmine.png new file mode 100644 index 0000000..1f13cb0 Binary files /dev/null and b/rpiKivyStreamDeck/icons/redmine.png differ diff --git a/rpiKivyStreamDeck/icons/taskmgr.png b/rpiKivyStreamDeck/icons/taskmgr.png new file mode 100644 index 0000000..915e284 Binary files /dev/null and b/rpiKivyStreamDeck/icons/taskmgr.png differ diff --git a/rpiKivyStreamDeck/icons/youtube.png b/rpiKivyStreamDeck/icons/youtube.png new file mode 100644 index 0000000..5924c8d Binary files /dev/null and b/rpiKivyStreamDeck/icons/youtube.png differ diff --git a/rpiKivyDash/main.py b/rpiKivyStreamDeck/main.py similarity index 72% rename from rpiKivyDash/main.py rename to rpiKivyStreamDeck/main.py index 9f95f3a..aa16388 100644 --- a/rpiKivyDash/main.py +++ b/rpiKivyStreamDeck/main.py @@ -5,8 +5,7 @@ from kivy.config import Config import time import sys -# Kivy 앱의 크기와 설정을 고정합니다. -# 풀스크린과 테두리 없음을 설정하여 라즈베리파이 LCD에 맞춰 실행되도록 합니다. +# Kivy 앱 설정 Config.set('graphics', 'fullscreen', '1') Config.set('graphics', 'borderless', '1') Config.set('graphics', 'resizable', '0') @@ -14,46 +13,52 @@ Config.set('graphics', 'width', '480') Config.set('graphics', 'height', '320') Config.set('graphics', 'show_cursor', '0') -# 서버의 블루투스 MAC 주소와 UUID를 설정합니다. -# 이 값은 갤럭시북의 정보와 일치해야 합니다. SERVER_MAC = '8C:E9:EE:C9:33:4D' 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 +from kivy.graphics import Color, RoundedRectangle, Line from kivy.clock import Clock - -# Kivy 애플리케이션 클래스 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)) - - # Kivy UI 구성 - root = BoxLayout(orientation='vertical', padding=10) - - title = Label(text="MK's GalaxyBook5 Dashboard", font_size=25, size_hint_y=None, height=40) - root.add_widget(title) - grid = GridLayout(cols=4, rows=2, spacing=10, padding=1) - - # cmder.json 파일에서 버튼 데이터 로드 + 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"] @@ -64,34 +69,46 @@ class DashboardApp(App): self.status_label.text = "Attempting to connect..." self.status_label.size_hint_y = None - self.status_label.height = 30 + 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.settimeout(5.0) self.client_sock.connect((SERVER_MAC, PORT)) self.is_connected = True self.update_status_label("Connected!") print("[DEBUG] 연결 성공!") - - # 연결 성공 시 5초마다 상태 업데이트 스케줄링 Clock.schedule_interval(self.get_system_status, 5) except bluetooth.btcommon.BluetoothError as e: @@ -100,24 +117,21 @@ class DashboardApp(App): if self.client_sock: self.client_sock.close() self.client_sock = None - # 연결 실패 시 5초 후 재시도 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}") @@ -128,12 +142,11 @@ class DashboardApp(App): 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) # 녹색 + self.status_label.color = (0, 1, 0, 1) else: - self.status_label.color = (1, 0, 0, 1) # 빨간색 + self.status_label.color = (1, 0, 0, 1) def send_command(self, command): - """UI에서 호출될 함수: 서버로 명령을 보냅니다.""" if not self.is_connected or not self.client_sock: self.update_status_label("Not connected. Cannot send command.") print("Not connected to server.") @@ -146,7 +159,7 @@ class DashboardApp(App): 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}") @@ -154,8 +167,6 @@ class DashboardApp(App): 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) @@ -163,15 +174,20 @@ class IconButton(ButtonBehavior, BoxLayout): self.app_instance = app_instance with self.canvas.before: - Color(0.2, 0.2, 0.2, 1) + Color(0.5, 0.5, 0.5, 1) self.rect = RoundedRectangle(radius=[20], pos=self.pos, size=self.size) - self.bind(pos=self.update_rect, size=self.update_rect) + 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_rect(self, *args): + 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}") @@ -179,7 +195,3 @@ class IconButton(ButtonBehavior, BoxLayout): if __name__ == "__main__": DashboardApp().run() - # 버전 체크용 코드: 파일이 올바르게 복사되었는지 확인합니다. - # 이 숫자는 이 코드가 생성된 시점을 나타내는 고유 식별자입니다. - print(f"File version: 8207010") - diff --git a/rpiKivyDash/requirements.txt b/rpiKivyStreamDeck/requirements.txt similarity index 100% rename from rpiKivyDash/requirements.txt rename to rpiKivyStreamDeck/requirements.txt diff --git a/rpiKivyDash/tests/ble_client.py b/rpiKivyStreamDeck/tests/ble_client.py similarity index 100% rename from rpiKivyDash/tests/ble_client.py rename to rpiKivyStreamDeck/tests/ble_client.py diff --git a/rpiKivyDash/tests/fullscreen.py b/rpiKivyStreamDeck/tests/fullscreen.py similarity index 100% rename from rpiKivyDash/tests/fullscreen.py rename to rpiKivyStreamDeck/tests/fullscreen.py diff --git a/rpiKivyDash/tests/rf_client.py b/rpiKivyStreamDeck/tests/rf_client.py similarity index 100% rename from rpiKivyDash/tests/rf_client.py rename to rpiKivyStreamDeck/tests/rf_client.py diff --git a/services/kivy-dashboard.service b/services/kivy-dashboard.service new file mode 100644 index 0000000..5a865fc --- /dev/null +++ b/services/kivy-dashboard.service @@ -0,0 +1,14 @@ +[Unit] +Description=Kivy Dashboard Service +After=network.target + +[Service] +ExecStart=/home/mkpark/pyqt6Dashboard/.venv/bin/python /home/mkpark/pyqt6Dashboard/rpiKivyStreamDeck/main.py +WorkingDirectory=/home/mkpark/pyqt6Dashboard/rpiKivyStreamDeck +Restart=always +User=mkpark +Environment=DISPLAY=:0 +Environment=XAUTHORITY=/home/mkpark/.Xauthority + +[Install] +WantedBy=graphical.target