chore(core/eckhart): extend device menu FwUI param list
What changed, and why it matters
This commit is a routine feature expansion of the Trezor hardware wallet's on-device settings menu for the upcoming 'Eckhart' model. It adds new menu options and result codes for Bluetooth pairing, PIN/wipe-code management, screen brightness, haptic feedback, and LED toggles. Most of the new menu items are not yet wired to real functionality and are marked with 'TODO implement' placeholders. There is no indication this change fixes or introduces a security vulnerability.
No security action required. Treat as normal UI feature development. Reviewers may want to verify that placeholder TODOs in `device_menu.py` do not accidentally expose sensitive actions before they are fully implemented, but the current diff does not enable any such behavior.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch extends the FirmwareUI show_device_menu API and the DeviceMenuResult enum across Rust UI layouts (Bolt, Caesar, Delizia, Eckhart) and Python homescreen code. It increases the paired-device list capacity from 1 to 8 (MAX_PAIRED_DEVICES), introduces new qstrs/translation keys, and refactors device_menu.py to dispatch the expanded result set. The non-Eckhart layouts return ValueError('show_device_menu not supported') as before. Several new parameters are accepted as Option<T>/None and are not acted upon. The commit is tagged [no changelog] and titled as a chore.
Changed components
core/embed/rust/src/ui/api/firmware_micropython.rscore/embed/rust/src/ui/layout/device_menu_result.rscore/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rscore/embed/rust/src/ui/layout_eckhart/component_msg_obj.rscore/embed/rust/src/ui/ui_firmware.rscore/src/apps/homescreen/device_menu.pyInspect captured patch +359 / −98
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index 53cb58cb..718b3fb4 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -23,20 +23,26 @@ static void _librust_qstrs(void) {
MP_QSTR_BacklightLevels;
MP_QSTR_BackupFailed;
MP_QSTR_BleInterface;
+ MP_QSTR_Bluetooth;
MP_QSTR_CANCELLED;
MP_QSTR_CONFIRMED;
MP_QSTR_CheckBackup;
MP_QSTR_DIM;
MP_QSTR_DONE;
+ MP_QSTR_DeviceConnect;
MP_QSTR_DeviceDisconnect;
MP_QSTR_DeviceMenuResult;
MP_QSTR_DeviceName;
MP_QSTR_DevicePair;
+ MP_QSTR_DeviceUnpair;
+ MP_QSTR_DeviceUnpairAll;
+ MP_QSTR_HapticFeedback;
MP_QSTR_INFO;
MP_QSTR_INITIAL;
MP_QSTR_LOW;
MP_QSTR_LayoutObj;
MP_QSTR_LayoutState;
+ MP_QSTR_LedEnabled;
MP_QSTR_MAX;
MP_QSTR_MESSAGE_NAME;
MP_QSTR_MESSAGE_WIRE_TYPE;
@@ -45,6 +51,8 @@ static void _librust_qstrs(void) {
MP_QSTR_MsgDef;
MP_QSTR_NONE;
MP_QSTR_NORMAL;
+ MP_QSTR_PinCode;
+ MP_QSTR_PinRemove;
MP_QSTR_RESUME;
MP_QSTR_RX_PACKET_LEN;
MP_QSTR_SWIPE_DOWN;
@@ -56,7 +64,9 @@ static void _librust_qstrs(void) {
MP_QSTR_TRANSITIONING;
MP_QSTR_TX_PACKET_LEN;
MP_QSTR_TranslationsHeader;
+ MP_QSTR_WipeCode;
MP_QSTR_WipeDevice;
+ MP_QSTR_WipeRemove;
MP_QSTR___del__;
MP_QSTR___dict__;
MP_QSTR___name__;
@@ -169,6 +179,7 @@ static void _librust_qstrs(void) {
MP_QSTR_ble__unpair_title;
MP_QSTR_ble__version;
MP_QSTR_ble_event;
+ MP_QSTR_bluetooth;
MP_QSTR_bootscreen;
MP_QSTR_br_code;
MP_QSTR_br_name;
@@ -225,6 +236,7 @@ static void _librust_qstrs(void) {
MP_QSTR_cancel;
MP_QSTR_cancel_text;
MP_QSTR_case_sensitive;
+ MP_QSTR_check_backup;
MP_QSTR_check_homescreen_format;
MP_QSTR_chunkify;
MP_QSTR_code;
@@ -258,6 +270,7 @@ static void _librust_qstrs(void) {
MP_QSTR_confirm_value;
MP_QSTR_confirm_value_intro;
MP_QSTR_confirm_with_info;
+ MP_QSTR_connected_idx;
MP_QSTR_connection_flags;
MP_QSTR_continue_recovery_homepage;
MP_QSTR_count;
@@ -311,6 +324,7 @@ static void _librust_qstrs(void) {
MP_QSTR_get;
MP_QSTR_get_language;
MP_QSTR_get_transition_out;
+ MP_QSTR_haptic_feedback;
MP_QSTR_haptic_feedback__disable;
MP_QSTR_haptic_feedback__enable;
MP_QSTR_haptic_feedback__subtitle;
@@ -388,6 +402,7 @@ static void _librust_qstrs(void) {
MP_QSTR_language__changed;
MP_QSTR_language__progress;
MP_QSTR_language__title;
+ MP_QSTR_led_enabled;
MP_QSTR_lines;
MP_QSTR_load_from_flash;
MP_QSTR_lockable;
@@ -480,6 +495,7 @@ static void _librust_qstrs(void) {
MP_QSTR_pin__turn_off;
MP_QSTR_pin__turn_on;
MP_QSTR_pin__wrong_pin;
+ MP_QSTR_pin_code;
MP_QSTR_plurals__contains_x_keys;
MP_QSTR_plurals__lock_after_x_days;
MP_QSTR_plurals__lock_after_x_hours;
@@ -687,6 +703,7 @@ static void _librust_qstrs(void) {
MP_QSTR_safety_checks__enforce_strict;
MP_QSTR_safety_checks__title;
MP_QSTR_safety_checks__title_safety_override;
+ MP_QSTR_screen_brightness;
MP_QSTR_sd_card__all_data_will_be_lost;
MP_QSTR_sd_card__card_required;
MP_QSTR_sd_card__disable;
@@ -859,6 +876,7 @@ static void _librust_qstrs(void) {
MP_QSTR_wipe__info;
MP_QSTR_wipe__title;
MP_QSTR_wipe__want_to_wipe;
+ MP_QSTR_wipe_code;
MP_QSTR_wipe_code__change;
MP_QSTR_wipe_code__changed;
MP_QSTR_wipe_code__diff_from_pin;
@@ -916,6 +934,8 @@ static void _librust_qstrs(void) {
MP_QSTR_words__name;
MP_QSTR_words__no;
MP_QSTR_words__not_recommended;
+ MP_QSTR_words__off;
+ MP_QSTR_words__on;
MP_QSTR_words__operation_cancelled;
MP_QSTR_words__outputs;
MP_QSTR_words__pay_attention;
diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs
index 6be9e56f..bf413e7f 100644
--- a/core/embed/rust/src/translations/generated/translated_string.rs
+++ b/core/embed/rust/src/translations/generated/translated_string.rs
@@ -1476,6 +1476,8 @@ pub enum TranslatedString {
words__security = 1092, // "Security"
ble__version = 1093, // "Bluetooth version"
homescreen__firmware_type = 1094, // "Firmware type"
+ words__off = 1095, // "OFF"
+ words__on = 1096, // "ON"
}
impl TranslatedString {
@@ -3271,6 +3273,8 @@ impl TranslatedString {
(Self::words__security, "Security"),
(Self::ble__version, "Bluetooth version"),
(Self::homescreen__firmware_type, "Firmware type"),
+ (Self::words__off, "OFF"),
+ (Self::words__on, "ON"),
];
#[cfg(feature = "micropython")]
@@ -4703,6 +4707,8 @@ impl TranslatedString {
(Qstr::MP_QSTR_words__name, Self::words__name),
(Qstr::MP_QSTR_words__no, Self::words__no),
(Qstr::MP_QSTR_words__not_recommended, Self::words__not_recommended),
+ (Qstr::MP_QSTR_words__off, Self::words__off),
+ (Qstr::MP_QSTR_words__on, Self::words__on),
(Qstr::MP_QSTR_words__operation_cancelled, Self::words__operation_cancelled),
(Qstr::MP_QSTR_words__outputs, Self::words__outputs),
(Qstr::MP_QSTR_words__pay_attention, Self::words__pay_attention),
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index b1789641..653ff143 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -23,7 +23,8 @@ use crate::{
util::{upy_disable_animation, RecoveryType},
},
ui_firmware::{
- FirmwareUI, MAX_CHECKLIST_ITEMS, MAX_GROUP_SHARE_LINES, MAX_WORD_QUIZ_ITEMS,
+ FirmwareUI, MAX_CHECKLIST_ITEMS, MAX_GROUP_SHARE_LINES, MAX_PAIRED_DEVICES,
+ MAX_WORD_QUIZ_ITEMS,
},
ModelUI,
},
@@ -937,20 +938,43 @@ extern "C" fn new_show_homescreen(n_args: usize, args: *const Obj, kwargs: *mut
extern "C" fn new_show_device_menu(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let failed_backup: bool = kwargs.get(Qstr::MP_QSTR_failed_backup)?.try_into()?;
+ let paired_devices: Obj = kwargs.get(Qstr::MP_QSTR_paired_devices)?;
+ let paired_devices: Vec<TString, MAX_PAIRED_DEVICES> = util::iter_into_vec(paired_devices)?;
+ let connected_idx: Option<usize> =
+ kwargs.get(Qstr::MP_QSTR_connected_idx)?.try_into_option()?;
+ let bluetooth: Option<bool> = kwargs.get(Qstr::MP_QSTR_bluetooth)?.try_into_option()?;
+ let pin_code: Option<bool> = kwargs.get(Qstr::MP_QSTR_pin_code)?.try_into_option()?;
+ let auto_lock_delay: Option<TString> = kwargs
+ .get(Qstr::MP_QSTR_auto_lock_delay)?
+ .try_into_option()?;
+ let wipe_code: Option<bool> = kwargs.get(Qstr::MP_QSTR_wipe_code)?.try_into_option()?;
+ let check_backup: bool = kwargs.get(Qstr::MP_QSTR_check_backup)?.try_into()?;
let device_name: Option<TString> =
kwargs.get(Qstr::MP_QSTR_device_name)?.try_into_option()?;
+ let screen_brightness: Option<TString> = kwargs
+ .get(Qstr::MP_QSTR_screen_brightness)?
+ .try_into_option()?;
+ let haptic_feedback: Option<bool> = kwargs
+ .get(Qstr::MP_QSTR_haptic_feedback)?
+ .try_into_option()?;
+ let led_enabled: Option<bool> = kwargs.get(Qstr::MP_QSTR_led_enabled)?.try_into_option()?;
let about_items: Obj = kwargs.get(Qstr::MP_QSTR_about_items)?;
- let paired_devices: Obj = kwargs.get(Qstr::MP_QSTR_paired_devices)?;
- let paired_devices: Vec<TString, 1> = util::iter_into_vec(paired_devices)?;
- let auto_lock_delay: TString<'static> =
- kwargs.get(Qstr::MP_QSTR_auto_lock_delay)?.try_into()?;
let layout = ModelUI::show_device_menu(
failed_backup,
- device_name,
- about_items,
paired_devices,
+ connected_idx,
+ bluetooth,
+ pin_code,
auto_lock_delay,
+ wipe_code,
+ check_backup,
+ device_name,
+ screen_brightness,
+ haptic_feedback,
+ led_enabled,
+ about_items,
)?;
+
let layout_obj = LayoutObj::new_root(layout)?;
Ok(layout_obj.into())
};
@@ -1878,10 +1902,18 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// def show_device_menu(
/// *,
/// failed_backup: bool,
- /// device_name: str | None,
- /// about_items: list[PropertyType],
/// paired_devices: Iterable[str],
- /// auto_lock_delay: str,
+ /// connected_idx: int | None,
+ /// bluetooth: bool | None,
+ /// pin_code: bool | None,
+ /// auto_lock_delay: str | None,
+ /// wipe_code: bool | None,
+ /// check_backup: bool,
+ /// device_name: str | None,
+ /// screen_brightness: str | None,
+ /// haptic_feedback: bool | None,
+ /// led_enabled: bool | None,
+ /// about_items: list[tuple[str | None, str | bytes | None, bool | None]],
/// ) -> LayoutObj[UiResult | DeviceMenuResult | tuple[DeviceMenuResult, int]]:
/// """Show the device menu."""
Qstr::MP_QSTR_show_device_menu => obj_fn_kw!(0, new_show_device_menu).as_obj(),
@@ -2078,12 +2110,22 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// class DeviceMenuResult:
/// """Result of a device menu operation."""
/// BackupFailed: ClassVar[DeviceMenuResult]
- /// DevicePair: ClassVar[DeviceMenuResult]
+ /// DeviceConnect: ClassVar[DeviceMenuResult]
/// DeviceDisconnect: ClassVar[DeviceMenuResult]
- /// CheckBackup: ClassVar[DeviceMenuResult]
- /// WipeDevice: ClassVar[DeviceMenuResult]
- /// ScreenBrightness: ClassVar[DeviceMenuResult]
+ /// DevicePair: ClassVar[DeviceMenuResult]
+ /// DeviceUnpair: ClassVar[DeviceMenuResult]
+ /// DeviceUnpairAll: ClassVar[DeviceMenuResult]
+ /// Bluetooth: ClassVar[DeviceMenuResult]
+ /// PinCode: ClassVar[DeviceMenuResult]
+ /// PinRemove: ClassVar[DeviceMenuResult]
/// AutoLockDelay: ClassVar[DeviceMenuResult]
+ /// WipeCode: ClassVar[DeviceMenuResult]
+ /// WipeRemove: ClassVar[DeviceMenuResult]
+ /// CheckBackup: ClassVar[DeviceMenuResult]
/// DeviceName: ClassVar[DeviceMenuResult]
+ /// ScreenBrightness: ClassVar[DeviceMenuResult]
+ /// HapticFeedback: ClassVar[DeviceMenuResult]
+ /// LedEnabled: ClassVar[DeviceMenuResult]
+ /// WipeDevice: ClassVar[DeviceMenuResult]
Qstr::MP_QSTR_DeviceMenuResult => DEVICE_MENU_RESULT.as_obj(),
};
diff --git a/core/embed/rust/src/ui/layout/device_menu_result.rs b/core/embed/rust/src/ui/layout/device_menu_result.rs
index dadb1320..e3116595 100644
--- a/core/embed/rust/src/ui/layout/device_menu_result.rs
+++ b/core/embed/rust/src/ui/layout/device_menu_result.rs
@@ -7,27 +7,52 @@ use crate::micropython::{
static DEVICE_MENU_RESULT_BASE_TYPE: Type = obj_type! { name: Qstr::MP_QSTR_DeviceMenuResult, };
+// Root menu
pub static BACKUP_FAILED: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+// Bluetooth
+pub static BLUETOOTH: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+// "Pair & Connect"
pub static DEVICE_PAIR: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
pub static DEVICE_DISCONNECT: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
-pub static CHECK_BACKUP: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
-pub static WIPE_DEVICE: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
-pub static SCREEN_BRIGHTNESS: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static DEVICE_CONNECT: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static DEVICE_UNPAIR: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static DEVICE_UNPAIR_ALL: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+// Security menu
+pub static PIN_CODE: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static PIN_REMOVE: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
pub static AUTO_LOCK_DELAY: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static WIPE_CODE: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static WIPE_REMOVE: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static CHECK_BACKUP: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+// Device menu
pub static DEVICE_NAME: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static SCREEN_BRIGHTNESS: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static HAPTIC_FEEDBACK: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static LED_ENABLED: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
+pub static WIPE_DEVICE: SimpleTypeObj = SimpleTypeObj::new(&DEVICE_MENU_RESULT_BASE_TYPE);
// Create a DeviceMenuResult class that contains all result types
static DEVICE_MENU_RESULT_TYPE: Type = obj_type! {
name: Qstr::MP_QSTR_DeviceMenuResult,
locals: &obj_dict! { obj_map! {
Qstr::MP_QSTR_BackupFailed => BACKUP_FAILED.as_obj(),
+ Qstr::MP_QSTR_Bluetooth => BLUETOOTH.as_obj(),
Qstr::MP_QSTR_DevicePair => DEVICE_PAIR.as_obj(),
Qstr::MP_QSTR_DeviceDisconnect => DEVICE_DISCONNECT.as_obj(),
- Qstr::MP_QSTR_CheckBackup => CHECK_BACKUP.as_obj(),
- Qstr::MP_QSTR_WipeDevice => WIPE_DEVICE.as_obj(),
- Qstr::MP_QSTR_ScreenBrightness => SCREEN_BRIGHTNESS.as_obj(),
+ Qstr::MP_QSTR_DeviceConnect => DEVICE_CONNECT.as_obj(),
+ Qstr::MP_QSTR_DeviceUnpair => DEVICE_UNPAIR.as_obj(),
+ Qstr::MP_QSTR_DeviceUnpairAll => DEVICE_UNPAIR_ALL.as_obj(),
+ Qstr::MP_QSTR_PinCode => PIN_CODE.as_obj(),
+ Qstr::MP_QSTR_PinRemove => PIN_REMOVE.as_obj(),
Qstr::MP_QSTR_AutoLockDelay => AUTO_LOCK_DELAY.as_obj(),
+ Qstr::MP_QSTR_WipeCode => WIPE_CODE.as_obj(),
+ Qstr::MP_QSTR_WipeRemove => WIPE_REMOVE.as_obj(),
+ Qstr::MP_QSTR_CheckBackup => CHECK_BACKUP.as_obj(),
Qstr::MP_QSTR_DeviceName => DEVICE_NAME.as_obj(),
+ Qstr::MP_QSTR_ScreenBrightness => SCREEN_BRIGHTNESS.as_obj(),
+ Qstr::MP_QSTR_HapticFeedback => HAPTIC_FEEDBACK.as_obj(),
+ Qstr::MP_QSTR_LedEnabled => LED_ENABLED.as_obj(),
+ Qstr::MP_QSTR_WipeDevice => WIPE_DEVICE.as_obj(),
} },
};
diff --git a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
index a311f92c..3c006957 100644
--- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
@@ -28,7 +28,7 @@ use crate::{
},
ui_firmware::{
FirmwareUI, ERROR_NOT_IMPLEMENTED, MAX_CHECKLIST_ITEMS, MAX_GROUP_SHARE_LINES,
- MAX_MENU_ITEMS, MAX_WORD_QUIZ_ITEMS,
+ MAX_MENU_ITEMS, MAX_PAIRED_DEVICES, MAX_WORD_QUIZ_ITEMS,
},
ModelUI,
},
@@ -934,10 +934,18 @@ impl FirmwareUI for UIBolt {
fn show_device_menu(
_failed_backup: bool,
+ _paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ _connected_idx: Option<usize>,
+ _bluetooth: Option<bool>,
+ _pin_code: Option<bool>,
+ _auto_lock_delay: Option<TString<'static>>,
+ _wipe_code: Option<bool>,
+ _check_backup: bool,
_device_name: Option<TString<'static>>,
+ _screen_brightness: Option<TString<'static>>,
+ _haptic_feedback: Option<bool>,
+ _led_enabled: Option<bool>,
_about_items: Obj,
- _paired_devices: heapless::Vec<TString<'static>, 1>,
- _auto_lock_delay: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(
c"show_device_menu not supported",
diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
index a6e887c3..c3993fa1 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -27,7 +27,7 @@ use crate::{
},
ui_firmware::{
FirmwareUI, ERROR_NOT_IMPLEMENTED, MAX_CHECKLIST_ITEMS, MAX_GROUP_SHARE_LINES,
- MAX_MENU_ITEMS, MAX_WORD_QUIZ_ITEMS,
+ MAX_MENU_ITEMS, MAX_PAIRED_DEVICES, MAX_WORD_QUIZ_ITEMS,
},
ModelUI,
},
@@ -1128,10 +1128,18 @@ impl FirmwareUI for UICaesar {
fn show_device_menu(
_failed_backup: bool,
+ _paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ _connected_idx: Option<usize>,
+ _bluetooth: Option<bool>,
+ _pin_code: Option<bool>,
+ _auto_lock_delay: Option<TString<'static>>,
+ _wipe_code: Option<bool>,
+ _check_backup: bool,
_device_name: Option<TString<'static>>,
+ _screen_brightness: Option<TString<'static>>,
+ _haptic_feedback: Option<bool>,
+ _led_enabled: Option<bool>,
_about_items: Obj,
- _paired_devices: Vec<TString<'static>, 1>,
- _auto_lock_delay: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(
c"show_device_menu not supported",
diff --git a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
index e3ff65c9..e8b429a6 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -29,7 +29,7 @@ use crate::{
},
ui_firmware::{
FirmwareUI, ERROR_NOT_IMPLEMENTED, MAX_CHECKLIST_ITEMS, MAX_GROUP_SHARE_LINES,
- MAX_MENU_ITEMS, MAX_WORD_QUIZ_ITEMS,
+ MAX_MENU_ITEMS, MAX_PAIRED_DEVICES, MAX_WORD_QUIZ_ITEMS,
},
ModelUI,
},
@@ -1018,10 +1018,18 @@ impl FirmwareUI for UIDelizia {
fn show_device_menu(
_failed_backup: bool,
+ _paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ _connected_idx: Option<usize>,
+ _bluetooth: Option<bool>,
+ _pin_code: Option<bool>,
+ _auto_lock_delay: Option<TString<'static>>,
+ _wipe_code: Option<bool>,
+ _check_backup: bool,
_device_name: Option<TString<'static>>,
+ _screen_brightness: Option<TString<'static>>,
+ _haptic_feedback: Option<bool>,
+ _led_enabled: Option<bool>,
_about_items: Obj,
- _paired_devices: heapless::Vec<TString<'static>, 1>,
- _auto_lock_delay: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(
c"show_device_menu not supported",
diff --git a/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs b/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs
index 53671be6..00b3e142 100644
--- a/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs
@@ -148,16 +148,34 @@ impl ComponentMsgObj for SetBrightnessScreen {
impl ComponentMsgObj for DeviceMenuScreen {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
+ // Root menu
DeviceMenuMsg::BackupFailed => Ok(BACKUP_FAILED.as_obj()),
+ // Bluetooth
+ DeviceMenuMsg::Bluetooth => Ok(BLUETOOTH.as_obj()),
+ // "Pair & Connect"
DeviceMenuMsg::DevicePair => Ok(DEVICE_PAIR.as_obj()),
- DeviceMenuMsg::DeviceDisconnect(index) => {
- Ok(new_tuple(&[DEVICE_DISCONNECT.as_obj(), index.try_into()?])?)
+ DeviceMenuMsg::DeviceDisconnect => Ok(DEVICE_DISCONNECT.as_obj()),
+ DeviceMenuMsg::DeviceConnect(index) => {
+ Ok(new_tuple(&[DEVICE_CONNECT.as_obj(), index.try_into()?])?)
}
+ DeviceMenuMsg::DeviceUnpair(index) => {
+ Ok(new_tuple(&[DEVICE_UNPAIR.as_obj(), index.try_into()?])?)
+ }
+ DeviceMenuMsg::DeviceUnpairAll => Ok(DEVICE_UNPAIR_ALL.as_obj()),
+ // Security menu
+ DeviceMenuMsg::PinCode => Ok(PIN_CODE.as_obj()),
+ DeviceMenuMsg::PinRemove => Ok(PIN_REMOVE.as_obj()),
+ DeviceMenuMsg::AutoLockDelay => Ok(AUTO_LOCK_DELAY.as_obj()),
+ DeviceMenuMsg::WipeCode => Ok(WIPE_CODE.as_obj()),
+ DeviceMenuMsg::WipeRemove => Ok(WIPE_REMOVE.as_obj()),
DeviceMenuMsg::CheckBackup => Ok(CHECK_BACKUP.as_obj()),
- DeviceMenuMsg::WipeDevice => Ok(WIPE_DEVICE.as_obj()),
+ // Device menu
DeviceMenuMsg::DeviceName => Ok(DEVICE_NAME.as_obj()),
DeviceMenuMsg::ScreenBrightness => Ok(SCREEN_BRIGHTNESS.as_obj()),
- DeviceMenuMsg::AutoLockDelay => Ok(AUTO_LOCK_DELAY.as_obj()),
+ DeviceMenuMsg::HapticFeedback => Ok(HAPTIC_FEEDBACK.as_obj()),
+ DeviceMenuMsg::LedEnabled => Ok(LED_ENABLED.as_obj()),
+ DeviceMenuMsg::WipeDevice => Ok(WIPE_DEVICE.as_obj()),
+ // nothing selected
DeviceMenuMsg::Close => Ok(CANCELLED.as_obj()),
}
}
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
index fd11762d..c406b256 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
@@ -5,7 +5,6 @@ use crate::{
micropython::{gc::GcBox, obj::Obj},
strutil::TString,
translations::TR,
- trezorhal::storage::has_pin,
ui::{
component::{
text::{
@@ -17,6 +16,7 @@ use crate::{
geometry::{LinearPlacement, Rect},
layout::util::PropsList,
shape::Renderer,
+ ui_firmware::MAX_PAIRED_DEVICES,
},
};
@@ -33,11 +33,19 @@ use super::{
};
use heapless::Vec;
+// - root
+// - pair & connect
+// - settings
+// - security
+// - pin code
+// - wipe code
+// - device
+const MAX_SUBMENUS: usize = 8; // TODO: decrease to 7 BLE implementation
const MAX_DEPTH: usize = 3;
-const MAX_SUBSCREENS: usize = 10;
-const MAX_SUBMENUS: usize = MAX_SUBSCREENS - 2 /* (about and device screen) */;
+// submenus, device screens, regulatory and about screens
+const MAX_SUBSCREENS: usize = MAX_SUBMENUS + MAX_PAIRED_DEVICES + 2;
-const DISCONNECT_DEVICE_MENU_INDEX: usize = 1;
+const DIS_CONNECT_DEVICE_MENU_INDEX: usize = 0;
#[derive(Clone)]
enum Action {
@@ -52,20 +60,34 @@ pub enum DeviceMenuMsg {
// Root menu
BackupFailed,
+ // Bluetooth
+ Bluetooth,
+
// "Pair & Connect"
- DevicePair, // pair a new device
- DeviceDisconnect(
- usize, /* which device to disconnect, index in the list of devices */
+ DevicePair, // pair a new device
+ DeviceDisconnect, // disconnect a device
+ DeviceConnect(
+ usize, /* which device to connect, index in the list of devices */
+ ),
+ DeviceUnpair(
+ usize, /* which device to unpair, index in the list of devices */
),
+ DeviceUnpairAll,
// Security menu
+ PinCode,
+ PinRemove,
+ AutoLockDelay,
+ WipeCode,
+ WipeRemove,
CheckBackup,
- WipeDevice,
// Device menu
DeviceName,
ScreenBrightness,
- AutoLockDelay,
+ HapticFeedback,
+ LedEnabled,
+ WipeDevice,
// nothing selected
Close,
@@ -168,13 +190,21 @@ pub struct DeviceMenuScreen {
}
impl DeviceMenuScreen {
+ #[allow(clippy::too_many_arguments)]
pub fn new(
failed_backup: bool,
- about_items: Obj,
+ paired_devices: Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ _connected_idx: Option<usize>,
+ _bluetooth: Option<bool>,
+ _pin_code: Option<bool>,
+ auto_lock_delay: Option<TString<'static>>,
+ _wipe_code: Option<bool>,
+ _check_backup: bool,
device_name: Option<TString<'static>>,
- // NB: we currently only support one device at a time.
- paired_devices: Vec<TString<'static>, 1>,
- auto_lock_delay: TString<'static>,
+ _screen_brightness: Option<TString<'static>>,
+ _haptic_feedback: Option<bool>,
+ led: Option<bool>,
+ about_items: Obj,
) -> Result<Self, Error> {
let mut screen = Self {
bounds: Rect::zero(),
@@ -189,14 +219,14 @@ impl DeviceMenuScreen {
let about = screen.add_subscreen(Subscreen::AboutScreen);
let regulatory = screen.add_subscreen(Subscreen::RegulatoryScreen);
let security = screen.add_security_menu();
- let device = screen.add_device_menu(device_name, regulatory, about, auto_lock_delay);
+ let device = screen.add_device_menu(device_name, regulatory, about, auto_lock_delay, led);
let settings = screen.add_settings_menu(security, device);
let is_connected = !paired_devices.is_empty(); // FIXME after BLE API has this
let connected_subtext: Option<TString<'static>> =
is_connected.then_some("1 device connected".into());
- let mut paired_device_indices: Vec<usize, 1> = Vec::new();
+ let mut paired_device_indices: Vec<usize, MAX_PAIRED_DEVICES> = Vec::new();
for (i, device) in paired_devices.iter().enumerate() {
unwrap!(paired_device_indices
.push(screen.add_subscreen(Subscreen::DeviceScreen(*device, i))));
@@ -215,8 +245,8 @@ impl DeviceMenuScreen {
fn add_paired_devices_menu(
&mut self,
- paired_devices: Vec<TString<'static>, 1>,
- paired_device_indices: Vec<usize, 1>,
+ paired_devices: Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ paired_device_indices: Vec<usize, MAX_PAIRED_DEVICES>,
) -> usize {
let mut items: Vec<MenuItem, SHORT_MENU_ITEMS> = Vec::new();
for (device, idx) in paired_devices.iter().zip(paired_device_indices) {
@@ -291,7 +321,8 @@ impl DeviceMenuScreen {
device_name: Option<TString<'static>>,
regulatory_index: usize,
about_index: usize,
- auto_lock_delay: TString<'static>,
+ auto_lock_delay: Option<TString<'static>>,
+ led: Option<bool>,
) -> usize {
let mut items: Vec<MenuItem, SHORT_MENU_ITEMS> = Vec::new();
if let Some(device_name) = device_name {
@@ -308,7 +339,7 @@ impl DeviceMenuScreen {
Some(Action::Return(DeviceMenuMsg::ScreenBrightness)),
)));
- if has_pin() {
+ if let Some(auto_lock_delay) = auto_lock_delay {
let mut autolock_delay_item = MenuItem::new(
TR::auto_lock__title.into(),
Some(Action::Return(DeviceMenuMsg::AutoLockDelay)),
@@ -317,6 +348,23 @@ impl DeviceMenuScreen {
unwrap!(items.push(autolock_delay_item));
}
+ if let Some(led) = led {
+ let mut led_item = MenuItem::new(
+ TR::words__led.into(),
+ Some(Action::Return(DeviceMenuMsg::LedEnabled)),
+ );
+ let subtext = if led {
+ (
+ TR::words__on.into(),
+ Some(&theme::TEXT_MENU_ITEM_SUBTITLE_GREEN),
+ )
+ } else {
+ (TR::words__off.into(), None)
+ };
+ led_item.with_subtext(Some(subtext));
+ unwrap!(items.push(led_item));
+ }
+
unwrap!(items.push(MenuItem::new(
TR::regulatory_certification__title.into(),
Some(Action::GoTo(regulatory_index))
@@ -527,9 +575,9 @@ impl Component for DeviceMenuScreen {
(Subscreen::Submenu(..) | Subscreen::DeviceScreen(..), ActiveScreen::Menu(menu)) => {
match menu.event(ctx, event) {
Some(VerticalMenuScreenMsg::Selected(index)) => {
- if let Subscreen::DeviceScreen(_, i) = subscreen {
- if index == DISCONNECT_DEVICE_MENU_INDEX {
- return Some(DeviceMenuMsg::DeviceDisconnect(*i));
+ if let Subscreen::DeviceScreen(_, _) = subscreen {
+ if index == DIS_CONNECT_DEVICE_MENU_INDEX {
+ return Some(DeviceMenuMsg::DeviceDisconnect);
}
} else {
return self.handle_submenu(ctx, index);
diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
index 5ca150fb..5c3aec15 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
@@ -27,7 +27,7 @@ use crate::{
},
ui_firmware::{
FirmwareUI, ERROR_NOT_IMPLEMENTED, MAX_CHECKLIST_ITEMS, MAX_GROUP_SHARE_LINES,
- MAX_MENU_ITEMS, MAX_WORD_QUIZ_ITEMS,
+ MAX_MENU_ITEMS, MAX_PAIRED_DEVICES, MAX_WORD_QUIZ_ITEMS,
},
ModelUI,
},
@@ -51,8 +51,6 @@ use super::{
UIEckhart,
};
-use heapless::Vec;
-
impl FirmwareUI for UIEckhart {
fn confirm_action(
title: TString<'static>,
@@ -1197,17 +1195,33 @@ impl FirmwareUI for UIEckhart {
fn show_device_menu(
failed_backup: bool,
+ paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ connected_idx: Option<usize>,
+ bluetooth: Option<bool>,
+ pin_code: Option<bool>,
+ auto_lock_delay: Option<TString<'static>>,
+ wipe_code: Option<bool>,
+ check_backup: bool,
device_name: Option<TString<'static>>,
+ screen_brightness: Option<TString<'static>>,
+ haptic_feedback: Option<bool>,
+ led_enabled: Option<bool>,
about_items: Obj,
- paired_devices: Vec<TString<'static>, 1>,
- auto_lock_delay: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
let layout = RootComponent::new(DeviceMenuScreen::new(
failed_backup,
- about_items,
- device_name,
paired_devices,
+ connected_idx,
+ bluetooth,
+ pin_code,
auto_lock_delay,
+ wipe_code,
+ check_backup,
+ device_name,
+ screen_brightness,
+ haptic_feedback,
+ led_enabled,
+ about_items,
)?);
Ok(layout)
}
diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs
index 65b806b5..ef7f87ad 100644
--- a/core/embed/rust/src/ui/ui_firmware.rs
+++ b/core/embed/rust/src/ui/ui_firmware.rs
@@ -16,6 +16,8 @@ pub const MAX_WORD_QUIZ_ITEMS: usize = 3;
pub const MAX_GROUP_SHARE_LINES: usize = 4;
pub const MAX_MENU_ITEMS: usize = 5;
+pub const MAX_PAIRED_DEVICES: usize = 8; // Maximum number of paired devices in the device menu
+
pub const ERROR_NOT_IMPLEMENTED: Error = Error::ValueError(c"not implemented");
pub trait FirmwareUI {
@@ -358,12 +360,21 @@ pub trait FirmwareUI {
lockable: bool,
) -> Result<impl LayoutMaybeTrace, Error>;
+ #[allow(clippy::too_many_arguments)]
fn show_device_menu(
failed_backup: bool,
+ paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ connected_idx: Option<usize>,
+ bluetooth: Option<bool>,
+ pin_code: Option<bool>,
+ auto_lock_delay: Option<TString<'static>>,
+ wipe_code: Option<bool>,
+ check_backup: bool,
device_name: Option<TString<'static>>,
+ screen_brightness: Option<TString<'static>>,
+ haptic_feedback: Option<bool>,
+ led_enabled: Option<bool>,
about_items: Obj,
- paired_devices: Vec<TString<'static>, 1>,
- auto_lock_delay: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error>;
fn show_pairing_device_name(
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index 3afb88c7..637793f4 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -623,10 +623,18 @@ def show_homescreen(
def show_device_menu(
*,
failed_backup: bool,
- device_name: str | None,
- about_items: list[PropertyType],
paired_devices: Iterable[str],
- auto_lock_delay: str,
+ connected_idx: int | None,
+ bluetooth: bool | None,
+ pin_code: bool | None,
+ auto_lock_delay: str | None,
+ wipe_code: bool | None,
+ check_backup: bool,
+ device_name: str | None,
+ screen_brightness: str | None,
+ haptic_feedback: bool | None,
+ led_enabled: bool | None,
+ about_items: list[tuple[str | None, str | bytes | None, bool | None]],
) -> LayoutObj[UiResult | DeviceMenuResult | tuple[DeviceMenuResult, int]]:
"""Show the device menu."""
@@ -843,10 +851,20 @@ class LayoutState:
class DeviceMenuResult:
"""Result of a device menu operation."""
BackupFailed: ClassVar[DeviceMenuResult]
- DevicePair: ClassVar[DeviceMenuResult]
+ DeviceConnect: ClassVar[DeviceMenuResult]
DeviceDisconnect: ClassVar[DeviceMenuResult]
- CheckBackup: ClassVar[DeviceMenuResult]
- WipeDevice: ClassVar[DeviceMenuResult]
- ScreenBrightness: ClassVar[DeviceMenuResult]
+ DevicePair: ClassVar[DeviceMenuResult]
+ DeviceUnpair: ClassVar[DeviceMenuResult]
+ DeviceUnpairAll: ClassVar[DeviceMenuResult]
+ Bluetooth: ClassVar[DeviceMenuResult]
+ PinCode: ClassVar[DeviceMenuResult]
+ PinRemove: ClassVar[DeviceMenuResult]
AutoLockDelay: ClassVar[DeviceMenuResult]
+ WipeCode: ClassVar[DeviceMenuResult]
+ WipeRemove: ClassVar[DeviceMenuResult]
+ CheckBackup: ClassVar[DeviceMenuResult]
DeviceName: ClassVar[DeviceMenuResult]
+ ScreenBrightness: ClassVar[DeviceMenuResult]
+ HapticFeedback: ClassVar[DeviceMenuResult]
+ LedEnabled: ClassVar[DeviceMenuResult]
+ WipeDevice: ClassVar[DeviceMenuResult]
diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi
index b06962e9..db942c6d 100644
--- a/core/mocks/trezortranslate_keys.pyi
+++ b/core/mocks/trezortranslate_keys.pyi
@@ -1019,6 +1019,8 @@ class TR:
words__name: str = "Name"
words__no: str = "No"
words__not_recommended: str = "Not recommended!"
+ words__off: str = "OFF"
+ words__on: str = "ON"
words__operation_cancelled: str = "Operation cancelled"
words__outputs: str = "outputs"
words__pay_attention: str = "Pay attention"
diff --git a/core/src/apps/homescreen/device_menu.py b/core/src/apps/homescreen/device_menu.py
index 7d2c2b04..c8a65a95 100644
--- a/core/src/apps/homescreen/device_menu.py
+++ b/core/src/apps/homescreen/device_menu.py
@@ -60,37 +60,65 @@ async def handle_device_menu() -> None:
trezorui_api.show_device_menu(
failed_backup=failed_backup,
paired_devices=paired_devices,
+ connected_idx=None, # TODO implement
+ bluetooth=None, # TODO implement
+ pin_code=None, # TODO implement
+ auto_lock_delay=auto_lock_delay,
+ wipe_code=False, # TODO implement
+ check_backup=False, # TODO implement
device_name=device_name,
+ screen_brightness=None, # TODO implement
+ haptic_feedback=None, # TODO implement
+ led_enabled=None, # TODO implement
about_items=[
(TR.homescreen__firmware_version, firmware_version, False),
(TR.homescreen__firmware_type, firmware_type, False),
(TR.ble__version, bluetooth_version, False),
],
- auto_lock_delay=auto_lock_delay,
),
"device_menu",
)
-
- if menu_result is DeviceMenuResult.DevicePair:
+ # Root menu
+ if menu_result is DeviceMenuResult.BackupFailed:
+ pass # TODO implement backup failed handling
+ # Pair & Connect
+ elif menu_result is DeviceMenuResult.DeviceDisconnect:
+ pass # TODO implement device disconnect handling
+ elif menu_result is DeviceMenuResult.DevicePair:
from apps.management.ble.pair_new_device import pair_new_device
await pair_new_device()
- elif menu_result is DeviceMenuResult.ScreenBrightness:
- from trezor.ui.layouts import set_brightness
-
- await set_brightness()
- elif menu_result is DeviceMenuResult.WipeDevice:
- from trezor.messages import WipeDevice
-
- from apps.management.wipe_device import wipe_device
-
- await wipe_device(WipeDevice())
+ elif menu_result is DeviceMenuResult.DeviceUnpairAll:
+ pass # TODO implement all devices unpair handling
+ elif isinstance(menu_result, tuple):
+ # It's a tuple with (result_type, index)
+ result_type, index = menu_result
+ if result_type is DeviceMenuResult.DeviceConnect:
+ pass # TODO implement device connect handling
+ elif result_type is DeviceMenuResult.DeviceUnpair:
+ pass # TODO implement device unpair handling
+ else:
+ raise RuntimeError(f"Unknown menu {result_type}, {index}")
+ # Bluetooth
+ elif menu_result is DeviceMenuResult.Bluetooth:
+ pass # TODO implement bluetooth handling
+ # Security settings
+ elif menu_result is DeviceMenuResult.PinCode:
+ pass # TODO implement pin code handling
+ elif menu_result is DeviceMenuResult.PinRemove:
+ pass # TODO implement pin remove handling
elif menu_result is DeviceMenuResult.AutoLockDelay:
-
if config.has_pin():
auto_lock_delay_ms = await _prompt_auto_lock_delay()
storage_device.set_autolock_delay_ms(auto_lock_delay_ms)
+ elif menu_result is DeviceMenuResult.WipeCode:
+ pass # TODO implement wipe code handling
+ elif menu_result is DeviceMenuResult.WipeRemove:
+ pass # TODO implement wipe remove handling
+ elif menu_result is DeviceMenuResult.CheckBackup:
+ pass # TODO implement check backup handling
+ # Device settings
elif menu_result is DeviceMenuResult.DeviceName:
from trezor.messages import ApplySettings
@@ -108,16 +136,17 @@ async def handle_device_menu() -> None:
)
assert isinstance(label, str)
await apply_settings(ApplySettings(label=label))
- elif isinstance(menu_result, tuple):
- # It's a tuple with (result_type, index)
- result_type, index = menu_result
- if result_type is DeviceMenuResult.DeviceDisconnect:
- from trezor.messages import BleUnpair
+ elif menu_result is DeviceMenuResult.ScreenBrightness:
+ pass # TODO implement screen brightness handling
+ elif menu_result is DeviceMenuResult.HapticFeedback:
+ pass # TODO implement haptic feedback handling
+ elif menu_result is DeviceMenuResult.LedEnabled:
+ pass # TODO implement led handling
+ elif menu_result is DeviceMenuResult.WipeDevice:
+ from trezor.messages import WipeDevice
- from apps.management.ble.unpair import unpair
+ from apps.management.wipe_device import wipe_device
- await unpair(BleUnpair(all=False)) # FIXME we can only unpair current
- else:
- raise RuntimeError(f"Unknown menu {result_type}, {index}")
+ await wipe_device(WipeDevice())
else:
raise RuntimeError(f"Unknown menu {menu_result}")
diff --git a/core/translations/en.json b/core/translations/en.json
index 96ad26e8..df929ab0 100644
--- a/core/translations/en.json
+++ b/core/translations/en.json
@@ -1236,6 +1236,8 @@
"words__name": "Name",
"words__no": "No",
"words__not_recommended": "Not recommended!",
+ "words__off": "OFF",
+ "words__on": "ON",
"words__operation_cancelled": "Operation cancelled",
"words__outputs": "outputs",
"words__pay_attention": "Pay attention",
diff --git a/core/translations/order.json b/core/translations/order.json
index 19887559..8f138ae1 100644
--- a/core/translations/order.json
+++ b/core/translations/order.json
@@ -1093,5 +1093,7 @@
"1091": "words__review",
"1092": "words__security",
"1093": "ble__version",
- "1094": "homescreen__firmware_type"
+ "1094": "homescreen__firmware_type",
+ "1095": "words__off",
+ "1096": "words__on"
}
diff --git a/core/translations/signatures.json b/core/translations/signatures.json
index dd49e4fb..098f3601 100644
--- a/core/translations/signatures.json
+++ b/core/translations/signatures.json
@@ -1,8 +1,8 @@
{
"current": {
- "merkle_root": "45ed07c6ca2fccece4e51ea0581b2360e0d399d8f5fe67ea96694e74c3b69eee",
- "datetime": "2025-08-24T17:39:52.334627+00:00",
- "commit": "1500a2c16f1e2aacd64ed91d59c4165953c3e834"
+ "merkle_root": "23b5c97117589855f7bdda8f991c369f1692f32e1ecc2cb6f94fc7f8a66b137d",
+ "datetime": "2025-08-24T18:25:25.894084+00:00",
+ "commit": "e20b161bf1df9f3da1b8329310e3d9668b21f07a"
},
"history": [
{
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.