action
This commit is contained in:
37
.gitea/workflows/deploy.yaml
Normal file
37
.gitea/workflows/deploy.yaml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: Deploy WebSocket Server
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main # 或者是 master,请根据您的分支名称修改
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu # 或者您的 Gitea runner 标签
|
||||||
|
steps:
|
||||||
|
- name: SSH Remote Commands
|
||||||
|
uses: appleboy/ssh-action@v1.0.0
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SERVER_HOST }}
|
||||||
|
username: ${{ secrets.SERVER_USERNAME }}
|
||||||
|
password: ${{ secrets.SERVER_PASSWORD }}
|
||||||
|
port: 22
|
||||||
|
script: |
|
||||||
|
# 进入项目目录
|
||||||
|
cd /root/V2_micropython/
|
||||||
|
|
||||||
|
# 拉取最新代码
|
||||||
|
echo "Pulling latest code..."
|
||||||
|
git pull
|
||||||
|
|
||||||
|
# 进入 websocket_server 目录
|
||||||
|
cd websocket_server
|
||||||
|
|
||||||
|
# 重启服务
|
||||||
|
echo "Restarting Docker Compose services..."
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 检查部署状态
|
||||||
|
echo "Checking deployment status..."
|
||||||
|
docker compose ps
|
||||||
157
display.py
157
display.py
@@ -211,7 +211,37 @@ class Display:
|
|||||||
self.tft.line(x, y + 5, x + 3, y + 8, st7789.GREEN)
|
self.tft.line(x, y + 5, x + 3, y + 8, st7789.GREEN)
|
||||||
self.tft.line(x + 3, y + 8, x + 10, y, st7789.GREEN)
|
self.tft.line(x + 3, y + 8, x + 10, y, st7789.GREEN)
|
||||||
|
|
||||||
def render_confirm_screen(self, asr_text=""):
|
def measure_text(self, text):
|
||||||
|
"""计算文本宽度"""
|
||||||
|
width = 0
|
||||||
|
for char in text:
|
||||||
|
if ord(char) > 127:
|
||||||
|
width += 16
|
||||||
|
else:
|
||||||
|
width += 8
|
||||||
|
return width
|
||||||
|
|
||||||
|
def draw_centered_text(self, text, x, y, w, h, color, bg=None):
|
||||||
|
"""在指定区域居中显示文本"""
|
||||||
|
if not self.tft: return
|
||||||
|
text_width = self.measure_text(text)
|
||||||
|
start_x = x + (w - text_width) // 2
|
||||||
|
start_y = y + (h - 16) // 2
|
||||||
|
|
||||||
|
# 确保不超出边界
|
||||||
|
start_x = max(x, start_x)
|
||||||
|
|
||||||
|
if bg is not None:
|
||||||
|
self.tft.fill_rect(x, y, w, h, bg)
|
||||||
|
self.text(text, start_x, start_y, color)
|
||||||
|
|
||||||
|
def draw_button(self, text, x, y, w, h, bg_color, text_color=st7789.WHITE):
|
||||||
|
"""绘制带居中文字的按钮"""
|
||||||
|
if not self.tft: return
|
||||||
|
self.tft.fill_rect(x, y, w, h, bg_color)
|
||||||
|
self.draw_centered_text(text, x, y, w, h, text_color)
|
||||||
|
|
||||||
|
def render_confirm_screen(self, asr_text="", waiting=False):
|
||||||
"""渲染确认界面"""
|
"""渲染确认界面"""
|
||||||
if not self.tft:
|
if not self.tft:
|
||||||
return
|
return
|
||||||
@@ -220,12 +250,14 @@ class Display:
|
|||||||
|
|
||||||
# Header
|
# Header
|
||||||
self.tft.fill_rect(0, 0, 240, 30, st7789.CYAN)
|
self.tft.fill_rect(0, 0, 240, 30, st7789.CYAN)
|
||||||
self.text("说完了吗?", 75, 8, st7789.BLACK)
|
self.draw_centered_text("说完了吗?", 0, 0, 240, 30, st7789.BLACK)
|
||||||
|
|
||||||
# Content box
|
# Content box
|
||||||
self.tft.fill_rect(10, 50, 220, 90, 0x4208) # DARKGREY
|
self.tft.fill_rect(10, 50, 220, 90, 0x4208) # DARKGREY
|
||||||
|
|
||||||
if asr_text:
|
if waiting:
|
||||||
|
self.draw_centered_text("正在识别...", 10, 50, 220, 90, st7789.YELLOW)
|
||||||
|
elif asr_text:
|
||||||
# 自动换行逻辑
|
# 自动换行逻辑
|
||||||
max_width = 200
|
max_width = 200
|
||||||
lines = []
|
lines = []
|
||||||
@@ -254,18 +286,119 @@ class Display:
|
|||||||
|
|
||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
# 计算水平居中
|
# 计算水平居中
|
||||||
line_width = 0
|
line_width = self.measure_text(line)
|
||||||
for c in line:
|
|
||||||
line_width += 16 if ord(c) > 127 else 8
|
|
||||||
|
|
||||||
center_x = 20 + (200 - line_width) // 2
|
center_x = 20 + (200 - line_width) // 2
|
||||||
self.text(line, center_x, start_y + i * 20, st7789.WHITE, wait=False)
|
self.text(line, center_x, start_y + i * 20, st7789.WHITE, wait=False)
|
||||||
else:
|
else:
|
||||||
self.text("未识别到文字", 70, 85, st7789.WHITE)
|
self.draw_centered_text("未识别到文字", 10, 50, 220, 90, st7789.WHITE)
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
self.tft.fill_rect(20, 160, 90, 30, st7789.GREEN)
|
self.draw_button("短按确认", 20, 160, 90, 30, st7789.GREEN, st7789.BLACK)
|
||||||
self.text("短按确认", 30, 168, st7789.BLACK)
|
self.draw_button("长按重录", 130, 160, 90, 30, st7789.RED, st7789.WHITE)
|
||||||
|
|
||||||
self.tft.fill_rect(130, 160, 90, 30, st7789.RED)
|
def render_recording_screen(self, asr_text="", audio_level=0, is_recording=False):
|
||||||
self.text("长按重录", 140, 168, st7789.WHITE)
|
"""渲染录音界面"""
|
||||||
|
if not self.tft:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.tft.fill(st7789.BLACK)
|
||||||
|
|
||||||
|
self.tft.fill_rect(0, 0, 240, 30, st7789.WHITE)
|
||||||
|
self.draw_centered_text("语音识别", 0, 0, 240, 30, st7789.BLACK)
|
||||||
|
|
||||||
|
self.draw_mic_icon(105, 50)
|
||||||
|
|
||||||
|
if audio_level > 0:
|
||||||
|
bar_width = min(int(audio_level * 2), 200)
|
||||||
|
self.tft.fill_rect(20, 100, bar_width, 10, st7789.GREEN)
|
||||||
|
|
||||||
|
if asr_text:
|
||||||
|
self.text(asr_text[:20], 20, 130, st7789.WHITE, wait=False)
|
||||||
|
|
||||||
|
if is_recording:
|
||||||
|
self.draw_button("松开停止", 60, 200, 120, 25, st7789.RED, st7789.WHITE)
|
||||||
|
else:
|
||||||
|
self.draw_button("长按录音", 60, 200, 120, 25, st7789.BLUE, st7789.WHITE)
|
||||||
|
|
||||||
|
def render_result_screen(self, status="", prompt="", image_received=False):
|
||||||
|
"""渲染结果界面"""
|
||||||
|
if not self.tft:
|
||||||
|
return
|
||||||
|
|
||||||
|
if status == "OPTIMIZING":
|
||||||
|
self.tft.fill(st7789.BLACK)
|
||||||
|
self.tft.fill_rect(0, 0, 240, 30, st7789.WHITE)
|
||||||
|
self.draw_centered_text("AI 生成中", 0, 0, 240, 30, st7789.BLACK)
|
||||||
|
|
||||||
|
self.draw_centered_text("正在思考...", 0, 60, 240, 20, st7789.CYAN)
|
||||||
|
self.draw_centered_text("优化提示词中", 0, 80, 240, 20, st7789.CYAN)
|
||||||
|
self.draw_progress_bar(40, 110, 160, 6, 0.3, st7789.CYAN)
|
||||||
|
|
||||||
|
elif status == "RENDERING":
|
||||||
|
self.tft.fill(st7789.BLACK)
|
||||||
|
self.tft.fill_rect(0, 0, 240, 30, st7789.WHITE)
|
||||||
|
self.draw_centered_text("AI 生成中", 0, 0, 240, 30, st7789.BLACK)
|
||||||
|
|
||||||
|
self.draw_centered_text("正在绘画...", 0, 60, 240, 20, st7789.YELLOW)
|
||||||
|
self.draw_centered_text("AI作画中", 0, 80, 240, 20, st7789.YELLOW)
|
||||||
|
self.draw_progress_bar(40, 110, 160, 6, 0.7, st7789.YELLOW)
|
||||||
|
|
||||||
|
elif status == "COMPLETE" or image_received:
|
||||||
|
# Don't clear screen, image is already there
|
||||||
|
self.tft.fill_rect(230, 230, 10, 10, st7789.GREEN)
|
||||||
|
|
||||||
|
elif status == "ERROR":
|
||||||
|
self.tft.fill(st7789.BLACK)
|
||||||
|
self.tft.fill_rect(0, 0, 240, 30, st7789.WHITE)
|
||||||
|
self.draw_centered_text("AI 生成中", 0, 0, 240, 30, st7789.BLACK)
|
||||||
|
self.draw_centered_text("生成失败", 0, 50, 240, 20, st7789.RED)
|
||||||
|
|
||||||
|
if prompt and not image_received:
|
||||||
|
self.tft.fill_rect(10, 140, 220, 50, 0x2124) # Dark Grey
|
||||||
|
self.text("提示词:", 15, 145, st7789.CYAN)
|
||||||
|
self.text(prompt[:25] + "..." if len(prompt) > 25 else prompt, 15, 165, st7789.WHITE)
|
||||||
|
|
||||||
|
if not image_received:
|
||||||
|
self.draw_button("长按返回", 60, 210, 120, 25, st7789.BLUE, st7789.WHITE)
|
||||||
|
|
||||||
|
def draw_loading_spinner(self, x, y, angle, color=st7789.WHITE):
|
||||||
|
"""绘制旋转加载图标"""
|
||||||
|
if not self.tft:
|
||||||
|
return
|
||||||
|
|
||||||
|
import math
|
||||||
|
rad = math.radians(angle)
|
||||||
|
|
||||||
|
center_x = x + 10
|
||||||
|
center_y = y + 10
|
||||||
|
radius = 8
|
||||||
|
|
||||||
|
for i in range(8):
|
||||||
|
theta = math.radians(i * 45) + rad
|
||||||
|
px = int(center_x + radius * math.cos(theta))
|
||||||
|
py = int(center_y + radius * math.sin(theta))
|
||||||
|
|
||||||
|
self.tft.pixel(px, py, color)
|
||||||
|
|
||||||
|
def draw_progress_bar(self, x, y, width, height, progress, color=st7789.CYAN):
|
||||||
|
"""绘制进度条"""
|
||||||
|
if not self.tft:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.tft.fill_rect(x, y, width, height, 0x4208) # DARKGREY
|
||||||
|
if progress > 0:
|
||||||
|
bar_width = int(width * min(progress, 1.0))
|
||||||
|
self.tft.fill_rect(x, y, bar_width, height, color)
|
||||||
|
|
||||||
|
def draw_mic_icon(self, x, y, active=True):
|
||||||
|
"""绘制麦克风图标"""
|
||||||
|
if not self.tft:
|
||||||
|
return
|
||||||
|
|
||||||
|
color = st7789.GREEN if active else 0x4208 # DARKGREY
|
||||||
|
|
||||||
|
self.tft.fill_rect(x + 5, y, 10, 5, color)
|
||||||
|
self.tft.fill_rect(x + 3, y + 5, 14, 10, color)
|
||||||
|
self.tft.fill_rect(x + 8, y + 15, 4, 8, color)
|
||||||
|
self.tft.fill_rect(x + 6, y + 23, 8, 2, color)
|
||||||
|
self.tft.fill_rect(x + 8, y + 25, 4, 3, color)
|
||||||
|
|||||||
203
main.py
203
main.py
@@ -79,7 +79,7 @@ def connect_wifi(display=None, max_retries=5):
|
|||||||
# 简单的加载动画
|
# 简单的加载动画
|
||||||
if display and display.tft:
|
if display and display.tft:
|
||||||
if time.ticks_ms() % 200 < 50: # 节流刷新
|
if time.ticks_ms() % 200 < 50: # 节流刷新
|
||||||
draw_loading_spinner(display, 120, 150, spinner_angle, st7789.CYAN)
|
display.draw_loading_spinner(120, 150, spinner_angle, st7789.CYAN)
|
||||||
spinner_angle = (spinner_angle + 45) % 360
|
spinner_angle = (spinner_angle + 45) % 360
|
||||||
|
|
||||||
if wlan.isconnected():
|
if wlan.isconnected():
|
||||||
@@ -108,164 +108,6 @@ def connect_wifi(display=None, max_retries=5):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def draw_mic_icon(display, x, y, active=True):
|
|
||||||
"""绘制麦克风图标"""
|
|
||||||
if not display or not display.tft:
|
|
||||||
return
|
|
||||||
|
|
||||||
color = st7789.GREEN if active else DARKGREY
|
|
||||||
|
|
||||||
display.tft.fill_rect(x + 5, y, 10, 5, color)
|
|
||||||
display.tft.fill_rect(x + 3, y + 5, 14, 10, color)
|
|
||||||
display.tft.fill_rect(x + 8, y + 15, 4, 8, color)
|
|
||||||
display.tft.fill_rect(x + 6, y + 23, 8, 2, color)
|
|
||||||
display.tft.fill_rect(x + 8, y + 25, 4, 3, color)
|
|
||||||
|
|
||||||
|
|
||||||
def draw_loading_spinner(display, x, y, angle, color=st7789.WHITE):
|
|
||||||
"""绘制旋转加载图标"""
|
|
||||||
if not display or not display.tft:
|
|
||||||
return
|
|
||||||
|
|
||||||
import math
|
|
||||||
rad = math.radians(angle)
|
|
||||||
|
|
||||||
# Clear previous (simple erase)
|
|
||||||
# This is tricky without a buffer, so we just draw over.
|
|
||||||
# For better performance we should remember previous pos.
|
|
||||||
|
|
||||||
center_x = x + 10
|
|
||||||
center_y = y + 10
|
|
||||||
radius = 8
|
|
||||||
|
|
||||||
for i in range(8):
|
|
||||||
theta = math.radians(i * 45) + rad
|
|
||||||
px = int(center_x + radius * math.cos(theta))
|
|
||||||
py = int(center_y + radius * math.sin(theta))
|
|
||||||
|
|
||||||
# Brightness based on angle (simulated by color or size)
|
|
||||||
# Here we just draw dots
|
|
||||||
display.tft.pixel(px, py, color)
|
|
||||||
|
|
||||||
def draw_check_icon(display, x, y):
|
|
||||||
"""绘制勾选图标"""
|
|
||||||
if not display or not display.tft:
|
|
||||||
return
|
|
||||||
|
|
||||||
display.tft.line(x, y + 5, x + 3, y + 8, st7789.GREEN)
|
|
||||||
display.tft.line(x + 3, y + 8, x + 10, y, st7789.GREEN)
|
|
||||||
|
|
||||||
|
|
||||||
def draw_progress_bar(display, x, y, width, height, progress, color=st7789.CYAN):
|
|
||||||
"""绘制进度条"""
|
|
||||||
if not display or not display.tft:
|
|
||||||
return
|
|
||||||
|
|
||||||
display.tft.fill_rect(x, y, width, height, DARKGREY)
|
|
||||||
if progress > 0:
|
|
||||||
bar_width = int(width * min(progress, 1.0))
|
|
||||||
display.tft.fill_rect(x, y, bar_width, height, color)
|
|
||||||
|
|
||||||
|
|
||||||
def render_recording_screen(display, asr_text="", audio_level=0, is_recording=False):
|
|
||||||
"""渲染录音界面"""
|
|
||||||
if not display or not display.tft:
|
|
||||||
return
|
|
||||||
|
|
||||||
display.tft.fill(st7789.BLACK)
|
|
||||||
|
|
||||||
display.tft.fill_rect(0, 0, 240, 30, st7789.WHITE)
|
|
||||||
display.text("语音识别", 80, 8, st7789.BLACK)
|
|
||||||
|
|
||||||
draw_mic_icon(display, 105, 50, True)
|
|
||||||
|
|
||||||
if audio_level > 0:
|
|
||||||
bar_width = min(int(audio_level * 2), 200)
|
|
||||||
display.tft.fill_rect(20, 100, bar_width, 10, st7789.GREEN)
|
|
||||||
|
|
||||||
if asr_text:
|
|
||||||
display.text(asr_text[:20], 20, 130, st7789.WHITE, wait=False)
|
|
||||||
|
|
||||||
display.tft.fill_rect(60, 200, 120, 25, st7789.RED)
|
|
||||||
if is_recording:
|
|
||||||
display.text("松开停止", 85, 205, st7789.WHITE)
|
|
||||||
else:
|
|
||||||
display.text("长按录音", 85, 205, st7789.WHITE)
|
|
||||||
|
|
||||||
|
|
||||||
def render_confirm_screen(display, asr_text=""):
|
|
||||||
"""渲染确认界面"""
|
|
||||||
if not display or not display.tft:
|
|
||||||
return
|
|
||||||
|
|
||||||
display.tft.fill(st7789.BLACK)
|
|
||||||
|
|
||||||
display.tft.fill_rect(0, 0, 240, 30, st7789.CYAN)
|
|
||||||
display.text("说完了吗?", 75, 8, st7789.BLACK)
|
|
||||||
|
|
||||||
display.tft.fill_rect(10, 50, 220, 80, DARKGREY)
|
|
||||||
display.text(asr_text if asr_text else "未识别到文字", 20, 75, st7789.WHITE)
|
|
||||||
|
|
||||||
display.tft.fill_rect(20, 150, 80, 30, st7789.GREEN)
|
|
||||||
display.text("短按确认", 30, 158, st7789.BLACK)
|
|
||||||
|
|
||||||
display.tft.fill_rect(140, 150, 80, 30, st7789.RED)
|
|
||||||
display.text("长按重录", 155, 158, st7789.WHITE)
|
|
||||||
|
|
||||||
|
|
||||||
def render_result_screen(display, status="", prompt="", image_received=False):
|
|
||||||
"""渲染结果界面"""
|
|
||||||
if not display or not display.tft:
|
|
||||||
return
|
|
||||||
|
|
||||||
if status == "OPTIMIZING":
|
|
||||||
display.tft.fill(st7789.BLACK)
|
|
||||||
display.tft.fill_rect(0, 0, 240, 30, st7789.WHITE)
|
|
||||||
display.text("AI 生成中", 80, 8, st7789.BLACK)
|
|
||||||
|
|
||||||
display.text("正在思考...", 80, 60, st7789.CYAN)
|
|
||||||
display.text("优化提示词中", 70, 80, st7789.CYAN)
|
|
||||||
draw_progress_bar(display, 40, 110, 160, 6, 0.3, st7789.CYAN)
|
|
||||||
# Spinner will be drawn by main loop
|
|
||||||
|
|
||||||
elif status == "RENDERING":
|
|
||||||
display.tft.fill(st7789.BLACK)
|
|
||||||
display.tft.fill_rect(0, 0, 240, 30, st7789.WHITE)
|
|
||||||
display.text("AI 生成中", 80, 8, st7789.BLACK)
|
|
||||||
|
|
||||||
display.text("正在绘画...", 80, 60, st7789.YELLOW)
|
|
||||||
display.text("AI作画中", 85, 80, st7789.YELLOW)
|
|
||||||
draw_progress_bar(display, 40, 110, 160, 6, 0.7, st7789.YELLOW)
|
|
||||||
# Spinner will be drawn by main loop
|
|
||||||
|
|
||||||
elif status == "COMPLETE" or image_received:
|
|
||||||
# Don't clear screen, image is already there
|
|
||||||
|
|
||||||
# Draw a small indicator to show it's done, but don't cover the image
|
|
||||||
# Maybe a small green dot in the corner?
|
|
||||||
display.tft.fill_rect(230, 230, 10, 10, st7789.GREEN)
|
|
||||||
|
|
||||||
elif status == "ERROR":
|
|
||||||
display.tft.fill(st7789.BLACK)
|
|
||||||
display.tft.fill_rect(0, 0, 240, 30, st7789.WHITE)
|
|
||||||
display.text("AI 生成中", 80, 8, st7789.BLACK)
|
|
||||||
display.text("生成失败", 80, 50, st7789.RED)
|
|
||||||
|
|
||||||
if prompt and not image_received:
|
|
||||||
display.tft.fill_rect(10, 140, 220, 50, 0x2124) # Dark Grey
|
|
||||||
display.text("提示词:", 15, 145, st7789.CYAN)
|
|
||||||
display.text(prompt[:25] + "..." if len(prompt) > 25 else prompt, 15, 165, st7789.WHITE)
|
|
||||||
|
|
||||||
# Only show back button if not showing full image, or maybe show it transparently?
|
|
||||||
# For now, let's not cover the image with the button hint
|
|
||||||
if not image_received:
|
|
||||||
display.tft.fill_rect(60, 210, 120, 25, st7789.BLUE)
|
|
||||||
display.text("长按返回", 90, 215, st7789.WHITE)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -368,9 +210,6 @@ def process_message(msg, display, image_state, image_data_list):
|
|||||||
def print_asr(text, display=None):
|
def print_asr(text, display=None):
|
||||||
"""打印ASR结果"""
|
"""打印ASR结果"""
|
||||||
print(f"ASR: {text}")
|
print(f"ASR: {text}")
|
||||||
if display and display.tft:
|
|
||||||
display.fill_rect(0, 40, 240, 160, st7789.BLACK)
|
|
||||||
display.text(text, 0, 40, st7789.WHITE, wait=False)
|
|
||||||
|
|
||||||
|
|
||||||
def get_boot_button_action(boot_btn):
|
def get_boot_button_action(boot_btn):
|
||||||
@@ -459,6 +298,7 @@ def main():
|
|||||||
current_status = ""
|
current_status = ""
|
||||||
image_generation_done = False
|
image_generation_done = False
|
||||||
confirm_waiting = False
|
confirm_waiting = False
|
||||||
|
recording_stop_time = 0
|
||||||
|
|
||||||
def connect_ws(force=False):
|
def connect_ws(force=False):
|
||||||
nonlocal ws
|
nonlocal ws
|
||||||
@@ -502,13 +342,13 @@ def main():
|
|||||||
# WiFi 和 WS 都连接成功后,进入录音界面
|
# WiFi 和 WS 都连接成功后,进入录音界面
|
||||||
ui_screen = UI_SCREEN_RECORDING
|
ui_screen = UI_SCREEN_RECORDING
|
||||||
if display.tft:
|
if display.tft:
|
||||||
render_recording_screen(display, "", 0, False)
|
display.render_recording_screen("", 0, False)
|
||||||
else:
|
else:
|
||||||
print("Running in offline mode")
|
print("Running in offline mode")
|
||||||
# 即使离线也进入录音界面(虽然不能用)
|
# 即使离线也进入录音界面(虽然不能用)
|
||||||
ui_screen = UI_SCREEN_RECORDING
|
ui_screen = UI_SCREEN_RECORDING
|
||||||
if display.tft:
|
if display.tft:
|
||||||
render_recording_screen(display, "离线模式", 0, False)
|
display.render_recording_screen("离线模式", 0, False)
|
||||||
|
|
||||||
read_buf = bytearray(4096)
|
read_buf = bytearray(4096)
|
||||||
last_audio_level = 0
|
last_audio_level = 0
|
||||||
@@ -533,18 +373,25 @@ def main():
|
|||||||
if time.ticks_diff(now, last_spinner_time) > 100:
|
if time.ticks_diff(now, last_spinner_time) > 100:
|
||||||
if display.tft:
|
if display.tft:
|
||||||
# Clear previous spinner (draw in BLACK)
|
# Clear previous spinner (draw in BLACK)
|
||||||
draw_loading_spinner(display, 110, 80, spinner_angle, st7789.BLACK)
|
display.draw_loading_spinner(110, 80, spinner_angle, st7789.BLACK)
|
||||||
|
|
||||||
spinner_angle = (spinner_angle + 45) % 360
|
spinner_angle = (spinner_angle + 45) % 360
|
||||||
|
|
||||||
# Draw new spinner
|
# Draw new spinner
|
||||||
color = st7789.CYAN if current_status == "OPTIMIZING" else st7789.YELLOW
|
color = st7789.CYAN if current_status == "OPTIMIZING" else st7789.YELLOW
|
||||||
draw_loading_spinner(display, 110, 80, spinner_angle, color)
|
display.draw_loading_spinner(110, 80, spinner_angle, color)
|
||||||
|
|
||||||
last_spinner_time = now
|
last_spinner_time = now
|
||||||
|
|
||||||
btn_action = get_boot_button_action(boot_btn)
|
btn_action = get_boot_button_action(boot_btn)
|
||||||
|
|
||||||
|
# ASR timeout check
|
||||||
|
if ui_screen == UI_SCREEN_CONFIRM and confirm_waiting:
|
||||||
|
if time.ticks_diff(time.ticks_ms(), recording_stop_time) > 2000:
|
||||||
|
confirm_waiting = False
|
||||||
|
if display.tft:
|
||||||
|
display.render_confirm_screen("", waiting=False)
|
||||||
|
|
||||||
# Hold to Record Logic (Press to Start, Release to Stop)
|
# Hold to Record Logic (Press to Start, Release to Stop)
|
||||||
if ui_screen == UI_SCREEN_RECORDING:
|
if ui_screen == UI_SCREEN_RECORDING:
|
||||||
if boot_btn.value() == 0 and not is_recording:
|
if boot_btn.value() == 0 and not is_recording:
|
||||||
@@ -556,7 +403,7 @@ def main():
|
|||||||
current_status = ""
|
current_status = ""
|
||||||
image_generation_done = False
|
image_generation_done = False
|
||||||
if display.tft:
|
if display.tft:
|
||||||
render_recording_screen(display, "", 0, True)
|
display.render_recording_screen("", 0, True)
|
||||||
if ws is None or not ws.is_connected():
|
if ws is None or not ws.is_connected():
|
||||||
connect_ws()
|
connect_ws()
|
||||||
if ws and ws.is_connected():
|
if ws and ws.is_connected():
|
||||||
@@ -574,8 +421,13 @@ def main():
|
|||||||
is_recording = False
|
is_recording = False
|
||||||
ui_screen = UI_SCREEN_CONFIRM
|
ui_screen = UI_SCREEN_CONFIRM
|
||||||
image_generation_done = False
|
image_generation_done = False
|
||||||
|
|
||||||
|
# 启动等待计时
|
||||||
|
confirm_waiting = True
|
||||||
|
recording_stop_time = time.ticks_ms()
|
||||||
|
|
||||||
if display.tft:
|
if display.tft:
|
||||||
render_confirm_screen(display, current_asr_text)
|
display.render_confirm_screen(current_asr_text, waiting=True)
|
||||||
# Consume action to prevent triggering other events
|
# Consume action to prevent triggering other events
|
||||||
btn_action = 0
|
btn_action = 0
|
||||||
|
|
||||||
@@ -591,7 +443,7 @@ def main():
|
|||||||
ui_screen = UI_SCREEN_RESULT
|
ui_screen = UI_SCREEN_RESULT
|
||||||
image_generation_done = False
|
image_generation_done = False
|
||||||
if display.tft:
|
if display.tft:
|
||||||
render_result_screen(display, "OPTIMIZING", current_asr_text, False)
|
display.render_result_screen("OPTIMIZING", current_asr_text, False)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
elif btn_action == 2:
|
elif btn_action == 2:
|
||||||
@@ -603,7 +455,7 @@ def main():
|
|||||||
is_recording = False
|
is_recording = False
|
||||||
image_generation_done = False
|
image_generation_done = False
|
||||||
if display.tft:
|
if display.tft:
|
||||||
render_recording_screen(display, "", 0, False)
|
display.render_recording_screen("", 0, False)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
elif btn_action == 3:
|
elif btn_action == 3:
|
||||||
@@ -637,12 +489,13 @@ def main():
|
|||||||
if event_data[0] == "asr":
|
if event_data[0] == "asr":
|
||||||
current_asr_text = event_data[1]
|
current_asr_text = event_data[1]
|
||||||
print(f"Received ASR: {current_asr_text}")
|
print(f"Received ASR: {current_asr_text}")
|
||||||
|
confirm_waiting = False
|
||||||
|
|
||||||
# 收到 ASR 结果,跳转到 CONFIRM 界面
|
# 收到 ASR 结果,跳转到 CONFIRM 界面
|
||||||
if ui_screen == UI_SCREEN_RECORDING or ui_screen == UI_SCREEN_CONFIRM:
|
if ui_screen == UI_SCREEN_RECORDING or ui_screen == UI_SCREEN_CONFIRM:
|
||||||
ui_screen = UI_SCREEN_CONFIRM
|
ui_screen = UI_SCREEN_CONFIRM
|
||||||
if display.tft:
|
if display.tft:
|
||||||
render_confirm_screen(display, current_asr_text)
|
display.render_confirm_screen(current_asr_text, waiting=False)
|
||||||
|
|
||||||
elif event_data[0] == "font_update":
|
elif event_data[0] == "font_update":
|
||||||
# 如果还在录音界面等待,刷新一下(虽然可能已经跳到 CONFIRM 了)
|
# 如果还在录音界面等待,刷新一下(虽然可能已经跳到 CONFIRM 了)
|
||||||
@@ -652,21 +505,21 @@ def main():
|
|||||||
current_status = event_data[1]
|
current_status = event_data[1]
|
||||||
status_text = event_data[2] if len(event_data) > 2 else ""
|
status_text = event_data[2] if len(event_data) > 2 else ""
|
||||||
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
||||||
render_result_screen(display, current_status, current_prompt, image_generation_done)
|
display.render_result_screen(current_status, current_prompt, image_generation_done)
|
||||||
|
|
||||||
elif event_data[0] == "prompt":
|
elif event_data[0] == "prompt":
|
||||||
current_prompt = event_data[1]
|
current_prompt = event_data[1]
|
||||||
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
||||||
render_result_screen(display, current_status, current_prompt, image_generation_done)
|
display.render_result_screen(current_status, current_prompt, image_generation_done)
|
||||||
|
|
||||||
elif event_data[0] == "image_done":
|
elif event_data[0] == "image_done":
|
||||||
image_generation_done = True
|
image_generation_done = True
|
||||||
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
||||||
render_result_screen(display, "COMPLETE", current_prompt, True)
|
display.render_result_screen("COMPLETE", current_prompt, True)
|
||||||
|
|
||||||
elif event_data[0] == "error":
|
elif event_data[0] == "error":
|
||||||
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
||||||
render_result_screen(display, "ERROR", current_prompt, False)
|
display.render_result_screen("ERROR", current_prompt, False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"WS Recv Error: {e}")
|
print(f"WS Recv Error: {e}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user