ghost-mirror/ghost_trigger.py
zrnek c8a7682eff Aktualizovat ghost_trigger.py
po prvním načtení skriptu se analyzuje složka s videi a udělá se seznam videí a jejich délka, aby pak přechod byl plynulejší. Dle délky souboru se pak i nastavuje zámek senzoru.
2025-10-30 21:33:56 +01:00

154 lines
No EOL
5 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
=============================================================================
Skript pro ovládání "digitálního ducha" v zrcadle (Projekt Zrcadlo)
=============================================================================
:Název skriptu: ghost_trigger.py
:Autor: zrnek
:Verze: 2.0.0 (Finální verze s IPC ovládáním)
:Datum: 30. října 2025
:Popis:
Tento skript běží na Raspberry Pi Zero 2 W a ovládá iluzi "ducha"
pro Halloweenskou párty.
Hlavní funkce:
1. Při startu analyzuje video soubory ve složce 'videos' a zjistí jejich délku.
2. Spustí přehrávač 'mpv' v režimu na pozadí, který zobrazuje černý
obrázek ('black.png') a čeká na příkazy.
3. Monitoruje pohybový PIR senzor.
4. Při detekci pohybu:
a. Okamžitě uzamkne senzor, aby se zabránilo opakovanému spuštění.
b. Počká na krátké "zpoždění pro překvapení" (definované v config.yaml).
c. Pošle 'mpv' přes 'socat' příkaz k plynulému přehrání náhodného videa.
d. Po skončení videa pošle příkaz pro návrat k černé obrazovce.
e. Udrží senzor zamčený po přesnou dobu trvání videa.
f. Odemkne senzor a čeká na další pohyb.
:Závislosti:
- Python 3
- Knihovny: PyYAML (`python3-yaml`), gpiozero (`python3-gpiozero`)
- Systémové nástroje: `mpv`, `socat`, `ffprobe` (z balíku `ffmpeg`)
:Struktura projektu:
/home/zrnek/duch/
├── config/
│ └── config.yaml (Konfigurační soubor)
├── videos/
│ ├── video1.mp4 (Soubory s videi duchů)
│ └── ...
├── black.png (Obrázek pro černé pozadí)
└── ghost_trigger.py (Tento skript)
:Spuštění:
Skript je navržen pro automatické spuštění jako systémová služba
prostřednictvím 'ghost-player.service'.
"""
import os
import yaml
import random
import subprocess
import time
from gpiozero import MotionSensor
from signal import pause
# --- Načtení konfigurace ---
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config/config.yaml")
config = {}
try:
with open(CONFIG_PATH, 'r') as f:
config = yaml.safe_load(f)
except Exception:
exit()
PIR_PIN = config.get('pir_pin', 17)
VIDEOS_DIR = os.path.join(os.path.dirname(__file__), config.get('videos_dir', 'videos'))
BLACK_IMAGE_PATH = os.path.join(os.path.dirname(__file__), "black.png")
VIDEO_EXTENSIONS = tuple(config.get('video_extensions', ['.mp4', '.mkv']))
MPV_SOCKET_PATH = "/tmp/mpvsocket"
# <<< Načtení hodnot ze souboru config.yaml >>>
SURPRISE_DELAY = config.get('surprise_delay', 1.5)
SENSOR_LOCK_DURATION = config.get('sensor_lock_duration', 30)
available_videos = []
is_playing = False
# --- Funkce pro ovládání mpv ---
def send_mpv_command(command_json):
"""Pošle JSON příkaz do běžícího mpv přes socat."""
full_command = f"echo '{command_json}' | socat - {MPV_SOCKET_PATH}"
try:
subprocess.run(full_command, shell=True, check=True, capture_output=True, text=True)
except subprocess.CalledProcessError:
pass
# --- Hlavní logika ---
def load_videos():
"""Načte seznam video souborů."""
global available_videos
try:
files = os.listdir(VIDEOS_DIR)
available_videos = [os.path.join(VIDEOS_DIR, f) for f in files if f.lower().endswith(VIDEO_EXTENSIONS)]
except Exception:
available_videos = []
def play_random_ghost():
"""Zpracuje detekci pohybu, počká a přehraje video, pokud již jiné neběží."""
global is_playing
if is_playing:
print("Pohyb ignorován, video již běží.")
return
try:
is_playing = True
print(f"Pohyb detekován, čekám {SURPRISE_DELAY}s pro moment překvapení...")
# <<< NOVINKA: Použití proměnné ze souboru config.yaml >>>
time.sleep(SURPRISE_DELAY)
print("Posílám příkaz do mpv...")
load_videos()
if not available_videos:
return
video_to_play = random.choice(available_videos)
command1 = f'{{"command": ["loadfile", "{video_to_play}", "replace", "loop=no"]}}'
send_mpv_command(command1)
command2 = f'{{"command": ["loadfile", "{BLACK_IMAGE_PATH}", "append-play"]}}'
send_mpv_command(command2)
finally:
print(f"Senzor bude znovu aktivní za {SENSOR_LOCK_DURATION}s.")
# <<< NOVINKA: Použití proměnné ze souboru config.yaml >>>
time.sleep(SENSOR_LOCK_DURATION)
is_playing = False
print("Senzor je opět aktivní.")
# --- Hlavní spuštění ---
os.system("pkill -f mpv")
time.sleep(1)
load_videos()
subprocess.Popen([
"/usr/bin/mpv", "--no-audio", "--fullscreen", "--loop-file=inf", "--idle",
f"--input-ipc-server={MPV_SOCKET_PATH}", BLACK_IMAGE_PATH
])
time.sleep(2)
pir = MotionSensor(PIR_PIN)
pir.when_motion = play_random_ghost
print("Aplikace ducha běží, mpv je ovládáno na dálku. Čekám na pohyb...")
try:
pause()
except KeyboardInterrupt:
print("Ukončuji aplikaci.")
os.system("pkill -f mpv")