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

161 lines
7.8 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ 'Edit' if editing else 'Add' }} Meter - HAMeter{% endblock %}
{% block page_title %}{{ 'Edit' if editing else 'Add' }} Meter{% endblock %}
{% block content %}
<div class="config-form-container">
<form id="meter-form" onsubmit="return false;">
<div class="form-group">
<label for="meter-id">Meter ID (ERT Serial Number) <span class="required">*</span></label>
<input type="number" id="meter-id" value="{{ meter.id if meter else prefill_id|default('', true) }}" {{ 'readonly' if editing }} required>
{% if editing %}<small>Meter ID cannot be changed after creation.</small>{% endif %}
</div>
<div class="form-group">
<label for="meter-protocol">Protocol <span class="required">*</span></label>
<select id="meter-protocol">
{% for p in protocols %}
<option value="{{ p }}" {{ 'selected' if (meter and meter.protocol == p) or (not meter and prefill_protocol|default('', true) == p) }}>{{ p }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="meter-name">Name <span class="required">*</span></label>
<input type="text" id="meter-name" value="{{ meter.name if meter else '' }}" required>
</div>
<div class="form-group">
<label for="meter-device-class">Device Class</label>
<select id="meter-device-class" onchange="onDeviceClassChange()">
<option value="" {{ 'selected' if not meter or not meter.device_class }}>None</option>
<option value="energy" {{ 'selected' if meter and meter.device_class == 'energy' }}>Energy (Electric)</option>
<option value="gas" {{ 'selected' if meter and meter.device_class == 'gas' }}>Gas</option>
<option value="water" {{ 'selected' if meter and meter.device_class == 'water' }}>Water</option>
</select>
</div>
<div class="form-group">
<label for="meter-unit">Unit of Measurement</label>
<input type="text" id="meter-unit" value="{{ meter.unit_of_measurement if meter else '' }}" placeholder="Auto-set by device class">
</div>
<div class="form-group">
<label for="meter-icon">Icon</label>
<input type="text" id="meter-icon" value="{{ meter.icon if meter else '' }}" placeholder="e.g. mdi:flash">
</div>
<div class="form-group">
<label for="meter-state-class">State Class</label>
<select id="meter-state-class">
<option value="total_increasing" {{ 'selected' if not meter or meter.state_class == 'total_increasing' }}>total_increasing</option>
<option value="measurement" {{ 'selected' if meter and meter.state_class == 'measurement' }}>measurement</option>
<option value="total" {{ 'selected' if meter and meter.state_class == 'total' }}>total</option>
</select>
</div>
<div class="form-group">
<label for="meter-multiplier">Calibration Multiplier</label>
<input type="number" id="meter-multiplier" step="any" value="{{ meter.multiplier if meter else '1.0' }}">
<small>Use <a href="/calibration">Calibration</a> to calculate this value.</small>
</div>
<div class="form-group">
<label>Rate Components (Cost Factors)</label>
<small>Add rate components from your utility bill to track costs.</small>
<div id="cost-factors-list">
{% if meter and meter.cost_factors %}
{% for cf in meter.cost_factors %}
<div class="cost-factor-row" data-index="{{ loop.index0 }}">
<input type="text" class="cf-name" value="{{ cf.name }}" placeholder="Name (e.g. Generation)">
<input type="number" class="cf-rate" step="any" value="{{ cf.rate }}" placeholder="Rate">
<select class="cf-type">
<option value="per_unit" {{ 'selected' if cf.type == 'per_unit' }}>Per Unit</option>
<option value="fixed" {{ 'selected' if cf.type == 'fixed' }}>Fixed</option>
</select>
<button type="button" class="btn btn-icon" onclick="removeCostFactor(this)" title="Remove">&times;</button>
</div>
{% endfor %}
{% endif %}
</div>
<button type="button" class="btn btn-secondary btn-sm" onclick="addCostFactor()" style="margin-top: 0.5rem;">+ Add Rate Component</button>
</div>
<div class="form-actions">
<a href="/config/meters" class="btn btn-secondary">Cancel</a>
<button type="button" class="btn btn-primary" onclick="saveMeter()">Save</button>
</div>
</form>
</div>
<script>
function onDeviceClassChange() {
const dc = document.getElementById('meter-device-class').value;
if (!dc) return;
fetch('/api/meter_defaults/' + dc)
.then(r => r.json())
.then(data => {
if (data.unit) document.getElementById('meter-unit').value = data.unit;
if (data.icon) document.getElementById('meter-icon').value = data.icon;
});
}
function addCostFactor(name, rate, type) {
const list = document.getElementById('cost-factors-list');
const row = document.createElement('div');
row.className = 'cost-factor-row';
row.innerHTML =
'<input type="text" class="cf-name" value="' + (name || '') + '" placeholder="Name (e.g. Generation)">' +
'<input type="number" class="cf-rate" step="any" value="' + (rate || '') + '" placeholder="Rate">' +
'<select class="cf-type">' +
'<option value="per_unit"' + (type === 'fixed' ? '' : ' selected') + '>Per Unit</option>' +
'<option value="fixed"' + (type === 'fixed' ? ' selected' : '') + '>Fixed</option>' +
'</select>' +
'<button type="button" class="btn btn-icon" onclick="removeCostFactor(this)" title="Remove">&times;</button>';
list.appendChild(row);
}
function removeCostFactor(btn) {
btn.closest('.cost-factor-row').remove();
}
function collectCostFactors() {
const rows = document.querySelectorAll('.cost-factor-row');
const factors = [];
rows.forEach(function(row) {
const name = row.querySelector('.cf-name').value.trim();
const rate = parseFloat(row.querySelector('.cf-rate').value);
const type = row.querySelector('.cf-type').value;
if (name && !isNaN(rate)) {
factors.push({ name: name, rate: rate, type: type });
}
});
return factors;
}
async function saveMeter() {
const data = {
id: parseInt(document.getElementById('meter-id').value),
protocol: document.getElementById('meter-protocol').value,
name: document.getElementById('meter-name').value,
device_class: document.getElementById('meter-device-class').value,
unit_of_measurement: document.getElementById('meter-unit').value,
icon: document.getElementById('meter-icon').value,
state_class: document.getElementById('meter-state-class').value,
multiplier: parseFloat(document.getElementById('meter-multiplier').value) || 1.0,
cost_factors: collectCostFactors(),
};
const editing = {{ 'true' if editing else 'false' }};
const url = editing ? '/api/config/meters/' + data.id : '/api/config/meters';
const method = editing ? 'PUT' : 'POST';
const resp = await fetch(url, {
method: method,
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data),
});
const res = await resp.json();
if (res.ok) {
showToast('Meter saved', 'success');
setTimeout(() => window.location.href = '/config/meters', 500);
} else {
showToast(res.error || 'Save failed', 'error');
}
}
</script>
{% endblock %}