component
Sunon MF40200V1-1000U-G99 · 5 V tachometer fan
Public Made by Adomby adom
Tachometer fan with factory flying leads, an explicit Adom connector harness and gentle GLB state demonstration.
← Commit history
Refresh asset hashes and verify independent fan instances
2 files changed
+25−1
provenance/assets.json+15−1@@ -75,7 +75,7 @@ }, { "file": "states/controller.mjs",- "sha256": "0fe8d265caa6554cdbba7fa7adc968896b08d224249176ddcd957aea5fa291e6",+ "sha256": "33876be5454e99b27cf0bef706ce81ab0a05775fc6dc292e87facbee16ca0519", "classification": "AI-created", "source": "Sunon D04114960G-01 pages 6 and 8; retrieved 2026-10-08", "units": "STEP/footprint mm; GLB metres, Z-up source",@@ -90,6 +90,20 @@ "units": "STEP/footprint mm; GLB metres, Z-up source", "license": "MIT for Adom-authored asset", "limits": "Nominal mounting envelope; rotor geometry/colors illustrative; native EDA and physical fit pending"+ },+ {+ "file": "states/controller.js",+ "sha256": "33876be5454e99b27cf0bef706ce81ab0a05775fc6dc292e87facbee16ca0519",+ "classification": "Adom-authored runtime or generated preview",+ "license": "MIT",+ "limits": "Simulated motion demonstration; no hardware control or physical qualification"+ },+ {+ "file": "viewer/index.html",+ "sha256": "bb3043e256eaef449d2953e7fb60a804119f044d9f2e656c68c88b6f8c1631fc",+ "classification": "Adom-authored runtime or generated preview",+ "license": "MIT",+ "limits": "Simulated motion demonstration; no hardware control or physical qualification" } ], "manufacturer_reference": "https://www.sunonusa.com/wp-content/uploads/2026/02/MF4020-Doc-0248743-2-0-3D.zip",
tests/test_motion.mjs+10@@ -5,3 +5,13 @@ assert.equal(c.apply({state:'on',speed:6000}).playbackRate,100);assert.equal(c.a c.apply({state:'off',speed:0});assert(group.paused);assert.throws(()=>c.apply({state:'off',speed:7000}));assert.throws(()=>c.apply({state:'on',speed:NaN}));assert.throws(()=>c.apply({state:'on',speed:10001}));assert.equal(c.apply({state:'on',speed:null,valid:false,source:'measured'}).state,'unknown');assert(group.paused); assert.throws(()=>bindFanMotion({animationGroups:[group,group],transformNodes:[rotor]},manifest)); console.log('motion adapter: rate mapping, off, invalid/stale and ambiguous binding checks passed');+// Repeated component instances with identical GLB names must remain independent.+const rotor2={name:'Fan.Rotor'};+const group2={...group,targetedAnimations:[{target:rotor2}],isStarted:false,paused:true};+const c2=bindFanMotion({animationGroups:[group2],transformNodes:[rotor2]},manifest);+c.apply({state:'on',speed:3000});c2.apply({state:'on',speed:7000});+assert.equal(group.speedRatio,50);assert.equal(group2.speedRatio,7000/60);+c.apply({state:'off',speed:0});assert(group.paused);assert.equal(group2.paused,false);+assert.throws(()=>c2.apply({state:'on',speed:Infinity}));assert(group2.paused);assert.equal(c2.status().state,'unknown');+assert.throws(()=>bindFanMotion({animationGroups:[{...group,targetedAnimations:[{target:{name:'Fan.Housing'}}]}],transformNodes:[rotor]},manifest));+console.log('instance isolation and fail-closed motion checks passed');