my-kicad-user-folder/3rdparty/plugins/com_github_Steffen-W_impartGUI/KiCadImport.py
2025-03-21 13:28:36 +08:00

923 lines
35 KiB
Python

#!/usr/bin/env python3
# coding: utf-8
# Assembles local KiCad component libraries from downloaded Octopart,
# Samacsys, Ultralibrarian and Snapeda zipfiles. Currently assembles just
# symbols and footptints. Tested with KiCad 7.0 for Ubuntu.
import pathlib
from enum import Enum
from zipfile import Path
from typing import Tuple, Union, Any
import re
import zipfile
from os import stat, remove
from os.path import isfile
import logging
if __name__ == "__main__":
from kicad_cli import kicad_cli
from s_expression_parse import parse_sexp, search_recursive, extract_properties
else:
from .kicad_cli import kicad_cli
from .s_expression_parse import parse_sexp, search_recursive, extract_properties
cli = kicad_cli()
class Modification(Enum):
MKDIR = 0
TOUCH_FILE = 1
MODIFIED_FILE = 2
EXTRACTED_FILE = 3
class ModifiedObject:
def __init__(self):
self.dict = {}
def append(self, obj: pathlib.Path, modification: Modification):
self.dict[obj] = modification
# keeps track of which files were modified in case an error occurs we can revert these changes before exiting
modified_objects = ModifiedObject()
def check_file(path: pathlib.Path):
"""
Check if file exists, if not create parent directories and touch file
:param path:
"""
if not path.exists():
if not path.parent.is_dir():
path.parent.mkdir(parents=True)
modified_objects.append(path.parent, Modification.MKDIR)
path.touch(mode=0o666)
modified_objects.append(path, Modification.TOUCH_FILE)
def unzip(root, suffix):
"""
return zipfile.Path starting with root ending with suffix else return None
"""
match = None
def zipper(parent):
if parent.name.endswith(suffix):
return parent
elif parent.is_dir():
for child in parent.iterdir():
match = zipper(child)
if match:
return match
match = zipper(root)
return match
class REMOTE_TYPES(Enum):
Octopart = 0
Samacsys = 1
UltraLibrarian = 2
Snapeda = 3
class import_lib:
def print(self, txt):
print("->" + txt)
def __init__(self):
self.KICAD_3RD_PARTY_LINK = "${KICAD_3RD_PARTY}"
def set_DEST_PATH(self, DEST_PATH_=pathlib.Path.home() / "KiCad"):
self.DEST_PATH = pathlib.Path(DEST_PATH_)
def cleanName(self, name):
invalid = '<>:"/\\|?* '
name = name.strip()
for char in invalid: # remove invalid characters
name = name.replace(char, "_")
return name
def get_remote_info(
self, zf: zipfile.ZipFile
) -> Tuple[Path, Path, Path, Path, REMOTE_TYPES]:
"""
:param root_path:
:type root_path: Path
:return: dcm_path, lib_path, footprint_path, model_path, remote_type
"""
self.footprint_name = None
root_path = zipfile.Path(zf)
self.dcm_path = root_path / "device.dcm"
self.lib_path = root_path / "device.lib"
self.footprint_path = root_path / "device.pretty"
self.model_path = root_path / "device.step"
# todo fill in model path for OCTOPART
if (
self.dcm_path.exists()
and self.lib_path.exists()
and self.footprint_path.exists()
):
remote_type = REMOTE_TYPES.Octopart
return (
self.dcm_path,
self.lib_path,
self.footprint_path,
self.model_path,
remote_type,
)
self.lib_path_new = unzip(root_path, ".kicad_sym")
directory = unzip(root_path, "KiCad")
if directory:
self.dcm_path = unzip(directory, ".dcm")
self.lib_path = unzip(directory, ".lib")
self.footprint_path = directory
self.model_path = unzip(root_path, ".step")
if not self.model_path:
self.model_path = unzip(root_path, ".stp")
assert self.dcm_path and (
self.lib_path or self.lib_path_new
), "Not in samacsys format"
remote_type = REMOTE_TYPES.Samacsys
return (
self.dcm_path,
self.lib_path,
self.footprint_path,
self.model_path,
remote_type,
)
directory = root_path / "KiCAD"
if directory.exists():
self.dcm_path = unzip(directory, ".dcm")
self.lib_path = unzip(directory, ".lib")
self.footprint_path = unzip(directory, ".pretty")
self.model_path = unzip(root_path, ".step")
if not self.model_path:
self.model_path = unzip(root_path, ".stp")
assert (
self.lib_path or self.lib_path_new
) and self.footprint_path, "Not in ultralibrarian format"
remote_type = REMOTE_TYPES.UltraLibrarian
return (
self.dcm_path,
self.lib_path,
self.footprint_path,
self.model_path,
remote_type,
)
footprint = unzip(root_path, ".kicad_mod")
self.lib_path = unzip(root_path, ".lib")
if self.lib_path or self.lib_path_new:
self.dcm_path = unzip(root_path, ".dcm")
# self.footprint_path = root_path
self.footprint_path = None
if footprint:
self.footprint_path = footprint.parent
self.model_path = unzip(root_path, ".step")
remote_type = REMOTE_TYPES.Snapeda
assert (
self.lib_path or self.lib_path_new
) and self.footprint_path, "Not in Snapeda format"
return (
self.dcm_path,
self.lib_path,
self.footprint_path,
self.model_path,
remote_type,
)
if footprint or self.lib_path_new:
assert False, "Unknown library zipfile"
else:
assert False, "zipfile is probably not a library to import"
def import_dcm(
self,
device: str,
remote_type: REMOTE_TYPES,
dcm_path: pathlib.Path,
overwrite_if_exists=True,
file_ending="",
) -> Tuple[Union[pathlib.Path, None], Union[pathlib.Path, None]]:
"""
# .dcm file parsing
# Note this reads in the existing dcm file for the particular remote repo, and tries to catch any duplicates
# before overwriting or creating duplicates. It reads the existing dcm file line by line and simply copy+paste
# each line if nothing will be overwritten or duplicated. If something could be overwritten or duplicated, the
# terminal will prompt whether to overwrite or to keep the existing content and ignore the new file contents.
:returns: dcm_file_read, dcm_file_write
"""
self.dcm_skipped = False
# Array of values defining all attributes of .dcm file
dcm_attributes = (
dcm_path.read_text(encoding="utf-8").splitlines()
if dcm_path
else ["#", "# " + device, "#", "$CMP " + device, "D", "F", "$ENDCMP"]
)
# Find which lines contain the component information (ignore the rest).
index_start = None
index_end = None
index_header_start = None
for attribute_idx, attribute in enumerate(dcm_attributes):
if index_start is None:
if attribute.startswith("#"):
if attribute.strip() == "#" and index_header_start is None:
index_header_start = attribute_idx # header start
elif attribute.startswith("$CMP "):
component_name = attribute[5:].strip()
if not self.cleanName(component_name) == self.cleanName(device):
raise Warning("Unexpected device in " + dcm_path.name)
dcm_attributes[attribute_idx] = attribute.replace(
component_name, device, 1
)
index_start = attribute_idx
else:
index_header_start = None
elif index_end is None:
if attribute.startswith("$CMP "):
raise Warning("Multiple devices in " + dcm_path.name)
elif attribute.startswith("$ENDCMP"):
index_end = attribute_idx + 1
elif attribute.startswith("D"):
description = attribute[2:].strip()
if description:
dcm_attributes[attribute_idx] = "D " + description
elif attribute.startswith("F"):
datasheet = attribute[2:].strip()
if datasheet:
dcm_attributes[attribute_idx] = "F " + datasheet
if index_end is None:
raise Warning(device + "not found in " + dcm_path.name)
dcm_file_read = self.DEST_PATH / (remote_type.name + file_ending + ".dcm")
dcm_file_write = self.DEST_PATH / (remote_type.name + ".dcm~")
overwrite_existing = overwrote_existing = False
check_file(dcm_file_read)
check_file(dcm_file_write)
with dcm_file_read.open("rt", encoding="utf-8") as readfile:
with dcm_file_write.open("wt", encoding="utf-8") as writefile:
if stat(dcm_file_read).st_size == 0:
# todo Handle appending to empty file
with dcm_file_read.open("wt", encoding="utf-8") as template_file:
template = ["EESchema-DOCLIB Version 2.0", "#End Doc Library"]
template_file.writelines(line + "\n" for line in template)
template_file.close()
for line in readfile:
if re.match("# *end ", line, re.IGNORECASE):
if not overwrote_existing:
writefile.write(
"\n".join(
dcm_attributes[
(
index_start
if index_header_start is None
else index_header_start
) : index_end
]
)
+ "\n"
)
writefile.write(line)
break
elif line.startswith("$CMP "):
component_name = line[5:].strip()
if component_name.startswith(device):
if overwrite_if_exists:
overwrite_existing = True
self.print("Overwrite existing dcm")
else:
overwrite_existing = False
# self.print("Import of dcm skipped")
self.dcm_skipped = True
return dcm_file_read, dcm_file_write
writefile.write(
"\n".join(dcm_attributes[index_start:index_end]) + "\n"
)
overwrote_existing = True
else:
writefile.write(line)
elif overwrite_existing:
if line.startswith("$ENDCMP"):
overwrite_existing = False
else:
writefile.write(line)
return dcm_file_read, dcm_file_write
def import_model(
self, model_path: pathlib.Path, remote_type: REMOTE_TYPES, overwrite_if_exists
) -> Union[pathlib.Path, None]:
# --------------------------------------------------------------------------------------------------------
# 3D Model file extraction
# --------------------------------------------------------------------------------------------------------
if not model_path:
return False
write_file = self.DEST_PATH / (remote_type.name + ".3dshapes") / model_path.name
self.model_skipped = False
if write_file.exists():
if overwrite_if_exists:
overwrite_existing = True
else:
self.print("Import of 3d model skipped")
self.model_skipped = True
return model_path
if model_path.is_file():
check_file(write_file)
write_file.write_bytes(model_path.read_bytes())
modified_objects.append(write_file, Modification.EXTRACTED_FILE)
if overwrite_if_exists:
self.print("Overwrite existing 3d model")
else:
self.print("Import 3d model")
return model_path
def import_footprint(
self,
remote_type: REMOTE_TYPES,
footprint_path: pathlib.Path,
found_model: pathlib.Path,
overwrite_if_exists=True,
) -> Tuple[Union[pathlib.Path, None], Union[pathlib.Path, None]]:
"""
# Footprint file parsing
:returns: footprint_file_read, footprint_file_write
"""
footprint_file_read = None
footprint_file_write = None
self.footprint_skipped = False
footprint_path_item_tmp = None
for (
footprint_path_item
) in footprint_path.iterdir(): # try to use only newer file
if footprint_path_item.name.endswith(".kicad_mod"):
footprint_path_item_tmp = footprint_path_item
break
elif footprint_path_item.name.endswith(".mod"):
footprint_path_item_tmp = footprint_path_item
footprint_path_item = footprint_path_item_tmp
if not footprint_path_item:
self.print("No footprint found")
return footprint_file_read, footprint_file_write
if footprint_path_item.name.endswith("mod"):
footprint = footprint_path_item.read_text(encoding="utf-8")
footprint_write_path = self.DEST_PATH / (remote_type.name + ".pretty")
footprint_file_read = footprint_write_path / footprint_path_item.name
footprint_file_write = footprint_write_path / (
footprint_path_item.name + "~"
)
if found_model:
footprint.splitlines()
model_path = f"{self.KICAD_3RD_PARTY_LINK}/{remote_type.name}.3dshapes/{found_model.name}"
model = [
f' (model "{model_path}"',
" (offset (xyz 0 0 0))",
" (scale (xyz 1 1 1))",
" (rotate (xyz 0 0 0))",
" )",
]
overwrite_existing = overwrote_existing = False
if footprint_file_read.exists():
if overwrite_if_exists:
overwrite_existing = True
self.print("Overwrite existing footprint")
else:
self.print("Import of footprint skipped")
self.footprint_skipped = True
return footprint_file_read, footprint_file_write
check_file(footprint_file_read)
with footprint_file_read.open("wt", encoding="utf-8") as wr:
wr.write(footprint)
overwrote_existing = True
check_file(footprint_file_write)
with footprint_file_read.open("rt", encoding="utf-8") as readfile:
with footprint_file_write.open("wt", encoding="utf-8") as writefile:
if stat(footprint_file_read).st_size == 0:
# todo Handle appending to empty file?
pass
lines = readfile.readlines()
write_3d_into_file = False
for line_idx, line in enumerate(lines):
if not write_3d_into_file and line_idx == len(lines) - 1:
writefile.writelines(
model_line + "\n" for model_line in model
)
writefile.write(line)
break
elif line.strip().startswith(r"(model"):
writefile.write(model[0] + "\n")
write_3d_into_file = True
else:
writefile.write(line)
self.print("Import footprint")
else:
check_file(footprint_file_write)
with footprint_file_write.open("wt", encoding="utf-8") as wr:
wr.write(footprint)
self.print("Import footprint")
return footprint_file_read, footprint_file_write
def import_lib(
self,
remote_type: REMOTE_TYPES,
lib_path: pathlib.Path,
overwrite_if_exists=True,
) -> Tuple[str, Union[pathlib.Path, None], Union[pathlib.Path, None]]:
"""
.lib file parsing
Note this reads in the existing lib file for the particular remote repo, and tries to catch any duplicates
before overwriting or creating duplicates. It reads the existing dcm file line by line and simply copy+paste
each line if nothing will be overwritten or duplicated. If something could be overwritten or duplicated, the
terminal will prompt whether to overwrite or to keep the existing content and ignore the new file contents.
:returns: device, lib_file_read, lib_file_write
"""
self.lib_skipped = False
device = None
lib_lines = lib_path.read_text(encoding="utf-8").splitlines()
# Find which lines contain the component information in file to be imported
index_start = None
index_end = None
index_header_start = None
for line_idx, line in enumerate(lib_lines):
if index_start is None:
if line.startswith("#"):
if line.strip() == "#" and index_header_start is None:
index_header_start = line_idx # header start
elif line.startswith("DEF "):
device = line.split()[1]
index_start = line_idx
else:
index_header_start = None
elif index_end is None:
if line.startswith("F2"):
footprint = line.split()[1]
footprint = footprint.strip('"')
self.footprint_name = self.cleanName(footprint)
lib_lines[line_idx] = line.replace(
footprint, remote_type.name + ":" + self.footprint_name, 1
)
elif line.startswith("ENDDEF"):
index_end = line_idx + 1
elif line.startswith("F1 "):
lib_lines[line_idx] = line.replace(device, device, 1)
elif line.startswith("DEF "):
raise Warning("Multiple devices in " + lib_path.name)
if index_end is None:
raise Warning(device + " not found in " + lib_path.name)
lib_file_read = self.DEST_PATH / (remote_type.name + ".lib")
lib_file_write = self.DEST_PATH / (remote_type.name + ".lib~")
overwrite_existing = overwrote_existing = overwritten = False
check_file(lib_file_read)
check_file(lib_file_write)
with lib_file_read.open("rt", encoding="utf-8") as readfile:
with lib_file_write.open("wt", encoding="utf-8") as writefile:
if stat(lib_file_read).st_size == 0:
# todo Handle appending to empty file
with lib_file_read.open("wt", encoding="utf-8") as template_file:
template = [
"EESchema-LIBRARY Version 2.4",
"#encoding utf-8",
"# End Library",
]
template_file.writelines(line + "\n" for line in template)
template_file.close()
# For each line in the existing lib file (not the file being read from the zip. The lib file you will
# add it to.)
for line in readfile:
# Is this trying to match ENDDRAW, ENDDEF, End Library or any of the above?
if re.match("# *end ", line, re.IGNORECASE):
# If you already overwrote the new info don't add it to the end
if not overwrote_existing:
writefile.write(
"\n".join(
lib_lines[
(
index_start
if index_header_start is None
else index_header_start
) : index_end
]
)
+ "\n"
)
writefile.write(line)
break
# Catch start of new component definition
elif line.startswith("DEF "):
component_name = line.split()[1]
# Catch if the currently read component matches the name of the component you are planning to
# write
if component_name.startswith(device):
# Ask if you want to overwrite existing component
if overwrite_if_exists:
overwrite_existing = True
overwritten = True
self.print("Overwrite existing lib")
else:
self.print("Import of lib skipped")
self.lib_skipped = True
return device, lib_file_read, lib_file_write
writefile.write(
"\n".join(lib_lines[index_start:index_end]) + "\n"
)
overwrote_existing = True
else:
writefile.write(line)
elif overwrite_existing:
if line.startswith("ENDDEF"):
overwrite_existing = False
else:
writefile.write(line)
if not overwritten:
self.print("Import lib")
return device, lib_file_read, lib_file_write
def import_lib_new(
self,
remote_type: REMOTE_TYPES,
lib_path: pathlib.Path,
overwrite_if_exists=True,
) -> Tuple[str, Union[pathlib.Path, None], Union[pathlib.Path, None]]:
symbol_name = None
def extract_symbol_names(input_text):
pattern = r'"(.*?)"' # Searches for text in quotes
# Searches for "(symbol" followed by text in quotes
pattern = r'\(symbol\s+"(.*?)"'
matches = re.findall(pattern, input_text)
return matches
symbol_name = None
def replace_footprint_name(string, original_name, remote_type_name, new_name):
escaped_original_name = re.escape(original_name)
pattern = rf'\(property\s+"Footprint"\s+"{escaped_original_name}"'
replacement = f'(property "Footprint" "{remote_type_name}:{new_name}"'
modified_string = re.sub(pattern, replacement, string, flags=re.MULTILINE)
return modified_string
def extract_symbol_section_new(sexpression: str, symbol_name):
pattern = re.compile(
r'\(symbol "{}"(.*?)\)\n\)'.format(re.escape(symbol_name)), re.DOTALL
)
match = pattern.search(sexpression)
if match:
return f'(symbol "{symbol_name}"{match.group(1)})'
return None
RAW_text = lib_path.read_text(encoding="utf-8") # new
sexp_list = parse_sexp(RAW_text) # new
symbol_name = search_recursive(sexp_list, "symbol") # new
symbol_properties = extract_properties(sexp_list) # new
symbol_section = extract_symbol_section_new(RAW_text, symbol_name)
Footprint_name_raw = symbol_properties["Footprint"] # new
self.footprint_name = self.cleanName(Footprint_name_raw)
symbol_section = replace_footprint_name(
symbol_section, Footprint_name_raw, remote_type.name, self.footprint_name
)
lib_file_read = self.DEST_PATH / (remote_type.name + ".kicad_sym")
lib_file_read_old = self.DEST_PATH / (remote_type.name + "_kicad_sym.kicad_sym")
lib_file_write = self.DEST_PATH / (remote_type.name + ".kicad_sym~")
if isfile(lib_file_read_old) and not isfile(lib_file_read):
lib_file_read = lib_file_read_old
if not lib_file_read.exists(): # library does not yet exist
with lib_file_write.open("wt", encoding="utf-8") as writefile:
text = RAW_text.strip().split("\n")
writefile.write(text[0] + "\n")
writefile.write(symbol_section + "\n")
writefile.write(text[-1])
check_file(lib_file_read)
self.print("Import kicad_sym")
return symbol_name, lib_file_read, lib_file_write
check_file(lib_file_read)
lib_file_txt = lib_file_read.read_text(encoding="utf-8")
existing_libs = extract_symbol_names(lib_file_txt)
if symbol_name in existing_libs:
if overwrite_if_exists:
self.print("Overwrite existing kicad_sym is not implemented") # TODO
else:
self.print("Import of kicad_sym skipped")
return symbol_name, lib_file_read, lib_file_write
closing_bracket = lib_file_txt.rfind(")")
with lib_file_write.open("wt", encoding="utf-8") as writefile:
writefile.write(lib_file_txt[:closing_bracket])
writefile.write(symbol_section + "\n")
writefile.write(lib_file_txt[closing_bracket:])
self.print("Import kicad_sym")
return symbol_name, lib_file_read, lib_file_write
def import_all(
self, zip_file: pathlib.Path, overwrite_if_exists=True, import_old_format=True
):
"""zip is a pathlib.Path to import the symbol from"""
if not zipfile.is_zipfile(zip_file):
return None
self.print(f"Import: {zip_file}")
with zipfile.ZipFile(zip_file) as zf:
(
dcm_path,
lib_path,
footprint_path,
model_path,
remote_type,
) = self.get_remote_info(zf)
self.print("Identify " + remote_type.name)
CompatibilityMode = False
if lib_path and not self.lib_path_new:
CompatibilityMode = True
temp_path = self.DEST_PATH / "temp.lib"
temp_path_new = self.DEST_PATH / "temp.kicad_sym"
if temp_path.exists():
remove(temp_path)
if temp_path_new.exists():
remove(temp_path_new)
with temp_path.open("wt", encoding="utf-8") as writefile:
text = lib_path.read_text(encoding="utf-8")
writefile.write(text)
if temp_path.exists() and cli.exists():
cli.upgrade_sym_lib(temp_path, temp_path_new)
self.print("compatibility mode: convert from .lib to .kicad_sym")
if temp_path_new.exists() and temp_path_new.is_file():
self.lib_path_new = temp_path_new
else:
self.print("error during conversion")
if self.lib_path_new:
device, lib_file_new_read, lib_file_new_write = self.import_lib_new(
remote_type, self.lib_path_new, overwrite_if_exists
)
file_ending = ""
if "_kicad_sym" in lib_file_new_read.name:
file_ending = "_kicad_sym"
dcm_file_new_read, dcm_file_new_write = self.import_dcm(
device,
remote_type,
dcm_path,
overwrite_if_exists,
file_ending=file_ending,
)
if not import_old_format:
lib_path = None
if CompatibilityMode:
if temp_path.exists():
remove(temp_path)
if temp_path_new.exists():
remove(temp_path_new)
if lib_path:
device, lib_file_read, lib_file_write = self.import_lib(
remote_type, lib_path, overwrite_if_exists
)
dcm_file_read, dcm_file_write = self.import_dcm(
device, remote_type, dcm_path, overwrite_if_exists
)
found_model = self.import_model(
model_path, remote_type, overwrite_if_exists
)
footprint_file_read, footprint_file_write = self.import_footprint(
remote_type, footprint_path, found_model, overwrite_if_exists
)
# replace read files with write files only after all operations succeeded
if self.lib_path_new:
if lib_file_new_write.exists():
lib_file_new_write.replace(lib_file_new_read)
if dcm_file_new_write.exists() and not self.dcm_skipped:
dcm_file_new_write.replace(dcm_file_new_read)
elif dcm_file_new_write.exists():
remove(dcm_file_new_write)
if lib_path:
if dcm_file_write.exists() and not self.dcm_skipped:
dcm_file_write.replace(dcm_file_read)
elif dcm_file_write.exists():
remove(dcm_file_write)
if lib_file_write.exists() and not self.lib_skipped:
lib_file_write.replace(lib_file_read)
elif lib_file_write.exists():
remove(lib_file_write)
if (
footprint_file_read
and (self.footprint_name != footprint_file_read.stem)
and not self.footprint_skipped
):
self.print(
'Warning renaming footprint file "'
+ footprint_file_read.stem
+ '" to "'
+ self.footprint_name
+ '"'
)
if footprint_file_read.exists():
remove(footprint_file_read)
footprint_file_read = footprint_file_read.parent / (
self.footprint_name + footprint_file_read.suffix
)
if (
footprint_file_write
and footprint_file_write.exists()
and not self.footprint_skipped
):
footprint_file_write.replace(footprint_file_read)
elif footprint_file_write and footprint_file_write.exists():
remove(footprint_file_write)
return ("OK",)
def main(
lib_file, lib_folder, overwrite=False, KICAD_3RD_PARTY_LINK="${KICAD_3RD_PARTY}"
):
lib_folder = pathlib.Path(lib_folder)
lib_file = pathlib.Path(lib_file)
print("overwrite", overwrite)
if not lib_folder.is_dir():
print(f"Error destination folder {lib_folder} does not exist!")
return 0
if not lib_file.is_file():
print(f"Error file {lib_folder} to be imported was not found!")
return 0
impart = import_lib()
impart.KICAD_3RD_PARTY_LINK = KICAD_3RD_PARTY_LINK
impart.set_DEST_PATH(lib_folder)
try:
(res,) = impart.import_all(lib_file, overwrite_if_exists=overwrite)
print(res)
except AssertionError as e:
print(e)
except Exception as e:
print(e)
if __name__ == "__main__":
import argparse
logging.basicConfig(level=logging.ERROR)
# Example: python plugins/KiCadImport.py --download-folder Demo/libs --lib-folder import_test
parser = argparse.ArgumentParser(
description="Import KiCad libraries from a file or folder."
)
# Create mutually exclusive arguments for file or folder
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"--download-folder",
help="Path to the folder with the zip files to be imported.",
)
group.add_argument("--download-file", help="Path to the zip file to import.")
group.add_argument("--easyeda", help="Import easyeda part. example: C2040")
parser.add_argument(
"--lib-folder",
required=True,
help="Destination folder for the imported KiCad files.",
)
parser.add_argument(
"--overwrite-if-exists",
action="store_true",
help="Overwrite existing files if they already exist",
)
parser.add_argument(
"--path-variable",
help="Example: if only project-specific '${KIPRJMOD}' standard is '${KICAD_3RD_PARTY}'",
)
args = parser.parse_args()
lib_folder = pathlib.Path(args.lib_folder)
if args.path_variable:
path_variable = str(args.path_variable).strip()
else:
path_variable = "${KICAD_3RD_PARTY}"
if args.download_file:
main(
lib_file=args.download_file,
lib_folder=args.lib_folder,
overwrite=args.overwrite_if_exists,
KICAD_3RD_PARTY_LINK=path_variable,
)
elif args.download_folder:
download_folder = pathlib.Path(args.download_folder)
if not download_folder.is_dir():
print(f"Error Source folder {download_folder} does not exist!")
elif not lib_folder.is_dir():
print(f"Error destination folder {lib_folder} does not exist!")
else:
for zip_file in download_folder.glob("*.zip"):
if (
zip_file.is_file() and zip_file.stat().st_size >= 1024
): # Check if it's a file and at least 1 KB
main(
lib_file=zip_file,
lib_folder=args.lib_folder,
overwrite=args.overwrite_if_exists,
KICAD_3RD_PARTY_LINK=path_variable,
)
elif args.easyeda:
if not lib_folder.is_dir():
print(f"Error destination folder {lib_folder} does not exist!")
else:
component_id = str(args.easyeda).strip
print("Try to import EeasyEDA / LCSC Part# : ", component_id)
from impart_easyeda import easyeda2kicad_wrapper
easyeda_import = easyeda2kicad_wrapper()
easyeda_import.full_import(
component_id="C2040",
base_folder=lib_folder,
overwrite=False,
lib_var=path_variable,
)