feat: v.1.1.0 work: добавил режим ручного управления. Теперь можно отключать расписание!

POST api/schedule/pause
POST api/schedule/resume
This commit is contained in:
Fedorov Dmitriy
2026-09-19 19:05:55 +03:00
parent b02d18d283
commit a5c9d07db5
7 changed files with 784 additions and 20 deletions
+73 -18
View File
@@ -4,6 +4,8 @@ from typing import Literal, Annotated
from fastapi import FastAPI, Request, HTTPException, Path
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pathlib import Path as FilePath
from app.qingping.service import QingpingService
from app.my_dataclasses import (
TION_MAC,
@@ -39,7 +41,12 @@ from schedule import (
load_schedule,
)
from app.qingping.service import QingpingService
SCHEDULE_STATE_FILE = (
FilePath(SCHEDULE_FILE)
.with_name("schedule_state.json")
)
def configure_logging() -> None:
noisy_loggers = (
@@ -104,6 +111,7 @@ async def lifespan(app: FastAPI):
config,
service,
check_interval=5,
state_path=SCHEDULE_STATE_FILE,
)
await schedule_service.start()
@@ -277,21 +285,27 @@ def get_effective_temperature() -> int:
async def execute_command(operation, override: ScheduledSettings | None = None):
try:
# Если расписание реально работает,
# ручная команда становится
# temporary override.
# При активном расписании ручная команда
# становится temporary override.
#
# Но если расписание paused,
# мы находимся в полноценном ручном режиме,
# поэтому команда идёт напрямую в Tion.
if (
override is not None
and schedule_service is not None
and schedule_service.running
and schedule_service.config.enabled
override is not None
and schedule_service is not None
and schedule_service.running
and schedule_service.config.enabled
and not schedule_service.paused
):
await schedule_service.apply_override(override)
await schedule_service.apply_override(
override
)
# Если расписание выключено или
# недоступно — обычное ручное управление.
else:
await service.execute(operation)
await service.execute(
operation
)
except Exception as exc:
raise HTTPException(
@@ -331,18 +345,19 @@ def get_schedule_status() -> dict:
if schedule_service is None:
return {
"available": False,
"enabled": False,
"running": False,
"current_action": None,
"current": None,
"next": None,
"auto_active": False,
"auto_fallback_speed": None,
"auto_target_temp": None,
"available": False,
"current_action": None,
"current": None,
"enabled": False,
"next": None,
"override_active": False,
"override_until": None,
"override_settings": {},
"paused": False,
"running": False,
"scheduled_settings": {},
"last_error": schedule_load_error,
}
@@ -385,6 +400,8 @@ def get_schedule_status() -> dict:
"enabled": resolution.enabled,
"running": schedule_service.running,
"paused": schedule_service.paused,
"current_action": current_action,
"current_time": current_time,
@@ -721,4 +738,42 @@ async def clear_schedule_override():
"ok": True,
"schedule": get_schedule_status(),
"auto": get_auto_status(),
}
# ----------------------------------------------------------------------
# Переход в полностью manual
# ----------------------------------------------------------------------
@app.post("/api/schedule/pause")
async def pause_schedule():
if schedule_service is None:
raise HTTPException(
status_code=503,
detail="Schedule service unavailable",
)
await schedule_service.pause()
return {
"ok": True,
"schedule": get_schedule_status(),
}
# ----------------------------------------------------------------------
# Возвращаем AUTO
# ----------------------------------------------------------------------
@app.post("/api/schedule/resume")
async def resume_schedule():
if schedule_service is None:
raise HTTPException(
status_code=503,
detail="Schedule service unavailable",
)
await schedule_service.resume()
return {
"ok": True,
"schedule": get_schedule_status(),
}
+34
View File
@@ -88,6 +88,40 @@ class AutoController:
resolution = self._schedule.resolve(now)
# ----------------------------------------------
# Расписание поставлено на паузу.
#
# Это полноценный ручной режим:
# AUTO вообще не имеет права управлять Tion.
# ----------------------------------------------
if getattr(
self._schedule,
"paused",
False,
):
self._state = "suspended"
self._reason = "schedule_paused"
# После перехода в ручной режим пользователь
# может изменить эти параметры напрямую.
# Поэтому старые значения AUTO больше
# нельзя считать достоверными.
self._target_speed = None
self._auto_speed = None
self._target_heater = None
self._auto_heater = None
self._target_temperature = None
self._temperature = None
self._temperature_source = None
self._last_error = None
return
# ----------------------------------------------
# Активен ручной override.
#