JLC2KiCadLib 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help me improve
4
+ title: ''
5
+ labels: ''
6
+
7
+ ---
8
+
9
+ **Describe the bug**
10
+ A clear and concise description of what the bug is.
11
+
12
+ **To Reproduce**
13
+ Steps to reproduce the behavior:
14
+ 1. LCSC part # that caused the issue
15
+ 2. Arguments used for the execution
16
+
17
+ **Expected behavior**
18
+ A clear and concise description of what you expected to happen.
19
+
20
+ **Screenshots**
21
+ If applicable, add screenshots to help explain your problem.
22
+
23
+ **Additional context**
24
+ Add any other context about the problem here.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+
7
+ ---
8
+
9
+ **Is your feature request related to a problem? Please describe.**
10
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11
+
12
+ **Describe the solution you'd like**
13
+ A clear and concise description of what you want to happen.
14
+
15
+ **Describe alternatives you've considered**
16
+ A clear and concise description of any alternative solutions or features you've considered.
17
+
18
+ **Additional context**
19
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,20 @@
1
+ .vscode/
2
+ KiCad
3
+ test.py
4
+ __pycache__
5
+ .gitignore
6
+ JLCPCB SMT Parts Library(*).csv
7
+ TestJLC2KiCad.py
8
+ *.log
9
+ *test*
10
+ failedComponents
11
+ build
12
+ JLCPCBsources
13
+ JLC2KiCadLib.egg-info
14
+ JLC2KiCad_lib/
15
+ dist/
16
+ TEST/
17
+ .venv
18
+ gen_lib.py
19
+ kicad-library-utils
20
+
@@ -0,0 +1,216 @@
1
+ import argparse
2
+ import json
3
+ import logging
4
+ from importlib.metadata import version as pkg_version
5
+
6
+ import requests
7
+
8
+ from . import helper
9
+
10
+ __version__ = pkg_version("JLC2KiCadLib")
11
+
12
+ from .footprint.footprint import create_footprint, get_footprint_info
13
+ from .symbol.symbol import create_symbol
14
+
15
+
16
+ def add_component(component_id, args):
17
+ logging.info(f"creating library for component {component_id}")
18
+ data = json.loads(
19
+ requests.get(
20
+ f"https://easyeda.com/api/products/{component_id}/svgs"
21
+ ).content.decode()
22
+ )
23
+
24
+ if not data["success"]:
25
+ logging.error(
26
+ f"failed to get component uuid for {component_id}\n"
27
+ "The component # is probably wrong. Check a possible typo and that the "
28
+ "component exists on easyEDA"
29
+ )
30
+ return ()
31
+
32
+ footprint_component_uuid = data["result"][-1]["component_uuid"]
33
+ symbol_component_uuid = [i["component_uuid"] for i in data["result"][:-1]]
34
+
35
+ if args.footprint_creation:
36
+ footprint_name, datasheet_link = create_footprint(
37
+ footprint_component_uuid=footprint_component_uuid,
38
+ component_id=component_id,
39
+ footprint_lib=args.footprint_lib,
40
+ output_dir=args.output_dir,
41
+ model_base_variable=args.model_base_variable,
42
+ model_dir=args.model_dir,
43
+ skip_existing=args.skip_existing,
44
+ models=args.models,
45
+ )
46
+ else:
47
+ _, datasheet_link, _, _ = get_footprint_info(footprint_component_uuid)
48
+ footprint_name = ""
49
+
50
+ if args.symbol_creation:
51
+ create_symbol(
52
+ symbol_component_uuid=symbol_component_uuid,
53
+ footprint_name=footprint_name.replace(
54
+ ".pretty", ""
55
+ ), # see https://github.com/TousstNicolas/JLC2KiCad_lib/issues/47
56
+ datasheet_link=datasheet_link,
57
+ library_name=args.symbol_lib,
58
+ symbol_path=args.symbol_lib_dir,
59
+ output_dir=args.output_dir,
60
+ component_id=component_id,
61
+ skip_existing=args.skip_existing,
62
+ )
63
+
64
+
65
+ def main():
66
+ parser = argparse.ArgumentParser(
67
+ description=(
68
+ "take a JLCPCB part # and create the according component'skicad's library"
69
+ ),
70
+ epilog=(
71
+ "example use : \n"
72
+ " JLC2KiCadLib C1337258 C24112 -dir My_lib "
73
+ "-symbol_lib My_Symbol_lib --no_footprint"
74
+ ),
75
+ formatter_class=argparse.RawDescriptionHelpFormatter,
76
+ )
77
+
78
+ parser.add_argument(
79
+ "components",
80
+ metavar="JLCPCB_part_#",
81
+ type=str,
82
+ nargs="+",
83
+ help="List of JLCPCB part # from the components you want to create",
84
+ )
85
+
86
+ parser.add_argument(
87
+ "-dir",
88
+ dest="output_dir",
89
+ type=str,
90
+ default="JLC2KiCad_lib",
91
+ help="Base directory for output library files",
92
+ )
93
+
94
+ parser.add_argument(
95
+ "--no_footprint",
96
+ dest="footprint_creation",
97
+ action="store_false",
98
+ help="Use --no_footprint if you do not want to create the footprint",
99
+ )
100
+
101
+ parser.add_argument(
102
+ "--no_symbol",
103
+ dest="symbol_creation",
104
+ action="store_false",
105
+ help="Use --no_symbol if you do not want to create the symbol",
106
+ )
107
+
108
+ parser.add_argument(
109
+ "-symbol_lib",
110
+ dest="symbol_lib",
111
+ type=str,
112
+ default=None,
113
+ help='Set symbol library name, default is "default_lib"',
114
+ )
115
+
116
+ parser.add_argument(
117
+ "-symbol_lib_dir",
118
+ dest="symbol_lib_dir",
119
+ type=str,
120
+ default="symbol",
121
+ help='Set symbol library path, default is "symbol" (relative to OUTPUT_DIR)',
122
+ )
123
+
124
+ parser.add_argument(
125
+ "-footprint_lib",
126
+ dest="footprint_lib",
127
+ type=str,
128
+ default="footprint",
129
+ help='Set footprint library name, default is "footprint"',
130
+ )
131
+
132
+ parser.add_argument(
133
+ "-models",
134
+ dest="models",
135
+ nargs="*",
136
+ choices=["STEP", "WRL"],
137
+ type=str,
138
+ default="STEP",
139
+ help=(
140
+ "Select the 3D model you want to use. Default is STEP. "
141
+ "If both are selected, only the STEP model will be added to the footprint "
142
+ "(the WRL model will still be generated alongside the STEP model). "
143
+ "If you do not want any model to be generated, use the --models "
144
+ "without arguments"
145
+ ),
146
+ )
147
+
148
+ parser.add_argument(
149
+ "-model_dir",
150
+ dest="model_dir",
151
+ type=str,
152
+ default="packages3d",
153
+ help=(
154
+ 'Set directory for storing 3d models, default is "packages3d" '
155
+ "(relative to FOOTPRINT_LIB)"
156
+ ),
157
+ )
158
+
159
+ parser.add_argument( # argument to skip already existing files and symbols
160
+ "--skip_existing",
161
+ dest="skip_existing",
162
+ action="store_true",
163
+ help=(
164
+ "Use --skip_existing if you want do not want to replace already existing "
165
+ "footprints and symbols"
166
+ ),
167
+ )
168
+
169
+ parser.add_argument(
170
+ "-model_base_variable",
171
+ dest="model_base_variable",
172
+ type=str,
173
+ default="",
174
+ help=(
175
+ "Use -model_base_variable if you want to specify the base path of the 3D "
176
+ "model using a path variable. If the specified variable starts with '$' it "
177
+ "is used 'as-is', otherwise it is encapsulated: $(MODEL_BASE_VARIABLE)"
178
+ ),
179
+ )
180
+
181
+ parser.add_argument(
182
+ "-logging_level",
183
+ dest="logging_level",
184
+ type=str,
185
+ default="INFO",
186
+ choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
187
+ help=(
188
+ "Set logging level. If DEBUG is used, the debug logs are only written in "
189
+ "the log file if the option --log_file is set "
190
+ ),
191
+ )
192
+
193
+ parser.add_argument(
194
+ "--log_file",
195
+ dest="log_file",
196
+ action="store_true",
197
+ help="Use --log_file if you want logs to be written in a file",
198
+ )
199
+
200
+ parser.add_argument(
201
+ "--version",
202
+ action="version",
203
+ version=f"%(prog)s {__version__}",
204
+ help="Print versin number and exit",
205
+ )
206
+
207
+ args = parser.parse_args()
208
+
209
+ helper.set_logging(args.logging_level, args.log_file)
210
+
211
+ for component in args.components:
212
+ add_component(component, args)
213
+
214
+
215
+ if __name__ == "__main__":
216
+ main()
File without changes
File without changes
@@ -0,0 +1,181 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ from dataclasses import dataclass
5
+
6
+ import requests
7
+ from KicadModTree import Footprint, KicadFileHandler, Pad, Text, Translation
8
+
9
+ from .footprint_handlers import handlers, mil2mm
10
+
11
+
12
+ @dataclass
13
+ class FootprintInfo:
14
+ max_X: float = -10000
15
+ max_Y: float = -10000
16
+ min_X: float = 10000
17
+ min_Y: float = 10000
18
+ footprint_name: str = ""
19
+ output_dir: str = ""
20
+ footprint_lib: str = ""
21
+ model_base_variable: str = ""
22
+ model_dir: str = ""
23
+ origin: tuple = (0, 0)
24
+ models: str = ""
25
+
26
+
27
+ def create_footprint(
28
+ footprint_component_uuid,
29
+ component_id,
30
+ footprint_lib,
31
+ output_dir,
32
+ model_base_variable,
33
+ model_dir,
34
+ skip_existing,
35
+ models,
36
+ ):
37
+ logging.info("Creating footprint ...")
38
+
39
+ (
40
+ footprint_name,
41
+ datasheet_link,
42
+ footprint_shape,
43
+ translation,
44
+ ) = get_footprint_info(footprint_component_uuid)
45
+
46
+ if skip_existing and os.path.isfile(
47
+ os.path.join(output_dir, footprint_lib, footprint_name + ".kicad_mod")
48
+ ):
49
+ logging.info(f"Footprint {footprint_name} already exists, skipping.")
50
+ return f"{footprint_lib}:{footprint_name}", datasheet_link
51
+
52
+ # init kicad footprint
53
+ kicad_mod = Footprint(f'"{footprint_name}"')
54
+ kicad_mod.setDescription(f"{footprint_name} footprint") # TODO Set real description
55
+ kicad_mod.setTags(f"{footprint_name} footprint {component_id}")
56
+
57
+ footprint_info = FootprintInfo(
58
+ footprint_name=footprint_name,
59
+ output_dir=output_dir,
60
+ footprint_lib=footprint_lib,
61
+ model_base_variable=model_base_variable,
62
+ model_dir=model_dir,
63
+ origin=translation,
64
+ models=models,
65
+ )
66
+
67
+ # for each line in data : use the appropriate handler
68
+ for line in footprint_shape:
69
+ args = [
70
+ i for i in line.split("~") if i
71
+ ] # split and remove empty string in list
72
+ model = args[0]
73
+ logging.debug(args)
74
+ if model not in handlers:
75
+ logging.warning(f"footprint : model not in handler : {model}")
76
+ else:
77
+ handlers.get(model)(args[1:], kicad_mod, footprint_info)
78
+
79
+ if any(
80
+ isinstance(child, Pad) and child.type == Pad.TYPE_THT
81
+ for child in kicad_mod.getAllChilds()
82
+ ):
83
+ kicad_mod.setAttribute("through_hole")
84
+ else:
85
+ kicad_mod.setAttribute("smd")
86
+
87
+ kicad_mod.insert(Translation(-mil2mm(translation[0]), -mil2mm(translation[1])))
88
+
89
+ # Translate the footprint max and min values to the origin
90
+ footprint_info.max_X -= mil2mm(translation[0])
91
+ footprint_info.max_Y -= mil2mm(translation[1])
92
+ footprint_info.min_X -= mil2mm(translation[0])
93
+ footprint_info.min_Y -= mil2mm(translation[1])
94
+
95
+ # set general values
96
+ kicad_mod.append(
97
+ Text(
98
+ type="reference",
99
+ text="REF**",
100
+ at=[
101
+ (footprint_info.min_X + footprint_info.max_X) / 2,
102
+ footprint_info.min_Y - 2,
103
+ ],
104
+ layer="F.SilkS",
105
+ )
106
+ )
107
+ kicad_mod.append(
108
+ Text(
109
+ type="user",
110
+ text="${REFERENCE}",
111
+ at=[
112
+ (footprint_info.min_X + footprint_info.max_X) / 2,
113
+ (footprint_info.min_Y + footprint_info.max_Y) / 2,
114
+ ],
115
+ layer="F.Fab",
116
+ )
117
+ )
118
+ kicad_mod.append(
119
+ Text(
120
+ type="value",
121
+ text=footprint_name,
122
+ at=[
123
+ (footprint_info.min_X + footprint_info.max_X) / 2,
124
+ footprint_info.max_Y + 2,
125
+ ],
126
+ layer="F.Fab",
127
+ )
128
+ )
129
+
130
+ if not os.path.exists(f"{output_dir}/{footprint_lib}"):
131
+ os.makedirs(f"{output_dir}/{footprint_lib}")
132
+
133
+ # output kicad model
134
+ file_handler = KicadFileHandler(kicad_mod)
135
+ file_handler.writeFile(f"{output_dir}/{footprint_lib}/{footprint_name}.kicad_mod")
136
+ logging.info(f"Created '{output_dir}/{footprint_lib}/{footprint_name}.kicad_mod'")
137
+
138
+ # return the datasheet link and footprint name to be linked with the symbol
139
+ return (f"{footprint_lib}:{footprint_name}", datasheet_link)
140
+
141
+
142
+ def get_footprint_info(footprint_component_uuid):
143
+ # fetch the component data from easyeda library
144
+ response = requests.get(
145
+ f"https://easyeda.com/api/components/{footprint_component_uuid}"
146
+ )
147
+
148
+ if response.status_code == requests.codes.ok:
149
+ data = json.loads(response.content.decode())
150
+ else:
151
+ logging.error(
152
+ "create_footprint error. Requests returned with error code "
153
+ f"{response.status_code}"
154
+ )
155
+ return ("", None, "", (0, 0))
156
+
157
+ footprint_shape = data["result"]["dataStr"]["shape"]
158
+ x = data["result"]["dataStr"]["head"]["x"]
159
+ y = data["result"]["dataStr"]["head"]["y"]
160
+ try:
161
+ datasheet_link = data["result"]["dataStr"]["head"]["c_para"]["link"]
162
+ except KeyError:
163
+ datasheet_link = ""
164
+ logging.warning("Could not retrieve datasheet link from EASYEDA")
165
+
166
+ footprint_name = (
167
+ data["result"]["title"]
168
+ .replace(" ", "_")
169
+ .replace("/", "_")
170
+ .replace("(", "_")
171
+ .replace(")", "_")
172
+ )
173
+
174
+ if not footprint_name:
175
+ footprint_name = "NoName"
176
+ logging.warning(
177
+ "Could not retrieve components information from EASYEDA, default name "
178
+ "'NoName'."
179
+ )
180
+
181
+ return (footprint_name, datasheet_link, footprint_shape, (x, y))