Add simulator architecture separation and warning system
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="warningBanner" class="warning-banner hidden"></div>
|
||||
|
||||
<main>
|
||||
<header>
|
||||
<h1>Xterra Dashboard</h1>
|
||||
@@ -30,7 +32,11 @@
|
||||
<div class="grid">
|
||||
<div class="card"><h2>Fridge A</h2><p><span id="dashFridge1">--</span>°F</p></div>
|
||||
<div class="card"><h2>Fridge B</h2><p><span id="dashFridge2">--</span>°F</p></div>
|
||||
<div class="card"><h2>Rear Seat</h2><p><span id="dashRear">--</span>°F</p></div>
|
||||
<div class="card kid-card">
|
||||
<h2>👧 Kid Area</h2>
|
||||
<p><span id="dashRear">--</span>°F</p>
|
||||
<small id="kidStatus">Normal</small>
|
||||
</div>
|
||||
<div class="card"><h2>Outside</h2><p><span id="dashOutside">--</span>°F</p></div>
|
||||
</div>
|
||||
|
||||
@@ -74,8 +80,10 @@
|
||||
<div>RS-485 <strong id="sysRs485">--</strong></div>
|
||||
<div>WiFi <strong id="sysWifi">--</strong></div>
|
||||
<div>WiFi Override <strong id="sysWifiOverride">--</strong></div>
|
||||
<div>Ignition <strong id="sysIgnition">--</strong></div>
|
||||
</div>
|
||||
<button onclick="enableWifi()">Enable WiFi 10 min</button>
|
||||
<button onclick="toggleIgnition()">Toggle Ignition</button>
|
||||
|
||||
<h2>Alarm Settings</h2>
|
||||
<div class="settings-list">
|
||||
@@ -143,6 +151,7 @@ function onOff(value) {
|
||||
|
||||
function checkAlarms(data) {
|
||||
const alarms = [];
|
||||
const warnings = [];
|
||||
|
||||
if (data.temps.rear_seat >= alarmConfig.rear_seat_critical) {
|
||||
alarms.push({
|
||||
@@ -151,35 +160,28 @@ function checkAlarms(data) {
|
||||
message: `${data.temps.rear_seat}°F detected near car seat area`
|
||||
});
|
||||
} else if (data.temps.rear_seat >= alarmConfig.rear_seat_warning) {
|
||||
alarms.push({
|
||||
key: 'rear_seat_warning',
|
||||
title: 'REAR SEAT TEMP HIGH',
|
||||
message: `${data.temps.rear_seat}°F detected near car seat area`
|
||||
});
|
||||
warnings.push(`Rear seat temp high: ${data.temps.rear_seat}°F`);
|
||||
}
|
||||
|
||||
if (data.temps.fridge_zone_1 >= alarmConfig.fridge_zone_1_warm) {
|
||||
alarms.push({
|
||||
key: 'fridge_zone_1_warm',
|
||||
title: 'FRIDGE ZONE 1 WARM',
|
||||
message: `${data.temps.fridge_zone_1}°F`
|
||||
});
|
||||
warnings.push(`Fridge Zone 1 warm: ${data.temps.fridge_zone_1}°F`);
|
||||
}
|
||||
|
||||
if (data.temps.fridge_zone_2 >= alarmConfig.fridge_zone_2_warm) {
|
||||
alarms.push({
|
||||
key: 'fridge_zone_2_warm',
|
||||
title: 'FRIDGE ZONE 2 WARM',
|
||||
message: `${data.temps.fridge_zone_2}°F`
|
||||
});
|
||||
warnings.push(`Fridge Zone 2 warm: ${data.temps.fridge_zone_2}°F`);
|
||||
}
|
||||
|
||||
if (data.battery.soc <= alarmConfig.battery_low) {
|
||||
alarms.push({
|
||||
key: 'battery_low',
|
||||
title: 'BATTERY LOW',
|
||||
message: `${data.battery.soc}% remaining`
|
||||
});
|
||||
warnings.push(`Battery low: ${data.battery.soc}%`);
|
||||
}
|
||||
|
||||
const banner = document.getElementById('warningBanner');
|
||||
|
||||
if (warnings.length > 0) {
|
||||
banner.textContent = `⚠ ${warnings.join(' | ')}`;
|
||||
banner.classList.remove('hidden');
|
||||
} else {
|
||||
banner.classList.add('hidden');
|
||||
}
|
||||
|
||||
const unacked = alarms.find(alarm => !acknowledgedAlarms.has(alarm.key));
|
||||
@@ -255,6 +257,15 @@ async function loadStatus() {
|
||||
document.getElementById('dashFridge1').textContent = data.temps.fridge_zone_1;
|
||||
document.getElementById('dashFridge2').textContent = data.temps.fridge_zone_2;
|
||||
document.getElementById('dashRear').textContent = data.temps.rear_seat;
|
||||
|
||||
const kidStatus = document.getElementById('kidStatus');
|
||||
if (data.temps.rear_seat >= alarmConfig.rear_seat_critical) {
|
||||
kidStatus.textContent = 'CRITICAL';
|
||||
} else if (data.temps.rear_seat >= alarmConfig.rear_seat_warning) {
|
||||
kidStatus.textContent = 'Warning';
|
||||
} else {
|
||||
kidStatus.textContent = 'Normal';
|
||||
}
|
||||
document.getElementById('dashOutside').textContent = data.temps.outside;
|
||||
document.getElementById('dashStarlink').textContent = `Starlink: ${onOff(data.relays.starlink)}`;
|
||||
document.getElementById('dashFridgeRelay').textContent = `Fridge: ${onOff(data.relays.fridge)}`;
|
||||
@@ -277,6 +288,7 @@ async function loadStatus() {
|
||||
document.getElementById('sysRs485').textContent = data.network.rs485_connected ? 'Connected' : 'Disconnected';
|
||||
document.getElementById('sysWifi').textContent = data.network.wifi_enabled ? 'Enabled' : 'Disabled';
|
||||
document.getElementById('sysWifiOverride').textContent = data.network.wifi_override_active ? 'Active' : 'Inactive';
|
||||
document.getElementById('sysIgnition').textContent = data.vehicle.ignition_on ? 'ON' : 'OFF';
|
||||
|
||||
checkAlarms(data);
|
||||
}
|
||||
@@ -291,6 +303,16 @@ async function toggleRelay(name) {
|
||||
loadStatus();
|
||||
}
|
||||
|
||||
async function toggleIgnition() {
|
||||
await fetch('/vehicle/ignition', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({})
|
||||
});
|
||||
|
||||
loadStatus();
|
||||
}
|
||||
|
||||
async function enableWifi() {
|
||||
await fetch('/network/wifi', {
|
||||
method: 'POST',
|
||||
|
||||
Reference in New Issue
Block a user