chore(core/eckhart): add device menu bluetooth setting
What changed, and why it matters
This commit adds a Bluetooth on/off menu item to the device settings screen for the upcoming Trezor Safe 7 (Eckhart layout). It is purely a user-interface change: it shows a label and a confirmation prompt, but the actual Bluetooth enable/disable logic is left as a TODO and does nothing. There is no security fix or security-relevant behavior change.
No security action required. Treat as normal feature/UI development. When the TODO is implemented, review the actual Bluetooth state-change logic for authorization and secure-storage handling.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch wires up a previously unused bluetooth parameter in the Eckhart device menu, adds translation strings (ble__enable, ble__disable, words__bluetooth), and makes selecting the Bluetooth menu item call confirm_action with a toggle prompt. The handler ends with pass # TODO implement bluetooth handling, so no Bluetooth state is actually modified. Translation signature metadata is updated to match the new strings.
Changed components
core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rscore/src/apps/homescreen/device_menu.pycore/translations/en.jsoncore/translations/order.jsoncore/translations/signatures.jsoncore/embed/rust/src/translations/generated/translated_string.rscore/embed/rust/librust_qstr.hcore/mocks/trezortranslate_keys.pyiInspect captured patch +59 / −8
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index c76c7a73..1e6ba4e3 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -171,6 +171,8 @@ static void _librust_qstrs(void) {
MP_QSTR_bitcoin__unverified_external_inputs;
MP_QSTR_bitcoin__valid_signature;
MP_QSTR_bitcoin__voting_rights;
+ MP_QSTR_ble__disable;
+ MP_QSTR_ble__enable;
MP_QSTR_ble__manage_paired;
MP_QSTR_ble__pair_new;
MP_QSTR_ble__pair_title;
@@ -916,6 +918,7 @@ static void _librust_qstrs(void) {
MP_QSTR_words__assets;
MP_QSTR_words__authenticate;
MP_QSTR_words__blockhash;
+ MP_QSTR_words__bluetooth;
MP_QSTR_words__buying;
MP_QSTR_words__cancel_and_exit;
MP_QSTR_words__cancel_question;
diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs
index 1877d88a..9d991c52 100644
--- a/core/embed/rust/src/translations/generated/translated_string.rs
+++ b/core/embed/rust/src/translations/generated/translated_string.rs
@@ -1503,6 +1503,9 @@ pub enum TranslatedString {
wipe_code__title = 1112, // "Wipe code"
words__disabled = 1113, // "Disabled"
words__enabled = 1114, // "Enabled"
+ ble__disable = 1115, // "Turn Bluetooth off?"
+ ble__enable = 1116, // "Turn Bluetooth on?"
+ words__bluetooth = 1117, // "Bluetooth"
}
impl TranslatedString {
@@ -3325,6 +3328,9 @@ impl TranslatedString {
(Self::wipe_code__title, "Wipe code"),
(Self::words__disabled, "Disabled"),
(Self::words__enabled, "Enabled"),
+ (Self::ble__disable, "Turn Bluetooth off?"),
+ (Self::ble__enable, "Turn Bluetooth on?"),
+ (Self::words__bluetooth, "Bluetooth"),
];
#[cfg(feature = "micropython")]
@@ -3402,6 +3408,8 @@ impl TranslatedString {
(Qstr::MP_QSTR_bitcoin__unverified_external_inputs, Self::bitcoin__unverified_external_inputs),
(Qstr::MP_QSTR_bitcoin__valid_signature, Self::bitcoin__valid_signature),
(Qstr::MP_QSTR_bitcoin__voting_rights, Self::bitcoin__voting_rights),
+ (Qstr::MP_QSTR_ble__disable, Self::ble__disable),
+ (Qstr::MP_QSTR_ble__enable, Self::ble__enable),
(Qstr::MP_QSTR_ble__manage_paired, Self::ble__manage_paired),
(Qstr::MP_QSTR_ble__pair_new, Self::ble__pair_new),
(Qstr::MP_QSTR_ble__pair_title, Self::ble__pair_title),
@@ -4753,6 +4761,7 @@ impl TranslatedString {
(Qstr::MP_QSTR_words__assets, Self::words__assets),
(Qstr::MP_QSTR_words__authenticate, Self::words__authenticate),
(Qstr::MP_QSTR_words__blockhash, Self::words__blockhash),
+ (Qstr::MP_QSTR_words__bluetooth, Self::words__bluetooth),
(Qstr::MP_QSTR_words__buying, Self::words__buying),
(Qstr::MP_QSTR_words__cancel_and_exit, Self::words__cancel_and_exit),
(Qstr::MP_QSTR_words__cancel_question, Self::words__cancel_question),
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 5d5087c5..cb22dc5d 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
@@ -196,7 +196,7 @@ impl DeviceMenuScreen {
failed_backup: bool,
paired_devices: Vec<TString<'static>, MAX_PAIRED_DEVICES>,
_connected_idx: Option<usize>,
- _bluetooth: Option<bool>,
+ bluetooth: Option<bool>,
pin_code: Option<bool>,
auto_lock_delay: Option<TString<'static>>,
wipe_code: Option<bool>,
@@ -236,7 +236,7 @@ impl DeviceMenuScreen {
regulatory,
about,
);
- let settings = screen.add_settings_menu(security, device);
+ let settings = screen.add_settings_menu(bluetooth, security, device);
let is_connected = !paired_devices.is_empty(); // FIXME after BLE API has this
let connected_subtext: Option<TString<'static>> =
@@ -302,8 +302,29 @@ impl DeviceMenuScreen {
self.add_subscreen(Subscreen::Submenu(submenu_index))
}
- fn add_settings_menu(&mut self, security_index: Option<usize>, device_index: usize) -> usize {
+ fn add_settings_menu(
+ &mut self,
+ bluetooth: Option<bool>,
+ security_index: Option<usize>,
+ device_index: usize,
+ ) -> usize {
let mut items: Vec<MenuItem, MEDIUM_MENU_ITEMS> = Vec::new();
+ if let Some(bluetooth) = bluetooth {
+ let mut bluetooth_item = MenuItem::new(
+ TR::words__bluetooth.into(),
+ Some(Action::Return(DeviceMenuMsg::Bluetooth)),
+ );
+ let subtext = if bluetooth {
+ (
+ TR::words__on.into(),
+ Some(&theme::TEXT_MENU_ITEM_SUBTITLE_GREEN),
+ )
+ } else {
+ (TR::words__off.into(), None)
+ };
+ bluetooth_item.with_subtext(Some(subtext));
+ unwrap!(items.push(bluetooth_item));
+ }
if let Some(security_index) = security_index {
unwrap!(items.push(MenuItem::new(
TR::words__security.into(),
diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi
index 28da1e6c..d75fbb3c 100644
--- a/core/mocks/trezortranslate_keys.pyi
+++ b/core/mocks/trezortranslate_keys.pyi
@@ -75,6 +75,8 @@ class TR:
bitcoin__unverified_external_inputs: str = "The transaction contains unverified external inputs."
bitcoin__valid_signature: str = "The signature is valid."
bitcoin__voting_rights: str = "Voting rights to"
+ ble__disable: str = "Turn Bluetooth off?"
+ ble__enable: str = "Turn Bluetooth on?"
ble__manage_paired: str = "Manage paired devices"
ble__pair_new: str = "Pair new device"
ble__pair_title: str = "Pair & connect"
@@ -1008,6 +1010,7 @@ class TR:
words__assets: str = "Assets"
words__authenticate: str = "Authenticate"
words__blockhash: str = "Blockhash"
+ words__bluetooth: str = "Bluetooth"
words__buying: str = "Buying"
words__cancel_and_exit: str = "Cancel and exit"
words__cancel_question: str = "Cancel?"
diff --git a/core/src/apps/homescreen/device_menu.py b/core/src/apps/homescreen/device_menu.py
index 5f7a639a..441b7b0d 100644
--- a/core/src/apps/homescreen/device_menu.py
+++ b/core/src/apps/homescreen/device_menu.py
@@ -37,7 +37,7 @@ async def handle_device_menu() -> None:
failed_backup=failed_backup,
paired_devices=paired_devices,
connected_idx=None,
- bluetooth=None,
+ bluetooth=True, # TODO implement bluetooth handling
pin_code=config.has_pin() if storage_device.is_initialized() else None,
auto_lock_delay=auto_lock_delay,
wipe_code=(
@@ -93,6 +93,15 @@ async def handle_device_menu() -> None:
raise RuntimeError(f"Unknown menu {result_type}, {index}")
# Bluetooth
elif menu_result is DeviceMenuResult.Bluetooth:
+ from trezor.ui.layouts import confirm_action
+
+ turned_on = ble.is_connected()
+
+ await confirm_action(
+ "ble__settings",
+ TR.words__bluetooth,
+ TR.ble__disable if turned_on else TR.ble__enable,
+ )
pass # TODO implement bluetooth handling
# Security settings
elif menu_result is DeviceMenuResult.PinCode:
diff --git a/core/translations/en.json b/core/translations/en.json
index 4d90c033..7ef0efc9 100644
--- a/core/translations/en.json
+++ b/core/translations/en.json
@@ -107,6 +107,8 @@
"bitcoin__unverified_external_inputs": "The transaction contains unverified external inputs.",
"bitcoin__valid_signature": "The signature is valid.",
"bitcoin__voting_rights": "Voting rights to",
+ "ble__disable": "Turn Bluetooth off?",
+ "ble__enable": "Turn Bluetooth on?",
"ble__manage_paired": "Manage paired devices",
"ble__pair_new": "Pair new device",
"ble__pair_title": "Pair & connect",
@@ -1225,6 +1227,7 @@
"words__assets": "Assets",
"words__authenticate": "Authenticate",
"words__blockhash": "Blockhash",
+ "words__bluetooth": "Bluetooth",
"words__buying": "Buying",
"words__cancel_and_exit": "Cancel and exit",
"words__cancel_question": "Cancel?",
diff --git a/core/translations/order.json b/core/translations/order.json
index 53251477..c6b7413d 100644
--- a/core/translations/order.json
+++ b/core/translations/order.json
@@ -1113,5 +1113,8 @@
"1111": "wipe_code__remove",
"1112": "wipe_code__title",
"1113": "words__disabled",
- "1114": "words__enabled"
+ "1114": "words__enabled",
+ "1115": "ble__disable",
+ "1116": "ble__enable",
+ "1117": "words__bluetooth"
}
diff --git a/core/translations/signatures.json b/core/translations/signatures.json
index 2a6cbeb5..367597d4 100644
--- a/core/translations/signatures.json
+++ b/core/translations/signatures.json
@@ -1,8 +1,8 @@
{
"current": {
- "merkle_root": "0fc289ede58a215cc208d7fa364371c68494c4ca6de6e247dd312fa130134a4f",
- "datetime": "2025-08-26T12:39:15.483270+00:00",
- "commit": "19f5faca506cc28166289f45029bd3b4ac5b93fe"
+ "merkle_root": "1f03e0ef6491c8ade57998fa0dd1af61f2c00c75296c3597a55ff6314a419f14",
+ "datetime": "2025-08-26T12:39:46.580646+00:00",
+ "commit": "29495149aa499ebb08c4c6b707e38998ca4818b2"
},
"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.