protected DeviceTivo foundTiVo(
      InetAddress address, String uid, String classification, String machine) {
    uid = "tivo:" + uid;

    DeviceImpl[] devices = device_manager.getDevices();

    String server_name = device_manager.getLocalServiceName();

    for (DeviceImpl device : devices) {

      if (device instanceof DeviceTivo) {

        DeviceTivo tivo = (DeviceTivo) device;

        if (device.getID().equals(uid)) {

          if (classification != null) {

            String existing_classification = device.getClassification();

            if (!classification.equals(existing_classification)) {

              device.setPersistentStringProperty(DeviceImpl.PP_REND_CLASSIFICATION, classification);
            }
          }

          tivo.found(this, address, server_name, machine);

          return (tivo);
        }
      }
    }

    // unfortunately we can't deduce the series from the browse request so start off with a series 3
    // this will be corrected later if we receive a beacon which *does* contain the series details

    if (classification == null) {

      classification = "tivo.series3";
    }

    DeviceTivo new_device = new DeviceTivo(device_manager, uid, classification);

    DeviceTivo result = (DeviceTivo) device_manager.addDevice(new_device);

    // possible race here so handle case where device already present

    if (result == new_device) {

      new_device.found(this, address, server_name, machine);
    }

    return (result);
  }
  protected void log(String str) {
    if (device_manager == null) {

      System.out.println(str);

    } else {

      device_manager.log("TiVo: " + str);
    }
  }
  protected void startUp() {
    plugin_interface = PluginInitializer.getDefaultInterface();

    if (COConfigurationManager.getStringParameter("ui").equals("az2")) {

      is_enabled = false;

    } else {

      is_enabled = COConfigurationManager.getBooleanParameter("devices.tivo.enabled", true);
    }

    uid = COConfigurationManager.getStringParameter("devices.tivo.uid", null);

    if (uid == null) {

      byte[] bytes = new byte[8];

      RandomUtils.nextBytes(bytes);

      uid = Base32.encode(bytes);

      COConfigurationManager.setParameter("devices.tivo.uid", uid);
    }

    boolean found_tivo = false;

    for (Device device : device_manager.getDevices()) {

      if (device instanceof DeviceTivo) {

        found_tivo = true;

        break;
      }
    }

    if (found_tivo || device_manager.getAutoSearch()) {

      search(found_tivo, false);
    }
  }
  protected void log(String str, Throwable e) {
    if (device_manager == null) {

      System.out.println(str);

      e.printStackTrace();

    } else {

      device_manager.log("TiVo: " + str, e);
    }
  }
  protected void setEnabled(boolean enabled) {
    COConfigurationManager.setParameter("devices.tivo.enabled", enabled);

    if (enabled) {

      search(false, true);

    } else {

      for (Device device : device_manager.getDevices()) {

        if (device instanceof DeviceTivo) {

          device.remove();
        }
      }
    }
  }
  protected byte[] encodeBeacon(boolean is_broadcast, int my_port) throws IOException {

    String beacon =
        "tivoconnect=1"
            + LF
            + "swversion=1"
            + LF
            + "method="
            + (is_broadcast ? "broadcast" : "connected")
            + LF
            + "identity="
            + uid
            + LF
            + "machine="
            + device_manager.getLocalServiceName()
            + LF
            + "platform=pc"
            + LF
            + "services=TiVoMediaServer:"
            + my_port
            + "/http";

    return (beacon.getBytes("ISO-8859-1"));
  }