Coverage for JLC2KiCadLib / symbol / symbol.py: 95%
76 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-04 10:00 +0100
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-04 10:00 +0100
1import json
2import logging
3import os
4import re
6import requests
8from .symbol_handlers import handlers
10template_lib_header = """\
11(kicad_symbol_lib (version 20210201) (generator TousstNicolas/JLC2KiCad_lib)
12"""
14template_lib_footer = ")\n"
16supported_value_types = [
17 "Resistance",
18 "Capacitance",
19 "Inductance",
20 "Frequency",
21] # define which attribute/value from JLCPCB/LCSC will be added in the "value" field
24def create_symbol(
25 symbol_component_uuid,
26 footprint_name,
27 datasheet_link,
28 library_name,
29 symbol_path,
30 output_dir,
31 component_id,
32 skip_existing,
33):
34 class kicad_symbol:
35 drawing = ""
36 pinNamesHide = "(pin_names hide)"
37 pinNumbersHide = "(pin_numbers hide)"
39 kicad_symbol = kicad_symbol()
41 ComponentName = ""
42 for component_uuid in symbol_component_uuid:
43 response = requests.get(f"https://easyeda.com/api/components/{component_uuid}")
44 if response.status_code == requests.codes.ok:
45 data = json.loads(response.content.decode())
46 else:
47 logging.error(
48 f"create_symbol error. Requests returned with error code "
49 f"{response.status_code}"
50 )
51 return ()
53 symbol_shape = data["result"]["dataStr"]["shape"]
54 symmbol_prefix = data["result"]["packageDetail"]["dataStr"]["head"]["c_para"][
55 "pre"
56 ].replace("?", "")
57 component_title = (
58 data["result"]["title"]
59 .replace(" ", "_")
60 .replace(".", "_")
61 .replace("/", "{slash}")
62 .replace("\\", "{backslash}")
63 .replace("<", "{lt}")
64 .replace(">", "{gt}")
65 .replace(":", "{colon}")
66 .replace('"', "{dblquote}")
67 )
69 component_types_values = []
70 for value_type in supported_value_types:
71 if value_type in data["result"]["dataStr"]["head"]["c_para"]:
72 component_types_values.append(
73 (
74 value_type,
75 data["result"]["dataStr"]["head"]["c_para"][value_type],
76 )
77 )
79 if not ComponentName:
80 ComponentName = component_title
81 component_title += "_0"
82 if (
83 len(symbol_component_uuid) >= 2
84 and component_uuid == symbol_component_uuid[0]
85 ):
86 continue
88 # if library_name is not defined, use component_title as library name
89 if not library_name:
90 library_name = ComponentName
92 filename = f"{output_dir}/{symbol_path}/{library_name}.kicad_sym"
94 logging.info(f"Creating symbol {component_title} in {library_name}")
96 kicad_symbol.drawing += f'''\n (symbol "{component_title}_1"'''
98 for line in symbol_shape:
99 args = [i for i in line.split("~")] # split arguments
100 model = args[0]
101 logging.debug(args)
102 if model not in handlers:
103 logging.warning("symbol : parsing model not in handler : " + model)
104 else:
105 handlers.get(model)(
106 data=args[1:],
107 translation=(
108 data["result"]["dataStr"]["head"]["x"],
109 data["result"]["dataStr"]["head"]["y"],
110 ),
111 kicad_symbol=kicad_symbol,
112 )
113 kicad_symbol.drawing += """\n )"""
115 # ruff: disable [E501]
116 template_lib_component = f"""\
117 (symbol "{ComponentName}" {kicad_symbol.pinNamesHide} {kicad_symbol.pinNumbersHide} (in_bom yes) (on_board yes)
118 (property "Reference" "{symmbol_prefix}" (id 0) (at 0 1.27 0)
119 (effects (font (size 1.27 1.27)))
120 )
121 (property "Value" "{ComponentName}" (id 1) (at 0 -2.54 0)
122 (effects (font (size 1.27 1.27)))
123 )
124 (property "Footprint" "{footprint_name}" (id 2) (at 0 -10.16 0)
125 (effects (font (size 1.27 1.27) italic) hide)
126 )
127 (property "Datasheet" "{datasheet_link}" (id 3) (at -2.286 0.127 0)
128 (effects (font (size 1.27 1.27)) (justify left) hide)
129 )
130 (property "ki_keywords" "{component_id}" (id 4) (at 0 0 0)
131 (effects (font (size 1.27 1.27)) hide)
132 )
133 (property "LCSC" "{component_id}" (id 5) (at 0 0 0)
134 (effects (font (size 1.27 1.27)) hide)
135 )
136 {get_type_values_properties(6, component_types_values)}{kicad_symbol.drawing}
137 )
138"""
139 # ruff: enable [E501]
141 if not os.path.exists(f"{output_dir}/{symbol_path}"):
142 os.makedirs(f"{output_dir}/{symbol_path}")
144 if os.path.exists(filename):
145 update_library(
146 library_name,
147 symbol_path,
148 ComponentName,
149 template_lib_component,
150 output_dir,
151 skip_existing,
152 )
153 else:
154 with open(filename, "w") as f:
155 logging.info(f"writing in {filename} file")
156 f.write(template_lib_header)
157 f.write(template_lib_footer)
158 update_library(
159 library_name,
160 symbol_path,
161 ComponentName,
162 template_lib_component,
163 output_dir,
164 skip_existing,
165 )
168def get_type_values_properties(start_index, component_types_values):
169 # ruff: disable [E501]
170 return "\n".join(
171 [
172 f"""(property "{type_value[0]}" "{type_value[1]}" (id {start_index + index}) (at 0 0 0)
173 (effects (font (size 1.27 1.27)) hide)
174 )"""
175 for index, type_value in enumerate(component_types_values)
176 ]
177 )
178 # ruff: enable [E501]
181def update_library(
182 library_name,
183 symbol_path,
184 component_title,
185 template_lib_component,
186 output_dir,
187 skip_existing,
188):
189 """
190 if component is already in library,
191 the library will be updated,
192 if not already present in library,
193 the component will be added at the end
194 """
196 with open(
197 f"{output_dir}/{symbol_path}/{library_name}.kicad_sym", "rb+"
198 ) as lib_file:
199 pattern = rf' \(symbol "{component_title}" (\n|.)*?\n \)'
200 file_content = lib_file.read().decode()
202 if f'symbol "{component_title}"' in file_content:
203 if skip_existing:
204 logging.info(
205 f"component {component_title} already in symbols library, skipping"
206 )
207 return
208 # use regex to find the old component template in the file and
209 # replace it with the new one
210 logging.info(
211 f"found component already in {library_name}, updating {library_name}"
212 )
213 sub = re.sub(
214 pattern=pattern,
215 repl=template_lib_component,
216 string=file_content,
217 flags=re.DOTALL,
218 count=1,
219 )
220 lib_file.seek(0)
221 # delete the file content and rewrite it
222 lib_file.truncate()
223 lib_file.write(sub.encode())
224 else:
225 # move before the library footer and write the component template
226 # see https://github.com/TousstNicolas/JLC2KiCad_lib/issues/46
227 new_content = file_content[: file_content.rfind(")")]
228 new_content = new_content + template_lib_component + template_lib_footer
229 lib_file.seek(0)
230 lib_file.write(new_content.encode())