|
|
|
@ -314,6 +314,9 @@ class HardwarePluginToScan(NamedTuple): |
|
|
|
exception: Optional[Exception] |
|
|
|
exception: Optional[Exception] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PLACEHOLDER_HW_CLIENT_LABELS = {"", " "} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DeviceMgr(ThreadJob): |
|
|
|
class DeviceMgr(ThreadJob): |
|
|
|
'''Manages hardware clients. A client communicates over a hardware |
|
|
|
'''Manages hardware clients. A client communicates over a hardware |
|
|
|
channel with the device. |
|
|
|
channel with the device. |
|
|
|
@ -507,7 +510,7 @@ class DeviceMgr(ThreadJob): |
|
|
|
'its seed (and passphrase, if any). Otherwise all bitcoins you ' |
|
|
|
'its seed (and passphrase, if any). Otherwise all bitcoins you ' |
|
|
|
'receive will be unspendable.').format(plugin.device)) |
|
|
|
'receive will be unspendable.').format(plugin.device)) |
|
|
|
|
|
|
|
|
|
|
|
def unpaired_device_infos(self, handler, plugin: 'HW_PluginBase', devices=None, |
|
|
|
def unpaired_device_infos(self, handler, plugin: 'HW_PluginBase', devices: List['Device'] = None, |
|
|
|
include_failing_clients=False): |
|
|
|
include_failing_clients=False): |
|
|
|
'''Returns a list of DeviceInfo objects: one for each connected, |
|
|
|
'''Returns a list of DeviceInfo objects: one for each connected, |
|
|
|
unpaired device accepted by the plugin.''' |
|
|
|
unpaired device accepted by the plugin.''' |
|
|
|
@ -537,7 +540,7 @@ class DeviceMgr(ThreadJob): |
|
|
|
return infos |
|
|
|
return infos |
|
|
|
|
|
|
|
|
|
|
|
def select_device(self, plugin: 'HW_PluginBase', handler, |
|
|
|
def select_device(self, plugin: 'HW_PluginBase', handler, |
|
|
|
keystore: 'Hardware_KeyStore', devices=None) -> 'DeviceInfo': |
|
|
|
keystore: 'Hardware_KeyStore', devices: List['Device'] = None) -> 'DeviceInfo': |
|
|
|
'''Ask the user to select a device to use if there is more than one, |
|
|
|
'''Ask the user to select a device to use if there is more than one, |
|
|
|
and return the DeviceInfo for the device.''' |
|
|
|
and return the DeviceInfo for the device.''' |
|
|
|
while True: |
|
|
|
while True: |
|
|
|
@ -557,12 +560,21 @@ class DeviceMgr(ThreadJob): |
|
|
|
devices = None |
|
|
|
devices = None |
|
|
|
if len(infos) == 1: |
|
|
|
if len(infos) == 1: |
|
|
|
return infos[0] |
|
|
|
return infos[0] |
|
|
|
# select device by label |
|
|
|
# select device by label automatically; |
|
|
|
for info in infos: |
|
|
|
# but only if not a placeholder label and only if there is no collision |
|
|
|
if info.label == keystore.label: |
|
|
|
device_labels = [info.label for info in infos] |
|
|
|
return info |
|
|
|
if (keystore.label not in PLACEHOLDER_HW_CLIENT_LABELS |
|
|
|
|
|
|
|
and device_labels.count(keystore.label) == 1): |
|
|
|
|
|
|
|
for info in infos: |
|
|
|
|
|
|
|
if info.label == keystore.label: |
|
|
|
|
|
|
|
return info |
|
|
|
|
|
|
|
# ask user to select device |
|
|
|
msg = _("Please select which {} device to use:").format(plugin.device) |
|
|
|
msg = _("Please select which {} device to use:").format(plugin.device) |
|
|
|
descriptions = [str(info.label) + ' (%s)'%(_("initialized") if info.initialized else _("wiped")) for info in infos] |
|
|
|
descriptions = ["{label} ({init}, {transport})" |
|
|
|
|
|
|
|
.format(label=info.label, |
|
|
|
|
|
|
|
init=(_("initialized") if info.initialized else _("wiped")), |
|
|
|
|
|
|
|
transport=info.device.transport_ui_string) |
|
|
|
|
|
|
|
for info in infos] |
|
|
|
c = handler.query_choice(msg, descriptions) |
|
|
|
c = handler.query_choice(msg, descriptions) |
|
|
|
if c is None: |
|
|
|
if c is None: |
|
|
|
raise UserCancelled() |
|
|
|
raise UserCancelled() |
|
|
|
|