app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
← Commit history
v1.9.41: solo layout + designators
4 files changed
+62−17
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.40+1.9.41
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.40",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.40/adom-bridge-fusion-v1.9.40.zip",- "sha256": "5c2fff630059f5e074919373fb50d45b1dc3eb25d48692ea8573952c53cf72bc",- "size": 385382,+ "version": "1.9.41",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.41/adom-bridge-fusion-v1.9.41.zip",+ "sha256": "eae894d94a8c4d977300b7524114a91ec4690babe0d819bdda7d675f94acc9ea",+ "size": 386088, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.40",+ "version": "1.9.41", "description": "Drive Autodesk Fusion 360 from the cloud: launch Fusion, electronics board layout, design rules, exports (STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers/BOM/CPL), fast APS server-indexed cloud search plus browse/recent/file-info/versions, cloud file download/upload and folder creation, and in-app parametric modeling (fusion_run_modeling_script). Never-charge: APS calls are capped to the free tier.", "homepage": "https://wiki.adom.inc/adom/fusion-bridge", "author": "Adom Inc.",
server.py+56−11@@ -6185,6 +6185,7 @@ def _mtp_extract(lbr_text, deviceset): if not ds: return None, "deviceset %r not found in the library" % deviceset gate = re.search(r'<gate name="([^"]+)" symbol="([^"]+)"', ds)+ prefix_m = re.search(r'<deviceset name="[^"]+"[^>]*prefix="([^"]+)"', ds) dev = re.search(r'<device name="[^"]*" package="([^"]+)"', ds) if not (gate and dev): return None, "deviceset %r has no gate/package device mapping" % deviceset@@ -6201,6 +6202,7 @@ def _mtp_extract(lbr_text, deviceset): layers = re.search(r'<layers>.*?</layers>', lbr_text, re.S) return {"deviceset": ds, "symbol": sym, "package": pkg, "connects": connects, "symName": gate.group(2), "gateName": gate.group(1), "pkgName": dev.group(1),+ "prefix": (prefix_m.group(1) if prefix_m else "U"), "layers": layers.group(0) if layers else ""}, None @@ -6280,7 +6282,7 @@ def _r3d_cache_write(urn): _json.dump({"0603WAF3300T5E": urn}, open(os.path.join(d, "r3d-urn.json"), "w")) -def _mtp_generate(lib_name, ex, part_name, urn="", rex=None, r_urn=""):+def _mtp_generate(lib_name, ex, part_name, urn="", rex=None, r_urn="", solo=True): """Build the .sch/.brd pair. R1 is the REAL bundled resistor part (rex) when available, falling back to the legacy synthetic ADOMR. Wiring: R1.pad2-side pin -> the part's first connected pin; GND -> its second; VCC -> R1's first pin."""@@ -6313,28 +6315,69 @@ def _mtp_generate(lib_name, ex, part_name, urn="", rex=None, r_urn=""): r_part = '<part name="R1" library="adomdemo" deviceset="ADOMR" device="" value="330"/>' r_pkg_name = "ADOMR0603" g = ex["gateName"]+ # Correct EE designator from the deviceset's own prefix (John, 2026-08-16: "why is+ # there a D1 and R1 in a capacitor demo?"): C->C1, D->D1, U->U1; the series resistor+ # is R1, so a resistor-under-test becomes R2.+ pfx = (ex.get("prefix") or "U").strip() or "U"+ part_ref = (pfx + "2") if (pfx.upper() == "R" and not solo) else (pfx + "1")+ if solo:+ # SOLO layout (John, 2026-08-16: "when demoing a single component, you don't need+ # extra components... it's just confusing"): JUST the part - one symbol on the+ # sheet, one element centered on a small board. No resistor, no nets.+ lib_solo = ('<library name="%s"><packages>%s</packages>' % (lib_name, ex["package"])+ + (('<packages3d><package3d name="' + part_name + '" urn="" wip_urn="' + urn + '" locally_modified="yes" type="model">'+ '<packageinstances><packageinstance name="' + ex["pkgName"] + '"/></packageinstances>'+ '</package3d></packages3d>') if urn else '')+ + '<symbols>%s</symbols><devicesets>%s</devicesets></library>' % (ex["symbol"], ex["deviceset"]))+ sch = (_MTP_HEAD + _MTP_SCH_LAYERS ++ '<schematic xreflabel="%F%N/%S.%C%R" xrefpart="/%S.%C%R">'+ '<libraries>' + lib_solo + '</libraries>'+ '<attributes/><variantdefs/><classes><class number="0" name="default" width="0" drill="0"/></classes>'+ '<parts><part name="' + part_ref + '" library="' + lib_name + '" deviceset="' + part_name + '" device=""/></parts>'+ '<sheets><sheet><plain>'+ '<text x="0" y="25.4" size="2.54" layer="97">' + part_name + ' - Adom component demo</text>'+ '</plain><instances>'+ '<instance part="' + part_ref + '" gate="' + ex["gateName"] + '" x="25.4" y="12.7"/>'+ '</instances><busses/><nets/></sheet></sheets></schematic></drawing></eagle>\n')+ brd = (_MTP_HEAD + ex["layers"] ++ '<board><plain>'+ '<wire x1="0" y1="0" x2="8" y2="0" width="0.2" layer="20"/>'+ '<wire x1="8" y1="0" x2="8" y2="6" width="0.2" layer="20"/>'+ '<wire x1="8" y1="6" x2="0" y2="6" width="0.2" layer="20"/>'+ '<wire x1="0" y1="6" x2="0" y2="0" width="0.2" layer="20"/></plain>'+ '<libraries><library name="' + lib_name + '"><packages>' + ex["package"] + '</packages>'+ + (('<packages3d><package3d name="' + part_name + '" urn="" wip_urn="' + urn + '" locally_modified="yes" type="model">'+ '<packageinstances><packageinstance name="' + ex["pkgName"] + '"/></packageinstances>'+ '</package3d></packages3d>') if urn else '')+ + '</library></libraries>'+ '<attributes/><variantdefs/><classes><class number="0" name="default" width="0" drill="0"/></classes>'+ '<designrules name="default"/><autorouter/>'+ '<elements><element name="' + part_ref + '" library="' + lib_name + '" package="' + ex["pkgName"] + '" value="" x="4" y="3"'+ + ((' package3d_urn="' + urn + '"') if urn else '') + '/></elements>'+ '<signals/></board></drawing></eagle>\n')+ return sch, brd sch = (_MTP_HEAD + _MTP_SCH_LAYERS + '<schematic xreflabel="%F%N/%S.%C%R" xrefpart="/%S.%C%R">' '<libraries>' + lib + rlib + '</libraries>' '<attributes/><variantdefs/><classes><class number="0" name="default" width="0" drill="0"/></classes>' '<parts>'- '<part name="U1" library="' + lib_name + '" deviceset="' + part_name + '" device=""/>'+ '<part name="' + part_ref + '" library="' + lib_name + '" deviceset="' + part_name + '" device=""/>' + r_part + '</parts><sheets><sheet><plain>' '<text x="0" y="30.48" size="2.54" layer="97">Adom generated test circuit: ' + part_name + '</text>' '</plain><instances>' '<instance part="R1" gate="' + r_gate + '" x="12.7" y="12.7"/>'- '<instance part="U1" gate="' + g + '" x="38.1" y="12.7"/>'+ '<instance part="' + part_ref + '" gate="' + g + '" x="38.1" y="12.7"/>' '</instances><busses/><nets>' '<net name="N1" class="0"><segment>' '<wire x1="17.78" y1="12.7" x2="25.4" y2="12.7" width="0.1524" layer="91"/>'- '<pinref part="R1" gate="' + r_gate + '" pin="' + r_pin2 + '"/><pinref part="U1" gate="' + g + '" pin="' + p1 + '"/>'+ '<pinref part="R1" gate="' + r_gate + '" pin="' + r_pin2 + '"/><pinref part="' + part_ref + '" gate="' + g + '" pin="' + p1 + '"/>' '</segment></net>' '<net name="VCC" class="0"><segment>' '<wire x1="7.62" y1="12.7" x2="2.54" y2="12.7" width="0.1524" layer="91"/>' '<pinref part="R1" gate="' + r_gate + '" pin="' + r_pin1 + '"/></segment></net>' '<net name="GND" class="0"><segment>'- '<pinref part="U1" gate="' + g + '" pin="' + p2 + '"/></segment></net>'+ '<pinref part="' + part_ref + '" gate="' + g + '" pin="' + p2 + '"/></segment></net>' '</nets></sheet></sheets></schematic></drawing></eagle>\n') brd = (_MTP_HEAD + ex["layers"] + '<board><plain>'@@ -6350,13 +6393,13 @@ def _mtp_generate(lib_name, ex, part_name, urn="", rex=None, r_urn=""): '<elements>' '<element name="R1" library="' + r_lib_name + '" package="' + r_pkg_name + '" value="330" x="3.5" y="4.5"' + ((' package3d_urn="' + r_urn + '"') if (rex and r_urn) else '') + '/>'- '<element name="U1" library="' + lib_name + '" package="' + ex["pkgName"] + '" value="" x="9.5" y="4.5"'+ '<element name="' + part_ref + '" library="' + lib_name + '" package="' + ex["pkgName"] + '" value="" x="9.5" y="4.5"' + ((' package3d_urn="' + urn + '"') if urn else '') + '/>' '</elements><signals>'- '<signal name="N1"><contactref element="R1" pad="' + r_pads[r_pin2] + '"/><contactref element="U1" pad="' + pads[p1] + '"/>'+ '<signal name="N1"><contactref element="R1" pad="' + r_pads[r_pin2] + '"/><contactref element="' + part_ref + '" pad="' + pads[p1] + '"/>' '<wire x1="4.35" y1="4.5" x2="8.5" y2="4.5" width="0.3" layer="1"/></signal>' '<signal name="VCC"><contactref element="R1" pad="' + r_pads[r_pin1] + '"/></signal>'- '<signal name="GND"><contactref element="U1" pad="' + pads[p2] + '"/></signal>'+ '<signal name="GND"><contactref element="' + part_ref + '" pad="' + pads[p2] + '"/></signal>' '</signals></board></drawing></eagle>\n') return sch, brd @@ -6412,10 +6455,11 @@ def _orchestrate_make_test_project(args: dict) -> dict: # element gets package3d_urn so the 3D PCB renders the REAL component model, not a # generic extrusion (John caught every test board showing black boxes, 2026-08-15). urn_m = re.search(r'wip_urn="([^"]+)"', lbr_text)- rex, _rtxt = _mtp_resistor()+ solo = not bool(args.get("withResistor"))+ rex, _rtxt = (None, None) if solo else _mtp_resistor() r_urn = _r3d_cached_urn() if rex else "" sch_text, brd_text = _mtp_generate(lib_name, ex, deviceset,- urn_m.group(1) if urn_m else "", rex=rex, r_urn=r_urn)+ urn_m.group(1) if urn_m else "", rex=rex, r_urn=r_urn, solo=solo) d = os.path.join(tempfile.gettempdir(), "adom-fusion-wrap") os.makedirs(d, exist_ok=True) base = "ADOM-TEST-" + re.sub(r"[^A-Za-z0-9_-]", "_", deviceset)[:40]@@ -6453,7 +6497,8 @@ def _orchestrate_make_test_project(args: dict) -> dict: "docBase": base.lower()} out = {"success": True, "schPath": sch_path, "brdPath": brd_path, "deviceset": deviceset, "steps": steps,- "circuit": "R1 (330, generated 0603) in series with %s; VCC/GND stubs" % deviceset,+ "circuit": ("JUST %s on a small board (solo demo layout)" % deviceset) if solo+ else "R1 (330) in series with %s; VCC/GND stubs" % deviceset, "_hint": ("Test project opened as TWO documents (board instantiated with placed parts, " "schematic as its drawn sheet) via the dialog-free Document.newDesignFromLocal " "path. The BOARD document is active: fusion_show_2d_board / fusion_show_3d_board "