1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-06-26 09:52:34 +00:00

feat(core): BLE pairing flow

[no changelog]
This commit is contained in:
Martin Milata 2025-04-08 17:05:30 +02:00
parent f077f2d1c1
commit c3afe4c67b
7 changed files with 80 additions and 20 deletions

View File

@ -1,4 +1,5 @@
use crate::{
strutil::TString,
translations::TR,
ui::{
component::{Component, Event, EventCtx, Timeout},
@ -128,6 +129,14 @@ impl ActionBar {
Self::new(Mode::PaginateOnly, None, None, None)
}
pub fn new_text_only(text: TString<'static>) -> Self {
Self::new_single(
Button::with_text(text)
.styled(theme::button_always_disabled())
.initially_enabled(false),
)
}
pub fn with_left_short(mut self, short: bool) -> Self {
if let Mode::Double { ref mut left_short } = self.mode {
*left_short = short;

View File

@ -541,6 +541,22 @@ pub const fn button_keyboard_next() -> ButtonStyleSheet {
}
}
// Things that look like button but don't do anything.
pub const fn button_always_disabled() -> ButtonStyleSheet {
let style = &ButtonStyle {
font: fonts::FONT_SATOSHI_MEDIUM_26,
text_color: GREY_LIGHT,
button_color: BG,
icon_color: GREY_LIGHT,
background_color: BG,
};
ButtonStyleSheet {
normal: style,
active: style,
disabled: style,
}
}
pub const fn input_mnemonic() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {

View File

@ -1048,9 +1048,7 @@ impl FirmwareUI for UIEckhart {
ops = ops.text(text, font);
let screen = TextScreen::new(FormattedText::new(ops))
.with_header(Header::new("Pair with new device".into()).with_close_button())
.with_action_bar(ActionBar::new_single(Button::with_text(
"Continue on host".into(),
)));
.with_action_bar(ActionBar::new_text_only("Continue on host".into()));
#[cfg(feature = "ble")]
let screen = crate::ui::component::BLEHandler::new(screen, true);
let layout = RootComponent::new(screen);

View File

@ -124,6 +124,7 @@ Q(apps.management.apply_flags)
Q(apps.management.apply_settings)
Q(apps.management.authenticate_device)
Q(apps.management.backup_device)
Q(apps.management.ble)
Q(apps.management.ble.pair_new_device)
Q(apps.management.ble.unpair)
Q(apps.management.change_language)
@ -168,6 +169,7 @@ Q(benchmark)
Q(benchmarks)
Q(bitcoin)
Q(bitcoinlike)
Q(ble)
Q(bolt)
Q(boot)
Q(cache)

View File

@ -1,6 +1,7 @@
import storage.device
import trezorble as ble
import trezorui_api
from trezor import TR, config, utils
from trezor import TR, config, log, utils
from trezor.ui.layouts import interact
from trezor.wire import ActionCancelled
from trezorui_api import DeviceMenuResult
@ -36,13 +37,19 @@ async def handle_device_menu() -> None:
)
# MOCK DATA
battery_percentage = 22
paired_devices = ["Trezor Suite"]
paired_devices = ["Trezor Suite"] if ble.is_connected() else []
# ###
firmware_version = ".".join(map(str, utils.VERSION))
device_name = storage.device.get_label() or "Trezor"
auto_lock_ms = storage.device.get_autolock_delay_ms()
auto_lock_delay = strings.format_autolock_duration(auto_lock_ms)
log.debug(
__name__,
f"device menu, BLE state: {ble.connection_flags()} (peers: {ble.peer_count()})",
)
menu_result = await interact(
trezorui_api.show_device_menu(
failed_backup=failed_backup,
@ -79,9 +86,11 @@ async def handle_device_menu() -> None:
# It's a tuple with (result_type, index)
result_type, index = menu_result
if result_type is DeviceMenuResult.DeviceDisconnect:
raise RuntimeError(
f"Device disconnect not implemented, device index: {index}"
)
from trezor.messages import BleUnpair
from apps.management.ble.unpair import unpair
await unpair(BleUnpair(all=False)) # FIXME we can only unpair current
else:
raise RuntimeError(f"Unknown menu {result_type}, {index}")
else:

View File

View File

@ -1,17 +1,43 @@
import trezorble as ble
import trezorui_api
from trezor.ui.layouts import interact
from storage import device as storage_device
from trezor import utils
from trezor.ui.layouts import CONFIRMED, raise_if_not_confirmed
from trezor.wire import ActionCancelled
def _end_pairing() -> None:
if ble.peer_count() > 0:
ble.start_advertising(True, storage_device.get_label())
else:
ble.stop_advertising()
async def pair_new_device() -> None:
label = "Trezor T3W1"
await interact(
trezorui_api.show_pairing_device_name(device_name=label),
label = storage_device.get_label() or utils.MODEL_FULL_NAME
ble.start_advertising(False, label)
try:
code = await raise_if_not_confirmed(
trezorui_api.show_pairing_device_name(
device_name=label,
),
None,
raise_on_cancel=None, # for UI testing
)
if not isinstance(code, int):
raise ActionCancelled
code = 12345
await interact(
trezorui_api.show_pairing_code(code=f"{code:0>6}"),
try:
result = await raise_if_not_confirmed(
trezorui_api.show_pairing_code(
code=f"{code:0>6}",
),
None,
)
except Exception:
ble.reject_pairing()
raise
else:
if result is CONFIRMED:
ble.allow_pairing(code)
finally:
_end_pairing()