I wanted to "keep the lights on" in my Study while I was working / playing on my laptop but not triggering the Hue motion sensor near the doorway. There are a couple of other ideas I have in this space including a switch for "work time" to disable motion lighting, but in terms of a more "automated" idea, here's the incomplete solution I have so far...
I plagiarised and updated some StackOverflow PowerShell to update a Virtual Motion Sensor device via Maker API on my HE hub. The original script looks for user input as an indicator that a user is still active. I then used the Maker API to update a virtual motion sensor device to reflect this.
I scheduled the modified PowerShell script to run every minute in a Windows Task Scheduler task, currently updating the HE Virtual motion sensor every time based on a threshold set at the top of the script (5 mins currently), see the variables at the top of the script and the web requests at the end of the script.
The motion sensor device does not have an "Auto Inactive" timeout set as I felt the max option of 1 minute was too small. I plan to implement a longer timeout with an RM rule. I also plan on calling the Maker API less by storing the current active / inactive status locally on the PC and only sending updates (not a major issue). I may also look at implementing some options when shutting down, putting the laptop to sleep or simply locking the laptop. The script may eventually become a Windows Service....
The Virtual Motion Sensor device is then used to keep a motion lighting app / rule "alive" while I work / play at my desk, e.g. I trigger motion as I enter the Study, but then continue to work on my laptop, ensuring downlights overhead remain on while I work.
I still need to iron out a few details including the annoying feature that the PowerShell window keeps popping up every minute when the task runs...
I would also like to expand on this concept to capture other details about my laptop such as battery PC, disk space, etc, but that will be a separate effort and most likely a separate script.
Simon
EDIT: Have included an updated Powershell script below that includes output to a log file. Have also included a slightly modified VB Script supplied by @reachjeffs.
PC_VirtualMotionSensor.ps1
$idleSeconds = 0
$heIPAddress = "192.168.0.123"
$heMakerAPIAppNo = "123"
$heMakerAPIToken = "ABC-XYZ-123"
$heMotionSensorDeviceId = "1234"
$inactiveSecsThreshold = 300
$logFile = "C:\HomeAutomation\Hubitat\PC_VirtualMotionSensor.log"
Function Write-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False)]
[ValidateSet("INFO","WARN","ERROR","FATAL","DEBUG")]
[String]
$Level = "INFO",
[Parameter(Mandatory=$True)]
[string]
$Message,
[Parameter(Mandatory=$False)]
[string]
$logfile
)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
$Line = "$Stamp $Level $Message"
If($logfile) {
Add-Content $logfile -Value $Line
}
Else {
Write-Output $Line
}
}
Add-Type @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace PInvoke.Win32 {
public static class UserInput {
[DllImport("user32.dll", SetLastError=false)]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
[StructLayout(LayoutKind.Sequential)]
private struct LASTINPUTINFO {
public uint cbSize;
public int dwTime;
}
public static DateTime LastInput {
get {
DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
return lastInput;
}
}
public static TimeSpan IdleTime {
get {
return DateTime.UtcNow.Subtract(LastInput);
}
}
public static int LastInputTicks {
get {
LASTINPUTINFO lii = new LASTINPUTINFO();
lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
GetLastInputInfo(ref lii);
return lii.dwTime;
}
}
}
}
'@
#Write-Host ("Last input " + [PInvoke.Win32.UserInput]::LastInput)
#Write-Host ("Idle for " + [PInvoke.Win32.UserInput]::IdleTime)
$idleSeconds = ([TimeSpan]::Parse([PInvoke.Win32.UserInput]::IdleTime)).TotalSeconds
if( $idleSeconds -lt $inactiveSecsThreshold) {
# Active
$activeURI = "http://$heIPAddress/apps/api/$heMakerAPIAppNo/devices/$heMotionSensorDeviceId/active?access_token=$heMakerAPIToken"
Write-Log "INFO" "Active, Last Activity $idleSeconds seconds ago, Active URI: $activeURI" "$logFile"
#Write-Host ("Active URI " + $activeURI)
Invoke-WebRequest -Uri "$activeURI" -UseBasicParsing
}
else {
# Inactive
$inactiveURI = "http://$heIPAddress/apps/api/$heMakerAPIAppNo/devices/$heMotionSensorDeviceId/inactive?access_token=$heMakerAPIToken"
Write-Log "INFO" "Inactive, Last Activity $idleSeconds seconds ago, Inactive URI: $inactiveURI" "$logFile"
#Write-Host ("Inactive URI " + $inactiveURI)
Invoke-WebRequest -Uri "$inactiveURI" -UseBasicParsing
}
PC_VirtualMotionSensor.vbs
Set objShell = CreateObject("Wscript.shell")
objShell.run "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -File " & Chr(34) & "C:\HomeAutomation\Hubitat\PC_VirtualMotionSensor.ps1" & Chr(34), 0
Set objShell = Nothing