@cfour-hi/dsh-plugin-devicex 0.0.0-stage → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +38 -2
- package/client.js +1008 -0
- package/cordis.patch.yml +3 -0
- package/host/actions-darwin.js +1261 -0
- package/host/actions-win32.js +1333 -0
- package/host/actions.js +45 -0
- package/host/benchmark.js +413 -0
- package/host/cleanup-darwin.js +715 -0
- package/host/cleanup-win32.js +760 -0
- package/host/cleanup.js +62 -0
- package/host/collector-darwin.js +2995 -0
- package/host/collector-win32.js +2060 -0
- package/host/collector.js +71 -0
- package/host/footprint-core.js +399 -0
- package/host/footprint-darwin.js +663 -0
- package/host/footprint-win32.js +411 -0
- package/host/footprint.js +284 -0
- package/host/proc.js +232 -0
- package/host/tool.js +254 -0
- package/host/transport.js +316 -0
- package/host/uninstall.js +244 -0
- package/host/win/hardware.ps1 +501 -0
- package/host/win/metrics.ps1 +535 -0
- package/host/win/network.ps1 +322 -0
- package/host/win/processes.ps1 +169 -0
- package/host/win/security.ps1 +197 -0
- package/host/win/software.ps1 +119 -0
- package/host/win/system.ps1 +279 -0
- package/host/win.js +187 -0
- package/icon.svg +18 -0
- package/index.js +67 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +60 -4
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# Device X - Windows hardware inventory (CIM/PnP).
|
|
2
|
+
#
|
|
3
|
+
# Emits exactly one JSON document on stdout (no other stdout text).
|
|
4
|
+
# Read-only: no admin rights, no network calls, no external modules.
|
|
5
|
+
$ErrorActionPreference = 'Stop'
|
|
6
|
+
|
|
7
|
+
$errors = @()
|
|
8
|
+
|
|
9
|
+
function To-LongOrNull($value) {
|
|
10
|
+
if ($null -eq $value) { return $null }
|
|
11
|
+
try { return [long]$value } catch { return $null }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function ConvertFrom-IdList($value) {
|
|
15
|
+
# WmiMonitorID string fields are arrays of ASCII code units ended by 0.
|
|
16
|
+
if ($null -eq $value) { return $null }
|
|
17
|
+
try {
|
|
18
|
+
$characters = @()
|
|
19
|
+
foreach ($item in @($value)) {
|
|
20
|
+
$code = [int]$item
|
|
21
|
+
if ($code -le 0) { break }
|
|
22
|
+
$characters += [char]$code
|
|
23
|
+
}
|
|
24
|
+
$text = (-join $characters).Trim()
|
|
25
|
+
if ($text.Length -gt 0) { return $text }
|
|
26
|
+
return $null
|
|
27
|
+
} catch { return $null }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function Get-GpuRegistryMemoryBytes {
|
|
31
|
+
# Win32_VideoController.AdapterRAM is UInt32 and wraps/zeroes for cards with
|
|
32
|
+
# more than 4 GB. The display class registry keys store the exact byte size in
|
|
33
|
+
# `HardwareInformation.qwMemorySize` (QWORD), keyed by DriverDesc (which is
|
|
34
|
+
# normally identical to Win32_VideoController.Name).
|
|
35
|
+
$memoryByName = @{}
|
|
36
|
+
$classRoot = 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}'
|
|
37
|
+
try {
|
|
38
|
+
$classKeys = @(Get-ChildItem -Path $classRoot -ErrorAction Stop)
|
|
39
|
+
} catch {
|
|
40
|
+
return $memoryByName
|
|
41
|
+
}
|
|
42
|
+
foreach ($classKey in $classKeys) {
|
|
43
|
+
if ([string]$classKey.PSChildName -notmatch '^\d{4}$') { continue }
|
|
44
|
+
try {
|
|
45
|
+
$properties = Get-ItemProperty -Path $classKey.PSPath -ErrorAction Stop
|
|
46
|
+
$memory = To-LongOrNull $properties.'HardwareInformation.qwMemorySize'
|
|
47
|
+
if ($null -eq $memory -or $memory -le 0) { continue }
|
|
48
|
+
$description = [string]$properties.DriverDesc
|
|
49
|
+
if ($description) { $memoryByName[$description] = $memory }
|
|
50
|
+
} catch { }
|
|
51
|
+
}
|
|
52
|
+
return $memoryByName
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# --------------------------------------------------------------------- CPU --
|
|
56
|
+
$cpu = $null
|
|
57
|
+
try {
|
|
58
|
+
$processors = @(Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop)
|
|
59
|
+
if ($processors.Count -gt 0) {
|
|
60
|
+
$first = $processors[0]
|
|
61
|
+
$physicalCores = 0
|
|
62
|
+
$logicalCores = 0
|
|
63
|
+
$l2CacheKB = 0
|
|
64
|
+
$l3CacheKB = 0
|
|
65
|
+
foreach ($processor in $processors) {
|
|
66
|
+
if ($null -ne $processor.NumberOfCores) { $physicalCores += [int]$processor.NumberOfCores }
|
|
67
|
+
if ($null -ne $processor.NumberOfLogicalProcessors) { $logicalCores += [int]$processor.NumberOfLogicalProcessors }
|
|
68
|
+
if ($null -ne $processor.L2CacheSize) { $l2CacheKB += [long]$processor.L2CacheSize }
|
|
69
|
+
if ($null -ne $processor.L3CacheSize) { $l3CacheKB += [long]$processor.L3CacheSize }
|
|
70
|
+
}
|
|
71
|
+
$cpu = [ordered]@{
|
|
72
|
+
name = [string]$first.Name
|
|
73
|
+
manufacturer = [string]$first.Manufacturer
|
|
74
|
+
physicalCores = $physicalCores
|
|
75
|
+
logicalCores = $logicalCores
|
|
76
|
+
maxClockSpeedMHz = To-LongOrNull $first.MaxClockSpeed
|
|
77
|
+
architecture = To-LongOrNull $first.Architecture
|
|
78
|
+
l2CacheKB = $l2CacheKB
|
|
79
|
+
l3CacheKB = $l3CacheKB
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch { $errors += ('Win32_Processor: ' + $_.Exception.Message) }
|
|
83
|
+
|
|
84
|
+
# ------------------------------------------------------------------ memory --
|
|
85
|
+
$modules = @()
|
|
86
|
+
$totalMemoryBytes = $null
|
|
87
|
+
try {
|
|
88
|
+
$physicalModules = @(Get-CimInstance -ClassName Win32_PhysicalMemory -ErrorAction Stop)
|
|
89
|
+
foreach ($module in $physicalModules) {
|
|
90
|
+
$modules += [ordered]@{
|
|
91
|
+
capacityBytes = To-LongOrNull $module.Capacity
|
|
92
|
+
speedMHz = To-LongOrNull $module.Speed
|
|
93
|
+
configuredSpeedMHz = To-LongOrNull $module.ConfiguredClockSpeed
|
|
94
|
+
manufacturer = [string]$module.Manufacturer
|
|
95
|
+
partNumber = [string]$module.PartNumber
|
|
96
|
+
deviceLocator = [string]$module.DeviceLocator
|
|
97
|
+
smbiosType = To-LongOrNull $module.SMBIOSMemoryType
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch { $errors += ('Win32_PhysicalMemory: ' + $_.Exception.Message) }
|
|
101
|
+
|
|
102
|
+
$arrayMaxCapacityBytes = $null
|
|
103
|
+
try {
|
|
104
|
+
$memoryArrays = @(Get-CimInstance -ClassName Win32_PhysicalMemoryArray -ErrorAction Stop)
|
|
105
|
+
if ($memoryArrays.Count -gt 0 -and $null -ne $memoryArrays[0].MaxCapacityEx) {
|
|
106
|
+
$maxKb = To-LongOrNull $memoryArrays[0].MaxCapacityEx
|
|
107
|
+
if ($null -ne $maxKb) { $arrayMaxCapacityBytes = $maxKb * 1024 }
|
|
108
|
+
}
|
|
109
|
+
} catch { $arrayMaxCapacityBytes = $null }
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
|
|
113
|
+
$totalMemoryBytes = To-LongOrNull $computerSystem.TotalPhysicalMemory
|
|
114
|
+
} catch { $errors += ('Win32_ComputerSystem: ' + $_.Exception.Message) }
|
|
115
|
+
|
|
116
|
+
if ($null -eq $totalMemoryBytes -and $modules.Count -gt 0) {
|
|
117
|
+
$sum = [long]0
|
|
118
|
+
$anyModule = $false
|
|
119
|
+
foreach ($module in $modules) {
|
|
120
|
+
if ($null -ne $module.capacityBytes) { $sum += [long]$module.capacityBytes; $anyModule = $true }
|
|
121
|
+
}
|
|
122
|
+
if ($anyModule) { $totalMemoryBytes = $sum }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
$memory = [ordered]@{
|
|
126
|
+
totalBytes = $totalMemoryBytes
|
|
127
|
+
arrayMaxCapacityBytes = $arrayMaxCapacityBytes
|
|
128
|
+
modules = $modules
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# ------------------------------------------------------- GPU and displays --
|
|
132
|
+
$gpuMemoryByName = Get-GpuRegistryMemoryBytes
|
|
133
|
+
$videoControllers = @()
|
|
134
|
+
try {
|
|
135
|
+
$adapters = @(Get-CimInstance -ClassName Win32_VideoController -ErrorAction Stop)
|
|
136
|
+
foreach ($adapter in $adapters) {
|
|
137
|
+
$adapterName = [string]$adapter.Name
|
|
138
|
+
$adapterRamBytes = To-LongOrNull $adapter.AdapterRAM
|
|
139
|
+
$adapterRamSource = $null
|
|
140
|
+
if ($null -ne $adapterRamBytes) { $adapterRamSource = 'cim' }
|
|
141
|
+
$registryMemory = $null
|
|
142
|
+
if ($adapterName -and $gpuMemoryByName.ContainsKey($adapterName)) {
|
|
143
|
+
$registryMemory = $gpuMemoryByName[$adapterName]
|
|
144
|
+
}
|
|
145
|
+
if ($null -ne $registryMemory -and $registryMemory -gt 0) {
|
|
146
|
+
$adapterRamBytes = $registryMemory
|
|
147
|
+
$adapterRamSource = 'registry'
|
|
148
|
+
} elseif ($null -ne $adapterRamBytes -and $adapterRamBytes -ge 4000000000) {
|
|
149
|
+
# UInt32 AdapterRAM saturates at 4 GB; do not report a bogus number.
|
|
150
|
+
$adapterRamBytes = $null
|
|
151
|
+
$adapterRamSource = 'cim-capped'
|
|
152
|
+
$errors += ('GPU memory for "' + $adapterName + '" is truncated at 4 GB (Win32_VideoController AdapterRAM); registry qwMemorySize unavailable')
|
|
153
|
+
}
|
|
154
|
+
$videoControllers += [ordered]@{
|
|
155
|
+
name = $adapterName
|
|
156
|
+
adapterRamBytes = $adapterRamBytes
|
|
157
|
+
adapterRamSource = $adapterRamSource
|
|
158
|
+
driverVersion = [string]$adapter.DriverVersion
|
|
159
|
+
videoProcessor = [string]$adapter.VideoProcessor
|
|
160
|
+
currentWidth = To-LongOrNull $adapter.CurrentHorizontalResolution
|
|
161
|
+
currentHeight = To-LongOrNull $adapter.CurrentVerticalResolution
|
|
162
|
+
currentRefreshHz = To-LongOrNull $adapter.CurrentRefreshRate
|
|
163
|
+
status = [string]$adapter.Status
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} catch { $errors += ('Win32_VideoController: ' + $_.Exception.Message) }
|
|
167
|
+
|
|
168
|
+
$monitors = @()
|
|
169
|
+
try {
|
|
170
|
+
$monitorIds = @(Get-CimInstance -Namespace 'root/wmi' -ClassName WmiMonitorID -ErrorAction Stop)
|
|
171
|
+
foreach ($monitor in $monitorIds) {
|
|
172
|
+
$monitors += [ordered]@{
|
|
173
|
+
name = ConvertFrom-IdList $monitor.UserFriendlyName
|
|
174
|
+
manufacturer = ConvertFrom-IdList $monitor.ManufacturerName
|
|
175
|
+
serial = ConvertFrom-IdList $monitor.SerialNumberID
|
|
176
|
+
active = [bool]$monitor.Active
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch { $errors += ('WmiMonitorID: ' + $_.Exception.Message); $monitors = @() }
|
|
180
|
+
|
|
181
|
+
# ---------------------------------------------------------------- storage --
|
|
182
|
+
$physicalDisks = @()
|
|
183
|
+
try {
|
|
184
|
+
$storageDisks = @(Get-PhysicalDisk -ErrorAction Stop)
|
|
185
|
+
foreach ($disk in $storageDisks) {
|
|
186
|
+
$mediaType = [string]$disk.MediaType
|
|
187
|
+
$physicalDisks += [ordered]@{
|
|
188
|
+
name = [string]$disk.FriendlyName
|
|
189
|
+
model = [string]$disk.Model
|
|
190
|
+
sizeBytes = To-LongOrNull $disk.Size
|
|
191
|
+
mediaType = $mediaType
|
|
192
|
+
smartStatus = [string]$disk.HealthStatus
|
|
193
|
+
serial = [string]$disk.SerialNumber
|
|
194
|
+
interfaceType = [string]$disk.BusType
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} catch { $errors += ('Get-PhysicalDisk: ' + $_.Exception.Message); $physicalDisks = @() }
|
|
198
|
+
|
|
199
|
+
$diskDrives = @()
|
|
200
|
+
try {
|
|
201
|
+
$drives = @(Get-CimInstance -ClassName Win32_DiskDrive -ErrorAction Stop)
|
|
202
|
+
foreach ($drive in $drives) {
|
|
203
|
+
$diskDrives += [ordered]@{
|
|
204
|
+
model = [string]$drive.Model
|
|
205
|
+
sizeBytes = To-LongOrNull $drive.Size
|
|
206
|
+
mediaType = [string]$drive.MediaType
|
|
207
|
+
interfaceType = [string]$drive.InterfaceType
|
|
208
|
+
serialNumber = [string]$drive.SerialNumber
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch { $errors += ('Win32_DiskDrive: ' + $_.Exception.Message) }
|
|
212
|
+
|
|
213
|
+
$volumes = @()
|
|
214
|
+
try {
|
|
215
|
+
$logicalDisks = @(Get-CimInstance -ClassName Win32_LogicalDisk -ErrorAction Stop)
|
|
216
|
+
foreach ($logicalDisk in $logicalDisks) {
|
|
217
|
+
$driveType = 0
|
|
218
|
+
if ($null -ne $logicalDisk.DriveType) { $driveType = [int]$logicalDisk.DriveType }
|
|
219
|
+
if ($driveType -ne 2 -and $driveType -ne 3) { continue }
|
|
220
|
+
$volumes += [ordered]@{
|
|
221
|
+
deviceId = [string]$logicalDisk.DeviceID
|
|
222
|
+
name = [string]$logicalDisk.VolumeName
|
|
223
|
+
fs = [string]$logicalDisk.FileSystem
|
|
224
|
+
totalBytes = To-LongOrNull $logicalDisk.Size
|
|
225
|
+
freeBytes = To-LongOrNull $logicalDisk.FreeSpace
|
|
226
|
+
driveType = $driveType
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} catch { $errors += ('Win32_LogicalDisk: ' + $_.Exception.Message) }
|
|
230
|
+
|
|
231
|
+
# ---------------------------------------------------------------- battery --
|
|
232
|
+
$battery = $null
|
|
233
|
+
try {
|
|
234
|
+
$batteries = @(Get-CimInstance -ClassName Win32_Battery -ErrorAction Stop)
|
|
235
|
+
if ($batteries.Count -gt 0) {
|
|
236
|
+
$batteryDevice = $batteries[0]
|
|
237
|
+
$percent = $null
|
|
238
|
+
if ($null -ne $batteryDevice.EstimatedChargeRemaining) { $percent = [int]$batteryDevice.EstimatedChargeRemaining }
|
|
239
|
+
$condition = $null
|
|
240
|
+
if ($batteryDevice.Status) {
|
|
241
|
+
$condition = [string]$batteryDevice.Status
|
|
242
|
+
if ($condition -eq 'OK') { $condition = 'Normal' }
|
|
243
|
+
}
|
|
244
|
+
$designMwh = To-LongOrNull $batteryDevice.DesignCapacity
|
|
245
|
+
$fullMwh = To-LongOrNull $batteryDevice.FullChargeCapacity
|
|
246
|
+
$voltageMv = To-LongOrNull $batteryDevice.DesignVoltage
|
|
247
|
+
if ($null -eq $designMwh -or $null -eq $fullMwh) {
|
|
248
|
+
try {
|
|
249
|
+
$staticBattery = @(Get-CimInstance -Namespace 'root/wmi' -ClassName BatteryStaticData -ErrorAction Stop)
|
|
250
|
+
if ($staticBattery.Count -gt 0) {
|
|
251
|
+
if ($null -eq $designMwh -and $null -ne $staticBattery[0].DesignedCapacity) {
|
|
252
|
+
$designMwh = [double]$staticBattery[0].DesignedCapacity
|
|
253
|
+
}
|
|
254
|
+
if ($null -eq $voltageMv -and $null -ne $staticBattery[0].DesignedVoltage) {
|
|
255
|
+
$voltageMv = [double]$staticBattery[0].DesignedVoltage
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
$fullBattery = @(Get-CimInstance -Namespace 'root/wmi' -ClassName BatteryFullChargedCapacity -ErrorAction Stop)
|
|
259
|
+
if ($fullBattery.Count -gt 0 -and $null -eq $fullMwh -and $null -ne $fullBattery[0].BatteryFullChargedCapacity) {
|
|
260
|
+
$fullMwh = [double]$fullBattery[0].BatteryFullChargedCapacity
|
|
261
|
+
}
|
|
262
|
+
} catch { }
|
|
263
|
+
}
|
|
264
|
+
$designCapacityMah = $null
|
|
265
|
+
$maxCapacityMah = $null
|
|
266
|
+
$healthPercent = $null
|
|
267
|
+
if ($null -ne $designMwh -and $null -ne $voltageMv -and $voltageMv -gt 0) {
|
|
268
|
+
$designCapacityMah = [int][math]::Round(([double]$designMwh * 1000.0) / [double]$voltageMv)
|
|
269
|
+
}
|
|
270
|
+
if ($null -ne $fullMwh -and $null -ne $voltageMv -and $voltageMv -gt 0) {
|
|
271
|
+
$maxCapacityMah = [int][math]::Round(([double]$fullMwh * 1000.0) / [double]$voltageMv)
|
|
272
|
+
}
|
|
273
|
+
if ($null -ne $designMwh -and $null -ne $fullMwh -and $designMwh -gt 0) {
|
|
274
|
+
$healthPercent = [int][math]::Round(([double]$fullMwh / [double]$designMwh) * 100.0)
|
|
275
|
+
}
|
|
276
|
+
$temperatureC = $null
|
|
277
|
+
try {
|
|
278
|
+
$wmib = Get-CimInstance -Namespace 'root/wmi' -ClassName BatteryStatus -ErrorAction Stop
|
|
279
|
+
if ($wmib -and $null -ne $wmib.Temperature) {
|
|
280
|
+
$temperatureC = [math]::Round(([double]$wmib.Temperature / 10.0) - 273.15, 1)
|
|
281
|
+
}
|
|
282
|
+
} catch { $temperatureC = $null }
|
|
283
|
+
$cycleCount = $null
|
|
284
|
+
try {
|
|
285
|
+
$cycleData = Get-CimInstance -Namespace 'root/wmi' -ClassName BatteryCycleCount -ErrorAction Stop
|
|
286
|
+
if ($cycleData -and $null -ne $cycleData.CycleCount) { $cycleCount = [int]$cycleData.CycleCount }
|
|
287
|
+
} catch { $cycleCount = $null }
|
|
288
|
+
$battery = [ordered]@{
|
|
289
|
+
present = $true
|
|
290
|
+
percent = $percent
|
|
291
|
+
condition = $condition
|
|
292
|
+
cycleCount = $cycleCount
|
|
293
|
+
designCapacityMah = $designCapacityMah
|
|
294
|
+
maxCapacityMah = $maxCapacityMah
|
|
295
|
+
temperatureC = $temperatureC
|
|
296
|
+
healthPercent = $healthPercent
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} catch { $errors += ('Win32_Battery: ' + $_.Exception.Message) }
|
|
300
|
+
|
|
301
|
+
# -------------------------------------------------------------------- USB --
|
|
302
|
+
$usb = @()
|
|
303
|
+
try {
|
|
304
|
+
$usbDevices = @(Get-PnpDevice -Class USB -PresentOnly -ErrorAction Stop)
|
|
305
|
+
foreach ($device in $usbDevices) {
|
|
306
|
+
$instanceId = [string]$device.InstanceId
|
|
307
|
+
if ($instanceId -notmatch 'VID_([0-9A-Fa-f]{4})&PID_([0-9A-Fa-f]{4})') { continue }
|
|
308
|
+
$vendorId = $matches[1].ToUpperInvariant()
|
|
309
|
+
$productId = $matches[2].ToUpperInvariant()
|
|
310
|
+
$serial = $null
|
|
311
|
+
if ($instanceId -match '^[^\\]+\\[^\\]+\\([^\\]+)$') {
|
|
312
|
+
if ($matches[1] -notmatch '&') { $serial = $matches[1] }
|
|
313
|
+
}
|
|
314
|
+
$usb += [ordered]@{
|
|
315
|
+
name = [string]$device.FriendlyName
|
|
316
|
+
vendor = $vendorId
|
|
317
|
+
productId = $productId
|
|
318
|
+
serial = $serial
|
|
319
|
+
speed = $null
|
|
320
|
+
bus = $null
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
} catch { $errors += ('Get-PnpDevice USB: ' + $_.Exception.Message) }
|
|
324
|
+
|
|
325
|
+
# -------------------------------------------------------------- Bluetooth --
|
|
326
|
+
$bluetoothControllers = @()
|
|
327
|
+
$bluetoothDevices = @()
|
|
328
|
+
try {
|
|
329
|
+
$bluetoothPnp = @(Get-PnpDevice -Class Bluetooth -PresentOnly -ErrorAction Stop)
|
|
330
|
+
foreach ($device in $bluetoothPnp) {
|
|
331
|
+
$instanceId = [string]$device.InstanceId
|
|
332
|
+
$friendlyName = [string]$device.FriendlyName
|
|
333
|
+
$deviceStatus = [string]$device.Status
|
|
334
|
+
if ($instanceId -match '^(BTHENUM|BTHLE)\\') {
|
|
335
|
+
$address = $null
|
|
336
|
+
if ($instanceId -match 'Dev_([0-9A-Fa-f]{12})') {
|
|
337
|
+
$hex = $matches[1].ToUpperInvariant()
|
|
338
|
+
$pairs = @()
|
|
339
|
+
$index = 0
|
|
340
|
+
while ($index -lt 12) {
|
|
341
|
+
$pairs += $hex.Substring($index, 2)
|
|
342
|
+
$index += 2
|
|
343
|
+
}
|
|
344
|
+
$address = ($pairs -join ':')
|
|
345
|
+
}
|
|
346
|
+
$deviceType = $null
|
|
347
|
+
if ($friendlyName -match '(?i)headphone|headset|speaker|audio|earbud') { $deviceType = 'audio' }
|
|
348
|
+
elseif ($friendlyName -match '(?i)keyboard|mouse|trackpad|gamepad|controller') { $deviceType = 'input' }
|
|
349
|
+
$bluetoothDevices += [ordered]@{
|
|
350
|
+
name = $friendlyName
|
|
351
|
+
address = $address
|
|
352
|
+
connected = ($deviceStatus -eq 'OK')
|
|
353
|
+
batteryPercent = $null
|
|
354
|
+
type = $deviceType
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
$state = 'off'
|
|
358
|
+
if ($deviceStatus -eq 'OK') { $state = 'on' }
|
|
359
|
+
$bluetoothControllers += [ordered]@{
|
|
360
|
+
name = $friendlyName
|
|
361
|
+
address = $null
|
|
362
|
+
chipset = $friendlyName
|
|
363
|
+
firmware = $null
|
|
364
|
+
state = $state
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
} catch { $errors += ('Get-PnpDevice Bluetooth: ' + $_.Exception.Message) }
|
|
369
|
+
|
|
370
|
+
# ------------------------------------------------------------------ audio --
|
|
371
|
+
$audio = @()
|
|
372
|
+
try {
|
|
373
|
+
$audioEndpoints = @(Get-PnpDevice -Class AudioEndpoint -PresentOnly -ErrorAction Stop)
|
|
374
|
+
foreach ($endpoint in $audioEndpoints) {
|
|
375
|
+
$instanceId = [string]$endpoint.InstanceId
|
|
376
|
+
$friendlyName = [string]$endpoint.FriendlyName
|
|
377
|
+
$kind = $null
|
|
378
|
+
if ($instanceId -match '\{[0-9A-Fa-f-]{36}\}') {
|
|
379
|
+
$guid = $matches[0]
|
|
380
|
+
if (Test-Path -LiteralPath ('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render\' + $guid)) {
|
|
381
|
+
$kind = 'output'
|
|
382
|
+
} elseif (Test-Path -LiteralPath ('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Capture\' + $guid)) {
|
|
383
|
+
$kind = 'input'
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (-not $kind) {
|
|
387
|
+
if ($friendlyName -match '(?i)microphone|mic\b|capture|input') { $kind = 'input' } else { $kind = 'output' }
|
|
388
|
+
}
|
|
389
|
+
$audio += [ordered]@{
|
|
390
|
+
name = $friendlyName
|
|
391
|
+
kind = $kind
|
|
392
|
+
manufacturer = $null
|
|
393
|
+
default = $null
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
} catch { $errors += ('Get-PnpDevice AudioEndpoint: ' + $_.Exception.Message) }
|
|
397
|
+
|
|
398
|
+
if ($audio.Count -eq 0) {
|
|
399
|
+
try {
|
|
400
|
+
$soundDevices = @(Get-CimInstance -ClassName Win32_SoundDevice -ErrorAction Stop)
|
|
401
|
+
foreach ($soundDevice in $soundDevices) {
|
|
402
|
+
$audio += [ordered]@{
|
|
403
|
+
name = [string]$soundDevice.Name
|
|
404
|
+
kind = 'output'
|
|
405
|
+
manufacturer = [string]$soundDevice.Manufacturer
|
|
406
|
+
default = $null
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
} catch { $errors += ('Win32_SoundDevice: ' + $_.Exception.Message) }
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
# ---------------------------------------------------------------- cameras --
|
|
413
|
+
$cameras = @()
|
|
414
|
+
try {
|
|
415
|
+
$cameraDevices = @(Get-PnpDevice -Class Camera,Image -PresentOnly -ErrorAction Stop)
|
|
416
|
+
foreach ($camera in $cameraDevices) {
|
|
417
|
+
$cameras += [ordered]@{
|
|
418
|
+
name = [string]$camera.FriendlyName
|
|
419
|
+
modelId = $null
|
|
420
|
+
uniqueId = [string]$camera.InstanceId
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
} catch { $errors += ('Get-PnpDevice Camera/Image: ' + $_.Exception.Message) }
|
|
424
|
+
|
|
425
|
+
# ---------------------------------------------------------------- drivers --
|
|
426
|
+
# driverProblems: PnP devices whose ConfigManagerErrorCode is non-zero (code 28
|
|
427
|
+
# = drivers not installed is the common "missing driver" case). drivers: the
|
|
428
|
+
# signed-driver inventory, with the same non-zero code joined in by DeviceID.
|
|
429
|
+
$driverProblems = @()
|
|
430
|
+
$problemByDeviceId = @{}
|
|
431
|
+
try {
|
|
432
|
+
$problemDevices = @(Get-CimInstance -ClassName Win32_PnPEntity -ErrorAction Stop)
|
|
433
|
+
foreach ($device in $problemDevices) {
|
|
434
|
+
if ($null -eq $device.ConfigManagerErrorCode) { continue }
|
|
435
|
+
$errorCode = [int]$device.ConfigManagerErrorCode
|
|
436
|
+
if ($errorCode -eq 0) { continue }
|
|
437
|
+
if ($driverProblems.Count -lt 50) {
|
|
438
|
+
$driverProblems += [ordered]@{
|
|
439
|
+
name = [string]$device.Name
|
|
440
|
+
code = [string]$errorCode
|
|
441
|
+
class = [string]$device.PNPClass
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
$problemDeviceId = [string]$device.DeviceID
|
|
445
|
+
if ($problemDeviceId) { $problemByDeviceId[$problemDeviceId] = [string]$errorCode }
|
|
446
|
+
}
|
|
447
|
+
} catch { $errors += ('Win32_PnPEntity: ' + $_.Exception.Message) }
|
|
448
|
+
|
|
449
|
+
$drivers = @()
|
|
450
|
+
try {
|
|
451
|
+
$signedDrivers = @(Get-CimInstance -ClassName Win32_PnPSignedDriver -ErrorAction Stop)
|
|
452
|
+
foreach ($driver in $signedDrivers) {
|
|
453
|
+
if ($drivers.Count -ge 250) { break }
|
|
454
|
+
$deviceName = [string]$driver.DeviceName
|
|
455
|
+
if (-not $deviceName) { continue }
|
|
456
|
+
$driverDate = $null
|
|
457
|
+
if ($null -ne $driver.DriverDate) {
|
|
458
|
+
try { $driverDate = ([datetime]$driver.DriverDate).ToString('yyyy-MM-dd') } catch { $driverDate = $null }
|
|
459
|
+
}
|
|
460
|
+
$driverDeviceId = [string]$driver.DeviceID
|
|
461
|
+
$driverProblem = $null
|
|
462
|
+
if ($driverDeviceId -and $problemByDeviceId.ContainsKey($driverDeviceId)) {
|
|
463
|
+
$driverProblem = [string]$problemByDeviceId[$driverDeviceId]
|
|
464
|
+
}
|
|
465
|
+
$drivers += [ordered]@{
|
|
466
|
+
name = $deviceName
|
|
467
|
+
class = [string]$driver.DeviceClass
|
|
468
|
+
manufacturer = [string]$driver.Manufacturer
|
|
469
|
+
version = [string]$driver.DriverVersion
|
|
470
|
+
date = $driverDate
|
|
471
|
+
signer = [string]$driver.DriverProviderName
|
|
472
|
+
inf = [string]$driver.InfName
|
|
473
|
+
deviceId = $driverDeviceId
|
|
474
|
+
kind = 'pnp'
|
|
475
|
+
problem = $driverProblem
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} catch { $errors += ('Win32_PnPSignedDriver: ' + $_.Exception.Message) }
|
|
479
|
+
|
|
480
|
+
# -------------------------------------------------------------------- emit --
|
|
481
|
+
$result = [ordered]@{
|
|
482
|
+
generatedAt = ([DateTimeOffset]::UtcNow).ToUnixTimeMilliseconds()
|
|
483
|
+
cpu = $cpu
|
|
484
|
+
memory = $memory
|
|
485
|
+
videoControllers = $videoControllers
|
|
486
|
+
monitors = $monitors
|
|
487
|
+
physicalDisks = $physicalDisks
|
|
488
|
+
diskDrives = $diskDrives
|
|
489
|
+
volumes = $volumes
|
|
490
|
+
battery = $battery
|
|
491
|
+
usb = $usb
|
|
492
|
+
bluetoothControllers = $bluetoothControllers
|
|
493
|
+
bluetoothDevices = $bluetoothDevices
|
|
494
|
+
audio = $audio
|
|
495
|
+
cameras = $cameras
|
|
496
|
+
drivers = $drivers
|
|
497
|
+
driverProblems = $driverProblems
|
|
498
|
+
errors = $errors
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
$result | ConvertTo-Json -Depth 8 -Compress
|