chore(core/eckhart): update device menu about screen
What changed, and why it matters
This commit is a routine user-interface update for the Trezor hardware wallet. It changes the 'About' screen in the device menu to show more information—firmware version, firmware type (Bitcoin-only or Universal), and a Bluetooth version number—instead of only the firmware version. There is no security-relevant change; it is purely a display/layout improvement.
No security action required. Treat as a normal UI/UX maintenance commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the device menu’s ‘About’ screen to accept a generic list of labeled properties (about_items) rather than a single firmware_version string. It adds translation keys for ‘Bluetooth version’ and ‘Firmware type’, updates the Python homescreen code to build a list of three property tuples, and adjusts the Rust UI trait signatures and the Eckhart layout implementation to render the list via PropsList. Other layout targets (Bolt, Caesar, Delizia) receive stub parameter updates. Translation signature metadata is refreshed as part of normal translation regeneration.
Changed components
core/src/apps/homescreen/device_menu.pycore/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rscore/embed/rust/src/ui/api/firmware_micropython.rscore/embed/rust/src/ui/ui_firmware.rscore/embed/rust/src/ui/layout_bolt/ui_firmware.rscore/embed/rust/src/ui/layout_caesar/ui_firmware.rscore/embed/rust/src/ui/layout_delizia/ui_firmware.rscore/embed/rust/src/ui/layout_eckhart/ui_firmware.rscore/embed/rust/src/ui/layout_eckhart/component_msg_obj.rscore/translations/en.jsoncore/translations/order.jsoncore/translations/signatures.jsoncore/mocks/generated/trezorui_api.pyicore/mocks/trezortranslate_keys.pyicore/embed/rust/librust_qstr.hcore/embed/rust/src/translations/generated/translated_string.rsInspect captured patch +64 / −41
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index a336fe73..53cb58cb 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -60,6 +60,7 @@ static void _librust_qstrs(void) {
MP_QSTR___del__;
MP_QSTR___dict__;
MP_QSTR___name__;
+ MP_QSTR_about_items;
MP_QSTR_account;
MP_QSTR_account_items;
MP_QSTR_account_path;
@@ -166,6 +167,7 @@ static void _librust_qstrs(void) {
MP_QSTR_ble__unpair_all;
MP_QSTR_ble__unpair_current;
MP_QSTR_ble__unpair_title;
+ MP_QSTR_ble__version;
MP_QSTR_ble_event;
MP_QSTR_bootscreen;
MP_QSTR_br_code;
@@ -302,7 +304,6 @@ static void _librust_qstrs(void) {
MP_QSTR_firmware_update__restart;
MP_QSTR_firmware_update__title;
MP_QSTR_firmware_update__title_fingerprint;
- MP_QSTR_firmware_version;
MP_QSTR_flow_confirm_output;
MP_QSTR_flow_confirm_set_new_pin;
MP_QSTR_flow_get_address;
@@ -319,6 +320,7 @@ static void _librust_qstrs(void) {
MP_QSTR_hold_danger;
MP_QSTR_homescreen__click_to_connect;
MP_QSTR_homescreen__click_to_unlock;
+ MP_QSTR_homescreen__firmware_type;
MP_QSTR_homescreen__firmware_version;
MP_QSTR_homescreen__set_default;
MP_QSTR_homescreen__settings_subtitle;
diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs
index 18f81661..6be9e56f 100644
--- a/core/embed/rust/src/translations/generated/translated_string.rs
+++ b/core/embed/rust/src/translations/generated/translated_string.rs
@@ -1474,6 +1474,8 @@ pub enum TranslatedString {
words__manage = 1090, // "Manage"
words__review = 1091, // "Review"
words__security = 1092, // "Security"
+ ble__version = 1093, // "Bluetooth version"
+ homescreen__firmware_type = 1094, // "Firmware type"
}
impl TranslatedString {
@@ -3267,6 +3269,8 @@ impl TranslatedString {
(Self::words__manage, "Manage"),
(Self::words__review, "Review"),
(Self::words__security, "Security"),
+ (Self::ble__version, "Bluetooth version"),
+ (Self::homescreen__firmware_type, "Firmware type"),
];
#[cfg(feature = "micropython")]
@@ -3350,6 +3354,7 @@ impl TranslatedString {
(Qstr::MP_QSTR_ble__unpair_all, Self::ble__unpair_all),
(Qstr::MP_QSTR_ble__unpair_current, Self::ble__unpair_current),
(Qstr::MP_QSTR_ble__unpair_title, Self::ble__unpair_title),
+ (Qstr::MP_QSTR_ble__version, Self::ble__version),
(Qstr::MP_QSTR_brightness__change_title, Self::brightness__change_title),
(Qstr::MP_QSTR_brightness__changed_title, Self::brightness__changed_title),
(Qstr::MP_QSTR_brightness__title, Self::brightness__title),
@@ -3883,6 +3888,7 @@ impl TranslatedString {
(Qstr::MP_QSTR_haptic_feedback__title, Self::haptic_feedback__title),
(Qstr::MP_QSTR_homescreen__click_to_connect, Self::homescreen__click_to_connect),
(Qstr::MP_QSTR_homescreen__click_to_unlock, Self::homescreen__click_to_unlock),
+ (Qstr::MP_QSTR_homescreen__firmware_type, Self::homescreen__firmware_type),
(Qstr::MP_QSTR_homescreen__firmware_version, Self::homescreen__firmware_version),
(Qstr::MP_QSTR_homescreen__set_default, Self::homescreen__set_default),
(Qstr::MP_QSTR_homescreen__settings_subtitle, Self::homescreen__settings_subtitle),
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index 6998fb1d..b1789641 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -937,17 +937,17 @@ 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 firmware_version: TString = kwargs.get(Qstr::MP_QSTR_firmware_version)?.try_into()?;
let device_name: Option<TString> =
kwargs.get(Qstr::MP_QSTR_device_name)?.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,
- firmware_version,
device_name,
+ about_items,
paired_devices,
auto_lock_delay,
)?;
@@ -1878,8 +1878,8 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// def show_device_menu(
/// *,
/// failed_backup: bool,
- /// firmware_version: str,
/// device_name: str | None,
+ /// about_items: list[PropertyType],
/// paired_devices: Iterable[str],
/// auto_lock_delay: str,
/// ) -> LayoutObj[UiResult | DeviceMenuResult | tuple[DeviceMenuResult, int]]:
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 6784be03..a311f92c 100644
--- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
@@ -934,8 +934,8 @@ impl FirmwareUI for UIBolt {
fn show_device_menu(
_failed_backup: bool,
- _firmware_version: TString<'static>,
_device_name: Option<TString<'static>>,
+ _about_items: Obj,
_paired_devices: heapless::Vec<TString<'static>, 1>,
_auto_lock_delay: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
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 2815e7b2..a6e887c3 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -1128,8 +1128,8 @@ impl FirmwareUI for UICaesar {
fn show_device_menu(
_failed_backup: bool,
- _firmware_version: TString<'static>,
_device_name: Option<TString<'static>>,
+ _about_items: Obj,
_paired_devices: Vec<TString<'static>, 1>,
_auto_lock_delay: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
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 26c218e6..e3ff65c9 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -1018,8 +1018,8 @@ impl FirmwareUI for UIDelizia {
fn show_device_menu(
_failed_backup: bool,
- _firmware_version: TString<'static>,
_device_name: Option<TString<'static>>,
+ _about_items: Obj,
_paired_devices: heapless::Vec<TString<'static>, 1>,
_auto_lock_delay: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
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 2b85d539..53671be6 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
@@ -145,7 +145,7 @@ impl ComponentMsgObj for SetBrightnessScreen {
}
}
-impl<'a> ComponentMsgObj for DeviceMenuScreen<'a> {
+impl ComponentMsgObj for DeviceMenuScreen {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
DeviceMenuMsg::BackupFailed => Ok(BACKUP_FAILED.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 93d5d4eb..fd11762d 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
@@ -2,19 +2,20 @@ use core::ops::{Deref, DerefMut};
use crate::{
error::Error,
- micropython::gc::GcBox,
+ micropython::{gc::GcBox, obj::Obj},
strutil::TString,
translations::TR,
trezorhal::storage::has_pin,
ui::{
component::{
text::{
- paragraphs::{Paragraph, Paragraphs},
+ paragraphs::{ParagraphSource, Paragraphs},
TextStyle,
},
Component, Event, EventCtx,
},
geometry::{LinearPlacement, Rect},
+ layout::util::PropsList,
shape::Renderer,
},
};
@@ -140,23 +141,23 @@ enum Subscreen {
// Used to preallocate memory for the largest enum variant
#[allow(clippy::large_enum_variant)]
-enum ActiveScreen<'a> {
+enum ActiveScreen {
Menu(VerticalMenuScreen<ShortMenuVec>),
- About(TextScreen<Paragraphs<[Paragraph<'a>; 2]>>),
+ About(TextScreen<Paragraphs<PropsList>>),
Regulatory(RegulatoryScreen),
// used only during `DeviceMenuScreen::new`
Empty,
}
-pub struct DeviceMenuScreen<'a> {
+pub struct DeviceMenuScreen {
bounds: Rect,
- firmware_version: TString<'static>,
+ about_items: Obj,
// These correspond to the currently active subscreen,
// which is one of the possible kinds of subscreens
// as defined by `enum Subscreen` (DeviceScreen is still a VerticalMenuScreen!)
// This way we only need to keep one screen at any time in memory.
- active_screen: GcBox<ActiveScreen<'a>>,
+ active_screen: GcBox<ActiveScreen>,
// Information needed to construct any subscreen on demand
submenus: GcBox<Vec<Submenu, MAX_SUBMENUS>>,
subscreens: Vec<Subscreen, MAX_SUBSCREENS>,
@@ -166,10 +167,10 @@ pub struct DeviceMenuScreen<'a> {
parent_subscreens: Vec<usize, MAX_DEPTH>,
}
-impl<'a> DeviceMenuScreen<'a> {
+impl DeviceMenuScreen {
pub fn new(
failed_backup: bool,
- firmware_version: TString<'static>,
+ about_items: Obj,
device_name: Option<TString<'static>>,
// NB: we currently only support one device at a time.
paired_devices: Vec<TString<'static>, 1>,
@@ -177,7 +178,7 @@ impl<'a> DeviceMenuScreen<'a> {
) -> Result<Self, Error> {
let mut screen = Self {
bounds: Rect::zero(),
- firmware_version,
+ about_items,
active_screen: GcBox::new(ActiveScreen::Empty)?,
active_subscreen: 0,
submenus: GcBox::new(Vec::new())?,
@@ -431,19 +432,21 @@ impl<'a> DeviceMenuScreen<'a> {
);
}
Subscreen::AboutScreen => {
- let about_content = Paragraphs::new([
- Paragraph::new(
- &theme::firmware::TEXT_SMALL_LIGHT,
- TR::homescreen__firmware_version,
- )
- .with_bottom_padding(theme::PROP_INNER_SPACING),
- Paragraph::new(&theme::firmware::TEXT_REGULAR, self.firmware_version),
- ])
- .with_placement(LinearPlacement::vertical());
-
*self.active_screen.deref_mut() = ActiveScreen::About(
- TextScreen::new(about_content)
- .with_header(Header::new(TR::words__about.into()).with_close_button()),
+ TextScreen::new(
+ PropsList::new_styled(
+ self.about_items,
+ &theme::TEXT_SMALL_LIGHT,
+ &theme::TEXT_MONO_MEDIUM_LIGHT,
+ &theme::TEXT_MONO_MEDIUM_LIGHT,
+ theme::PROP_INNER_SPACING,
+ theme::PROPS_SPACING,
+ )
+ .unwrap_or_else(|_| unwrap!(PropsList::empty()))
+ .into_paragraphs()
+ .with_placement(LinearPlacement::vertical()),
+ )
+ .with_header(Header::new(TR::words__about.into()).with_close_button()),
);
}
Subscreen::RegulatoryScreen => {
@@ -491,7 +494,7 @@ impl<'a> DeviceMenuScreen<'a> {
}
}
-impl<'a> Component for DeviceMenuScreen<'a> {
+impl Component for DeviceMenuScreen {
type Msg = DeviceMenuMsg;
fn place(&mut self, bounds: Rect) -> Rect {
@@ -568,7 +571,7 @@ impl<'a> Component for DeviceMenuScreen<'a> {
}
#[cfg(feature = "ui_debug")]
-impl<'a> crate::trace::Trace for DeviceMenuScreen<'a> {
+impl crate::trace::Trace for DeviceMenuScreen {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("DeviceMenuScreen");
}
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 c00fa95b..5ca150fb 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
@@ -1197,14 +1197,14 @@ impl FirmwareUI for UIEckhart {
fn show_device_menu(
failed_backup: bool,
- firmware_version: TString<'static>,
device_name: Option<TString<'static>>,
+ 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,
- firmware_version,
+ about_items,
device_name,
paired_devices,
auto_lock_delay,
diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs
index 0f488cc1..65b806b5 100644
--- a/core/embed/rust/src/ui/ui_firmware.rs
+++ b/core/embed/rust/src/ui/ui_firmware.rs
@@ -360,8 +360,8 @@ pub trait FirmwareUI {
fn show_device_menu(
failed_backup: bool,
- firmware_version: TString<'static>,
device_name: Option<TString<'static>>,
+ about_items: Obj,
paired_devices: Vec<TString<'static>, 1>,
auto_lock_delay: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error>;
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index 28ac8add..3afb88c7 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -623,8 +623,8 @@ def show_homescreen(
def show_device_menu(
*,
failed_backup: bool,
- firmware_version: str,
device_name: str | None,
+ about_items: list[PropertyType],
paired_devices: Iterable[str],
auto_lock_delay: str,
) -> LayoutObj[UiResult | DeviceMenuResult | tuple[DeviceMenuResult, int]]:
diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi
index 9d8aa122..b06962e9 100644
--- a/core/mocks/trezortranslate_keys.pyi
+++ b/core/mocks/trezortranslate_keys.pyi
@@ -81,6 +81,7 @@ class TR:
ble__unpair_all: str = "Unpair all bluetooth devices"
ble__unpair_current: str = "Unpair connected device"
ble__unpair_title: str = "Unpair"
+ ble__version: str = "Bluetooth version"
brightness__change_title: str = "Change display brightness"
brightness__changed_title: str = "Display brightness changed"
brightness__title: str = "Display brightness"
@@ -385,6 +386,7 @@ class TR:
haptic_feedback__title: str = "Haptic feedback"
homescreen__click_to_connect: str = "Click to Connect"
homescreen__click_to_unlock: str = "Click to Unlock"
+ homescreen__firmware_type: str = "Firmware type"
homescreen__firmware_version: str = "Firmware version"
homescreen__set_default: str = "Change wallpaper to default image?"
homescreen__settings_subtitle: str = "Settings"
diff --git a/core/src/apps/homescreen/device_menu.py b/core/src/apps/homescreen/device_menu.py
index d512b1f2..7d2c2b04 100644
--- a/core/src/apps/homescreen/device_menu.py
+++ b/core/src/apps/homescreen/device_menu.py
@@ -39,11 +39,13 @@ async def handle_device_menu() -> None:
paired_devices = ["Trezor Suite"] if ble.is_connected() else []
# ###
firmware_version = ".".join(map(str, utils.VERSION))
+ firmware_type = "Bitcoin-only" if utils.BITCOIN_ONLY else "Universal"
device_name = (
(storage_device.get_label() or "Trezor")
if storage_device.is_initialized()
else None
)
+ bluetooth_version = "2.3.1.1"
auto_lock_ms = storage_device.get_autolock_delay_ms()
auto_lock_delay = strings.format_autolock_duration(auto_lock_ms)
@@ -58,8 +60,12 @@ async def handle_device_menu() -> None:
trezorui_api.show_device_menu(
failed_backup=failed_backup,
paired_devices=paired_devices,
- firmware_version=firmware_version,
device_name=device_name,
+ 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",
diff --git a/core/translations/en.json b/core/translations/en.json
index a4fbf91c..96ad26e8 100644
--- a/core/translations/en.json
+++ b/core/translations/en.json
@@ -113,6 +113,7 @@
"ble__unpair_all": "Unpair all bluetooth devices",
"ble__unpair_current": "Unpair connected device",
"ble__unpair_title": "Unpair",
+ "ble__version": "Bluetooth version",
"brightness__change_title": "Change display brightness",
"brightness__changed_title": "Display brightness changed",
"brightness__title": "Display brightness",
@@ -447,6 +448,7 @@
"haptic_feedback__title": "Haptic feedback",
"homescreen__click_to_connect": "Click to Connect",
"homescreen__click_to_unlock": "Click to Unlock",
+ "homescreen__firmware_type": "Firmware type",
"homescreen__firmware_version": "Firmware version",
"homescreen__set_default": "Change wallpaper to default image?",
"homescreen__settings_subtitle": "Settings",
diff --git a/core/translations/order.json b/core/translations/order.json
index 9d9f120e..19887559 100644
--- a/core/translations/order.json
+++ b/core/translations/order.json
@@ -1091,5 +1091,7 @@
"1089": "words__led",
"1090": "words__manage",
"1091": "words__review",
- "1092": "words__security"
+ "1092": "words__security",
+ "1093": "ble__version",
+ "1094": "homescreen__firmware_type"
}
diff --git a/core/translations/signatures.json b/core/translations/signatures.json
index 748a0541..dd49e4fb 100644
--- a/core/translations/signatures.json
+++ b/core/translations/signatures.json
@@ -1,8 +1,8 @@
{
"current": {
- "merkle_root": "91e38f17eabf0592819660e07647cf906e858a4f00fee93ee796f48d9240d4dd",
- "datetime": "2025-08-24T17:34:38.888405+00:00",
- "commit": "d8cff7ac283765ed52b7b0ffcf1221a6a8bd1ea1"
+ "merkle_root": "45ed07c6ca2fccece4e51ea0581b2360e0d399d8f5fe67ea96694e74c3b69eee",
+ "datetime": "2025-08-24T17:39:52.334627+00:00",
+ "commit": "1500a2c16f1e2aacd64ed91d59c4165953c3e834"
},
"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.