Add comms simulation and config screen
This commit is contained in:
+37
-10
@@ -19,17 +19,17 @@ def status():
|
|||||||
|
|
||||||
@app.route("/battery")
|
@app.route("/battery")
|
||||||
def battery():
|
def battery():
|
||||||
return jsonify(pico.get_status()["battery"])
|
return jsonify(pico.get_status().get("battery", {}))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/temps")
|
@app.route("/temps")
|
||||||
def temps():
|
def temps():
|
||||||
return jsonify(pico.get_status()["temps"])
|
return jsonify(pico.get_status().get("temps", {}))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/relays")
|
@app.route("/relays")
|
||||||
def relays():
|
def relays():
|
||||||
return jsonify(pico.get_status()["relays"])
|
return jsonify(pico.get_status().get("relays", {}))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/relay/<name>", methods=["POST"])
|
@app.route("/relay/<name>", methods=["POST"])
|
||||||
@@ -37,38 +37,65 @@ def set_relay(name):
|
|||||||
data = request.get_json(force=True)
|
data = request.get_json(force=True)
|
||||||
response = pico.set_relay(name, data.get("state", False))
|
response = pico.set_relay(name, data.get("state", False))
|
||||||
|
|
||||||
if not response["success"]:
|
if not response.get("success"):
|
||||||
return jsonify(response), 404
|
return jsonify(response), 503
|
||||||
|
|
||||||
return jsonify(response)
|
return jsonify(response)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/network")
|
@app.route("/network")
|
||||||
def network():
|
def network():
|
||||||
return jsonify(pico.get_status()["network"])
|
return jsonify(pico.get_status().get("network", {}))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/network/wifi", methods=["POST"])
|
@app.route("/network/wifi", methods=["POST"])
|
||||||
def enable_wifi():
|
def enable_wifi():
|
||||||
data = request.get_json(force=True)
|
data = request.get_json(force=True)
|
||||||
minutes = int(data.get("minutes", 10))
|
minutes = int(data.get("minutes", 10))
|
||||||
return jsonify(pico.enable_wifi(minutes))
|
response = pico.enable_wifi(minutes)
|
||||||
|
|
||||||
|
if not response.get("success"):
|
||||||
|
return jsonify(response), 503
|
||||||
|
|
||||||
|
return jsonify(response)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/vehicle/ignition", methods=["POST"])
|
@app.route("/vehicle/ignition", methods=["POST"])
|
||||||
def toggle_ignition():
|
def toggle_ignition():
|
||||||
return jsonify(pico.toggle_ignition())
|
response = pico.toggle_ignition()
|
||||||
|
|
||||||
|
if not response.get("success"):
|
||||||
|
return jsonify(response), 503
|
||||||
|
|
||||||
|
return jsonify(response)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/sensor/<name>/fault", methods=["POST"])
|
@app.route("/sensor/<name>/fault", methods=["POST"])
|
||||||
def toggle_sensor_fault(name):
|
def toggle_sensor_fault(name):
|
||||||
response = pico.toggle_sensor_fault(name)
|
response = pico.toggle_sensor_fault(name)
|
||||||
|
|
||||||
if not response["success"]:
|
if not response.get("success"):
|
||||||
return jsonify(response), 404
|
return jsonify(response), 503
|
||||||
|
|
||||||
return jsonify(response)
|
return jsonify(response)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/comms")
|
||||||
|
def comms():
|
||||||
|
return jsonify(pico.get_comms())
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/comms/rs485/disconnect", methods=["POST"])
|
||||||
|
def disconnect_rs485():
|
||||||
|
pico.disconnect_rs485()
|
||||||
|
return jsonify({"success": True, "rs485_connected": False})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/comms/rs485/restore", methods=["POST"])
|
||||||
|
def restore_rs485():
|
||||||
|
pico.restore_rs485()
|
||||||
|
return jsonify({"success": True, "rs485_connected": True})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||||
|
|||||||
+69
-7
@@ -1,5 +1,6 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
from protocol import (
|
from protocol import (
|
||||||
MessageType,
|
|
||||||
status_request,
|
status_request,
|
||||||
set_relay_request,
|
set_relay_request,
|
||||||
enable_wifi_request,
|
enable_wifi_request,
|
||||||
@@ -13,20 +14,65 @@ class PicoSimulator:
|
|||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.last_status = None
|
self.last_status = None
|
||||||
self.primary_link = "rs485"
|
self.primary_link = "rs485"
|
||||||
|
self.rs485_connected = True
|
||||||
self.backup_link_available = True
|
self.backup_link_available = True
|
||||||
|
self.messages_sent = 0
|
||||||
|
self.messages_received = 0
|
||||||
|
self.last_message_time = None
|
||||||
|
|
||||||
def send_message(self, message):
|
def send_message(self, message):
|
||||||
"""
|
self.messages_sent += 1
|
||||||
This simulates the Pico sending a protocol message to the ESP32.
|
|
||||||
|
|
||||||
Later, this is where RS-485 serial communication will replace
|
if not self.rs485_connected:
|
||||||
the direct Python method call.
|
return {
|
||||||
"""
|
"type": "error",
|
||||||
return self.controller.handle_message(message)
|
"success": False,
|
||||||
|
"error": "RS-485 disconnected"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.controller.handle_message(message)
|
||||||
|
self.messages_received += 1
|
||||||
|
self.last_message_time = int(time.time())
|
||||||
|
return response
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
response = self.send_message(status_request())
|
response = self.send_message(status_request())
|
||||||
|
|
||||||
|
if response.get("type") == "error":
|
||||||
|
if self.last_status:
|
||||||
|
self.last_status["network"]["rs485_connected"] = False
|
||||||
|
self.last_status["network"]["communication_lost"] = True
|
||||||
|
self.last_status["network"]["messages_sent"] = self.messages_sent
|
||||||
|
self.last_status["network"]["messages_received"] = self.messages_received
|
||||||
|
self.last_status["network"]["last_message_time"] = self.last_message_time
|
||||||
|
return self.last_status
|
||||||
|
|
||||||
|
return {
|
||||||
|
"timestamp": int(time.time()),
|
||||||
|
"vehicle": {"ignition_on": True},
|
||||||
|
"battery": {},
|
||||||
|
"temps": {},
|
||||||
|
"sensor_health": {},
|
||||||
|
"relays": {},
|
||||||
|
"network": {
|
||||||
|
"rs485_connected": False,
|
||||||
|
"communication_lost": True,
|
||||||
|
"wifi_enabled": False,
|
||||||
|
"wifi_override_active": False,
|
||||||
|
"starlink_enabled": False,
|
||||||
|
"messages_sent": self.messages_sent,
|
||||||
|
"messages_received": self.messages_received,
|
||||||
|
"last_message_time": self.last_message_time
|
||||||
|
},
|
||||||
|
"config": {}
|
||||||
|
}
|
||||||
|
|
||||||
self.last_status = response["data"]
|
self.last_status = response["data"]
|
||||||
|
self.last_status["network"]["rs485_connected"] = self.rs485_connected
|
||||||
|
self.last_status["network"]["communication_lost"] = False
|
||||||
|
self.last_status["network"]["messages_sent"] = self.messages_sent
|
||||||
|
self.last_status["network"]["messages_received"] = self.messages_received
|
||||||
|
self.last_status["network"]["last_message_time"] = self.last_message_time
|
||||||
return self.last_status
|
return self.last_status
|
||||||
|
|
||||||
def set_relay(self, relay, state):
|
def set_relay(self, relay, state):
|
||||||
@@ -40,3 +86,19 @@ class PicoSimulator:
|
|||||||
|
|
||||||
def toggle_sensor_fault(self, sensor):
|
def toggle_sensor_fault(self, sensor):
|
||||||
return self.send_message(toggle_sensor_fault_request(sensor))
|
return self.send_message(toggle_sensor_fault_request(sensor))
|
||||||
|
|
||||||
|
def disconnect_rs485(self):
|
||||||
|
self.rs485_connected = False
|
||||||
|
|
||||||
|
def restore_rs485(self):
|
||||||
|
self.rs485_connected = True
|
||||||
|
|
||||||
|
def get_comms(self):
|
||||||
|
return {
|
||||||
|
"primary": self.primary_link,
|
||||||
|
"backup_available": self.backup_link_available,
|
||||||
|
"rs485_connected": self.rs485_connected,
|
||||||
|
"messages_sent": self.messages_sent,
|
||||||
|
"messages_received": self.messages_received,
|
||||||
|
"last_message_time": self.last_message_time
|
||||||
|
}
|
||||||
|
|||||||
@@ -308,3 +308,19 @@ nav button {
|
|||||||
color: #ffaaaa;
|
color: #ffaaaa;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comms-lost header {
|
||||||
|
border: 2px solid #b00020;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 10px;
|
||||||
|
background: #260808;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list input[type="checkbox"] {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#config .settings-list {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,6 +90,18 @@
|
|||||||
<button onclick="enableWifi()">Enable WiFi 10 min</button>
|
<button onclick="enableWifi()">Enable WiFi 10 min</button>
|
||||||
<button onclick="toggleIgnition()">Toggle Ignition</button>
|
<button onclick="toggleIgnition()">Toggle Ignition</button>
|
||||||
|
|
||||||
|
<h2>Communication</h2>
|
||||||
|
<div class="detail-list">
|
||||||
|
<div>Primary Link <strong id="commsPrimary">--</strong></div>
|
||||||
|
<div>Backup Available <strong id="commsBackup">--</strong></div>
|
||||||
|
<div>Messages Sent <strong id="commsSent">--</strong></div>
|
||||||
|
<div>Messages Received <strong id="commsReceived">--</strong></div>
|
||||||
|
<div>Last Message <strong id="commsLast">--</strong></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick="disconnectRs485()">Disconnect RS-485</button>
|
||||||
|
<button onclick="restoreRs485()">Restore RS-485</button>
|
||||||
|
|
||||||
<h2>Sensor Fault Simulation</h2>
|
<h2>Sensor Fault Simulation</h2>
|
||||||
<div class="settings-list">
|
<div class="settings-list">
|
||||||
<button onclick="toggleSensorFault('fridge_zone_1')">Toggle Fridge Zone 1 Fault</button>
|
<button onclick="toggleSensorFault('fridge_zone_1')">Toggle Fridge Zone 1 Fault</button>
|
||||||
@@ -98,35 +110,6 @@
|
|||||||
<button onclick="toggleSensorFault('outside')">Toggle Outside Fault</button>
|
<button onclick="toggleSensorFault('outside')">Toggle Outside Fault</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Alarm Settings</h2>
|
|
||||||
<div class="settings-list">
|
|
||||||
<label>
|
|
||||||
Rear Seat Warning °F
|
|
||||||
<input id="rearWarningInput" type="number" value="85">
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
Rear Seat Critical °F
|
|
||||||
<input id="rearCriticalInput" type="number" value="95">
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
Fridge Zone 1 Warm °F
|
|
||||||
<input id="fridgeZone1WarmInput" type="number" value="45">
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
Fridge Zone 2 Warm °F
|
|
||||||
<input id="fridgeZone2WarmInput" type="number" value="15">
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
Battery Low %
|
|
||||||
<input id="batteryLowInput" type="number" value="20">
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button onclick="saveAlarmSettings()">Save Alarm Settings</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -136,6 +119,7 @@
|
|||||||
<button onclick="showScreen('temps')">Temps</button>
|
<button onclick="showScreen('temps')">Temps</button>
|
||||||
<button onclick="showScreen('power')">Power</button>
|
<button onclick="showScreen('power')">Power</button>
|
||||||
<button onclick="showScreen('system')">System</button>
|
<button onclick="showScreen('system')">System</button>
|
||||||
|
<button onclick="showScreen('config')">Config</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -149,7 +133,8 @@ let alarmConfig = {
|
|||||||
rear_seat_critical: 95,
|
rear_seat_critical: 95,
|
||||||
fridge_zone_1_warm: 45,
|
fridge_zone_1_warm: 45,
|
||||||
fridge_zone_2_warm: 15,
|
fridge_zone_2_warm: 15,
|
||||||
battery_low: 20
|
battery_low: 20,
|
||||||
|
audible_alarms: true
|
||||||
};
|
};
|
||||||
|
|
||||||
function showScreen(id) {
|
function showScreen(id) {
|
||||||
@@ -186,7 +171,11 @@ function checkAlarms(data) {
|
|||||||
const alarms = [];
|
const alarms = [];
|
||||||
const warnings = [];
|
const warnings = [];
|
||||||
|
|
||||||
for (const [name, healthy] of Object.entries(data.sensor_health)) {
|
if (data.network.communication_lost) {
|
||||||
|
warnings.push('Communication lost: RS-485 disconnected');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [name, healthy] of Object.entries(data.sensor_health || {})) {
|
||||||
if (!healthy) {
|
if (!healthy) {
|
||||||
warnings.push(`Sensor fault: ${name.replaceAll('_', ' ')}`);
|
warnings.push(`Sensor fault: ${name.replaceAll('_', ' ')}`);
|
||||||
}
|
}
|
||||||
@@ -232,6 +221,10 @@ function checkAlarms(data) {
|
|||||||
|
|
||||||
function showAlarm(alarm) {
|
function showAlarm(alarm) {
|
||||||
activeAlarmKey = alarm.key;
|
activeAlarmKey = alarm.key;
|
||||||
|
|
||||||
|
if (alarmConfig.audible_alarms) {
|
||||||
|
console.log('BEEP BEEP BEEP - alarm sound placeholder');
|
||||||
|
}
|
||||||
document.getElementById('alarmTitle').textContent = alarm.title;
|
document.getElementById('alarmTitle').textContent = alarm.title;
|
||||||
document.getElementById('alarmMessage').textContent = alarm.message;
|
document.getElementById('alarmMessage').textContent = alarm.message;
|
||||||
document.getElementById('alarmOverlay').classList.remove('hidden');
|
document.getElementById('alarmOverlay').classList.remove('hidden');
|
||||||
@@ -265,6 +258,7 @@ function loadAlarmSettings() {
|
|||||||
document.getElementById('fridgeZone1WarmInput').value = alarmConfig.fridge_zone_1_warm;
|
document.getElementById('fridgeZone1WarmInput').value = alarmConfig.fridge_zone_1_warm;
|
||||||
document.getElementById('fridgeZone2WarmInput').value = alarmConfig.fridge_zone_2_warm;
|
document.getElementById('fridgeZone2WarmInput').value = alarmConfig.fridge_zone_2_warm;
|
||||||
document.getElementById('batteryLowInput').value = alarmConfig.battery_low;
|
document.getElementById('batteryLowInput').value = alarmConfig.battery_low;
|
||||||
|
document.getElementById('audibleAlarmsInput').checked = alarmConfig.audible_alarms;
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveAlarmSettings() {
|
function saveAlarmSettings() {
|
||||||
@@ -273,7 +267,8 @@ function saveAlarmSettings() {
|
|||||||
rear_seat_critical: Number(document.getElementById('rearCriticalInput').value),
|
rear_seat_critical: Number(document.getElementById('rearCriticalInput').value),
|
||||||
fridge_zone_1_warm: Number(document.getElementById('fridgeZone1WarmInput').value),
|
fridge_zone_1_warm: Number(document.getElementById('fridgeZone1WarmInput').value),
|
||||||
fridge_zone_2_warm: Number(document.getElementById('fridgeZone2WarmInput').value),
|
fridge_zone_2_warm: Number(document.getElementById('fridgeZone2WarmInput').value),
|
||||||
battery_low: Number(document.getElementById('batteryLowInput').value)
|
battery_low: Number(document.getElementById('batteryLowInput').value),
|
||||||
|
audible_alarms: document.getElementById('audibleAlarmsInput').checked
|
||||||
};
|
};
|
||||||
|
|
||||||
localStorage.setItem('alarmConfig', JSON.stringify(alarmConfig));
|
localStorage.setItem('alarmConfig', JSON.stringify(alarmConfig));
|
||||||
@@ -291,16 +286,22 @@ async function loadStatus() {
|
|||||||
document.getElementById('statusLine').textContent =
|
document.getElementById('statusLine').textContent =
|
||||||
`RS-485: ${data.network.rs485_connected ? 'Connected' : 'Lost'} | WiFi: ${data.network.wifi_enabled ? 'Enabled' : 'Disabled'}`;
|
`RS-485: ${data.network.rs485_connected ? 'Connected' : 'Lost'} | WiFi: ${data.network.wifi_enabled ? 'Enabled' : 'Disabled'}`;
|
||||||
|
|
||||||
document.getElementById('dashSoc').textContent = data.battery.soc;
|
if (data.network.communication_lost) {
|
||||||
document.getElementById('dashRuntime').textContent = data.battery.runtime_hours;
|
document.body.classList.add('comms-lost');
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove('comms-lost');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('dashSoc').textContent = data.battery.soc ?? '--';
|
||||||
|
document.getElementById('dashRuntime').textContent = data.battery.runtime_hours ?? '--';
|
||||||
document.getElementById('dashFridge1').textContent = tempText(data.temps.fridge_zone_1);
|
document.getElementById('dashFridge1').textContent = tempText(data.temps.fridge_zone_1);
|
||||||
document.getElementById('dashFridge2').textContent = tempText(data.temps.fridge_zone_2);
|
document.getElementById('dashFridge2').textContent = tempText(data.temps.fridge_zone_2);
|
||||||
document.getElementById('dashRear').textContent = tempText(data.temps.rear_seat);
|
document.getElementById('dashRear').textContent = tempText(data.temps.rear_seat);
|
||||||
|
|
||||||
setSensorCard('cardFridge1', 'healthFridge1', data.sensor_health.fridge_zone_1);
|
setSensorCard('cardFridge1', 'healthFridge1', data.sensor_health?.fridge_zone_1 ?? true);
|
||||||
setSensorCard('cardFridge2', 'healthFridge2', data.sensor_health.fridge_zone_2);
|
setSensorCard('cardFridge2', 'healthFridge2', data.sensor_health?.fridge_zone_2 ?? true);
|
||||||
setSensorCard('cardRear', 'kidStatus', data.sensor_health.rear_seat);
|
setSensorCard('cardRear', 'kidStatus', data.sensor_health?.rear_seat ?? true);
|
||||||
setSensorCard('cardOutside', 'healthOutside', data.sensor_health.outside);
|
setSensorCard('cardOutside', 'healthOutside', data.sensor_health?.outside ?? true);
|
||||||
|
|
||||||
const kidStatus = document.getElementById('kidStatus');
|
const kidStatus = document.getElementById('kidStatus');
|
||||||
if (data.sensor_health.rear_seat && data.temps.rear_seat >= alarmConfig.rear_seat_critical) {
|
if (data.sensor_health.rear_seat && data.temps.rear_seat >= alarmConfig.rear_seat_critical) {
|
||||||
@@ -315,11 +316,11 @@ async function loadStatus() {
|
|||||||
document.getElementById('dashFridgeRelay').textContent = `Fridge: ${onOff(data.relays.fridge)}`;
|
document.getElementById('dashFridgeRelay').textContent = `Fridge: ${onOff(data.relays.fridge)}`;
|
||||||
|
|
||||||
document.getElementById('batSoc').textContent = data.battery.soc;
|
document.getElementById('batSoc').textContent = data.battery.soc;
|
||||||
document.getElementById('batVoltage').textContent = data.battery.voltage;
|
document.getElementById('batVoltage').textContent = data.battery.voltage ?? '--';
|
||||||
document.getElementById('batCurrent').textContent = data.battery.current;
|
document.getElementById('batCurrent').textContent = data.battery.current ?? '--';
|
||||||
document.getElementById('batRemaining').textContent = data.battery.remaining_ah;
|
document.getElementById('batRemaining').textContent = data.battery.remaining_ah ?? '--';
|
||||||
document.getElementById('batRuntime').textContent = data.battery.runtime_hours;
|
document.getElementById('batRuntime').textContent = data.battery.runtime_hours ?? '--';
|
||||||
document.getElementById('batTemp').textContent = data.battery.temperature_f;
|
document.getElementById('batTemp').textContent = data.battery.temperature_f ?? '--';
|
||||||
|
|
||||||
document.getElementById('tempFridge1').textContent = tempText(data.temps.fridge_zone_1);
|
document.getElementById('tempFridge1').textContent = tempText(data.temps.fridge_zone_1);
|
||||||
document.getElementById('tempFridge2').textContent = tempText(data.temps.fridge_zone_2);
|
document.getElementById('tempFridge2').textContent = tempText(data.temps.fridge_zone_2);
|
||||||
@@ -332,8 +333,17 @@ async function loadStatus() {
|
|||||||
document.getElementById('sysRs485').textContent = data.network.rs485_connected ? 'Connected' : 'Disconnected';
|
document.getElementById('sysRs485').textContent = data.network.rs485_connected ? 'Connected' : 'Disconnected';
|
||||||
document.getElementById('sysWifi').textContent = data.network.wifi_enabled ? 'Enabled' : 'Disabled';
|
document.getElementById('sysWifi').textContent = data.network.wifi_enabled ? 'Enabled' : 'Disabled';
|
||||||
document.getElementById('sysWifiOverride').textContent = data.network.wifi_override_active ? 'Active' : 'Inactive';
|
document.getElementById('sysWifiOverride').textContent = data.network.wifi_override_active ? 'Active' : 'Inactive';
|
||||||
document.getElementById('sysIgnition').textContent = data.vehicle.ignition_on ? 'ON' : 'OFF';
|
document.getElementById('sysIgnition').textContent = data.vehicle?.ignition_on ? 'ON' : 'OFF';
|
||||||
updateSleepOverlay(data.vehicle.ignition_on);
|
|
||||||
|
document.getElementById('commsPrimary').textContent = data.network.rs485_connected ? 'RS-485' : 'RS-485 LOST';
|
||||||
|
document.getElementById('commsBackup').textContent = 'WiFi';
|
||||||
|
document.getElementById('commsSent').textContent = data.network.messages_sent ?? '--';
|
||||||
|
document.getElementById('commsReceived').textContent = data.network.messages_received ?? '--';
|
||||||
|
document.getElementById('commsLast').textContent = data.network.last_message_time
|
||||||
|
? new Date(data.network.last_message_time * 1000).toLocaleTimeString()
|
||||||
|
: '--';
|
||||||
|
|
||||||
|
updateSleepOverlay(data.vehicle?.ignition_on ?? true);
|
||||||
|
|
||||||
checkAlarms(data);
|
checkAlarms(data);
|
||||||
}
|
}
|
||||||
@@ -385,6 +395,26 @@ async function toggleIgnition() {
|
|||||||
loadStatus();
|
loadStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function disconnectRs485() {
|
||||||
|
await fetch('/comms/rs485/disconnect', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({})
|
||||||
|
});
|
||||||
|
|
||||||
|
loadStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restoreRs485() {
|
||||||
|
await fetch('/comms/rs485/restore', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({})
|
||||||
|
});
|
||||||
|
|
||||||
|
loadStatus();
|
||||||
|
}
|
||||||
|
|
||||||
async function toggleSensorFault(name) {
|
async function toggleSensorFault(name) {
|
||||||
await fetch(`/sensor/${name}/fault`, {
|
await fetch(`/sensor/${name}/fault`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
Reference in New Issue
Block a user