120 lines
4.6 KiB
HTML
120 lines
4.6 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Discovery - HAMeter{% endblock %}
|
|
{% block page_title %}Discovery{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="discovery-page">
|
|
<div class="discovery-info">
|
|
<p>Discovery mode listens for all nearby meter transmissions. This will <strong>temporarily stop</strong> meter monitoring while active.</p>
|
|
</div>
|
|
|
|
<div class="discovery-controls">
|
|
<div class="form-group form-inline">
|
|
<label for="duration">Duration (seconds)</label>
|
|
<input type="number" id="duration" value="120" min="10" max="600">
|
|
</div>
|
|
<div class="btn-group">
|
|
<button class="btn btn-primary" id="btn-start" onclick="startDiscovery()">Start Discovery</button>
|
|
<button class="btn btn-danger hidden" id="btn-stop" onclick="stopDiscovery()">Stop</button>
|
|
</div>
|
|
<div class="discovery-status hidden" id="discovery-status">
|
|
<span class="spinner-sm"></span>
|
|
<span id="discovery-timer">Listening...</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-container">
|
|
<table class="data-table" id="discovery-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Meter ID</th>
|
|
<th>Protocol</th>
|
|
<th>Count</th>
|
|
<th>Last Reading</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="discovery-tbody">
|
|
<tr class="empty-row"><td colspan="5">No meters found yet. Start discovery to scan.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let discoveryTimer = null;
|
|
let discoveryStart = null;
|
|
let discoveryDuration = 120;
|
|
const configuredIds = new Set({{ configured_ids|tojson }});
|
|
|
|
function startDiscovery() {
|
|
discoveryDuration = parseInt(document.getElementById('duration').value) || 120;
|
|
fetch('/api/discovery/start', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({duration: discoveryDuration}),
|
|
}).then(r => r.json()).then(data => {
|
|
if (data.ok) {
|
|
discoveryStart = Date.now();
|
|
document.getElementById('btn-start').classList.add('hidden');
|
|
document.getElementById('btn-stop').classList.remove('hidden');
|
|
document.getElementById('discovery-status').classList.remove('hidden');
|
|
document.getElementById('discovery-tbody').innerHTML =
|
|
'<tr class="empty-row"><td colspan="5">Listening for meters...</td></tr>';
|
|
discoveryTimer = setInterval(updateTimer, 1000);
|
|
}
|
|
});
|
|
}
|
|
|
|
function stopDiscovery() {
|
|
fetch('/api/discovery/stop', {method: 'POST'});
|
|
endDiscovery();
|
|
}
|
|
|
|
function endDiscovery() {
|
|
clearInterval(discoveryTimer);
|
|
document.getElementById('btn-start').classList.remove('hidden');
|
|
document.getElementById('btn-stop').classList.add('hidden');
|
|
document.getElementById('discovery-status').classList.add('hidden');
|
|
}
|
|
|
|
function updateTimer() {
|
|
const elapsed = Math.floor((Date.now() - discoveryStart) / 1000);
|
|
const remaining = discoveryDuration - elapsed;
|
|
if (remaining <= 0) {
|
|
endDiscovery();
|
|
return;
|
|
}
|
|
document.getElementById('discovery-timer').textContent =
|
|
'Listening... ' + remaining + 's remaining';
|
|
}
|
|
|
|
// SSE handler updates discovery results
|
|
if (typeof window._onDiscoveryUpdate === 'undefined') {
|
|
window._onDiscoveryUpdate = function(results) {
|
|
const tbody = document.getElementById('discovery-tbody');
|
|
if (!tbody) return;
|
|
if (!results || Object.keys(results).length === 0) return;
|
|
|
|
let html = '';
|
|
// Sort by count descending
|
|
const entries = Object.entries(results).sort((a, b) => (b[1].count || 0) - (a[1].count || 0));
|
|
for (const [mid, info] of entries) {
|
|
const meterId = parseInt(mid);
|
|
const configured = configuredIds.has(meterId);
|
|
html += '<tr>' +
|
|
'<td><code>' + mid + '</code></td>' +
|
|
'<td><span class="badge badge-protocol">' + (info.protocol || '').toUpperCase() + '</span></td>' +
|
|
'<td>' + (info.count || 0) + '</td>' +
|
|
'<td>' + (info.last_consumption || '--') + '</td>' +
|
|
'<td>' + (configured
|
|
? '<span class="text-muted">Configured</span>'
|
|
: '<a href="/config/meters/add?id=' + mid + '&protocol=' + (info.protocol || '') + '" class="btn btn-sm btn-primary">Add</a>'
|
|
) + '</td></tr>';
|
|
}
|
|
tbody.innerHTML = html;
|
|
};
|
|
}
|
|
</script>
|
|
{% endblock %}
|