68 lines
6.3 KiB
PowerShell
68 lines
6.3 KiB
PowerShell
# Ember: the SHIPPED app tunes itself (6 October 2026). Unelevated, no kit, no second engine, no --stop-miners:
|
|
# the installed app (0.3.14 or later: Ember Tune, the Power Helper task, /api/tune/goal, the climb) runs one tune
|
|
# per card through the registered task (no prompt) while it mines. This script only asks and watches:
|
|
# POST api/tune/goal (the climb on, balanced) and POST api/sweep/start per card, then reads api/state and the app's
|
|
# own log for the TUNE rows. It never quits, pauses or resumes the installed app (the 5 October rule) and never
|
|
# touches the cards itself. At the end the climb switch goes back to what it was.
|
|
# Order: the AMD card's ladder first (the ADLX apply-on-ack fix, bd7fcf4), then each NVIDIA card's climb.
|
|
$ErrorActionPreference = 'Continue'
|
|
function Say([string] $s) { Write-Output ('[' + (Get-Date -Format 'HH:mm:ss') + '] ' + $s) }
|
|
$appDir = Join-Path $env:LOCALAPPDATA 'igneum\app'
|
|
$u = Join-Path $appDir 'app.url'
|
|
if (-not (Test-Path -LiteralPath $u)) { Write-Output 'RESULT TUNE error=no_app_url (installed app not running)'; exit 1 }
|
|
$base = (Get-Content -LiteralPath $u -Raw).Trim()
|
|
function State() { try { return ((Invoke-WebRequest -Uri ($base + 'api/state') -UseBasicParsing -TimeoutSec 5).Content | ConvertFrom-Json) } catch { return $null } }
|
|
function Post([string] $path, [string] $json) { try { Invoke-WebRequest -Uri ($base + $path) -Method POST -Body $json -ContentType 'application/json' -UseBasicParsing -TimeoutSec 5 | Out-Null; return $true } catch { Say ($path + ': ' + $_.Exception.Message); return $false } }
|
|
$s = State
|
|
if (-not $s) { Write-Output 'RESULT TUNE error=state_unreadable'; exit 1 }
|
|
$ver = [string]$s.version
|
|
Write-Output ('RESULT TUNE installed app ' + $ver + ' power_control=' + $s.settings.power_control + ' tuning_off=' + $s.settings.tuning_off + ' climb_before=' + $s.settings.tune_climb + ' goal_before=' + $s.settings.tune_goal)
|
|
$t = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
|
|
Write-Output ('RESULT TUNE task ' + $(if ($t) { $t.Actions[0].Execute + ' state=' + $t.State } else { 'NOT REGISTERED' }))
|
|
if (-not $t) { Write-Output 'RESULT TUNE error=task_not_registered (the NVIDIA climbs would need a prompt; stopping)'; exit 1 }
|
|
if (-not $s.settings.power_control) { Write-Output 'RESULT TUNE error=power_control_off'; exit 1 }
|
|
$climbBefore = [bool]$s.settings.tune_climb
|
|
$cards = @($s.mining.cards | Where-Object { $_.enabled -and ($_.vendor -eq 'nvidia' -or $_.vendor -eq 'amd') -and $_.name -notmatch 'Radeon\(TM\) Graphics' })
|
|
Write-Output ('RESULT TUNE cards ' + (($cards | ForEach-Object { $_.key + ' (' + $_.name + ', ' + $_.vendor + ', sweep_state ' + $_.sweep_state + ', line "' + $_.tune_line + '")' }) -join ' | '))
|
|
foreach ($c in $cards) { Write-Output ('RESULT TUNE before key=' + $c.key + ' power_w=' + $c.power_w + ' power_pct=' + $c.power_pct + ' clock_cap=' + $c.clock_cap_mhz + ' mhs=' + $c.mhs + ' line=' + $c.tune_line) }
|
|
# the climb on (NVIDIA cards; an AMD card has no clock knob and runs its ladder regardless)
|
|
if (-not (Post 'api/tune/goal' '{"goal":"balanced","climb":true}')) { Write-Output 'RESULT TUNE error=tune_goal_refused'; exit 1 }
|
|
Start-Sleep -Seconds 2
|
|
$order = @($cards | Where-Object { $_.vendor -eq 'amd' }) + @($cards | Where-Object { $_.vendor -eq 'nvidia' })
|
|
$deadline = (Get-Date).AddMinutes(27)
|
|
$logs = Join-Path $appDir 'logs'
|
|
function LogMark() { $l = Get-ChildItem -Path $logs -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($l) { return @{ f = $l.FullName; n = (Get-Content -LiteralPath $l.FullName).Count } } else { return @{ f = ''; n = 0 } } }
|
|
function LogSince($m) { if (-not $m.f) { return @() }; $all = @(Get-Content -LiteralPath $m.f -ErrorAction SilentlyContinue); if ($all.Count -le $m.n) { return @() }; return @($all[$m.n..($all.Count - 1)]) }
|
|
$rows = 0
|
|
foreach ($c in $order) {
|
|
if ((Get-Date) -gt $deadline) { Say 'budget spent before ' + $c.key; break }
|
|
$m = LogMark
|
|
$body = '{"key":"' + ($c.key -replace '"', '\"') + '"}'
|
|
if (-not (Post 'api/sweep/start' $body)) { Write-Output ('RESULT TUNE error=sweep_start_refused key=' + $c.key); continue }
|
|
Say ('tune asked for ' + $c.key)
|
|
$seen = 0; $done = $false; $started = Get-Date
|
|
while (-not $done -and (Get-Date) -lt $deadline) {
|
|
Start-Sleep -Seconds 10
|
|
$new = @(LogSince $m)
|
|
while ($seen -lt $new.Count) { $l = [string]$new[$seen]; $seen++; if ($l -match 'TUNE (card=|chosen|start|aborted|progress)') { if ($l -notmatch 'TUNE progress') { Write-Output ('RESULT ' + ($l -replace '^.*?(TUNE )', 'TUNE ')) }; if ($l -match 'TUNE card=') { $rows++ } } }
|
|
$st = State
|
|
if ($st) {
|
|
$cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1
|
|
if ($cc) {
|
|
$running = ($cc.sweep_state -eq 'running')
|
|
if (-not $running -and ((Get-Date) - $started).TotalSeconds -gt 180) { $done = $true }
|
|
if ($running -and $cc.tune_steps -gt 0 -and ($seen % 6 -eq 0)) { Say ($c.key + ': step ' + $cc.tune_step + ' of ' + $cc.tune_steps + ', ' + $cc.sweep_note) }
|
|
}
|
|
}
|
|
}
|
|
$st = State
|
|
if ($st) { $cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1; if ($cc) { Write-Output ('RESULT TUNE after key=' + $c.key + ' power_w=' + $cc.power_w + ' power_pct=' + $cc.power_pct + ' clock_cap=' + $cc.clock_cap_mhz + ' line=' + $cc.tune_line + ' source=' + $cc.tune_source + ' note=' + $cc.sweep_note); if ($cc.tune_curve) { Write-Output ('RESULT TUNE curve key=' + $c.key + ' ' + (($cc.tune_curve | ForEach-Object { ($_.clock_mhz) + 'MHz/' + ($_.mem_mhz) + 'mem/' + ($_.power_pct) + '%:' + $_.mhs + 'MH/s ' + $_.watts + 'W ' + $_.eff + ' ' + $_.mark }) -join ' | ')) } } }
|
|
}
|
|
# the climb switch back to what it was
|
|
Post 'api/tune/goal' ('{"climb":' + $(if ($climbBefore) { 'true' } else { 'false' }) + '}') | Out-Null
|
|
Write-Output ('RESULT TUNE climb_restored=' + $climbBefore + ' rows=' + $rows)
|
|
$smi = Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'
|
|
if (-not (Test-Path $smi)) { $smi = Join-Path $env:SystemRoot 'System32\nvidia-smi.exe' }
|
|
if (Test-Path $smi) { Write-Output ('RESULT TUNE nvidia_now ' + ((& $smi --query-gpu=index,name,power.draw,power.limit,clocks.gr,clocks.mem --format=csv,noheader 2>&1 | Out-String) -replace "`r?`n", ' | ')) }
|
|
if ($rows -eq 0) { Write-Output 'RESULT TUNE error=no_rows'; exit 1 }
|
|
exit 0
|