--- a/src/ohproduct.cxx
+++ b/src/ohproduct.cxx
@@ -39,15 +39,21 @@
 #include "ohreceiver.hxx"
 #include "ohsndrcv.hxx"
 #include "ohinfo.hxx"
+#include "conftree.hxx"
 
 using namespace std;
 using namespace std::placeholders;
 
+static void listScripts(vector<pair<string, string> >& sources);
+
 static const string sTpProduct("urn:av-openhome-org:service:Product:1");
 static const string sIdProduct("urn:av-openhome-org:serviceId:Product");
 
 static string csxml("<SourceList>\n");
 static string csattrs("Info Time Volume");
+
+// This can be replaced by config data in listScripts()
+static string scripts_dir("/usr/share/upmpdcli/src_scripts");
 
 // (Type, Name) list
 static vector<pair<string, string> > o_sources;
@@ -80,6 +86,8 @@
             }
         }
     }
+
+    listScripts(o_sources);
 
     for (auto it = o_sources.begin(); it != o_sources.end(); it++) {
         string visible = it->first.compare("Receiver") ? "1" : "0";
@@ -251,49 +259,57 @@
 
         m_dev->m_ohif->setMetatext("");
 
+        string curtp = o_sources[m_sourceIndex].first;
         string curnm = o_sources[m_sourceIndex].second;
-        if (m_dev->m_ohpl && !curnm.compare("Playlist")) {
+        if (m_dev->m_ohpl && !curtp.compare("Playlist") &&
+            !curnm.compare("Playlist")) {
             m_dev->m_ohpl->iStop();
             m_dev->m_ohpl->setActive(false);
-        } else if (m_dev->m_ohrcv && !curnm.compare("Receiver")) {
+        } else if (m_dev->m_ohrcv && !curtp.compare("Receiver") &&
+            !curnm.compare("Receiver")) {
             m_dev->m_ohrcv->iStop();
             m_dev->m_ohrcv->setActive(false);
-        } else if (m_dev->m_ohrd && !curnm.compare("Radio")) {
+        } else if (m_dev->m_ohrd && !curtp.compare("Radio") &&
+            !curnm.compare("Radio")) {
             m_dev->m_ohrd->iStop();
             m_dev->m_ohrd->setActive(false);
         } else if (m_dev->m_sndrcv && m_dev->m_ohpl &&
+                   !curtp.compare("Playlist") &&
                    !curnm.compare("SenderReceiverPL")) {
             m_dev->m_sndrcv->stop();
             m_dev->m_ohpl->setActive(false);
         } else if (m_dev->m_sndrcv && m_dev->m_ohrd &&
+                   !curtp.compare("Radio") &&
                    !curnm.compare("SenderReceiverRD")) {
             m_dev->m_ohrd->setActive(false);
             m_dev->m_sndrcv->stop();
-        }
-
+        } else {
+            // External inputs managed by scripts Analog/Digital/Hdmi etc.
+            m_dev->m_sndrcv->stop();
+        }
+
+        string newtp = o_sources[sindex].first;
         string newnm = o_sources[sindex].second;
         if (m_dev->m_ohpl && !newnm.compare("Playlist")) {
             m_dev->m_ohpl->setActive(true);
-            m_sourceIndex = sindex;
         } else if (m_dev->m_ohrcv && !newnm.compare("Receiver")) {
             m_dev->m_ohrcv->setActive(true);
-            m_sourceIndex = sindex;
         } else if (m_dev->m_ohrd && !newnm.compare("Radio")) {
             m_dev->m_ohrd->setActive(true);
-            m_sourceIndex = sindex;
         } else if (m_dev->m_ohpl && m_dev->m_sndrcv &&
                    !newnm.compare("SenderReceiverPL")) {
-            // Events are generated by playlist
             m_dev->m_ohpl->setActive(true);
-            m_dev->m_sndrcv->start(false, savedms);
-            m_sourceIndex = sindex;
+            m_dev->m_sndrcv->start(string(), savedms);
         } else if (m_dev->m_ohrd && m_dev->m_sndrcv &&
                    !newnm.compare("SenderReceiverRD")) {
-            // Events are generated by radio
             m_dev->m_ohrd->setActive(true);
-            m_dev->m_sndrcv->start(true, 0);
-            m_sourceIndex = sindex;
-        }
+            m_dev->m_sndrcv->start(string());
+        } else {
+            string sname = newtp + "-" + newnm;
+            string spath = path_cat(scripts_dir, sname);
+            m_dev->m_sndrcv->start(spath);
+        }
+        m_sourceIndex = sindex;
 
         m_dev->loopWakeup();
     }
@@ -365,3 +381,56 @@
     data.addarg("Value", "0");
     return UPNP_E_SUCCESS;
 }
+
+#include <sys/types.h>
+#include <dirent.h>
+
+// Script names are like Type-Name
+// Type may be Analog or Digital or Hdmi and is not specially
+// distinguished on value (but must be one of the three).
+//
+// Name is arbitrary
+static void listScripts(vector<pair<string, string> >& sources)
+{
+    if (!g_config)
+        return;
+
+    g_config->get("ohsrc_scripts_dir", scripts_dir);
+
+    DIR *dirp = opendir(scripts_dir.c_str());
+    if (dirp == 0) {
+        LOGERR("Error opening scripts dir " << scripts_dir << " errno " <<
+               errno << endl);
+        return;
+    }
+
+    struct dirent *ent;
+    while ((ent = readdir(dirp)) != 0) {
+        string tpnm(ent->d_name);
+        if (tpnm.size() == 0 || tpnm[0] == '.') {
+            continue;
+        }
+        string::size_type dash = tpnm.find_first_of("-");
+        if (dash == string::npos)
+            continue;
+
+
+        string tp(tpnm.substr(0, dash));
+        string nm(tpnm.substr(dash+1));
+        if (tp.compare("Analog") && tp.compare("Digital") &&
+            tp.compare("Hdmi")) {
+            LOGERR("listScripts: bad source type: " << tp << endl);
+            continue;
+        }
+
+        if (access(path_cat(scripts_dir, tpnm).c_str(), X_OK) != 0) {
+            LOGERR("listScripts: script " << tpnm << " is not executable" <<
+                   endl);
+            continue;
+        }
+
+        sources.push_back(pair<string, string>(tp, nm));
+    }
+    closedir(dirp);
+    return;
+}