Source code for circuit_synth.kicad.bom_exporter

"""BOM (Bill of Materials) export functionality using KiCad CLI."""

import subprocess
from pathlib import Path
from typing import Dict, Any, Optional
import logging

logger = logging.getLogger(__name__)


[docs] class BOMExporter: """Export Bill of Materials from KiCad schematic files using kicad-cli."""
[docs] @staticmethod def export_csv( schematic_file: Path, output_file: Path, fields: Optional[str] = None, labels: Optional[str] = None, group_by: Optional[str] = None, exclude_dnp: bool = False, ) -> Dict[str, Any]: """ Export BOM from a KiCad schematic to CSV format using kicad-cli. Args: schematic_file: Path to .kicad_sch schematic file output_file: Path where CSV BOM should be written fields: Comma-separated fields to export. Default: all fields from schematic labels: Comma-separated column headers. Must match number of fields. group_by: Field to group references by (e.g., "Value" to group by component value) exclude_dnp: Whether to exclude "Do not populate" components Returns: dict: Result dictionary with keys: - success: bool - True if BOM was successfully exported - file: Path - Path to generated BOM file - component_count: int - Number of components in BOM - error: str (optional) - Error message if export failed Raises: FileNotFoundError: If kicad-cli is not available or schematic file not found subprocess.CalledProcessError: If kicad-cli returns non-zero exit code """ schematic_file = Path(schematic_file) output_file = Path(output_file) # Verify schematic file exists if not schematic_file.exists(): raise FileNotFoundError(f"Schematic file not found: {schematic_file}") # Create output directory if needed output_file.parent.mkdir(parents=True, exist_ok=True) # Build kicad-cli command cmd = [ "kicad-cli", "sch", "export", "bom", "--output", str(output_file), ] # Add optional parameters if fields: cmd.extend(["--fields", fields]) if labels: cmd.extend(["--labels", labels]) if group_by: cmd.extend(["--group-by", group_by]) if exclude_dnp: cmd.append("--exclude-dnp") # Add input file cmd.append(str(schematic_file)) logger.debug(f"Running BOM export: {' '.join(cmd)}") try: # Execute kicad-cli result = subprocess.run( cmd, capture_output=True, text=True, check=True, ) # Count components in BOM by reading CSV component_count = 0 try: with open(output_file, 'r') as f: # Skip header row next(f) # Count remaining lines component_count = sum(1 for _ in f) except Exception as e: logger.warning(f"Could not count components in BOM: {e}") logger.info(f"BOM exported successfully: {output_file} ({component_count} components)") return { "success": True, "file": output_file, "component_count": component_count, } except FileNotFoundError: error_msg = ( "kicad-cli not found. Ensure KiCad 8.0+ is installed and " "kicad-cli is available in PATH." ) logger.error(error_msg) raise FileNotFoundError(error_msg) except subprocess.CalledProcessError as e: error_msg = f"kicad-cli BOM export failed: {e.stderr}" logger.error(error_msg) raise RuntimeError(error_msg) from e