@@ -308,6 +309,45 @@ const char OPENTAG3D_WRITER_HTML[] PROGMEM = R"rawliteral(
syncColorPicker('colorPicker', 'colorHex');
setupAdvancedToggle('advancedToggle', 'advancedBox');
+ // Auto-fill temps and density from material selection
+ var baseMaterialEl = document.getElementById('base_material');
+ var modifiersEl = document.getElementById('material_modifiers');
+ var ot3dFieldMap = {
+ minPrintTemp: 'min_print_temp_c', maxPrintTemp: 'max_print_temp_c',
+ minBedTemp: 'min_bed_temp_c', maxBedTemp: 'max_bed_temp_c',
+ density: 'density'
+ };
+ trackAutoFill(['print_temp_c','bed_temp_c','min_print_temp_c','max_print_temp_c','min_bed_temp_c','max_bed_temp_c','density']);
+ function ot3dAutoFill() {
+ var name = baseMaterialEl.value;
+ var mod = modifiersEl ? modifiersEl.value : '';
+ if (mod && mod !== 'None') name = name + '-' + mod;
+ autoFillMaterialData(name, ot3dFieldMap);
+ // Also fill the basic print/bed temp fields
+ var m = lookupMaterial(name);
+ if (m) {
+ var pt = document.getElementById('print_temp_c');
+ if (pt && pt.dataset.autoFilled !== 'false') { pt.value = m.extruder_temp; pt.dataset.autoFilled = 'true'; }
+ var bt = document.getElementById('bed_temp_c');
+ if (bt && bt.dataset.autoFilled !== 'false') { bt.value = m.bed_temp; bt.dataset.autoFilled = 'true'; }
+ }
+ }
+ baseMaterialEl.addEventListener('input', ot3dAutoFill);
+ if (modifiersEl) modifiersEl.addEventListener('change', ot3dAutoFill);
+ loadMaterialDb().then(function(db) {
+ var dl = document.getElementById('material-list');
+ var existing = {};
+ var opts = dl.querySelectorAll('option');
+ for (var i = 0; i < opts.length; i++) existing[opts[i].value.toUpperCase()] = true;
+ Object.keys(db).sort().forEach(function(key) {
+ if (!existing[key]) {
+ var opt = document.createElement('option');
+ opt.value = db[key].material || key;
+ dl.appendChild(opt);
+ }
+ });
+ });
+
var EXTENDED_IDS = [
'serial_number', 'online_url', 'measured_filament_weight_g',
'empty_spool_weight_g', 'min_print_temp_c', 'max_print_temp_c',
diff --git a/src/SharedJS.h b/src/SharedJS.h
index 72a75ea..bc454c6 100644
--- a/src/SharedJS.h
+++ b/src/SharedJS.h
@@ -129,6 +129,125 @@ function setupAdvancedToggle(toggleId, boxId) {
return { open: function(){ set(true); }, close: function(){ set(false); } };
}
+/* ---- Material data auto-fill ---- */
+
+// Hardcoded fallback — works offline when API is unreachable
+var _materialFallback = {
+ 'PLA': {extruder_temp:240, bed_temp:55, density:1.24},
+ 'PETG': {extruder_temp:270, bed_temp:75, density:1.27},
+ 'ABS': {extruder_temp:280, bed_temp:90, density:1.04},
+ 'ASA': {extruder_temp:280, bed_temp:100, density:1.05},
+ 'TPU': {extruder_temp:250, bed_temp:40, density:1.21},
+ 'PC': {extruder_temp:290, bed_temp:115, density:1.3},
+ 'PCTG': {extruder_temp:270, bed_temp:75, density:1.21},
+ 'PP': {extruder_temp:250, bed_temp:60, density:0.9},
+ 'HIPS': {extruder_temp:270, bed_temp:95, density:1.03},
+ 'PVA': {extruder_temp:240, bed_temp:60, density:1.23},
+ 'PEEK': {extruder_temp:410, bed_temp:145, density:1.32},
+ 'PA6': {extruder_temp:300, bed_temp:45, density:1.52},
+ 'PA12': {extruder_temp:300, bed_temp:45, density:1.52},
+ 'PEI': {extruder_temp:380, bed_temp:130, density:1.27},
+ 'CPE': {extruder_temp:270, bed_temp:80, density:1.25}
+};
+var _materialDb = {};
+var _materialDbLoaded = false;
+
+function loadMaterialDb() {
+ if (_materialDbLoaded) return Promise.resolve(_materialDb);
+ // Start with fallback data
+ Object.keys(_materialFallback).forEach(function(k) {
+ _materialDb[k] = _materialFallback[k];
+ });
+ return fetch('https://api.tigertag.io/api:tigertag/SpoolmanDB/materials')
+ .then(function(r) { return r.ok ? r.json() : []; })
+ .then(function(data) {
+ if (Array.isArray(data)) {
+ data.forEach(function(m) {
+ if (m.material) _materialDb[m.material.toUpperCase()] = m;
+ });
+ }
+ _materialDbLoaded = true;
+ return _materialDb;
+ })
+ .catch(function() {
+ _materialDbLoaded = true;
+ return _materialDb;
+ });
+}
+
+function lookupMaterial(name) {
+ if (!name) return null;
+ var key = name.toUpperCase().replace(/\s+/g, '');
+ // Exact match first
+ if (_materialDb[key]) return _materialDb[key];
+ // Try with common separators
+ var withDash = key.replace(/([A-Z]+)(\d)/, '$1-$2');
+ if (_materialDb[withDash]) return _materialDb[withDash];
+ // Prefix match (e.g. "PA6 (Nylon 6)" → "PA6")
+ var prefix = key.split(/[^A-Z0-9-]/)[0];
+ if (prefix && _materialDb[prefix]) return _materialDb[prefix];
+ return null;
+}
+
+// Track which fields the user has manually edited
+function trackAutoFill(fieldIds) {
+ fieldIds.forEach(function(id) {
+ var el = document.getElementById(id);
+ if (!el) return;
+ el.dataset.autoFilled = 'true';
+ el.addEventListener('input', function() {
+ el.dataset.autoFilled = 'false';
+ });
+ el.addEventListener('change', function() {
+ if (el.value === '') el.dataset.autoFilled = 'true';
+ });
+ });
+}
+
+function autoFillMaterialData(materialName, fieldMap) {
+ var m = lookupMaterial(materialName);
+ if (!m) return;
+ if (m.extruder_temp) {
+ if (fieldMap.minPrintTemp) {
+ var el = document.getElementById(fieldMap.minPrintTemp);
+ if (el && el.dataset.autoFilled !== 'false') {
+ el.value = Math.max(0, m.extruder_temp - 10);
+ el.dataset.autoFilled = 'true';
+ }
+ }
+ if (fieldMap.maxPrintTemp) {
+ var el = document.getElementById(fieldMap.maxPrintTemp);
+ if (el && el.dataset.autoFilled !== 'false') {
+ el.value = m.extruder_temp + 10;
+ el.dataset.autoFilled = 'true';
+ }
+ }
+ }
+ if (m.bed_temp) {
+ if (fieldMap.minBedTemp) {
+ var el = document.getElementById(fieldMap.minBedTemp);
+ if (el && el.dataset.autoFilled !== 'false') {
+ el.value = Math.max(0, m.bed_temp - 5);
+ el.dataset.autoFilled = 'true';
+ }
+ }
+ if (fieldMap.maxBedTemp) {
+ var el = document.getElementById(fieldMap.maxBedTemp);
+ if (el && el.dataset.autoFilled !== 'false') {
+ el.value = m.bed_temp + 5;
+ el.dataset.autoFilled = 'true';
+ }
+ }
+ }
+ if (m.density && fieldMap.density) {
+ var el = document.getElementById(fieldMap.density);
+ if (el && el.dataset.autoFilled !== 'false') {
+ el.value = m.density;
+ el.dataset.autoFilled = 'true';
+ }
+ }
+}
+
/* ---- Tag kind labels ---- */
var TAG_KIND_LABELS = {
'OpenPrintTag': 'OpenPrintTag',
diff --git a/src/TigerTagWriterHTML.h b/src/TigerTagWriterHTML.h
index 7ec8312..ddf87a6 100644
--- a/src/TigerTagWriterHTML.h
+++ b/src/TigerTagWriterHTML.h
@@ -42,69 +42,73 @@ const char TIGERTAG_WRITER_HTML[] PROGMEM = R"rawliteral(
Filament
-
-
+
+
+
+
-
-
+
+
+
+
@@ -310,6 +314,23 @@ const char TIGERTAG_WRITER_HTML[] PROGMEM = R"rawliteral(
syncColorPicker('colorPicker', 'colorHex');
setupAdvancedToggle('advancedToggle', 'advancedBox');
+ // Sync brand name input → hidden brand_id
+ var brandNameEl = document.getElementById('brand_name');
+ var brandIdEl = document.getElementById('brand_id');
+ var brandList = document.getElementById('brand-list');
+ if (brandNameEl && brandIdEl && brandList) {
+ brandNameEl.addEventListener('input', function() {
+ var opts = brandList.querySelectorAll('option');
+ brandIdEl.value = '65535'; // default to Generic
+ for (var i = 0; i < opts.length; i++) {
+ if (opts[i].value === brandNameEl.value) {
+ brandIdEl.value = opts[i].dataset.id;
+ break;
+ }
+ }
+ });
+ }
+
// Fetch TigerTag API data — fallback to hardcoded options on failure
var materialData = {}; // Store full material data for auto-fill on selection
@@ -346,21 +367,44 @@ const char TIGERTAG_WRITER_HTML[] PROGMEM = R"rawliteral(
}
}
+ // Sync material search → hidden material_id
+ var materialSearchEl = document.getElementById('material_search');
+ var materialIdEl = document.getElementById('material_id');
+ var materialListEl = document.getElementById('material-list');
+
+ function syncMaterialId() {
+ var opts = materialListEl.querySelectorAll('option');
+ materialIdEl.value = '38219'; // default PLA
+ for (var i = 0; i < opts.length; i++) {
+ if (opts[i].value === materialSearchEl.value) {
+ materialIdEl.value = opts[i].dataset.id;
+ break;
+ }
+ }
+ }
+
+ trackAutoFill(['nozzle_min','nozzle_max','bed_min','bed_max']);
+
function autoFillFromMaterial() {
- var id = document.getElementById('material_id').value;
+ syncMaterialId();
+ var id = materialIdEl.value;
var m = materialData[id];
if (!m) return;
+ var nMin = document.getElementById('nozzle_min');
+ var nMax = document.getElementById('nozzle_max');
+ var bMin = document.getElementById('bed_min');
+ var bMax = document.getElementById('bed_max');
if (m.extruder_temp) {
- document.getElementById('nozzle_min').value = Math.max(0, m.extruder_temp - 10);
- document.getElementById('nozzle_max').value = m.extruder_temp + 10;
+ if (nMin && nMin.dataset.autoFilled !== 'false') { nMin.value = Math.max(0, m.extruder_temp - 10); nMin.dataset.autoFilled = 'true'; }
+ if (nMax && nMax.dataset.autoFilled !== 'false') { nMax.value = m.extruder_temp + 10; nMax.dataset.autoFilled = 'true'; }
}
if (m.bed_temp) {
- document.getElementById('bed_min').value = Math.max(0, m.bed_temp - 5);
- document.getElementById('bed_max').value = m.bed_temp + 5;
+ if (bMin && bMin.dataset.autoFilled !== 'false') { bMin.value = Math.max(0, m.bed_temp - 5); bMin.dataset.autoFilled = 'true'; }
+ if (bMax && bMax.dataset.autoFilled !== 'false') { bMax.value = m.bed_temp + 5; bMax.dataset.autoFilled = 'true'; }
}
}
- document.getElementById('material_id').addEventListener('change', autoFillFromMaterial);
+ materialSearchEl.addEventListener('input', autoFillFromMaterial);
(async function loadTigerTagAPI() {
try {
@@ -374,10 +418,16 @@ const char TIGERTAG_WRITER_HTML[] PROGMEM = R"rawliteral(
if (materials) {
materials.forEach(function(m) { materialData[m.id] = m; });
materials.sort(function(a, b) { return a.material.localeCompare(b.material); });
- populateSelect('material_id', materials,
- function(m) { return m.id; },
- function(m) { return m.material; }
- );
+ var dl = document.getElementById('material-list');
+ if (dl && materials.length > dl.options.length) {
+ dl.innerHTML = '';
+ materials.forEach(function(m) {
+ var opt = document.createElement('option');
+ opt.value = m.material;
+ opt.dataset.id = m.id;
+ dl.appendChild(opt);
+ });
+ }
}
}
} catch(e) { /* keep hardcoded fallback */ }
@@ -392,10 +442,16 @@ const char TIGERTAG_WRITER_HTML[] PROGMEM = R"rawliteral(
]);
if (brands) {
brands.sort(function(a, b) { return a.name.localeCompare(b.name); });
- populateSelect('brand_id', brands,
- function(b) { return b.id; },
- function(b) { return b.name; }
- );
+ var dl = document.getElementById('brand-list');
+ if (dl && brands.length > dl.options.length) {
+ dl.innerHTML = '';
+ brands.forEach(function(b) {
+ var opt = document.createElement('option');
+ opt.value = b.name;
+ opt.dataset.id = b.id;
+ dl.appendChild(opt);
+ });
+ }
}
}
} catch(e) { /* keep hardcoded fallback */ }
diff --git a/src/UIDRegistrationHTML.h b/src/UIDRegistrationHTML.h
index 9c825f8..126085a 100644
--- a/src/UIDRegistrationHTML.h
+++ b/src/UIDRegistrationHTML.h
@@ -50,31 +50,32 @@ const char UID_REGISTRATION_HTML[] PROGMEM = R"rawliteral(
-
+
+
@@ -210,8 +211,32 @@ const char UID_REGISTRATION_HTML[] PROGMEM = R"rawliteral(
syncColorPicker('colorPicker', 'colorHex');
setupAdvancedToggle('advancedToggle', 'advancedBox');
+ // Auto-fill temps and density from material selection
+ var nfcFieldMap = {
+ minPrintTemp: 'min_print_temp', maxPrintTemp: 'max_print_temp',
+ minBedTemp: 'min_bed_temp', maxBedTemp: 'max_bed_temp',
+ density: 'density'
+ };
+ trackAutoFill(['min_print_temp','max_print_temp','min_bed_temp','max_bed_temp','density']);
+ materialTypeEl.addEventListener('input', function() {
+ autoFillMaterialData(materialTypeEl.value, nfcFieldMap);
+ });
+ loadMaterialDb().then(function(db) {
+ var dl = document.getElementById('material-list');
+ var existing = {};
+ var opts = dl.querySelectorAll('option');
+ for (var i = 0; i < opts.length; i++) existing[opts[i].value.toUpperCase()] = true;
+ Object.keys(db).sort().forEach(function(key) {
+ if (!existing[key]) {
+ var opt = document.createElement('option');
+ opt.value = db[key].material || key;
+ dl.appendChild(opt);
+ }
+ });
+ });
+
function syncMaterialNameFromSelection() {
- var selectedText = materialTypeEl.options[materialTypeEl.selectedIndex].text;
+ var selectedText = materialTypeEl.value || 'PLA';
if (!materialNameEl.value.trim() || materialNameEl.dataset.autoFilled === 'true') {
materialNameEl.value = selectedText.toUpperCase();
materialNameEl.dataset.autoFilled = 'true';
@@ -223,7 +248,7 @@ const char UID_REGISTRATION_HTML[] PROGMEM = R"rawliteral(
resultBox.className = type ? 'result ' + type : 'result';
}
- materialTypeEl.addEventListener('change', syncMaterialNameFromSelection);
+ materialTypeEl.addEventListener('input', syncMaterialNameFromSelection);
materialNameEl.addEventListener('input', function() {
materialNameEl.value = materialNameEl.value.toUpperCase();