[RELEASE] PC Motion Sensor

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
4 Likes

This is an interesting idea. I may have to do something similar. Thank you for sharing.

The easiest way I've seen to get rid of the PS window popping up is to use a VBScript "helper" to launch the PS script from your scheduled task. The following is from memory, so you may need to tweak it a little.

In this example, C:\Tasks contains your PS script (named PCMotionSensor.ps1 here) and a new VBScript Helper.vbs. Adjust path and filenames as needed.

Task action:
Program - C:\WINDOWS\System32\wscript.exe
Arguments - "C:\Tasks\Helper.vbs"

Contents of Helper.vbs:
Set objShell = CreateObject("Wscript.shell")
objShell.run "powershell -File " & Chr(34) & "C:\Tasks\PCMotionSensor.ps1" & Chr(34), 0
Set objShell = Nothing

Good luck and thanks again for sharing the idea.

Jeff

2 Likes

That did the trick, thanks @reachjeffs.

I think the only change I needed to make was the Execution Policy Bypass switch. I'll keep playing with the script some more to introduce some logging and config settings, etc. Then I might write up some more details around setting it up and put it in my GitHub repository.

1 Like

Very nice. I'll be following this topic closely, and will give it a go when it's matured. Thanks

1 Like

Thanks @mike. I've made a small update to the original post with the logging and my version of the VB Script.

Uploaded the scripts and some instructions to my GitHub repository. Enjoy!!

2 Likes

Nice work man !
I achieved something similar using an Iris motion sensor aimed at my mouse pad !
This keeps my motion lighting rule engaged.

Your solution is WAY more creative. :blush::pray:

2 Likes

Thanks, I had also seen someone with a motion sensor under the desk, but wanted something a little more consistent. It also helped me learn a few more things in PowerShell, so it was a good exercise in a few different ways.

Well, it looks mature enough for me now! Will install tomorrow.

Question: if I remote desktop into the computer, will it record as activity and turn on my lights?

2 Likes

Not sure TBH, will be interesting to see...

1 Like

Actually I think I get now what you meant about turning on your lights, i.e. you were worried about it happening while you are not in the room / house. That will come down to how you use the virtual motion sensor device within rules on your HE hub. I have mine setup to keep a motion lighting "rule" active, rather than being the trigger, the trigger to turn the lights on is still my physical Hue motion sensor.

It should be pretty easy to also check for my presense = true, before turning/keeping lights on.

1 Like

Thanks, I've got it working.

  1. Instructions were easy to follow until I got to an issue, which is easily rectified. The file PC_VirtualMotionSensor.vbs contains a reference to your subdirectory. My setup didnt work until I pointed it to my subdirectory. It would be good to fix that, if possible.
  2. Is there a way to create a task that sends inactive upon shutdown? Thinking about this, if the computer shutsdown, it wont send an inactive. Lights will remain on. This may be helped by 3 & 4.
  3. There is also a need to turn off "auto inactive" on the device page. It's a pity that we can have auto inactive set at 5 minutes. Someone on these forums was asking recently for the ability to set it at 5 minutes. Was that you?
  4. It could be possible to ping the IP of the computer, and if it's not present, then set the device as inactive.
2 Likes

Glad you got it working.

1 - Good pickup, I'll update the instructions or code, I should be able to leave out the directory I would expect.

2 - I'd been thinking the same thing for a couple of different things like shutdowns, screen saver, putting the pc into sleep, etc. Certainly on my list.

3 - Again good pickup, I did that on my setup but forgot to include it in the instructions. I plan to setup a rule to do it, and had the same thought as you about how it was a shame there was no 5 minute auto-inactive. I don't think it was me requesting the change...

4 - Good idea, not sure whether web pinger could be useful here or something similar...

Thanks again for testing it out and giving the feedback.

Simon

1 Like

I updated the VB Script to remove the full path to the Powershell script, but needed to update the Task Scheduler Action setup and instructions to include setting the Start-in folder for the action.

I also did various updates to the Readme, but nothing that you would need to change, just wording used and some steps to validate the install and setup.

I created an enhancement ticket on my GitHub repository to look at the inactive update when the PC shuts down. I might setup a few more at some point to look at some of the other scenarios I was interested in like when the PC is locked or the screen saver is activated.

I took a look at web pinger and it wants a web URL, so not something I could use to check for the PC being available, but I'm sure there's something else that does that. I'll keep looking.

Simon

what about using something like this, and while your computer is successfully pingable, it'll keep the lights on

1 Like

That's what I had in mind, however, I've gone with auto off in Hubitat after 1 minute, and getting the powershell script to check every minute, so that means it's going to auto off 1 minute after the last "active". It's been working well in my office, which doubles as my gaming station in the evenings.

It's already added into my Node Red logic, and working very well. Thanks @sburke781

1 Like

Great to hear it's working for you @mike. I'd be worried about the auto-off switching it off when it shouldn't, but sometimes you need to just try these things and if they work just run with it.

@dadarkgtprince - I'm not sure whether the http presence app/ driver would work if you don't have an endpoint (web server) running on the PC? I didn't read too much of the description, so will keep reading...

you can alter it a bit to fit your needs. i use it to ping my phone, which i'm not hosting a web server on. i think code 408 is if it can't be reached, and code 200 is if it can reach it but no active web server to ping. i forgot the exact codes, but it's something like that. updating the driver to look for those codes without hosting a web server has worked great for me

1 Like

I have installed both the HTTP Presence and iPhone Presence drivers, but infortunately I get a connection timeout for my laptop. The iPhone one works with my mobile phone (Pixel 2XL) and the HTTP one works with checking my Grafana install on my rpi is up, so they will come in handy, just not for checking my laptop is online.

I might just write a little script to run on my laptop as a heartbeat, essentially just updating a presence device on the hub every X minutes, rather than the HE hub calling out to the laptop.

Thanks for the tip on the drivers though, like I said, they'll come in handy for other things I'd like to setup.