import os import logging import pytz from datetime import datetime, timedelta from dotenv import load_dotenv from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore from apscheduler.triggers.cron import CronTrigger from sqlalchemy import delete from app.core.database import sync_engine, async_session from app.models.event_log import EventLog from app.drivers.wiz import WizDriver load_dotenv() logger = logging.getLogger(__name__) TZ_NAME = os.getenv("APP_TIMEZONE", "Asia/Novosibirsk") app_tz = pytz.timezone(TZ_NAME) RETENTION_DAYS = int(os.getenv("EVENT_LOG_RETENTION_DAYS", "30")) jobstores = {"default": SQLAlchemyJobStore(engine=sync_engine)} scheduler = AsyncIOScheduler(jobstores=jobstores, timezone=app_tz) async def execute_lamp_command(ip: str, params: dict): """ Универсальное выполнение команды. params может содержать: state, dimming, temp, sceneId, r, g, b """ driver = WizDriver() await driver.set_pilot(ip, params) logger.info(f"Сработало расписание для {ip}: {params}") async def cleanup_old_events(): """Удаляет записи event_log старше RETENTION_DAYS.""" cutoff = (datetime.now() - timedelta(days=RETENTION_DAYS)).isoformat() async with async_session() as session: result = await session.execute( delete(EventLog).where(EventLog.timestamp < cutoff) ) await session.commit() if result.rowcount: logger.info( f"Очистка лога: удалено {result.rowcount} записей старше {RETENTION_DAYS} дней" ) async def start_scheduler(): if not scheduler.running: scheduler.start() # Очистка лога -- раз в сутки в 03:00 scheduler.add_job( cleanup_old_events, CronTrigger(hour=3, minute=0, timezone=app_tz), id="cleanup_event_log", name="Очистка старых событий", replace_existing=True, ) logger.info(f"Планировщик запущен. Таймзона: {TZ_NAME}")