147 lines
5.6 KiB
HTML
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 %}
|