Hi,
I'd like to use AHK to control my Philips Hue Lights and have it execute a script which turns off (or dims) the lights if the monitor is turned off, or if the display is sent to standby (the computer does not enter standby).
From the event manager I cannot see any actions that I could trigger using Task Scheduler - so I'm looking to AHK instead.
Does anyone know how I can see if the display is on? The monitor is connected via displayport - so windows is aware when the monitor is turned off as it's effectively disconnected as far as I can tell.
Thanks in advance...
Detect when montior is turned off or idle?
- Masonjar13
- Posts: 1555
- Joined: 20 Jul 2014, 10:16
- Location: Не Россия
- Contact:
Re: Detect when montior is turned off or idle?
I haven't really tried, but you could try using SysGet,monitorCount,80 or SysGet,monitorCount,MonitorCount (there is a difference, so try both) and check if monitorCount = 0. It may not be set to 0 by it turning off though, you'll have to test it.
Re: Detect when montior is turned off or idle?
Thanks for that - doesn't quite seem to work though. It will return 2 with both displays powered on, and 1 with only 1 display powered on. But with both displays powered off it still returns 1.
- Masonjar13
- Posts: 1555
- Joined: 20 Jul 2014, 10:16
- Location: Не Россия
- Contact:
Re: Detect when montior is turned off or idle?
This should work if the monitor is physically turned off, but not sure if it triggers with idle-off. To accommodate for idle-off, you can use A_TimeIdle.
Untested. It should beep when the monitor is off (probably won't work if the audio is integrated into the monitor).
EDIT: Added support for W8+
Untested. It should beep when the monitor is off (probably won't work if the audio is integrated into the monitor).
EDIT: Added support for W8+
Code: Select all
#persistent
onMessage(0x218,"WM_POWERBROADCAST")
global monitorStatus:=1
loop {
while(!monitorStatus){
if(a_index=1)
soundbeep
sleep 100
}
sleep 100
}
return
WM_POWERBROADCAST(wParam,lParam){
static PBT_POWERSETTINGCHANGE:=0x8013
static GUID_MONITOR_POWER_ON:="02731015-4510-4526-99e6-e5a17ebd1aea"
static GUID_CONSOLE_DISPLAY_STATE:="6fe69556-704a-47a0-8f24-c28d936fda47"
if(wParam=PBT_POWERSETTINGCHANGE){
if(numGet(lParam,0)=GUID_MONITOR_POWER_ON || numGet(lParam,0)=GUID_CONSOLE_DISPLAY_STATE)
monitorStatus:=numGet(lParam,20)?1:0
}
}
Re: Detect when montior is turned off or idle?
Brilliant, thanks I'll have a play with it later.
- Masonjar13
- Posts: 1555
- Joined: 20 Jul 2014, 10:16
- Location: Не Россия
- Contact:
Re: Detect when montior is turned off or idle?
I was noticing that messages wouldn't be sent to the script, when I stumbled upon this. After implementing, I'm still not having the script receive any messages (function is never triggered), though. Maybe someone else could help..
Code: Select all
#persistent
GUID_MONITOR_POWER_ON:="02731015-4510-4526-99e6-e5a17ebd1aea"
GUID_CONSOLE_DISPLAY_STATE:="6fe69556-704a-47a0-8f24-c28d936fda47"
global monitorStatus:=1
global cnt:=0
onMessage(0x218,"WM_POWERBROADCAST")
if a_OSVersion in WIN_8,WIN_8.1,WIN_10
rhandle:=dllCall("RegisterPowerSettingNotification","UInt",a_scriptHwnd,"Str",GUID_CONSOLE_DISPLAY_STATE,"Int",0)
else
rhandle:=dllCall("RegisterPowerSettingNotification","UInt",a_scriptHwnd,"Str",GUID_MONITOR_POWER_ON,"Int",0)
setTimer,checkMonitor,1000
return
checkMonitor:
while(!monitorStatus){
if(a_index=1)
msgbox ok
sleep 1000
}
return
WM_POWERBROADCAST(wParam,lParam){
cnt++
monitorStatus:=numGet(lParam,20)?1:0
return
}
- Masonjar13
- Posts: 1555
- Joined: 20 Jul 2014, 10:16
- Location: Не Россия
- Contact:
Re: Detect when montior is turned off or idle?
It's now receiving the messages, but I can NOT find the Data member. Documentation suggests that the first parameter (GUID) is 16-bytes, the second (DWord) is 4-bytes, and the 3rd (DWord) is 4-bytes. The third is suppose to have the answer, so the correct offset should be 20, but that appears to be out of range.
Code: Select all
#persistent
#singleInstance force
GUID_MONITOR_POWER_ON:="02731015-4510-4526-99e6-e5a17ebd1aea"
GUID_CONSOLE_DISPLAY_STATE:="6fe69556-704a-47a0-8f24-c28d936fda47"
global monitorStatus:=1
global newGUID:=""
varSetCapacity(newGUID,16,0)
if a_OSVersion in WIN_8,WIN_8.1,WIN_10
dllCall("Rpcrt4\UuidFromString","Str",GUID_CONSOLE_DISPLAY_STATE,"UInt",&newGUID)
else
dllCall("Rpcrt4\UuidFromString","Str",GUID_MONITOR_POWER_ON,"UInt",&newGUID)
rhandle:=dllCall("RegisterPowerSettingNotification","UInt",a_scriptHwnd,"Str",strGet(&newGUID),"Int",0)
onMessage(0x218,"WM_POWERBROADCAST")
setTimer,checkMonitor,1000
return
checkMonitor:
while(!monitorStatus){
if(a_index=1)
msgbox ok
sleep 1000
}
return
WM_POWERBROADCAST(wParam,lParam){
static PBT_POWERSETTINGCHANGE:=0x8013
if(wParam=PBT_POWERSETTINGCHANGE){
if(subStr(strGet(lParam),1,strLen(strGet(lParam))-1)=strGet(&newGUID)){
fileAppend,% "lParam Data: " numGet(lParam,20,"UInt") "`n`n",file.txt ; Problem testing line
;monitorStatus:=numGet(lParam,20,"UInt")?1:0 ; Problem line
}
}
return
}
Re: Detect when montior is turned off or idle?
Even if the offset is correct, your usage is not. See the second paragraph for "VarOrAddress".numGet(lParam,20,"UInt")
- Masonjar13
- Posts: 1555
- Joined: 20 Jul 2014, 10:16
- Location: Не Россия
- Contact:
Re: Detect when montior is turned off or idle?
I knew I was doing something wrong, I just didn't know WHAT. Thank you, lexikos. The following now works (tested on Windows 8.1)(the offset was correct).lexikos wrote:Even if the offset is correct, your usage is not. See the second paragraph for "VarOrAddress".
Code: Select all
#persistent
#singleInstance force
GUID_MONITOR_POWER_ON:="02731015-4510-4526-99e6-e5a17ebd1aea"
GUID_CONSOLE_DISPLAY_STATE:="6fe69556-704a-47a0-8f24-c28d936fda47"
global monitorStatus:=1
global newGUID:=""
varSetCapacity(newGUID,16,0)
if a_OSVersion in WIN_8,WIN_8.1,WIN_10
dllCall("Rpcrt4\UuidFromString","Str",GUID_CONSOLE_DISPLAY_STATE,"UInt",&newGUID)
else
dllCall("Rpcrt4\UuidFromString","Str",GUID_MONITOR_POWER_ON,"UInt",&newGUID)
rhandle:=dllCall("RegisterPowerSettingNotification","UInt",a_scriptHwnd,"Str",strGet(&newGUID),"Int",0)
onMessage(0x218,"WM_POWERBROADCAST")
setTimer,checkMonitor,500
return
checkMonitor:
while(!monitorStatus){
if(a_index=1)
msgbox ok
sleep 500
}
return
WM_POWERBROADCAST(wParam,lParam){
static PBT_POWERSETTINGCHANGE:=0x8013
if(wParam=PBT_POWERSETTINGCHANGE){
if(subStr(strGet(lParam),1,strLen(strGet(lParam))-1)=strGet(&newGUID)){
;fileAppend,% "lParam Data: " numGet(lParam+0,20,"UInt") "`n`n",file.txt
monitorStatus:=numGet(lParam+0,20,"UInt")?1:0
}
}
return
}
Windows 8+ uses GUID_CONSOLE_DISPLAY_STATE which also reports when the screen is dimmed. 2 = screen dimmed, 1 = screen on, 0 = screen off.
Windows 7 and before use GUID_CONSOLE_DISPLAY_STATE which only supports screen on and off.
Windows XP and below will not work with RegisterPowerNotification as it doesn't exist before Vista, but it should work with that part removed.
Re: Detect when montior is turned off or idle?
Masonjar13 wrote: ↑05 May 2016, 14:23I knew I was doing something wrong, I just didn't know WHAT. Thank you, lexikos. The following now works (tested on Windows 8.1)(the offset was correct).lexikos wrote:Even if the offset is correct, your usage is not. See the second paragraph for "VarOrAddress".A few extra notes to whom it may concern (OP).Code: Select all
#persistent #singleInstance force GUID_MONITOR_POWER_ON:="02731015-4510-4526-99e6-e5a17ebd1aea" GUID_CONSOLE_DISPLAY_STATE:="6fe69556-704a-47a0-8f24-c28d936fda47" global monitorStatus:=1 global newGUID:="" varSetCapacity(newGUID,16,0) if a_OSVersion in WIN_8,WIN_8.1,WIN_10 dllCall("Rpcrt4\UuidFromString","Str",GUID_CONSOLE_DISPLAY_STATE,"UInt",&newGUID) else dllCall("Rpcrt4\UuidFromString","Str",GUID_MONITOR_POWER_ON,"UInt",&newGUID) rhandle:=dllCall("RegisterPowerSettingNotification","UInt",a_scriptHwnd,"Str",strGet(&newGUID),"Int",0) onMessage(0x218,"WM_POWERBROADCAST") setTimer,checkMonitor,500 return checkMonitor: while(!monitorStatus){ if(a_index=1) msgbox ok sleep 500 } return WM_POWERBROADCAST(wParam,lParam){ static PBT_POWERSETTINGCHANGE:=0x8013 if(wParam=PBT_POWERSETTINGCHANGE){ if(subStr(strGet(lParam),1,strLen(strGet(lParam))-1)=strGet(&newGUID)){ ;fileAppend,% "lParam Data: " numGet(lParam+0,20,"UInt") "`n`n",file.txt monitorStatus:=numGet(lParam+0,20,"UInt")?1:0 } } return }
Windows 8+ uses GUID_CONSOLE_DISPLAY_STATE which also reports when the screen is dimmed. 2 = screen dimmed, 1 = screen on, 0 = screen off.
Windows 7 and before use GUID_CONSOLE_DISPLAY_STATE which only supports screen on and off.
Windows XP and below will not work with RegisterPowerNotification as it doesn't exist before Vista, but it should work with that part removed.
Just wanted to say thanks for this. I knew I had to use RegisterPowerSettingNotification and UuidFromString, but there's no way I could have figured out all the byte offsets
Somewhat annoyingly, Windows sends a 0x1 (display on) immediately after RegisterPowerSettingNotification. My workaround is to ignore the event if A_TickCount is n milliseconds after RegisterPowerSettingNotification (since we can't just ignore the first instance since we don't know if this behaviour is the same on all systems).
Also there is a typo at the end of your post.
Last edited by pneumatic on 13 Jan 2020, 15:02, edited 2 times in total.
Re: Detect when montior is turned off or idle?
Masonjar13 wrote: ↑05 May 2016, 14:23Code: Select all
#persistent #singleInstance force GUID_MONITOR_POWER_ON:="02731015-4510-4526-99e6-e5a17ebd1aea" GUID_CONSOLE_DISPLAY_STATE:="6fe69556-704a-47a0-8f24-c28d936fda47" global monitorStatus:=1 global newGUID:="" varSetCapacity(newGUID,16,0) if a_OSVersion in WIN_8,WIN_8.1,WIN_10 dllCall("Rpcrt4\UuidFromString","Str",GUID_CONSOLE_DISPLAY_STATE,"UInt",&newGUID) else dllCall("Rpcrt4\UuidFromString","Str",GUID_MONITOR_POWER_ON,"UInt",&newGUID) rhandle:=dllCall("RegisterPowerSettingNotification","UInt",a_scriptHwnd,"Str",strGet(&newGUID),"Int",0) onMessage(0x218,"WM_POWERBROADCAST") setTimer,checkMonitor,500 return checkMonitor: while(!monitorStatus){ if(a_index=1) msgbox ok sleep 500 } return WM_POWERBROADCAST(wParam,lParam){ static PBT_POWERSETTINGCHANGE:=0x8013 if(wParam=PBT_POWERSETTINGCHANGE){ if(subStr(strGet(lParam),1,strLen(strGet(lParam))-1)=strGet(&newGUID)){ ;fileAppend,% "lParam Data: " numGet(lParam+0,20,"UInt") "`n`n",file.txt monitorStatus:=numGet(lParam+0,20,"UInt")?1:0 } } return }
I just have a question about this:
Code: Select all
monitorStatus := numGet(lParam+0, 20, "UInt") ? 1 : 0
Are you deliberately using the ternary operator to also include the case where the NumGet returns ANY non-blank or non-zero value?
In other words, NumGet could return 1 or 12345 or "some string" and you still intend for monitorStatus to become 1?
Because on my test systems, monitorStatus is always 1 for display on. I'd like to know if I'm being too strict in requiring that monitorStatus must be exactly 1.
In other words, did you do some testing and found that NumGet sometimes returns some other non-blank, non-zero value when the monitor is turned on?
Thanks
- Masonjar13
- Posts: 1555
- Joined: 20 Jul 2014, 10:16
- Location: Не Россия
- Contact:
Re: Detect when montior is turned off or idle?
Considering this is a 4 year old thread.. Idk. But taking a look at the structure, if you're using 7 or below, GUID_MONITOR_POWER_ON is needed, which reports either 0 or 1. But, for 8+, GUID_CONSOLE_DISPLAY_STATE is used, which has 0, 1, or 2, where 2 = dimmed. How I had written it, dimmed is synonymous with on.
I have no recollection of even writing this, so couldn't tell you why I chose to do so.
I have no recollection of even writing this, so couldn't tell you why I chose to do so.
Re: Detect when montior is turned off or idle?
Masonjar13 wrote: ↑13 Jan 2020, 15:34Considering this is a 4 year old thread.. Idk. But taking a look at the structure, if you're using 7 or below, GUID_MONITOR_POWER_ON is needed, which reports either 0 or 1. But, for 8+, GUID_CONSOLE_DISPLAY_STATE is used, which has 0, 1, or 2, where 2 = dimmed. How I had written it, dimmed is synonymous with on.
I see, thanks. I will just compare it to their explicit values for now. I don't actually need to detect dimming (my screen doesn't even dim and Windows still sends 0x2 about 10 seconds before turning the display off)
Also just adding some random possibly useful info for anyone else who happens to be reading this thread in future
- At the start of msg monitor func, I'm using Critical , 1000 to ensure window messages are buffered for 1000ms instead of being discarded (because in some cases multiple messages arrive very quickly)
- At the start of msg monitor func, only process it if ( hwnd = A_ScriptHwnd ) otherwise it gets called for each GUI window that is open (however the next bit will solve that too)
- Do your payload in a separate thread eg. with SetTimer , MyThread, -1 and make MyThread subroutine Critical. This further guards against multiple messages clashing and code being executed out of order.
- At the end of msg monitor func, return true according to WinAPI "An application should return TRUE if it processes this message."
Re: Detect when montior is turned off or idle?
@pneumatic, can you please post your updated script? I don't really follow some of the changes you mentioned.
Who is online
Users browsing this forum: Bing [Bot] and 174 guests