import asyncio
import logging
from devplacepy.database import get_setting, set_setting
logger = logging.getLogger(__name__)
class ServiceManager:
def __init__(self):
self._services = {}
self._owns_lock = False
def set_lock_owner(self, owns: bool) -> None:
self._owns_lock = owns
def owns_lock(self) -> bool:
return self._owns_lock
def register(self, service) -> None:
self._services[service.name] = service
logger.info(f"Registered service: {service.name}")
def get_service(self, name: str):
return self._services.get(name)
def describe_all(self) -> list[dict]:
return [svc.describe() for svc in self._services.values()]
def set_enabled(self, name: str, enabled: bool) -> bool:
svc = self.get_service(name)
if svc is None:
return False
set_setting(svc.enabled_key, "1" if enabled else "0")
logger.info(f"Service {name} enabled={enabled}")
return True
def send_command(self, name: str, verb: str) -> bool:
svc = self.get_service(name)
if svc is None:
return False
raw = get_setting(svc.command_key, "")
counter = 0
if raw and ":" in raw:
try:
counter = int(raw.split(":", 1)[1])
except ValueError:
counter = 0
set_setting(svc.command_key, f"{verb}:{counter + 1}")
logger.info(f"Service {name} command={verb}")
return True
def save_config(self, name: str, form: dict) -> dict | None:
svc = self.get_service(name)
if svc is None:
return None
errors = {}
valid = {}
for field in svc.all_fields():
if field.key not in form:
continue
raw = form[field.key]
if field.secret and raw == "":
continue
try:
valid[field.key] = field.to_storage(field.coerce(raw))
except ValueError as e:
errors[field.key] = str(e)
if errors:
return {"ok": False, "errors": errors}
for key, value in valid.items():
set_setting(key, value)
logger.info(f"Service {name} config saved ({len(valid)} fields)")
return {"ok": True, "errors": {}}
def supervise(self) -> None:
for svc in self._services.values():
svc.start_supervisor()
logger.info(f"Supervising service: {svc.name}")
async def shutdown_all(self) -> None:
tasks = []
for svc in self._services.values():
svc.request_shutdown()
if svc._task is not None:
svc._task.cancel()
tasks.append(svc._task)
if tasks:
await asyncio.gather(*tasks, return_exceptions=True)
logger.info("All services stopped")
service_manager = ServiceManager()