diff --git a/lib/wp/factory.c b/lib/wp/factory.c
index f8c71d468731d90d94b05d57375954943b99b05b..7292166379d3fad564ce2e40752851e0ad193f07 100644
--- a/lib/wp/factory.c
+++ b/lib/wp/factory.c
@@ -46,7 +46,7 @@ wp_factory_class_init (WpFactoryClass * klass)
 WpFactory *
 wp_factory_new (WpCore * core, const gchar * name, WpFactoryFunc func)
 {
-  g_autoptr (WpFactory) f = NULL;
+  WpFactory *f = NULL;
 
   g_return_val_if_fail (name != NULL && *name != '\0', NULL);
   g_return_val_if_fail (func != NULL, NULL);
diff --git a/modules/module-pipewire/simple-endpoint-link.c b/modules/module-pipewire/simple-endpoint-link.c
index 93dc160a79a4841aaf35ece03fb6048268b2c3c6..2b9d075846feebee84b3617ff39ec7bd43edb523 100644
--- a/modules/module-pipewire/simple-endpoint-link.c
+++ b/modules/module-pipewire/simple-endpoint-link.c
@@ -44,6 +44,8 @@ simple_endpoint_link_create (WpEndpointLink * self, GVariant * src_data,
     GVariant * sink_data, GError ** error)
 {
   /* TODO create pw_links based on the nodes & ports described in src/sink_data */
+
+  return TRUE;
 }
 
 static void
diff --git a/modules/module-pipewire/simple-endpoint.c b/modules/module-pipewire/simple-endpoint.c
index 61439cdbc6d92c59b75a8624f4d2a0fbe67d0eae..52ce5d4c0a482a365a3271692939a596937ed919 100644
--- a/modules/module-pipewire/simple-endpoint.c
+++ b/modules/module-pipewire/simple-endpoint.c
@@ -95,6 +95,8 @@ simple_endpoint_prepare_link (WpEndpoint * self, guint32 stream_id,
 {
   /* TODO: verify that the remote end supports the same media type */
   /* TODO: fill @properties with (node id, array(port ids)) */
+
+  return TRUE;
 }
 
 static void
diff --git a/modules/module-pw-alsa-udev.c b/modules/module-pw-alsa-udev.c
index 156882d5f5e8aae681f1102344ad36299534696e..6875a8237d1d1d4ada5cc9af62844771bc4ec7bb 100644
--- a/modules/module-pw-alsa-udev.c
+++ b/modules/module-pw-alsa-udev.c
@@ -14,7 +14,143 @@
 #include <wp/wp.h>
 #include <pipewire/pipewire.h>
 
+#define NAME "module-pw-alsa-udev"
+
+struct impl {
+  WpCore *wp_core;
+
+  struct pw_remote *remote;
+  struct spa_hook remote_listener;
+
+  struct pw_registry_proxy *registry_proxy;
+  struct spa_hook registry_listener;
+};
+
+static void
+handle_node(struct impl *impl, uint32_t id, uint32_t parent_id,
+            const struct spa_dict *props)
+{
+  const gchar *media_class = NULL, *node_name = NULL;
+  struct spa_proxy *proxy = NULL;
+  GVariantBuilder b;
+  g_autoptr(GVariant) endpoint_props = NULL;
+
+  /* Make sure the node has properties */
+  if (!props) {
+    g_warning(NAME" %p: node has no properties, skipping...", impl);
+    return;
+  }
+
+  /* Make sure the media class is audio */
+  /* FIXME: need to handle only alsa nodes */
+  media_class = spa_dict_lookup(props, "media.class");
+  if (!g_str_has_prefix (media_class, "Audio/"))
+    return;
+
+  /* Get the device name */
+  node_name = spa_dict_lookup(props, "node.name");
+  
+  /* Get the proxy */
+  proxy = pw_registry_proxy_bind (impl->registry_proxy, id,
+      PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0);
+
+  /* Build the GVariant properties for the endpoint */
+  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&b, "{sv}", "name",
+      g_variant_new_take_string (g_strdup_printf ("Endpoint %u: %s", id, node_name)));
+  g_variant_builder_add (&b, "{sv}",
+      "media-class", g_variant_new_string (media_class));
+  g_variant_builder_add (&b, "{sv}",
+      "node-proxy", g_variant_new_uint64 ((guint64) proxy));
+  endpoint_props = g_variant_builder_end (&b);
+
+  /* Create the endpoint */
+  wp_factory_make (impl->wp_core, "pw-audio-softdsp-endpoint", WP_TYPE_ENDPOINT,
+      endpoint_props);
+}
+
+static void
+registry_global(void *data,uint32_t id, uint32_t parent_id,
+		uint32_t permissions, uint32_t type, uint32_t version,
+		const struct spa_dict *props)
+{
+  struct impl *impl = data;
+
+  /* Only handle nodes */
+  switch (type) {
+  case PW_TYPE_INTERFACE_Node:
+    handle_node(impl, id, parent_id, props);
+    break;
+
+  default:
+    break;
+  }
+}
+
+static const struct pw_registry_proxy_events registry_events = {
+  PW_VERSION_REGISTRY_PROXY_EVENTS,
+  .global = registry_global,
+};
+
+static void on_state_changed(void *_data, enum pw_remote_state old,
+    enum pw_remote_state state, const char *error)
+{
+  struct impl *impl = _data;
+  struct pw_core_proxy *core_proxy;
+
+  switch (state) {
+  case PW_REMOTE_STATE_CONNECTED:
+    core_proxy = pw_remote_get_core_proxy (impl->remote);
+    impl->registry_proxy = pw_core_proxy_get_registry (core_proxy,
+        PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0);
+    pw_registry_proxy_add_listener(impl->registry_proxy,
+        &impl->registry_listener, &registry_events, impl);
+    break;
+  
+  case PW_REMOTE_STATE_ERROR:
+    /* TODO quit wireplumber */
+    break;
+
+  case PW_REMOTE_STATE_UNCONNECTED:
+    /* TODO quit wireplumber */
+    break;
+  
+  default:
+    break;
+  }
+}
+
+static const struct pw_remote_events remote_events = {
+  PW_VERSION_REMOTE_EVENTS,
+  .state_changed = on_state_changed,
+};
+
+static void
+module_destroy (gpointer data)
+{
+  struct impl *impl = data;
+
+  g_debug ("module-pipewire destroy");
+
+  g_slice_free (struct impl, impl);
+}
+
 void
 wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
 {
+  /* This needs to create the alsa sink and alsa source nodes, but since
+   * this is already implemented in the alsa-module of pipewire, for now
+   * we just listen for the alsa nodes created by pipewire. We eventually
+   * need to move all the node creation logic here */
+
+  /* Create the impl */
+  struct impl *impl = g_new0(struct impl, 1);
+  impl->wp_core = core;
+  impl->remote = wp_core_get_global(core, WP_GLOBAL_PW_REMOTE);
+
+  /* Set destroy callback for impl */
+  wp_module_set_destroy_callback (module, module_destroy, impl);
+
+  /* Add a state changed listener */
+  pw_remote_add_listener(impl->remote, &impl->remote_listener, &remote_events, impl);
 }
diff --git a/modules/module-pw-audio-softdsp-endpoint.c b/modules/module-pw-audio-softdsp-endpoint.c
index b2534c0a7c60ab2ec0cc38dbfe6c4267a95e3ebf..802b7a5d601a689034e54e7b15ecdf0160b1eab7 100644
--- a/modules/module-pw-audio-softdsp-endpoint.c
+++ b/modules/module-pw-audio-softdsp-endpoint.c
@@ -35,6 +35,7 @@ static gboolean
 endpoint_prepare_link (WpEndpoint * self, guint32 stream_id,
     WpEndpointLink * link, GVariant ** properties, GError ** error)
 {
+  return TRUE;
 }
 
 static void
@@ -48,13 +49,22 @@ endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass)
 static gpointer
 endpoint_factory (WpFactory * factory, GType type, GVariant * properties)
 {
+  const gchar *name = NULL;
+  const gchar *media_class = NULL;
+
   if (type != WP_TYPE_ENDPOINT)
     return NULL;
 
-  /* TODO: retrieve pw_node* from @properties and keep it
-   * TODO: populate media_class and name on the endpoint
-   */
-  return g_object_new (endpoint_get_type (), NULL);
+  /* Get the name and media-class */
+  if (!g_variant_lookup (properties, "name", "&s", &name))
+      return NULL;
+  if (!g_variant_lookup (properties, "media-class", "&s", &media_class))
+      return NULL;
+
+  return g_object_new (endpoint_get_type (),
+      "name", name,
+      "media-class", media_class,
+      NULL);
 }
 
 void