molecule
AM/FM Receiver Molecule
Public Made by Adomby adom
USB-powered AM/FM receiver on an Adom Molecule: discrete BJT and op-amp superhet (10.7 MHz and 455 kHz IFs), STM32G0B1 tuning, self-calibration and USB audio over one USB-C cable. 96 x 96 mm pin grid, 4 layers, KiCad 10.
main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
"""JLCPCB parts lookup (public parts-search API) with a small on-disk cache."""
import json, os, time, urllib.request, hashlib
API = 'https://jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList'
CACHE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'build', 'jlc_cache')
def _post(body):
os.makedirs(CACHE, exist_ok=True)
key = hashlib.sha1(json.dumps(body, sort_keys=True).encode()).hexdigest()
path = os.path.join(CACHE, key + '.json')
if os.path.exists(path) and time.time() - os.path.getmtime(path) < 6 * 3600:
return json.load(open(path))
req = urllib.request.Request(API, data=json.dumps(body).encode(), method='POST',
headers={'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0'})
for attempt in range(4):
try:
with urllib.request.urlopen(req, timeout=40) as r:
d = json.load(r)
json.dump(d, open(path, 'w'))
return d
except Exception as e:
time.sleep(2 + 3 * attempt)
err = e
raise err
def norm(it):
attrs = {a.get('attribute_name_en'): a.get('attribute_value_name') for a in (it.get('attributes') or [])}
return dict(code=it['componentCode'], mpn=it.get('componentModelEn'), brand=it.get('componentBrandEn'),
package=it.get('componentSpecificationEn'), type=it.get('componentLibraryType'),
preferred=bool(it.get('preferredComponentFlag')), stock=it.get('stockCount') or 0,
category=it.get('firstSortName'), describe=it.get('describe') or '', attrs=attrs,
datasheet=it.get('dataManualUrl'), price=(it.get('componentPrices') or [{}])[0].get('productPrice'))
def search(keyword, size=50, pages=1):
out = []
for pg in range(1, pages + 1):
d = _post({'keyword': keyword, 'currentPage': pg, 'pageSize': size})
lst = ((d.get('data') or {}).get('componentPageInfo') or {}).get('list') or []
out += [norm(i) for i in lst]
if len(lst) < size:
break
return out
def part(code):
r = [p for p in search(code, size=10) if p['code'] == code]
return r[0] if r else None
def fmt(p):
return '%-10s %-6s%s stock=%-9d %-24s %-18s %s' % (
p['code'], p['type'], '*' if p['preferred'] else ' ', p['stock'], (p['mpn'] or '')[:24],
(p['package'] or '')[:18], p['describe'][:90])
if __name__ == '__main__':
import sys
for q in sys.argv[1:]:
print('==', q)
for p in search(q, size=15):
print(' ', fmt(p))