SHA256
homeserver: рендейм subserver→homeserver, документ деривации ключей, запрет пустого пароля
Основное (наша работа в этой сессии): - Переименование «subserver» → «homeserver» по всему проекту: основной ESP32-скетч (папка shine_subserver_ui → shine_homeserver_ui, .ino, flash-скрипт, режим burn.sh homeserver-ui), скетч lvgl_nav_minimal_test (ключ homeserver.key:<имя>), spec-доки reference/*, формат PDA (терминология session_type=100 «Homeserver пользователя»), константа SESSION_TYPE_HOMESERVER в JS и Rust (значение 100 не менялось, формат не затронут), pending/future доки, AGENTS.md, DAO-док. Сохранены отдельный lvgl_subserver_touch_test и историческая пометка о рендейме в DERIVATION.md. - Новый источник истины по деривации ключей: Dev_Docs/Keys/DERIVATION.md (Argon2id-секрет из пароля, формула Ed25519(SHA-256(base64(secret)|suffix)), суффиксы root/bch/dev/homeserver.key, Solana-ключ = dev.key). Уточнены роли root (главный/master) и dev (пополняемый кошелёк) в Dev_Docs/Keys/README.md. - UI: убран легаси-путь пустого пароля (derivePasswordSeed и др.), deriveMasterSecretFromPassword бросает ошибку на пустом пароле, register-view блокирует пустой пароль; экран пополнения переведён на канонический device-адрес из preGeneratedKeyBundle (удалён расходящийся deriveWalletFromPassword). Включены также параллельные правки Solana-аудита №3 (были в рабочем дереве, переплетены в lib.rs): - shine_users: defense-in-depth «строгий список аккаунтов» (require!(it.next().is_none())) в init/update economy config и create/update user PDA, плюс описание в doc/programs/shine_users.md; - Dev_Docs/audit/Solana-audit-3-by-Claude-12июня2026.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
cf6a2830c8
commit
42dcf6970d
+19
-19
@@ -1,4 +1,4 @@
|
||||
# SHiNE ESP32 Subserver UI Nav Minimal Spec
|
||||
# SHiNE ESP32 Homeserver UI Nav Minimal Spec
|
||||
|
||||
Минимальный навигационный прототип для `Waveshare ESP32-S3-Touch-AMOLED-2.16`.
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
- `WIFI_SCREEN`
|
||||
- `SERVER_SCREEN`
|
||||
- `ACCOUNT_SCREEN`
|
||||
- `ACCOUNT_SUBSERVER_SCREEN`
|
||||
- `ACCOUNT_HOMESERVER_SCREEN`
|
||||
- `ACCOUNT_SECRET_SCREEN`
|
||||
- `SECRET_SHOW_SCREEN`
|
||||
- `SECRET_GENERATE_*`
|
||||
@@ -33,7 +33,7 @@
|
||||
## HOME
|
||||
|
||||
Показывает:
|
||||
- сверху слева значение сабсервера или `subserver not set`;
|
||||
- сверху слева значение homeserver или `homeserver not set`;
|
||||
- ниже значение логина или `login not set`;
|
||||
- справа от строки логина индикатор статуса Solana-аккаунта:
|
||||
- зелёный — все ключи совпадают;
|
||||
@@ -51,7 +51,7 @@
|
||||
- строка `SHiNE: <server> connected/account not configured/unavailable`;
|
||||
- при отсутствии пользователя в Solana PDA слева снизу появляется кнопка `REGISTER ACCOUNT`;
|
||||
- снизу кнопку `SETTINGS`, уменьшенную примерно до половины ширины экрана и сдвинутую к правому краю.
|
||||
- внизу на тёмной полосе подпись `SHiNE subserver (v.0.18)`.
|
||||
- внизу на тёмной полосе подпись `SHiNE homeserver (v.0.18)`.
|
||||
|
||||
Строка Wi-Fi на `HOME`:
|
||||
- `Wi-Fi (not configured) not configured`
|
||||
@@ -65,11 +65,11 @@
|
||||
|
||||
Фоновая логика:
|
||||
- пока открыт `HOME`, экран сам обновляется примерно раз в секунду;
|
||||
- при наличии `login + secret + subserver` и Wi-Fi устройство читает Solana `user_pda` напрямую через RPC;
|
||||
- сравниваются `root key`, `blockchain key`, `device key` и `subserver` session-запись типа `100`;
|
||||
- при наличии `login + secret + homeserver` и Wi-Fi устройство читает Solana `user_pda` напрямую через RPC;
|
||||
- сравниваются `root key`, `blockchain key`, `device key` и `homeserver` session-запись типа `100`;
|
||||
- для строки `SHiNE:` устройство держит отдельную WebSocket-сессию с сервером SHiNE:
|
||||
- авторизация через `AuthChallenge/CreateAuthSession` или `SessionChallenge/SessionLogin`;
|
||||
- session key = публичный `subserver key`;
|
||||
- session key = публичный `homeserver key`;
|
||||
- подтверждение создания сессии подписывается `device key`;
|
||||
- heartbeat выполняется `Ping` раз в минуту.
|
||||
|
||||
@@ -164,26 +164,26 @@
|
||||
- заголовок `ACCOUNT`;
|
||||
- статусное сообщение;
|
||||
- кнопку `Login (<value|not set>)`;
|
||||
- кнопку `Subserver (<value|not set>)`;
|
||||
- кнопку `Homeserver (<value|not set>)`;
|
||||
- кнопку `Secret (<*****|not set>)`.
|
||||
|
||||
Переходы:
|
||||
- свайп вправо -> `SETTINGS_MENU`
|
||||
- `Login` -> `TEXT_EDIT_SCREEN`
|
||||
- `Subserver` -> `ACCOUNT_SUBSERVER_SCREEN`
|
||||
- `Homeserver` -> `ACCOUNT_HOMESERVER_SCREEN`
|
||||
- `Secret` -> `ACCOUNT_SECRET_SCREEN`
|
||||
|
||||
## ACCOUNT_SUBSERVER_SCREEN
|
||||
## ACCOUNT_HOMESERVER_SCREEN
|
||||
|
||||
Показывает:
|
||||
- текущий `subserver`;
|
||||
- рекомендацию оставить `subserver1`, если устройство одно;
|
||||
- кнопку `USE SUBSERVER1`;
|
||||
- текущий `homeserver`;
|
||||
- рекомендацию оставить `homeserver1`, если устройство одно;
|
||||
- кнопку `USE HOMESERVER1`;
|
||||
- кнопку `EDIT MANUALLY`;
|
||||
- кнопку `BACK`.
|
||||
|
||||
Переходы:
|
||||
- `USE SUBSERVER1` -> сохраняет `subserver1` и возвращает в `ACCOUNT_SCREEN`
|
||||
- `USE HOMESERVER1` -> сохраняет `homeserver1` и возвращает в `ACCOUNT_SCREEN`
|
||||
- `EDIT MANUALLY` -> `TEXT_EDIT_SCREEN`
|
||||
- свайп вправо -> `ACCOUNT_SCREEN`
|
||||
|
||||
@@ -212,8 +212,8 @@
|
||||
- `Blockchain key priv (base58)`;
|
||||
- `Device key (base58)`;
|
||||
- `Device key priv (base58)`;
|
||||
- `Subserver key (base58)`;
|
||||
- `Subserver key priv (base58)`;
|
||||
- `Homeserver key (base58)`;
|
||||
- `Homeserver key priv (base58)`;
|
||||
- для каждого поля показывается формула derivation;
|
||||
- значения ключей показываются полными строками увеличенным шрифтом;
|
||||
- кнопку `BACK`.
|
||||
@@ -293,7 +293,7 @@
|
||||
|
||||
Используется `Preferences` (NVS памяти ESP32):
|
||||
- `login`
|
||||
- `subserver`
|
||||
- `homeserver`
|
||||
- `secret_set`
|
||||
|
||||
## Детали клавиатуры
|
||||
@@ -312,7 +312,7 @@
|
||||
- `DEL`
|
||||
- `SAVE`
|
||||
- `CANCEL`
|
||||
- ниже рамки клавиатурного блока остаётся отдельная тёмная полоса с версией `SHiNE subserver (v.0.18)`.
|
||||
- ниже рамки клавиатурного блока остаётся отдельная тёмная полоса с версией `SHiNE homeserver (v.0.18)`.
|
||||
|
||||
## Жесты
|
||||
|
||||
@@ -329,7 +329,7 @@
|
||||
- `WIFI_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
||||
- `SERVER_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
||||
- `ACCOUNT_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
||||
- `ACCOUNT_SUBSERVER_SCREEN`: свайп вправо -> `ACCOUNT_SCREEN`
|
||||
- `ACCOUNT_HOMESERVER_SCREEN`: свайп вправо -> `ACCOUNT_SCREEN`
|
||||
- `ACCOUNT_SECRET_SCREEN`: свайп вправо -> `ACCOUNT_SCREEN`
|
||||
- `TEXT_EDIT_SCREEN`: свайп влево/вправо -> переключение страниц клавиатуры
|
||||
- переключение страниц клавиатуры срабатывает только если свайп начался в зоне самой клавиатуры, а не по всему экрану редактора
|
||||
+14
-14
@@ -1,8 +1,8 @@
|
||||
# SHiNE ESP32 Subserver UI Spec
|
||||
# SHiNE ESP32 Homeserver UI Spec
|
||||
|
||||
## Назначение
|
||||
|
||||
Этот документ описывает актуальный UI-прототип сабсервера `SHiNE` для платы `Waveshare ESP32-S3-Touch-AMOLED-2.16`.
|
||||
Этот документ описывает актуальный UI-прототип homeserver `SHiNE` для платы `Waveshare ESP32-S3-Touch-AMOLED-2.16`.
|
||||
|
||||
Документ является источником истины для Arduino-скетча:
|
||||
|
||||
@@ -37,13 +37,13 @@
|
||||
|
||||
## Основная идея устройства
|
||||
|
||||
Устройство работает как отдельный сабсервер:
|
||||
Устройство работает как отдельный homeserver:
|
||||
|
||||
- хранит секрет на самом устройстве;
|
||||
- позволяет ввести логин, секрет и имя сабсервера;
|
||||
- позволяет ввести логин, секрет и имя homeserver;
|
||||
- показывает адрес кошелька устройства;
|
||||
- позволяет пополнить баланс перед регистрацией;
|
||||
- после выполнения условий даёт зарегистрировать устройство как сабсервер;
|
||||
- после выполнения условий даёт зарегистрировать устройство как homeserver;
|
||||
- после регистрации может принимать входящие запросы на вход и на подпись.
|
||||
|
||||
`SD`-карта не нужна для постоянного хранения секрета в этом прототипе.
|
||||
@@ -57,7 +57,7 @@
|
||||
- `Wi-Fi SSID`;
|
||||
- `Wi-Fi password`;
|
||||
- `login`;
|
||||
- `session/subserver name`;
|
||||
- `session/homeserver name`;
|
||||
- `master secret`;
|
||||
- `wallet address`;
|
||||
- `user pda address`;
|
||||
@@ -142,7 +142,7 @@
|
||||
|
||||
- крупный статус регистрации;
|
||||
- имя логина;
|
||||
- имя сабсервера;
|
||||
- имя homeserver;
|
||||
- короткий статус Wi-Fi;
|
||||
- короткий статус сервера;
|
||||
- короткий статус баланса.
|
||||
@@ -162,14 +162,14 @@
|
||||
|
||||
Если регистрация уже сделана:
|
||||
|
||||
- вместо призыва к регистрации показывается статус `Сабсервер активен`.
|
||||
- вместо призыва к регистрации показывается статус `Homeserver активен`.
|
||||
|
||||
## Экран STATUS
|
||||
|
||||
Показывает сводку:
|
||||
|
||||
- логин;
|
||||
- сабсервер;
|
||||
- homeserver;
|
||||
- есть ли секрет;
|
||||
- зарегистрировано ли устройство;
|
||||
- подключён ли Wi-Fi;
|
||||
@@ -256,7 +256,7 @@
|
||||
Показывает:
|
||||
|
||||
- логин;
|
||||
- имя сабсервера;
|
||||
- имя homeserver;
|
||||
- статус секрета;
|
||||
- короткий отпечаток секрета;
|
||||
- статус регистрации;
|
||||
@@ -266,7 +266,7 @@
|
||||
|
||||
- `Изменить логин`
|
||||
- `Секрет`
|
||||
- `Имя сабсервера`
|
||||
- `Имя homeserver`
|
||||
- `Сгенерировать`
|
||||
- `Очистить`
|
||||
- `Назад`
|
||||
@@ -394,7 +394,7 @@ QR должен быть сканируемым, а не декоративны
|
||||
- `SSID`
|
||||
- `Пароль Wi-Fi`
|
||||
- `Логин`
|
||||
- `Имя сабсервера`
|
||||
- `Имя homeserver`
|
||||
- `API URL`
|
||||
- `RPC URL`
|
||||
- `WS URL`
|
||||
@@ -432,13 +432,13 @@ QR должен быть сканируемым, а не декоративны
|
||||
5. проверить или задать серверные адреса;
|
||||
6. открыть `Аккаунт`;
|
||||
7. ввести логин;
|
||||
8. задать имя сабсервера;
|
||||
8. задать имя homeserver;
|
||||
9. сгенерировать секрет;
|
||||
10. открыть `Кошелёк`;
|
||||
11. при необходимости пополнить баланс;
|
||||
12. вернуться на `HOME`;
|
||||
13. нажать `Зарегистрировать`;
|
||||
14. после подтверждения увидеть статус `Сабсервер активен`.
|
||||
14. после подтверждения увидеть статус `Homeserver активен`.
|
||||
|
||||
Примечание:
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
- `hello` — базовый тест экрана (пример `01_HelloWorld`)
|
||||
- `simple` — простой кастомный тест: экран + touch + запись/проигрывание + наклон (IMU)
|
||||
- `argon2` — генерация masterSecret через Argon2id с SD-картой как памятью (тест скорости)
|
||||
- `subserver-ui` — основной UI-прототип сабсервера SHiNE: NVS, PIN, Wi-Fi, серверы, кошелёк, QR, запросы
|
||||
- `homeserver-ui` — основной UI-прототип homeserver SHiNE: NVS, PIN, Wi-Fi, серверы, кошелёк, QR, запросы
|
||||
- `text-test` — диагностический экран рендера текста: default font, U8g2 ASCII, U8g2 кириллица, кнопки с подписями
|
||||
- `gfx-text-test` — тот же тест рендера текста, но уже внутри новой папки `test_sketches/`
|
||||
- `gfx-layout-test` — тест геометрии и нижних рядов кнопок
|
||||
@@ -22,9 +22,9 @@
|
||||
- `lvgl-interaction-test` — экран на `LVGL` с большим числом кнопок и сообщением о нажатой кнопке
|
||||
- `lvgl-touch-debug-test` — точечная диагностика touch: сырые координаты, маркер точки и большая тест-кнопка `LVGL`
|
||||
- `lvgl-official-based-test` — наш минимальный экран, но на максимально близкой к официальному `LVGL_Widgets` инициализации
|
||||
- `lvgl-subserver-touch-test` — гибрид: `LVGL`-интерфейс, но display/touch init и raw touch-read взяты из `shine_subserver_ui`; подтверждено на устройстве, touch работает, зелёных линий по краям нет
|
||||
- `lvgl-subserver-touch-test` — гибрид: `LVGL`-интерфейс, но display/touch init и raw touch-read взяты из `shine_homeserver_ui`; подтверждено на устройстве, touch работает, зелёных линий по краям нет
|
||||
- `lvgl-russian-font-test` — тест кастомного `LVGL`-шрифта с кириллицей: русские кнопки, длинные подписи и статусы
|
||||
- `lvgl-nav-minimal-test` — новый минимальный UI-каркас сабсервера: `HOME`, `SETTINGS_MENU`, `Wi-Fi`, `Server`, `Account`, свайпы, крупные кнопки и реальная настройка Wi-Fi с сохранением в NVS
|
||||
- `lvgl-nav-minimal-test` — новый минимальный UI-каркас homeserver: `HOME`, `SETTINGS_MENU`, `Wi-Fi`, `Server`, `Account`, свайпы, крупные кнопки и реальная настройка Wi-Fi с сохранением в NVS
|
||||
|
||||
Запуск:
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
- `./burn.sh audio`
|
||||
- `./burn.sh hello`
|
||||
- `./burn.sh simple`
|
||||
- `./burn.sh subserver-ui`
|
||||
- `./burn.sh homeserver-ui`
|
||||
- `./burn.sh text-test`
|
||||
- `./burn.sh gfx-text-test`
|
||||
- `./burn.sh gfx-layout-test`
|
||||
@@ -43,4 +43,4 @@
|
||||
- `./burn.sh lvgl-subserver-touch-test`
|
||||
- `./burn.sh lvgl-russian-font-test`
|
||||
- `./burn.sh lvgl-nav-minimal-test`
|
||||
- `./flash_shine_subserver_ui.sh` - автоматически находит USB-порт и заливает `shine_subserver_ui`
|
||||
- `./flash_shine_homeserver_ui.sh` - автоматически находит USB-порт и заливает `shine_homeserver_ui`
|
||||
|
||||
@@ -34,7 +34,7 @@ case "${MODE}" in
|
||||
audio) SKETCH_DIR="${DEMO_BASE}/examples/07_ES8311" ;;
|
||||
simple) SKETCH_DIR="${ROOT_DIR}/simple_av_test" ;;
|
||||
argon2) SKETCH_DIR="${ROOT_DIR}/argon2_sd_test" ;;
|
||||
subserver-ui) SKETCH_DIR="${ROOT_DIR}/shine_subserver_ui" ;;
|
||||
homeserver-ui) SKETCH_DIR="${ROOT_DIR}/shine_homeserver_ui" ;;
|
||||
text-test) SKETCH_DIR="${ROOT_DIR}/text_render_test" ;;
|
||||
gfx-text-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/gfx_text_render_test" ;;
|
||||
gfx-layout-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/gfx_button_layout_test" ;;
|
||||
@@ -47,7 +47,7 @@ case "${MODE}" in
|
||||
lvgl-nav-minimal-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_nav_minimal_test" ;;
|
||||
*)
|
||||
echo "Unknown mode: ${MODE}" >&2
|
||||
echo "Use one of: hello, widgets, audio, simple, argon2, subserver-ui, text-test, gfx-text-test, gfx-layout-test, lvgl-basic-test, lvgl-interaction-test, lvgl-touch-debug-test, lvgl-official-based-test, lvgl-subserver-touch-test, lvgl-russian-font-test, lvgl-nav-minimal-test" >&2
|
||||
echo "Use one of: hello, widgets, audio, simple, argon2, homeserver-ui, text-test, gfx-text-test, gfx-layout-test, lvgl-basic-test, lvgl-interaction-test, lvgl-touch-debug-test, lvgl-official-based-test, lvgl-subserver-touch-test, lvgl-russian-font-test, lvgl-nav-minimal-test" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
+2
-2
@@ -43,9 +43,9 @@ fi
|
||||
if [[ -z "${PORT}" ]]; then
|
||||
echo "Не удалось автоматически найти USB-порт ESP32." >&2
|
||||
echo "Подключите плату и проверьте 'arduino-cli board list'." >&2
|
||||
echo "Либо укажите порт вручную: PORT=/dev/ttyACM0 ./flash_shine_subserver_ui.sh" >&2
|
||||
echo "Либо укажите порт вручную: PORT=/dev/ttyACM0 ./flash_shine_homeserver_ui.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "== Найден порт: ${PORT}"
|
||||
PORT="${PORT}" "${ROOT_DIR}/burn.sh" subserver-ui
|
||||
PORT="${PORT}" "${ROOT_DIR}/burn.sh" homeserver-ui
|
||||
+21
-21
@@ -97,7 +97,7 @@ enum ActionId {
|
||||
ACT_VERIFY_SERVERS,
|
||||
ACT_SET_TEST_SERVERS,
|
||||
ACT_EDIT_LOGIN,
|
||||
ACT_EDIT_SUBSERVER,
|
||||
ACT_EDIT_HOMESERVER,
|
||||
ACT_GENERATE_SECRET,
|
||||
ACT_CLEAR_ACCOUNT,
|
||||
ACT_SHOW_QR,
|
||||
@@ -137,7 +137,7 @@ enum EditTarget {
|
||||
EDIT_SSID,
|
||||
EDIT_WIFI_PASSWORD,
|
||||
EDIT_LOGIN,
|
||||
EDIT_SUBSERVER,
|
||||
EDIT_HOMESERVER,
|
||||
EDIT_API,
|
||||
EDIT_RPC,
|
||||
EDIT_WS,
|
||||
@@ -174,7 +174,7 @@ struct AppData {
|
||||
String wifiSsid;
|
||||
String wifiPassword;
|
||||
String login;
|
||||
String subserverName;
|
||||
String homeserverName;
|
||||
String secret;
|
||||
String walletAddress;
|
||||
String userPdaAddress;
|
||||
@@ -551,7 +551,7 @@ static bool canRegister() {
|
||||
|
||||
static String registrationSummary() {
|
||||
if (gData.registered) {
|
||||
return "Сабсервер активен";
|
||||
return "Homeserver активен";
|
||||
}
|
||||
if (!gData.wifiReady) {
|
||||
return "Нужен Wi-Fi";
|
||||
@@ -1179,7 +1179,7 @@ static bool awaitTransactionConfirmation(const String &signatureB58, String &mes
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool registerSubserverOnSolana(String &messageOut) {
|
||||
static bool registerHomeserverOnSolana(String &messageOut) {
|
||||
messageOut = "";
|
||||
if (!gDerivedKeys.ready) {
|
||||
if (!restoreDerivedKeysFromSecret()) {
|
||||
@@ -1656,7 +1656,7 @@ static bool refreshWalletBalance(String &messageOut) {
|
||||
static void seedRequests() {
|
||||
gRequests[0].type = "Вход в сессию";
|
||||
gRequests[0].actor = "Chrome / aidarkc";
|
||||
gRequests[0].details = "Клиент просит подключиться к сабсерверу и открыть сессию без ввода пароля.";
|
||||
gRequests[0].details = "Клиент просит подключиться к homeserverу и открыть сессию без ввода пароля.";
|
||||
gRequests[0].status = "Ожидает";
|
||||
|
||||
gRequests[1].type = "Подпись сообщения";
|
||||
@@ -1670,7 +1670,7 @@ static void loadDefaults() {
|
||||
gData.wifiSsid = "";
|
||||
gData.wifiPassword = "";
|
||||
gData.login = "";
|
||||
gData.subserverName = "subserver1";
|
||||
gData.homeserverName = "homeserver1";
|
||||
gData.secret = "";
|
||||
gData.walletAddress = "";
|
||||
gData.userPdaAddress = "";
|
||||
@@ -1692,7 +1692,7 @@ static void saveData() {
|
||||
gPrefs.putString("wifi_ssid", gData.wifiSsid);
|
||||
gPrefs.putString("wifi_pass", gData.wifiPassword);
|
||||
gPrefs.putString("login", gData.login);
|
||||
gPrefs.putString("subserver", gData.subserverName);
|
||||
gPrefs.putString("homeserver", gData.homeserverName);
|
||||
gPrefs.putString("secret", gData.secret);
|
||||
gPrefs.putString("wallet", gData.walletAddress);
|
||||
gPrefs.putString("user_pda", gData.userPdaAddress);
|
||||
@@ -1714,7 +1714,7 @@ static void loadData() {
|
||||
gData.wifiSsid = gPrefs.getString("wifi_ssid", gData.wifiSsid);
|
||||
gData.wifiPassword = gPrefs.getString("wifi_pass", gData.wifiPassword);
|
||||
gData.login = gPrefs.getString("login", gData.login);
|
||||
gData.subserverName = gPrefs.getString("subserver", gData.subserverName);
|
||||
gData.homeserverName = gPrefs.getString("homeserver", gData.homeserverName);
|
||||
gData.secret = gPrefs.getString("secret", gData.secret);
|
||||
gData.walletAddress = gPrefs.getString("wallet", gData.walletAddress);
|
||||
gData.userPdaAddress = gPrefs.getString("user_pda", gData.userPdaAddress);
|
||||
@@ -1758,8 +1758,8 @@ static void generateSecretAndWallet() {
|
||||
gData.registrationSignature = "";
|
||||
gData.registered = false;
|
||||
gData.online = false;
|
||||
if (gData.subserverName.length() == 0) {
|
||||
gData.subserverName = "subserver1";
|
||||
if (gData.homeserverName.length() == 0) {
|
||||
gData.homeserverName = "homeserver1";
|
||||
}
|
||||
saveData();
|
||||
}
|
||||
@@ -1815,7 +1815,7 @@ static String editTargetLabel() {
|
||||
case EDIT_SSID: return "SSID";
|
||||
case EDIT_WIFI_PASSWORD: return "Пароль Wi-Fi";
|
||||
case EDIT_LOGIN: return "Логин";
|
||||
case EDIT_SUBSERVER: return "Имя сабсервера";
|
||||
case EDIT_HOMESERVER: return "Имя homeserver";
|
||||
case EDIT_API: return "API URL";
|
||||
case EDIT_RPC: return "RPC URL";
|
||||
case EDIT_WS: return "WS URL";
|
||||
@@ -1846,7 +1846,7 @@ static void drawHomeScreen() {
|
||||
drawPanel(20, 92, 440, 98, C_PANEL, C_BORDER, 16);
|
||||
drawText(36, 122, registrationSummary(), canRegister() || gData.registered ? C_ACCENT : C_WARN, (const uint8_t *)FONT_HEAD);
|
||||
drawText(36, 152, "Логин: " + (gData.login.length() ? gData.login : "не задан"), C_TEXT, (const uint8_t *)FONT_BODY);
|
||||
drawText(36, 174, "Сабсервер: " + gData.subserverName, C_MUTE, (const uint8_t *)FONT_BODY);
|
||||
drawText(36, 174, "Homeserver: " + gData.homeserverName, C_MUTE, (const uint8_t *)FONT_BODY);
|
||||
|
||||
drawPanel(20, 204, 210, 82, C_CARD, C_BORDER, 12);
|
||||
drawText(34, 232, "Wi-Fi", C_TEXT, (const uint8_t *)FONT_BODY);
|
||||
@@ -1871,7 +1871,7 @@ static void drawStatusScreen() {
|
||||
drawTopBar("Статус");
|
||||
drawPanel(20, 92, 440, 286, C_PANEL, C_BORDER, 16);
|
||||
drawText(36, 122, "Логин: " + (gData.login.length() ? gData.login : "не задан"), C_TEXT);
|
||||
drawText(36, 148, "Сабсервер: " + gData.subserverName, C_TEXT);
|
||||
drawText(36, 148, "Homeserver: " + gData.homeserverName, C_TEXT);
|
||||
drawText(36, 174, "Секрет: " + boolText(gData.secretReady, "сохранён", "не задан"), gData.secretReady ? C_ACCENT : C_WARN);
|
||||
drawText(36, 200, "Отпечаток: " + (gData.secretReady ? shortenValue(gData.secret) : "-"), C_MUTE, (const uint8_t *)FONT_SMALL);
|
||||
drawText(36, 226, "Wi-Fi: " + boolText(gData.wifiReady, "готов", "не готов"), gData.wifiReady ? C_ACCENT : C_WARN);
|
||||
@@ -1947,13 +1947,13 @@ static void drawAccountScreen() {
|
||||
drawTopBar("Аккаунт");
|
||||
drawPanel(20, 92, 440, 188, C_PANEL, C_BORDER, 16);
|
||||
drawText(36, 122, "Логин: " + (gData.login.length() ? gData.login : "не задан"), C_TEXT);
|
||||
drawText(36, 152, "Сабсервер: " + gData.subserverName, C_TEXT);
|
||||
drawText(36, 152, "Homeserver: " + gData.homeserverName, C_TEXT);
|
||||
drawText(36, 182, "Секрет: " + boolText(gData.secretReady, "сохранён", "не задан"), gData.secretReady ? C_ACCENT : C_WARN);
|
||||
drawText(36, 212, "Кошелёк: " + (gData.walletAddress.length() ? shortenValue(gData.walletAddress, 10, 8) : "не создан"), C_MUTE, (const uint8_t *)FONT_SMALL);
|
||||
drawText(36, 236, "Регистрация: " + boolText(gData.registered, "выполнена", "не выполнена"), gData.registered ? C_ACCENT : C_WARN);
|
||||
drawText(36, 260, "PDA: " + registrationDetailsShort(), C_MUTE, (const uint8_t *)FONT_SMALL);
|
||||
addButton(20, 300, 212, 48, ACT_EDIT_LOGIN, "Изменить логин");
|
||||
addButton(248, 300, 212, 48, ACT_EDIT_SUBSERVER, "Имя сабсервера");
|
||||
addButton(248, 300, 212, 48, ACT_EDIT_HOMESERVER, "Имя homeserver");
|
||||
addButton(20, 360, 212, 48, ACT_GENERATE_SECRET, "Сгенерировать", true, C_OK);
|
||||
addButton(248, 360, 212, 48, ACT_CLEAR_ACCOUNT, "Очистить", true, C_BUTTON2);
|
||||
addButton(20, 420, 440, 36, ACT_BACK, "Назад");
|
||||
@@ -2193,9 +2193,9 @@ static void applyEditValue() {
|
||||
gData.registrationSignature = "";
|
||||
gNotice = "Логин сохранён";
|
||||
break;
|
||||
case EDIT_SUBSERVER:
|
||||
gData.subserverName = value.length() ? value : "subserver1";
|
||||
gNotice = "Имя сабсервера сохранено";
|
||||
case EDIT_HOMESERVER:
|
||||
gData.homeserverName = value.length() ? value : "homeserver1";
|
||||
gNotice = "Имя homeserver сохранено";
|
||||
break;
|
||||
case EDIT_API:
|
||||
gData.apiUrl = value;
|
||||
@@ -2351,7 +2351,7 @@ static void handleAction(ActionId action) {
|
||||
}
|
||||
if (action == ACT_CONFIRM_YES) {
|
||||
if (gConfirmTarget == CONFIRM_REGISTER) {
|
||||
registerSubserverOnSolana(gNotice);
|
||||
registerHomeserverOnSolana(gNotice);
|
||||
} else if (gConfirmTarget == CONFIRM_CLEAR_ACCOUNT) {
|
||||
gData.secret = "";
|
||||
gData.walletAddress = "";
|
||||
@@ -2445,7 +2445,7 @@ static void handleAction(ActionId action) {
|
||||
gNeedRedraw = true;
|
||||
break;
|
||||
case ACT_EDIT_LOGIN: openEdit(EDIT_LOGIN, gData.login, false); break;
|
||||
case ACT_EDIT_SUBSERVER: openEdit(EDIT_SUBSERVER, gData.subserverName, false); break;
|
||||
case ACT_EDIT_HOMESERVER: openEdit(EDIT_HOMESERVER, gData.homeserverName, false); break;
|
||||
case ACT_GENERATE_SECRET:
|
||||
generateSecretAndWallet();
|
||||
gNotice = gData.secretReady ? "Секрет сгенерирован, device-кошелёк выведен из него" : "Не удалось сгенерировать секрет";
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Набор отдельных диагностических скетчей для `Waveshare ESP32-S3-Touch-AMOLED-2.16`.
|
||||
|
||||
Скетчи в этой папке нужны для быстрой проверки конкретных гипотез без влияния на основной `shine_subserver_ui`.
|
||||
Скетчи в этой папке нужны для быстрой проверки конкретных гипотез без влияния на основной `shine_homeserver_ui`.
|
||||
|
||||
## Список
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
- `lvgl_interaction_test/` - расширенный тест `LVGL` с 9 кнопками, touch-вводом и статусом нажатия
|
||||
- `lvgl_touch_debug_test/` - диагностика touch: сырые координаты, точка касания и одна большая кнопка `LVGL`
|
||||
- `lvgl_official_based_test/` - минимальный наш экран поверх максимально близкой к официальному `05_LVGL_Widgets` инициализации
|
||||
- `lvgl_subserver_touch_test/` - гибридный тест: `LVGL`-экран с инициализацией дисплея и чтением touch из `shine_subserver_ui`; подтверждён на реальном устройстве
|
||||
- `lvgl_subserver_touch_test/` - гибридный тест: `LVGL`-экран с инициализацией дисплея и чтением touch из `shine_homeserver_ui`; подтверждён на реальном устройстве
|
||||
- `lvgl_russian_font_test/` - тест кастомного кириллического `LVGL`-шрифта с русскими кнопками, длинными строками и рабочим touch
|
||||
- `lvgl_nav_minimal_test/` - новый минимальный навигационный каркас сабсервера на рабочем `LVGL + subserver touch`, расширенный настройкой Wi-Fi и сохранением в NVS
|
||||
|
||||
|
||||
+67
-67
@@ -48,7 +48,7 @@
|
||||
#define TEXT_EDIT_PANEL_Y 112
|
||||
#define TEXT_EDIT_PANEL_W 460
|
||||
#define TEXT_EDIT_PANEL_H 330
|
||||
#define TEST_VERSION "SHiNE subserver (v.0.18)"
|
||||
#define TEST_VERSION "SHiNE homeserver (v.0.18)"
|
||||
|
||||
static const char *kShineUsersProgramId = "FZS1YctoeEhCkZ5VTjsysUFAXR8CqxYztcLboXcg2Rpm";
|
||||
static const char *kShineUsersUserPdaSeedPrefix = "user_login=";
|
||||
@@ -60,7 +60,7 @@ static const uint8_t kBlockTypeServerProfile = 30;
|
||||
static const uint8_t kBlockTypeAccessServers = 40;
|
||||
static const uint8_t kBlockTypeSessions = 50;
|
||||
static const uint8_t kBlockTypeTrustedState = 70;
|
||||
static const uint8_t kSessionTypeSubserver = 100;
|
||||
static const uint8_t kSessionTypeHomeserver = 100;
|
||||
|
||||
enum Screen {
|
||||
SCREEN_HOME,
|
||||
@@ -68,7 +68,7 @@ enum Screen {
|
||||
SCREEN_WIFI,
|
||||
SCREEN_SERVER,
|
||||
SCREEN_ACCOUNT,
|
||||
SCREEN_ACCOUNT_SUBSERVER,
|
||||
SCREEN_ACCOUNT_HOMESERVER,
|
||||
SCREEN_ACCOUNT_SECRET,
|
||||
SCREEN_SECRET_SHOW,
|
||||
SCREEN_SECRET_GENERATE_INFO,
|
||||
@@ -99,10 +99,10 @@ enum ActionId {
|
||||
ACTION_SERVER_EDIT_SOLANA,
|
||||
ACTION_SERVER_EDIT_SHINE,
|
||||
ACTION_ACCOUNT_EDIT_LOGIN,
|
||||
ACTION_ACCOUNT_EDIT_SUBSERVER,
|
||||
ACTION_ACCOUNT_EDIT_HOMESERVER,
|
||||
ACTION_ACCOUNT_EDIT_SECRET,
|
||||
ACTION_ACCOUNT_SUBSERVER_USE_DEFAULT,
|
||||
ACTION_ACCOUNT_SUBSERVER_EDIT_MANUAL,
|
||||
ACTION_ACCOUNT_HOMESERVER_USE_DEFAULT,
|
||||
ACTION_ACCOUNT_HOMESERVER_EDIT_MANUAL,
|
||||
ACTION_SECRET_SHOW,
|
||||
ACTION_SECRET_MANUAL,
|
||||
ACTION_SECRET_GENERATE,
|
||||
@@ -129,7 +129,7 @@ enum EditContext {
|
||||
EDIT_CONTEXT_SOLANA_RPC,
|
||||
EDIT_CONTEXT_SHINE_SERVER,
|
||||
EDIT_CONTEXT_LOGIN,
|
||||
EDIT_CONTEXT_SUBSERVER,
|
||||
EDIT_CONTEXT_HOMESERVER,
|
||||
EDIT_CONTEXT_SECRET_MANUAL,
|
||||
EDIT_CONTEXT_SECRET_GENERATE_PASSWORD,
|
||||
};
|
||||
@@ -216,7 +216,7 @@ static String gSolanaRpcUrl = "https://api.devnet.solana.com";
|
||||
static String gShineServerUrl = "https://shineup.me";
|
||||
static String gServerStatusMessage = "Edit RPC or shine host";
|
||||
static String gLoginValue;
|
||||
static String gSubserverValue = "subserver1";
|
||||
static String gHomeserverValue = "homeserver1";
|
||||
static bool gSecretConfigured = false;
|
||||
static String gSecretBase58;
|
||||
static uint8_t gSecretBytes[32] = {};
|
||||
@@ -257,8 +257,8 @@ static String gBlockchainPubB58;
|
||||
static String gBlockchainPrivB58;
|
||||
static String gDevicePubB58;
|
||||
static String gDevicePrivB58;
|
||||
static String gSubserverPubB58;
|
||||
static String gSubserverPrivB58;
|
||||
static String gHomeserverPubB58;
|
||||
static String gHomeserverPrivB58;
|
||||
|
||||
static EditContext gEditContext = EDIT_CONTEXT_NONE;
|
||||
static Screen gEditReturnScreen = SCREEN_HOME;
|
||||
@@ -305,7 +305,7 @@ static void restoreTextareaFromEditValue();
|
||||
static void refreshDerivedKeys();
|
||||
static void clearDerivedKeys();
|
||||
static String loginDisplayValue();
|
||||
static String subserverDisplayValue();
|
||||
static String homeserverDisplayValue();
|
||||
static String homeSecretStatus();
|
||||
static String secretButtonValue();
|
||||
static void clearSecretValue();
|
||||
@@ -687,13 +687,13 @@ static bool parseUrlHostPortPath(const String &url, String &hostOut, uint16_t &p
|
||||
return hostOut.length() > 0 && portOut > 0;
|
||||
}
|
||||
|
||||
static String subserverKeySuffix() {
|
||||
String name = gSubserverValue;
|
||||
static String homeserverKeySuffix() {
|
||||
String name = gHomeserverValue;
|
||||
name.trim();
|
||||
if (name.isEmpty()) {
|
||||
name = "subserver1";
|
||||
name = "homeserver1";
|
||||
}
|
||||
return String("subserver.key:") + name;
|
||||
return String("homeserver.key:") + name;
|
||||
}
|
||||
|
||||
static void deriveKeyPairFromSecretSuffix(const uint8_t *secret32, const String &suffix, String &pubB58, String &privB58) {
|
||||
@@ -718,8 +718,8 @@ static void clearDerivedKeys() {
|
||||
gBlockchainPrivB58 = "";
|
||||
gDevicePubB58 = "";
|
||||
gDevicePrivB58 = "";
|
||||
gSubserverPubB58 = "";
|
||||
gSubserverPrivB58 = "";
|
||||
gHomeserverPubB58 = "";
|
||||
gHomeserverPrivB58 = "";
|
||||
}
|
||||
|
||||
static void refreshDerivedKeys() {
|
||||
@@ -730,7 +730,7 @@ static void refreshDerivedKeys() {
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, "root.key", gRootPubB58, gRootPrivB58);
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, "bch.key", gBlockchainPubB58, gBlockchainPrivB58);
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, "dev.key", gDevicePubB58, gDevicePrivB58);
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, subserverKeySuffix(), gSubserverPubB58, gSubserverPrivB58);
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, homeserverKeySuffix(), gHomeserverPubB58, gHomeserverPrivB58);
|
||||
}
|
||||
|
||||
static bool deriveKeypairFromSeed32(const uint8_t seed32[32], uint8_t pub32[32], uint8_t sec64[64]) {
|
||||
@@ -1302,20 +1302,20 @@ static void refreshAccountPdaStatus() {
|
||||
mismatch = "blockchain key mismatch";
|
||||
} else if (memcmp(devicePub, pdaState.deviceKey32, 32) != 0) {
|
||||
mismatch = "device key mismatch";
|
||||
} else if (gSubserverValue.isEmpty()) {
|
||||
mismatch = "subserver not set";
|
||||
} else if (gHomeserverValue.isEmpty()) {
|
||||
mismatch = "homeserver not set";
|
||||
} else {
|
||||
bool foundSession = false;
|
||||
bool sessionMismatch = false;
|
||||
for (const auto &session : pdaState.sessions) {
|
||||
if (session.sessionType == kSessionTypeSubserver && session.sessionName == gSubserverValue) {
|
||||
if (session.sessionType == kSessionTypeHomeserver && session.sessionName == gHomeserverValue) {
|
||||
foundSession = true;
|
||||
if (gSubserverPubB58.isEmpty()) {
|
||||
if (gHomeserverPubB58.isEmpty()) {
|
||||
sessionMismatch = true;
|
||||
} else {
|
||||
uint8_t subserverPub[32] = {};
|
||||
if (!base58ToFixed32(gSubserverPubB58, subserverPub)
|
||||
|| memcmp(subserverPub, session.sessionPubKey32, 32) != 0) {
|
||||
uint8_t homeserverPub[32] = {};
|
||||
if (!base58ToFixed32(gHomeserverPubB58, homeserverPub)
|
||||
|| memcmp(homeserverPub, session.sessionPubKey32, 32) != 0) {
|
||||
sessionMismatch = true;
|
||||
}
|
||||
}
|
||||
@@ -1323,9 +1323,9 @@ static void refreshAccountPdaStatus() {
|
||||
}
|
||||
}
|
||||
if (!foundSession) {
|
||||
mismatch = "subserver not in PDA";
|
||||
mismatch = "homeserver not in PDA";
|
||||
} else if (sessionMismatch) {
|
||||
mismatch = "subserver key mismatch";
|
||||
mismatch = "homeserver key mismatch";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1565,7 +1565,7 @@ static bool ensureShineSessionAuthenticated(String &errorOut) {
|
||||
errorOut = "Wi-Fi disconnected";
|
||||
return false;
|
||||
}
|
||||
if (gLoginValue.isEmpty() || !gSecretConfigured || gSubserverValue.isEmpty()) {
|
||||
if (gLoginValue.isEmpty() || !gSecretConfigured || gHomeserverValue.isEmpty()) {
|
||||
errorOut = "account not configured";
|
||||
return false;
|
||||
}
|
||||
@@ -1605,7 +1605,7 @@ static bool ensureShineSessionAuthenticated(String &errorOut) {
|
||||
uint8_t subPub[32] = {};
|
||||
uint8_t subSec[64] = {};
|
||||
if (!deriveSeedKeypairFromBase58(gDevicePrivB58, deviceSeed, devicePub, deviceSec)
|
||||
|| !deriveSeedKeypairFromBase58(gSubserverPrivB58, subSeed, subPub, subSec)) {
|
||||
|| !deriveSeedKeypairFromBase58(gHomeserverPrivB58, subSeed, subPub, subSec)) {
|
||||
errorOut = "local key derive failed";
|
||||
return false;
|
||||
}
|
||||
@@ -1634,7 +1634,7 @@ static bool ensureShineSessionAuthenticated(String &errorOut) {
|
||||
+ "\",\"sessionKey\":\"" + jsonEscape(sessionKey)
|
||||
+ "\",\"timeMs\":" + String((unsigned long long)timeMs)
|
||||
+ ",\"signatureB64\":\"" + jsonEscape(bytesToBase64String(signature, 64))
|
||||
+ "\",\"clientInfo\":\"ESP32 subserver\"}";
|
||||
+ "\",\"clientInfo\":\"ESP32 homeserver\"}";
|
||||
String loginResp;
|
||||
if (shineWsRequest(gShineWs, "SessionLogin", loginReq, loginResp)) {
|
||||
if (jsonInt64Field(loginResp, "status", statusCode) && statusCode == 200) {
|
||||
@@ -1691,7 +1691,7 @@ static bool ensureShineSessionAuthenticated(String &errorOut) {
|
||||
+ ",\"authNonce\":\"" + jsonEscape(authNonce)
|
||||
+ "\",\"deviceKey\":\"" + jsonEscape(bytesToBase64String(devicePub, 32))
|
||||
+ "\",\"signatureB64\":\"" + jsonEscape(bytesToBase64String(signature, 64))
|
||||
+ "\",\"clientInfo\":\"ESP32 subserver\"}";
|
||||
+ "\",\"clientInfo\":\"ESP32 homeserver\"}";
|
||||
String createResp;
|
||||
if (!shineWsRequest(gShineWs, "CreateAuthSession", createReq, createResp)) {
|
||||
errorOut = "CreateAuthSession failed";
|
||||
@@ -1710,7 +1710,7 @@ static bool ensureShineSessionAuthenticated(String &errorOut) {
|
||||
|
||||
static void manageShineConnection() {
|
||||
String serverLabel = gShineServerUrl.isEmpty() ? "not set" : gShineServerUrl;
|
||||
if (gLoginValue.isEmpty() || !gSecretConfigured || gSubserverValue.isEmpty()) {
|
||||
if (gLoginValue.isEmpty() || !gSecretConfigured || gHomeserverValue.isEmpty()) {
|
||||
gShineStatusLine = String("SHiNE: ") + serverLabel + " account not configured";
|
||||
clearShineSessionState(false);
|
||||
return;
|
||||
@@ -1802,7 +1802,7 @@ static void loadPrefs() {
|
||||
gSolanaRpcUrl = gPrefs.getString("solana_rpc", "https://api.devnet.solana.com");
|
||||
gShineServerUrl = gPrefs.getString("shine_server", "https://shineup.me");
|
||||
gLoginValue = gPrefs.getString("login", "");
|
||||
gSubserverValue = gPrefs.getString("subserver", "subserver1");
|
||||
gHomeserverValue = gPrefs.getString("homeserver", "homeserver1");
|
||||
gSecretConfigured = gPrefs.getBool("secret_set", false);
|
||||
gSecretBase58 = gPrefs.getString("secret_b58", "");
|
||||
if (gSecretConfigured && gPrefs.getBytesLength("secret_bytes") == 32) {
|
||||
@@ -1850,7 +1850,7 @@ static void saveServerPrefs() {
|
||||
|
||||
static void saveAccountPrefs() {
|
||||
gPrefs.putString("login", gLoginValue);
|
||||
gPrefs.putString("subserver", gSubserverValue);
|
||||
gPrefs.putString("homeserver", gHomeserverValue);
|
||||
gPrefs.putBool("secret_set", gSecretConfigured);
|
||||
gPrefs.putString("secret_b58", gSecretBase58);
|
||||
if (gSecretConfigured) {
|
||||
@@ -1925,8 +1925,8 @@ static String loginDisplayValue() {
|
||||
return gLoginValue.isEmpty() ? "login not set" : gLoginValue;
|
||||
}
|
||||
|
||||
static String subserverDisplayValue() {
|
||||
return gSubserverValue.isEmpty() ? "subserver not set" : gSubserverValue;
|
||||
static String homeserverDisplayValue() {
|
||||
return gHomeserverValue.isEmpty() ? "homeserver not set" : gHomeserverValue;
|
||||
}
|
||||
|
||||
static String homeSecretStatus() {
|
||||
@@ -2234,13 +2234,13 @@ static void applyEditorValue() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gEditContext == EDIT_CONTEXT_SUBSERVER) {
|
||||
if (gEditContext == EDIT_CONTEXT_HOMESERVER) {
|
||||
value.trim();
|
||||
gSubserverValue = value;
|
||||
gHomeserverValue = value;
|
||||
refreshDerivedKeys();
|
||||
saveAccountPrefs();
|
||||
markAccountStateDirty();
|
||||
gAccountStatusMessage = gSubserverValue.isEmpty() ? "Subserver cleared" : "Subserver saved";
|
||||
gAccountStatusMessage = gHomeserverValue.isEmpty() ? "Homeserver cleared" : "Homeserver saved";
|
||||
showScreen(SCREEN_ACCOUNT);
|
||||
return;
|
||||
}
|
||||
@@ -2465,8 +2465,8 @@ static void actionButtonCb(lv_event_t *event) {
|
||||
gLoginValue,
|
||||
false);
|
||||
break;
|
||||
case ACTION_ACCOUNT_EDIT_SUBSERVER:
|
||||
showScreen(SCREEN_ACCOUNT_SUBSERVER);
|
||||
case ACTION_ACCOUNT_EDIT_HOMESERVER:
|
||||
showScreen(SCREEN_ACCOUNT_HOMESERVER);
|
||||
break;
|
||||
case ACTION_ACCOUNT_EDIT_SECRET:
|
||||
showScreen(SCREEN_ACCOUNT_SECRET);
|
||||
@@ -2506,20 +2506,20 @@ static void actionButtonCb(lv_event_t *event) {
|
||||
case ACTION_SECRET_GENERATE_CANCEL_NO:
|
||||
showScreen(SCREEN_SECRET_GENERATE_RUNNING);
|
||||
break;
|
||||
case ACTION_ACCOUNT_SUBSERVER_USE_DEFAULT:
|
||||
gSubserverValue = "subserver1";
|
||||
case ACTION_ACCOUNT_HOMESERVER_USE_DEFAULT:
|
||||
gHomeserverValue = "homeserver1";
|
||||
refreshDerivedKeys();
|
||||
saveAccountPrefs();
|
||||
markAccountStateDirty();
|
||||
gAccountStatusMessage = "Subserver set to subserver1";
|
||||
gAccountStatusMessage = "Homeserver set to homeserver1";
|
||||
showScreen(SCREEN_ACCOUNT);
|
||||
break;
|
||||
case ACTION_ACCOUNT_SUBSERVER_EDIT_MANUAL:
|
||||
openEditor(EDIT_CONTEXT_SUBSERVER,
|
||||
case ACTION_ACCOUNT_HOMESERVER_EDIT_MANUAL:
|
||||
openEditor(EDIT_CONTEXT_HOMESERVER,
|
||||
SCREEN_ACCOUNT,
|
||||
"EDIT SUBSERVER",
|
||||
"EDIT HOMESERVER",
|
||||
"",
|
||||
gSubserverValue,
|
||||
gHomeserverValue,
|
||||
false);
|
||||
break;
|
||||
case ACTION_BACK_SECRET_MENU:
|
||||
@@ -2616,17 +2616,17 @@ static void makeVersionTag() {
|
||||
static void drawHome() {
|
||||
setRootStyle();
|
||||
|
||||
lv_obj_t *subserver = lv_label_create(gRoot);
|
||||
lv_label_set_text(subserver, subserverDisplayValue().c_str());
|
||||
lv_obj_set_style_text_font(subserver, &lv_font_montserrat_18, 0);
|
||||
lv_obj_set_style_text_color(subserver, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_align(subserver, LV_ALIGN_TOP_LEFT, 24, 18);
|
||||
lv_obj_t *homeserver = lv_label_create(gRoot);
|
||||
lv_label_set_text(homeserver, homeserverDisplayValue().c_str());
|
||||
lv_obj_set_style_text_font(homeserver, &lv_font_montserrat_18, 0);
|
||||
lv_obj_set_style_text_color(homeserver, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_align(homeserver, LV_ALIGN_TOP_LEFT, 24, 18);
|
||||
|
||||
lv_obj_t *login = lv_label_create(gRoot);
|
||||
lv_label_set_text(login, loginDisplayValue().c_str());
|
||||
lv_obj_set_style_text_font(login, &lv_font_montserrat_22, 0);
|
||||
lv_obj_set_style_text_color(login, lv_color_hex(0xD5DEE7), 0);
|
||||
lv_obj_align_to(login, subserver, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6);
|
||||
lv_obj_align_to(login, homeserver, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6);
|
||||
|
||||
lv_obj_t *accountDot = lv_obj_create(gRoot);
|
||||
lv_obj_set_size(accountDot, 14, 14);
|
||||
@@ -2793,23 +2793,23 @@ static void drawAccountScreen() {
|
||||
showMessageAt(gAccountStatusMessage, 56);
|
||||
|
||||
String loginButton = String("Login (") + (gLoginValue.isEmpty() ? "not set" : gLoginValue) + ")";
|
||||
String subserverButton = String("Subserver (") + (gSubserverValue.isEmpty() ? "not set" : gSubserverValue) + ")";
|
||||
String homeserverButton = String("Homeserver (") + (gHomeserverValue.isEmpty() ? "not set" : gHomeserverValue) + ")";
|
||||
String secretButton = String("Secret (") + secretButtonValue() + ")";
|
||||
|
||||
makeButton(loginButton.c_str(), 22, 118, 436, 84, 0x355C7D, ACTION_ACCOUNT_EDIT_LOGIN, &lv_font_montserrat_20);
|
||||
makeButton(subserverButton.c_str(), 22, 222, 436, 84, 0x355C7D, ACTION_ACCOUNT_EDIT_SUBSERVER, &lv_font_montserrat_20);
|
||||
makeButton(homeserverButton.c_str(), 22, 222, 436, 84, 0x355C7D, ACTION_ACCOUNT_EDIT_HOMESERVER, &lv_font_montserrat_20);
|
||||
makeButton(secretButton.c_str(), 22, 326, 436, 84, 0x355C7D, ACTION_ACCOUNT_EDIT_SECRET, &lv_font_montserrat_20);
|
||||
makeBody("Swipe right to return to Settings.", 420, 420);
|
||||
makeVersionTag();
|
||||
}
|
||||
|
||||
static void drawAccountSubserverScreen() {
|
||||
static void drawAccountHomeserverScreen() {
|
||||
setRootStyle();
|
||||
makeTitle("SUBSERVER", 18, &lv_font_montserrat_24);
|
||||
showMessageAt(String("Current: ") + subserverDisplayValue(), 56);
|
||||
makeBody("If you only use one subserver, keep the default name subserver1.", 98, 420);
|
||||
makeButton("USE SUBSERVER1", 22, 202, 436, 84, 0x2A9D8F, ACTION_ACCOUNT_SUBSERVER_USE_DEFAULT, &lv_font_montserrat_22);
|
||||
makeButton("EDIT MANUALLY", 22, 306, 436, 84, 0x355C7D, ACTION_ACCOUNT_SUBSERVER_EDIT_MANUAL, &lv_font_montserrat_22);
|
||||
makeTitle("HOMESERVER", 18, &lv_font_montserrat_24);
|
||||
showMessageAt(String("Current: ") + homeserverDisplayValue(), 56);
|
||||
makeBody("If you only use one homeserver, keep the default name homeserver1.", 98, 420);
|
||||
makeButton("USE HOMESERVER1", 22, 202, 436, 84, 0x2A9D8F, ACTION_ACCOUNT_HOMESERVER_USE_DEFAULT, &lv_font_montserrat_22);
|
||||
makeButton("EDIT MANUALLY", 22, 306, 436, 84, 0x355C7D, ACTION_ACCOUNT_HOMESERVER_EDIT_MANUAL, &lv_font_montserrat_22);
|
||||
makeButton("BACK", 140, 402, 200, 54, 0x5A6570, ACTION_BACK_ACCOUNT, &lv_font_montserrat_20);
|
||||
makeVersionTag();
|
||||
}
|
||||
@@ -2876,8 +2876,8 @@ static void drawSecretShowScreen() {
|
||||
addKeyBlock("Blockchain key priv (base58)", "sha256(base64(secret)|bch.key)", gBlockchainPrivB58);
|
||||
addKeyBlock("Device key (base58)", "pub from sha256(base64(secret)|dev.key)", gDevicePubB58);
|
||||
addKeyBlock("Device key priv (base58)", "sha256(base64(secret)|dev.key)", gDevicePrivB58);
|
||||
addKeyBlock("Subserver key (base58)", String("pub from sha256(base64(secret)|") + subserverKeySuffix() + ")", gSubserverPubB58);
|
||||
addKeyBlock("Subserver key priv (base58)", String("sha256(base64(secret)|") + subserverKeySuffix() + ")", gSubserverPrivB58);
|
||||
addKeyBlock("Homeserver key (base58)", String("pub from sha256(base64(secret)|") + homeserverKeySuffix() + ")", gHomeserverPubB58);
|
||||
addKeyBlock("Homeserver key priv (base58)", String("sha256(base64(secret)|") + homeserverKeySuffix() + ")", gHomeserverPrivB58);
|
||||
} else {
|
||||
showMessageAt("Secret not set", 96);
|
||||
}
|
||||
@@ -3084,8 +3084,8 @@ static void rebuildScreen() {
|
||||
case SCREEN_ACCOUNT:
|
||||
drawAccountScreen();
|
||||
break;
|
||||
case SCREEN_ACCOUNT_SUBSERVER:
|
||||
drawAccountSubserverScreen();
|
||||
case SCREEN_ACCOUNT_HOMESERVER:
|
||||
drawAccountHomeserverScreen();
|
||||
break;
|
||||
case SCREEN_ACCOUNT_SECRET:
|
||||
drawAccountSecretScreen();
|
||||
@@ -3219,7 +3219,7 @@ static void handleSwipe(SwipeDirection swipe) {
|
||||
case SCREEN_ACCOUNT:
|
||||
handleAccountSwipe(swipe);
|
||||
break;
|
||||
case SCREEN_ACCOUNT_SUBSERVER:
|
||||
case SCREEN_ACCOUNT_HOMESERVER:
|
||||
case SCREEN_ACCOUNT_SECRET:
|
||||
handleAccountSubscreenSwipe(swipe);
|
||||
break;
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
#include <Arduino_GFX_Library.h>
|
||||
#include <TouchDrvCSTXXX.hpp>
|
||||
|
||||
// Подтверждено на устройстве: LVGL-рендер работает вместе с touch-путём из shine_subserver_ui.
|
||||
// Подтверждено на устройстве: LVGL-рендер работает вместе с touch-путём из shine_homeserver_ui.
|
||||
|
||||
#define PIN_LCD_CS 12
|
||||
#define PIN_LCD_SCLK 38
|
||||
@@ -146,7 +146,7 @@ static void createUi() {
|
||||
lv_obj_align(gVersionLabel, LV_ALIGN_TOP_MID, 0, 12);
|
||||
|
||||
lv_obj_t *subtitle = lv_label_create(screen);
|
||||
lv_label_set_text(subtitle, "Touch path comes from shine_subserver_ui. Tap buttons and watch status.");
|
||||
lv_label_set_text(subtitle, "Touch path comes from shine_homeserver_ui. Tap buttons and watch status.");
|
||||
lv_obj_set_width(subtitle, 436);
|
||||
lv_label_set_long_mode(subtitle, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_text_font(subtitle, &lv_font_montserrat_14, 0);
|
||||
|
||||
Reference in New Issue
Block a user