diff --git a/assets/webconfig/i18n/cs.json b/assets/webconfig/i18n/cs.json
index 96c269e3..f5e77635 100644
--- a/assets/webconfig/i18n/cs.json
+++ b/assets/webconfig/i18n/cs.json
@@ -316,13 +316,13 @@
"wiz_hue_failure_connection": "Časový limit: Stiskněte tlačítko Bridge v průběhu 30 sekund",
"wiz_hue_failure_user": "Uživatel nebyl nalezen, vytvořte nový pomocí tlačítka níže nebo zadejte platné ID uživatele a stiskněte symbol „znovu načíst“.",
"wiz_hue_press_link": "Prosím stiskněte tlačítko odkazu na Hue Bridge.",
- "wiz_hue_ids_disabled": "Deaktivováno",
- "wiz_hue_ids_entire": "Celý obrázek",
"wiz_hue_noids": "Tento Hue Bridge nemá žárovky/lampy, předtím je spárujte s aplikací Hue Apps",
- "wiz_hue_pos": "Poloha",
"wiz_hue_searchb": "Hledání v Bridge ...",
"wiz_hue_blinkblue": "Nechte ID $1 svítit modře",
- "wiz_hue_ident": "Identifikovat",
+ "wiz_hue_disabled": "Deaktivováno",
+ "wiz_hue_entire": "Celý obrázek",
+ "wiz_pos": "Poloha",
+ "wiz_identify": "Identifikovat",
"wiz_cc_title": "Průvodce kalibrací barev",
"wiz_cc_intro1": "Tento průvodce vás provede kalibrací LED diod. Používáte-li Kodi, mohou být kalibrační obrázky a videa odesílány přímo na kodi bez dalších úkolů na vaší straně. Pokud tomu tak není, musíte tyto soubory sami stáhnout a použít je, pokud si to průvodce přeje.",
"wiz_cc_kwebs": "Kodi webový server (IP:Port)",
diff --git a/assets/webconfig/i18n/de.json b/assets/webconfig/i18n/de.json
index ee803ee2..f8f832d1 100644
--- a/assets/webconfig/i18n/de.json
+++ b/assets/webconfig/i18n/de.json
@@ -355,9 +355,9 @@
"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_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_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...",
@@ -365,9 +365,9 @@
"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_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_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_clientkey": "Clientkey:",
@@ -376,13 +376,9 @@
"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, indem du auf den nachfolgenden Button klickst, oder gib eine bereits registrierte an und klicke dann auf das \"neu laden\" Symbol.",
"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_hue_e_create_user": "Neuen Benutzer und Clientkey erstellen",
"wiz_hue_e_clientkey_needed": "Für die Verwendung der Entertainment API, ist ein zum Usernamen passender Clientkey erforderlich. Bitte einen vorhandenen eingeben oder über die Schaltflächen unten einen neuen erstellen.",
"wiz_hue_e_use_groupid": "Gruppen ID $1 verwenden",
@@ -396,6 +392,16 @@
"wiz_hue_e_desc2": "Nun kannst du die Entertainment Gruppe auswählen, welche die Lampen zur Verwendung mit Hyperion beinhaltet.",
"wiz_hue_e_desc3": "Nun kannst du auswählen, mit welcher Position die jeweilige Lampe \"im Bild\" sitzen soll. Eine Vorauswahl der Position, wurde Anhand der konfigurierten Positionen der Lampen in der Entertainment Gruppe gewählt. Dies ist nur eine Empfehlung und kann beliebig angepasst werden. Als Hilfe zur Identifizierung kannst du sie mit einem Klick auf den rechten Button kurz aufleuchten lassen und die Auswahl zu verbessern.",
"wiz_hue_e_use_group": "Gruppe verwenden",
+ "wiz_yeelight_title": "Yeelight Assistent",
+ "wiz_yeelight_intro1": "Dieser Assistent hilft Dir bei der Konfiguration von Hyperion für Yeelight. Zu den Funktionen zählen ein automatisches finden der Yeelights, die einzelnen Lampen unterschiedlichen Bereichen im Bild zuzuordnen und weitere Einstellungen von Hyperion automatisch anzupassen. Kurz gesagt: Komplette Einrichtung mit ein paar Klicks.",
+ "wiz_yeelight_desc2": "Nun kannst Du auswählen, welche der Lampen 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_yeelight_noLights": "Es wurden keine Yeelights gefunden! Bitte verbinde die Yeelights mit dem Netzwerk oder konfiguriere sie manuell.",
+ "wiz_yeelight_unsupported" : "Nicht Unterstützt",
+ "wiz_ids_disabled": "Deaktiviert",
+ "wiz_ids_entire": "Ganzes Bild",
+ "wiz_pos": "Position/Status",
+ "wiz_identify" : "Identifiziere",
+ "wiz_identify_light": "Identifiziere $1",
"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)",
@@ -438,7 +444,8 @@
"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_targetIpHost_title": "Ziel IP/Rechnername",
+ "edt_dev_spec_networkDeviceName_title" : "Gerätename im Netzwerk",
"edt_dev_spec_outputPath_title": "Ausgabepfad",
"edt_dev_spec_delayAfterConnect_title": "Verzögerung nach Verbindung",
"edt_dev_spec_FCsetConfig_title": "Wende fadecandy Konfiguration an",
@@ -451,20 +458,33 @@
"edt_dev_spec_username_title": "Benutzername",
"edt_dev_spec_lightid_title": "Lampen ID(s)",
"edt_dev_spec_lightid_itemtitle": "ID",
+ "edt_dev_spec_lights_title" : "Lampe(n)",
+ "edt_dev_spec_lights_itemtitle" : "Lampe",
+ "edt_dev_spec_lights_name" : "Name",
"edt_dev_spec_transistionTime_title": "Übergangszeit",
"edt_dev_spec_blackLightsTimeout_title": "Signal Erkennung Timeout bei schwarz",
+ "edt_dev_spec_transistionTimeExtra_title" : "Extra Übergangszeit bei Schwarz",
+ "edt_dev_spec_transeffect_title" : "Übergangseffekt",
+ "edt_conf_enum_transeffect_smooth" : "Gleichmäßig",
+ "edt_conf_enum_transeffect_sudden" : "Sofort",
+ "edt_dev_spec_debugLevel_title" : "Debug Stufe",
+ "edt_conf_enum_dl_nodebug" : "Keine Debugausgabe",
+ "edt_conf_enum_dl_verbose1" : "Stufe 1",
+ "edt_conf_enum_dl_verbose2" : "Stufe 2",
+ "edt_conf_enum_dl_verbose3" : "Stufe 3",
"edt_dev_spec_brightnessThreshold_title": "Signal Erkennung Helligkeitsminimum",
"edt_dev_spec_switchOffOnBlack_title": "Aus bei schwarz",
+ "edt_dev_spec_switchOffOnbelowMinBrightness_title" : "Aus bei Minimum",
"edt_dev_spec_brightnessFactor_title": "Helligkeitsfaktor",
- "edt_dev_spec_brightnessMin_title": "Helligkeit minimum",
- "edt_dev_spec_brightnessMax_title": "Helligkeit maximum",
+ "edt_dev_spec_brightnessMin_title": "Helligkeitsminimum",
+ "edt_dev_spec_brightnessMax_title": "Helligkeitsmaximum",
"edt_dev_spec_sslReadTimeout_title" : "Streamer lese Timeout",
"edt_dev_spec_sslHSTimeoutMin_title" : "Streamer Handshake minimum Timeout",
"edt_dev_spec_sslHSTimeoutMax_title" : "Streamer Handshake maximum Timeout",
"edt_dev_spec_verbose_title": "Logge alle Hue Commandos",
"edt_dev_spec_debugStreamer_title": "Streamer Debugging",
"edt_dev_spec_debugLevel_title": "Streamer Verbindung Debug Stufe",
- "edt_dev_spec_restoreOriginalState_title" : "Lampen Originalzustand wiederherstellen",
+ "edt_dev_spec_restoreOriginalState_title" : "Bei Deaktivierung, Lampen Originalzustand wiederherstellen",
"edt_dev_spec_useEntertainmentAPI_title": "Hue Entertainment API verwenden",
"edt_dev_spec_ledType_title": "LED typ",
"edt_dev_spec_uid_title": "UID",
@@ -511,6 +531,7 @@
"edt_conf_enum_brg": "BRG",
"edt_conf_enum_gbr": "GBR",
"edt_conf_enum_grb": "GRB",
+ "edt_conf_enum_hsv" : "HSV",
"edt_conf_enum_linear": "Linear",
"edt_conf_enum_PAL": "PAL",
"edt_conf_enum_NTSC": "NTSC",
diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json
index 018279d1..e34c61da 100644
--- a/assets/webconfig/i18n/en.json
+++ b/assets/webconfig/i18n/en.json
@@ -376,13 +376,9 @@
"wiz_hue_failure_connection" : "Timeout: Please press the bridge button within the period of 30 seconds",
"wiz_hue_failure_user" : "User not found, create a new one with the button below or input a valid user id and press the \"reload\" symbol.",
"wiz_hue_press_link" : "Please press link button on the Hue Bridge.",
- "wiz_hue_ids_disabled" : "Deactivated",
- "wiz_hue_ids_entire" : "Whole picture",
"wiz_hue_noids" : "This Hue bridge has no bulbs/stripes, please pair them before with the Hue Apps",
- "wiz_hue_pos": "Position/State",
"wiz_hue_searchb": "Searching for bridge...",
"wiz_hue_blinkblue": "Let ID $1 light up blue",
- "wiz_hue_ident" : "Identify",
"wiz_hue_e_create_user" : "Create new User and clientkey",
"wiz_hue_e_clientkey_needed": "A clientkey that matches the username is required to use the entertainment API. Please enter an existing one or use the button below to create a new one.",
"wiz_hue_e_use_groupid": "Use group ID $1",
@@ -396,6 +392,16 @@
"wiz_hue_e_desc2" : "Now choose your entertainment group, which has all your lights inside for use with Hyperion.",
"wiz_hue_e_desc3": "Now you can choose in which position the respective lamp should be \"in the picture\". A preselection of the position was made based on the configured positions of the lights in the entertainment group. This is just a recommendation and can be customized as desired. You can therefore highlight them briefly by clicking on the right button to improve the selection.",
"wiz_hue_e_use_group" : "Use group",
+ "wiz_yeelight_title" : "Yeelight Wizard",
+ "wiz_yeelight_intro1" : "This wizards configures Hyperion for the Yeelight system. Features are the Yeelighs' auto detection, setting each light to a specific position on your picture or disable it and tune the Hyperion settings automatically! So in short: All you need are some clicks and you are done!",
+ "wiz_yeelight_desc2" : "Now choose which lamps should be added. The position assigns the lamp to a specific position on your \"picture\". Disabled lamps won't be added. To identify single lamps press the button on the right.",
+ "wiz_yeelight_noLights": "No Yeelights found! Please get the lights connected to the network or configure them mannually.",
+ "wiz_yeelight_unsupported" : "Unsupported",
+ "wiz_pos": "Position/State",
+ "wiz_ids_disabled" : "Deactivated",
+ "wiz_ids_entire" : "Whole picture",
+ "wiz_identify" : "Identify",
+ "wiz_identify_light": "Identify $1",
"wiz_cc_title" : "Colour calibration wizard",
"wiz_cc_intro1" : "This wizard will guide you through your led calibration. If you are using Kodi, the calibration pictures and videos can be sent directly to it without further actions on your side. If not, you will need to download these files yourself and display them when the wizard needs you to adjust the setting.",
"wiz_cc_kwebs" : "Kodi webserver (IP:Port)",
@@ -438,7 +444,9 @@
"edt_dev_spec_orbIds_title" : "Orb ID(s)",
"edt_dev_spec_useOrbSmoothing_title" : "Use orb smoothing",
"edt_dev_spec_targetIp_title" : "Target IP",
- "edt_dev_spec_targetIpHost_title" : "Target IP/hostname",
+ "edt_dev_spec_targetIpHost_title" : "Target IP/Hostname",
+ "edt_dev_spec_networkDeviceName_title" : "Network devicename",
+ "edt_dev_spec_networkDevicePort_title" : "Port",
"edt_dev_spec_outputPath_title" : "Output path",
"edt_dev_spec_delayAfterConnect_title" : "Delay after connect",
"edt_dev_spec_FCsetConfig_title" : "Set fadecandy configuration",
@@ -451,10 +459,23 @@
"edt_dev_spec_username_title" : "Username",
"edt_dev_spec_lightid_title" : "Light ID(s)",
"edt_dev_spec_lightid_itemtitle" : "ID",
+ "edt_dev_spec_lights_title" : "Light(s)",
+ "edt_dev_spec_lights_itemtitle" : "Light",
+ "edt_dev_spec_lights_name" : "Name",
"edt_dev_spec_transistionTime_title" : "Transition time",
"edt_dev_spec_blackLightsTimeout_title": "Signal detection timeout on black",
+ "edt_dev_spec_transistionTimeExtra_title" : "Extra time darkness",
+ "edt_dev_spec_transeffect_title" : "Transition effect",
+ "edt_conf_enum_transeffect_smooth" : "Smooth",
+ "edt_conf_enum_transeffect_sudden" : "Sudden",
+ "edt_dev_spec_debugLevel_title" : "Debug level",
+ "edt_conf_enum_dl_nodebug" : "No Debug output",
+ "edt_conf_enum_dl_verbose1" : "Verbosity level 1",
+ "edt_conf_enum_dl_verbose2" : "Verbosity level 2",
+ "edt_conf_enum_dl_verbose3" : "Verbosity level 3",
"edt_dev_spec_brightnessThreshold_title": "Signal detection brightness minimum",
"edt_dev_spec_switchOffOnBlack_title" : "Switch off on black",
+ "edt_dev_spec_switchOffOnbelowMinBrightness_title" : "Switch-off, below minimum",
"edt_dev_spec_brightnessFactor_title" : "Brightness factor",
"edt_dev_spec_brightnessMin_title": "Brightness minimum",
"edt_dev_spec_brightnessMax_title": "Brightness maximum",
@@ -464,7 +485,7 @@
"edt_dev_spec_verbose_title": "Log all Hue commands",
"edt_dev_spec_debugStreamer_title": "Streamer Debug",
"edt_dev_spec_debugLevel_title": "Streamer Connection Debug Level",
- "edt_dev_spec_restoreOriginalState_title" : "Restore lights' original state",
+ "edt_dev_spec_restoreOriginalState_title" : "Restore lights' original state when disabled",
"edt_dev_spec_useEntertainmentAPI_title": "Use Hue Entertainment API",
"edt_dev_spec_ledType_title" : "LED Type",
"edt_dev_spec_uid_title" : "UID",
@@ -512,6 +533,7 @@
"edt_conf_enum_brg" : "BRG",
"edt_conf_enum_gbr" : "GBR",
"edt_conf_enum_grb" : "GRB",
+ "edt_conf_enum_hsv" : "HSV",
"edt_conf_enum_linear" : "Linear",
"edt_conf_enum_PAL" : "PAL",
"edt_conf_enum_NTSC" : "NTSC",
diff --git a/assets/webconfig/i18n/es.json b/assets/webconfig/i18n/es.json
index cf9475b5..e463d804 100644
--- a/assets/webconfig/i18n/es.json
+++ b/assets/webconfig/i18n/es.json
@@ -316,13 +316,14 @@
"wiz_hue_failure_connection": "El tiempo de conexión expiró. Por favor, pulsa el botón a tiempo.",
"wiz_hue_failure_user": "Usuario no encontrado, crea uno nuevo debajo o introduce un ID de usuario válido",
"wiz_hue_press_link": "Por favor, presione el botón de enlace en el Puente de Matiz.",
- "wiz_hue_ids_disabled": "Desactivado",
- "wiz_hue_ids_entire": "Imagen completa",
"wiz_hue_noids": "Este puente de Matiz no tiene bombillas/tiras, por favor, emparéjalos antes con las aplicaciones de Hue",
- "wiz_hue_pos": "Posición/Estado",
"wiz_hue_searchb": "Buscando el puente...",
"wiz_hue_blinkblue": "Permite a ID $1 encender el azul",
"wiz_hue_ident": "Identificar",
+ "wiz_ids_disabled": "Desactivado",
+ "wiz_ids_entire": "Imagen completa",
+ "wiz_pos": "Posición/Estado",
+ "wiz_identify": "Identificar",
"wiz_cc_title": "Asistente de calibración de color",
"wiz_cc_intro1": "Este asistente te guiará a través de la calibración led. Si estás utilizando Kodi, las imágenes de calibración y los videos se pueden enviar directamente a kodi sin más tareas de tu lado. Si no, necesitas descargar estos archivos tú mismo y aplicarlos, si el asistente lo desea.",
"wiz_cc_kwebs": "Servidor web Kodi (IP:PUERTO)",
diff --git a/assets/webconfig/i18n/it.json b/assets/webconfig/i18n/it.json
index 70275db2..c9ee39c8 100644
--- a/assets/webconfig/i18n/it.json
+++ b/assets/webconfig/i18n/it.json
@@ -316,13 +316,13 @@
"wiz_hue_failure_connection": "Timeout: premi il bottone del bridge entro il periodo di 30 secondi",
"wiz_hue_failure_user": "Utente non trovato, creane uno nuovo qui sotto o inserisci un id utente valido e premi il simbolo di \"ricarica\".",
"wiz_hue_press_link": "Premi il bottone di collegamento sull'Hue Bridge.",
- "wiz_hue_ids_disabled": "Disattiva",
- "wiz_hue_ids_entire": "Immagine intera",
"wiz_hue_noids": "questo Hue bridge non ha lampadine/strisce, prima associale con l'Hue App",
- "wiz_hue_pos": "Posizione/Stato",
"wiz_hue_searchb": "Cercando il bridge...",
"wiz_hue_blinkblue": "ID $1 si illumina di blu",
- "wiz_hue_ident": "Identifica",
+ "wiz_ids_disabled": "Disattiva",
+ "wiz_ids_entire": "Immagine intera",
+ "wiz_pos": "Posizione/Stato",
+ "wiz_identify" : "Identifica",
"wiz_cc_title": "Assistente calibrazione colore",
"wiz_cc_intro1": "Questo assistente ti guiderà attraverso la calibrazione dei tuoi led. Se stai usando Kodi, le immagini e i video di calibrazione possono essere mandati direttamente a Kodi senza altro lavoro da parte tua. Altrimenti devi scaricare questi file e applicarli tu stesso quando l'assistente necessita di regolare le impostazioni.",
"wiz_cc_kwebs": "Webserver Kodi (IP:Porta)",
diff --git a/assets/webconfig/i18n/sv.json b/assets/webconfig/i18n/sv.json
index 5aa03e29..ba75868a 100644
--- a/assets/webconfig/i18n/sv.json
+++ b/assets/webconfig/i18n/sv.json
@@ -316,13 +316,13 @@
"wiz_hue_failure_connection": "Timeout: Tryck på bryggknappen inom 30 sekunder",
"wiz_hue_failure_user": "Användaren hittades inte, skapa en ny med knappen nedan eller mata in ett giltigt användar-ID och tryck på \"ladda om\"",
"wiz_hue_press_link": "Tryck på länkknappen på HUE-bryggan.",
- "wiz_hue_ids_disabled": "Inaktiverad",
- "wiz_hue_ids_entire": "Hela bilden",
"wiz_hue_noids": "Denna Hue-brygga har inga LED-lampor/-tejp, vänligen koppla ihop dem med HUE-appen innan",
- "wiz_hue_pos": "Position/Läge",
"wiz_hue_searchb": "Letar efter brygga...",
"wiz_hue_blinkblue": "Låt ID $1 lysa blått",
- "wiz_hue_ident": "Identifiera",
+ "wiz_ids_disabled": "Inaktiverad",
+ "wiz_ids_entire": "Hela bilden",
+ "wiz_pos": "Position/Läge",
+ "wiz_identify": "Identifiera",
"wiz_cc_title": "Färgkalibreringsguiden",
"wiz_cc_intro1": "Den här guiden leder dig igenom din LED-kalibrering. Om du använder Kodi kan kalibreringsbilder och videoklipp skickas direkt utan ytterligare åtgärder från din sida. Om inte, måste du ladda ner dessa filer själv och visa dem när guiden vill justera inställningen.",
"wiz_cc_kwebs": "Kodi webbserver (IP:Port)",
diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js
old mode 100755
new mode 100644
index 5d310914..5db3676f
--- a/assets/webconfig/js/content_leds.js
+++ b/assets/webconfig/js/content_leds.js
@@ -489,7 +489,7 @@ $(document).ready(function() {
$("#leddevices").off().on("change", function() {
var generalOptions = window.serverSchema.properties.device;
- // Modified schema entry "hardwareLedCount" in generalOptions to minimum LedCount
+ // Modified schema entry "hardwareLedCount" in generalOptions to minimum LedCount
var ledType = $(this).val();
//philipshueentertainment backward fix
@@ -535,6 +535,20 @@ $(document).ready(function() {
});
$("#root_specificOptions_useEntertainmentAPI").trigger("change");
}
+/*
+ else if(ledType == "wled") {
+ var ledWizardType = (this.checked) ? "wled" : ledType;
+ var data = { type: ledWizardType };
+ var wled_title = 'wiz_wled_title';
+ changeWizard(data, wled_title, startWizardWLED);
+ }
+*/
+ else if(ledType == "yeelight") {
+ var ledWizardType = (this.checked) ? "yeelight" : ledType;
+ var data = { type: ledWizardType };
+ var yeelight_title = 'wiz_yeelight_title';
+ changeWizard(data, yeelight_title, startWizardYeelight);
+ }
function changeWizard(data, hint, fn) {
$('#btn_wiz_holder').html("")
@@ -551,7 +565,7 @@ $(document).ready(function() {
var devRPiSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'ws2812spi'];
var devRPiPWM = ['ws281x'];
var devRPiGPIO = ['piblaster'];
- var devNET = ['atmoorb', 'fadecandy', 'philipshue', 'nanoleaf', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udph801', 'udpraw'];
+ var devNET = ['atmoorb', 'fadecandy', 'philipshue', 'nanoleaf', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udph801', 'udpraw', 'wled', 'yeelight'];
var devUSB = ['adalight', 'dmx', 'atmo', 'hyperionusbasp', 'lightpack', 'multilightpack', 'paintpack', 'rawhid', 'sedu', 'tpm2', 'karate'];
var optArr = [[]];
diff --git a/assets/webconfig/js/hyperion.js b/assets/webconfig/js/hyperion.js
index 79c6fc54..94cab009 100644
--- a/assets/webconfig/js/hyperion.js
+++ b/assets/webconfig/js/hyperion.js
@@ -118,15 +118,19 @@ function initWebSocket()
var response = JSON.parse(event.data);
var success = response.success;
var cmd = response.command;
+ var tan = response.tan
if (success || typeof(success) == "undefined")
{
$(window.hyperion).trigger({type:"cmd-"+cmd, response:response});
}
else
{
- var error = response.hasOwnProperty("error")? response.error : "unknown";
- $(window.hyperion).trigger({type:"error",reason:error});
- console.log("[window.websocket::onmessage] ",error)
+ // skip tan -1 error handling
+ if(tan != -1){
+ var error = response.hasOwnProperty("error")? response.error : "unknown";
+ $(window.hyperion).trigger({type:"error",reason:error});
+ console.log("[window.websocket::onmessage] ",error)
+ }
}
}
catch(exception_error)
@@ -165,6 +169,53 @@ function sendToHyperion(command, subcommand, msg)
window.websocket.send('{"command":"'+command+'", "tan":'+window.wsTan+subcommand+msg+'}');
}
+// Send a json message to Hyperion and wait for a matching response
+// A response matches, when command(+subcommand) of request and response is the same
+// command: The string command
+// subcommand: The optional string subcommand
+// data: The json data as Object
+// tan: The optional tan, default 1. If the tan is -1, we skip global response error handling
+// Returns data of response or false if timeout
+async function sendAsyncToHyperion (command, subcommand, data, tan = 1) {
+ let obj = { command, tan }
+ if (subcommand) {Object.assign(obj, {subcommand})}
+ if (data) { Object.assign(obj, data) }
+
+ //if (process.env.DEV || sstore.getters['common/getDebugState']) console.log('SENDAS', obj)
+ return __sendAsync(obj)
+}
+
+// Send a json message to Hyperion and wait for a matching response
+// A response matches, when command(+subcommand) of request and response is the same
+// Returns data of response or false if timeout
+async function __sendAsync (data) {
+ return new Promise((resolve, reject) => {
+ let cmd = data.command
+ let subc = data.subcommand
+ let tan = data.tan;
+ if (subc)
+ cmd = `${cmd}-${subc}`
+
+ let func = (e) => {
+ let rdata;
+ try {
+ rdata = JSON.parse(e.data)
+ } catch (error) {
+ console.error("[window.websocket::onmessage] ",error)
+ resolve(false)
+ }
+ if (rdata.command == cmd && rdata.tan == tan) {
+ window.websocket.removeEventListener('message', func)
+ resolve(rdata)
+ }
+ }
+ // after 7 sec we resolve false
+ setTimeout(() => { window.websocket.removeEventListener('message', func); resolve(false) }, 7000)
+ window.websocket.addEventListener('message', func)
+ window.websocket.send(JSON.stringify(data) + '\n')
+ })
+}
+
// -----------------------------------------------------------
// wrapped server commands
@@ -396,3 +447,25 @@ function requestAdjustment(type, value, complete)
else
sendToHyperion("adjustment", "", '"adjustment": {"'+type+'": '+value+'}');
}
+
+async function requestLedDeviceDiscovery(type)
+{
+ let data = { ledDeviceType: type };
+
+ return sendAsyncToHyperion("leddevice", "discover", data, Math.floor(Math.random() * 1000) );
+}
+
+async function requestLedDeviceProperties(type, params)
+{
+ let data = { ledDeviceType: type, params: params };
+
+ return sendAsyncToHyperion("leddevice", "getProperties", data, Math.floor(Math.random() * 1000));
+}
+
+function requestLedDeviceIdentification(type, params)
+{
+ sendToHyperion("leddevice", "identify", '"ledDeviceType": "'+type+'","params": '+JSON.stringify(params)+'');
+
+ //let data = {ledDeviceType: type, params: params};
+ //sendToHyperion("leddevice", "identify", data );
+}
diff --git a/assets/webconfig/js/wizard.js b/assets/webconfig/js/wizard.js
index b019b581..f7a6f33a 100644
--- a/assets/webconfig/js/wizard.js
+++ b/assets/webconfig/js/wizard.js
@@ -514,7 +514,66 @@ function beginWizardCC()
$('#btn_wizard_colorcalibration').off().on('click', startWizardCC);
-//hue wizard
+// Layout positions
+var lightPosTop = {hmin: 0.15, hmax: 0.85, vmin: 0 , vmax: 0.2 };
+var lightPosTopLeft = {hmin: 0 , hmax: 0.15, vmin: 0 , vmax: 0.15};
+var lightPosTopRight = {hmin: 0.85, hmax: 1.0 , vmin: 0 , vmax: 0.15};
+var lightPosBottom = {hmin: 0.15, hmax: 0.85, vmin: 0.8 , vmax: 1.0 };
+var lightPosBottomLeft = {hmin: 0 , hmax: 0.15, vmin: 0.85, vmax: 1.0 };
+var lightPosBottomRight = {hmin: 0.85, hmax: 1.0 , vmin: 0.85, vmax: 1.0 };
+var lightPosLeft = {hmin: 0 , hmax: 0.15, vmin: 0.15, vmax: 0.85};
+var lightPosLeftTop = {hmin: 0 , hmax: 0.15, vmin: 0 , vmax: 0.5 };
+var lightPosLeftMiddle = {hmin: 0 , hmax: 0.15, vmin: 0.25, vmax: 0.75};
+var lightPosLeftBottom = {hmin: 0 , hmax: 0.15, vmin: 0.5 , vmax: 1.0 };
+var lightPosRight = {hmin: 0.85, hmax: 1.0 , vmin: 0.15, vmax: 0.85};
+var lightPosRightTop = {hmin: 0.85, hmax: 1.0 , vmin: 0 , vmax: 0.5 };
+var lightPosRightMiddle = {hmin: 0.85, hmax: 1.0 , vmin: 0.25, vmax: 0.75};
+var lightPosRightBottom = {hmin: 0.85, hmax: 1.0 , vmin: 0.5 , vmax: 1.0 };
+var lightPosEntire = {hmin: 0.0 , hmax: 1.0 , vmin: 0.0 , vmax: 1.0 };
+
+function assignLightPos(id, pos, name)
+{
+ var i = null;
+
+ if(pos === "top")
+ i = lightPosTop;
+ else if(pos === "topleft")
+ i = lightPosTopLeft;
+ else if(pos === "topright")
+ i = lightPosTopRight;
+ else if(pos === "bottom")
+ i = lightPosBottom;
+ else if(pos === "bottomleft")
+ i = lightPosBottomLeft;
+ else if(pos === "bottomright")
+ i = lightPosBottomRight;
+ else if(pos === "left")
+ i = lightPosLeft;
+ else if(pos === "lefttop")
+ i = lightPosLeftTop;
+ else if(pos === "leftmiddle")
+ i = lightPosLeftMiddle;
+ else if(pos === "leftbottom")
+ i = lightPosLeftBottom;
+ else if(pos === "right")
+ i = lightPosRight;
+ else if(pos === "righttop")
+ i = lightPosRightTop;
+ else if(pos === "rightmiddle")
+ i = lightPosRightMiddle;
+ else if(pos === "rightbottom")
+ i = lightPosRightBottom;
+ else
+ i = lightPosEntire;
+
+ i.name = name;
+ return i;
+}
+
+//****************************
+// Wizard Philips Hue
+//****************************
+
var hueIPs = [];
var hueIPsinc = 0;
var lightIDs = null;
@@ -522,22 +581,6 @@ var groupIDs = null;
var lightLocation = [];
var groupLights = [];
var groupLightsLocations = [];
-
-var huePosTop = {hmin: 0.15, hmax: 0.85, vmin: 0 , vmax: 0.2 };
-var huePosTopLeft = {hmin: 0 , hmax: 0.15, vmin: 0 , vmax: 0.15};
-var huePosTopRight = {hmin: 0.85, hmax: 1.0 , vmin: 0 , vmax: 0.15};
-var huePosBottom = {hmin: 0.15, hmax: 0.85, vmin: 0.8 , vmax: 1.0 };
-var huePosBottomLeft = {hmin: 0 , hmax: 0.15, vmin: 0.85, vmax: 1.0 };
-var huePosBottomRight = {hmin: 0.85, hmax: 1.0 , vmin: 0.85, vmax: 1.0 };
-var huePosLeft = {hmin: 0 , hmax: 0.15, vmin: 0.15, vmax: 0.85};
-var huePosLeftTop = {hmin: 0 , hmax: 0.15, vmin: 0 , vmax: 0.5 };
-var huePosLeftMiddle = {hmin: 0 , hmax: 0.15, vmin: 0.25, vmax: 0.75};
-var huePosLeftBottom = {hmin: 0 , hmax: 0.15, vmin: 0.5 , vmax: 1.0 };
-var huePosRight = {hmin: 0.85, hmax: 1.0 , vmin: 0.15, vmax: 0.85};
-var huePosRightTop = {hmin: 0.85, hmax: 1.0 , vmin: 0 , vmax: 0.5 };
-var huePosRightMiddle = {hmin: 0.85, hmax: 1.0 , vmin: 0.25, vmax: 0.75};
-var huePosRightBottom = {hmin: 0.85, hmax: 1.0 , vmin: 0.5 , vmax: 1.0 };
-var huePosEntire = {hmin: 0.0 , hmax: 1.0 , vmin: 0.0 , vmax: 1.0 };
var hueType = "philipshue";
function startWizardPhilipsHue(e)
@@ -585,7 +628,7 @@ function startWizardPhilipsHue(e)
$('#wizp2_body').append('
'+$.i18n('wiz_hue_desc2')+'
');
}
createTable("lidsh", "lidsb", "hue_ids_t");
- $('.lidsh').append(createTableRow([$.i18n('edt_dev_spec_lightid_title'),$.i18n('wiz_hue_pos'),$.i18n('wiz_hue_ident')], true));
+ $('.lidsh').append(createTableRow([$.i18n('edt_dev_spec_lightid_title'),$.i18n('wiz_pos'),$.i18n('wiz_identify')], true));
$('#wizp2_footer').html(' '+$.i18n('general_btn_save')+' '+$.i18n('general_btn_cancel')+' ');
$('#wizp3_body').html(''+$.i18n('wiz_hue_press_link')+' ');
@@ -684,45 +727,6 @@ function checkUserResult(reply, usr) {
}
};
-function assignHuePos(id, pos)
-{
- var i = null;
-
- if(pos == "top")
- i = huePosTop;
- else if(pos == "topleft")
- i = huePosTopLeft;
- else if(pos == "topright")
- i = huePosTopRight;
- else if(pos == "bottom")
- i = huePosBottom;
- else if(pos == "bottomleft")
- i = huePosBottomLeft;
- else if(pos == "bottomright")
- i = huePosBottomRight;
- else if(pos == "left")
- i = huePosLeft;
- else if(pos == "lefttop")
- i = huePosLeftTop;
- else if(pos == "leftmiddle")
- i = huePosLeftMiddle;
- else if(pos == "leftbottom")
- i = huePosLeftBottom;
- else if(pos == "right")
- i = huePosRight;
- else if(pos == "righttop")
- i = huePosRightTop;
- else if(pos == "rightmiddle")
- i = huePosRightMiddle;
- else if(pos == "rightbottom")
- i = huePosRightBottom;
- else
- i = huePosEntire;
-
- i.name = lightIDs[id].name;
- return i;
-}
-
function identHueId(id, off, oState)
{
if(off !== true)
@@ -751,6 +755,72 @@ function useGroupId(id)
get_hue_lights();
}
+async function discover_hue_bridges(){
+
+ const res = await requestLedDeviceDiscovery ('philipshue');
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process devices returned by discovery
+ console.log(r);
+
+ if(r.devices.length == 0)
+ $('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip'));
+ else
+ {
+ for(const device of r.devices)
+ {
+ console.log("Device:", device);
+
+ var ip = device.hostname + ":" + device.port;
+ console.log("Host:", ip);
+
+ hueIPs.push({internalipaddress : ip});
+ }
+ var usr = $('#user').val();
+ if(usr != "") {
+ checkHueBridge(checkUserResult, usr);
+ } else {
+ checkHueBridge(checkBridgeResult);
+ }
+ }
+ }
+}
+
+async function getProperties_hue_bridge(hostAddress, username, resourceFilter){
+
+ let params = { host: hostAddress, user: username, filter: resourceFilter};
+
+ const res = await requestLedDeviceProperties ('philipshue', params);
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process properties returned
+ console.log(r);
+ }
+}
+
+function identify_hue_device(hostAddress, username, id){
+
+ console.log("identify_hue_device");
+
+ let params = { host: hostAddress, user: username, lightId: id };
+
+ const res = requestLedDeviceIdentification ("philipshue", params);
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+ console.log(r);
+ }
+}
+
function getHueIPs(){
$('#wiz_hue_ipstate').html($.i18n('wiz_hue_searchb'));
$.ajax({
@@ -795,7 +865,8 @@ function beginWizardHue()
//check if ip is empty/reachable/search for bridge
if(eV("output") == "")
{
- getHueIPs();
+ //getHueIPs();
+ discover_hue_bridges();
}
else
{
@@ -810,8 +881,13 @@ function beginWizardHue()
}
$('#retry_bridge').off().on('click', function(){
- if($('#ip').val()!="") hueIPs.unshift({internalipaddress : $('#ip').val()});
- hueIPsinc = 0;
+ if($('#ip').val()!="")
+ {
+ hueIPs.unshift({internalipaddress : $('#ip').val()})
+ hueIPsinc = 0;
+ }
+ else discover_hue_bridges();
+
var usr = $('#user').val();
if(usr != "") {
checkHueBridge(checkUserResult, usr);
@@ -843,7 +919,7 @@ function beginWizardHue()
if($('#hue_'+key).val() != "disabled")
{
finalLightIds.push(key);
- var idx_content = assignHuePos(key, $('#hue_'+key).val());
+ var idx_content = assignLightPos(key, $('#hue_'+key).val(), lightIDs[key].name);
hueLedConfig.push(JSON.parse(JSON.stringify(idx_content)));
}
}
@@ -860,7 +936,9 @@ function beginWizardHue()
c.brightnessCompensation = 0;
//device config
- var d = sc.device;
+
+ //Start with a clean configuration
+ var d = {};
d.output = $('#ip').val();
d.username = $('#user').val();
d.type = 'philipshue';
@@ -903,6 +981,9 @@ function beginWizardHue()
//smoothing on
sc.smoothing.enable = true;
}
+
+ window.serverConfig.device = d;
+
requestWriteConfig(sc, true);
resetWizard();
});
@@ -1098,14 +1179,14 @@ function get_hue_lights(){
for(var opt in lightOptions)
{
var val = lightOptions[opt];
- var txt = (val != 'entire' && val != 'disabled') ? 'conf_leds_layout_cl_' : 'wiz_hue_ids_';
+ var txt = (val != 'entire' && val != 'disabled') ? 'conf_leds_layout_cl_' : 'wiz_ids_';
options+= ''+$.i18n(txt+val)+' ';
}
$('.lidsb').append(createTableRow([lightid+' ('+r[lightid].name+')', ''
+ options
- + ' ',''+$.i18n('wiz_hue_blinkblue',lightid)+' ']));
+ + '',''+$.i18n('wiz_hue_blinkblue',lightid)+' ']));
}
if(hueType != 'philipshueentertainment')
@@ -1140,3 +1221,494 @@ function abortConnection(UserInterval){
$('#wizp3').toggle(false);
$("#wiz_hue_usrstate").html($.i18n('wiz_hue_failure_connection'));
}
+
+//****************************
+// Wizard WLED
+//****************************
+var lights = null;
+function startWizardWLED(e)
+{
+ //create html
+
+ var wled_title = 'wiz_wled_title';
+ var wled_intro1 = 'wiz_wled_intro1';
+
+ $('#wiz_header').html(' '+$.i18n(wled_title));
+ $('#wizp1_body').html(''+$.i18n(wled_title)+' '+$.i18n(wled_intro1)+'
');
+ $('#wizp1_footer').html(' '+$.i18n('general_btn_continue')+' '+$.i18n('general_btn_cancel')+' ');
+
+ /*$('#wizp2_body').html('
');
+
+ $('#wh_topcontainer').append('
');
+
+ $('#wizp2_body').append(''+$.i18n('wiz_wled_desc2')+'
');
+
+ createTable("lidsh", "lidsb", "hue_ids_t");
+ $('.lidsh').append(createTableRow([$.i18n('edt_dev_spec_lights_title'),$.i18n('wiz_pos'),$.i18n('wiz_identify')], true));
+ $('#wizp2_footer').html(' '+$.i18n('general_btn_save')+' '+$.i18n('general_btn_cancel')+' ');
+*/
+ //open modal
+ $("#wizard_modal").modal({
+ backdrop : "static",
+ keyboard: false,
+ show: true
+ });
+
+ //listen for continue
+ $('#btn_wiz_cont').off().on('click',function() {
+
+// For testing only
+ discover_wled();
+
+ var hostAddress = conf_editor.getEditor("root.specificOptions.host").getValue();
+ if(hostAddress != "")
+ {
+ getProperties_wled(hostAddress);
+ identify_wled(hostAddress)
+ }
+
+// For testing only
+
+ });
+}
+
+async function discover_wled(){
+
+ const res = await requestLedDeviceDiscovery ('wled');
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process devices returned by discovery
+ console.log(r);
+
+ if(r.devices.length == 0)
+ $('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip'));
+ else
+ {
+ for(const device of r.devices)
+ {
+ console.log("Device:", device);
+
+ var ip = device.hostname + ":" + device.port;
+ console.log("Host:", ip);
+
+ //wledIPs.push({internalipaddress : ip});
+ }
+ }
+ }
+}
+
+async function getProperties_wled(hostAddress, resourceFilter){
+
+ let params = { host: hostAddress, filter: resourceFilter};
+
+ const res = await requestLedDeviceProperties ('wled', params);
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process properties returned
+ console.log(r);
+ }
+}
+
+function identify_wled(hostAddress){
+
+ let params = { host: hostAddress };
+
+ const res = requestLedDeviceIdentification ("wled", params);
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+
+ const r = res.info
+ console.log(r);
+ }
+}
+
+//****************************
+// Wizard Yeelight
+//****************************
+var lights = null;
+function startWizardYeelight(e)
+{
+ //create html
+
+ var yeelight_title = 'wiz_yeelight_title';
+ var yeelight_intro1 = 'wiz_yeelight_intro1';
+
+ $('#wiz_header').html(' '+$.i18n(yeelight_title));
+ $('#wizp1_body').html(''+$.i18n(yeelight_title)+' '+$.i18n(yeelight_intro1)+'
');
+
+ $('#wizp1_footer').html(' '
+ +$.i18n('general_btn_continue')+' '
+ +$.i18n('general_btn_cancel')+' ');
+
+ $('#wizp2_body').html('
');
+
+ $('#wh_topcontainer').append('
');
+
+ $('#wizp2_body').append(''+$.i18n('wiz_yeelight_desc2')+'
');
+
+ createTable("lidsh", "lidsb", "yee_ids_t");
+ $('.lidsh').append(createTableRow([$.i18n('edt_dev_spec_lights_title'),$.i18n('wiz_pos'),$.i18n('wiz_identify')], true));
+ $('#wizp2_footer').html(' '
+ +$.i18n('general_btn_save')+' '
+ +$.i18n('general_btn_cancel')+'');
+
+ //open modal
+ $("#wizard_modal").modal({backdrop : "static", keyboard: false, show: true });
+
+ //listen for continue
+ $('#btn_wiz_cont').off().on('click',function() {
+ beginWizardYeelight();
+ $('#wizp1').toggle(false);
+ $('#wizp2').toggle(true);
+ });
+}
+
+function beginWizardYeelight()
+{
+ lights = [];
+ configuredLights = conf_editor.getEditor("root.specificOptions.lights").getValue();
+
+ discover_yeelight_lights();
+
+ $('#btn_wiz_save').off().on("click", function(){
+ var yeelightLedConfig = [];
+ var finalLights = [];
+
+ //create yeelight led config
+ for(var key in lights)
+ {
+ if($('#yee_'+key).val() !== "disabled")
+ {
+ //delete lights[key].model;
+
+ // Set Name to layout-position, if empty
+ if ( lights[key].name === "" )
+ {
+ lights[key].name = $.i18n( 'conf_leds_layout_cl_'+$('#yee_'+key).val() );
+ }
+
+ finalLights.push( lights[key]);
+
+ var name = lights[key].host;
+ if ( lights[key].name !== "")
+ name += '_'+lights[key].name;
+
+ var idx_content = assignLightPos(key, $('#yee_'+key).val(), name);
+ yeelightLedConfig.push(JSON.parse(JSON.stringify(idx_content)));
+ }
+ }
+
+ //LED layout
+ window.serverConfig.leds = yeelightLedConfig;
+
+ //LED device config
+ //Start with a clean configuration
+ var d = {};
+
+ d.type = 'yeelight';
+ d.hardwareLedCount = finalLights.length;
+ d.colorOrder = conf_editor.getEditor("root.generalOptions.colorOrder").getValue();
+ d.colorModel = parseInt(conf_editor.getEditor("root.specificOptions.colorModel").getValue());
+
+ d.transEffect = parseInt(conf_editor.getEditor("root.specificOptions.transEffect").getValue());
+ d.transTime = parseInt(conf_editor.getEditor("root.specificOptions.transTime").getValue());
+ d.extraTimeDarkness = parseInt(conf_editor.getEditor("root.specificOptions.extraTimeDarkness").getValue());
+
+ d.brightnessMin = parseInt(conf_editor.getEditor("root.specificOptions.brightnessMin").getValue());
+ d.brightnessSwitchOffOnMinimum = JSON.parse(conf_editor.getEditor("root.specificOptions.brightnessSwitchOffOnMinimum").getValue());
+ d.brightnessMax = parseInt(conf_editor.getEditor("root.specificOptions.brightnessMax").getValue());
+ d.brightnessFactor = parseFloat(conf_editor.getEditor("root.specificOptions.brightnessFactor").getValue());
+
+ d.latchTime = parseInt(conf_editor.getEditor("root.specificOptions.latchTime").getValue());;
+ d.debugLevel = parseInt(conf_editor.getEditor("root.specificOptions.debugLevel").getValue());
+
+ d.lights = finalLights;
+
+ window.serverConfig.device = d;
+
+ //smoothing off
+ window.serverConfig.smoothing.enable = false;
+
+ requestWriteConfig(window.serverConfig, true);
+ resetWizard();
+ });
+
+ $('#btn_wiz_abort').off().on('click', resetWizard);
+}
+
+function getHostInLights(hostname) {
+ return lights.filter(
+ function(lights) {
+ return lights.host === hostname
+ }
+ );
+}
+
+async function discover_yeelight_lights(){
+
+ var light = {};
+ // Get discovered lights
+ const res = await requestLedDeviceDiscovery ('yeelight');
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process devices returned by discovery
+ for(const device of r.devices)
+ {
+ //console.log("Device:", device);
+
+ if( device.hostname !== "")
+ {
+ if ( getHostInLights ( device.hostname ).length === 0 )
+ {
+ light = {};
+ light.host = device.hostname;
+ light.port = device.port;
+
+ light.name = device.other.name;
+ light.model = device.other.model;
+ lights.push(light);
+ }
+ }
+ }
+
+ // Add additional items from configuration
+ for(var keyConfig in configuredLights)
+ {
+
+ var [host, port]= configuredLights[keyConfig].host.split(":", 2);
+
+ //In case port has been explicitly provided, overwrite port given as part of hostname
+ if ( configuredLights[keyConfig].port !== 0 )
+ port = configuredLights[keyConfig].port;
+
+ if ( host !== "" )
+ if ( getHostInLights ( host ).length === 0 )
+ {
+ light = {};
+ light.host = host;
+ light.port = port;
+ light.name = configuredLights[keyConfig].name;
+ light.model = "color4";
+ lights.push(light);
+ }
+ }
+
+ assign_yeelight_lights();
+ }
+}
+
+function assign_yeelight_lights(){
+
+ var models = ['color', 'color1', 'color2', 'color4', 'stripe', 'strip1'];
+
+ // If records are left for configuration
+ if(Object.keys(lights).length > 0)
+ {
+ $('#wh_topcontainer').toggle(false);
+ $('#yee_ids_t, #btn_wiz_save').toggle(true);
+
+ var lightOptions = [
+ "top", "topleft", "topright",
+ "bottom", "bottomleft", "bottomright",
+ "left", "lefttop", "leftmiddle", "leftbottom",
+ "right", "righttop", "rightmiddle", "rightbottom",
+ "entire"
+ ];
+
+ lightOptions.unshift("disabled");
+
+ $('.lidsb').html("");
+ var pos = "";
+
+ for(var lightid in lights)
+ {
+ var lightHostname = lights[lightid].host;
+ var lightPort = lights[lightid].port;
+ var lightName = lights[lightid].name;
+
+ if ( lightName === "" )
+ lightName = $.i18n('edt_dev_spec_lights_itemtitle');
+
+ var options = "";
+ for(var opt in lightOptions)
+ {
+ var val = lightOptions[opt];
+ var txt = (val !== 'entire' && val !== 'disabled') ? 'conf_leds_layout_cl_' : 'wiz_ids_';
+ options+= ''+$.i18n(txt+val)+' ';
+ }
+
+ if (! models.includes (lights[lightid].model) )
+ {
+ var enabled = 'disabled'
+ options = ''+$.i18n('wiz_yeelight_unsupported')+' ';
+ }
+
+ $('.lidsb').append(createTableRow([(parseInt(lightid, 10) + 1)+'. '+lightName+' ('+lightHostname+')', ''
+ + options
+ + ' ',''
+ + $.i18n('wiz_identify_light',lightName)+' ']));
+ }
+
+ $('.yee_sel_watch').bind("change", function(){
+ var cC = 0;
+ for(var key in lights)
+ {
+ if($('#yee_'+key).val() !== "disabled")
+ {
+ cC++;
+ }
+ }
+ if ( cC === 0)
+ $('#btn_wiz_save').attr("disabled",true);
+ else
+ $('#btn_wiz_save').attr("disabled",false);
+ });
+ $('.yee_sel_watch').trigger('change');
+ }
+ else
+ {
+ var noLightsTxt = ''+$.i18n('wiz_yeelight_noLights')+'
';
+ $('#wizp2_body').append(noLightsTxt);
+ }
+}
+
+async function getProperties_yeelight(hostname, port){
+
+ let params = { hostname: hostname, port: port};
+
+ const res = await requestLedDeviceProperties ('yeelight', params);
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process properties returned
+ console.log(r);
+ }
+}
+
+function identify_yeelight_device(hostname, port){
+
+ let params = { hostname: hostname, port: port };
+
+ const res = requestLedDeviceIdentification ("yeelight", params);
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+ }
+}
+
+//****************************
+// Wizard/Routines Nanoleaf
+//****************************
+async function discover_nanoleaf(){
+
+ const res = await requestLedDeviceDiscovery ('nanoleaf');
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process devices returned by discovery
+ console.log(r);
+
+ if(r.devices.length == 0)
+ $('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip'));
+ else
+ {
+ for(const device of r.devices)
+ {
+ console.log("Device:", device);
+
+ var ip = device.hostname + ":" + device.port;
+ console.log("Host:", ip);
+
+ //nanoleafIPs.push({internalipaddress : ip});
+ }
+ }
+ }
+}
+
+async function getProperties_nanoleaf(hostAddress, authToken, resourceFilter){
+
+ let params = { host: hostAddress, token: authToken, filter: resourceFilter};
+
+ const res = await requestLedDeviceProperties ('nanoleaf', params);
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process properties returned
+ console.log(r);
+ }
+}
+
+function identify_nanoleaf(hostAddress, authToken){
+
+ let params = { host: hostAddress, token: authToken};
+
+ const res = requestLedDeviceIdentification ("nanoleaf", params);
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+
+ const r = res.info
+ console.log(r);
+ }
+}
+
+//****************************
+// Wizard/Routines RS232-Devices
+//****************************
+async function discover_providerRs232(rs232Type){
+
+ const res = await requestLedDeviceDiscovery (rs232Type);
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process serialPorts returned by discover
+ console.log(r);
+ }
+}
+
+//****************************
+// Wizard/Routines HID (USB)-Devices
+//****************************
+async function discover_providerHid(hidType){
+
+ const res = await requestLedDeviceDiscovery (hidType);
+ console.log("discover_providerHid" ,res);
+
+ // TODO: error case unhandled
+ // res can be: false (timeout) or res.error (not found)
+ if(res && !res.error){
+ const r = res.info
+
+ // Process HID returned by discover
+ console.log(r);
+ }
+}
+
diff --git a/dependencies/external/rpi_ws281x b/dependencies/external/rpi_ws281x
index 6a720cbd..68c6da2d 160000
--- a/dependencies/external/rpi_ws281x
+++ b/dependencies/external/rpi_ws281x
@@ -1 +1 @@
-Subproject commit 6a720cbd42d30be28e0f5c5ff6b1c00a4588a29b
+Subproject commit 68c6da2de32249d126264a363cc5ab788c87cc8b
diff --git a/effects/ledtest.json b/effects/ledtest.json
index 3c4c4c27..8a02b0ad 100644
--- a/effects/ledtest.json
+++ b/effects/ledtest.json
@@ -3,9 +3,9 @@
"script" : "ledtest.py",
"args" :
{
- "sleepTime" : 0.20,
+ "sleepTime" : 0.50,
"testleds" : "all",
- "smoothing-custom-settings" : true,
+ "smoothing-custom-settings" : false,
"smoothing-time_ms" : 500,
"smoothing-updateFrequency" : 20.0
}
diff --git a/effects/ledtest.py b/effects/ledtest.py
index 0174c3ab..bbdb7056 100644
--- a/effects/ledtest.py
+++ b/effects/ledtest.py
@@ -7,7 +7,7 @@ import time
#import colorsys
# Get parameters
-sleepTime = float(hyperion.args.get('sleepTime', 0.2))
+sleepTime = float(hyperion.args.get('sleepTime', 0.5))
testleds = hyperion.args.get('testleds', "all")
ledlist = hyperion.args.get('ledlist', "1")
diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h
index 9524d0c2..7ae8c9da 100644
--- a/include/api/JsonAPI.h
+++ b/include/api/JsonAPI.h
@@ -94,7 +94,7 @@ signals:
void callbackMessage(QJsonObject);
///
- /// Signal emits whenever a jsonmessage should be forwarded
+ /// Signal emits whenever a JSON-message should be forwarded
///
void forwardJsonMessage(QJsonObject);
@@ -247,7 +247,7 @@ private:
///
void handleLoggingCommand(const QJsonObject &message, const QString &command, const int tan);
- /// Handle an incoming JSON Proccessing message
+ /// Handle an incoming JSON Processing message
///
/// @param message the incoming message
///
@@ -271,6 +271,12 @@ private:
///
void handleInstanceCommand(const QJsonObject &message, const QString &command, const int tan);
+ /// Handle an incoming JSON Led Device message
+ ///
+ /// @param message the incoming message
+ ///
+ void handleLedDeviceCommand(const QJsonObject &message, const QString &command, const int tan);
+
///
/// Handle an incoming JSON message of unknown type
///
diff --git a/include/leddevice/LedDevice.h b/include/leddevice/LedDevice.h
index fdc02736..30efd19a 100644
--- a/include/leddevice/LedDevice.h
+++ b/include/leddevice/LedDevice.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef LEDEVICE_H
+#define LEDEVICE_H
// qt includes
#include
@@ -28,204 +29,397 @@ typedef LedDevice* ( *LedDeviceCreateFuncType ) ( const QJsonObject& );
typedef std::map LedDeviceRegistry;
///
-/// Interface (pure virtual base class) for LedDevices.
+/// @brief Interface (pure virtual base class) for LED-devices.
///
class LedDevice : public QObject
{
Q_OBJECT
public:
- LedDevice(const QJsonObject& config = QJsonObject(), QObject* parent = nullptr);
+
+ ///
+ /// @brief Constructs LED-device
+ ///
+ /// @param deviceConfig Device's configuration as JSON-Object
+ /// @param parent QT parent
+ ///
+ LedDevice(const QJsonObject& deviceConfig = QJsonObject(), QObject* parent = nullptr);
+
+ ///
+ /// @brief Destructor of the LED-device
+ ///
~LedDevice() override;
///
- /// @brief Get color order of device
+ /// @brief Get color order of device.
+ ///
/// @return The color order
///
const QString & getColorOrder() const { return _colorOrder; }
///
- /// @brief Set the current active ledDevice type
+ /// @brief Set the current active LED-device type.
///
/// @param deviceType Device's type
///
void setActiveDeviceType(const QString& deviceType);
///
- /// @brief Get the current active ledDevice type
+ /// @brief Get the current active LED-device type.
///
const QString & getActiveDeviceType() const { return _activeDeviceType; }
+ ///
+ /// @brief Set the number of LEDs supported by the device.
+ ///
+ /// @param[in] ledCount Number of device LEDs
+ ///
void setLedCount(unsigned int ledCount);
+
+ ///
+ /// @brief Get the number of LEDs supported by the device.
+ ///
+ /// @return Number of device's LEDs
+ ///
unsigned int getLedCount() const { return _ledCount; }
- bool enabled() const { return _enabled; }
+ ///
+ /// @brief Check, if the device is enabled.
+ ///
+ /// @return True, if enabled
+ ///
+ bool isEnabled() const { return _isEnabled; }
+ ///
+ /// @brief Set a device's latch time.
+ ///
+ /// Latch time is the time-frame a device requires until the next update can be processed.
+ /// During that time-frame any updates done via updateLeds are skipped.
+ ///
+ /// @param[in] latchTime_ms Latch time in milliseconds
+ ///
+ void setLatchTime(int latchTime_ms);
+
+ ///
+ /// @brief Get the currently defined LatchTime.
+ ///
+ /// @return Latch time in milliseconds
+ ///
int getLatchTime() const { return _latchTime_ms; }
- void setLatchTime( int latchTime_ms );
///
- /// Check, if device is ready to be used
- /// i.e. initialisation and configuration were successfull
+ /// @brief Discover devices of this type available (for configuration).
+ /// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways.
///
- /// @return True if device is ready
+ /// @return A JSON structure holding a list of devices found
///
- bool isReady() const { return _deviceReady; }
+ virtual QJsonObject discover();
///
- /// Check, if device is in error state
+ /// @brief Discover first device of this type available (for configuration).
+ /// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways.
///
- /// @return True if device is in error
+ /// @return A string of the device found
///
- bool isInError() const { return _deviceInError; }
+ virtual QString discoverFirst();
- inline bool componentState() const { return enabled(); }
-
- /// Prints the RGB-Color values to stdout.
///
- /// @param[in] ledValues The RGB-color per led
+ /// @brief Get the device's properties
///
- static void printLedValues (const std::vector& ledValues );
+ /// Used in context of a set of devices of the same type.
+ ///
+ /// @param[in] params Parameters to address device
+ /// @return A JSON structure holding the device's properties
+ ///
+ virtual QJsonObject getProperties(const QJsonObject& params);
+ ///
+ /// @brief Send an update to the device to identify it.
+ ///
+ /// Used in context of a set of devices of the same type.
+ ///
+ /// @param[in] params Parameters to address device
+ ///
+ virtual void identify(const QJsonObject& params) {}
+
+ ///
+ /// @brief Check, if device is properly initialised
+ ///
+ /// i.e. initialisation and configuration were successful.
+ ///
+ /// @return True, if device is initialised
+ ///
+ bool isInitialised() const { return _isDeviceInitialised; }
+
+ ///
+ /// @brief Check, if device is ready to be used.
+ ///
+ /// i.e. initialisation and opening were successful.
+ ///
+ /// @return True, if device is ready
+ ///
+ bool isReady() const { return _isDeviceReady; }
+
+ ///
+ /// @brief Check, if device is in error state.
+ ///
+ /// @return True, if device is in error
+ ///
+ bool isInError() const { return _isDeviceInError; }
+
+ ///
+ /// @brief Get the LED-Device component's state.
+ ///
+ /// @return True, if enabled
+ ///
+ inline bool componentState() const { return isEnabled(); }
+
+ ///
+ /// @brief Prints the color values to stdout.
+ ///
+ /// @param[in] ledValues The color per led
+ ///
+ static void printLedValues(const std::vector& ledValues);
public slots:
- ///
- /// Is called on thread start, all construction tasks and init should run here
- ///
- virtual void start() { _deviceReady = (open() == 0); }
///
- /// Update the RGB-Color values to the leds.
- /// Handles refreshing of leds.
+ /// @brief Is called on thread start, all construction tasks and init should run here.
///
- /// @param[in] ledValues The RGB-color per led
+ virtual void start();
+
+ ///
+ /// @brief Stops the device.
+ ///
+ /// Includes switching-off the device and stopping refreshes.
+ ///
+ virtual void stop();
+
+ ///
+ /// @brief Update the color values of the device's LEDs.
+ ///
+ /// Handles refreshing of LEDs.
+ ///
+ /// @param[in] ledValues The color per LED
/// @return Zero on success else negative (i.e. device is not ready)
///
virtual int updateLeds(const std::vector& ledValues);
///
- /// Closes the output device.
- /// Includes switching-off the device and stopping refreshes
+ /// @brief Enables/disables the device for output.
///
- virtual void close();
-
+ /// If the device is not ready, it will not be enabled.
///
- /// Enables/disables the device for output.
- /// If the device is not ready, it will not be enabled
+ /// @param[in] enable The new state of the device
///
- /// @param enable The new state of the device
- ///
- void setEnable(bool enable); ///
+ void setEnable(bool enable);
signals:
///
- /// Emits whenever the led device switches between on/off
- /// @param newState The new state of the device
+ /// @brief Emits whenever the LED-Device switches between on/off.
+ ///
+ /// @param[in] newState The new state of the device
///
void enableStateChanged(bool newState);
protected:
///
- /// Initialise a device's configuration
+ /// @brief Initialise the device's configuration.
///
- /// @param deviceConfig the json device config
- /// @return True if success
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig);
///
- /// Opens and initiatialises the output device
+ /// @brief Opens the output device.
///
- /// @return Zero on succes (i.e. device is ready and enabled) else negative
+ /// @return Zero, on success (i.e. device is ready), else negative
///
virtual int open();
///
- /// Writes the RGB-Color values to the leds.
+ /// @brief Closes the output device.
///
- /// @param[in] ledValues The RGB-color per led
+ /// @return Zero on success (i.e. device is closed), else negative
///
- /// @return Zero on success else negative
+ virtual int close();
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
///
virtual int write(const std::vector& ledValues) = 0;
///
- /// Writes "BLACK" to the output stream
+ /// @brief Writes "BLACK" to the output stream,
+ /// even if the device is not in enabled state (allowing to have a defined state during device power-off).
+ /// @note: latch-time is considered between each write
///
+ /// @param[in] numberOfWrites Write Black given number of times
/// @return Zero on success else negative
///
- virtual int writeBlack();
+ virtual int writeBlack(int numberOfBlack=1);
- // Helper to pipe device config from constructor to start()
+ ///
+ /// @brief Switch the LEDs on.
+ ///
+ /// Takes care that the device is opened and powered-on.
+ /// Depending on the configuration, the device may store its current state for later restore.
+ /// @see powerOn, storeState
+ ///
+ /// @return True, if success
+ ///
+ virtual bool switchOn();
+
+ ///
+ /// @brief Switch the LEDs off.
+ ///
+ /// Takes care that the LEDs and device are switched-off and device is closed.
+ /// Depending on the configuration, the device may be powered-off or restored to its previous state.
+ /// @see powerOff, restoreState
+ ///
+ /// @return True, if success
+ ///
+ virtual bool switchOff();
+
+ ///
+ /// @brief Power-/turn on the LED-device.
+ ///
+ /// Powers-/Turns on the LED hardware, if supported.
+ ///
+ /// @return True, if success
+ ///
+ virtual bool powerOn();
+
+ ///
+ /// @brief Power-/turn off the LED-device.
+ ///
+ /// Depending on the device's capability, the device is powered-/turned off or
+ /// an off state is simulated by writing "Black to LED" (default).
+ ///
+ /// @return True, if success
+ ///
+ virtual bool powerOff();
+
+ ///
+ /// @brief Store the device's original state.
+ ///
+ /// Save the device's state before hyperion color streaming starts allowing to restore state during switchOff().
+ ///
+ /// @return True, if success
+ ///
+ virtual bool storeState();
+
+ ///
+ /// @brief Restore the device's original state.
+ ///
+ /// Restore the device's state as before hyperion color streaming started.
+ /// This includes the on/off state of the device.
+ ///
+ /// @return True, if success
+ ///
+ virtual bool restoreState();
+
+ ///
+ /// @brief Converts an uint8_t array to hex string.
+ ///
+ /// @param data uint8_t array
+ /// @param size of the array
+ /// @param number Number of array items to be converted.
+ /// @return array as string of hex values
+ QString uint8_t_to_hex_string(const uint8_t * data, const qint64 size, qint64 number = -1) const;
+
+ /// Current device's type
+ QString _activeDeviceType;
+
+ /// Helper to pipe device configuration from constructor to start()
QJsonObject _devConfig;
- /// The common Logger instance for all LedDevices
+ /// The common Logger instance for all LED-devices
Logger * _log;
/// The buffer containing the packed RGB values
std::vector _ledBuffer;
- bool _deviceReady;
- bool _deviceInError;
+ /// Timer object which makes sure that LED data is written at a minimum rate
+ /// e.g. some devices will switch off when they do not receive data at least every 15 seconds
+ QTimer* _refreshTimer;
- QString _activeDeviceType;
+ // Device configuration parameters
+ /// Number of hardware LEDs supported by device.
unsigned int _ledCount;
unsigned int _ledRGBCount;
unsigned int _ledRGBWCount;
- /// Timer object which makes sure that led data is written at a minimum rate
- /// e.g. Adalight device will switch off when it does not receive data at least every 15 seconds
- QTimer* _refresh_timer;
- int _refresh_timer_interval;
+ /// Refresh interval in milliseconds
+ int _refreshTimerInterval_ms;
+
+ /// Time a device requires mandatorily between two writes (in milliseconds)
+ int _latchTime_ms;
+
+ /// Does the device allow restoring the original state?
+ bool _isRestoreOrigState;
+
+ /// Device, lights state before streaming via hyperion
+ QJsonObject _orignalStateValues;
+
+ // Device states
+ /// Is the device enabled?
+ bool _isEnabled;
+
+ /// Is the device initialised?
+ bool _isDeviceInitialised;
+
+ /// Is the device ready for processing?
+ bool _isDeviceReady;
+
+ /// Is the device in error state and stopped?
+ bool _isDeviceInError;
+
+ /// Is the device in the switchOff process?
+ bool _isInSwitchOff;
/// Timestamp of last write
QDateTime _lastWriteTime;
- /// Time a device requires mandatorily between two writes
- int _latchTime_ms;
-
protected slots:
- /// Write the last data to the leds again
+ ///
+ /// @brief Write the last data to the LEDs again.
///
/// @return Zero on success else negative
///
- int rewriteLeds();
+ int rewriteLEDs();
- /// Switch the leds off
- /// Writes "Black to LED" or may switch-off the LED hardware, if supported
///
- virtual int switchOff();
-
- /// Switch the leds on
- /// May switch-on the LED hardware, if supported
+ /// @brief Set device in error state
///
- virtual int switchOn();
-
- /// Set device in error state
+ /// @param[in] errorMsg The error message to be logged
///
- /// @param errorMsg The error message to be logged
- ///
- virtual void setInError( const QString& errorMsg);
+ virtual void setInError( const QString& errorMsg);
private:
- /// Start new refresh cycle
- ///
+ /// @brief Start a new refresh cycle
void startRefreshTimer();
- /// Stop refresh cycle
- ///
+ /// @brief Stop refresh cycle
void stopRefreshTimer();
+ /// Is last write refreshing enabled?
+ bool _isRefreshEnabled;
- bool _componentRegistered;
- bool _enabled;
- bool _refresh_enabled;
+ /// Order of Colors supported by the device
+ /// "RGB", "BGR", "RBG", "BRG", "GBR", "GRB"
QString _colorOrder;
/// Last LED values written
- std::vector _last_ledValues;
+ std::vector _lastLedValues;
};
+
+#endif // LEDEVICE_H
diff --git a/include/leddevice/LedDeviceFactory.h b/include/leddevice/LedDeviceFactory.h
index 5a26a8ed..ea7be292 100644
--- a/include/leddevice/LedDeviceFactory.h
+++ b/include/leddevice/LedDeviceFactory.h
@@ -1,7 +1,7 @@
+#ifndef LEDEVICEFACTORY_H
+#define LEDEVICEFACTORY_H
-#pragma once
-
-// Leddevice includes
+// LedDevice includes
#include
///
@@ -17,7 +17,9 @@ public:
/// @param deviceConfig The configuration of the led-device
///
/// @return The constructed LedDevice or nullptr if configuration is invalid. The ownership of
- /// the constructed LedDevice is tranferred to the caller
+ /// the constructed LedDevice is transferred to the caller
///
static LedDevice * construct(const QJsonObject & deviceConfig);
};
+
+#endif // LEDEVICEFACTORY_H
diff --git a/include/leddevice/LedDeviceWrapper.h b/include/leddevice/LedDeviceWrapper.h
index ec3743b6..059d53ea 100644
--- a/include/leddevice/LedDeviceWrapper.h
+++ b/include/leddevice/LedDeviceWrapper.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef LEDEVICEWRAPPER_H
+#define LEDEVICEWRAPPER_H
// util
#include
@@ -21,8 +22,8 @@ public:
explicit LedDeviceWrapper(Hyperion* hyperion);
~LedDeviceWrapper() override;
///
- /// @brief Contructs a new LedDevice, moves to thread and starts
- /// @param config With the given config
+ /// @brief Constructs a new LedDevice, moves to thread and starts
+ /// @param config With the given configuration
///
void createLedDevice(const QJsonObject& config);
@@ -33,19 +34,19 @@ public:
static const QJsonObject getLedDeviceSchemas();
///
- /// @brief add all device constrcutors to the map
+ /// @brief add all device constructors to the map
///
static int addToDeviceMap(QString name, LedDeviceCreateFuncType funcPtr);
///
- /// @brief Return all available device contructors
- /// @return device constrcutors
+ /// @brief Return all available device constructors
+ /// @return device constructors
///
static const LedDeviceRegistry& getDeviceMap();
///
- /// @brief Get the current latchtime of the ledDevice
- /// @ return latchtime in ms
+ /// @brief Get the current latch time of the ledDevice
+ /// @ return latch time in ms
///
int getLatchTime();
@@ -57,7 +58,7 @@ public:
///
/// @brief Return the last enable state
///
- const bool & enabled() { return _enabled; }
+ bool enabled();
///
/// @brief Get the current colorOrder from device
@@ -65,14 +66,14 @@ public:
const QString & getColorOrder();
///
- /// @brief Get the number of Leds from device
+ /// @brief Get the number of LEDs from device
///
unsigned int getLedCount() const;
public slots:
///
/// @brief Handle new component state request
- /// @apram component The comp from enum
+ /// @param component The comp from enum
/// @param state The new state
///
void handleComponentState(const hyperion::Components component, const bool state);
@@ -100,7 +101,7 @@ private slots:
protected:
- /// contains all available led device constrcutors
+ /// contains all available led device constructors
static LedDeviceRegistry _ledDeviceMap;
private:
@@ -117,3 +118,5 @@ private:
// the enable state
bool _enabled;
};
+
+#endif // LEDEVICEWRAPPER_H
diff --git a/include/ssdp/SSDPDiscover.h b/include/ssdp/SSDPDiscover.h
index e149a0e5..ba3adc4b 100644
--- a/include/ssdp/SSDPDiscover.h
+++ b/include/ssdp/SSDPDiscover.h
@@ -1,7 +1,12 @@
-#pragma once
+#ifndef SSDPDISCOVER_H
+#define SSDPDISCOVER_H
#include
#include
+#include
+#include
+
+#include
class QUdpSocket;
@@ -11,6 +16,23 @@ enum class searchType{
STY_JSONSERVER
};
+struct SSDPService {
+ QString cacheControl;
+ QUrl location;
+ QString server;
+ QString searchTarget;
+ QString uniqueServiceName;
+ QMap otherHeaders;
+};
+
+// Default values
+static const char DEFAULT_SEARCH_ADDRESS[] = "239.255.255.250";
+static const int DEFAULT_SEARCH_PORT = 1900;
+static const char DEFAULT_FILTER[] = ".*";
+static const char DEFAULT_FILTER_HEADER[] = "ST";
+
+constexpr std::chrono::milliseconds DEFAULT_SSDP_TIMEOUT{5000}; // timeout in ms
+
///
/// @brief Search for SSDP sessions, used by stand-alone capture binaries
///
@@ -37,6 +59,120 @@ public:
///
const QString getFirstService(const searchType &type = searchType::STY_WEBSERVER,const QString &st = "urn:hyperion-project.org:device:basic:1", const int &timeout_ms = 3000);
+ ///
+ /// @brief Discover services via ssdp.
+ ///
+ /// Records meeting the search target and filter criteria ( setSearchFilter() ) are stored in a map using the given element as a key.
+ ///
+ /// The search result can be accessed via getServicesDiscoveredJson() or getServicesDiscovered()
+ ///
+ /// Usage sample:
+ /// @code
+ ///
+ /// SSDPDiscover discover;
+ ///
+ /// discover.skipDuplicateKeys(true);
+ /// QString searchTargetFilter = "(.*)IpBridge(.*)";
+ /// discover.setSearchFilter(searchTargetFilter, "SERVER");
+ /// QString searchTarget = "upnp:rootdevice";
+ ///
+ /// if ( discover.discoverServices(searchTarget) > 0 )
+ /// deviceList = discover.getServicesDiscoveredJson();
+ ///
+ ///@endcode
+ ///
+ /// @param[in] searchTarget The ssdp discovery search target (ST)
+ /// @param[in] key Element used as key for the result map
+ ///
+ /// @return Number of service records found (meeting the search & filter criteria)
+ ///
+ int discoverServices(const QString &searchTarget="ssdp:all", const QString &key="LOCATION");
+
+ ///
+ /// @brief Get services discovered during discoverServices()
+ ///
+ /// @return Map of discovered services
+ ///
+ const QMap getServicesDiscovered () { return _services; }
+
+ ///
+ /// @brief Get services discovered during discoverServices().
+ ///
+ /// Hostname and domain are resolved from IP-address and stored in extra elements
+ ///
+ /// Sample result:
+ /// @code
+ ///
+ /// [{
+ /// "cache-control": "max-age=100",
+ /// "domain": "fritz.box",
+ /// "hostname": "ubuntu1910",
+ /// "id": "http://192.168.2.152:8081/description.xml",
+ /// "ip": "192.168.2.152",
+ /// "location": "http://192.168.2.152:8081/description.xml",
+ /// "other": { "ext": "", "host": "239.255.255.250:1900", "hue-bridgeid": "000C29FFFED8D52D"},
+ /// "port": 8081,
+ /// "server": "Linux/3.14.0 UPnP/1.0 IpBridge/1.19.0",
+ /// "st": "upnp:rootdevice",
+ /// "usn": "uuid:2f402f80-da50-11e1-9b23-000c29d8d52d::upnp:rootdevice"
+ /// }]
+ ///
+ ///@endcode
+ ///
+ /// @return Discovered services as JSON-document
+ ///
+ QJsonArray getServicesDiscoveredJson();
+
+ ///
+ /// @brief Set the ssdp discovery address (HOST)
+ ///
+ /// @param[in] IP-address used during discovery
+ ///
+ void setAddress ( const QString &address) { _ssdpAddr = QHostAddress(address); }
+
+ ///
+ /// @brief Set the ssdp discovery port (HOST)
+ ///
+ /// @param[in] port used during discovery
+ ///
+ void setPort ( quint16 port) { _ssdpPort = port; }
+
+ ///
+ /// @brief Set the ssdp discovery max wait time (MX)
+ ///
+ /// @param[in] maxWaitResponseTime
+ ///
+ void setMaxWaitResponseTime ( int maxWaitResponseTime) { _ssdpMaxWaitResponseTime = maxWaitResponseTime; }
+
+ ///
+ /// @brief Set the ssdp discovery search target (ST)
+ ///
+ /// @param[in] searchTarget
+ ///
+ void setSearchTarget ( const QString &searchTarget) { _searchTarget = searchTarget; }
+
+ ///
+ /// @brief Set the ssdp discovery search target filter
+ ///
+ /// @param[in] filter as regular expression
+ /// @param[in] filterHeader Header element the filter is applied to
+ ///
+ /// @return True, if valid regular expression
+ ///
+ bool setSearchFilter ( const QString &filter=DEFAULT_FILTER, const QString &filterHeader="ST");
+
+ ///
+ /// @brief Set the ssdp discovery search target and filter to default values
+ ///
+ void clearSearchFilter () { _filter=DEFAULT_FILTER; _filterHeader="ST"; }
+
+ ///
+ /// @brief Skip duplicate records with the same key-value
+ ///
+ /// @param[in] skip True: skip records with duplicate key-values, False: Allow duplicate key-values
+ ///
+ void skipDuplicateKeys( bool skip ) { _skipDupKeys = skip; }
+
signals:
///
/// @brief Emits whenever a new service has been found, search started with searchForService()
@@ -60,6 +196,21 @@ private:
Logger* _log;
QUdpSocket* _udpSocket;
- QString _searchTarget;
+ QHostAddress _ssdpAddr;
+ quint16 _ssdpPort;
+
+ int _ssdpMaxWaitResponseTime;
+ int _ssdpTimeout;
+
+ QMap _services;
+
QStringList _usnList;
+ QString _searchTarget;
+
+ QString _filter;
+ QString _filterHeader;
+ QRegularExpression _regExFilter;
+ bool _skipDupKeys;
};
+
+#endif // SSDPDISCOVER_H
diff --git a/include/utils/QStringUtils.h b/include/utils/QStringUtils.h
new file mode 100644
index 00000000..52d245c0
--- /dev/null
+++ b/include/utils/QStringUtils.h
@@ -0,0 +1,42 @@
+#ifndef QSTRINGUTILS_H
+#define QSTRINGUTILS_H
+
+#include
+#include
+
+namespace QStringUtils {
+
+enum class SplitBehavior {
+ KeepEmptyParts,
+ SkipEmptyParts,
+};
+
+inline QStringList split (const QString &string, const QString &sep, SplitBehavior behavior = SplitBehavior::KeepEmptyParts, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+{
+ #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
+ return behavior == SplitBehavior::SkipEmptyParts ? string.split(sep, Qt::SkipEmptyParts , cs) : string.split(sep, Qt::KeepEmptyParts , cs);
+ #else
+ return behavior == SplitBehavior::SkipEmptyParts ? string.split(sep, QString::SkipEmptyParts , cs) : string.split(sep, QString::KeepEmptyParts , cs);
+ #endif
+}
+
+inline QStringList split (const QString &string, QChar sep, SplitBehavior behavior = SplitBehavior::KeepEmptyParts, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+{
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
+ return behavior == SplitBehavior::SkipEmptyParts ? string.split(sep, Qt::SkipEmptyParts , cs) : string.split(sep, Qt::KeepEmptyParts , cs);
+#else
+ return behavior == SplitBehavior::SkipEmptyParts ? string.split(sep, QString::SkipEmptyParts , cs) : string.split(sep, QString::KeepEmptyParts , cs);
+#endif
+}
+
+inline QStringList split (const QString &string, const QRegExp &rx, SplitBehavior behavior = SplitBehavior::KeepEmptyParts)
+{
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
+ return behavior == SplitBehavior::SkipEmptyParts ? string.split(rx, Qt::SkipEmptyParts) : string.split(rx, Qt::KeepEmptyParts);
+#else
+ return behavior == SplitBehavior::SkipEmptyParts ? string.split(rx, QString::SkipEmptyParts) : string.split(rx, QString::KeepEmptyParts);
+#endif
+}
+}
+
+#endif // QSTRINGUTILS_H
diff --git a/libsrc/api/JSONRPC_schema/schema-leddevice.json b/libsrc/api/JSONRPC_schema/schema-leddevice.json
new file mode 100644
index 00000000..73da6c9e
--- /dev/null
+++ b/libsrc/api/JSONRPC_schema/schema-leddevice.json
@@ -0,0 +1,28 @@
+{
+ "type":"object",
+ "required":true,
+ "properties":{
+ "command": {
+ "type" : "string",
+ "required" : true,
+ "enum" : ["leddevice"]
+ },
+ "tan" : {
+ "type" : "integer"
+ },
+ "subcommand": {
+ "type" : "string",
+ "required" : true,
+ "enum" : ["discover","getProperties","identify"]
+ },
+ "ledDeviceType": {
+ "type" : "string",
+ "required" : true
+ },
+ "params": {
+ "type" : "object",
+ "required" : false
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/libsrc/api/JSONRPC_schema/schema.json b/libsrc/api/JSONRPC_schema/schema.json
index 6cc245fa..e13982fe 100644
--- a/libsrc/api/JSONRPC_schema/schema.json
+++ b/libsrc/api/JSONRPC_schema/schema.json
@@ -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", "instance", "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", "leddevice", "transform", "correction" , "temperature"]
}
}
}
diff --git a/libsrc/api/JSONRPC_schemas.qrc b/libsrc/api/JSONRPC_schemas.qrc
index 387d97dd..2018684f 100644
--- a/libsrc/api/JSONRPC_schemas.qrc
+++ b/libsrc/api/JSONRPC_schemas.qrc
@@ -20,6 +20,7 @@
JSONRPC_schema/schema-videomode.json
JSONRPC_schema/schema-authorize.json
JSONRPC_schema/schema-instance.json
+ JSONRPC_schema/schema-leddevice.json
JSONRPC_schema/schema-hyperion-classic.json
JSONRPC_schema/schema-hyperion-classic.json
diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp
index c9861aa6..51ac6d5a 100644
--- a/libsrc/api/JsonAPI.cpp
+++ b/libsrc/api/JsonAPI.cpp
@@ -18,6 +18,10 @@
// hyperion includes
#include
+
+#include
+#include
+
#include
#include
#include
@@ -192,6 +196,8 @@ proceed:
handleVideoModeCommand(message, command, tan);
else if (command == "instance")
handleInstanceCommand(message, command, tan);
+ else if (command == "leddevice")
+ handleLedDeviceCommand(message, command, tan);
// BEGIN | The following commands are derecated but used to ensure backward compatibility with hyperion Classic remote control
else if (command == "clearall")
@@ -1385,6 +1391,80 @@ void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &c
}
}
+void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString &command, const int tan)
+{
+ Debug(_log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ const QString &subc = message["subcommand"].toString().trimmed();
+ const QString &devType = message["ledDeviceType"].toString().trimmed();
+
+ QString full_command = command + "-" + subc;
+
+ // TODO: Validate that device type is a valid one
+/* if ( ! valid type )
+ {
+ sendErrorReply("Unknown device", full_command, tan);
+ }
+ else
+*/ {
+ if (subc == "discover")
+ {
+
+ QJsonObject config;
+ config.insert("type", devType);
+
+ // Pointer of current led device
+ LedDevice* _ledDevice;
+ _ledDevice = LedDeviceFactory::construct(config);
+
+ QJsonObject devicesDiscovered = _ledDevice->discover();
+
+ Debug(_log, "response: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ sendSuccessDataReply(QJsonDocument(devicesDiscovered), full_command, tan);
+
+ delete _ledDevice;
+ }
+ else if (subc == "getProperties")
+ {
+ const QJsonObject ¶ms = message["params"].toObject();
+
+ QJsonObject config;
+ config.insert("type", devType);
+
+ // Pointer of current led device
+ LedDevice* _ledDevice;
+ _ledDevice = LedDeviceFactory::construct(config);
+
+ QJsonObject deviceProperties = _ledDevice->getProperties(params);
+
+ Debug(_log, "response: [%s]", QString(QJsonDocument(deviceProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ sendSuccessDataReply(QJsonDocument(deviceProperties), full_command, tan);
+
+ delete _ledDevice;
+ }
+ else if (subc == "identify")
+ {
+ const QJsonObject ¶ms = message["params"].toObject();
+
+ QJsonObject config;
+ config.insert("type", devType);
+
+ // Pointer of current led device
+ LedDevice* _ledDevice;
+ _ledDevice = LedDeviceFactory::construct(config);
+
+ _ledDevice->identify(params);
+
+ sendSuccessReply(full_command, tan);
+ delete _ledDevice;
+ }
+ else
+ {
+ sendErrorReply("Unknown or missing subcommand", full_command, tan);
+ }
+ }
+}
+
void JsonAPI::handleNotImplemented()
{
sendErrorReply("Command not implemented");
diff --git a/libsrc/boblightserver/BoblightClientConnection.cpp b/libsrc/boblightserver/BoblightClientConnection.cpp
index 0336f34f..cb1d7040 100644
--- a/libsrc/boblightserver/BoblightClientConnection.cpp
+++ b/libsrc/boblightserver/BoblightClientConnection.cpp
@@ -18,6 +18,7 @@
#include
#include "HyperionConfig.h"
#include
+#include
// project includes
#include "BoblightClientConnection.h"
@@ -81,7 +82,7 @@ void BoblightClientConnection::readData()
void BoblightClientConnection::socketClosed()
{
// clear the current channel
- if (_priority != 0 && _priority >= 128 && _priority < 254)
+ if (_priority >= 128 && _priority < 254)
_hyperion->clear(_priority);
emit connectionClosed(this);
@@ -90,12 +91,7 @@ void BoblightClientConnection::socketClosed()
void BoblightClientConnection::handleMessage(const QString & message)
{
//std::cout << "boblight message: " << message.toStdString() << std::endl;
- #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- QStringList messageParts = message.split(" ", Qt::SkipEmptyParts);
- #else
- QStringList messageParts = message.split(" ", QString::SkipEmptyParts);
- #endif
-
+ QStringList messageParts = QStringUtils::split(message," ",QStringUtils::SplitBehavior::SkipEmptyParts);
if (messageParts.size() > 0)
{
if (messageParts[0] == "hello")
@@ -217,7 +213,7 @@ void BoblightClientConnection::handleMessage(const QString & message)
}
else if (messageParts[0] == "sync")
{
- if (_priority != 0 && _priority >= 128 && _priority < 254)
+ if ( _priority >= 128 && _priority < 254)
_hyperion->setInput(_priority, _ledColors); // send current color values to hyperion
return;
diff --git a/libsrc/leddevice/LedDevice.cpp b/libsrc/leddevice/LedDevice.cpp
index 3ef2e63d..bb446e39 100644
--- a/libsrc/leddevice/LedDevice.cpp
+++ b/libsrc/leddevice/LedDevice.cpp
@@ -1,5 +1,4 @@
#include
-#include
//QT include
#include
@@ -13,102 +12,115 @@
#include "hyperion/Hyperion.h"
#include
-LedDevice::LedDevice(const QJsonObject& config, QObject* parent)
+//std includes
+#include
+#include
+
+LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent)
: QObject(parent)
- , _devConfig(config)
- , _log(Logger::getInstance("LEDDEVICE"))
- , _ledBuffer(0)
- , _deviceReady(false)
- , _deviceInError(false)
- , _refresh_timer(new QTimer(this))
- , _refresh_timer_interval(0)
- , _lastWriteTime(QDateTime::currentDateTime())
- , _latchTime_ms(0)
- , _componentRegistered(false)
- , _enabled(false)
- , _refresh_enabled(false)
+ , _devConfig(deviceConfig)
+ , _log(Logger::getInstance("LEDDEVICE"))
+ , _ledBuffer(0)
+ , _refreshTimer(nullptr)
+ , _refreshTimerInterval_ms(0)
+ , _latchTime_ms(0)
+ , _isRestoreOrigState(false)
+ , _isEnabled(false)
+ , _isDeviceInitialised(false)
+ , _isDeviceReady(false)
+ , _isDeviceInError(false)
+ , _isInSwitchOff (false)
+ , _lastWriteTime(QDateTime::currentDateTime())
+ , _isRefreshEnabled (false)
{
- // setup refreshTimer
- _refresh_timer->setTimerType(Qt::PreciseTimer);
- _refresh_timer->setInterval( _refresh_timer_interval );
- connect(_refresh_timer, SIGNAL(timeout()), this, SLOT(rewriteLeds()));
+
}
LedDevice::~LedDevice()
{
- delete _refresh_timer;
+ delete _refreshTimer;
+}
+
+void LedDevice::start()
+{
+ Info(_log, "Start LedDevice '%s'.", QSTRING_CSTR(_activeDeviceType));
+
+ // setup refreshTimer
+ if ( _refreshTimer == nullptr )
+ {
+ _refreshTimer = new QTimer(this);
+ _refreshTimer->setTimerType(Qt::PreciseTimer);
+ _refreshTimer->setInterval( _refreshTimerInterval_ms );
+ connect(_refreshTimer, &QTimer::timeout, this, &LedDevice::rewriteLEDs );
+ }
+
+ close();
+
+ _isDeviceInitialised = false;
+ // General initialisation and configuration of LedDevice
+ if ( init(_devConfig) )
+ {
+ // Everything is OK -> enable device
+ _isDeviceInitialised = true;
+ setEnable(true);
+ }
+}
+
+void LedDevice::stop()
+{
+ setEnable(false);
+ this->stopRefreshTimer();
}
int LedDevice::open()
{
- int retval = -1;
- QString errortext;
- _deviceReady = false;
+ _isDeviceReady = true;
+ int retval = 0;
+
+ return retval;
+}
+
+int LedDevice::close()
+{
+ _isDeviceReady = false;
+ int retval = 0;
- // General initialisation and configuration of LedDevice
- if ( init(_devConfig) )
- {
- // Everything is OK -> enable device
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
return retval;
}
void LedDevice::setInError(const QString& errorMsg)
{
- _deviceInError = true;
- _deviceReady = false;
- _enabled = false;
+ _isDeviceInError = true;
+ _isDeviceReady = false;
+ _isEnabled = false;
this->stopRefreshTimer();
Error(_log, "Device disabled, device '%s' signals error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(errorMsg));
- emit enableStateChanged(_enabled);
-}
-
-void LedDevice::close()
-{
- switchOff();
- this->stopRefreshTimer();
+ emit enableStateChanged(_isEnabled);
}
void LedDevice::setEnable(bool enable)
{
- if ( !_deviceReady && enable )
+ bool isSwitched = false;
+ // switch off device when disabled, default: set black to LEDs when they should go off
+ if ( _isEnabled && !enable)
{
- Debug(_log, "Device '%s' was not ready! Trying to re-open.", QSTRING_CSTR(_activeDeviceType));
- if ( open() < 0 )
- {
- Error(_log, "Device '%s' cannot be enabled, as it is not ready!", QSTRING_CSTR(_activeDeviceType));
- return;
- }
- else
- {
- // Open worked
- _deviceInError = false;
- }
- }
-
- // emit signal when state changed
- if ( _enabled != enable )
- {
- emit enableStateChanged(enable);
- }
- // switch off device when disabled, default: set black to leds when they should go off
- if ( _enabled && !enable )
- {
- switchOff();
+ isSwitched = switchOff();
}
else
{
// switch on device when enabled
- if ( !_enabled && enable )
+ if ( !_isEnabled && enable)
{
- switchOn();
+ isSwitched = switchOn();
}
}
- _enabled = enable;
+
+ if ( isSwitched )
+ {
+ _isEnabled = enable;
+ emit enableStateChanged(enable);
+ }
}
void LedDevice::setActiveDeviceType(const QString& deviceType)
@@ -118,28 +130,28 @@ void LedDevice::setActiveDeviceType(const QString& deviceType)
bool LedDevice::init(const QJsonObject &deviceConfig)
{
- //Debug(_log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ Debug(_log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData() );
_colorOrder = deviceConfig["colorOrder"].toString("RGB");
- _activeDeviceType = deviceConfig["type"].toString("file").toLower();
setLedCount(static_cast( deviceConfig["currentLedCount"].toInt(1) )); // property injected to reflect real led count
- _latchTime_ms = deviceConfig["latchTime"].toInt( _latchTime_ms );
- _refresh_timer_interval = deviceConfig["rewriteTime"].toInt( _refresh_timer_interval);
+ _latchTime_ms =deviceConfig["latchTime"].toInt( _latchTime_ms );
+ _refreshTimerInterval_ms = deviceConfig["rewriteTime"].toInt( _refreshTimerInterval_ms);
- if ( _refresh_timer_interval > 0 )
+ if ( _refreshTimerInterval_ms > 0 )
{
- _refresh_enabled = true;
+ _isRefreshEnabled = true;
- if ( _refresh_timer_interval <= _latchTime_ms )
+ if (_refreshTimerInterval_ms <= _latchTime_ms )
{
int new_refresh_timer_interval = _latchTime_ms + 10;
- Warning(_log, "latchTime(%d) is bigger/equal rewriteTime(%d), set rewriteTime to %dms", _latchTime_ms, _refresh_timer_interval, new_refresh_timer_interval);
- _refresh_timer_interval = new_refresh_timer_interval;
+ Warning(_log, "latchTime(%d) is bigger/equal rewriteTime(%d), set rewriteTime to %dms", _latchTime_ms, _refreshTimerInterval_ms, new_refresh_timer_interval);
+ _refreshTimerInterval_ms = new_refresh_timer_interval;
+ _refreshTimer->setInterval( _refreshTimerInterval_ms );
}
- //Debug(_log, "Refresh interval = %dms",_refresh_timer_interval );
- _refresh_timer->setInterval( _refresh_timer_interval );
+ Debug(_log, "Refresh interval = %dms",_refreshTimerInterval_ms );
+ _refreshTimer->setInterval( _refreshTimerInterval_ms );
_lastWriteTime = QDateTime::currentDateTime();
@@ -150,21 +162,21 @@ bool LedDevice::init(const QJsonObject &deviceConfig)
void LedDevice::startRefreshTimer()
{
- if ( _deviceReady )
+ if ( _isDeviceReady && _isEnabled )
{
- _refresh_timer->start();
+ _refreshTimer->start();
}
}
void LedDevice::stopRefreshTimer()
{
- _refresh_timer->stop();
+ _refreshTimer->stop();
}
int LedDevice::updateLeds(const std::vector& ledValues)
{
int retval = 0;
- if ( !_deviceReady || _deviceInError )
+ if ( !isEnabled() || !_isDeviceReady || _isDeviceInError )
{
//std::cout << "LedDevice::updateLeds(), LedDevice NOT ready!" << std::endl;
return -1;
@@ -179,16 +191,16 @@ int LedDevice::updateLeds(const std::vector& ledValues)
_lastWriteTime = QDateTime::currentDateTime();
// if device requires refreshing, save Led-Values and restart the timer
- if ( _refresh_enabled )
+ if ( _isRefreshEnabled && _isEnabled )
{
this->startRefreshTimer();
- _last_ledValues = ledValues;
+ _lastLedValues = ledValues;
}
}
else
{
//std::cout << "LedDevice::updateLeds(), Skip write. elapsedTime (" << elapsedTimeMs << ") ms < _latchTime_ms (" << _latchTime_ms << ") ms" << std::endl;
- if ( _refresh_enabled )
+ if ( _isRefreshEnabled )
{
//Stop timer to allow for next non-refresh update
this->stopRefreshTimer();
@@ -198,30 +210,187 @@ int LedDevice::updateLeds(const std::vector& ledValues)
return retval;
}
-int LedDevice::writeBlack()
+int LedDevice::rewriteLEDs()
{
- return _deviceReady ? updateLeds(std::vector(static_cast(_ledCount), ColorRgb::BLACK )) : -1;
+ int retval = -1;
+
+ if ( _isDeviceReady && _isEnabled )
+ {
+// qint64 elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime());
+// std::cout << "LedDevice::rewriteLEDs(): Rewrite LEDs now, elapsedTime [" << elapsedTimeMs << "] ms" << std::endl;
+// //:TESTING: Inject "white" output records to differentiate from normal writes
+// _lastLedValues.clear();
+// _lastLedValues.resize(static_cast(_ledCount), ColorRgb::WHITE);
+// printLedValues(_lastLedValues);
+// //:TESTING:
+
+ retval = write(_lastLedValues);
+ _lastWriteTime = QDateTime::currentDateTime();
+ }
+ else
+ {
+ // If Device is not ready stop timer
+ this->stopRefreshTimer();
+ }
+ return retval;
}
-int LedDevice::switchOff()
+int LedDevice::writeBlack(int numberOfBlack)
{
- // Stop refresh timer to ensure that "write Black" is executed
- this->stopRefreshTimer();
+ int rc = -1;
- if ( _latchTime_ms > 0 )
+ for (int i = 0; i < numberOfBlack; i++)
{
- // Wait latchtime before writing black
- QEventLoop loop;
- QTimer::singleShot( _latchTime_ms, &loop, SLOT( quit() ) );
- loop.exec();
+ if ( _latchTime_ms > 0 )
+ {
+ // Wait latch time before writing black
+ QEventLoop loop;
+ QTimer::singleShot( _latchTime_ms, &loop, SLOT( quit() ) );
+ loop.exec();
+ }
+ rc = write(std::vector(static_cast(_ledCount), ColorRgb::BLACK ));
}
- int rc = writeBlack();
return rc;
}
-int LedDevice::switchOn()
+bool LedDevice::switchOn()
{
- return 0;
+ bool rc = false;
+ if ( _isDeviceInitialised && ! _isDeviceReady && ! _isEnabled )
+ {
+ _isDeviceInError = false;
+ if ( open() < 0 )
+ {
+ rc = false;
+ }
+ else
+ {
+ storeState();
+
+ if ( powerOn() )
+ {
+ _isEnabled = true;
+ rc = true;
+ }
+ }
+ }
+ return rc;
+}
+
+bool LedDevice::switchOff()
+{
+ bool rc = false;
+
+ if ( _isDeviceInitialised )
+ {
+ // Disable device to ensure no standard Led updates are written/processed
+ _isEnabled = false;
+ _isInSwitchOff = true;
+
+ this->stopRefreshTimer();
+
+ rc = true;
+
+ if ( _isDeviceReady )
+ {
+ if ( _isRestoreOrigState )
+ {
+ //Restore devices state
+ restoreState();
+ }
+ else
+ {
+ powerOff();
+ }
+
+ }
+ if ( close() < 0 )
+ {
+ rc = false;
+ }
+ }
+ return rc;
+}
+
+
+bool LedDevice::powerOff()
+{
+ bool rc = false;
+
+ // Simulate power-off by writing a final "Black" to have a defined outcome
+ if ( writeBlack() >= 0 )
+ {
+ rc = true;
+ }
+ return rc;
+}
+
+bool LedDevice::powerOn()
+{
+ bool rc = true;
+ return rc;
+}
+
+bool LedDevice::storeState()
+{
+ bool rc = true;
+
+ if ( _isRestoreOrigState )
+ {
+ // Save device's original state
+ // _originalStateValues = get device's state;
+ // store original power on/off state, if available
+ }
+ return rc;
+}
+
+bool LedDevice::restoreState()
+{
+ bool rc = true;
+
+ if ( _isRestoreOrigState )
+ {
+ // Restore device's original state
+ // update device using _originalStateValues
+ // update original power on/off state, if supported
+ }
+ return rc;
+}
+
+QJsonObject LedDevice::discover()
+{
+ QJsonObject devicesDiscovered;
+
+ devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
+
+ QJsonArray deviceList;
+ devicesDiscovered.insert("devices", deviceList);
+
+ Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ return devicesDiscovered;
+}
+
+QString LedDevice::discoverFirst()
+{
+ QString deviceDiscovered;
+
+ Debug(_log, "deviceDiscovered: [%s]", QSTRING_CSTR(deviceDiscovered) );
+ return deviceDiscovered;
+}
+
+
+QJsonObject LedDevice::getProperties(const QJsonObject& params)
+{
+ Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ QJsonObject properties;
+
+ QJsonObject deviceProperties;
+ properties.insert("properties", deviceProperties);
+
+ Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ return properties;
}
void LedDevice::setLedCount(unsigned int ledCount)
@@ -237,31 +406,6 @@ void LedDevice::setLatchTime( int latchTime_ms )
Debug(_log, "LatchTime updated to %dms", this->getLatchTime());
}
-int LedDevice::rewriteLeds()
-{
- int retval = -1;
-
- if ( _deviceReady )
- {
-// qint64 elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime());
-// std::cout << "LedDevice::rewriteLEDs(): Rewrite LEDs now, elapsedTime [" << elapsedTimeMs << "] ms" << std::endl;
-// //:TESTING: Inject "white" output records to differentiate from normal writes
-// _last_ledValues.clear();
-// _last_ledValues.resize(static_cast(_ledCount), ColorRgb::WHITE);
-// printLedValues(_last_ledValues);
- //:TESTING:
-
- retval = write(_last_ledValues);
- _lastWriteTime = QDateTime::currentDateTime();
- }
- else
- {
- // If Device is not ready stop timer
- this->stopRefreshTimer();
- }
- return retval;
-}
-
void LedDevice::printLedValues(const std::vector& ledValues)
{
std::cout << "LedValues [" << ledValues.size() <<"] [";
@@ -271,3 +415,18 @@ void LedDevice::printLedValues(const std::vector& ledValues)
}
std::cout << "]" << std::endl;
}
+
+QString LedDevice::uint8_t_to_hex_string(const uint8_t * data, const qint64 size, qint64 number) const
+{
+ if ( number <= 0 || number > size)
+ {
+ number = size;
+ }
+
+ QByteArray bytes (reinterpret_cast(data), number);
+ #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
+ return bytes.toHex(':');
+ #else
+ return bytes.toHex();
+ #endif
+}
diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp
index 017771ef..0b0690ac 100644
--- a/libsrc/leddevice/LedDeviceFactory.cpp
+++ b/libsrc/leddevice/LedDeviceFactory.cpp
@@ -18,7 +18,6 @@ LedDevice * LedDeviceFactory::construct(const QJsonObject & deviceConfig)
{
Logger * log = Logger::getInstance("LEDDEVICE");
QJsonDocument config(deviceConfig);
- QString ss(config.toJson(QJsonDocument::Indented));
QString type = deviceConfig["type"].toString("UNSPECIFIED").toLower();
@@ -31,7 +30,6 @@ LedDevice * LedDeviceFactory::construct(const QJsonObject & deviceConfig)
if (dev.first == type)
{
device = dev.second(deviceConfig);
- Info(log,"LedDevice '%s' found.", QSTRING_CSTR(dev.first));
break;
}
}
diff --git a/libsrc/leddevice/LedDeviceSchemas.qrc b/libsrc/leddevice/LedDeviceSchemas.qrc
index f59d19f5..f90c354f 100644
--- a/libsrc/leddevice/LedDeviceSchemas.qrc
+++ b/libsrc/leddevice/LedDeviceSchemas.qrc
@@ -33,5 +33,7 @@
schemas/schema-ws281x.json
schemas/schema-karate.json
schemas/schema-nanoleaf.json
+ schemas/schema-wled.json
+ schemas/schema-yeelight.json
diff --git a/libsrc/leddevice/LedDeviceTemplate.cpp b/libsrc/leddevice/LedDeviceTemplate.cpp
index 3d6567e1..ddfe1ebc 100644
--- a/libsrc/leddevice/LedDeviceTemplate.cpp
+++ b/libsrc/leddevice/LedDeviceTemplate.cpp
@@ -4,7 +4,9 @@ LedDeviceTemplate::LedDeviceTemplate(const QJsonObject &deviceConfig)
: LedDevice()
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceTemplate::construct(const QJsonObject &deviceConfig)
@@ -14,18 +16,25 @@ LedDevice* LedDeviceTemplate::construct(const QJsonObject &deviceConfig)
bool LedDeviceTemplate::init(const QJsonObject &deviceConfig)
{
- bool isInitOK = LedDevice::init(deviceConfig);
+ bool isInitOK = false;
- // Initiatiale LedDevice configuration and execution environment
- // ...
- if ( 0 /*Error during init*/)
+ // Initialise sub-class
+ if ( LedDevice::init(deviceConfig) )
{
- //Build an errortext, illustrative
- QString errortext = QString ("Error message: %1").arg("errno/text");
- this->setInError(errortext);
- isInitOK = false;
+ // Initialise LedDevice configuration and execution environment
+ // ...
+ if ( 0 /*Error during init*/)
+ {
+ //Build an errortext, illustrative
+ QString errortext = QString ("Error message: %1").arg("errno/text");
+ this->setInError(errortext);
+ isInitOK = false;
+ }
+ else
+ {
+ isInitOK = true;
+ }
}
-
return isInitOK;
}
@@ -33,42 +42,45 @@ int LedDeviceTemplate::open()
{
int retval = -1;
QString errortext;
- _deviceReady = false;
+ _isDeviceReady = false;
- // General initialisation and configuration of LedDevice
- if ( init(_devConfig) )
+ // Try to open the LedDevice
+ //...
+
+ if ( false /*If opening failed*/ )
{
- // Open/Start LedDevice based on configuration
- //...
+ //Build an errortext, illustrative
+ errortext = QString ("Failed to xxx. Error message: %1").arg("errno/text");
+ }
+ else
+ {
+ // Everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
+ }
- if ( false /*If opening failed*/ )
- {
- //Build an errortext, illustrative
- errortext = QString ("Failed to xxx. Error message: %1").arg("errno/text");
- }
- else
- {
- // Everything is OK -> enable device
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
-
- // On error/exceptions, set LedDevice in error
- if ( retval < 0 )
- {
- this->setInError( errortext );
- }
+ // On error/exceptions, set LedDevice in error
+ if ( retval < 0 )
+ {
+ this->setInError( errortext );
}
return retval;
}
-void LedDeviceTemplate::close()
+int LedDeviceTemplate::close()
{
- LedDevice::close();
-
- // LedDevice specific closing activites
+ // LedDevice specific closing activities
//...
+ int retval = 0;
+ _isDeviceReady = false;
+
+ // Test, if device requires closing
+ if ( true /*If device is still open*/ )
+ {
+ // Close device
+ // Everything is OK -> device is closed
+ }
+ return retval;
}
int LedDeviceTemplate::write(const std::vector & ledValues)
diff --git a/libsrc/leddevice/LedDeviceTemplate.h b/libsrc/leddevice/LedDeviceTemplate.h
index 65954a80..6096c30c 100644
--- a/libsrc/leddevice/LedDeviceTemplate.h
+++ b/libsrc/leddevice/LedDeviceTemplate.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef LEDEVICETEMPLATE_H
+#define LEDEVICETEMPLATE_H
// LedDevice includes
#include
@@ -10,46 +11,56 @@
class LedDeviceTemplate : public LedDevice
{
public:
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs a specific LED-device
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceTemplate(const QJsonObject &deviceConfig);
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
static LedDevice* construct(const QJsonObject &deviceConfig);
+protected:
+
///
- /// Sets configuration
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
///
- /// @param deviceConfig the json device config
- /// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
-public slots:
///
- /// Closes the output device.
- /// Includes switching-off the device and stopping refreshes
+ /// @brief Opens the output device.
///
- virtual void close() override;
-
-protected:
- ///
- /// Opens and initiatialises the output device
- ///
- /// @return Zero on succes (i.e. device is ready and enabled) else negative
+ /// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
- /// Writes the led color values to the led-device
///
- /// @param ledValues The color-value per led
- /// @return Zero on succes else negative
- //////
+ /// @brief Closes the output device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
virtual int write(const std::vector & ledValues) override;
private:
-
};
+
+#endif // LEDEVICETEMPLATE_H
diff --git a/libsrc/leddevice/LedDeviceWrapper.cpp b/libsrc/leddevice/LedDeviceWrapper.cpp
index 0890c120..e5bd76d1 100644
--- a/libsrc/leddevice/LedDeviceWrapper.cpp
+++ b/libsrc/leddevice/LedDeviceWrapper.cpp
@@ -22,10 +22,10 @@ LedDeviceWrapper::LedDeviceWrapper(Hyperion* hyperion)
, _ledDevice(nullptr)
, _enabled(false)
{
- // prepare the device constrcutor map
+ // prepare the device constructor map
#define REGISTER(className) LedDeviceWrapper::addToDeviceMap(QString(#className).toLower(), LedDevice##className::construct);
- // the REGISTER() calls are autogenerated by cmake.
+ // the REGISTER() calls are auto-generated by cmake.
#include "LedDevice_register.cpp"
#undef REGISTER
@@ -59,7 +59,7 @@ void LedDeviceWrapper::createLedDevice(const QJsonObject& config)
connect(this, &LedDeviceWrapper::updateLeds, _ledDevice, &LedDevice::updateLeds, Qt::QueuedConnection);
connect(this, &LedDeviceWrapper::setEnable, _ledDevice, &LedDevice::setEnable);
- connect(this, &LedDeviceWrapper::closeLedDevice, _ledDevice, &LedDevice::close, Qt::BlockingQueuedConnection);
+ connect(this, &LedDeviceWrapper::closeLedDevice, _ledDevice, &LedDevice::stop, Qt::BlockingQueuedConnection);
connect(_ledDevice, &LedDevice::enableStateChanged, this, &LedDeviceWrapper::handleInternalEnableState, Qt::QueuedConnection);
@@ -72,7 +72,7 @@ const QJsonObject LedDeviceWrapper::getLedDeviceSchemas()
// make sure the resources are loaded (they may be left out after static linking)
Q_INIT_RESOURCE(LedDeviceSchemas);
- // read the json schema from the resource
+ // read the JSON schema from the resource
QDir d(":/leddevices/");
QStringList l = d.entryList();
QJsonObject result, schemaJson;
@@ -91,7 +91,7 @@ const QJsonObject LedDeviceWrapper::getLedDeviceSchemas()
QJsonObject schema;
if(!JsonUtils::parse(schemaPath, data, schema, Logger::getInstance("LedDevice")))
{
- throw std::runtime_error("ERROR: Json schema wrong of file: " + item.toStdString());
+ throw std::runtime_error("ERROR: JSON schema wrong of file: " + item.toStdString());
}
schemaJson = schema;
@@ -134,6 +134,11 @@ unsigned int LedDeviceWrapper::getLedCount() const
return _ledDevice->getLedCount();
}
+bool LedDeviceWrapper::enabled()
+{
+ return _enabled;
+}
+
void LedDeviceWrapper::handleComponentState(const hyperion::Components component, const bool state)
{
if(component == hyperion::COMP_LEDDEVICE)
@@ -155,7 +160,7 @@ void LedDeviceWrapper::handleInternalEnableState(bool newState)
void LedDeviceWrapper::stopDeviceThread()
{
- // turns the leds off & stop refresh timers
+ // turns the LEDs off & stop refresh timers
emit closeLedDevice();
std::cout << "[hyperiond LedDeviceWrapper] LedDevice \'" << QSTRING_CSTR(_ledDevice->getActiveDeviceType()) << "\' closed" << std::endl;
diff --git a/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.cpp b/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.cpp
index 833d4d2f..532dddd6 100644
--- a/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.cpp
+++ b/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.cpp
@@ -5,11 +5,12 @@
// Local Hyperion includes
#include "LedDeviceHyperionUsbasp.h"
-// Static constants which define the Hyperion Usbasp device
-uint16_t LedDeviceHyperionUsbasp::_usbVendorId = 0x16c0;
-uint16_t LedDeviceHyperionUsbasp::_usbProductId = 0x05dc;
-QString LedDeviceHyperionUsbasp::_usbProductDescription = "Hyperion led controller";
-
+// Constants which define the Hyperion USBasp device
+namespace {
+uint16_t _usbVendorId = 0x16c0;
+uint16_t _usbProductId = 0x05dc;
+QString _usbProductDescription = "Hyperion led controller";
+}
LedDeviceHyperionUsbasp::LedDeviceHyperionUsbasp(const QJsonObject &deviceConfig)
: LedDevice()
@@ -17,11 +18,17 @@ LedDeviceHyperionUsbasp::LedDeviceHyperionUsbasp(const QJsonObject &deviceConfig
, _deviceHandle(nullptr)
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDeviceHyperionUsbasp::~LedDeviceHyperionUsbasp()
{
+ if (_libusbContext != nullptr)
+ {
+ libusb_exit(_libusbContext);
+ }
}
LedDevice* LedDeviceHyperionUsbasp::construct(const QJsonObject &deviceConfig)
@@ -31,18 +38,68 @@ LedDevice* LedDeviceHyperionUsbasp::construct(const QJsonObject &deviceConfig)
bool LedDeviceHyperionUsbasp::init(const QJsonObject &deviceConfig)
{
- bool isInitOK = LedDevice::init(deviceConfig);
+ bool isInitOK = false;
- QString ledType = deviceConfig["ledType"].toString("ws2801");
- if (ledType != "ws2801" && ledType != "ws2812")
+ // Initialise sub-class
+ if ( LedDevice::init(deviceConfig) )
{
- QString errortext = QString ("Invalid ledType; must be 'ws2801' or 'ws2812'.");
- this->setInError(errortext);
- isInitOK = false;
- }
- else
- {
- _writeLedsCommand = (ledType == "ws2801") ? CMD_WRITE_WS2801 : CMD_WRITE_WS2812;
+ QString ledType = deviceConfig["ledType"].toString("ws2801");
+ if (ledType != "ws2801" && ledType != "ws2812")
+ {
+ QString errortext = QString ("Invalid ledType; must be 'ws2801' or 'ws2812'.");
+ this->setInError(errortext);
+ isInitOK = false;
+ }
+ else
+ {
+ _writeLedsCommand = (ledType == "ws2801") ? CMD_WRITE_WS2801 : CMD_WRITE_WS2812;
+
+ int error;
+ // initialize the USB context
+ if ( (error = libusb_init(&_libusbContext)) != LIBUSB_SUCCESS )
+ {
+ _libusbContext = nullptr;
+
+ QString errortext = QString ("Error while initializing USB context(%1):%2").arg(error).arg(libusb_error_name(error));
+ this->setInError(errortext);
+ isInitOK = false;
+ }
+ else
+ {
+ Debug(_log, "USB context initialized");
+ //libusb_set_debug(_libusbContext, 3);
+
+ // retrieve the list of USB devices
+ libusb_device ** deviceList;
+ ssize_t deviceCount = libusb_get_device_list(_libusbContext, &deviceList);
+
+ // iterate the list of devices
+ for (ssize_t i = 0 ; i < deviceCount; ++i)
+ {
+ // try to open and initialize the device
+ if ( testAndOpen(deviceList[i]) == 0 )
+ {
+ _device = deviceList[i];
+ // a device was successfully opened. break from list
+ break;
+ }
+ }
+
+ // free the device list
+ libusb_free_device_list(deviceList, 1);
+
+ if (_deviceHandle == nullptr)
+ {
+ QString errortext;
+ errortext = QString ("No %1 has been found").arg( _usbProductDescription);
+ this->setInError( errortext );
+ }
+ else
+ {
+ isInitOK = true;
+ }
+ }
+ }
}
return isInitOK;
@@ -51,73 +108,29 @@ bool LedDeviceHyperionUsbasp::init(const QJsonObject &deviceConfig)
int LedDeviceHyperionUsbasp::open()
{
int retval = -1;
- QString errortext;
- _deviceReady = false;
+ _isDeviceReady = false;
- // General initialisation and configuration of LedDevice
- if ( init(_devConfig) )
+ if ( libusb_open(_device, &_deviceHandle) != LIBUSB_SUCCESS )
{
- int error;
-
- // initialize the usb context
- if ((error = libusb_init(&_libusbContext)) != LIBUSB_SUCCESS)
- {
- //Error(_log, "Error while initializing USB context(%d):%s", error, libusb_error_name(error));
- errortext = QString ("Error while initializing USB context(%1):%2").arg( error).arg(libusb_error_name(error));
- _libusbContext = nullptr;
- }
- else
- {
- //libusb_set_debug(_libusbContext, 3);
- Debug(_log, "USB context initialized");
-
- // retrieve the list of usb devices
- libusb_device ** deviceList;
- ssize_t deviceCount = libusb_get_device_list(_libusbContext, &deviceList);
-
- // iterate the list of devices
- for (ssize_t i = 0 ; i < deviceCount; ++i)
- {
- // try to open and initialize the device
- error = testAndOpen(deviceList[i]);
-
- if (error == 0)
- {
- // a device was sucessfully opened. break from list
- break;
- }
- }
-
- // free the device list
- libusb_free_device_list(deviceList, 1);
-
- if (_deviceHandle == nullptr)
- {
- //Error(_log, "No %s has been found", QSTRING_CSTR(_usbProductDescription));
- errortext = QString ("No %1 has been found").arg( _usbProductDescription);
- }
- else
- {
- // Everything is OK -> enable device
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
- }
- // On error/exceptions, set LedDevice in error
- if ( retval < 0 )
- {
- this->setInError( errortext );
- }
+ QString errortext = QString ("Failed to open [%1]").arg(_usbProductDescription);
+ this->setInError(errortext);
}
+ else
+ {
+ // Everything is OK -> enable device
+ _isDeviceReady = true;
+ retval = 0;
+ }
+
return retval;
}
-void LedDeviceHyperionUsbasp::close()
+int LedDeviceHyperionUsbasp::close()
{
- LedDevice::close();
+ int retval = 0;
+ _isDeviceReady = false;
- // LedDevice specific closing activites
+ // LedDevice specific closing activities
if (_deviceHandle != nullptr)
{
libusb_release_interface(_deviceHandle, 0);
@@ -126,12 +139,7 @@ void LedDeviceHyperionUsbasp::close()
_deviceHandle = nullptr;
}
-
- if (_libusbContext != nullptr)
- {
- libusb_exit(_libusbContext);
- _libusbContext = nullptr;
- }
+ return retval;
}
int LedDeviceHyperionUsbasp::testAndOpen(libusb_device * device)
@@ -176,7 +184,7 @@ int LedDeviceHyperionUsbasp::write(const std::vector &ledValues)
{
int nbytes = libusb_control_transfer(
_deviceHandle, // device handle
- LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, // request type
+ static_cast( LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT ), // request type
_writeLedsCommand, // request
0, // value
0, // index
@@ -184,7 +192,7 @@ int LedDeviceHyperionUsbasp::write(const std::vector &ledValues)
(3*_ledCount) & 0xffff, // length
5000); // timeout
- // Disabling interupts for a little while on the device results in a PIPE error. All seems to keep functioning though...
+ // Disabling interrupts for a little while on the device results in a PIPE error. All seems to keep functioning though...
if(nbytes < 0 && nbytes != LIBUSB_ERROR_PIPE)
{
Error(_log, "Error while writing data to %s (%s)", QSTRING_CSTR(_usbProductDescription), libusb_error_name(nbytes));
diff --git a/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.h b/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.h
index 47a8ca1d..83010098 100644
--- a/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.h
+++ b/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef LEDEVICEHYPERIONUSBASP_H
+#define LEDEVICEHYPERIONUSBASP_H
// stl includes
#include
@@ -32,7 +33,7 @@ public:
///
/// Sets configuration
///
- /// @param deviceConfig the json device config
+ /// @para#endif // LEDEVICETEMPLATE_Hm deviceConfig the json device config
/// @return true if success
bool init(const QJsonObject &deviceConfig) override;
@@ -49,7 +50,7 @@ public slots:
/// Closes the output device.
/// Includes switching-off the device and stopping refreshes
///
- virtual void close() override;
+ virtual int close() override;
protected:
///
@@ -85,11 +86,11 @@ protected:
/// libusb context
libusb_context * _libusbContext;
+ /// libusb device
+ libusb_device * _device;
+
/// libusb device handle
libusb_device_handle * _deviceHandle;
-
- /// Usb device identifiers
- static uint16_t _usbVendorId;
- static uint16_t _usbProductId;
- static QString _usbProductDescription;
};
+
+#endif // LEDEVICEHYPERIONUSBASP_H
diff --git a/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp b/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp
index df9cfe64..9e2abdb0 100644
--- a/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp
+++ b/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp
@@ -32,20 +32,6 @@ enum DATA_VERSION_INDEXES{
INDEX_FW_VER_MINOR
};
-LedDeviceLightpack::LedDeviceLightpack(const QString & serialNumber)
- : LedDevice()
- , _libusbContext(nullptr)
- , _deviceHandle(nullptr)
- , _busNumber(-1)
- , _addressNumber(-1)
- , _serialNumber(serialNumber)
- , _firmwareVersion({-1,-1})
- , _bitsPerChannel(-1)
- , _hwLedCount(-1)
-{
- _deviceReady = false;
-}
-
LedDeviceLightpack::LedDeviceLightpack(const QJsonObject &deviceConfig)
: LedDevice()
, _libusbContext(nullptr)
@@ -55,13 +41,20 @@ LedDeviceLightpack::LedDeviceLightpack(const QJsonObject &deviceConfig)
, _firmwareVersion({-1,-1})
, _bitsPerChannel(-1)
, _hwLedCount(-1)
+ ,_isOpen(false)
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDeviceLightpack::~LedDeviceLightpack()
{
+ if (_libusbContext != nullptr)
+ {
+ libusb_exit(_libusbContext);
+ }
}
LedDevice* LedDeviceLightpack::construct(const QJsonObject &deviceConfig)
@@ -71,36 +64,29 @@ LedDevice* LedDeviceLightpack::construct(const QJsonObject &deviceConfig)
bool LedDeviceLightpack::init(const QJsonObject &deviceConfig)
{
- bool isInitOK = LedDevice::init(deviceConfig);
- _serialNumber = deviceConfig["output"].toString("");
+ bool isInitOK = false;
- return isInitOK;
-}
-
-int LedDeviceLightpack::open()
-{
- int retval = -1;
- QString errortext;
- _deviceReady = false;
-
- // General initialisation and configuration of LedDevice
- if ( init(_devConfig) )
+ // Initialise sub-class
+ if ( LedDevice::init(deviceConfig) )
{
- int error;
+ _serialNumber = deviceConfig["serial"].toString("");
- // initialize the usb context
- if ((error = libusb_init(&_libusbContext)) != LIBUSB_SUCCESS)
+ int error;
+ // initialize the USB context
+ if ( (error = libusb_init(&_libusbContext)) != LIBUSB_SUCCESS )
{
- //Error(_log, "Error while initializing USB context(%d): %s", error, libusb_error_name(error));
- errortext = QString ("Error while initializing USB context(%1):%2").arg( error).arg(libusb_error_name(error));
_libusbContext = nullptr;
+
+ QString errortext = QString ("Error while initializing USB context(%1):%2").arg(error).arg(libusb_error_name(error));
+ this->setInError(errortext);
+ isInitOK = false;
}
else
{
- //libusb_set_debug(_libusbContext, 3);
Debug(_log, "USB context initialized");
+ //libusb_set_debug(_libusbContext, 3);
- // retrieve the list of usb devices
+ // retrieve the list of USB devices
libusb_device ** deviceList;
ssize_t deviceCount = libusb_get_device_list(_libusbContext, &deviceList);
@@ -108,11 +94,10 @@ int LedDeviceLightpack::open()
for (ssize_t i = 0 ; i < deviceCount; ++i)
{
// try to open and initialize the device
- error = testAndOpen(deviceList[i], _serialNumber);
-
- if (error == 0)
+ if (testAndOpen(deviceList[i], _serialNumber) == 0)
{
- // a device was sucessfully opened. break from list
+ _device = deviceList[i];
+ // a device was successfully opened. break from list
break;
}
}
@@ -122,53 +107,62 @@ int LedDeviceLightpack::open()
if (_deviceHandle == nullptr)
{
+ QString errortext;
if (_serialNumber.isEmpty())
{
- //Warning(_log, "No Lightpack device has been found");
errortext = QString ("No Lightpack devices were found");
}
else
{
- //Error(_log,"No Lightpack device has been found with serial %", QSTRING_CSTR(_serialNumber));
errortext = QString ("No Lightpack device has been found with serial %1").arg( _serialNumber);
}
+ this->setInError( errortext );
}
else
{
- // Everything is OK -> enable device
- _deviceReady = true;
- setEnable(true);
- retval = 0;
+ isInitOK = true;
}
}
- // On error/exceptions, set LedDevice in error
- if ( retval < 0 )
- {
- this->setInError( errortext );
- }
}
+ return isInitOK;
+}
+
+int LedDeviceLightpack::open()
+{
+ int retval = -1;
+ _isDeviceReady = false;
+
+ if ( libusb_open(_device, &_deviceHandle) != LIBUSB_SUCCESS )
+ {
+ QString errortext = QString ("Failed to open [%1]").arg(_serialNumber);
+ this->setInError(errortext);
+ }
+ else
+ {
+ // Everything is OK -> enable device
+ _isDeviceReady = true;
+ retval = 0;
+ }
+
return retval;
}
-void LedDeviceLightpack::close()
+int LedDeviceLightpack::close()
{
- LedDevice::close();
+ int retval = 0;
+ _isDeviceReady = false;
- // LedDevice specific closing activites
+ // LedDevice specific closing activities
if (_deviceHandle != nullptr)
{
+ _isOpen = false;
libusb_release_interface(_deviceHandle, LIGHTPACK_INTERFACE);
libusb_attach_kernel_driver(_deviceHandle, LIGHTPACK_INTERFACE);
libusb_close(_deviceHandle);
_deviceHandle = nullptr;
}
-
- if (_libusbContext != nullptr)
- {
- libusb_exit(_libusbContext);
- _libusbContext = nullptr;
- }
+ return retval;
}
int LedDeviceLightpack::testAndOpen(libusb_device * device, const QString & requestedSerialNumber)
@@ -184,7 +178,7 @@ int LedDeviceLightpack::testAndOpen(libusb_device * device, const QString & requ
if ((deviceDescriptor.idVendor == USB_VENDOR_ID && deviceDescriptor.idProduct == USB_PRODUCT_ID) ||
(deviceDescriptor.idVendor == USB_OLD_VENDOR_ID && deviceDescriptor.idProduct == USB_OLD_PRODUCT_ID))
{
- Info(_log, "Found a lightpack device. Retrieving more information...");
+ Info(_log, "Found a Lightpack device. Retrieving more information...");
// get the hardware address
int busNumber = libusb_get_bus_number(device);
@@ -226,7 +220,7 @@ int LedDeviceLightpack::testAndOpen(libusb_device * device, const QString & requ
uint8_t buffer[256];
error = libusb_control_transfer(
_deviceHandle,
- LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+ static_cast( LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE),
0x01,
0x0100,
0,
@@ -289,19 +283,19 @@ int LedDeviceLightpack::testAndOpen(libusb_device * device, const QString & requ
int LedDeviceLightpack::write(const std::vector &ledValues)
{
- return write(ledValues.data(), ledValues.size());
+ return write(ledValues.data(), static_cast(ledValues.size()));
}
int LedDeviceLightpack::write(const ColorRgb * ledValues, int size)
{
- int count = qMin(_hwLedCount, static_cast( _ledCount));
+ int count = qMin(_hwLedCount, static_cast( size ));
for (int i = 0; i < count ; ++i)
{
const ColorRgb & color = ledValues[i];
- // copy the most significant bits of the rgb values to the first three bytes
- // offset 1 to accomodate for the command byte
+ // copy the most significant bits of the RGB values to the first three bytes
+ // offset 1 to accommodate for the command byte
_ledBuffer[6*i+1] = color.red;
_ledBuffer[6*i+2] = color.green;
_ledBuffer[6*i+3] = color.blue;
@@ -315,14 +309,13 @@ int LedDeviceLightpack::write(const ColorRgb * ledValues, int size)
return error >= 0 ? 0 : error;
}
-int LedDeviceLightpack::switchOff()
+bool LedDeviceLightpack::powerOff()
{
- int rc = LedDevice::switchOff();
- if ( _deviceReady )
- {
- unsigned char buf[1] = {CMD_OFF_ALL};
- rc = writeBytes(buf, sizeof(buf)) == sizeof(buf);
- }
+ bool rc = false;
+
+ unsigned char buf[1] = {CMD_OFF_ALL};
+ rc = writeBytes(buf, sizeof(buf)) == sizeof(buf);
+
return rc;
}
@@ -338,7 +331,7 @@ int LedDeviceLightpack::writeBytes(uint8_t *data, int size)
// std::cout << std::endl;
int error = libusb_control_transfer(_deviceHandle,
- LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+ static_cast( LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE ),
0x09,
(2 << 8),
0x00,
@@ -356,7 +349,13 @@ int LedDeviceLightpack::writeBytes(uint8_t *data, int size)
int LedDeviceLightpack::disableSmoothing()
{
unsigned char buf[2] = {CMD_SET_SMOOTH_SLOWDOWN, 0};
- return writeBytes(buf, sizeof(buf)) == sizeof(buf);
+
+ int rc = 0;
+ if ( writeBytes(buf, sizeof(buf)) == sizeof(buf) )
+ {
+ rc = 1;
+ }
+ return rc;
}
libusb_device_handle * LedDeviceLightpack::openDevice(libusb_device *device)
diff --git a/libsrc/leddevice/dev_hid/LedDeviceLightpack.h b/libsrc/leddevice/dev_hid/LedDeviceLightpack.h
index d9f43bc7..bf197bda 100644
--- a/libsrc/leddevice/dev_hid/LedDeviceLightpack.h
+++ b/libsrc/leddevice/dev_hid/LedDeviceLightpack.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef LEDEVICELIGHTPACK_H
+#define LEDEVICELIGHTPACK_H
// stl includes
#include
@@ -15,79 +16,93 @@
class LedDeviceLightpack : public LedDevice
{
public:
+
///
- /// Constructs the LedDeviceLightpack
+ /// @brief Constructs a Lightpack LED-device
///
/// @param serialNumber serial output device
///
- LedDeviceLightpack(const QString & serialNumber = "");
+ explicit LedDeviceLightpack(const QString & serialNumber = "");
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs a Lightpack LED-device
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceLightpack(const QJsonObject &deviceConfig);
///
- /// Sets configuration
- ///
- /// @param deviceConfig the json device config
- /// @return true if success
- bool init(const QJsonObject &deviceConfig) override;
-
- /// constructs leddevice
- static LedDevice* construct(const QJsonObject &deviceConfig);
-
- ///
- /// Destructor of the LedDevice; closes the output device if it is open
+ /// @brief Destructor of the LedDevice
///
virtual ~LedDeviceLightpack() override;
///
- /// Opens and configures the output device
+ /// @brief Constructs the LED-device
///
- /// @return Zero on succes else negative
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
///
- int open() override;
+ static LedDevice* construct(const QJsonObject &deviceConfig);
///
- /// Writes the RGB-Color values to the leds.
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Opens the output device.
+ ///
+ /// @return Zero on success (i.e. device is ready), else negative
+ ///
+ virtual int open() override;
+
+ ///
+ /// @brief Closes the output device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
+ ///
+ /// @brief Power-/turn off the Nanoleaf device.
+ ///
+ /// @return True if success
+ ///
+ virtual bool powerOff() override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues Array of RGB values
/// @param[in] size The number of RGB values
///
- /// @return Zero on success else negative
+ /// @return Zero on success, else negative
///
int write(const ColorRgb * ledValues, int size);
///
- /// Switch the leds off
+ /// @brief Get the serial number of the Lightpack
///
- /// @return Zero on success else negative
- ///
- virtual int switchOff() override;
-
- /// Get the serial of the Lightpack
+ /// @return Serial Number
+ /// ///
const QString & getSerialNumber() const;
-public slots:
- ///
- /// Closes the output device.
- /// Includes switching-off the device and stopping refreshes
- ///
- virtual void close() override;
+ bool isOpen(){ return _isOpen; }
protected:
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ virtual int write(const std::vector & ledValues) override;
+
private:
- ///
- /// Writes the RGB-Color values to the leds.
- ///
- /// @param[in] ledValues The RGB-color per led
- ///
- /// @return Zero on success else negative
- ///
- virtual int write(const std::vector& ledValues) override;
///
/// Test if the device is a (or the) lightpack we are looking for
@@ -114,6 +129,9 @@ private:
/// libusb context
libusb_context * _libusbContext;
+ /// libusb device
+ libusb_device * _device;
+
/// libusb device handle
libusb_device_handle * _deviceHandle;
@@ -134,4 +152,8 @@ private:
/// count of real hardware leds
int _hwLedCount;
+
+ bool _isOpen;
};
+
+#endif // LEDEVICELIGHTPACK_H
diff --git a/libsrc/leddevice/dev_hid/LedDeviceMultiLightpack.cpp b/libsrc/leddevice/dev_hid/LedDeviceMultiLightpack.cpp
index 97cd8ca6..fe87612a 100644
--- a/libsrc/leddevice/dev_hid/LedDeviceMultiLightpack.cpp
+++ b/libsrc/leddevice/dev_hid/LedDeviceMultiLightpack.cpp
@@ -22,14 +22,19 @@ LedDeviceMultiLightpack::LedDeviceMultiLightpack(const QJsonObject &deviceConfig
, _lightpacks()
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDeviceMultiLightpack::~LedDeviceMultiLightpack()
{
for (LedDeviceLightpack * device : _lightpacks)
{
- delete device;
+ if ( device != nullptr)
+ {
+ delete device;
+ }
}
}
@@ -38,14 +43,12 @@ LedDevice* LedDeviceMultiLightpack::construct(const QJsonObject &deviceConfig)
return new LedDeviceMultiLightpack(deviceConfig);
}
-int LedDeviceMultiLightpack::open()
+bool LedDeviceMultiLightpack::init(const QJsonObject &deviceConfig)
{
- int retval = -1;
- QString errortext;
- _deviceReady = false;
+ bool isInitOK = false;
- // General initialisation and configuration of LedDevice
- if ( init(_devConfig) )
+ // Initialise sub-class
+ if ( LedDevice::init(deviceConfig) )
{
// retrieve a list with Lightpack serials
QStringList serialList = getLightpackSerials();
@@ -53,47 +56,84 @@ int LedDeviceMultiLightpack::open()
// sort the list of Lightpacks based on the serial to get a fixed order
std::sort(_lightpacks.begin(), _lightpacks.end(), compareLightpacks);
- // open each lightpack device
+ // open each Lightpack device
for (auto serial : serialList)
{
- LedDeviceLightpack * device = new LedDeviceLightpack(serial);
- int error = device->open();
+ QJsonObject devConfig;
+ devConfig["serial"] = serial;
+ devConfig["latchTime"] = deviceConfig["latchTime"];
+ devConfig["rewriteTime"] = deviceConfig["rewriteTime"];
- if (error == 0)
+ LedDeviceLightpack * device = new LedDeviceLightpack(devConfig);
+
+ device->start();
+ if (device->open() == 0)
{
_lightpacks.push_back(device);
}
else
{
- //Error(_log, "Error while creating Lightpack device with serial %s", QSTRING_CSTR(serial));
- errortext = QString ("Error while creating Lightpack device with serial %1").arg( serial );
+ Error(_log, "Error while creating Lightpack device with serial %s", QSTRING_CSTR(serial));
delete device;
}
}
- if (_lightpacks.size() == 0)
+ if (_lightpacks.empty())
{
//Warning(_log, "No Lightpack devices were found");
- errortext = QString ("No Lightpack devices were found");
+ QString errortext = QString ("No Lightpack devices were found");
+ this->setInError(errortext);
+ isInitOK = false;
}
else
{
Info(_log, "%d Lightpack devices were found", _lightpacks.size());
-
- // Everything is OK -> enable device
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
- // On error/exceptions, set LedDevice in error
- if ( retval < 0 )
- {
- this->setInError( errortext );
+ isInitOK = true;
}
}
+ return isInitOK;
+}
+
+int LedDeviceMultiLightpack::open()
+{
+ int retval = -1;
+ _isDeviceReady = false;
+
+ int lightsInError = 0;
+ // open each Lightpack device
+ for (LedDeviceLightpack * device : _lightpacks)
+ {
+ if (device->open() < 0)
+ {
+ Error( _log, "Failed to open [%s]", QSTRING_CSTR(device->getSerialNumber()) );
+ ++lightsInError;
+ }
+ }
+
+ if ( lightsInError < static_cast(_lightpacks.size()) )
+ {
+ // Everything is OK -> enable device
+ _isDeviceReady = true;
+ retval = 0;
+ }
+ else
+ {
+ this->setInError( "All Lightpacks failed to be opened!" );
+ }
return retval;
}
+int LedDeviceMultiLightpack::close()
+{
+ _isDeviceReady = false;
+
+ for (LedDeviceLightpack * device : _lightpacks)
+ {
+ device->close();
+ }
+ return 0;
+}
+
int LedDeviceMultiLightpack::write(const std::vector &ledValues)
{
const ColorRgb * data = ledValues.data();
@@ -105,7 +145,10 @@ int LedDeviceMultiLightpack::write(const std::vector &ledValues)
if (count > 0)
{
- device->write(data, count);
+ if ( device->isOpen() )
+ {
+ device->write(data, count);
+ }
data += count;
size -= count;
@@ -119,14 +162,16 @@ int LedDeviceMultiLightpack::write(const std::vector &ledValues)
return 0;
}
-int LedDeviceMultiLightpack::switchOff()
+bool LedDeviceMultiLightpack::powerOff()
{
for (LedDeviceLightpack * device : _lightpacks)
{
- device->switchOff();
+ if ( device->isOpen() )
+ {
+ device->powerOff();
+ }
}
-
- return 0;
+ return true;
}
QStringList LedDeviceMultiLightpack::getLightpackSerials()
@@ -135,7 +180,7 @@ QStringList LedDeviceMultiLightpack::getLightpackSerials()
Logger * log = Logger::getInstance("LedDevice");
Debug(log, "Getting list of Lightpack serials");
- // initialize the usb context
+ // initialize the USB context
libusb_context * libusbContext;
int error = libusb_init(&libusbContext);
if (error != LIBUSB_SUCCESS)
@@ -147,7 +192,7 @@ QStringList LedDeviceMultiLightpack::getLightpackSerials()
//libusb_set_debug(_libusbContext, 3);
Info(log, "USB context initialized in multi Lightpack device");
- // retrieve the list of usb devices
+ // retrieve the list of USB devices
libusb_device ** deviceList;
ssize_t deviceCount = libusb_get_device_list(libusbContext, &deviceList);
@@ -165,7 +210,7 @@ QStringList LedDeviceMultiLightpack::getLightpackSerials()
if ((deviceDescriptor.idVendor == USB_VENDOR_ID && deviceDescriptor.idProduct == USB_PRODUCT_ID) ||
(deviceDescriptor.idVendor == USB_OLD_VENDOR_ID && deviceDescriptor.idProduct == USB_OLD_PRODUCT_ID))
{
- Info(log, "Found a lightpack device. Retrieving serial...");
+ Info(log, "Found a Lightpack device. Retrieving serial...");
// get the serial number
QString serialNumber;
@@ -183,7 +228,7 @@ QStringList LedDeviceMultiLightpack::getLightpackSerials()
}
}
- Error(log, "Lightpack device found with serial %s", QSTRING_CSTR(serialNumber));;
+ Info(log, "Lightpack device found with serial %s", QSTRING_CSTR(serialNumber));
serialList.append(serialNumber);
}
}
diff --git a/libsrc/leddevice/dev_hid/LedDeviceMultiLightpack.h b/libsrc/leddevice/dev_hid/LedDeviceMultiLightpack.h
index c1bec619..256c3d7b 100644
--- a/libsrc/leddevice/dev_hid/LedDeviceMultiLightpack.h
+++ b/libsrc/leddevice/dev_hid/LedDeviceMultiLightpack.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef LEDEVICEMULTILIGHTPACK_H
+#define LEDEVICEMULTILIGHTPACK_H
// stl includes
#include
@@ -19,43 +20,67 @@
class LedDeviceMultiLightpack : public LedDevice
{
public:
- ///
- /// Constructs specific LedDevice
- ///
- explicit LedDeviceMultiLightpack(const QJsonObject &);
///
- /// Destructor of the LedDevice; closes the output device if it is open
+ /// @brief Constructs a LedDevice of multiple Lightpack LED-devices
+ ///
+ /// @param deviceConfig Device's configuration as JSON-Object
+ ///
+ explicit LedDeviceMultiLightpack(const QJsonObject &deviceConfig);
+
+ ///
+ /// @brief Destructor of the LedDevice
///
virtual ~LedDeviceMultiLightpack() override;
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
static LedDevice* construct(const QJsonObject &deviceConfig);
- ///
- virtual int switchOff() override;
-
protected:
+
///
- /// Opens and configures the output device7
+ /// @brief Initialise the device's configuration
///
- /// @return Zero on succes else negative
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
///
- int open() override;
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
///
- /// Switch the leds off
+ /// @brief Opens the output device.
///
- /// @return Zero on success else negative
+ /// @return Zero on success (i.e. device is ready), else negative
+ ///
+ virtual int open() override;
+
+ ///
+ /// @brief Closes the output device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
+ ///
+ /// @brief Power-/turn off the Nanoleaf device.
+ ///
+ /// @return True if success
+ ///
+ virtual bool powerOff() override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ int write(const std::vector & ledValues) override;
private:
- ///
- /// Writes the RGB-Color values to the leds.
- ///
- /// @param[in] ledValues The RGB-color per led
- ///
- /// @return Zero on success else negative
- ///
- virtual int write(const std::vector& ledValues) override;
static QStringList getLightpackSerials();
static QString getString(libusb_device * device, int stringDescriptorIndex);
@@ -63,3 +88,5 @@ private:
/// buffer for led data
std::vector _lightpacks;
};
+
+#endif // LEDEVICEMULTILIGHTPACK_H
diff --git a/libsrc/leddevice/dev_hid/LedDevicePaintpack.cpp b/libsrc/leddevice/dev_hid/LedDevicePaintpack.cpp
index 14939760..61fed68a 100644
--- a/libsrc/leddevice/dev_hid/LedDevicePaintpack.cpp
+++ b/libsrc/leddevice/dev_hid/LedDevicePaintpack.cpp
@@ -5,9 +5,11 @@ LedDevicePaintpack::LedDevicePaintpack(const QJsonObject &deviceConfig)
: ProviderHID()
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
_useFeature = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDevicePaintpack::construct(const QJsonObject &deviceConfig)
@@ -17,12 +19,17 @@ LedDevice* LedDevicePaintpack::construct(const QJsonObject &deviceConfig)
bool LedDevicePaintpack::init(const QJsonObject &deviceConfig)
{
- bool isInitOK = ProviderHID::init(deviceConfig);
+ bool isInitOK = false;
- _ledBuffer.resize(_ledRGBCount + 2, uint8_t(0));
- _ledBuffer[0] = 3;
- _ledBuffer[1] = 0;
+ // Initialise sub-class
+ if ( ProviderHID::init(deviceConfig) )
+ {
+ _ledBuffer.resize(_ledRGBCount + 2, uint8_t(0));
+ _ledBuffer[0] = 3;
+ _ledBuffer[1] = 0;
+ isInitOK = true;
+ }
return isInitOK;
}
diff --git a/libsrc/leddevice/dev_hid/LedDevicePaintpack.h b/libsrc/leddevice/dev_hid/LedDevicePaintpack.h
index 86f1967d..95c7cc99 100644
--- a/libsrc/leddevice/dev_hid/LedDevicePaintpack.h
+++ b/libsrc/leddevice/dev_hid/LedDevicePaintpack.h
@@ -1,38 +1,48 @@
-#pragma once
+#ifndef LEDEVICEPAINTTPACK_H
+#define LEDEVICEPAINTTPACK_H
// Hyperion includes
#include "ProviderHID.h"
///
-/// LedDevice implementation for a paintpack device ()
+/// LedDevice implementation for a paintpack LED-device
///
class LedDevicePaintpack : public ProviderHID
{
public:
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs a Paintpack LED-device
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDevicePaintpack(const QJsonObject &deviceConfig);
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
static LedDevice* construct(const QJsonObject &deviceConfig);
+private:
+
///
- /// Sets configuration
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
///
- /// @param deviceConfig the json device config
- /// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
-private:
///
- /// Writes the RGB-Color values to the leds.
+ /// @brief Writes the RGB-Color values to the LEDs.
///
- /// @param[in] ledValues The RGB-color per led
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
///
- /// @return Zero on success else negative
- ///
- virtual int write(const std::vector& ledValues) override;
+ virtual int write(const std::vector & ledValues) override;
};
+
+#endif // LEDEVICEPAINTTPACK_H
diff --git a/libsrc/leddevice/dev_hid/LedDeviceRawHID.cpp b/libsrc/leddevice/dev_hid/LedDeviceRawHID.cpp
index 0e940b32..89468659 100644
--- a/libsrc/leddevice/dev_hid/LedDeviceRawHID.cpp
+++ b/libsrc/leddevice/dev_hid/LedDeviceRawHID.cpp
@@ -5,7 +5,9 @@ LedDeviceRawHID::LedDeviceRawHID(const QJsonObject &deviceConfig)
: ProviderHID()
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
_useFeature = true;
}
@@ -17,10 +19,14 @@ LedDevice* LedDeviceRawHID::construct(const QJsonObject &deviceConfig)
bool LedDeviceRawHID::init(const QJsonObject &deviceConfig)
{
- bool isInitOK = ProviderHID::init(deviceConfig);
-
- _ledBuffer.resize(_ledRGBCount);
+ bool isInitOK = false;
+ // Initialise sub-class
+ if ( ProviderHID::init(deviceConfig) )
+ {
+ _ledBuffer.resize(_ledRGBCount);
+ isInitOK = true;
+ }
return isInitOK;
}
diff --git a/libsrc/leddevice/dev_hid/LedDeviceRawHID.h b/libsrc/leddevice/dev_hid/LedDeviceRawHID.h
index 45310712..df409b75 100644
--- a/libsrc/leddevice/dev_hid/LedDeviceRawHID.h
+++ b/libsrc/leddevice/dev_hid/LedDeviceRawHID.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef LEDEVICERAWHID_H
+#define LEDEVICERAWHID_H
// Qt includes
#include
@@ -11,31 +12,40 @@
///
class LedDeviceRawHID : public ProviderHID
{
-
public:
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs a Raw-HID LED-device
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceRawHID(const QJsonObject &deviceConfig);
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
static LedDevice* construct(const QJsonObject &deviceConfig);
+private:
+
///
- /// Sets configuration
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
///
- /// @param deviceConfig the json device config
- /// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
-private:
///
- /// Writes the led color values to the led-device
+ /// @brief Writes the RGB-Color values to the LEDs.
///
- /// @param ledValues The color-value per led
- /// @return Zero on succes else negative
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
///
virtual int write(const std::vector & ledValues) override;
};
+
+#endif // LEDEVICERAWHID_H
diff --git a/libsrc/leddevice/dev_hid/ProviderHID.cpp b/libsrc/leddevice/dev_hid/ProviderHID.cpp
index 224b0260..289a5996 100644
--- a/libsrc/leddevice/dev_hid/ProviderHID.cpp
+++ b/libsrc/leddevice/dev_hid/ProviderHID.cpp
@@ -21,54 +21,59 @@ ProviderHID::ProviderHID()
ProviderHID::~ProviderHID()
{
+ if (_deviceHandle != nullptr)
+ {
+ hid_close(_deviceHandle);
+ }
+ hid_exit();
}
bool ProviderHID::init(const QJsonObject &deviceConfig)
{
- bool isInitOK = LedDevice::init(deviceConfig);
+ bool isInitOK = false;
- _delayAfterConnect_ms = deviceConfig["delayAfterConnect"].toInt(0);
- auto VendorIdString = deviceConfig["VID"].toString("0x2341").toStdString();
- auto ProductIdString = deviceConfig["PID"].toString("0x8036").toStdString();
+ // Initialise sub-class
+ if ( LedDevice::init(deviceConfig) )
+ {
+ _delayAfterConnect_ms = deviceConfig["delayAfterConnect"].toInt(0);
+ auto VendorIdString = deviceConfig["VID"].toString("0x2341").toStdString();
+ auto ProductIdString = deviceConfig["PID"].toString("0x8036").toStdString();
- // Convert HEX values to integer
- _VendorId = std::stoul(VendorIdString, nullptr, 16);
- _ProductId = std::stoul(ProductIdString, nullptr, 16);
+ // Convert HEX values to integer
+ _VendorId = std::stoul(VendorIdString, nullptr, 16);
+ _ProductId = std::stoul(ProductIdString, nullptr, 16);
+ // Initialize the USB context
+ if ( hid_init() != 0)
+ {
+ this->setInError("Error initializing the HIDAPI context");
+ isInitOK = false;
+ }
+ else
+ {
+ Debug(_log,"HIDAPI initialized");
+ isInitOK = true;
+ }
+ }
return isInitOK;
}
int ProviderHID::open()
{
int retval = -1;
- QString errortext;
- _deviceReady = false;
+ _isDeviceReady = false;
- if ( init(_devConfig) )
+ // Open the device
+ Info(_log, "Opening device: VID %04hx PID %04hx\n", _VendorId, _ProductId);
+ _deviceHandle = hid_open(_VendorId, _ProductId, nullptr);
+
+ if (_deviceHandle == nullptr)
{
- // Initialize the usb context
- int error = hid_init();
- if (error != 0)
- {
- //Error(_log, "Error while initializing the hidapi context");
- errortext = "Error while initializing the hidapi context";
- }
- else
- {
- Debug(_log,"Hidapi initialized");
+ // Failed to open the device
+ this->setInError( "Failed to open HID device. Maybe your PID/VID setting is wrong? Make sure to add a udev rule/use sudo." );
- // Open the device
- Info(_log, "Opening device: VID %04hx PID %04hx\n", _VendorId, _ProductId);
- _deviceHandle = hid_open(_VendorId, _ProductId, nullptr);
-
- if (_deviceHandle == nullptr)
- {
- // Failed to open the device
- Error(_log,"Failed to open HID device. Maybe your PID/VID setting is wrong? Make sure to add a udev rule/use sudo.");
- errortext = "Failed to open HID device";
-
- // http://www.signal11.us/oss/hidapi/
- /*
+ // http://www.signal11.us/oss/hidapi/
+ /*
std::cout << "Showing a list of all available HID devices:" << std::endl;
auto devs = hid_enumerate(0x00, 0x00);
auto cur_dev = devs;
@@ -83,45 +88,38 @@ int ProviderHID::open()
}
hid_free_enumeration(devs);
*/
- }
- else
- {
- Info(_log,"Opened HID device successful");
- // Everything is OK -> enable device
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
-
- // Wait after device got opened if enabled
- if (_delayAfterConnect_ms > 0)
- {
- _blockedForDelay = true;
- QTimer::singleShot(_delayAfterConnect_ms, this, SLOT(unblockAfterDelay()));
- Debug(_log, "Device blocked for %d ms", _delayAfterConnect_ms);
- }
- }
- // On error/exceptions, set LedDevice in error
- if ( retval < 0 )
- {
- this->setInError( errortext );
- }
}
+ else
+ {
+ Info(_log,"Opened HID device successful");
+ // Everything is OK -> enable device
+ _isDeviceReady = true;
+ retval = 0;
+ }
+
+ // Wait after device got opened if enabled
+ if (_delayAfterConnect_ms > 0)
+ {
+ _blockedForDelay = true;
+ QTimer::singleShot(_delayAfterConnect_ms, this, &ProviderHID::unblockAfterDelay );
+ Debug(_log, "Device blocked for %d ms", _delayAfterConnect_ms);
+ }
+
return retval;
}
-void ProviderHID::close()
+int ProviderHID::close()
{
- LedDevice::close();
+ int retval = 0;
+ _isDeviceReady = false;
- // LedDevice specific closing activites
+ // LedDevice specific closing activities
if (_deviceHandle != nullptr)
{
hid_close(_deviceHandle);
_deviceHandle = nullptr;
}
-
- hid_exit();
+ return retval;
}
int ProviderHID::writeBytes(const unsigned size, const uint8_t * data)
@@ -138,7 +136,7 @@ int ProviderHID::writeBytes(const unsigned size, const uint8_t * data)
// Try again in 3 seconds
int delay_ms = 3000;
_blockedForDelay = true;
- QTimer::singleShot(delay_ms, this, SLOT(unblockAfterDelay()));
+ QTimer::singleShot(delay_ms, this, &ProviderHID::unblockAfterDelay );
Debug(_log,"Device blocked for %d ms", delay_ms);
}
// Return here, to not write led data if the device should be blocked after connect
@@ -188,3 +186,38 @@ void ProviderHID::unblockAfterDelay()
Debug(_log,"Device unblocked");
_blockedForDelay = false;
}
+
+QJsonObject ProviderHID::discover()
+{
+ QJsonObject devicesDiscovered;
+ devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
+
+ QJsonArray deviceList;
+
+ // Discover HID Devices
+ auto devs = hid_enumerate(0x00, 0x00);
+
+ if ( devs != nullptr )
+ {
+ auto cur_dev = devs;
+ while (cur_dev)
+ {
+ QJsonObject deviceInfo;
+ deviceInfo.insert("manufacturer",QString::fromWCharArray(cur_dev->manufacturer_string));
+ deviceInfo.insert("path",cur_dev->path);
+ deviceInfo.insert("productIdentifier", QString("0x%1").arg(static_cast(cur_dev->product_id),0,16));
+ deviceInfo.insert("release_number",QString("0x%1").arg(static_cast(cur_dev->release_number),0,16));
+ deviceInfo.insert("serialNumber",QString::fromWCharArray(cur_dev->serial_number));
+ deviceInfo.insert("usage_page", QString("0x%1").arg(static_cast(cur_dev->usage_page),0,16));
+ deviceInfo.insert("vendorIdentifier", QString("0x%1").arg(static_cast(cur_dev->vendor_id),0,16));
+ deviceInfo.insert("interface_number",cur_dev->interface_number);
+ deviceList.append(deviceInfo);
+
+ cur_dev = cur_dev->next;
+ }
+ hid_free_enumeration(devs);
+ }
+
+ devicesDiscovered.insert("devices", deviceList);
+ return devicesDiscovered;
+}
diff --git a/libsrc/leddevice/dev_hid/ProviderHID.h b/libsrc/leddevice/dev_hid/ProviderHID.h
index 9d8cff2c..dce62bc3 100644
--- a/libsrc/leddevice/dev_hid/ProviderHID.h
+++ b/libsrc/leddevice/dev_hid/ProviderHID.h
@@ -1,6 +1,5 @@
-#pragma once
-
-#include
+#ifndef PROVIDERHID_H
+#define PROVIDERHID_H
// libusb include
#include
@@ -16,46 +15,57 @@ class ProviderHID : public LedDevice
Q_OBJECT
public:
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs a HID (USB) LED-device
+ ///
+ /// @param deviceConfig Device's configuration as JSON-Object
///
ProviderHID();
///
- /// Destructor of the LedDevice; closes the output device if it is open
+ /// @brief Destructor of the LedDevice
///
virtual ~ProviderHID() override;
///
- /// Sets configuration
+ /// @brief Discover HIB (USB) devices available (for configuration).
///
- /// @param deviceConfig the json device config
- /// @return true if success
- virtual bool init(const QJsonObject &deviceConfig) override;
-
-public slots:
+ /// @return A JSON structure holding a list of devices found
///
- /// Closes the output device.
- /// Includes switching-off the device and stopping refreshes
- ///
- virtual void close() override;
+ virtual QJsonObject discover() override;
protected:
- ///
- /// Opens and configures the output device
- ///
- /// @return Zero on succes else negative
- ///
- int open() override;
- /**
- * Writes the given bytes to the HID-device and
- *
- * @param[in] size The length of the data
- * @param[in] data The data
- *
- * @return Zero on succes else negative
- */
+ ///
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Opens the output device.
+ ///
+ /// @return Zero on success (i.e. device is ready), else negative
+ ///
+ virtual int open() override;
+
+ ///
+ /// @brief Closes the output device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
+ ///
+ /// @brief Write the given bytes to the HID-device
+ ///
+ /// @param[in[ size The length of the data
+ /// @param[in] data The data
+ /// @return Zero on success, else negative
+ ///
int writeBytes(const unsigned size, const uint8_t *data);
// HID VID and PID
@@ -74,4 +84,10 @@ protected:
private slots:
/// Unblock the device after a connection delay
void unblockAfterDelay();
+
+
+private:
+
};
+
+#endif // PROVIDERHID_H
diff --git a/libsrc/leddevice/dev_net/LedDeviceAtmoOrb.cpp b/libsrc/leddevice/dev_net/LedDeviceAtmoOrb.cpp
index b23d5aec..9aceeb74 100644
--- a/libsrc/leddevice/dev_net/LedDeviceAtmoOrb.cpp
+++ b/libsrc/leddevice/dev_net/LedDeviceAtmoOrb.cpp
@@ -1,5 +1,6 @@
// Local-Hyperion includes
#include "LedDeviceAtmoOrb.h"
+#include
// qt includes
#include
@@ -19,7 +20,9 @@ LedDeviceAtmoOrb::LedDeviceAtmoOrb(const QJsonObject &deviceConfig)
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceAtmoOrb::construct(const QJsonObject &deviceConfig)
@@ -31,7 +34,7 @@ LedDeviceAtmoOrb::~LedDeviceAtmoOrb()
{
if ( _udpSocket != nullptr )
{
- _udpSocket->deleteLater();
+ delete _udpSocket;
}
}
@@ -49,30 +52,31 @@ bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig)
_multiCastGroupPort = static_cast(deviceConfig["port"].toInt(MULTICAST_GROUPL_DEFAULT_PORT));
_numLeds = deviceConfig["numLeds"].toInt(LEDS_DEFAULT_NUMBER);
- #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- const QStringList orbIds = deviceConfig["orbIds"].toString().simplified().remove(" ").split(",", Qt::SkipEmptyParts);
- #else
- const QStringList orbIds = deviceConfig["orbIds"].toString().simplified().remove(" ").split(",", QString::SkipEmptyParts);
- #endif
-
+ QStringList orbIds = QStringUtils::split(deviceConfig["orbIds"].toString().simplified().remove(" "),",", QStringUtils::SplitBehavior::SkipEmptyParts);
_orbIds.clear();
- for(auto & id_str : orbIds)
+ for (auto & id_str : orbIds)
{
bool ok;
int id = id_str.toInt(&ok);
if (ok)
{
if ( id < 1 || id > 255 )
+ {
Warning(_log, "Skip orb id '%d'. IDs must be in range 1-255", id);
+ }
else
+ {
_orbIds.append(id);
+ }
}
else
+ {
Error(_log, "orb id '%s' is not a number", QSTRING_CSTR(id_str));
+ }
}
- if ( _orbIds.size() == 0 )
+ if ( _orbIds.empty() )
{
this->setInError("No valid OrbIds found!");
isInitOK = false;
@@ -89,43 +93,40 @@ bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig)
int LedDeviceAtmoOrb::open()
{
int retval = -1;
- _deviceReady = false;
+ _isDeviceReady = false;
- if ( init(_devConfig) )
+ // Try to bind the UDP-Socket
+ if ( _udpSocket != nullptr )
{
- // Try to bind the UDP-Socket
- if ( _udpSocket != nullptr )
+ _groupAddress = QHostAddress(_multicastGroup);
+ if ( !_udpSocket->bind(QHostAddress::AnyIPv4, _multiCastGroupPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint) )
{
- _groupAddress = QHostAddress(_multicastGroup);
- if ( !_udpSocket->bind(QHostAddress::AnyIPv4, _multiCastGroupPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint) )
+ QString errortext = QString ("(%1) %2, MulticastGroup: (%3)").arg(_udpSocket->error()).arg(_udpSocket->errorString(), _multicastGroup);
+ this->setInError( errortext );
+ }
+ else
+ {
+ _joinedMulticastgroup = _udpSocket->joinMulticastGroup(_groupAddress);
+ if ( !_joinedMulticastgroup )
{
- QString errortext = QString ("(%1) %2, MulticastGroup: (%3)").arg(_udpSocket->error()).arg(_udpSocket->errorString()).arg(_multicastGroup);
+ QString errortext = QString ("(%1) %2, MulticastGroup: (%3)").arg(_udpSocket->error()).arg(_udpSocket->errorString(), _multicastGroup);
this->setInError( errortext );
}
else
{
- _joinedMulticastgroup = _udpSocket->joinMulticastGroup(_groupAddress);
- if ( !_joinedMulticastgroup )
- {
- QString errortext = QString ("(%1) %2, MulticastGroup: (%3)").arg(_udpSocket->error()).arg(_udpSocket->errorString()).arg(_multicastGroup);
- this->setInError( errortext );
- }
- else
- {
- // Everything is OK, device is ready
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
+ // Everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
}
}
}
return retval;
}
-void LedDeviceAtmoOrb::close()
+int LedDeviceAtmoOrb::close()
{
- LedDevice::close();
+ int retval = 0;
+ _isDeviceReady = false;
if ( _udpSocket != nullptr )
{
@@ -137,6 +138,7 @@ void LedDeviceAtmoOrb::close()
// Everything is OK -> device is closed
}
}
+ return retval;
}
int LedDeviceAtmoOrb::write(const std::vector &ledValues)
@@ -211,7 +213,9 @@ int LedDeviceAtmoOrb::write(const std::vector &ledValues)
void LedDeviceAtmoOrb::setColor(int orbId, const ColorRgb &color, int commandType)
{
QByteArray bytes;
- bytes.resize(5 + _numLeds * 3);
+
+ // 5 bytes command-header + 3 bytes color information
+ bytes.resize(5 + 3);
bytes.fill('\0');
// Command identifier: C0FFEE
@@ -230,7 +234,6 @@ void LedDeviceAtmoOrb::setColor(int orbId, const ColorRgb &color, int commandTyp
bytes[6] = static_cast(color.green);
bytes[7] = static_cast(color.blue);
- // TODO: Why is the datagram _numLeds * 3 in size, if only bypes 5,6,7 are updated with the color?
//std::cout << "Orb [" << orbId << "] Cmd [" << bytes.toHex(':').toStdString() <<"]"<< std::endl;
sendCommand(bytes);
diff --git a/libsrc/leddevice/dev_net/LedDeviceAtmoOrb.h b/libsrc/leddevice/dev_net/LedDeviceAtmoOrb.h
index fc01ba33..7fdd199f 100644
--- a/libsrc/leddevice/dev_net/LedDeviceAtmoOrb.h
+++ b/libsrc/leddevice/dev_net/LedDeviceAtmoOrb.h
@@ -1,9 +1,8 @@
-#pragma once
+#ifndef LEDEVICEATMOORB_H
+#define LEDEVICEATMOORB_H
// Qt includes
-#include
-#include
-#include
+#include
#include
#include
@@ -25,38 +24,39 @@ class LedDeviceAtmoOrb : public LedDevice
public:
///
- /// Constructs specific LedDevice
+ /// @brief Constructs an AtmoOrb LED-device
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceAtmoOrb(const QJsonObject &deviceConfig);
///
- /// Sets configuration
- ///
- /// @param deviceConfig the json device config
- /// @return true if success
- bool init(const QJsonObject &deviceConfig) override;
-
- /// constructs leddevice
- static LedDevice* construct(const QJsonObject &deviceConfig);
- ///
- /// Destructor of this device
+ /// @brief Destructor of the LedDevice
///
virtual ~LedDeviceAtmoOrb() override;
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
+ static LedDevice* construct(const QJsonObject &deviceConfig);
+
protected:
///
- /// Initialise device's network details
+ /// @brief Initialise the device's configuration
///
- /// @return True if success
- bool initNetwork();
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
///
- /// Opens and initiatialises the output device
+ /// @brief Opens the output device.
///
- /// @return Zero on succes (i.e. device is ready and enabled) else negative
+ /// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
@@ -65,18 +65,18 @@ protected:
///
/// @return Zero on success (i.e. device is closed), else negative
///
- virtual void close() override;
+ virtual int close() override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ virtual int write(const std::vector & ledValues) override;
private:
- ///
- /// Sends the given led-color values to the Orbs
- ///
- /// @param ledValues The color-value per led
- /// @return Zero on success else negative
- ///
- virtual int write(const std::vector &ledValues) override;
-
///
/// Set Orbcolor
///
@@ -93,9 +93,6 @@ private:
///
void sendCommand(const QByteArray &bytes);
- /// QNetworkAccessManager object for sending requests.
- QNetworkAccessManager *_networkmanager;
-
/// QUdpSocket object used to send data over
QUdpSocket * _udpSocket;
@@ -132,3 +129,5 @@ private:
QMap lastColorBlueMap;
};
+
+#endif // LEDEVICEATMOORB_H
diff --git a/libsrc/leddevice/dev_net/LedDeviceFadeCandy.cpp b/libsrc/leddevice/dev_net/LedDeviceFadeCandy.cpp
index 49a433de..15de0b5b 100644
--- a/libsrc/leddevice/dev_net/LedDeviceFadeCandy.cpp
+++ b/libsrc/leddevice/dev_net/LedDeviceFadeCandy.cpp
@@ -6,22 +6,37 @@
typedef SSIZE_T ssize_t;
#endif
-static const signed MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds
-static const unsigned OPC_SET_PIXELS = 0; // OPC command codes
-static const unsigned OPC_SYS_EX = 255; // OPC command codes
-static const unsigned OPC_HEADER_SIZE = 4; // OPC header size
+// Constants
+namespace {
+
+const signed MAX_NUM_LEDS = 10000; // OPC can handle 21845 LEDs - in theory, fadecandy device should handle 10000 LEDs
+const unsigned OPC_SET_PIXELS = 0; // OPC command codes
+const unsigned OPC_SYS_EX = 255; // OPC command codes
+const unsigned OPC_HEADER_SIZE = 4; // OPC header size
+
+} //End of constants
+
+// TCP elements
+const quint16 STREAM_DEFAULT_PORT = 7890;
LedDeviceFadeCandy::LedDeviceFadeCandy(const QJsonObject &deviceConfig)
: LedDevice()
- , _client(nullptr)
+ , _client(nullptr)
+ ,_host()
+ ,_port(STREAM_DEFAULT_PORT)
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDeviceFadeCandy::~LedDeviceFadeCandy()
{
- _client->deleteLater();
+ if ( _client != nullptr )
+ {
+ delete _client;
+ }
}
LedDevice* LedDeviceFadeCandy::construct(const QJsonObject &deviceConfig)
@@ -31,46 +46,59 @@ LedDevice* LedDeviceFadeCandy::construct(const QJsonObject &deviceConfig)
bool LedDeviceFadeCandy::init(const QJsonObject &deviceConfig)
{
- bool isInitOK = LedDevice::init(deviceConfig);
+ bool isInitOK = false;
- if ( isInitOK )
+ if ( LedDevice::init(deviceConfig) )
{
- if (_ledCount > MAX_NUM_LEDS)
+ if (getLedCount() > MAX_NUM_LEDS)
{
- //Error(_log, "fadecandy/opc: Invalid attempt to write led values. Not more than %d leds are allowed.", MAX_NUM_LEDS);
QString errortext = QString ("More LED configured than allowed (%1)").arg(MAX_NUM_LEDS);
this->setInError(errortext);
isInitOK = false;
}
else
{
- _host = deviceConfig["output"].toString("127.0.0.1");
- _port = deviceConfig["port"].toInt(7890);
- _channel = deviceConfig["channel"].toInt(0);
- _gamma = deviceConfig["gamma"].toDouble(1.0);
- _noDither = ! deviceConfig["dither"].toBool(false);
- _noInterp = ! deviceConfig["interpolation"].toBool(false);
- _manualLED = deviceConfig["manualLed"].toBool(false);
- _ledOnOff = deviceConfig["ledOn"].toBool(false);
- _setFcConfig = deviceConfig["setFcConfig"].toBool(false);
+ _host = deviceConfig["output"].toString("127.0.0.1");
+ _port = deviceConfig["port"].toInt(STREAM_DEFAULT_PORT);
- _whitePoint_r = 1.0;
- _whitePoint_g = 1.0;
- _whitePoint_b = 1.0;
-
- const QJsonArray whitePointConfig = deviceConfig["whitePoint"].toArray();
- if ( !whitePointConfig.isEmpty() && whitePointConfig.size() == 3 )
+ //If host not configured the init fails
+ if ( _host.isEmpty() )
{
- _whitePoint_r = whitePointConfig[0].toDouble() / 255.0;
- _whitePoint_g = whitePointConfig[1].toDouble() / 255.0;
- _whitePoint_b = whitePointConfig[2].toDouble() / 255.0;
+ this->setInError("No target hostname nor IP defined");
}
+ else
+ {
+ _channel = deviceConfig["channel"].toInt(0);
+ _gamma = deviceConfig["gamma"].toDouble(1.0);
+ _noDither = ! deviceConfig["dither"].toBool(false);
+ _noInterp = ! deviceConfig["interpolation"].toBool(false);
+ _manualLED = deviceConfig["manualLed"].toBool(false);
+ _ledOnOff = deviceConfig["ledOn"].toBool(false);
+ _setFcConfig = deviceConfig["setFcConfig"].toBool(false);
- _opc_data.resize( _ledRGBCount + OPC_HEADER_SIZE );
- _opc_data[0] = _channel;
- _opc_data[1] = OPC_SET_PIXELS;
- _opc_data[2] = _ledRGBCount >> 8;
- _opc_data[3] = _ledRGBCount & 0xff;
+ _whitePoint_r = 1.0;
+ _whitePoint_g = 1.0;
+ _whitePoint_b = 1.0;
+
+ const QJsonArray whitePointConfig = deviceConfig["whitePoint"].toArray();
+ if ( !whitePointConfig.isEmpty() && whitePointConfig.size() == 3 )
+ {
+ _whitePoint_r = whitePointConfig[0].toDouble() / 255.0;
+ _whitePoint_g = whitePointConfig[1].toDouble() / 255.0;
+ _whitePoint_b = whitePointConfig[2].toDouble() / 255.0;
+ }
+
+ _opc_data.resize( _ledRGBCount + OPC_HEADER_SIZE );
+ _opc_data[0] = _channel;
+ _opc_data[1] = OPC_SET_PIXELS;
+ _opc_data[2] = _ledRGBCount >> 8;
+ _opc_data[3] = _ledRGBCount & 0xff;
+
+ if ( initNetwork() )
+ {
+ isInitOK = true;
+ }
+ }
}
}
return isInitOK;
@@ -78,62 +106,77 @@ bool LedDeviceFadeCandy::init(const QJsonObject &deviceConfig)
bool LedDeviceFadeCandy::initNetwork()
{
- bool isInitOK = true;
+ bool isInitOK = false;
- // TODO: Add Network-Error handling
- _client = new QTcpSocket(this);
+ if ( _client == nullptr )
+ {
+ _client = new QTcpSocket(this);
+ isInitOK = true;
+ }
return isInitOK;
}
int LedDeviceFadeCandy::open()
{
int retval = -1;
- _deviceReady = false;
+ QString errortext;
+ _isDeviceReady = false;
- if ( init(_devConfig) )
+ // Try to open the LedDevice
+ if ( !tryConnect() )
{
- if ( !initNetwork() )
- {
- this->setInError( "Network error!" );
- }
- else
- {
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
+ errortext = QString ("Failed to open device.");
+ this->setInError( errortext );
+ }
+ else
+ {
+ // Everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
}
return retval;
}
-void LedDeviceFadeCandy::close()
+int LedDeviceFadeCandy::close()
{
- LedDevice::close();
+ int retval = 0;
+ _isDeviceReady = false;
- // LedDevice specific closing activites
- _client->close();
+ // LedDevice specific closing activities
+ if ( _client != nullptr )
+ {
+ _client->close();
+ // Everything is OK -> device is closed
+ }
+ return retval;
}
-
bool LedDeviceFadeCandy::isConnected()
{
- return _client->state() == QAbstractSocket::ConnectedState;
+ bool connected = false;
+ if ( _client != nullptr )
+ {
+ connected = _client->state() == QAbstractSocket::ConnectedState;
+ }
+ return connected;
}
bool LedDeviceFadeCandy::tryConnect()
{
- if ( _client->state() == QAbstractSocket::UnconnectedState ) {
- _client->connectToHost( _host, _port);
- if ( _client->waitForConnected(1000) )
- {
- Info(_log,"fadecandy/opc: connected to %s:%i on channel %i", QSTRING_CSTR(_host), _port, _channel);
- if (_setFcConfig)
+ if ( _client != nullptr )
+ {
+ if ( _client->state() == QAbstractSocket::UnconnectedState ) {
+ _client->connectToHost( _host, _port);
+ if ( _client->waitForConnected(1000) )
{
- sendFadeCandyConfiguration();
+ Info(_log,"fadecandy/opc: connected to %s:%i on channel %i", QSTRING_CSTR(_host), _port, _channel);
+ if (_setFcConfig)
+ {
+ sendFadeCandyConfiguration();
+ }
}
}
}
-
return isConnected();
}
@@ -148,15 +191,16 @@ int LedDeviceFadeCandy::write( const std::vector & ledValues )
idx += 3;
}
- return ( transferData()<0 ? -1 : 0 );
+ int retval = transferData()<0 ? -1 : 0;
+ return retval;
}
int LedDeviceFadeCandy::transferData()
{
- if (LedDevice::enabled())
- if ( isConnected() || tryConnect() )
- return _client->write( _opc_data, _opc_data.size() );
-
+ if ( isConnected() || tryConnect() )
+ {
+ return _client->write( _opc_data, _opc_data.size() );
+ }
return -2;
}
diff --git a/libsrc/leddevice/dev_net/LedDeviceFadeCandy.h b/libsrc/leddevice/dev_net/LedDeviceFadeCandy.h
index e3cf0bdb..274b6b6c 100644
--- a/libsrc/leddevice/dev_net/LedDeviceFadeCandy.h
+++ b/libsrc/leddevice/dev_net/LedDeviceFadeCandy.h
@@ -1,10 +1,11 @@
-#pragma once
+#ifndef LEDEVICEFADECANDY_H
+#define LEDEVICEFADECANDY_H
// STL/Qt includes
#include
#include
-// Leddevice includes
+// LedDevice includes
#include
///
@@ -17,9 +18,9 @@ class LedDeviceFadeCandy : public LedDevice
public:
///
- /// Constructs the LedDevice for fadecandy/opc server
+ /// @brief Constructs a LED-device for fadecandy/opc server
///
- /// following code shows all config options
+ /// Following code shows all configuration options
/// @code
/// "device" :
/// {
@@ -37,84 +38,95 @@ public:
/// },
///@endcode
///
- /// @param deviceConfig json config for fadecandy
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceFadeCandy(const QJsonObject &deviceConfig);
///
- /// Destructor of the LedDevice; closes the tcp client
+ /// @brief Destructor of the LedDevice
///
- virtual ~LedDeviceFadeCandy();
+ ~LedDeviceFadeCandy() override;
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig);
- ///
- /// Sets configuration
- ///
- /// @param deviceConfig the json device config
- /// @return true if success
- bool init(const QJsonObject &deviceConfig) override;
-
-public slots:
-
- ///
- /// Closes the output device.
- /// Includes switching-off the device and stopping refreshes
- ///
- virtual void close() override;
-
protected:
///
- /// Initialise device's network details
+ /// @brief Initialise the Nanoleaf device's configuration and network address details
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Opens the output device.
+ ///
+ /// @return Zero on success (i.e. device is ready), else negative
+ ///
+ virtual int open() override;
+
+ ///
+ /// @brief Closes the output device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ virtual int write(const std::vector & ledValues) override;
+
+private:
+
+ ///
+ /// @brief Initialise device's network details
///
/// @return True if success
bool initNetwork();
///
- /// Opens and initiatialises the output device
+ /// @brief try to establish connection to opc server, if not connected yet
///
- /// @return Zero on succes (i.e. device is ready and enabled) else negative
- ///
- virtual int open() override;
-
-private:
- ///
- /// Writes the led color values to the led-device
- ///
- /// @param ledValues The color-value per led
- /// @return Zero on succes else negative
- ///
- virtual int write(const std::vector& ledValues) override;
-
- /// try to establish connection to opc server, if not connected yet
- ///
- /// @return true if connection is established
+ /// @return True, if connection is established
///
bool tryConnect();
- /// return the conenction state
///
- /// @return True if connection established
+ /// @brief Return the connection state
+ ///
+ /// @return True, if connection established
///
bool isConnected();
- /// transfer current opc_data buffer to opc server
///
- /// @return amount of transfered bytes. -1 error while transfering, -2 error while connecting
+ /// @brief Transfer current opc_data buffer to opc server
+ ///
+ /// @return amount of transferred bytes. -1 error while transferring, -2 error while connecting
///
int transferData();
- /// send system exclusive commands
///
- /// @param systemId fadecandy device identifier (for standard fadecandy always: 1)
- /// @param commandId id of command
- /// @param msg the sysEx message
- /// @return amount bytes written, -1 if fail
+ /// @brief Send system exclusive commands
+ ///
+ /// @param[in] systemId fadecandy device identifier (for standard fadecandy always: 1)
+ /// @param[in] commandId id of command
+ /// @param[in] msg the sysEx message
+ /// @return amount bytes written, -1 if failed
int sendSysEx(uint8_t systemId, uint8_t commandId, QByteArray msg);
- /// sends the configuration to fcserver
+ ///
+ /// @brief Sends the configuration to fadecandy cserver
+ ///
void sendFadeCandyConfiguration();
QTcpSocket* _client;
@@ -135,3 +147,5 @@ private:
bool _ledOnOff;
};
+
+#endif // LEDEVICEFADECANDY_H
diff --git a/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp
index a5d79cb1..7912f093 100644
--- a/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp
+++ b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp
@@ -1,8 +1,8 @@
// Local-Hyperion includes
#include "LedDeviceNanoleaf.h"
-// ssdp discover
#include
+#include
// Qt includes
#include
@@ -12,61 +12,66 @@
#include
#include
-//
-static const bool verbose = false;
-static const bool verbose3 = false;
+// Constants
+namespace {
-// Controller configuration settings
-static const char CONFIG_ADDRESS[] = "host";
-//static const char CONFIG_PORT[] = "port";
-static const char CONFIG_AUTH_TOKEN[] ="token";
+const bool verbose = false;
+const bool verbose3 = false;
-static const char CONFIG_PANEL_ORDER_TOP_DOWN[] ="panelOrderTopDown";
-static const char CONFIG_PANEL_ORDER_LEFT_RIGHT[] ="panelOrderLeftRight";
-static const char CONFIG_PANEL_START_POS[] ="panelStartPos";
+// Configuration settings
+const char CONFIG_ADDRESS[] = "host";
+//const char CONFIG_PORT[] = "port";
+const char CONFIG_AUTH_TOKEN[] ="token";
+
+const char CONFIG_PANEL_ORDER_TOP_DOWN[] ="panelOrderTopDown";
+const char CONFIG_PANEL_ORDER_LEFT_RIGHT[] ="panelOrderLeftRight";
+const char CONFIG_PANEL_START_POS[] ="panelStartPos";
// Panel configuration settings
-static const char PANEL_LAYOUT[] = "layout";
-static const char PANEL_NUM[] = "numPanels";
-static const char PANEL_ID[] = "panelId";
-static const char PANEL_POSITIONDATA[] = "positionData";
-static const char PANEL_SHAPE_TYPE[] = "shapeType";
-//static const char PANEL_ORIENTATION[] = "0";
-static const char PANEL_POS_X[] = "x";
-static const char PANEL_POS_Y[] = "y";
+const char PANEL_LAYOUT[] = "layout";
+const char PANEL_NUM[] = "numPanels";
+const char PANEL_ID[] = "panelId";
+const char PANEL_POSITIONDATA[] = "positionData";
+const char PANEL_SHAPE_TYPE[] = "shapeType";
+//const char PANEL_ORIENTATION[] = "0";
+const char PANEL_POS_X[] = "x";
+const char PANEL_POS_Y[] = "y";
// List of State Information
-static const char STATE_ON[] = "on";
-static const char STATE_ONOFF_VALUE[] = "value";
-static const char STATE_VALUE_TRUE[] = "true";
-static const char STATE_VALUE_FALSE[] = "false";
+const char STATE_ON[] = "on";
+const char STATE_ONOFF_VALUE[] = "value";
+const char STATE_VALUE_TRUE[] = "true";
+const char STATE_VALUE_FALSE[] = "false";
// Device Data elements
-static const char DEV_DATA_NAME[] = "name";
-static const char DEV_DATA_MODEL[] = "model";
-static const char DEV_DATA_MANUFACTURER[] = "manufacturer";
-static const char DEV_DATA_FIRMWAREVERSION[] = "firmwareVersion";
+const char DEV_DATA_NAME[] = "name";
+const char DEV_DATA_MODEL[] = "model";
+const char DEV_DATA_MANUFACTURER[] = "manufacturer";
+const char DEV_DATA_FIRMWAREVERSION[] = "firmwareVersion";
// Nanoleaf Stream Control elements
-//static const char STREAM_CONTROL_IP[] = "streamControlIpAddr";
-static const char STREAM_CONTROL_PORT[] = "streamControlPort";
-//static const char STREAM_CONTROL_PROTOCOL[] = "streamControlProtocol";
+//const char STREAM_CONTROL_IP[] = "streamControlIpAddr";
+const char STREAM_CONTROL_PORT[] = "streamControlPort";
+//const char STREAM_CONTROL_PROTOCOL[] = "streamControlProtocol";
const quint16 STREAM_CONTROL_DEFAULT_PORT = 60222; //Fixed port for Canvas;
// Nanoleaf OpenAPI URLs
-static const char API_DEFAULT_PORT[] = "16021";
-static const char API_URL_FORMAT[] = "http://%1:%2/api/v1/%3/%4";
-static const char API_ROOT[] = "";
-//static const char API_EXT_MODE_STRING_V1[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\"}}";
-static const char API_EXT_MODE_STRING_V2[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\", \"extControlVersion\" : \"v2\"}}";
-static const char API_STATE[] ="state";
-static const char API_PANELLAYOUT[] = "panelLayout";
-static const char API_EFFECT[] = "effects";
+const int API_DEFAULT_PORT = 16021;
+const char API_BASE_PATH[] = "/api/v1/%1/";
+const char API_ROOT[] = "";
+//const char API_EXT_MODE_STRING_V1[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\"}}";
+const char API_EXT_MODE_STRING_V2[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\", \"extControlVersion\" : \"v2\"}}";
+const char API_STATE[] ="state";
+const char API_PANELLAYOUT[] = "panelLayout";
+const char API_EFFECT[] = "effects";
// Nanoleaf ssdp services
-static const char SSDP_CANVAS[] = "nanoleaf:nl29";
-static const char SSDP_LIGHTPANELS[] = "nanoleaf_aurora:light";
-const int SSDP_TIMEOUT = 5000; // timout in ms
+const char SSDP_ID[] = "ssdp:all";
+const char SSDP_FILTER_HEADER[] = "ST";
+const char SSDP_CANVAS[] = "nanoleaf:nl29";
+const char SSDP_LIGHTPANELS[] = "nanoleaf_aurora:light";
+
+} //End of constants
// Nanoleaf Panel Shapetypes
enum SHAPETYPES {
@@ -84,6 +89,23 @@ enum EXTCONTROLVERSIONS {
EXTCTRLVER_V2
};
+LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject &deviceConfig)
+ : ProviderUdp()
+ ,_restApi(nullptr)
+ ,_apiPort(API_DEFAULT_PORT)
+ ,_topDown(true)
+ ,_leftRight(true)
+ ,_startPos(0)
+ ,_endPos(0)
+ ,_extControlVersion (EXTCTRLVER_V2),
+ _panelLedCount(0)
+{
+ _devConfig = deviceConfig;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
+}
+
LedDevice* LedDeviceNanoleaf::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceNanoleaf(deviceConfig);
@@ -91,17 +113,11 @@ LedDevice* LedDeviceNanoleaf::construct(const QJsonObject &deviceConfig)
LedDeviceNanoleaf::~LedDeviceNanoleaf()
{
- _networkmanager->deleteLater();
-}
-
-LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject &deviceConfig)
- : ProviderUdp()
-{
- _devConfig = deviceConfig;
- _deviceReady = false;
- _networkmanager = nullptr;
- _extControlVersion = EXTCTRLVER_V2;
- _panelLedCount = 0;
+ if ( _restApi != nullptr )
+ {
+ delete _restApi;
+ _restApi = nullptr;
+ }
}
bool LedDeviceNanoleaf::init(const QJsonObject &deviceConfig)
@@ -116,74 +132,89 @@ bool LedDeviceNanoleaf::init(const QJsonObject &deviceConfig)
DebugIf(verbose, _log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData() );
- bool isInitOK = LedDevice::init(deviceConfig);
+ bool isInitOK = false;
- if ( isInitOK )
+ if ( LedDevice::init(deviceConfig) )
{
uint configuredLedCount = this->getLedCount();
Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
Debug(_log, "LedCount : %u", configuredLedCount);
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
- Debug(_log, "RefreshTime : %d", _refresh_timer_interval);
+ Debug(_log, "RefreshTime : %d", _refreshTimerInterval_ms);
Debug(_log, "LatchTime : %d", this->getLatchTime());
// Read panel organisation configuration
if ( deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].isString() )
- _topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toString().toInt() == 0 ? true : false;
+ {
+ _topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toString().toInt() == 0;
+ }
else
- _topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toInt() == 0 ? true : false;
+ {
+ _topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toInt() == 0;
+ }
if ( deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].isString() )
- _leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toString().toInt() == 0 ? true : false;
+ {
+ _leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toString().toInt() == 0;
+ }
else
- _leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toInt() == 0 ? true : false;
+ {
+ _leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toInt() == 0;
+ }
- _startPos = deviceConfig[ CONFIG_PANEL_START_POS ].toInt(0);
+ _startPos = static_cast( deviceConfig[ CONFIG_PANEL_START_POS ].toInt(0) );
+
+ // TODO: Allow to handle port dynamically
//Set hostname as per configuration and_defaultHost default port
_hostname = deviceConfig[ CONFIG_ADDRESS ].toString();
- _api_port = API_DEFAULT_PORT;
- _auth_token = deviceConfig[ CONFIG_AUTH_TOKEN ].toString();
+ _apiPort = API_DEFAULT_PORT;
+ _authToken = deviceConfig[ CONFIG_AUTH_TOKEN ].toString();
- //If host not configured then discover device
+ //If host not configured the init failed
if ( _hostname.isEmpty() )
{
- //Discover Nanoleaf device
- if ( !discoverDevice() )
+ this->setInError("No target hostname nor IP defined");
+ isInitOK = false;
+ }
+ else
+ {
+ if ( initRestAPI( _hostname, _apiPort, _authToken ) )
{
- this->setInError("No target IP defined nor Nanoleaf device was discovered");
- return false;
+ // Read LedDevice configuration and validate against device configuration
+ if ( initLedsConfiguration() )
+ {
+ // Set UDP streaming host and port
+ _devConfig["host"] = _hostname;
+ _devConfig["port"] = STREAM_CONTROL_DEFAULT_PORT;
+
+ isInitOK = ProviderUdp::init(_devConfig);
+ Debug(_log, "Hostname/IP : %s", QSTRING_CSTR( _hostname ));
+ Debug(_log, "Port : %d", _port);
+ }
}
}
-
- // Set UDP streaming port
- _devConfig["host"] = _hostname;
- _devConfig["port"] = STREAM_CONTROL_DEFAULT_PORT;
- isInitOK = ProviderUdp::init(_devConfig);
-
- Debug(_log, "Hostname/IP : %s", QSTRING_CSTR( _hostname ));
- Debug(_log, "Port : %d", _port);
}
return isInitOK;
}
-bool LedDeviceNanoleaf::initLeds()
+bool LedDeviceNanoleaf::initLedsConfiguration()
{
bool isInitOK = true;
//Get Nanoleaf device details and configuration
- _networkmanager = new QNetworkAccessManager();
// Read Panel count and panel Ids
- QString url = getUrl(_hostname, _api_port, _auth_token, API_ROOT );
- QJsonDocument doc = getJson( url );
- if ( this->isInError() )
+ _restApi->setPath(API_ROOT);
+ httpResponse response = _restApi->get();
+ if ( response.error() )
{
+ this->setInError ( response.getErrorReason() );
isInitOK = false;
}
else
{
- QJsonObject jsonAllPanelInfo = doc.object();
+ QJsonObject jsonAllPanelInfo = response.getBody().object();
QString deviceName = jsonAllPanelInfo[DEV_DATA_NAME].toString();
_deviceModel = jsonAllPanelInfo[DEV_DATA_MODEL].toString();
@@ -205,7 +236,7 @@ bool LedDeviceNanoleaf::initLeds()
std::map> panelMap;
// Loop over all children.
- for (const QJsonValue & value : positionData)
+ for (const QJsonValue value : positionData)
{
QJsonObject panelObj = value.toObject();
@@ -239,9 +270,13 @@ bool LedDeviceNanoleaf::initLeds()
DebugIf(verbose3, _log, "panelMap[%u][%u]=%u", posY->first, posX->first, posX->second );
if ( _topDown )
+ {
_panelIds.push_back(posX->second);
+ }
else
+ {
_panelIds.push_front(posX->second);
+ }
}
}
else
@@ -252,9 +287,13 @@ bool LedDeviceNanoleaf::initLeds()
DebugIf(verbose3, _log, "panelMap[%u][%u]=%u", posY->first, posX->first, posX->second );
if ( _topDown )
+ {
_panelIds.push_back(posX->second);
+ }
else
+ {
_panelIds.push_front(posX->second);
+ }
}
}
}
@@ -300,197 +339,199 @@ bool LedDeviceNanoleaf::initLeds()
}
}
}
+ return isInitOK;
+}
+bool LedDeviceNanoleaf::initRestAPI(const QString &hostname, const int port, const QString &token )
+{
+ bool isInitOK = false;
+
+ if ( _restApi == nullptr )
+ {
+ _restApi = new ProviderRestApi(hostname, port );
+
+ //Base-path is api-path + authentication token
+ _restApi->setBasePath( QString(API_BASE_PATH).arg(token) );
+
+ isInitOK = true;
+ }
return isInitOK;
}
int LedDeviceNanoleaf::open()
{
int retval = -1;
- _deviceReady = false;
+ _isDeviceReady = false;
- if ( init(_devConfig) )
+ // Set Nanoleaf to External Control (UDP) mode
+ Debug(_log, "Set Nanoleaf to External Control (UDP) streaming mode");
+ QJsonDocument responseDoc = changeToExternalControlMode();
+ // Resolve port for Light Panels
+ QJsonObject jsonStreamControllInfo = responseDoc.object();
+ if ( ! jsonStreamControllInfo.isEmpty() )
{
- if ( !initNetwork() )
- {
- this->setInError( "UDP Network error!" );
- }
- else
- {
- if ( initLeds() )
- {
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
- }
+ //Set default streaming port
+ _port = static_cast(jsonStreamControllInfo[STREAM_CONTROL_PORT].toInt());
+ }
+
+ if ( ProviderUdp::open() == 0 )
+ {
+ // Everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
}
return retval;
}
-bool LedDeviceNanoleaf::discoverDevice()
+QJsonObject LedDeviceNanoleaf::discover()
{
+ QJsonObject devicesDiscovered;
+ devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
- bool isDeviceFound (false);
- // device searching by ssdp
- QString address;
+ QJsonArray deviceList;
+
+ // Discover Nanoleaf Devices
SSDPDiscover discover;
- // Discover Canvas device
- address = discover.getFirstService(searchType::STY_WEBSERVER, SSDP_CANVAS, SSDP_TIMEOUT);
+ // Search for Canvas and Light-Panels
+ QString searchTargetFilter = QString("%1|%2").arg(SSDP_CANVAS, SSDP_LIGHTPANELS);
- //No Canvas device not found
- if ( address.isEmpty() ) {
- // Discover Light Panels (Aurora) device
- address = discover.getFirstService(searchType::STY_WEBSERVER, SSDP_LIGHTPANELS, SSDP_TIMEOUT);
+ discover.setSearchFilter(searchTargetFilter, SSDP_FILTER_HEADER);
+ QString searchTarget = SSDP_ID;
- if ( address.isEmpty() ) {
- Warning(_log, "No Nanoleaf device discovered");
- }
+ if ( discover.discoverServices(searchTarget) > 0 )
+ {
+ deviceList = discover.getServicesDiscoveredJson();
}
- // Canvas or Light Panels found
- if ( ! address.isEmpty() ) {
- Info(_log, "Nanoleaf device discovered at [%s]", QSTRING_CSTR( address ));
- isDeviceFound = true;
- // Resolve hostname and port (or use default API port)
- #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- QStringList addressparts = address.split(":", Qt::SkipEmptyParts);
- #else
- QStringList addressparts = address.split(":", QString::SkipEmptyParts);
- #endif
- _hostname = addressparts[0];
- _api_port = addressparts[1];
- }
- return isDeviceFound;
+ devicesDiscovered.insert("devices", deviceList);
+ Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ return devicesDiscovered;
}
+QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
+{
+ Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ QJsonObject properties;
+
+ // Get Nanoleaf device properties
+ QString host = params["host"].toString("");
+ if ( !host.isEmpty() )
+ {
+ QString authToken = params["token"].toString("");
+ QString filter = params["filter"].toString("");
+
+ // Resolve hostname and port (or use default API port)
+ QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
+ QString apiHost = addressparts[0];
+ int apiPort;
+
+ if ( addressparts.size() > 1)
+ {
+ apiPort = addressparts[1].toInt();
+ }
+ else
+ {
+ apiPort = API_DEFAULT_PORT;
+ }
+
+ initRestAPI(apiHost, apiPort, authToken);
+ _restApi->setPath(filter);
+
+ // Perform request
+ httpResponse response = _restApi->get();
+ if ( response.error() )
+ {
+ Warning (_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
+ }
+
+ properties.insert("properties", response.getBody().object());
+
+ Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ }
+ return properties;
+}
+
+void LedDeviceNanoleaf::identify(const QJsonObject& params)
+{
+ Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ QJsonObject properties;
+
+ // Get Nanoleaf device properties
+ QString host = params["host"].toString("");
+ if ( !host.isEmpty() )
+ {
+ QString authToken = params["token"].toString("");
+
+ // Resolve hostname and port (or use default API port)
+ QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
+ QString apiHost = addressparts[0];
+ int apiPort;
+
+ if ( addressparts.size() > 1)
+ {
+ apiPort = addressparts[1].toInt();
+ }
+ else
+ {
+ apiPort = API_DEFAULT_PORT;
+ }
+
+ initRestAPI(apiHost, apiPort, authToken);
+ _restApi->setPath("identify");
+
+ // Perform request
+ httpResponse response = _restApi->put();
+ if ( response.error() )
+ {
+ Warning (_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
+ }
+ }
+}
+
+bool LedDeviceNanoleaf::powerOn()
+{
+ if ( _isDeviceReady)
+ {
+ //Power-on Nanoleaf device
+ _restApi->setPath(API_STATE);
+ _restApi->put( getOnOffRequest(true) );
+ }
+ return true;
+}
+
+bool LedDeviceNanoleaf::powerOff()
+{
+ if ( _isDeviceReady)
+ {
+ //Power-off the Nanoleaf device physically
+ _restApi->setPath(API_STATE);
+ _restApi->put( getOnOffRequest(false) );
+ }
+ return true;
+}
+
+QString LedDeviceNanoleaf::getOnOffRequest (bool isOn ) const
+{
+ QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
+ return QString( "{\"%1\":{\"%2\":%3}}" ).arg(STATE_ON, STATE_ONOFF_VALUE, state);
+}
QJsonDocument LedDeviceNanoleaf::changeToExternalControlMode()
{
-
- QString url = getUrl(_hostname, _api_port, _auth_token, API_EFFECT );
- QJsonDocument jsonDoc;
-
_extControlVersion = EXTCTRLVER_V2;
//Enable UDP Mode v2
- jsonDoc= putJson(url, API_EXT_MODE_STRING_V2);
- return jsonDoc;
-}
+ _restApi->setPath(API_EFFECT);
+ httpResponse response =_restApi->put(API_EXT_MODE_STRING_V2);
-QString LedDeviceNanoleaf::getUrl(QString host, QString port, QString auth_token, QString endpoint) const {
- return QString(API_URL_FORMAT).arg(host, port, auth_token, endpoint);
-}
-
-QJsonDocument LedDeviceNanoleaf::getJson(QString url)
-{
-
- Debug(_log, "GET: [%s]", QSTRING_CSTR( url ));
-
- // Perfrom request
- QNetworkRequest request(url);
- QNetworkReply* reply = _networkmanager->get(request);
- // Connect requestFinished signal to quit slot of the loop.
- QEventLoop loop;
- loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
- // Go into the loop until the request is finished.
- loop.exec();
-
- QJsonDocument jsonDoc;
- if(reply->operation() == QNetworkAccessManager::GetOperation)
- {
- jsonDoc = handleReply( reply );
- }
- // Free space.
- reply->deleteLater();
- // Return response
- return jsonDoc;
-}
-
-QJsonDocument LedDeviceNanoleaf::putJson(QString url, QString json)
-{
-
- Debug(_log, "PUT: [%s] [%s]", QSTRING_CSTR( url ), QSTRING_CSTR( json ) );
- // Perfrom request
- QNetworkRequest request(url);
- QNetworkReply* reply = _networkmanager->put(request, json.toUtf8());
- // Connect requestFinished signal to quit slot of the loop.
- QEventLoop loop;
- loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
- // Go into the loop until the request is finished.
- loop.exec();
-
- QJsonDocument jsonDoc;
- if(reply->operation() == QNetworkAccessManager::PutOperation)
- {
- jsonDoc = handleReply( reply );
- }
- // Free space.
- reply->deleteLater();
-
- // Return response
- return jsonDoc;
-}
-
-QJsonDocument LedDeviceNanoleaf::handleReply(QNetworkReply* const &reply )
-{
-
- QJsonDocument jsonDoc;
-
- int httpStatusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
- Debug(_log, "Reply.httpStatusCode [%d]", httpStatusCode );
-
- if(reply->error() == QNetworkReply::NoError)
- {
- if ( httpStatusCode != 204 ){
- QByteArray response = reply->readAll();
- QJsonParseError error;
- jsonDoc = QJsonDocument::fromJson(response, &error);
- if (error.error != QJsonParseError::NoError)
- {
- this->setInError ( "Got invalid response" );
- }
- else {
- //Debug
- QString strJson(jsonDoc.toJson(QJsonDocument::Compact));
- DebugIf(verbose, _log, "Reply: [%s]", strJson.toUtf8().constData() );
- }
- }
- }
- else
- {
- QString errorReason;
- if ( httpStatusCode > 0 ) {
- QString httpReason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
- QString advise;
- switch ( httpStatusCode ) {
- case 400:
- advise = "Check Request Body";
- break;
- case 401:
- advise = "Check Authentication Token (API Key)";
- break;
- case 404:
- advise = "Check Resource given";
- break;
- default:
- break;
- }
- errorReason = QString ("%1:%2 [%3 %4] - %5").arg(_hostname, _api_port, QString(httpStatusCode) , httpReason, advise);
- }
- else {
- errorReason = QString ("%1:%2 - %3").arg(_hostname, _api_port, reply->errorString());
- }
- this->setInError ( errorReason );
- }
- // Return response
- return jsonDoc;
+ return response.getBody();
}
int LedDeviceNanoleaf::write(const std::vector & ledValues)
{
-
int retVal = 0;
uint udpBufferSize;
@@ -573,46 +614,6 @@ int LedDeviceNanoleaf::write(const std::vector & ledValues)
return retVal;
}
-QString LedDeviceNanoleaf::getOnOffRequest (bool isOn ) const
-{
- QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
- return QString( "{\"%1\":{\"%2\":%3}}" ).arg(STATE_ON, STATE_ONOFF_VALUE, state);
-}
-
-int LedDeviceNanoleaf::switchOn()
-{
- if ( _deviceReady)
- {
- // Set Nanoleaf to External Control (UDP) mode
- Debug(_log, "Set Nanoleaf to External Control (UDP) streaming mode");
- QJsonDocument responseDoc = changeToExternalControlMode();
- // Resolve port for Ligh Panels
- QJsonObject jsonStreamControllInfo = responseDoc.object();
- if ( ! jsonStreamControllInfo.isEmpty() ) {
- _port = static_cast(jsonStreamControllInfo[STREAM_CONTROL_PORT].toInt());
- }
-
- //Switch on Nanoleaf device
- QString url = getUrl(_hostname, _api_port, _auth_token, API_STATE );
- putJson(url, this->getOnOffRequest(true) );
- }
- return 0;
-}
-
-int LedDeviceNanoleaf::switchOff()
-{
- //Set all LEDs to Black
- int rc = LedDevice::switchOff();
-
- if ( _deviceReady)
- {
- //Switch off Nanoleaf device physically
- QString url = getUrl(_hostname, _api_port, _auth_token, API_STATE );
- putJson(url, getOnOffRequest(false) );
- }
- return rc;
-}
-
std::string LedDeviceNanoleaf:: uint8_vector_to_hex_string( const std::vector& buffer ) const
{
std::stringstream ss;
diff --git a/libsrc/leddevice/dev_net/LedDeviceNanoleaf.h b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.h
index d2d1f0af..02c1a90f 100644
--- a/libsrc/leddevice/dev_net/LedDeviceNanoleaf.h
+++ b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.h
@@ -1,12 +1,11 @@
-#pragma once
+#ifndef LEDEVICENANOLEAF_H
+#define LEDEVICENANOLEAF_H
-// Leddevice includes
+// LedDevice includes
#include
+#include "ProviderRestApi.h"
#include "ProviderUdp.h"
-// ssdp discover
-#include
-
// Qt includes
#include
#include
@@ -19,85 +18,141 @@ class LedDeviceNanoleaf : public ProviderUdp
{
public:
///
- /// Constructs the LedDevice for Nanoleaf LightPanels (aka Aurora) or Canvas
+ /// @brief Constructs LED-device for Nanoleaf LightPanels (aka Aurora) or Canvas
///
- /// following code shows all config options
+ /// following code shows all configuration options
/// @code
/// "device" :
/// {
- /// "type" : "nanoleaf"
- /// "output" : "hostname or IP", // Optional. If empty, device is tried to be discovered
- /// "token" : "Authentication Token",
+ /// "type" : "nanoleaf"
+ /// "host" : "hostname or IP",
+ /// "token": "Authentication Token",
/// },
///@endcode
///
- /// @param deviceConfig json config for nanoleaf
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceNanoleaf(const QJsonObject &deviceConfig);
///
- /// Destructor of the LedDevice; closes the tcp client
+ /// @brief Destructor of the LED-device
///
virtual ~LedDeviceNanoleaf() override;
- /// Constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig);
- /// Switch the device on
- virtual int switchOn() override;
+ ///
+ /// @brief Discover Nanoleaf devices available (for configuration).
+ ///
+ /// @return A JSON structure holding a list of devices found
+ ///
+ virtual QJsonObject discover() override;
- /// Switch the device off
- virtual int switchOff() override;
+ ///
+ /// @brief Get the Nanoleaf device's resource properties
+ ///
+ /// Following parameters are required
+ /// @code
+ /// {
+ /// "host" : "hostname or IP [:port]",
+ /// "token" : "authentication token",
+ /// "filter": "resource to query", root "/" is used, if empty
+ /// }
+ ///@endcode
+ ///
+ /// @param[in] params Parameters to query device
+ /// @return A JSON structure holding the device's properties
+ ///
+ virtual QJsonObject getProperties(const QJsonObject& params) override;
+
+ ///
+ /// @brief Send an update to the Nanoleaf device to identify it.
+ ///
+ /// Following parameters are required
+ /// @code
+ /// {
+ /// "host" : "hostname or IP [:port]",
+ /// "token" : "authentication token",
+ /// }
+ ///@endcode
+ ///
+ /// @param[in] params Parameters to address device
+ ///
+ virtual void identify(const QJsonObject& params) override;
protected:
///
- /// Writes the led color values to the led-device
+ /// @brief Initialise the Nanoleaf device's configuration and network address details
///
- /// @param ledValues The color-value per led
- /// @return Zero on succes else negative
- ///
- virtual int write(const std::vector & ledValues) override;
-
- ///
- /// Initialise Nanoleaf device's configuration and network address details
- ///
- /// @param deviceConfig the json device config
- /// @return True if success
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
///
bool init(const QJsonObject &deviceConfig) override;
///
- /// Get Nanoleaf device details and configuration
+ /// @brief Opens the output device.
///
- /// @return True, if Nanoleaf device capabilities fit configuration
- ///
- bool initLeds();
-
- ///
- /// Opens and initiatialises the output device
- ///
- /// @return Zero on succes (i.e. device is ready and enabled) else negative
+ /// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
-private:
///
- /// Discover Nanoleaf device via SSDP identifiers
+ /// @brief Writes the RGB-Color values to the LEDs.
///
- /// @return True, if Nanoleaf device was found
- ///
- bool discoverDevice();
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ //////
+ virtual int write(const std::vector & ledValues) override;
///
- /// Change Nanoleaf device to External Control (UDP) mode
+ /// @brief Power-/turn on the Nanoleaf device.
+ ///
+ /// @brief Store the device's original state.
+ ///
+ virtual bool powerOn() override;
+
+ ///
+ /// @brief Power-/turn off the Nanoleaf device.
+ ///
+ /// @return True if success
+ ///
+ virtual bool powerOff() override;
+
+private:
+
+ ///
+ /// @brief Initialise the access to the REST-API wrapper
+ ///
+ /// @param[in] host
+ /// @param[in] port
+ /// @param[in] authentication token
+ ///
+ /// @return True, if success
+ ///
+ bool initRestAPI(const QString &hostname, const int port, const QString &token );
+
+ ///
+ /// @brief Get Nanoleaf device details and configuration
+ ///
+ /// @return True, if Nanoleaf device capabilities fit configuration
+ ///
+ bool initLedsConfiguration();
+
+ ///
+ /// @brief Change Nanoleaf device to External Control (UDP) mode
///
/// @return Response from device
- ///
+ ///@brief
QJsonDocument changeToExternalControlMode();
///
- /// Get command to switch Nanoleaf device on or off
+ /// @brief Get command to power Nanoleaf device on or off
///
/// @param isOn True, if to switch on device
/// @return Command to switch device on/off
@@ -105,54 +160,18 @@ private:
QString getOnOffRequest (bool isOn ) const;
///
- /// Get command as url
- ///
- /// @param host Hostname or IP
- /// @param port IP-Port
- /// @param _auth_token Authorization token
- /// @param Endpoint command for request
- /// @return Url to execute endpoint/command
- ///
- QString getUrl(QString host, QString port, QString auth_token, QString endpoint) const;
-
- ///
- /// Execute GET request
- ///
- /// @param url GET request for url
- /// @return Response from device
- ///
- QJsonDocument getJson(QString url);
-
- ///
- /// Execute PUT request
- ///
- /// @param Url for PUT request
- /// @param json Command for request
- /// @return Response from device
- ///
- QJsonDocument putJson(QString url, QString json);
-
- ///
- /// Handle replys for GET and PUT requests
- ///
- /// @param reply Network reply
- /// @return Response for request, if no error
- ///
- QJsonDocument handleReply(QNetworkReply* const &reply );
-
- ///
- /// convert vector to hex string
+ /// @brief Convert vector to hex string
///
/// @param uint8_t vector
/// @return vector as string of hex values
std::string uint8_vector_to_hex_string( const std::vector& buffer ) const;
- // QNetworkAccessManager object for sending requests.
- QNetworkAccessManager* _networkmanager;
+ ///REST-API wrapper
+ ProviderRestApi* _restApi;
QString _hostname;
- QString _api_port;
- QString _auth_token;
+ int _apiPort;
+ QString _authToken;
bool _topDown;
bool _leftRight;
@@ -163,9 +182,13 @@ private:
QString _deviceModel;
QString _deviceFirmwareVersion;
ushort _extControlVersion;
- /// The number of panels with leds
+
+ /// The number of panels with LEDs
uint _panelLedCount;
- /// Array of the pannel ids.
+
+ /// Array of the panel ids.
QVector _panelIds;
};
+
+#endif // LEDEVICENANOLEAF_H
diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp
index d4268799..c2ca0835 100644
--- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp
+++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp
@@ -1,98 +1,129 @@
// Local-Hyperion includes
#include "LedDevicePhilipsHue.h"
-// ssdp discover
#include
+#include
+
+#include
bool verbose = false;
+// Constants
+namespace {
+
// Configuration settings
-static const char CONFIG_ADDRESS[] = "output";
-//static const char CONFIG_PORT[] = "port";
-static const char CONFIG_USERNAME[] = "username";
-static const char CONFIG_CLIENTKEY[] = "clientkey";
-static const char CONFIG_BRIGHTNESSFACTOR[] = "brightnessFactor";
-static const char CONFIG_TRANSITIONTIME[] = "transitiontime";
-static const char CONFIG_BLACK_LIGHTS_TIMEOUT[] = "blackLightsTimeout";
-static const char CONFIG_ON_OFF_BLACK[] = "switchOffOnBlack";
-static const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
-static const char CONFIG_LIGHTIDS[] = "lightIds";
-static const char CONFIG_USE_HUE_ENTERTAINMENT_API[] = "useEntertainmentAPI";
-static const char CONFIG_GROUPID[] = "groupId";
+const char CONFIG_ADDRESS[] = "output";
+//const char CONFIG_PORT[] = "port";
+const char CONFIG_USERNAME[] = "username";
+const char CONFIG_CLIENTKEY[] = "clientkey";
+const char CONFIG_BRIGHTNESSFACTOR[] = "brightnessFactor";
+const char CONFIG_TRANSITIONTIME[] = "transitiontime";
+const char CONFIG_BLACK_LIGHTS_TIMEOUT[] = "blackLightsTimeout";
+const char CONFIG_ON_OFF_BLACK[] = "switchOffOnBlack";
+const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
+const char CONFIG_LIGHTIDS[] = "lightIds";
+const char CONFIG_USE_HUE_ENTERTAINMENT_API[] = "useEntertainmentAPI";
+const char CONFIG_GROUPID[] = "groupId";
-static const char CONFIG_VERBOSE[] = "verbose";
-static const char CONFIG_BRIGHTNESS_MIN[] = "brightnessMin";
-static const char CONFIG_BRIGHTNESS_MAX[] = "brightnessMax";
-static const char CONFIG_BRIGHTNESS_THRESHOLD[] = "brightnessThreshold";
+const char CONFIG_VERBOSE[] = "verbose";
+const char CONFIG_BRIGHTNESS_MIN[] = "brightnessMin";
+const char CONFIG_BRIGHTNESS_MAX[] = "brightnessMax";
+const char CONFIG_BRIGHTNESS_THRESHOLD[] = "brightnessThreshold";
-static const char CONFIG_SSL_HANDSHAKE_TIMEOUT_MIN[] = "sslHSTimeoutMin";
-static const char CONFIG_SSL_HANDSHAKE_TIMEOUT_MAX[] = "sslHSTimeoutMax";
-static const char CONFIG_SSL_READ_TIMEOUT[] = "sslReadTimeout";
+const char CONFIG_SSL_HANDSHAKE_TIMEOUT_MIN[] = "sslHSTimeoutMin";
+const char CONFIG_SSL_HANDSHAKE_TIMEOUT_MAX[] = "sslHSTimeoutMax";
+const char CONFIG_SSL_READ_TIMEOUT[] = "sslReadTimeout";
// Device Data elements
-static const char DEV_DATA_BRIDGEID[] = "bridgeid";
-static const char DEV_DATA_MODEL[] = "modelid";
-static const char DEV_DATA_NAME[] = "name";
-//static const char DEV_DATA_MANUFACTURER[] = "manufacturer";
-static const char DEV_DATA_FIRMWAREVERSION[] = "swversion";
-static const char DEV_DATA_APIVERSION[] = "apiversion";
+const char DEV_DATA_BRIDGEID[] = "bridgeid";
+const char DEV_DATA_MODEL[] = "modelid";
+const char DEV_DATA_NAME[] = "name";
+//const char DEV_DATA_MANUFACTURER[] = "manufacturer";
+const char DEV_DATA_FIRMWAREVERSION[] = "swversion";
+const char DEV_DATA_APIVERSION[] = "apiversion";
// Philips Hue OpenAPI URLs
-static const char API_DEFAULT_PORT[] = "80";
-static const char API_URL_FORMAT[] = "http://%1:%2/api/%3/%4";
-static const char API_ROOT[] = "";
-static const char API_STATE[] = "state";
-static const char API_CONFIG[] = "config";
-static const char API_LIGHTS[] = "lights";
-static const char API_GROUPS[] = "groups";
+const int API_DEFAULT_PORT = -1; //Use default port per communication scheme
+const char API_BASE_PATH[] = "/api/%1/";
+const char API_ROOT[] = "";
+const char API_STATE[] = "state";
+const char API_CONFIG[] = "config";
+const char API_LIGHTS[] = "lights";
+const char API_GROUPS[] = "groups";
// List of Group / Stream Information
-static const char API_GROUP_NAME[] = "name";
-static const char API_GROUP_TYPE[] = "type";
-static const char API_GROUP_TYPE_ENTERTAINMENT[] = "Entertainment";
-static const char API_STREAM[] = "stream";
-static const char API_STREAM_ACTIVE[] = "active";
-static const char API_STREAM_ACTIVE_VALUE_TRUE[] = "true";
-static const char API_STREAM_ACTIVE_VALUE_FALSE[] = "false";
-static const char API_STREAM_OWNER[] = "owner";
-static const char API_STREAM_RESPONSE_FORMAT[] = "/%1/%2/%3/%4";
+const char API_GROUP_NAME[] = "name";
+const char API_GROUP_TYPE[] = "type";
+const char API_GROUP_TYPE_ENTERTAINMENT[] = "Entertainment";
+const char API_STREAM[] = "stream";
+const char API_STREAM_ACTIVE[] = "active";
+const char API_STREAM_ACTIVE_VALUE_TRUE[] = "true";
+const char API_STREAM_ACTIVE_VALUE_FALSE[] = "false";
+const char API_STREAM_OWNER[] = "owner";
+const char API_STREAM_RESPONSE_FORMAT[] = "/%1/%2/%3/%4";
// List of resources
-static const char API_XY_COORDINATES[] = "xy";
-static const char API_BRIGHTNESS[] = "bri";
-static const char API_TRANSITIONTIME[] = "transitiontime";
-static const char API_MODEID[] = "modelid";
+const char API_XY_COORDINATES[] = "xy";
+const char API_BRIGHTNESS[] = "bri";
+//const char API_SATURATION[] = "sat";
+const char API_TRANSITIONTIME[] = "transitiontime";
+const char API_MODEID[] = "modelid";
// List of State Information
-static const char API_STATE_ON[] = "on";
-static const char API_STATE_VALUE_TRUE[] = "true";
-static const char API_STATE_VALUE_FALSE[] = "false";
+const char API_STATE_ON[] = "on";
+const char API_STATE_VALUE_TRUE[] = "true";
+const char API_STATE_VALUE_FALSE[] = "false";
// List of Error Information
-static const char API_ERROR[] = "error";
-static const char API_ERROR_ADDRESS[] = "address";
-static const char API_ERROR_DESCRIPTION[] = "description";
-static const char API_ERROR_TYPE[] = "type";
+const char API_ERROR[] = "error";
+const char API_ERROR_ADDRESS[] = "address";
+const char API_ERROR_DESCRIPTION[] = "description";
+const char API_ERROR_TYPE[] = "type";
// List of Success Information
-static const char API_SUCCESS[] = "success";
+const char API_SUCCESS[] = "success";
// Phlips Hue ssdp services
-static const char SSDP_ID[] = "urn:schemas-upnp-org:device:Basic:1";
-const int SSDP_TIMEOUT = 5000; // timout in ms
+const char SSDP_ID[] = "upnp:rootdevice";
+const char SSDP_FILTER[] = "(.*)IpBridge(.*)";
+const char SSDP_FILTER_HEADER[] = "SERVER";
// DTLS Connection / SSL / Cipher Suite
-static const char API_SSL_SERVER_NAME[] = "Hue";
-static const char API_SSL_SEED_CUSTOM[] = "dtls_client";
+const char API_SSL_SERVER_NAME[] = "Hue";
+const char API_SSL_SEED_CUSTOM[] = "dtls_client";
const int API_SSL_SERVER_PORT = 2100;
const int STREAM_CONNECTION_RETRYS = 5;
-const int STREAM_REWRITE_TIME = 20;
const int STREAM_SSL_HANDSHAKE_ATTEMPTS = 5;
-const int STREAM_SSL_HANDSHAKE_TIMEOUT_MIN = 400;
-const int STREAM_SSL_HANDSHAKE_TIMEOUT_MAX = 1000;
-const int STREAM_SSL_READ_TIMEOUT = 0;
+constexpr std::chrono::milliseconds STREAM_REWRITE_TIME{20};
const int SSL_CIPHERSUITES[2] = { MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, 0 };
+//Streaming message header and payload definition
+const uint8_t HEADER[] =
+{
+ 'H', 'u', 'e', 'S', 't', 'r', 'e', 'a', 'm', //protocol
+ 0x01, 0x00, //version 1.0
+ 0x01, //sequence number 1
+ 0x00, 0x00, //Reserved write 0’s
+ 0x01, //xy Brightness
+ 0x00, // Reserved, write 0’s
+};
+
+const uint8_t PAYLOAD_PER_LIGHT[] =
+{
+ 0x01, 0x00, 0x06, //light ID
+ //color: 16 bpc
+ 0xff, 0xff,
+ 0xff, 0xff,
+ 0xff, 0xff,
+ /*
+ (message.R >> 8) & 0xff, message.R & 0xff,
+ (message.G >> 8) & 0xff, message.G & 0xff,
+ (message.B >> 8) & 0xff, message.B & 0xff
+ */
+};
+
+} //End of constants
+
bool operator ==(const CiColor& p1, const CiColor& p2)
{
return ((p1.x == p2.x) && (p1.y == p2.y) && (p1.bri == p2.bri));
@@ -191,6 +222,7 @@ double CiColor::crossProduct(XYColor p1, XYColor p2)
bool CiColor::isPointInLampsReach(CiColor p, const CiColorTriangle &colorSpace)
{
+ bool rc = false;
XYColor v1 = { colorSpace.green.x - colorSpace.red.x, colorSpace.green.y - colorSpace.red.y };
XYColor v2 = { colorSpace.blue.x - colorSpace.red.x, colorSpace.blue.y - colorSpace.red.y };
XYColor q = { p.x - colorSpace.red.x, p.y - colorSpace.red.y };
@@ -198,9 +230,9 @@ bool CiColor::isPointInLampsReach(CiColor p, const CiColorTriangle &colorSpace)
double t = crossProduct(v1, q) / crossProduct(v1, v2);
if ( ( s >= 0.0 ) && ( t >= 0.0 ) && ( s + t <= 1.0 ) )
{
- return true;
+ rc = true;
}
- return false;
+ return rc;
}
XYColor CiColor::getClosestPointToPoint(XYColor a, XYColor b, CiColor p)
@@ -233,23 +265,24 @@ double CiColor::getDistanceBetweenTwoPoints(CiColor p1, XYColor p2)
LedDevicePhilipsHueBridge::LedDevicePhilipsHueBridge(const QJsonObject &deviceConfig)
: ProviderUdpSSL()
+ , _restApi(nullptr)
+ , _apiPort(API_DEFAULT_PORT)
, _useHueEntertainmentAPI(false)
- , _networkmanager(nullptr)
, _api_major(0)
, _api_minor(0)
, _api_patch(0)
, _isHueEntertainmentReady(false)
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
}
LedDevicePhilipsHueBridge::~LedDevicePhilipsHueBridge()
{
- if ( _networkmanager != nullptr )
+ if ( _restApi != nullptr )
{
- delete _networkmanager;
- _networkmanager = nullptr;
+ delete _restApi;
+ _restApi = nullptr;
}
}
@@ -267,81 +300,107 @@ bool LedDevicePhilipsHueBridge::init(const QJsonObject &deviceConfig)
DebugIf( verbose, _log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData() );
- bool isInitOK = LedDevice::init(deviceConfig);
+ bool isInitOK = false;
- log( "DeviceType", "%s", QSTRING_CSTR( this->getActiveDeviceType() ) );
- log( "LedCount", "%u", this->getLedCount() );
- log( "ColorOrder", "%s", QSTRING_CSTR( this->getColorOrder() ) );
- log( "RefreshTime", "%d", _refresh_timer_interval );
- log( "LatchTime", "%d", this->getLatchTime() );
-
- if ( isInitOK )
+ if ( LedDevice::init(deviceConfig) )
{
+
+ log( "DeviceType", "%s", QSTRING_CSTR( this->getActiveDeviceType() ) );
+ log( "LedCount", "%u", this->getLedCount() );
+ log( "ColorOrder", "%s", QSTRING_CSTR( this->getColorOrder() ) );
+ log( "RefreshTime", "%d", _refreshTimerInterval_ms );
+ log( "LatchTime", "%d", this->getLatchTime() );
+
//Set hostname as per configuration and_defaultHost default port
QString address = deviceConfig[ CONFIG_ADDRESS ].toString();
- if ( !address.isEmpty() )
+ //If host not configured the init failed
+ if ( address.isEmpty() )
{
- #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- QStringList addressparts = address.split(":", Qt::SkipEmptyParts);
- #else
- QStringList addressparts = address.split(":", QString::SkipEmptyParts);
- #endif
-
+ this->setInError("No target hostname nor IP defined");
+ return false;
+ }
+ else
+ {
+ QStringList addressparts = QStringUtils::split(address,":", QStringUtils::SplitBehavior::SkipEmptyParts);
_hostname = addressparts[0];
+ log( "Hostname/IP", "%s", QSTRING_CSTR( _hostname ) );
+
if ( addressparts.size() > 1 )
{
- _api_port = addressparts[1];
+ _apiPort = addressparts[1].toInt();
+ log( "Port", "%u", _apiPort );
}
- else
+
+ _username = deviceConfig[ CONFIG_USERNAME ].toString();
+
+ if ( initRestAPI( _hostname, _apiPort, _username ) )
{
- _api_port = API_DEFAULT_PORT;
+ if ( initMaps() )
+ {
+ isInitOK = ProviderUdpSSL::init(_devConfig);
+ }
}
}
- _username = deviceConfig[ CONFIG_USERNAME ].toString();
-
- log( "Hostname/IP", "%s", QSTRING_CSTR( _hostname ) );
- log( "Port", "%s", QSTRING_CSTR( _api_port ) );
}
+
+ return isInitOK;
+}
+
+bool LedDevicePhilipsHueBridge::initRestAPI(const QString &hostname, const int port, const QString &token )
+{
+ bool isInitOK = false;
+
+ if ( _restApi == nullptr )
+ {
+ _restApi = new ProviderRestApi(hostname, port);
+
+ //Base-path is api-path + authentication token (here username)
+ _restApi->setBasePath( QString(API_BASE_PATH).arg(token) );
+
+ isInitOK = true;
+ }
+
+
return isInitOK;
}
int LedDevicePhilipsHueBridge::open()
{
- return open( _hostname, _api_port, _username );
-}
+ int retval = -1;
+ _isDeviceReady = false;
-int LedDevicePhilipsHueBridge::open( const QString& hostname, const QString& port, const QString& username )
-{
- _deviceInError = false;
- bool isInitOK = true;
-
- //If host not configured then discover device
- if ( hostname.isEmpty() )
+ if( _useHueEntertainmentAPI )
{
- //Discover Philips Hue Bridge device
- if ( !discoverDevice() )
+ // Open bridge for streaming
+ if ( ProviderUdpSSL::open() == 0 )
{
- this->setInError( "No target IP defined nor Philips Hue Bridge was discovered" );
- return false;
+ // Everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
}
}
else
{
- _hostname = hostname;
- _api_port = port;
+ // Everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
}
- _username = username;
- //Get Philips Hue Bridge details and configuration
- if ( _networkmanager == nullptr )
+ return retval;
+}
+
+int LedDevicePhilipsHueBridge::close()
+{
+ _isDeviceReady = false;
+ int retval = 0;
+
+ if( _useHueEntertainmentAPI )
{
- _networkmanager = new QNetworkAccessManager();
+ retval = ProviderUdpSSL::close();
}
- isInitOK = initMaps();
-
- return isInitOK;
+ return retval;
}
const int *LedDevicePhilipsHueBridge::getCiphersuites()
@@ -366,8 +425,12 @@ void LedDevicePhilipsHueBridge::log(const char* msg, const char* type, ...)
QJsonDocument LedDevicePhilipsHueBridge::getAllBridgeInfos()
{
// Read Groups/ Lights and Light-Ids
- QString url = getUrl( _hostname, _api_port, _username, API_ROOT );
- return getJson( url );
+ _restApi->setPath(API_ROOT);
+
+ httpResponse response = _restApi->get();
+ checkApiError(response.getBody());
+
+ return response.getBody();
}
bool LedDevicePhilipsHueBridge::initMaps()
@@ -385,14 +448,17 @@ bool LedDevicePhilipsHueBridge::initMaps()
else
{
setBridgeConfig( doc );
- if( _useHueEntertainmentAPI ) setGroupMap( doc );
+ if( _useHueEntertainmentAPI )
+ {
+ setGroupMap( doc );
+ }
setLightsMap( doc );
}
return isInitOK;
}
-void LedDevicePhilipsHueBridge::setBridgeConfig(QJsonDocument doc)
+void LedDevicePhilipsHueBridge::setBridgeConfig(const QJsonDocument &doc)
{
QJsonObject jsonConfigInfo = doc.object()[ API_CONFIG ].toObject();
if ( verbose )
@@ -406,12 +472,7 @@ void LedDevicePhilipsHueBridge::setBridgeConfig(QJsonDocument doc)
_deviceFirmwareVersion = jsonConfigInfo[DEV_DATA_FIRMWAREVERSION].toString();
_deviceAPIVersion = jsonConfigInfo[DEV_DATA_APIVERSION].toString();
- #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- QStringList apiVersionParts = _deviceAPIVersion.split(".", Qt::SkipEmptyParts);
- #else
- QStringList apiVersionParts = _deviceAPIVersion.split(".", QString::SkipEmptyParts);
- #endif
-
+ QStringList apiVersionParts = QStringUtils::split(_deviceAPIVersion,".", QStringUtils::SplitBehavior::SkipEmptyParts);
if ( !apiVersionParts.isEmpty() )
{
_api_major = apiVersionParts[0].toUInt();
@@ -438,7 +499,7 @@ void LedDevicePhilipsHueBridge::setBridgeConfig(QJsonDocument doc)
log( "EntertainmentReady", "%d", _isHueEntertainmentReady );
}
-void LedDevicePhilipsHueBridge::setLightsMap(QJsonDocument doc)
+void LedDevicePhilipsHueBridge::setLightsMap(const QJsonDocument &doc)
{
QJsonObject jsonLightsInfo = doc.object()[ API_LIGHTS ].toObject();
@@ -447,12 +508,12 @@ void LedDevicePhilipsHueBridge::setLightsMap(QJsonDocument doc)
// Get all available light ids and their values
QStringList keys = jsonLightsInfo.keys();
- _ledCount = keys.size();
+ _ledCount = static_cast(keys.size());
_lightsMap.clear();
- for ( unsigned int i = 0; i < _ledCount; ++i )
+ for ( int i = 0; i < static_cast(_ledCount); ++i )
{
- _lightsMap.insert(keys.at(i).toUInt(), jsonLightsInfo.take(keys.at(i)).toObject());
+ _lightsMap.insert(keys.at(i).toUShort(), jsonLightsInfo.take(keys.at(i)).toObject());
}
if ( getLedCount() == 0 )
@@ -465,7 +526,7 @@ void LedDevicePhilipsHueBridge::setLightsMap(QJsonDocument doc)
}
}
-void LedDevicePhilipsHueBridge::setGroupMap(QJsonDocument doc)
+void LedDevicePhilipsHueBridge::setGroupMap(const QJsonDocument &doc)
{
QJsonObject jsonGroupsInfo = doc.object()[ API_GROUPS ].toObject();
@@ -474,47 +535,15 @@ void LedDevicePhilipsHueBridge::setGroupMap(QJsonDocument doc)
// Get all available group ids and their values
QStringList keys = jsonGroupsInfo.keys();
- unsigned int _groupsCount = keys.size();
+ int _groupsCount = keys.size();
_groupsMap.clear();
- for ( unsigned int i = 0; i < _groupsCount; ++i )
+ for ( int i = 0; i < _groupsCount; ++i )
{
- _groupsMap.insert( keys.at(i).toUInt(), jsonGroupsInfo.take(keys.at(i)).toObject() );
+ _groupsMap.insert( keys.at(i).toUShort(), jsonGroupsInfo.take(keys.at(i)).toObject() );
}
}
-bool LedDevicePhilipsHueBridge::discoverDevice()
-{
- bool isDeviceFound( false );
-
- // device searching by ssdp
- QString address;
- SSDPDiscover discover;
-
- // Discover Philips Hue Bridge
- address = discover.getFirstService( searchType::STY_WEBSERVER, SSDP_ID, SSDP_TIMEOUT );
- if ( address.isEmpty() )
- {
- Warning(_log, "No Philips Hue Bridge discovered" );
- }
- else
- {
- // Philips Hue Bridge found
- Info(_log, "Philips Hue Bridge discovered at [%s]", QSTRING_CSTR( address ) );
- isDeviceFound = true;
-
- #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- QStringList addressparts = address.split(":", Qt::SkipEmptyParts);
- #else
- QStringList addressparts = address.split(":", QString::SkipEmptyParts);
- #endif
-
- _hostname = addressparts[0];
- _api_port = addressparts[1];
- }
- return isDeviceFound;
-}
-
const QMap& LedDevicePhilipsHueBridge::getLightMap(void)
{
return _lightsMap;
@@ -525,7 +554,7 @@ const QMap& LedDevicePhilipsHueBridge::getGroupMap(void)
return _groupsMap;
}
-QString LedDevicePhilipsHueBridge::getGroupName(unsigned int groupId)
+QString LedDevicePhilipsHueBridge::getGroupName(quint16 groupId)
{
QString groupName;
if( _groupsMap.contains( groupId ) )
@@ -535,12 +564,12 @@ QString LedDevicePhilipsHueBridge::getGroupName(unsigned int groupId)
}
else
{
- Error(_log, "Group ID %d doesn't exists on this bridge", groupId );
+ Error(_log, "Group ID %u doesn't exists on this bridge", groupId );
}
return groupName;
}
-QJsonArray LedDevicePhilipsHueBridge::getGroupLights(unsigned int groupId)
+QJsonArray LedDevicePhilipsHueBridge::getGroupLights(quint16 groupId)
{
QJsonArray groupLights;
// search user groupid inside _groupsMap and create light if found
@@ -570,144 +599,50 @@ QJsonArray LedDevicePhilipsHueBridge::getGroupLights(unsigned int groupId)
return groupLights;
}
-QString LedDevicePhilipsHueBridge::getUrl(QString host, QString port, QString auth_token, QString endpoint) const {
- return QString(API_URL_FORMAT).arg( host, port, auth_token, endpoint );
-}
-
-QJsonDocument LedDevicePhilipsHueBridge::getJson(QString url)
+bool LedDevicePhilipsHueBridge::checkApiError(const QJsonDocument &response )
{
- DebugIf(verbose, _log, "GET: [%s]", QSTRING_CSTR( url ));
-
- // Perfrom request
- QNetworkRequest request(url);
- QNetworkReply* reply = _networkmanager->get(request);
- // Connect requestFinished signal to quit slot of the loop.
- QEventLoop loop;
- loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
- // Go into the loop until the request is finished.
- loop.exec();
-
- QJsonDocument jsonDoc;
- if( reply->operation() == QNetworkAccessManager::GetOperation )
- {
- jsonDoc = handleReply( reply );
- }
- // Free space.
- reply->deleteLater();
- // Return response
- return jsonDoc;
-}
-
-QJsonDocument LedDevicePhilipsHueBridge::putJson(QString url, QString json)
-{
- DebugIf(verbose, _log, "PUT: [%s] [%s]", QSTRING_CSTR( url ), QSTRING_CSTR( json ) );
- // Perfrom request
- QNetworkRequest request( url );
- QNetworkReply* reply = _networkmanager->put( request, json.toUtf8() );
- // Connect requestFinished signal to quit slot of the loop.
- QEventLoop loop;
- loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
- // Go into the loop until the request is finished.
- loop.exec();
-
- QJsonDocument jsonDoc;
- if( reply->operation() == QNetworkAccessManager::PutOperation )
- {
- jsonDoc = handleReply( reply );
- }
- // Free space.
- reply->deleteLater();
- // Return response
- return jsonDoc;
-}
-
-QJsonDocument LedDevicePhilipsHueBridge::handleReply(QNetworkReply* const &reply )
-{
- QJsonDocument jsonDoc;
-
- int httpStatusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
- DebugIf(verbose, _log, "Reply.httpStatusCode [%d]", httpStatusCode );
+ bool apiError = false;
QString errorReason;
- if( reply->error() == QNetworkReply::NoError )
+ QString strJson(response.toJson(QJsonDocument::Compact));
+ DebugIf(verbose, _log, "Reply: [%s]", strJson.toUtf8().constData() );
+
+ QVariantList rspList = response.toVariant().toList();
+ if ( !rspList.isEmpty() )
{
- if ( httpStatusCode != 204 )
+ QVariantMap map = rspList.first().toMap();
+ if ( map.contains( API_ERROR ) )
{
- QByteArray response = reply->readAll();
- QJsonParseError error;
- jsonDoc = QJsonDocument::fromJson(response, &error);
- if ( error.error != QJsonParseError::NoError )
+ // API call failed to execute an error message was returned
+ QString errorAddress = map.value(API_ERROR).toMap().value(API_ERROR_ADDRESS).toString();
+ QString errorDesc = map.value(API_ERROR).toMap().value(API_ERROR_DESCRIPTION).toString();
+ QString errorType = map.value(API_ERROR).toMap().value(API_ERROR_TYPE).toString();
+
+ log( "Error Type", "%s", QSTRING_CSTR( errorType ) );
+ log( "Error Address", "%s", QSTRING_CSTR( errorAddress ) );
+ log( "Error Address Description", "%s", QSTRING_CSTR( errorDesc ) );
+
+ if( errorType != "901" )
{
- this->setInError( "Got invalid response" );
- }
- else
- {
- QString strJson(jsonDoc.toJson(QJsonDocument::Compact));
- DebugIf(verbose, _log, "Reply: [%s]", strJson.toUtf8().constData() );
-
- QVariantList rspList = jsonDoc.toVariant().toList();
- if ( !rspList.isEmpty() )
- {
- QVariantMap map = rspList.first().toMap();
- if ( map.contains( API_ERROR ) )
- {
- // API call failsed to execute an error message was returned
- QString errorAddress = map.value(API_ERROR).toMap().value(API_ERROR_ADDRESS).toString();
- QString errorDesc = map.value(API_ERROR).toMap().value(API_ERROR_DESCRIPTION).toString();
- QString errorType = map.value(API_ERROR).toMap().value(API_ERROR_TYPE).toString();
-
- log( "Error Type", "%s", QSTRING_CSTR( errorType ) );
- log( "Error Address", "%s", QSTRING_CSTR( errorAddress ) );
- log( "Error Address Description", "%s", QSTRING_CSTR( errorDesc ) );
-
- if( errorType != "901" )
- {
- errorReason = QString ("(%1) %2, Resource:%3").arg(errorType, errorDesc, errorAddress);
- this->setInError( errorReason );
- }
- }
- }
+ errorReason = QString ("(%1) %2, Resource:%3").arg(errorType, errorDesc, errorAddress);
+ this->setInError( errorReason );
+ apiError = true;
}
}
}
- else
- {
- if ( httpStatusCode > 0 )
- {
- QString httpReason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
- QString advise;
- switch ( httpStatusCode ) {
- case 400:
- advise = "Check Request Body";
- break;
- case 401:
- advise = "Check Authentication Token (API Key)";
- break;
- case 404:
- advise = "Check Resource given";
- break;
- default:
- break;
- }
- errorReason = QString( "%1:%2 [%3 %4] - %5").arg( _hostname, _api_port, QString(httpStatusCode), httpReason, advise );
- }
- else
- {
- errorReason = QString( "%1:%2 - %3").arg( _hostname, _api_port, reply->errorString() );
- }
- this->setInError( errorReason );
- }
- // Return response
- return jsonDoc;
+ return apiError;
}
QJsonDocument LedDevicePhilipsHueBridge::post(const QString& route, const QString& content)
{
- QString url = getUrl(_hostname, _api_port, _username, route );
- return putJson( url, content );
+ _restApi->setPath(route);
+
+ httpResponse response = _restApi->put(content);
+ checkApiError(response.getBody());
+ return response.getBody();
}
-void LedDevicePhilipsHueBridge::setLightState(const unsigned int lightId, QString state)
+void LedDevicePhilipsHueBridge::setLightState(const unsigned int lightId, const QString &state)
{
DebugIf( verbose, _log, "SetLightState [%u]: %s", lightId, QSTRING_CSTR(state) );
post( QString("%1/%2/%3").arg( API_LIGHTS ).arg( lightId ).arg( API_STATE ), state );
@@ -715,17 +650,19 @@ void LedDevicePhilipsHueBridge::setLightState(const unsigned int lightId, QStrin
QJsonDocument LedDevicePhilipsHueBridge::getGroupState(const unsigned int groupId)
{
- QString url = getUrl( _hostname, _api_port, _username, QString("%1/%2").arg( API_GROUPS ).arg( groupId ) );
- return getJson( url );
+ _restApi->setPath( QString("%1/%2").arg( API_GROUPS ).arg( groupId ) );
+ httpResponse response = _restApi->get();
+ checkApiError(response.getBody());
+ return response.getBody();
}
QJsonDocument LedDevicePhilipsHueBridge::setGroupState(const unsigned int groupId, bool state)
{
QString active = state ? API_STREAM_ACTIVE_VALUE_TRUE : API_STREAM_ACTIVE_VALUE_FALSE;
- return post( QString("%1/%2").arg( API_GROUPS ).arg( groupId ), QString("{\"%1\":{\"%2\":%3}}").arg( API_STREAM ).arg( API_STREAM_ACTIVE ).arg( active ) );
+ return post( QString("%1/%2").arg( API_GROUPS ).arg( groupId ), QString("{\"%1\":{\"%2\":%3}}").arg( API_STREAM, API_STREAM_ACTIVE, active ) );
}
-bool LedDevicePhilipsHueBridge::isStreamOwner(const QString streamOwner)
+bool LedDevicePhilipsHueBridge::isStreamOwner(const QString &streamOwner)
{
return ( streamOwner != "" && streamOwner == _username );
}
@@ -839,7 +776,7 @@ void PhilipsHueLight::setOnOffState(bool on)
this->_on = on;
}
-void PhilipsHueLight::setTransitionTime(unsigned int transitionTime)
+void PhilipsHueLight::setTransitionTime(int transitionTime)
{
this->_transitionTime = transitionTime;
}
@@ -854,7 +791,7 @@ bool PhilipsHueLight::getOnOffState() const
return _on;
}
-unsigned int PhilipsHueLight::getTransitionTime() const
+int PhilipsHueLight::getTransitionTime() const
{
return _transitionTime;
}
@@ -874,7 +811,6 @@ LedDevicePhilipsHue::LedDevicePhilipsHue(const QJsonObject& deviceConfig)
, _switchOffOnBlack(false)
, _brightnessFactor(1.0)
, _transitionTime(1)
- , _isRestoreOrigState(true)
, _lightStatesRestored(false)
, _isInitLeds(false)
, _lightsCount(0)
@@ -885,15 +821,17 @@ LedDevicePhilipsHue::LedDevicePhilipsHue(const QJsonObject& deviceConfig)
, _blackLightsTimer(nullptr)
, _blackLightsTimeout(15000)
, _brightnessThreshold(0.0)
- , _handshake_timeout_min(STREAM_SSL_HANDSHAKE_TIMEOUT_MIN)
- , _handshake_timeout_max(STREAM_SSL_HANDSHAKE_TIMEOUT_MAX)
- , _ssl_read_timeout(STREAM_SSL_READ_TIMEOUT)
+ , _handshake_timeout_min(STREAM_SSL_HANDSHAKE_TIMEOUT_MIN.count())
+ , _handshake_timeout_max(STREAM_SSL_HANDSHAKE_TIMEOUT_MAX.count())
+ , _ssl_read_timeout(STREAM_SSL_READ_TIMEOUT.count())
, _stopConnection(false)
, start_retry_left(3)
, stop_retry_left(3)
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDevicePhilipsHue::construct(const QJsonObject &deviceConfig)
@@ -903,10 +841,7 @@ LedDevice* LedDevicePhilipsHue::construct(const QJsonObject &deviceConfig)
LedDevicePhilipsHue::~LedDevicePhilipsHue()
{
- if ( _blackLightsTimer != nullptr )
- {
- _blackLightsTimer->deleteLater();
- }
+ delete _blackLightsTimer;
}
bool LedDevicePhilipsHue::init(const QJsonObject &deviceConfig)
@@ -917,32 +852,32 @@ bool LedDevicePhilipsHue::init(const QJsonObject &deviceConfig)
if ( isInitOK )
{
- // Initiatiale LedDevice configuration and execution environment
+ // Initialise LedDevice configuration and execution environment
_switchOffOnBlack = _devConfig[CONFIG_ON_OFF_BLACK].toBool(true);
_blackLightsTimeout = _devConfig[CONFIG_BLACK_LIGHTS_TIMEOUT].toInt(15000);
_brightnessFactor = _devConfig[CONFIG_BRIGHTNESSFACTOR].toDouble(1.0);
_transitionTime = _devConfig[CONFIG_TRANSITIONTIME].toInt(1);
_isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(true);
- _groupId = _devConfig[CONFIG_GROUPID].toInt(0);
+ _groupId = static_cast(_devConfig[CONFIG_GROUPID].toInt(0));
_brightnessMin = _devConfig[CONFIG_BRIGHTNESS_MIN].toDouble(0.0);
_brightnessMax = _devConfig[CONFIG_BRIGHTNESS_MAX].toDouble(1.0);
_brightnessThreshold = _devConfig[CONFIG_BRIGHTNESS_THRESHOLD].toDouble(0.0);
- _handshake_timeout_min = _devConfig[CONFIG_SSL_HANDSHAKE_TIMEOUT_MIN].toInt(STREAM_SSL_HANDSHAKE_TIMEOUT_MIN);
- _handshake_timeout_max = _devConfig[CONFIG_SSL_HANDSHAKE_TIMEOUT_MAX].toInt(STREAM_SSL_HANDSHAKE_TIMEOUT_MAX);
- _ssl_read_timeout = _devConfig[CONFIG_SSL_READ_TIMEOUT].toInt(STREAM_SSL_READ_TIMEOUT);
+ _handshake_timeout_min = _devConfig[CONFIG_SSL_HANDSHAKE_TIMEOUT_MIN].toInt(STREAM_SSL_HANDSHAKE_TIMEOUT_MIN.count());
+ _handshake_timeout_max = _devConfig[CONFIG_SSL_HANDSHAKE_TIMEOUT_MAX].toInt(STREAM_SSL_HANDSHAKE_TIMEOUT_MAX.count());
+ _ssl_read_timeout = _devConfig[CONFIG_SSL_READ_TIMEOUT].toInt(STREAM_SSL_READ_TIMEOUT.count());
- if( _brightnessMin < 0.0 ) _brightnessMin = 0.0;
- if( _brightnessMax > 1.0 ) _brightnessMax = 1.0;
- if( _brightnessThreshold < 0.0 ) _brightnessThreshold = 0.0;
- if( _brightnessThreshold > 1.0 ) _brightnessThreshold = 1.0;
+ if( _brightnessMin < 0.0 ) { _brightnessMin = 0.0; }
+ if( _brightnessMax > 1.0 ) { _brightnessMax = 1.0; }
+ if( _brightnessThreshold < 0.0 ) { _brightnessThreshold = 0.0; }
+ if( _brightnessThreshold > 1.0 ) { _brightnessThreshold = 1.0; }
- if( _handshake_timeout_min <= 0 ) _handshake_timeout_min = 1;
+ if( _handshake_timeout_min <= 0 ) { _handshake_timeout_min = 1; }
- log( "Off on Black", "%d", _switchOffOnBlack );
+ log( "Off on Black", "%d", static_cast( _switchOffOnBlack ) );
log( "Brightness Factor", "%f", _brightnessFactor );
log( "Transition Time", "%d", _transitionTime );
- log( "Restore Original State", "%d", _isRestoreOrigState );
- log( "Use Hue Entertainment API", "%d", _useHueEntertainmentAPI );
+ log( "Restore Original State", "%d", static_cast( _isRestoreOrigState ) );
+ log( "Use Hue Entertainment API", "%d", static_cast( _useHueEntertainmentAPI) );
if( _useHueEntertainmentAPI )
{
@@ -958,6 +893,8 @@ bool LedDevicePhilipsHue::init(const QJsonObject &deviceConfig)
_useHueEntertainmentAPI = false;
}
}
+
+ isInitOK = initLeds();
}
return isInitOK;
@@ -990,7 +927,7 @@ bool LedDevicePhilipsHue::setLights()
if( !lArray.empty() )
{
- for(const auto id : lArray)
+ for (const auto id : lArray)
{
unsigned int lightId = id.toString().toUInt();
if( lightId > 0 )
@@ -998,7 +935,10 @@ bool LedDevicePhilipsHue::setLights()
if(std::find(_lightIds.begin(), _lightIds.end(), lightId) == _lightIds.end())
{
_lightIds.emplace_back(lightId);
- if(!lightIDStr.isEmpty()) lightIDStr.append(", ");
+ if(!lightIDStr.isEmpty())
+ {
+ lightIDStr.append(", ");
+ }
lightIDStr.append(QString::number(lightId));
}
}
@@ -1024,34 +964,6 @@ bool LedDevicePhilipsHue::setLights()
return isInitOK;
}
-int LedDevicePhilipsHue::open()
-{
- int retval = -1;
- QString errortext;
- _deviceReady = false;
-
- // General initialisation and configuration of LedDevice
- if ( init(_devConfig) )
- {
- if ( LedDevicePhilipsHueBridge::open() )
- // Open/Start LedDevice based on configuration
- {
- if ( initLeds() )
- {
- // Everything is OK -> enable device
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
- else
- {
- Debug(_log, "Device not usable." );
- }
- }
- }
- return retval;
-}
-
bool LedDevicePhilipsHue::initLeds()
{
bool isInitOK = false;
@@ -1067,7 +979,7 @@ bool LedDevicePhilipsHue::initLeds()
_devConfig["host"] = _hostname;
_devConfig["sslport"] = API_SSL_SERVER_PORT;
_devConfig["servername"] = API_SSL_SERVER_NAME;
- _devConfig["rewriteTime"] = STREAM_REWRITE_TIME;
+ _devConfig["rewriteTime"] = static_cast( STREAM_REWRITE_TIME.count() );
_devConfig["psk"] = _devConfig[ CONFIG_CLIENTKEY ].toString();
_devConfig["psk_identity"] = _devConfig[ CONFIG_USERNAME ].toString();
_devConfig["seed_custom"] = API_SSL_SEED_CUSTOM;
@@ -1105,7 +1017,7 @@ bool LedDevicePhilipsHue::initLeds()
return isInitOK;
}
-bool LedDevicePhilipsHue::updateLights(QMap map)
+bool LedDevicePhilipsHue::updateLights(const QMap &map)
{
bool isInitOK = true;
@@ -1143,7 +1055,7 @@ bool LedDevicePhilipsHue::updateLights(QMap map)
return isInitOK;
}
-bool LedDevicePhilipsHue::initStream()
+bool LedDevicePhilipsHue::openStream()
{
bool isInitOK = false;
@@ -1165,7 +1077,7 @@ bool LedDevicePhilipsHue::initStream()
{
Debug(_log, "Stream successful stopped");
//Restore Philips Hue devices state
- restoreOriginalState();
+ restoreState();
isInitOK = startStream();
}
else
@@ -1212,10 +1124,11 @@ bool LedDevicePhilipsHue::startStream()
{
Debug(_log, "Start entertainment stream");
+ bool rc = false;
if ( setStreamGroupState( true ) )
{
start_retry_left = 3;
- return true;
+ rc = true;
}
else
{
@@ -1229,23 +1142,24 @@ bool LedDevicePhilipsHue::startStream()
// stream is not active
if( !streamState )
{
- return ( start_retry_left-- > 0 ) ? startStream() : false;
+ rc = ( start_retry_left-- > 0 ) ? startStream() : false;
}
}
}
}
- return false;
+ return rc;
}
bool LedDevicePhilipsHue::stopStream()
{
ProviderUdpSSL::closeSSLConnection();
+ bool rc = false;
if ( setStreamGroupState( false ) )
{
stop_retry_left = 3;
- return true;
+ rc = true;
}
else
{
@@ -1259,13 +1173,13 @@ bool LedDevicePhilipsHue::stopStream()
// stream is still active
if( streamState )
{
- return (stop_retry_left-- > 0) ? stopStream() : false;
+ rc = (stop_retry_left-- > 0) ? stopStream() : false;
}
}
}
}
- return false;
+ return rc;
}
bool LedDevicePhilipsHue::getStreamGroupState()
@@ -1298,23 +1212,42 @@ bool LedDevicePhilipsHue::setStreamGroupState(bool state)
QJsonDocument doc = setGroupState( _groupId, state );
QVariant rsp = doc.toVariant();
- QVariantMap map = rsp.toList().first().toMap();
- if ( !map.contains( API_SUCCESS ) )
+ QVariantList list = rsp.toList();
+ if ( !list.isEmpty() )
{
- this->setInError( QString("set stream to %1: Neither error nor success contained in Bridge response...").arg( active ) );
- }
- else
- {
- QString valueName = QString( API_STREAM_RESPONSE_FORMAT ).arg( API_GROUPS ).arg( _groupId ).arg( API_STREAM ).arg( API_STREAM_ACTIVE );
- if(!map.value( API_SUCCESS ).toMap().value( valueName ).isValid())
+ QVariantMap map = list.first().toMap();
+
+ if ( !map.contains( API_SUCCESS ) )
{
- this->setInError( QString("set stream to %1: Bridge response is not Valid").arg( active ) );
+ this->setInError( QString("set stream to %1: Neither error nor success contained in Bridge response...").arg( active ) );
}
else
{
- bool groupStreamState = map.value( API_SUCCESS ).toMap().value( valueName ).toBool();
- return ( groupStreamState == state );
+ //Check original Hue response {"success":{"/groups/groupID/stream/active":activeYesNo}}
+ QString valueName = QString( API_STREAM_RESPONSE_FORMAT ).arg( API_GROUPS ).arg( _groupId ).arg( API_STREAM, API_STREAM_ACTIVE );
+ if(!map.value( API_SUCCESS ).toMap().value( valueName ).isValid())
+ {
+ //Workaround
+ //Check diyHue response {"success":{"/groups/groupID/stream":{"active":activeYesNo}}}
+ QString diyHueValueName = QString( "/%1/%2/%3" ).arg( API_GROUPS ).arg( _groupId ).arg( API_STREAM);
+ QJsonObject diyHueActiveState = map.value( API_SUCCESS ).toMap().value( diyHueValueName ).toJsonObject();
+
+ if( diyHueActiveState.isEmpty() )
+ {
+ this->setInError( QString("set stream to %1: Bridge response is not Valid").arg( active ) );
+ }
+ else
+ {
+ bool groupStreamState = diyHueActiveState[API_STREAM_ACTIVE].toBool();
+ return ( groupStreamState == state );
+ }
+ }
+ else
+ {
+ bool groupStreamState = map.value( API_SUCCESS ).toMap().value( valueName ).toBool();
+ return ( groupStreamState == state );
+ }
}
}
@@ -1323,33 +1256,9 @@ bool LedDevicePhilipsHue::setStreamGroupState(bool state)
QByteArray LedDevicePhilipsHue::prepareStreamData()
{
- static const uint8_t HEADER[] =
- {
- 'H', 'u', 'e', 'S', 't', 'r', 'e', 'a', 'm', //protocol
- 0x01, 0x00, //version 1.0
- 0x01, //sequence number 1
- 0x00, 0x00, //Reserved write 0’s
- 0x01, //xy Brightness
- 0x00, // Reserved, write 0’s
- };
-
- static const uint8_t PAYLOAD_PER_LIGHT[] =
- {
- 0x01, 0x00, 0x06, //light ID
- //color: 16 bpc
- 0xff, 0xff,
- 0xff, 0xff,
- 0xff, 0xff,
- /*
- (message.R >> 8) & 0xff, message.R & 0xff,
- (message.G >> 8) & 0xff, message.G & 0xff,
- (message.B >> 8) & 0xff, message.B & 0xff
- */
- };
-
QByteArray msg;
msg.reserve(static_cast(sizeof(HEADER) + sizeof(PAYLOAD_PER_LIGHT) * _lights.size()));
- msg.append((char*)HEADER, sizeof(HEADER));
+ msg.append((const char*)HEADER, sizeof(HEADER));
for (PhilipsHueLight& light : _lights)
{
@@ -1370,43 +1279,70 @@ QByteArray LedDevicePhilipsHue::prepareStreamData()
return msg;
}
-void LedDevicePhilipsHue::restoreOriginalState()
+void LedDevicePhilipsHue::stop()
{
- if ( _isRestoreOrigState && !_lightStatesRestored )
- {
- _lightStatesRestored = true;
-
- if( !_lightIds.empty() )
- {
- for ( PhilipsHueLight& light : _lights )
- {
- setLightState( light.getId(),light.getOriginalState() );
- }
- }
- }
+ stopBlackTimeoutTimer();
+ LedDevicePhilipsHueBridge::stop();
}
-void LedDevicePhilipsHue::close()
+int LedDevicePhilipsHue::open()
{
- _isInitLeds = false;
- this->stopBlackTimeoutTimer();
+ int retval = -1;
+ _isDeviceReady = false;
- LedDevicePhilipsHueBridge::close();
-
- if ( _deviceReady )
+ if( _useHueEntertainmentAPI )
{
- if ( !_useHueEntertainmentAPI )
+ if ( openStream() )
{
- //Restore Philips Hue devices state
- restoreOriginalState();
+ // Everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
+ }
+ else
+ {
+ // TODO: Stop device (or fallback to classic mode) - suggest to stop device to meet user expectation
+ //_useHueEntertainmentAPI = false; -to be removed, if 1
+ // Everything is OK, device is ready
}
}
+ else
+ {
+ // Classic mode, everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
+ }
+
+ return retval;
+}
+
+int LedDevicePhilipsHue::close()
+{
+ int retval = -1;
+
+ retval = LedDevicePhilipsHueBridge::close();
+
+ return retval;
+}
+
+bool LedDevicePhilipsHue::switchOff()
+{
+ Debug(_log, "");
+
+ this->stopBlackTimeoutTimer();
+
+ stop_retry_left = 3;
+ stopStream();
+
+ return LedDevicePhilipsHueBridge::switchOff();
}
int LedDevicePhilipsHue::write(const std::vector & ledValues)
{
// lights will be empty sometimes
- if( _lights.empty() ) return -1;
+ if( _lights.empty() )
+ {
+ return -1;
+ }
// more lights then leds, stop always
if( ledValues.size() < getLightsCount() )
@@ -1417,14 +1353,17 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues)
writeSingleLights( ledValues );
- if( _useHueEntertainmentAPI && !noSignalDetection() && _isInitLeds ) writeStream();
+ if( _useHueEntertainmentAPI && !noSignalDetection() && _isInitLeds )
+ {
+ writeStream();
+ }
return 0;
}
void LedDevicePhilipsHue::noSignalTimeout()
{
- Debug(_log, "No Signal (timeout: %sms), only black color detected - stop stream for \"%s\" [%u]", QSTRING_CSTR( QString::number( _blackLightsTimer->remainingTime() ) ), QSTRING_CSTR(_groupName), _groupId );
+ Debug(_log, "No Signal (timeout: %dms), only black color detected - stop stream for \"%s\" [%u]", _blackLightsTimer->remainingTime(), QSTRING_CSTR(_groupName), _groupId );
_stopConnection = true;
switchOff();
}
@@ -1514,7 +1453,7 @@ int LedDevicePhilipsHue::writeSingleLights(const std::vector& ledValue
void LedDevicePhilipsHue::writeStream()
{
QByteArray streamData = prepareStreamData();
- writeBytes( streamData.size(), reinterpret_cast( streamData.data() ) );
+ writeBytes( static_cast(streamData.size()), reinterpret_cast( streamData.data() ) );
}
void LedDevicePhilipsHue::setOnOffState(PhilipsHueLight& light, bool on)
@@ -1523,7 +1462,7 @@ void LedDevicePhilipsHue::setOnOffState(PhilipsHueLight& light, bool on)
{
light.setOnOffState( on );
QString state = on ? API_STATE_VALUE_TRUE : API_STATE_VALUE_FALSE;
- setLightState( light.getId(), QString("{\"%1\": %2 }").arg( API_STATE_ON ).arg( state ) );
+ setLightState( light.getId(), QString("{\"%1\": %2 }").arg( API_STATE_ON, state ) );
}
}
@@ -1563,7 +1502,7 @@ void LedDevicePhilipsHue::setState(PhilipsHueLight& light, bool on, const CiColo
{
light.setOnOffState( on );
QString state = on ? API_STATE_VALUE_TRUE : API_STATE_VALUE_FALSE;
- stateCmd += QString("\"%1\":%2,").arg( API_STATE_ON ).arg( state );
+ stateCmd += QString("\"%1\":%2,").arg( API_STATE_ON, state );
}
if ( light.getTransitionTime() != _transitionTime )
@@ -1591,70 +1530,31 @@ void LedDevicePhilipsHue::setLightsCount( unsigned int lightsCount )
_lightsCount = lightsCount;
}
-bool LedDevicePhilipsHue::reinitLeds()
+bool LedDevicePhilipsHue::powerOn()
{
- bool isInitOK = initMaps();
-
- if( isInitOK )
+ if ( _isDeviceReady)
{
- isInitOK = initLeds();
- }
-
- return isInitOK;
-}
-
-int LedDevicePhilipsHue::switchOn()
-{
- if ( _deviceReady )
- {
- if( !_isInitLeds )
+ // TODO: Question: Not clear, if setstream state on will turn of the lights
+ // or do they need to be turned off classically?
+ if ( !_useHueEntertainmentAPI )
{
- _useHueEntertainmentAPI = _devConfig[CONFIG_USE_HUE_ENTERTAINMENT_API].toBool(false);
- Debug(_log, "Update Bridge, Group and Light states");
- reinitLeds();
- }
-
- bool isInitOK = false;
-
- if( _useHueEntertainmentAPI )
- {
- isInitOK = initStream();
- }
-
- if( !isInitOK )
- {
- _useHueEntertainmentAPI = false;
-
- //Switch on Philips Hue devices physically
+ //Switch off Philips Hue devices physically
for ( PhilipsHueLight& light : _lights )
{
setOnOffState( light, true );
}
}
- _lightStatesRestored = false;
}
- return 0;
+ return true;
}
-int LedDevicePhilipsHue::switchOff()
+bool LedDevicePhilipsHue::powerOff()
{
- this->stopBlackTimeoutTimer();
-
- //Set all LEDs to Black
- int rc = LedDevice::switchOff();
-
- if ( _deviceReady )
+ if ( _isDeviceReady)
{
- if( _useHueEntertainmentAPI )
- {
- stop_retry_left = 3;
- if( stopStream() )
- {
- //Restore Philips Hue devices state
- restoreOriginalState();
- }
- }
- else
+ // TODO: Question: Not clear, if setstream state off will turn of the lights
+ // or do they need to be turned off classically
+ if ( !_useHueEntertainmentAPI )
{
//Switch off Philips Hue devices physically
for ( PhilipsHueLight& light : _lights )
@@ -1662,7 +1562,154 @@ int LedDevicePhilipsHue::switchOff()
setOnOffState( light, false );
}
}
- _isInitLeds = false;
}
+ return true;
+}
+
+bool LedDevicePhilipsHue::storeState()
+{
+ bool rc = true;
+
+ if ( _isRestoreOrigState )
+ {
+ // Save device's original state
+ //_orignalStateValues = get device's state;
+
+ // TODO: Move saveOriginalState out of the HueLight constructor,
+ // as the light state may have change since last close and needs to be stored again before reopen
+ }
+
return rc;
}
+
+bool LedDevicePhilipsHue::restoreState()
+{
+ bool rc = true;
+
+ if ( _isRestoreOrigState && !_lightStatesRestored )
+ {
+ // Restore device's original state
+ _lightStatesRestored = true;
+
+ if( !_lightIds.empty() )
+ {
+ for ( PhilipsHueLight& light : _lights )
+ {
+ setLightState( light.getId(),light.getOriginalState() );
+ }
+ }
+ }
+
+ return rc;
+}
+
+QJsonObject LedDevicePhilipsHue::discover()
+{
+ QJsonObject devicesDiscovered;
+ devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
+
+ QJsonArray deviceList;
+
+ // Discover Devices
+ SSDPDiscover discover;
+
+ discover.skipDuplicateKeys(false);
+ discover.setSearchFilter(SSDP_FILTER, SSDP_FILTER_HEADER);
+ QString searchTarget = SSDP_ID;
+
+ if ( discover.discoverServices(searchTarget) > 0 )
+ {
+ deviceList = discover.getServicesDiscoveredJson();
+ }
+
+ devicesDiscovered.insert("devices", deviceList);
+ Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ return devicesDiscovered;
+}
+
+QJsonObject LedDevicePhilipsHue::getProperties(const QJsonObject& params)
+{
+ QJsonObject properties;
+
+ // Get Phillips-Bridge device properties
+ QString host = params["host"].toString("");
+ if ( !host.isEmpty() )
+ {
+ QString username = params["user"].toString("");
+ QString filter = params["filter"].toString("");
+
+ // Resolve hostname and port (or use default API port)
+ QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
+ QString apiHost = addressparts[0];
+ int apiPort;
+
+ if ( addressparts.size() > 1 )
+ {
+ apiPort = addressparts[1].toInt();
+ }
+ else
+ {
+ apiPort = API_DEFAULT_PORT;
+ }
+
+ initRestAPI(apiHost, apiPort, username);
+ _restApi->setPath(filter);
+
+ // Perform request
+ httpResponse response = _restApi->get();
+ if ( response.error() )
+ {
+ Warning (_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
+ }
+
+ // Perform request
+ properties.insert("properties", response.getBody().object());
+ }
+ return properties;
+}
+
+void LedDevicePhilipsHue::identify(const QJsonObject& params)
+{
+ Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ QJsonObject properties;
+
+ // Identify Phillips-Bridge device
+ QString host = params["host"].toString("");
+ if ( !host.isEmpty() )
+ {
+ QString username = params["user"].toString("");
+ int lightId = params["lightId"].toInt(0);
+
+ // Resolve hostname and port (or use default API port)
+ QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
+ QString apiHost = addressparts[0];
+ int apiPort;
+
+ if ( addressparts.size() > 1 )
+ {
+ apiPort = addressparts[1].toInt();
+ }
+ else
+ {
+ apiPort = API_DEFAULT_PORT;
+ }
+
+ initRestAPI(apiHost, apiPort, username);
+
+ QString resource = QString("%1/%2/%3").arg( API_LIGHTS ).arg( lightId ).arg( API_STATE);
+ _restApi->setPath(resource);
+
+ QString stateCmd;
+ stateCmd += QString("\"%1\":%2,").arg( API_STATE_ON, API_STATE_VALUE_TRUE );
+ stateCmd += QString("\"%1\":\"%2\"").arg( "alert", "select" );
+ stateCmd = "{" + stateCmd + "}";
+
+ // Perform request
+ httpResponse response = _restApi->put(stateCmd);
+ if ( response.error() )
+ {
+ Warning (_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
+ }
+ }
+}
diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.h b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.h
index c6b78f5a..d18709ed 100644
--- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.h
+++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.h
@@ -12,8 +12,9 @@
#include
#include
-// Leddevice includes
+// LedDevice includes
#include
+#include "ProviderRestApi.h"
#include "ProviderUdpSSL.h"
/**
@@ -134,7 +135,7 @@ public:
///
/// @param transitionTime the transition time between colors in multiples of 100 ms
///
- void setTransitionTime(unsigned int transitionTime);
+ void setTransitionTime(int transitionTime);
///
/// @param color the color to set
@@ -144,7 +145,7 @@ public:
unsigned int getId() const;
bool getOnOffState() const;
- unsigned int getTransitionTime() const;
+ int getTransitionTime() const;
CiColor getColor() const;
///
@@ -162,7 +163,7 @@ private:
unsigned int _id;
unsigned int _ledidx;
bool _on;
- unsigned int _transitionTime;
+ int _transitionTime;
CiColor _color;
/// darkes blue color in hue lamp GAMUT = black
CiColor _colorBlack;
@@ -185,14 +186,18 @@ class LedDevicePhilipsHueBridge : public ProviderUdpSSL
public:
explicit LedDevicePhilipsHueBridge(const QJsonObject &deviceConfig);
- ~LedDevicePhilipsHueBridge();
+ ~LedDevicePhilipsHueBridge() override;
///
- /// Sets configuration
+ /// @brief Initialise the access to the REST-API wrapper
///
- /// @param deviceConfig the json device config
- /// @return true if success
- virtual bool init(const QJsonObject &deviceConfig) override;
+ /// @param[in] host
+ /// @param[in] port
+ /// @param[in] authentication token
+ ///
+ /// @return True, if success
+ ///
+ bool initRestAPI(const QString &hostname, const int port, const QString &token );
///
/// @param route the route of the POST request.
@@ -201,28 +206,56 @@ public:
///
QJsonDocument post(const QString& route, const QString& content);
- void setLightState(unsigned int lightId = 0, QString state = "");
+ void setLightState(unsigned int lightId = 0, const QString &state = "");
const QMap& getLightMap();
const QMap& getGroupMap();
- QString getGroupName(unsigned int groupId = 0);
+ QString getGroupName(quint16 groupId = 0);
+
+ QJsonArray getGroupLights(quint16 groupId = 0);
- QJsonArray getGroupLights(unsigned int groupId = 0);
-public slots:
- ///
- /// Connect to bridge to check availbility and user
- ///
- virtual int open(void) override;
- virtual int open( const QString& hostname, const QString& port, const QString& username );
protected:
+ ///
+ /// @brief Initialise the Hue-Bridge configuration and network address details
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Opens the Hue-Bridge device and its SSL-connection
+ ///
+ /// @return Zero on success (i.e. device is ready), else negative
+ ///
+ virtual int open(void) override;
+
+ ///
+ /// @brief Closes the Hue-Bridge device and its SSL-connection
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
+ ///
+ /// @brief Check, if Hue API response indicate error
+ ///
+ /// @param[in] response from Hue-Bridge in JSON-format
+ /// return True, Hue Bridge reports error
+ ///
+ bool checkApiError(const QJsonDocument &response );
+
+ ///REST-API wrapper
+ ProviderRestApi* _restApi;
+
/// Ip address of the bridge
QString _hostname;
- QString _api_port;
+ int _apiPort;
/// User name for the API ("newdeveloper")
QString _username;
@@ -231,7 +264,7 @@ protected:
QJsonDocument getGroupState( unsigned int groupId );
QJsonDocument setGroupState( unsigned int groupId, bool state);
- bool isStreamOwner(const QString streamOwner);
+ bool isStreamOwner(const QString &streamOwner);
bool initMaps();
void log(const char* msg, const char* type, ...);
@@ -240,56 +273,10 @@ protected:
private:
- ///
- /// Discover device via SSDP identifiers
- ///
- /// @return True, if device was found
- ///
- bool discoverDevice();
-
- ///
- /// Get command as url
- ///
- /// @param host Hostname or IP
- /// @param port IP-Port
- /// @param _auth_token Authorization token
- /// @param Endpoint command for request
- /// @return Url to execute endpoint/command
- ///
- QString getUrl(QString host, QString port, QString auth_token, QString endpoint) const;
-
- ///
- /// Execute GET request
- ///
- /// @param url GET request for url
- /// @return Response from device
- ///
- QJsonDocument getJson(QString url);
-
- ///
- /// Execute PUT request
- ///
- /// @param Url for PUT request
- /// @param json Command for request
- /// @return Response from device
- ///
- QJsonDocument putJson(QString url, QString json);
-
- ///
- /// Handle replys for GET and PUT requests
- ///
- /// @param reply Network reply
- /// @return Response for request, if no error
- ///
- QJsonDocument handleReply(QNetworkReply* const &reply );
-
QJsonDocument getAllBridgeInfos();
- void setBridgeConfig( QJsonDocument doc );
- void setLightsMap( QJsonDocument doc );
- void setGroupMap( QJsonDocument doc );
-
- /// QNetworkAccessManager for sending requests.
- QNetworkAccessManager* _networkmanager;
+ void setBridgeConfig( const QJsonDocument &doc );
+ void setLightsMap( const QJsonDocument &doc );
+ void setGroupMap( const QJsonDocument &doc );
//Philips Hue Bridge details
QString _deviceModel;
@@ -320,105 +307,209 @@ class LedDevicePhilipsHue: public LedDevicePhilipsHueBridge
public:
///
- /// Constructs specific LedDevice
+ /// @brief Constructs LED-device for Philips Hue Lights system
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDevicePhilipsHue(const QJsonObject &deviceConfig);
///
- /// Destructor of this device
+ /// @brief Destructor of the LED-device
///
virtual ~LedDevicePhilipsHue();
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig);
///
- /// Sets configuration
+ /// @brief Discover devices of this type available (for configuration).
+ /// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways.
///
- /// @param deviceConfig the json device config
- /// @return true if success
- virtual bool init(const QJsonObject &deviceConfig) override;
-
- /// Switch the device on
- virtual int switchOn() override;
-
- /// Switch the device off
- virtual int switchOff() override;
-
- /// creates new PhilipsHueLight(s) based on user lightid with bridge feedback
+ /// @return A JSON structure holding a list of devices found
///
- /// @param map Map of lightid/value pairs of bridge
- ///
- void newLights(QMap map);
+ virtual QJsonObject discover() override;
+ ///
+ /// @brief Get the Hue Bridge device's resource properties
+ ///
+ /// Following parameters are required
+ /// @code
+ /// {
+ /// "host" : "hostname or IP [:port]",
+ /// "user" : "username",
+ /// "filter": "resource to query", root "/" is used, if empty
+ /// }
+ ///@endcode
+ ///
+ /// @param[in] params Parameters to query device
+ /// @return A JSON structure holding the device's properties
+ ///
+ virtual QJsonObject getProperties(const QJsonObject& params) override;
+
+ ///
+ /// @brief Send an update to the device to identify it.
+ ///
+ /// Used in context of a set of devices of the same type.
+ ///
+ /// @param[in] params Parameters to address device
+ ///
+ virtual void identify(const QJsonObject& params) override;
+
+ ///
+ /// @brief Get the number of LEDs supported by the device.
+ ///
+ /// @return Number of device's LEDs
+ ///
unsigned int getLightsCount() const { return _lightsCount; }
- void setLightsCount( unsigned int lightsCount);
-
- bool initStream();
- bool getStreamGroupState();
- bool setStreamGroupState(bool state);
- bool startStream();
- bool stopStream();
void setOnOffState(PhilipsHueLight& light, bool on);
void setTransitionTime(PhilipsHueLight& light);
void setColor(PhilipsHueLight& light, CiColor& color);
void setState(PhilipsHueLight& light, bool on, const CiColor& color);
- void restoreOriginalState();
-
public slots:
///
- /// Closes the output device.
- /// Includes switching-off the device and stopping refreshes
+ /// @brief Stops the device.
///
- virtual void close() override;
-
-private slots:
- /// creates new PhilipsHueLight(s) based on user lightid with bridge feedback
+ /// Includes switching-off the device and stopping refreshes.
///
- /// @param map Map of lightid/value pairs of bridge
- ///
- bool updateLights(QMap map);
-
- void noSignalTimeout();
+ virtual void stop() override;
protected:
///
- /// Opens and initiatialises the output device
+ /// Initialise the device's configuration
///
- /// @return Zero on succes (i.e. device is ready and enabled) else negative
+ /// @param deviceConfig Device's configuration in JSON
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Opens the output device
+ ///
+ /// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
///
- /// Get Philips Hue device details and configuration
+ /// @brief Closes the output device.
///
- /// @return True, if Nanoleaf device capabilities fit configuration
+ /// @return Zero on success (i.e. device is closed), else negative
///
- bool initLeds();
- bool reinitLeds();
+ virtual int close() override;
///
- /// Writes the RGB-Color values to the leds.
+ /// @brief Writes the RGB-Color values to the LEDs.
///
- /// @param[in] ledValues The RGB-color per led
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
///
- /// @return Zero on success else negative
+ virtual int write(const std::vector& ledValues) override;
+
///
- virtual int write(const std::vector & ledValues) override;
+ /// @brief Switch the LEDs on.
+ ///
+ /// Takes care that the device is opened and powered-on.
+ /// Depending on the configuration, the device may store its current state for later restore.
+ /// @see powerOn, storeState
+ ///
+ /// @return True if success
+ ///
+ //virtual bool switchOn() override;
+
+ ///
+ /// @brief Switch the LEDs off.
+ ///
+ /// Takes care that the LEDs and device are switched-off and device is closed.
+ /// Depending on the configuration, the device may be powered-off or restored to its previous state.
+ /// @see powerOff, restoreState
+ ///
+ /// @return True, if success
+ ///
+ virtual bool switchOff() override;
+
+ ///
+ /// @brief Power-/turn on the LED-device.
+ ///
+ /// Powers-/Turns on the LED hardware, if supported.
+ ///
+ /// @return True, if success
+ ///
+ virtual bool powerOn() override;
+
+ ///
+ /// @brief Power-/turn off the LED-device.
+ ///
+ /// Depending on the device's capability, the device is powered-/turned off or
+ /// an off state is simulated by writing "Black to LED" (default).
+ ///
+ /// @return True, if success
+ ///
+ virtual bool powerOff() override;
+
+ ///
+ /// @brief Store the device's original state.
+ ///
+ /// Save the device's state before hyperion color streaming starts allowing to restore state during switchOff().
+ ///
+ /// @return True if success
+ ///
+ virtual bool storeState() override;
+
+ ///
+ /// @brief Restore the device's original state.
+ ///
+ /// Restore the device's state as before hyperion color streaming started.
+ /// This includes the on/off state of the device.
+ ///
+ /// @return True, if success
+ ///
+ virtual bool restoreState() override;
+
+private slots:
+
+ void noSignalTimeout();
private:
+ bool initLeds();
+
+ ///
+ /// @brief Creates new PhilipsHueLight(s) based on user lightid with bridge feedback
+ ///
+ /// @param map Map of lightid/value pairs of bridge
+ ///
+ void newLights(QMap map);
+
bool setLights();
- int writeSingleLights(const std::vector& ledValues);
+ /// creates new PhilipsHueLight(s) based on user lightid with bridge feedback
+ ///
+ /// @param map Map of lightid/value pairs of bridge
+ ///
+ bool updateLights(const QMap &map);
+
+ ///
+ /// @brief Set the number of LEDs supported by the device.
+ ///
+ /// @rparam[in] Number of device's LEDs
+ //
+ void setLightsCount( unsigned int lightsCount);
+
+ bool openStream();
+ bool getStreamGroupState();
+ bool setStreamGroupState(bool state);
+ bool startStream();
+ bool stopStream();
void writeStream();
+ int writeSingleLights(const std::vector& ledValues);
bool noSignalDetection();
@@ -430,21 +521,20 @@ private:
bool _switchOffOnBlack;
/// The brightness factor to multiply on color change.
double _brightnessFactor;
- /// Transition time in multiples of 100 ms.
- /// The default of the Hue lights is 400 ms, but we may want it snapier.
- unsigned int _transitionTime;
+ /// Transition time in multiples of 100 ms.
+ /// The default of the Hue lights is 400 ms, but we may want it snappier.
+ int _transitionTime;
- bool _isRestoreOrigState;
bool _lightStatesRestored;
bool _isInitLeds;
/// Array of the light ids.
- std::vector _lightIds;
+ std::vector _lightIds;
/// Array to save the lamps.
std::vector _lights;
unsigned int _lightsCount;
- unsigned int _groupId;
+ quint16 _groupId;
double _brightnessMin;
double _brightnessMax;
@@ -452,7 +542,7 @@ private:
bool _allLightsBlack;
QTimer* _blackLightsTimer;
- unsigned int _blackLightsTimeout;
+ int _blackLightsTimeout;
double _brightnessThreshold;
int _handshake_timeout_min;
@@ -466,4 +556,5 @@ private:
int start_retry_left;
int stop_retry_left;
+
};
diff --git a/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp b/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp
index 3a6ac537..be779ebb 100644
--- a/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp
+++ b/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp
@@ -1,10 +1,14 @@
#include "LedDeviceTpm2net.h"
+const ushort TPM2_DEFAULT_PORT = 65506;
+
LedDeviceTpm2net::LedDeviceTpm2net(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceTpm2net::construct(const QJsonObject &deviceConfig)
@@ -14,13 +18,19 @@ LedDevice* LedDeviceTpm2net::construct(const QJsonObject &deviceConfig)
bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig)
{
+ bool isInitOK = false;
+
_port = TPM2_DEFAULT_PORT;
- bool isInitOK = ProviderUdp::init(deviceConfig);
- _tpm2_max = deviceConfig["max-packet"].toInt(170);
- _tpm2ByteCount = 3 * _ledCount;
- _tpm2TotalPackets = 1 + _tpm2ByteCount / _tpm2_max;
+ // Initialise sub-class
+ if ( ProviderUdp::init(deviceConfig) )
+ {
+ _tpm2_max = deviceConfig["max-packet"].toInt(170);
+ _tpm2ByteCount = 3 * _ledCount;
+ _tpm2TotalPackets = 1 + _tpm2ByteCount / _tpm2_max;
+ isInitOK = true;
+ }
return isInitOK;
}
diff --git a/libsrc/leddevice/dev_net/LedDeviceTpm2net.h b/libsrc/leddevice/dev_net/LedDeviceTpm2net.h
index 2abdae83..70338f7c 100644
--- a/libsrc/leddevice/dev_net/LedDeviceTpm2net.h
+++ b/libsrc/leddevice/dev_net/LedDeviceTpm2net.h
@@ -1,44 +1,53 @@
-#pragma once
+#ifndef LEDEVICETPM2NET_H
+#define LEDEVICETPM2NET_H
// hyperion includes
#include "ProviderUdp.h"
-const ushort TPM2_DEFAULT_PORT = 65506;
-
///
-/// Implementation of the LedDevice interface for sending led colors via udp tpm2.net packets
+/// Implementation of the LedDevice interface for sending LED colors via udp tpm2.net packets
///
class LedDeviceTpm2net : public ProviderUdp
{
public:
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs a TPM2 LED-device fed via UDP
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceTpm2net(const QJsonObject &deviceConfig);
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
static LedDevice* construct(const QJsonObject &deviceConfig);
+private:
+
///
- /// Sets configuration
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
///
- /// @param deviceConfig the json device config
- /// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
-private:
///
- /// Writes the led color values to the led-device
+ /// @brief Writes the RGB-Color values to the LEDs.
///
- /// @param ledValues The color-value per led
- /// @return Zero on succes else negative
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
///
- virtual int write(const std::vector &ledValues) override;
+ virtual int write(const std::vector & ledValues) override;
int _tpm2_max;
int _tpm2ByteCount;
int _tpm2TotalPackets;
int _tpm2ThisPacket;
};
+
+#endif // LEDEVICETPM2NET_H
diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpArtNet.cpp b/libsrc/leddevice/dev_net/LedDeviceUdpArtNet.cpp
index ce32138b..22c1a15d 100644
--- a/libsrc/leddevice/dev_net/LedDeviceUdpArtNet.cpp
+++ b/libsrc/leddevice/dev_net/LedDeviceUdpArtNet.cpp
@@ -1,3 +1,6 @@
+// hyperion local includes
+#include "LedDeviceUdpArtNet.h"
+
#ifdef _WIN32
#include
#else
@@ -6,16 +9,18 @@
#include
-// hyperion local includes
-#include "LedDeviceUdpArtNet.h"
+const ushort ARTNET_DEFAULT_PORT = 6454;
LedDeviceUdpArtNet::LedDeviceUdpArtNet(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
+
LedDevice* LedDeviceUdpArtNet::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceUdpArtNet(deviceConfig);
@@ -23,12 +28,18 @@ LedDevice* LedDeviceUdpArtNet::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpArtNet::init(const QJsonObject &deviceConfig)
{
+ bool isInitOK = false;
+
_port = ARTNET_DEFAULT_PORT;
- bool isInitOK = ProviderUdp::init(deviceConfig);
- _artnet_universe = deviceConfig["universe"].toInt(1);
- _artnet_channelsPerFixture = deviceConfig["channelsPerFixture"].toInt(3);
+ // Initialise sub-class
+ if ( ProviderUdp::init(deviceConfig) )
+ {
+ _artnet_universe = deviceConfig["universe"].toInt(1);
+ _artnet_channelsPerFixture = deviceConfig["channelsPerFixture"].toInt(3);
+ isInitOK = true;
+ }
return isInitOK;
}
@@ -51,7 +62,6 @@ void LedDeviceUdpArtNet::prepare(const unsigned this_universe, const unsigned th
artnet_packet.SubUni = this_universe & 0xff ;
artnet_packet.Net = (this_universe >> 8) & 0x7f;
artnet_packet.Length = htons(this_dmxChannelCount);
-
}
int LedDeviceUdpArtNet::write(const std::vector &ledValues)
diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpArtNet.h b/libsrc/leddevice/dev_net/LedDeviceUdpArtNet.h
index 8f2f3da7..f48ad508 100644
--- a/libsrc/leddevice/dev_net/LedDeviceUdpArtNet.h
+++ b/libsrc/leddevice/dev_net/LedDeviceUdpArtNet.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef LEDEVICEUDPARTNET_H
+#define LEDEVICEUDPARTNET_H
// hyperion includes
#include "ProviderUdp.h"
@@ -13,9 +14,7 @@
*
**/
-const ushort ARTNET_DEFAULT_PORT = 6454;
-
-#define DMX_MAX 512 // 512 usable slots
+const int DMX_MAX = 512; // 512 usable slots
// http://stackoverflow.com/questions/16396013/artnet-packet-structure
typedef union
@@ -23,7 +22,7 @@ typedef union
#pragma pack(push, 1)
struct {
char ID[8]; // "Art-Net"
- uint16_t OpCode; // See Doc. Table 1 - OpCodes eg. 0x5000 OpOutput / OpDmx
+ uint16_t OpCode; // See Doc. Table 1 - OpCodes e.g. 0x5000 OpOutput / OpDmx
uint16_t ProtVer; // 0x0e00 (aka 14)
uint8_t Sequence; // monotonic counter
uint8_t Physical; // 0x00
@@ -39,42 +38,54 @@ typedef union
} artnet_packet_t;
///
-/// Implementation of the LedDevice interface for sending led colors via udp/E1.31 packets
+/// Implementation of the LedDevice interface for sending LED colors to an Art-Net LED-device via UDP
///
class LedDeviceUdpArtNet : public ProviderUdp
{
public:
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs an Art-Net LED-device fed via UDP
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceUdpArtNet(const QJsonObject &deviceConfig);
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
static LedDevice* construct(const QJsonObject &deviceConfig);
- ///
- /// Sets configuration
- ///
- /// @param deviceConfig the json device config
- /// @return true if success
- bool init(const QJsonObject &deviceConfig) override;
-
private:
- ///
- /// Writes the led color values to the led-device
- ///
- /// @param ledValues The color-value per led
- /// @return Zero on succes else negative
- ///
- virtual int write(const std::vector &ledValues) override;
+ ///
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ virtual int write(const std::vector & ledValues) override;
+
+ ///
+ /// @brief Generate Art-Net communication header
+ ///
void prepare(const unsigned this_universe, const unsigned this_sequence, const unsigned this_dmxChannelCount);
-
artnet_packet_t artnet_packet;
uint8_t _artnet_seq = 1;
int _artnet_channelsPerFixture = 3;
int _artnet_universe = 1;
};
+
+#endif // LEDEVICEUDPARTNET_H
diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpE131.cpp b/libsrc/leddevice/dev_net/LedDeviceUdpE131.cpp
index 06f9cd54..0f6a972b 100644
--- a/libsrc/leddevice/dev_net/LedDeviceUdpE131.cpp
+++ b/libsrc/leddevice/dev_net/LedDeviceUdpE131.cpp
@@ -9,18 +9,43 @@
// hyperion local includes
#include "LedDeviceUdpE131.h"
+const ushort E131_DEFAULT_PORT = 5568;
+
+/* defined parameters from http://tsp.esta.org/tsp/documents/docs/BSR_E1-31-20xx_CP-2014-1009r2.pdf */
+const uint32_t VECTOR_ROOT_E131_DATA = 0x00000004;
+//#define VECTOR_ROOT_E131_EXTENDED 0x00000008
+const uint8_t VECTOR_DMP_SET_PROPERTY = 0x02;
+const uint32_t VECTOR_E131_DATA_PACKET = 0x00000002;
+//#define VECTOR_E131_EXTENDED_SYNCHRONIZATION 0x00000001
+//#define VECTOR_E131_EXTENDED_DISCOVERY 0x00000002
+//#define VECTOR_UNIVERSE_DISCOVERY_UNIVERSE_LIST 0x00000001
+//#define E131_E131_UNIVERSE_DISCOVERY_INTERVAL 10 // seconds
+//#define E131_NETWORK_DATA_LOSS_TIMEOUT 2500 // milli econds
+//#define E131_DISCOVERY_UNIVERSE 64214
+const int DMX_MAX = 512; // 512 usable slots
+
LedDeviceUdpE131::LedDeviceUdpE131(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
+}
+
+LedDevice* LedDeviceUdpE131::construct(const QJsonObject &deviceConfig)
+{
+ return new LedDeviceUdpE131(deviceConfig);
}
bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig)
{
+ bool isInitOK = false;
+
_port = E131_DEFAULT_PORT;
- bool isInitOK = ProviderUdp::init(deviceConfig);
- if ( isInitOK )
+
+ // Initialise sub-class
+ if ( ProviderUdp::init(deviceConfig) )
{
_e131_universe = deviceConfig["universe"].toInt(1);
_e131_source_name = deviceConfig["source-name"].toString("hyperion on "+QHostInfo::localHostName());
@@ -29,22 +54,26 @@ bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig)
if (_json_cid.isEmpty())
{
_e131_cid = QUuid::createUuid();
- Debug( _log, "e131 no cid found, generated %s", QSTRING_CSTR(_e131_cid.toString()));
+ Debug( _log, "e131 no CID found, generated %s", QSTRING_CSTR(_e131_cid.toString()));
+ isInitOK = true;
}
else
{
_e131_cid = QUuid(_json_cid);
- Debug( _log, "e131 cid found, using %s", QSTRING_CSTR(_e131_cid.toString()));
+ if ( !_e131_cid.isNull() )
+ {
+ Debug( _log, "e131 CID found, using %s", QSTRING_CSTR(_e131_cid.toString()));
+ isInitOK = true;
+ }
+ else
+ {
+ this->setInError("CID configured is not a valid UUID. Format expected is \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"");
+ }
}
}
return isInitOK;
}
-LedDevice* LedDeviceUdpE131::construct(const QJsonObject &deviceConfig)
-{
- return new LedDeviceUdpE131(deviceConfig);
-}
-
// populates the headers
void LedDeviceUdpE131::prepare(const unsigned this_universe, const unsigned this_dmxChannelCount)
{
@@ -120,4 +149,3 @@ int LedDeviceUdpE131::write(const std::vector &ledValues)
return retVal;
}
-
diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpE131.h b/libsrc/leddevice/dev_net/LedDeviceUdpE131.h
index c155eec8..35836895 100644
--- a/libsrc/leddevice/dev_net/LedDeviceUdpE131.h
+++ b/libsrc/leddevice/dev_net/LedDeviceUdpE131.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef LEDEVICEUDPE131_H
+#define LEDEVICEUDPE131_H
// hyperion includes
#include "ProviderUdp.h"
@@ -18,32 +19,30 @@
*
**/
-const ushort E131_DEFAULT_PORT = 5568;
-
/* E1.31 Packet Offsets */
-#define E131_ROOT_PREAMBLE_SIZE 0
-#define E131_ROOT_POSTAMBLE_SIZE 2
-#define E131_ROOT_ID 4
-#define E131_ROOT_FLENGTH 16
-#define E131_ROOT_VECTOR 18
-#define E131_ROOT_CID 22
+//#define E131_ROOT_PREAMBLE_SIZE 0
+//#define E131_ROOT_POSTAMBLE_SIZE 2
+//#define E131_ROOT_ID 4
+//#define E131_ROOT_FLENGTH 16
+//#define E131_ROOT_VECTOR 18
+//#define E131_ROOT_CID 22
-#define E131_FRAME_FLENGTH 38
-#define E131_FRAME_VECTOR 40
-#define E131_FRAME_SOURCE 44
-#define E131_FRAME_PRIORITY 108
-#define E131_FRAME_RESERVED 109
-#define E131_FRAME_SEQ 111
-#define E131_FRAME_OPT 112
-#define E131_FRAME_UNIVERSE 113
+//#define E131_FRAME_FLENGTH 38
+//#define E131_FRAME_VECTOR 40
+//#define E131_FRAME_SOURCE 44
+//#define E131_FRAME_PRIORITY 108
+//#define E131_FRAME_RESERVED 109
+//#define E131_FRAME_SEQ 111
+//#define E131_FRAME_OPT 112
+//#define E131_FRAME_UNIVERSE 113
-#define E131_DMP_FLENGTH 115
-#define E131_DMP_VECTOR 117
-#define E131_DMP_TYPE 118
-#define E131_DMP_ADDR_FIRST 119
-#define E131_DMP_ADDR_INC 121
-#define E131_DMP_COUNT 123
-#define E131_DMP_DATA 125
+//#define E131_DMP_FLENGTH 115
+//#define E131_DMP_VECTOR 117
+//#define E131_DMP_TYPE 118
+//#define E131_DMP_ADDR_FIRST 119
+//#define E131_DMP_ADDR_INC 121
+//#define E131_DMP_COUNT 123
+const unsigned int E131_DMP_DATA=125;
/* E1.31 Packet Structure */
typedef union
@@ -83,51 +82,49 @@ typedef union
uint8_t raw[638];
} e131_packet_t;
-/* defined parameters from http://tsp.esta.org/tsp/documents/docs/BSR_E1-31-20xx_CP-2014-1009r2.pdf */
-#define VECTOR_ROOT_E131_DATA 0x00000004
-#define VECTOR_ROOT_E131_EXTENDED 0x00000008
-#define VECTOR_DMP_SET_PROPERTY 0x02
-#define VECTOR_E131_DATA_PACKET 0x00000002
-#define VECTOR_E131_EXTENDED_SYNCHRONIZATION 0x00000001
-#define VECTOR_E131_EXTENDED_DISCOVERY 0x00000002
-#define VECTOR_UNIVERSE_DISCOVERY_UNIVERSE_LIST 0x00000001
-#define E131_E131_UNIVERSE_DISCOVERY_INTERVAL 10 // seconds
-#define E131_NETWORK_DATA_LOSS_TIMEOUT 2500 // milli econds
-#define E131_DISCOVERY_UNIVERSE 64214
-#define DMX_MAX 512 // 512 usable slots
-
///
/// Implementation of the LedDevice interface for sending led colors via udp/E1.31 packets
///
class LedDeviceUdpE131 : public ProviderUdp
{
public:
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs an E1.31 LED-device fed via UDP
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceUdpE131(const QJsonObject &deviceConfig);
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
static LedDevice* construct(const QJsonObject &deviceConfig);
- ///
- /// Sets configuration
- ///
- /// @param deviceConfig the json device config
- /// @return true if success
- bool init(const QJsonObject &deviceConfig) override;
-
private:
- ///
- /// Writes the led color values to the led-device
- ///
- /// @param ledValues The color-value per led
- /// @return Zero on succes else negative
- ///
- virtual int write(const std::vector &ledValues) override;
+ ///
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ virtual int write(const std::vector & ledValues) override;
+
+ ///
+ /// @brief Generate E1.31 communication header
+ ///
void prepare(const unsigned this_universe, const unsigned this_dmxChannelCount);
e131_packet_t e131_packet;
@@ -137,3 +134,5 @@ private:
QString _e131_source_name;
QUuid _e131_cid;
};
+
+#endif // LEDEVICEUDPE131_H
diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpH801.cpp b/libsrc/leddevice/dev_net/LedDeviceUdpH801.cpp
index 7577408a..52b7d0cf 100644
--- a/libsrc/leddevice/dev_net/LedDeviceUdpH801.cpp
+++ b/libsrc/leddevice/dev_net/LedDeviceUdpH801.cpp
@@ -1,13 +1,20 @@
#include "LedDeviceUdpH801.h"
+// Constants
+namespace {
+
const ushort H801_DEFAULT_PORT = 30977;
-static const char H801_DEFAULT_HOST[] = "255.255.255.255";
+const char H801_DEFAULT_HOST[] = "255.255.255.255";
+
+} //End of constants
LedDeviceUdpH801::LedDeviceUdpH801(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceUdpH801::construct(const QJsonObject &deviceConfig)
@@ -17,13 +24,15 @@ LedDevice* LedDeviceUdpH801::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpH801::init(const QJsonObject &deviceConfig)
{
+ bool isInitOK = false;
+
/* The H801 port is fixed */
_latchTime_ms = 10;
_port = H801_DEFAULT_PORT;
_defaultHost = H801_DEFAULT_HOST;
- bool isInitOK = ProviderUdp::init(deviceConfig);
- if ( isInitOK )
+ // Initialise sub-class
+ if ( ProviderUdp::init(deviceConfig) )
{
_ids.clear();
QJsonArray lArray = deviceConfig["lightIds"].toArray();
@@ -44,6 +53,8 @@ bool LedDeviceUdpH801::init(const QJsonObject &deviceConfig)
}
Debug(_log, "H801 using %s:%d", _address.toString().toStdString().c_str(), _port);
+
+ isInitOK = true;
}
return isInitOK;
}
diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpH801.h b/libsrc/leddevice/dev_net/LedDeviceUdpH801.h
index 70dde8c5..9a106e01 100644
--- a/libsrc/leddevice/dev_net/LedDeviceUdpH801.h
+++ b/libsrc/leddevice/dev_net/LedDeviceUdpH801.h
@@ -1,16 +1,50 @@
-#pragma once
+#ifndef LEDEVICEUDPH801_H
+#define LEDEVICEUDPH801_H
// hyperion includes
#include "ProviderUdp.h"
///
-/// Implementation of the LedDevice interface for sending led colors via udp.
+/// Implementation of the LedDevice interface for sending LED colors to a H801 LED-device via UDP
///
///
-
class LedDeviceUdpH801: public ProviderUdp
{
-protected:
+public:
+
+ ///
+ /// @brief Constructs a H801 LED-device fed via UDP
+ ///
+ /// @param deviceConfig Device's configuration as JSON-Object
+ ///
+ explicit LedDeviceUdpH801(const QJsonObject &deviceConfig);
+
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
+ static LedDevice* construct(const QJsonObject &deviceConfig);
+
+private:
+
+ ///
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ virtual int write(const std::vector & ledValues) override;
+
QList _ids;
QByteArray _message;
const int _prefix_size = 2;
@@ -18,29 +52,6 @@ protected:
const int _id_size = 3;
const int _suffix_size = 1;
-public:
- ///
- /// Constructs specific LedDevice
- ///
- /// @param deviceConfig json device config
- ///
- explicit LedDeviceUdpH801(const QJsonObject &deviceConfig);
-
- /// constructs leddevice
- static LedDevice* construct(const QJsonObject &deviceConfig);
- ///
- /// Sets configuration
- ///
- /// @param deviceConfig the json device config
- /// @return true if success
- bool init(const QJsonObject &deviceConfig) override;
-
-private:
- ///
- /// Writes the led color values to the led-device
- ///
- /// @param ledValues The color-value per led
- /// @return Zero on succes else negative
- ///
- virtual int write(const std::vector &ledValues) override;
};
+
+#endif // LEDEVICEUDPH801_H
diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpRaw.cpp b/libsrc/leddevice/dev_net/LedDeviceUdpRaw.cpp
index 0b49cbb1..0502eb35 100644
--- a/libsrc/leddevice/dev_net/LedDeviceUdpRaw.cpp
+++ b/libsrc/leddevice/dev_net/LedDeviceUdpRaw.cpp
@@ -1,10 +1,14 @@
#include "LedDeviceUdpRaw.h"
+const ushort RAW_DEFAULT_PORT=5568;
+
LedDeviceUdpRaw::LedDeviceUdpRaw(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceUdpRaw::construct(const QJsonObject &deviceConfig)
@@ -15,6 +19,8 @@ LedDevice* LedDeviceUdpRaw::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpRaw::init(const QJsonObject &deviceConfig)
{
_port = RAW_DEFAULT_PORT;
+
+ // Initialise sub-class
bool isInitOK = ProviderUdp::init(deviceConfig);
return isInitOK;
}
@@ -23,5 +29,5 @@ int LedDeviceUdpRaw::write(const std::vector &ledValues)
{
const uint8_t * dataPtr = reinterpret_cast(ledValues.data());
- return writeBytes((unsigned)_ledRGBCount, dataPtr);
+ return writeBytes(_ledRGBCount, dataPtr);
}
diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpRaw.h b/libsrc/leddevice/dev_net/LedDeviceUdpRaw.h
index a3beb82a..7cdb8595 100644
--- a/libsrc/leddevice/dev_net/LedDeviceUdpRaw.h
+++ b/libsrc/leddevice/dev_net/LedDeviceUdpRaw.h
@@ -1,39 +1,48 @@
-#pragma once
+#ifndef LEDEVICEUDPRAW_H
+#define LEDEVICEUDPRAW_H
// hyperion includes
#include "ProviderUdp.h"
-#define RAW_DEFAULT_PORT 5568
-
///
-/// Implementation of the LedDevice interface for sending led colors via udp.
+/// Implementation of the LedDevice interface for sending LED colors via UDP
///
class LedDeviceUdpRaw : public ProviderUdp
{
public:
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs a LED-device fed via UDP
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceUdpRaw(const QJsonObject &deviceConfig);
- /// constructs leddevice
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
static LedDevice* construct(const QJsonObject &deviceConfig);
- ///
- /// Sets configuration
- ///
- /// @param deviceConfig the json device config
- /// @return true if success
- bool init(const QJsonObject &deviceConfig) override;
+protected:
-private:
///
- /// Writes the led color values to the led-device
+ /// @brief Initialise the device's configuration
///
- /// @param ledValues The color-value per led
- /// @return Zero on succes else negative
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
///
- virtual int write(const std::vector &ledValues) override;
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ virtual int write(const std::vector & ledValues) override;
};
+
+#endif // LEDEVICEUDPRAW_H
diff --git a/libsrc/leddevice/dev_net/LedDeviceWled.cpp b/libsrc/leddevice/dev_net/LedDeviceWled.cpp
new file mode 100644
index 00000000..952814ef
--- /dev/null
+++ b/libsrc/leddevice/dev_net/LedDeviceWled.cpp
@@ -0,0 +1,291 @@
+// Local-Hyperion includes
+#include "LedDeviceWled.h"
+
+#include
+#include
+
+// Constants
+namespace {
+
+// Configuration settings
+const char CONFIG_ADDRESS[] = "host";
+
+// UDP elements
+const quint16 STREAM_DEFAULT_PORT = 19446;
+
+// WLED JSON-API elements
+const int API_DEFAULT_PORT = -1; //Use default port per communication scheme
+
+const char API_BASE_PATH[] = "/json/";
+const char API_PATH_INFO[] = "info";
+const char API_PATH_STATE[] = "state";
+
+// List of State Information
+const char STATE_ON[] = "on";
+const char STATE_VALUE_TRUE[] = "true";
+const char STATE_VALUE_FALSE[] = "false";
+
+// WLED ssdp services
+// TODO: WLED - Update ssdp discovery parameters when available
+const char SSDP_ID[] = "ssdp:all";
+const char SSDP_FILTER[] = "(.*)";
+const char SSDP_FILTER_HEADER[] = "ST";
+
+} //End of constants
+
+LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig)
+ : ProviderUdp()
+ ,_restApi(nullptr)
+ ,_apiPort(API_DEFAULT_PORT)
+{
+ _devConfig = deviceConfig;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
+}
+
+LedDeviceWled::~LedDeviceWled()
+{
+ if ( _restApi != nullptr )
+ {
+ delete _restApi;
+ _restApi = nullptr;
+ }
+}
+
+LedDevice* LedDeviceWled::construct(const QJsonObject &deviceConfig)
+{
+ return new LedDeviceWled(deviceConfig);
+}
+
+bool LedDeviceWled::init(const QJsonObject &deviceConfig)
+{
+ Debug(_log, "");
+ bool isInitOK = false;
+
+ // Initialise LedDevice sub-class, ProviderUdp::init will be executed later, if connectivity is defined
+ if ( LedDevice::init(deviceConfig) )
+ {
+ // Initialise LedDevice configuration and execution environment
+ uint configuredLedCount = this->getLedCount();
+ Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
+ Debug(_log, "LedCount : %u", configuredLedCount);
+ Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
+ Debug(_log, "LatchTime : %d", this->getLatchTime());
+
+ //Set hostname as per configuration
+ QString address = deviceConfig[ CONFIG_ADDRESS ].toString();
+
+ //If host not configured the init fails
+ if ( address.isEmpty() )
+ {
+ this->setInError("No target hostname nor IP defined");
+ return false;
+ }
+ else
+ {
+ QStringList addressparts = QStringUtils::split(address,":", QStringUtils::SplitBehavior::SkipEmptyParts);
+ _hostname = addressparts[0];
+ if ( addressparts.size() > 1 )
+ {
+ _apiPort = addressparts[1].toInt();
+ }
+ else
+ {
+ _apiPort = API_DEFAULT_PORT;
+ }
+
+ if ( initRestAPI( _hostname, _apiPort ) )
+ {
+ // Update configuration with hostname without port
+ _devConfig["host"] = _hostname;
+ _devConfig["port"] = STREAM_DEFAULT_PORT;
+
+ isInitOK = ProviderUdp::init(_devConfig);
+ Debug(_log, "Hostname/IP : %s", QSTRING_CSTR( _hostname ));
+ Debug(_log, "Port : %d", _port);
+ }
+ }
+ }
+ Debug(_log, "[%d]", isInitOK);
+ return isInitOK;
+}
+
+bool LedDeviceWled::initRestAPI(const QString &hostname, const int port )
+{
+ Debug(_log, "");
+ bool isInitOK = false;
+
+ if ( _restApi == nullptr )
+ {
+ _restApi = new ProviderRestApi(hostname, port);
+ _restApi->setBasePath( API_BASE_PATH );
+
+ isInitOK = true;
+ }
+
+ Debug(_log, "[%d]", isInitOK);
+ return isInitOK;
+}
+
+QString LedDeviceWled::getOnOffRequest (bool isOn ) const
+{
+ QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
+ return QString( "{\"%1\":%2}" ).arg( STATE_ON, state);
+}
+
+bool LedDeviceWled::powerOn()
+{
+ Debug(_log, "");
+ bool on = true;
+ if ( _isDeviceReady)
+ {
+ //Power-on WLED device
+ _restApi->setPath(API_PATH_STATE);
+ httpResponse response = _restApi->put(getOnOffRequest(true));
+ if ( response.error() )
+ {
+ this->setInError ( response.getErrorReason() );
+ on = false;
+ }
+ }
+ return on;
+}
+
+bool LedDeviceWled::powerOff()
+{
+ Debug(_log, "");
+ bool off = true;
+ if ( _isDeviceReady)
+ {
+ // Write a final "Black" to have a defined outcome
+ writeBlack();
+
+ //Power-off the WLED device physically
+ _restApi->setPath(API_PATH_STATE);
+ httpResponse response = _restApi->put(getOnOffRequest(false));
+ if ( response.error() )
+ {
+ this->setInError ( response.getErrorReason() );
+ off = false;
+ }
+ }
+ return off;
+}
+
+QJsonObject LedDeviceWled::discover()
+{
+ QJsonObject devicesDiscovered;
+ devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
+
+ QJsonArray deviceList;
+
+ // Discover WLED Devices
+ SSDPDiscover discover;
+ discover.skipDuplicateKeys(true);
+ discover.setSearchFilter(SSDP_FILTER, SSDP_FILTER_HEADER);
+ QString searchTarget = SSDP_ID;
+
+ if ( discover.discoverServices(searchTarget) > 0 )
+ {
+ deviceList = discover.getServicesDiscoveredJson();
+ }
+
+ devicesDiscovered.insert("devices", deviceList);
+ Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ return devicesDiscovered;
+}
+
+QJsonObject LedDeviceWled::getProperties(const QJsonObject& params)
+{
+ Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ QJsonObject properties;
+
+ // Get Nanoleaf device properties
+ QString host = params["host"].toString("");
+ if ( !host.isEmpty() )
+ {
+ QString filter = params["filter"].toString("");
+
+ // Resolve hostname and port (or use default API port)
+ QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
+ QString apiHost = addressparts[0];
+ int apiPort;
+
+ if ( addressparts.size() > 1)
+ {
+ apiPort = addressparts[1].toInt();
+ }
+ else
+ {
+ apiPort = API_DEFAULT_PORT;
+ }
+
+ if ( filter.startsWith("/") )
+ filter.remove(0,1);
+
+ initRestAPI(apiHost, apiPort);
+ _restApi->setPath(API_PATH_INFO);
+
+ // Perform request
+ // TODO: WLED::getProperties - Check, if filter is supported
+ httpResponse response = _restApi->put(filter);
+ if ( response.error() )
+ {
+ Warning (_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
+ }
+
+ properties.insert("properties", response.getBody().object());
+
+ Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ }
+ return properties;
+}
+
+void LedDeviceWled::identify(const QJsonObject& params)
+{
+ Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ QJsonObject properties;
+
+ // Get Nanoleaf device properties
+ QString host = params["host"].toString("");
+ if ( !host.isEmpty() )
+ {
+ // Resolve hostname and port (or use default API port)
+ QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
+ QString apiHost = addressparts[0];
+ int apiPort;
+
+ if ( addressparts.size() > 1)
+ apiPort = addressparts[1].toInt();
+ else
+ apiPort = API_DEFAULT_PORT;
+
+ // TODO: WLED::identify - Replace with valid identification code
+
+ // initRestAPI(apiHost, apiPort);
+
+ // QString resource = QString("%1/%2/%3").arg( API_LIGHTS ).arg( lightId ).arg( API_STATE);
+ // _restApi->setPath(resource);
+
+ // QString stateCmd;
+ // stateCmd += QString("\"%1\":%2,").arg( API_STATE_ON ).arg( API_STATE_VALUE_TRUE );
+ // stateCmd += QString("\"%1\":\"%2\"").arg( "alert" ).arg( "select" );
+ // stateCmd = "{" + stateCmd + "}";
+
+ // // Perform request
+ // httpResponse response = _restApi->put(stateCmd);
+ // if ( response.error() )
+ // {
+ // Warning (_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
+ // }
+ }
+}
+
+int LedDeviceWled::write(const std::vector &ledValues)
+{
+ const uint8_t * dataPtr = reinterpret_cast(ledValues.data());
+
+ return writeBytes( _ledRGBCount, dataPtr);
+}
diff --git a/libsrc/leddevice/dev_net/LedDeviceWled.h b/libsrc/leddevice/dev_net/LedDeviceWled.h
new file mode 100644
index 00000000..172f62d0
--- /dev/null
+++ b/libsrc/leddevice/dev_net/LedDeviceWled.h
@@ -0,0 +1,132 @@
+#ifndef LEDDEVICEWLED_H
+#define LEDDEVICEWLED_H
+
+// LedDevice includes
+#include
+#include "ProviderRestApi.h"
+#include "ProviderUdp.h"
+
+///
+/// Implementation of a WLED-device
+/// ...
+///
+///
+class LedDeviceWled : public ProviderUdp
+{
+
+public:
+ ///
+ /// @brief Constructs a WLED-device
+ ///
+ /// @param deviceConfig Device's configuration as JSON-Object
+ ///
+ explicit LedDeviceWled(const QJsonObject &deviceConfig);
+
+ ///
+ /// @brief Destructor of the WLED-device
+ ///
+ virtual ~LedDeviceWled() override;
+
+ ///
+ /// @brief Constructs the WLED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ static LedDevice* construct(const QJsonObject &deviceConfig);
+
+ ///
+ /// @brief Discover WLED devices available (for configuration).
+ ///
+ /// @return A JSON structure holding a list of devices found
+ ///
+ virtual QJsonObject discover() override;
+
+ ///
+ /// @brief Get the WLED device's resource properties
+ ///
+ /// Following parameters are required
+ /// @code
+ /// {
+ /// "host" : "hostname or IP [:port]",
+ /// "filter": "resource to query", root "/" is used, if empty
+ /// }
+ ///@endcode
+ ///
+ /// @param[in] params Parameters to query device
+ /// @return A JSON structure holding the device's properties
+ ///
+ virtual QJsonObject getProperties(const QJsonObject& params) override;
+
+ ///
+ /// @brief Send an update to the WLED device to identify it.
+ ///
+ /// Following parameters are required
+ /// @code
+ /// {
+ /// "host" : "hostname or IP [:port]",
+ /// }
+ ///@endcode
+ ///
+ /// @param[in] params Parameters to address device
+ ///
+ virtual void identify(const QJsonObject& params) override;
+
+protected:
+
+ ///
+ /// @brief Initialise the WLED device's configuration and network address details
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ virtual int write(const std::vector & ledValues) override;
+
+ ///
+ /// @brief Power-/turn on the WLED device.
+ ///
+ /// @brief Store the device's original state.
+ ///
+ virtual bool powerOn() override;
+
+ ///
+ /// @brief Power-/turn off the WLED device.
+ ///
+ /// @return True if success
+ ///
+ virtual bool powerOff() override;
+
+private:
+
+ ///
+ /// @brief Initialise the access to the REST-API wrapper
+ ///
+ /// @param[in] host
+ /// @param[in] port
+ /// @return True, if success
+ ///
+ bool initRestAPI(const QString &hostname, const int port );
+
+ ///
+ /// @brief Get command to power WLED-device on or off
+ ///
+ /// @param isOn True, if to switch on device
+ /// @return Command to switch device on/off
+ ///
+ QString getOnOffRequest (bool isOn ) const;
+
+ ///REST-API wrapper
+ ProviderRestApi* _restApi;
+
+ QString _hostname;
+ int _apiPort;
+};
+
+#endif // LEDDEVICEWLED_H
diff --git a/libsrc/leddevice/dev_net/LedDeviceYeelight.cpp b/libsrc/leddevice/dev_net/LedDeviceYeelight.cpp
new file mode 100644
index 00000000..236acbb7
--- /dev/null
+++ b/libsrc/leddevice/dev_net/LedDeviceYeelight.cpp
@@ -0,0 +1,1502 @@
+#include "LedDeviceYeelight.h"
+
+#include
+#include
+
+// Qt includes
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+// Constants
+namespace {
+
+const bool verbose = false;
+const bool verbose3 = false;
+
+constexpr std::chrono::milliseconds WRITE_TIMEOUT{1000}; // device write timeout in ms
+constexpr std::chrono::milliseconds READ_TIMEOUT{1000}; // device read timeout in ms
+constexpr std::chrono::milliseconds CONNECT_TIMEOUT{1000}; // device connect timeout in ms
+constexpr std::chrono::milliseconds CONNECT_STREAM_TIMEOUT{1000}; // device streaming connect timeout in ms
+
+const bool TEST_CORRELATION_IDS = false; //Ignore, if yeelight sends responses in different order as request commands
+
+// Configuration settings
+const char CONFIG_LIGHTS [] = "lights";
+
+const char CONFIG_COLOR_MODEL [] = "colorModel";
+const char CONFIG_TRANS_EFFECT [] = "transEffect";
+const char CONFIG_TRANS_TIME [] = "transTime";
+const char CONFIG_EXTRA_TIME_DARKNESS[] = "extraTimeDarkness";
+const char CONFIG_DEBUGLEVEL [] = "debugLevel";
+
+const char CONFIG_BRIGHTNESS_MIN[] = "brightnessMin";
+const char CONFIG_BRIGHTNESS_SWITCHOFF[] = "brightnessSwitchOffOnMinimum";
+const char CONFIG_BRIGHTNESS_MAX[] = "brightnessMax";
+const char CONFIG_BRIGHTNESSFACTOR[] = "brightnessFactor";
+
+const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
+
+const char CONFIG_QUOTA_WAIT_TIME[] = "quotaWait";
+
+// Yeelights API
+const int API_DEFAULT_PORT = 55443;
+const quint16 API_DEFAULT_QUOTA_WAIT_TIME = 1000;
+
+// Yeelight API Command
+const char API_COMMAND_ID[] = "id";
+const char API_COMMAND_METHOD[] = "method";
+const char API_COMMAND_PARAMS[] = "params";
+const char API_COMMAND_PROPS[] = "props";
+
+const char API_PARAM_CLASS_COLOR[] = "color";
+const char API_PARAM_CLASS_HSV[] = "hsv";
+
+const char API_PROP_NAME[] = "name";
+const char API_PROP_MODEL[] = "model";
+const char API_PROP_FWVER[] = "fw_ver";
+
+const char API_PROP_POWER[] = "power";
+const char API_PROP_MUSIC[] = "music_on";
+const char API_PROP_RGB[] = "rgb";
+const char API_PROP_CT[] = "ct";
+const char API_PROP_COLORFLOW[] = "cf";
+const char API_PROP_BRIGHT[] = "bright";
+
+// List of Result Information
+const char API_RESULT_ID[] = "id";
+const char API_RESULT[] = "result";
+//const char API_RESULT_OK[] = "OK";
+
+// List of Error Information
+const char API_ERROR[] = "error";
+const char API_ERROR_CODE[] = "code";
+const char API_ERROR_MESSAGE[] = "message";
+
+// Yeelight ssdp services
+const char SSDP_ID[] = "wifi_bulb";
+const char SSDP_FILTER[] = "yeelight(.*)";
+const char SSDP_FILTER_HEADER[] = "Location";
+const quint16 SSDP_PORT = 1982;
+
+} //End of constants
+
+YeelightLight::YeelightLight( Logger *log, const QString &hostname, quint16 port = API_DEFAULT_PORT)
+ :_log(log)
+ ,_debugLevel(0)
+ ,_isInError(false)
+ ,_host (hostname)
+ ,_port(port)
+ ,_tcpSocket(nullptr)
+ ,_tcpStreamSocket(nullptr)
+ ,_correlationID(0)
+ ,_lastWriteTime(QDateTime::currentMSecsSinceEpoch())
+ ,_lastColorRgbValue(0)
+ ,_transitionEffect(YeelightLight::API_EFFECT_SMOOTH)
+ ,_transitionDuration(API_PARAM_DURATION.count())
+ ,_extraTimeDarkness(API_PARAM_EXTRA_TIME_DARKNESS.count())
+ ,_brightnessMin(0)
+ ,_isBrightnessSwitchOffMinimum(false)
+ ,_brightnessMax(100)
+ ,_brightnessFactor(1.0)
+ ,_transitionEffectParam(API_PARAM_EFFECT_SMOOTH)
+ ,_waitTimeQuota(API_DEFAULT_QUOTA_WAIT_TIME)
+ ,_isOn(false)
+ ,_isInMusicMode(false)
+{
+ _name = hostname;
+
+}
+
+YeelightLight::~YeelightLight()
+{
+ log (3,"~YeelightLight()","" );
+ if ( _tcpSocket != nullptr)
+ {
+ delete _tcpSocket;
+ }
+ log (2,"~YeelightLight()","void" );
+}
+
+void YeelightLight::setHostname( const QString &hostname, quint16 port = API_DEFAULT_PORT )
+{
+ log (3,"setHostname()","" );
+ _host = hostname;
+ _port =port;
+}
+
+void YeelightLight::setStreamSocket( QTcpSocket* socket )
+{
+ log (3,"setStreamSocket()","" );
+ _tcpStreamSocket = socket;
+}
+
+bool YeelightLight::open()
+{
+ _isInError = false;
+ bool rc = false;
+
+ if ( _tcpSocket == nullptr )
+ {
+ _tcpSocket = new QTcpSocket();
+ }
+
+ if ( _tcpSocket->state() == QAbstractSocket::ConnectedState )
+ {
+ log (2,"open()","Device is already connected, skip opening: [%d]", _tcpSocket->state());
+ rc = true;
+ }
+ else
+ {
+ _tcpSocket->connectToHost( _host, _port);
+
+ if ( _tcpSocket->waitForConnected( CONNECT_TIMEOUT.count() ) )
+ {
+ if ( _tcpSocket->state() != QAbstractSocket::ConnectedState )
+ {
+ this->setInError( _tcpSocket->errorString() );
+ rc = false;
+ }
+ else
+ {
+ log (2,"open()","Successfully opened Yeelight: %s", QSTRING_CSTR(_host));
+ rc = true;
+ }
+ }
+ else
+ {
+ this->setInError( _tcpSocket->errorString() );
+ rc = false;
+ }
+ }
+ return rc;
+}
+
+bool YeelightLight::close()
+{
+ bool rc = true;
+
+ if ( _tcpSocket != nullptr )
+ {
+ // Test, if device requires closing
+ if ( _tcpSocket->isOpen() )
+ {
+ log (2,"close()","Close Yeelight: %s", QSTRING_CSTR(_host));
+ _tcpSocket->close();
+ // Everything is OK -> device is closed
+ }
+ }
+
+ if ( _tcpStreamSocket != nullptr )
+ {
+ // Test, if stream socket requires closing
+ if ( _tcpStreamSocket->isOpen() )
+ {
+ log (2,"close()","Close stream Yeelight: %s", QSTRING_CSTR(_host));
+ _tcpStreamSocket->close();
+ }
+ }
+ return rc;
+}
+
+int YeelightLight::writeCommand( const QJsonDocument &command )
+{
+ QJsonArray result;
+ return writeCommand(command, result );
+}
+
+int YeelightLight::writeCommand( const QJsonDocument &command, QJsonArray &result )
+{
+ log( 3,
+ "writeCommand()",
+ "isON[%d], isInMusicMode[%d]",
+ static_cast( _isOn ), static_cast( _isInMusicMode ) );
+ if (_debugLevel >= 2)
+ {
+ QString help = command.toJson(QJsonDocument::Compact);
+ log (2,"writeCommand()","%s", QSTRING_CSTR(help));
+ }
+
+ int rc = -1;
+
+ if ( ! _isInError && _tcpSocket->isOpen() )
+ {
+ qint64 bytesWritten = _tcpSocket->write( command.toJson(QJsonDocument::Compact) + "\r\n");
+ if (bytesWritten == -1 )
+ {
+ this->setInError( QString ("Write Error: %1").arg(_tcpSocket->errorString()) );
+ }
+ else
+ {
+ if ( ! _tcpSocket->waitForBytesWritten(WRITE_TIMEOUT.count()) )
+ {
+ QString errorReason = QString ("(%1) %2").arg(_tcpSocket->error()).arg( _tcpSocket->errorString());
+ log ( 2, "Error:", "bytesWritten: [%d], %s", bytesWritten, QSTRING_CSTR(errorReason));
+ this->setInError ( errorReason );
+ }
+ else
+ {
+ log ( 3, "Success:", "Bytes written [%d]", bytesWritten );
+
+ // Avoid to overrun the Yeelight Command Quota
+ qint64 elapsedTime = QDateTime::currentMSecsSinceEpoch() - _lastWriteTime;
+
+ if ( elapsedTime < _waitTimeQuota )
+ {
+ int waitTime = _waitTimeQuota;
+ log ( 1, "writeCommand():", "Wait %dms, elapsedTime: %dms < quotaTime: %dms", waitTime, elapsedTime, _waitTimeQuota);
+
+ // Wait time (in ms) before doing next write to not overrun Yeelight command quota
+ std::this_thread::sleep_for(std::chrono::milliseconds(_waitTimeQuota));
+ }
+ }
+
+ if ( _tcpSocket->waitForReadyRead(READ_TIMEOUT.count()) )
+ {
+ do
+ {
+ log ( 3, "Reading:", "Bytes available [%d]", _tcpSocket->bytesAvailable() );
+ while ( _tcpSocket->canReadLine() )
+ {
+ QByteArray response = _tcpSocket->readLine();
+
+ YeelightResponse yeeResponse = handleResponse( _correlationID, response );
+ switch ( yeeResponse.error() ) {
+
+ case YeelightResponse::API_NOTIFICATION:
+ rc=0;
+ break;
+ case YeelightResponse::API_OK:
+ result = yeeResponse.getResult();
+ rc=0;
+ break;
+ case YeelightResponse::API_ERROR:
+ result = yeeResponse.getResult();
+ QString errorReason = QString ("(%1) %2").arg(yeeResponse.getErrorCode()).arg( yeeResponse.getErrorReason() );
+ if ( yeeResponse.getErrorCode() != -1)
+ {
+ this->setInError ( errorReason );
+ rc =-1;
+ }
+ else
+ {
+ //(-1) client quota exceeded
+ log ( 1, "writeCommand():", "%s", QSTRING_CSTR(errorReason) );
+ rc = -2;
+ }
+ break;
+ }
+ }
+ log ( 3, "Info:", "Trying to read more responses");
+ }
+ while ( _tcpSocket->waitForReadyRead(500) );
+ }
+
+ log ( 3, "Info:", "No more responses available");
+ }
+
+ //In case of no error or quota exceeded, update late write time avoiding immediate next write
+ if ( rc == 0 || rc == -2 )
+ {
+ _lastWriteTime = QDateTime::currentMSecsSinceEpoch();
+ }
+ }
+ else
+ {
+ log ( 2, "Info:", "Skip write. Device is in error");
+ }
+
+ log (3,"writeCommand() rc","%d", rc );
+ return rc;
+}
+
+bool YeelightLight::streamCommand( const QJsonDocument &command )
+{
+ // ToDo: Wofür gibt es isON, wenn es beim StreamCommand nicht verwendet wird?
+ //log (3,"streamCommand()","isON[%d], isInMusicMode[%d]", _isOn, _isInMusicMode );
+ if (_debugLevel >= 2)
+ {
+ QString help = command.toJson(QJsonDocument::Compact);
+ log (3,"streamCommand()","%s", QSTRING_CSTR(help));
+ }
+
+ bool rc = false;
+
+ if ( ! _isInError && _tcpStreamSocket->isOpen() )
+ {
+ qint64 bytesWritten = _tcpStreamSocket->write( command.toJson(QJsonDocument::Compact) + "\r\n");
+ if (bytesWritten == -1 )
+ {
+ this->setInError( QString ("Streaming Error %1").arg(_tcpStreamSocket->errorString()) );
+ }
+ else
+ {
+ if ( ! _tcpStreamSocket->waitForBytesWritten(WRITE_TIMEOUT.count()) )
+ {
+ int error = _tcpStreamSocket->error();
+ QString errorReason = QString ("(%1) %2").arg(error).arg( _tcpStreamSocket->errorString());
+ log ( 1, "Error:", "bytesWritten: [%d], %s", bytesWritten, QSTRING_CSTR(errorReason));
+
+ if ( error == QAbstractSocket::RemoteHostClosedError )
+ {
+ log (1,"streamCommand()","RemoteHostClosedError - Give it a retry");
+ _isInMusicMode = false;
+ rc = true;
+ }
+ else
+ {
+ this->setInError ( errorReason );
+ }
+ }
+ else
+ {
+ log ( 3, "Success:", "Bytes written [%d]", bytesWritten );
+ rc = true;
+ }
+ }
+ }
+ else
+ {
+ log ( 2, "Info:", "Skip write. Device is in error");
+ }
+
+ //log (2,"streamCommand() rc","%d, isON[%d], isInMusicMode[%d]", rc, _isOn, _isInMusicMode );
+ return rc;
+}
+
+YeelightResponse YeelightLight::handleResponse(int correlationID, QByteArray const &response )
+{
+ log (3,"handleResponse()","" );
+
+ //std::cout << _name.toStdString() <<"| Response: [" << response.toStdString() << "]" << std::endl << std::flush;
+
+ YeelightResponse yeeResponse;
+ QString errorReason;
+
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(response, &error);
+
+ if (error.error != QJsonParseError::NoError)
+ {
+ yeeResponse.setErrorCode (-10000);
+ yeeResponse.setErrorReason( "Got invalid response" );
+ }
+ else
+ {
+ QString strJson(jsonDoc.toJson(QJsonDocument::Compact));
+
+ QJsonObject jsonObj = jsonDoc.object();
+
+ if ( !jsonObj[API_COMMAND_METHOD].isNull() )
+ {
+ yeeResponse.setError(YeelightResponse::API_NOTIFICATION);
+ yeeResponse.setResult( QJsonArray() );
+ // Do process notifications only for debugging
+ if ( verbose3 )
+ {
+ log ( 3, "Info:", "Notification found : [%s]", QSTRING_CSTR( jsonObj[API_COMMAND_METHOD].toString()));
+
+ QString method = jsonObj[API_COMMAND_METHOD].toString();
+
+ if ( method == API_COMMAND_PROPS )
+ {
+
+ if ( jsonObj.contains(API_COMMAND_PARAMS) && jsonObj[API_COMMAND_PARAMS].isObject() )
+ {
+ QVariantMap paramsMap = jsonObj[API_COMMAND_PARAMS].toVariant().toMap();
+
+ // Loop over all children.
+ for (const QString & property : paramsMap.keys())
+ {
+ QString value = paramsMap[property].toString();
+ log ( 3, "Notification ID:", "[%s]:[%s]", QSTRING_CSTR( property ), QSTRING_CSTR( value ));
+ }
+ }
+ }
+ else
+ {
+ log ( 1, "Error:", "Invalid notification message: [%s]", strJson.toUtf8().constData() );
+ }
+ }
+ }
+ else
+ {
+ int id = jsonObj[API_RESULT_ID].toInt();
+ //log ( 3, "Correlation ID:", "%d", id );
+
+ if ( id != correlationID && TEST_CORRELATION_IDS)
+ {
+ errorReason = QString ("%1| API is out of sync, received ID [%2], expected [%3]").
+ arg( _name ).arg( id ).arg( correlationID );
+
+ yeeResponse.setErrorCode (-11000);
+ yeeResponse.setErrorReason( errorReason );
+
+ this->setInError ( errorReason );
+ }
+ else
+ {
+
+ if ( jsonObj.contains(API_RESULT) && jsonObj[API_RESULT].isArray() )
+ {
+
+ // API call returned an result
+ yeeResponse.setResult( jsonObj[API_RESULT].toArray() );
+
+ // Break down result only for debugging
+ if ( verbose3 )
+ {
+ // Debug output
+ if(!yeeResponse.getResult().empty())
+ {
+ for(const auto item : yeeResponse.getResult())
+ {
+ log ( 3, "Result:", "%s", QSTRING_CSTR( item.toString() ));
+ }
+ }
+ }
+ }
+ else
+ {
+ yeeResponse.setError(YeelightResponse::API_ERROR);
+ if ( jsonObj.contains(API_ERROR) && jsonObj[API_ERROR].isObject() )
+ {
+ QVariantMap errorMap = jsonObj[API_ERROR].toVariant().toMap();
+
+ yeeResponse.setErrorCode (errorMap.value(API_ERROR_CODE).toInt());
+ yeeResponse.setErrorReason( errorMap.value(API_ERROR_MESSAGE).toString() );
+ }
+ else
+ {
+ yeeResponse.setErrorCode (-10010);
+ yeeResponse.setErrorReason( "No valid result message" );
+ log ( 1, "Reply:", "[%s]", strJson.toUtf8().constData());
+ }
+ }
+ }
+ }
+ }
+ log (3,"handleResponse()", "yeeResponse.error [%d]", yeeResponse.error() );
+ return yeeResponse;
+}
+
+void YeelightLight::setInError(const QString& errorMsg)
+{
+ _isInError = true;
+ Error(_log, "Yeelight device '%s' signals error: '%s'", QSTRING_CSTR( _name ), QSTRING_CSTR(errorMsg));
+}
+
+QJsonDocument YeelightLight::getCommand(const QString &method, const QJsonArray ¶ms)
+{
+ //Increment Correlation-ID
+ ++_correlationID;
+
+ QJsonObject obj;
+ obj.insert(API_COMMAND_ID,_correlationID);
+ obj.insert(API_COMMAND_METHOD,method);
+ obj.insert(API_COMMAND_PARAMS,params);
+
+ return QJsonDocument(obj);
+}
+
+QJsonObject YeelightLight::getProperties()
+{
+ log (3,"getProperties()","" );
+ QJsonObject properties;
+
+ //Selected properties
+ //QJsonArray propertyList = { API_PROP_NAME, API_PROP_MODEL, API_PROP_POWER, API_PROP_RGB, API_PROP_BRIGHT, API_PROP_CT, API_PROP_FWVER };
+
+ //All properties
+ QJsonArray propertyList = {"power","bright","ct","rgb","hue","sat","color_mode","flowing","delayoff","music_on","name","bg_power","bg_flowing","bg_ct","bg_bright","bg_hue","bg_sat","bg_rgb","nl_br","active_mode" };
+
+ QJsonDocument command = getCommand( API_METHOD_GETPROP, propertyList );
+
+ QJsonArray result;
+
+ if ( writeCommand( command, result ) > -1 )
+ {
+
+ // Debug output
+ if( !result.empty())
+ {
+ int i = 0;
+ for(const auto item : result)
+ {
+ log (1,"Property:", "%s = %s", QSTRING_CSTR( propertyList.at(i).toString() ), QSTRING_CSTR( item.toString() ));
+ properties.insert( propertyList.at(i).toString(), item );
+ ++i;
+ }
+ }
+ }
+
+ log (2,"getProperties()","QJsonObject");
+ return properties;
+}
+
+bool YeelightLight::identify()
+{
+ log (3,"identify()","" );
+ bool rc = true;
+
+ /*
+ count 6, total number of visible state changing before color flow is stopped
+ action 0, 0 means smart LED recover to the state before the color flow started
+
+ Duration: 500, Gradual change timer sleep-time, in milliseconds
+ Mode: 1, color
+ Value: 100, RGB value when mode is 1 (blue)
+ Brightness: 100, Brightness value
+
+ Duration: 500
+ Mode: 1
+ Value: 16711696 (red)
+ Brightness: 10
+ */
+ QJsonArray colorflowParams = { API_PROP_COLORFLOW, 6, 0, "500,1,100,100,500,1,16711696,10"};
+
+ //Blink White
+ //QJsonArray colorflowParams = { API_PROP_COLORFLOW, 6, 0, "500,2,4000,1,500,2,4000,50"};
+
+ QJsonDocument command = getCommand( API_METHOD_SETSCENE, colorflowParams );
+
+ if ( writeCommand( command ) < 0 )
+ {
+ rc= false;
+ }
+
+ log( 2, "identify() rc","%d", static_cast(rc) );
+ return rc;
+}
+
+bool YeelightLight::isInMusicMode( bool deviceCheck)
+{
+ bool inMusicMode = false;
+
+ if ( deviceCheck )
+ {
+ // Get status from device directly
+ QJsonArray propertylist = { API_PROP_MUSIC };
+
+ QJsonDocument command = getCommand( API_METHOD_GETPROP, propertylist );
+
+ QJsonArray result;
+
+ if ( writeCommand( command, result ) >= 0 )
+ {
+ if( !result.empty())
+ {
+ inMusicMode = result.at(0).toString() == "1";
+ }
+ }
+ }
+ else
+ {
+ // Test indirectly avoiding command quota
+ if ( _tcpStreamSocket != nullptr)
+ {
+ if ( _tcpStreamSocket->state() == QAbstractSocket::ConnectedState )
+ {
+ log (3,"isInMusicMode", "Yes, as socket is in ConnectedState");
+ inMusicMode = true;
+ }
+ else
+ {
+ log (1,"isInMusicMode", "No, StreamSocket state: %d", _tcpStreamSocket->state());
+ }
+ }
+ }
+ _isInMusicMode = inMusicMode;
+
+ log( 3, "isInMusicMode()", "%d", static_cast( _isInMusicMode ) );
+
+ return _isInMusicMode;
+}
+
+void YeelightLight::mapProperties(const QJsonObject &properties)
+{
+ log (3,"mapProperties()","" );
+
+ if ( _name.isEmpty() )
+ {
+ _name = properties.value(API_PROP_NAME).toString();
+ if ( _name.isEmpty() )
+ {
+ _name = _host;
+ }
+ }
+ _model = properties.value(API_PROP_MODEL).toString();
+ _fw_ver = properties.value(API_PROP_FWVER).toString();
+
+ _power = properties.value(API_PROP_POWER).toString();
+ _colorRgbValue = properties.value(API_PROP_RGB).toString().toInt();
+ _bright = properties.value(API_PROP_BRIGHT).toString().toInt();
+ _ct = properties.value(API_PROP_CT).toString().toInt();
+
+ log (2,"mapProperties() rc","void" );
+}
+
+void YeelightLight::storeState()
+{
+ log (3,"storeState()","" );
+
+ _originalStateProperties = this->getProperties();
+ mapProperties( _originalStateProperties );
+
+ log (2,"storeState() rc","void" );
+}
+
+bool YeelightLight::restoreState()
+{
+ log (3,"restoreState()","" );
+ bool rc = false;
+
+ QJsonArray paramlist = { API_PARAM_CLASS_COLOR, _colorRgbValue, _bright };
+
+ if ( _isInMusicMode )
+ {
+ rc = streamCommand( getCommand( API_METHOD_SETSCENE, paramlist ) );
+ }
+ else
+ {
+ if ( writeCommand( getCommand( API_METHOD_SETSCENE, paramlist ) ) >= 0 )
+ {
+ rc =true;
+ }
+ }
+
+ log( 2, "restoreState() rc","%d", static_cast(rc) );
+ return rc;
+}
+
+bool YeelightLight::setPower(bool on)
+{
+ return setPower( on, _transitionEffect, _transitionDuration);
+}
+
+bool YeelightLight::setPower(bool on, YeelightLight::API_EFFECT effect, int duration, API_MODE mode)
+{
+ bool rc = false;
+ log( 3,
+ "setPower()",
+ "isON[%d], isInMusicMode[%d]",
+ static_cast( _isOn), static_cast(_isInMusicMode ) );
+
+ // Disable music mode to get power-off command executed
+ if ( !on && _isInMusicMode )
+ {
+ if ( _tcpStreamSocket != nullptr )
+ {
+ _tcpStreamSocket->close();
+ }
+ }
+
+ QString powerParam = on ? API_METHOD_POWER_ON : API_METHOD_POWER_OFF;
+ QString effectParam = effect == YeelightLight::API_EFFECT_SMOOTH ? API_PARAM_EFFECT_SMOOTH : API_PARAM_EFFECT_SUDDEN;
+
+ QJsonArray paramlist = { powerParam, effectParam, duration, mode };
+
+ // If power off was successful, automatically music-mode is off too
+ if ( writeCommand( getCommand( API_METHOD_POWER, paramlist ) ) > -1 )
+ {
+ _isOn = on;
+ if ( !on )
+ {
+ _isInMusicMode = false;
+ }
+ rc =true;
+ }
+ log( 2,
+ "setPower() rc",
+ "%d, isON[%d], isInMusicMode[)%d]",
+ static_cast(rc), static_cast( _isOn ), static_cast( _isInMusicMode ) );
+
+ return rc;
+}
+
+bool YeelightLight::setColorRGB(const ColorRgb &color)
+{
+ bool rc = true;
+
+ int colorParam = (color.red * 65536) + (color.green * 256) + color.blue;
+
+ if ( colorParam == 0 )
+ {
+ colorParam = 1;
+ }
+
+ if ( colorParam != _lastColorRgbValue )
+ {
+ int bri = std::max( { color.red, color.green, color.blue } ) * 100 / 255;
+ int duration = _transitionDuration;
+
+ if ( bri < _brightnessMin )
+ {
+ if ( _isBrightnessSwitchOffMinimum )
+ {
+ log( 2,
+ "Set Color RGB:",
+ "Turn off, brightness [%d] < _brightnessMin [%d], "
+ "_isBrightnessSwitchOffMinimum [%d]",
+ bri,_brightnessMin, static_cast(_isBrightnessSwitchOffMinimum ) );
+ // Set brightness to 0
+ bri = 0;
+ duration = _transitionDuration + _extraTimeDarkness;
+ }
+ else
+ {
+ //If not switchOff on MinimumBrightness, avoid switch-off
+ log( 2,
+ "Set Color RGB:",
+ "Set brightness[%d] to minimum brightness [%d], if not _isBrightnessSwitchOffMinimum [%d]",
+ bri, _brightnessMin, static_cast( _isBrightnessSwitchOffMinimum ) );
+ bri = _brightnessMin;
+ }
+ }
+ else
+ {
+ bri = ( qMin( _brightnessMax, static_cast (_brightnessFactor * qMax( _brightnessMin, bri ) ) ) );
+ }
+
+ log ( 3, "Set Color RGB:", "{%u,%u,%u} -> [%d], [%d], [%d], [%d]", color.red, color.green, color.blue, colorParam, bri, _transitionEffect, _transitionDuration );
+ QJsonArray paramlist = { API_PARAM_CLASS_COLOR, colorParam, bri };
+
+ // Only add transition effect and duration, if device smoothing is configured (older FW do not support this parameters in set_scene
+ if ( _transitionEffect == YeelightLight::API_EFFECT_SMOOTH )
+ {
+ paramlist << _transitionEffectParam << duration;
+ }
+
+ bool writeOK = false;
+ if ( _isInMusicMode )
+ {
+ writeOK = streamCommand( getCommand( API_METHOD_SETSCENE, paramlist ) );
+ }
+ else
+ {
+ if ( writeCommand( getCommand( API_METHOD_SETSCENE, paramlist ) ) >= 0 )
+ {
+ writeOK = true;
+ }
+ }
+ if ( writeOK )
+ {
+ _lastColorRgbValue = colorParam;
+ }
+ else
+ {
+ rc = false;
+ }
+ }
+ //log (2,"setColorRGB() rc","%d, isON[%d], isInMusicMode[%d]", rc, _isOn, _isInMusicMode );
+ return rc;
+}
+
+bool YeelightLight::setColorHSV(const ColorRgb &colorRGB)
+{
+ bool rc = true;
+
+ QColor color(colorRGB.red, colorRGB.green, colorRGB.blue);
+
+ if ( color != _color )
+ {
+ int hue;
+ int sat;
+ int bri;
+ int duration = _transitionDuration;
+
+ color.getHsv( &hue, &sat, &bri);
+
+ //Align to Yeelight number ranges (hue: 0-359, sat: 0-100, bri: 0-100)
+ if ( hue == -1)
+ {
+ hue = 0;
+ }
+ sat = sat * 100 / 255;
+ bri = bri * 100 / 255;
+
+ if ( bri < _brightnessMin )
+ {
+ if ( _isBrightnessSwitchOffMinimum )
+ {
+ log( 2,
+ "Set Color HSV:",
+ "Turn off, brightness [%d] < _brightnessMin [%d], "
+ "_isBrightnessSwitchOffMinimum [%d]",
+ bri,
+ _brightnessMin,
+ static_cast( _isBrightnessSwitchOffMinimum ) );
+ // Set brightness to 0
+ bri = 0;
+ duration = _transitionDuration + _extraTimeDarkness;
+ }
+ else
+ {
+ //If not switchOff on MinimumBrightness, avoid switch-off
+ log( 2,
+ "Set Color HSV:",
+ "Set brightness[%d] to minimum brightness [%d], if not _isBrightnessSwitchOffMinimum [%d]",
+ bri, _brightnessMin, static_cast( _isBrightnessSwitchOffMinimum ));
+ bri = _brightnessMin;
+ }
+ }
+ else
+ {
+ bri = ( qMin( _brightnessMax, static_cast (_brightnessFactor * qMax( _brightnessMin, bri ) ) ) );
+ }
+ log ( 2, "Set Color HSV:", "{%u,%u,%u}, [%d], [%d]", hue, sat, bri, _transitionEffect, duration );
+ QJsonArray paramlist = { API_PARAM_CLASS_HSV, hue, sat, bri };
+
+ // Only add transition effect and duration, if device smoothing is configured (older FW do not support this parameters in set_scene
+ if ( _transitionEffect == YeelightLight::API_EFFECT_SMOOTH )
+ {
+ paramlist << _transitionEffectParam << duration;
+ }
+
+ bool writeOK=false;
+ if ( _isInMusicMode )
+ {
+ writeOK = streamCommand( getCommand( API_METHOD_SETSCENE, paramlist ) );
+ }
+ else
+ {
+ if ( writeCommand( getCommand( API_METHOD_SETSCENE, paramlist ) ) >= 0 )
+ {
+ writeOK = true;
+ }
+ }
+
+ if ( writeOK )
+ {
+ _isOn = true;
+ if ( bri == 0 )
+ {
+ _isOn = false;
+ _isInMusicMode = false;
+ }
+ _color = color;
+ }
+ else
+ {
+ rc = false;
+ }
+ }
+ else
+ {
+ //log ( 3, "setColorHSV", "Skip update. Same Color as before");
+ }
+ log( 3,
+ "setColorHSV() rc",
+ "%d, isON[%d], isInMusicMode[%d]",
+ static_cast( rc ), static_cast( _isOn ), static_cast( _isInMusicMode ) );
+ return rc;
+}
+
+
+void YeelightLight::setTransitionEffect ( YeelightLight::API_EFFECT effect ,int duration )
+{
+ if( effect != _transitionEffect )
+ {
+ _transitionEffect = effect;
+ _transitionEffectParam = effect == YeelightLight::API_EFFECT_SMOOTH ? API_PARAM_EFFECT_SMOOTH : API_PARAM_EFFECT_SUDDEN;
+ }
+
+ if( duration != _transitionDuration )
+ {
+ _transitionDuration = duration;
+ }
+
+}
+
+void YeelightLight::setBrightnessConfig (int min, int max, bool switchoff, int extraTime, double factor )
+{
+ _brightnessMin = min;
+ _isBrightnessSwitchOffMinimum = switchoff;
+ _brightnessMax = max;
+ _brightnessFactor = factor;
+ _extraTimeDarkness = extraTime;
+}
+
+bool YeelightLight::setMusicMode(bool on, const QHostAddress &hostAddress, int port)
+{
+ bool rc = false;
+ int musicModeParam = on ? API_METHOD_MUSIC_MODE_ON : API_METHOD_MUSIC_MODE_OFF;
+
+ QJsonArray paramlist = { musicModeParam };
+
+ if ( on )
+ {
+ paramlist << hostAddress.toString() << port;
+ }
+
+ // Music Mode is only on, if write did not fail nor quota was exceeded
+ if ( writeCommand( getCommand( API_METHOD_MUSIC_MODE, paramlist ) ) > -1 )
+ {
+ _isInMusicMode = on;
+ rc = true;
+ }
+
+ log( 2,
+ "setMusicMode() rc", "%d, isInMusicMode[%d]", static_cast( rc ), static_cast( _isInMusicMode ) );
+ return rc;
+}
+
+void YeelightLight::log(const int logLevel, const char* msg, const char* type, ...)
+{
+ if ( logLevel <= _debugLevel)
+ {
+ const size_t max_val_length = 1024;
+ char val[max_val_length];
+ va_list args;
+ va_start(args, type);
+ vsnprintf(val, max_val_length, type, args);
+ va_end(args);
+ std::string s = msg;
+ uint max = 20;
+ s.append(max - s.length(), ' ');
+
+ Debug( _log, "%d|%15.15s| %s: %s", logLevel, QSTRING_CSTR(_name), s.c_str(), val);
+ }
+}
+
+//---------------------------------------------------------------------------------
+
+LedDeviceYeelight::LedDeviceYeelight(const QJsonObject &deviceConfig)
+ : LedDevice()
+ ,_lightsCount (0)
+ ,_outputColorModel(0)
+ ,_transitionEffect(YeelightLight::API_EFFECT_SMOOTH)
+ ,_transitionDuration(API_PARAM_DURATION.count())
+ ,_extraTimeDarkness(0)
+ ,_brightnessMin(0)
+ ,_isBrightnessSwitchOffMinimum(false)
+ ,_brightnessMax(100)
+ ,_brightnessFactor(1.0)
+ ,_waitTimeQuota(API_DEFAULT_QUOTA_WAIT_TIME)
+ ,_debuglevel(0)
+ ,_musicModeServerPort(-1)
+{
+ _devConfig = deviceConfig;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
+}
+
+LedDeviceYeelight::~LedDeviceYeelight()
+{
+ if ( _tcpMusicModeServer != nullptr )
+ {
+ delete _tcpMusicModeServer;
+ }
+}
+
+LedDevice* LedDeviceYeelight::construct(const QJsonObject &deviceConfig)
+{
+ return new LedDeviceYeelight(deviceConfig);
+}
+
+bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
+{
+ // Overwrite non supported/required features
+ if (deviceConfig["rewriteTime"].toInt(0) > 0)
+ {
+ Info (_log, "Yeelights do not require rewrites. Refresh time is ignored.");
+ _devConfig["rewriteTime"] = 0;
+ }
+
+ DebugIf(verbose, _log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ bool isInitOK = false;
+
+ if ( LedDevice::init(deviceConfig) )
+ {
+ Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
+ Debug(_log, "LedCount : %u", this->getLedCount());
+ Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
+ Debug(_log, "RefreshTime : %d", _refreshTimerInterval_ms);
+ Debug(_log, "LatchTime : %d", this->getLatchTime());
+
+ //Get device specific configuration
+
+ bool ok;
+ if ( deviceConfig[ CONFIG_COLOR_MODEL ].isString() )
+ {
+ _outputColorModel = deviceConfig[ CONFIG_COLOR_MODEL ].toString().toInt(&ok,MODEL_RGB);
+ }
+ else
+ {
+ _outputColorModel = deviceConfig[ CONFIG_COLOR_MODEL ].toInt(MODEL_RGB);
+ }
+
+ if ( deviceConfig[ CONFIG_TRANS_EFFECT ].isString() )
+ {
+ _transitionEffect = static_cast( deviceConfig[ CONFIG_TRANS_EFFECT ].toString().toInt(&ok, YeelightLight::API_EFFECT_SMOOTH) );
+ }
+ else
+ {
+ _transitionEffect = static_cast( deviceConfig[ CONFIG_TRANS_EFFECT ].toInt(YeelightLight::API_EFFECT_SMOOTH) );
+ }
+
+ _transitionDuration = deviceConfig[ CONFIG_TRANS_TIME ].toInt(API_PARAM_DURATION.count());
+ _extraTimeDarkness = _devConfig[CONFIG_EXTRA_TIME_DARKNESS].toInt(0);
+
+ _brightnessMin = _devConfig[CONFIG_BRIGHTNESS_MIN].toInt(0);
+ _isBrightnessSwitchOffMinimum = _devConfig[CONFIG_BRIGHTNESS_SWITCHOFF].toBool(true);
+ _brightnessMax = _devConfig[CONFIG_BRIGHTNESS_MAX].toInt(100);
+ _brightnessFactor = _devConfig[CONFIG_BRIGHTNESSFACTOR].toDouble(1.0);
+
+ if ( deviceConfig[ CONFIG_DEBUGLEVEL ].isString() )
+ {
+ _debuglevel = deviceConfig[ CONFIG_DEBUGLEVEL ].toString().toInt();
+ }
+ else
+ {
+ _debuglevel = deviceConfig[ CONFIG_DEBUGLEVEL ].toInt(0);
+ }
+
+ QString outputColorModel = _outputColorModel == MODEL_RGB ? "RGB": "HSV";
+ QString transitionEffect = _transitionEffect == YeelightLight::API_EFFECT_SMOOTH ? API_PARAM_EFFECT_SMOOTH : API_PARAM_EFFECT_SUDDEN;
+
+ Debug(_log, "colorModel : %s", QSTRING_CSTR(outputColorModel));
+ Debug(_log, "Transitioneffect : %s", QSTRING_CSTR(transitionEffect));
+ Debug(_log, "Transitionduration: %d", _transitionDuration);
+ Debug(_log, "Extra time darkn. : %d", _extraTimeDarkness );
+
+ Debug(_log, "Brightn. Min : %d", _brightnessMin );
+ Debug(_log, "Brightn. Min Off : %d", _isBrightnessSwitchOffMinimum );
+ Debug(_log, "Brightn. Max : %d", _brightnessMax );
+ Debug(_log, "Brightn. Factor : %.2f", _brightnessFactor );
+
+ _isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(false);
+ Debug(_log, "RestoreOrigState : %d", _isRestoreOrigState);
+
+ _waitTimeQuota = _devConfig[CONFIG_QUOTA_WAIT_TIME].toInt(0);
+ Debug(_log, "Wait time (quota) : %d", _waitTimeQuota );
+
+ Debug(_log, "Debuglevel : %d", _debuglevel);
+
+ QJsonArray configuredYeelightLights = _devConfig[CONFIG_LIGHTS].toArray();
+ uint configuredYeelightsCount = 0;
+ for (const QJsonValue light : configuredYeelightLights)
+ {
+ QString host = light.toObject().value("host").toString();
+ int port = light.toObject().value("port").toInt(API_DEFAULT_PORT);
+ if ( !host.isEmpty() )
+ {
+ QString name = light.toObject().value("name").toString();
+ Debug(_log, "Light [%u] - %s (%s:%d)", configuredYeelightsCount, QSTRING_CSTR(name), QSTRING_CSTR(host), port );
+ ++configuredYeelightsCount;
+ }
+ }
+ Debug(_log, "Light configured : %u", configuredYeelightsCount );
+
+ uint configuredLedCount = this->getLedCount();
+ if (configuredYeelightsCount < configuredLedCount )
+ {
+ QString errorReason = QString("Not enough Yeelights [%1] for configured LEDs [%2] found!")
+ .arg(configuredYeelightsCount)
+ .arg(configuredLedCount);
+ this->setInError(errorReason);
+ isInitOK = false;
+ }
+ else
+ {
+
+ if ( configuredYeelightsCount > configuredLedCount )
+ {
+ Warning(_log, "More Yeelights defined [%u] than configured LEDs [%u].", configuredYeelightsCount, configuredLedCount );
+ }
+
+ _lightsAddressList.clear();
+ for (int j = 0; j < static_cast( configuredLedCount ); ++j)
+ {
+ QString address = configuredYeelightLights[j].toObject().value("host").toString();
+ int port = configuredYeelightLights[j].toObject().value("port").toInt(API_DEFAULT_PORT);
+
+ QStringList addressparts = QStringUtils::split(address,":", QStringUtils::SplitBehavior::SkipEmptyParts);
+ QString apiHost = addressparts[0];
+ int apiPort = port;
+
+ _lightsAddressList.append( {apiHost, apiPort} );
+ }
+
+ if ( updateLights(_lightsAddressList) )
+ {
+ isInitOK = true;
+ }
+ }
+ }
+ return isInitOK;
+}
+
+bool LedDeviceYeelight::startMusicModeServer()
+{
+ DebugIf(verbose, _log, "enabled [%d], _isDeviceReady [%d]", _isEnabled, _isDeviceReady);
+
+ bool rc = false;
+ if ( _tcpMusicModeServer == nullptr )
+ {
+ _tcpMusicModeServer = new QTcpServer(this);
+ }
+
+ if ( ! _tcpMusicModeServer->isListening() )
+ {
+ if (! _tcpMusicModeServer->listen())
+ {
+ QString errorReason = QString ("(%1) %2").arg(_tcpMusicModeServer->serverError()).arg( _tcpMusicModeServer->errorString());
+ Error( _log, "Error: MusicModeServer: %s", QSTRING_CSTR(errorReason));
+ this->setInError ( errorReason );
+
+ Error( _log, "Failed to start music mode server");
+ }
+ else
+ {
+ QList ipAddressesList = QNetworkInterface::allAddresses();
+ // use the first non-localhost IPv4 address
+ for (int i = 0; i < ipAddressesList.size(); ++i) {
+ if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
+ (ipAddressesList.at(i).toIPv4Address() != 0U))
+ {
+ _musicModeServerAddress = ipAddressesList.at(i);
+ break;
+ }
+ }
+ if ( _musicModeServerAddress.isNull() )
+ {
+ Error( _log, "Failed to resolve IP for music mode server");
+ }
+ }
+ }
+
+ if ( _tcpMusicModeServer->isListening() )
+ {
+ _musicModeServerPort = _tcpMusicModeServer->serverPort();
+ Debug (_log, "The music mode server is running at %s:%d", QSTRING_CSTR(_musicModeServerAddress.toString()), _musicModeServerPort);
+ rc = true;
+ }
+ DebugIf(verbose, _log, "rc [%d], enabled [%d], _isDeviceReady [%d]", rc, _isEnabled, _isDeviceReady);
+ return rc;
+}
+
+bool LedDeviceYeelight::stopMusicModeServer()
+{
+ DebugIf(verbose, _log, "enabled [%d], _isDeviceReady [%d]", _isEnabled, _isDeviceReady);
+
+ bool rc = false;
+ if ( _tcpMusicModeServer != nullptr )
+ {
+ Debug(_log, "Stop MusicModeServer");
+ _tcpMusicModeServer->close();
+ rc = true;
+ }
+ DebugIf(verbose, _log, "rc [%d], enabled [%d], _isDeviceReady [%d]", rc, _isEnabled, _isDeviceReady);
+ return rc;
+}
+
+int LedDeviceYeelight::open()
+{
+ DebugIf(verbose, _log, "enabled [%d], _isDeviceReady [%d]", _isEnabled, _isDeviceReady);
+ int retval = -1;
+ _isDeviceReady = false;
+
+ // Open/Start LedDevice based on configuration
+ if ( !_lights.empty() )
+ {
+ if ( startMusicModeServer() )
+ {
+ int lightsInError = 0;
+ for (YeelightLight& light : _lights)
+ {
+ light.setTransitionEffect( _transitionEffect, _transitionDuration );
+ light.setBrightnessConfig( _brightnessMin, _brightnessMax, _isBrightnessSwitchOffMinimum, _extraTimeDarkness, _brightnessFactor );
+ light.setQuotaWaitTime(_waitTimeQuota);
+ light.setDebuglevel(_debuglevel);
+
+ if ( ! light.open() )
+ {
+ Error( _log, "Failed to open [%s]", QSTRING_CSTR(light.getName()) );
+ ++lightsInError;
+ }
+ }
+ if ( lightsInError < static_cast(_lights.size()) )
+ {
+ // Everything is OK -> enable device
+ _isDeviceReady = true;
+ retval = 0;
+ }
+ else
+ {
+ this->setInError( "All Yeelights failed to be opened!" );
+ }
+ }
+ }
+ else
+ {
+ // On error/exceptions, set LedDevice in error
+ }
+
+ DebugIf(verbose, _log, "retval [%d], enabled [%d], _isDeviceReady [%d]", retval, _isEnabled, _isDeviceReady);
+ return retval;
+}
+
+int LedDeviceYeelight::close()
+{
+ DebugIf(verbose, _log, "enabled [%d], _isDeviceReady [%d]", _isEnabled, _isDeviceReady);
+ int retval = 0;
+ _isDeviceReady = false;
+
+ // LedDevice specific closing activities
+
+ //Close all Yeelight lights
+ for (YeelightLight& light : _lights)
+ {
+ light.close();
+ }
+
+ //Close music mode server
+ stopMusicModeServer();
+
+ DebugIf(verbose, _log, "retval [%d], enabled [%d], _isDeviceReady [%d]", retval, _isEnabled, _isDeviceReady);
+ return retval;
+}
+
+bool LedDeviceYeelight::updateLights(const QVector &list)
+{
+ bool rc = false;
+ DebugIf(verbose, _log, "enabled [%d], _isDeviceReady [%d]", _isEnabled, _isDeviceReady);
+ if(!_lightsAddressList.empty())
+ {
+ // search user light-id inside map and create light if found
+ _lights.clear();
+
+ _lights.reserve( static_cast( _lightsAddressList.size() ));
+
+ for(auto & yeelightAddress : _lightsAddressList )
+ {
+ QString host = yeelightAddress.host;
+
+ if ( list.contains(yeelightAddress) )
+ {
+ int port = yeelightAddress.port;
+
+ Debug(_log,"Add Yeelight %s:%d", QSTRING_CSTR(host), port );
+ _lights.emplace_back( _log, host, port );
+ }
+ else
+ {
+ Warning(_log,"Configured light-address %s is not available", QSTRING_CSTR(host) );
+ }
+ }
+ setLightsCount ( static_cast( _lights.size() ));
+ rc = true;
+ }
+ return rc;
+}
+
+bool LedDeviceYeelight::powerOn()
+{
+ if ( _isDeviceReady)
+ {
+ //Power-on all Yeelights
+ for (YeelightLight& light : _lights)
+ {
+ if ( light.isReady() && !light.isInMusicMode() )
+ {
+ light.setPower(true, YeelightLight::API_EFFECT_SMOOTH, 5000);
+ }
+ }
+ }
+ return true;
+}
+
+bool LedDeviceYeelight::powerOff()
+{
+ if ( _isDeviceReady)
+ {
+ writeBlack();
+
+ //Power-off all Yeelights
+ for (YeelightLight& light : _lights)
+ {
+ light.setPower( false, _transitionEffect, API_PARAM_DURATION_POWERONOFF.count());
+ }
+ }
+ return true;
+}
+
+bool LedDeviceYeelight::storeState()
+{
+ bool rc = true;
+
+ for (YeelightLight& light : _lights)
+ {
+ light.storeState();
+ }
+ return rc;
+}
+
+bool LedDeviceYeelight::restoreState()
+{
+ bool rc = true;
+
+ for (YeelightLight& light : _lights)
+ {
+ light.restoreState();
+ if ( !light.wasOriginallyOn() )
+ {
+ light.setPower( false, _transitionEffect, API_PARAM_DURATION_POWERONOFF.count());
+ }
+ }
+ return rc;
+}
+
+QJsonObject LedDeviceYeelight::discover()
+{
+ QJsonObject devicesDiscovered;
+ devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
+
+ QJsonArray deviceList;
+
+ // Discover WLED Devices
+ SSDPDiscover discover;
+ discover.setPort(SSDP_PORT);
+ discover.skipDuplicateKeys(true);
+ discover.setSearchFilter(SSDP_FILTER, SSDP_FILTER_HEADER);
+ QString searchTarget = SSDP_ID;
+
+ if ( discover.discoverServices(searchTarget) > 0 )
+ {
+ deviceList = discover.getServicesDiscoveredJson();
+ }
+
+ devicesDiscovered.insert("devices", deviceList);
+ Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ return devicesDiscovered;
+}
+
+QJsonObject LedDeviceYeelight::getProperties(const QJsonObject& params)
+{
+ Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+ QJsonObject properties;
+
+ QString apiHostname = params["hostname"].toString("");
+ quint16 apiPort = static_cast( params["port"].toInt(API_DEFAULT_PORT) );
+ Debug (_log, "apiHost [%s], apiPort [%d]", QSTRING_CSTR(apiHostname), apiPort);
+
+ if ( !apiHostname.isEmpty() )
+ {
+ YeelightLight yeelight(_log, apiHostname, apiPort);
+
+ //yeelight.setDebuglevel(3);
+ if ( yeelight.open() )
+ {
+ properties.insert("properties", yeelight.getProperties());
+ yeelight.close();
+ }
+ }
+ Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ return properties;
+}
+
+void LedDeviceYeelight::identify(const QJsonObject& params)
+{
+ Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
+
+ QString apiHostname = params["hostname"].toString("");
+ quint16 apiPort = static_cast( params["port"].toInt(API_DEFAULT_PORT) );
+ Debug (_log, "apiHost [%s], apiPort [%d]", QSTRING_CSTR(apiHostname), apiPort);
+
+ if ( !apiHostname.isEmpty() )
+ {
+ YeelightLight yeelight(_log, apiHostname, apiPort);
+ //yeelight.setDebuglevel(3);
+
+ if ( yeelight.open() )
+ {
+ yeelight.identify();
+ yeelight.close();
+ }
+ }
+}
+
+int LedDeviceYeelight::write(const std::vector & ledValues)
+{
+ //DebugIf(verbose, _log, "enabled [%d], _isDeviceReady [%d]", _isEnabled, _isDeviceReady);
+ int rc = -1;
+
+ //Update on all Yeelights by iterating through lights and set colors.
+ unsigned int idx = 0;
+ int lightsInError = 0;
+ for (YeelightLight& light : _lights)
+ {
+ // Get color
+ ColorRgb color = ledValues.at(idx);
+
+ if ( light.isReady() )
+ {
+ bool skipWrite = false;
+ if ( !light.isInMusicMode() )
+ {
+ if ( light.setMusicMode(true, _musicModeServerAddress, _musicModeServerPort) )
+ {
+ // Wait for callback of the device to establish streaming socket
+ if ( _tcpMusicModeServer->waitForNewConnection(CONNECT_STREAM_TIMEOUT.count()) )
+ {
+ light.setStreamSocket( _tcpMusicModeServer->nextPendingConnection() );
+ }
+ else
+ {
+ QString errorReason = QString ("(%1) %2").arg(_tcpMusicModeServer->serverError()).arg( _tcpMusicModeServer->errorString());
+ Warning( _log, "write Error [%s]: _tcpMusicModeServer: %s", QSTRING_CSTR(light.getName()), QSTRING_CSTR(errorReason));
+ light.setInError("Failed to get stream socket");
+ }
+ }
+ else
+ {
+ DebugIf(verbose,_log, "setMusicMode failed due to command quota issue, skip write and try with next");
+ skipWrite = true;
+ }
+ }
+
+ if ( !skipWrite )
+ {
+ // Update light with given color
+ if ( _outputColorModel == MODEL_RGB )
+ {
+ light.setColorRGB( color );
+ }
+ else
+ {
+ light.setColorHSV( color );
+ }
+ }
+ }
+ else
+ {
+ ++lightsInError;
+ }
+ ++idx;
+ }
+
+ if ( ! (lightsInError < static_cast(_lights.size())) )
+ {
+ this->setInError( "All Yeelights in error - stopping device!" );
+ }
+ else
+ {
+ // Minimum one Yeelight device is working, continue updating devices
+ rc = 0;
+ }
+
+ //DebugIf(verbose, _log, "rc [%d]", rc );
+
+ return rc;
+}
diff --git a/libsrc/leddevice/dev_net/LedDeviceYeelight.h b/libsrc/leddevice/dev_net/LedDeviceYeelight.h
new file mode 100644
index 00000000..f70a9e13
--- /dev/null
+++ b/libsrc/leddevice/dev_net/LedDeviceYeelight.h
@@ -0,0 +1,627 @@
+#ifndef LEDEVICEYEELIGHT_H
+#define LEDEVICEYEELIGHT_H
+
+// LedDevice includes
+#include
+
+// Qt includes
+#include
+#include
+#include
+#include
+
+#include
+
+// Constants
+namespace {
+
+// List of State Information
+const char API_METHOD_POWER[] = "set_power";
+const char API_METHOD_POWER_ON[] = "on";
+const char API_METHOD_POWER_OFF[] = "off";
+
+const char API_METHOD_MUSIC_MODE[] = "set_music";
+const int API_METHOD_MUSIC_MODE_ON = 1;
+const int API_METHOD_MUSIC_MODE_OFF = 0;
+
+const char API_METHOD_SETRGB[] = "set_rgb";
+const char API_METHOD_SETSCENE[] = "set_scene";
+const char API_METHOD_GETPROP[] = "get_prop";
+
+const char API_PARAM_EFFECT_SUDDEN[] = "sudden";
+const char API_PARAM_EFFECT_SMOOTH[] = "smooth";
+
+constexpr std::chrono::milliseconds API_PARAM_DURATION{50};
+constexpr std::chrono::milliseconds API_PARAM_DURATION_POWERONOFF{1000};
+constexpr std::chrono::milliseconds API_PARAM_EXTRA_TIME_DARKNESS{200};
+
+} //End of constants
+///
+/// Response object for Yeelight-API calls and JSON-responses
+///
+class YeelightResponse
+{
+public:
+
+ enum API_REPLY{
+ API_OK,
+ API_ERROR,
+ API_NOTIFICATION,
+ };
+
+ explicit YeelightResponse() {}
+
+ API_REPLY error() { return _error;}
+ void setError(const YeelightResponse::API_REPLY replyType) { _error = replyType; }
+
+ QJsonArray getResult() const { return _resultArray; }
+ void setResult(const QJsonArray &result) { _resultArray = result; }
+
+ int getErrorCode() const { return _errorCode; }
+ void setErrorCode(const int &errorCode) { _errorCode = errorCode; _error = API_ERROR;}
+
+ QString getErrorReason() const { return _errorReason; }
+ void setErrorReason(const QString &errorReason) { _errorReason = errorReason; }
+
+private:
+
+ QJsonArray _resultArray;
+ API_REPLY _error = API_OK;
+
+ int _errorCode = 0;
+ QString _errorReason;
+};
+
+///
+/// Implementation of one Yeelight light.
+///
+class YeelightLight
+{
+
+public:
+
+ enum API_EFFECT{
+ API_EFFECT_SMOOTH,
+ API_EFFECT_SUDDEN
+ };
+
+ enum API_MODE{
+ API_TURN_ON_MODE,
+ API_CT_MODE,
+ API_RGB_MODE,
+ API_HSV_MODE,
+ API_COLOR_FLOW_MODE,
+ API_NIGHT_LIGHT_MODE
+ };
+
+ /// @brief Constructs one Yeelight light
+ ///
+ /// @param[in] log Logger instance
+ /// @param[in] hostname or IP-address
+ /// @param[in] port, default port 55443 is used when not provided
+ ///
+ YeelightLight( Logger *log, const QString &hostname, quint16 port);
+
+ ///
+ /// @brief Destructor of the Yeelight light
+ ///
+ virtual ~YeelightLight();
+
+ ///
+ /// @brief Set the Yeelight light connectivity parameters
+ ///
+ /// @param[in] hostname or IP-address
+ /// @param[in] port, default port 55443 is used when not provided
+ ///
+ void setHostname( const QString &hostname, quint16 port);
+
+ ///
+ /// @brief Set the Yeelight light name
+ ///
+ /// @param[in] name
+ ///
+ void setName( const QString& name ) { _name = name; }
+
+ ///
+ /// @brief Get the Yeelight light name
+ ///
+ /// @return The Yeelight light name
+ ///
+ QString getName() const { return _name; }
+
+ ///
+ /// @brief Opens the Yeelight light connectivity
+ ///
+ /// @return True, on success (i.e. device is open)
+ ///
+ bool open();
+
+ ///
+ /// @brief Closes the Yeelight light connectivity
+ ///
+ /// @return True, on success (i.e. device is closed)
+ ///
+ bool close();
+
+ ///
+ /// @brief Send a command to light up Yeelight light to allow identification
+ ///
+ /// @return True, if success
+ ///
+ bool identify();
+
+ ///
+ /// @brief Execute a Yeelight-API command
+ ///
+ /// @param[in] command The API command request in JSON
+ /// @return 0: success, -1: error, -2: command quota exceeded
+ ///
+ int writeCommand( const QJsonDocument &command );
+
+ ///
+ /// @brief Execute a Yeelight-API command
+ ///
+ /// @param[in] command The API command request in JSON
+ /// @param[out] result The response to the command in JSON
+ /// @return 0: success, -1: error, -2: command quota exceeded
+ ///
+ int writeCommand( const QJsonDocument &command, QJsonArray &result );
+
+ ///
+ /// @brief Stream a Yeelight-API command
+ ///
+ /// Yeelight must be in music mode, i.e. Streaming socket is established
+ ///
+ /// @param[in] command The API command request in JSON
+ /// @return True, on success
+ ///
+ bool streamCommand( const QJsonDocument &command );
+
+ ///
+ /// @brief Set the Yeelight light streaming socket
+ ///
+ /// @param[in] socket
+ ///
+ void setStreamSocket( QTcpSocket* socket );
+
+ ///
+ /// @brief Power on/off on the Yeelight light
+ ///
+ /// @param[in] on True: power on, False: power off
+ ///
+ /// @return True, if success
+ ///
+ bool setPower( bool on );
+
+ ///
+ /// @brief Power on/off on the Yeelight light
+ ///
+ /// @param[in] on True: power on, False: power off
+ /// @param[in] effect Transition effect, sudden or smooth
+ /// @param[in] duration Duration of the transition, if smooth
+ /// @param[in] mode Color mode after powering on
+ ///
+ /// @return True, if success
+ ///
+ bool setPower( bool on, API_EFFECT effect, int duration, API_MODE mode = API_RGB_MODE );
+
+ ///
+ /// @brief Set the Yeelight light to the given color (using RGB mode)
+ ///
+ /// @param[in] color as RGB value
+ ///
+ /// @return True, if success
+ ///
+ bool setColorRGB( const ColorRgb &color );
+
+ ///
+ /// @brief Set the Yeelight light to the given color (using HSV mode)
+ ///
+ /// @param[in] color as RGB value
+ ///
+ /// @return True, if success
+ ///
+ bool setColorHSV( const ColorRgb &color );
+
+ ///
+ /// @brief Set the Yeelight light effect and duration while transiting between color updates
+ ///
+ /// @param[in] effect Transition effect, sudden or smooth
+ /// @param[in] duration Duration of the transition, if smooth
+ ///
+ void setTransitionEffect ( API_EFFECT effect ,int duration = API_PARAM_DURATION.count() );
+
+ ///
+ /// @brief Set the Yeelight light brightness configuration behaviour
+ ///
+ /// @param[in] min Minimum Brightness (in %). Every value lower than minimum will be set to minimum.
+ /// @param[in] max Maximum Brightness (in %). Every value greater than maximum will be set to maximum.
+ /// @param[in] switchoff True, power-off light, if brightness is lower then minimum
+ /// @param[in] extraTime Additional time (in ms), which added to transition duration while powering-off
+ /// @param[in] factor Brightness factor to multiply on color change.
+ ///
+ void setBrightnessConfig (int min = 1, int max = 100, bool switchoff = false, int extraTime = 0, double factor = 1);
+
+ ///
+ /// @brief Set the Yeelight light into music-mode
+ ///
+ /// @param[in] on True: music-mode on, False: music-mode off
+ /// @param[in] hostAddress of the music-mode server
+ /// @param[in] port of the music-mode server
+ ///
+ bool setMusicMode( bool on, const QHostAddress &hostAddress = {} , int port = -1 );
+
+ ///
+ /// @brief Set the wait-time between two Yeelight light commands
+ ///
+ /// The write of a command is delayed by the given wait-time, if the last write happen in the wait-time time frame.
+ /// Used to avoid that the Yeelight light runs into the quota exceed error scenario.
+ /// A Yeelight light can do 60 commands/min ( -> wait-time = 1000ms).
+ ///
+ /// @param[in] waitTime in milliseconds
+ ///
+ void setQuotaWaitTime( int waitTime ) { _waitTimeQuota = waitTime; }
+
+ ///
+ /// @brief Get the Yeelight light properties
+ ///
+ /// @return properties as JSON-object
+ ///
+ QJsonObject getProperties();
+
+ ///
+ /// @brief Get the Yeelight light properties and store them along the Yeelight light for later access
+ ///
+ void storeState();
+
+ ///
+ /// @brief Restore the Yeelight light's original state.
+ ///
+ /// Restore the device's state as before hyperion color streaming started.
+ ///
+ /// @return True, if success
+ ///
+ virtual bool restoreState();
+
+ ///
+ /// @brief Check, if light was originally powered on before hyperion color streaming started..
+ ///
+ /// @return True, if light was on at start
+ ///
+ bool wasOriginallyOn() const { return _power == API_METHOD_POWER_ON ? true : false; }
+
+ ///
+ /// @brief Check, if the Yeelight light is ready for updates
+ ///
+ /// @return True, if ready
+ ///
+ bool isReady() const { return !_isInError; }
+
+ ///
+ /// @brief Check, if the Yeelight light is powered on
+ ///
+ /// @return True, if powered on
+ ///
+ bool isOn() const { return _isOn; }
+
+ ///
+ /// @brief Check, if the Yeelight light is in music-mode
+ ///
+ /// @return True, if in music mode
+ ///
+ bool isInMusicMode( bool deviceCheck = false );
+
+ ///
+ /// @brief Set the Yeelight light in error state
+ ///
+ /// @param[in] errorMsg The error message to be logged
+ ///
+ void setInError( const QString& errorMsg );
+
+ ///
+ /// @brief Set the Yeelight light debug-level
+ ///
+ /// @param[in] level Debug level (0: no debug output, 1-3: verbosity level)
+ ///
+ void setDebuglevel ( int level ) { _debugLevel = level; }
+
+private:
+
+ YeelightResponse handleResponse(int correlationID, QByteArray const &response );
+
+ ///
+ /// @brief Build Yeelight-API command
+ ///
+ /// @param[in] method Control method to be invoked
+ /// @param[in] params Parameters for control method
+ /// @return Yeelight-API command in JSON format
+ ///
+ QJsonDocument getCommand(const QString &method, const QJsonArray ¶ms);
+
+ ///
+ /// @brief Map Yeelight light properties into the Yeelight light members for direct access
+ ///
+ /// @param[in] properties Yeelight light's properties as JSON-Object
+ ///
+ void mapProperties(const QJsonObject &properties);
+
+ ///
+ /// @brief Write a Yeelight light specific log-line for debugging purposed
+ ///
+ /// @param[in] logLevel Debug level (0: no debug output, 1-3: verbosity level)
+ /// @param[in] msg Log message prefix (max 20 characters)
+ /// @param[in] type log message text
+ /// @param[in] ... variable input to log message text
+ /// ///
+ void log(const int logLevel,const char* msg, const char* type, ...);
+
+ Logger* _log;
+ int _debugLevel;
+
+ /// Error status of Yeelight light
+ bool _isInError;
+
+ /// IP address/port of the Yeelight light
+ QString _host;
+ quint16 _port;
+
+ /// Yeelight light communication socket
+ QTcpSocket* _tcpSocket;
+ /// Music mode server communication socket
+ QTcpSocket* _tcpStreamSocket;
+
+ /// ID of last command written or streamed
+ int _correlationID;
+ /// Timestamp of last write
+ qint64 _lastWriteTime;
+
+ /// Last color written to Yeelight light (RGB represented as QColor)
+ QColor _color;
+ /// Last color written to Yeelight light (RGB represented as int)
+ int _lastColorRgbValue;
+
+ /// Yeelight light behavioural parameters
+ API_EFFECT _transitionEffect;
+ int _transitionDuration;
+ int _extraTimeDarkness;
+
+ int _brightnessMin;
+ bool _isBrightnessSwitchOffMinimum;
+ int _brightnessMax;
+ double _brightnessFactor;
+
+ QString _transitionEffectParam;
+
+ /// Wait time to avoid quota exceed scenario
+ int _waitTimeQuota;
+
+ /// Yeelight light properties
+ QJsonObject _originalStateProperties;
+ QString _name;
+ QString _model;
+ QString _power;
+ QString _fw_ver;
+ int _colorRgbValue;
+ int _bright;
+ int _ct;
+
+ /// Yeelight light status
+ bool _isOn;
+ bool _isInMusicMode;
+};
+
+///
+/// Implementation of the LedDevice interface for sending to
+/// Yeelight devices via network
+///
+class LedDeviceYeelight : public LedDevice
+{
+public:
+
+ ///
+ /// @brief Constructs a Yeelight LED-device serving multiple lights
+ ///
+ /// @param deviceConfig Device's configuration as JSON-Object
+ ///
+ explicit LedDeviceYeelight(const QJsonObject &deviceConfig);
+
+ ///
+ /// @brief Destructor of the LedDevice
+ ///
+ virtual ~LedDeviceYeelight() override;
+
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
+ static LedDevice* construct(const QJsonObject &deviceConfig);
+
+ ///
+ /// @brief Discover Yeelight devices available (for configuration).
+ ///
+ /// @return A JSON structure holding a list of devices found
+ ///
+ virtual QJsonObject discover() override;
+
+ ///
+ /// @brief Get a Yeelight device's resource properties
+ ///
+ /// Following parameters are required
+ /// @code
+ /// {
+ /// "hostname" : "hostname or IP",
+ /// "port" : port, default port 55443 is used when not provided
+ /// }
+ ///@endcode
+ ///
+ /// @param[in] params Parameters to query device
+ /// @return A JSON structure holding the device's properties
+ ///
+ virtual QJsonObject getProperties(const QJsonObject& params) override;
+
+ ///
+ /// @brief Send an update to the Yeelight device to identify it.
+ ///
+ /// Following parameters are required
+ /// @code
+ /// {
+ /// "hostname" : "hostname or IP",
+ /// "port" : port, default port 55443 is used when not provided
+ /// }
+ ///@endcode
+ ///
+ /// @param[in] params Parameters to address device
+ ///
+ virtual void identify(const QJsonObject& params) override;
+
+protected:
+
+ ///
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Opens the output device.
+ ///
+ /// @return Zero on success (i.e. device is ready), else negative
+ ///
+ virtual int open() override;
+
+ ///
+ /// @brief Closes the output device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ virtual int write(const std::vector & ledValues) override;
+
+ ///
+ /// @brief Power-/turn on the Nanoleaf device.
+ ///
+ /// @brief Store the device's original state.
+ ///
+ virtual bool powerOn() override;
+
+ ///
+ /// @brief Power-/turn off the Nanoleaf device.
+ ///
+ /// @return True if success
+ ///
+ virtual bool powerOff() override;
+
+ ///
+ /// @brief Store the device's original state.
+ ///
+ /// Save the device's state before hyperion color streaming starts allowing to restore state during switchOff().
+ ///
+ /// @return True if success
+ ///
+ virtual bool storeState() override;
+
+ ///
+ /// @brief Restore the device's original state.
+ ///
+ /// Restore the device's state as before hyperion color streaming started.
+ /// This includes the on/off state of the device.
+ ///
+ /// @return True, if success
+ ///
+ virtual bool restoreState() override;
+
+private:
+
+ struct yeelightAddress {
+ QString host;
+ int port;
+
+ bool operator == (yeelightAddress const& a) const
+ {
+ return ((host == a.host) && (port == a.port));
+ }
+ };
+
+ enum COLOR_MODEL{
+ MODEL_HSV,
+ MODEL_RGB
+ };
+
+ ///
+ /// @brief Start music-mode server
+ ///
+ /// @return True, if music mode server is running
+ ///
+ bool startMusicModeServer();
+
+ ///
+ /// @brief Stop music-mode server
+ ///
+ /// @return True, if music mode server has been stopped
+ ///
+ bool stopMusicModeServer();
+
+ ///
+ /// @brief Update list of Yeelight lights handled by the LED-device
+ ///
+ /// @param[in] list List of Yeelight lights
+ ///
+ /// @return False, if no lights were provided
+ ///
+ bool updateLights(const QVector &list);
+
+ ///
+ /// @brief Set the number of Yeelight lights handled by the LED-device
+ ///
+ /// @param[in] lightsCount Number of Yeelight lights
+ ///
+ void setLightsCount( unsigned int lightsCount ) { _lightsCount = lightsCount; }
+
+ ///
+ /// @brief Get the number of Yeelight lights handled by the LED-device
+ ///
+ /// @return Number of Yeelight lights
+ ///
+ uint getLightsCount() const { return _lightsCount; }
+
+ /// Array of the Yeelight addresses handled by the LED-device
+ QVector _lightsAddressList;
+
+ /// Array to save the lights
+ std::vector _lights;
+ unsigned int _lightsCount;
+
+ /// Yeelight configuration/behavioural parameters
+ int _outputColorModel;
+ YeelightLight::API_EFFECT _transitionEffect;
+ int _transitionDuration;
+ int _extraTimeDarkness;
+
+ int _brightnessMin;
+ bool _isBrightnessSwitchOffMinimum;
+ int _brightnessMax;
+ double _brightnessFactor;
+
+ int _waitTimeQuota;
+
+ int _debuglevel;
+
+ ///Music mode Server details
+ QHostAddress _musicModeServerAddress;
+ int _musicModeServerPort;
+ QTcpServer* _tcpMusicModeServer = nullptr;
+
+};
+
+#endif // LEDEVICEYEELIGHT_H
diff --git a/libsrc/leddevice/dev_net/ProviderRestApi.cpp b/libsrc/leddevice/dev_net/ProviderRestApi.cpp
new file mode 100644
index 00000000..783667aa
--- /dev/null
+++ b/libsrc/leddevice/dev_net/ProviderRestApi.cpp
@@ -0,0 +1,247 @@
+// Local-Hyperion includes
+#include "ProviderRestApi.h"
+
+// Qt includes
+#include
+#include
+#include
+
+//std includes
+#include
+
+// Constants
+namespace {
+
+const QChar ONE_SLASH = '/';
+
+} //End of constants
+
+ProviderRestApi::ProviderRestApi(const QString &host, const int &port, const QString &basePath)
+ :_log(Logger::getInstance("LEDDEVICE"))
+ ,_networkManager(nullptr)
+ ,_scheme("http")
+ ,_hostname(host)
+ ,_port(port)
+{
+ _networkManager = new QNetworkAccessManager();
+
+ _apiUrl.setScheme(_scheme);
+ _apiUrl.setHost(host);
+ _apiUrl.setPort(port);
+ _basePath = basePath;
+}
+
+ProviderRestApi::ProviderRestApi(const QString &host, const int &port)
+ : ProviderRestApi(host, port, "") {}
+
+ProviderRestApi::ProviderRestApi()
+ : ProviderRestApi("", -1) {}
+
+ProviderRestApi::~ProviderRestApi()
+{
+ if ( _networkManager != nullptr )
+ {
+ delete _networkManager;
+ }
+}
+
+void ProviderRestApi::setBasePath(const QString &basePath)
+{
+ _basePath.clear();
+ appendPath (_basePath, basePath );
+}
+
+void ProviderRestApi::setPath ( const QString &path )
+{
+ _path.clear();
+ appendPath (_path, path );
+}
+
+void ProviderRestApi::appendPath ( const QString &path )
+{
+ appendPath (_path, path );
+}
+
+void ProviderRestApi::appendPath ( QString& path, const QString &appendPath) const
+{
+ if ( !appendPath.isEmpty() && appendPath != ONE_SLASH )
+ {
+ if (path.isEmpty() || path == ONE_SLASH )
+ {
+ path.clear();
+ if (appendPath[0] != ONE_SLASH )
+ {
+ path.push_back(ONE_SLASH);
+ }
+ }
+ else if (path[path.size()-1] == ONE_SLASH && appendPath[0] == ONE_SLASH)
+ {
+ path.chop(1);
+ }
+ else if (path[path.size()-1] != ONE_SLASH && appendPath[0] != ONE_SLASH)
+ {
+ path.push_back(ONE_SLASH);
+ }
+ else
+ {
+ // Only one slash.
+ }
+
+ path.append(appendPath);
+ }
+}
+
+void ProviderRestApi::setFragment(const QString &fragment)
+{
+ _fragment = fragment;
+}
+
+void ProviderRestApi::setQuery(const QUrlQuery &query)
+{
+ _query = query;
+}
+
+QUrl ProviderRestApi::getUrl() const
+{
+ QUrl url = _apiUrl;
+
+ QString fullPath = _basePath;
+ appendPath (fullPath, _path );
+
+ url.setPath(fullPath);
+ url.setFragment( _fragment );
+ url.setQuery( _query );
+ return url;
+}
+
+httpResponse ProviderRestApi::get()
+{
+ return get( getUrl() );
+}
+
+httpResponse ProviderRestApi::get(const QUrl &url)
+{
+ Debug(_log, "GET: [%s]", QSTRING_CSTR( url.toString() ));
+
+ // Perform request
+ QNetworkRequest request(url);
+ QNetworkReply* reply = _networkManager->get(request);
+ // Connect requestFinished signal to quit slot of the loop.
+ QEventLoop loop;
+ loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
+ // Go into the loop until the request is finished.
+ loop.exec();
+
+ httpResponse response;
+ if(reply->operation() == QNetworkAccessManager::GetOperation)
+ {
+ response = getResponse(reply );
+ }
+ // Free space.
+ reply->deleteLater();
+ // Return response
+ return response;
+}
+
+httpResponse ProviderRestApi::put(const QString &body)
+{
+ return put( getUrl(), body );
+}
+
+httpResponse ProviderRestApi::put(const QUrl &url, const QString &body)
+{
+ Debug(_log, "PUT: [%s] [%s]", QSTRING_CSTR( url.toString() ), QSTRING_CSTR( body ) );
+ // Perform request
+ QNetworkRequest request(url);
+ QNetworkReply* reply = _networkManager->put(request, body.toUtf8());
+ // Connect requestFinished signal to quit slot of the loop.
+ QEventLoop loop;
+ loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
+ // Go into the loop until the request is finished.
+ loop.exec();
+
+ httpResponse response;
+ if(reply->operation() == QNetworkAccessManager::PutOperation)
+ {
+ response = getResponse(reply);
+ }
+ // Free space.
+ reply->deleteLater();
+
+ // Return response
+ return response;
+}
+
+httpResponse ProviderRestApi::getResponse(QNetworkReply* const &reply)
+{
+ httpResponse response;
+
+ int httpStatusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
+ response.setHttpStatusCode(httpStatusCode);
+
+ Debug(_log, "Reply.httpStatusCode [%d]", httpStatusCode );
+
+ response.setNetworkReplyError(reply->error());
+
+ if(reply->error() == QNetworkReply::NoError)
+ {
+ if ( httpStatusCode != 204 ){
+ QByteArray replyData = reply->readAll();
+
+ if ( !replyData.isEmpty())
+ {
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(replyData, &error);
+
+ if (error.error != QJsonParseError::NoError)
+ {
+ //Received not valid JSON response
+ //std::cout << "Response: [" << replyData.toStdString() << "]" << std::endl;
+ response.setError(true);
+ response.setErrorReason(error.errorString());
+ }
+ else
+ {
+ //std::cout << "Response: [" << QString (jsonDoc.toJson(QJsonDocument::Compact)).toStdString() << "]" << std::endl;
+ response.setBody( jsonDoc );
+ }
+ }
+ else
+ { // Create valid body which is empty
+ response.setBody( QJsonDocument() );
+ }
+ }
+ }
+ else
+ {
+ QString errorReason;
+ if ( httpStatusCode > 0 ) {
+ QString httpReason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
+ QString advise;
+ switch ( httpStatusCode ) {
+ case 400:
+ advise = "Check Request Body";
+ break;
+ case 401:
+ advise = "Check Authentication Token (API Key)";
+ break;
+ case 404:
+ advise = "Check Resource given";
+ break;
+ default:
+ break;
+ }
+ errorReason = QString ("[%3 %4] - %5").arg(QString(httpStatusCode) , httpReason, advise);
+ }
+ else {
+ errorReason = reply->errorString();
+ }
+ response.setError(true);
+ response.setErrorReason(errorReason);
+
+ // Create valid body which is empty
+ response.setBody( QJsonDocument() );
+ }
+ return response;
+}
+
diff --git a/libsrc/leddevice/dev_net/ProviderRestApi.h b/libsrc/leddevice/dev_net/ProviderRestApi.h
new file mode 100644
index 00000000..06c34219
--- /dev/null
+++ b/libsrc/leddevice/dev_net/ProviderRestApi.h
@@ -0,0 +1,216 @@
+#ifndef PROVIDERRESTKAPI_H
+#define PROVIDERRESTKAPI_H
+
+// Local-Hyperion includes
+#include
+
+// Qt includes
+#include
+#include
+#include
+#include
+
+///
+/// Response object for REST-API calls and JSON-responses
+///
+class httpResponse
+{
+public:
+
+ explicit httpResponse() {}
+
+ bool error() { return _hasError;}
+ void setError(const bool hasError) { _hasError = hasError; }
+
+ QJsonDocument getBody() const { return _responseBody; }
+ void setBody(const QJsonDocument &body) { _responseBody = body; }
+
+ QString getErrorReason() const { return _errorReason; }
+ void setErrorReason(const QString &errorReason) { _errorReason = errorReason; }
+
+ int getHttpStatusCode() const { return _httpStatusCode; }
+ void setHttpStatusCode(const int httpStatusCode) { _httpStatusCode = httpStatusCode; }
+
+ QNetworkReply::NetworkError getNetworkReplyError() const { return _networkReplyError; }
+ void setNetworkReplyError (const QNetworkReply::NetworkError networkReplyError) { _networkReplyError = networkReplyError; }
+
+private:
+
+ QJsonDocument _responseBody;
+ bool _hasError = false;
+ QString _errorReason;
+
+ int _httpStatusCode = 0;
+ QNetworkReply::NetworkError _networkReplyError = QNetworkReply::NoError;
+};
+
+///
+/// Wrapper class supporting REST-API calls with JSON requests and responses
+///
+/// Usage sample:
+/// @code
+///
+/// ProviderRestApi* _restApi = new ProviderRestApi(hostname, port );
+///
+/// _restApi->setBasePath( QString("/api/%1/").arg(token) );
+/// _restApi->setPath( QString("%1/%2").arg( "groups" ).arg( groupId ) );
+///
+/// httpResponse response = _restApi->get();
+/// if ( !response.error() )
+/// response.getBody();
+///
+/// delete _restApi;
+///
+///@endcode
+///
+class ProviderRestApi
+{
+public:
+
+ ///
+ /// @brief Constructor of the REST-API wrapper
+ ///
+ explicit ProviderRestApi();
+
+ ///
+ /// @brief Constructor of the REST-API wrapper
+ ///
+ /// @param[in] host
+ /// @param[in] port
+ ///
+ explicit ProviderRestApi(const QString &host, const int &port);
+
+ ///
+ /// @brief Constructor of the REST-API wrapper
+ ///
+ /// @param[in] host
+ /// @param[in] port
+ /// @param[in] API base-path
+ ///
+ explicit ProviderRestApi(const QString &host, const int &port, const QString &basePath);
+
+ ///
+ /// @brief Destructor of the REST-API wrapper
+ ///
+ virtual ~ProviderRestApi();
+
+ ///
+ /// @brief Get the URL as defined using scheme, host, port, API-basepath, path, query, fragment
+ ///
+ /// @return url
+ ///
+ QUrl getUrl() const;
+
+ ///
+ /// @brief Set an API's base path (the stable path element before addressing resources)
+ ///
+ /// @param[in] basePath, e.g. "/api/v1/" or "/json"
+ ///
+ void setBasePath(const QString &basePath);
+
+ ///
+ /// @brief Set an API's path to address resources
+ ///
+ /// @param[in] path, e.g. "/lights/1/state/"
+ ///
+ void setPath ( const QString &path );
+
+ ///
+ /// @brief Append an API's path element to path set before
+ ///
+ /// @param[in] path
+ ///
+ void appendPath (const QString &appendPath);
+
+ ///
+ /// @brief Set an API's fragment
+ ///
+ /// @param[in] fragment, e.g. "question3"
+ ///
+ void setFragment(const QString&fragment);
+
+ ///
+ /// @brief Set an API's query string
+ ///
+ /// @param[in] query, e.g. "&A=128&FX=0"
+ ///
+ void setQuery(const QUrlQuery &query);
+
+ ///
+ /// @brief Execute GET request
+ ///
+ /// @return Response The body of the response in JSON
+ ///
+ httpResponse get();
+
+ ///
+ /// @brief Execute GET request
+ ///
+ /// @param[in] url GET request for URL
+ /// @return Response The body of the response in JSON
+ ///
+ httpResponse get(const QUrl &url);
+
+ ///
+ /// @brief Execute PUT request
+ ///
+ /// @param[in] body The body of the request in JSON
+ /// @return Response The body of the response in JSON
+ ///
+ httpResponse put(const QString &body = "");
+
+ ///
+ /// @brief Execute PUT request
+ ///
+ /// @param[in] URL for PUT request
+ /// @param[in] body The body of the request in JSON
+ /// @return Response The body of the response in JSON
+ ///
+ httpResponse put(const QUrl &url, const QString &body = "");
+
+ ///
+ /// @brief Execute POST request
+ ///
+ /// @param[in] body The body of the request in JSON
+ /// @return Response The body of the response in JSON
+ ///
+ httpResponse post(QString body = "");
+
+ ///
+ /// @brief Handle responses for REST requests
+ ///
+ /// @param[in] reply Network reply
+ /// @return Response The body of the response in JSON
+ ///
+ httpResponse getResponse(QNetworkReply* const &reply);
+
+private:
+
+ ///
+ /// @brief Append an API's path element to path given as param
+ ///
+ /// @param[in/out] path to be updated
+ /// @param[in] path, element to be appended
+ ///
+ void appendPath (QString &path, const QString &appendPath) const;
+
+ Logger* _log;
+
+ // QNetworkAccessManager object for sending REST-requests.
+ QNetworkAccessManager* _networkManager;
+
+ QUrl _apiUrl;
+
+ QString _scheme;
+ QString _hostname;
+ int _port;
+
+ QString _basePath;
+ QString _path;
+
+ QString _fragment;
+ QUrlQuery _query;
+
+};
+
+#endif // PROVIDERRESTKAPI_H
diff --git a/libsrc/leddevice/dev_net/ProviderUdp.cpp b/libsrc/leddevice/dev_net/ProviderUdp.cpp
index 3df9b8e4..9c9c124c 100644
--- a/libsrc/leddevice/dev_net/ProviderUdp.cpp
+++ b/libsrc/leddevice/dev_net/ProviderUdp.cpp
@@ -22,7 +22,7 @@ ProviderUdp::ProviderUdp()
, _port(1)
, _defaultHost("127.0.0.1")
{
- _deviceReady = false;
+ _isDeviceReady = false;
_latchTime_ms = 1;
}
@@ -30,59 +30,65 @@ ProviderUdp::~ProviderUdp()
{
if ( _udpSocket != nullptr )
{
- _udpSocket->deleteLater();
+ delete _udpSocket;
}
}
bool ProviderUdp::init(const QJsonObject &deviceConfig)
{
- bool isInitOK = LedDevice::init(deviceConfig);
+ bool isInitOK = false;
- QString host = deviceConfig["host"].toString(_defaultHost);
+ // Initialise sub-class
+ if ( LedDevice::init(deviceConfig) )
+ {
+ QString host = deviceConfig["host"].toString(_defaultHost);
- if (_address.setAddress(host) )
- {
- Debug( _log, "Successfully parsed %s as an ip address.", deviceConfig["host"].toString().toStdString().c_str());
- }
- else
- {
- Debug( _log, "Failed to parse [%s] as an ip address.", deviceConfig["host"].toString().toStdString().c_str());
- QHostInfo info = QHostInfo::fromName(host);
- if (info.addresses().isEmpty())
+ if (_address.setAddress(host) )
{
- Debug( _log, "Failed to parse [%s] as a hostname.", deviceConfig["host"].toString().toStdString().c_str());
- QString errortext = QString ("Invalid target address [%1]!").arg(host);
- this->setInError ( errortext );
- return false;
+ Debug( _log, "Successfully parsed %s as an ip address.", deviceConfig["host"].toString().toStdString().c_str());
}
else
{
- Debug( _log, "Successfully parsed %s as a hostname.", deviceConfig["host"].toString().toStdString().c_str());
- _address = info.addresses().first();
+ Debug( _log, "Failed to parse [%s] as an ip address.", deviceConfig["host"].toString().toStdString().c_str());
+ QHostInfo info = QHostInfo::fromName(host);
+ if (info.addresses().isEmpty())
+ {
+ Debug( _log, "Failed to parse [%s] as a hostname.", deviceConfig["host"].toString().toStdString().c_str());
+ QString errortext = QString ("Invalid target address [%1]!").arg(host);
+ this->setInError ( errortext );
+ return false;
+ }
+ else
+ {
+ Debug( _log, "Successfully parsed %s as a hostname.", deviceConfig["host"].toString().toStdString().c_str());
+ _address = info.addresses().first();
+ }
+ }
+
+ int config_port = deviceConfig["port"].toInt(_port);
+ if ( config_port <= 0 || config_port > MAX_PORT )
+ {
+ QString errortext = QString ("Invalid target port [%1]!").arg(config_port);
+ this->setInError ( errortext );
+ isInitOK = false;
+ }
+ else
+ {
+ _port = static_cast(config_port);
+ Debug( _log, "UDP using %s:%d", _address.toString().toStdString().c_str() , _port );
+
+ _udpSocket = new QUdpSocket(this);
+
+ isInitOK = true;
}
}
-
- int config_port = deviceConfig["port"].toInt(_port);
- if ( config_port <= 0 || config_port > MAX_PORT )
- {
- QString errortext = QString ("Invalid target port [%1]!").arg(config_port);
- this->setInError ( errortext );
- isInitOK = false;
- }
- else
- {
- _port = static_cast(config_port);
- Debug( _log, "UDP using %s:%d", _address.toString().toStdString().c_str() , _port );
- }
-
return isInitOK;
}
-bool ProviderUdp::initNetwork()
+int ProviderUdp::open()
{
- bool isInitOK = false;
-
- _udpSocket = new QUdpSocket(this);
+ int retval = -1;
+ _isDeviceReady = false;
// Try to bind the UDP-Socket
if ( _udpSocket != nullptr )
@@ -94,37 +100,21 @@ bool ProviderUdp::initNetwork()
QString warntext = QString ("Could not bind local address: %1, (%2) %3").arg(localAddress.toString()).arg(_udpSocket->error()).arg(_udpSocket->errorString());
Warning ( _log, "%s", QSTRING_CSTR(warntext));
}
- isInitOK = true;
+ // Everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
}
- return isInitOK;
-}
-
-int ProviderUdp::open()
-{
- int retval = -1;
- QString errortext;
- _deviceReady = false;
-
- if ( init(_devConfig) )
+ else
{
- if ( ! initNetwork())
- {
- this->setInError( "UDP Network error!" );
- }
- else
- {
- // Everything is OK -> enable device
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
+ this->setInError( " Open error. UDP Socket not initialised!" );
}
return retval;
}
-void ProviderUdp::close()
+int ProviderUdp::close()
{
- LedDevice::close();
+ int retval = 0;
+ _isDeviceReady = false;
if ( _udpSocket != nullptr )
{
@@ -136,6 +126,7 @@ void ProviderUdp::close()
// Everything is OK -> device is closed
}
}
+ return retval;
}
int ProviderUdp::writeBytes(const unsigned size, const uint8_t * data)
diff --git a/libsrc/leddevice/dev_net/ProviderUdp.h b/libsrc/leddevice/dev_net/ProviderUdp.h
index d4582c71..910b682c 100644
--- a/libsrc/leddevice/dev_net/ProviderUdp.h
+++ b/libsrc/leddevice/dev_net/ProviderUdp.h
@@ -1,13 +1,15 @@
-#pragma once
+#ifndef PROVIDERUDP_H
+#define PROVIDERUDP_H
+
+// LedDevice includes
+#include
// Hyperion includes
-#include
#include
-// qt
+// Qt includes
#include
-
-class QUdpSocket;
+#include
///
/// The ProviderUdp implements an abstract base-class for LedDevices using UDP packets.
@@ -15,53 +17,49 @@ class QUdpSocket;
class ProviderUdp : public LedDevice
{
public:
+
///
- /// Constructs specific LedDevice
+ /// @brief Constructs an UDP LED-device
///
ProviderUdp();
///
- /// Destructor of the LedDevice; closes the output device if it is open
+ /// @brief Destructor of the UDP LED-device
///
virtual ~ProviderUdp() override;
- ///
- /// Sets configuration
- ///
- /// @param deviceConfig the json device config
- /// @return true if success
- virtual bool init(const QJsonObject &deviceConfig) override;
-
-public slots:
- ///
- /// Closes the output device.
- /// Includes switching-off the device and stopping refreshes
- ///
- virtual void close() override;
-
protected:
///
- /// Initialise device's network details
+ /// @brief Initialise the UDP device's configuration and network address details
///
- /// @return True if success
- bool initNetwork();
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ virtual bool init(const QJsonObject &deviceConfig) override;
///
- /// Opens and configures the output device
+ /// @brief Opens the output device.
///
- /// @return Zero on succes else negative
+ /// @return Zero on success (i.e. device is ready), else negative
///
- int open() override;
+ virtual int open() override;
///
- /// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
+ /// @brief Closes the UDP device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
+ ///
+ /// @brief Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
/// values are latched.
///
/// @param[in] size The length of the data
/// @param[in] data The data
///
- /// @return Zero on succes else negative
+ /// @return Zero on success, else negative
///
int writeBytes(const unsigned size, const uint8_t *data);
@@ -71,3 +69,5 @@ protected:
ushort _port;
QString _defaultHost;
};
+
+#endif // PROVIDERUDP_H
diff --git a/libsrc/leddevice/dev_net/ProviderUdpSSL.cpp b/libsrc/leddevice/dev_net/ProviderUdpSSL.cpp
index 0eab17a8..6facf2f7 100644
--- a/libsrc/leddevice/dev_net/ProviderUdpSSL.cpp
+++ b/libsrc/leddevice/dev_net/ProviderUdpSSL.cpp
@@ -12,6 +12,9 @@
// Local Hyperion includes
#include "ProviderUdpSSL.h"
+const int MAX_RETRY = 5;
+const ushort MAX_PORT_SSL = 65535;
+
ProviderUdpSSL::ProviderUdpSSL()
: LedDevice()
, client_fd()
@@ -29,16 +32,16 @@ ProviderUdpSSL::ProviderUdpSSL()
, _server_name()
, _psk()
, _psk_identity()
- , _read_timeout(0)
- , _handshake_timeout_min(400)
- , _handshake_timeout_max(1000)
+ , _read_timeout(STREAM_SSL_READ_TIMEOUT.count())
+ , _handshake_timeout_min(STREAM_SSL_HANDSHAKE_TIMEOUT_MIN.count())
+ , _handshake_timeout_max(STREAM_SSL_HANDSHAKE_TIMEOUT_MAX.count())
, _handshake_attempts(5)
, _retry_left(MAX_RETRY)
, _stopConnection(true)
, _debugStreamer(false)
, _debugLevel(0)
{
- _deviceReady = false;
+ _isDeviceReady = false;
_latchTime_ms = 1;
}
@@ -48,114 +51,131 @@ ProviderUdpSSL::~ProviderUdpSSL()
bool ProviderUdpSSL::init(const QJsonObject &deviceConfig)
{
- bool isInitOK = LedDevice::init(deviceConfig);
+ bool isInitOK = false;
- _debugStreamer = deviceConfig["debugStreamer"].toBool(false);
- _debugLevel = deviceConfig["debugLevel"].toString().toInt(0);
-
- //PSK Pre Shared Key
- _psk = deviceConfig["psk"].toString();
- _psk_identity = deviceConfig["psk_identity"].toString();
- _port = deviceConfig["sslport"].toInt(2100);
- _server_name = deviceConfig["servername"].toString();
-
- if( deviceConfig.contains("transport_type") ) _transport_type = deviceConfig["transport_type"].toString("DTLS");
- if( deviceConfig.contains("seed_custom") ) _custom = deviceConfig["seed_custom"].toString("dtls_client");
- if( deviceConfig.contains("retry_left") ) _retry_left = deviceConfig["retry_left"].toInt(MAX_RETRY);
- if( deviceConfig.contains("read_timeout") ) _read_timeout = deviceConfig["read_timeout"].toInt(0);
- if( deviceConfig.contains("hs_timeout_min") ) _handshake_timeout_min = deviceConfig["hs_timeout_min"].toInt(400);
- if( deviceConfig.contains("hs_timeout_max") ) _handshake_timeout_max = deviceConfig["hs_timeout_max"].toInt(1000);
- if( deviceConfig.contains("hs_attempts") ) _handshake_attempts = deviceConfig["hs_attempts"].toInt(5);
-
- QString host = deviceConfig["host"].toString(_defaultHost);
- QStringList debugLevels = QStringList() << "No Debug" << "Error" << "State Change" << "Informational" << "Verbose";
-
- configLog( "SSL Streamer Debug", "%s", ( _debugStreamer ) ? "yes" : "no" );
- configLog( "SSL DebugLevel", "[%d] %s", _debugLevel, QSTRING_CSTR( debugLevels[ _debugLevel ]) );
-
- configLog( "SSL Servername", "%s", QSTRING_CSTR( _server_name ) );
- configLog( "SSL Host", "%s", QSTRING_CSTR( host ) );
- configLog( "SSL Port", "%d", _port );
- configLog( "PSK", "%s", QSTRING_CSTR( _psk ) );
- configLog( "PSK-Identity", "%s", QSTRING_CSTR( _psk_identity ) );
- configLog( "SSL Transport Type", "%s", QSTRING_CSTR( _transport_type ) );
- configLog( "SSL Seed Custom", "%s", QSTRING_CSTR( _custom ) );
- configLog( "SSL Retry Left", "%d", _retry_left );
- configLog( "SSL Read Timeout", "%d", _read_timeout );
- configLog( "SSL Handshake Timeout min", "%d", _handshake_timeout_min );
- configLog( "SSL Handshake Timeout max", "%d", _handshake_timeout_max );
- configLog( "SSL Handshake attempts", "%d", _handshake_attempts );
-
- if ( _address.setAddress(host) )
+ // Initialise sub-class
+ if ( LedDevice::init(deviceConfig) )
{
- Debug( _log, "Successfully parsed %s as an ip address.", QSTRING_CSTR( host ) );
- }
- else
- {
- Debug( _log, "Failed to parse [%s] as an ip address.", QSTRING_CSTR( host ) );
- QHostInfo info = QHostInfo::fromName(host);
- if ( info.addresses().isEmpty() )
+ _debugStreamer = deviceConfig["debugStreamer"].toBool(false);
+ _debugLevel = deviceConfig["debugLevel"].toString().toInt(0);
+
+ //PSK Pre Shared Key
+ _psk = deviceConfig["psk"].toString();
+ _psk_identity = deviceConfig["psk_identity"].toString();
+ _port = deviceConfig["sslport"].toInt(2100);
+ _server_name = deviceConfig["servername"].toString();
+
+ if( deviceConfig.contains("transport_type") ) _transport_type = deviceConfig["transport_type"].toString("DTLS");
+ if( deviceConfig.contains("seed_custom") ) _custom = deviceConfig["seed_custom"].toString("dtls_client");
+ if( deviceConfig.contains("retry_left") ) _retry_left = deviceConfig["retry_left"].toInt(MAX_RETRY);
+ if( deviceConfig.contains("read_timeout") ) _read_timeout = deviceConfig["read_timeout"].toInt(0);
+ if( deviceConfig.contains("hs_timeout_min") ) _handshake_timeout_min = deviceConfig["hs_timeout_min"].toInt(400);
+ if( deviceConfig.contains("hs_timeout_max") ) _handshake_timeout_max = deviceConfig["hs_timeout_max"].toInt(1000);
+ if( deviceConfig.contains("hs_attempts") ) _handshake_attempts = deviceConfig["hs_attempts"].toInt(5);
+
+ QString host = deviceConfig["host"].toString(_defaultHost);
+ QStringList debugLevels = QStringList() << "No Debug" << "Error" << "State Change" << "Informational" << "Verbose";
+
+ configLog( "SSL Streamer Debug", "%s", ( _debugStreamer ) ? "yes" : "no" );
+ configLog( "SSL DebugLevel", "[%d] %s", _debugLevel, QSTRING_CSTR( debugLevels[ _debugLevel ]) );
+
+ configLog( "SSL Servername", "%s", QSTRING_CSTR( _server_name ) );
+ configLog( "SSL Host", "%s", QSTRING_CSTR( host ) );
+ configLog( "SSL Port", "%d", _port );
+ configLog( "PSK", "%s", QSTRING_CSTR( _psk ) );
+ configLog( "PSK-Identity", "%s", QSTRING_CSTR( _psk_identity ) );
+ configLog( "SSL Transport Type", "%s", QSTRING_CSTR( _transport_type ) );
+ configLog( "SSL Seed Custom", "%s", QSTRING_CSTR( _custom ) );
+ configLog( "SSL Retry Left", "%d", _retry_left );
+ configLog( "SSL Read Timeout", "%d", _read_timeout );
+ configLog( "SSL Handshake Timeout min", "%d", _handshake_timeout_min );
+ configLog( "SSL Handshake Timeout max", "%d", _handshake_timeout_max );
+ configLog( "SSL Handshake attempts", "%d", _handshake_attempts );
+
+ if ( _address.setAddress(host) )
{
- Debug( _log, "Failed to parse [%s] as a hostname.", QSTRING_CSTR( host ) );
- QString errortext = QString("Invalid target address [%1]!").arg(host);
+ Debug( _log, "Successfully parsed %s as an ip address.", QSTRING_CSTR( host ) );
+ }
+ else
+ {
+ Debug( _log, "Failed to parse [%s] as an ip address.", QSTRING_CSTR( host ) );
+ QHostInfo info = QHostInfo::fromName(host);
+ if ( info.addresses().isEmpty() )
+ {
+ Debug( _log, "Failed to parse [%s] as a hostname.", QSTRING_CSTR( host ) );
+ QString errortext = QString("Invalid target address [%1]!").arg(host);
+ this->setInError( errortext );
+ isInitOK = false;
+ }
+ else
+ {
+ Debug( _log, "Successfully parsed %s as a hostname.", QSTRING_CSTR( host ) );
+ _address = info.addresses().first();
+ }
+ }
+
+ int config_port = deviceConfig["sslport"].toInt(_port);
+
+ if ( config_port <= 0 || config_port > MAX_PORT_SSL )
+ {
+ QString errortext = QString ("Invalid target port [%1]!").arg(config_port);
this->setInError( errortext );
isInitOK = false;
}
else
{
- Debug( _log, "Successfully parsed %s as a hostname.", QSTRING_CSTR( host ) );
- _address = info.addresses().first();
+ _ssl_port = config_port;
+ Debug( _log, "UDP SSL using %s:%u", QSTRING_CSTR( _address.toString() ), _ssl_port );
+ isInitOK = true;
}
}
-
- int config_port = deviceConfig["sslport"].toInt(_port);
-
- if ( config_port <= 0 || config_port > MAX_PORT_SSL )
- {
- QString errortext = QString ("Invalid target port [%1]!").arg(config_port);
- this->setInError( errortext );
- isInitOK = false;
- }
- else
- {
- _ssl_port = config_port;
- Debug( _log, "UDP SSL using %s:%u", QSTRING_CSTR( _address.toString() ), _ssl_port );
- }
return isInitOK;
}
int ProviderUdpSSL::open()
{
int retval = -1;
- QString errortext;
- _deviceReady = false;
+ _isDeviceReady = false;
- if ( init(_devConfig) )
+ // TODO: Question: Just checking .... Is this one time initialisation or required with every open request (during switch-off/switch-on)?
+ // In case one time initialisation, it should go to the init method.
+ // Everything that is required to pen a UDP-SSL connection again (after it maybe was closed remotely should go here)
+
+ if ( !initNetwork() )
{
- if ( !initNetwork() )
- {
- this->setInError( "UDP SSL Network error!" );
- }
- else
- {
- // Everything is OK -> enable device
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
+ this->setInError( "UDP SSL Network error!" );
+ }
+ else
+ {
+ // Everything is OK -> enable device
+ _isDeviceReady = true;
+ retval = 0;
}
return retval;
}
-void ProviderUdpSSL::close()
+int ProviderUdpSSL::close()
{
- LedDevice::close();
- closeSSLConnection();
+ // LedDevice specific closing activities
+ int retval = 0;
+ _isDeviceReady = false;
+
+ // TODO: You may want to check, if the device is already closed or close it and return, if ok or not
+ // Test, if device requires closing
+ if ( true /*If device is still open*/ )
+ {
+ // Close device
+
+ LedDevice::close();
+ closeSSLConnection();
+ // Everything is OK -> device is closed
+ }
+ return retval;
}
void ProviderUdpSSL::closeSSLConnection()
{
- if( _deviceReady && !_stopConnection )
+ if( _isDeviceReady && !_stopConnection )
{
closeSSLNotify();
freeSSLConnection();
@@ -412,7 +432,7 @@ bool ProviderUdpSSL::startSSLHandshake()
void ProviderUdpSSL::freeSSLConnection()
{
- sslLog( "SSL Connection cleanup..." );
+ sslLog( "SSL Connection clean-up..." );
_stopConnection = true;
@@ -425,15 +445,15 @@ void ProviderUdpSSL::freeSSLConnection()
mbedtls_x509_crt_free(&cacert);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
- sslLog( "SSL Connection cleanup...ok" );
+ sslLog( "SSL Connection clean-up...ok" );
}
catch (std::exception &e)
{
- sslLog( QString("SSL Connection cleanup Error: %s").arg( e.what() ) );
+ sslLog( QString("SSL Connection clean-up Error: %s").arg( e.what() ) );
}
catch (...)
{
- sslLog( "SSL Connection cleanup Error: " );
+ sslLog( "SSL Connection clean-up Error: " );
}
}
diff --git a/libsrc/leddevice/dev_net/ProviderUdpSSL.h b/libsrc/leddevice/dev_net/ProviderUdpSSL.h
index da0a5a7f..08475ef5 100644
--- a/libsrc/leddevice/dev_net/ProviderUdpSSL.h
+++ b/libsrc/leddevice/dev_net/ProviderUdpSSL.h
@@ -1,4 +1,5 @@
-#pragma once
+#ifndef PROVIDERUDPSSL_H
+#define PROVIDERUDPSSL_H
#include
#include
@@ -36,6 +37,7 @@
#include
#include
+#include
#include
#include
@@ -45,12 +47,11 @@
#include
#include
-#define READ_TIMEOUT_MS 1000
-#define MAX_RETRY 5
-
//----------- END mbedtls
-const ushort MAX_PORT_SSL = 65535;
+constexpr std::chrono::milliseconds STREAM_SSL_HANDSHAKE_TIMEOUT_MIN{400};
+constexpr std::chrono::milliseconds STREAM_SSL_HANDSHAKE_TIMEOUT_MAX{1000};
+constexpr std::chrono::milliseconds STREAM_SSL_READ_TIMEOUT{0};
class ProviderUdpSSL : public LedDevice
{
@@ -58,44 +59,45 @@ class ProviderUdpSSL : public LedDevice
public:
///
- /// Constructs specific LedDevice
+ /// @brief Constructs an UDP SSL LED-device
///
ProviderUdpSSL();
///
- /// Destructor of the LedDevice; closes the output device if it is open
+ /// @brief Destructor of the LED-device
///
virtual ~ProviderUdpSSL() override;
- ///
- /// Sets configuration
- ///
- /// @param deviceConfig the json device config
- /// @return true if success
- virtual bool init(const QJsonObject &deviceConfig) override;
-
-public slots:
- ///
- /// Closes the output device.
- /// Includes switching-off the device and stopping refreshes
- ///
- virtual void close() override;
-
protected:
///
- /// Initialise device's network details
+ /// @brief Initialise the UDP-SSL device's configuration and network address details
///
- /// @return True if success
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success#endif // PROVIDERUDP_H
///
- bool initNetwork();
+ virtual bool init(const QJsonObject &deviceConfig) override;
///
- /// Opens and configures the output device
+ /// @brief Opens the output device.
///
- /// @return Zero on succes else negative
+ /// @return Zero on success (i.e. device is ready), else negative
///
- int open() override;
+ virtual int open() override;
+
+ ///
+ /// @brief Closes the output device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
+ ///
+ /// @brief Initialise device's network details
+ ///
+ /// @return True, if success
+ ///
+ bool initNetwork();
///
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
@@ -208,3 +210,5 @@ private:
bool _debugStreamer;
int _debugLevel;
};
+
+#endif // PROVIDERUDPSSL_H
diff --git a/libsrc/leddevice/dev_other/LedDeviceFile.cpp b/libsrc/leddevice/dev_other/LedDeviceFile.cpp
index 0cbc7735..bb9b24d8 100644
--- a/libsrc/leddevice/dev_other/LedDeviceFile.cpp
+++ b/libsrc/leddevice/dev_other/LedDeviceFile.cpp
@@ -9,16 +9,15 @@ LedDeviceFile::LedDeviceFile(const QJsonObject &deviceConfig)
, _file (nullptr)
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
_printTimeStamp = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDeviceFile::~LedDeviceFile()
{
- if ( _file != nullptr )
- {
- _file->deleteLater();
- }
+ delete _file;
}
LedDevice* LedDeviceFile::construct(const QJsonObject &deviceConfig)
@@ -49,38 +48,30 @@ void LedDeviceFile::initFile(const QString &fileName)
int LedDeviceFile::open()
{
int retval = -1;
- QString errortext;
- _deviceReady = false;
+ _isDeviceReady = false;
- if ( init(_devConfig) )
+ if ( ! _file->isOpen() )
{
- if ( ! _file->isOpen() )
+ Debug(_log, "QIODevice::WriteOnly, %s", QSTRING_CSTR(_fileName));
+ if ( !_file->open(QIODevice::WriteOnly | QIODevice::Text) )
{
- Debug(_log, "QIODevice::WriteOnly, %s", QSTRING_CSTR(_fileName));
- if ( !_file->open(QIODevice::WriteOnly | QIODevice::Text) )
- {
- errortext = QString ("(%1) %2, file: (%3)").arg(_file->error()).arg(_file->errorString()).arg(_fileName);
- }
- else
- {
- _deviceReady = true;
- setEnable(true);
- retval = 0;
- }
-
- if ( retval < 0 )
- {
- this->setInError( errortext );
- }
+ QString errortext = QString ("(%1) %2, file: (%3)").arg(_file->error()).arg(_file->errorString(),_fileName);
+ this->setInError( errortext );
+ }
+ else
+ {
+ _isDeviceReady = true;
+ retval = 0;
}
}
return retval;
}
-void LedDeviceFile::close()
+int LedDeviceFile::close()
{
- LedDevice::close();
+ int retval = 0;
+ _isDeviceReady = false;
if ( _file != nullptr)
{
// Test, if device requires closing
@@ -91,6 +82,7 @@ void LedDeviceFile::close()
_file->close();
}
}
+ return retval;
}
int LedDeviceFile::write(const std::vector & ledValues)
diff --git a/libsrc/leddevice/dev_other/LedDeviceFile.h b/libsrc/leddevice/dev_other/LedDeviceFile.h
index b9f5b852..d5a6ae22 100644
--- a/libsrc/leddevice/dev_other/LedDeviceFile.h
+++ b/libsrc/leddevice/dev_other/LedDeviceFile.h
@@ -35,14 +35,6 @@ public:
/// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig);
- public slots:
- ///
- /// @brief Closes the output device.
- ///
- /// @return Zero on success (i.e. device is closed), else negative
- ///
- virtual void close() override;
-
protected:
///
@@ -60,6 +52,13 @@ protected:
///
virtual int open() override;
+ ///
+ /// @brief Closes the output device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
+
///
/// @brief Writes the RGB-Color values to the LEDs.
///
diff --git a/libsrc/leddevice/dev_other/LedDevicePiBlaster.cpp b/libsrc/leddevice/dev_other/LedDevicePiBlaster.cpp
index a3f38813..89ef50e5 100644
--- a/libsrc/leddevice/dev_other/LedDevicePiBlaster.cpp
+++ b/libsrc/leddevice/dev_other/LedDevicePiBlaster.cpp
@@ -13,7 +13,9 @@ LedDevicePiBlaster::LedDevicePiBlaster(const QJsonObject &deviceConfig)
: _fid(nullptr)
{
_devConfig = deviceConfig;
- _deviceReady = false;
+ _isDeviceReady = false;
+
+ _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
signal(SIGPIPE, SIG_IGN);
@@ -55,7 +57,7 @@ bool LedDevicePiBlaster::init(const QJsonObject &deviceConfig)
return false;
}
- // walk through the json config and populate the mapping tables
+ // walk through the JSON configuration and populate the mapping tables
for(QJsonArray::const_iterator gpioArray = gpioMapping.begin(); gpioArray != gpioMapping.end(); ++gpioArray)
{
const QJsonObject value = (*gpioArray).toObject();
@@ -84,56 +86,57 @@ int LedDevicePiBlaster::open()
{
int retval = -1;
QString errortext;
- _deviceReady = false;
+ _isDeviceReady = false;
- if ( init(_devConfig) )
+ if (_fid != nullptr)
{
- if (_fid != nullptr)
+ // The file pointer is already open
+ errortext = QString ("Device (%1) is already open.").arg(_deviceName);
+ }
+ else
+ {
+ if (!QFile::exists(_deviceName))
{
- // The file pointer is already open
- errortext = QString ("Device (%1) is already open.").arg(_deviceName);
+ errortext = QString ("The device (%1) does not yet exist.").arg(_deviceName);
}
else
{
- if (!QFile::exists(_deviceName))
+ _fid = fopen(QSTRING_CSTR(_deviceName), "w");
+ if (_fid == nullptr)
{
- errortext = QString ("The device (%1) does not yet exist.").arg(_deviceName);
+ errortext = QString ("Failed to open device (%1). Error message: %2").arg(_deviceName, strerror(errno));
}
else
{
- _fid = fopen(QSTRING_CSTR(_deviceName), "w");
- if (_fid == nullptr)
- {
- errortext = QString ("Failed to open device (%1). Error message: %2").arg(_deviceName, strerror(errno));
- }
- else
- {
- Info( _log, "Connected to device(%s)", QSTRING_CSTR(_deviceName));
- retval = 0;
- setEnable(true);
- }
+ Info( _log, "Connected to device(%s)", QSTRING_CSTR(_deviceName));
+
+ // Everything is OK, device is ready
+ _isDeviceReady = true;
+ retval = 0;
}
}
+ }
- if ( retval < 0 )
- {
- this->setInError( errortext );
- }
+ // On error/exceptions, set LedDevice in error
+ if ( retval < 0 )
+ {
+ this->setInError( errortext );
}
return retval;
}
-void LedDevicePiBlaster::close()
+int LedDevicePiBlaster::close()
{
- LedDevice::close();
+ int retval = 0;
+ _isDeviceReady = false;
- // LedDevice specific closing activites
- // Close the device (if it is opened)
+ // Test, if device requires closing
if (_fid != nullptr)
{
fclose(_fid);
_fid = nullptr;
}
+ return retval;
}
int LedDevicePiBlaster::write(const std::vector & ledValues)
diff --git a/libsrc/leddevice/dev_other/LedDevicePiBlaster.h b/libsrc/leddevice/dev_other/LedDevicePiBlaster.h
index fb6ff795..5ece8737 100644
--- a/libsrc/leddevice/dev_other/LedDevicePiBlaster.h
+++ b/libsrc/leddevice/dev_other/LedDevicePiBlaster.h
@@ -1,57 +1,64 @@
-#pragma once
+#ifndef LEDEVICEPIBLASTER_H
+#define LEDEVICEPIBLASTER_H
-// Hyperion-Leddevice includes
+// LedDevice includes
#include
///
/// Implementation of the LedDevice interface for writing to pi-blaster based PWM LEDs
///
-
class LedDevicePiBlaster : public LedDevice
{
public:
///
- /// Constructs specific LedDevice
+ /// @brief Constructs a pi-Blaster LED-device
///
- /// @param deviceConfig json device config
+ /// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDevicePiBlaster(const QJsonObject &deviceConfig);
+ ///
+ /// @brief Destructor of the LedDevice
+ ///
virtual ~LedDevicePiBlaster() override;
///
- /// Sets configuration
+ /// @brief Constructs the LED-device
///
- /// @param deviceConfig the json device config
- /// @return true if success
- bool init(const QJsonObject &deviceConfig) override;
-
- /// constructs leddevice
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig);
-public slots:
- ///
- /// Closes the output device.
- /// Includes switching-off the device and stopping refreshes
- ///
- virtual void close() override;
-
protected:
+
+ ///
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ bool init(const QJsonObject &deviceConfig) override;
+
///
/// Attempts to open the piblaster-device. This will only succeed if the device is not yet open
/// and the device is available.
///
- /// @return Zero on succes else negative
+ /// @return Zero on success (i.e. device is ready), else negative
///
- int open() override;
+ virtual int open() override;
+
+ ///
+ /// @brief Closes the output device.
+ ///
+ /// @return Zero on success (i.e. device is closed), else negative
+ ///
+ virtual int close() override;
private:
+
///
- /// Writes the colors to the PiBlaster device
+ /// @brief Writes the RGB-Color values to the LEDs.
///
- /// @param ledValues The color value for each led
- ///
- /// @return Zero on success else negative
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
///
int write(const std::vector