Files
HAMeter/hameter/web/templates/calibration.html
2026-03-06 12:25:27 -05:00

147 lines
5.6 KiB
HTML

{% extends "base.html" %}
{% block title %}Calibration - HAMeter{% endblock %}
{% block page_title %}Calibration{% endblock %}
{% block content %}
<div class="config-form-container">
{% if not meters %}
<div class="empty-state">
<p>No meters configured. <a href="/config/meters/add">Add a meter</a> first.</p>
</div>
{% else %}
<div class="calibration-info">
<p>Calibrate the multiplier that converts raw meter readings to actual units (kWh, gallons, etc.).</p>
<p><strong>How:</strong> Read the display on your physical meter, enter it below along with the current raw reading from HAMeter, and the multiplier will be calculated automatically.</p>
</div>
<form id="calibration-form" onsubmit="return false;">
<div class="form-group">
<label for="cal-meter">Select Meter</label>
<select id="cal-meter" onchange="onMeterSelect()">
{% for meter in meters %}
<option value="{{ meter.id }}"
data-unit="{{ meter.unit_of_measurement }}"
data-multiplier="{{ meter.multiplier }}"
data-raw="{{ readings.get(meter.id, '')|attr('raw_consumption') if readings.get(meter.id) else '' }}">
{{ meter.name }} ({{ meter.id }})
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="cal-raw">Current Raw Reading (from HAMeter)</label>
<input type="number" id="cal-raw" step="any">
<small>This is the raw, uncalibrated value. You can find it on the Dashboard.</small>
</div>
<div class="form-group">
<label for="cal-physical">Current Physical Meter Reading</label>
<input type="number" id="cal-physical" step="any">
<small>Read this from the display on your physical meter. <span id="cal-unit-hint"></span></small>
</div>
<div class="form-actions">
<button type="button" class="btn btn-primary" onclick="calculate()">Calculate</button>
</div>
</form>
<div class="calibration-result hidden" id="cal-result">
<h3>Result</h3>
<div class="result-grid">
<div class="result-item">
<span class="result-label">New Multiplier</span>
<span class="result-value" id="cal-new-multiplier">--</span>
</div>
<div class="result-item">
<span class="result-label">Current Multiplier</span>
<span class="result-value" id="cal-current-multiplier">--</span>
</div>
<div class="result-item">
<span class="result-label">Preview</span>
<span class="result-value" id="cal-preview">--</span>
</div>
</div>
<div class="form-actions">
<button type="button" class="btn btn-primary" onclick="applyMultiplier()">Apply Multiplier</button>
</div>
</div>
{% endif %}
</div>
<script>
let selectedMeterId = null;
let calculatedMultiplier = null;
function onMeterSelect() {
const sel = document.getElementById('cal-meter');
const opt = sel.options[sel.selectedIndex];
selectedMeterId = parseInt(sel.value);
const raw = opt.dataset.raw;
if (raw) document.getElementById('cal-raw').value = raw;
document.getElementById('cal-current-multiplier').textContent = opt.dataset.multiplier;
const unit = opt.dataset.unit;
document.getElementById('cal-unit-hint').textContent = unit ? '(in ' + unit + ')' : '';
}
async function calculate() {
const raw = parseFloat(document.getElementById('cal-raw').value);
const physical = parseFloat(document.getElementById('cal-physical').value);
if (!raw || !physical) {
showToast('Enter both values', 'error');
return;
}
const resp = await fetch('/api/calibration/calculate', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({raw_reading: raw, physical_reading: physical}),
});
const data = await resp.json();
if (data.error) {
showToast(data.error, 'error');
return;
}
calculatedMultiplier = data.multiplier;
document.getElementById('cal-new-multiplier').textContent = data.multiplier;
document.getElementById('cal-preview').textContent = data.preview;
document.getElementById('cal-result').classList.remove('hidden');
}
async function applyMultiplier() {
if (!selectedMeterId || !calculatedMultiplier) return;
const resp = await fetch('/api/calibration/apply', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({meter_id: selectedMeterId, multiplier: calculatedMultiplier}),
});
const data = await resp.json();
if (data.ok) {
showToast('Multiplier applied', 'success');
if (data.restart_required) showRestartBanner();
} else {
showToast(data.error || 'Failed', 'error');
}
}
// Auto-select meter from query param or default to first
document.addEventListener('DOMContentLoaded', function() {
const params = new URLSearchParams(window.location.search);
const meterId = params.get('meter');
if (meterId) {
const sel = document.getElementById('cal-meter');
if (sel) {
for (let i = 0; i < sel.options.length; i++) {
if (sel.options[i].value === meterId) {
sel.selectedIndex = i;
break;
}
}
}
}
onMeterSelect();
});
</script>
{% endblock %}