Add status control in web UI

This commit is contained in:
Артём Кокос
2026-03-02 20:11:59 +07:00
parent 450fd54b80
commit 9a47f6f936

View File

@@ -70,8 +70,14 @@
<div v-for="(group, id) in groups" :key="id" class="glass p-8 rounded-[2.5rem] relative group hover:border-orange-500/50 transition-all">
<div class="flex justify-between items-start mb-8">
<div>
<h2 class="text-2xl font-black text-white">{{ group.name }}</h2>
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-tighter">{{ id }} • {{ group.device_ids?.length || 0 }} ламп</span>
<h2 class="text-2xl font-black text-white flex items-center gap-2">
{{ group.name }}
<span :class="sliders[id]?.state ? 'bg-green-500' : 'bg-slate-600'"
class="w-2 h-2 rounded-full shadow-[0_0_8px_rgba(0,0,0,0.5)]"></span>
</h2>
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-tighter">
{{ id }} • {{ group.device_ids?.length || 0 }} ламп
</span>
</div>
<div class="flex gap-2">
<button @click="deleteGroup(id)" class="bg-slate-800 hover:bg-red-900/40 p-3 rounded-xl transition-all text-slate-500 hover:text-red-500" title="Удалить группу">
@@ -152,6 +158,7 @@
devices: [],
sliders: {},
newGroup: { id: '', name: '', macs: [] },
isLoadingStatus: false,
allScenes: [
"ocean", "romance", "party", "fireplace", "cozy", "forest",
"pastel_colors", "wake_up", "bedtime", "warm_white", "daylight",
@@ -199,14 +206,31 @@
if (!this.apiKey) return;
const gData = await this.request('/devices/groups');
const dData = await this.request('/devices');
if (gData) this.groups = gData;
if (gData) {
this.groups = gData;
// Инициализируем слайдеры для новых групп
Object.keys(this.groups).forEach(id => {
if (!this.sliders[id]) {
this.sliders[id] = { brightness: 100, temp: 3000, state: false };
}
});
// Сразу после получения списка групп — запрашиваем их состояние
await this.syncGroupStatuses();
}
if (dData) this.devices = Object.values(dData);
Object.keys(this.groups).forEach(id => {
if (!this.sliders[id]) this.sliders[id] = { brightness: 100, temp: 3000 };
});
},
async control(id, params) { await this.request(`/control/group/${id}`, 'POST', params); },
toggleGroup(id, state) { this.control(id, { state: state }); },
async control(id, params) {
await this.request(`/control/group/${id}`, 'POST', params);
await this.syncGroupStatuses();
},
toggleGroup(id, state) {
// Оптимистично меняем состояние в интерфейсе
if (this.sliders[id]) {
this.sliders[id].state = state;
}
this.control(id, { state: state });
},
setBrightness(id, val) { this.control(id, { brightness: val }); },
setTemp(id, val) { this.control(id, { temp: val }); },
setScene(id, scene) { this.control(id, { scene }); },
@@ -234,7 +258,29 @@
await this.request('/devices/rescan', 'POST');
setTimeout(this.fetchData, 2000);
},
async blink(deviceId) { await this.request(`/control/device/${deviceId}/blink`, 'POST'); }
async blink(deviceId) { await this.request(`/control/device/${deviceId}/blink`, 'POST'); },
async syncGroupStatuses() {
if (this.isLoadingStatus) return;
this.isLoadingStatus = true;
for (const groupId of Object.keys(this.groups)) {
const data = await this.request(`/control/group/${groupId}/status`);
if (data && data.results && data.results.length > 0) {
// Берем состояние первой доступной лампы в группе как эталонное
const firstValid = data.results.find(r => r.status && !r.error);
if (firstValid) {
const s = firstValid.status;
// Обновляем ползунки, если пользователь ими сейчас не двигает
this.sliders[groupId] = {
brightness: s.dimming || 100,
temp: s.temp || 3000,
state: s.state
};
}
}
}
this.isLoadingStatus = false;
}
},
mounted() {
if (this.apiKey) {