Skip to content

Commit fc25eb2

Browse files
authored
Merge pull request #5116 from wled/add-report-version-feature
Add report version feature
2 parents dc5732a + 33411f0 commit fc25eb2

File tree

2 files changed

+192
-1
lines changed

2 files changed

+192
-1
lines changed

wled00/data/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ input[type=range]::-moz-range-thumb {
794794
/* buttons */
795795
.btn {
796796
padding: 8px;
797-
/*margin: 10px 4px;*/
797+
margin: 10px 4px;
798798
width: 230px;
799799
font-size: 19px;
800800
color: var(--c-d);

wled00/data/index.js

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,8 @@ function parseInfo(i) {
693693
// gId("filterVol").classList.add("hide"); hideModes(" ♪"); // hide volume reactive effects
694694
// gId("filterFreq").classList.add("hide"); hideModes(" ♫"); // hide frequency reactive effects
695695
// }
696+
// Check for version upgrades on page load
697+
checkVersionUpgrade(i);
696698
}
697699

698700
//https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
@@ -3304,6 +3306,195 @@ function simplifyUI() {
33043306
gId("btns").style.display = "none";
33053307
}
33063308

3309+
// Version reporting feature
3310+
var versionCheckDone = false;
3311+
3312+
function checkVersionUpgrade(info) {
3313+
// Only check once per page load
3314+
if (versionCheckDone) return;
3315+
versionCheckDone = true;
3316+
3317+
// Suppress feature if in AP mode (no internet connection available)
3318+
if (info.wifi && info.wifi.ap) return;
3319+
3320+
// Fetch version-info.json using existing /edit endpoint
3321+
fetch(getURL('/edit?func=edit&path=/version-info.json'), {
3322+
method: 'get'
3323+
})
3324+
.then(res => {
3325+
if (res.status === 404) {
3326+
// File doesn't exist - first install, show install prompt
3327+
showVersionUpgradePrompt(info, null, info.ver);
3328+
return null;
3329+
}
3330+
if (!res.ok) {
3331+
throw new Error('Failed to fetch version-info.json');
3332+
}
3333+
return res.json();
3334+
})
3335+
.then(versionInfo => {
3336+
if (!versionInfo) return; // 404 case already handled
3337+
3338+
// Check if user opted out
3339+
if (versionInfo.neverAsk) return;
3340+
3341+
// Check if version has changed
3342+
const currentVersion = info.ver;
3343+
const storedVersion = versionInfo.version || '';
3344+
3345+
if (storedVersion && storedVersion !== currentVersion) {
3346+
// Version has changed, show upgrade prompt
3347+
showVersionUpgradePrompt(info, storedVersion, currentVersion);
3348+
} else if (!storedVersion) {
3349+
// Empty version in file, show install prompt
3350+
showVersionUpgradePrompt(info, null, currentVersion);
3351+
}
3352+
})
3353+
.catch(e => {
3354+
console.log('Failed to load version-info.json', e);
3355+
// On error, save current version for next time
3356+
if (info && info.ver) {
3357+
updateVersionInfo(info.ver, false);
3358+
}
3359+
});
3360+
}
3361+
3362+
function showVersionUpgradePrompt(info, oldVersion, newVersion) {
3363+
// Determine if this is an install or upgrade
3364+
const isInstall = !oldVersion;
3365+
3366+
// Create overlay and dialog
3367+
const overlay = d.createElement('div');
3368+
overlay.id = 'versionUpgradeOverlay';
3369+
overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:10000;display:flex;align-items:center;justify-content:center;';
3370+
3371+
const dialog = d.createElement('div');
3372+
dialog.style.cssText = 'background:var(--c-1);border-radius:10px;padding:25px;max-width:500px;margin:20px;box-shadow:0 4px 6px rgba(0,0,0,0.3);';
3373+
3374+
// Build contextual message based on install vs upgrade
3375+
const title = isInstall
3376+
? '🎉 Thank you for installing WLED!'
3377+
: '🎉 WLED Upgrade Detected!';
3378+
3379+
const description = isInstall
3380+
? `You are now running WLED <strong style="text-wrap: nowrap">${newVersion}</strong>.`
3381+
: `Your WLED has been upgraded from <strong style="text-wrap: nowrap">${oldVersion}</strong> to <strong style="text-wrap: nowrap">${newVersion}</strong>.`;
3382+
3383+
const question = 'Help make WLED better with a one-time hardware report? It includes only device details like chip type, LED count, etc. — never personal data or your activities.'
3384+
3385+
dialog.innerHTML = `
3386+
<h2 style="margin-top:0;color:var(--c-f);">${title}</h2>
3387+
<p style="color:var(--c-f);">${description}</p>
3388+
<p style="color:var(--c-f);">${question}</p>
3389+
<p style="color:var(--c-f);font-size:0.9em;">
3390+
<a href="https://kno.wled.ge/about/privacy-policy/" target="_blank" style="color:var(--c-6);">Learn more about what data is collected and why</a>
3391+
</p>
3392+
<div style="margin-top:20px;">
3393+
<button id="versionReportYes" class="btn">Yes</button>
3394+
<button id="versionReportNo" class="btn">Not Now</button>
3395+
<button id="versionReportNever" class="btn">Never Ask</button>
3396+
</div>
3397+
`;
3398+
3399+
overlay.appendChild(dialog);
3400+
d.body.appendChild(overlay);
3401+
3402+
// Add event listeners
3403+
gId('versionReportYes').addEventListener('click', () => {
3404+
reportUpgradeEvent(info, oldVersion);
3405+
d.body.removeChild(overlay);
3406+
});
3407+
3408+
gId('versionReportNo').addEventListener('click', () => {
3409+
// Don't update version, will ask again on next load
3410+
d.body.removeChild(overlay);
3411+
});
3412+
3413+
gId('versionReportNever').addEventListener('click', () => {
3414+
updateVersionInfo(newVersion, true);
3415+
d.body.removeChild(overlay);
3416+
showToast('You will not be asked again.');
3417+
});
3418+
}
3419+
3420+
function reportUpgradeEvent(info, oldVersion) {
3421+
showToast('Reporting upgrade...');
3422+
3423+
// Fetch fresh data from /json/info endpoint as requested
3424+
fetch(getURL('/json/info'), {
3425+
method: 'get'
3426+
})
3427+
.then(res => res.json())
3428+
.then(infoData => {
3429+
// Map to UpgradeEventRequest structure per OpenAPI spec
3430+
// Required fields: deviceId, version, previousVersion, releaseName, chip, ledCount, isMatrix, bootloaderSHA256
3431+
const upgradeData = {
3432+
deviceId: infoData.deviceId, // Use anonymous unique device ID
3433+
version: infoData.ver || '', // Current version string
3434+
previousVersion: oldVersion || '', // Previous version from version-info.json
3435+
releaseName: infoData.release || '', // Release name (e.g., "WLED 0.15.0")
3436+
chip: infoData.arch || '', // Chip architecture (esp32, esp8266, etc)
3437+
ledCount: infoData.leds ? infoData.leds.count : 0, // Number of LEDs
3438+
isMatrix: !!(infoData.leds && infoData.leds.matrix), // Whether it's a 2D matrix setup
3439+
bootloaderSHA256: infoData.bootloaderSHA256 || '', // Bootloader SHA256 hash
3440+
brand: infoData.brand, // Device brand (always present)
3441+
product: infoData.product, // Product name (always present)
3442+
flashSize: infoData.flash // Flash size (always present)
3443+
};
3444+
3445+
// Add optional fields if available
3446+
if (infoData.psram !== undefined) upgradeData.psramSize = infoData.psram;
3447+
// Note: partitionSizes not currently available in /json/info endpoint
3448+
3449+
// Make AJAX call to postUpgradeEvent API
3450+
return fetch('https://usage.wled.me/api/usage/upgrade', {
3451+
method: 'POST',
3452+
headers: {
3453+
'Content-Type': 'application/json'
3454+
},
3455+
body: JSON.stringify(upgradeData)
3456+
});
3457+
})
3458+
.then(res => {
3459+
if (res.ok) {
3460+
showToast('Thank you for reporting!');
3461+
updateVersionInfo(info.ver, false);
3462+
} else {
3463+
showToast('Report failed. Please try again later.', true);
3464+
// Do NOT update version info on failure - user will be prompted again
3465+
}
3466+
})
3467+
.catch(e => {
3468+
console.log('Failed to report upgrade', e);
3469+
showToast('Report failed. Please try again later.', true);
3470+
// Do NOT update version info on error - user will be prompted again
3471+
});
3472+
}
3473+
3474+
function updateVersionInfo(version, neverAsk) {
3475+
const versionInfo = {
3476+
version: version,
3477+
neverAsk: neverAsk
3478+
};
3479+
3480+
// Create a Blob with JSON content and use /upload endpoint
3481+
const blob = new Blob([JSON.stringify(versionInfo)], {type: 'application/json'});
3482+
const formData = new FormData();
3483+
formData.append('data', blob, 'version-info.json');
3484+
3485+
fetch(getURL('/upload'), {
3486+
method: 'POST',
3487+
body: formData
3488+
})
3489+
.then(res => res.text())
3490+
.then(data => {
3491+
console.log('Version info updated', data);
3492+
})
3493+
.catch(e => {
3494+
console.log('Failed to update version-info.json', e);
3495+
});
3496+
}
3497+
33073498
size();
33083499
_C.style.setProperty('--n', N);
33093500

0 commit comments

Comments
 (0)