diff -r c8a028da847d buildbot/scheduler.py
--- a/buildbot/scheduler.py	Tue Jul 08 11:28:42 2008 -0400
+++ b/buildbot/scheduler.py	Tue Jul 08 11:29:02 2008 -0400
@@ -380,6 +380,51 @@
                                self.reason,
                                properties=self.properties)
         self.submitBuildSet(bs)
+
+
+
+class Idle(BaseUpstreamScheduler):
+    compare_attrs = ('name', 'builderNames', 'idleBuildTimer', 'branch',
+                     'properties')
+
+    def __init__(self, name, builderNames, idleBuildTimer, branch=None,
+                 properties={}):
+        BaseUpstreamScheduler.__init__(self, name, properties,
+                                       notifyScheduler=self)
+        self.builderNames = builderNames
+        self.branch = branch
+        self.idleBuildTimer = idleBuildTimer
+        self.reason = ("The Idle scheduler name '%s' triggered this build"
+                       % name)
+        self.runningBuilds = 0
+        self.timer = internet.TimerService(self.idleBuildTimer,
+                                           self.doPeriodicBuild)
+        self.timer.setServiceParent(self)
+
+    def listBuilderNames(self):
+        return self.builderNames
+
+    def getPendingBuildTimes(self):
+        return []
+
+    def doPeriodicBuild(self):
+        bs = buildset.BuildSet(self.builderNames,
+                               SourceStamp(branch=self.branch),
+                               self.reason,
+                               properties=self.properties)
+        self.submitBuildSet(bs)
+
+    def buildStarted(self, build):
+        if build.builder.name in self.builderNames:
+            self.runningBuilds += 1
+            if self.timer._loop.running:
+                self.timer._loop.stop()
+
+    def buildFinished(self, res, br):
+        if br.builder.name in self.builderNames:
+            self.runningBuilds -= 1
+            if self.runningBuilds == 0:
+                self.timer._loop.start(self.idleBuildTimer, now=False)
 
 
 
diff -r c8a028da847d buildbot/test/test_run.py
--- a/buildbot/test/test_run.py	Tue Jul 08 11:28:42 2008 -0400
+++ b/buildbot/test/test_run.py	Tue Jul 08 11:29:02 2008 -0400
@@ -67,6 +67,40 @@
                       'builddir': 'dummy', 'factory': f2})
 c['builders'].append({'name': 'dummy2', 'slavename': 'bot1',
                       'builddir': 'dummy2', 'factory': f2})
+"""
+
+config_idle = config_base + """
+c['slaves'].append(BuildSlave('bot2', 'sekrit'))
+
+from buildbot.scheduler import Idle, AnyBranchScheduler, Scheduler
+
+f2 = factory.BuildFactory([s(dummy.Dummy, timeout=2)])
+
+c['schedulers'] = []
+idle = Idle('dummy', ['dummy'], 4)
+c['schedulers'].append(idle)
+c['schedulers'].append(AnyBranchScheduler('dummy2', None, 0, ['dummy'],
+                       notifyScheduler=idle))
+
+c['builders'] = [{'name': 'dummy', 'slavenames': ['bot1', 'bot2'],
+                 'builddir': 'dummy', 'factory': f2}]
+"""
+
+config_idle_2 = config_idle + """
+c['schedulers'] = []
+idle = Idle('idledummy', ['dummy'], 4)
+c['schedulers'].append(idle)
+c['schedulers'].append(Scheduler('dummy', 'branch1', 0, ['dummy'],
+                       notifyScheduler=idle))
+idle2 = Idle('idledummy2', ['dummy2'], 4)
+c['schedulers'].append(idle2)
+c['schedulers'].append(Scheduler('dummy2', 'branch2', 0, ['dummy2'],
+                       notifyScheduler=idle2))
+
+c['builders'] = [{'name': 'dummy', 'slavenames': ['bot1'],
+                 'builddir': 'dummy', 'factory': f2},
+                 {'name': 'dummy2', 'slavenames': ['bot2'],
+                 'builddir': 'dummy2', 'factory': f2}]
 """
 
 config_2 = config_base + """
@@ -213,6 +247,144 @@
         d.addCallback(_check)
 
         return d
+
+
+class Idle(RunMixin, unittest.TestCase):
+    def setUp(self):
+        RunMixin.setUp(self)
+
+        self.master.loadConfig(config_idle)
+        self.master.readConfig = True
+        self.master.startService()
+
+        return self.connectSlave()
+
+    def isBuilding(self, builder='dummy'):
+        b = self.master.botmaster.builders['dummy']
+        return len(b.building)
+
+    def forceBuild(self, branch=None):
+        c = changes.Change("foo", ["blah"], "stuff", branch=branch)
+        self.master.change_svc.addChange(c)
+
+    def testIdleTimerReset(self):
+        # idle timer is set to 4, if we wait that long we should have a build
+        # going
+        self.failUnlessEqual(self.isBuilding(), 1)
+        d = defer.Deferred()
+        reactor.callLater(2.1, d.callback, None)
+        d.addCallback(self._testIdleTimerReset_1)
+        return d
+
+    def _testIdleTimerReset_1(self, res):
+        self.failUnlessEqual(self.isBuilding(), 0)
+        # now wait until after it is done and force a build
+        d = defer.Deferred()
+        reactor.callLater(2, d.callback, None)
+        d.addCallback(self._testIdleTimerReset_2)
+        return d
+
+    def _testIdleTimerReset_2(self, res):
+        self.forceBuild()
+        d = defer.Deferred()
+        # need extra time here because changes are delayed slightly
+        reactor.callLater(4, d.callback, None)
+        d.addCallback(self._testIdleTimerReset_3)
+        return d
+
+    def _testIdleTimerReset_3(self, res):
+        self.failUnlessEqual(self.isBuilding(), 0)
+        d = defer.Deferred()
+        reactor.callLater(4, d.callback, None)
+        d.addCallback(self._testIdleTimerReset_4)
+        return d
+
+    def _testIdleTimerReset_4(self, res):
+        self.failUnlessEqual(self.isBuilding(), 1)
+
+    def testIdleTimerManyBuilds(self):
+        d = self.connectSlave(slavename='bot2')
+        d.addCallback(self._testIdleTimerManyBuilds_1)
+        return d
+
+    def _testIdleTimerManyBuilds_1(self, res):
+        self.forceBuild()
+        self.forceBuild()
+        d = defer.Deferred()
+        reactor.callLater(1, d.callback, None)
+        d.addCallback(self._testIdleTimerManyBuilds_2)
+        return d
+
+    def _testIdleTimerManyBuilds_2(self, res):
+        b = self.master.botmaster.builders['dummy']
+        self.failUnlessEqual(self.isBuilding(), 2)
+        d = defer.Deferred()
+        reactor.callLater(4, d.callback, None)
+        d.addCallback(self._testIdleTimerManyBuilds_3)
+        return d
+
+    def _testIdleTimerManyBuilds_3(self, res):
+        self.failUnlessEqual(self.isBuilding(), 0)
+
+class Idle2(RunMixin, unittest.TestCase):
+    def setUp(self):
+        RunMixin.setUp(self)
+
+        self.master.loadConfig(config_idle_2)
+        self.master.readConfig = True
+        self.master.startService()
+
+        d = self.connectSlave()
+        d.addCallback(self._setUp_1)
+        return d
+
+    def _setUp_1(self, res):
+        return self.connectSlave(builders=['dummy2'], slavename='bot2')
+
+    def isBuilding(self, builder='dummy'):
+        b = self.master.botmaster.builders['dummy']
+        return len(b.building)
+
+    def forceBuild(self, branch=None):
+        c = changes.Change("foo", ["blah"], "stuff", branch=branch)
+        self.master.change_svc.addChange(c)
+
+    def testTwoBranches(self):
+        d = defer.Deferred()
+        reactor.callLater(4, d.callback, None)
+        d.addCallback(self._testTwoBranches_1)
+        return d
+
+    def _testTwoBranches_1(self, res):
+        d1 = defer.Deferred()
+        reactor.callLater(2, d1.callback, None)
+        d1.addCallback(self._testTwoBranches_branch1_1)
+        d2 = defer.Deferred()
+        reactor.callLater(2, d2.callback, None)
+        d2.addCallback(self._testTwoBranches_branch2_1)
+        return defer.DeferredList([d1,d2])
+
+    def _testTwoBranches_branch1_1(self, res):
+        self.failUnlessEqual(self.isBuilding(), 1)
+
+        d = defer.Deferred()
+        reactor.callLater(6, d.callback, None)
+        d.addCallback(self._testTwoBranches_branch1_2)
+        return d
+
+    def _testTwoBranches_branch1_2(self, res):
+        self.failUnlessEqual(self.isBuilding(), 1)
+
+    def _testTwoBranches_branch2_1(self, res):
+        self.failUnlessEqual(self.isBuilding('dummy'), 1)
+
+        d = defer.Deferred()
+        reactor.callLater(3, d.callback, None)
+        d.addCallback(self._testTwoBranches_branch2_2)
+        return d
+
+    def _testTwoBranches_branch2_2(self, res):
+        self.failUnlessEqual(self.isBuilding('dummy'), 0)
 
 
 class Ping(RunMixin, unittest.TestCase):

