Skip to content

Commit 284d60d

Browse files
committed
feat: inverter model dropdown on settings page
Replace the free-text inverter model input with a dropdown populated from the driver registry, so only real, supported models can be selected (no typos or garbage values). - settings.py: derive inverter_model options from the driver registry (single source of truth) via a lazy _inverter_model_options() helper, with a static fallback if the registry can't be imported. - index.html: swap the text input for a <select> and populate it from the API's options list, preserving the currently-configured model. This also fixes the drift where the hardcoded options list was missing the 5 new drivers (lxp_6k/12k/18k, fortress_envy_12k, bigbattery_sna_6k).
1 parent b03a8ca commit 284d60d

2 files changed

Lines changed: 52 additions & 9 deletions

File tree

‎api/static/index.html‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ <h3>Timer Schedule</h3>
392392
<div class="field"><label>Dongle Port</label><input type="number" data-key="dongle_port" placeholder="8000"></div>
393393
<div class="field"><label>Datalog Serial</label><input type="text" data-key="datalog_serial" placeholder="BJxxxxxxxx"></div>
394394
<div class="field"><label>Inverter Serial</label><input type="text" data-key="inverter_serial" placeholder="5xxxxxxxx"></div>
395-
<div class="field"><label>Inverter Model</label><input type="text" data-key="inverter_model" placeholder="eg4_6000xp"></div>
395+
<div class="field"><label>Inverter Model</label><select data-key="inverter_model" id="inverter-model-select"></select></div>
396396
</div>
397397
</div></div>
398398
<div class="card" style="margin-top:1em;"><div class="card-section"><h3>Limits & Preferences</h3>
@@ -1030,6 +1030,9 @@ <h4 style="margin-bottom:0.5rem;">${g.title}</h4>
10301030
else el.value = val;
10311031
}
10321032
});
1033+
// Populate the inverter model dropdown now that settings (and its options
1034+
// list) are loaded.
1035+
populateInverterModels();
10331036
}
10341037

10351038
// Populate the timezone dropdown with all IANA timezones.
@@ -1044,6 +1047,21 @@ <h4 style="margin-bottom:0.5rem;">${g.title}</h4>
10441047
const curVal = (cur !== null && typeof cur === 'object') ? cur.value : cur;
10451048
if (curVal && list.includes(curVal)) sel.value = curVal;
10461049
}
1050+
1051+
// Populate the inverter model dropdown from the API's options list (derived
1052+
// from the driver registry), so only real, supported models can be selected.
1053+
function populateInverterModels() {
1054+
const sel = document.getElementById('inverter-model-select');
1055+
if (!sel) return;
1056+
const meta = settings.inverter_model;
1057+
const options = (meta && Array.isArray(meta.options)) ? meta.options : [];
1058+
// options is a list of [value, label] pairs from the API.
1059+
const list = options.map(o => (Array.isArray(o) ? o : [o.value, o.label]));
1060+
sel.innerHTML = list.map(([v, l]) => `<option value="${v}">${l}</option>`).join('');
1061+
// Preserve the currently-configured model if present.
1062+
const curVal = (meta !== null && typeof meta === 'object') ? meta.value : meta;
1063+
if (curVal && list.some(([v]) => v === curVal)) sel.value = curVal;
1064+
}
10471065
async function saveConfiguration() {
10481066
const msg = document.getElementById('config-msg');
10491067
msg.textContent = 'Saving...'; msg.style.color = 'var(--light-text-color)';

‎collector/settings.py‎

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -220,21 +220,46 @@ def effective_value(name: str, db_value: Optional[str]):
220220

221221
# ── Setting metadata for the UI ──
222222

223-
# Each setting's display info for the settings page form
224-
SETTING_META = {
225-
"inverter_model": {
226-
"label": "Model",
227-
"type": "select",
228-
"section": "inverter",
229-
"options": [
223+
224+
def _inverter_model_options():
225+
"""Return the (value, label) pairs for the inverter_model dropdown.
226+
227+
Derived from the driver registry so the settings page can never drift out
228+
of sync with the drivers that actually exist. Imported lazily to avoid a
229+
circular import (registry -> registers, not settings).
230+
"""
231+
try:
232+
from .drivers.registry import DRIVERS
233+
from .drivers import ModelDriver
234+
options = []
235+
for name in sorted(DRIVERS):
236+
try:
237+
drv = DRIVERS[name]()
238+
label = drv.label if isinstance(drv, ModelDriver) else name
239+
except Exception:
240+
label = name
241+
options.append((name, label))
242+
return options
243+
except Exception:
244+
# Fall back to a static list if the registry can't be imported.
245+
return [
230246
("eg4_6000xp", "EG4 6000XP"),
231247
("eg4_12000xp", "EG4 12000XP"),
232248
("eg4_18kpv", "EG4 18KPV"),
233249
("eg4_12kpv", "EG4 12kPV"),
234250
("luxpower_sna", "Luxpower SNA"),
235251
("eg4_6500ex", "EG4 6500EX-48 (legacy)"),
236252
("eg4_3000ehv", "EG4 3000EHV-48 (legacy)"),
237-
],
253+
]
254+
255+
256+
# Each setting's display info for the settings page form
257+
SETTING_META = {
258+
"inverter_model": {
259+
"label": "Model",
260+
"type": "select",
261+
"section": "inverter",
262+
"options": _inverter_model_options(),
238263
"hint": "Inverter model (Luxpower SNA / 18KPV families only)",
239264
},
240265
"pv_max_power": {

0 commit comments

Comments
 (0)