import asyncio import logging import signal import threading from meshbot import config from meshbot.auth import hash_password from meshbot.database import Database from meshbot.bot import MeshBot from meshbot.nina import NinaBot from meshbot.scheduler import Scheduler from meshbot.webserver import WebServer, WebSocketManager logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", ) logger = logging.getLogger(__name__) async def main(): logger.info("Starting %s v%s", config.get("bot.name"), config.get("version")) # Database db = Database(config.get("database.path", "data/meshdd.db")) await db.connect() # Seed initial admin if no users exist if not await db.get_all_users(): await db.create_user( email="admin@localhost", password=hash_password("changeme"), name="Administrator", role="admin", is_verified=1, ) logger.info("Initial admin created: admin@localhost / changeme <- bitte Passwort aendern!") # WebSocket Manager ws_manager = WebSocketManager() # Bot loop = asyncio.get_event_loop() bot = MeshBot(db, loop) bot.ws_manager = ws_manager # Scheduler scheduler = Scheduler(bot, ws_manager) # NINA nina = NinaBot(bot.send_message, ws_manager) # Webserver webserver = WebServer(db, ws_manager, bot, scheduler, nina) runner = await webserver.start(config.get("web.host", "0.0.0.0"), config.get("web.port", 8080)) # Connect Meshtastic in a thread (blocking call) connect_thread = threading.Thread(target=bot.connect, daemon=True) connect_thread.start() # Watch config for changes asyncio.create_task(config.watch()) # Scheduler tasks asyncio.create_task(scheduler.watch()) asyncio.create_task(scheduler.run()) # NINA tasks asyncio.create_task(nina.watch()) await nina.start() # Wait for shutdown stop_event = asyncio.Event() def _signal_handler(): logger.info("Shutdown signal received") stop_event.set() for sig in (signal.SIGINT, signal.SIGTERM): loop.add_signal_handler(sig, _signal_handler) try: await stop_event.wait() finally: logger.info("Shutting down...") try: await nina.stop() except Exception: logger.exception("Error stopping NINA") try: bot.disconnect() except Exception: logger.exception("Error disconnecting bot") try: await ws_manager.close_all() except Exception: logger.exception("Error closing WebSocket connections") try: await runner.cleanup() except Exception: logger.exception("Error cleaning up web runner") await db.close() logger.info("Shutdown complete") if __name__ == "__main__": asyncio.run(main())