Skip to content

Instantly share code, notes, and snippets.

@LeoDJ
Last active May 13, 2024 03:19
Show Gist options
  • Save LeoDJ/eaab4bd9d3dd082eea5555a312046bb8 to your computer and use it in GitHub Desktop.
Save LeoDJ/eaab4bd9d3dd082eea5555a312046bb8 to your computer and use it in GitHub Desktop.
Workaround for Windows Modern Standby waking up all the damn time by simply putting it back to sleep if it wakes up for the wrong reason

Windows Modern Standby Wakeup Source Selection Workaround

I was very frustrated that my Framework 16 (Ryzen 7040 series) doesn't appear to support S3 sleep at all anymore.
In the meantime I've used hibernate, but it takes quite long to wake and stresses the SSD unnecessarily.
I can live with the higher power draw of modern / (dis)connected standby, but not with it waking up constantly, if even the lightest gust of wind grazes the laptop in any way.
(Lid opened, lid closed, power un/plugged, USB un/plugged, mouse moved, key pressed, randomly during the night, etc.)

All I want is for my laptop to only wake from standby upon a power button press and nothing else.

And after hours of searching I've concluded that there is no official way to select which wakeup sources should wake from modern standby.

All of the "normal" ways that used to work in the past for normal standby don't work anymore.
(powercfg /devicequery wake_armed returns no devices, yet it wakes from any USB keyboard and mouse anyways)

At least the wakeup reason is listed in the Windows event log, which lead me to the following workaround idea:
Simply go right back to sleep if the wakeup reason doesn't match a whitelist.

I've tested it a bit and it seems to work okay-ish.

Workaround Installation

  1. Download the goToSleep.bat to any location.
  2. Open the "Task Scheduler" (taskschd.msc)
  3. Either try importing the ModernStandbyWorkaround_Task.xml file (not sure if it works)
    1. Open the task, go to Actions, edit the "Start program" action and put in the path to the downloaded goToSleep.bat
  4. OR create a new task
    1. Name it however you like, e.g. "MondernStandbyIsShit"
    2. Switch to the "Trigger" tab and create a new trigger
      1. Begin the task > "On an event"
      2. Settings > Custom
      3. Click on "New event filter" > XML tab
      4. Paste the following XPath filter:
      <QueryList>
        <Query Id="0" Path="System">
          <Select Path="System">
            *[System[Provider[@Name='Microsoft-Windows-Kernel-Power'] and (EventID=507)]]
            and
            *[EventData[Data[@Name='Reason'] != '1' and Data[@Name='Reason'] != '31' and Data[@Name='Reason'] != '44']]
          </Select>
        </Query>
      </QueryList>
      1. Change line 6 so it filters for the event IDs you want whitelisted. (Event IDs listed below)
        This line currently only allowes wakes on power button, key press and fingerprint reader read.
      2. OK > OK
    3. Switch to the Actions tab and create a new action
      1. Action: "Start program"
      2. Provide the path to the goToSleep.bat file
    4. Conditions tab
      1. Uncheck "Start the task only if the computer is on AC power"
  5. It's a good idea to temporarily increase the timout value in the script to 10-15s during testing, otherwise you may run into a standby-loop!

If I forgot a step, feel free to try to figure it out on your own or leave a comment ^^

Wakeup Reason IDs

On my Framework 16, I've gathered the following wakeup reasons:

ID Reason (as printed in the event entry)
1 Power Button
5 AC/DC Display Burst (power plugged in?)
15 Lid
20 Sleep, Hibernate, or Shutdown
28 AC/DC Display Burst Suppressed
31 Input Keyboard
32 Input Mouse
33 Input Touch (also touchpad)
44 44 (Fingeprint reader on my Framework 16)
@echo off
echo "Going to sleep in 1s!"
timeout 1
REM Current way to enter modern standby ("turn off monitors"):
powershell -command $obj = Add-Type -MemberDefinition '[DllImport(""""user32.dll"""")] public static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);' -Name fn -Namespace ns -PassThru; $obj::SendMessage(0xffff, 0x0112, 0xF170, 2)
REM Old sleep method, won't work, only goes into hibernate (also technically the wrong way to call SetSuspendState):
REM rundll32.exe powrprof.dll,SetSuspendState 0,1,1
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2024-05-13T03:03:36.8710082</Date>
<Author>LEO-FW16\LeoDJ</Author>
<URI>\ModernStandbyWorkaround</URI>
</RegistrationInfo>
<Triggers>
<EventTrigger>
<Enabled>true</Enabled>
<Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="System"&gt;&lt;Select Path="System"&gt;
*[System[Provider[@Name='Microsoft-Windows-Kernel-Power'] and (EventID=507)]]
and
*[EventData[Data[@Name='Reason'] != '1' and Data[@Name='Reason'] != '31' and Data[@Name='Reason'] != '44']]
&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
</EventTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>S-1-5-21-1427359075-2161949306-620542353-1001</UserId>
<LogonType>InteractiveToken</LogonType>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT72H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>C:\REPLACEME\goToSleep.bat</Command>
</Exec>
</Actions>
</Task>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment