65 lines
3.3 KiB
PowerShell
65 lines
3.3 KiB
PowerShell
# Sends an EVision per-key colour frame (same packets as SignalRGB's EVision plugin)
|
|||
|
|
# Usage: evision_led.ps1 -Leds "0=ff0000,21=00ff00" [-Fill 000000] [-Count 128] [-Seconds 0] [-SoftwareMode]
|
||
|
|
param(
|
||
|
|
[string]$Leds = '',
|
||
|
|
[string]$Fill = '000000',
|
||
|
|
[int]$Count = 128,
|
||
|
|
[double]$Seconds = 0,
|
||
|
|
[switch]$SoftwareMode,
|
||
|
|
[string]$Match = 'VID_320F&PID_502A&MI_01&COL04'
|
||
|
|
)
|
||
|
|
Add-Type @"
|
||
|
|
using System;
|
||
|
|
using System.Runtime.InteropServices;
|
||
|
|
public static class EV {
|
||
|
|
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
|
||
|
|
public static extern IntPtr CreateFile(string n, uint a, uint s, IntPtr sa, uint c, uint f, IntPtr t);
|
||
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
||
|
|
public static extern bool WriteFile(IntPtr h, byte[] b, int n, out int w, IntPtr o);
|
||
|
|
[DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr h);
|
||
|
|
}
|
||
|
|
"@
|
||
|
|
$dev = Get-PnpDevice -PresentOnly -Class HIDClass | Where-Object { $_.InstanceId -match [regex]::Escape($Match) } | Select-Object -First 1
|
||
|
|
if (-not $dev) { throw "RGB endpoint $Match not found" }
|
||
|
|
$path = '\\?\' + ($dev.InstanceId -replace '\\','#') + '#{4d1e55b2-f16f-11cf-88cb-001111000030}'
|
||
|
|
$h = [EV]::CreateFile($path, [uint32]3221225472, 3, [IntPtr]::Zero, 3, 0, [IntPtr]::Zero)
|
||
|
|
if ($h -eq [IntPtr]-1) { throw "open failed, error $([Runtime.InteropServices.Marshal]::GetLastWin32Error())" }
|
||
|
|
|
||
|
|
function Send([byte[]]$pkt) {
|
||
|
|
$buf = New-Object byte[] 64; [Array]::Copy($pkt, $buf, [Math]::Min(64, $pkt.Length))
|
||
|
|
$w = 0
|
||
|
|
if (-not [EV]::WriteFile($h, $buf, 64, [ref]$w, [IntPtr]::Zero)) { throw "write failed, error $([Runtime.InteropServices.Marshal]::GetLastWin32Error())" }
|
||
|
|
}
|
||
|
|
function Rgb([string]$hex) { $hex = $hex.TrimStart('#'); ,([byte[]]@([Convert]::ToByte($hex.Substring(0,2),16), [Convert]::ToByte($hex.Substring(2,2),16), [Convert]::ToByte($hex.Substring(4,2),16))) }
|
||
|
|
|
||
|
|
# Build the colour buffer
|
||
|
|
$data = New-Object byte[] ($Count * 3)
|
||
|
|
$f = Rgb $Fill
|
||
|
|
for ($i = 0; $i -lt $Count; $i++) { $data[$i*3] = $f[0]; $data[$i*3+1] = $f[1]; $data[$i*3+2] = $f[2] }
|
||
|
|
foreach ($e in ($Leds -split ',' | Where-Object { $_ })) {
|
||
|
|
$idx, $col = $e.Split('=')
|
||
|
|
if ($idx -match '^(\d+)-(\d+)$') { $range = [int]$Matches[1]..[int]$Matches[2] } else { $range = @([int]$idx) }
|
||
|
|
$c = Rgb $col
|
||
|
|
foreach ($i in $range) { if ($i -lt $Count) { $data[$i*3] = $c[0]; $data[$i*3+1] = $c[1]; $data[$i*3+2] = $c[2] } }
|
||
|
|
}
|
||
|
|
|
||
|
|
function SendFrame {
|
||
|
|
$per = 56
|
||
|
|
for ($off = 0; $off -lt $data.Length; $off += $per) {
|
||
|
|
$chunk = New-Object byte[] $per
|
||
|
|
[Array]::Copy($data, $off, $chunk, 0, [Math]::Min($per, $data.Length - $off))
|
||
|
|
$pkt = [byte[]](@(0x04, 0, 0, 0x12, $per, ($off -band 0xFF), (($off -shr 8) -band 0xFF), 0) + $chunk)
|
||
|
|
# checksum = 16-bit sum of bytes 3..63 (matches the plugin's formula)
|
||
|
|
$sum = 0; for ($k = 3; $k -lt 64; $k++) { $sum += $pkt[$k] }
|
||
|
|
$pkt[1] = $sum -band 0xFF; $pkt[2] = ($sum -shr 8) -band 0xFF
|
||
|
|
Send $pkt
|
||
|
|
Start-Sleep -Milliseconds 1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
if ($SoftwareMode) { Send ([byte[]]@(0x04, 0x8c, 0x00, 0x0b, 0x30, 0x50, 0x01)); Start-Sleep -Milliseconds 20 }
|
||
|
|
$end = (Get-Date).AddSeconds($Seconds); $n = 0
|
||
|
|
do { SendFrame; $n++; if ($Seconds -gt 0) { Start-Sleep -Milliseconds 100 } } while ((Get-Date) -lt $end)
|
||
|
|
"sent $n frame(s), $([Math]::Ceiling($data.Length / 56)) packets each"
|
||
|
|
} finally { [void][EV]::CloseHandle($h) }
|