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/recheck_board_sourcing.py
"""Read public JLCPCB listings without authentication, ordering or substitution."""
import concurrent.futures
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import re
import urllib.request
ROOT = Path(__file__).resolve().parents[1]
OUTPUT = ROOT / 'evidence/R7-order-review/sourcing'
def walk_records(node):
if isinstance(node, dict):
yield node
for child in node.values():
yield from walk_records(child)
elif isinstance(node, list):
for child in node:
yield from walk_records(child)
def listing_records(page):
frames = []
for match in re.finditer(r'self\.__next_f\.push\((.*?)\)</script>', page, re.S):
frame = json.loads(match.group(1))
if len(frame) > 1 and isinstance(frame[1], str):
frames.append(frame[1])
for line in ''.join(frames).splitlines():
_, separator, body = line.partition(':')
if separator and body[:1] in ('{', '['):
try:
record = json.loads(body)
except json.JSONDecodeError:
# RSC text/stream records are not JSON component records.
continue
yield from walk_records(record)
def inspect_part(part):
number = part['lcsc']
url = part['source_link']
with urllib.request.urlopen(url, timeout=45) as response:
raw = response.read()
(OUTPUT / f'{number}.html').write_bytes(raw)
records = [record for record in listing_records(raw.decode())
if record.get('componentCode') == number]
identities = [r for r in records if r.get('componentModelEn')]
if not identities or any(r['componentModelEn'] != part['manufacturer_part_number']
for r in identities):
raise ValueError(f'{number}: exact manufacturer part number mismatch')
assembly = next(r for r in records if 'assemblyMode' in r)
buying = next(r for r in records if 'canPresaleNumber' in r)
required = part['quantity'] * 5
result = {
'lcsc': number, 'mpn': part['manufacturer_part_number'],
'references': part['reference_designators'], 'url': url,
'listing_sha256': hashlib.sha256(raw).hexdigest(),
'checked_at_utc': datetime.now(timezone.utc).isoformat(),
'quantity_for_five_finished_boards': required,
'stock_fields': {key: buying[key] for key in
['canPresaleNumber', 'overseasStockCount', 'preMinPurchaseNum']},
'assembly_fields': {key: assembly.get(key) for key in
['assemblyMode', 'componentLibraryType', 'xrayFlag',
'moistureSensitivityLevelEn', 'fixtureFlag',
'needAuditFlag', 'manufacturerBlackFlag',
'orderInstructionEnglish']},
'status': 'PASS' if buying['canPresaleNumber'] >= required and
assembly['assemblyMode'] == 'smtWeld' and
not assembly.get('manufacturerBlackFlag') else 'BLOCKED',
'scope': 'Public inventory/listing only; attrition, stock reservation and '
'supplier-processed PCBA mapping still require order-preview review.'
}
(OUTPUT / f'{number}.json').write_text(json.dumps(result, indent=2) + '\n')
return result
def main():
OUTPUT.mkdir(parents=True, exist_ok=True)
parts = json.loads((ROOT / 'bom.json').read_text())['parts']
rows = []
failures = []
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
future_parts = {executor.submit(inspect_part, part): part for part in parts}
for future in concurrent.futures.as_completed(future_parts):
part = future_parts[future]
try:
result = future.result()
rows.append(result)
print(part['lcsc'], result['status'], result['stock_fields'], flush=True)
except Exception as error:
failure = {'lcsc': part['lcsc'], 'error': str(error)}
failures.append(failure)
print('BLOCKED', failure, flush=True)
report = {'parts': sorted(rows, key=lambda row: row['lcsc']),
'failures': failures, 'expected_exact_identities': len(parts),
'all_pass': len(rows) == len(parts) and not failures and
all(row['status'] == 'PASS' for row in rows)}
(OUTPUT.parent / 'sourcing-recheck.json').write_text(json.dumps(report, indent=2) + '\n')
raise SystemExit(not report['all_pass'])
if __name__ == '__main__':
main()