From c661e2450dab9f26b88543cb082bff27461bced6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9A=D0=BE=D0=BA=D0=BE?= =?UTF-8?q?=D1=81?= Date: Mon, 2 Mar 2026 20:44:25 +0700 Subject: [PATCH] Web-UI: Schedules support --- app/api/routes/schedules.py | 26 +++++++++-- static/index.html | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 4 deletions(-) diff --git a/app/api/routes/schedules.py b/app/api/routes/schedules.py index b9fd24d..676b9f2 100644 --- a/app/api/routes/schedules.py +++ b/app/api/routes/schedules.py @@ -90,19 +90,37 @@ async def add_cron_task( @router.get("/tasks") async def get_all_tasks(): jobs = [] - for job in scheduler.get_jobs(): + # Разбираем строку типа "Group: bedroom | {'state': True}" + # Вытаскиваем цель (bedroom) и состояние (True/False) + name_parts = job.name.split("|") + target = name_parts[0].replace("Group:", "").replace("Device:", "").strip() + + # Пытаемся понять, ВКЛ или ВЫКЛ задача + is_on = "True" in job.name + jobs.append( { "id": job.id, - "name": job.name, + "target_id": target, + "state": is_on, + # Достаем время из триггера APScheduler "next_run": ( job.next_run_time.isoformat() if job.next_run_time else None ), - "params": str(job.args[1]) if len(job.args) > 1 else None, + # Вытаскиваем час и минуту прямо из настроек триггера для красоты + "hour": ( + str(job.trigger.fields[5]).zfill(2) + if hasattr(job.trigger, "fields") + else "??" + ), + "minute": ( + str(job.trigger.fields[6]).zfill(2) + if hasattr(job.trigger, "fields") + else "??" + ), } ) - return {"tasks": jobs} diff --git a/static/index.html b/static/index.html index 7a4b2d5..9f93ba6 100644 --- a/static/index.html +++ b/static/index.html @@ -58,10 +58,68 @@ +
+
+

Новая задача

+
+ + +
+ + : + +
+ + + +
+
+ +
+

Активные задачи

+
+
+
+
+ {{ task.hour }}:{{ task.minute }} + + {{ groups[task.target_id]?.name || task.target_id }} + +
+
+ + {{ task.state ? 'ВКЛЮЧИТЬ' : 'ВЫКЛЮЧИТЬ' }} + + + + {{ task.next_run ? 'След. запуск: ' + new Date(task.next_run).toLocaleDateString() : 'Разовая задача' }} + +
+
+ +
+
Задач пока нет
+
+
+
+

Создайте группу в админке, чтобы управлять лампами

@@ -159,6 +217,10 @@ sliders: {}, newGroup: { id: '', name: '', macs: [] }, isLoadingStatus: false, + taskHour: '12', // Начальное значение часа + taskMin: '00', // Начальное значение минут + newTask: { target_id: '', time: '', state: true }, + tasks: [], allScenes: [ "ocean", "romance", "party", "fireplace", "cozy", "forest", "pastel_colors", "wake_up", "bedtime", "warm_white", "daylight", @@ -219,6 +281,7 @@ await this.syncGroupStatuses(); } if (dData) this.devices = Object.values(dData); + this.fetchTasks(); }, async control(id, params) { await this.request(`/control/group/${id}`, 'POST', params); @@ -280,6 +343,32 @@ } } this.isLoadingStatus = false; + }, + async fetchTasks() { + const data = await this.request('/schedules/tasks'); + if (data) this.tasks = data.tasks; + }, + async addSchedule() { + // Проверяем, выбрана ли группа + if (!this.newTask.target_id) { + alert("Выбери группу!"); + return; + } + + // Отправляем данные. Теперь берем их прямо из taskHour и taskMin + await this.request('/schedules/cron', 'POST', { + target_id: this.newTask.target_id, + hour: this.taskHour, + minute: this.taskMin, + is_group: true, + state: this.newTask.state + }); + + this.fetchTasks(); // Обновляем список + }, + async deleteTask(id) { + await this.request(`/schedules/${id}`, 'DELETE'); + this.fetchTasks(); } }, mounted() {