initial commit
This commit is contained in:
146
hameter/web/templates/calibration.html
Normal file
146
hameter/web/templates/calibration.html
Normal file
@@ -0,0 +1,146 @@
|
||||
{% 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 %}
|
||||
Reference in New Issue
Block a user