AnasSarkiz/magnetic-shutter-remote-r8
A USB‑C rechargeable battery-powered ESP32‑C3 wireless controller with Li‑ion charging, protected 3.3‑V buck‑boost regulation, thermal/safety monitoring, status LEDs, buttons, programming UART, crystal clock, and 2.4‑GHz antenna matching network.
- Version
- 0.4.0-prototype
- License
- unset
- Stars
- 0
scripts/package-r9-native-exports.py
"""Package official native exports unchanged; retain exact input/file provenance."""
import argparse
import hashlib
import json
from pathlib import Path
from zipfile import ZipFile
NATIVE_FILES = {'F_Cu.gbr','F_SilkScreen.gbr','F_Mask.gbr','F_Paste.gbr',
'B_Cu.gbr','B_SilkScreen.gbr','B_Mask.gbr','B_Paste.gbr',
'Edge_Cuts.gbr','F_Fab.gbr','drill-L1-L2.drl','drill_npth.drl'}
def package(options):
circuit_bytes=options.circuit.read_bytes()
circuit=json.loads(circuit_bytes)
if any(e['type'].endswith('_error') for e in circuit) or not any(e['type']=='pcb_trace' for e in circuit):
raise ValueError('Refusing failed or unrouted native circuit')
options.directory.mkdir(parents=True,exist_ok=True)
if set(p.name for p in options.directory.iterdir())-NATIVE_FILES:
raise ValueError('Unexpected existing file in native export directory; preserve and review it')
files=[]
with ZipFile(options.archive) as archive:
if set(archive.namelist())!=NATIVE_FILES|{'bom.csv','pick_and_place.csv'}:
raise ValueError('Official export file coverage differs')
for name in sorted(NATIVE_FILES):
raw=archive.read(name);target=options.directory/name;target.write_bytes(raw)
if target.read_bytes()!=raw:raise ValueError('Native file bytes changed')
files.append({'path':str(target),'sha256':hashlib.sha256(raw).hexdigest(),'bytes':len(raw),'matches_native_cli_bytes':True})
report={'source_circuit_sha256':hashlib.sha256(circuit_bytes).hexdigest(),
'official_cli_zip_sha256':hashlib.sha256(options.archive.read_bytes()).hexdigest(),
'native_fabrication_files':files,
'assembly_files':'Use separately audited JLCPCB-BOM.csv/JLCPCB-CPL.csv, not default CLI CSVs',
'manufacturing_acceptance':'Independent CAM review required; packaging is not a release gate pass'}
options.report.write_text(json.dumps(report,indent=2)+'\n')
print('12 exact native files retained; no Circuit JSON/Gerber geometry edited')
if __name__=='__main__':
parser=argparse.ArgumentParser()
parser.add_argument('archive',type=Path);parser.add_argument('circuit',type=Path)
parser.add_argument('--directory',type=Path,required=True);parser.add_argument('--report',type=Path,required=True)
package(parser.parse_args())