-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcamera-check.ps1
233 lines (205 loc) · 7.65 KB
/
camera-check.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#Requires -RunAsAdministrator
<#
Tested on Windows 11
This works for the following programs so far:
- Teams
- Zoom
- obs64
What doesn't seem to work, unless you run this script as admin:
- Slack preview window (svchost)
- Teams from webapp in Edge (svchost)
- Camera app (svchost)
- Camtasia Capture (svchost)
#>
Write-Host "Current PSScriptRoot [$PSScriptRoot]"
. $PSScriptRoot/utils.ps1
$location = Get-Location
$handleExe = "$location\Handle\handle64.exe"
function Init-Script {
Write-Message "Using [$handleExe] to search for the handles"
if (!(Test-Path (Split-Path -Path $handleExe))) {
# make directory if it doesn't exist
New-Item -ItemType Directory -Path $location\Handle
}
# check if the file exists
if (!(Test-Path $handleExe))
{
Write-Message "[$handleExe] does not exists so downloading it first"
$zipFile = "$location\Handle\handle.zip"
if (!(Test-Path $zipFile)) {
$downloadUri = "https://download.sysinternals.com/files/Handle.zip"
Write-Message "Downloading handle from [$downloadUri]"
Invoke-RestMethod -Uri $downloadUri -outFile $zipFile
}
$ExtractPath = "$location\Handle\"
$ExtractShell = New-Object -ComObject Shell.Application
$ExtractFiles = $ExtractShell.Namespace($zipFile).Items()
$ExtractShell.NameSpace($ExtractPath).CopyHere($ExtractFiles)
Start-Process $ExtractPath
# unzip the file
Write-Message "Unzipping [$zipFile] to [$location\Handle]"
$unzip = New-Object -ComObject Shell.Application
if (!(Test-Path $handleExe)) {
Write-Message "handle64.exe not found in [$(Get-Location)], cannot continue"
return
}
}
}
# always init the script and download the handle64.exe if not available
Init-Script
function Check-Device {
param(
[object] $device,
[int] $deviceCount
)
# load Physical Device Object Name
$property = Get-PnpDeviceProperty -InstanceId $device.InstanceId -KeyName "DEVPKEY_Device_PDOName"
if ($property.Data.Length -eq 0) {
Write-Message "$deviceCount. No PDON found for [$($device.FriendlyName)] so skipping it"
return
}
Write-Message "$deviceCount. Checking handles in use for [$($device.FriendlyName)] and PDON [$($property.Data)]"
$handles = $(& $handleExe -NoBanner -a "$($property.Data)")
if ($handles -gt 0) {
if ($handles[0].ToLower().StartsWith("no matching handles found")){
Write-Message " - No handles found for [$($device.FriendlyName)]"
}
else {
Write-Message " - Found [$($handles.Length)] handles on $($device.FriendlyName)"
$processes = @()
foreach ($handle in $handles) {
# remove all spaces
$nospaceshandle = $handle.Replace(" ", "")
if ($nospaceshandle.Length -gt 0) {
# Write-Host $handle
$splitted = $handle.Split(" ")
$process = $splitted[0]
if (!($processes.Contains($process))) {
$processes += $process
}
}
}
if ($processes.Length -eq 0) {
Write-Message " - No handles found for [$($device.FriendlyName)]"
}
else {
foreach ($process in $processes) {
Write-Message " - Found process [$($process)] that has a handle on [$($device.FriendlyName)]"
Write-Host "$(Get-Date -Format "HH:mm:ss") " $process -ForegroundColor Green
return $true
}
}
}
}
return $false
}
function Test-Loop {
while ($true) {
Write-Message "Searching for camera devices..."
$devices = Get-PnpDevice -Class Camera
Write-Message "Found [$($devices.Count)] camera devices"
$deviceCount = 0
foreach ($device in $devices) {
$deviceCount++
$result = Check-Device $device $deviceCount
}
Write-Message ""
}
Write-Message "Done"
}
function Get-CameraActive {
Write-Message "Searching for camera devices..."
$devices = Get-PnpDevice -Class Camera,Image
Write-Message "Found [$($devices.Count)] camera devices"
$deviceCount = 0
foreach ($device in $devices) {
$deviceCount++
if ($device.FriendlyName.StartsWith("HP60843E.localdomain")) {
# skip this device
Write-Message " Skipping [$($device.FriendlyName)]"
continue
}
$result = Check-Device $device $deviceCount
if ($result) {
Write-Message "Found active camera device"
return $true
}
}
return $false
}
function CheckCameraOnceWithAction {
Write-Messge "Checking for camera devices from CheckCameraOnceWithAction"
$active = Get-CameraActive
Run-Action $active
}
function LoopWithAction {
while ($true) {
$start = Get-Date
$logonui = Get-Process logonui -ErrorAction SilentlyContinue
if ($null -ne $logonui) {
# if logonui is running, the user is not logged in at all, so if the lights are already off, we can stop the execution
Write-Message "Found logonui process"
$state = getEntityState -entityId $checkEntityIdState
Write-Message "Current entity state is [$($state.state)]"
if ($state.state -ne "on") {
Write-Message "The lights are off already and the user is not authenticated, skipping all checks"
}
}
else {
# check if there are more then 1 monitor available (the laptop itself already is the first one :-) )
$displays = Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorBasicDisplayParams
if ($displays.Count -gt 1) {
Write-Message "Found [$($displays.Count)] monitors"
# check normally
$active = Get-CameraActive
Run-Action $active
}
else {
Write-Message "Found [$($displays.Count)] monitors, skipping camera check"
}
}
# don't run again unless a minute has passed
$end = Get-Date
$duration = $end - $start
if ($duration.TotalSeconds -lt 60) {
Write-Message "Sleeping for $((60-$duration.TotalSeconds).ToString('#')) seconds"
Start-Sleep (60-($duration.TotalSeconds))
}
}
}
# lamp living room to test:
# $entityId = "switch.shelly_plug_s_9a57b1_relay_0"
# actual office camera lights:
$entityId = "script.camera_lights"
$checkEntityIdState = "light.key_light_left"
function Run-Action {
param(
[bool] $active = $false
)
Write-Message "Running action to make the state [$active]"
. $PSScriptRoot/trigger-homeassistant.ps1
$state = getEntityState -entityId $checkEntityIdState
Write-Message "Current entity state is [$($state.state)]"
if ($active) {
if ($state.state -eq "on") {
Write-Message "Already active, no need to do anything"
}
else {
Write-Message "Turning on"
runScript -entityId $entityId
}
}
else {
if ($state.state -eq "off") {
Write-Message "Already off, no need to do anything"
}
else {
Write-Message "Turning off"
runScript -entityId $entityId
}
}
}
$logFileName="CameraCheck.log"
#Test-Loop
LoopWithAction
#CheckCameraOnceWithAction