second part of PR #578

Signed-off-by: Paulchen-Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
Paulchen-Panther 2019-07-14 22:43:22 +02:00
parent ea796160af
commit 90599e820a
No known key found for this signature in database
GPG Key ID: 84E3B692456B6840
52 changed files with 2354 additions and 536 deletions

View File

@ -4,20 +4,6 @@
<h3 class="page-header"><i class="fa fa-wrench fa-fw"></i><span data-i18n="conf_general_label_title">General</span></h3>
<div class="row" id="conf_cont"></div>
<div class="row">
<div class="col-lg-6" id="conf_imp">
<div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-wrench fa-fw"></i><span data-i18n="conf_general_impexp_title"></span></div>
<div class="panel-body">
<p data-i18n="conf_general_impexp_l1" style="font-weight:bold"></p>
<p data-i18n="conf_general_impexp_l2" style="font-weight:bold"></p>
<input type="file" id="select_import_conf" accept=".json">
</div>
<div class="panel-footer" style="text-align:right">
<button type="file" class="btn btn-primary" id="btn_import_conf" data-i18n="conf_general_impexp_impbtn" disabled>Import</button>
<button class="btn btn-primary" id="btn_export_conf" data-i18n="conf_general_impexp_expbtn">Export</button>
</div>
</div>
</div>
<div class="col-lg-6" id="tok_desc">
<div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-key fa-fw"></i><span data-i18n="conf_general_tok_title"></span></div>
@ -39,6 +25,41 @@
</div>
</div>
</div>
<div class="col-lg-6" id="inst_desc">
<div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-wrench fa-fw"></i><span data-i18n="conf_general_inst_title"></span></div>
<div class="panel-body">
<div id="inst_desc_cont"></div>
<div id="itable"></div>
<div style="margin: 30px 0; border-top:1px solid #d0d0d0"></div>
<div class="row">
<div class="col-lg-6">
<p data-i18n="conf_general_inst_name_title" style="font-weight:bold"></p>
</div>
<div class="col-lg-6">
<input class="form-control" id="inst_name" type="text"></input>
</div>
</div>
<div>
<button class="btn btn-primary" id="btn_create_inst" data-i18n="conf_general_createInst_btn" disabled>Create New Token</button>
</div>
</div>
</div>
</div>
<div class="col-lg-6" id="conf_imp">
<div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-wrench fa-fw"></i><span data-i18n="conf_general_impexp_title"></span></div>
<div class="panel-body">
<p data-i18n="conf_general_impexp_l1" style="font-weight:bold"></p>
<p data-i18n="conf_general_impexp_l2" style="font-weight:bold"></p>
<input type="file" id="select_import_conf" accept=".json">
</div>
<div class="panel-footer" style="text-align:right">
<button type="file" class="btn btn-primary" id="btn_import_conf" data-i18n="conf_general_impexp_impbtn" disabled>Import</button>
<button class="btn btn-primary" id="btn_export_conf" data-i18n="conf_general_impexp_expbtn">Export</button>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,4 +1,5 @@
{
<<<<<<< HEAD
"general_webui_title": "Hyperion - Web Konfiguration",
"general_country_de": "Deutschland",
"general_country_us": "Amerika",
@ -776,4 +777,791 @@
"edt_msg_button_delete_row_title_short": "Löschen",
"edt_msg_button_collapse": "Einklappen",
"edt_msg_button_expand": "Ausklappen"
=======
"general_webui_title": "Hyperion - Web Konfiguration",
"general_country_de": "Deutschland",
"general_country_us": "Amerika",
"general_country_uk": "England",
"general_country_fr": "Frankreich",
"general_country_es": "Spanien",
"general_country_it": "Italien",
"general_country_nl": "Niederlande",
"general_speech_de": "Deutsch",
"general_speech_en": "Englisch",
"general_speech_es": "Spanisch",
"general_speech_it": "Italienisch",
"general_speech_cs": "Tschechisch",
"general_access_default": "Standard",
"general_access_advanced": "Fortgeschritten",
"general_access_expert": "Experte",
"general_comp_SMOOTHING": "Glättung",
"general_comp_BLACKBORDER": "Schwarze Balken Erkennung",
"general_comp_FORWARDER": "Weiterleitung",
"general_comp_UDPLISTENER": "UDP Listener",
"general_comp_BOBLIGHTSERVER": "Boblight Server",
"general_comp_FLATBUFSERVER": "Flatbuffers Server",
"general_comp_PROTOSERVER": "Protocol Buffers Server",
"general_comp_GRABBER": "Plattform Aufnahme",
"general_comp_V4L": "USB Aufnahme",
"general_comp_LEDDEVICE": "LED Hardware",
"general_col_red": "rot",
"general_col_green": "grün",
"general_col_blue": "blau",
"general_button_savesettings": "Einstellungen speichern",
"general_btn_yes": "Ja",
"general_btn_ok": "OK",
"general_btn_cancel": "Abbrechen",
"general_btn_start" : "Start",
"general_btn_stop" : "Stop",
"general_btn_continue": "Fortfahren",
"general_btn_delete" : "Löschen",
"general_btn_save": "Speichern",
"general_btn_saveandreload": "Speichern und neu laden",
"general_btn_restarthyperion": "Hyperion neustarten",
"general_btn_off": "Aus",
"general_btn_on": "An",
"general_btn_next": "Weiter",
"general_btn_back": "Zurück",
"general_btn_iswitch": "Switch",
"general_wiki_moreto": "Mehr Informationen zu \"$1\" findest du in unserem Wiki",
"dashboard_label_intro": "Das Dashboard zeigt dir Informationen zum Systemstatus, ob Updates verfügbar sind, den Komponentenstatus sowie die letzten Blog-Posts vom Hyperion Team.",
"dashboard_infobox_label_title": "Information",
"dashboard_infobox_label_currenthyp": "Deine Hyperion Version:",
"dashboard_infobox_label_latesthyp": "Aktuellste Hyperion Version:",
"dashboard_infobox_label_platform": "Plattform:",
"dashboard_infobox_label_instance": "Instanz:",
"dashboard_infobox_label_ports": "Ports (flat|proto):",
"dashboard_infobox_label_versionbranch": "Versionszweig:",
"dashboard_infobox_message_updatewarning": "Eine aktuellere Version von Hyperion ist verfügbar! (V$1)",
"dashboard_infobox_message_updatesuccess": "Du nutzt die aktuellste Version von Hyperion.",
"dashboard_infobox_label_statush": "Hyperion Status:",
"dashboard_infobox_label_smartacc": "Schnellzugriff",
"dashboard_infobox_label_enableh": "Aktiviere Hyperion",
"dashboard_infobox_label_disableh": "Deaktiviere Hyperion",
"dashboard_componentbox_label_title": "Komponenten Status",
"dashboard_componentbox_label_comp": "Komponente",
"dashboard_componentbox_label_status": "Status",
"dashboard_newsbox_label_title": "Hyperion-Blog",
"dashboard_newsbox_visitblog": "Besuche den Hyperion-Blog",
"dashboard_newsbox_noconn": "Fehler bei dem Versuch die letzten Blog-Posts zu laden, funtkioniert dein Internet?",
"dashboard_newsbox_readmore": "Weiterlesen",
"dashboard_alert_message_confedit_t": "Konfiguration geändert",
"dashboard_alert_message_confedit": "Deine Hyperion Konfiguration wurde verändert. Um die Änderungen anzuwenden, starte Hyperion neu.",
"dashboard_alert_message_disabled_t" : "LED Hardware instanz deaktiviert",
"dashboard_alert_message_disabled" : "Diese Instanz ist momentan deaktiviert! Um sie zu nutzen, musst du sie zuerst wieder im Dashboard aktivieren.",
"dashboard_alert_message_confsave_success_t": "Konfiguration gespeichert",
"dashboard_alert_message_confsave_success": "Deine Hyperion Konfiguration wurde erfolgreich gespeichert. Deine Änderungen sind somit übernommen.",
"main_menu_dashboard_token": "Dashboard",
"main_menu_configuration_token": "Konfiguration",
"main_menu_general_conf_token": "Allgemein",
"main_menu_leds_conf_token": "LED Hardware",
"main_menu_grabber_conf_token": "Aufnahme Hardware",
"main_menu_effect_conf_token": "Effekte",
"main_menu_colors_conf_token": "Bildverarbeitung",
"main_menu_network_conf_token": "Netzwerk",
"main_menu_remotecontrol_token": "Fernbedienung",
"main_menu_effectsconfigurator_token": "Effekt Konfigurator",
"main_menu_support_token": "Hilfe",
"main_menu_update_token": "Update",
"main_menu_system_token": "System",
"main_menu_input_selection_token": "Eingabeauswahl",
"main_menu_logging_token": "Protokoll",
"main_menu_webconfig_token": "Web Konfiguration",
"main_menu_about_token": "Über Hyperion",
"main_ledsim_title": "LED Visualisierung",
"main_ledsim_text": "Eine live Visualisierung deiner LED Farben, sofern verfügbar kann ein live Video dazugeschalten werden.",
"main_ledsim_btn_toggleleds": "Zeige LEDs",
"main_ledsim_btn_togglelednumber": "LED Nummern",
"main_ledsim_btn_togglelivevideo": "Live Video",
"conf_general_label_title": "Allgemeine Einstellungen",
"conf_general_intro": "Grundsätzliche Einstellungen zu Hyperion oder WebUI, die in keine andere Kategorie passen.",
"conf_general_impexp_title": "Importiere/Exportiere Konfiguration",
"conf_general_impexp_l1": "Importiere eine bestehende Konfiguration, indem du unten eine Datei auswählst und anschließend auf \"Importieren\" klickst.",
"conf_general_impexp_l2": "Exportiere eine Konfiguration, indem du auf \"Exportieren\" klickst. Dein Browser startet einen Download.",
"conf_general_impexp_impbtn": "Importieren",
"conf_general_impexp_expbtn": "Exportieren",
"conf_general_tok_title" : "Token Management",
"conf_general_tok_desc" : "Tokens erlauben andere Anwendungen auf die Hyperion API zuzugreifen. Eine Anwendung kann ein Token anfordern welches von dir bestätigt werden muss oder du erstellst dir selbst ein neues Token. Diese Tokens werden nur benötigt, wenn \"API Autorisierung\" in den Netzwerkeinstellungen aktiviert ist.",
"conf_general_tok_cidhead" : "Beschreibung",
"conf_general_tok_lastuse" : "Zuletzt genutzt",
"conf_general_tok_comment_title" : "Token Beschreibung",
"conf_general_createToken_btn" : "Erstelle Token",
"conf_general_tok_diaTitle" : "Neues Token erstellt!",
"conf_general_tok_diaMsg" : "Hier ist dein neues Token, welches für den Zugriff auf die Hyperion API verwendet werden kann. Aus Sicherheitsgründen können Tokens nach der Erstellung nur einmalig eingesehen werden, notiere es dir daher jetzt.",
"conf_general_inst_title" : "LED Hardware Instanz Management",
"conf_general_inst_desc" : "Nutze unterschiedliche LED Hardware zur selben Zeit. Instanzen laufen unabhängig voneinander, das ermöglicht verschiedene LED Layouts und Kalibrierungseinstellungen. Laufende Instanzen sind über die obere Iconleiste auswählbar",
"conf_general_inst_namehead" : "Instanzname",
"conf_general_inst_actionhead" : "Aktion",
"conf_general_inst_name_title" : "Neuer Instanzname",
"conf_general_createInst_btn" : "Instanz erstellen",
"conf_general_inst_delreq_h" : "LED Hardware instanz löschen",
"conf_general_inst_delreq_t" : "Bist du dir sicher die Instanz \"$1\" zu löschen? Alle zugehörigen Einstellungen werden ebenfalls entfernt.",
"conf_helptable_option": "Option",
"conf_helptable_expl": "Erklärung",
"conf_effect_path_intro": "Hier kannst du Ordner angeben, die beim Laden von Effekten berücksichtig werden sollen. Zusätzlich können Effekte anhand ihres Namens deaktiviert werden um sie aus Listen zu löschen.",
"conf_effect_fgeff_intro": "Definiere einen Start Effekt/Farbe, dieser wird angezeigt, wenn Hyperion startet für die angegebene Dauer.",
"conf_effect_bgeff_intro": "Definiere einen Hintergrund Effekt/Farbe. Dieser wird aktiv, wenn Hyperion sich im Leerlauf befindet. Wird immer mit Priorität 255 gestartet.",
"conf_leds_device_intro": "Wähle eine Methode zur Steuerung deiner LEDs aus, sie sind unterteilt in verschiedene Kategorien. Neben den allgemeinen Optionen die für alle gültig sind, gibt es auch spezfische die sich unterscheiden je nach Wahl.",
"conf_leds_layout_intro": "Du benötigst ebenfalls ein LED Layout, welches deine LED-Positionen wiederspiegelt. Das klassische Layout wird für gewöhnlichen für TVs verwendet, Hyperion unterstützt aber auch LED Wände (Matrix). Die Ansicht des LAYOUTS ist die perspektive VOR dem Fernseher, nicht dahinter.",
"conf_leds_nav_label_ledcontroller": "LED Steuerung",
"conf_leds_nav_label_ledlayout": "LED Layout",
"conf_leds_contr_label_contrtype": "Steuerungstyp:",
"conf_leds_optgroup_RPiSPI": "RPi SPI",
"conf_leds_optgroup_RPiPWM": "RPi PWM",
"conf_leds_optgroup_RPiGPIO": "RPi GPIO",
"conf_leds_optgroup_network": "Netzwerk",
"conf_leds_optgroup_usb": "USB",
"conf_leds_optgroup_debug": "Debug",
"conf_leds_layout_btn_checklist": "Zeige Checkliste",
"conf_leds_layout_checkp1": "Die schwarze eingefärbte LED ist die erste LED. Das ist der Punkt, an dem die Daten eingespeist werden.",
"conf_leds_layout_checkp2": "Das Layout ist die Ansicht vor dem Fernseher stehend, nicht dahinter.",
"conf_leds_layout_checkp3": "Stelle sicher, dass die Richtung richtig eingestellt ist, dazu ist die zweite und dritte LED grau markiert um den Datenfluss anzuzeigen.",
"conf_leds_layout_checkp4": "Vorgang Lücke: Solltest du eine Lücke benötigen, ignoriere diese bei der LED Angabe Oben/Unten/Rechts/Links und gebe anschließend unter Lückenlänge an, wieviel LEDs du abziehen möchtest. Verändere jetzt die Lückenposition, um die Lücke an die richtige Stelle zu rücken.",
"conf_leds_layout_frame": "Klassisches Layout (Rahmen)",
"conf_leds_layout_matrix": "Matrix Layout (LED Wand)",
"conf_leds_layout_generatedconf": "Generierte/Aktuelle LED Konfiguration",
"conf_leds_layout_button_savelay": "Speichere Layout",
"conf_leds_layout_button_updsim": "Aktualisiere Vorschau",
"conf_leds_layout_peview": "LED Layout Vorschau",
"conf_leds_layout_advanced": "Erweiterte Optionen",
"conf_leds_layout_preview_originCL": "Erstellt von: Klassisches Layout (Rahmen)",
"conf_leds_layout_preview_originTEXT": "Erstellt von: Textfeld",
"conf_leds_layout_preview_originMA": "Erstellt von: Matrix Layout (LED Wand)",
"conf_leds_layout_preview_totalleds": "LEDs gesamt: $1",
"conf_leds_layout_preview_ledpower": "Max. Stromstärke: $1 A",
"conf_leds_layout_preview_l1": "Das ist die erste LED (Einspeisung)",
"conf_leds_layout_preview_l2": "Das visualisiert die Richtung des Datenstroms (zweite/dritte LED)",
"conf_leds_layout_cl_top": "Oben",
"conf_leds_layout_cl_bottom": "Unten",
"conf_leds_layout_cl_left": "Links",
"conf_leds_layout_cl_right": "Rechts",
"conf_leds_layout_cl_gaglength": "Lückenlänge",
"conf_leds_layout_cl_gappos": "Lückenposition",
"conf_leds_layout_cl_inppos": "Einspeisepunkt",
"conf_leds_layout_cl_reversdir": "Richtung umkehren",
"conf_leds_layout_cl_hleddepth": "Horizontale LED Tiefe",
"conf_leds_layout_cl_vleddepth": "Vertikale LED Tiefe",
"conf_leds_layout_cl_generate": "Generiere LED Konfiguration",
"conf_leds_layout_cl_edgegap": "Rahmenabstand",
"conf_leds_layout_cl_cornergap": "Eckabstand",
"conf_leds_layout_cl_overlap": "Überlappung",
"conf_leds_layout_ma_horiz": "Horizontal",
"conf_leds_layout_ma_vert": "Vertikal",
"conf_leds_layout_ma_cabling": "Verkabelung",
"conf_leds_layout_ma_optsnake": "Schlange",
"conf_leds_layout_ma_optparallel": "Parallel",
"conf_leds_layout_ma_order": "Reihenfolge",
"conf_leds_layout_ma_opthoriz": "Horizontal",
"conf_leds_layout_ma_optvert": "Vertikal",
"conf_leds_layout_ma_position": "Einpeisepunkt",
"conf_leds_layout_ma_opttopleft": "Oben links",
"conf_leds_layout_ma_opttopright": "Oben rechts",
"conf_leds_layout_ma_optbottomleft": "Unten links",
"conf_leds_layout_ma_optbottomright": "Unten rechts",
"conf_leds_layout_textf1": "Das Textfeld zeigt dir dein aktuell geladenes Layout, sofern du kein neues Layout mit den Optionen oben erstellt hast. Optional kann man die Werte hier weiter bearbeiten.",
"conf_grabber_fg_intro": "Plattform Aufnahme ist das lokale System auf dem Hyperion installiert wurde, welches als Bildquelle dient.",
"conf_grabber_v4l_intro": "USB Aufnahme ist ein Gerät, welches via USB angeschlossen ist und als Bildquelle dient.",
"conf_colors_color_intro": "Erstelle Kalibrierungsprofile die einzelnen Komponenten zugewisen werden können. Passe dabei Farben, Gamma, Helligkeit, Kompensation und mehr an.",
"conf_colors_smoothing_intro": "Glätte den Farbverlauf und Helligkeitsänderungen um nicht von schnellen Übergängen abgelenkt zu werden.",
"conf_colors_blackborder_intro": "Ignoriere schwarze Balken, jeder Modus nutzt einen anderen Algorithmus um diese zu erkennen. Erhöhe die Schwelle, sollte es nicht funktionieren.",
"conf_network_net_intro" : "Einstellungen zum Netzwerk die für alle Netzwerk-Dienste gelten",
"conf_network_json_intro": "Der JSON-RPC-Port dieser Hyperion-Instanz, wird genutzt zur Fernsteuerung.",
"conf_network_bobl_intro": "Boblight Empfänger",
"conf_network_udpl_intro": "UDP Empfänger",
"conf_network_fbs_intro": "Google Flatbuffers Empfänger. Wird genutzt für schnellen Bildempfang.",
"conf_network_proto_intro": "Der PROTO-Port dieser Hyperion-Instanz, wird genutzt für \"Bildstreams\" (HyperionScreenCap, Kodi Addon, ...)",
"conf_network_forw_intro": "Leite alles an eine zweite Hyperion Instanz weiter, diese kann dann mit einer anderen LED Steuerung genutzt werden",
"conf_logging_label_intro": "Überprüfe die Meldungen im Prokotoll um zu erfahren was Hyperion gerade beschäftigt. Je nach eingestellter Protokoll-Stufe siehst du mehr oder weniger Informationen.",
"conf_logging_btn_pbupload": "Bericht für Supportanfrage hochladen",
"conf_logging_btn_autoscroll": "Automatisch scrollen",
"conf_logging_nomessage": "Keine Einträge vorhanden.",
"conf_logging_uploading": "Aufbereitung der Daten...",
"conf_logging_yourlink": "Link zu deinem Bericht",
"conf_logging_uplfailed": "Hochladen fehlgeschlagen! Überprüfe deine Internetverbindung!",
"conf_logging_report": "Bericht",
"conf_logging_lastreports": "Frühere Berichte",
"conf_logging_uplpolicy": "Hiermit akzeptierst du die",
"conf_logging_contpolicy": "Berichts-Datenschutzerklärung",
"conf_webconfig_label_intro": "Einstellungen zur Webkonfiguration. Änderungen können die Erreichbarkeit des Webinterfaces beeinflussen.",
"remote_losthint": "Notiz: Alle Änderungen gehen nach einem Neustart verloren.",
"remote_color_label": "Farbe/Effekt",
"remote_color_intro": "Setze einen Effekt oder eine Farbe. Auch deine selbst erstellten Effekte sind gelistet (sofern verfügbar). $1",
"remote_color_button_reset": "Farbe/Effekt zurücksetzen",
"remote_color_label_color": "Farbe:",
"remote_effects_label_effects": "Effekt:",
"remote_adjustment_label": "Farbanpassung",
"remote_adjustment_intro": "Verändere live Farbe/Helligkeit/Kompensation. $1",
"remote_videoMode_label": "Video Modus",
"remote_videoMode_intro": "Wähle zwischen verschiedenen Video Modi um neben 2D auch 3D Filme zu genießen. Unterstützt werden alle Aufnahmearten. $1",
"remote_videoMode_3DSBS": "3DSBS",
"remote_videoMode_3DTAB": "3DTAB",
"remote_videoMode_2D": "2D",
"remote_input_label": "Quellenauswahl",
"remote_input_intro": "Hyperion nutzt ein Prioritätensystem um die Quelle zu wählen. Alles was du setzt hat eine Priorität (Effekte/Farben/Plattform Aufnahme/USB Aufnahme und Netzwerkquellen). Standardmäßig nutzt Hyperion die Quelle mit der niedrigsten Priorität. Hier kannst du aktiv Einfluss darauf nehmen. $1",
"remote_input_label_autoselect": "Automatische Auswahl",
"remote_input_setsource_btn": "Wähle Quelle",
"remote_input_sourceactiv_btn": "Quelle aktiv",
"remote_input_origin": "Ursprung",
"remote_input_owner": "Typ",
"remote_input_priority": "Priorität",
"remote_input_status": "Status/Aktion",
"remote_input_duration": "Dauer:",
"remote_input_ip": "IP:",
"remote_input_clearall": "Lösche alle Effekte/Farben",
"remote_components_label": "Komponentensteuerung",
"remote_components_intro": "Starte und stoppe Komponenten von Hyperion. $1",
"remote_optgroup_usreffets": "Benutzer Effekte",
"remote_optgroup_syseffets": "Mitgelieferte Effekte",
"remote_maptype_label": "LED-Bereich Zuordnung",
"remote_maptype_intro": "Für gewöhnlich entscheidet dein LED Layout welchen Bildbereich welche LED bekommt, dies kann hier geändert werden. $1",
"remote_maptype_label_multicolor_mean": "Mehrfarbig",
"remote_maptype_label_unicolor_mean": "Einfarbig",
"effectsconfigurator_label_intro": "Erstelle auf Grundlage der Basiseffekte neue Effekt die nach deinen Wünschen angepasst sind. Je nach Effekt stehen Optionen wie Farbe, Geschwindigkeit, oder Richtung und vieles mehr zur Auswahl.",
"effectsconfigurator_label_chooseeff": "Template auswählen",
"effectsconfigurator_editdeleff": "Entferne/Lade Effekt",
"effectsconfigurator_button_saveeffect": "Effekt speichern",
"effectsconfigurator_label_effectname": "Effektname",
"effectsconfigurator_button_starttest": "Starte Test",
"effectsconfigurator_button_stoptest": "Stoppe Test",
"effectsconfigurator_button_conttest": "Fortlaufender Test",
"effectsconfigurator_button_deleffect": "Effekt entfernen",
"effectsconfigurator_button_editeffect": "Effekt laden",
"support_label_title": "Unterstütze Hyperion",
"support_label_intro": "Hyperion ist ein kostenloses Open Source Projekt und ein kleines Team arbeitet an seiner Weiterentwicklung. Darum benötigen wir DEINE Unterstützung um weiter in bessere Infrastruktur und Weiterentwicklung investieren zu können.",
"support_label_spreadtheword": "Weitersagen!",
"support_label_fbtext": "Teile Inhalte in Facebook und halte dich und andere auf dem Laufenden",
"support_label_twtext": "Nutze die 140 Zeichen und bleibe auf dem Laufenden auch auf Twitter",
"support_label_ggtext": "Platziere uns in deinen Kreisen auf Google+",
"support_label_yttext": "Gelangweilt von Bildern? Werfe einen Blick auf unsere Youtube Videos",
"support_label_igtext": "Schau doch mal bei Instagram vorbei!",
"support_label_donate": "Spende oder nutze unsere Affiliate Links",
"support_label_affinstr1": "Klicke auf den Link deines Landes",
"support_label_affinstr2": "Kaufe wie gewohnt ein, abhängig von deinem Umsatz bekommen wir eine kleine Provision",
"support_label_affinstr3": "Du zahlst immer den selben Preis. Teste es!",
"support_label_btctext": "Adresse:",
"support_label_donationpp": "Spende:",
"support_label_webrestitle": "Informationsquellen und Hilfe",
"support_label_webpagetitle": "Internetseite",
"support_label_webpagetext": "Das Zuhause von Hyperion",
"support_label_wikititle": "Wiki",
"support_label_wikitext": "Von A bis Z - Alles wissenwerte zu Hyperion",
"support_label_forumtitle": "Forum",
"support_label_forumtext": "Diskussion und Hilfestellung von der Community",
"support_label_ghtext": "Besuche uns auf Github",
"update_label_intro": "Diese Seite zeigt dir alle verfügbaren Versionen von Hyperion, du kannst nach Belieben eine aktuellere Version installieren oder eine Ältere. Die aktuellsten Versionen befinden sich immer oben.",
"update_label_description": "Beschreibung:",
"update_button_install": "Installieren",
"update_button_changelog": "Zeige Änderungsprotokoll",
"update_label_type": "Art:",
"update_versreminder": "Deine Version: $1",
"update_error_getting_versions": "Wir hatten Probleme die verfügbaren Versionen zu ermitteln.",
"about_version": "Version",
"about_build": "Build",
"about_builddate": "Build Datum",
"about_translations": "Übersetzungen",
"about_resources": "$1 Bibliotheken",
"about_contribute": "Entwickel Hyperion mit uns weiter!",
"about_credits": "Einen Dank an alle Entwickler!",
"info_conlost_label_title": "Verbindung zum Hyperion Service unterbrochen!",
"info_conlost_label_reason": "Mögliche Ursachen:",
"info_conlost_label_reason1": "- Schlechte WLAN Verbindung",
"info_conlost_label_reason2": "- Ein Update wird durchgeführt",
"info_conlost_label_reason3": "- Hyperion wird nicht mehr ausgeführt",
"info_conlost_label_autorecon": "Du wirst verbunden, sobald Hyperion wieder verfügbar ist.",
"info_conlost_label_autorefresh": "Diese Seite wird automatisch aktualisiert.",
"info_conlost_label_reload": "Automatisches verbinden gestoppt - limit überschritten. Lade die Seite neu oder klick mich.",
"info_restart_title": "Startet gerade neu...",
"info_restart_rightback": "Hyperion ist gleich wieder für dich da!",
"info_restart_contus": "Solltest du nach 20 Sekunden immer noch hier sein, ist etwas schief gelaufen. Öffne bitte in unserem Support Forum ein neues Thema...",
"info_restart_contusa": "...mit deinen letztes Schritten. Danke!",
"info_404": "Die angeforderte Seite ist nicht verfügbar!",
"infoDialog_general_success_title": "Erfolg",
"infoDialog_general_error_title": "Fehler",
"infoDialog_general_warning_title": "Warnung",
"infoDialog_checklist_title": "Checkliste!",
"infoDialog_effconf_deleted_text": "Der Effekt \"$1\" wurde erfolgreich entfernt!",
"infoDialog_effconf_created_text": "Der Effekt \"$1\" wurde erfolgreich erstellt!",
"InfoDialog_lang_title": "Spracheinstellung",
"InfoDialog_lang_text": "Sollte dir die Vorauswahl der automatischen Spracherkennung nicht gefallen, kannst du die Sprache hier manuell festlegen.",
"InfoDialog_access_title": "Einstellungsstufe",
"InfoDialog_access_text": "Je höher die Stufe je mehr Einstellungen und Funktionen stehen zur Verfügung. Empfohlen ist \"Standard\".",
"InfoDialog_nowrite_title": "Fehler beim Schreibzugriff!",
"InfoDialog_nowrite_text": "Hyperion hat keinen Schreibzugriff auf die aktuell geladene Konfiguration. Bitte korrigiere die Dateizugriffsrechte um fortzufahren.",
"InfoDialog_nowrite_foottext": "Die Webkonfiguration wird automatisch wieder freigegeben, sobald das Problem behoben wurde!",
"infoDialog_wizrgb_text": "Deine RGB Byte Reihenfolge ist bereits richtig eingestellt.",
"infoDialog_writeimage_error_text": "Die ausgewählte Datei \"$1\" ist keine Bilddatei oder ist beschädigt! Bitte wähle eine andere Bilddatei aus.",
"infoDialog_writeconf_error_text": "Das speichern der Konfiguration ist fehlgeschlagen.",
"infoDialog_import_jsonerror_text": "Die ausgewählte Konfigurations-Datei \"$1\" ist keine .json Datei oder ist beschädigt! Fehlermeldung: ($2)",
"infoDialog_import_hyperror_text": "Die ausgewählte Konfigurations-Datei \"$1\" kann nicht importiert werden. Sie ist nicht kompatibel mit Hyperion 2.0 und höher!",
"infoDialog_import_comperror_text": "Dein Browser unterstützt leider keinen Import. Bitte versuche es mit einem anderen Browser erneut.",
"infoDialog_import_confirm_title": "Bestätige Import",
"infoDialog_import_confirm_text": "Bist du sicher, dass du die Konfigurations-Datei \"$1\" importieren möchtest? Diese Aktion kann nicht rückgängig gemacht werden!",
"InfoDialog_iswitch_title": "Hyperion switcher",
"InfoDialog_iswitch_text": "Sollte in deinem lokalen Netzwerk Hyperion mehr als einmal laufen, kannst du hier zwischen den Web Konfigurationen hin und her schalten. Wähle dazu die Instanz unten aus und switche!",
"wiz_wizavail": "Assistent verfügbar",
"wiz_guideyou": "Der $1 wird dich durch die Konfiguration leiten, drücke dazu einfach den Button!",
"wiz_rgb_title": "RGB Byte Reihenfolge Assistent",
"wiz_rgb_intro1": "Dieser Assisent wird dir dabei helfen die richtige Byte Reihenfolge für deine leds zu finden. Klicke auf Fortfahren um zu beginnen.",
"wiz_rgb_intro2": "Wann benötigt man diesen Assistenten? Zur Erstkonfiguration oder wenn deine LEDs zb rot leuchten sollten, sie aber blau oder grün sind.",
"wiz_rgb_expl": "Der Farbpunkt ändert alle x Sekunden die Farbe (rot, grün), zur selben Zeit ändern deine LEDs die Farbe ebenfalls. Beantworte die Fragen unten, um deine RGB Byte Reihenfolge zu überprüfen/korrigieren.",
"wiz_rgb_switchevery": "Ändere Farbe alle...",
"wiz_rgb_q": "Welche Farbe zeigen deine LEDs, wenn der Farbpunkt oben...",
"wiz_rgb_qrend": "...rot ist?",
"wiz_rgb_qgend": "...grün ist?",
"wiz_hue_title": "Philips Hue Assistent",
"wiz_hue_intro1": "Dieser Assistent hilft dir bei der Konfiguration von Hyperion für Philips Hue. Zu den Funktionen zählen ein automatisches finden der Hue Bridge, einen neuen Benutzer erstellen, die einzelnen Lampen unterschiedlichen Bereichen im Bild zuzuordnen und weitere Einstellungen von Hyperion automatisch anzupassen. Kurz gesagt: Komplette Einrichtung mit ein paar Klicks.",
"wiz_hue_desc1": "Es wird automatisch nach der Hue Bridge gesucht, solltest sie nicht gefunden werden, gebe die IP an und drücke den \"neu laden\" Button. Danach benötigst du eine gültige Benutzer ID, diese kann auch erstellt werden.",
"wiz_hue_desc2": "Nun kannst du auswählen, welche der Lampen (IDs) hinzugefügt werden sollen. Mit der Position wählst du aus, wo die jeweilige Lampe \"im Bild\" sitzen soll. Deaktivierte Lampen werden nicht hinzugefügt. Als Hilfe zur Identifizierung kannst du sie mit einem Klick auf den rechten Button kurz aufleuchten lassen.",
"wiz_hue_ip": "Hue Bridge IP:",
"wiz_hue_username": "Benutzer ID:",
"wiz_hue_create_user": "Neuen Benutzer erstellen",
"wiz_hue_failure_ip": "Keine Hue Bridge gefunden, bitte überprüfe die IP",
"wiz_hue_failure_connection": "Zeitüberschreitung. Bitte drücke die Taste auf deiner Hue Bridge rechtzeitig",
"wiz_hue_failure_user": "Benutzer ID wurde nicht gefunden, erstelle eine Neue oder gib eine bereits registrierte an.",
"wiz_hue_press_link": "Bitte \"Link\" Taste auf der Hue Bridge drücken.",
"wiz_hue_ids_disabled": "Deaktiviert",
"wiz_hue_ids_entire": "Ganzes Bild",
"wiz_hue_noids": "Diese Hue Bridge hat keine verbundenen Lampen, bitte verbinde diese zuerst mit deiner Hue Bridge (Nutze die Hue Apps dafür)",
"wiz_hue_pos": "Position/Status",
"wiz_hue_searchb": "Suche nach Hue Bridge...",
"wiz_hue_blinkblue": "Lasse ID $1 blau aufleuchten",
"wiz_hue_ident": "Identifiziere",
"wiz_cc_title": "Farbkalibrierungs Assistent",
"wiz_cc_intro1": "Der Assistent wird dich durch die Kalibrierung deiner LEDs leiten. Sofern du Kodi nutzt, können die Bilder und Testvideos direkt an Kodi geschickt werden. Andernfalls musst du das Material selbst herunterladen und anwenden.",
"wiz_cc_kwebs": "Kodi Webserver (IP:Port)",
"wiz_cc_kodidiscon": "Kodi Webserver nicht gefunden, fahre ohne Kodi-Unterstützung fort.",
"wiz_cc_kodidisconlink": "Download Link Bilder:",
"wiz_cc_kodicon": "Kodi Webserver gefunden, fahre mit Kodi-Unterstützung fort.",
"wiz_cc_kodimsg_start": "Test bestanden - Zeit zu beginnen",
"wiz_cc_kodishould": "Kodi sollte jetzt folgendes Bild anzeigen: $1",
"wiz_cc_lettvshow": "Lass dabei deinen Fernseher folgendes Bild anzeigen: $1",
"wiz_cc_lettvshowm": "Überprüfe dies mithilfe folgender Bildern: $1",
"wiz_cc_adjustit": "Verändere dein \"$1\", bis du zufrieden bist. Beachte: Je mehr du reduzierst bzw von dem Standardwert abweichst, je mehr veränderst du den maximalen Farbraum, was alle Farben die daraus abgeleitet werden ebenfalls betrifft. Je nach TV/LED Farbspektrum sind die Ergebnisse unterschiedlich.",
"wiz_cc_adjustgamma": "Gamma: Was du jetzt tun musst ist, jeden Gamma-Kanal so einstellen, dass der \"Grauverlauf\" auf den LEDs nicht grünlich/rötlich/bläulich aussieht. Neutral ist übrigens 1.0. Beispiel: Sollte dein grau etwas rötlich sein bedeutet dies, dass du dein Gamma für Rot erhöhen musst um den Rot-Anteil zu verringern (Je mehr Gamma, desto weniger Farbe).",
"wiz_cc_chooseid": "Wähle einen Namen für dieses Farb-Profil.",
"wiz_cc_btn_switchpic": "Testbild ändern",
"wiz_cc_backlight": "Zusätzlich kannst du eine Hintergrundbeluchtung einstellen, um \"irritierende Farben\" bei fast schwarzem Bild zu vermeiden oder du den Wechsel zwischen Farbe und Aus als zu anstrengend empfindest. Zusätzlich kann bestimmt werden, ob diese farbig oder nur weiß sein soll. Wird automatisch deaktiviert im Zustand \"Aus\" sowie bei \"Farbe\" und \"Effekt\".",
"wiz_cc_testintro": "Nun ist es an der Zeit für einen Testlauf.",
"wiz_cc_testintrok": "Klicke auf einen Button, um eines der Testvideos abzuspielen.",
"wiz_cc_testintrowok": "Unter folgendem Link findest du ein paar Testvideos zum herunterladen und abspielen:",
"wiz_cc_link": "Klick mich",
"wiz_cc_morethanone": "Du hast mehr als 1 Profil, bitte wähle das zu kalibrierende Profil",
"wiz_cc_btn_stop": "Stoppe Video",
"wiz_cc_summary": "Im folgenden eine Zusammenfassung deiner Einstellungen. Während du ein Video abspielst, kannst du hier weiter ausprobieren. Wenn du fertig bist, klicke auf speichern.",
"edt_dev_auth_key_title": "Authentisierungstoken",
"edt_dev_enum_subtract_minimum": "Subtrahiere minimum",
"edt_dev_enum_sub_min_cool_adjust": "Minimale Anpassung: cool",
"edt_dev_enum_sub_min_warm_adjust": "Minimale Anpassung: warm",
"edt_dev_enum_white_off": "Weiß ist aus",
"edt_dev_general_heading_title": "Allgemeine Einstellungen",
"edt_dev_general_name_title": "Name der Konfiguration",
"edt_dev_general_hardwareLedCount_title": "Anzahl Hardware LEDs",
"edt_dev_general_colorOrder_title": "RGB Byte Reihenfolge",
"edt_dev_general_rewriteTime_title": "Aktualisierungszeit",
"edt_dev_spec_header_title": "Spezifische Einstellungen",
"edt_dev_spec_baudrate_title": "Baudrate",
"edt_dev_spec_spipath_title": "SPI Pfad",
"edt_dev_spec_invert_title": "Invertiere Signal",
"edt_dev_spec_multicastGroup_title": "Multicast Gruppe",
"edt_dev_spec_numberOfLeds_title": "Anzahl der LEDs",
"edt_dev_spec_port_title": "Port",
"edt_dev_spec_orbIds_title": "Orb ID(s)",
"edt_dev_spec_useOrbSmoothing_title": "Nutze Orb Glättung",
"edt_dev_spec_targetIp_title": "Ziel IP",
"edt_dev_spec_targetIpHost_title": "Ziel IP/hostname",
"edt_dev_spec_outputPath_title": "Ausgabepfad",
"edt_dev_spec_delayAfterConnect_title": "Verzögerung nach Verbindung",
"edt_dev_spec_FCsetConfig_title": "Wende fadecandy Konfiguration an",
"edt_dev_spec_FCmanualControl_title": "Manuelle Steuerung der fadecandy LEDs",
"edt_dev_spec_FCledToOn_title": "Fadecandy LEDs set to on",
"edt_dev_spec_interpolation_title": "Interpolation",
"edt_dev_spec_dithering_title": "Dithering",
"edt_dev_spec_gamma_title": "Gamma",
"edt_dev_spec_whitepoint_title": "Weißpunkt",
"edt_dev_spec_username_title": "Benutzername",
"edt_dev_spec_lightid_title": "Lampen ID(s)",
"edt_dev_spec_lightid_itemtitle": "ID",
"edt_dev_spec_transistionTime_title": "Übergangszeit",
"edt_dev_spec_switchOffOnBlack_title": "Aus bei schwarz",
"edt_dev_spec_brightnessFactor_title": "Helligkeitsfaktor",
"edt_dev_spec_ledType_title": "LED typ",
"edt_dev_spec_uid_title": "UID",
"edt_dev_spec_intervall_title": "Intervall",
"edt_dev_spec_latchtime_title": "Sperrzeit",
"edt_dev_spec_maxPacket_title": "Paketgröße",
"edt_dev_spec_serial_title": "Seriennummer",
"edt_dev_spec_vid_title": "VID",
"edt_dev_spec_pid_title": "PID",
"edt_dev_spec_cid_title": "CID",
"edt_dev_spec_LBap102Mode_title": "LightBerry APA102 Modus",
"edt_dev_spec_universe_title": "Universum",
"edt_dev_spec_whiteLedAlgor_title": "Weiß Algorithmus",
"edt_dev_spec_useRgbwProtocol_title": "Nutze RGBW Protokoll",
"edt_dev_spec_maximumLedCount_title": "Maximale Anzahl LEDs",
"edt_dev_spec_gpioNumber_title": "GPIO Nummer",
"edt_dev_spec_dmaNumber_title": "DMA Kanal",
"edt_dev_spec_gpioMap_title": "GPIO Zuweisung",
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo",
"edt_dev_spec_gpioBcm_title": "GPIO Pin",
"edt_dev_spec_ledIndex_title": "LED index",
"edt_dev_spec_colorComponent_title": "Farbkomponente",
"edt_conf_general_enable_title": "Aktiviert",
"edt_conf_general_enable_expl": "Wenn aktiviert, ist die Komponente aktiv.",
"edt_conf_general_priority_title": "Priorität",
"edt_conf_general_priority_expl": "Die Priorität dieser Komponente.",
"edt_conf_general_port_title": "Port",
"edt_conf_general_port_expl": "Der genutzte Port.",
"edt_conf_enum_color": "Farbe",
"edt_conf_enum_effect": "Effekt",
"edt_conf_enum_multicolor_mean": "Mehrfarbig",
"edt_conf_enum_unicolor_mean": "Einfarbig",
"edt_conf_enum_rgb": "RGB",
"edt_conf_enum_bgr": "BGR",
"edt_conf_enum_rbg": "RBG",
"edt_conf_enum_brg": "BRG",
"edt_conf_enum_gbr": "GBR",
"edt_conf_enum_grb": "GRB",
"edt_conf_enum_linear": "Linear",
"edt_conf_enum_PAL": "PAL",
"edt_conf_enum_NTSC": "NTSC",
"edt_conf_enum_SECAM": "SECAM",
"edt_conf_enum_NO_CHANGE": "Auto",
"edt_conf_enum_logsilent": "Stille",
"edt_conf_enum_logwarn": "Warnung",
"edt_conf_enum_logverbose": "Ausführlich",
"edt_conf_enum_logdebug": "Debug",
"edt_conf_enum_bbdefault": "Standard",
"edt_conf_enum_bbclassic": "Klassisch",
"edt_conf_enum_bbosd": "OSD",
"edt_conf_enum_automatic": "Automatisch",
"edt_conf_gen_heading_title": "Allgemeine Einstellungen",
"edt_conf_gen_name_title": "Name der Konfiguration",
"edt_conf_gen_name_expl": "Der Name wird verwendet, um Hyperion besser zu identifizieren. (Hilfreich bei mehreren Instanzen)",
"edt_conf_gen_showOptHelp_title": "Zeige Erklärungen",
"edt_conf_gen_showOptHelp_expl": "Zeige alle verfügbaren Options-Erklärungen. Empfohlen für Anfänger",
"edt_conf_gen_versionBranch_title": "Versionszweig",
"edt_conf_gen_versionBranch_expl": "Gibt an, welcher Versionszweig für die Suche von neuen Hyperion Versionen genutzt werden soll.",
"edt_conf_color_heading_title": "Farbkalibrierung",
"edt_conf_color_channelAdjustment_header_itemtitle": "Profil",
"edt_conf_color_channelAdjustment_header_title": "Anpassung Farbkanäle",
"edt_conf_color_channelAdjustment_header_expl": "Passe die Farbkanäle deinen LEDs an",
"edt_conf_color_imageToLedMappingType_title": "LED-Bereich Zuordnungstyp",
"edt_conf_color_imageToLedMappingType_expl": "Sofern nicht \"Mehrfarbig\", wird dein LED Layout mit einer anderen Bildzuweisung überschrieben",
"edt_conf_color_id_title": "ID",
"edt_conf_color_id_expl": "Eine vom Benutzer frei angegebene ID.",
"edt_conf_color_leds_title": "LED index",
"edt_conf_color_leds_expl": "Zugewiesen zu allen (*) LEDs oder nur zu bestimmten LED Nummern (0-17).",
"edt_conf_color_black_title": "Schwarz",
"edt_conf_color_black_expl": "Kalibrierter Schwarzwert.",
"edt_conf_color_white_title": "Weiß",
"edt_conf_color_white_expl": "Kalibrierter Weißwert.",
"edt_conf_color_red_title": "Rot",
"edt_conf_color_red_expl": "Kalibrierter Rotwert.",
"edt_conf_color_green_title": "Grün",
"edt_conf_color_green_expl": "Kalibrierter Grünwert.",
"edt_conf_color_blue_title": "Blau",
"edt_conf_color_blue_expl": "Kalibrierter Blauwert.",
"edt_conf_color_cyan_title": "Cyan",
"edt_conf_color_cyan_expl": "Kalibrierter Cyanwert.",
"edt_conf_color_magenta_title": "Magenta",
"edt_conf_color_magenta_expl": "Kalibrierter Magentawert.",
"edt_conf_color_yellow_title": "Gelb",
"edt_conf_color_yellow_expl": "Kalibrierter Gelbwert.",
"edt_conf_color_gammaRed_title": "Gamma Rot",
"edt_conf_color_gammaRed_expl": "Gamma von rot. 1.0 ist neutral. Über 1.0 wird rot reduziert, unter 1.0 wird rot erhöht.",
"edt_conf_color_gammaGreen_title": "Gamma Grün",
"edt_conf_color_gammaGreen_expl": "Gamma von grün. 1.0 ist neutral. Über 1.0 wird grün reduziert, unter 1.0 wird grün erhöht.",
"edt_conf_color_gammaBlue_title": "Gamma Blau",
"edt_conf_color_gammaBlue_expl": "Gamma von blau. 1.0 ist neutral. Über 1.0 wird blau reduziert, unter 1.0 wird blau erhöht.",
"edt_conf_color_backlightThreshold_title": "Hintergrund - beleuchtung",
"edt_conf_color_backlightThreshold_expl": "Eine Beleuchtung die dauerhaft aktiv ist. (Automatisch deaktiviert bei Effekten, Farben oder im Zustand \"Aus\")",
"edt_conf_color_backlightColored_title": "Farbige Hintergrund - beleuchtung",
"edt_conf_color_backlightColored_expl": "Die Hintergrundbeleuchtung kann mit oder ohne Farbanteile genutzt werden.",
"edt_conf_color_brightness_title": "Helligkeit",
"edt_conf_color_brightness_expl": "Die gesamte Helligkeit",
"edt_conf_color_brightnessComp_title": "Helligkeits Kompensation",
"edt_conf_color_brightnessComp_expl": "Kompensiert unterschiede in der Helligkeit zwischen Rot Grün Blau, Cyan Magenta Gelb und weiß. 100 ist volle Kompensation, 0 keine Kompensation",
"edt_conf_smooth_heading_title": "Glättung",
"edt_conf_smooth_type_title": "Art",
"edt_conf_smooth_type_expl": "Algorithmus der Glättung.",
"edt_conf_smooth_time_ms_title": "Zeit",
"edt_conf_smooth_time_ms_expl": "Wie lange soll die Glättung Bilder sammeln?",
"edt_conf_smooth_updateFrequency_title": "Aktualisierungsfrequenz",
"edt_conf_smooth_updateFrequency_expl": "Die Geschwindigkeit der Datenausgabe an die LED Steuerung.",
"edt_conf_smooth_updateDelay_title": "Aktualisierungsverzögerung",
"edt_conf_smooth_updateDelay_expl": "Verzögere die Ausgabe, sollte dein ambient light schneller sein als dein TV.",
"edt_conf_smooth_continuousOutput_title": "Fortlaufende Ausgabe",
"edt_conf_smooth_continuousOutput_expl": "Aktualisiere die LEDs, auch wenn das Bild sich nicht geändert hat.",
"edt_conf_v4l2_heading_title": "USB Aufnahme",
"edt_conf_v4l2_device_title": "Gerät",
"edt_conf_v4l2_device_expl": "Der Pfad zum USB (v4l) Aufnahmegerät. Wähle 'auto' für automatische Erkennung. Beispiel: '/dev/video0'",
"edt_conf_v4l2_standard_title": "Videoformat",
"edt_conf_v4l2_standard_expl": "Wähle das passende Videoformat deiner Region. Auf 'Auto' wird der gewählte Modus vom v4l interface beibehalten.",
"edt_conf_v4l2_sizeDecimation_title": "Bildverkleinerung Faktor",
"edt_conf_v4l2_sizeDecimation_expl": "Der Faktor der Bildverkleinerung ausgehend von der ursprünglichen Größe, 1 bedeutet keine Änderung (originales Bild).",
"edt_conf_v4l2_cropLeft_title": "Entferne links",
"edt_conf_v4l2_cropLeft_expl": "Anzahl der Pixel auf der linken Seite die vom Bild entfernt werden.",
"edt_conf_v4l2_cropRight_title": "Entferne rechts",
"edt_conf_v4l2_cropRight_expl": "Anzahl der Pixel auf der rechten Seite die vom Bild entfernt werden.",
"edt_conf_v4l2_cropTop_title": "Entferne oben",
"edt_conf_v4l2_cropTop_expl": "Anzahl der Pixel auf der oberen Seite die vom Bild entfernt werden.",
"edt_conf_v4l2_cropBottom_title": "Entferne unten",
"edt_conf_v4l2_cropBottom_expl": "Anzahl der Pixel auf der unteren Seite die vom Bild entfernt werden.",
"edt_conf_v4l2_signalDetection_title": "Signal Erkennung",
"edt_conf_v4l2_signalDetection_expl": "Wenn aktiviert, wird die USB Aufnahme temporär bei \"kein Signal\" abgeschalten. Das Bild muss dazu 4 Sekunden lang unter die Schwellwerte fallen.",
"edt_conf_v4l2_redSignalThreshold_title": "Rote Signalschwelle",
"edt_conf_v4l2_redSignalThreshold_expl": "Je höher die rote Schwelle je eher wird abgeschalten bei entsprechendem rot-Anteil.",
"edt_conf_v4l2_greenSignalThreshold_title": "Grüne Signalschwelle",
"edt_conf_v4l2_greenSignalThreshold_expl": "Je höher die grüne Schwelle je eher wird abgeschalten bei entsprechendem grün-Anteil.",
"edt_conf_v4l2_blueSignalThreshold_title": "Blaue Signalschwelle",
"edt_conf_v4l2_blueSignalThreshold_expl": "Je höher die blaue Schwelle je eher wird abgeschalten bei entsprechendem blau-Anteil.",
"edt_conf_v4l2_sDVOffsetMin_title": "Signal Erkennung VMin",
"edt_conf_v4l2_sDVOffsetMin_expl": "Signal Erkennungs-Bereich vertikal minimum (0.0-1.0)",
"edt_conf_v4l2_sDHOffsetMin_title": "Signal Erkennung HMin",
"edt_conf_v4l2_sDHOffsetMin_expl": "Signal Erkennungs-Bereich horizontal minimum (0.0-1.0)",
"edt_conf_v4l2_sDVOffsetMax_title": "Signal Erkennung VMax",
"edt_conf_v4l2_sDVOffsetMax_expl": "Signal Erkennungs-Bereich vertikal maximum (0.0-1.0)",
"edt_conf_v4l2_sDHOffsetMax_title": "Signal Erkennung HMax",
"edt_conf_v4l2_sDHOffsetMax_expl": "Signal Erkennungs-Bereich horizontal maximum (0.0-1.0)",
"edt_conf_instCapture_heading_title": "Instance Aufnahme",
"edt_conf_instC_systemEnable_title": "Aktiviere Plattform Aufnahme",
"edt_conf_instC_systemEnable_expl": "Aktiviert die Plattform Aufnahme für diese LED Hardware Instanz",
"edt_conf_instC_v4lEnable_title": "Aktiviere USB Aufnahme",
"edt_conf_instC_v4lEnable_expl": "Aktiviert die USB Aufnahme für diese LED Hardware Instanz",
"edt_conf_fg_heading_title": "Plattform Aufnahme",
"edt_conf_fg_type_title": "Typ",
"edt_conf_fg_type_expl": "Art der Plattform Aufnahme, standard ist 'auto'",
"edt_conf_fg_frequency_Hz_title": "Aufnahmefrequenz",
"edt_conf_fg_frequency_Hz_expl": "Wie schnell neue Bilder aufgenommen werden.",
"edt_conf_fg_width_title": "Breite",
"edt_conf_fg_width_expl": "Verkleinere Bild auf dieser Breite, da das Rohmaterial viel Leistung benötigen würde.",
"edt_conf_fg_height_title": "Höhe",
"edt_conf_fg_height_expl": "Verkleinere Bild auf dieser Höhe, da das Rohmaterial viel Leistung benötigen würde.",
"edt_conf_fg_pixelDecimation_title": "Bildverkleinerung Faktor",
"edt_conf_fg_pixelDecimation_expl": "Bildverkleinerung (Faktor) ausgehend von der original Größe. 1 für unveränderte/originale Größe.",
"edt_conf_fg_device_title": "Device",
"edt_conf_fg_display_title": "Display",
"edt_conf_fg_display_expl": "Gebe an von welchem Desktop aufgenommen werden soll. (Multi Monitor Setup)",
"edt_conf_bb_heading_title": "Schwarze Balken Erkennung",
"edt_conf_bb_threshold_title": "Schwelle",
"edt_conf_bb_threshold_expl": "Wenn die Erkennung nicht funktioniert, erhöhe die Schwelle um auf 'graues' schwarz zu reagieren.",
"edt_conf_bb_unknownFrameCnt_title": "Unbekannte Bilder",
"edt_conf_bb_unknownFrameCnt_expl": "Anzahl an Bildern die negativ sind, welche den Rand auf 0 zurücksetzen.",
"edt_conf_bb_borderFrameCnt_title": "Randbilder",
"edt_conf_bb_borderFrameCnt_expl": "Anzahl an Bildern bis ein neuer Rand festgelegt wird.",
"edt_conf_bb_maxInconsistentCnt_title": "Inkosistente Bilder",
"edt_conf_bb_maxInconsistentCnt_expl": "Anzahl der zu ignorierenden Bilder bis ein neuer Rand überprüft wird.",
"edt_conf_bb_blurRemoveCnt_title": "Unscharfe Pixel",
"edt_conf_bb_blurRemoveCnt_expl": "Anzahl an Pixeln, die zusätzlich vom Rand abgeschnitten werden.",
"edt_conf_bb_mode_title": "Modus",
"edt_conf_bb_mode_expl": "Algorithmus zur Auswertung. (siehe Wiki)",
"edt_conf_fge_heading_title": "Start Effekt/Farbe",
"edt_conf_fge_type_title": "Typ",
"edt_conf_fge_type_expl": "Wähle zwischen einem Effekt oder einer Farbe.",
"edt_conf_fge_color_title": "Farbe",
"edt_conf_fge_color_expl": "Sofern der Typ \"Farbe\" ist, stelle hier eine Farbe deiner Wahl sein.",
"edt_conf_fge_effect_title": "Effekt",
"edt_conf_fge_effect_expl": "Sofern der Typ \"Effekt\" ist, wähle hier einen Effekt deiner Wahl. (Gilt auch für selbst erstellte)",
"edt_conf_fge_duration_ms_title": "Dauer",
"edt_conf_fge_duration_ms_expl": "Dauer des Effekts/Farbe beim Hyperion Start.",
"edt_conf_bge_heading_title": "Hintergrund Effekt/Farbe",
"edt_conf_fw_heading_title": "Weiterleitung",
"edt_conf_fw_json_title": "Liste von Json zielen",
"edt_conf_fw_json_expl": "Ein Json Ziel pro Zeile. Bestehend aus IP:PORT (Beispiel: 127.0.0.1:19446)",
"edt_conf_fw_json_itemtitle": "Json Ziel",
"edt_conf_fw_proto_title": "Liste von Proto zielen",
"edt_conf_fw_proto_expl": "Ein Proto Ziel pro Zeile. Bestehend aus IP:PORT (Beispiel: 127.0.0.1:19401)",
"edt_conf_fw_proto_itemtitle": "Proto Ziel",
"edt_conf_net_heading_title" : "Network",
"edt_conf_net_internetAccessAPI_title":"Internet API Zugriff",
"edt_conf_net_internetAccessAPI_expl":"Erlaube Zugriff auf die Hyperion API/Webinterface aus dem Internet, deaktivieren für höhere Sicherheit.",
"edt_conf_net_ipWhitelist_title":"Erlaubte IP's",
"edt_conf_net_ipWhitelist_expl":"Anstatt den Zugriff für alle Verbindungen aus dem Internet zu erlauben kannst du hier Ausnahmen für zugelassene IP Adressen hinzufügen.",
"edt_conf_net_ip_itemtitle":"IP",
"edt_conf_net_apiAuth_title":"API Authentifizierung",
"edt_conf_net_apiAuth_expl":"Zwinge alle Anwendungen welche die Hyperion API nutzen sich zu authentifizieren. Aktivieren für höhere Sicherheit, da nun jede neue Anwendung einmalig von dir bestätigt werden muss.",
"edt_conf_net_localApiAuth_title" : "Lokale API Authentifizierung",
"edt_conf_net_localApiAuth_expl" : "Wenn aktiviert, müssen Verbindungen aus dem Heimnetzwerk mit einem Token authentifiziert werden.",
"edt_conf_js_heading_title": "JSON Server",
"edt_conf_fbs_heading_title": "Flatbuffers Server",
"edt_conf_fbs_timeout_title": "Zeitüberschreitung",
"edt_conf_fbs_timeout_expl": "Wenn für die angegebene Zeit keine Daten empfangen werden, wird die Komponente (vorübergehend) deaktiviert",
"edt_conf_pbs_heading_title": "Protocol Buffers Server",
"edt_conf_pbs_timeout_title": "Zeitüberschreitung",
"edt_conf_pbs_timeout_expl": "Wenn für die angegebene Zeit keine Daten empfangen werden, wird die Komponente (vorübergehend) deaktiviert",
"edt_conf_bobls_heading_title": "Boblight Server",
"edt_conf_udpl_heading_title": "UDP Listener",
"edt_conf_udpl_address_title": "Adresse",
"edt_conf_udpl_address_expl": "Die Adresse auf der UDP Pakete akzeptiert werden.",
"edt_conf_udpl_timeout_title": "Zeitüberschreitung",
"edt_conf_udpl_timeout_expl": "Wenn für die angegebene Zeit keine UDP Pakete empfangen werden, wird die Komponente (vorübergehend) deaktiviert",
"edt_conf_udpl_shared_title": "Gemeinsam genutzt",
"edt_conf_udpl_shared_expl": "Wird gemeinsam über alle Hyperion Instanzen genutzt.",
"edt_conf_webc_heading_title": "Web Konfiguration",
"edt_conf_webc_docroot_title": "Verzeichnis",
"edt_conf_webc_docroot_expl": "Lokaler Pfad zum WebUI Wurzelverzeichnis (Nur für WebUI Entwickler)",
"edt_conf_effp_heading_title": "Effekt Pfade",
"edt_conf_effp_paths_title": "Effekt Pfad(e)",
"edt_conf_effp_paths_expl": "Es können mehrere Ordner definiert werden die Effekte enthalten. Der Effekt Konfigurator speichert immer im Ersten Ordner.",
"edt_conf_effp_paths_itemtitle": "Pfad",
"edt_conf_effp_disable_title": "Deaktivierte Effekte",
"edt_conf_effp_disable_expl": "Trage hier die Namen der Effekte ein, die in Effektlisten nicht mehr zur Auswahl stehen sollen.",
"edt_conf_effp_disable_itemtitle": "Effekt",
"edt_conf_log_heading_title": "Protokoll",
"edt_conf_log_level_title": "Protokollstufe",
"edt_conf_log_level_expl": "Abhängig der Stufe sind weniger oder mehr Meldungen sichtbar.",
"edt_eff_smooth_custom": "Aktivere Glättung",
"edt_eff_smooth_time_ms": "Glättung: Zeit",
"edt_eff_smooth_updateFrequency": "Glättung: Aktualisierungsfrequenz",
"edt_eff_waves_header": "Wellen",
"edt_eff_waves_header_desc": "Gestalte Wellen aus Farbe! Mische dazu deine lieblings Farben und wähle einen Mittelpunkt.",
"edt_eff_gif_header": "GIF's",
"edt_eff_gif_header_desc": "Dieser Effekt spielt .gif Dateien ab. Bietet die Möglichkeit kleine GIF-Videos abzuspielen.",
"edt_eff_candle_header": "Kerze",
"edt_eff_candle_header_desc": "Flackerndes Kerzenlicht",
"edt_eff_police_header": "Polizei",
"edt_eff_police_header_desc": "Lights like a police car in action",
"edt_eff_fade_header": "Farbübergang",
"edt_eff_fade_header_desc": "Farbübergange für alle LED's",
"edt_eff_rainbowmood_header": "Regenbogen",
"edt_eff_rainbowmood_header_desc": "Alle LEDs Regenbogen Farbübergang",
"edt_eff_knightrider_header": "Knight Rider",
"edt_eff_knightrider_header_desc": "K.I.T.T ist zurück! Der Front-Scanner des bekannten Autos, diesmal nicht nur in rot.",
"edt_eff_lightclock_header": "Lichtuhr",
"edt_eff_lightclock_header_desc": "Eine echte Uhr als Licht! Passe die Farben von Stunden, Minuten, Sekunden deinen Vorstellungen an. Optional können 3/6/9/12 Uhr Markierungen aktiviert werden. Sollte die Uhr eine falsche Zeit anzeigen, überprüfe die Uhrzeit deines Systems.",
"edt_eff_pacman_header": "Pac-Man",
"edt_eff_pacman_header_desc": "Klein gefräßig und gelb, wer wird überleben?",
"edt_eff_moodblobs_header": "Stimmungskugeln",
"edt_eff_moodblobs_header_desc": "Entspannt den Abend beginnen mit langsam bewegenden Farbkugeln die ebenso sanft ihre Farbe verändern.",
"edt_eff_swirl_header": "Farbwirbel",
"edt_eff_swirl_header_desc": "Ein Wirbel mit frei wählbaren Farben. Die Farben werden gleichmäßig auf 360° aufgeteilt, dazwischen werden Farbübergänge berechnet. Zusätzlich kann ein zweiter Wirbel über den Ersten gelegt werden (Transparenz beachten!). Tipp: Eine Widerholung der selben Farbe erhöht deren \"größe\" und verringert den Bereich des Farbübergangs zu benachbarten Farben.",
"edt_eff_random_header": "Zufällig",
"edt_eff_random_header_desc": "Pixel-Farb-Mix",
"edt_eff_systemshutdown_header": "Herunterfahren",
"edt_eff_systemshutdown_header_desc": "Eine kurze Animation gefolgt von einem möglicherweise echten Herunterfahren des Systems",
"edt_eff_snake_header": "Schlange",
"edt_eff_snake_header_desc": "Wo ist das Futter?",
"edt_eff_sparks_header": "Funken",
"edt_eff_sparks_header_desc": "Ein Sternenfunkeln, wahlweise in festgelegter Farbe oder zufällig. Passe Helligkeit, Sättigung und Geschwindigkeit an.",
"edt_eff_traces_header": "Farbspuren",
"edt_eff_traces_header_desc": "Erfordert eine Neugestaltung",
"edt_eff_x-mas_header": "Weihnachten",
"edt_eff_x-mas_header_desc": "Ein Hauch von Weihnachten",
"edt_eff_trails_header": "Sternschnuppen",
"edt_eff_trails_header_desc": "In verschiedenen Farben, wünsch dir was!",
"edt_eff_flag_header": "Flaggen",
"edt_eff_flag_header_desc": "Verpasse deinen LEDs die Farben deines Landes. Du kannst mehr als eine Flagge auswählen, je nach Intervall werden diese dann abwechselnd angezeigt.",
"edt_eff_enum_all": "Alle",
"edt_eff_enum_all-together": "Alle zusammen",
"edt_eff_enum_list": "LED Liste",
"edt_eff_count": "Anzahl",
"edt_eff_color": "Farbe",
"edt_eff_colorrandom": "Zufällige Farbe",
"edt_eff_colorone": "Farbe eins",
"edt_eff_colortwo": "Farbe zwei",
"edt_eff_colorcount": "Farblänge",
"edt_eff_rotationtime": "Rotationszeit",
"edt_eff_sleeptime": "Schlafzeit",
"edt_eff_image": "Bilddatei",
"edt_eff_fps": "Bilder pro Sekunde",
"edt_eff_reversedirection": "Richtung umkehren",
"edt_eff_fadeintime": "Zeit für Einblendung",
"edt_eff_fadeouttime": "Zeit für Ausblendung",
"edt_eff_repeat": "Wiederholung",
"edt_eff_repeatcount": "Anzahl Wiederholung",
"edt_eff_colorendtime": "Zeit für Start-Farbe",
"edt_eff_colorstarttime": "Zeit für End-Farbe",
"edt_eff_colorstart": "Farbe Start",
"edt_eff_colorend": "Farbe Ende",
"edt_eff_maintain_end_color": "Behalte Endfarbe",
"edt_eff_colorshift": "Farbverschiebung",
"edt_eff_whichleds": "Welche LEDs",
"edt_eff_ledlist": "LED Liste",
"edt_eff_speed": "Geschwindigkeit",
"edt_eff_fadefactor": "Verblass Faktor",
"edt_eff_showseconds": "Zeige Sekunden",
"edt_eff_blobcount": "Kugelanzahl",
"edt_eff_huechange": "Farbänderung",
"edt_eff_basecolorchange": "Basisfarben verändern",
"edt_eff_basecolorchangerate": "BF Geschwindigkeit",
"edt_eff_basecolorrangeleft": "BF Bereich links",
"edt_eff_basecolorrangeright": "BF Bereich rechts",
"edt_eff_brightness": "Helligkeit",
"edt_eff_centerx": "Mittelpunkt X-Achse",
"edt_eff_centery": "Mittelpunkt Y-Achse",
"edt_eff_saturation": "Sättigung",
"edt_eff_colorevel": "Farbstufe",
"edt_eff_whitelevel": "Weißstufe",
"edt_eff_alarmcolor": "Alarm Farbe",
"edt_eff_postcolor": "Startfarbe",
"edt_eff_enableshutdown": "Echtes herunterfahren",
"edt_eff_length": "Länge",
"edt_eff_frequency": "Frequenz",
"edt_eff_min_len": "Minimale Länge",
"edt_eff_max_len": "Maximale Länge",
"edt_eff_height": "Höhe",
"edt_eff_offset": "Verschiebung",
"edt_eff_colorHour": "Farbe Stunde",
"edt_eff_colorMinute": "Farbe Minute",
"edt_eff_colorSecond": "Farbe Sekunde",
"edt_eff_colorMarker": "Marker Farbe",
"edt_eff_markerWidth": "Marker Breite",
"edt_eff_markerDepth": "Marker Tiefe",
"edt_eff_markerEnable": "Zeige Marker",
"edt_eff_backgroundColor": "Hintergrundfarbe",
"edt_eff_countries": "Länder",
"edt_eff_interval": "Intervall",
"edt_eff_margin": "Abstand",
"edt_eff_customColor": "Benutzerdefinierte Farbe",
"edt_eff_randomCenter": "Zufälliger Mittelpunkt",
"edt_eff_enableSecondSwirl": "Zweiter Wirbel",
"edt_eff_reverseRandomTime": "Richtungswechsel alle",
"edt_append_ns": "ns",
"edt_append_ms": "ms",
"edt_append_s": "s",
"edt_append_hz": "Hz",
"edt_append_pixel": "Pixel",
"edt_append_percent": "%",
"edt_append_degree": "°",
"edt_append_sdegree": "s/grad",
"edt_append_leds": "LEDs",
"edt_msg_error_notset": "Attribut muss gesetzt sein",
"edt_msg_error_notempty": "Eingabe benötigt",
"edt_msg_error_enum": "Die Eingabe muss einem der aufgeführten Werte entsprechen",
"edt_msg_error_anyOf": "Die Eingabe muss gegen mindestens eines der gegebenen Schemata validiert werden können",
"edt_msg_error_oneOf": "Die Eingabe muss gegen genau eines der gegebenen Schemata validiert werden können. Momentan können $1 Schemata validiert werden",
"edt_msg_error_not": "Die Eingabe darf nicht gegen das gegebene Schema validiert werden können",
"edt_msg_error_type_union": "Die Eingabe muss einem der gegebenen Typen entsprechen",
"edt_msg_error_type": "Die Eingabe muss vom Typ $1 sein",
"edt_msg_error_disallow_union": "Die Eingabe darf nicht einem der gegebenen Werte entsprechen",
"edt_msg_error_disallow": "Die Eingabe muss vom Typ $1 sein",
"edt_msg_error_multipleOf": "Die Eingabe muss ein Vielfaches von $1 sein",
"edt_msg_error_maximum_excl": "Der Wert muss kleiner als $1 sein",
"edt_msg_error_maximum_incl": "Der Wert darf höchstens $1 sein",
"edt_msg_error_minimum_excl": "Der Wert muss größer als $1 sein",
"edt_msg_error_minimum_incl": "Der Wert muss mindestens $1 sein",
"edt_msg_error_maxLength": "Die Eingabe darf höchstens $1 Zeichen lang sein",
"edt_msg_error_minLength": "Die Eingabe muss mindestens $1 Zeichen lang sein",
"edt_msg_error_pattern": "Die Eingabe muss dem gegebenen Muster entsprechen",
"edt_msg_error_additionalItems": "In diesem Feld sind keine weiteren Elemente erlaubt",
"edt_msg_error_maxItems": "Das Feld darf höchstens $1 Element(e) beinhalten",
"edt_msg_error_minItems": "Das Feld muss mindestens $1 Element(e) beinhalten",
"edt_msg_error_uniqueItems": "Das Feld darf nur einzigartige Elemente beinhalten",
"edt_msg_error_maxProperties": "Das Objekt darf höchstens $1 Attribute habe",
"edt_msg_error_minProperties": "Das Objekt muss mindestens $1 Attribute haben",
"edt_msg_error_required": "Das Objekt beinhaltet nicht das benötigte Attribut '$1'",
"edt_msg_error_additional_properties": "Es sind keine weiteren Attribute erlaubt. $1 muss entfernt werden",
"edt_msg_error_dependency": "Das Attribut $1 ist zwingend erforderlich",
"edt_msg_button_delete_all": "Alle",
"edt_msg_button_delete_all_title": "Alle löschen",
"edt_msg_button_delete_last": "Letzes $1-Element",
"edt_msg_button_delete_last_title": "Letzes $1-Element löschen",
"edt_msg_button_add_row_title": "$1 Hinzufügen",
"edt_msg_button_move_down_title": "Nach unten verschieben",
"edt_msg_button_move_up_title": "Nach oben verschieben",
"edt_msg_button_delete_row_title": "$1 Löschen",
"edt_msg_button_delete_row_title_short": "Löschen",
"edt_msg_button_collapse": "Einklappen",
"edt_msg_button_expand": "Ausklappen"
>>>>>>> 04d62c2... second part of PR #578
}

View File

@ -31,11 +31,12 @@
"general_button_savesettings" : "Save settings",
"general_btn_yes" : "Yes",
"general_btn_ok" : "OK",
"general_btn_start" : "Start",
"general_btn_stop" : "Stop",
"general_btn_cancel" : "Cancel",
"general_btn_delete" : "Delete",
"general_btn_continue" : "Continue",
"general_btn_save" : "Save",
"general_btn_saverestart" : "Save and restart",
"general_btn_saveandreload" : "Save and reload",
"general_btn_restarthyperion" : "Restart Hyperion",
"general_btn_off" : "Off",
@ -67,8 +68,8 @@
"dashboard_newsbox_readmore" : "Read more",
"dashboard_alert_message_confedit_t" : "Configuration modified",
"dashboard_alert_message_confedit" : "Your Hyperion configuration has been modified. To apply it, restart Hyperion.",
"dashboard_alert_message_disabled_t" : "Hyperion disabled",
"dashboard_alert_message_disabled" : "Hyperion is currently disabled! To use it again, enable it at the dashboard.",
"dashboard_alert_message_disabled_t" : "LED hardware instance disabled",
"dashboard_alert_message_disabled" : "This instance is currently disabled! To use it again, enable it at the dashboard.",
"dashboard_alert_message_confsave_success_t" : "Configuration saved",
"dashboard_alert_message_confsave_success" : "Your Hyperion configuration has been saved successfully. Your changes are now active.",
"main_menu_dashboard_token" : "Dashboard",
@ -100,7 +101,7 @@
"conf_general_impexp_l2" : "Export a configuration by clicking on \"Export\". Your browser starts a download.",
"conf_general_impexp_impbtn" : "Import",
"conf_general_impexp_expbtn" : "Export",
"conf_general_tok_title" : "Token management",
"conf_general_tok_title" : "Token Management",
"conf_general_tok_desc" : "Tokens grant other applications access to the Hyperion API, an application can request a token where you need to accept it or you create them on your own below. These tokens are just required when \"API Authorization\" is enabled in network settings.",
"conf_general_tok_cidhead" : "Description",
"conf_general_tok_lastuse" : "Last use",
@ -108,6 +109,14 @@
"conf_general_createToken_btn" : "Create Token",
"conf_general_tok_diaTitle" : "New Token created!",
"conf_general_tok_diaMsg" : "Here is your new token which can be used to grant an application access to the Hyperion API. For security reasons you can't view it again so use/note it now.",
"conf_general_inst_title" : "LED Hardware Instance Management",
"conf_general_inst_desc" : "Use different LED hardware at the same time. Each instance runs independent of each other which allows different LED layouts and calibration settings. Running instances are available at the top icon bar",
"conf_general_inst_namehead" : "Instance name",
"conf_general_inst_actionhead" : "Action",
"conf_general_inst_name_title" : "New Instance name",
"conf_general_createInst_btn" : "Create Instance",
"conf_general_inst_delreq_h" : "Delete LED Hardware instance",
"conf_general_inst_delreq_t" : "Are you sure that you want to delete instance \"$1\"? All settings will be deleted too.",
"conf_helptable_option" : "Option",
"conf_helptable_expl" : "Explanation",
"conf_effect_path_intro" : "Load effects from the defined paths. Additional you can disable single effects by name to hide them from all effect lists.",
@ -592,7 +601,7 @@
"edt_conf_net_ipWhitelist_expl":"You can whitelist IP addresses instead allowing all connections from internet to connect to the Hyperion API/Webinterface.",
"edt_conf_net_ip_itemtitle":"IP",
"edt_conf_net_apiAuth_title":"API Authentication",
"edt_conf_net_apiAuth_expl":"Enforce all applications that use the Hyperion API to authenticate themself against Hyperion. Higher security, as you control the access and revoke it at any time.",
"edt_conf_net_apiAuth_expl":"Enforce all applications that use the Hyperion API to authenticate themself against Hyperion (Exception see \"Local API Authentication\"). Higher security, as you control the access and revoke it at any time.",
"edt_conf_net_localApiAuth_title" : "Local API Authentication",
"edt_conf_net_localApiAuth_expl" : "When enabled, connections from your home network needs to authenticate themself against Hyperion too.",
"edt_conf_js_heading_title" : "JSON Server",

View File

@ -93,11 +93,20 @@
<!-- /.navbar-header -->
<ul class="nav navbar-top-links navbar-right">
<li class="dropdown" id="btn_hypinstanceswitch" style="display:none">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-exchange fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul id="hyp_inst_listing" class="dropdown-menu dropdown-alerts">
</ul>
</li>
<!--
<li class="dropdown" id="btn_instanceswitch" style="display:none">
<a>
<i class="fa fa-exchange fa-fw"></i>
</a>
</li>
-->
<li class="dropdown" id="btn_open_ledsim">
<a>
<i class="fa fa-television fa-fw"></i>
@ -192,13 +201,6 @@
<!-- Page Content -->
<div id="page-wrapper" style="padding-top:10px; overflow: hidden;">
<div id="hyperion_reload_notify" style="display:none;padding:0 10px;margin:0">
<div class="bs-callout bs-callout-warning">
<h4 data-i18n="dashboard_alert_message_confedit_t"></h4>
<span data-i18n="dashboard_alert_message_confedit"></span>
<p><button id="btn_hyperion_reload" class="btn btn-warning btn-sm" style="margin-top:10px" data-i18n="general_btn_restarthyperion"></button></p>
</div>
</div>
<div id="hyperion_config_write_success_notify" style="display:none;padding:0 10px;margin:0">
<div class="bs-callout bs-callout-success">
<h4 data-i18n="dashboard_alert_message_confsave_success_t"></h4>

View File

@ -76,7 +76,7 @@ $(document).ready( function() {
// add more info
$('#dash_leddevice').html(window.serverInfo.ledDevices.active);
$('#dash_currv').html(window.currentChannel+' '+window.currentVersion);
$('#dash_instance').html(window.serverConfig.general.name);
$('#dash_instance').html(window.currentHyperionInstanceName);
$('#dash_ports').html(window.serverConfig.flatbufServer.port+' | '+window.serverConfig.protoServer.port);
$('#dash_watchedversionbranch').html(window.serverConfig.general.watchedVersionBranch);

View File

@ -28,7 +28,6 @@ $(document).ready( function() {
// Token handling
function buildTokenList()
{
console.log(tokenList)
$('.tktbody').html("");
for(var key in tokenList)
{
@ -69,6 +68,59 @@ $(document).ready( function() {
buildTokenList()
});
// Instance handling
function handleInstanceStartStop(e)
{
var inst = e.currentTarget.id.split("_")[1];
var start = (e.currentTarget.className == "btn btn-danger")
requestInstanceStartStop(inst, start)
}
function handleInstanceDelete(e)
{
var inst = e.currentTarget.id.split("_")[1];
showInfoDialog('delplug',$.i18n('conf_general_inst_delreq_h'),$.i18n('conf_general_inst_delreq_t',getInstanceNameByIndex(inst)));
$("#id_btn_yes").off().on('click', function(){
requestInstanceDelete(inst)
});
}
function buildInstanceList()
{
var inst = serverInfo.instance
$('.itbody').html("");
for(var key in inst)
{
var startBtnColor = inst[key].running ? "success" : "danger";
var startBtnText = inst[key].running ? $.i18n('general_btn_stop') : $.i18n('general_btn_start');
var startBtn = "-"
var delBtn = "-";
if(inst[key].instance > 0)
{
delBtn = '<button id="instdel_'+inst[key].instance+'" type="button" class="btn btn-danger">'+$.i18n('general_btn_delete')+'</button>';
startBtn = '<button id="inst_'+inst[key].instance+'" type="button" class="btn btn-'+startBtnColor+'">'+startBtnText+'</button>';
}
$('.itbody').append(createTableRow([inst[key].friendly_name, startBtn, delBtn], false, true));
$('#inst_'+inst[key].instance).off().on('click', handleInstanceStartStop);
$('#instdel_'+inst[key].instance).off().on('click', handleInstanceDelete);
}
}
createTable('ithead', 'itbody', 'itable');
$('.ithead').html(createTableRow([$.i18n('conf_general_inst_namehead'), $.i18n('conf_general_inst_actionhead'), $.i18n('general_btn_delete')], true, true));
buildInstanceList();
$('#inst_name').off().on('input',function(e) {
(e.currentTarget.value.length >= 5) ? $('#btn_create_inst').attr('disabled', false) : $('#btn_create_inst').attr('disabled', true);
});
$('#btn_create_inst').off().on('click',function(e) {
requestInstanceCreate($('#inst_name').val());
});
$(hyperion).off("instance-updated").on("instance-updated", function(event) {
buildInstanceList()
});
//import
function dis_imp_btn(state)
{
@ -149,6 +201,7 @@ $(document).ready( function() {
if(window.showOptHelp)
createHint("intro", $.i18n('conf_general_intro'), "editor_container");
createHint("intro", $.i18n('conf_general_tok_desc'), "tok_desc_cont");
createHint("intro", $.i18n('conf_general_inst_desc'), "inst_desc_cont");
removeOverlay();
});

View File

@ -1,3 +1,5 @@
var instNameInit = false
$(document).ready( function() {
loadContentTo("#container_connection_lost","connection_lost");
@ -21,10 +23,19 @@ $(document).ready( function() {
}
});
if (window.serverInfo.hyperion.enabled)
$("#hyperion_disabled_notify").fadeOut("fast");
// determine button visibility
var running = window.serverInfo.instance.filter(entry => entry.running);
if(running.length > 1)
$('#btn_hypinstanceswitch').toggle(true)
else
$("#hyperion_disabled_notify").fadeIn("fast");
$('#btn_hypinstanceswitch').toggle(false)
// update listing at button
updateHyperionInstanceListing()
if(!instNameInit)
{
window.currentHyperionInstanceName = getInstanceNameByIndex(0);
instNameInit = true;
}
updateSessions();
}); // end cmd-serverinfo
@ -113,6 +124,50 @@ $(document).ready( function() {
$(window.hyperion).trigger("components-updated");
});
$(window.hyperion).on("cmd-instance-update", function(event) {
window.serverInfo.instance = event.response.data
var avail = event.response.data;
// notify the update
$(window.hyperion).trigger("instance-updated");
// if our current instance is no longer available we are at instance 0 again.
var isInData = false;
for(var key in avail)
{
if(avail[key].instance == currentHyperionInstance && avail[key].running)
{
isInData = true;
}
}
if(!isInData)
{
currentHyperionInstance = 0;
currentHyperionInstanceName = getInstanceNameByIndex(0);
requestServerConfig();
setTimeout(requestServerInfo,100)
setTimeout(requestTokenInfo,200)
setTimeout(loadContent,300, undefined, true)
}
// determine button visibility
var running = serverInfo.instance.filter(entry => entry.running);
if(running.length > 1)
$('#btn_hypinstanceswitch').toggle(true)
else
$('#btn_hypinstanceswitch').toggle(false)
// update listing for button
updateHyperionInstanceListing()
});
$(window.hyperion).on("cmd-instance-switchTo", function(event){
requestServerConfig();
setTimeout(requestServerInfo,200)
setTimeout(requestTokenInfo,400)
setTimeout(loadContent,400, undefined, true)
});
$(window.hyperion).on("cmd-effects-update", function(event){
window.serverInfo.effects = event.response.data.effects
});

View File

@ -224,7 +224,7 @@ $(document).ready(function() {
var enable_icon = (components[idx].enabled? "fa-play" : "fa-stop");
var comp_name = components[idx].name;
var comp_btn_id = "comp_btn_"+comp_name;
var comp_goff = hyperionEnabled? "enabled" : "disabled";
var comp_goff = hyperionEnabled? "enabled" : "disabled";
// create btn if not there
if ($("#"+comp_btn_id).length == 0)

View File

@ -25,6 +25,8 @@ window.loggingHandlerInstalled = false;
window.watchdog = 0;
window.debugMessagesActive = true;
window.wSess = [];
window.currentHyperionInstance = 0;
window.currentHyperionInstanceName = "?";
window.comps = [];
tokenList = {};
@ -188,9 +190,32 @@ function requestTokenDelete(id)
sendToHyperion("authorize","deleteToken",'"id":"'+id+'"');
}
function requestInstanceStartStop(inst, start)
{
if(start)
sendToHyperion("instance","startInstance",'"instance": '+inst);
else
sendToHyperion("instance","stopInstance",'"instance": '+inst);
}
function requestInstanceDelete(inst)
{
sendToHyperion("instance","deleteInstance",'"instance": '+inst);
}
function requestInstanceCreate(name)
{
sendToHyperion("instance","createInstance",'"name": "'+name+'"');
}
function requestInstanceSwitch(inst)
{
sendToHyperion("instance","switchTo",'"instance": '+inst);
}
function requestServerInfo()
{
sendToHyperion("serverinfo","",'"subscribe":["components-update","sessions-update","priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update", "settings-update"]');
sendToHyperion("serverinfo","",'"subscribe":["components-update","sessions-update","priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update", "settings-update", "instance-update"]');
}
function requestSysInfo()

View File

@ -118,6 +118,49 @@ function loadContent(event, forceRefresh)
}
}
function getInstanceNameByIndex(index)
{
var instData = window.serverInfo.instance
for(var key in instData)
{
if(instData[key].instance == index)
return instData[key].friendly_name;
}
return "unknown"
}
function updateHyperionInstanceListing()
{
var data = window.serverInfo.instance.filter(entry => entry.running);
$('#hyp_inst_listing').html("");
for(var key in data)
{
var currInstMarker = (data[key].instance == window.currentHyperionInstance) ? "component-on" : "";
var html = '<li id="hyperioninstance_'+data[key].instance+'"> \
<a> \
<div> \
<i class="fa fa-circle fa-fw '+currInstMarker+'"></i> \
<span>'+data[key].friendly_name+'</span> \
</div> \
</a> \
</li> '
if(data.length-1 > key)
html += '<li class="divider"></li>'
$('#hyp_inst_listing').append(html);
$('#hyperioninstance_'+data[key].instance).off().on("click",function(e){
var inst = e.currentTarget.id.split("_")[1]
requestInstanceSwitch(inst)
window.currentHyperionInstance = inst;
window.currentHyperionInstanceName = getInstanceNameByIndex(inst);
updateHyperionInstanceListing()
});
}
}
function loadContentTo(containerId, fileName)
{
$(containerId).load("/content/"+fileName+".html");
@ -191,6 +234,11 @@ function showInfoDialog(type,header,message)
$('#id_footer').html('<button type="button" id="id_btn_import" class="btn btn-warning" data-dismiss="modal"><i class="fa fa-fw fa-save"></i>'+$.i18n('general_btn_saverestart')+'</button>');
$('#id_footer').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
}
else if (type == "delplug"){
$('#id_body').html('<i style="margin-bottom:20px" class="fa fa-warning modal-icon-warning">');
$('#id_footer').html('<button type="button" id="id_btn_yes" class="btn btn-warning" data-dismiss="modal"><i class="fa fa-fw fa-trash"></i>'+$.i18n('general_btn_yes')+'</button>');
$('#id_footer').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
}
else if (type == "checklist")
{
$('#id_body').html('<img style="margin-bottom:20px" src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!">');

View File

@ -11,8 +11,12 @@
#include <QMutex>
#include <QString>
// HyperionInstanceManager
#include <hyperion/HyperionIManager.h>
class JsonCB;
class AuthManager;
class HyperionIManager;
class JsonAPI : public QObject
{
@ -68,6 +72,14 @@ private slots:
///
void handleTokenResponse(const bool& success, QObject* caller, const QString& token, const QString& comment, const QString& id);
///
/// @brief Handle whenever the state of a instance (HyperionIManager) changes according to enum instanceState
/// @param instaneState A state from enum
/// @param instance The index of instance
/// @param name The name of the instance, just available with H_CREATED
///
void handleInstanceStateChange(const instanceState& state, const quint8& instance, const QString& name = QString());
signals:
///
/// Signal emits with the reply message provided with handleMessage()
@ -99,6 +111,9 @@ private:
/// Log instance
Logger* _log;
/// Hyperion instance manager
HyperionIManager* _instanceManager;
/// Hyperion instance
Hyperion* _hyperion;
@ -125,6 +140,14 @@ private:
/// timeout for led color refresh
volatile qint64 _led_stream_timeout;
///
/// @brief Handle the switches of Hyperion instances
/// @param instance the instance to switch
/// @param forced indicate if it was a forced switch by system
/// @return true on success. false if not found
///
const bool handleInstanceSwitch(const quint8& instance = 0, const bool& forced = false);
///
/// Handle an incoming JSON Color message
///
@ -181,6 +204,13 @@ private:
///
void handleClearCommand(const QJsonObject & message, const QString &command, const int tan);
///
/// Handle an incoming JSON Clearall message
///
/// @param message the incoming message
///
void handleClearallCommand(const QJsonObject & message, const QString &command, const int tan);
///
/// Handle an incoming JSON Adjustment message
///
@ -259,12 +289,11 @@ private:
///
const bool handleHTTPAuth(const QString& command, const int& tan, const QString& token);
///
/// Handle an incoming JSON Clearall message
/// Handle an incoming JSON instance message
///
/// @param message the incoming message
///
void handleClearallCommand(const QJsonObject & message, const QString &command, const int tan);
void handleInstanceCommand(const QJsonObject & message, const QString &command, const int tan);
///
/// Handle an incoming JSON message of unknown type

View File

@ -23,7 +23,7 @@ class JsonCB : public QObject
Q_OBJECT
public:
JsonCB(QObject* parent);
JsonCB(Hyperion* hyperion, QObject* parent);
///
/// @brief Subscribe to future data updates given by cmd
@ -94,6 +94,11 @@ private slots:
///
void handleSettingsChange(const settings::type& type, const QJsonDocument& data);
///
/// @brief Handle Hyperion instance manager change
///
void handleInstanceChange();
private:
/// pointer of Hyperion instance
Hyperion* _hyperion;

View File

@ -16,13 +16,9 @@ class AuthTable : public DBManager
public:
/// construct wrapper with auth table
AuthTable(const QString& rootPath, QObject* parent = nullptr)
AuthTable(QObject* parent = nullptr)
: DBManager(parent)
{
// Init Hyperion database usage
setRootPath(rootPath);
setDB("hyperion");
// init Auth table
setTable("auth");
// create table columns

219
include/db/InstanceTable.h Normal file
View File

@ -0,0 +1,219 @@
#pragma once
// db
#include <db/DBManager.h>
#include <db/SettingsTable.h>
// qt
#include <QDateTime>
///
/// @brief Hyperion instance manager specific database interface. prepares also the Hyperion database for all follow up usage (Init QtSqlConnection) along with db name
///
class InstanceTable : public DBManager
{
public:
InstanceTable(const QString& rootPath, QObject* parent = nullptr)
: DBManager(parent)
{
// Init Hyperion database usage
setRootPath(rootPath);
setDB("hyperion");
// Init instance table
setTable("instances");
createTable(QStringList()<<"instance INTEGER"<<"friendly_name TEXT"<<"enabled INTEGER DEFAULT 0"<<"last_use TEXT");
// create the first Hyperion instance index 0
createInstance();
};
~InstanceTable(){};
///
/// @brief Create a new Hyperion instance entry, the name needs to be unique
/// @param name The name of the instance
/// @param[out] inst The id that has been assigned
/// @return True on success else false
///
inline const bool createInstance(const QString& name, quint8& inst)
{
VectorPair fcond;
fcond.append(CPair("friendly_name",name));
// check duplicate
if(!recordExists(fcond))
{
inst = 0;
VectorPair cond;
cond.append(CPair("instance",inst));
// increment to next avail index
while(recordExists(cond))
{
inst++;
cond.removeFirst();
cond.append(CPair("instance",inst));
}
// create
QVariantMap data;
data["friendly_name"] = name;
data["instance"] = inst;
VectorPair lcond;
return createRecord(lcond, data);
}
return false;
}
///
/// @brief Delete a Hyperion instance
/// @param inst The id that has been assigned
/// @return True on success else false
///
inline const bool deleteInstance(const quint8& inst)
{
VectorPair cond;
cond.append(CPair("instance",inst));
if(deleteRecord(cond))
{
// delete settings entries
SettingsTable settingsTable(inst);
settingsTable.deleteInstance();
return true;
}
return false;
}
///
/// @brief Assign a new name for the given instance
/// @param inst The instance index
/// @param name The new name of the instance
/// @return True on success else false (instance not found)
///
inline const bool saveName(const quint8& inst, const QString& name)
{
if(instanceExist(inst))
{
VectorPair cond;
cond.append(CPair("instance",inst));
QVariantMap data;
data["friendly_name"] = name;
return updateRecord(cond, data);
}
return false;
}
///
/// @brief Get all instances with all columns
/// @param justEnabled return just enabled instances if true
/// @return The found instances
///
inline QVector<QVariantMap> getAllInstances(const bool& justEnabled = false)
{
QVector<QVariantMap> results;
getRecords(results);
if(justEnabled)
{
for (auto it = results.begin(); it != results.end();)
{
if( ! (*it)["enabled"].toBool())
{
it = results.erase(it);
continue;
}
++it;
}
}
return results;
}
///
/// @brief Test if instance record exists
/// @param[in] user The user id
/// @return true on success else false
///
inline const bool instanceExist(const quint8& inst)
{
VectorPair cond;
cond.append(CPair("instance",inst));
return recordExists(cond);
}
///
/// @brief Get instance name by instance index
/// @param index The index to search for
/// @return The name of this index, may return NOT FOUND if not found
///
inline const QString getNamebyIndex(const quint8 index)
{
QVariantMap results;
VectorPair cond;
cond.append(CPair("instance", index));
getRecord(cond, results, QStringList("friendly_name"));
QString name = results["friendly_name"].toString();
return name.isEmpty() ? "NOT FOUND" : name;
}
///
/// @brief Update 'last_use' timestamp
/// @param inst The instance to update
///
inline void setLastUse(const quint8& inst)
{
VectorPair cond;
cond.append(CPair("instance", inst));
QVariantMap map;
map["last_use"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
updateRecord(cond, map);
}
///
/// @brief Update 'enabled' column by instance index
/// @param inst The instance to update
/// @param newState True when enabled else false
///
inline void setEnable(const quint8& inst, const bool& newState)
{
VectorPair cond;
cond.append(CPair("instance", inst));
QVariantMap map;
map["enabled"] = newState;
updateRecord(cond, map);
}
///
/// @brief Get state of 'enabled' column by instance index
/// @param inst The instance to get
/// @return True when enabled else false
///
inline const bool isEnabled(const quint8& inst)
{
VectorPair cond;
cond.append(CPair("instance", inst));
QVariantMap results;
getRecord(cond, results);
return results["enabled"].toBool();
}
private:
///
/// @brief Create first Hyperion instance entry, if index 0 is not found.
///
inline void createInstance()
{
QVariantMap data;
data["friendly_name"] = "First LED Hardware instance";
VectorPair cond;
cond.append(CPair("instance", 0));
if(createRecord(cond, data))
setEnable(0, true);
else
throw std::runtime_error("Failed to create Hyperion root instance in db! This should never be the case...");
}
};

62
include/db/MetaTable.h Normal file
View File

@ -0,0 +1,62 @@
#pragma once
// hyperion
#include <db/DBManager.h>
// qt
#include <QDateTime>
#include <QUuid>
#include <QNetworkInterface>
#include <QCryptographicHash>
///
/// @brief meta table specific database interface
///
class MetaTable : public DBManager
{
public:
/// construct wrapper with plugins table and columns
MetaTable(QObject* parent = nullptr)
: DBManager(parent)
{
setTable("meta");
createTable(QStringList()<<"uuid TEXT"<<"created_at TEXT");
};
~MetaTable(){};
///
/// @brief Get the uuid, if the uuid is not set it will be created
/// @return The uuid
///
inline const QString getUUID()
{
QVector<QVariantMap> results;
getRecords(results, QStringList() << "uuid");
for(const auto & entry : results)
{
if(!entry["uuid"].toString().isEmpty())
return entry["uuid"].toString();
}
// create new uuidv5 based on net adapter MAC, save to db and return
QString hash;
foreach(QNetworkInterface interface, QNetworkInterface::allInterfaces())
{
if (!(interface.flags() & QNetworkInterface::IsLoopBack))
{
hash = QCryptographicHash::hash(interface.hardwareAddress().toLocal8Bit(),QCryptographicHash::Sha1).toHex();
break;
}
}
const QString newUuid = QUuid::createUuidV5(QUuid(), hash).toString().mid(1, 36);
VectorPair cond;
cond.append(CPair("uuid",newUuid));
QVariantMap map;
map["created_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
createRecord(cond, map);
return newUuid;
}
};

View File

@ -95,6 +95,16 @@ public:
return results["config"].toString();
}
///
/// @brief Delete all settings entries associated with this instance, called from InstanceTable of HyperionIManager
///
inline void deleteInstance() const
{
VectorPair cond;
cond.append(CPair("hyperion_inst",_hyperion_inst));
deleteRecord(cond);
}
inline bool isSettingGlobal(const QString& type) const
{
// list of global settings

View File

@ -26,6 +26,9 @@ public slots:
void setSignalDetectionEnable(bool enable);
void setDeviceVideoStandard(QString device, VideoStandard videoStandard);
signals:
void componentStateChanged(const hyperion::Components component, bool enable);
private slots:
void newFrame(const Image<ColorRgb> & image);
void readError(const char* err);

View File

@ -7,6 +7,7 @@
#include <QMap>
class AuthTable;
class MetaTable;
class QTimer;
///
@ -19,7 +20,7 @@ class AuthManager : public QObject
private:
friend class HyperionDaemon;
/// constructor is private, can be called from HyperionDaemon
AuthManager(const QString& rootPath, QObject* parent = 0);
AuthManager(QObject* parent = 0);
public:
struct AuthDefinition{
@ -31,6 +32,12 @@ public:
QString lastUse;
};
///
/// @brief Get the unique id (imported from removed class 'Stats')
/// @return The unique id
///
const QString & getID() { return _uuid; };
///
/// @brief Get all available token entries
///
@ -40,13 +47,13 @@ public:
/// @brief Check authorization is required according to the user setting
/// @return True if authorization required else false
///
const bool & isAuthRequired();
bool & isAuthRequired();
///
/// @brief Check if authorization is required for local network connections
/// @return True if authorization required else false
///
const bool & isLocalAuthRequired();
bool & isLocalAuthRequired();
///
/// @brief Create a new token and skip the usual chain
@ -61,14 +68,14 @@ public:
/// @param pw The password
/// @return True if authorized else false
///
const bool isUserAuthorized(const QString& user, const QString& pw);
bool isUserAuthorized(const QString& user, const QString& pw);
///
/// @brief Check if token is authorized
/// @param token The token
/// @return True if authorized else false
///
const bool isTokenAuthorized(const QString& token);
bool isTokenAuthorized(const QString& token);
///
/// @brief Generate a new pending token request with the provided comment and id as identifier helper
@ -83,14 +90,14 @@ public:
/// @param id The id of the request
/// @return True on success, false if not found
///
const bool acceptTokenRequest(const QString& id);
bool acceptTokenRequest(const QString& id);
///
/// @brief Deny a token request by id, inform the requester
/// @param id The id of the request
/// @return True on success, false if not found
///
const bool denyTokenRequest(const QString& id);
bool denyTokenRequest(const QString& id);
///
/// @brief Get pending requests
@ -103,7 +110,7 @@ public:
/// @param id The token id
/// @return True on success else false (or not found)
///
const bool deleteToken(const QString& id);
bool deleteToken(const QString& id);
/// Pointer of this instance
static AuthManager* manager;
@ -140,6 +147,12 @@ private:
/// Database interface for auth table
AuthTable* _authTable;
/// Database interface for meta table
MetaTable* _metaTable;
/// Unique ID (imported from removed class 'Stats')
QString _uuid;
/// All pending requests
QMap<QString,AuthDefinition> _pendingRequests;

View File

@ -9,6 +9,7 @@
#include <grabber/VideoStandard.h>
#include <utils/ImageResampler.h>
#include <utils/Logger.h>
#include <utils/Components.h>
///
/// @brief The Grabber class is responsible to apply image resizes (with or without ImageResampler)
@ -96,6 +97,12 @@ public:
///
void setEnabled(bool enable);
signals:
///
/// @brief PIPE component state changes from HyperionDaemon to V4L2Grabber
///
void componentStateChanged(const hyperion::Components component, bool enable);
protected:
ImageResampler _imageResampler;

View File

@ -11,7 +11,6 @@
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QFileSystemWatcher>
#include <QMutex>
// hyperion-utils includes
@ -36,7 +35,6 @@
#include <utils/settings.h>
// Forward class declaration
class QTimer;
class HyperionDaemon;
class ImageProcessor;
class MessageForwarder;
@ -71,28 +69,13 @@ public:
///
/// Destructor; cleans up resources
///
~Hyperion();
virtual ~Hyperion();
///
/// free all alocated objects, should be called only from constructor or before restarting hyperion
///
void freeObjects(bool emitCloseSignal=false);
///
/// @brief creates a new Hyperion instance, usually called from the Hyperion Daemon
/// @param[in] daemon The Hyperion daemon parent
/// @param[in] instance The instance id
/// @param[in] rootPath Root path of all hyperion userdata
/// @return Hyperion instance pointer
///
static Hyperion* initInstance(HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath);
///
/// @brief Get a pointer of this Hyperion instance
/// @return Hyperion instance pointer
///
static Hyperion* getInstance();
///
/// @brief Get a pointer to the effect engine
/// @return EffectEngine instance pointer
@ -122,6 +105,12 @@ public:
///
bool saveSettings(QJsonObject config, const bool& correct = false);
///
/// @brief Get instance index of this instance
/// @return The index of this instance
///
const quint8 & getInstanceIndex() { return _instIndex; };
///
/// Returns the number of attached leds
///
@ -194,14 +183,6 @@ public:
/// @return json config
const QJsonObject& getQJsonConfig();
/// get path+filename of configfile
/// @return the current config path+filename
QString getConfigFilePath() { return _configFile; };
/// get filename of configfile
/// @return the current config filename
QString getConfigFileName() const;
///
/// @brief Register a new input by priority, the priority is not active (timeout -100 isn't muxer recognized) until you start to update the data with setInput()
/// A repeated call to update the base data of a known priority won't overwrite their current timeout
@ -244,21 +225,9 @@ public:
ComponentRegister& getComponentRegister() { return _componentRegister; };
bool configModified() { return _configMod; };
bool configWriteable() { return _configWrite; };
/// gets the methode how image is maped to leds
const int & getLedMappingType();
/// get the root path for all hyperion user data files
const QString &getRootPath() { return _rootPath; };
/// get unique id per instance
const QString &getId(){ return _id; };
/// set unique id
void setId(QString id){ _id = id; };
int getLatchTime() const;
/// forward smoothing config
@ -295,13 +264,6 @@ public slots:
///
bool setInputImage(const int priority, const Image<ColorRgb>& image, int64_t timeout_ms = -1, const bool& clearEffect = true);
///
/// @brief Set the given priority to inactive
/// @param priority The priority
/// @return True on success false if not found
///
bool setInputInactive(const quint8& priority);
///
/// Writes a single color to all the leds for the given time and priority
/// Registers comp color or provided type against muxer
@ -314,6 +276,13 @@ public slots:
///
void setColor(int priority, const ColorRgb &ledColor, const int timeout_ms = -1, const QString& origin = "System" ,bool clearEffects = true);
///
/// @brief Set the given priority to inactive
/// @param priority The priority
/// @return True on success false if not found
///
bool setInputInactive(const quint8& priority);
///
/// Returns the list with unique adjustment identifiers
/// @return The list with adjustment identifiers
@ -372,8 +341,15 @@ public slots:
///
void setVideoMode(const VideoMode& mode);
public:
static Hyperion *_hyperion;
///
/// @brief Init after thread start
///
void start();
///
/// @brief Stop the execution of this thread, helper to properly track eventing
///
void stop();
signals:
/// Signal which is emitted when a priority channel is actively cleared
@ -453,6 +429,16 @@ signals:
///
void rawLedColors(const std::vector<ColorRgb>& ledValues);
///
/// @brief Emits before thread quit is requested
///
void finished();
///
/// @brief Emits after thread has been started
///
void started();
private slots:
///
/// Updates the priority muxer with the current time and (re)writes the led color with applied
@ -460,9 +446,6 @@ private slots:
///
void update();
/// check for configWriteable and modified changes, called by _fsWatcher or fallback _cTimer
void checkConfigState(QString cfile = NULL);
///
/// @brief Apply ComponentRegister emits for COMP_ALL. Enables/Disables core timers
/// @param comp The component
@ -477,17 +460,23 @@ private slots:
///
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
///
/// @brief Apply new videoMode from Daemon to _currVideoMode
///
void handleNewVideoMode(const VideoMode& mode) { _currVideoMode = mode; };
private:
friend class HyperionDaemon;
friend class HyperionIManager;
///
/// Constructs the Hyperion instance based on the given Json configuration
/// @brief Constructs the Hyperion instance, just accessible for HyperionIManager
/// @param instance The instance index
///
/// @param[in] qjsonConfig The Json configuration
///
Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath);
Hyperion(const quint8& instance);
/// The parent Hyperion Daemon
HyperionDaemon* _daemon;
/// instance index
const quint8 _instIndex;
/// Settings manager of this instance
SettingsManager* _settingsManager;
@ -521,17 +510,8 @@ private:
/// Effect engine
EffectEngine * _effectEngine;
// Message forwarder
MessageForwarder * _messageForwarder;
/// the name of config file
QString _configFile;
/// root path for all hyperion user data files
QString _rootPath;
/// unique id per instance
QString _id;
// // Message forwarder
// MessageForwarder * _messageForwarder;
/// Logger instance
Logger * _log;
@ -539,25 +519,11 @@ private:
/// count of hardware leds
unsigned _hwLedCount;
QByteArray _configHash;
QSize _ledGridSize;
/// Store the previous compID for smarter update()
hyperion::Components _prevCompId;
/// Observe filesystem changes (_configFile), if failed use Timer
QFileSystemWatcher _fsWatcher;
QTimer* _cTimer;
/// holds the prev states of configWriteable and modified
bool _prevConfigMod = false;
bool _prevConfigWrite = true;
/// holds the current states of configWriteable and modified
bool _configMod = false;
bool _configWrite = true;
/// Background effect instance, kept active to react on setting changes
BGEffectHandler* _BGEffectHandler;
/// Capture control for Daemon native capture
@ -566,6 +532,8 @@ private:
/// buffer for leds (with adjustment)
std::vector<ColorRgb> _ledBuffer;
VideoMode _currVideoMode = VIDEO_2D;
/// Boblight instance
BoblightServer* _boblightServer;

View File

@ -0,0 +1,177 @@
#pragma once
// util
#include <utils/Logger.h>
#include <utils/VideoMode.h>
#include <utils/settings.h>
#include <utils/Components.h>
// qt
#include <QMap>
class Hyperion;
class InstanceTable;
enum instanceState{
H_STARTED,
H_ON_STOP,
H_STOPPED,
H_CREATED,
H_DELETED
};
///
/// @brief HyperionInstanceManager manages the instances of the the Hyperion class
///
class HyperionIManager : public QObject
{
Q_OBJECT
public:
// global instance pointer
static HyperionIManager* getInstance() { return HIMinstance; };
static HyperionIManager* HIMinstance;
///
/// @brief Is given instance running?
/// @param inst The instance to check
/// @return True when running else false
///
const bool IsInstanceRunning(const quint8& inst) { return _runningInstances.contains(inst); };
///
/// @brief Get a Hyperion instance by index
/// @param intance the index
/// @return Hyperion instance, if index is not found returns instance 0
///
Hyperion* getHyperionInstance(const quint8& instance = 0);
///
/// @brief Get instance data of all instaces in db + running state
///
const QVector<QVariantMap> getInstanceData();
///
/// @brief Start a Hyperion instance
/// @param instance Instance index
/// @param block If true return when thread has been started
/// @return Return true on success, false if not found in db
///
const bool startInstance(const quint8& inst, const bool& block = false);
///
/// @brief Stop a Hyperion instance
/// @param instance Instance index
/// @param block If true return when thread has been started
/// @return Return true on success, false if not found in db
///
const bool stopInstance(const quint8& inst, const bool& block = false);
///
/// @brief Create a new Hyperion instance entry in db
/// @param name The friendly name of the instance
/// @param start If true it will be started after creation (async)
/// @return Return true on success false if name is already in use or a db error occurred
///
const bool createInstance(const QString& name, const bool& start = false);
///
/// @brief Delete Hyperion instance entry in db. Cleanup also all associated table data for this instance
/// @param inst The instance index
/// @return Return true on success, false if not found or not allowed
///
const bool deleteInstance(const quint8& inst);
///
/// @brief Assign a new name to the given instance
/// @param inst The instance index
/// @param name The instance name index
/// @return Return true on success, false if not found
///
const bool saveName(const quint8& inst, const QString& name);
signals:
///
/// @brief Emits whenever the state of a instance changes according to enum instanceState
/// @param instaneState A state from enum
/// @param instance The index of instance
/// @param name The name of the instance, just available with H_CREATED
///
void instanceStateChanged(const instanceState& state, const quint8& instance, const QString& name = QString());
///
/// @brief Emits whenever something changes, the lazy version of instanceStateChanged (- H_ON_STOP) + saveName() emit
///
void change();
signals:
///////////////////////////////////////
/// FROM HYPERIONDAEMON TO HYPERION ///
///////////////////////////////////////
///
/// @brief PIPE videoMode back to Hyperion
///
void newVideoMode(const VideoMode& mode);
///////////////////////////////////////
/// FROM HYPERION TO HYPERIONDAEMON ///
///////////////////////////////////////
///
/// @brief PIPE settings events from Hyperion
///
void settingsChanged(const settings::type& type, const QJsonDocument& data);
///
/// @brief PIPE videoMode request changes from Hyperion to HyperionDaemon
///
void requestVideoMode(const VideoMode& mode);
///
/// @brief PIPE component state changes from Hyperion to HyperionDaemon
///
void componentStateChanged(const hyperion::Components component, bool enable);
private slots:
///
/// @brief handle started signal of Hyperion instances
///
void handleStarted();
///
/// @brief handle finished signal of Hyperion instances
///
void handleFinished();
private:
friend class HyperionDaemon;
///
/// @brief Construct the Manager
/// @param The root path of all userdata
///
HyperionIManager(const QString& rootPath, QObject* parent = nullptr);
///
/// @brief Start all instances that are marked as enabled in db. Non blocking
///
void startAll();
///
/// @brief Stop all instances, used from hyperiond
///
void stopAll();
///
/// @brief check if a instance is allowed for management. Instance 0 represents the root instance
/// @apram inst The instance to check
///
const bool isInstAllowed(const quint8& inst) { return (inst > 0); };
private:
Logger* _log;
InstanceTable* _instanceTable;
const QString _rootPath;
QMap<quint8, Hyperion*> _runningInstances;
QList<quint8> _startQueue;
};

View File

@ -18,11 +18,10 @@ class SettingsManager : public QObject
public:
///
/// @brief Construct a settings manager and assign a hyperion instance
/// @params instance Instance number of Hyperion
/// @params configFile The config file
/// @params hyperion The parent hyperion instance
/// @params instance Instance index of HyperionInstanceManager
/// @params parent The parent hyperion instance
///
SettingsManager(const quint8& instance, const QString& configFile, Hyperion* hyperion = nullptr);
SettingsManager(const quint8& instance, QObject* parent = nullptr);
///
/// @brief Save a complete json config

View File

@ -1,18 +1,18 @@
#pragma once
// qt includes
#include <QObject>
#include <QString>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QTimer>
// STL incldues
// STL includes
#include <vector>
#include <map>
#include <algorithm>
#include <QTimer>
// Utility includes
#include <utils/ColorRgb.h>
#include <utils/ColorRgbw.h>

View File

@ -19,6 +19,7 @@
class BonjourServiceRegister;
class QUdpSocket;
class NetOrigin;
class Hyperion;
///
/// This class creates a UDP server which accepts connections from boblight clients.
@ -57,7 +58,7 @@ public slots:
///
void stop();
void componentStateChanged(const hyperion::Components component, bool enable);
void updatedComponentState(const hyperion::Components component, const bool enable);
///
/// @brief Handle settings update from Hyperion Settingsmanager emit or this constructor

View File

@ -55,12 +55,6 @@ QString getDirName( QString sourceFile);
///
bool removeFile(const QString& path, Logger* log, bool ignError=false);
///
/// @brief Convert a path that may contain special placeholders
/// @param[in] path The path to convert
///
QString convertPath(const QString path);
///
/// @brief resolve the file error and print a message
/// @param[in] file The file which caused the error

View File

@ -0,0 +1,29 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["instance"]
},
"subcommand" : {
"type" : "string",
"required" : true,
"enum" : ["createInstance","deleteInstance","startInstance","stopInstance","saveName","switchTo"]
},
"tan" : {
"type" : "integer"
},
"instance" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"name": {
"type": "string",
"minLength" : 5
}
},
"additionalProperties": false
}

View File

@ -5,7 +5,7 @@
"command": {
"type" : "string",
"required" : true,
"enum" : ["color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging", "processing", "sysinfo", "videomode", "authorize", "transform", "correction" , "temperature"]
"enum" : ["color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging", "processing", "sysinfo", "videomode", "authorize", "instance", "transform", "correction" , "temperature"]
}
}
}

View File

@ -19,6 +19,7 @@
<file alias="schema-processing">JSONRPC_schema/schema-processing.json</file>
<file alias="schema-videomode">JSONRPC_schema/schema-videomode.json</file>
<file alias="schema-authorize">JSONRPC_schema/schema-authorize.json</file>
<file alias="schema-instance">JSONRPC_schema/schema-instance.json</file>
<!-- The following schemas are derecated but used to ensure backward compatibility with hyperion Classic remote control-->
<file alias="schema-transform">JSONRPC_schema/schema-hyperion-classic.json</file>
<file alias="schema-correction">JSONRPC_schema/schema-hyperion-classic.json</file>

View File

@ -49,8 +49,9 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, const bool& localConnection,
, _noListener(noListener)
, _peerAddress(peerAddress)
, _log(log)
, _hyperion(Hyperion::getInstance())
, _jsonCB(new JsonCB(this))
, _instanceManager(HyperionIManager::getInstance())
, _hyperion(nullptr)
, _jsonCB(nullptr)
, _streaming_logging_activated(false)
, _image_stream_timeout(0)
, _led_stream_timeout(0)
@ -65,11 +66,60 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, const bool& localConnection,
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &JsonAPI::handlePendingTokenRequest);
connect(_authManager, &AuthManager::tokenResponse, this, &JsonAPI::handleTokenResponse);
// the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
// listen for killed instances
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &JsonAPI::handleInstanceStateChange);
// notify hyperion about a jsonMessageForward
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
// init Hyperion pointer
handleInstanceSwitch(0);
// // notify hyperion about a jsonMessageForward
// connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
}
const bool JsonAPI::handleInstanceSwitch(const quint8& inst, const bool& forced)
{
// check if we are already on the requested instance
if(_hyperion != nullptr && _hyperion->getInstanceIndex() == inst)
return true;
if(_instanceManager->IsInstanceRunning(inst))
{
Debug(_log,"Client '%s' switch to Hyperion instance %d", QSTRING_CSTR(_peerAddress), inst);
// cut all connections between hyperion / plugins and this
if(_hyperion != nullptr)
disconnect(_hyperion, 0, this, 0);
// get new Hyperion pointer
_hyperion = _instanceManager->getHyperionInstance(inst);
// the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client
QStringList cbCmds;
if(_jsonCB != nullptr)
{
cbCmds = _jsonCB->getSubscribedCommands();
delete _jsonCB;
}
_jsonCB = new JsonCB(_hyperion, this);
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
// read subs
for(const auto & entry : cbCmds)
{
_jsonCB->subscribeFor(entry);
}
// // imageStream last state
// if(_ledcolorsImageActive)
// connect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage, Qt::UniqueConnection);
//
// //ledColor stream last state
// if(_ledcolorsLedsActive)
// connect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate, Qt::UniqueConnection);
return true;
}
return false;
}
void JsonAPI::handleMessage(const QString& messageString, const QString& httpAuthHeader)
@ -140,6 +190,7 @@ void JsonAPI::handleMessage(const QString& messageString, const QString& httpAut
else if (command == "logging") handleLoggingCommand (message, command, tan);
else if (command == "processing") handleProcessingCommand (message, command, tan);
else if (command == "videomode") handleVideoModeCommand (message, command, tan);
else if (command == "instance") handleInstanceCommand (message, command, tan);
// BEGIN | The following commands are derecated but used to ensure backward compatibility with hyperion Classic remote control
else if (command == "clearall")
@ -290,7 +341,7 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, c
hyperion["channel" ] = QString(HYPERION_VERSION_CHANNEL);
hyperion["build" ] = QString(HYPERION_BUILD_ID);
hyperion["time" ] = QString(__DATE__ " " __TIME__);
hyperion["id" ] = _hyperion->getId();
hyperion["id" ] = _authManager->getID();
info["hyperion"] = hyperion;
// send the result
@ -485,14 +536,6 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
info["components"] = component;
info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(_hyperion->getLedMappingType());
// Add Hyperion
QJsonObject hyperion;
hyperion["config_modified" ] = _hyperion->configModified();
hyperion["config_writeable"] = _hyperion->configWriteable();
hyperion["enabled"] = _hyperion->getComponentRegister().isComponentEnabled(hyperion::COMP_ALL) ? true : false;
info["hyperion"] = hyperion;
// add sessions
QJsonArray sessions;
for (auto session: BonjourBrowserWrapper::getInstance()->getAllServices())
@ -509,6 +552,19 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
}
info["sessions"] = sessions;
// add instance info
QJsonArray instanceInfo;
for(const auto & entry : _instanceManager->getInstanceData())
{
QJsonObject obj;
obj.insert("friendly_name", entry["friendly_name"].toString());
obj.insert("instance", entry["instance"].toInt());
//obj.insert("last_use", entry["last_use"].toString());
obj.insert("running", entry["running"].toBool());
instanceInfo.append(obj);
}
info["instance"] = instanceInfo;
// BEGIN | The following entries are derecated but used to ensure backward compatibility with hyperion Classic remote control
// TODO Output the real transformation information instead of default
@ -656,6 +712,17 @@ void JsonAPI::handleClearCommand(const QJsonObject& message, const QString& comm
sendSuccessReply(command, tan);
}
void JsonAPI::handleClearallCommand(const QJsonObject& message, const QString& command, const int tan)
{
emit forwardJsonMessage(message);
// clear priority
_hyperion->clearall();
// send reply
sendSuccessReply(command, tan);
}
void JsonAPI::handleAdjustmentCommand(const QJsonObject& message, const QString& command, const int tan)
{
const QJsonObject & adjustment = message["adjustment"].toObject();
@ -887,8 +954,6 @@ void JsonAPI::handleComponentStateCommand(const QJsonObject& message, const QStr
{
if(_hyperion->getComponentRegister().setHyperionEnable(compState))
sendSuccessReply(command, tan);
else
sendErrorReply(QString("Hyperion is already %1").arg(compState ? "enabled" : "disabled"), command, tan );
return;
}
@ -1187,15 +1252,76 @@ const bool JsonAPI::handleHTTPAuth(const QString& command, const int& tan, const
return false;
}
void JsonAPI::handleClearallCommand(const QJsonObject& message, const QString& command, const int tan)
void JsonAPI::handleInstanceCommand(const QJsonObject & message, const QString &command, const int tan)
{
emit forwardJsonMessage(message);
const QString & subc = message["subcommand"].toString();
const quint8 & inst = message["instance"].toInt();
const QString & name = message["name"].toString();
// clear priority
_hyperion->clearall();
if(subc == "switchTo")
{
if(handleInstanceSwitch(inst))
sendSuccessReply(command+"-"+subc, tan);
else
sendErrorReply("Selected Hyperion instance isn't running",command+"-"+subc, tan);
return;
}
// send reply
sendSuccessReply(command, tan);
if(subc == "startInstance")
{
// silent fail
_instanceManager->startInstance(inst);
sendSuccessReply(command+"-"+subc, tan);
return;
}
if(subc == "stopInstance")
{
// silent fail
_instanceManager->stopInstance(inst);
sendSuccessReply(command+"-"+subc, tan);
return;
}
if(subc == "deleteInstance")
{
if(_userAuthorized)
{
if(_instanceManager->deleteInstance(inst))
sendSuccessReply(command+"-"+subc, tan);
else
sendErrorReply(QString("Failed to delete instance '%1'").arg(inst), command+"-"+subc, tan);
}
else
sendErrorReply("No Authorization",command+"-"+subc, tan);
return;
}
// create and save name requires name
if(name.isEmpty())
sendErrorReply("Name string required for this command",command+"-"+subc, tan);
if(subc == "createInstance")
{
if(_userAuthorized)
{
if(_instanceManager->createInstance(name))
sendSuccessReply(command+"-"+subc, tan);
else
sendErrorReply(QString("The instance name '%1' is already in use").arg(name), command+"-"+subc, tan);
}
else
sendErrorReply("No Authorization",command+"-"+subc, tan);
return;
}
if(subc == "saveName")
{
// silent fail
_instanceManager->saveName(inst,name);
sendSuccessReply(command+"-"+subc, tan);
return;
}
}
void JsonAPI::handleNotImplemented()
@ -1363,3 +1489,17 @@ void JsonAPI::handleTokenResponse(const bool& success, QObject* caller, const QS
sendErrorReply("Token request timeout or denied", cmd);
}
}
void JsonAPI::handleInstanceStateChange(const instanceState& state, const quint8& instance, const QString& name)
{
switch(state){
case H_ON_STOP:
if(_hyperion->getInstanceIndex() == instance)
{
handleInstanceSwitch();
}
break;
default:
break;
}
}

View File

@ -3,13 +3,23 @@
// hyperion
#include <hyperion/Hyperion.h>
// HyperionIManager
#include <hyperion/HyperionIManager.h>
// components
#include <hyperion/ComponentRegister.h>
// bonjour wrapper
#include <bonjour/bonjourbrowserwrapper.h>
// priorityMuxer
#include <hyperion/PriorityMuxer.h>
// utils
#include <utils/ColorSys.h>
// qt
#include <QDateTime>
// Image to led map helper
@ -17,15 +27,15 @@
using namespace hyperion;
JsonCB::JsonCB(QObject* parent)
JsonCB::JsonCB(Hyperion* hyperion, QObject* parent)
: QObject(parent)
, _hyperion(Hyperion::getInstance())
, _hyperion(hyperion)
, _componentRegister(& _hyperion->getComponentRegister())
, _bonjour(BonjourBrowserWrapper::getInstance())
, _prioMuxer(_hyperion->getMuxerInstance())
{
_availableCommands << "components-update" << "sessions-update" << "priorities-update" << "imageToLedMapping-update"
<< "adjustment-update" << "videomode-update" << "effects-update" << "settings-update";
<< "adjustment-update" << "videomode-update" << "effects-update" << "settings-update" << "instance-update";
}
bool JsonCB::subscribeFor(const QString& type)
@ -82,6 +92,12 @@ bool JsonCB::subscribeFor(const QString& type)
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange, Qt::UniqueConnection);
}
if(type == "instance-update")
{
_subscribedCommands << type;
connect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange, Qt::UniqueConnection);
}
return true;
}
@ -301,3 +317,19 @@ void JsonCB::handleSettingsChange(const settings::type& type, const QJsonDocumen
doCallback("settings-update", QVariant(dat));
}
void JsonCB::handleInstanceChange()
{
QJsonArray arr;
for(const auto & entry : HyperionIManager::getInstance()->getInstanceData())
{
QJsonObject obj;
obj.insert("friendly_name", entry["friendly_name"].toString());
obj.insert("instance", entry["instance"].toInt());
//obj.insert("last_use", entry["last_use"].toString());
obj.insert("running", entry["running"].toBool());
arr.append(obj);
}
doCallback("instance-update", QVariant(arr));
}

View File

@ -34,7 +34,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <utils/Logger.h>
#include <HyperionConfig.h>
#include <hyperion/Hyperion.h>
#include <hyperion/AuthManager.h>
BonjourServiceRegister::BonjourServiceRegister(QObject *parent)
: QObject(parent), dnssref(0), bonjourSocket(0)
@ -79,7 +79,7 @@ void BonjourServiceRegister::registerService(const BonjourRecord &record, quint1
}
#endif
// base txtRec
std::vector<std::pair<std::string, std::string> > txtBase = {{"id",Hyperion::getInstance()->getId().toStdString()},{"version",HYPERION_VERSION}};
std::vector<std::pair<std::string, std::string> > txtBase = {{"id",AuthManager::getInstance()->getID().toStdString()},{"version",HYPERION_VERSION}};
// create txt record
TXTRecordRef txtRec;
TXTRecordCreate(&txtRec,0,NULL);

View File

@ -52,9 +52,7 @@ QSqlDatabase DBManager::getDB() const
QSqlDatabase db = QSqlDatabase::database(_dbn);
if (db.isOpen() && db.isValid())
{
return db;
}
else
{
db = QSqlDatabase::addDatabase("QSQLITE", _dbn);

View File

@ -7,6 +7,8 @@
#include <QRgb>
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
FlatBufferClient::FlatBufferClient(QTcpSocket* socket, const int &timeout, QObject *parent)
: QObject(parent)
, _log(Logger::getInstance("FLATBUFSERVER"))
@ -15,7 +17,7 @@ FlatBufferClient::FlatBufferClient(QTcpSocket* socket, const int &timeout, QObje
, _timeoutTimer(new QTimer(this))
, _timeout(timeout * 1000)
, _priority()
, _hyperion(Hyperion::getInstance())
, _hyperion(HyperionIManager::getInstance()->getHyperionInstance())
{
// timer setup
_timeoutTimer->setSingleShot(true);

View File

@ -17,6 +17,7 @@
#include <linux/videodev2.h>
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
#include <QDirIterator>
#include <QFileInfo>
@ -57,9 +58,9 @@ V4L2Grabber::V4L2Grabber(const QString & device
setPixelDecimation(pixelDecimation);
getV4Ldevices();
// listen for component change for build-in grabber only
if (Hyperion::_hyperion)
connect(Hyperion::getInstance(), &Hyperion::componentStateChanged, this, &V4L2Grabber::componentStateChanged);
// connect componentStateChange only for build-in grabber
if (HyperionIManager::HIMinstance)
connect(this, &Grabber::componentStateChanged, this, &V4L2Grabber::componentStateChanged);
// init
setDeviceVideoStandard(device, videoStandard);

View File

@ -21,8 +21,10 @@ V4L2Wrapper::V4L2Wrapper(const QString &device,
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
// Handle the image in the captured thread using a direct connection
QObject::connect(&_grabber, SIGNAL(newFrame(Image<ColorRgb>)), this, SLOT(newFrame(Image<ColorRgb>)), Qt::DirectConnection);
QObject::connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
connect(&_grabber, SIGNAL(newFrame(Image<ColorRgb>)), this, SLOT(newFrame(Image<ColorRgb>)), Qt::DirectConnection);
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
connect(this, &V4L2Wrapper::componentStateChanged, _ggrabber, &Grabber::componentStateChanged);
}
bool V4L2Wrapper::start()

View File

@ -1,7 +1,8 @@
#include <hyperion/AuthManager.h>
// util
// db
#include <db/AuthTable.h>
#include <db/MetaTable.h>
// qt
#include <QJsonObject>
@ -9,15 +10,19 @@
AuthManager* AuthManager::manager = nullptr;
AuthManager::AuthManager(const QString& rootPath, QObject* parent)
AuthManager::AuthManager(QObject* parent)
: QObject(parent)
, _authTable(new AuthTable(rootPath, this))
, _authTable(new AuthTable(this))
, _metaTable(new MetaTable(this))
, _pendingRequests()
, _authRequired(true)
, _timer(new QTimer(this))
{
AuthManager::manager = this;
// get uuid
_uuid = _metaTable->getUUID();
// setup timer
_timer->setInterval(1000);
connect(_timer, &QTimer::timeout, this, &AuthManager::checkTimeout);
@ -29,12 +34,12 @@ AuthManager::AuthManager(const QString& rootPath, QObject* parent)
}
}
const bool & AuthManager::isAuthRequired()
bool & AuthManager::isAuthRequired()
{
return _authRequired;
}
const bool & AuthManager::isLocalAuthRequired()
bool & AuthManager::isLocalAuthRequired()
{
return _localAuthRequired;
}
@ -72,12 +77,12 @@ const QVector<AuthManager::AuthDefinition> AuthManager::getTokenList()
return finalVec;
}
const bool AuthManager::isUserAuthorized(const QString& user, const QString& pw)
bool AuthManager::isUserAuthorized(const QString& user, const QString& pw)
{
return _authTable->isUserAuthorized(user, pw);
}
const bool AuthManager::isTokenAuthorized(const QString& token)
bool AuthManager::isTokenAuthorized(const QString& token)
{
return _authTable->tokenExist(token);
}
@ -93,7 +98,7 @@ void AuthManager::setNewTokenRequest(QObject* caller, const QString& comment, co
}
}
const bool AuthManager::acceptTokenRequest(const QString& id)
bool AuthManager::acceptTokenRequest(const QString& id)
{
if(_pendingRequests.contains(id))
{
@ -106,7 +111,7 @@ const bool AuthManager::acceptTokenRequest(const QString& id)
return false;
}
const bool AuthManager::denyTokenRequest(const QString& id)
bool AuthManager::denyTokenRequest(const QString& id)
{
if(_pendingRequests.contains(id))
{
@ -122,7 +127,7 @@ const QMap<QString, AuthManager::AuthDefinition> AuthManager::getPendingRequests
return _pendingRequests;
}
const bool AuthManager::deleteToken(const QString& id)
bool AuthManager::deleteToken(const QString& id)
{
if(_authTable->deleteToken(id))
{

View File

@ -25,7 +25,7 @@ GrabberWrapper::GrabberWrapper(QString grabberName, Grabber * ggrabber, unsigned
connect(_timer, &QTimer::timeout, this, &GrabberWrapper::action);
// connect the image forwarding
_grabberName.startsWith("V4L")
(_grabberName.startsWith("V4L"))
? connect(this, &GrabberWrapper::systemImage, GlobalSignals::getInstance(), &GlobalSignals::setV4lImage)
: connect(this, &GrabberWrapper::systemImage, GlobalSignals::getInstance(), &GlobalSignals::setSystemImage);
}

View File

@ -5,17 +5,9 @@
#include <unistd.h>
// QT includes
#include <QDateTime>
#include <QThread>
#include <QRegExp>
#include <QString>
#include <QStringList>
#include <QCryptographicHash>
#include <QTimer>
#include <QFile>
#include <QFileInfo>
#include <QHostInfo>
#include <QCryptographicHash>
#include <QThread>
// hyperion include
#include <hyperion/Hyperion.h>
@ -35,9 +27,6 @@
// effect engine includes
#include <effectengine/EffectEngine.h>
// Hyperion Daemon
#include <../src/hyperiond/hyperiond.h>
// settingsManagaer
#include <hyperion/SettingsManager.h>
@ -50,28 +39,10 @@
// Boblight
#include <boblightserver/BoblightServer.h>
Hyperion* Hyperion::_hyperion = nullptr;
Hyperion* Hyperion::initInstance( HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath)
{
if ( Hyperion::_hyperion != nullptr )
throw std::runtime_error("Hyperion::initInstance can be called only one time");
Hyperion::_hyperion = new Hyperion(daemon, instance, configFile, rootPath);
return Hyperion::_hyperion;
}
Hyperion* Hyperion::getInstance()
{
if ( Hyperion::_hyperion == nullptr )
throw std::runtime_error("Hyperion::getInstance used without call of Hyperion::initInstance before");
return Hyperion::_hyperion;
}
Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath)
: _daemon(daemon)
, _settingsManager(new SettingsManager(instance, configFile, this))
Hyperion::Hyperion(const quint8& instance)
: QObject()
, _instIndex(instance)
, _settingsManager(new SettingsManager(instance, this))
, _componentRegister(this)
, _ledString(hyperion::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())))
, _ledStringClone(hyperion::createLedStringClone(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())))
@ -79,21 +50,33 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
, _muxer(_ledString.leds().size())
, _raw2ledAdjustment(hyperion::createLedColorsAdjustment(_ledString.leds().size(), getSetting(settings::COLOR).object()))
, _effectEngine(nullptr)
, _messageForwarder(new MessageForwarder(this))
, _configFile(configFile)
, _rootPath(rootPath)
// , _messageForwarder(new MessageForwarder(this))
, _log(Logger::getInstance("HYPERION"))
, _hwLedCount()
, _configHash()
, _ledGridSize(hyperion::getLedLayoutGridSize(getSetting(settings::LEDS).array()))
, _prevCompId(hyperion::COMP_INVALID)
, _ledBuffer(_ledString.leds().size(), ColorRgb::BLACK)
{
}
Hyperion::~Hyperion()
{
freeObjects(false);
}
void Hyperion::start()
{
// forward settings changed to Hyperion
connect(_settingsManager, &SettingsManager::settingsChanged, this, &Hyperion::settingsChanged);
// get newVideoMode from HyperionIManager
connect(this, &Hyperion::newVideoMode, this, &Hyperion::handleNewVideoMode);
if (!_raw2ledAdjustment->verifyAdjustments())
{
Warning(_log, "At least one led has no color calibration, please add all leds from your led layout to an 'LED index' field!");
}
// handle hwLedCount
_hwLedCount = qMax(unsigned(getSetting(settings::DEVICE).object()["hardwareLedCount"].toInt(getLedCount())), getLedCount());
@ -103,12 +86,14 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
{
_ledStringColorOrder.push_back(led.colorOrder);
}
for (Led& led : _ledStringClone.leds())
{
_ledStringColorOrder.insert(_ledStringColorOrder.begin() + led.index, led.colorOrder);
}
// connect Hyperion::update with Muxer visible priority changes as muxer updates independent
connect(&_muxer, &PriorityMuxer::visiblePriorityChanged, this, &Hyperion::update);
// listens for ComponentRegister changes of COMP_ALL to perform core enable/disable actions
connect(&_componentRegister, &ComponentRegister::updatedComponentState, this, &Hyperion::updatedComponentState);
@ -135,18 +120,6 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
_effectEngine = new EffectEngine(this);
connect(_effectEngine, &EffectEngine::effectListUpdated, this, &Hyperion::effectListUpdated);
// setup config state checks and initial shot
checkConfigState();
if(_fsWatcher.addPath(_configFile))
QObject::connect(&_fsWatcher, &QFileSystemWatcher::fileChanged, this, &Hyperion::checkConfigState);
else
{
_cTimer = new QTimer(this);
Warning(_log,"Filesystem Observer failed for file: %s, use fallback timer", _configFile.toStdString().c_str());
connect(_cTimer, SIGNAL(timeout()), this, SLOT(checkConfigState()));
_cTimer->start(2000);
}
// initial startup effect
hyperion::handleInitialEffect(this, getSetting(settings::FGEFFECT).object());
@ -163,13 +136,15 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
_boblightServer = new BoblightServer(this, getSetting(settings::BOBLSERVER));
connect(this, &Hyperion::settingsChanged, _boblightServer, &BoblightServer::handleSettingsUpdate);
// set unique id
_id = QString(QCryptographicHash::hash(getConfigFileName().toLocal8Bit(),QCryptographicHash::Sha1).toHex());
// instance inited
emit started();
// enter thread event loop
}
Hyperion::~Hyperion()
void Hyperion::stop()
{
freeObjects(false);
emit finished();
thread()->quit();
}
void Hyperion::freeObjects(bool emitCloseSignal)
@ -187,7 +162,7 @@ void Hyperion::freeObjects(bool emitCloseSignal)
delete _captureCont;
delete _effectEngine;
delete _raw2ledAdjustment;
delete _messageForwarder;
// delete _messageForwarder;
delete _settingsManager;
delete _ledDeviceWrapper;
}
@ -238,9 +213,6 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum
// handle hwLedCount update
_hwLedCount = qMax(unsigned(getSetting(settings::DEVICE).object()["hardwareLedCount"].toInt(getLedCount())), getLedCount());
// update led count in device
//_ledDeviceWrapper->setLedCount(_hwLedCount);
// change in leds are also reflected in adjustment
delete _raw2ledAdjustment;
_raw2ledAdjustment = hyperion::createLedColorsAdjustment(_ledString.leds().size(), getSetting(settings::COLOR).object());
@ -264,17 +236,11 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum
_imageProcessor->setLedString(_ledString);
}
/* // reinit led device type on change
if(_device->getActiveDevice() != dev["type"].toString("file").toLower())
{
}
// update led count
_device->setLedCount(_hwLedCount);
*/
// do always reinit until the led devices can handle dynamic changes
dev["currentLedCount"] = int(_hwLedCount); // Inject led count info
_ledDeviceWrapper->createLedDevice(dev);
}
// update once to push single color sets / adjustments/ ledlayout resizes and update ledBuffer color
update();
}
@ -289,12 +255,6 @@ bool Hyperion::saveSettings(QJsonObject config, const bool& correct)
return _settingsManager->saveSettings(config, correct);
}
QString Hyperion::getConfigFileName() const
{
QFileInfo cF(_configFile);
return cF.fileName();
}
int Hyperion::getLatchTime() const
{
return _ledDeviceWrapper->getLatchTime();
@ -310,40 +270,6 @@ unsigned Hyperion::getLedCount() const
return _ledString.leds().size();
}
void Hyperion::checkConfigState(QString cfile)
{
// Check config modifications
QFile f(_configFile);
if (f.open(QFile::ReadOnly))
{
QCryptographicHash hash(QCryptographicHash::Sha1);
if (hash.addData(&f))
{
if (_configHash.size() == 0)
{
_configHash = hash.result();
}
_configMod = _configHash != hash.result() ? true : false;
}
}
f.close();
if(_prevConfigMod != _configMod)
{
_prevConfigMod = _configMod;
}
// Check config writeable
QFile file(_configFile);
QFileInfo fileInfo(file);
_configWrite = fileInfo.isWritable() && fileInfo.isReadable() ? true : false;
if(_prevConfigWrite != _configWrite)
{
_prevConfigWrite = _configWrite;
}
}
void Hyperion::setSourceAutoSelectEnabled(bool enabled)
{
if(_muxer.setSourceAutoSelectEnabled(enabled))
@ -367,6 +293,7 @@ void Hyperion::setNewComponentState(const hyperion::Components& component, const
void Hyperion::setComponentState(const hyperion::Components component, const bool state)
{
// TODO REMOVE THIS STEP
emit componentStateChanged(component, state);
}
@ -547,7 +474,7 @@ void Hyperion::setVideoMode(const VideoMode& mode)
const VideoMode & Hyperion::getCurrentVideoMode()
{
return _daemon->getVideoMode();
return _currVideoMode;
}
const QString & Hyperion::getActiveDevice()
@ -567,9 +494,6 @@ void Hyperion::updatedComponentState(const hyperion::Components comp, const bool
_prevCompId = comp;
_raw2ledAdjustment->setBacklightEnabled((_prevCompId != hyperion::COMP_COLOR && _prevCompId != hyperion::COMP_EFFECT));
}
if(comp == hyperion::COMP_ALL)
_muxer.setEnable(state); // first muxer to update all inputs
}
void Hyperion::update()

View File

@ -0,0 +1,207 @@
#include <hyperion/HyperionIManager.h>
// hyperion
#include <hyperion/Hyperion.h>
#include <db/InstanceTable.h>
// qt
#include <QThread>
HyperionIManager* HyperionIManager::HIMinstance;
HyperionIManager::HyperionIManager(const QString& rootPath, QObject* parent)
: QObject(parent)
, _log(Logger::getInstance("HYPERION"))
, _instanceTable( new InstanceTable(rootPath, this) )
, _rootPath( rootPath )
{
HIMinstance = this;
qRegisterMetaType<instanceState>("instanceState");
}
Hyperion* HyperionIManager::getHyperionInstance(const quint8& instance)
{
if(_runningInstances.contains(instance))
return _runningInstances.value(instance);
Warning(_log,"The requested instance index '%d' with name '%s' isn't running, return main instance", instance, QSTRING_CSTR(_instanceTable->getNamebyIndex(instance)));
return _runningInstances.value(0);
}
const QVector<QVariantMap> HyperionIManager::getInstanceData()
{
QVector<QVariantMap> instances = _instanceTable->getAllInstances();
for( auto & entry : instances)
{
// add running state
entry["running"] = _runningInstances.contains(entry["instance"].toInt());
}
return instances;
}
void HyperionIManager::startAll()
{
for(const auto entry : _instanceTable->getAllInstances(true))
{
startInstance(entry["instance"].toInt());
}
}
void HyperionIManager::stopAll()
{
// copy the instances due to loop corruption, even with .erase() return next iter
QMap<quint8, Hyperion*> instCopy = _runningInstances;
for(const auto instance : instCopy)
{
instance->stop();
}
}
const bool HyperionIManager::startInstance(const quint8& inst, const bool& block)
{
if(_instanceTable->instanceExist(inst))
{
if(!_runningInstances.contains(inst) && !_startQueue.contains(inst))
{
QThread* hyperionThread = new QThread();
Hyperion* hyperion = new Hyperion(inst);
hyperion->moveToThread(hyperionThread);
// setup thread management
connect(hyperionThread, &QThread::started, hyperion, &Hyperion::start);
connect(hyperionThread, &QThread::finished, hyperionThread, &QObject::deleteLater);
connect(hyperion, &Hyperion::started, HyperionIManager::getInstance(), &HyperionIManager::handleStarted);
connect(hyperion, &Hyperion::finished, HyperionIManager::getInstance(), &HyperionIManager::handleFinished);
// setup further connections
// from Hyperion
connect(hyperion, &Hyperion::settingsChanged, HyperionIManager::getInstance(), &HyperionIManager::settingsChanged);
connect(hyperion, &Hyperion::videoMode, HyperionIManager::getInstance(), &HyperionIManager::requestVideoMode);
connect(hyperion, &Hyperion::componentStateChanged, HyperionIManager::getInstance(), &HyperionIManager::componentStateChanged);
// to Hyperion
connect(HyperionIManager::getInstance(), &HyperionIManager::newVideoMode, hyperion, &Hyperion::newVideoMode);
// add to queue and start
_startQueue << inst;
hyperionThread->start();
// update db
_instanceTable->setLastUse(inst);
_instanceTable->setEnable(inst, true);
if(block)
{
while(!hyperionThread->isRunning()){};
}
return true;
}
Debug(_log,"Can't start Hyperion instance index '%d' with name '%s' it's already running or queued for start", inst, QSTRING_CSTR(_instanceTable->getNamebyIndex(inst)));
return false;
}
Debug(_log,"Can't start Hyperion instance index '%d' it doesn't exist in DB", inst);
return false;
}
const bool HyperionIManager::stopInstance(const quint8& inst, const bool& block)
{
// inst 0 can't be stopped
if(!isInstAllowed(inst))
return false;
if(_instanceTable->instanceExist(inst))
{
if(_runningInstances.contains(inst))
{
// notify a ON_STOP rather sooner than later, queued signal listener should have some time to drop the pointer before it's deleted
emit instanceStateChanged(H_ON_STOP, inst);
Hyperion* hyperion = _runningInstances.value(inst);
hyperion->stop();
// update db
_instanceTable->setEnable(inst, false);
if(block)
{
hyperion->thread()->wait(10000);
}
return true;
}
Debug(_log,"Can't stop Hyperion instance index '%d' with name '%s' it's not running'", inst, QSTRING_CSTR(_instanceTable->getNamebyIndex(inst)));
return false;
}
Debug(_log,"Can't stop Hyperion instance index '%d' it doesn't exist in DB", inst);
return false;
}
const bool HyperionIManager::createInstance(const QString& name, const bool& start)
{
quint8 inst;
if(_instanceTable->createInstance(name, inst))
{
Info(_log,"New Hyperion instance created with name '%s'",QSTRING_CSTR(name));
emit instanceStateChanged(H_CREATED, inst, name);
emit change();
if(start)
startInstance(inst);
return true;
}
return false;
}
const bool HyperionIManager::deleteInstance(const quint8& inst)
{
// inst 0 can't be deleted
if(!isInstAllowed(inst))
return false;
// stop it if required as blocking
stopInstance(inst, true);
if(_instanceTable->deleteInstance(inst))
{
Info(_log,"Hyperion instance with index '%d' has been deleted", inst);
emit instanceStateChanged(H_DELETED, inst);
emit change();
return true;
}
return false;
}
const bool HyperionIManager::saveName(const quint8& inst, const QString& name)
{
if(_instanceTable->saveName(inst, name))
{
emit change();
return true;
}
return false;
}
void HyperionIManager::handleFinished()
{
Hyperion* hyperion = qobject_cast<Hyperion*>(sender());
const quint8 & instance = hyperion->getInstanceIndex();
Info(_log,"Hyperion instance '%s' has been stopped", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance)));
_runningInstances.remove(instance);
hyperion->deleteLater();
emit instanceStateChanged(H_STOPPED, instance);
emit change();
}
void HyperionIManager::handleStarted()
{
Hyperion* hyperion = qobject_cast<Hyperion*>(sender());
const quint8 & instance = hyperion->getInstanceIndex();
Info(_log,"Hyperion instance '%s' has been started", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance)));
_startQueue.removeAll(instance);
_runningInstances.insert(instance, hyperion);
emit instanceStateChanged(H_STARTED, instance);
emit change();
}

View File

@ -22,9 +22,9 @@ PriorityMuxer::PriorityMuxer(int ledCount)
, _activeInputs()
, _lowestPriorityInfo()
, _sourceAutoSelectEnabled(true)
, _updateTimer(new QTimer(this))
, _timer(new QTimer(this))
, _blockTimer(new QTimer(this))
, _updateTimer(new QTimer())
, _timer(new QTimer())
, _blockTimer(new QTimer())
{
// init lowest priority info
_lowestPriorityInfo.priority = PriorityMuxer::LOWEST_PRIORITY;

View File

@ -12,13 +12,10 @@
// write config to filesystem
#include <utils/JsonUtils.h>
// hyperion
#include <hyperion/Hyperion.h>
QJsonObject SettingsManager::schemaJson;
SettingsManager::SettingsManager(const quint8& instance, const QString& configFile, Hyperion* hyperion)
: _hyperion(hyperion)
SettingsManager::SettingsManager(const quint8& instance, QObject* parent)
: QObject(parent)
, _log(Logger::getInstance("SettingsManager"))
, _sTable(new SettingsTable(instance, this))
{
@ -41,41 +38,6 @@ SettingsManager::SettingsManager(const quint8& instance, const QString& configFi
if(!JsonUtils::readFile(":/hyperion_default.config", defaultConfig, _log))
throw std::runtime_error("Failed to read default config");
// TODO BEGIN - remove when database migration is done
Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile));
QJsonSchemaChecker schemaCheckerT;
schemaCheckerT.setSchema(schemaJson);
if(!JsonUtils::readFile(configFile, _qconfig, _log))
throw std::runtime_error("Failed to load config!");
// validate config with schema and correct it if required
QPair<bool, bool> validate = schemaCheckerT.validate(_qconfig);
// errors in schema syntax, abort
if (!validate.second)
{
foreach (auto & schemaError, schemaCheckerT.getMessages())
Error(_log, "Schema Syntax Error: %s", QSTRING_CSTR(schemaError));
throw std::runtime_error("ERROR: Hyperion schema has syntax errors!");
}
// errors in configuration, correct it!
if (!validate.first)
{
Warning(_log,"Errors have been found in the configuration file. Automatic correction has been applied");
_qconfig = schemaCheckerT.getAutoCorrectedConfig(_qconfig);
foreach (auto & schemaError, schemaCheckerT.getMessages())
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
if (!JsonUtils::write(configFile, _qconfig, _log))
throw std::runtime_error("ERROR: Can't save configuration file, aborting");
}
// TODO END - remove when database migration is done
// transform json to string lists
QStringList keyList = defaultConfig.keys();
QStringList defValueList;
@ -163,13 +125,6 @@ bool SettingsManager::saveSettings(QJsonObject config, const bool& correct)
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
}
// save data to file
if(_hyperion != nullptr)
{
if(!JsonUtils::write(_hyperion->getConfigFilePath(), config, _log))
return false;
}
// store the new config
_qconfig = config;

View File

@ -10,8 +10,6 @@
#include "hyperion/Hyperion.h"
#include <utils/JsonUtils.h>
#include <QDebug>
LedDevice::LedDevice(const QJsonObject& config, QObject* parent)
: QObject(parent)
, _devConfig(config)

View File

@ -10,7 +10,10 @@
// Hyperion includes
#include <hyperion/Hyperion.h>
// Hyperion instance manager includes
#include <hyperion/HyperionIManager.h>
// TODO Remove this class if third-party apps have been migrated (eg. Hyperion Android Gabber, Windows Screen grabber etc.)
ProtoClientConnection::ProtoClientConnection(QTcpSocket* socket, const int &timeout, QObject *parent)
: QObject(parent)
@ -20,7 +23,7 @@ ProtoClientConnection::ProtoClientConnection(QTcpSocket* socket, const int &time
, _timeoutTimer(new QTimer(this))
, _timeout(timeout * 1000)
, _priority()
, _hyperion(Hyperion::getInstance())
, _hyperion(HyperionIManager::getInstance()->getHyperionInstance())
{
// timer setup
_timeoutTimer->setSingleShot(true);

View File

@ -4,6 +4,7 @@
#include "SSDPDescription.h"
#include <hyperion/Hyperion.h>
#include <HyperionConfig.h>
#include <hyperion/AuthManager.h>
#include <QNetworkInterface>
#include <QNetworkConfigurationManager>
@ -140,5 +141,5 @@ const QString SSDPHandler::buildDesc()
/// %2 friendly name Hyperion 2.0.0 (192.168.0.177)
/// %3 modelNumber 2.0.0
/// %4 serialNumber / UDN (H ID) Fjsa723dD0....
return SSDP_DESCRIPTION.arg(getBaseAddress(), QString("Hyperion (%2)").arg(_localAddress), QString(HYPERION_VERSION), Hyperion::getInstance()->getId());
return SSDP_DESCRIPTION.arg(getBaseAddress(), QString("Hyperion (%2)").arg(_localAddress), QString(HYPERION_VERSION), AuthManager::getInstance()->getID());
}

View File

@ -1,10 +1,14 @@
#include <ssdp/SSDPServer.h>
// util
// utils
#include <utils/SysInfo.h>
#include <hyperion/Hyperion.h>
// Hyperion
#include <HyperionConfig.h>
// auth manager
#include <hyperion/AuthManager.h>
#include <QUdpSocket>
#include <QDateTime>
@ -96,7 +100,7 @@ void SSDPServer::initServer()
_serverHeader = data.prettyName+"/"+data.productVersion+" UPnP/1.0 Hyperion/"+QString(HYPERION_VERSION);
// usn uuid
_uuid = Hyperion::getInstance()->getId();
_uuid = AuthManager::getInstance()->getID();
connect(_udpSocket, &QUdpSocket::readyRead, this, &SSDPServer::readPendingDatagrams);
}

View File

@ -6,6 +6,7 @@
// hyperion includes
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
#include "HyperionConfig.h"
// utils includes
@ -27,9 +28,6 @@ UDPListener::UDPListener(const QJsonDocument& config)
, _listenPort(0)
, _netOrigin(NetOrigin::getInstance())
{
// listen for component change
connect(Hyperion::getInstance(), &Hyperion::componentStateChanged, this, &UDPListener::componentStateChanged);
// init
handleSettingsUpdate(settings::UDPLISTENER, config);
}
@ -41,14 +39,14 @@ UDPListener::~UDPListener()
delete _server;
}
void UDPListener::start()
{
if ( active() )
return;
QHostAddress mcastGroup;
if (_listenAddress.isInSubnet(QHostAddress::parseSubnet("224.0.0.0/4"))) {
if (_listenAddress.isInSubnet(QHostAddress::parseSubnet("224.0.0.0/4")))
{
mcastGroup = _listenAddress;
}
@ -59,7 +57,8 @@ void UDPListener::start()
else
{
Info(_log, "Started, listening on %s:%d", _listenAddress.toString().toStdString().c_str(), _listenPort);
if (!mcastGroup.isNull()) {
if (!mcastGroup.isNull())
{
bool joinGroupOK = _server->joinMulticastGroup(_listenAddress);
InfoIf ( joinGroupOK, _log, "Multicast enabled");
WarningIf( ! joinGroupOK, _log, "Multicast failed");
@ -78,8 +77,6 @@ void UDPListener::start()
_serviceRegister->registerService("_hyperiond-udp._udp", _listenPort);
}
}
Hyperion::getInstance()->getComponentRegister().componentStateChanged(COMP_UDPLISTENER, _isActive);
}
void UDPListener::stop()
@ -90,10 +87,9 @@ void UDPListener::stop()
_server->close();
_isActive = false;
Info(_log, "Stopped");
Hyperion::getInstance()->getComponentRegister().componentStateChanged(COMP_UDPLISTENER, _isActive);
}
void UDPListener::componentStateChanged(const hyperion::Components component, bool enable)
void UDPListener::updatedComponentState(const hyperion::Components component, bool enable)
{
if (component == COMP_UDPLISTENER)
{
@ -110,10 +106,10 @@ uint16_t UDPListener::getPort() const
return _server->localPort();
}
void UDPListener::readPendingDatagrams()
{
while (_server->hasPendingDatagrams()) {
while (_server->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(_server->pendingDatagramSize());
QHostAddress sender;
@ -126,7 +122,6 @@ void UDPListener::readPendingDatagrams()
}
}
void UDPListener::processTheDatagram(const QByteArray * datagram, const QHostAddress * sender)
{
int packetLedCount = datagram->size()/3;
@ -134,7 +129,8 @@ void UDPListener::processTheDatagram(const QByteArray * datagram, const QHostAdd
std::vector<ColorRgb> _ledColors(packetLedCount, ColorRgb::BLACK);
for (int ledIndex=0; ledIndex < packetLedCount; ledIndex++) {
for (int ledIndex=0; ledIndex < packetLedCount; ledIndex++)
{
ColorRgb & rgb = _ledColors[ledIndex];
rgb.red = datagram->at(ledIndex*3+0);
rgb.green = datagram->at(ledIndex*3+1);

View File

@ -95,12 +95,6 @@ namespace FileUtils {
return true;
}
QString convertPath(const QString path)
{
QString p = path;
return p.replace(QString("$ROOT"), Hyperion::getInstance()->getRootPath());
}
void resolveFileError(const QFile& file, Logger* log)
{
QFile::FileError error = file.error();

View File

@ -18,7 +18,6 @@
#include <utils/Components.h>
#include <utils/JsonUtils.h>
#include <hyperion/Hyperion.h>
#include <jsonserver/JsonServer.h>
#include <udplistener/UDPListener.h>
#include <webserver/WebServer.h>
@ -43,6 +42,9 @@
// AuthManager
#include <hyperion/AuthManager.h>
// InstanceManager Hyperion
#include <hyperion/HyperionIManager.h>
// NetOrigin checks
#include <utils/NetOrigin.h>
@ -54,10 +56,11 @@
HyperionDaemon* HyperionDaemon::daemon = nullptr;
HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObject *parent, const bool& logLvlOverwrite)
HyperionDaemon::HyperionDaemon(const QString rootPath, QObject *parent, const bool& logLvlOverwrite)
: QObject(parent)
, _log(Logger::getInstance("DAEMON"))
, _authManager(new AuthManager(rootPath, this))
, _instanceManager(new HyperionIManager(rootPath, this))
, _authManager(new AuthManager(this))
, _bonjourBrowserWrapper(new BonjourBrowserWrapper())
, _netOrigin(new NetOrigin(this))
, _pyInit(new PythonInit())
@ -71,7 +74,6 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje
, _fbGrabber(nullptr)
, _osxGrabber(nullptr)
, _qtGrabber(nullptr)
, _hyperion(nullptr)
, _ssdp(nullptr)
, _currVideoMode(VIDEO_2D)
{
@ -86,7 +88,7 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje
qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");
// init settings
_settingsManager = new SettingsManager(0,configFile);
_settingsManager = new SettingsManager(0,this);
// set inital log lvl if the loglvl wasn't overwritten by arg
if(!logLvlOverwrite)
@ -104,38 +106,33 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje
connect(this, &HyperionDaemon::settingsChanged, _netOrigin, &NetOrigin::handleSettingsUpdate);
_netOrigin->handleSettingsUpdate(settings::NETWORK, _settingsManager->getSetting(settings::NETWORK));
// spawn all Hyperion instances before network services
_hyperion = Hyperion::initInstance(this, 0, configFile, rootPath);
Info(_log, "Hyperion initialized");
// spawn all Hyperion instances (non blocking)
_instanceManager->startAll();
//connect(_hyperion,SIGNAL(closing()),this,SLOT(freeObjects())); // TODO for app restart, refactor required
// listen for setting changes
connect(_hyperion, &Hyperion::settingsChanged, this, &HyperionDaemon::settingsChanged);
// pipe settings changes and component state changes from HyperionIManager to Daemon
connect(_instanceManager, &HyperionIManager::settingsChanged, this, &HyperionDaemon::settingsChanged);
connect(_instanceManager, &HyperionIManager::componentStateChanged, this, &HyperionDaemon::componentStateChanged);
// listen for setting changes of framegrabber and v4l2
connect(this, &HyperionDaemon::settingsChanged, this, &HyperionDaemon::handleSettingsUpdate);
// forward videoModes from Hyperion to Daemon evaluation
connect(_hyperion, &Hyperion::videoMode, this, &HyperionDaemon::setVideoMode);
// forward videoMode changes from Daemon to Hyperion
connect(this, &HyperionDaemon::videoMode, _hyperion, &Hyperion::newVideoMode);
// forward videoModes from HyperionIManager to Daemon evaluation
connect(_instanceManager, &HyperionIManager::requestVideoMode, this, &HyperionDaemon::setVideoMode);
// return videoMode changes from Daemon to HyperionIManager
connect(this, &HyperionDaemon::videoMode, _instanceManager, &HyperionIManager::newVideoMode);
// ---- grabber -----
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_AMLOGIC)
Warning(_log, "No platform capture can be instantiated, because all grabbers have been left out from the build");
#endif
// get power state of system/v4l2 capture
const QJsonObject & grabberConfig = getSetting(settings::INSTCAPTURE).object();
// init system capture (framegrabber) and update power state
// init system capture (framegrabber)
handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
_hyperion->setComponentState(hyperion::COMP_GRABBER, grabberConfig["systemEnable"].toBool(true));
// init v4l2 capture and update power state
// init v4l2 capture
handleSettingsUpdate(settings::V4L2, getSetting(settings::V4L2));
_hyperion->setComponentState(hyperion::COMP_V4L, grabberConfig["v4lEnable"].toBool(true));
// ---- network services -----
startNetworkServices();
@ -144,7 +141,6 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje
HyperionDaemon::~HyperionDaemon()
{
freeObjects();
delete _hyperion;
delete _settingsManager;
delete _pyInit;
}
@ -165,7 +161,6 @@ const QJsonDocument HyperionDaemon::getSetting(const settings::type &type)
void HyperionDaemon::freeObjects()
{
_hyperion->clearall(true);
// destroy network first as a client might want to access hyperion
delete _jsonServer;
_flatBufferServer->thread()->quit();
@ -178,6 +173,10 @@ void HyperionDaemon::freeObjects()
_webserver->thread()->quit();
_webserver->thread()->wait(1000);
delete _udpListener;
// stop Hyperions (non blocking)
_instanceManager->stopAll();
delete _bonjourBrowserWrapper;
delete _amlGrabber;
delete _dispmanx;
@ -224,12 +223,13 @@ void HyperionDaemon::startNetworkServices()
connect( pThread, &QThread::started, _protoServer, &ProtoServer::initServer );
connect( pThread, &QThread::finished, _protoServer, &QObject::deleteLater );
connect( pThread, &QThread::finished, pThread, &QObject::deleteLater );
connect(this, &HyperionDaemon::settingsChanged, _protoServer, &ProtoServer::handleSettingsUpdate);
connect( this, &HyperionDaemon::settingsChanged, _protoServer, &ProtoServer::handleSettingsUpdate );
pThread->start();
// Create UDP listener
_udpListener = new UDPListener(getSetting(settings::UDPLISTENER));
connect(this, &HyperionDaemon::settingsChanged, _udpListener, &UDPListener::handleSettingsUpdate);
connect(this, &HyperionDaemon::componentStateChanged, _udpListener, &UDPListener::updatedComponentState);
// Create Webserver in thread
_webserver = new WebServer(getSetting(settings::WEBSERVER));
@ -241,7 +241,7 @@ void HyperionDaemon::startNetworkServices()
connect(this, &HyperionDaemon::settingsChanged, _webserver, &WebServer::handleSettingsUpdate);
wsThread->start();
// create ssdp server in thread
// Create SSDP server in thread
_ssdp = new SSDPHandler(_webserver, getSetting(settings::FLATBUFSERVER).object()["port"].toInt());
QThread* ssdpThread = new QThread(this);
_ssdp->moveToThread(ssdpThread);
@ -431,6 +431,7 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type& settingsType, co
// connect to HyperionDaemon signal
connect(this, &HyperionDaemon::videoMode, _v4l2Grabber, &V4L2Wrapper::setVideoMode);
connect(this, &HyperionDaemon::settingsChanged, _v4l2Grabber, &V4L2Wrapper::handleSettingsUpdate);
connect(this, &HyperionDaemon::componentStateChanged, _v4l2Grabber, &V4L2Wrapper::componentStateChanged);
#else
Error(_log, "The v4l2 grabber can not be instantiated, because it has been left out from the build");
#endif

View File

@ -46,13 +46,13 @@
#endif
#include <utils/Logger.h>
#include <utils/Image.h>
#include <utils/VideoMode.h>
// settings management
#include <utils/settings.h>
#include <utils/Components.h>
class Hyperion;
class HyperionIManager;
class SysTray;
class JsonServer;
class UDPListener;
@ -73,7 +73,7 @@ class HyperionDaemon : public QObject
friend SysTray;
public:
HyperionDaemon(QString configFile, QString rootPath, QObject *parent, const bool& logLvlOverwrite );
HyperionDaemon(QString rootPath, QObject *parent, const bool& logLvlOverwrite );
~HyperionDaemon();
///
@ -100,15 +100,28 @@ public slots:
void freeObjects();
signals:
///////////////////////////////////////
/// FROM HYPERIONDAEMON TO HYPERION ///
///////////////////////////////////////
///
/// @brief After eval of setVideoMode this signal emits with a new one on change
///
void videoMode(const VideoMode& mode);
///////////////////////////////////////
/// FROM HYPERION TO HYPERIONDAEMON ///
///////////////////////////////////////
///
/// @brief PIPE settings events from Hyperion class to HyperionDaemon components
///
void settingsChanged(const settings::type& type, const QJsonDocument& data);
///
/// @brief After eval of setVideoMode this signal emits with a new one on change
/// @brief PIPE component state changes events from Hyperion class to HyperionDaemon components
///
void videoMode(const VideoMode& mode);
void componentStateChanged(const hyperion::Components component, bool enable);
private slots:
///
@ -133,6 +146,7 @@ private:
void createGrabberQt(const QJsonObject & grabberConfig);
Logger* _log;
HyperionIManager* _instanceManager;
AuthManager* _authManager;
BonjourBrowserWrapper* _bonjourBrowserWrapper;
NetOrigin* _netOrigin;
@ -147,7 +161,6 @@ private:
FramebufferWrapper* _fbGrabber;
OsxWrapper* _osxGrabber;
QtWrapper* _qtGrabber;
Hyperion* _hyperion;
SSDPHandler* _ssdp;
FlatBufferServer* _flatBufferServer;
ProtoServer* _protoServer;

View File

@ -20,6 +20,7 @@
#include <QDir>
#include <QStringList>
#include <QSystemTrayIcon>
#include <QProcess>
#include "HyperionConfig.h"
@ -39,10 +40,84 @@ using namespace commandline;
#define PERM0664 QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther | QFileDevice::WriteOwner | QFileDevice::WriteGroup
unsigned int getProcessIdsByProcessName(const char* processName, QStringList &listOfPids)
{
// Clear content of returned list of PIDS
listOfPids.clear();
#if defined(WIN32)
// Get the list of process identifiers.
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
return 0;
// Calculate how many process identifiers were returned.
cProcesses = cbNeeded / sizeof(DWORD);
// Search for a matching name for each process
for (i = 0; i < cProcesses; i++)
{
if (aProcesses[i] != 0)
{
char szProcessName[MAX_PATH] = {0};
DWORD processID = aProcesses[i];
// Get a handle to the process.
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
// Get the process name
if (NULL != hProcess)
{
HMODULE hMod;
DWORD cbNeeded;
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
GetModuleBaseNameA(hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(char));
// Release the handle to the process.
CloseHandle(hProcess);
if (*szProcessName != 0 && strcmp(processName, szProcessName) == 0)
listOfPids.append(QString::number(processID));
}
}
}
return listOfPids.count();
#else
// Run pgrep, which looks through the currently running processses and lists the process IDs
// which match the selection criteria to stdout.
QProcess process;
process.start("pgrep", QStringList() << processName);
process.waitForReadyRead();
QByteArray bytes = process.readAllStandardOutput();
process.terminate();
process.waitForFinished();
process.kill();
// Output is something like "2472\n2323" for multiple instances
if (bytes.isEmpty())
return 0;
listOfPids = QString(bytes).split("\n", QString::SkipEmptyParts);
return listOfPids.count();
#endif
}
void signal_handler(const int signum)
{
/// Hyperion instance
Hyperion* _hyperion = Hyperion::getInstance();
// SIGUSR1 and SIGUSR2 must be rewritten
// Hyperion Managment instance
HyperionIManager* _hyperion = HyperionIManager::getInstance();
if(signum == SIGCHLD)
{
@ -54,8 +129,8 @@ void signal_handler(const int signum)
{
if (_hyperion != nullptr)
{
_hyperion->setComponentState(hyperion::COMP_SMOOTHING, false);
_hyperion->setComponentState(hyperion::COMP_LEDDEVICE, false);
// _hyperion->setComponentState(hyperion::COMP_SMOOTHING, false);
// _hyperion->setComponentState(hyperion::COMP_LEDDEVICE, false);
}
return;
}
@ -63,29 +138,18 @@ void signal_handler(const int signum)
{
if (_hyperion != nullptr)
{
_hyperion->setComponentState(hyperion::COMP_LEDDEVICE, true);
_hyperion->setComponentState(hyperion::COMP_SMOOTHING, true);
// _hyperion->setComponentState(hyperion::COMP_LEDDEVICE, true);
// _hyperion->setComponentState(hyperion::COMP_SMOOTHING, true);
}
return;
}
QCoreApplication::quit();
// reset signal handler to default (in case this handler is not capable of stopping)
signal(signum, SIG_DFL);
}
void startNewHyperion(int parentPid, std::string hyperionFile, std::string configFile)
{
pid_t childPid = fork(); // child pid should store elsewhere for later use
if ( childPid == 0 )
{
sleep(3);
execl(hyperionFile.c_str(), hyperionFile.c_str(), "--parent", QString::number(parentPid).toStdString().c_str(), configFile.c_str(), NULL);
exit(0);
}
}
QCoreApplication* createApplication(int &argc, char *argv[])
{
bool isGuiApp = false;
@ -127,7 +191,7 @@ QCoreApplication* createApplication(int &argc, char *argv[])
QApplication* app = new QApplication(argc, argv);
app->setApplicationDisplayName("Hyperion");
app->setWindowIcon(QIcon(":/hyperion-icon-32px.png"));
return app;
return app;
}
QCoreApplication* app = new QCoreApplication(argc, argv);
@ -144,8 +208,20 @@ int main(int argc, char** argv)
Logger* log = Logger::getInstance("MAIN");
Logger::setLogLevel(Logger::WARNING);
// check if we are running already an instance
// TODO Do not use pgrep on linux, instead iter /proc
// TODO Allow one session per user
// http://www.qtcentre.org/threads/44489-Get-Process-ID-for-a-running-application
QStringList listOfPids;
if(getProcessIdsByProcessName("hyperiond", listOfPids) > 1)
{
Error(log, "The Hyperion Daemon is already running, abort start");
return 0;
}
// Initialising QCoreApplication
QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
bool isGuiApp = (qobject_cast<QApplication *>(app.data()) != 0 && QSystemTrayIcon::isSystemTrayAvailable());
signal(SIGINT, signal_handler);
@ -165,20 +241,14 @@ int main(int argc, char** argv)
BooleanOption & versionOption = parser.add<BooleanOption>(0x0, "version", "Show version information");
Option & rootPathOption = parser.add<Option> (0x0, "rootPath", "Overwrite root path for all hyperion user files, defaults to home directory of current user");
IntOption & parentOption = parser.add<IntOption> ('p', "parent", "pid of parent hyperiond"); // 2^22 is the max for Linux
BooleanOption & silentOption = parser.add<BooleanOption>('s', "silent", "do not print any outputs");
BooleanOption & verboseOption = parser.add<BooleanOption>('v', "verbose", "Increase verbosity");
BooleanOption & debugOption = parser.add<BooleanOption>('d', "debug", "Show debug messages");
parser.add<BooleanOption>(0x0, "desktop", "show systray on desktop");
parser.add<BooleanOption>(0x0, "service", "force hyperion to start as console service");
Option & exportConfigOption = parser.add<Option> (0x0, "export-config", "export default config to file");
Option & exportEfxOption = parser.add<Option> (0x0, "export-effects", "export effects to given path");
parser.addPositionalArgument("config-files", QCoreApplication::translate("main", "Configuration file"), "config.file");
parser.process(*qApp);
QStringList configFiles = parser.positionalArguments();
parser.process(*qApp);
int logLevelCheck = 0;
if (parser.isSet(silentOption))
@ -207,10 +277,10 @@ int main(int argc, char** argv)
if (parser.isSet(versionOption))
{
std::cout
<< "Hyperion Ambilight Deamon (" << getpid() << ")" << std::endl
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl
<< "\tBuild Time: " << __DATE__ << " " << __TIME__ << std::endl;
std::cout
<< "Hyperion Ambilight Deamon (" << getpid() << ")" << std::endl
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl
<< "\tBuild Time: " << __DATE__ << " " << __TIME__ << std::endl;
return 0;
}
@ -229,9 +299,7 @@ int main(int argc, char** argv)
{
destFileName = destDir.dirName()+"/"+filename;
if (QFile::exists(destFileName))
{
QFile::remove(destFileName);
}
std::cout << "Extract: " << filename.toStdString() << " ... ";
if (QFile::copy(QString(":/effects/")+filename, destFileName))
@ -267,82 +335,16 @@ int main(int argc, char** argv)
}
}
// create /.hyperion folder for default path, check if the directory is read/writeable
// Note: No further checks inside Hyperion. FileUtils::writeFile() will resolve permission errors and others that occur during runtime
// NOTE: No further checks inside Hyperion. FileUtils::writeFile() will resolve permission errors and others that occur during runtime
QDir mDir(rootPath);
QFileInfo mFi(rootPath);
if(!mDir.mkpath(rootPath) || !mFi.isWritable() || !mDir.isReadable())
{
throw std::runtime_error("The specified root path can't be created or isn't read/writeable. Please setup the permissions correctly!");
}
// determine name of config file, defaults to hyperion_main.json
// create config folder
QString cPath(rootPath+"/config");
QDir().mkpath(rootPath+"/config");
if (configFiles.size() > 0)
{
// use argument config file
// check if file has a path and ends with .json
if(configFiles[0].contains("/"))
throw std::runtime_error("Don't provide a path to config file, just a config name is allowed!");
if(!configFiles[0].endsWith(".json"))
configFiles[0].append(".json");
configFiles.prepend(cPath+"/"+configFiles[0]);
}
else
{
// use default config file
configFiles.append(cPath+"/hyperion_main.json");
}
bool exportDefaultConfig = false;
bool exitAfterExportDefaultConfig = false;
QString exportConfigFileTarget;
if (parser.isSet(exportConfigOption))
{
exportDefaultConfig = true;
exitAfterExportDefaultConfig = true;
exportConfigFileTarget = exportConfigOption.value(parser);
}
else if ( ! QFile::exists(configFiles[0]) )
{
exportDefaultConfig = true;
exportConfigFileTarget = configFiles[0];
Warning(log, "Create new config file (%s)",QSTRING_CSTR(configFiles[0]));
}
if (exportDefaultConfig)
{
Q_INIT_RESOURCE(resource);
QDir().mkpath(FileUtils::getDirName(exportConfigFileTarget));
if (QFile::copy(":/hyperion_default.config",exportConfigFileTarget))
{
QFile::setPermissions(exportConfigFileTarget, PERM0664 );
Info(log, "export complete.");
if (exitAfterExportDefaultConfig) return 0;
}
else
{
Error(log, "error while export to %s", QSTRING_CSTR(exportConfigFileTarget) );
return 1;
}
}
int parentPid = parser.value(parentOption).toInt();
if (parentPid > 0 )
{
Info(log, "hyperiond client, parent is pid %d", parentPid);
#ifndef __APPLE__
prctl(PR_SET_PDEATHSIG, SIGHUP);
#endif
}
HyperionDaemon* hyperiond = nullptr;
try
{
hyperiond = new HyperionDaemon(configFiles[0], rootPath, qApp, bool(logLevelCheck));
hyperiond = new HyperionDaemon(rootPath, qApp, bool(logLevelCheck));
}
catch (std::exception& e)
{

View File

@ -20,6 +20,7 @@ SysTray::SysTray(HyperionDaemon *hyperiond)
, _colorDlg(this)
, _hyperiond(hyperiond)
, _hyperion(nullptr)
, _instanceManager(HyperionIManager::getInstance())
, _webPort(8090)
{
Q_INIT_RESOURCE(resources);
@ -28,16 +29,8 @@ SysTray::SysTray(HyperionDaemon *hyperiond)
WebServer* webserver = hyperiond->getWebServerInstance();
connect(webserver, &WebServer::portChanged, this, &SysTray::webserverPortChanged);
_hyperion = Hyperion::getInstance();
createTrayIcon();
connect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
connect(&_colorDlg, SIGNAL(currentColorChanged(const QColor&)), this, SLOT(setColor(const QColor &)));
QIcon icon(":/hyperion-icon-32px.png");
_trayIcon->setIcon(icon);
_trayIcon->show();
setWindowIcon(icon);
_colorDlg.setOptions(QColorDialog::NoButtons);
// instance changes
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &SysTray::handleInstanceStateChange);
}
SysTray::~SysTray()
@ -148,3 +141,29 @@ void SysTray::clearEfxColor()
{
_hyperion->clear(1);
}
void SysTray::handleInstanceStateChange(const instanceState& state, const quint8& instance, const QString& name)
{
switch(state){
case H_STARTED:
if(instance == 0)
{
_hyperion = _instanceManager->getHyperionInstance(0);
createTrayIcon();
connect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
connect(&_colorDlg, SIGNAL(currentColorChanged(const QColor&)), this, SLOT(setColor(const QColor &)));
QIcon icon(":/hyperion-icon-32px.png");
_trayIcon->setIcon(icon);
_trayIcon->show();
setWindowIcon(icon);
_colorDlg.setOptions(QColorDialog::NoButtons);
}
break;
default:
break;
}
}

View File

@ -7,6 +7,7 @@
#include <QCloseEvent>
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
class HyperionDaemon;
@ -35,21 +36,27 @@ private slots:
///
void webserverPortChanged(const quint16& port) { _webPort = port; };
///
/// @brief is called whenever a hyperion isntance state changes
///
void handleInstanceStateChange(const instanceState& state, const quint8& instance, const QString& name);
private:
void createTrayIcon();
QAction *quitAction;
QAction *startAction;
QAction *stopAction;
QAction *colorAction;
QAction *settingsAction;
QAction *clearAction;
QAction *quitAction;
QAction *startAction;
QAction *stopAction;
QAction *colorAction;
QAction *settingsAction;
QAction *clearAction;
QSystemTrayIcon *_trayIcon;
QMenu *_trayIconMenu;
QMenu *_trayIconEfxMenu;
QColorDialog _colorDlg;
HyperionDaemon *_hyperiond;
Hyperion *_hyperion;
quint16 _webPort;
QSystemTrayIcon *_trayIcon;
QMenu *_trayIconMenu;
QMenu *_trayIconEfxMenu;
QColorDialog _colorDlg;
HyperionDaemon *_hyperiond;
Hyperion *_hyperion;
HyperionIManager *_instanceManager;
quint16 _webPort;
};