5

I'm attempting to automatize lights around the man cave, ideally i would like to have the lights to be on as long as the computer screens are on and the lights go off when screensaver kicks in, without using any additional resident software.

So far i managed to catch events 4802 (screensaver start) and 4803 (screensaver stop) and using task scheduler and curl script to fire http request to the machine that handles the lights, and it works, to an extent.

Problem is that whenever the machine enters low power state, screensaver gets killed and 4803 is logged, and i can't differentiate between screensaver being killed by user input or by the windows entering low power state.

Basically i need to catch some events that happen when the screens turn off and on. Is there anything (in generic win7 installation) i could hitch the task on to?

Thanks.

EDIT:

I apologize to people who already answered this question, my question was not worded clearly enough. The machine in question does not actually enter sleep state, hence there is no event 42(kernel-power) or event 1(power-troubleshooter) generated in event logs, the machine only enters some kind of low-power state where it consumes about 53W, instead of the usual 180W, while i could use this to track the power state of this one specific machine, i would not be able to use the same method of detection for the other 3 computers in the room, since they do not have the required hardware (UPS) connected to them.

What i am looking for, is events that occur whenever monitors of the computer are powered off and back on by windows (not by their respective power buttons) as a result of the windows power management. Basically anything that would allow me to synchronize the lights in the cave with the state of the monitors and allow the task scheduler to fire off script that manipulates the lights.

Dingo
  • 151
  • 1
  • 6
  • Possible duplicate of [How can I tell if my computer went to sleep?](http://superuser.com/questions/485131/how-can-i-tell-if-my-computer-went-to-sleep) – DavidPostill Dec 16 '15 at 17:07
  • this sounds like a cpu c-state. But I have no clue how to query it. Two alternatives come to mind: one involving a super-low res out of focus webcam pointed at a screen and a routine that polls the image and averages the pixels for intensity (the light automation machine would handle this); a light sensor for outdoor lights attached to the corner of a screen or perhaps via an optical cable (to avoid false positives from the lights themselves). – Yorik Dec 16 '15 at 22:46
  • my problem is that i need to make it work on 4 separate computers, and eventually 6, each controlling its own light, i really wanted to avoid having resident running in the background just to do this. And i had my share of crazy ideas .. like copper coil around the video cable to detect presence (or absence) of video signal .. but that's like using sledge hammer on finishing nails, there has to be better solution - somewhere, something clever, maybe a service that shuts down when the monitors are off, and starts again when they on, something clever enough i'd never think of it ... – Dingo Dec 17 '15 at 00:27
  • It sounds like you have a strong preference for a hardware solution, but maybe WMI's [Win32_ComputerSystem](https://msdn.microsoft.com/en-us/library/aa394102(v=vs.85).aspx).PowerState or [Win32_PowerMeter](https://msdn.microsoft.com/en-us/library/dd904529(v=vs.85).aspx) might help. Also see the [Win32_PowerMeterEvent](https://msdn.microsoft.com/en-us/library/dd904530(v=vs.85).aspx), which you can use to programmatically respond to changes in the power meter without polling. – Zev Spitz Dec 17 '15 at 06:54
  • Have you seen [this](http://stackoverflow.com/a/3913079/111794)? – Zev Spitz Dec 17 '15 at 07:13

1 Answers1

3

Hopefully you've found another solution for this task by now, either way this was an interesting problem I wanted to take a crack at, and maybe someone else will benefit. Rather than trying to monitor screensaver states, my solution uses the "idleness" monitoring options within task scheduler. While this was built on Windows 10, I believe the functions used should be the same in W7.

Overview

  • The "Idle" windows scheduled task triggers the "Idle" script, and uses the built in idle options on the conditions tab to wait till computer is idle to start, and then stop if/when the computer ceases to be idle.
  • The "Idle" script triggers the "Watcher" script, takes your chosen action (dim lights) then continues to run waiting on explorer.exe to exit, which will (should) never happen.
  • The "Watcher" script finds the process for the "Idle" script and waits on it to exit before completing its action (turn lights back on).

Basic path of events

  1. The "Idle" task attempts to run every 1 minute, but only will run if PC has been idle for your chosen amount of time.
  2. "Idle" task runs the "Idle" script. The script triggers "Watcher" script, triggers your chosen action (dim lights), and then uses the "wait-process" cmdlet to wait for explorer.exe to exit, which should never happen assuming Windows is stable, and therefor continues to run as long as PC is idle.
  3. "Watcher" script starts and watches for the process of the "Idle" script to exit.
  4. User input causes PC to exit idle state, killing the "Idle" task, therefore killing the powershell.exe process of the "Idle" script.
  5. "Watcher" script sees the "Idle" script process exit, turns the lights back on and then exits.

Setup

  1. Create your script folder which will contain both scripts.
  2. Copy the powershell executable into your new folder folder:

    "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" >>>> C:\scripts\dimlights\
    
  3. Create "Idle.ps1" and paste in script. On the first line of the script, specify your folder containing scripts and copy of powershell.exe:

    $myScriptDir = "C:\scripts\dimlights\"
    #============= Variables ==================
    $logfile = join-path $myScriptDir log.txt
    $nid = (Get-Process explorer).Id
    $mydate = Get-date
    
    #============= Code ==================
    start-process powershell.exe -argumentlist "-NonInteractive –ExecutionPolicy Bypass -file $myScriptDir\watcher.ps1"
    echo "$mydate - PID: $nid - Idle start action" | out-file -Append $logfile
    #----------------Insert chosen action code here----------------
    Wait-Process $nid
    

    $mydate = Get-date echo "$mydate - PID: $nid - Last line in Idle, should not have been reached." | out-file -Append $logfile

  4. Create "Watcher.ps1" in same folder and paste in script, again specifying the folder:

    $myScriptDir = "C:\scripts\dimlights\"
    #============= Variables ==================
    $myPSPath = join-path $myScriptDir powershell.exe
    $logfile = join-path $myScriptDir log.txt
    $nid = (Get-Process powershell | Where-Object {$_.path -eq $myPSPath}).id
    $mydate = Get-date
    
    #============= Code ==================
    echo "$mydate - PID: $nid - Watcher started" | out-file -Append $logfile
    if ($nid -ne $null){
    Wait-Process -id $nid
    $mydate = Get-date
    echo "$mydate - PID: $nid - Watcher end action" | out-file -Append $logfile
    #---------------- Insert chosen wake up action code here ----------------
    }
    else {
    echo "$mydate - PID: $nid - No such process, exiting watcher." | out-file -Append $logfile
    

    }

  5. Open task scheduler as admin and create the "Idle" task;

    • On the general tab, choose to "change user or group" that the task is run as.
    • In the "Select user or group" window, click the advanced button, then "find now".
    • Scroll down to find the "SYSTEM" account, click once to highlight, then click OK.
    • Click OK again.
    • Switch to the triggers tab and create a one time trigger a few minutes in the future.
    • Under advanced settings, check the box to "Repeat task every:" and then type into the box "1 minute", and change the "for a duration of" to indefinitely.
    • Switch to the actions tab and add a new action;
    • The "program/script" box should contain the path to your powershell.exe that you copied to the scripts folder earlier.
    • Add the following arguments, replacing the file path with your own:

      -ExecutionPolicy Bypass -file "C:\scripts\idle.ps1"

    • Go to the conditions tab and check the box to only start if idle, setting the idle time to your chosen time out.

    • Check the other two boxes under the idle section "Stop if the computer ceases to be idle", and "Restart if the idle state resume".
    • Click "OK" to save and close the scheduled task.

Hope someone finds this useful!

Tim

Smithy
  • 69
  • 1
  • 4
  • I ended up using combination of things to figure out whether the lights should turn off, on event 4802 script fires to dim lights to 25%, simultaneously the UPS gets polled for power usage every 10 seconds until the draw drops below 65 watts (this computer draws 61 watts when idle with monitors off), then the lights turn off. I had trouble minimizing the delay between screens coming up and the lights turning on, but i think your approach gives me a new idea how to deal with it. Thanks :) – Dingo Feb 16 '17 at 01:54
  • Glad I could help! And yes I would think this should make for a pretty quick firing of whatever control command you're using for the lights, since the next lines of code run essentially instantaneously after the wait-process detects the exit of the idle script. I take it you're relying on UPS power draw readings to detect the exit from low power state? – Smithy Feb 17 '17 at 01:50
  • Aye, each monitor draws about 15 watts, and they all come up at the same time. That is enough increase in power draw (~60W) to eliminate cases of windows updating in middle of the night and the increased power consumption triggering the lights. – Dingo Feb 18 '17 at 01:59