diff --git a/.gitignore b/.gitignore
index df8c4f952c9bc501a30687288d53c3588f2eb0e9..d1ca9460850b3536596df778b2f460f938d6ac8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 *~
-build/
\ No newline at end of file
+build/
+*.pyc
diff --git a/appliance_setup/appliance_setup.py b/appliance_setup/appliance_setup.py
index 542b957362577ab7986f44a30ec39a031cc87557..360dfa05cbfe3b0daa0bc01811d42bd7b0547893 100755
--- a/appliance_setup/appliance_setup.py
+++ b/appliance_setup/appliance_setup.py
@@ -27,17 +27,19 @@
 ## over TCP/UDP. On Windows it creates a virtual USB port, so for Linux
 ## support, we'll need a proper kernel module doing the USB port forwarding...
 
-import device_types
+from device_types import *
 from user_interface import *
-import helpers
-import configuration
+from helpers import *
+from configuration import *
 
 #use Data::Dumper;
 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";
+print """\
+appliance_setup.py - version %.2f
+Linux configuration utility for wireless appliances
+(C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>";
+License: GPL v2 or later
+""" % (VERSION);
 
 
 
@@ -47,7 +49,7 @@ VERSION=0.01;
 
 def ask_device (ui):
   devices = [];
-  for f in device_types.device_factories:
+  for f in device_factories:
     devices.extend (f.detect (ui));
 
   dev = None;
@@ -58,12 +60,12 @@ def ask_device (ui):
     dev = devices[0];
 
   if dev:
-    ui.progress ("Using device: %s" % dev.device_string ());
+    ui.progress ("Using device: \n\n\t%s\n" % 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:
+    for f in device_factories:
       devices.extend (["\t-) %s" % d for d in f.list_supported ()])
     errmsg += "\n".join( devices )
     ui.progress (errmsg)
@@ -72,121 +74,6 @@ def ask_device (ui):
 
 
 """
-###############################################################################
-##  USB DEVICE MANAGEMENT / COMMUNICATION FUNCTIONS
-###############################################################################
-
-
-
-sub device_reset ($) {
-  my $dev = shift;
-  progress "Resetting the device, please wait";
-  my $res = bulk_data_write ($dev, "9002:\n", 6);
-  debug "\tSent 9002 reset command: %d\n", $res;
-  my $reset_done = 0;
-  # Poll the device until it doesn't respond. Then sleep for ~15 seconds
-  do {
-    my $readdata = "";
-    $res = $dev->bulk_read (2, $readdata, 13, 100);
-    debug "\tData received: $readdata (len: $res)\n";
-    sleep (1);
-  } while ($res > 0);
-  sleep_progress (20);
-  device_close ($dev);
-  progress "Device was reset. Please press Return.";
-  <STDIN>;
-}
-
-
-###############################################################################
-##  DEVICE CONFIGURATION HANDLING
-###############################################################################
-
-
-
-sub device_config_request ($) { 
-  my $dev = shift;
-  my $cfg = "";
-  debug "Reading current configuration...\n";
-  my $res = bulk_data_write ($dev, "9100:\n", 6);
-  $res = bulk_data_read ($dev, $cfg, 25600);
-  return $cfg;
-}
-
-sub device_config_send ($$) {
-  my $dev = shift;
-  my $cfg = shift;
-  my $tmp = "";
-  progress "Sending new configuation to the device. Please wait";
-  debug "\tNew device config: %s\n", $cfg;
-  my $res = bulk_data_write ($dev, $cfg, length($cfg));
-  sleep_progress (10);
-  # Now reset the device:
-  return device_reset ($dev);
-}
-
-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], ...]
-# into a string to be sent to the device
-sub config_create ($) {
-  my $cfg = shift;
-  my $result = "";
-  foreach my $o (@$cfg) {
-    $result .= sprintf ("%s %s:%s\n", $o->[0], $o->[1], $o->[2]);
-  }
-  return $result;
-}
-
-sub set_hostname ($) {
-  my $dev = shift;
-  my $hostname = ask_input ("New hostname: ", \&is_hostname,
-      "\tA hostname can only contain A-Z, a-z, 0-9 and -, and may have 1 to 64 characters\n");;
-  my $config = config_create ([["0001", "BOX_NAME", $hostname]]);
-  debug "\tChanging hostname using config\n\t%s\n", $config;
-  return device_config_send ($dev, $config);
-}
-
-sub device_config_save ($$) {
-  my $filename = shift;
-  my $config = shift;
-  my $status = open FILE, ">$filename";
-  if (!$status) {
-    printf "ERROR: %s\n", $!;
-    return;
-  }
-  print FILE $config;
-  progress "Current print server configuration saved to file '%s'\n", $filename;
-}
-
-sub device_config_restore ($$) {
-  my $dev = shift;
-  my $filename = shift;
-  my $status = open FILE, "<$filename";
-  if (!$status) {
-    printf "ERROR: %s\n", $!;
-    return;
-  }
-  my $config = do { local $/; <FILE> };
-  debug "Configuration file '%s' contains the data:\n", $filename, $config;
-  progress "Loaded configuration data from file '%s', sending it to the device.\n";
-  return device_config_send ($dev, $config);
-}
-
-
-
-
-
 
 ###############################################################################
 ##  WIRELESS NETWORK DETECTION
@@ -347,36 +234,36 @@ sub wifi_ask_security ($$) {
 sub wifi_config_create ($) {
   my $newconfig = shift;
   my $config = [
-    ["4000", "IP_ADDR",   $newconfig->{'ip_addr'}],  # IP-address
-    ["4001", "GATEWAY",   $newconfig->{'gateway'}],
-    ["4002", "MASK   ",   $newconfig->{'mask'}],
-    ["4020", "DHCP_MODE", $newconfig->{'dhcp'}],
-    ["4021", "",          $newconfig->{'dhcp'}], # Unknown, == 4020
-    ["4022", "",          $newconfig->{'dhcp'}], # Unknown, == 4020
-    ["5000", "",          ""], # Unknown, always empty
-    ["5003", "",          ""], # Unknown, always empty
-    ["5101", "",          ""], # Unknown, always empty
-    ["5102", "",          ""], # Unknown, always empty
-    ["5104", "",          ""], # Unknown, always empty
-    ["5103", "",          ""], # Unknown, always empty
-    ["7000", "SSID",      $newconfig->{'SSID'}],
-    ["7001", "DOMAIN",    ""],
-    ["7002", "CHANNEL",   $newconfig->{'channel'}],
-    ["7003", "SECMODE",   $newconfig->{'secmode'}],
-    ["7004", "KEY_LEN",   $newconfig->{'key_len'}],
-    ["7005", "DEFAULTKEY","0"],
-    ["7006", "KEY0",      $newconfig->{'key'}],
-    ["7007", "KEY1",      ""],
-    ["7008", "KEY2",      ""],
-    ["7009", "KEY3",      ""],
-    ["7012", "AUTHEN",    $newconfig->{'authen'}],
-    ["7013", "MODE",      "0"],
-    ["7018", "PSKALSET",  $newconfig->{'pskalset'}],
-    ["7019", "PSKKEY",    $newconfig->{'pskkey'}],
-    ["7024", "STAMODE",   "0"],
-    ["7025", "APCHANNEL", "5"],
-    ["7026", "CHMODE",    "1"],
-    ["7030", "WLMODE",    "0"],
+    ("4000", "IP_ADDR",   $newconfig->{'ip_addr'}),  # IP-address
+    ("4001", "GATEWAY",   $newconfig->{'gateway'}),
+    ("4002", "MASK   ",   $newconfig->{'mask'}),
+    ("4020", "DHCP_MODE", $newconfig->{'dhcp'}),
+    ("4021", "",          $newconfig->{'dhcp'}), # Unknown, == 4020
+    ("4022", "",          $newconfig->{'dhcp'}), # Unknown, == 4020
+    ("5000", "",          ""), # Unknown, always empty
+    ("5003", "",          ""), # Unknown, always empty
+    ("5101", "",          ""), # Unknown, always empty
+    ("5102", "",          ""), # Unknown, always empty
+    ("5104", "",          ""), # Unknown, always empty
+    ("5103", "",          ""), # Unknown, always empty
+    ("7000", "SSID",      $newconfig->{'SSID'}),
+    ("7001", "DOMAIN",    ""),
+    ("7002", "CHANNEL",   $newconfig->{'channel'}),
+    ("7003", "SECMODE",   $newconfig->{'secmode'}),
+    ("7004", "KEY_LEN",   $newconfig->{'key_len'}),
+    ("7005", "DEFAULTKEY","0"),
+    ("7006", "KEY0",      $newconfig->{'key'}),
+    ("7007", "KEY1",      ""),
+    ("7008", "KEY2",      ""),
+    ("7009", "KEY3",      ""),
+    ("7012", "AUTHEN",    $newconfig->{'authen'}),
+    ("7013", "MODE",      "0"),
+    ("7018", "PSKALSET",  $newconfig->{'pskalset'}),
+    ("7019", "PSKKEY",    $newconfig->{'pskkey'}),
+    ("7024", "STAMODE",   "0"),
+    ("7025", "APCHANNEL", "5"),
+    ("7026", "CHMODE",    "1"),
+    ("7030", "WLMODE",    "0"),
   ];
   return config_create ($config);
 }
@@ -448,19 +335,24 @@ sub adhoc_config_create ($) {
   return config_create ($config);
 }
 
-
+"""
 
 ###############################################################################
 ##  MAIN OPERATIONS: ASK FOR OPERATION AND HANDLE IT
 ###############################################################################
 
 
-sub ask_operation () {
-  return ask_choice (
-    "What do you want to do?", 
-    ["Setup a wireless connection through a wifi router or access point ('infrastructure')",
-     "Setup a direct wireless connection with the printer ('ad-hoc')",
-     -1,
+def ask_operation (ui, dev):
+  # build the list of possible operations
+  options = []
+  optionstrings = []
+  if (dev.capabilities['wifi']):
+    optionstrings.append ("Setup a wireless connection through a wifi router or access point ('infrastructure')")
+    options.append ("wifi_infrastructure")
+  if (dev.capabilities['ad-hoc']):
+    optionstrings.append ("Setup a direct wireless connection with the printer ('ad-hoc')")
+    options.append ("wifi_adhoc")
+  optionstrings += [-1,
      "Change the name of the device",
      -1,
      "Save the device configuration to a file",
@@ -468,37 +360,38 @@ sub ask_operation () {
      -1,
      "Reload the configuration",
      "Exit"
-    ],
-    ["wifi_infrastructure",
-     "wifi_adhoc",
-     "change_hostname",
+    ]
+  options += ["change_hostname",
      "config_save",
      "config_restore",
      "reload",
      "exit"
-    ],
-    "exit");
-}
-"""
+    ]
+
+  return ui.ask_choice ("What do you want to do?", optionstrings, options, "exit")
+
 
 
 def main ():
   reconfigured = False
   operation = ""
-  UI = ConsoleInterface (True)
+  UI = ConsoleInterface (debug=True)
   
   dev = ask_device (UI)
   if (not dev or not dev.device_open ()):
     return
 
+  ## get the config and print the current configuration
   cfg = dev.config_retrieve ()
-  #my @config = config_parse ($cfg);
-  #print_current_configuration (\@config, "Current configuration of the device:\n");
+  config = Config (UI, cfgstring=cfg)
+  UI.progress (config.print_configuration ("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") {
+  operation = ask_operation (UI, dev)
+  UI.debug ("\tSelected operation: %s\n" % operation)
+  if (operation == "wifi_infrastructure"):
     ## Infrastructure, scan for APs
+    # TODO
     #my ($aps,$newap) = wifi_ask_ssid ($dev);
     #my %newconfig = ('SSID' => $newap);
     #debug ("\tSelected wireless network: %s\n", $newap);
@@ -507,9 +400,10 @@ def main ():
     #%newconfig = (%newconfig, %security, %ip);
     #my $newcfg = wifi_config_create (\%newconfig);
     #device_config_send ($dev, $newcfg);
-    #$reconfigured = 1;
-  #} elsif ($operation eq "wifi_adhoc") {
+    reconfigured = True
+  elif (operation == "wifi_adhoc"):
     ## Ad-hoc connection to print server
+    # TODO
     #my $adhocssid = adhoc_ask_ssid ($dev);
     #adhoc_send_unknown_command ($dev);
     #my $channel = adhoc_ask_channel ($dev, $adhocssid);
@@ -519,36 +413,41 @@ def main ():
                     #);
     #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 ()
-#}
-
+    reconfigured = True
+  elif (operation == "change_hostname"):
+    hostname = UI.ask_input ("New hostname: ", is_hostname,
+      "\tA hostname can only contain A-Z, a-z, 0-9 and -, and may have 1 to 64 characters; you entered '%s'\n")
+    dev.set_hostname (hostname)
+    reconfigured = True
+  elif (operation == "config_save"):
+    filename = UI.ask_input ("Filename to save to: ", is_filename, "")
+    if filename:
+      config.save (filename)
+  elif (operation == "config_restore"):
+    filename = UI.ask_input ("Filename to load from: ", is_filename, "")
+    newconfig = Config (UI)
+    newconfig.load (filename)
+    newconfig.remove_readonly ()
+    dev.config_send (newconfig)
+    reconfigured = True
+  elif (operation == "reload"):
+    ## do nothing, just call main...
+    return main ()
+  elif (operation == "exit"):
+    dev.device_close ()
+    exit
+  else:
+    UI.error ("unknown operation %s\n" % operation)
+
+  dev.device_close ()
+  # if the configuration was changed, print the new config after the device was reset
+  if reconfigured:
+    dev = ask_device(UI)
+    if (not dev or not dev.device_open ()):
+      return
+    cfg = dev.config_retrieve ()
+    config = Config (UI, cfgstring=cfg)
+    UI.progress (config.print_configuration ("New configuration of the device:\n"))
 
 
 
diff --git a/appliance_setup/configuration.py b/appliance_setup/configuration.py
index 30bd5cfb2e5a7e7926279f48b81ba9451224d020..b118a58ae0dd101192a249d9fd2d35ab6804fea6 100644
--- a/appliance_setup/configuration.py
+++ b/appliance_setup/configuration.py
@@ -6,22 +6,41 @@
 ## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>
 ## License: GPL v2 or later
 
+import re
+from helpers import *
 
 class Config:
-  cfgdata = [];
-  cfgdesc = [];
-  def __init__ (self, ui):
+  cfgdata = []
+  cfgstring = None
+  # Readonly entries are: 7014 (LINKINFO)
+  readonly = ['7014']
+  # initialize config with string or list; If both given, list takes precedence
+  def __init__ (self, ui, cfgstring = '', cfg = []):
     self.ui = ui
+    if (len(cfg) > 0):
+      self.cfgdata = cfg
+    else:
+      self.cfgstring = cfgstring
+      self.config_parse (self.cfgstring)
+
+  def get (self, line):
+    return find (lambda d: d[0] == line, self.cfgdata)
 
   def val (self, line):
-    return self.cfgdata.get (line, None)
+    e = self.get (line)
+    return e[2] if (isinstance(e,(list,tuple)) and len(e)>2) else '';
+
+  def set_config (self, cfg):
+    self.cfgdata = cfg
 
-  def print_configuration (self):
-    # TODO: ui.progress (text);
-    res = "\tDevice name:   %(0001)s\n" % self.cfgdata;
+  def print_configuration (self, text):
+    #print self.cfgstring
+    ##print self.cfgdata
+    #print self.val('0012')
+    res = text + "\tDevice name:   %s\n" % self.val('0001')
     if (self.val('0012') != "Enable"):
       res += "\tTCP/IP not yet configured and/or enabled.\n\n"
-      return
+      return res
     adhoc = (self.val('7024') == '1')
     if adhoc:
       # ad-hoc network:
@@ -43,9 +62,9 @@ class Config:
     else:
       # infrastructure network
       sec = self.val('7003');
-      res += "\tSSID:          %s\n", self.val('7000')
-      res += "\tChannel:       %s\n", self.val('7002')
-      res += "\tSecurity:      %s\n", security2string (sec)
+      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
         pass
@@ -74,73 +93,61 @@ class Config:
     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:
+    if not data:
+      return
+    self.cfgstring = data
+    self.cfgdata = []
+    entries = data.split ('\n')
+    entry_re = re.compile ("^([0-9]{4}) ([A-Z_0-9 ]+):(.*)$");
+    for l in filter (None, 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
+      if m:
+        self.cfgdata.append (m.group (1, 2, 3))
+      else:
+        print "Config line not in correct format:\n\t%s" % l
+
+  def to_string (self, cfg=None):
+    res = ""
+    if (not cfg):
+      cfg = self.cfgdata
+    for (l, key, value) in cfg:
+      res += "%s %s:%s\n" % (l, key, value)
+    return res
+    
+  def save (self, filename):
+    try:
+      f = open(filename, 'w')
+      f.write (self.to_string ())
+      f.close ()
+      self.ui.progress ("Current print server configuration saved to file '%s'\n" % filename)
+    except Exception,e:
+      self.ui.error (e.message)
+
+  def load (self, filename):
+    try:
+      f = open(filename, 'r')
+      self.cfgstring = f.read ()
+      f.close ()
+      self.ui.debug ("Configuration file '%s' contains the data:\n%s" % (filename, self.cfgstring))
+      self.config_parse (self.cfgstring)
+      self.ui.progress ("Loaded configuration data from file '%s', sending it to the device.\n" % filename);
+    except Exception,e:
+      self.ui.error (e.message)
+  
+  ## Remove all entries that are read-only, and cannot be changed on the device
+  def remove_readonly (self):
+    self.cfgdata = filter (lambda d: d[0] not in self.readonly, self.cfgdata)
 
 
-###############################################################################
-##  DEVICE CONFIGURATION HANDLING
-###############################################################################
 
-"""
-sub config_parse ($) {
-  my $cfg = shift;
-}
 
-# Convert a list of the form [ ["OPTIONID", "OPTIONNAME", VALUE], ...]
-# into a string to be sent to the device
-sub config_create ($) {
-  my $cfg = shift;
-  my $result = "";
-  foreach my $o (@$cfg) {
-    $result .= sprintf ("%s %s:%s\n", $o->[0], $o->[1], $o->[2]);
-  }
-  return $result;
-}
 
-sub set_hostname ($) {
-  my $dev = shift;
-  my $hostname = ask_input ("New hostname: ", \&is_hostname,
-      "\tA hostname can only contain A-Z, a-z, 0-9 and -, and may have 1 to 64 characters\n");;
-  my $config = config_create ([["0001", "BOX_NAME", $hostname]]);
-  debug "\tChanging hostname using config\n\t%s\n", $config;
-  return device_config_send ($dev, $config);
-}
 
-sub device_config_save ($$) {
-  my $filename = shift;
-  my $config = shift;
-  my $status = open FILE, ">$filename";
-  if (!$status) {
-    printf "ERROR: %s\n", $!;
-    return;
-  }
-  print FILE $config;
-  progress "Current print server configuration saved to file '%s'\n", $filename;
-}
+###############################################################################
+##  DEVICE CONFIGURATION HANDLING
+###############################################################################
 
-sub device_config_restore ($$) {
-  my $dev = shift;
-  my $filename = shift;
-  my $status = open FILE, "<$filename";
-  if (!$status) {
-    printf "ERROR: %s\n", $!;
-    return;
-  }
-  my $config = do { local $/; <FILE> };
-  debug "Configuration file '%s' contains the data:\n", $filename, $config;
-  progress "Loaded configuration data from file '%s', sending it to the device.\n";
-  return device_config_send ($dev, $config);
-}
+"""
 
 
 
@@ -407,105 +414,4 @@ sub adhoc_config_create ($) {
   return config_create ($config);
 }
 
-
-
-###############################################################################
-##  MAIN OPERATIONS: ASK FOR OPERATION AND HANDLE IT
-###############################################################################
-
-
-sub ask_operation () {
-  return ask_choice (
-    "What do you want to do?", 
-    ["Setup a wireless connection through a wifi router or access point ('infrastructure')",
-     "Setup a direct wireless connection with the printer ('ad-hoc')",
-     -1,
-     "Change the name of the device",
-     -1,
-     "Save the device configuration to a file",
-     "Restore the device configuration from a file",
-     -1,
-     "Reload the configuration",
-     "Exit"
-    ],
-    ["wifi_infrastructure",
-     "wifi_adhoc",
-     "change_hostname",
-     "config_save",
-     "config_restore",
-     "reload",
-     "exit"
-    ],
-    "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");
-  
-  # Loop in main if "reload" is selected...
-  main () if ($operation eq "reload");
-}
-
-
-
-
-###############################################################################
-##  MAIN FUNCTION
-###############################################################################
-
-main ();
 """
\ No newline at end of file
diff --git a/appliance_setup/device.py b/appliance_setup/device.py
index 18fd6f393aea768d9726e74750db30b2c8aaa3f7..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/appliance_setup/device.py
+++ b/appliance_setup/device.py
@@ -1,2 +0,0 @@
-class Device:
-    def __init__ (
\ No newline at end of file
diff --git a/appliance_setup/device_types.py b/appliance_setup/device_types.py
index 0c5bd2c3d16a8e79702837db0110b22daac1b491..6a21e9870cde6372a2ad44f3e70ee430f6fafd80 100755
--- a/appliance_setup/device_types.py
+++ b/appliance_setup/device_types.py
@@ -7,6 +7,9 @@
 ## License: GPL v2 or later
 
 import usb
+import time
+from helpers import *
+from configuration import *
 
 device_factories = [];
 
@@ -21,9 +24,10 @@ class DeviceFactory:
 ## Base class
 class Device:
     ui = None
-    capabilities = {'wifi': False, 'ad-hoc': False}
+    capabilities = {}
     def __init__ (self, ui):
       self.ui = ui
+      self.capabilities = {'wifi': False, 'ad-hoc': False}
 
     def device_string (self):
       pass
@@ -47,6 +51,11 @@ class Device:
       pass
     def error (self, msg):
       self.ui.error (msg)
+    def set_hostname (self, hostname):
+      config = Config (self.ui, cfg=[('0001', 'BOX_NAME', hostname)]);
+      self.ui.debug ("\tChanging hostname using config\n\t%s\n" % config.to_string ())
+      return self.config_send (config)
+
 
 class USBDeviceFactory (DeviceFactory):
     supported_devices = (
@@ -79,8 +88,11 @@ class USBDevice (Device):
   bus = ""
   handle = None
   thisboxname = "XXXXXXXX"
+  boxname = ""
   def __init__ (self, ui, dev, bus, desc):
     Device.__init__ (self, ui)
+    self.capabilities['wifi'] = True
+    self.capabilities['ad-hoc'] = True
     self.dev = dev
     self.desc = desc
     self.bus = bus
@@ -112,17 +124,46 @@ class USBDevice (Device):
     return self.handle;
  
   def device_close (self):
-    self.handle.releaseInterface(0)
-    self.handle.reset ()
+    if (self.handle):
+      self.handle.releaseInterface()
+      self.handle.reset ()
     self.handle = None
-    
+
   def config_retrieve (self):
-    self.ui.debug ("Reading current configuration...\n");
+    self.ui.debug ("Reading current configuration...");
     cfg = ""
     res = self.bulk_data_write ("9100:\n", 6)
-    #res = bulk_data_read ($cfg, 25600);
-    #return $cfg;
-
+    return self.bulk_data_read (25600)
+
+  def config_send (self, config):
+    self.ui.progress ("Sending new configuation to the device. Please wait")
+    cfgstring = config.to_string ()
+    self.ui.debug ("\tNew device config: %s\n" % cfgstring)
+
+    self.bulk_data_write (cfgstring, len(cfgstring))
+    self.ui.sleep_progress (10)
+    # Now reset the device:
+    return self.device_reset ()
+
+  def device_reset (self):
+    self.ui.progress ("Resetting the device, please wait")
+    res = self.bulk_data_write ("9002:\n", 6)
+    self.ui.debug ("\tSent 9002 reset command: %d" % res)
+    # Poll the device until it doesn't respond. Then sleep for ~15 seconds
+    try:
+      i=0
+      while (i < 15):
+        readdata = self.handle.bulkRead (2, 13)
+        res = len(readdata)
+        self.ui.debug ("\tData received: %s (len: %s)" % (readdata, res) )
+        time.sleep (1)
+        i += 1
+    except usb.USBError,e:
+      self.ui.debug (e.message)
+
+    self.ui.sleep_progress (20)
+    self.device_close ()
+    self.ui.wait_enter ("Device was reset. Please press Return.")
 
   def bulk_data_write (self, data, length):
     # Add the header (12 bytes): '@\0', packet length, boxname (="XXXXXXXX")
@@ -135,41 +176,43 @@ class USBDevice (Device):
       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 ($$$):
+  # Read and return USB bulk data
+  def bulk_data_read (self, length, timeout=500):
     # 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); 
+    try:
+      res = self.handle.bulkRead (2, length+13, timeout);
+    except Exception, e:
+      self.error ("reading bulk data; message: %s" % e.message)
+      return None
+
+    # bulkRead returns a tuple, convert to a binary string
+    res = ''.join(chr(v) for v in res)
+    if (len (res) == 0):
+      self.ui.debug ("\tEmpty response received\n")
+      return ''
+
+    self.ui.debug ("read %d bytes: \n" % len (res))
+    self.ui.hex_print (res)
     # 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);
+    if (not res.startswith ("@\0\0")):
+      self.error ("reading data: Wrong header %s" % res[0:3])
+    (datalen,) = struct.unpack ("H", res[3:5]);
+    realdatalen = len (res)-13
+    if (datalen != realdatalen):
+      self.error ("reading data: Expected %d bytes of data, got %d\n" % (datalen, realdatalen))
+    self.boxname = res[5:13]
     ## FIXME: Check the name of the box...
-  
     ## cut off the header:
-    $_[0]=substr ($readdata, 13);
-    return $res;
+    return res[13:]
+
+
 
 
 
+####################################################################################
+#   Ethernet II proto 8888 devices (e.g. VAP11G)                                 ###
+####################################################################################
+
 
 import select
 import socket
@@ -178,16 +221,40 @@ import subprocess
 import re
 
 class Ether8888Packet:
-  def __init__ (self):
-    pass
+  ethHeaderFmt = "!6s6sH"
+  headerFmt = "<2s2s2sHH"
+  localAddr="\x00\x00\x00\x00\x00\x00"
+  remoteAddr="\xff\xff\xff\xff\xff\xff"
+  proto = 0x8888
+  def __init__ (self, localAddr="\x00\x00\x00\x00\x00\x00", remoteAddr="\xff\xff\xff\xff\xff\xff", proto=0x8888):
+    self.proto = proto
+    self.localAddr = localAddr
+    self.remoteAddr = remoteAddr
+
   def decode (self, data):
-    self.dstAddr, self.srcAddr, self.proto = struct.unpack("!6s6sH",data[:14])
+    self.dstAddr, self.srcAddr, self.proto = struct.unpack(self.ethHeaderFmt,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 (self.headerFmt, 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
+
+  def encode (self, cmd, unknown, nrPackets, dataLen, data):
+    # TODO
+    self.dstAddr, self.srcAddr, self.proto = struct.pack(self.ethHeaderFmt,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])
+        struct.unpack (self.headerFmt, self.ethData[:10])
     self.payload = self.ethData[10:self.dataLen+10]
 
     if (self.dataLen != len (self.payload)):
@@ -255,8 +322,8 @@ device_factories.append (Ether8888DeviceFactory());
 ## ethernet devices (using ethernet II proto 0x8888), like the VAP11G
 class Ether8888Device (Device):
   ifName = ""
-  destAddr = ""
-  srcAddr = ""
+  localAddr = ""
+  remoteAddr = ""
   proto = 0x8888;
   desc = ""
   socket = None
@@ -268,6 +335,8 @@ class Ether8888Device (Device):
     self.desc = desc
     self.srcAddr = None
     self.destAddr = None
+    self.capabilities['wifi'] = True;
+    #self.capabilities['ad-hoc'] = False;
 
   def device_string (self):
     return "%s (Ethernet interface %s, MAC %12s)" % (
diff --git a/appliance_setup/helpers.py b/appliance_setup/helpers.py
index ad1f09be29e54fa6223d20e63122cdee89124f8a..0e26a92bfdd4b48e92077b5daae7877b70112350 100755
--- a/appliance_setup/helpers.py
+++ b/appliance_setup/helpers.py
@@ -6,6 +6,7 @@
 ## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>
 ## License: GPL v2 or later
 
+import re
 
 def trim (str):
   return str.strip ()
@@ -15,20 +16,20 @@ 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"
-  }.get (sec, "Unknown (%d)" % sec)
+    '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 (%s)" % sec)
 
 def encryption2string (enc):
-  return {0: "TKIP", 1: "AES"}.get (enc, "Unknown (%d)" % enc)
+  return {'0': "TKIP", '1': "AES"}.get (enc, "Unknown (%s)" % enc)
 
 def authentication2string (auth):
-  return {1: "Open System", 2: "Shared Key"}.get (auth, "Unknown (%d)" % auth)
+  return {'1': "Open System", '2': "Shared Key"}.get (auth, "Unknown (%s)" % auth)
 
 def is_wep_key (key):
   return (re.match("[0-9a-fA-F]{10}$", key) or re.match('[0-9a-fA-F]{26}$', key))
@@ -41,3 +42,9 @@ def is_filename (fn):
 
 def is_hostname (hostname):
   return re.match("[-A-Za-z0-9]{1,63}$", hostname)
+
+def find(f, seq):
+  """Return first item in sequence where f(item) == True."""
+  for item in seq:
+    if f(item): 
+      return item
\ No newline at end of file
diff --git a/appliance_setup/user_interface.py b/appliance_setup/user_interface.py
index 817bedd49721a42be0a3addbe82b7788770d291c..8a55de1062c2f9f83b520a4d660977d017d9569d 100755
--- a/appliance_setup/user_interface.py
+++ b/appliance_setup/user_interface.py
@@ -7,13 +7,16 @@
 ## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>
 ## License: GPL v2 or later
 
-import re;
+import re
+import time
+import sys
+from helpers import *
 
 class UserInterface:
   is_debug = False
   def __init__ (self, debug):
     self.is_debug = debug;
-  def progress (self, str):
+  def progress (self, str, newline=True):
     pass
   def debug (self, str):
     pass
@@ -50,35 +53,45 @@ class UserInterface:
     pass
   def hex_print (self, data):
     pass
+  def wait_enter (self, text):
+    pass
 
 class ConsoleInterface (UserInterface):
   def __init__ (self, debug):
     UserInterface.__init__ (self, debug);
-  def progress (self, str):
-    print str;
+  def progress (self, str, newline=True):
+    if newline:
+      print str
+    else:
+      print str,
+    sys.stdout.flush()
   def debug (self, str):
-    if (self.debug):
+    if self.is_debug:
       print str;
+      sys.stdout.flush()
   def error (self, str):
     print "ERROR " + str;
+    sys.stdout.flush()
   def hex_print (self, data):
-    i = 0;
+    if (not self.is_debug): 
+      return
+    i = 0; index = 0;
     readable=""
     line = ""
     for c in data:
       line += ("%02x " % ord(c))
-      readable += c;
+      readable += c if (ord (c) >31 and ord (c) <= 126) else ".";
       i += 1
       if (i % 8 == 0):
         line += " "
-      if (i == 16):
-        line += ("  %s" % readable)
-        print (line)
-        i = 0
+      if (i % 16 == 0):
+        print ("0x%04x  %34s  %16s" % (index, line, readable))
+        index += 0x10;
         readable = ""
         line = ""
-    line += ("  %s" % readable)
-    print (line)
+    if (len (line) > 0):
+      print ("0x%04x  %34s  %16s" % (index, line, readable))
+    sys.stdout.flush()
 
   def input (self, str):
     s = raw_input (str).strip ();
@@ -86,9 +99,10 @@ class ConsoleInterface (UserInterface):
 
   def sleep_progress (self, dur):
     for i in range(dur):
-      progress (".")
-      sleep (1)
-    progress ("\n")
+      sys.stdout.write(".")
+      sys.stdout.flush()
+      time.sleep (1)
+    self.progress ("")
 
   def ask_choice (self, text, options, values, default):
     print text
@@ -149,3 +163,6 @@ class ConsoleInterface (UserInterface):
       "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 wait_enter (self, text):
+    raw_input (text)
\ No newline at end of file