diff --git a/appliance_setup/appliance_setup.py b/appliance_setup/appliance_setup.py
index 78e724b8e41ec9841691793859e57d46c494e113..542b957362577ab7986f44a30ec39a031cc87557 100755
--- a/appliance_setup/appliance_setup.py
+++ b/appliance_setup/appliance_setup.py
@@ -28,143 +28,55 @@
 ## support, we'll need a proper kernel module doing the USB port forwarding...
 
 import device_types
-import user_interface
+from user_interface import *
 import helpers
+import configuration
 
 #use Data::Dumper;
-
-our $debug=0;
-
-our $VERSION=0.01;
-print "appliance_setup.py - version $VERSION\n";
-print "Linux configuration utility for the HP 2101nw wireless G USB print server.\n\n";
-print "(C) 2011 Reinhold Kainhofer <reinhold\@kainhofer.com>\n";
-print "License: GPL v2 or later\n\n";
+VERSION=0.01;
+#print "appliance_setup.py - version $VERSION";
+#print "Linux configuration utility for the HP 2101nw wireless G USB print server.\n";
+#print "(C) 2011 Reinhold Kainhofer <reinhold\@kainhofer.com>";
+#print "License: GPL v2 or later\n\n";
 
 
 
 # The PC always identifies itself as XXXXXXXX.
-our $thisboxname = "XXXXXXXX";
+#our $thisboxname = "XXXXXXXX";
 
 
+def ask_device (ui):
+  devices = [];
+  for f in device_types.device_factories:
+    devices.extend (f.detect (ui));
 
-###############################################################################
-##  USB DEVICE MANAGEMENT / COMMUNICATION FUNCTIONS
-###############################################################################
+  dev = None;
+  if len(devices) > 1:
+    devicedescriptions = [d.device_string () for d in devices];
+    dev = ui.ask_choice ("Multiple supported devices detected:", devicedescriptions, devices, devices[0]);
+  elif len(devices) == 1:
+    dev = devices[0];
 
+  if dev:
+    ui.progress ("Using device: %s" % dev.device_string ());
+  else:
+    errmsg = ("ERROR: No supported device was found...\n\n"
+             "Supported devices are:\n");
+    devices = [];
+    for f in device_types.device_factories:
+      devices.extend (["\t-) %s" % d for d in f.list_supported ()])
+    errmsg += "\n".join( devices )
+    ui.progress (errmsg)
+  return dev;
 
-sub device_detect () {
-  my $usb = Device::USB->new();
-  my @devices;
-  my @devicedescriptions;
 
-  foreach (@supported_devices) {
-    foreach my $dev ($usb->list_devices (@$_)) {
-      push @devices, $dev;
-      push @devicedescriptions, sprintf ("%s / ID %04x:%04x (%s: %s)", $dev->filename(),
-             $dev->idVendor(), $dev->idProduct(),
-             $dev->manufacturer(), $dev->product());
-    }
-  }
-  my $dev;
-  if (scalar (@devices) > 1) {
-    $dev = ask_choice ("Multiple supported devices detected:", \@devicedescriptions, \@devices, $devices[0]);
-  } elsif (scalar (@devices) == 1) {
-    $dev = @devices[0];
-  }
-  if ($dev) {
-    progress ("Using device: %04x:%04x (%s: %s)\n\n",
-             $dev->idVendor(), $dev->idProduct(),
-             $dev->manufacturer(), $dev->product() );
-  } else {
-    progress "\tNo supported devices found\n\n";
-    print "ERROR: No supported device was found...\n\n";
-    print "Supported devices are (USB IDs): \n";
-    foreach (@supported_devices) {
-      printf "\t%04x:%04x (%s)\n", @$_;
-    }
-    print "\n";
-    print "Please connect your HP wireless USB print server to \n";
-    print "the computer with the black USB cable (micro USB plug) \n";
-    print "and run this script again.\n\n";
-  }
-  return $dev;
-}
-
-sub device_open ($) {
-  my $dev = shift;
-  my $res = $dev->open;
-  if ($res<0) {
-    printf "ERROR opening device (%d): %s\n", $res, $!;
-  }
-  $res = $dev->detach_kernel_driver_np(0);
-  # No error check, Linux always gives an error!
-  $res = $dev->set_configuration(1);
-  if ($res<0) {
-    printf "ERROR setting configuration 1 (%d): %s\n", $res, $!;
-  }
-  $res = $dev->claim_interface(0);
-  if ($res<0) {
-    printf "ERROR claiming interface 0 (%d): %s\n", $res, $!;
-  }
-}
-
-sub device_close ($) {
-  my $dev = shift;
-  $dev->release_interface(0);
-  $dev->reset;
-}
 
+"""
+###############################################################################
+##  USB DEVICE MANAGEMENT / COMMUNICATION FUNCTIONS
+###############################################################################
 
 
-sub bulk_data_write ($$$) {
-  my $dev = shift;
-  my $data = shift;
-  my $len = shift;
-  # Add the header (12 bytes): '@\0', packet length, boxname (="XXXXXXXX") 
-  my $request = sprintf ("@\0%s%s%s", pack ("v", $len), $thisboxname, $data);
-  debug "Request: $request\n";
-  my $res = $dev->bulk_write (1, $request, length($request), 500);
-  if ($res<0) {
-    printf "ERROR write bulk data (%d): %s\n", $res, $!;
-  }
-  return $res;
-}
-
-# Read USB bulk data into the second argument (output argument!), return 
-# size of data
-sub bulk_data_read ($$$) {
-  my $dev = shift;
-  # TODO: Get rid of the length argument, simply read all that is sent
-  # FIXME: Read bulk data in loops until there is nothing left to read?
-  my $len = $_[1];
-  my $readdata = "";
-  my $res = $dev->bulk_read (2, $readdata, $len+13, 500);
-  if ($res<0) {
-    printf "ERROR reading bulk data (%d): %s\n", $res, $!;
-    return;
-  }
-  if ($res == 0) {
-    debug "\tEmpty response received\n";
-    return 0;
-  }
-  debug ("read %d bytes: \n%s\n\n", $res, $readdata); 
-  # Check and cut off the header after some sanity checks:
-  if (substr ($readdata, 0, 3) ne "\@\0\0") {
-    printf "ERROR reading data: Wrong header %s\n", substr ($readdata, 0, 3);
-  }
-  my $datalen = unpack ("v", substr ($readdata, 3, 2));
-  $res = $res-13;
-  if ($datalen != $res) {
-    printf "ERROR reading data: Expected %d bytes of data, got %d\n", $datalen, $res;
-  }
-  my $boxname = substr ($readdata, 5, 8);
-  # FIXME: Check the name of the box...
-  
-  # cut off the header:
-  $_[0]=substr ($readdata, 13);
-  return $res;
-}
 
 sub device_reset ($) {
   my $dev = shift;
@@ -191,80 +103,8 @@ sub device_reset ($) {
 ###############################################################################
 
 
-sub print_current_configuration ($$) {
-  my $config = shift;
-  my $text = shift;
-  sub val ($$) {
-    my $config = shift;
-    my $var = shift;
-    foreach my $e (@$config) {
-      return $e->[2] if ($e->[0] eq $var);
-    }
-    return undef;
-  }
-  progress $text;
-  printf "\tDevice name:   %s\n", val($config, '0001');
-  if (val($config,'0012') ne "Enable") {
-    printf "\tTCP/IP not yet configured and/or enabled.\n\n";
-    return;
-  }
-  my $adhoc = val($config,'7024') == '1';
-  if ($adhoc) {
-    # ad-hoc network:
-    my $sec = val($config,'7103');
-    printf "\tWireless mode: Ad-hoc network\n";
-    printf "\tSSID:          %s\n", val($config,'7100');
-    printf "\tChannel:       %s\n", val($config,'7102');
-    printf "\tSecurity:      %s\n", security2string ($sec);
-    if ($sec == 0) {
-      # None => nothing to display
-    } elsif ($sec == 1) {
-      # WEP => Print passphrase
-      printf "\tWEP Key:       %s\n", val($config,'7106');
-    } else {
-      # WPA 1/2 or mixed
-      printf "\tEncryption:    %s\n", encryption2string(val($config,'7118'));
-      printf "\tPassphrase:    %s\n", val($config,'7119');
-    }
-  } else {
-    # infrastructure network
-    my $sec = val($config,'7003');
-    printf "\tSSID:          %s\n", val($config,'7000');
-    printf "\tChannel:       %s\n", val($config,'7002');
-    printf "\tSecurity:      %s\n", security2string ($sec);
-    if ($sec == 0) {
-      # None => nothing to display
-    } elsif ($sec == 1) {
-      # WEP => Print passphrase
-      printf "\tAuthentication:  %s\n", authentication2string(val($config,'7012'));
-      printf "\tWEP Key:       %s\n", val($config,'7006');
-    } else {
-      # WPA 1/2 or mixed
-      printf "\tEncryption:    %s\n", encryption2string(val($config,'7018'));
-      printf "\tPassphrase:    %s\n", val($config,'7019');
-    }
-  }
-  
-  my $dhcp = (val($config,'4020') eq "Enable");
-  printf "\tIPv4 method:   %s\n", $dhcp?"DHCP":"manual";
-  my $associated = val($config,'7014') =~ m/STATE:Associated/;
-  if ($associated || !$dhcp) {
-    printf "\tIP address:    %s\n", val($config,'4000');
-    printf "\tGateway:       %s\n", val($config,'4001');
-    printf "\tNetmask:       %s\n", val($config,'4002');
-  }
-  if ($associated) {
-    my $assinfo = val($config,'7014');
-    printf "\tLink state:    %s\n", $assinfo;
-  } else {
-    # Not connected
-    printf "\tWireless not connected\n";
-  }
-  print "\n\n";
-}
-
 
-sub device_config_request ($) {
+sub device_config_request ($) { 
   my $dev = shift;
   my $cfg = "";
   debug "Reading current configuration...\n";
@@ -639,67 +479,75 @@ sub ask_operation () {
     ],
     "exit");
 }
+"""
 
-sub main () {
-  my $reconfigured = 0;
-  my $dev = device_detect () or exit;
-  device_open ($dev);
-  my $cfg = device_config_request ($dev);
-  my @config = config_parse ($cfg);
-  print_current_configuration (\@config, "Current configuration of the device:\n");
-  # Ask what to do (set wifi AP, ad-hoc, save, restore)
-  my $operation = ask_operation ();
-  debug ("\tSelected operation: %s\n", $operation);
-  if ($operation eq "wifi_infrastructure") {
-    # Infrastructure, scan for APs
-    my ($aps,$newap) = wifi_ask_ssid ($dev);
-    my %newconfig = ('SSID' => $newap);
-    debug ("\tSelected wireless network: %s\n", $newap);
-    my %security = wifi_ask_security ($newap, $aps);
-    my %ip = wifi_ask_address ($newap, $aps);
-    %newconfig = (%newconfig, %security, %ip);
-    my $newcfg = wifi_config_create (\%newconfig);
-    device_config_send ($dev, $newcfg);
-    $reconfigured = 1;
-  } elsif ($operation eq "wifi_adhoc") {
-    # Ad-hoc connection to print server
-    my $adhocssid = adhoc_ask_ssid ($dev);
-    adhoc_send_unknown_command ($dev);
-    my $channel = adhoc_ask_channel ($dev, $adhocssid);
-    my %newconfig = ('SSID' => $adhocssid,
-                     'channel' => $channel,
-                     adhoc_ask_security ($adhocssid)
-                    );
-    my $newcfg = adhoc_config_create (\%newconfig);
-    device_config_send ($dev, $newcfg);
-    $reconfigured = 1;
-  } elsif ($operation eq "change_hostname") {
-    set_hostname ($dev);
-    $reconfigured = 1;
-  } elsif ($operation eq "config_save") {
-    my $filename = ask_input ("Filename to save to: ", \&is_filename, "");
-    device_config_save ($filename, $cfg);
-  } elsif ($operation eq "config_restore") {
-    my $filename = ask_input ("Filename to load from: ", \&is_filename, "");
-    device_config_restore ($dev, $filename);
-  } elsif ($operation eq "reload") {
-    # do nothing, we'll call main below...
-  } elsif ($operation eq "exit") {
-    device_close ($dev);
-    exit;
-  } else {
-    printf "ERROR: unknown operation %s\n", $operation;
-  }
-  device_close ($dev);
-  # if the configuration was changed, print the new config after the device was reset
-  $dev = device_detect () or exit;
-  device_open ($dev);
-  @config = config_parse (device_config_request ($dev));
-  print_current_configuration (\@config, "New configuration of the device:\n");
+
+def main ():
+  reconfigured = False
+  operation = ""
+  UI = ConsoleInterface (True)
   
-  # Loop in main if "reload" is selected...
-  main () if ($operation eq "reload");
-}
+  dev = ask_device (UI)
+  if (not dev or not dev.device_open ()):
+    return
+
+  cfg = dev.config_retrieve ()
+  #my @config = config_parse ($cfg);
+  #print_current_configuration (\@config, "Current configuration of the device:\n");
+  ## Ask what to do (set wifi AP, ad-hoc, save, restore)
+  #my $operation = ask_operation ();
+  #debug ("\tSelected operation: %s\n", $operation);
+  #if ($operation eq "wifi_infrastructure") {
+    ## Infrastructure, scan for APs
+    #my ($aps,$newap) = wifi_ask_ssid ($dev);
+    #my %newconfig = ('SSID' => $newap);
+    #debug ("\tSelected wireless network: %s\n", $newap);
+    #my %security = wifi_ask_security ($newap, $aps);
+    #my %ip = wifi_ask_address ($newap, $aps);
+    #%newconfig = (%newconfig, %security, %ip);
+    #my $newcfg = wifi_config_create (\%newconfig);
+    #device_config_send ($dev, $newcfg);
+    #$reconfigured = 1;
+  #} elsif ($operation eq "wifi_adhoc") {
+    ## Ad-hoc connection to print server
+    #my $adhocssid = adhoc_ask_ssid ($dev);
+    #adhoc_send_unknown_command ($dev);
+    #my $channel = adhoc_ask_channel ($dev, $adhocssid);
+    #my %newconfig = ('SSID' => $adhocssid,
+                     #'channel' => $channel,
+                     #adhoc_ask_security ($adhocssid)
+                    #);
+    #my $newcfg = adhoc_config_create (\%newconfig);
+    #device_config_send ($dev, $newcfg);
+    #$reconfigured = 1;
+  #} elsif ($operation eq "change_hostname") {
+    #set_hostname ($dev);
+    #$reconfigured = 1;
+  #} elsif ($operation eq "config_save") {
+    #my $filename = ask_input ("Filename to save to: ", \&is_filename, "");
+    #device_config_save ($filename, $cfg);
+  #} elsif ($operation eq "config_restore") {
+    #my $filename = ask_input ("Filename to load from: ", \&is_filename, "");
+    #device_config_restore ($dev, $filename);
+  #} elsif ($operation eq "reload") {
+    ## do nothing, we'll call main below...
+  #} elsif ($operation eq "exit") {
+    #device_close ($dev);
+    #exit;
+  #} else {
+    #printf "ERROR: unknown operation %s\n", $operation;
+  #}
+  #device_close ($dev);
+  ## if the configuration was changed, print the new config after the device was reset
+  #$dev = device_detect () or exit;
+  #device_open ($dev);
+  #@config = config_parse (device_config_request ($dev));
+  #print_current_configuration (\@config, "New configuration of the device:\n");
+  
+  ## Loop in main if "reload" is selected...
+  if (operation == "reload"):
+    main ()
+#}
 
 
 
diff --git a/appliance_setup/configuration.py b/appliance_setup/configuration.py
index ab58c0d388792bcd4931d8c90eb079da4fbcca95..30bd5cfb2e5a7e7926279f48b81ba9451224d020 100644
--- a/appliance_setup/configuration.py
+++ b/appliance_setup/configuration.py
@@ -8,92 +8,92 @@
 
 
 class Config:
-  self.cfgdata = [];
-  self.cfgdesc = [];
+  cfgdata = [];
+  cfgdesc = [];
   def __init__ (self, ui):
     self.ui = ui
-    
+
   def val (self, line):
     return self.cfgdata.get (line, None)
 
   def print_configuration (self):
     # TODO: ui.progress (text);
-    ui.print ("\tDevice name:   %(0001)s\n" % self.cfgdata);
-    if (val('0012') != "Enable"):
-      ui.print "\tTCP/IP not yet configured and/or enabled.\n\n";
+    res = "\tDevice name:   %(0001)s\n" % self.cfgdata;
+    if (self.val('0012') != "Enable"):
+      res += "\tTCP/IP not yet configured and/or enabled.\n\n"
       return
-    adhoc = (val('7024') == '1');
+    adhoc = (self.val('7024') == '1')
     if adhoc:
       # ad-hoc network:
-      my $sec = val($config,'7103');
-      printf "\tWireless mode: Ad-hoc network\n";
-      printf "\tSSID:          %s\n", val($config,'7100');
-      printf "\tChannel:       %s\n", val($config,'7102');
-      printf "\tSecurity:      %s\n", security2string ($sec);
-      if ($sec == 0) {
+      sec = self.val('7103');
+      res += "\tWireless mode: Ad-hoc network\n"
+      res += "\tSSID:          %s\n" % self.val('7100')
+      res += "\tChannel:       %s\n" % self.val('7102')
+      res += "\tSecurity:      %s\n" % security2string (sec)
+      if (sec == 0):
         # None => nothing to display
-      } elsif ($sec == 1) {
+        pass
+      elif (sec == 1):
         # WEP => Print passphrase
-        printf "\tWEP Key:       %s\n", val($config,'7106');
-      } else {
+        res += "\tWEP Key:       %s\n" % self.val('7106')
+      else:
         # WPA 1/2 or mixed
-        printf "\tEncryption:    %s\n", encryption2string(val($config,'7118'));
-        printf "\tPassphrase:    %s\n", val($config,'7119');
-      }
-    } else {
+        res += "\tEncryption:    %s\n" % encryption2string(self.val('7118'))
+        res += "\tPassphrase:    %s\n" % self.val('7119')
+    else:
       # infrastructure network
-      my $sec = val($config,'7003');
-      printf "\tSSID:          %s\n", val($config,'7000');
-      printf "\tChannel:       %s\n", val($config,'7002');
-      printf "\tSecurity:      %s\n", security2string ($sec);
-      if ($sec == 0) {
+      sec = self.val('7003');
+      res += "\tSSID:          %s\n", self.val('7000')
+      res += "\tChannel:       %s\n", self.val('7002')
+      res += "\tSecurity:      %s\n", security2string (sec)
+      if (sec == 0):
         # None => nothing to display
-      } elsif ($sec == 1) {
+        pass
+      elif (sec == 1):
         # WEP => Print passphrase
-        printf "\tAuthentication:  %s\n", authentication2string(val($config,'7012'));
-        printf "\tWEP Key:       %s\n", val($config,'7006');
-      } else {
+        res += "\tAuthentication:  %s\n" % authentication2string(self.val('7012'))
+        res += "\tWEP Key:       %s\n" % self.val('7006')
+      else:
         # WPA 1/2 or mixed
-        printf "\tEncryption:    %s\n", encryption2string(val($config,'7018'));
-        printf "\tPassphrase:    %s\n", val($config,'7019');
-      }
-    }
+        res += "\tEncryption:    %s\n" % encryption2string(self.val('7018'))
+        res += "\tPassphrase:    %s\n" % self.val('7019')
   
-    my $dhcp = (val($config,'4020') eq "Enable");
-    printf "\tIPv4 method:   %s\n", $dhcp?"DHCP":"manual";
-    my $associated = val($config,'7014') =~ m/STATE:Associated/;
-    if ($associated || !$dhcp) {
-      printf "\tIP address:    %s\n", val($config,'4000');
-      printf "\tGateway:       %s\n", val($config,'4001');
-      printf "\tNetmask:       %s\n", val($config,'4002');
-    }
-    if ($associated) {
-      my $assinfo = val($config,'7014');
-      printf "\tLink state:    %s\n", $assinfo;
-    } else {
+    dhcp = (self.val('4020') == "Enable")
+    res += "\tIPv4 method:   %s\n" % ("DHCP" if dhcp else "manual")
+    associated = (self.val('7014').find ("STATE:Associated") >= 0)
+    if (associated or not dhcp):
+      res += "\tIP address:    %s\n" % self.val('4000')
+      res += "\tGateway:       %s\n" % self.val('4001')
+      res += "\tNetmask:       %s\n" % self.val('4002')
+    if associated:
+      res += "\tLink state:    %s\n" % self.val('7014')
+    else:
       # Not connected
-      printf "\tWireless not connected\n";
-    }
-    print "\n\n";
-    
-  pass
+      res += "\tWireless not connected\n"
+    res += "\n\n"
+    return res
+
+  def config_parse (self, data):
+    entries = data.split ('\n');
+    self.cfgdata = [];
+    self.cfgdesc = [];
+    entry_re = re.compile ("^([0-9]{4}) ([A-Z]+):(.*)$");
+    for l in entries:
+      m = entry_re.match (l)
+      (line, name, value) = m.group (1, 2, 3)
+      self.cfgdata[line] = value
+      self.cfgdesc[line] = name
+    print self.cfgdata
+    print self.cfgdesc
 
 
 ###############################################################################
 ##  DEVICE CONFIGURATION HANDLING
 ###############################################################################
 
-
+"""
 sub config_parse ($) {
   my $cfg = shift;
-  my @entries = split ('\n', $cfg);
-  my @config = ();
-  foreach (@entries) {
-    my $key = substr ($_, 0, 4);
-    (my $name, my $value) = split (/:/, substr ($_, 5), 2);
-    push (@config, [$key, $name, $value]);
-  }
-  return @config;
 }
 
 # Convert a list of the form [ ["OPTIONID", "OPTIONNAME", VALUE], ...]
@@ -508,3 +508,4 @@ sub main () {
 ###############################################################################
 
 main ();
+"""
\ No newline at end of file
diff --git a/appliance_setup/device_types.py b/appliance_setup/device_types.py
index 2d443560682a5e9c592a51c33086c1ab5fb0f2e7..0c5bd2c3d16a8e79702837db0110b22daac1b491 100755
--- a/appliance_setup/device_types.py
+++ b/appliance_setup/device_types.py
@@ -8,10 +8,25 @@
 
 import usb
 
-## Base class
-class DeviceType:
+device_factories = [];
+
+class DeviceFactory:
     def __init__ (self):
       pass
+    def list_supported (self):
+      return [];
+    def detect (self, ui):
+      return [];
+
+## Base class
+class Device:
+    ui = None
+    capabilities = {'wifi': False, 'ad-hoc': False}
+    def __init__ (self, ui):
+      self.ui = ui
+
+    def device_string (self):
+      pass
 
     def detect_devices (self):
       pass
@@ -30,76 +45,254 @@ class DeviceType:
       pass
     def print_supported_devices (self):
       pass
+    def error (self, msg):
+      self.ui.error (msg)
+
+class USBDeviceFactory (DeviceFactory):
+    supported_devices = (
+      {'vendor_id':0x03f0, 'product_id':0xcd02, 'desc':"HP 2101nw wireless G USB print server"},
+    );
+    def __init__ (self):
+      DeviceFactory.__init__ (self)
+      pass
+    def list_supported (self):
+      devices = [];
+      for d in self.supported_devices:
+        devices.append ("%(desc)s, USB %(vendor_id)04x:%(product_id)04x" % d);
+      return devices;
+
+    def detect (self, ui):
+      devices = [];
+      for d in self.supported_devices:
+        for bus in usb.busses():
+          for dev in bus.devices:
+            if (dev.idVendor == d['vendor_id'] and dev.idProduct == d['product_id']):
+              devices.append (USBDevice (ui, dev, bus.dirname, d['desc']));
+      return devices;
+
+device_factories.append (USBDeviceFactory());
 
 ## USB-based devices like the HP2101nw
-class USBDevice (DeviceType):
+class USBDevice (Device):
+  dev = None
+  desc = "Unknown"
+  bus = ""
+  handle = None
+  thisboxname = "XXXXXXXX"
+  def __init__ (self, ui, dev, bus, desc):
+    Device.__init__ (self, ui)
+    self.dev = dev
+    self.desc = desc
+    self.bus = bus
+
+  def device_string (self):
+    return "%s (USB device %04x:%04x, Bus %s Device %s)" % (
+           self.desc, self.dev.idVendor, self.dev.idProduct,
+           self.bus, self.dev.filename);
+
+  def device_open (self):
+    # TODO: Use exceptions rather than those nasty if (res<0) checks
+    self.handle = self.dev.open ()
+    if (self.handle < 0):
+      self.error ("opening device (%d): %s" % res)
+      self.device_close ()
+      return False
+    try: #Linux will ALWAYS fail here!
+      res = self.handle.detachKernelDriver (0)
+    except usb.USBError:
+      pass
+
+    try:
+      self.handle.setConfiguration(1)
+      self.handle.claimInterface(0);
+    except Exception, e:
+      self.error ("setting configuration 1 and claiming interface 0 (%d): %s\n" % (1, e.message));
+      self.device_close ()
+      return False
+    return self.handle;
+ 
+  def device_close (self):
+    self.handle.releaseInterface(0)
+    self.handle.reset ()
+    self.handle = None
+    
+  def config_retrieve (self):
+    self.ui.debug ("Reading current configuration...\n");
+    cfg = ""
+    res = self.bulk_data_write ("9100:\n", 6)
+    #res = bulk_data_read ($cfg, 25600);
+    #return $cfg;
+
+
+  def bulk_data_write (self, data, length):
+    # Add the header (12 bytes): '@\0', packet length, boxname (="XXXXXXXX")
+    request = "@\0" + struct.pack("<H8s", length, self.thisboxname) + data
+    self.ui.debug ("Request: " + request)
+    res = None
+    try:
+      res = self.handle.bulkWrite (1, request, 500)
+    except Exception, e:
+      self.error ("writing bulk data; message: %s" % e.message)
+    return res
+
+# Read USB bulk data into the second argument (output argument!), return 
+# size of data
+  def bulk_data_read ($$$):
+    # TODO: Get rid of the length argument, simply read all that is sent
+    # FIXME: Read bulk data in loops until there is nothing left to read?
+    my $len = $_[1];
+    my $readdata = "";
+    res = self.handle.bulkRead (2, $readdata, $len+13, 500);
+    if ($res<0) {
+      printf "ERROR reading bulk data (%d): %s\n", $res, $!;
+      return;
+    }
+    if ($res == 0) {
+      debug "\tEmpty response received\n";
+      return 0;
+    }
+    debug ("read %d bytes: \n%s\n\n", $res, $readdata); 
+    # Check and cut off the header after some sanity checks:
+    if (substr ($readdata, 0, 3) ne "\@\0\0") {
+      printf "ERROR reading data: Wrong header %s\n", substr ($readdata, 0, 3);
+    }
+    my $datalen = unpack ("v", substr ($readdata, 3, 2));
+    $res = $res-13;
+    if ($datalen != $res) {
+      printf "ERROR reading data: Expected %d bytes of data, got %d\n", $datalen, $res;
+    }
+    my $boxname = substr ($readdata, 5, 8);
+    ## FIXME: Check the name of the box...
+  
+    ## cut off the header:
+    $_[0]=substr ($readdata, 13);
+    return $res;
+
+
+
+
+import select
+import socket
+import struct
+import subprocess
+import re
+
+class Ether8888Packet:
+  def __init__ (self):
+    pass
+  def decode (self, data):
+    self.dstAddr, self.srcAddr, self.proto = struct.unpack("!6s6sH",data[:14])
+    if self.proto != 0x8888:
+      # FIXME: Error message about invalid packet
+      return False
+    self.ethData = data[14:]
+    self.cmd,self.unknown,self.zero,self.nrPackets,self.dataLen = \
+        struct.unpack ("<2s2s2sHH", self.ethData[:10])
+    self.payload = self.ethData[10:self.dataLen+10]
+
+    if (self.dataLen != len (self.payload)):
+      # FIXME: Error message about invalid packet
+      return False
+    return True
+
+class Ether8888DeviceFactory (DeviceFactory):
     supported_devices = (
-      {'vendor_id':0x03f0, 'product_id':0xcd02, 'desc':"HP 2101nw wireless G USB print server"}
+      {'desc':"Vonets VAP11G wireless bridge"},
     );
-    dev = None;
+    proto=0x8888;
     def __init__ (self):
-      DeviceType.__init__ ()
-      
-    def detect_devices ():
-      devices = ();
+      DeviceFactory.__init__ (self)
+      pass
+    def list_supported (self):
+      devices = [];
       for d in self.supported_devices:
-        for dev in (usb.list_devices (d['vendor_id'], d['product_id'])):
-          devices.append (dev);
-          devicedescriptions.append ("%s / ID %04x:%04x (%s: %s)" % (dev.filename(),
-             dev.idVendor(), dev.idProduct(),
-             dev.manufacturer(), dev.product());
-      
-      if len(devices) > 1:
-        dev = ask_choice ("Multiple supported devices detected:", devicedescriptions, devices, devices[0]);
-      else:
-        def = devices[0];
-
-      if dev:
-        progress ("Using device: %04x:%04x (%s: %s)\n\n",
-               dev.idVendor(), dev.idProduct(),
-               dev.manufacturer(), dev.product() );
-      else:
-        progress "\tNo supported devices found\n\n";
-        print "ERROR: No supported device was found...\n\n";
-        print "Supported devices are (USB IDs): \n";
-        for d in (self.supported_devices):
-          print "\t%(vendor_id)04x:%(product_id)04x (%(desc)s)\n" % d;
-        print "\n";
-        print "Please connect your HP wireless USB print server to \n";
-        print "the computer with the black USB cable (micro USB plug) \n";
-        print "and run this script again.\n\n";
-      return dev;
-
-    def device_open () {
-      res = self.dev->open;
-      if (res<0) {
-    printf "ERROR opening device (%d): %s\n", $res, $!;
-  }
-  $res = $dev->detach_kernel_driver_np(0);
-  # No error check, Linux always gives an error!
-  $res = $dev->set_configuration(1);
-  if ($res<0) {
-    printf "ERROR setting configuration 1 (%d): %s\n", $res, $!;
-  }
-  $res = $dev->claim_interface(0);
-  if ($res<0) {
-    printf "ERROR claiming interface 0 (%d): %s\n", $res, $!;
-  }
-}
-
-sub device_close ($) {
-  my $dev = shift;
-  $dev->release_interface(0);
-  $dev->reset;
-}
+        devices.append ("%(desc)s, Ethernet, type 0x%(proto)04x" % dict({'proto': self.proto}, **d));
+      return devices;
 
+    def detect (self, ui):
+      devices = [];
+      try:
+        ifconfig = subprocess.check_output ("ifconfig")
+        if_re = re.compile ("([a-zA-Z0-9]+)\s+Link")
+        interfaces = [i for i in if_re.findall (ifconfig) if (not i == "lo")];
+
+        # Create Ethernet II broadcast socket of ethertype 0x8888:
+        sockets = []
+        bcAddr = "\xFF\xFF\xFF\xFF\xFF\xFF";
+        for i in interfaces:
+          s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, self.proto)
+          s.bind((i,self.proto))
+          ifName,ifProto,pktType,hwType,hwAddr = s.getsockname()
+          txFrame = struct.pack("!6s6sH",bcAddr,hwAddr,self.proto) + "\x00"*0x10
+          # Send and wait for response
+          s.send (txFrame)
+          sockets.append (s)
+
+        found = True
+        while found:
+          (sread, swrite, sx) = select.select(sockets, [], [], 0.5)
+          found = len (sread) > 0;
+          for sock in sread:
+            rxFrame = sock.recv (1500)
+            pack = Ether8888Packet()
+            if pack.decode (rxFrame) and (not pack.dstAddr == bcAddr):
+              ifName,ifProto,pktType,hwType,hwAddr = sock.getsockname()
+              devices.append (Ether8888Device (ui, ifName, ifProto, pack.srcAddr, "Vonets VAP11G wireless bridge"));
+        for s in sockets:
+          s.close ();
+
+      except socket.error, e:
+        ui.progress ("")
+        ui.progress ("WARNING: Not running with root privileges, the following devices will NOT be found:")
+        for i in self.list_supported ():
+          ui.progress ("\t-) %s" % i)
+        ui.progress ("")
+        pass
+      return devices;
+
+device_factories.append (Ether8888DeviceFactory());
 
 ## ethernet devices (using ethernet II proto 0x8888), like the VAP11G
-class Ether8888Device (DeviceType):
-    def __init__ (self):
-      DeviceType.__init__ ();
+class Ether8888Device (Device):
+  ifName = ""
+  destAddr = ""
+  srcAddr = ""
+  proto = 0x8888;
+  desc = ""
+  socket = None
+  def __init__ (self, ui, iface, proto, mac, desc):
+    Device.__init__ (self, ui);
+    self.ifName = iface
+    self.mac = mac
+    self.proto = proto
+    self.desc = desc
+    self.srcAddr = None
+    self.destAddr = None
 
-## FTP-based devices like ???
-class FTPDevice (DeviceType):
-    def __init__ (self):
-      DeviceType.__init__ ();
+  def device_string (self):
+    return "%s (Ethernet interface %s, MAC %12s)" % (
+           self.desc, self.ifName, ':'.join('%02x' % ord(b) for b in self.mac));
+
+  def device_open (self):
+    try:
+      s = socket.socket (socket.AF_PACKET, socket.SOCK_RAW, self.proto)
+      s.bind ((self.ifName,self.proto))
+      ifName,ifProto,pktType,hwType,hwAddr = s.getsockname()
+      self.socket = s
+    except socket.error, e:
+      self.error ("Error opening socket for device '%s' on interface '%s'; message: %s" % (self.desc, self.ifName, e.message))
+      s.close ()
+      return False
+    self.ui.progress ("Successfully opened socket for device '%s' on interface '%s'" % (self.desc, self.ifName))
+    return True;
+
+  def device_close (self):
+    if self.socket:
+      self.socket.close ()
+    self.socket = None
+
+
+### FTP-based devices like ???
+#class FTPDevice (Device):
+#    def __init__ (self):
+#      Device.__init__ (self);
diff --git a/appliance_setup/helpers.py b/appliance_setup/helpers.py
index 222811432f33aea5a316f02f0711ec15145673d5..ad1f09be29e54fa6223d20e63122cdee89124f8a 100755
--- a/appliance_setup/helpers.py
+++ b/appliance_setup/helpers.py
@@ -15,13 +15,13 @@ def is_ip_address (ip):
 
 def security2string (sec):
   return {
-    0 => "None",
-    1 => "WEP",
-    2 => "WPA-PSK",
-    4 => "WPA2-PSK", # WPA2-PSK TKIP
-    6 => "WPA/WPA2-PSK", # WPA/WPA2 and WPA2-PSK CCMP
-    7 => "WPA-EAP",
-    8 => "WPA2-EAP"
+    0: "None",
+    1: "WEP",
+    2: "WPA-PSK",
+    4: "WPA2-PSK", # WPA2-PSK TKIP
+    6: "WPA/WPA2-PSK", # WPA/WPA2 and WPA2-PSK CCMP
+    7: "WPA-EAP",
+    8: "WPA2-EAP"
   }.get (sec, "Unknown (%d)" % sec)
 
 def encryption2string (enc):
@@ -31,10 +31,10 @@ def authentication2string (auth):
   return {1: "Open System", 2: "Shared Key"}.get (auth, "Unknown (%d)" % auth)
 
 def is_wep_key (key):
-  return (re.match("[0-9a-fA-F]{10}$", key) || re.match('[0-9a-fA-F]{26}$', key))
+  return (re.match("[0-9a-fA-F]{10}$", key) or re.match('[0-9a-fA-F]{26}$', key))
 
 def is_wpa_passphrase (passphrase):
-  return (0 < len (passphrase) && len (passphrase) < 64)
+  return (0 < len (passphrase) and len (passphrase) < 64)
 
 def is_filename (fn):
   return (len(fn)>0)
diff --git a/appliance_setup/user_interface.py b/appliance_setup/user_interface.py
index 52d397867fae131460adf6c82f2741788035fa89..817bedd49721a42be0a3addbe82b7788770d291c 100755
--- a/appliance_setup/user_interface.py
+++ b/appliance_setup/user_interface.py
@@ -7,110 +7,145 @@
 ## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>
 ## License: GPL v2 or later
 
+import re;
+
 class UserInterface:
-    def __init__ (self, debug):
-      self.debug = debug;
-    def progress (self, str):
-      pass
-    def debug (self, str):
-      pass
-    def input (self, str):
-      pass
-    ## Sleep for dur seconds, show some kind of progress bar / dots
-    def sleep_progress (self, dur):
-      pass
-    ## Give the user a number of choices to select from.
-    ##   text ...    text to print before the options
-    ##   options ... array ref containing human-readable option values (-1 for separator)
-    ##   values ...  array ref with option values (-1 in $options are ignored!)
-    ##   default ... default value if empty input is given
-    def ask_choice (self, text, options, values, default):
-      pass
-    # Ask the user for some text input, which will be validated (if invalid input
-    # is given, the user is notified and asked again.
-    #     text ...      text to ask the user
-    #     validator ... callback function to validate the input
-    #     message ...   error message printed if validation fails.
-    def ask_input (self, text, validator, message):
-      pass
-    def display_device_settings (self):
-      pass
-    def print_wireless_networks (self):
-      pass
-    def ask_operation (self):
-      pass
-    def ask_ip_address (self, string):
-      pass
-    def ask_wep_key (self, ap):
-      pass
+  is_debug = False
+  def __init__ (self, debug):
+    self.is_debug = debug;
+  def progress (self, str):
+    pass
+  def debug (self, str):
+    pass
+  def error (self, str):
+    pass
+  def input (self, str):
+    pass
+  ## Sleep for dur seconds, show some kind of progress bar / dots
+  def sleep_progress (self, dur):
+    pass
+  ## Give the user a number of choices to select from.
+  ##   text ...    text to print before the options
+  ##   options ... array ref containing human-readable option values (-1 for separator)
+  ##   values ...  array ref with option values (-1 in $options are ignored!)
+  ##   default ... default value if empty input is given
+  def ask_choice (self, text, options, values, default):
+    pass
+  # Ask the user for some text input, which will be validated (if invalid input
+  # is given, the user is notified and asked again.
+  #     text ...      text to ask the user
+  #     validator ... callback function to validate the input
+  #     message ...   error message printed if validation fails.
+  def ask_input (self, text, validator, message):
+    pass
+  def display_device_settings (self):
+    pass
+  def print_wireless_networks (self):
+    pass
+  def ask_operation (self):
+    pass
+  def ask_ip_address (self, string):
+    pass
+  def ask_wep_key (self, ap):
+    pass
+  def hex_print (self, data):
+    pass
 
 class ConsoleInterface (UserInterface):
-    def __init__ (self, debug):
-      UserInterface.__init__ (debug);
-    def progress (self, str):
+  def __init__ (self, debug):
+    UserInterface.__init__ (self, debug);
+  def progress (self, str):
+    print str;
+  def debug (self, str):
+    if (self.debug):
       print str;
-    def debug (self, str):
-      if (self.debug):
-        print str;
-    def input (self, str):
-      s = raw_input (str).strip ();
-      return s
+  def error (self, str):
+    print "ERROR " + str;
+  def hex_print (self, data):
+    i = 0;
+    readable=""
+    line = ""
+    for c in data:
+      line += ("%02x " % ord(c))
+      readable += c;
+      i += 1
+      if (i % 8 == 0):
+        line += " "
+      if (i == 16):
+        line += ("  %s" % readable)
+        print (line)
+        i = 0
+        readable = ""
+        line = ""
+    line += ("  %s" % readable)
+    print (line)
+
+  def input (self, str):
+    s = raw_input (str).strip ();
+    return s
 
-    def sleep_progress (self, dur):
-      for i in range(dur):
-        progress "."
-        sleep (1);
-      progress "\n"
+  def sleep_progress (self, dur):
+    for i in range(dur):
+      progress (".")
+      sleep (1)
+    progress ("\n")
 
-    def ask_choice (self, text, options, values, default):
-      print "%s\n" % text
-      i = 1
-      defindex = 1
-      for o in options:
-        if (o == -1):
-          print "\t---\n"
-        else:
-          print "\t%d) %s\n" % (i, o)
-          if (values[i-1] == default):
-            defindex = i
-          i += 1
-      while (retval == undefined):
-        op = self.input ("Your choice (default: %d): " % i);
-        if (op == ""):
-          retval = default;
-        elif (re.match ('[1-9][0-9]*$', op) && ($op <= len (values))):
-          retval = values.get (op-1);
-        else:
-          print "\tInvalid input. Please enter a number from 1 to %d, or simply press Return to use the default.\n" % len(values);
-      return retval;
+  def ask_choice (self, text, options, values, default):
+    print text
+    i = 1
+    defindex = 1
+    for o in options:
+      if (o == -1):
+        print "\t---"
+      else:
+        print "\t%d) %s" % (i, o)
+        if (values[i-1] == default):
+          defindex = i
+        i += 1
+    print ("")
+    retval = None
+    while (retval == None):
+      op = self.input ("Your choice (default: %d): " % defindex);
+      index = None;
+      try:
+        index = int (op);
+      except ValueError:
+        pass
+      if (op == ""):
+        retval = default;
+      elif (index != None and index <= len (values)):
+        retval = values[index-1];
+      else:
+        print "\tInvalid input. Please enter a number from 1 to %d, or simply press Return to use the default." % len(values);
+    return retval;
 
-    # Ask the user for some text input, which will be validated (if invalid input
-    # is given, the user is notified and asked again.
-    #     text ...      text to ask the user
-    #     validator ... callback function to validate the input
-    #     message ...   error message printed if validation fails.
-    def ask_input (self, text, validator, message):
-      retval = ""
-      valid = False
-      while (!valid):
-        retval = self.input (text)
-        valid = validator (retval)
-        if (!valid):
-          print message % retval
-      return retval
+  # Ask the user for some text input, which will be validated (if invalid input
+  # is given, the user is notified and asked again.
+  #     text ...      text to ask the user
+  #     validator ... callback function to validate the input
+  #     message ...   error message printed if validation fails.
+  def ask_input (self, text, validator, message):
+    retval = ""
+    valid = False
+    while (not valid):
+      retval = self.input (text)
+      valid = validator (retval)
+      if (not valid):
+        print message % retval
+    return retval
 
-    def display_device_settings (self):
-      pass
-    def print_wireless_networks (self):
-      pass
-    def ask_operation (self):
-      pass
+  def display_device_settings (self):
+    pass
+  def print_wireless_networks (self):
+    pass
+  def ask_operation (self):
+    pass
 
-    def ask_ip_address (self, string):
-      return self.ask_input (string, self.is_ip_address, "\tInvalid input %s. Please enter a string of the form xxx.xxx.xxx.xxx, where each xxx is a number from 0 to 255.\n")
+  def ask_ip_address (self, string):
+    return self.ask_input (string, self.is_ip_address, "\tInvalid input %s. Please enter a string of the form xxx.xxx.xxx.xxx, where each xxx is a number from 0 to 255.\n")
 
-    def ask_wep_key (self, ap):
-      return self.ask_input (
-        "Please enter the WEP key (10 or 26 characters, 0-9 and a-f) for network '%s': " % ap,
-        helpers.is_wep_key,
-        "\tInvalid key %s. Length can only be 10 or 26, use only digits 0-9 and letters a-f.\n\n")
+  def ask_wep_key (self, ap):
+    return self.ask_input (
+      "Please enter the WEP key (10 or 26 characters, 0-9 and a-f) for network '%s': " % ap,
+      helpers.is_wep_key,
+      "\tInvalid key %s. Length can only be 10 or 26, use only digits 0-9 and letters a-f.\n\n")