tigertag 1.0.0
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.
- package/CHANGELOG.md +35 -0
- package/LICENSE.md +21 -0
- package/README.md +653 -0
- package/bin/tigertag.js +132 -0
- package/database/id_aspect.json +1 -0
- package/database/id_brand.json +1 -0
- package/database/id_diameter.json +1 -0
- package/database/id_material.json +1 -0
- package/database/id_measure_unit.json +1 -0
- package/database/id_type.json +1 -0
- package/database/id_version.json +1 -0
- package/database/last_update.json +1 -0
- package/package.json +54 -0
- package/src/db.js +292 -0
- package/src/index.js +62 -0
- package/src/signature.js +130 -0
- package/src/tag.js +1245 -0
package/bin/tigertag.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// TigerTag RFID Guide
|
|
5
|
+
// Copyright (C) 2025 TigerTag
|
|
6
|
+
//
|
|
7
|
+
// This program is free software: you can redistribute it and/or modify
|
|
8
|
+
// it under the terms of the GNU General Public License as published by
|
|
9
|
+
// the Free Software Foundation, either version 3 of the License.
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const { TigerTag, TigerTagDB, SignatureResult, syncDatabases } = require('../src/index');
|
|
15
|
+
const { _BUNDLED_DB_PATH } = require('../src/db');
|
|
16
|
+
|
|
17
|
+
const VERSION = require('../package.json').version;
|
|
18
|
+
|
|
19
|
+
const HELP = `
|
|
20
|
+
Usage: tigertag [dump.bin] [options]
|
|
21
|
+
|
|
22
|
+
Parse, verify, and export TigerTag RFID chip dumps.
|
|
23
|
+
|
|
24
|
+
Arguments:
|
|
25
|
+
dump.bin Binary .bin file to parse
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
--db <path> Database folder (default: bundled database)
|
|
29
|
+
--json Output as JSON
|
|
30
|
+
--raw Raw protocol fields, no DB lookup
|
|
31
|
+
--no-sync Do not auto-download databases
|
|
32
|
+
--sync-only Update databases and exit
|
|
33
|
+
--version Show version
|
|
34
|
+
-h, --help Show this help
|
|
35
|
+
|
|
36
|
+
Dump formats:
|
|
37
|
+
180 bytes Full chip dump (pages 0-44): UID auto-extracted, signature verifiable
|
|
38
|
+
144 bytes User data + signature (pages 0x04-0x27)
|
|
39
|
+
80 bytes User data only (pages 0x04-0x17)
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
tigertag dump.bin Parse + human-readable output
|
|
43
|
+
tigertag dump.bin --json Output as JSON
|
|
44
|
+
tigertag dump.bin --raw Raw protocol fields, no DB lookup
|
|
45
|
+
tigertag --sync-only Update reference databases and exit
|
|
46
|
+
|
|
47
|
+
Spec: https://github.com/TigerTag-Project/TigerTag-RFID-Guide
|
|
48
|
+
`.trim();
|
|
49
|
+
|
|
50
|
+
function parseArgs(argv) {
|
|
51
|
+
const args = { dump: null, db: null, json: false, raw: false, noSync: false, syncOnly: false };
|
|
52
|
+
for (let i = 0; i < argv.length; i++) {
|
|
53
|
+
const a = argv[i];
|
|
54
|
+
if (a === '--json') args.json = true;
|
|
55
|
+
else if (a === '--raw') args.raw = true;
|
|
56
|
+
else if (a === '--no-sync') args.noSync = true;
|
|
57
|
+
else if (a === '--sync-only') args.syncOnly = true;
|
|
58
|
+
else if (a === '--version') { console.log(`tigertag ${VERSION}`); process.exit(0); }
|
|
59
|
+
else if (a === '-h' || a === '--help') { console.log(HELP); process.exit(0); }
|
|
60
|
+
else if (a === '--db') args.db = argv[++i];
|
|
61
|
+
else if (!a.startsWith('-')) args.dump = a;
|
|
62
|
+
}
|
|
63
|
+
return args;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function main() {
|
|
67
|
+
const args = parseArgs(process.argv.slice(2));
|
|
68
|
+
const dbPath = args.db ? path.resolve(args.db) : _BUNDLED_DB_PATH;
|
|
69
|
+
|
|
70
|
+
if (args.syncOnly) {
|
|
71
|
+
try {
|
|
72
|
+
const updated = await syncDatabases(dbPath, { verbose: true });
|
|
73
|
+
if (updated.length > 0) {
|
|
74
|
+
console.log(`\nUpdated ${updated.length} file(s): ${updated.join(', ')}`);
|
|
75
|
+
} else {
|
|
76
|
+
console.log('\nAll databases already up to date.');
|
|
77
|
+
}
|
|
78
|
+
} catch (err) {
|
|
79
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!args.dump) {
|
|
86
|
+
console.log(HELP);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let raw;
|
|
91
|
+
try {
|
|
92
|
+
raw = fs.readFileSync(args.dump);
|
|
93
|
+
} catch (_) {
|
|
94
|
+
process.stderr.write(`Error: file not found: ${args.dump}\n`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let tag;
|
|
99
|
+
try {
|
|
100
|
+
tag = TigerTag.fromDump(raw);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (const w of tag.validate()) {
|
|
107
|
+
process.stderr.write(`Warning: ${w}\n`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (args.raw) {
|
|
111
|
+
console.log(JSON.stringify(tag.toRawDict(), null, 2));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const db = new TigerTagDB({ dbPath });
|
|
116
|
+
const sigResult = tag.isSigned
|
|
117
|
+
? tag.verify(db)
|
|
118
|
+
: new SignatureResult(SignatureResult.UNSIGNED);
|
|
119
|
+
|
|
120
|
+
if (args.json) {
|
|
121
|
+
const d = tag.toDict(db);
|
|
122
|
+
d.signature = sigResult.toDict();
|
|
123
|
+
console.log(JSON.stringify(d, null, 2, (_, v) => typeof v === 'bigint' ? v.toString() : v));
|
|
124
|
+
} else {
|
|
125
|
+
console.log(tag.pretty(db, sigResult));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
main().catch((err) => {
|
|
130
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"id":0,"label":"-","color_count":0},{"id":21,"label":"Clear","color_count":1},{"id":24,"label":"Tricolor","color_count":3},{"id":64,"label":"Glitter","color_count":1},{"id":67,"label":"Translucent","color_count":1},{"id":91,"label":"Glow in the Dark","color_count":1},{"id":92,"label":"Silk","color_count":1},{"id":97,"label":"Lithophane","color_count":1},{"id":104,"label":"Basic","color_count":1},{"id":123,"label":"Wood","color_count":1},{"id":126,"label":"Pearl","color_count":1},{"id":129,"label":"Gloss","color_count":1},{"id":134,"label":"Satin","color_count":1},{"id":145,"label":"Rainbow","color_count":3},{"id":168,"label":"Thermoreactif","color_count":1},{"id":173,"label":"Stone","color_count":1},{"id":216,"label":"Neon","color_count":1},{"id":220,"label":"Pastel","color_count":1},{"id":232,"label":"Marble","color_count":1},{"id":238,"label":"Carbon","color_count":1},{"id":247,"label":"Matt","color_count":1},{"id":252,"label":"Bicolor","color_count":2},{"id":255,"label":"None","color_count":0}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"id":1,"name":"Atome3D","type_ids":[142,173]},{"id":1068,"name":"SainSmart","type_ids":[142]},{"id":1120,"name":"Proto-Pasta","type_ids":[142]},{"id":1421,"name":"3DJake","type_ids":[142]},{"id":2517,"name":"Smart Materials 3D","type_ids":[142]},{"id":2833,"name":"Xstrand","type_ids":[142]},{"id":3132,"name":"Hatchbox","type_ids":[142]},{"id":3924,"name":"FiloAlfa","type_ids":[142]},{"id":4011,"name":"QIDI Tech","type_ids":[142]},{"id":4048,"name":"Owa","type_ids":[142]},{"id":4344,"name":"MatterHackers","type_ids":[142]},{"id":4356,"name":"Landu","type_ids":[142]},{"id":4565,"name":"Valment","type_ids":[142]},{"id":4700,"name":"Filforme","type_ids":[142]},{"id":6305,"name":"Markforged","type_ids":[142]},{"id":7432,"name":"Aliz","type_ids":[142]},{"id":7674,"name":"Extrudr","type_ids":[142]},{"id":7812,"name":"Jayo","type_ids":[142]},{"id":7980,"name":"Fillamentum","type_ids":[142]},{"id":8182,"name":"Fiberlogy","type_ids":[142]},{"id":8303,"name":"GST3D","type_ids":[142]},{"id":8384,"name":"Taulman3D","type_ids":[142]},{"id":8496,"name":"CaiLab","type_ids":[142]},{"id":8586,"name":"NinjaTek","type_ids":[142]},{"id":8675,"name":"SOVB 3D","type_ids":[142]},{"id":8756,"name":"BlueCast","type_ids":[173]},{"id":8921,"name":"Duramic 3D","type_ids":[142]},{"id":8990,"name":"Ice Filaments","type_ids":[142]},{"id":9192,"name":"3D Solutech","type_ids":[142]},{"id":9299,"name":"FDPlast","type_ids":[142]},{"id":9394,"name":"Gizmo Dorks","type_ids":[142]},{"id":9596,"name":"Ziro","type_ids":[142]},{"id":9798,"name":"AMOLEN","type_ids":[142]},{"id":11379,"name":"Filaments.ca","type_ids":[142]},{"id":11429,"name":"3D4Makers","type_ids":[142]},{"id":11501,"name":"InnovateFil","type_ids":[142]},{"id":12345,"name":"MakerBot","type_ids":[142]},{"id":12498,"name":"Forshape","type_ids":[142]},{"id":12635,"name":"Snapmaker","type_ids":[142]},{"id":13667,"name":"RS Pro","type_ids":[142]},{"id":14250,"name":"Jessie (Printed Solid)","type_ids":[142]},{"id":14982,"name":"3D-Fuel","type_ids":[142]},{"id":15899,"name":"Kimya","type_ids":[142]},{"id":15962,"name":"Anycubic","type_ids":[142,173]},{"id":18629,"name":"PrintoMax 3D","type_ids":[142]},{"id":19265,"name":"CC3D","type_ids":[142]},{"id":19961,"name":"Rosa3D","type_ids":[142]},{"id":20523,"name":"Raise3D","type_ids":[142,173]},{"id":20851,"name":"Tronxy","type_ids":[142]},{"id":22652,"name":"Spectrum","type_ids":[142]},{"id":23181,"name":"ArianePlast","type_ids":[142]},{"id":23456,"name":"Monoprice","type_ids":[142]},{"id":24363,"name":"Tecbears","type_ids":[142]},{"id":26379,"name":"Formlabs","type_ids":[173]},{"id":26595,"name":"Sovol","type_ids":[142]},{"id":26956,"name":"Creality","type_ids":[142,173]},{"id":28055,"name":"TAGin3D","type_ids":[142]},{"id":28136,"name":"Polar Filament","type_ids":[142]},{"id":28940,"name":"Eryone","type_ids":[142]},{"id":28988,"name":"KINGROON","type_ids":[142]},{"id":29045,"name":"Yousu","type_ids":[142]},{"id":29302,"name":"IIIDMAX","type_ids":[142]},{"id":31438,"name":"Wondermaker","type_ids":[142]},{"id":32348,"name":"Addnorth","type_ids":[142]},{"id":32587,"name":"Amazon","type_ids":[142]},{"id":33566,"name":"Siraya Tech","type_ids":[142,173]},{"id":33594,"name":"DEEPLEE","type_ids":[142]},{"id":33788,"name":"Verbatim","type_ids":[142]},{"id":34567,"name":"Push Plastic","type_ids":[142]},{"id":34597,"name":"EconoFil","type_ids":[142]},{"id":35123,"name":"Bambu Lab","type_ids":[142]},{"id":35501,"name":"Zortrax","type_ids":[142]},{"id":35857,"name":"Nobufil","type_ids":[142]},{"id":35882,"name":"Phrozen","type_ids":[142,173]},{"id":36702,"name":"Tianse","type_ids":[142]},{"id":37434,"name":"Winkle","type_ids":[142]},{"id":38533,"name":"Lotactree","type_ids":[142]},{"id":39002,"name":"GreenGate3D","type_ids":[142]},{"id":39382,"name":"Longer","type_ids":[142]},{"id":39652,"name":"3DXTech","type_ids":[142]},{"id":41847,"name":"OneFil","type_ids":[142]},{"id":41932,"name":"Jamg He","type_ids":[173,142]},{"id":42911,"name":"UltiMaker","type_ids":[142]},{"id":44630,"name":"NIT","type_ids":[142]},{"id":45670,"name":"Panchroma","type_ids":[142]},{"id":45678,"name":"Atomic Filament","type_ids":[142]},{"id":46010,"name":"AceAddity","type_ids":[142,173]},{"id":46203,"name":"Overture","type_ids":[142]},{"id":46392,"name":"Prusament","type_ids":[142,173]},{"id":47560,"name":"Wanhao","type_ids":[142]},{"id":47930,"name":"eSun","type_ids":[142,173]},{"id":48261,"name":"Gsun3D","type_ids":[142]},{"id":48804,"name":"R3D","type_ids":[142]},{"id":49784,"name":"GIANTARN","type_ids":[142]},{"id":50311,"name":"G3D Pro","type_ids":[142,173]},{"id":50604,"name":"Polymaker","type_ids":[142]},{"id":51139,"name":"FusRock","type_ids":[142]},{"id":51443,"name":"BASF","type_ids":[142,173]},{"id":51857,"name":"Sunlu","type_ids":[142,173]},{"id":52222,"name":"ColorFabb","type_ids":[142]},{"id":52467,"name":"Geeetech","type_ids":[142]},{"id":52757,"name":"Yumi","type_ids":[142]},{"id":53043,"name":"FormFutura","type_ids":[142,173]},{"id":53640,"name":"Magigoo","type_ids":[142]},{"id":53856,"name":"Lattice Medical","type_ids":[142]},{"id":54112,"name":"Kexcelled","type_ids":[142]},{"id":55229,"name":"Filament PM","type_ids":[142]},{"id":55763,"name":"Nanovia","type_ids":[142]},{"id":55869,"name":"Biqu","type_ids":[142]},{"id":56780,"name":"Fiberon","type_ids":[142]},{"id":56789,"name":"Coex 3D","type_ids":[142]},{"id":57209,"name":"FrancoFil","type_ids":[142]},{"id":57632,"name":"ELEGOO","type_ids":[142,173]},{"id":58231,"name":"IC3D","type_ids":[142]},{"id":58410,"name":"AzureFilm","type_ids":[142]},{"id":58972,"name":"Tinmorry","type_ids":[142]},{"id":59597,"name":"Filamentive","type_ids":[142]},{"id":60882,"name":"Recreus","type_ids":[142]},{"id":62436,"name":"Ambrosia","type_ids":[142]},{"id":63024,"name":"Multicomp Pro","type_ids":[142]},{"id":63340,"name":"Flashforge","type_ids":[142]},{"id":65535,"name":"Generic","type_ids":[142,173]}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"id":56,"label":"1.75"},{"id":221,"label":"2.85"}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"id":18775,"label":"PE-CF","material_type":"PE","filled_type":"CF","density":1.1,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":175,"nozzleTempMax":220,"dryTemp":80,"dryTime":12,"bedTempMin":55,"bedTempMax":60},"metadata":{"bambuID":"GFP98"}},{"id":34944,"label":"PETG-GF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":230,"nozzleTempMax":270,"dryTemp":70,"dryTime":8,"bedTempMin":70,"bedTempMax":90},"metadata":{"bambuID":"GFG50","crealityID":"00014","crealityPressureAdvance":0.03}},{"id":9691,"label":"EVA","material_type":"EVA","filled_type":null,"density":0.94,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":175,"nozzleTempMax":220,"dryTemp":50,"dryTime":4,"bedTempMin":55,"bedTempMax":60},"metadata":{"bambuID":"GFR99"}},{"id":49804,"label":"ASA-AF","material_type":"ASA","filled_type":"AF","density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":250,"nozzleTempMax":280,"dryTemp":80,"dryTime":8,"bedTempMin":90,"bedTempMax":95},"metadata":{"bambuID":"GFB51"}},{"id":7951,"label":"PETG-rCF","material_type":"PETG","filled_type":"CF","density":1.27,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":270,"dryTemp":70,"dryTime":8,"bedTempMin":65,"bedTempMax":70},"metadata":{"bambuID":"GFG98","crealityID":"00014","crealityPressureAdvance":0.03}},{"id":11053,"label":"PET-CF","material_type":"PET","filled_type":"CF","density":1.3,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":290,"dryTemp":85,"dryTime":12,"bedTempMin":90,"bedTempMax":95},"metadata":{"bambuID":"GFG99","crealityID":"00013","crealityPressureAdvance":0.03}},{"id":53890,"label":"PCTG-CF","material_type":"PCTG","filled_type":"CF","density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":270,"dryTemp":65,"dryTime":8,"bedTempMin":70,"bedTempMax":75},"metadata":{"crealityID":"00014","crealityPressureAdvance":0.03,"bambuID":"GFG97"}},{"id":51636,"label":"PES","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":340,"nozzleTempMax":380,"dryTemp":120,"dryTime":6,"bedTempMin":120,"bedTempMax":140},"metadata":{"crealityID":"00001","crealityPressureAdvance":0.04,"bambuID":"GFA00"}},{"id":18130,"label":"PS","material_type":"PS","filled_type":null,"density":1.05,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":260,"dryTemp":75,"dryTime":2,"bedTempMin":60,"bedTempMax":100},"metadata":{}},{"id":9456,"label":"PLA Marble","material_type":"PLA","filled_type":null,"density":1.24,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":260,"dryTemp":50,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"bambuID":"GFA07"}},{"id":27676,"label":"ASA-CF","material_type":"ASA","filled_type":"CF","density":1.05,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":250,"nozzleTempMax":280,"dryTemp":80,"dryTime":8,"bedTempMin":90,"bedTempMax":95},"metadata":{"bambuID":"GFB51","crealityID":"00007","crealityPressureAdvance":0.06}},{"id":30594,"label":"PA-GF","material_type":"PA","filled_type":"GF","density":1.52,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":300,"dryTemp":85,"dryTime":12,"bedTempMin":40,"bedTempMax":45},"metadata":{}},{"id":58142,"label":"TPU-GF","material_type":"TPU","filled_type":"GF","density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":200,"nozzleTempMax":250,"dryTemp":75,"dryTime":8,"bedTempMin":35,"bedTempMax":40},"metadata":{"crealityID":"00005","crealityPressureAdvance":0.04,"bambuID":"GFU99"}},{"id":24270,"label":"PPS-CF","material_type":"PPS","filled_type":"CF","density":1.38,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":310,"nozzleTempMax":340,"dryTemp":130,"dryTime":12,"bedTempMin":110,"bedTempMax":115},"metadata":{"crealityID":"00018","bambuID":"GFT98"}},{"id":48001,"label":"PLA Wood","material_type":"PLA","filled_type":"Wood","density":1.28,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":60,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"crealityID":"17001","crealityPressureAdvance":0.04,"bambuID":"GFA16"}},{"id":56527,"label":"PEI-1010","material_type":"PEI-1010","filled_type":null,"density":1.27,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":370,"nozzleTempMax":420,"dryTemp":130,"dryTime":12,"bedTempMin":150,"bedTempMax":160},"metadata":{}},{"id":24116,"label":"TPC","material_type":"TPC","filled_type":null,"density":1.15,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":245,"dryTemp":75,"dryTime":8,"bedTempMin":60,"bedTempMax":90},"metadata":{}},{"id":57469,"label":"PETG HF","material_type":"PETG","filled_type":null,"density":1.27,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":270,"dryTemp":70,"dryTime":8,"bedTempMin":70,"bedTempMax":75},"metadata":{"bambuID":"GFG96","crealityID":"06002","crealityPressureAdvance":0.05}},{"id":46591,"label":"PLA+","material_type":"PLA","filled_type":null,"density":1.24,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":50,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"bambuID":"GFL03","crealityID":"09001","crealityPressureAdvance":0.04}},{"id":425,"label":"ABS-CF","material_type":"ABS","filled_type":"CF","density":1.04,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":280,"dryTemp":80,"dryTime":8,"bedTempMin":85,"bedTempMax":90},"metadata":{"bambuID":"GFB99","crealityID":"00004","crealityPressureAdvance":0.06}},{"id":42962,"label":"PP-GF","material_type":"PP","filled_type":"GF","density":0.9,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":250,"dryTemp":60,"dryTime":4,"bedTempMin":55,"bedTempMax":60},"metadata":{"bambuID":"GFP95"}},{"id":10122,"label":"PEKK-CF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":350,"nozzleTempMax":400,"dryTemp":120,"dryTime":6,"bedTempMin":120,"bedTempMax":140},"metadata":{"crealityID":"00001","crealityPressureAdvance":0.04,"bambuID":"GFA00"}},{"id":27268,"label":"PCTPE","material_type":"PCPTFE","filled_type":null,"density":1.01,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":235,"nozzleTempMax":245,"dryTemp":90,"dryTime":3,"bedTempMin":50,"bedTempMax":55},"metadata":{}},{"id":59328,"label":"PA","material_type":"PA","filled_type":null,"density":1.52,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":280,"dryTemp":85,"dryTime":12,"bedTempMin":100,"bedTempMax":105},"metadata":{"bambuID":"GFN99","crealityID":"00008","crealityPressureAdvance":0.058}},{"id":18703,"label":"PETP","material_type":"PET","filled_type":null,"density":1.38,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":260,"dryTemp":70,"dryTime":8,"bedTempMin":90,"bedTempMax":95},"metadata":{}},{"id":33958,"label":"TPE","material_type":"TPE","filled_type":null,"density":1,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":210,"nozzleTempMax":260,"dryTemp":65,"dryTime":8,"bedTempMin":45,"bedTempMax":60},"metadata":{}},{"id":48310,"label":"PLA-CF","material_type":"PLA","filled_type":"CF","density":1.24,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":60,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"bambuID":"GFL98","crealityID":"00006","crealityPressureAdvance":0.05}},{"id":31011,"label":"ASA-AERO","material_type":"ASA","filled_type":"AERO","density":1.05,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":280,"dryTemp":80,"dryTime":8,"bedTempMin":95,"bedTempMax":100},"metadata":{"bambuID":"GFB02"}},{"id":27635,"label":"PE","material_type":"PE","filled_type":null,"density":0.94,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":175,"nozzleTempMax":220,"dryTemp":0,"dryTime":0,"bedTempMin":55,"bedTempMax":60},"metadata":{"bambuID":"GFP99"}},{"id":42623,"label":"PMMA","material_type":"PMMA","filled_type":null,"density":1.18,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":265,"dryTemp":80,"dryTime":4,"bedTempMin":100,"bedTempMax":110},"metadata":{}},{"id":24629,"label":"PLA High Speed","material_type":"PLA","filled_type":null,"density":1.24,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":50,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"bambuID":"GFL95","crealityID":"01001","crealityPressureAdvance":0.04}},{"id":65535,"label":"None","material_type":"None","filled_type":null,"density":null,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":0,"nozzleTempMax":0,"dryTemp":0,"dryTime":0,"bedTempMin":0,"bedTempMax":0},"metadata":{}},{"id":3368,"label":"PC-ABS","material_type":"PC","filled_type":null,"density":1.19,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":290,"dryTemp":85,"dryTime":8,"bedTempMin":90,"bedTempMax":95},"metadata":{}},{"id":50206,"label":"POM","material_type":"POM","filled_type":null,"density":1.4,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":230,"nozzleTempMax":250,"dryTemp":75,"dryTime":3,"bedTempMin":90,"bedTempMax":110},"metadata":{}},{"id":43518,"label":"TPU","material_type":"TPU","filled_type":null,"density":1.21,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":200,"nozzleTempMax":250,"dryTemp":75,"dryTime":8,"bedTempMin":35,"bedTempMax":40},"metadata":{"bambuID":"GFU99","crealityID":"00005","crealityPressureAdvance":0.04}},{"id":7649,"label":"PETG HS","material_type":"PETG","filled_type":null,"density":1.27,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":270,"dryTemp":70,"dryTime":8,"bedTempMin":70,"bedTempMax":75},"metadata":{"bambuID":"GFG96","crealityID":"06002","crealityPressureAdvance":0.05}},{"id":45962,"label":"PVB","material_type":"PVB","filled_type":null,"density":1.07,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":205,"nozzleTempMax":225,"dryTemp":45,"dryTime":8,"bedTempMin":30,"bedTempMax":70},"metadata":{}},{"id":12216,"label":"PEEK-GF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":380,"nozzleTempMax":420,"dryTemp":120,"dryTime":6,"bedTempMin":130,"bedTempMax":160},"metadata":{"crealityID":"00001","crealityPressureAdvance":0.04,"bambuID":"GFA00"}},{"id":30884,"label":"PP","material_type":"PP","filled_type":null,"density":0.9,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":250,"dryTemp":60,"dryTime":4,"bedTempMin":55,"bedTempMax":60},"metadata":{"bambuID":"GFP97","crealityID":"00019","crealityPressureAdvance":0.4}},{"id":39944,"label":"PA-CF","material_type":"PA","filled_type":"CF","density":1.52,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":300,"dryTemp":85,"dryTime":12,"bedTempMin":100,"bedTempMax":105},"metadata":{"bambuID":"GFN98","crealityID":"00009","crealityPressureAdvance":0.044}},{"id":1173,"label":"PA6-GF","material_type":"PA6","filled_type":"GF","density":1.52,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":300,"dryTemp":85,"dryTime":12,"bedTempMin":40,"bedTempMax":45},"metadata":{"bambuID":"GFN08"}},{"id":62335,"label":"PEI-1010-CF","material_type":"PEI-1010","filled_type":"CF","density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":370,"nozzleTempMax":430,"dryTemp":150,"dryTime":8,"bedTempMin":80,"bedTempMax":100},"metadata":{}},{"id":51007,"label":"Biopolymer","material_type":"","filled_type":null,"density":null,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":50,"dryTime":8,"bedTempMin":45,"bedTempMax":5},"metadata":{"bambuID":"GFL99","crealityID":"00001","crealityPressureAdvance":0.04}},{"id":8394,"label":"Castable Resin","material_type":"","filled_type":null,"density":1.05,"filled":false,"product_type_id":173,"recommended":{"washTime":0,"postCuringTemp":0,"postCuringTime":0,"workTempMin":0,"workTempMax":0},"metadata":{}},{"id":24115,"label":"SEBS","material_type":"SEBS","filled_type":null,"density":0.9,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":245,"nozzleTempMax":260,"dryTemp":65,"dryTime":3,"bedTempMin":70,"bedTempMax":80},"metadata":{}},{"id":56666,"label":"PA6","material_type":"PA6","filled_type":null,"density":1.52,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":280,"dryTemp":85,"dryTime":12,"bedTempMin":100,"bedTempMax":105},"metadata":{"bambuID":"GFN05","crealityID":"00015","crealityPressureAdvance":0.044}},{"id":48469,"label":"PEEK-CF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":380,"nozzleTempMax":420,"dryTemp":120,"dryTime":6,"bedTempMin":130,"bedTempMax":160},"metadata":{"crealityID":"00001","crealityPressureAdvance":0.04,"bambuID":"GFA00"}},{"id":35100,"label":"ASA-GF","material_type":"ASA","filled_type":"GF","density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":250,"nozzleTempMax":280,"dryTemp":80,"dryTime":8,"bedTempMin":90,"bedTempMax":95},"metadata":{"bambuID":"GFB51","crealityID":"00007","crealityPressureAdvance":0.06}},{"id":14508,"label":"PEI-9085","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":350,"nozzleTempMax":390,"dryTemp":150,"dryTime":12,"bedTempMin":120,"bedTempMax":160},"metadata":{"crealityID":"00001","crealityPressureAdvance":0.04,"bambuID":"GFA00"}},{"id":5238,"label":"PETG-PTFE","material_type":"PETG","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":230,"nozzleTempMax":255,"dryTemp":55,"dryTime":6,"bedTempMin":70,"bedTempMax":80},"metadata":{"bambuID":"GFG50","crealityID":"00014","crealityPressureAdvance":0.03}},{"id":54568,"label":"ASA+","material_type":"ASA","filled_type":null,"density":null,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":250,"nozzleTempMax":280,"dryTemp":65,"dryTime":8,"bedTempMin":90,"bedTempMax":110},"metadata":{}},{"id":9483,"label":"PVA","material_type":"PVA","filled_type":null,"density":1.23,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":85,"dryTime":12,"bedTempMin":30,"bedTempMax":60},"metadata":{"bambuID":"GFS99","crealityID":"00011"}},{"id":10272,"label":"PSU","material_type":"PSU","filled_type":null,"density":1.24,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":360,"nozzleTempMax":400,"dryTemp":110,"dryTime":4,"bedTempMin":140,"bedTempMax":160},"metadata":{}},{"id":41134,"label":"PEI-9085-CF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":360,"nozzleTempMax":400,"dryTemp":150,"dryTime":12,"bedTempMin":120,"bedTempMax":160},"metadata":{"crealityID":"00001","crealityPressureAdvance":0.04,"bambuID":"GFA00"}},{"id":12844,"label":"ASA","material_type":"ASA","filled_type":null,"density":1.05,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":280,"dryTemp":80,"dryTime":8,"bedTempMin":95,"bedTempMax":100},"metadata":{"bambuID":"GFB98","crealityID":"00007","crealityPressureAdvance":0.06}},{"id":63946,"label":"TPI","material_type":"TPI","filled_type":null,"density":1.38,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":360,"nozzleTempMax":390,"dryTemp":120,"dryTime":24,"bedTempMin":0,"bedTempMax":0},"metadata":{}},{"id":55279,"label":"PBT","material_type":"PBT","filled_type":null,"density":1.2,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":235,"nozzleTempMax":245,"dryTemp":75,"dryTime":6,"bedTempMin":0,"bedTempMax":0},"metadata":{}},{"id":20073,"label":"PVC","material_type":"PVC","filled_type":null,"density":1.38,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":215,"nozzleTempMax":230,"dryTemp":80,"dryTime":1,"bedTempMin":80,"bedTempMax":85},"metadata":{}},{"id":21307,"label":"SBS","material_type":"SBS","filled_type":null,"density":null,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":195,"nozzleTempMax":250,"dryTemp":50,"dryTime":4,"bedTempMin":0,"bedTempMax":55},"metadata":{"crealityID":"00004","crealityPressureAdvance":0.06,"bambuID":"GFB99"}},{"id":8504,"label":"PPA-CF","material_type":"PPA","filled_type":"CF","density":1.42,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":280,"nozzleTempMax":320,"dryTemp":110,"dryTime":12,"bedTempMin":100,"bedTempMax":105},"metadata":{"bambuID":"GFN97"}},{"id":35377,"label":"PEI-9085-GF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":360,"nozzleTempMax":400,"dryTemp":150,"dryTime":12,"bedTempMin":120,"bedTempMax":160},"metadata":{"crealityID":"00001","crealityPressureAdvance":0.04,"bambuID":"GFA00"}},{"id":38250,"label":"PEI-1010-GF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":380,"nozzleTempMax":430,"dryTemp":150,"dryTime":12,"bedTempMin":120,"bedTempMax":160},"metadata":{"crealityID":"00001","crealityPressureAdvance":0.04,"bambuID":"GFA00"}},{"id":29815,"label":"PEEK","material_type":"PEEK","filled_type":null,"density":1.32,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":390,"nozzleTempMax":410,"dryTemp":120,"dryTime":6,"bedTempMin":130,"bedTempMax":145},"metadata":{}},{"id":38256,"label":"PETG","material_type":"PETG","filled_type":null,"density":1.27,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":270,"dryTemp":70,"dryTime":8,"bedTempMin":70,"bedTempMax":75},"metadata":{"bambuID":"GFG99","crealityID":"00003","crealityPressureAdvance":0.07}},{"id":12878,"label":"CoPE","material_type":"","filled_type":null,"density":null,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":70,"dryTime":6,"bedTempMin":30,"bedTempMax":45},"metadata":{"bambuID":"GFU99","crealityID":"00005","crealityPressureAdvance":0.04}},{"id":55418,"label":"PETG-CF","material_type":"PETG","filled_type":"CF","density":1.27,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":270,"dryTemp":70,"dryTime":8,"bedTempMin":75,"bedTempMax":80},"metadata":{"bambuID":"GFG98","crealityID":"00014","crealityPressureAdvance":0.03}},{"id":61563,"label":"PC-PBT-GF","material_type":"PC","filled_type":"GF","density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":280,"dryTemp":90,"dryTime":2,"bedTempMin":90,"bedTempMax":100},"metadata":{"crealityID":"00021","crealityPressureAdvance":0.05,"bambuID":"GFC99"}},{"id":34409,"label":"TPS","material_type":"TPS","filled_type":null,"density":1,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":280,"nozzleTempMax":290,"dryTemp":80,"dryTime":24,"bedTempMin":70,"bedTempMax":90},"metadata":{}},{"id":1680,"label":"PE-GF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":260,"dryTemp":80,"dryTime":8,"bedTempMin":60,"bedTempMax":80},"metadata":{"crealityID":"00013","crealityPressureAdvance":0.03,"bambuID":"GFP98"}},{"id":59849,"label":"PCL","material_type":"","filled_type":null,"density":null,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":80,"nozzleTempMax":140,"dryTemp":0,"dryTime":0,"bedTempMin":25,"bedTempMax":45},"metadata":{"crealityID":"00001","crealityPressureAdvance":0.04,"bambuID":"GFA00"}},{"id":20588,"label":"PC-CF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":270,"nozzleTempMax":310,"dryTemp":90,"dryTime":8,"bedTempMin":100,"bedTempMax":120},"metadata":{"crealityID":"00021","crealityPressureAdvance":0.05,"bambuID":"GFC99"}},{"id":15041,"label":"PCTG","material_type":"PCTG","filled_type":null,"density":1.21,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":270,"dryTemp":65,"dryTime":8,"bedTempMin":70,"bedTempMax":75},"metadata":{"bambuID":"GFG97"}},{"id":46154,"label":"PPS","material_type":"PPS","filled_type":null,"density":1.35,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":300,"nozzleTempMax":340,"dryTemp":130,"dryTime":12,"bedTempMin":110,"bedTempMax":115},"metadata":{"crealityID":"00017","bambuID":"GFT97"}},{"id":3481,"label":"PCTG-GF","material_type":"PCTG","filled_type":"GF","density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":270,"dryTemp":65,"dryTime":8,"bedTempMin":70,"bedTempMax":75},"metadata":{"crealityID":"00014","crealityPressureAdvance":0.03,"bambuID":"GFG97"}},{"id":46276,"label":"PPA-GF","material_type":"PPA","filled_type":"GF","density":1.45,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":280,"nozzleTempMax":320,"dryTemp":110,"dryTime":12,"bedTempMin":100,"bedTempMax":105},"metadata":{"bambuID":"GFN96"}},{"id":23080,"label":"PAHT","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":300,"dryTemp":90,"dryTime":12,"bedTempMin":90,"bedTempMax":110},"metadata":{"crealityID":"00016","crealityPressureAdvance":0.058,"bambuID":"GFN04"}},{"id":52077,"label":"PET","material_type":"PET","filled_type":null,"density":1.27,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":290,"dryTemp":85,"dryTime":12,"bedTempMin":65,"bedTempMax":70},"metadata":{"bambuID":"GFG99","crealityID":"00020","crealityPressureAdvance":0.13}},{"id":22652,"label":"PAHT-GF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":270,"nozzleTempMax":310,"dryTemp":90,"dryTime":12,"bedTempMin":90,"bedTempMax":110},"metadata":{"crealityID":"00016","crealityPressureAdvance":0.058,"bambuID":"GFN04"}},{"id":5733,"label":"TPU-AMS","material_type":"TPU","filled_type":null,"density":1.21,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":200,"nozzleTempMax":250,"dryTemp":75,"dryTime":8,"bedTempMin":35,"bedTempMax":40},"metadata":{"crealityID":"00005","crealityPressureAdvance":0.04,"bambuID":"GFU02"}},{"id":38219,"label":"PLA","material_type":"PLA","filled_type":null,"density":1.24,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":50,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"bambuID":"GFL99","crealityID":"00001","crealityPressureAdvance":0.04}},{"id":22678,"label":"PET-GF","material_type":"PET","filled_type":"GF","density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":280,"nozzleTempMax":300,"dryTemp":50,"dryTime":8,"bedTempMin":80,"bedTempMax":85},"metadata":{"bambuID":"GFG98","crealityID":"00013","crealityPressureAdvance":0.03}},{"id":2053,"label":"PA12-GF","material_type":"PA12","filled_type":"GF","density":1.52,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":300,"dryTemp":85,"dryTime":12,"bedTempMin":40,"bedTempMax":45},"metadata":{}},{"id":12264,"label":"PA6-CF","material_type":"PA6","filled_type":"CF","density":1.52,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":300,"dryTemp":85,"dryTime":12,"bedTempMin":40,"bedTempMax":45},"metadata":{"crealityID":"00015","crealityPressureAdvance":0.044,"bambuID":"GFN05"}},{"id":29272,"label":"PA11","material_type":"","filled_type":null,"density":null,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":290,"dryTemp":80,"dryTime":12,"bedTempMin":80,"bedTempMax":100},"metadata":{"crealityID":"00008","crealityPressureAdvance":0.058,"bambuID":"GFN99"}},{"id":51861,"label":"PETG-ESD","material_type":"PETG","filled_type":null,"density":1.27,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":270,"dryTemp":70,"dryTime":8,"bedTempMin":65,"bedTempMax":70},"metadata":{"bambuID":"GFG50","crealityID":"00014","crealityPressureAdvance":0.03}},{"id":47651,"label":"PC-PBT-CF","material_type":"PC","filled_type":"CF","density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":280,"dryTemp":90,"dryTime":2,"bedTempMin":90,"bedTempMax":100},"metadata":{"crealityID":"00021","crealityPressureAdvance":0.05,"bambuID":"GFC99"}},{"id":20562,"label":"ABS","material_type":"ABS","filled_type":null,"density":1.04,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":280,"dryTemp":80,"dryTime":8,"bedTempMin":85,"bedTempMax":90},"metadata":{"bambuID":"GFB99","crealityID":"00004","crealityPressureAdvance":0.06}},{"id":53970,"label":"PEKK","material_type":"PEKK","filled_type":null,"density":1.28,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":380,"nozzleTempMax":440,"dryTemp":130,"dryTime":4,"bedTempMin":120,"bedTempMax":140},"metadata":{}},{"id":18451,"label":"PA11-CF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":270,"nozzleTempMax":300,"dryTemp":80,"dryTime":12,"bedTempMin":80,"bedTempMax":100},"metadata":{"crealityID":"00009","crealityPressureAdvance":0.044,"bambuID":"GFN98"}},{"id":6605,"label":"PA11-GF","material_type":"","filled_type":null,"density":null,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":270,"nozzleTempMax":300,"dryTemp":80,"dryTime":12,"bedTempMin":80,"bedTempMax":100},"metadata":{"bambuID":"GFN08","crealityID":"00015","crealityPressureAdvance":0.044}},{"id":48815,"label":"PAHT-CF","material_type":"PAHT","filled_type":"CF","density":1.52,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":300,"dryTemp":85,"dryTime":12,"bedTempMin":100,"bedTempMax":105},"metadata":{"crealityID":"00016","crealityPressureAdvance":0.058,"bambuID":"GFN04"}},{"id":39667,"label":"PA12-CF","material_type":"PA12","filled_type":"CF","density":1.52,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":300,"dryTemp":85,"dryTime":12,"bedTempMin":40,"bedTempMax":45},"metadata":{"crealityID":"00009","crealityPressureAdvance":0.044,"bambuID":"GFL52"}},{"id":49074,"label":"ABS-GF","material_type":"ABS","filled_type":"GF","density":1.04,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":280,"dryTemp":80,"dryTime":8,"bedTempMin":85,"bedTempMax":90},"metadata":{}},{"id":55796,"label":"PA12","material_type":"PA12","filled_type":null,"density":1.52,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":250,"nozzleTempMax":290,"dryTemp":85,"dryTime":12,"bedTempMin":70,"bedTempMax":90},"metadata":{}},{"id":58498,"label":"PEBA","material_type":"PEBA","filled_type":null,"density":1.01,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":225,"nozzleTempMax":250,"dryTemp":70,"dryTime":3,"bedTempMin":95,"bedTempMax":100},"metadata":{}},{"id":61048,"label":"PVDF","material_type":"PVDF","filled_type":null,"density":1.78,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":265,"dryTemp":0,"dryTime":0,"bedTempMin":90,"bedTempMax":110},"metadata":{}},{"id":26029,"label":"HIPS","material_type":"HIPS","filled_type":null,"density":1.03,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":270,"dryTemp":80,"dryTime":6,"bedTempMin":90,"bedTempMax":95},"metadata":{"bambuID":"GFS98","crealityID":"00012"}},{"id":8345,"label":"PLA+ Silk","material_type":"PLA","filled_type":null,"density":1.24,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":50,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"bambuID":"GFA06","crealityID":"00002","crealityPressureAdvance":0.04}},{"id":13850,"label":"PPA","material_type":"PPA","filled_type":null,"density":1.15,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":280,"nozzleTempMax":320,"dryTemp":110,"dryTime":12,"bedTempMin":100,"bedTempMax":105},"metadata":{}},{"id":10738,"label":"PC-PTFE","material_type":"PC","filled_type":"PTFE","density":1.3,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":270,"nozzleTempMax":290,"dryTemp":80,"dryTime":4,"bedTempMin":110,"bedTempMax":130},"metadata":{}},{"id":4587,"label":"PC-PBT","material_type":"PC","filled_type":null,"density":1.2,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":280,"dryTemp":90,"dryTime":2,"bedTempMin":90,"bedTempMax":100},"metadata":{"bambuID":"GFC00","crealityID":"00021","crealityPressureAdvance":0.05}},{"id":10187,"label":"PHA","material_type":"PHA","filled_type":null,"density":1.25,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":50,"dryTime":24,"bedTempMin":55,"bedTempMax":60},"metadata":{"bambuID":"GFR98"}},{"id":735,"label":"ABS-AF","material_type":"ABS","filled_type":"AF","density":1.04,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":240,"nozzleTempMax":290,"dryTemp":85,"dryTime":8,"bedTempMin":85,"bedTempMax":90},"metadata":{"bambuID":"GFB00","crealityID":"00004","crealityPressureAdvance":0.06}},{"id":28110,"label":"SBC","material_type":"SBC","filled_type":null,"density":1.01,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":260,"dryTemp":45,"dryTime":6,"bedTempMin":65,"bedTempMax":70},"metadata":{}},{"id":50497,"label":"PP-CF","material_type":"PP","filled_type":"CF","density":0.9,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":220,"nozzleTempMax":250,"dryTemp":60,"dryTime":4,"bedTempMin":55,"bedTempMax":60},"metadata":{"bambuID":"GFP96"}},{"id":30458,"label":"PC","material_type":"PC","filled_type":null,"density":1.3,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":260,"nozzleTempMax":290,"dryTemp":85,"dryTime":8,"bedTempMin":110,"bedTempMax":115},"metadata":{"bambuID":"GFC99","crealityID":"00021","crealityPressureAdvance":0.05}},{"id":10478,"label":"Castable Filament","material_type":"","filled_type":null,"density":null,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":220,"dryTemp":55,"dryTime":12,"bedTempMin":30,"bedTempMax":70},"metadata":{}},{"id":48047,"label":"TPU High Speed","material_type":"TPU","filled_type":null,"density":1.21,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":200,"nozzleTempMax":250,"dryTemp":75,"dryTime":8,"bedTempMin":35,"bedTempMax":40},"metadata":{"bambuID":"GFU00","crealityID":"00005","crealityPressureAdvance":0.04}},{"id":18922,"label":"PLA-ESD","material_type":"PLA","filled_type":null,"density":1.24,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":50,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"bambuID":"GFL98","crealityID":"00006","crealityPressureAdvance":0.05}},{"id":11506,"label":"PLA-AERO","material_type":"PLA","filled_type":"AERO","density":1.24,"filled":true,"product_type_id":142,"recommended":{"nozzleTempMin":210,"nozzleTempMax":260,"dryTemp":50,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"crealityID":"00007","crealityPressureAdvance":0.06,"bambuID":"GFA11"}},{"id":49152,"label":"PPSU","material_type":"PPSU","filled_type":null,"density":1.37,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":360,"nozzleTempMax":400,"dryTemp":130,"dryTime":12,"bedTempMin":140,"bedTempMax":170},"metadata":{}},{"id":10602,"label":"PLA Silk","material_type":"PLA","filled_type":null,"density":1.24,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":50,"dryTime":8,"bedTempMin":45,"bedTempMax":55},"metadata":{"bambuID":"GFL96","crealityID":"00002","crealityPressureAdvance":0.04}},{"id":34049,"label":"BVOH","material_type":"BVOH","filled_type":null,"density":1.14,"filled":false,"product_type_id":142,"recommended":{"nozzleTempMin":190,"nozzleTempMax":240,"dryTemp":85,"dryTime":12,"bedTempMin":55,"bedTempMax":60},"metadata":{"bambuID":"GFS97","crealityID":"00010","crealityPressureAdvance":0.4}}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"id":10,"label":"mg","type":"weight"},{"id":21,"label":"g","type":"weight"},{"id":35,"label":"kg","type":"weight"},{"id":48,"label":"ml","type":"volume"},{"id":62,"label":"cl","type":"volume"},{"id":79,"label":"L","type":"volume"},{"id":95,"label":"m³","type":"volume"},{"id":112,"label":"mm","type":"size"},{"id":130,"label":"cm","type":"size"},{"id":149,"label":"m","type":"size"},{"id":170,"label":"m²","type":"area"}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"id":142,"label":"Filament"},{"id":173,"label":"Resin"}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"id":0,"version":"0.0","name":"RFID Empty","tag":"TIGER_TAG_UNINITIALIZED","public_key":""},{"id":1542820452,"version":"1.0","name":"TigerTag","tag":"TIGER_TAG_MAKER_V1.0","public_key":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwtX8JRYMoAXTbkU7EXJYKX7g4Mf0\nZ3WUuuGzlfyiEiS5UseXT6l1t1ZbMgzsg5IVA0TB7+/w6eyTlgnz/HXONw==\n-----END PUBLIC KEY-----"},{"id":1816240865,"version":"1.0","name":"TigerTag Init","tag":"TIGER_TAG_INIT","public_key":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwtX8JRYMoAXTbkU7EXJYKX7g4Mf0\nZ3WUuuGzlfyiEiS5UseXT6l1t1ZbMgzsg5IVA0TB7+/w6eyTlgnz/HXONw==\n-----END PUBLIC KEY-----"},{"id":3155151767,"version":"1.0","name":"TigerTag+","tag":"TIGER_TAG_PRO_V1.0","public_key":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwtX8JRYMoAXTbkU7EXJYKX7g4Mf0\nZ3WUuuGzlfyiEiS5UseXT6l1t1ZbMgzsg5IVA0TB7+/w6eyTlgnz/HXONw==\n-----END PUBLIC KEY-----"}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"versions":1763073059935,"types":1777884684291,"brands":1777885837902,"filament_diameters":1777895560487,"filament_materials":1778798861002,"aspects":1777894570720,"measure_units":1777896731691}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tigertag",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "JavaScript SDK for TigerTag RFID material identification",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"tigertag": "bin/tigertag.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src/",
|
|
11
|
+
"database/",
|
|
12
|
+
"bin/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE.md",
|
|
15
|
+
"CHANGELOG.md"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"tigertag",
|
|
22
|
+
"rfid",
|
|
23
|
+
"nfc",
|
|
24
|
+
"materials",
|
|
25
|
+
"manufacturing",
|
|
26
|
+
"ntag213",
|
|
27
|
+
"identification",
|
|
28
|
+
"3d-printing",
|
|
29
|
+
"filament",
|
|
30
|
+
"resin",
|
|
31
|
+
"ecdsa",
|
|
32
|
+
"electron"
|
|
33
|
+
],
|
|
34
|
+
"author": "TigerTag <tigertag@tigertag.io> (https://tigertag.io)",
|
|
35
|
+
"license": "GPL-3.0",
|
|
36
|
+
"homepage": "https://tigertag.io",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/TigerTag-Project/TigerTag-SDK-JS.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/TigerTag-Project/TigerTag-SDK-JS/issues"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"test": "jest --testPathPattern=test/",
|
|
46
|
+
"generate-fixtures": "node scripts/generate_fixtures.js",
|
|
47
|
+
"playground": "node tools/server.js"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"jest": "^29.0.0",
|
|
51
|
+
"nfc-pcsc": "^0.8.1",
|
|
52
|
+
"ws": "^8.20.1"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/db.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TigerTag RFID Guide
|
|
4
|
+
// Copyright (C) 2025 TigerTag
|
|
5
|
+
//
|
|
6
|
+
// This program is free software: you can redistribute it and/or modify
|
|
7
|
+
// it under the terms of the GNU General Public License as published by
|
|
8
|
+
// the Free Software Foundation, either version 3 of the License.
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* TigerTag reference database loader and sync utilities.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const _API_BASE = 'https://api.tigertag.io/api:tigertag';
|
|
18
|
+
const _GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/TigerTag-Project/TigerTag-RFID-Guide/main/database';
|
|
19
|
+
const _HTTP_TIMEOUT = 30000;
|
|
20
|
+
|
|
21
|
+
// Maps last_update key → [API endpoint, local filename]
|
|
22
|
+
const _DATASETS = {
|
|
23
|
+
versions: ['version/get/all', 'id_version.json'],
|
|
24
|
+
types: ['type/get/all', 'id_type.json'],
|
|
25
|
+
brands: ['brand/get/all', 'id_brand.json'],
|
|
26
|
+
filament_diameters: ['diameter/filament/get/all', 'id_diameter.json'],
|
|
27
|
+
filament_materials: ['material/get/all', 'id_material.json'],
|
|
28
|
+
aspects: ['aspect/get/all', 'id_aspect.json'],
|
|
29
|
+
measure_units: ['measure_unit/get/all', 'id_measure_unit.json'],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const _BUNDLED_DB_PATH = path.join(__dirname, '..', 'database');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Download or update TigerTag reference JSON databases.
|
|
36
|
+
* Tries the live TigerTag API first; falls back to the GitHub mirror.
|
|
37
|
+
* Only downloads files whose timestamp has changed.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} [dbPath] - Folder where JSON files are stored (created if missing).
|
|
40
|
+
* @param {object} [options]
|
|
41
|
+
* @param {boolean} [options.force=false] - Re-download all files even if up to date.
|
|
42
|
+
* @param {boolean} [options.verbose=true] - Print progress to stdout.
|
|
43
|
+
* @returns {Promise<string[]>} List of filenames that were downloaded/updated.
|
|
44
|
+
*/
|
|
45
|
+
async function syncDatabases(dbPath, { force = false, verbose = true } = {}) {
|
|
46
|
+
const resolvedPath = path.resolve(dbPath || _BUNDLED_DB_PATH);
|
|
47
|
+
fs.mkdirSync(resolvedPath, { recursive: true });
|
|
48
|
+
|
|
49
|
+
const lastUpdatePath = path.join(resolvedPath, 'last_update.json');
|
|
50
|
+
|
|
51
|
+
const _log = verbose ? (msg) => console.log(msg) : () => {};
|
|
52
|
+
|
|
53
|
+
async function _get(url) {
|
|
54
|
+
const controller = new AbortController();
|
|
55
|
+
const timer = setTimeout(() => controller.abort(), _HTTP_TIMEOUT);
|
|
56
|
+
try {
|
|
57
|
+
const resp = await fetch(url, { signal: controller.signal });
|
|
58
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
59
|
+
const text = await resp.text();
|
|
60
|
+
return { data: JSON.parse(text), text };
|
|
61
|
+
} finally {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let remoteData, remoteText, datasetUrlFn;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const result = await _get(`${_API_BASE}/all/last_update`);
|
|
70
|
+
remoteData = result.data;
|
|
71
|
+
remoteText = result.text;
|
|
72
|
+
datasetUrlFn = (endpoint, _filename) => `${_API_BASE}/${endpoint}`;
|
|
73
|
+
_log('[info] source: api');
|
|
74
|
+
} catch (exc) {
|
|
75
|
+
_log(`[warn] TigerTag API unreachable (${exc.message}), falling back to GitHub mirror`);
|
|
76
|
+
try {
|
|
77
|
+
const result = await _get(`${_GITHUB_RAW_BASE}/last_update.json`);
|
|
78
|
+
remoteData = result.data;
|
|
79
|
+
remoteText = result.text;
|
|
80
|
+
datasetUrlFn = (_endpoint, filename) => `${_GITHUB_RAW_BASE}/${filename}`;
|
|
81
|
+
_log('[info] source: github');
|
|
82
|
+
} catch (exc2) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Both API and GitHub mirror are unreachable.\nAPI error: ${exc.message}\nGitHub error: ${exc2.message}\nCheck your internet connection.`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let localData = {};
|
|
90
|
+
if (fs.existsSync(lastUpdatePath)) {
|
|
91
|
+
try {
|
|
92
|
+
localData = JSON.parse(fs.readFileSync(lastUpdatePath, 'utf8'));
|
|
93
|
+
} catch (_) {}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const updated = [];
|
|
97
|
+
|
|
98
|
+
for (const [key, [endpoint, filename]] of Object.entries(_DATASETS)) {
|
|
99
|
+
const remoteTs = remoteData[key];
|
|
100
|
+
const localTs = localData[key];
|
|
101
|
+
const localFile = path.join(resolvedPath, filename);
|
|
102
|
+
|
|
103
|
+
if (remoteTs == null) {
|
|
104
|
+
_log(`[skip] ${key}: not in last_update payload`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!force && remoteTs === localTs && fs.existsSync(localFile)) {
|
|
109
|
+
_log(`[ok] ${filename}: up to date`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
_log(`[sync] ${filename}: ${localTs} → ${remoteTs}`);
|
|
114
|
+
const controller = new AbortController();
|
|
115
|
+
const timer = setTimeout(() => controller.abort(), _HTTP_TIMEOUT);
|
|
116
|
+
try {
|
|
117
|
+
const resp = await fetch(datasetUrlFn(endpoint, filename), { signal: controller.signal });
|
|
118
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
119
|
+
const data = await resp.json();
|
|
120
|
+
fs.writeFileSync(localFile, JSON.stringify(data, null, 2), 'utf8');
|
|
121
|
+
updated.push(filename);
|
|
122
|
+
} finally {
|
|
123
|
+
clearTimeout(timer);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (updated.length > 0 || JSON.stringify(localData) !== JSON.stringify(remoteData)) {
|
|
128
|
+
fs.writeFileSync(lastUpdatePath, remoteText, 'utf8');
|
|
129
|
+
if (!updated.includes('last_update.json')) updated.push('last_update.json');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return updated;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Loads and exposes TigerTag JSON reference databases.
|
|
137
|
+
*
|
|
138
|
+
* Ships with bundled JSON files so the SDK works offline immediately after install.
|
|
139
|
+
* Optionally syncs updates from the TigerTag API or GitHub mirror via sync().
|
|
140
|
+
*
|
|
141
|
+
* All ID lookups return the full JSON entry object (or null if not found).
|
|
142
|
+
* The JSON files are the single source of truth — no hardcoded ID mappings.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* const db = new TigerTagDB();
|
|
146
|
+
* const mat = db.material(38219);
|
|
147
|
+
* console.log(mat.label); // "PLA"
|
|
148
|
+
* console.log(mat.density); // 1.24
|
|
149
|
+
*/
|
|
150
|
+
class TigerTagDB {
|
|
151
|
+
/**
|
|
152
|
+
* @param {object} [options]
|
|
153
|
+
* @param {string} [options.dbPath] - Path to database directory. Defaults to bundled DB.
|
|
154
|
+
* @param {boolean} [options.autoSync] - Flag; call db.sync() manually to download files.
|
|
155
|
+
* @param {boolean} [options.verbose] - Print sync progress (default true).
|
|
156
|
+
*/
|
|
157
|
+
constructor({ dbPath, autoSync = false, verbose = true } = {}) {
|
|
158
|
+
this._path = dbPath ? path.resolve(dbPath) : _BUNDLED_DB_PATH;
|
|
159
|
+
this._autoSync = autoSync;
|
|
160
|
+
this._verbose = verbose;
|
|
161
|
+
this._ensureDb();
|
|
162
|
+
this._versions = this._load('id_version.json');
|
|
163
|
+
this._materials = this._load('id_material.json');
|
|
164
|
+
this._aspects = this._load('id_aspect.json');
|
|
165
|
+
this._types = this._load('id_type.json');
|
|
166
|
+
this._diameters = this._load('id_diameter.json');
|
|
167
|
+
this._brands = this._load('id_brand.json');
|
|
168
|
+
this._units = this._load('id_measure_unit.json');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
_ensureDb() {
|
|
172
|
+
const missing = TigerTagDB.REQUIRED_FILES.filter(
|
|
173
|
+
(fn) => !fs.existsSync(path.join(this._path, fn)),
|
|
174
|
+
);
|
|
175
|
+
if (missing.length === 0) return;
|
|
176
|
+
|
|
177
|
+
// Fallback to bundled database if user provided a custom path
|
|
178
|
+
if (this._path !== _BUNDLED_DB_PATH && fs.existsSync(_BUNDLED_DB_PATH)) {
|
|
179
|
+
const stillMissing = TigerTagDB.REQUIRED_FILES.filter(
|
|
180
|
+
(fn) => !fs.existsSync(path.join(_BUNDLED_DB_PATH, fn)),
|
|
181
|
+
);
|
|
182
|
+
if (stillMissing.length === 0) {
|
|
183
|
+
this._path = _BUNDLED_DB_PATH;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
process.stderr.write(
|
|
189
|
+
`\nTigerTag database files not found.\n Expected: ${path.resolve(this._path)}\n\n Missing:\n`
|
|
190
|
+
+ missing.map((fn) => ` - ${fn}`).join('\n')
|
|
191
|
+
+ '\n\n Call await db.sync() to download them.\n\n',
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
_load(filename) {
|
|
196
|
+
const fp = path.join(this._path, filename);
|
|
197
|
+
if (!fs.existsSync(fp)) return [];
|
|
198
|
+
try {
|
|
199
|
+
return JSON.parse(fs.readFileSync(fp, 'utf8'));
|
|
200
|
+
} catch (_) {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_reloadAll() {
|
|
206
|
+
this._versions = this._load('id_version.json');
|
|
207
|
+
this._materials = this._load('id_material.json');
|
|
208
|
+
this._aspects = this._load('id_aspect.json');
|
|
209
|
+
this._types = this._load('id_type.json');
|
|
210
|
+
this._diameters = this._load('id_diameter.json');
|
|
211
|
+
this._brands = this._load('id_brand.json');
|
|
212
|
+
this._units = this._load('id_measure_unit.json');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
static _find(table, idValue) {
|
|
216
|
+
return table.find((e) => e.id === idValue) || null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Manually trigger a database update.
|
|
221
|
+
* @param {boolean} [force=false] - Re-download all files even if up to date.
|
|
222
|
+
* @returns {Promise<string[]>} List of filenames that were downloaded/updated.
|
|
223
|
+
*/
|
|
224
|
+
async sync(force = false) {
|
|
225
|
+
const updated = await syncDatabases(this._path, { force, verbose: this._verbose });
|
|
226
|
+
this._reloadAll();
|
|
227
|
+
return updated;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Look up a version entry by id_tigertag value. Includes public_key for signature verification.
|
|
232
|
+
* @param {number} id
|
|
233
|
+
* @returns {object|null}
|
|
234
|
+
*/
|
|
235
|
+
version(id) { return TigerTagDB._find(this._versions, id); }
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Look up a material entry. Includes density, recommended temps, bambuID, etc.
|
|
239
|
+
* @param {number} id
|
|
240
|
+
* @returns {object|null}
|
|
241
|
+
*/
|
|
242
|
+
material(id) { return TigerTagDB._find(this._materials, id); }
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Look up an aspect entry. Includes color_count.
|
|
246
|
+
* @param {number} id
|
|
247
|
+
* @returns {object|null}
|
|
248
|
+
*/
|
|
249
|
+
aspect(id) { return TigerTagDB._find(this._aspects, id); }
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Look up a type entry.
|
|
253
|
+
* @param {number} id
|
|
254
|
+
* @returns {object|null}
|
|
255
|
+
*/
|
|
256
|
+
type(id) { return TigerTagDB._find(this._types, id); }
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Look up a diameter entry.
|
|
260
|
+
* @param {number} id
|
|
261
|
+
* @returns {object|null}
|
|
262
|
+
*/
|
|
263
|
+
diameter(id) { return TigerTagDB._find(this._diameters, id); }
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Look up a brand entry.
|
|
267
|
+
* @param {number} id
|
|
268
|
+
* @returns {object|null}
|
|
269
|
+
*/
|
|
270
|
+
brand(id) { return TigerTagDB._find(this._brands, id); }
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Look up a measure unit entry.
|
|
274
|
+
* @param {number} id
|
|
275
|
+
* @returns {object|null}
|
|
276
|
+
*/
|
|
277
|
+
unit(id) { return TigerTagDB._find(this._units, id); }
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Safe label string from any DB entry object.
|
|
281
|
+
* @param {object|null} entry
|
|
282
|
+
* @returns {string}
|
|
283
|
+
*/
|
|
284
|
+
static label(entry) {
|
|
285
|
+
if (!entry) return 'Unknown';
|
|
286
|
+
return entry.label || entry.name || 'Unknown';
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
TigerTagDB.REQUIRED_FILES = Object.values(_DATASETS).map(([, fn]) => fn);
|
|
291
|
+
|
|
292
|
+
module.exports = { TigerTagDB, syncDatabases, _BUNDLED_DB_PATH };
|