URI: 
       twizard: scan hardware devices directly - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 4781df9d2107ec65181e99073aaac4bbf18ade91
   DIR parent 0520eda628eab626b8e698e8166a9d72798d92ce
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Tue, 23 Aug 2016 13:40:11 +0200
       
       wizard: scan hardware devices directly
       
       Diffstat:
         M gui/qt/installwizard.py             |      17 ++++++++++++-----
         M lib/base_wizard.py                  |      69 ++++++++++++++++++-------------
         M lib/plugins.py                      |      11 +++++------
         M plugins/trezor/plugin.py            |       5 ++---
         M plugins/trustedcoin/trustedcoin.py  |       4 ++--
       
       5 files changed, 62 insertions(+), 44 deletions(-)
       ---
   DIR diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py
       t@@ -338,13 +338,13 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
                    self.show_message(msg)
        
            @wizard_dialog
       -    def confirm_dialog(self, msg, run_next):
       -        self.confirm(msg)
       +    def confirm_dialog(self, title, message, run_next):
       +        self.confirm(message, title)
        
       -    def confirm(self, msg):
       +    def confirm(self, message, title):
                vbox = QVBoxLayout()
       -        vbox.addWidget(WWLabel(msg))
       -        self.set_main_layout(vbox)
       +        vbox.addWidget(WWLabel(message))
       +        self.set_main_layout(vbox, title)
        
            @wizard_dialog
            def action_dialog(self, action, run_next):
       t@@ -379,6 +379,13 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
                self.set_main_layout(vbox, '')
                return clayout.selected_index()
        
       +    def get_passphrase(self, msg, confirm):
       +        phrase = self.pw_layout(msg, PW_PASSPHRASE)
       +        if phrase is None:
       +            raise UserCancelled
       +        return phrase
       +
       +
            @wizard_dialog
            def account_id_dialog(self, run_next):
                message = '\n'.join([
   DIR diff --git a/lib/base_wizard.py b/lib/base_wizard.py
       t@@ -117,12 +117,12 @@ class BaseWizard(object):
                        ('create_seed', _('Create a new seed')),
                        ('restore_seed', _('I already have a seed')),
                        ('restore_from_key', _('Import keys')),
       -                ('choose_hw',  _('Use hardware device')),
       +                ('choose_device',  _('Use hardware device')),
                    ]
                else:
                    choices = [
                        ('restore_from_key', _('Import cosigner key')),
       -                ('choose_hw',  _('Cosign with hardware device')),
       +                ('choose_device',  _('Cosign with hardware device')),
                    ]
        
                self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)
       t@@ -165,46 +165,59 @@ class BaseWizard(object):
                    ])
                self.restore_keys_dialog(title=title, message=message, run_next=self.on_restore, is_valid=v)
        
       -    def choose_hw(self):
       -        hw_wallet_types, choices = self.plugins.hardware_wallets('create')
       -        choices = zip(hw_wallet_types, choices)
       +    def choose_device(self):
                title = _('Hardware Keystore')
       -        if choices:
       -            msg = _('Select the type of device') + ':'
       -        else:
       -            msg = ' '.join([
       +        # check available plugins
       +        support = self.plugins.get_hardware_support()
       +        if not support:
       +            msg = '\n'.join([
                        _('No hardware wallet support found on your system.'),
                        _('Please install the relevant libraries (eg python-trezor for Trezor).'),
                    ])
       -        self.choice_dialog(title=title, message=msg, choices=choices, run_next=self.on_hardware)
       -
       -    def on_hardware(self, hw_type):
       -        self.hw_type = hw_type
       -        title = _('Hardware wallet') + ' [%s]' % hw_type
       -        message = _('Do you have a device, or do you want to restore a wallet using an existing seed?')
       -        choices = [
       -            ('on_hardware_device', _('I have a %s device')%hw_type),
       -            ('on_hardware_seed', _('I have a %s seed')%hw_type),
       -        ]
       -        self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)
       -
       -    def on_hardware_device(self):
       -        f = lambda x: self.run('on_hardware_account_id', x)
       +            self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_device())
       +            return
       +        # scan devices
       +        devices = []
       +        for name, description, plugin in support:
       +            devmgr = plugin.device_manager()
       +            try:
       +                u = devmgr.unpaired_device_infos(self, plugin)
       +            except:
       +                print "error", name
       +                continue
       +            devices += map(lambda x: (name, x), u)
       +        if not devices:
       +            msg = '\n'.join([
       +                _('No hardware device detected.'),
       +                _('To trigger a rescan, press \'next\'.'),
       +            ])
       +            self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_device())
       +            return
       +        # select device
       +        self.devices = devices
       +        choices = []
       +        for name, device_info in devices:
       +            choices.append( ((name, device_info), device_info.description) )
       +        msg = _('Select a device') + ':'
       +        self.choice_dialog(title=title, message=msg, choices=choices, run_next=self.on_device)
       +
       +    def on_device(self, name, device_info):
       +        f = lambda x: self.run('on_hardware_account_id', name, device_info, x)
                self.account_id_dialog(run_next=f)
        
       -    def on_hardware_account_id(self, account_id):
       +    def on_hardware_account_id(self, hw_type, device_info, account_id):
                from keystore import hardware_keystore, bip44_derivation
                derivation = bip44_derivation(int(account_id))
       -        plugin = self.plugins.get_plugin(self.hw_type)
       -        xpub = plugin.setup_device(derivation, self)
       +        plugin = self.plugins.get_plugin(hw_type)
       +        xpub = plugin.setup_device(device_info, derivation, self)
                # create keystore
                d = {
                    'type': 'hardware',
       -            'hw_type': self.hw_type,
       +            'hw_type': hw_type,
                    'derivation': derivation,
                    'xpub': xpub,
                }
       -        k = hardware_keystore(self.hw_type, d)
       +        k = hardware_keystore(hw_type, d)
                self.on_keystore(k, None)
        
            def on_hardware_seed(self):
   DIR diff --git a/lib/plugins.py b/lib/plugins.py
       t@@ -139,19 +139,18 @@ class Plugins(DaemonThread):
                requires = d.get('requires_wallet_type', [])
                return not requires or w.wallet_type in requires
        
       -    def hardware_wallets(self, action):
       -        wallet_types, descs = [], []
       +    def get_hardware_support(self):
       +        out = []
                for name, (gui_good, details) in self.hw_wallets.items():
                    if gui_good:
                        try:
                            p = self.get_plugin(name)
       -                    if action == 'restore' or p.is_enabled():
       -                        wallet_types.append(details[1])
       -                        descs.append(details[2])
       +                    if p.is_enabled():
       +                        out.append([name, details[2], p])
                        except:
                            traceback.print_exc()
                            self.print_error("cannot load plugin for:", name)
       -        return wallet_types, descs
       +        return out
        
            def register_wallet_type(self, name, gui_good, wallet_type):
                from wallet import register_wallet_type, register_constructor
   DIR diff --git a/plugins/trezor/plugin.py b/plugins/trezor/plugin.py
       t@@ -199,13 +199,12 @@ class TrezorCompatiblePlugin(HW_PluginBase):
                    client.load_device_by_xprv(item, pin, passphrase_protection,
                                               label, language)
        
       -    def setup_device(self, derivation, wizard):
       +    def setup_device(self, device_info, derivation, wizard):
                '''Called when creating a new wallet.  Select the device to use.  If
                the device is uninitialized, go through the intialization
                process.'''
       -        handler = self.create_handler(wizard)
       +        handler = wizard
                devmgr = self.device_manager()
       -        device_info = devmgr.select_device(handler, self)
                device_id = device_info.device.id_
                if not device_info.initialized:
                    self.initialize_device(device_id, wizard, handler)
   DIR diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py
       t@@ -337,7 +337,7 @@ class TrustedCoinPlugin(BasePlugin):
            def show_disclaimer(self, wizard):
                wizard.set_icon(':icons/trustedcoin.png')
                wizard.stack = []
       -        wizard.confirm_dialog('\n\n'.join(DISCLAIMER), run_next = lambda x: wizard.run('choose_seed'))
       +        wizard.confirm_dialog(title='Disclaimer', message='\n\n'.join(DISCLAIMER), run_next = lambda x: wizard.run('choose_seed'))
        
            def choose_seed(self, wizard):
                title = _('Create or restore')
       t@@ -372,7 +372,7 @@ class TrustedCoinPlugin(BasePlugin):
                ]
                msg = '\n\n'.join(msg)
                wizard.stack = []
       -        wizard.confirm_dialog(msg, run_next = lambda x: wizard.run('create_remote_key'))
       +        wizard.confirm_dialog(title='', message=msg, run_next = lambda x: wizard.run('create_remote_key'))
        
            def restore_wallet(self, wizard):
                title = _("Restore two-factor Wallet")