{% extends 'signage/_layout.html.twig' %} {% import 'signage/_macros.html.twig' as sg %} {% block signage_title %}{{ device.name }}{% endblock %} {% block vendor_stylesheets %}{% endblock %} {% block signage_javascripts %} {% endblock %} {% set weekday_labels = {1: 'Mo', 2: 'Di', 3: 'Mi', 4: 'Do', 5: 'Fr', 6: 'Sa', 7: 'So'} %} {% set source_labels = {'default': 'Standard', 'event': 'Termin', 'command': 'Sofort'} %} {% set tz = zone.name %} {% block signage_content %} {% set s = status %} {% set editor = is_granted('ROLE_SIGNAGE_EDITOR') %} {% set admin = is_granted('ROLE_SIGNAGE_ADMIN') %} {% set colors = {} %} {% for p in draft.manifest.playlists %}{% set colors = colors|merge({('k' ~ p.id): loop.index0 % 8}) %}{% endfor %} {% set can_publish = draft.errors is empty and draft_changed %} Alle Geräte

{{ device.name }}

{{ sg.reachability(s) }} {{ device.deviceKey }} {{ device.mode == 'split' ? 'Zwei Bildschirme' : 'Gespiegelt' }} {{ constant('App\\Entity\\Signage\\SignageDevice::PLAYBACKS')[device.playback] }} {% for target in device.targets %}{{ sg.orientation(device.orientation(target), device.targets|length > 1 ? target ~ ': ') }}{% endfor %} {{ tz }} {% if device.hostname %}{{ device.hostname }}{% endif %} {% if device.playerVersion %}Player {{ device.playerVersion }}{% endif %}
{% if editor %}
{% endif %}
{# ================= Übersicht ================= #}

Veröffentlichung

{{ sg.release_steps(s) }} {% if s.download and s.download.percent is not null and s.release_state == 'transferring' %}
Version {{ s.download.version ?? '?' }} wird übertragen · {{ s.download.missing ?? '?' }} Dateien fehlen {{ sg.bytes(s.download.bytes_done) }} / {{ sg.bytes(s.download.bytes_total) }}
{% endif %} {% if draft.errors is not empty %}
Der Entwurf kann so nicht veröffentlicht werden
    {% for e in draft.errors %}
  • {{ e }}
  • {% endfor %}
{% elseif draft_changed %}
{{ latest ? 'Änderungen gegenüber Version ' ~ latest.version : 'Noch nichts veröffentlicht' }} Neue Version {{ draft.manifest.version }}: {{ draft.manifest.media|length }} Videos, {{ draft.manifest.playlists|length }} Playlists, {{ draft.manifest.events|length }} Termine. {% if editor %}Oben rechts auf „Veröffentlichen“ klicken.{% endif %}
{% else %}
Alles veröffentlichtDer Entwurf entspricht Version {{ latest.version }}.
{% endif %}
Veröffentlicht
{% if latest %}Version {{ latest.version }} · {{ latest.createdAt|date('d.m.Y H:i', tz) }} · {{ latest.createdBy ?? 'system' }}{% else %}–{% endif %}
Auf dem Gerät
aktiv {{ s.active_version ?? '–' }}{% if s.pending_version %} · ausstehend {{ s.pending_version }}{% endif %}

Live vom Gerät

{% if s.last_heartbeat_at %}Rückmeldung {{ s.last_heartbeat_at|date('H:i:s', tz) }}{% endif %}
{% if not s.reachable and s.last_heartbeat_at %}
Keine Rückmeldung seit über 2 Minuten. Das heißt nicht, dass die Wiedergabe steht.
{% elseif not s.last_heartbeat_at %}
Das Gerät hat sich noch nie gemeldet. Einrichtung siehe Tab „Einstellungen“.
{% endif %}
{% for o in s.outputs %}
{{ o.target }} · {{ o.alive ? 'läuft' : 'steht' }}
{{ playlist_names[o.playlist_id] ?? o.playlist_id ?? 'keine Playlist' }}
{% for e in o.errors %}
{{ e }}
{% endfor %}
{% if o.source %}{{ source_labels[o.source] ?? o.source }}{% endif %}
{% else %}
Keine Angaben.
{% endfor %}
Speicher frei
{{ sg.bytes(s.disk_free_bytes) }}
Uhrabweichung
{{ s.clock_offset_s is not null ? s.clock_offset_s|number_format(1, ',', '.') ~ ' s' : '–' }}
IP
{{ device.lastHeartbeatIp ?? '–' }}
{% if s.errors is not empty %}
    {% for e in s.errors %}
  • {{ e }}
  • {% endfor %}
{% endif %}

Sofortwiedergabe

Spielt eine Playlist für begrenzte Zeit – unabhängig vom Kalender.
{% if editor %} {% if latest %}
{% set outputs = latest.manifest.device.outputs %}
{% if outputs|length > 1 %}
{% for o in outputs %} {% endfor %}
{% else %}
{{ outputs[0].target ?? '–' }}
{% endif %}
{% for m in [15, 30, 60, 120] %}{% endfor %}
min
{% else %}

Erst eine Version veröffentlichen.

{% endif %} {% endif %}
    {% for row in commands %} {% set c = row.command %} {% set last = c.results|last %}
  • {{ playlist_names[c.playlistKey] ?? c.playlistKey }} auf {{ c.target }}
    {{ c.issuedAt|date('d.m. H:i', tz) }} – {{ c.expiresAt|date('H:i', tz) }} {% if c.lastState %}Gerät: {{ c.lastState }} {{ c.lastStateAt|date('H:i:s', tz) }}{% if last and last.detail %} · {{ last.detail }}{% endif %}{% endif %} {{ c.commandKey }}
    {% if row.open %}
    {% endif %}
    {% if row.open %}{{ row.state }}{% else %}{{ row.state }}{% endif %} {% if row.open and editor %}
    {% endif %}
  • {% else %}
  • Bisher keine Sofortaufträge.
  • {% endfor %}
{# ================= Zeitplan ================= #} {% set cal_orientations = {} %} {% for target in device.targets %}{% set cal_orientations = cal_orientations|merge({(target): device.orientation(target)}) %}{% endfor %}

Planen {{ editor ? 'Playlist oder Video in den Kalender ziehen · Termin verschieben · Unterkante ziehen ändert das Ende' : 'nur Ansicht' }}

{% if device.targets|length > 1 %}
{% for target in device.targets %} {% endfor %}
{% endif %}
{% if editor %} {% endif %}

Lokale Gerätezeit ({{ tz }}). Änderungen werden sofort gespeichert und auf Überschneidungen geprüft, aufs Gerät kommen sie erst mit „Veröffentlichen“.

Was läuft wann? Vorschau des Entwurfs

{% set step = preview_days ~ ' days' %}
Heute
{% for p in draft.manifest.playlists %}{{ p.name }}{% endfor %} blass: Standard-Playlist schraffiert: Sofortauftrag
{% for target, rows in preview %}
{{ target }}
03691215182124
{% for row in rows %} {% set dow = row.date|date('N', false) %}
{{ weekday_labels[dow] }} {{ row.date|date('d.m.', false) }}
{% for seg in row.segments %} {% set label = playlist_names[seg.playlist_id] ?? (seg.playlist_id ?: 'keine Playlist') %}
{{ label }}
{% endfor %}
{% endfor %} {% endfor %}

Lokale Gerätezeit ({{ tz }}). Der Player rechnet selbst; maßgeblich ist das Gerät.

Termine

{% if editor %}Termin anlegen{% endif %}
{% if events is empty %}
Keine Termine

Es läuft die Standard-Playlist. Mit Terminen spielst du zu festen Zeiten etwas anderes.

{% else %}
    {% for e in events %}
  • {{ e.title }}
    {{ e.playlist.name }} {{ e.target }} ab {{ e.startDate|date('d.m.Y') }}{% if e.untilDate %} bis {{ e.untilDate|date('d.m.Y') }}{% endif %} {% if e.exceptions|length %}{{ e.exceptions|length }} Ausnahme{{ e.exceptions|length > 1 ? 'n' }}{% endif %} {% if e.target not in device.targets %}passt nicht zum Modus{% endif %}
    {% for n, l in weekday_labels %}{{ l }}{% endfor %} {{ e.startTime|slice(0, 5) }}–{{ e.endTime|slice(0, 5) }}{{ e.endDayOffset ? ' +1' }} {{ e.priority }} {% if editor %}
    {% endif %}
  • {% endfor %}
{% endif %}
{# ================= Einstellungen ================= #}

Standard-Playlist

Läuft immer dann, wenn kein Termin und keine Sofortwiedergabe aktiv ist.

{% for target in device.targets %} {% set current = device.outputConfig(target).default_playlist %}
{% endfor %} {% if editor %}{% endif %}

Einrichtung und Zugang

Eingerichtet
{{ device.enrolledAt ? device.enrolledAt|date('d.m.Y H:i', tz) : 'noch nicht' }}
Aktive Tokens
{{ active_tokens|length }} {% for t in active_tokens %}
seit {{ t.createdAt|date('d.m.Y H:i', tz) }} · zuletzt {{ t.lastUsedAt ? t.lastUsedAt|date('d.m.Y H:i', tz) : 'nie' }}
{% endfor %}
{% if admin %}
{% if active_tokens is not empty %}
{% endif %}

Ein neuer Einrichtungscode ersetzt beim Einlösen alle bisherigen Tokens.

{% endif %}

Gerätekonfiguration

{% if admin %}
Bildschirme
Nach einem Moduswechsel erneut speichern, um Anschlüsse der neuen Ausgaben einzutragen.
Wiedergabe
{% for key, label in constant('App\\Entity\\Signage\\SignageDevice::PLAYBACKS') %}
{% endfor %}
Bei „Browser“ enthält das Manifest device.playback mit dem Wert browser. Bei HDMI fehlt das Feld, damit ältere Player die Version annehmen. Der Player muss die Option kennen, sonst lehnt er eine Browser-Version ab. Adressen am Gerät: bei gleichem Bild /wand/shared, sonst /wand/screen-1 und /wand/screen-2.
Ausgaben
{% for target in device.targets %} {% set cfg = device.outputConfig(target) %}
{{ target }}
{% endfor %}
Das Gerät dreht den Bildschirm, sobald die nächste Version aktiv wird. Im gespiegelten Modus gilt die Drehung für beide Bildschirme, im getrennten Modus braucht jede gedrehte Ausgabe ihren eigenen Anschluss (z. B. HDMI-1). Welche Richtung zur Montage passt, sieht man erst am Bild: Steht es auf dem Kopf, die andere Richtung veröffentlichen.
Wirksam erst nach dem Veröffentlichen.
{% else %}
Wiedergabe
{{ constant('App\\Entity\\Signage\\SignageDevice::PLAYBACKS')[device.playback] }}
{% for target in device.targets %} {% set cfg = device.outputConfig(target) %}
{{ target }}
{{ constant('App\\Entity\\Signage\\SignageDevice::ROTATIONS')[cfg.rotation] }} · Anschluss {{ cfg.connector ?? 'automatisch' }} · Audio {{ cfg.audio_device ?? 'stumm' }}
{% endfor %}
{% endif %}
{# ================= Versionen ================= #}
{% if releases is empty %}
Noch keine Version

Sobald du veröffentlichst, erscheint hier jede Version mit ihrem Manifest.

{% else %}
    {% for r in releases %}
  • v{{ r.version }}
    Version {{ r.version }} {% if r.version == s.active_version %}aktiv auf dem Gerät{% endif %} {% if r.version == s.pending_version and r.version != s.active_version %}ausstehend{% endif %}
    {{ r.createdAt|date('d.m.Y H:i', tz) }}{{ r.createdBy ?? 'system' }}
    Manifest {{ r.manifestSha256|slice(0, 8) }}
  • {% endfor %}
{% endif %}
{% endblock %}