Thursday, 17 October 2013

What's waking up my computer at 3am?

I was working late last night, and when I was done, I chose 'Hibernate' when I was closing my laptop. This morning, I went to grab my laptop and noticed it was warm (with the lid down). Something had caused it to wake up, but what?

First stop, Event Viewer. In the System Log, there's this event:

The system has returned from a low power state.

Sleep Time: ‎2013‎-‎10‎-‎16T14:59:15.107518600Z 
Wake Time: ‎2013‎-‎10‎-‎16T16:44:40.891642600Z

Wake Source: Unknown

Well that's not particularly useful.

I remember previously having a similar problem caused by a scheduled task – so maybe it would be worth checking what scheduled tasks are active and allowed to wake up the computer? You can view all scheduled tasks by opening the Windows Task Scheduler application. The problem is that tasks are listed hierarchically, so you'd need to drill down into every folder and review each task. Maybe PowerShell can do this more efficiently?

Graimer posts a good example of querying scheduled tasks on Stack Overflow.

I've extended it so that I can filter just the tasks with WakeToRun and Enabled both true.

function getTasks($path) {
    $out = @()

    # Get root tasks
    $schedule.GetFolder($path).GetTasks(0) | % {
        $xml = [xml]$_.xml
        $obj = New-Object psobject -Property @{
            "Name" = $_.Name
            "Path" = $_.Path
            "LastRunTime" = $_.LastRunTime
            "NextRunTime" = $_.NextRunTime
            #"WakeToRun" = [bool]::Parse( $xml.Task.Settings.WakeToRun
            #"Enabled" = [bool] $xml.Task.Settings.Enabled
            "Actions" = ($xml.Task.Actions.Exec | % { "$($_.Command) $($_.Arguments)" }) -join "`n"
        }

        if ($xml.Task.Settings.WakeToRun)
        {
            Add-Member -InputObject $obj -Name "WakeToRun" -Value ([bool]::Parse( $xml.Task.Settings.WakeToRun )) -MemberType NoteProperty
        }

        if ($xml.Task.Settings.Enabled)
        {
            Add-Member -InputObject $obj -Name "Enabled" -Value ([bool]::Parse( $xml.Task.Settings.Enabled )) -MemberType NoteProperty
        }

        $out += $obj
    }

    # Get tasks from subfolders
    $schedule.GetFolder($path).GetFolders(0) | % {
        $out += getTasks($_.Path)
    }

    #Output
    $out
}

$tasks = @()

$schedule = New-Object -ComObject "Schedule.Service"
$schedule.Connect() 

# Start inventory
$tasks += getTasks("\")

# Close com
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($schedule) | Out-Null
Remove-Variable schedule

# Output all tasks
$tasks | Where-Object { $_.WakeToRun -and $_.Enabled }
(It appears that these extra properties are sometimes null or don't exist, so I had to add them conditionally)

So, what were the results? There were two:

Actions     :  
Path        : \Microsoft\Windows\TaskScheduler\Manual Maintenance
Name        : Manual Maintenance
LastRunTime : 30/12/1899 12:00:00 AM
NextRunTime : 30/12/1899 12:00:00 AM
WakeToRun   : True
Enabled     : True

Actions     :  
Path        : \Microsoft\Windows\TaskScheduler\Regular Maintenance
Name        : Regular Maintenance
LastRunTime : 17/10/2013 8:16:31 AM
NextRunTime : 18/10/2013 3:00:04 AM
WakeToRun   : True
Enabled     : True

I think we can exclude the first, as it has never run. The second looks interesting as the NextRunTime is pretty close to the time that my laptop resumed. I'll change the WakeToRun setting on this task to see if the problem goes away.