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

1import json 

2import logging 

3import os 

4import re 

5 

6import requests 

7 

8from .symbol_handlers import handlers 

9 

10template_lib_header = """\ 

11(kicad_symbol_lib (version 20210201) (generator TousstNicolas/JLC2KiCad_lib) 

12""" 

13 

14template_lib_footer = ")\n" 

15 

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 

22 

23 

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)" 

38 

39 kicad_symbol = kicad_symbol() 

40 

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 () 

52 

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 ) 

68 

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 ) 

78 

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 

87 

88 # if library_name is not defined, use component_title as library name 

89 if not library_name: 

90 library_name = ComponentName 

91 

92 filename = f"{output_dir}/{symbol_path}/{library_name}.kicad_sym" 

93 

94 logging.info(f"Creating symbol {component_title} in {library_name}") 

95 

96 kicad_symbol.drawing += f'''\n (symbol "{component_title}_1"''' 

97 

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 )""" 

114 

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] 

140 

141 if not os.path.exists(f"{output_dir}/{symbol_path}"): 

142 os.makedirs(f"{output_dir}/{symbol_path}") 

143 

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 ) 

166 

167 

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] 

179 

180 

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 """ 

195 

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() 

201 

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())