feat: integrate bettervent.me device database and add BTU/kW unit switcher

Add bettervent.me provider with lazy-cached device list (~6,700 Eurovent-certified
heat pumps), search API endpoint, device search UI with auto-populate, BTU/kW unit
switcher for European users, and extended AC fields (SEER, SCOP, COP, TOL, Tbiv,
refrigerant). Closes #2.
This commit is contained in:
2026-02-11 00:31:39 +01:00
parent 21154d5d7f
commit a334dd57a0
14 changed files with 820 additions and 37 deletions

View File

@@ -265,6 +265,23 @@
<section id="tab-ac" class="tab-panel hidden">
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">{{t "setup.ac.help"}}</p>
<div id="ac-no-rooms" class="hidden text-sm text-amber-600 dark:text-amber-400 mb-3">{{t "setup.ac.noRooms"}}</div>
<!-- Device search -->
<div class="mb-3">
<label class="block text-sm font-medium mb-1">{{t "setup.ac.search.label"}}</label>
<div class="relative">
<input type="text" id="ac-device-search" placeholder="{{t "setup.ac.search.placeholder"}}" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
<div id="ac-search-results" class="hidden absolute z-40 w-full mt-1 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg max-h-64 overflow-y-auto"></div>
</div>
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">{{t "setup.ac.search.hint"}} — <a href="https://bettervent.me" class="text-orange-600 dark:text-orange-400 hover:underline" target="_blank" rel="noopener">bettervent.me</a></p>
</div>
<!-- Unit toggle -->
<div class="flex items-center gap-2 mb-3">
<span class="text-sm text-gray-500 dark:text-gray-400">{{t "setup.ac.unit.switch"}}:</span>
<button type="button" id="ac-unit-toggle" class="px-3 py-1 text-xs rounded-full border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 transition font-medium" data-unit="btuh">BTU/h</button>
</div>
<form id="ac-form" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm space-y-3 mb-4">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
<div>
@@ -279,7 +296,7 @@
</select>
</div>
<div>
<label class="block text-sm font-medium mb-1">{{t "setup.ac.capacity.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.capacity.tooltip"}}">?</span></label>
<label class="block text-sm font-medium mb-1" id="ac-capacity-label">{{t "setup.ac.capacity.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.capacity.tooltip"}}">?</span></label>
<input type="number" name="capacityBtu" step="100" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
</div>
<div>
@@ -299,7 +316,7 @@
</label>
</div>
<div>
<label class="block text-sm font-medium mb-1">{{t "setup.ac.heatingCapacity.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.heatingCapacity.tooltip"}}">?</span></label>
<label class="block text-sm font-medium mb-1" id="ac-heating-capacity-label">{{t "setup.ac.heatingCapacity.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.heatingCapacity.tooltip"}}">?</span></label>
<input type="number" name="heatingCapacityBtu" step="100" value="0" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
</div>
</div>
@@ -313,6 +330,20 @@
</div>
<input type="hidden" name="id" value="">
</form>
<!-- Extended device info (shown after search selection or when editing device with extended data) -->
<div id="ac-extended-info" class="hidden bg-gray-50 dark:bg-gray-900 rounded-lg p-3 text-sm space-y-1 mb-4">
<div class="font-medium text-gray-600 dark:text-gray-300 mb-1" id="ac-extended-title"></div>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-0.5 text-xs text-gray-500 dark:text-gray-400">
<div>{{t "setup.ac.seer"}}: <span id="ac-ext-seer" class="font-medium text-gray-700 dark:text-gray-200"></span></div>
<div>{{t "setup.ac.scop"}}: <span id="ac-ext-scop" class="font-medium text-gray-700 dark:text-gray-200"></span></div>
<div>{{t "setup.ac.cop"}}: <span id="ac-ext-cop" class="font-medium text-gray-700 dark:text-gray-200"></span></div>
<div>{{t "setup.ac.tol"}}: <span id="ac-ext-tol" class="font-medium text-gray-700 dark:text-gray-200"></span></div>
<div>{{t "setup.ac.tbiv"}}: <span id="ac-ext-tbiv" class="font-medium text-gray-700 dark:text-gray-200"></span></div>
<div>{{t "setup.ac.refrigerant"}}: <span id="ac-ext-refrigerant" class="font-medium text-gray-700 dark:text-gray-200"></span></div>
</div>
</div>
<div id="ac-list" class="space-y-2">
<p class="text-sm text-gray-400 dark:text-gray-500">{{t "setup.ac.noItems"}}</p>
</div>