Add status control in web UI
This commit is contained in:
@@ -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 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 class="flex justify-between items-start mb-8">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-2xl font-black text-white">{{ group.name }}</h2>
|
<h2 class="text-2xl font-black text-white flex items-center gap-2">
|
||||||
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-tighter">{{ id }} • {{ group.device_ids?.length || 0 }} ламп</span>
|
{{ 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>
|
||||||
<div class="flex gap-2">
|
<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="Удалить группу">
|
<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: [],
|
devices: [],
|
||||||
sliders: {},
|
sliders: {},
|
||||||
newGroup: { id: '', name: '', macs: [] },
|
newGroup: { id: '', name: '', macs: [] },
|
||||||
|
isLoadingStatus: false,
|
||||||
allScenes: [
|
allScenes: [
|
||||||
"ocean", "romance", "party", "fireplace", "cozy", "forest",
|
"ocean", "romance", "party", "fireplace", "cozy", "forest",
|
||||||
"pastel_colors", "wake_up", "bedtime", "warm_white", "daylight",
|
"pastel_colors", "wake_up", "bedtime", "warm_white", "daylight",
|
||||||
@@ -199,14 +206,31 @@
|
|||||||
if (!this.apiKey) return;
|
if (!this.apiKey) return;
|
||||||
const gData = await this.request('/devices/groups');
|
const gData = await this.request('/devices/groups');
|
||||||
const dData = await this.request('/devices');
|
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);
|
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); },
|
async control(id, params) {
|
||||||
toggleGroup(id, state) { this.control(id, { state: state }); },
|
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 }); },
|
setBrightness(id, val) { this.control(id, { brightness: val }); },
|
||||||
setTemp(id, val) { this.control(id, { temp: val }); },
|
setTemp(id, val) { this.control(id, { temp: val }); },
|
||||||
setScene(id, scene) { this.control(id, { scene }); },
|
setScene(id, scene) { this.control(id, { scene }); },
|
||||||
@@ -234,7 +258,29 @@
|
|||||||
await this.request('/devices/rescan', 'POST');
|
await this.request('/devices/rescan', 'POST');
|
||||||
setTimeout(this.fetchData, 2000);
|
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() {
|
mounted() {
|
||||||
if (this.apiKey) {
|
if (this.apiKey) {
|
||||||
|
|||||||
Reference in New Issue
Block a user