feat(core/eckhart): add host info screen to device menu
What changed, and why it matters
This commit adds a new 'Host info' screen to the device menu on Trezor hardware wallets (Eckhart layout only). It lets users view the name and Bluetooth MAC address of each paired host device. There is no security vulnerability here—this is purely a user-interface feature.
No security action required. This is a benign UI feature addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change extends the device menu UI to display a HostInfoScreen for each paired Bluetooth host. It updates the paired_devices parameter from a list of strings to a list of (MAC, optional name) tuples, adds new translation strings (ble__host_info, ble__mac_address), and wires the new screen into the Eckhart layout’s navigation. Other layouts (Bolt, Caesar, Delizia) only update their type signatures because they do not yet implement this screen. The Python side now passes the full host info tuple instead of a derived hostname.
Changed components
core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rscore/embed/rust/src/ui/api/firmware_micropython.rscore/src/apps/homescreen/device_menu.pycore/translations/en.jsonInspect captured patch +165 / −47
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index 7731966d..1dc1da53 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -190,7 +190,9 @@ static void _librust_qstrs(void) {
MP_QSTR_ble__forget_this_description;
MP_QSTR_ble__forget_this_device;
MP_QSTR_ble__forget_this_success;
+ MP_QSTR_ble__host_info;
MP_QSTR_ble__limit_reached;
+ MP_QSTR_ble__mac_address;
MP_QSTR_ble__manage_paired;
MP_QSTR_ble__pair_new;
MP_QSTR_ble__pair_title;
diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs
index b8dc83ab..f0c0d928 100644
--- a/core/embed/rust/src/translations/generated/translated_string.rs
+++ b/core/embed/rust/src/translations/generated/translated_string.rs
@@ -1548,6 +1548,8 @@ pub enum TranslatedString {
wipe_code__pin_not_set_description = 1158, // "PIN must be set before enabling wipe code."
wipe_code__cancel_setup = 1159, // {"Bolt": "Cancel wipe code setup", "Caesar": "Cancel wipe code setup", "Delizia": "Cancel wipe code setup", "Eckhart": "Cancel wipe code setup?"}
homescreen__backup_needed_info = 1160, // "Open Trezor Suite and create a wallet backup. This is the only way to recover access to your assets."
+ ble__host_info = 1161, // "Host info"
+ ble__mac_address = 1162, // "MAC address"
}
impl TranslatedString {
@@ -3530,6 +3532,8 @@ impl TranslatedString {
#[cfg(feature = "layout_eckhart")]
(Self::wipe_code__cancel_setup, "Cancel wipe code setup?"),
(Self::homescreen__backup_needed_info, "Open Trezor Suite and create a wallet backup. This is the only way to recover access to your assets."),
+ (Self::ble__host_info, "Host info"),
+ (Self::ble__mac_address, "MAC address"),
];
#[cfg(feature = "micropython")]
@@ -3618,7 +3622,9 @@ impl TranslatedString {
(Qstr::MP_QSTR_ble__forget_this_description, Self::ble__forget_this_description),
(Qstr::MP_QSTR_ble__forget_this_device, Self::ble__forget_this_device),
(Qstr::MP_QSTR_ble__forget_this_success, Self::ble__forget_this_success),
+ (Qstr::MP_QSTR_ble__host_info, Self::ble__host_info),
(Qstr::MP_QSTR_ble__limit_reached, Self::ble__limit_reached),
+ (Qstr::MP_QSTR_ble__mac_address, Self::ble__mac_address),
(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),
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index 0e128c01..3c76e174 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -3,6 +3,7 @@ use crate::{
micropython::{
buffer::StrBuffer,
gc::Gc,
+ iter::IterBuf,
list::List,
macros::{obj_fn_0, obj_fn_1, obj_fn_kw, obj_module},
map::Map,
@@ -942,8 +943,17 @@ extern "C" fn new_show_device_menu(n_args: usize, args: *const Obj, kwargs: *mut
.try_into_option()?;
let backup_failed: bool = kwargs.get(Qstr::MP_QSTR_backup_failed)?.try_into()?;
let backup_needed: bool = kwargs.get(Qstr::MP_QSTR_backup_needed)?.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 paired_obj: Obj = kwargs.get(Qstr::MP_QSTR_paired_devices)?;
+ let mut paired_devices: heapless::Vec<
+ (TString<'static>, Option<TString<'static>>),
+ MAX_PAIRED_DEVICES,
+ > = heapless::Vec::new();
+ for device in IterBuf::new().try_iterate(paired_obj)? {
+ let [mac, name]: [Obj; 2] = util::iter_into_array(device)?;
+ let mac: TString<'static> = mac.try_into()?;
+ let name: Option<TString<'static>> = name.try_into_option()?;
+ unwrap!(paired_devices.push((mac, name)));
+ }
let connected_idx: Option<u8> =
kwargs.get(Qstr::MP_QSTR_connected_idx)?.try_into_option()?;
let pin_enabled: Option<bool> = kwargs.get(Qstr::MP_QSTR_pin_enabled)?.try_into_option()?;
@@ -1926,7 +1936,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// init_submenu_idx: int | None,
/// backup_failed: bool,
/// backup_needed: bool,
- /// paired_devices: Iterable[str],
+ /// paired_devices: Iterable[tuple[str, str | None]],
/// connected_idx: int | None,
/// pin_enabled: bool | None,
/// auto_lock: tuple[str, str] | None,
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 a815c1c3..c5285cba 100644
--- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
@@ -938,7 +938,10 @@ impl FirmwareUI for UIBolt {
_init_submenu_idx: Option<u8>,
_backup_failed: bool,
_backup_needed: bool,
- _paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ _paired_devices: heapless::Vec<
+ (TString<'static>, Option<TString<'static>>),
+ MAX_PAIRED_DEVICES,
+ >,
_connected_idx: Option<u8>,
_pin_enabled: Option<bool>,
_auto_lock: Option<[TString<'static>; 2]>,
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 0ee0caea..0c8329a1 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -1135,7 +1135,10 @@ impl FirmwareUI for UICaesar {
_init_submenu_idx: Option<u8>,
_backup_failed: bool,
_backup_needed: bool,
- _paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ _paired_devices: heapless::Vec<
+ (TString<'static>, Option<TString<'static>>),
+ MAX_PAIRED_DEVICES,
+ >,
_connected_idx: Option<u8>,
_pin_enabled: Option<bool>,
_auto_lock: Option<[TString<'static>; 2]>,
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 6babf624..0bb902da 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -1020,7 +1020,10 @@ impl FirmwareUI for UIDelizia {
_init_submenu_idx: Option<u8>,
_backup_failed: bool,
_backup_needed: bool,
- _paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ _paired_devices: heapless::Vec<
+ (TString<'static>, Option<TString<'static>>),
+ MAX_PAIRED_DEVICES,
+ >,
_connected_idx: Option<u8>,
_pin_enabled: Option<bool>,
_auto_lock: Option<[TString<'static>; 2]>,
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 6e7876dd..1bba1637 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
@@ -92,8 +92,6 @@ const MAX_SUBMENUS: usize = 9;
// submenus, device screens, regulatory and about screens
const MAX_SUBSCREENS: usize = MAX_SUBMENUS + MAX_PAIRED_DEVICES + 2;
-const DISCONNECT_DEVICE_MENU_INDEX: usize = 0;
-
#[derive(Clone)]
enum Action {
/// Go to a registered submenu by id (static)
@@ -262,6 +260,13 @@ enum Subscreen {
TString<'static>, /* device name */
bool, /* is the device connected? */
u8, /* index in the list of devices */
+ u8, /* host info screen index */
+ ),
+
+ HostInfoScreen(
+ TString<'static>, /* device name */
+ TString<'static>, /* MAC address */
+ u8, /* parent screen index */
),
// The about screen
@@ -277,6 +282,7 @@ enum Subscreen {
enum ActiveScreen {
Menu(VerticalMenuScreen<MediumMenuVec>, DeviceMenuId),
Device(VerticalMenuScreen<ShortMenuVec>),
+ HostInfo(TextScreen<Paragraphs<[Paragraph<'static>; 4]>>),
About(TextScreen<Paragraphs<PropsList>>),
BackupInfo(TextScreen<Paragraphs<Paragraph<'static>>>),
Regulatory(RegulatoryScreen),
@@ -310,7 +316,10 @@ impl DeviceMenuScreen {
init_submenu_idx: Option<u8>,
backup_failed: bool,
backup_needed: bool,
- paired_devices: Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ paired_devices: heapless::Vec<
+ (TString<'static>, Option<TString<'static>>),
+ MAX_PAIRED_DEVICES,
+ >,
connected_idx: Option<u8>,
pin_enabled: Option<bool>,
auto_lock: Option<[TString<'static>; 2]>,
@@ -353,10 +362,33 @@ impl DeviceMenuScreen {
is_connected.then_some(TR::words__connected.into());
let mut submenu_indices: Vec<u8, MAX_PAIRED_DEVICES> = Vec::new();
- for (i, device) in (0u8..).zip(paired_devices.iter()) {
+ for (i, (mac, name)) in (0u8..).zip(paired_devices.iter()) {
let connected = connected_idx == Some(i);
- unwrap!(submenu_indices
- .push(screen.add_subscreen(Subscreen::DeviceScreen(*device, connected, i))));
+ let device_name = if let Some(name) = name { *name } else { *mac };
+ // Add the host info subscreen first, so that we can reference it from the device subscreen
+ let host_info = screen.add_subscreen(Subscreen::HostInfoScreen(
+ device_name,
+ *mac,
+ 0, /* dummy value because the device subscreen doesn't exist yet */
+ ));
+ // Add the device subscreen with the reference to the host info subscreen
+ let device = screen.add_subscreen(Subscreen::DeviceScreen(
+ device_name,
+ connected,
+ i,
+ host_info,
+ ));
+
+ // Update the parent index in the host info screen to break the circular dependency
+ if let Subscreen::HostInfoScreen(_, _, parent_idx) =
+ &mut screen.subscreens[usize::from(host_info)]
+ {
+ *parent_idx = device;
+ } else {
+ unreachable!();
+ }
+
+ unwrap!(submenu_indices.push(device));
}
screen.register_pair_and_connect_menu(paired_devices, submenu_indices, connected_idx);
@@ -393,19 +425,18 @@ impl DeviceMenuScreen {
fn register_pair_and_connect_menu(
&mut self,
- paired_devices: Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ paired_devices: Vec<(TString<'static>, Option<TString<'static>>), MAX_PAIRED_DEVICES>,
submenu_indices: Vec<u8, MAX_PAIRED_DEVICES>,
connected_idx: Option<u8>,
) {
let mut items: Vec<MenuItem, MEDIUM_MENU_ITEMS> = Vec::new();
- for (device_idx, (device, submenu_idx)) in
- (0u8..).zip(paired_devices.iter().zip(submenu_indices))
- {
+ for (i, ((mac, name), device)) in (0u8..).zip(paired_devices.iter().zip(submenu_indices)) {
let connection_status = match connected_idx {
- Some(idx) if idx == device_idx => Some(true),
+ Some(idx) if idx == i => Some(true),
_ => Some(false),
};
- let item_device = MenuItem::go_to_subscreen(*device, submenu_idx)
+ let device_title = if let Some(name) = name { *name } else { *mac };
+ let item_device = MenuItem::go_to_subscreen(device_title, device)
.with_connection_status(connection_status);
items.add(item_device);
}
@@ -775,7 +806,7 @@ impl DeviceMenuScreen {
id,
);
}
- Subscreen::DeviceScreen(device, connected, _) => {
+ Subscreen::DeviceScreen(device, connected, ..) => {
let mut menu = VerticalMenu::empty();
if connected {
menu.item(Button::new_menu_item(
@@ -783,6 +814,10 @@ impl DeviceMenuScreen {
theme::menu_item_title(),
));
}
+ menu.item(Button::new_menu_item(
+ TR::ble__host_info.into(),
+ theme::menu_item_title(),
+ ));
menu.item(Button::new_menu_item(
TR::words__forget.into(),
theme::menu_item_title_orange(),
@@ -800,6 +835,28 @@ impl DeviceMenuScreen {
.with_subtitle(device),
);
}
+ Subscreen::HostInfoScreen(name, mac, ..) => {
+ let show_name = if name == mac {
+ TR::words__unknown.into()
+ } else {
+ name
+ };
+ *self.active_screen.deref_mut() = ActiveScreen::HostInfo(
+ TextScreen::new(
+ Paragraphs::new([
+ Paragraph::new(&theme::TEXT_MEDIUM_EXTRA_LIGHT, TR::words__name)
+ .with_bottom_padding(theme::PROP_INNER_SPACING),
+ Paragraph::new(&theme::TEXT_MONO_LIGHT, show_name)
+ .with_bottom_padding(theme::TEXT_VERTICAL_SPACING),
+ Paragraph::new(&theme::TEXT_MEDIUM_EXTRA_LIGHT, TR::ble__mac_address)
+ .with_bottom_padding(theme::PROP_INNER_SPACING),
+ Paragraph::new(&theme::TEXT_MONO_LIGHT, mac),
+ ])
+ .with_placement(LinearPlacement::vertical()),
+ )
+ .with_header(Header::new(TR::ble__host_info.into()).with_close_button()),
+ );
+ }
Subscreen::AboutScreen => {
*self.active_screen.deref_mut() = ActiveScreen::About(
TextScreen::new(
@@ -866,6 +923,13 @@ impl DeviceMenuScreen {
}
fn go_back(&mut self, ctx: &mut EventCtx) -> Option<DeviceMenuMsg> {
+ let active_subscreen = &self.subscreens[usize::from(self.active_subscreen)];
+ if let Subscreen::HostInfoScreen(_name, _mac, parent_idx) = active_subscreen {
+ // Handle going back from the HostInfoScreen
+ self.activate_subscreen(*parent_idx, ctx);
+ return None;
+ }
+
let parent = match self.subscreens[usize::from(self.active_subscreen)] {
Subscreen::Submenu(_, id) => match id {
DeviceMenuId::Root => return Some(DeviceMenuMsg::Close),
@@ -881,6 +945,7 @@ impl DeviceMenuScreen {
Subscreen::DeviceScreen(..) => DeviceMenuId::PairAndConnect,
Subscreen::AboutScreen | Subscreen::RegulatoryScreen => DeviceMenuId::Device,
Subscreen::BackupInfoScreen => DeviceMenuId::Root,
+ Subscreen::HostInfoScreen(..) => unreachable!(),
};
self.activate_subscreen(unwrap!(self.try_resolve_submenu(parent)), ctx);
@@ -905,6 +970,9 @@ impl Component for DeviceMenuScreen {
ActiveScreen::Device(device) => {
device.place(bounds);
}
+ ActiveScreen::HostInfo(host_info) => {
+ host_info.place(bounds);
+ }
ActiveScreen::About(about) => {
about.place(bounds);
}
@@ -931,6 +999,7 @@ impl Component for DeviceMenuScreen {
ActiveScreen::Device(_) => DeviceMenuId::PairAndConnect,
ActiveScreen::Regulatory(_) | ActiveScreen::About(_) => DeviceMenuId::Device,
ActiveScreen::Empty | ActiveScreen::BackupInfo(_) => DeviceMenuId::Root,
+ ActiveScreen::HostInfo(_) => DeviceMenuId::PairAndConnect,
};
return Some(DeviceMenuMsg::RefreshMenu(submenu_idx));
@@ -953,30 +1022,43 @@ impl Component for DeviceMenuScreen {
_ => {}
}
}
- (Subscreen::DeviceScreen(_, connected, device_idx), ActiveScreen::Device(menu)) => {
- match menu.event(ctx, event) {
- Some(VerticalMenuScreenMsg::Selected(button_idx)) => match button_idx {
- DISCONNECT_DEVICE_MENU_INDEX if *connected => {
+ (
+ Subscreen::DeviceScreen(_, connected, device_idx, host_info_idx),
+ ActiveScreen::Device(menu),
+ ) => match menu.event(ctx, event) {
+ Some(VerticalMenuScreenMsg::Selected(button_idx)) => {
+ match (button_idx, *connected) {
+ (0, true) => {
return Some(DeviceMenuMsg::DisconnectDevice);
}
- _ => {
+ (0, false) | (1, true) => {
+ self.activate_subscreen(*host_info_idx, ctx);
+ return None;
+ }
+ (1, false) | (2, true) => {
return Some(DeviceMenuMsg::UnpairDevice(*device_idx));
}
- },
- Some(VerticalMenuScreenMsg::Back) => {
- return self.go_back(ctx);
+ _ => {}
}
- Some(VerticalMenuScreenMsg::Close) => {
- return Some(DeviceMenuMsg::Close);
- }
- _ => {}
}
- }
+ Some(VerticalMenuScreenMsg::Back) => {
+ return self.go_back(ctx);
+ }
+ Some(VerticalMenuScreenMsg::Close) => {
+ return Some(DeviceMenuMsg::Close);
+ }
+ _ => {}
+ },
(Subscreen::AboutScreen, ActiveScreen::About(about)) => {
if let Some(TextScreenMsg::Cancelled) = about.event(ctx, event) {
return self.go_back(ctx);
}
}
+ (Subscreen::HostInfoScreen(..), ActiveScreen::HostInfo(host_info)) => {
+ if let Some(TextScreenMsg::Cancelled) = host_info.event(ctx, event) {
+ return self.go_back(ctx);
+ }
+ }
(Subscreen::RegulatoryScreen, ActiveScreen::Regulatory(regulatory)) => {
if let Some(RegulatoryMsg::Cancelled) = regulatory.event(ctx, event) {
return self.go_back(ctx);
@@ -997,6 +1079,7 @@ impl Component for DeviceMenuScreen {
match self.active_screen.deref() {
ActiveScreen::Menu(menu, ..) => menu.render(target),
ActiveScreen::Device(device) => device.render(target),
+ ActiveScreen::HostInfo(host_info) => host_info.render(target),
ActiveScreen::About(about) => about.render(target),
ActiveScreen::Regulatory(regulatory) => regulatory.render(target),
ActiveScreen::BackupInfo(backup_info) => backup_info.render(target),
@@ -1017,6 +1100,9 @@ impl crate::trace::Trace for DeviceMenuScreen {
ActiveScreen::Device(ref screen) => {
t.child("Device", screen);
}
+ ActiveScreen::HostInfo(ref screen) => {
+ t.child("HostInfo", screen);
+ }
ActiveScreen::About(ref screen) => {
t.child("About", screen);
}
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 ea61a037..ff0539f3 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
@@ -1204,7 +1204,10 @@ impl FirmwareUI for UIEckhart {
init_submenu_idx: Option<u8>,
backup_failed: bool,
backup_needed: bool,
- paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ paired_devices: heapless::Vec<
+ (TString<'static>, Option<TString<'static>>),
+ MAX_PAIRED_DEVICES,
+ >,
connected_idx: Option<u8>,
pin_enabled: Option<bool>,
auto_lock: Option<[TString<'static>; 2]>,
diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs
index 261a9e20..1ba7fd2c 100644
--- a/core/embed/rust/src/ui/ui_firmware.rs
+++ b/core/embed/rust/src/ui/ui_firmware.rs
@@ -365,7 +365,10 @@ pub trait FirmwareUI {
init_submenu_idx: Option<u8>,
backup_failed: bool,
backup_needed: bool,
- paired_devices: heapless::Vec<TString<'static>, MAX_PAIRED_DEVICES>,
+ paired_devices: heapless::Vec<
+ (TString<'static>, Option<TString<'static>>),
+ MAX_PAIRED_DEVICES,
+ >,
connected_idx: Option<u8>,
pin_enabled: Option<bool>,
auto_lock: Option<[TString<'static>; 2]>,
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index 0b01b17b..e31b3b7b 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -629,7 +629,7 @@ def show_device_menu(
init_submenu_idx: int | None,
backup_failed: bool,
backup_needed: bool,
- paired_devices: Iterable[str],
+ paired_devices: Iterable[tuple[str, str | None]],
connected_idx: int | None,
pin_enabled: bool | None,
auto_lock: tuple[str, str] | None,
diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi
index 5b6bbc38..25716489 100644
--- a/core/mocks/trezortranslate_keys.pyi
+++ b/core/mocks/trezortranslate_keys.pyi
@@ -86,7 +86,9 @@ class TR:
ble__forget_this_description: str = "It will be removed, and you'll need to pair it again before use."
ble__forget_this_device: str = "Forget this device?"
ble__forget_this_success: str = "Host removed."
+ ble__host_info: str = "Host info"
ble__limit_reached: str = "Limit of paired devices reached"
+ ble__mac_address: str = "MAC address"
ble__manage_paired: str = "Manage paired devices"
ble__pair_new: str = "Pair new device"
ble__pair_title: str = "Pair & connect"
diff --git a/core/src/apps/homescreen/device_menu.py b/core/src/apps/homescreen/device_menu.py
index 9fffaecb..0a6d977d 100644
--- a/core/src/apps/homescreen/device_menu.py
+++ b/core/src/apps/homescreen/device_menu.py
@@ -40,13 +40,6 @@ def _get_hostinfo(
return (mac, None)
-def _get_hostname(
- ble_addr: AnyBytes, hostname_map: dict[AnyBytes, ThpPairedCacheEntry]
-) -> str:
- mac, hostinfo = _get_hostinfo(ble_addr, hostname_map)
- return mac if hostinfo is None else hostinfo[0]
-
-
def _find_device(connected_addr: bytes | None, bonds: list[bytes]) -> int | None:
if connected_addr is None:
return None
@@ -97,7 +90,7 @@ async def handle_device_menu() -> None:
hostname_map = {e.mac_addr: e for e in paired_cache.load()}
if __debug__:
log.debug(__name__, "hostname_map: %s", hostname_map)
- paired_devices = [_get_hostname(bond, hostname_map) for bond in bonds]
+ paired_devices = [_get_hostinfo(bond, hostname_map) for bond in bonds]
if utils.USE_NRF:
nrf_version = utils.nrf_get_version()
diff --git a/core/translations/en.json b/core/translations/en.json
index 0212b168..56b7a34e 100644
--- a/core/translations/en.json
+++ b/core/translations/en.json
@@ -118,7 +118,9 @@
"ble__forget_this_description": "It will be removed, and you'll need to pair it again before use.",
"ble__forget_this_device": "Forget this device?",
"ble__forget_this_success": "Host removed.",
+ "ble__host_info": "Host info",
"ble__limit_reached": "Limit of paired devices reached",
+ "ble__mac_address": "MAC address",
"ble__manage_paired": "Manage paired devices",
"ble__pair_new": "Pair new device",
"ble__pair_title": "Pair & connect",
diff --git a/core/translations/order.json b/core/translations/order.json
index d7730659..237b4a5a 100644
--- a/core/translations/order.json
+++ b/core/translations/order.json
@@ -1159,5 +1159,7 @@
"1157": "pin__wipe_code_exists_title",
"1158": "wipe_code__pin_not_set_description",
"1159": "wipe_code__cancel_setup",
- "1160": "homescreen__backup_needed_info"
+ "1160": "homescreen__backup_needed_info",
+ "1161": "ble__host_info",
+ "1162": "ble__mac_address"
}
diff --git a/core/translations/signatures.json b/core/translations/signatures.json
index 95df6afc..2c5e836b 100644
--- a/core/translations/signatures.json
+++ b/core/translations/signatures.json
@@ -1,8 +1,8 @@
{
"current": {
- "merkle_root": "78559795eb01cf0830f6adc2fcb119810557d2477ae4e54c6624a93ddea8e4a3",
- "datetime": "2025-09-22T21:35:46.898951+00:00",
- "commit": "e1c57e0c8712c1519400d65818f93a4073612922"
+ "merkle_root": "54b1039c5d64d427e960ee926487ebbef9945fa9d7a5369273aa7d041776a69d",
+ "datetime": "2025-09-23T09:41:07.139457+00:00",
+ "commit": "1b8a80023d7587f3f734c7364d476529dd1e61d2"
},
"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.