From 05a1d6f7711fcdd72b3dcd70454f3c5449f0455b Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 5 Feb 2020 15:43:19 +0100 Subject: [PATCH] core/debug: add "show arbitrary screen" capability, for easier prototyping --- common/protob/messages-debug.proto | 27 ++++ common/protob/messages.proto | 1 + core/src/apps/debug/__init__.py | 1 + core/src/apps/debug/show_text.py | 50 +++++++ core/src/trezor/messages/DebugLinkShowText.py | 37 +++++ .../trezor/messages/DebugLinkShowTextItem.py | 29 ++++ .../trezor/messages/DebugLinkShowTextStyle.py | 12 ++ core/src/trezor/messages/MessageType.py | 1 + docs/python/show-text-01.png | Bin 0 -> 7362 bytes docs/python/show-text-02.png | Bin 0 -> 5910 bytes docs/python/show-text-03.png | Bin 0 -> 7465 bytes docs/python/show-text-04.png | Bin 0 -> 8218 bytes docs/python/show-text-05.png | Bin 0 -> 5971 bytes docs/python/show-text-06.png | Bin 0 -> 7426 bytes docs/python/show-text-07.png | Bin 0 -> 7423 bytes docs/python/show-text.md | 138 ++++++++++++++++++ legacy/firmware/protob/Makefile | 3 +- legacy/firmware/protob/messages-debug.options | 1 - python/src/trezorlib/cli/debug.py | 73 +++++++++ python/src/trezorlib/cli/trezorctl.py | 4 +- python/src/trezorlib/debuglink.py | 15 ++ .../trezorlib/messages/DebugLinkShowText.py | 37 +++++ .../messages/DebugLinkShowTextItem.py | 29 ++++ .../messages/DebugLinkShowTextStyle.py | 12 ++ python/src/trezorlib/messages/MessageType.py | 1 + python/src/trezorlib/messages/__init__.py | 3 + 26 files changed, 471 insertions(+), 3 deletions(-) create mode 100644 core/src/apps/debug/show_text.py create mode 100644 core/src/trezor/messages/DebugLinkShowText.py create mode 100644 core/src/trezor/messages/DebugLinkShowTextItem.py create mode 100644 core/src/trezor/messages/DebugLinkShowTextStyle.py create mode 100644 docs/python/show-text-01.png create mode 100644 docs/python/show-text-02.png create mode 100644 docs/python/show-text-03.png create mode 100644 docs/python/show-text-04.png create mode 100644 docs/python/show-text-05.png create mode 100644 docs/python/show-text-06.png create mode 100644 docs/python/show-text-07.png create mode 100644 docs/python/show-text.md create mode 100644 python/src/trezorlib/cli/debug.py create mode 100644 python/src/trezorlib/messages/DebugLinkShowText.py create mode 100644 python/src/trezorlib/messages/DebugLinkShowTextItem.py create mode 100644 python/src/trezorlib/messages/DebugLinkShowTextStyle.py diff --git a/common/protob/messages-debug.proto b/common/protob/messages-debug.proto index db9d9dd55..6a1876c18 100644 --- a/common/protob/messages-debug.proto +++ b/common/protob/messages-debug.proto @@ -57,6 +57,33 @@ message DebugLinkRecordScreen { optional string target_directory = 1; // empty or missing to stop recording } +/** + * Request: Show text on the screen + * @start + * @next Success + */ + message DebugLinkShowText { + optional string header_text = 1; // screen header text + repeated DebugLinkShowTextItem body_text = 2; // body text segments + optional string header_icon = 3; // icon name in ui.style + optional string icon_color = 4; // color name in ui.style + + message DebugLinkShowTextItem { + optional DebugLinkShowTextStyle style = 1; + optional string content = 2; + } + + enum DebugLinkShowTextStyle { + NORMAL = 0; + BOLD = 1; + MONO = 2; + MONO_BOLD = 3; + BR = 4; + BR_HALF = 5; + SET_COLOR = 6; + } +} + /** * Request: Computer asks for device state * @start diff --git a/common/protob/messages.proto b/common/protob/messages.proto index a9c74fd63..38a39ed71 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -109,6 +109,7 @@ enum MessageType { MessageType_DebugLinkLayout = 9001 [(wire_debug_out) = true]; MessageType_DebugLinkReseedRandom = 9002 [(wire_debug_in) = true]; MessageType_DebugLinkRecordScreen = 9003 [(wire_debug_in) = true]; + MessageType_DebugLinkShowText = 9004 [(wire_debug_in) = true]; // Ethereum MessageType_EthereumGetPublicKey = 450 [(wire_in) = true]; diff --git a/core/src/apps/debug/__init__.py b/core/src/apps/debug/__init__.py index 4f1004f98..93bdced3a 100644 --- a/core/src/apps/debug/__init__.py +++ b/core/src/apps/debug/__init__.py @@ -145,6 +145,7 @@ if __debug__: config.wipe() wire.add(MessageType.LoadDevice, __name__, "load_device") + wire.add(MessageType.DebugLinkShowText, __name__, "show_text") wire.register(MessageType.DebugLinkDecision, dispatch_DebugLinkDecision) wire.register(MessageType.DebugLinkGetState, dispatch_DebugLinkGetState) wire.register(MessageType.DebugLinkReseedRandom, dispatch_DebugLinkReseedRandom) diff --git a/core/src/apps/debug/show_text.py b/core/src/apps/debug/show_text.py new file mode 100644 index 000000000..7e8df5d27 --- /dev/null +++ b/core/src/apps/debug/show_text.py @@ -0,0 +1,50 @@ +import trezor.messages.DebugLinkShowTextStyle as S +from trezor import ui, wire +from trezor.messages.DebugLinkShowText import DebugLinkShowText +from trezor.messages.Success import Success +from trezor.ui import style, text +from trezor.ui.text import Text + +from apps.common.confirm import confirm + +STYLES = { + S.NORMAL: ui.NORMAL, + S.BOLD: ui.BOLD, + S.MONO: ui.MONO, + S.MONO_BOLD: ui.MONO_BOLD, + S.BR: text.BR, + S.BR_HALF: text.BR_HALF, +} + + +async def show_text(ctx: wire.Context, msg: DebugLinkShowText) -> Success: + if msg.header_icon is not None: + icon_name = "ICON_" + msg.header_icon + icon = getattr(style, icon_name) + if not isinstance(icon, str): + raise wire.DataError("Invalid icon name: {}".format(msg.header_icon)) + else: + icon = style.ICON_DEFAULT + + if msg.icon_color is not None: + color = getattr(style, msg.icon_color) + if not isinstance(color, int): + raise wire.DataError("Invalid color name: {}".format(msg.icon_color)) + else: + color = style.ORANGE_ICON + + dlg = Text(msg.header_text, icon, color, new_lines=False) + for item in msg.body_text: + if item.style in STYLES: + dlg.content.append(STYLES[item.style]) + elif item.style == S.SET_COLOR: + color = getattr(style, item.content) + if not isinstance(color, int): + raise wire.DataError("Invalid color name: {}".format(item.content)) + dlg.content.append(color) + + elif item.content is not None: + dlg.content.append(item.content) + + await confirm(ctx, dlg) + return Success("text shown") diff --git a/core/src/trezor/messages/DebugLinkShowText.py b/core/src/trezor/messages/DebugLinkShowText.py new file mode 100644 index 000000000..00a3a3f2f --- /dev/null +++ b/core/src/trezor/messages/DebugLinkShowText.py @@ -0,0 +1,37 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +from .DebugLinkShowTextItem import DebugLinkShowTextItem + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class DebugLinkShowText(p.MessageType): + MESSAGE_WIRE_TYPE = 9004 + + def __init__( + self, + header_text: str = None, + body_text: List[DebugLinkShowTextItem] = None, + header_icon: str = None, + icon_color: str = None, + ) -> None: + self.header_text = header_text + self.body_text = body_text if body_text is not None else [] + self.header_icon = header_icon + self.icon_color = icon_color + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('header_text', p.UnicodeType, 0), + 2: ('body_text', DebugLinkShowTextItem, p.FLAG_REPEATED), + 3: ('header_icon', p.UnicodeType, 0), + 4: ('icon_color', p.UnicodeType, 0), + } diff --git a/core/src/trezor/messages/DebugLinkShowTextItem.py b/core/src/trezor/messages/DebugLinkShowTextItem.py new file mode 100644 index 000000000..cf4e34881 --- /dev/null +++ b/core/src/trezor/messages/DebugLinkShowTextItem.py @@ -0,0 +1,29 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + EnumTypeDebugLinkShowTextStyle = Literal[0, 1, 2, 3, 4, 5, 6] + except ImportError: + pass + + +class DebugLinkShowTextItem(p.MessageType): + + def __init__( + self, + style: EnumTypeDebugLinkShowTextStyle = None, + content: str = None, + ) -> None: + self.style = style + self.content = content + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('style', p.EnumType("DebugLinkShowTextStyle", (0, 1, 2, 3, 4, 5, 6)), 0), + 2: ('content', p.UnicodeType, 0), + } diff --git a/core/src/trezor/messages/DebugLinkShowTextStyle.py b/core/src/trezor/messages/DebugLinkShowTextStyle.py new file mode 100644 index 000000000..6ddc6028f --- /dev/null +++ b/core/src/trezor/messages/DebugLinkShowTextStyle.py @@ -0,0 +1,12 @@ +# Automatically generated by pb2py +# fmt: off +if False: + from typing_extensions import Literal + +NORMAL = 0 # type: Literal[0] +BOLD = 1 # type: Literal[1] +MONO = 2 # type: Literal[2] +MONO_BOLD = 3 # type: Literal[3] +BR = 4 # type: Literal[4] +BR_HALF = 5 # type: Literal[5] +SET_COLOR = 6 # type: Literal[6] diff --git a/core/src/trezor/messages/MessageType.py b/core/src/trezor/messages/MessageType.py index 87fdc57a3..d2a40123a 100644 --- a/core/src/trezor/messages/MessageType.py +++ b/core/src/trezor/messages/MessageType.py @@ -76,6 +76,7 @@ DebugLinkFlashErase = 113 # type: Literal[113] DebugLinkLayout = 9001 # type: Literal[9001] DebugLinkReseedRandom = 9002 # type: Literal[9002] DebugLinkRecordScreen = 9003 # type: Literal[9003] +DebugLinkShowText = 9004 # type: Literal[9004] if not utils.BITCOIN_ONLY: EthereumGetPublicKey = 450 # type: Literal[450] EthereumPublicKey = 451 # type: Literal[451] diff --git a/docs/python/show-text-01.png b/docs/python/show-text-01.png new file mode 100644 index 0000000000000000000000000000000000000000..cc7b7c10e88ee5ecd5a95aed6a6d9837e704f829 GIT binary patch literal 7362 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 literal 0 HcmV?d00001 diff --git a/docs/python/show-text-02.png b/docs/python/show-text-02.png new file mode 100644 index 0000000000000000000000000000000000000000..cce2e128b9572b65325eede9dfcac39ab98bf0bc GIT binary patch literal 5910 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 literal 0 HcmV?d00001 diff --git a/docs/python/show-text-06.png b/docs/python/show-text-06.png new file mode 100644 index 0000000000000000000000000000000000000000..ba4d981135be5e11d365f9588113e6f5fb2de3f6 GIT binary patch literal 7426 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%0DIHY%sc33g(M~wRk=ni>v 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 This word is **bold** and this is `monospaced`. + +This will not work: + +> Em**bold**ened middle, mono`space`d middle. + +A line that is too long to fit on screen will automatically break, using a hyphen (`-`) +character. This is usually undesirable. Instead, a manual line-break should be inserted +in the appropriate place. + +## Command line syntax + +The most basic way to put words on screen is this: + +```sh +trezorctl debug show-text "My hovercraft is full of eels, call the porter, there is a frog in my bidet." +``` + +The above command will show: + +![Screenshot01](show-text-01.png) + +Notice the "quotes" around the text. The whole body text must be enquoted. + +To use quotes inside the text, prefix them with backslashes `\`: + +```sh +trezorctl debug show-text "My \"hovercraft\" is full of eels." +``` + +![Screenshot02](show-text-02.png) + +### Line breaks + +Let's insert some line breaks. Do that by placing `@@BR` in the appropriate place +in the text: + +```sh +trezorctl debug show-text "My hovercraft is full of @@BR eels, call the porter, @@BR there is a frog in my @@BR bidet." +``` + +![Screenshot03](show-text-03.png) + +Better! + +### Text styles + +Now let's add some style. Use `@@BOLD` to start printing in bold. Use `@@NORMAL` +to go back to normal text. `@@MONO` and `@@MONO_BOLD` works similarly. + +```sh +trezorctl debug show-text "My hovercraft is @@BOLD full of @@BR eels. @@NORMAL Call the porter, @@BR there is a @@MONO frog @@NORMAL in my @@BR bidet." +``` + +![Screenshot04](show-text-04.png) + +### Line spacing + +Adding another `@@BR` after a `@@BR` will leave one line empty -- just like pressing +\ twice. + +If you don't want a full empty line, you can make a half-break with `@@BR_HALF`. + +```sh +trezorctl debug show-text "Line one. @@BR @@BR Line two. @@BR @@BR_HALF Line three." +``` + +![Screenshot05](show-text-05.png) + + +### Text colors + +To switch to one of the [available colors](../../core/src/trezor/ui/style.py#L14-L47), +use the color name prefixed with `%%`: e.g., `%%RED`, `%%LIGHT_BLUE`... + +To switch back to the default color, you can use `%%FG`: + +```sh +trezorctl debug show-text "My %%RED hovercraft is @@BOLD full %%GREEN of @@BR eels. @@NORMAL Call %%ORANGE the %%FG porter." +``` + +![Screenshot06](show-text-06.png) + +### Headers + +The default header says "Showing text" with an orange gear icon. It is possible to +change all of that. + +To change the text, use `-h` option: + +```sh +trezorctl debug show-text -h "Hello world" "My hovercraft is full." +``` + +To change the icon, you can pick [an icon name from here](../../core/src/trezor/ui/style.py#L50-L70) and specify it with the `-i` option: + +```sh +trezorctl debug show-text -i RECEIVE "My hovercraft is full." +``` + +The icons are defined as shapes, and you can specify a custom color [from the list](../../core/src/trezor/ui/style.py#L14-L47) with the `-c` option: + +```sh +trezorctl debug show-text -c RED "My hovercraft is full." +``` + +### Putting it all together + +Here is how to reproduce the confirmation screen after the wallet is created: + +```sh +trezorctl debug show-text -h "Success" -i CONFIRM -c GREEN "@@BOLD New wallet created @@BR successfully! @@BR @@BR_HALF @@NORMAL You should back up your @@BR new wallet right now." +``` + +![Screenshot07](show-text-07.png) diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile index 72ce15436..3ebe6dd30 100644 --- a/legacy/firmware/protob/Makefile +++ b/legacy/firmware/protob/Makefile @@ -2,7 +2,8 @@ ifneq ($(V),1) Q := @ endif -SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdProtect Tezos WebAuthn DebugLinkRecordScreen DebugLinkReseedRandom +SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdProtect Tezos WebAuthn \ + DebugLinkRecordScreen DebugLinkReseedRandom DebugLinkShowText ifeq ($(BITCOIN_ONLY), 1) SKIPPED_MESSAGES += Ethereum Lisk NEM Stellar diff --git a/legacy/firmware/protob/messages-debug.options b/legacy/firmware/protob/messages-debug.options index a5b7a122c..496ed2278 100644 --- a/legacy/firmware/protob/messages-debug.options +++ b/legacy/firmware/protob/messages-debug.options @@ -17,4 +17,3 @@ DebugLinkMemoryWrite.memory max_size:1024 # unused fields DebugLinkState.layout_lines max_count:10 max_size:30 DebugLinkLayout.lines max_count:10 max_size:30 -DebugLinkRecordScreen.target_directory max_size:16 diff --git a/python/src/trezorlib/cli/debug.py b/python/src/trezorlib/cli/debug.py new file mode 100644 index 000000000..379cd7243 --- /dev/null +++ b/python/src/trezorlib/cli/debug.py @@ -0,0 +1,73 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2019 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import click + +from .. import debuglink +from ..messages import DebugLinkShowTextStyle as S + + +@click.group(name="debug") +def cli(): + """Miscellaneous debug features.""" + + +STYLES = { + "@@NORMAL": S.NORMAL, + "@@BOLD": S.BOLD, + "@@MONO": S.MONO, + "@@MONO_BOLD": S.MONO_BOLD, + "@@BR": S.BR, + "@@BR_HALF": S.BR_HALF, +} + + +@cli.command() +@click.option("-i", "--icon", help="Header icon name") +@click.option("-c", "--color", help="Header icon color") +@click.option("-h", "--header", help="Header text", default="Showing text") +@click.argument("body") +@click.pass_obj +def show_text(connect, icon, color, header, body): + """Show text on Trezor display. + + For usage instructions, see: + https://github.com/trezor/trezor-firmware/blob/master/docs/python/show-text.md + """ + body = body.split() + body_text = [] + words = [] + + def _flush(): + if words: + body_text.append((None, " ".join(words))) + words.clear() + + for word in body: + if word in STYLES: + _flush() + body_text.append((STYLES[word], None)) + elif word.startswith("%%"): + _flush() + body_text.append((S.SET_COLOR, word[2:])) + else: + words.append(word) + + _flush() + + return debuglink.show_text( + connect(), header, body_text, icon=icon, icon_color=color + ) diff --git a/python/src/trezorlib/cli/trezorctl.py b/python/src/trezorlib/cli/trezorctl.py index 95878fd9d..baf9a28a0 100755 --- a/python/src/trezorlib/cli/trezorctl.py +++ b/python/src/trezorlib/cli/trezorctl.py @@ -33,6 +33,7 @@ from . import ( cardano, cosi, crypto, + debug, device, eos, ethereum, @@ -59,6 +60,7 @@ COMMAND_ALIASES = { "sd-protect": device.sd_protect, "load-device": device.load, "self-test": device.self_test, + "show-text": debug.show_text, "get-entropy": crypto.get_entropy, "encrypt-keyvalue": crypto.encrypt_keyvalue, "decrypt-keyvalue": crypto.decrypt_keyvalue, @@ -296,7 +298,7 @@ cli.add_command(stellar.cli) cli.add_command(tezos.cli) cli.add_command(firmware.firmware_update) - +cli.add_command(debug.cli) # # Main diff --git a/python/src/trezorlib/debuglink.py b/python/src/trezorlib/debuglink.py index 4e7d4e539..d88043d53 100644 --- a/python/src/trezorlib/debuglink.py +++ b/python/src/trezorlib/debuglink.py @@ -510,3 +510,18 @@ def self_test(client): payload=b"\x00\xFF\x55\xAA\x66\x99\x33\xCCABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\x00\xFF\x55\xAA\x66\x99\x33\xCC" ) ) + + +@expect(proto.Success, field="message") +def show_text(client, header_text, body_text, icon=None, icon_color=None): + body_text = [ + proto.DebugLinkShowTextItem(style=style, content=content) + for style, content in body_text + ] + msg = proto.DebugLinkShowText( + header_text=header_text, + body_text=body_text, + header_icon=icon, + icon_color=icon_color, + ) + return client.call(msg) diff --git a/python/src/trezorlib/messages/DebugLinkShowText.py b/python/src/trezorlib/messages/DebugLinkShowText.py new file mode 100644 index 000000000..aaa0f6581 --- /dev/null +++ b/python/src/trezorlib/messages/DebugLinkShowText.py @@ -0,0 +1,37 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +from .DebugLinkShowTextItem import DebugLinkShowTextItem + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class DebugLinkShowText(p.MessageType): + MESSAGE_WIRE_TYPE = 9004 + + def __init__( + self, + header_text: str = None, + body_text: List[DebugLinkShowTextItem] = None, + header_icon: str = None, + icon_color: str = None, + ) -> None: + self.header_text = header_text + self.body_text = body_text if body_text is not None else [] + self.header_icon = header_icon + self.icon_color = icon_color + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('header_text', p.UnicodeType, 0), + 2: ('body_text', DebugLinkShowTextItem, p.FLAG_REPEATED), + 3: ('header_icon', p.UnicodeType, 0), + 4: ('icon_color', p.UnicodeType, 0), + } diff --git a/python/src/trezorlib/messages/DebugLinkShowTextItem.py b/python/src/trezorlib/messages/DebugLinkShowTextItem.py new file mode 100644 index 000000000..55cb1bd9b --- /dev/null +++ b/python/src/trezorlib/messages/DebugLinkShowTextItem.py @@ -0,0 +1,29 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + EnumTypeDebugLinkShowTextStyle = Literal[0, 1, 2, 3, 4, 5, 6] + except ImportError: + pass + + +class DebugLinkShowTextItem(p.MessageType): + + def __init__( + self, + style: EnumTypeDebugLinkShowTextStyle = None, + content: str = None, + ) -> None: + self.style = style + self.content = content + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('style', p.EnumType("DebugLinkShowTextStyle", (0, 1, 2, 3, 4, 5, 6)), 0), + 2: ('content', p.UnicodeType, 0), + } diff --git a/python/src/trezorlib/messages/DebugLinkShowTextStyle.py b/python/src/trezorlib/messages/DebugLinkShowTextStyle.py new file mode 100644 index 000000000..6ddc6028f --- /dev/null +++ b/python/src/trezorlib/messages/DebugLinkShowTextStyle.py @@ -0,0 +1,12 @@ +# Automatically generated by pb2py +# fmt: off +if False: + from typing_extensions import Literal + +NORMAL = 0 # type: Literal[0] +BOLD = 1 # type: Literal[1] +MONO = 2 # type: Literal[2] +MONO_BOLD = 3 # type: Literal[3] +BR = 4 # type: Literal[4] +BR_HALF = 5 # type: Literal[5] +SET_COLOR = 6 # type: Literal[6] diff --git a/python/src/trezorlib/messages/MessageType.py b/python/src/trezorlib/messages/MessageType.py index f66649b06..bc8df2258 100644 --- a/python/src/trezorlib/messages/MessageType.py +++ b/python/src/trezorlib/messages/MessageType.py @@ -74,6 +74,7 @@ DebugLinkFlashErase = 113 # type: Literal[113] DebugLinkLayout = 9001 # type: Literal[9001] DebugLinkReseedRandom = 9002 # type: Literal[9002] DebugLinkRecordScreen = 9003 # type: Literal[9003] +DebugLinkShowText = 9004 # type: Literal[9004] EthereumGetPublicKey = 450 # type: Literal[450] EthereumPublicKey = 451 # type: Literal[451] EthereumGetAddress = 56 # type: Literal[56] diff --git a/python/src/trezorlib/messages/__init__.py b/python/src/trezorlib/messages/__init__.py index bd1bfa8e3..8fdb40269 100644 --- a/python/src/trezorlib/messages/__init__.py +++ b/python/src/trezorlib/messages/__init__.py @@ -49,6 +49,8 @@ from .DebugLinkMemoryRead import DebugLinkMemoryRead from .DebugLinkMemoryWrite import DebugLinkMemoryWrite from .DebugLinkRecordScreen import DebugLinkRecordScreen from .DebugLinkReseedRandom import DebugLinkReseedRandom +from .DebugLinkShowText import DebugLinkShowText +from .DebugLinkShowTextItem import DebugLinkShowTextItem from .DebugLinkState import DebugLinkState from .DebugLinkStop import DebugLinkStop from .DebugMoneroDiagAck import DebugMoneroDiagAck @@ -270,6 +272,7 @@ from . import BinanceOrderType from . import BinanceTimeInForce from . import ButtonRequestType from . import Capability +from . import DebugLinkShowTextStyle from . import DebugSwipeDirection from . import FailureType from . import InputScriptType