first commit
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
"""Service de chargement et sauvegarde de la configuration YAML."""
|
||||
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AppConfig:
|
||||
name: str = "JH Photomaton"
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8090
|
||||
debug: bool = False
|
||||
secret_key: str = ""
|
||||
admin_password: str = "PhotoBooth2026!"
|
||||
|
||||
|
||||
@dataclass
|
||||
class PhotoboothConfig:
|
||||
base_url: str = "http://localhost:8083"
|
||||
data_dir: str = "/home/pi/photobooth-data"
|
||||
config_file: str = "/home/pi/.config/photobooth-app/config.json"
|
||||
media_dir: str = "/home/pi/photobooth-data/media/processed_full"
|
||||
userdata_dir: str = "/home/pi/photobooth-data/userdata"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ButtonConfig:
|
||||
pin: int = 23
|
||||
relay_pin: int = 12
|
||||
debounce_ms: int = 50
|
||||
double_click_ms: int = 400
|
||||
long_press_ms: int = 1500
|
||||
max_clicks: int = 4
|
||||
long_press_action: str = "print_last"
|
||||
print_enabled: bool = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class LEDEffectConfig:
|
||||
color: list = field(default_factory=lambda: [0, 30, 80])
|
||||
mode: str = "solid"
|
||||
speed: float = 0.05
|
||||
flashes: int = 2
|
||||
flash_duration: float = 0.1
|
||||
duration: float = 2.0
|
||||
|
||||
|
||||
@dataclass
|
||||
class LEDConfig:
|
||||
pin: int = 18
|
||||
count: int = 35
|
||||
brightness: int = 180
|
||||
freq_hz: int = 800000
|
||||
dma: int = 10
|
||||
strip_type: str = "WS2812"
|
||||
effects: dict = field(default_factory=dict)
|
||||
|
||||
def get_effect(self, name: str) -> LEDEffectConfig:
|
||||
raw = self.effects.get(name, {})
|
||||
return LEDEffectConfig(
|
||||
color=raw.get("color", [0, 30, 80]),
|
||||
mode=raw.get("mode", "solid"),
|
||||
speed=raw.get("speed", 0.05),
|
||||
flashes=raw.get("flashes", 2),
|
||||
flash_duration=raw.get("flash_duration", 0.1),
|
||||
duration=raw.get("duration", 2.0),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PrintConfig:
|
||||
mode: str = "validation"
|
||||
script_path: str = "/home/pi/photobooth-data/script/script_print.sh"
|
||||
default_copies: int = 1
|
||||
printers: list = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class GalleryConfig:
|
||||
public_enabled: bool = True
|
||||
photos_per_page: int = 24
|
||||
qr_base_url: str = "https://photomaton.lessapinsduweb.com"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Config:
|
||||
app: AppConfig = field(default_factory=AppConfig)
|
||||
photobooth: PhotoboothConfig = field(default_factory=PhotoboothConfig)
|
||||
button: ButtonConfig = field(default_factory=ButtonConfig)
|
||||
leds: LEDConfig = field(default_factory=LEDConfig)
|
||||
print: PrintConfig = field(default_factory=PrintConfig)
|
||||
gallery: GalleryConfig = field(default_factory=GalleryConfig)
|
||||
button_actions: dict = field(default_factory=dict)
|
||||
|
||||
|
||||
class ConfigService:
|
||||
def __init__(self, path: Path):
|
||||
self.path = path
|
||||
self._config: Config | None = None
|
||||
|
||||
def load(self) -> Config:
|
||||
if not self.path.exists():
|
||||
logger.warning(f"Config introuvable: {self.path} — utilisation des valeurs par défaut")
|
||||
self._config = Config()
|
||||
return self._config
|
||||
|
||||
with open(self.path, encoding="utf-8") as f:
|
||||
raw: dict[str, Any] = yaml.safe_load(f) or {}
|
||||
|
||||
cfg = Config()
|
||||
|
||||
if "app" in raw:
|
||||
cfg.app = AppConfig(**{k: v for k, v in raw["app"].items() if hasattr(AppConfig, k)})
|
||||
|
||||
if "photobooth" in raw:
|
||||
cfg.photobooth = PhotoboothConfig(**{k: v for k, v in raw["photobooth"].items() if hasattr(PhotoboothConfig, k)})
|
||||
|
||||
if "button" in raw:
|
||||
cfg.button = ButtonConfig(**{k: v for k, v in raw["button"].items() if hasattr(ButtonConfig, k)})
|
||||
|
||||
if "leds" in raw:
|
||||
led_raw = raw["leds"]
|
||||
cfg.leds = LEDConfig(
|
||||
pin=led_raw.get("pin", 18),
|
||||
count=led_raw.get("count", 35),
|
||||
brightness=led_raw.get("brightness", 180),
|
||||
freq_hz=led_raw.get("freq_hz", 800000),
|
||||
dma=led_raw.get("dma", 10),
|
||||
strip_type=led_raw.get("strip_type", "WS2812"),
|
||||
effects=led_raw.get("effects", {}),
|
||||
)
|
||||
|
||||
if "print" in raw:
|
||||
cfg.print = PrintConfig(**{k: v for k, v in raw["print"].items() if hasattr(PrintConfig, k)})
|
||||
|
||||
if "gallery" in raw:
|
||||
cfg.gallery = GalleryConfig(**{k: v for k, v in raw["gallery"].items() if hasattr(GalleryConfig, k)})
|
||||
|
||||
cfg.button_actions = raw.get("button_actions", {
|
||||
1: {"label": "Photo normale", "photobooth_index": 0},
|
||||
2: {"label": "Photo étoile", "photobooth_index": 1},
|
||||
3: {"label": "Photo cailloux", "photobooth_index": 2},
|
||||
4: {"label": "Photo soirée", "photobooth_index": 3},
|
||||
})
|
||||
|
||||
self._config = cfg
|
||||
logger.info("Configuration chargée depuis %s", self.path)
|
||||
return cfg
|
||||
|
||||
def save_button_actions(self, button_actions: dict):
|
||||
"""Met à jour uniquement la section button_actions dans settings.yaml."""
|
||||
with open(self.path, encoding="utf-8") as f:
|
||||
raw = yaml.safe_load(f) or {}
|
||||
|
||||
raw["button_actions"] = button_actions
|
||||
|
||||
with open(self.path, "w", encoding="utf-8") as f:
|
||||
yaml.dump(raw, f, allow_unicode=True, default_flow_style=False)
|
||||
|
||||
if self._config:
|
||||
self._config.button_actions = button_actions
|
||||
|
||||
def save_print_mode(self, mode: str):
|
||||
with open(self.path, encoding="utf-8") as f:
|
||||
raw = yaml.safe_load(f) or {}
|
||||
raw.setdefault("print", {})["mode"] = mode
|
||||
with open(self.path, "w", encoding="utf-8") as f:
|
||||
yaml.dump(raw, f, allow_unicode=True, default_flow_style=False)
|
||||
if self._config:
|
||||
self._config.print.mode = mode
|
||||
|
||||
@property
|
||||
def config(self) -> Config:
|
||||
if self._config is None:
|
||||
self.load()
|
||||
return self._config
|
||||
Reference in New Issue
Block a user