Files
vf-cuda-grid/controller/cuda_grid_controller/state.py
T
gx 37232ae1b9 controller: Phase 3 — Python sidecar skeleton (MQTT + ZMQ + HTTP + HA Discovery)
cuda-grid-controller (Python 3.11+) — control plane между HA/MQTT/HTTP
и FFmpeg's vf_cuda_grid filter через ZMQ.

Modules (~700 LOC Python):
- config.py — Pydantic schema (broker, instances[], ha_discovery, http, log) + YAML loader
- layouts.py — registry известных layouts (sync с vf_cuda_grid.c Phase 2)
- ha_discovery.py — HA MQTT Discovery payloads (select.layout, sensor.current_layout,
  binary_sensor.online per instance + global device entry)
- zmq_client.py — async ZMQ REQ socket к FFmpeg zmq filter
  (target command args → reply parsing)
- state.py — in-memory ControllerState (active_layout per instance, asyncio.Lock)
- mqtt_loop.py — aiomqtt async loop: subscribe cuda_grid/cmd/<inst>/+/+,
  publish cuda_grid/state/* (retained) + cuda_grid/event/*, LWT, HA status reconnect
- dispatch.py — CommandDispatcher: layout.set action → ZMQ send_command + state update + events
- http_api.py — FastAPI: /health, /layouts, /state, POST /layout/{inst}/set
- __main__.py — typer CLI, asyncio.gather(mqtt_loop, uvicorn.server)

Examples + Dockerfile:
- examples/controller.yaml — 2 instances (livingroom_tv, public_stream)
- Dockerfile — python:3.11-slim, ENTRYPOINT cuda-grid-controller
- README — overview, usage, FFmpeg side filter graph

End-to-end flow ready:
  HA dashboard → MQTT → controller → ZMQ → FFmpeg process_command → layout switch
  ↓
  state публикуется обратно в MQTT → HA UI обновляется

Phase 3 deliverable per gx/vf-cuda-grid#1. Phase 4 = overlays (rect/text/icon).
2026-05-19 21:52:11 +01:00

34 lines
980 B
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""In-memory state controller'а."""
from __future__ import annotations
import asyncio
from dataclasses import dataclass, field
@dataclass
class InstanceState:
name: str
active_layout: str
# Future: fps_out, dropped_frames, motion_cameras, last_event_ts
@dataclass
class ControllerState:
instances: dict[str, InstanceState] = field(default_factory=dict)
_lock: asyncio.Lock = field(default_factory=asyncio.Lock)
async def set_layout(self, instance: str, layout: str) -> None:
async with self._lock:
st = self.instances.get(instance)
if st is None:
st = InstanceState(name=instance, active_layout=layout)
self.instances[instance] = st
else:
st.active_layout = layout
async def get_layout(self, instance: str) -> str | None:
async with self._lock:
st = self.instances.get(instance)
return st.active_layout if st else None