feat: add firmware versioning and device_type support (NebuleAir/ModuleAir)
- Add VERSION file (1.0.0) and changelog.json for firmware tracking - Add device_type config param (nebuleair_pro default, backward compatible via INSERT OR IGNORE) - Add device_type select in admin.html Protected Settings - Add version badge and changelog modal in Updates section - Add get_firmware_version and get_changelog PHP endpoints - Display firmware version in update_firmware.sh after git pull Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
21
changelog.json
Normal file
21
changelog.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"versions": [
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"date": "2026-02-11",
|
||||||
|
"changes": {
|
||||||
|
"features": [
|
||||||
|
"Support multi-device : NebuleAir Pro / ModuleAir Pro",
|
||||||
|
"Systeme de versioning firmware",
|
||||||
|
"Changelog viewer dans l'interface web"
|
||||||
|
],
|
||||||
|
"improvements": [],
|
||||||
|
"fixes": [],
|
||||||
|
"compatibility": [
|
||||||
|
"Les capteurs existants sont automatiquement configures en 'nebuleair_pro'"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notes": "Premiere version tracee. Les capteurs anterieurs recevront device_type=nebuleair_pro par defaut lors de la mise a jour."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
161
html/admin.html
161
html/admin.html
@@ -174,6 +174,13 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="device_type" class="form-label">Device Type</label>
|
||||||
|
<select class="form-select protected-checkbox" id="device_type" onchange="update_config_sqlite('device_type', this.value)" disabled>
|
||||||
|
<option value="nebuleair_pro">NebuleAir Pro</option>
|
||||||
|
<option value="moduleair_pro">ModuleAir Pro</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input-group mb-3" id="sondes_envea_div"></div>
|
<div class="input-group mb-3" id="sondes_envea_div"></div>
|
||||||
|
|
||||||
@@ -219,7 +226,11 @@
|
|||||||
<!-- UPDATE-->
|
<!-- UPDATE-->
|
||||||
|
|
||||||
<div class="col-lg-4 col-12">
|
<div class="col-lg-4 col-12">
|
||||||
<h3 class="mt-4">Updates</h3>
|
<div class="d-flex align-items-center mt-4 mb-2">
|
||||||
|
<h3 class="mb-0 me-2">Updates</h3>
|
||||||
|
<span id="firmwareVersionBadge" class="badge bg-secondary">Version...</span>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-info ms-2" onclick="showChangelogModal()">Changelog</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary" onclick="updateFirmware()" id="updateBtn">
|
<button type="submit" class="btn btn-primary" onclick="updateFirmware()" id="updateBtn">
|
||||||
<span id="updateBtnText">Update firmware</span>
|
<span id="updateBtnText">Update firmware</span>
|
||||||
@@ -334,6 +345,28 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Changelog Modal -->
|
||||||
|
<div class="modal fade" id="changelogModal" tabindex="-1" aria-labelledby="changelogModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="changelogModalLabel">Changelog</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="changelogModalBody">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -437,6 +470,12 @@ window.onload = function() {
|
|||||||
checkbox_aircarto.checked = response["send_aircarto"];
|
checkbox_aircarto.checked = response["send_aircarto"];
|
||||||
checkbox_miotiq.checked = response["send_miotiq"];
|
checkbox_miotiq.checked = response["send_miotiq"];
|
||||||
|
|
||||||
|
// Set device type
|
||||||
|
const device_type_select = document.getElementById("device_type");
|
||||||
|
if (response["device_type"]) {
|
||||||
|
device_type_select.value = response["device_type"];
|
||||||
|
}
|
||||||
|
|
||||||
// Set CPU power mode
|
// Set CPU power mode
|
||||||
const cpu_power_mode_select = document.getElementById("cpu_power_mode");
|
const cpu_power_mode_select = document.getElementById("cpu_power_mode");
|
||||||
if (response["cpu_power_mode"]) {
|
if (response["cpu_power_mode"]) {
|
||||||
@@ -556,6 +595,9 @@ window.onload = function() {
|
|||||||
// Load services on page load
|
// Load services on page load
|
||||||
refreshServices();
|
refreshServices();
|
||||||
|
|
||||||
|
// Load firmware version
|
||||||
|
loadFirmwareVersion();
|
||||||
|
|
||||||
} //end window.onload
|
} //end window.onload
|
||||||
|
|
||||||
|
|
||||||
@@ -1644,6 +1686,123 @@ function toggleProtectedSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
__ __ _ _
|
||||||
|
\ \ / /__ _ __ ___(_) ___ _ __ (_)_ __ __ _
|
||||||
|
\ \ / / _ \ '__/ __| |/ _ \| '_ \| | '_ \ / _` |
|
||||||
|
\ V / __/ | \__ \ | (_) | | | | | | | | (_| |
|
||||||
|
\_/ \___|_| |___/_|\___/|_| |_|_|_| |_|\__, |
|
||||||
|
|___/
|
||||||
|
*/
|
||||||
|
|
||||||
|
function loadFirmwareVersion() {
|
||||||
|
$.ajax({
|
||||||
|
url: 'launcher.php?type=get_firmware_version',
|
||||||
|
dataType: 'json',
|
||||||
|
method: 'GET',
|
||||||
|
cache: false,
|
||||||
|
success: function(response) {
|
||||||
|
const badge = document.getElementById('firmwareVersionBadge');
|
||||||
|
if (response.success) {
|
||||||
|
badge.textContent = 'v' + response.version;
|
||||||
|
badge.className = 'badge bg-primary';
|
||||||
|
} else {
|
||||||
|
badge.textContent = 'Version unknown';
|
||||||
|
badge.className = 'badge bg-secondary';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
const badge = document.getElementById('firmwareVersionBadge');
|
||||||
|
badge.textContent = 'Version unknown';
|
||||||
|
badge.className = 'badge bg-secondary';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showChangelogModal() {
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('changelogModal'));
|
||||||
|
modal.show();
|
||||||
|
|
||||||
|
// Load changelog data
|
||||||
|
$.ajax({
|
||||||
|
url: 'launcher.php?type=get_changelog',
|
||||||
|
dataType: 'json',
|
||||||
|
method: 'GET',
|
||||||
|
cache: false,
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success && response.changelog) {
|
||||||
|
displayChangelog(response.changelog);
|
||||||
|
} else {
|
||||||
|
document.getElementById('changelogModalBody').innerHTML =
|
||||||
|
'<div class="alert alert-warning">Could not load changelog.</div>';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
document.getElementById('changelogModalBody').innerHTML =
|
||||||
|
'<div class="alert alert-danger">Failed to load changelog.</div>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayChangelog(data) {
|
||||||
|
const container = document.getElementById('changelogModalBody');
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
data.versions.forEach(function(version) {
|
||||||
|
html += `<div class="card mb-3">`;
|
||||||
|
html += `<div class="card-header d-flex justify-content-between align-items-center">`;
|
||||||
|
html += `<h5 class="mb-0">v${version.version}</h5>`;
|
||||||
|
html += `<span class="text-muted">${version.date}</span>`;
|
||||||
|
html += `</div>`;
|
||||||
|
html += `<div class="card-body">`;
|
||||||
|
|
||||||
|
// Features
|
||||||
|
if (version.changes.features && version.changes.features.length > 0) {
|
||||||
|
html += `<h6 class="text-success">Features</h6><ul>`;
|
||||||
|
version.changes.features.forEach(function(f) {
|
||||||
|
html += `<li>${f}</li>`;
|
||||||
|
});
|
||||||
|
html += `</ul>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Improvements
|
||||||
|
if (version.changes.improvements && version.changes.improvements.length > 0) {
|
||||||
|
html += `<h6 class="text-info">Improvements</h6><ul>`;
|
||||||
|
version.changes.improvements.forEach(function(i) {
|
||||||
|
html += `<li>${i}</li>`;
|
||||||
|
});
|
||||||
|
html += `</ul>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fixes
|
||||||
|
if (version.changes.fixes && version.changes.fixes.length > 0) {
|
||||||
|
html += `<h6 class="text-danger">Fixes</h6><ul>`;
|
||||||
|
version.changes.fixes.forEach(function(f) {
|
||||||
|
html += `<li>${f}</li>`;
|
||||||
|
});
|
||||||
|
html += `</ul>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compatibility
|
||||||
|
if (version.changes.compatibility && version.changes.compatibility.length > 0) {
|
||||||
|
html += `<div class="alert alert-warning mt-2 mb-0"><strong>Compatibility:</strong><ul class="mb-0">`;
|
||||||
|
version.changes.compatibility.forEach(function(c) {
|
||||||
|
html += `<li>${c}</li>`;
|
||||||
|
});
|
||||||
|
html += `</ul></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notes
|
||||||
|
if (version.notes) {
|
||||||
|
html += `<p class="text-muted mt-2 mb-0"><em>${version.notes}</em></p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += `</div></div>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
container.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1515,6 +1515,47 @@ if ($type == "detect_envea_device") {
|
|||||||
|___/
|
|___/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Get firmware version from VERSION file
|
||||||
|
if ($type == "get_firmware_version") {
|
||||||
|
$versionFile = '/var/www/nebuleair_pro_4g/VERSION';
|
||||||
|
if (file_exists($versionFile)) {
|
||||||
|
$version = trim(file_get_contents($versionFile));
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'version' => $version
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'version' => 'unknown'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get changelog from changelog.json
|
||||||
|
if ($type == "get_changelog") {
|
||||||
|
$changelogFile = '/var/www/nebuleair_pro_4g/changelog.json';
|
||||||
|
if (file_exists($changelogFile)) {
|
||||||
|
$changelog = json_decode(file_get_contents($changelogFile), true);
|
||||||
|
if ($changelog !== null) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'changelog' => $changelog
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Invalid changelog format'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Changelog file not found'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get current CPU power mode
|
// Get current CPU power mode
|
||||||
if ($type == "get_cpu_power_mode") {
|
if ($type == "get_cpu_power_mode") {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ config_entries = [
|
|||||||
("MPPT", "0", "bool"),
|
("MPPT", "0", "bool"),
|
||||||
("NOISE", "0", "bool"),
|
("NOISE", "0", "bool"),
|
||||||
("modem_version", "XXX", "str"),
|
("modem_version", "XXX", "str"),
|
||||||
|
("device_type", "nebuleair_pro", "str"),
|
||||||
("language", "fr", "str"),
|
("language", "fr", "str"),
|
||||||
("wifi_power_saving", "0", "bool"),
|
("wifi_power_saving", "0", "bool"),
|
||||||
("cpu_power_mode", "normal", "str")
|
("cpu_power_mode", "normal", "str")
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ fi
|
|||||||
git pull origin $(git branch --show-current)
|
git pull origin $(git branch --show-current)
|
||||||
check_status "Git pull"
|
check_status "Git pull"
|
||||||
|
|
||||||
|
# Display firmware version
|
||||||
|
if [ -f "/var/www/nebuleair_pro_4g/VERSION" ]; then
|
||||||
|
print_status "Firmware version: $(cat /var/www/nebuleair_pro_4g/VERSION)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Step 2: Update database configuration
|
# Step 2: Update database configuration
|
||||||
print_status ""
|
print_status ""
|
||||||
print_status "Step 2: Updating database configuration..."
|
print_status "Step 2: Updating database configuration..."
|
||||||
|
|||||||
Reference in New Issue
Block a user