From 0539eeb2524792c0b12d0bbd6b41c3850ef2e58d Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 11:25:05 +0000 Subject: [PATCH] Intel Arc in the app (0.3.20): vendor intel from the OpenCL vendor string and the Windows name, the INTEL badge and its token, the no-cap row naming IGCL, the measure-only tune words, the view test and the ui-mock scenario (the six app files of intel-arc 18453bb1, applied as its own diff; the test's prove sentence in this tree's wording) Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/detect.rs | 63 ++++++++++++++++++++++++++++++++- app/igneum-app/src/sweep.rs | 3 ++ app/igneum-app/ui/app.css | 7 ++-- app/igneum-app/ui/app.js | 5 ++- app/igneum-app/ui/view.test.mjs | 17 +++++++++ tools/ui-mock/server.mjs | 7 ++++ 6 files changed, 97 insertions(+), 5 deletions(-) diff --git a/app/igneum-app/src/detect.rs b/app/igneum-app/src/detect.rs index 9e69fa419..5dfc0bfc4 100644 --- a/app/igneum-app/src/detect.rs +++ b/app/igneum-app/src/detect.rs @@ -111,8 +111,11 @@ fn card(index: usize, name: &str, vendor: &str, worker: &str, detail: &str, devi /// CL_DEVICE_NAME: PC 1's Ryzen iGPU is "gfx1036", 5 October 2026). pub fn looks_integrated(name: &str) -> bool { let n = name.to_ascii_lowercase(); + if n.contains("arc") && n.contains("intel") { + return arc_is_integrated(&n); + } let integrated = ["radeon(tm) graphics", "radeon graphics", "vega 8", "vega 7", "vega 6", "vega 3", "vega 11", "iris", "uhd graphics", "hd graphics", "intel(r) graphics", "intel graphics", "apu", "780m", "760m", "680m", "610m", "890m", "880m"]; - if integrated.iter().any(|k| n.contains(k)) && !n.contains("arc ") { + if integrated.iter().any(|k| n.contains(k)) { return true; } // AMD APU graphics by gfx code (approximate list from AMD's ROCm and Mesa target tables): Raven/Picasso gfx902 and @@ -123,6 +126,22 @@ pub fn looks_integrated(name: &str) -> bool { apu.iter().any(|k| code == *k || code.starts_with(&format!("{k}:")) || code.starts_with(&format!("{k} "))) } +/// An Intel name carrying "Arc" (lower-cased): the cards ("Arc(TM) A770", "Arc(TM) B580", "Arc(TM) Pro A60") are +/// discrete; the Arc-branded processor graphics are integrated: the bare "Intel(R) Arc(TM) Graphics" of Meteor Lake +/// and Arrow Lake, the "Arc(TM) 130V / 140V" of Lunar Lake and the "Arc(TM) 1xxT" of Panther Lake (approximate names +/// from Intel's product pages, 7 October 2026; the B580 is the first Intel card in hand, docs/plans/intel-arc.md). +fn arc_is_integrated(lower: &str) -> bool { + let after = lower.split("arc").nth(1).unwrap_or("").trim_start_matches("(tm)").trim_start_matches("(r)").trim(); + let word = after.split(|c: char| !c.is_ascii_alphanumeric()).next().unwrap_or(""); + // "graphics" or nothing after "Arc": the processor graphics; "130v", "140t": digits first, the processor graphics + if word.is_empty() || word == "graphics" || word.starts_with(|c: char| c.is_ascii_digit()) { + return true; + } + // "pro", "a770", "b580": a card. Anything else unknown is read as a card (on by default; a wrong call costs an + // iGPU 8 identities at 1 to 2 MH/s, a card called integrated would sit off with no reason the owner can see) + false +} + /// One row of Windows' adapter list (Win32_VideoController), the part this app reads. #[derive(Clone, Debug, Default, PartialEq)] pub struct Adapter { @@ -186,6 +205,10 @@ pub fn vendor_of(name: &str) -> &'static str { "amd" } else if n.contains("apple") { "apple" + } else if n.contains("intel") { + // Arc cards and Intel processor graphics alike (the kind tells them apart); the first Intel card is the B580 + // on PC 1, 7 October 2026 (docs/plans/intel-arc.md) + "intel" } else { "other" } @@ -440,6 +463,9 @@ impl ClDevice { "nvidia" } else if self.vendor.contains("Advanced Micro") || self.vendor.contains("AMD") || self.name.contains("Radeon") || self.name.contains("AMD") || self.name.to_ascii_lowercase().starts_with("gfx") { "amd" + } else if self.vendor.contains("Intel") || self.name.contains("Intel") { + // "Intel(R) Corporation" on the "Intel(R) OpenCL Graphics" platform (the Arc driver's runtime) + "intel" } else { "other" } @@ -809,6 +835,8 @@ mod tests { // the Mac: detect() labels Apple silicon "apple" itself; the name rules do not call it integrated assert!(!looks_integrated("Apple M5 Max")); assert_eq!(vendor_of("Apple M5 Max"), "apple"); + assert_eq!(vendor_of("Intel(R) Arc(TM) B580 Graphics"), "intel"); + assert_eq!(vendor_of("Intel(R) UHD Graphics 770"), "intel"); assert_eq!(vendor_of("gfx1036"), "amd"); assert_eq!(vendor_of("AMD Radeon RX 9070 XT"), "amd"); assert_eq!(vendor_of("NVIDIA GeForce RTX 5090"), "nvidia"); @@ -951,6 +979,39 @@ mod tests { assert!(diff.removed.is_empty()); } + #[test] + /// The first Intel card (the B580 on PC 1, 7 October 2026, docs/plans/intel-arc.md): the Arc driver's OpenCL + /// platform lists it as "Intel(R) Arc(TM) B580 Graphics" under vendor "Intel(R) Corporation" (the line shape is + /// the worker's; the device name and vendor string are Intel's documented forms, approximate until the PC 1 job + /// prints them). It becomes a discrete 12 GB card, vendor intel, worker OpenCL, on with 8 identities; the + /// Arc-branded processor graphics stay integrated; the NVIDIA cards beside it are untouched. + #[test] + fn an_intel_arc_card_on_the_intel_platform_is_a_discrete_intel_card() { + let list = String::from(PC1_LIST) + + " [4] Intel(R) Arc(TM) B580 Graphics | Intel(R) OpenCL Graphics (OpenCL 3.0 )\n GPU, vendor Intel(R) Corporation, driver 32.0.101.9034, OpenCL C 3.0, 160 compute units, 2850 MHz, pci 05:00.0\n global 12208 MiB, max alloc 4095 MiB, local 64 KiB, max work-group 1024, sub-group extension: cl_intel_subgroups\n"; + let (devs, _) = parse_opencl_list(&list); + let arc = devs.iter().find(|d| d.name.contains("B580")).expect("the Arc line parses"); + assert_eq!(arc.vendor_word(), "intel"); + assert_eq!(arc.mem_mb, 12208); + assert_eq!(arc.bus, "05:00.0"); + let d = assemble(pc1_inputs(&list)); + let c = d.cards.iter().find(|c| c.vendor == "intel").expect("an intel card"); + assert_eq!((c.name.as_str(), c.kind.as_str(), c.worker.as_str(), c.device.as_str(), c.enabled, c.identities), ("Intel(R) Arc(TM) B580 Graphics", "discrete", "OpenCL", "4", true, 8)); + assert_eq!(c.key, "intel:Intel(R) Arc(TM) B580 Graphics"); + assert_eq!(c.vram_mb, 12208); + assert!(!c.sweep_supported, "no cap through OpenCL"); + assert!(c.sweep_note.contains("Intel Arc"), "{}", c.sweep_note); + assert!(d.listed(c), "the OpenCL list answered, so a vanished Arc can be called removed"); + // the Arc names: cards discrete, processor graphics integrated + for card in ["Intel(R) Arc(TM) B580 Graphics", "Intel(R) Arc(TM) A770 Graphics", "Intel(R) Arc(TM) A380 Graphics", "Intel(R) Arc(TM) Pro A60 Graphics", "Intel(R) Arc(TM) B570 Graphics"] { + assert_eq!(classify_kind(card, None), "discrete", "{card}"); + } + for igpu in ["Intel(R) Arc(TM) Graphics", "Intel(R) Arc(TM) 140V GPU (16GB)", "Intel(R) Arc(TM) 130V GPU", "Intel(R) Arc(TM) 140T GPU"] { + assert_eq!(classify_kind(igpu, None), "integrated", "{igpu}"); + } + assert_eq!(classify_kind("Intel(R) UHD Graphics 770", None), "integrated"); + } + #[test] fn keys_number_identical_cards() { let mut cards = vec![card(0, "NVIDIA GeForce RTX 5090", "nvidia", "CUDA", "", "0"), card(1, "NVIDIA GeForce RTX 5090", "nvidia", "CUDA", "", "1"), card(2, "gfx1201", "amd", "OpenCL", "", "2")]; diff --git a/app/igneum-app/src/sweep.rs b/app/igneum-app/src/sweep.rs index 92e402eee..b015afab8 100644 --- a/app/igneum-app/src/sweep.rs +++ b/app/igneum-app/src/sweep.rs @@ -439,6 +439,9 @@ pub fn unsupported_reason(vendor: &str, power_default_w: f64, device: &str) -> O // Ember Tune (src/ember.rs, 5 October 2026): AMD is tuned through igneum-gpu-telemetry, Apple measures only; // the tune itself says which at its start (the card row's note) "apple" | "amd" => None, + // the first Intel card, the B580 on PC 1 (7 October 2026, docs/plans/intel-arc.md section 2.3): OpenCL + // exposes neither a cap nor a reading; Intel's path is IGCL (ControlLib.dll), the telemetry helper's next piece + "intel" => Some("not available: Intel Arc exposes no power cap or reading through OpenCL (the driver's IGCL counters come next)"), _ => Some("not available: no power reading or cap for this card"), } } diff --git a/app/igneum-app/ui/app.css b/app/igneum-app/ui/app.css index 05f823426..0871f213c 100644 --- a/app/igneum-app/ui/app.css +++ b/app/igneum-app/ui/app.css @@ -17,7 +17,7 @@ :root{ /* colour, dark */ --obsidian:#0C0C0E;--graphite:#16161A;--row:#111114;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--ember-hi:#FF6A2B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E; - --ember-12:rgba(242,84,27,.12);--ember-40:rgba(242,84,27,.4);--molten-10:rgba(255,179,92,.1);--molten-40:rgba(255,179,92,.4);--nvidia:#8BE37A;--rail-bg:#111114;--hover:rgba(255,255,255,.04);--top-bg:rgba(12,12,14,.86);--shadow:rgba(0,0,0,.6);--scrim:rgba(12,12,14,.72);--ok:#FFB35C; + --ember-12:rgba(242,84,27,.12);--ember-40:rgba(242,84,27,.4);--molten-10:rgba(255,179,92,.1);--molten-40:rgba(255,179,92,.4);--nvidia:#8BE37A;--intel:#7CC4FF;--rail-bg:#111114;--hover:rgba(255,255,255,.04);--top-bg:rgba(12,12,14,.86);--shadow:rgba(0,0,0,.6);--scrim:rgba(12,12,14,.72);--ok:#FFB35C; /* type scale */ --t-xs:11px;--t-sm:12px;--t-base:13px;--t-md:14px;--t-lg:15px;--t-xl:17px;--t-num:24px;--t-hero:34px;--t-h2:28px;--t-h1:44px; /* spacing scale */ @@ -29,10 +29,10 @@ --included:#3B7DD8;--excluded:#B0362B} @media (prefers-color-scheme:light){:root:not([data-theme="dark"]){ --obsidian:#F4F1EC;--graphite:#FFFFFF;--row:#FAF8F5;--line:#E2DED8;--line-2:#CFCAC2;--ember:#E04A14;--ember-hi:#F2541B;--molten:#B8731F;--bone:#16161A;--ash:#6B6B70;--ink-2:#3C3C42;--ember-ink:#FFFFFF; - --ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F;--included:#2A62B8;--excluded:#B0362B}} + --ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--intel:#1C6FD6;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F;--included:#2A62B8;--excluded:#B0362B}} :root[data-theme="light"]{ --obsidian:#F4F1EC;--graphite:#FFFFFF;--row:#FAF8F5;--line:#E2DED8;--line-2:#CFCAC2;--ember:#E04A14;--ember-hi:#F2541B;--molten:#B8731F;--bone:#16161A;--ash:#6B6B70;--ink-2:#3C3C42;--ember-ink:#FFFFFF; - --ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F;--included:#2A62B8;--excluded:#B0362B} + --ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--intel:#1C6FD6;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F;--included:#2A62B8;--excluded:#B0362B} *{box-sizing:border-box} html,body{height:100%} body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--sans);font-size:var(--t-md);line-height:1.5;-webkit-font-smoothing:antialiased;overflow:hidden;user-select:none;-webkit-user-select:none;font-variant-numeric:tabular-nums} @@ -199,6 +199,7 @@ body[data-phase="welcome"] #screen-welcome,body[data-phase="cards"] #screen-card .badge.apple{color:var(--bone)} .badge.nvidia{color:var(--nvidia)} .badge.amd{color:var(--ember)} +.badge.intel{color:var(--intel)} .gpu-row .info{flex:1;min-width:180px;display:flex;flex-direction:column;gap:var(--s-1)} .gpu-row .name{font-family:var(--head);font-weight:700;font-size:var(--t-xl);line-height:1.2;display:flex;align-items:center;gap:10px;flex-wrap:wrap} .kind{font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;border-radius:999px;padding:2px 8px;border:1px solid var(--line-2);color:var(--ash);font-weight:500;white-space:nowrap} diff --git a/app/igneum-app/ui/app.js b/app/igneum-app/ui/app.js index 194f78665..6c372834b 100644 --- a/app/igneum-app/ui/app.js +++ b/app/igneum-app/ui/app.js @@ -354,6 +354,7 @@ var View = (function () { if (cd.power_w > 0 || cd.temp_gpu > 0) return ''; if (cd.vendor === 'apple') return 'No power or temperature reading on Apple silicon'; if (cd.kind === 'integrated') return 'No power or temperature reading on an integrated GPU'; + if (cd.vendor === 'intel') return 'No power or temperature reading on Intel Arc yet: the driver exposes them through IGCL, not OpenCL'; return ''; } // the row's cells: rate, watts with MH/W, £ a day, temperature; a cell with no number is left out, never n/a @@ -1071,6 +1072,7 @@ var TuneLine = (function () { // the model: {kind: running|tuned|measured|stopped|idle|off, text, note} function model(cd, now) { cd = cd || {}; + if (cd.vendor === 'intel') return { kind: 'off', text: 'tuning: measure only on Intel Arc once a power reading exists', note: 'no cap through OpenCL; IGCL next' }; if (cd.vendor !== 'nvidia' && cd.vendor !== 'amd' && cd.vendor !== 'apple') return { kind: 'off', text: cd.sweep_note || 'tuning: no power or clock control for this card' }; if (cd.sweep_state === 'running') return { kind: 'running', text: cd.sweep_note || 'tuning: running' }; var when = cd.sweep_at && now ? ', ' + rel(now - cd.sweep_at) + ' ago' : ''; @@ -1914,7 +1916,7 @@ if (typeof document !== 'undefined') (function () { // ---------- render ---------- function badgeHtml(cd) { if (cd.vendor === 'apple') return ''; - return cd.vendor === 'nvidia' ? 'NV' : cd.vendor === 'amd' ? 'AMD' : 'GPU'; + return cd.vendor === 'nvidia' ? 'NV' : cd.vendor === 'amd' ? 'AMD' : cd.vendor === 'intel' ? 'INTEL' : 'GPU'; } function vramText(cd) { if (!cd.vram_mb) return ''; var gb = cd.vram_mb / 1024; return (gb >= 10 ? Math.round(gb) : gb.toFixed(gb < 4 ? 1 : 0)) + ' GB' + (cd.kind === 'apple' ? ' unified' : ''); } // ---------- the first-run GPU rows: one per card, with a switch ---------- @@ -2057,6 +2059,7 @@ if (typeof document !== 'undefined') (function () { '

Lower is cooler and quieter. Most cards hash nearly as fast at 70%. ' + (cd.pinned ? 'Set by hand, so Ember leaves it. ' : 'Default: Ember may move it.') + '

'; h += cd.power_applied ? '
Cap in force: ' + want + ' W' + (cd.sweep_pct === pct && cd.sweep_pct ? ' Ember’s choice' : '') + '
' : '
Cap not set yet. Windows needs the administrator prompt.
'; } else if (cd.vendor === 'apple') h += '

Apple silicon manages its own power. There is no cap to set.

'; + else if (cd.vendor === 'intel') h += '

Intel Arc exposes no power cap through OpenCL; the card runs at the driver’s default limit (Intel Graphics Software can set one). No watts reading yet, so the MH/W cell stays empty until the IGCL reader lands.

'; var tl = []; if (cd.temp_mem > 0) tl.push('memory ' + Math.round(cd.temp_mem) + ' °C'); if (cd.gclk_mhz > 0) tl.push('core clock ' + Math.round(cd.gclk_mhz) + ' MHz' + (cd.clock_cap_mhz > 0 ? ' (cap ' + cd.clock_cap_mhz + ')' : '')); diff --git a/app/igneum-app/ui/view.test.mjs b/app/igneum-app/ui/view.test.mjs index db83cb0e2..82ee155da 100644 --- a/app/igneum-app/ui/view.test.mjs +++ b/app/igneum-app/ui/view.test.mjs @@ -51,6 +51,23 @@ test('a GPU row: temperatures turn amber then red; unknown numbers are empty', ( assert.equal(apple.meta, '128 GB unified'); }); +test('an Intel Arc card is a discrete Intel row: INTEL badge, no power reading sentence, measure-only tune words (intel-arc, 7 October 2026)', () => { + const arc = card({ key: 'intel:Intel(R) Arc(TM) B580 Graphics', name: 'Intel(R) Arc(TM) B580 Graphics', vendor: 'intel', kind: 'discrete', worker: 'OpenCL', vram_mb: 12208, enabled: true, state: 'mining', hash_now: 15.2, power_w: 0, temp_gpu: 0, sweep_supported: false, sweep_state: 'unsupported', sweep_note: 'not available: Intel Arc exposes no power cap or reading through OpenCL (the driver\'s IGCL counters come next)' }); + const r = V.cardRow(arc); + assert.equal(r.vendor, 'intel'); + assert.equal(r.kindWord, 'Discrete'); + assert.equal(r.integrated, false); + assert.equal(r.on, true); + assert.ok(r.meta.startsWith('12 GB'), r.meta); + assert.equal(r.power, '', 'no watts cell without a reading'); + assert.equal(V.noReading(arc), 'No power or temperature reading on Intel Arc yet: the driver exposes them through IGCL, not OpenCL'); + assert.equal(V.tunable(arc), false, 'no cap, no clock control: not tunable'); + assert.equal(V.proveTier(arc), 'Intel(R) Arc(TM) B580 Graphics: cannot prove yet (proving needs an NVIDIA card).'); + const tm = mod.exports.TuneLine.model(arc, 1000); + assert.equal(tm.kind, 'off'); + assert.equal(tm.text, 'tuning: measure only on Intel Arc once a power reading exists'); +}); + test('an integrated GPU is shown as integrated and off, with its reason', () => { const r = V.cardRow(card({ key: 'intel:1:UHD', name: 'Intel UHD Graphics 770', vendor: 'other', kind: 'integrated', enabled: false, state: 'off', hash_now: 0, reason: 'integrated GPU: slow and shares the machine memory', power_w: 0, temp_gpu: 0 })); assert.equal(r.integrated, true); diff --git a/tools/ui-mock/server.mjs b/tools/ui-mock/server.mjs index 840bda4a9..231211135 100644 --- a/tools/ui-mock/server.mjs +++ b/tools/ui-mock/server.mjs @@ -199,6 +199,13 @@ function stateNow() { s.heat = { on: true, phase: resting ? 'resting' : 'heating', set_c: 20, room_c: resting ? 20.1 : 19.6, room_source: 'typed', room_error_c: 0, room_age_s: 2400, duty: 0.6, duty_hour: 0.58, heat_w: resting ? 0 : 448, full_w: 448, heat_avg_w: 269, until_s: resting ? 230 : 312, note: resting ? 'Holding 20.0 °C: room 20.1 °C from your reading 40 min ago. resting, heats again in 4 min. Heat 60% of the time.' : 'Holding 20.0 °C: room 19.6 °C from your reading 40 min ago. heating, 5 min to go. Heat 60% of the time.', period_s: 600, offset_c: 7.5, offset_learned: true }; s.events = [{ kind: 'info', t: t - 312, text: 'heat mode: heating again' }, { kind: 'info', t: t - 2400, text: 'heat mode on: holding 20.0 °C (schedule 06:00 20, 22:00 16); the cards heat for a share of every 10 minutes and rest for the rest' }].concat(s.events).slice(0, 40); } + if (scenario === 'intel') { + // PC 1 after the B580 swap (7 October 2026, docs/plans/intel-arc.md): the 5090 and the first Intel card; the Arc's + // numbers are the plan's expectation, not a measurement, until the PC 1 job's row lands + s.mining.cards = [{ ...recState.mining.cards[0], key: 'nvidia:NVIDIA GeForce RTX 5090', name: 'NVIDIA GeForce RTX 5090', kind: 'discrete', vendor: 'nvidia', worker: 'CUDA', detail: '21760 cores', vram_mb: 32768, device: '0', identities: 8, ids: ['8fafda27', '1b2c3d4e', '5f6a7b8c'], power_default_w: 575, power_pct: 80, power_applied: true, power_limit_w: 460, power_w: 227, temp_gpu: 61, temp_mem: 70, hash_now: 127.7, hash_avg: 127.1, eff_mhw: 0.563, sweep_supported: true, sweep_state: 'idle', sweep_note: '', tune_line: 'Tuned: 127.7 MH/s at 227 W (0.563 MH/W)', tune_source: 'full', tune_control: true, tune_clock_mhz: 1854, sweep_pct: 100, sweep_watts: 227, sweep_at: t - 7200 }, + { ...recState.mining.cards[0], key: 'intel:Intel(R) Arc(TM) B580 Graphics', name: 'Intel(R) Arc(TM) B580 Graphics', code: 'Intel(R) Arc(TM) B580 Graphics', platform: 'Intel(R) OpenCL Graphics (OpenCL 3.0 ), driver 32.0.101.9034', kind: 'discrete', vendor: 'intel', worker: 'OpenCL', detail: '160 compute units', vram_mb: 12208, device: '1', bus: '05:00.0', identities: 8, ids: ['c0ffee01', 'c0ffee02'], enabled: true, state: 'mining', hash_now: 15.2, hash_avg: 15.0, accepted: 3, rejected: 0, power_default_w: 0, power_pct: 0, power_applied: false, power_w: 0, temp_gpu: 0, temp_mem: 0, eff_mhw: 0, sweep_supported: false, sweep_state: 'unsupported', sweep_note: 'not available: Intel Arc exposes no power cap or reading through OpenCL (the driver\'s IGCL counters come next)', tune_line: '', tune_source: '', tune_control: false, driver: '32.0.101.9034' }]; + s.mining.hash_total = 142.9; s.display_name = 'pc-1'; + } if (scenario === 'nvidia') { s.mining.cards = [{ ...recState.mining.cards[0], key: 'nvidia:0:NVIDIA GeForce RTX 5090', name: 'NVIDIA GeForce RTX 5090', kind: 'discrete', vendor: 'nvidia', worker: 'CUDA', detail: '21760 cores', vram_mb: 32768, device: '0', identities: 8, ids: ['8fafda27', '1b2c3d4e', '5f6a7b8c'], power_default_w: 575, power_pct: 80, power_applied: true, power_limit_w: 460, power_w: 441, temp_gpu: 71, temp_mem: 92, telemetry_at: t, hash_now: 412.5, hash_avg: 405.2 }, { ...recState.mining.cards[0], key: 'nvidia:1:NVIDIA GeForce RTX 4070', name: 'NVIDIA GeForce RTX 4070', kind: 'discrete', vendor: 'nvidia', worker: 'CUDA', detail: '5888 cores', vram_mb: 12288, device: '1', identities: 2, ids: ['9a8b7c6d'], power_default_w: 200, power_pct: 80, power_applied: false, power_note: 'nvidia-smi: wanted 160 W, card reports 200 W', power_limit_w: 200, power_w: 188, temp_gpu: 64, temp_mem: 0, telemetry_at: t, hash_now: 98.1, hash_avg: 97.4, state: 'restarting', restart_in_s: 4 },