Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
"""
JLCSearch API client (public, no authentication required)
Alternative to official JLCPCB API using the community-maintained
jlcsearch service at https://jlcsearch.tscircuit.com/
"""
import logging
import time
from typing import Any, Callable, Dict, List, Optional, Union
import requests
logger = logging.getLogger("kicad_interface")
class JLCSearchClient:
"""
Client for JLCSearch public API (tscircuit)
Provides access to JLCPCB parts database without authentication
via the community-maintained jlcsearch service.
"""
BASE_URL = "https://jlcsearch.tscircuit.com"
def __init__(self) -> None:
"""Initialize JLCSearch API client"""
pass
def search_components(
self, category: str = "components", limit: int = 100, offset: int = 0, **filters: Dict
) -> List[Dict]:
"""
Search components in JLCSearch database
Args:
category: Component category (e.g., "resistors", "capacitors", "components")
limit: Maximum number of results
offset: Offset for pagination
**filters: Additional filters (e.g., package="0603", resistance=1000)
Returns:
List of component dicts
"""
url = f"{self.BASE_URL}/{category}/list.json"
params = {"limit": limit, "offset": offset, **filters}
try:
response = requests.get(url, params=params, timeout=30)
response.raise_for_status()
data = response.json()
# The response has the category name as key
# e.g., {"resistors": [...]} or {"components": [...]}
for key, value in data.items():
if isinstance(value, list):
return value
return []
except requests.exceptions.RequestException as e:
logger.error(f"Failed to search JLCSearch: {e}")
raise Exception(f"JLCSearch API request failed: {e}")
def search_resistors(
self, resistance: Optional[int] = None, package: Optional[str] = None, limit: int = 100
) -> List[Dict]:
"""
Search for resistors
Args:
resistance: Resistance value in ohms
package: Package type (e.g., "0603", "0805")
limit: Maximum results
Returns:
List of resistor dicts with fields:
- lcsc: LCSC number (integer)
- mfr: Manufacturer part number
- package: Package size
- is_basic: True if basic library part
- resistance: Resistance in ohms
- tolerance_fraction: Tolerance (0.01 = 1%)
- power_watts: Power rating in mW
- stock: Available stock
- price1: Price per unit
"""
filters: Dict[str, Any] = {}
if resistance is not None:
filters["resistance"] = resistance
if package:
filters["package"] = package
return self.search_components("resistors", limit=limit, **filters)
def search_capacitors(
self, capacitance: Optional[float] = None, package: Optional[str] = None, limit: int = 100
) -> List[Dict]:
"""
Search for capacitors
Args:
capacitance: Capacitance value in farads
package: Package type
limit: Maximum results
Returns:
List of capacitor dicts
"""
filters: Dict[str, Any] = {}
if capacitance is not None:
filters["capacitance"] = capacitance
if package:
filters["package"] = package
return self.search_components("capacitors", limit=limit, **filters)
def get_part_by_lcsc(self, lcsc_number: int) -> Optional[Dict]:
"""
Get part details by LCSC number
Args:
lcsc_number: LCSC number (integer, without 'C' prefix)
Returns:
Part dict or None if not found
"""
# Search across all components filtering by LCSC
# Note: jlcsearch doesn't have a dedicated single-part endpoint
# so we search and filter
try:
results = self.search_components("components", limit=1, lcsc=lcsc_number)
return results[0] if results else None
except Exception as e:
logger.error(f"Failed to get part C{lcsc_number}: {e}")
return None
# NOTE: bulk catalog download was removed (issue #199). The JLCSearch
# endpoint is a *search front-end* that ignores the ``offset`` parameter,
# so offset-paged "download everything" loops returned the same first 100
# parts forever. Full-catalog download now uses a prebuilt source via
# ``commands.jlcpcb_downloader.download_database()``. This client remains
# for interactive/parametric lookups only (search_components, etc.).
def test_jlcsearch_connection() -> bool:
"""
Test JLCSearch API connection
Returns:
True if connection successful, False otherwise
"""
try:
client = JLCSearchClient()
# Test by searching for 1k resistors
results = client.search_resistors(resistance=1000, limit=5)
logger.info(f"JLCSearch API connection test successful - found {len(results)} resistors")
return True
except Exception as e:
logger.error(f"JLCSearch API connection test failed: {e}")
return False
if __name__ == "__main__":
# Test the JLCSearch client
logging.basicConfig(level=logging.INFO)
print("Testing JLCSearch API connection...")
if test_jlcsearch_connection():
print("✓ Connection successful!")
client = JLCSearchClient()
print("\nSearching for 1k 0603 resistors...")
resistors = client.search_resistors(resistance=1000, package="0603", limit=5)
print(f"✓ Found {len(resistors)} resistors")
if resistors:
print(f"\nExample resistor:")
r = resistors[0]
print(f" LCSC: C{r.get('lcsc')}")
print(f" MFR: {r.get('mfr')}")
print(f" Package: {r.get('package')}")
print(f" Resistance: {r.get('resistance')}Ω")
print(f" Tolerance: {r.get('tolerance_fraction', 0) * 100}%")
print(f" Power: {r.get('power_watts')}mW")
print(f" Stock: {r.get('stock')}")
print(f" Price: ${r.get('price1')}")
print(f" Basic Library: {'Yes' if r.get('is_basic') else 'No'}")
else:
print("✗ Connection failed")
670d2a9c4This file is pinned to the selected release. Download the original to open it in your design tool.