diff --git a/config.c b/config.c
index e5f5463..794c9f8 100644
--- a/config.c
+++ b/config.c
@@ -395,6 +395,9 @@ cSetup::cSetup(void)
   PositionerSpeed = 15;
   PositionerSwing = 650;
   PositionerLastLon = 0;
+  PowerdownEnabled = 0;
+  PowerdownTimeoutM = 15;
+  PowerdownWakeupH = 4;
   SetSystemTime = 0;
   TimeSource = 0;
   TimeTransponder = 0;
@@ -622,6 +625,9 @@ bool cSetup::Parse(const char *Name, const char *Value)
   else if (!strcasecmp(Name, "PositionerSpeed"))     PositionerSpeed    = atoi(Value);
   else if (!strcasecmp(Name, "PositionerSwing"))     PositionerSwing    = atoi(Value);
   else if (!strcasecmp(Name, "PositionerLastLon"))   PositionerLastLon  = atoi(Value);
+  else if (!strcasecmp(Name, "PowerdownEnabled"))    PowerdownEnabled   = atoi(Value);
+  else if (!strcasecmp(Name, "PowerdownTimeoutM"))   PowerdownTimeoutM  = atoi(Value);
+  else if (!strcasecmp(Name, "PowerdownWakeupH"))    PowerdownWakeupH   = atoi(Value);
   else if (!strcasecmp(Name, "SetSystemTime"))       SetSystemTime      = atoi(Value);
   else if (!strcasecmp(Name, "TimeSource"))          TimeSource         = cSource::FromString(Value);
   else if (!strcasecmp(Name, "TimeTransponder"))     TimeTransponder    = atoi(Value);
@@ -753,6 +759,9 @@ bool cSetup::Save(void)
   Store("PositionerSpeed",    PositionerSpeed);
   Store("PositionerSwing",    PositionerSwing);
   Store("PositionerLastLon",  PositionerLastLon);
+  Store("PowerdownEnabled",   PowerdownEnabled);
+  Store("PowerdownTimeoutM",  PowerdownTimeoutM);
+  Store("PowerdownWakeupH",   PowerdownWakeupH);
   Store("SetSystemTime",      SetSystemTime);
   Store("TimeSource",         cSource::ToString(TimeSource));
   Store("TimeTransponder",    TimeTransponder);
diff --git a/config.h b/config.h
index e5565da..7a73d9d 100644
--- a/config.h
+++ b/config.h
@@ -273,6 +273,9 @@ public:
   int PositionerSpeed;
   int PositionerSwing;
   int PositionerLastLon;
+  int PowerdownEnabled;
+  int PowerdownTimeoutM;
+  int PowerdownWakeupH;
   int SetSystemTime;
   int TimeSource;
   int TimeTransponder;
diff --git a/device.c b/device.c
index 542d120..adbe973 100644
--- a/device.c
+++ b/device.c
@@ -104,6 +104,9 @@ cDevice::cDevice(void)
   dvbSubtitleConverter = NULL;
   autoSelectPreferredSubtitleLanguage = true;
 
+  idleTimerExpires = time(NULL) + Setup.PowerdownTimeoutM * 60;
+  wakeupTimerExpires = 0;
+
   for (int i = 0; i < MAXRECEIVERS; i++)
       receiver[i] = NULL;
 
@@ -745,6 +748,11 @@ bool cDevice::SwitchChannel(int Direction)
   return result;
 }
 
+// While switching to a channel, the device will be kept powered up
+// for at least this number of seconds before a receiver is attached.
+// Must be less than cEITScanner::ScanTimeout.
+#define CHANNEL_SWITCH_POWERUP_TIMEOUT  10
+
 eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
 {
   cStatus::MsgChannelSwitch(this, 0, LiveView);
@@ -778,6 +786,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
         Result = scrNotAvailable;
      }
   else {
+     // Power up the device
+     PowerUp(CHANNEL_SWITCH_POWERUP_TIMEOUT);
      // Stop section handling:
      if (sectionHandler) {
         sectionHandler->SetStatus(false);
@@ -843,8 +853,11 @@ int cDevice::Occupied(void) const
 
 void cDevice::SetOccupied(int Seconds)
 {
-  if (Seconds >= 0)
+  if (Seconds >= 0) {
      occupiedTimeout = time(NULL) + min(Seconds, MAXOCCUPIEDTIMEOUT);
+     // avoid short power-down/power-up cycles
+     SetIdleTimer(true, Seconds + 30);
+     }
 }
 
 bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
@@ -1675,6 +1688,7 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
             startScrambleDetection = time(NULL);
             }
          Start();
+         SetIdleTimer(false);
          return true;
          }
       }
@@ -1708,8 +1722,10 @@ void cDevice::Detach(cReceiver *Receiver)
            camSlot->Assign(NULL);
         }
      }
-  if (!receiversLeft)
+  if (!receiversLeft) {
      Cancel(-1);
+     SetIdleTimer(true);
+     }
 }
 
 void cDevice::DetachAll(int Pid)
@@ -1731,6 +1747,82 @@ void cDevice::DetachAllReceivers(void)
       Detach(receiver[i]);
 }
 
+void cDevice::CheckIdle(void)
+{
+  if (!SupportsPowerDown() || !Setup.PowerdownEnabled)
+     return;
+  cMutexLock MutexLock(&mutexPowerSaving);
+  if (idleTimerExpires != 0 && time(NULL) > idleTimerExpires) {
+     // idle, powered up
+     dsyslog("power saving: device %d idle timer expired", CardIndex() + 1);
+     SetIdleTimer(false);
+     if (Setup.PowerdownWakeupH != 0)
+        wakeupTimerExpires = time(NULL) + Setup.PowerdownWakeupH * 3600;
+     else
+        dsyslog("power saving: waking up is disabled");
+     if (!IsPoweredDown()) {
+        dsyslog("power saving: powering device %d down", CardIndex() + 1);
+        if (sectionHandler) {
+           sectionHandler->SetStatus(false);
+           sectionHandler->SetChannel(NULL);
+           }
+        PowerDownMode(true);
+        }
+     }
+  if (wakeupTimerExpires != 0 && time(NULL) > wakeupTimerExpires) {
+     // idle, powered down
+     dsyslog("power saving: device %d wakeup timer expired", CardIndex() + 1);
+     SetIdleTimer(true);
+     if (IsPoweredDown()) {
+        dsyslog("power saving: waking up device %d", CardIndex() + 1);
+        PowerDownMode(false);
+        }
+     }
+}
+
+void cDevice::SetIdleTimer(bool On, int ExtraTimeoutS)
+{
+  if (!SupportsPowerDown())
+     return;
+  cMutexLock MutexLock(&mutexPowerSaving);
+  if (On) {
+     int Tout = Setup.PowerdownTimeoutM * 60;
+     time_t Now = time(NULL);
+     if (ExtraTimeoutS > 0) {
+        if (idleTimerExpires >= Now + ExtraTimeoutS)
+           return;
+        Tout = ExtraTimeoutS;
+        }
+     idleTimerExpires = Now + Tout;
+     if (Setup.PowerdownEnabled)
+        dsyslog("power saving: set device %d idle timer to %d sec", CardIndex() + 1, Tout);
+     }
+  else {
+     idleTimerExpires = 0;
+     if (Setup.PowerdownEnabled)
+        dsyslog("power saving: disable device %d idle timer", CardIndex() + 1);
+     }
+  wakeupTimerExpires = 0;
+}
+
+bool cDevice::PoweredDown(void)
+{
+  if (SupportsPowerDown() && Setup.PowerdownEnabled) {
+     cMutexLock MutexLock(&mutexPowerSaving);
+     return IsPoweredDown();
+     }
+  else
+     return false;
+}
+
+void cDevice::PowerUp(int ExtraTimeoutS)
+{
+  cMutexLock MutexLock(&mutexPowerSaving);
+  SetIdleTimer(true, ExtraTimeoutS);
+  if (SupportsPowerDown() && IsPoweredDown())
+     PowerDownMode(false);
+}
+
 // --- cTSBuffer -------------------------------------------------------------
 
 cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
diff --git a/device.h b/device.h
index 31ee303..cc40bb7 100644
--- a/device.h
+++ b/device.h
@@ -821,6 +821,37 @@ public:
        ///< Detaches all receivers from this device for this pid.
   virtual void DetachAllReceivers(void);
        ///< Detaches all receivers from this device.
+
+// Power saving facilities
+
+private:
+  cMutex mutexPowerSaving;
+  time_t idleTimerExpires, wakeupTimerExpires;
+  void PowerUp(int ExtraTimeoutS);
+       ///< If the device is powered down, powers it up and keeps it
+       ///< powered up for at least ExtraTimeoutS seconds (see
+       ///< cDevice::SetIdleTimer()).
+public:
+  void CheckIdle(void);
+       ///< Should be called periodically in the main loop.
+  bool PoweredDown(void);
+       ///< Returns true if the device is powered down "logically", that is,
+       ///< idle tasks like EPG scanning are disabled.
+  void SetIdleTimer(bool On, int ExtraTimeoutS = 0);
+       ///< Starts/disables the idle timer. This timer must be started when
+       ///< a device gets idle and must be disabled when it is receiving.
+       ///< If ExtraTimeoutS is greater than zero and On is true, a new timer
+       ///< won't be set, but the device will be kept powered up for at least
+       ///< ExtraTimeoutS seconds.
+protected:
+       ///< NOTE: IsTunedToTransponder() should return false if the
+       ///< device is powered down.
+  virtual bool IsPoweredDown(void) {return false;}
+       ///< Returns true if the device is powered down "physically".
+  virtual void PowerDownMode(bool On) {};
+       ///< Actually powers the device down/up.
+  virtual bool SupportsPowerDown() {return false;}
+       ///< Returns true if a derived device supports power saving.
   };
 
 /// Derived cDevice classes that can receive channels will have to provide
diff --git a/dvbdevice.c b/dvbdevice.c
index 63af52e..87555b7 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -348,6 +348,8 @@ public:
   const cPositioner *Positioner(void) const { return positioner; }
   int GetSignalStrength(void) const;
   int GetSignalQuality(void) const;
+  bool IsPoweredDown(void) {return fd_frontend < 0;}
+  void PowerDownMode(bool On);
   };
 
 cMutex cDvbTuner::bondMutex;
@@ -544,6 +546,8 @@ void cDvbTuner::ClearEventQueue(void) const
 
 bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
 {
+  if (fd_frontend < 0)
+     return false;
   ClearEventQueue();
   while (1) {
         if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1)
@@ -559,6 +563,8 @@ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
 
 int cDvbTuner::GetSignalStrength(void) const
 {
+  if (fd_frontend < 0)
+     return -1;
   ClearEventQueue();
   uint16_t Signal;
   while (1) {
@@ -1001,6 +1007,26 @@ void cDvbTuner::Action(void)
         }
 }
 
+void cDvbTuner::PowerDownMode(bool On)
+{
+  cMutexLock MutexLock(&mutex);
+  if (On && fd_frontend >= 0) {
+     isyslog("dvb tuner: power-down - closing frontend %d/%d", adapter, frontend);
+     tunerStatus = tsIdle;
+     close(fd_frontend);
+     fd_frontend = -1;
+     }
+  if (!On && fd_frontend < 0) {
+     cString Filename = cString::sprintf("%s/%s%d/%s%d",
+        DEV_DVB_BASE, DEV_DVB_ADAPTER, adapter, DEV_DVB_FRONTEND, frontend);
+     isyslog("dvb tuner: power-up - opening frontend %d/%d", adapter, frontend);
+     fd_frontend = open(Filename, O_RDWR | O_NONBLOCK);
+     if (fd_frontend < 0)
+        esyslog("ERROR: can't open DVB device frontend %d/%d", adapter, frontend);
+     tunerStatus = tsIdle;
+     }
+}
+
 // --- cDvbSourceParam -------------------------------------------------------
 
 class cDvbSourceParam : public cSourceParam {
@@ -1712,6 +1738,19 @@ void cDvbDevice::DetachAllReceivers(void)
   needsDetachBondedReceivers = false;
 }
 
+bool cDvbDevice::IsPoweredDown(void)
+{
+  if (dvbTuner)
+     return dvbTuner->IsPoweredDown();
+  return false;
+}
+
+void cDvbDevice::PowerDownMode(bool On)
+{
+  if (dvbTuner)
+     dvbTuner->PowerDownMode(On);
+}
+
 // --- cDvbDeviceProbe -------------------------------------------------------
 
 cList<cDvbDeviceProbe> DvbDeviceProbes;
diff --git a/dvbdevice.h b/dvbdevice.h
index 5ae4952..e21c652 100644
--- a/dvbdevice.h
+++ b/dvbdevice.h
@@ -290,6 +290,13 @@ protected:
   virtual void CloseDvr(void);
   virtual bool GetTSPacket(uchar *&Data);
   virtual void DetachAllReceivers(void);
+
+// Power saving facilities
+
+protected:
+  virtual bool IsPoweredDown(void);
+  virtual void PowerDownMode(bool On);
+  virtual bool SupportsPowerDown() {return true;}
   };
 
 // A plugin that implements a DVB device derived from cDvbDevice needs to create
diff --git a/eitscan.c b/eitscan.c
index 41ac25e..765055c 100644
--- a/eitscan.c
+++ b/eitscan.c
@@ -144,7 +144,8 @@ void cEITScanner::Process(void)
            bool AnyDeviceSwitched = false;
            for (int i = 0; i < cDevice::NumDevices(); i++) {
                cDevice *Device = cDevice::GetDevice(i);
-               if (Device && Device->ProvidesEIT()) {
+               if (Device && Device->ProvidesEIT()
+                     && (!Device->PoweredDown() || lastActivity == 0)) { // powered up or forced scan
                   for (cScanData *ScanData = scanList->First(); ScanData; ScanData = scanList->Next(ScanData)) {
                       const cChannel *Channel = ScanData->GetChannel();
                       if (Channel) {
@@ -165,6 +166,10 @@ void cEITScanner::Process(void)
                                            }
                                         }
                                      //dsyslog("EIT scan: device %d  source  %-8s tp %5d", Device->DeviceNumber() + 1, *cSource::ToString(Channel->Source()), Channel->Transponder());
+                                     if (lastActivity == 0)
+                                        // forced scan - set idle timer for each channel switch;
+                                        // this prevents powering down while scanning a transponder
+                                        Device->SetIdleTimer(true, ScanTimeout + 5);
                                      Device->SwitchChannel(Channel, false);
                                      scanList->Del(ScanData);
                                      AnyDeviceSwitched = true;
diff --git a/menu.c b/menu.c
index 569900c..5a89771 100644
--- a/menu.c
+++ b/menu.c
@@ -3715,6 +3715,12 @@ void cMenuSetupLNB::Setup(void)
      Add(new cMenuEditIntxItem(tr("Setup.LNB$Positioner speed (degrees/s)"), &data.PositionerSpeed, 1, 1800, 10));
      }
 
+  Add(new cMenuEditBoolItem(tr("Setup.LNB$Enable power saving"), &data.PowerdownEnabled));
+  if (data.PowerdownEnabled) {
+     Add(new cMenuEditIntItem(tr("Setup.LNB$Power down an idle device after (min)"), &data.PowerdownTimeoutM));
+     Add(new cMenuEditIntItem(tr("Setup.LNB$Wake up from power-down after (h)"), &data.PowerdownWakeupH));
+     }
+
   SetCurrent(Get(current));
   Display();
 }
@@ -3723,6 +3729,7 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
 {
   int oldDiSEqC = data.DiSEqC;
   int oldUsePositioner = data.UsePositioner;
+  int oldPowerdownEnabled = data.PowerdownEnabled;
   bool DeviceBondingsChanged = false;
   if (Key == kOk) {
      cString NewDeviceBondings = satCableNumbers.ToString();
@@ -3731,7 +3738,7 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
      }
   eOSState state = cMenuSetupBase::ProcessKey(Key);
 
-  if (Key != kNone && (data.DiSEqC != oldDiSEqC || data.UsePositioner != oldUsePositioner))
+  if (Key != kNone && (data.DiSEqC != oldDiSEqC || data.UsePositioner != oldUsePositioner || data.PowerdownEnabled != oldPowerdownEnabled))
      Setup();
   else if (DeviceBondingsChanged)
      cDvbDevice::BondDevices(data.DeviceBondings);
diff --git a/vdr.c b/vdr.c
index 6b0bf2b..c8de702 100644
--- a/vdr.c
+++ b/vdr.c
@@ -1515,6 +1515,12 @@ int main(int argc, char *argv[])
 
         ReportEpgBugFixStats();
 
+        for (int i = 0; i < cDevice::NumDevices(); i++) {
+           cDevice *d = cDevice::GetDevice(i);
+           if (d)
+              d->CheckIdle();
+           }
+
         // Main thread hooks of plugins:
         PluginManager.MainThreadHook();
         }
