Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
# 英文字体
"lora_regular": "Lora-Regular.ttf",
"lora_bold": "Lora-Bold.ttf",
"lora_italic": "Lora-Italic.ttf",
"inter_medium": "Inter_24pt-Medium.ttf",
}

Expand Down Expand Up @@ -397,4 +398,4 @@ def get_default_llm_model_for_provider(provider: Optional[str]) -> str:
return "deepseek-chat"
if p == "moonshot":
return "moonshot-v1-8k"
return DEFAULT_LLM_MODEL
return DEFAULT_LLM_MODEL
3 changes: 3 additions & 0 deletions backend/core/modes/builtin/en/word_of_the_day.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
{
"type": "text",
"field": "phonetic",
"font_name": "Lora-Regular.ttf",
"font_size": 11,
"align": "center",
"max_lines": 1
Expand Down Expand Up @@ -121,6 +122,7 @@
{
"type": "text",
"field": "phonetic",
"font_name": "Lora-Regular.ttf",
"font_size": 8,
"align": "center"
},
Expand Down Expand Up @@ -160,6 +162,7 @@
{
"type": "text",
"field": "phonetic",
"font_name": "Lora-Regular.ttf",
"font_size": 16,
"align": "center",
"max_lines": 1
Expand Down
2 changes: 1 addition & 1 deletion backend/core/modes/builtin/word_of_the_day.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
{
"type": "text",
"field": "phonetic",
"font": "noto_serif_light",
"font": "lora_regular",
"font_size": 11,
"align": "center",
"max_lines": 1
Expand Down
45 changes: 23 additions & 22 deletions backend/scripts/setup_fonts.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#!/usr/bin/env python3
from __future__ import annotations

import argparse
import json
import os
import shutil
import sys
import tempfile
import urllib.error
import urllib.request
import argparse
import json
import os
import shutil
import sys
import tempfile
import urllib.error
import urllib.request

BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
FONTS_DIR = os.path.join(BACKEND_DIR, "fonts")
Expand All @@ -28,6 +28,7 @@
"Lora": [
"Lora-Regular.ttf",
"Lora-Bold.ttf",
"Lora-Italic.ttf",
],
"Inter": [
"Inter_24pt-Medium.ttf",
Expand Down Expand Up @@ -84,9 +85,9 @@ def _download_file(url: str, target: str, user_agent: str) -> None:
with os.fdopen(fd, "wb") as tmp_f:
tmp_f.write(data)
os.replace(tmp_path, target)
except OSError:
os.unlink(tmp_path)
raise
except OSError:
os.unlink(tmp_path)
raise


def _copy_if_needed(src: str, dst: str, force: bool) -> bool:
Expand Down Expand Up @@ -125,15 +126,15 @@ def _install_vector_fonts(force: bool) -> tuple[int, int]:
try:
manifest = _fetch_manifest(family)
url_maps[family] = _build_url_map(manifest)
except (
json.JSONDecodeError,
OSError,
TimeoutError,
urllib.error.URLError,
ValueError,
) as exc:
print(f" [ERROR] Failed manifest for {display_name}: {exc}", file=sys.stderr)
url_maps[family] = {}
except (
json.JSONDecodeError,
OSError,
TimeoutError,
urllib.error.URLError,
ValueError,
) as exc:
print(f" [ERROR] Failed manifest for {display_name}: {exc}", file=sys.stderr)
url_maps[family] = {}

success_count = 0
for family, name in all_needed:
Expand All @@ -147,8 +148,8 @@ def _install_vector_fonts(force: bool) -> tuple[int, int]:
size_mb = os.path.getsize(target) / (1024 * 1024)
print(f" \u2713 {name} ({size_mb:.1f} MB)")
success_count += 1
except (OSError, TimeoutError, urllib.error.URLError, ValueError) as exc:
print(f" [ERROR] Failed {name}: {exc}", file=sys.stderr)
except (OSError, TimeoutError, urllib.error.URLError, ValueError) as exc:
print(f" [ERROR] Failed {name}: {exc}", file=sys.stderr)
return success_count, len(all_needed)


Expand Down
15 changes: 11 additions & 4 deletions firmware/src/display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,11 @@ void showModePreview(const char *modeName) {
int loadY = H / 2 + (H < 200 ? 10 : 20);
drawText(loading, loadX, loadY, loadScale);

epdDisplayFast(imgBuf);
#if defined(EPD_PANEL_583) || defined(EPD_PANEL_75)
epdDisplay(imgBuf);
#else
epdDisplayFast(imgBuf);
#endif
Serial.printf("Mode preview shown: %s\n", modeName);
}

Expand All @@ -410,12 +414,15 @@ void smartDisplay(const uint8_t *image) {
Serial.printf("smartDisplay: full refresh (cycle %d)\n", refreshCount);
epdDisplay(image);
} else {
#if defined(EPD_PANEL_42_GXEPD2_GYE042A87)
Serial.printf("smartDisplay: full refresh fallback (cycle %d)\n", refreshCount);
#if defined(EPD_PANEL_583) || defined(EPD_PANEL_75) || defined(EPD_PANEL_42_GXEPD2_GYE042A87)
// Large/slow panels: always full refresh to prevent ghosting.
// These devices refresh every 10-60 min so fast refresh offers no benefit.
Serial.printf("smartDisplay: full refresh (large panel, cycle %d)\n", refreshCount);
epdDisplay(image);
#else
Serial.printf("smartDisplay: fast refresh (cycle %d)\n", refreshCount);
#endif
epdDisplayFast(image);
#endif
}
refreshCount++;
}
17 changes: 6 additions & 11 deletions firmware/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,34 +358,29 @@ void loop() {
static unsigned long lastAlertPollAt = 0;
static bool alertVisible = false;
static unsigned long alertShownAt = 0;
static uint8_t alertBackupBuf[IMG_BUF_LEN];
static bool hasAlertBackup = false;

if (focusListening) {
unsigned long nowMs = millis();
if (!alertVisible) {
const unsigned long ALERT_INTERVAL_MS = 10000UL;
if (lastAlertPollAt == 0 || nowMs - lastAlertPollAt >= ALERT_INTERVAL_MS) {
lastAlertPollAt = nowMs;
memcpy(alertBackupBuf, imgBuf, IMG_BUF_LEN);
hasAlertBackup = true;
if (fetchFocusAlertBMP()) {
epdDisplayFast(imgBuf);
smartDisplay(imgBuf);
alertVisible = true;
alertShownAt = nowMs;
} else {
if (hasAlertBackup) memcpy(imgBuf, alertBackupBuf, IMG_BUF_LEN);
hasAlertBackup = false;
// fetchFocusAlertBMP may have partially written imgBuf on error;
// restore from flash cache (saved after every successful fetch).
cacheLoad(imgBuf, IMG_BUF_LEN);
}
}
} else {
const unsigned long ALERT_DISPLAY_MS = 30000UL;
if (nowMs - alertShownAt >= ALERT_DISPLAY_MS) {
if (hasAlertBackup) {
memcpy(imgBuf, alertBackupBuf, IMG_BUF_LEN);
epdDisplayFast(imgBuf);
if (cacheLoad(imgBuf, IMG_BUF_LEN)) {
smartDisplay(imgBuf);
}
hasAlertBackup = false;
alertVisible = false;
}
}
Expand Down