LED Device Features, Fixes and Refactoring (Resubmit PR855) (#875)

* Refactor LedDevices - Initial version
* Small renamings
* Add WLED as own device
* Lpd8806 Remove open() method
* remove dependency on Qt 5.10
* Lpd8806 Remove open() method
* Update WS281x
* Update WS2812SPI
* Add writeBlack for WLED powerOff
* WLED remove extra bracket
* Allow different Nanoleaf panel numbering sequence (Feature req.#827)
* build(deps): bump websocket-extensions from 0.1.3 to 0.1.4 in /docs (#826)
* Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4.
  - [Release notes](https://github.com/faye/websocket-extensions-node/releases)
  - [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
  - [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)
* Fix typos
* Nanoleaf clean-up
* Yeelight support, generalize wizard elements
* Update Yeelight to handle quota in music mode
* Yeelight extend rage for extraTimeDarkness for testing
* Clean-up - Add commentary, Remove development debug statements
* Fix brightnessSwitchOffOnMinimum typo and default value
* Yeelight support restoreOriginalState, additional Fixes
* WLED - Remove UDP-Port, as it is not configurable
* Fix merging issue
* Remove QHostAddress::operator=(const QString&)' is deprecated
* Windows compile errors and (Qt 5.15 deprecation) warnings
* Fix order includes
* LedDeviceFile Support Qt5.7 and greater
* Windows compatibility and other Fixes
* Fix Qt Version compatability
* Rs232 - Resolve portname from unix /dev/ style, fix DMX sub-type support
* Disable WLED Wizard Button (until Wizard is available)
* Yeelight updates
* Add wrong log-type as per #505
* Fixes and Clean-up after clang-tidy report
* Fix udpe131 not enabled for generated CID
* Change timer into dynamic for Qt Thread-Affinity
* Hue clean-up and diyHue workaround
* Updates after review feedback by m-seker
* Add "chrono" includes
This commit is contained in:
LordGrey 2020-07-12 20:27:56 +02:00 committed by GitHub
parent 3b48d8c9d6
commit 7389068a66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
125 changed files with 8864 additions and 3217 deletions

View File

@ -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)",

View File

@ -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",

View File

@ -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",

View File

@ -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)",

View File

@ -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)",

View File

@ -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)",

16
assets/webconfig/js/content_leds.js Executable file → Normal file
View File

@ -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 = [[]];

View File

@ -118,17 +118,21 @@ 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
{
// 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)
{
$(window.hyperion).trigger({type:"error",reason: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 );
}

View File

@ -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('<div id="hue_ids_t" style="display:none"><p style="font-weight:bold" id="hue_id_headline">'+$.i18n('wiz_hue_desc2')+'</p></div>');
}
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('<button type="button" class="btn btn-primary" id="btn_wiz_save" style="display:none"><i class="fa fa-fw fa-save"></i>'+$.i18n('general_btn_save')+'</button><button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
$('#wizp3_body').html('<span>'+$.i18n('wiz_hue_press_link')+'</span> <br /><br /><center><span id="connectionTime"></span><br /><i class="fa fa-cog fa-spin" style="font-size:100px"></i></center>');
@ -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()});
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+= '<option value="'+val+'"';
if(pos == val) options+=' selected="selected"';
options+= '>'+$.i18n(txt+val)+'</option>';
}
$('.lidsb').append(createTableRow([lightid+' ('+r[lightid].name+')', '<select id="hue_'+lightid+'" class="hue_sel_watch form-control">'
+ options
+ '</select>','<button class="btn btn-sm btn-primary" onClick=get_light_state('+lightid+')>'+$.i18n('wiz_hue_blinkblue',lightid)+'</button>']));
+ '</select>','<button class="btn btn-sm btn-primary" onClick=identify_hue_device("'+$("#ip").val()+'","'+$("#user").val()+'",'+lightid+')>'+$.i18n('wiz_hue_blinkblue',lightid)+'</button>']));
}
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('<i class="fa fa-magic fa-fw"></i>'+$.i18n(wled_title));
$('#wizp1_body').html('<h4 style="font-weight:bold;text-transform:uppercase;">'+$.i18n(wled_title)+'</h4><p>'+$.i18n(wled_intro1)+'</p>');
$('#wizp1_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_cont"><i class="fa fa-fw fa-check"></i>'+$.i18n('general_btn_continue')+'</button><button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
/*$('#wizp2_body').html('<div id="wh_topcontainer"></div>');
$('#wh_topcontainer').append('<div class="form-group" id="usrcont" style="display:none"></div>');
$('#wizp2_body').append('<div id="hue_ids_t" style="display:none"><p style="font-weight:bold" id="hue_id_headline">'+$.i18n('wiz_wled_desc2')+'</p></div>');
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('<button type="button" class="btn btn-primary" id="btn_wiz_save" style="display:none"><i class="fa fa-fw fa-save"></i>'+$.i18n('general_btn_save')+'</button><button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
*/
//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('<i class="fa fa-magic fa-fw"></i>'+$.i18n(yeelight_title));
$('#wizp1_body').html('<h4 style="font-weight:bold;text-transform:uppercase;">'+$.i18n(yeelight_title)+'</h4><p>'+$.i18n(yeelight_intro1)+'</p>');
$('#wizp1_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_cont"><i class="fa fa-fw fa-check"></i>'
+$.i18n('general_btn_continue')+'</button><button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'
+$.i18n('general_btn_cancel')+'</button>');
$('#wizp2_body').html('<div id="wh_topcontainer"></div>');
$('#wh_topcontainer').append('<div class="form-group" id="usrcont" style="display:none"></div>');
$('#wizp2_body').append('<div id="yee_ids_t" style="display:none"><p style="font-weight:bold" id="yee_id_headline">'+$.i18n('wiz_yeelight_desc2')+'</p></div>');
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('<button type="button" class="btn btn-primary" id="btn_wiz_save" style="display:none"><i class="fa fa-fw fa-save"></i>'
+$.i18n('general_btn_save')+'</button><buttowindow.serverConfig.device = d;n type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>'
+$.i18n('general_btn_cancel')+'</button>');
//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+= '<option value="'+val+'"';
if(pos === val) options+=' selected="selected"';
options+= '>'+$.i18n(txt+val)+'</option>';
}
if (! models.includes (lights[lightid].model) )
{
var enabled = 'disabled'
options = '<option value=disabled>'+$.i18n('wiz_yeelight_unsupported')+'</option>';
}
$('.lidsb').append(createTableRow([(parseInt(lightid, 10) + 1)+'. '+lightName+' ('+lightHostname+')', '<select id="yee_'+lightid+'" '+enabled+' class="yee_sel_watch form-control">'
+ options
+ '</select>','<button class="btn btn-sm btn-primary" onClick=identify_yeelight_device("'+lightHostname+'",'+lightPort+')>'
+ $.i18n('wiz_identify_light',lightName)+'</button>']));
}
$('.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 = '<p style="font-weight:bold;color:red;">'+$.i18n('wiz_yeelight_noLights')+'</p>';
$('#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);
}
}

@ -1 +1 @@
Subproject commit 6a720cbd42d30be28e0f5c5ff6b1c00a4588a29b
Subproject commit 68c6da2de32249d126264a363cc5ab788c87cc8b

View File

@ -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
}

View File

@ -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")

View File

@ -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
///

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICE_H
#define LEDEVICE_H
// qt includes
#include <QObject>
@ -28,204 +29,397 @@ typedef LedDevice* ( *LedDeviceCreateFuncType ) ( const QJsonObject& );
typedef std::map<QString,LedDeviceCreateFuncType> 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; }
int getLatchTime() const { return _latchTime_ms; }
///
/// @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);
///
/// Check, if device is ready to be used
/// i.e. initialisation and configuration were successfull
/// @brief Get the currently defined LatchTime.
///
/// @return True if device is ready
/// @return Latch time in milliseconds
///
bool isReady() const { return _deviceReady; }
int getLatchTime() const { return _latchTime_ms; }
///
/// Check, if device is in error state
/// @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 in error
/// @return A JSON structure holding a list of devices found
///
bool isInError() const { return _deviceInError; }
virtual QJsonObject discover();
inline bool componentState() const { return enabled(); }
/// Prints the RGB-Color values to stdout.
///
/// @param[in] ledValues The RGB-color per led
/// @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 A string of the device found
///
virtual QString discoverFirst();
///
/// @brief Get the device's properties
///
/// 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<ColorRgb>& 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<ColorRgb>& 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<ColorRgb>& 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<uint8_t> _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 errorMsg The error message to be logged
/// @param[in] errorMsg The error message to be logged
///
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<ColorRgb> _last_ledValues;
std::vector<ColorRgb> _lastLedValues;
};
#endif // LEDEVICE_H

View File

@ -1,7 +1,7 @@
#ifndef LEDEVICEFACTORY_H
#define LEDEVICEFACTORY_H
#pragma once
// Leddevice includes
// LedDevice includes
#include <leddevice/LedDevice.h>
///
@ -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

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICEWRAPPER_H
#define LEDEVICEWRAPPER_H
// util
#include <utils/Logger.h>
@ -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,13 +34,13 @@ 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();
@ -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

View File

@ -1,7 +1,12 @@
#pragma once
#ifndef SSDPDISCOVER_H
#define SSDPDISCOVER_H
#include <utils/Logger.h>
#include <QHostAddress>
#include <QUrl>
#include <QRegularExpression>
#include <chrono>
class QUdpSocket;
@ -11,6 +16,23 @@ enum class searchType{
STY_JSONSERVER
};
struct SSDPService {
QString cacheControl;
QUrl location;
QString server;
QString searchTarget;
QString uniqueServiceName;
QMap <QString,QString> 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<QString, SSDPService> 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<QString, SSDPService> _services;
QStringList _usnList;
QString _searchTarget;
QString _filter;
QString _filterHeader;
QRegularExpression _regExFilter;
bool _skipDupKeys;
};
#endif // SSDPDISCOVER_H

View File

@ -0,0 +1,42 @@
#ifndef QSTRINGUTILS_H
#define QSTRINGUTILS_H
#include <QString>
#include <QStringList>
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

View File

@ -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
}

View File

@ -5,7 +5,7 @@
"command": {
"type" : "string",
"required" : true,
"enum" : ["color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging", "processing", "sysinfo", "videomode", "authorize", "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"]
}
}
}

View File

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

View File

@ -18,6 +18,10 @@
// hyperion includes
#include <leddevice/LedDeviceWrapper.h>
#include <leddevice/LedDevice.h>
#include <leddevice/LedDeviceFactory.h>
#include <hyperion/GrabberWrapper.h>
#include <utils/jsonschema/QJsonFactory.h>
#include <utils/jsonschema/QJsonSchemaChecker.h>
@ -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 &params = 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 &params = 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");

View File

@ -18,6 +18,7 @@
#include <hyperion/ImageProcessor.h>
#include "HyperionConfig.h"
#include <hyperion/Hyperion.h>
#include <utils/QStringUtils.h>
// 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;

View File

@ -1,5 +1,4 @@
#include <leddevice/LedDevice.h>
#include <sstream>
//QT include
#include <QResource>
@ -13,102 +12,115 @@
#include "hyperion/Hyperion.h"
#include <utils/JsonUtils.h>
LedDevice::LedDevice(const QJsonObject& config, QObject* parent)
//std includes
#include <sstream>
#include <iomanip>
LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent)
: QObject(parent)
, _devConfig(config)
, _devConfig(deviceConfig)
, _log(Logger::getInstance("LEDDEVICE"))
, _ledBuffer(0)
, _deviceReady(false)
, _deviceInError(false)
, _refresh_timer(new QTimer(this))
, _refresh_timer_interval(0)
, _lastWriteTime(QDateTime::currentDateTime())
, _refreshTimer(nullptr)
, _refreshTimerInterval_ms(0)
, _latchTime_ms(0)
, _componentRegistered(false)
, _enabled(false)
, _refresh_enabled(false)
, _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;
}
int LedDevice::open()
void LedDevice::start()
{
int retval = -1;
QString errortext;
_deviceReady = false;
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
_deviceReady = true;
_isDeviceInitialised = true;
setEnable(true);
retval = 0;
}
}
void LedDevice::stop()
{
setEnable(false);
this->stopRefreshTimer();
}
int LedDevice::open()
{
_isDeviceReady = true;
int retval = 0;
return retval;
}
int LedDevice::close()
{
_isDeviceReady = false;
int 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<unsigned int>( 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);
_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<ColorRgb>& 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<ColorRgb>& 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,16 +210,37 @@ int LedDevice::updateLeds(const std::vector<ColorRgb>& ledValues)
return retval;
}
int LedDevice::writeBlack()
int LedDevice::rewriteLEDs()
{
return _deviceReady ? updateLeds(std::vector<ColorRgb>(static_cast<unsigned long>(_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<unsigned long>(_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;
for (int i = 0; i < numberOfBlack; i++)
{
if ( _latchTime_ms > 0 )
{
// Wait latch time before writing black
@ -215,13 +248,149 @@ int LedDevice::switchOff()
QTimer::singleShot( _latchTime_ms, &loop, SLOT( quit() ) );
loop.exec();
}
int rc = writeBlack();
rc = write(std::vector<ColorRgb>(static_cast<unsigned long>(_ledCount), ColorRgb::BLACK ));
}
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<unsigned long>(_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<ColorRgb>& ledValues)
{
std::cout << "LedValues [" << ledValues.size() <<"] [";
@ -271,3 +415,18 @@ void LedDevice::printLedValues(const std::vector<ColorRgb>& 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<const char*>(data), number);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return bytes.toHex(':');
#else
return bytes.toHex();
#endif
}

View File

@ -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;
}
}

View File

@ -33,5 +33,7 @@
<file alias="schema-ws281x">schemas/schema-ws281x.json</file>
<file alias="schema-karate">schemas/schema-karate.json</file>
<file alias="schema-nanoleaf">schemas/schema-nanoleaf.json</file>
<file alias="schema-wled">schemas/schema-wled.json</file>
<file alias="schema-yeelight">schemas/schema-yeelight.json</file>
</qresource>
</RCC>

View File

@ -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,9 +16,12 @@ 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
// Initialise sub-class
if ( LedDevice::init(deviceConfig) )
{
// Initialise LedDevice configuration and execution environment
// ...
if ( 0 /*Error during init*/)
{
@ -25,7 +30,11 @@ bool LedDeviceTemplate::init(const QJsonObject &deviceConfig)
this->setInError(errortext);
isInitOK = false;
}
else
{
isInitOK = true;
}
}
return isInitOK;
}
@ -33,12 +42,9 @@ int LedDeviceTemplate::open()
{
int retval = -1;
QString errortext;
_deviceReady = false;
_isDeviceReady = false;
// General initialisation and configuration of LedDevice
if ( init(_devConfig) )
{
// Open/Start LedDevice based on configuration
// Try to open the LedDevice
//...
if ( false /*If opening failed*/ )
@ -48,9 +54,8 @@ int LedDeviceTemplate::open()
}
else
{
// Everything is OK -> enable device
_deviceReady = true;
setEnable(true);
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
@ -59,16 +64,23 @@ int LedDeviceTemplate::open()
{
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<ColorRgb> & ledValues)

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICETEMPLATE_H
#define LEDEVICETEMPLATE_H
// LedDevice includes
#include <leddevice/LedDevice.h>
@ -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<ColorRgb> & ledValues) override;
private:
};
#endif // LEDEVICETEMPLATE_H

View File

@ -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] <INFO> LedDevice \'" << QSTRING_CSTR(_ledDevice->getActiveDeviceType()) << "\' closed" << std::endl;

View File

@ -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,8 +38,11 @@ LedDevice* LedDeviceHyperionUsbasp::construct(const QJsonObject &deviceConfig)
bool LedDeviceHyperionUsbasp::init(const QJsonObject &deviceConfig)
{
bool isInitOK = LedDevice::init(deviceConfig);
bool isInitOK = false;
// Initialise sub-class
if ( LedDevice::init(deviceConfig) )
{
QString ledType = deviceConfig["ledType"].toString("ws2801");
if (ledType != "ws2801" && ledType != "ws2812")
{
@ -43,35 +53,23 @@ bool LedDeviceHyperionUsbasp::init(const QJsonObject &deviceConfig)
else
{
_writeLedsCommand = (ledType == "ws2801") ? CMD_WRITE_WS2801 : CMD_WRITE_WS2812;
}
return isInitOK;
}
int LedDeviceHyperionUsbasp::open()
{
int retval = -1;
QString errortext;
_deviceReady = false;
// General initialisation and configuration of LedDevice
if ( init(_devConfig) )
{
int error;
// initialize the usb context
// 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);
@ -79,11 +77,10 @@ int LedDeviceHyperionUsbasp::open()
for (ssize_t i = 0 ; i < deviceCount; ++i)
{
// try to open and initialize the device
error = testAndOpen(deviceList[i]);
if (error == 0)
if ( testAndOpen(deviceList[i]) == 0 )
{
// a device was sucessfully opened. break from list
_device = deviceList[i];
// a device was successfully opened. break from list
break;
}
}
@ -93,31 +90,47 @@ int LedDeviceHyperionUsbasp::open()
if (_deviceHandle == nullptr)
{
//Error(_log, "No %s has been found", QSTRING_CSTR(_usbProductDescription));
QString errortext;
errortext = QString ("No %1 has been found").arg( _usbProductDescription);
this->setInError( errortext );
}
else
{
isInitOK = true;
}
}
}
}
return isInitOK;
}
int LedDeviceHyperionUsbasp::open()
{
int retval = -1;
_isDeviceReady = false;
if ( libusb_open(_device, &_deviceHandle) != LIBUSB_SUCCESS )
{
QString errortext = QString ("Failed to open [%1]").arg(_usbProductDescription);
this->setInError(errortext);
}
else
{
// Everything is OK -> enable device
_deviceReady = true;
setEnable(true);
_isDeviceReady = true;
retval = 0;
}
}
// On error/exceptions, set LedDevice in error
if ( retval < 0 )
{
this->setInError( errortext );
}
}
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<ColorRgb> &ledValues)
{
int nbytes = libusb_control_transfer(
_deviceHandle, // device handle
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, // request type
static_cast<uint8_t>( 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<ColorRgb> &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));

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICEHYPERIONUSBASP_H
#define LEDEVICEHYPERIONUSBASP_H
// stl includes
#include <vector>
@ -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

View File

@ -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()
// Initialise sub-class
if ( LedDevice::init(deviceConfig) )
{
int retval = -1;
QString errortext;
_deviceReady = false;
_serialNumber = deviceConfig["serial"].toString("");
// General initialisation and configuration of LedDevice
if ( init(_devConfig) )
{
int error;
// initialize the usb context
// 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
{
isInitOK = true;
}
}
}
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
_deviceReady = true;
setEnable(true);
_isDeviceReady = true;
retval = 0;
}
}
// On error/exceptions, set LedDevice in error
if ( retval < 0 )
{
this->setInError( errortext );
}
}
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<uint8_t>( 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<ColorRgb> &ledValues)
{
return write(ledValues.data(), ledValues.size());
return write(ledValues.data(), static_cast<int>(ledValues.size()));
}
int LedDeviceLightpack::write(const ColorRgb * ledValues, int size)
{
int count = qMin(_hwLedCount, static_cast<int>( _ledCount));
int count = qMin(_hwLedCount, static_cast<int>( 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()
{
int rc = LedDevice::switchOff();
if ( _deviceReady )
bool LedDeviceLightpack::powerOff()
{
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<uint8_t>( 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)

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICELIGHTPACK_H
#define LEDEVICELIGHTPACK_H
// stl includes
#include <cstdint>
@ -15,80 +16,94 @@
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:
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
///
/// @return Zero on success else negative
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
private:
///
/// 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

View File

@ -22,30 +22,33 @@ LedDeviceMultiLightpack::LedDeviceMultiLightpack(const QJsonObject &deviceConfig
, _lightpacks()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDeviceMultiLightpack::~LedDeviceMultiLightpack()
{
for (LedDeviceLightpack * device : _lightpacks)
{
if ( device != nullptr)
{
delete device;
}
}
}
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());
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<int>(_lightpacks.size()) )
{
// Everything is OK -> enable device
_deviceReady = true;
setEnable(true);
_isDeviceReady = true;
retval = 0;
}
// On error/exceptions, set LedDevice in error
if ( retval < 0 )
else
{
this->setInError( errortext );
}
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<ColorRgb> &ledValues)
{
const ColorRgb * data = ledValues.data();
@ -104,8 +144,11 @@ int LedDeviceMultiLightpack::write(const std::vector<ColorRgb> &ledValues)
int count = qMin(static_cast<int>( device->getLedCount()), size);
if (count > 0)
{
if ( device->isOpen() )
{
device->write(data, count);
}
data += count;
size -= count;
@ -119,14 +162,16 @@ int LedDeviceMultiLightpack::write(const std::vector<ColorRgb> &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);
}
}

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICEMULTILIGHTPACK_H
#define LEDEVICEMULTILIGHTPACK_H
// stl includes
#include <vector>
@ -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<ColorRgb> & 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<ColorRgb>& ledValues) override;
static QStringList getLightpackSerials();
static QString getString(libusb_device * device, int stringDescriptorIndex);
@ -63,3 +88,5 @@ private:
/// buffer for led data
std::vector<LedDeviceLightpack *> _lightpacks;
};
#endif // LEDEVICEMULTILIGHTPACK_H

View File

@ -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;
// Initialise sub-class
if ( ProviderHID::init(deviceConfig) )
{
_ledBuffer.resize(_ledRGBCount + 2, uint8_t(0));
_ledBuffer[0] = 3;
_ledBuffer[1] = 0;
isInitOK = true;
}
return isInitOK;
}

View File

@ -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
///
/// @return Zero on success else negative
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
};
#endif // LEDEVICEPAINTTPACK_H

View File

@ -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);
bool isInitOK = false;
// Initialise sub-class
if ( ProviderHID::init(deviceConfig) )
{
_ledBuffer.resize(_ledRGBCount);
isInitOK = true;
}
return isInitOK;
}

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICERAWHID_H
#define LEDEVICERAWHID_H
// Qt includes
#include <QTimer>
@ -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<ColorRgb> & ledValues) override;
};
#endif // LEDEVICERAWHID_H

View File

@ -21,12 +21,20 @@ 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;
// 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();
@ -35,27 +43,25 @@ bool ProviderHID::init(const QJsonObject &deviceConfig)
_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;
if ( init(_devConfig) )
{
// 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");
_isDeviceReady = false;
// Open the device
Info(_log, "Opening device: VID %04hx PID %04hx\n", _VendorId, _ProductId);
@ -64,8 +70,7 @@ int ProviderHID::open()
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";
this->setInError( "Failed to open HID device. Maybe your PID/VID setting is wrong? Make sure to add a udev rule/use sudo." );
// http://www.signal11.us/oss/hidapi/
/*
@ -88,8 +93,7 @@ int ProviderHID::open()
{
Info(_log,"Opened HID device successful");
// Everything is OK -> enable device
_deviceReady = true;
setEnable(true);
_isDeviceReady = true;
retval = 0;
}
@ -97,31 +101,25 @@ int ProviderHID::open()
if (_delayAfterConnect_ms > 0)
{
_blockedForDelay = true;
QTimer::singleShot(_delayAfterConnect_ms, this, SLOT(unblockAfterDelay()));
QTimer::singleShot(_delayAfterConnect_ms, this, &ProviderHID::unblockAfterDelay );
Debug(_log, "Device blocked for %d ms", _delayAfterConnect_ms);
}
}
// On error/exceptions, set LedDevice in error
if ( retval < 0 )
{
this->setInError( errortext );
}
}
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<ushort>(cur_dev->product_id),0,16));
deviceInfo.insert("release_number",QString("0x%1").arg(static_cast<ushort>(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<ushort>(cur_dev->usage_page),0,16));
deviceInfo.insert("vendorIdentifier", QString("0x%1").arg(static_cast<ushort>(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;
}

View File

@ -1,6 +1,5 @@
#pragma once
#include <QObject>
#ifndef PROVIDERHID_H
#define PROVIDERHID_H
// libusb include
#include <hidapi/hidapi.h>
@ -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

View File

@ -1,5 +1,6 @@
// Local-Hyperion includes
#include "LedDeviceAtmoOrb.h"
#include <utils/QStringUtils.h>
// qt includes
#include <QUdpSocket>
@ -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,12 +52,7 @@ bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig)
_multiCastGroupPort = static_cast<quint16>(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)
@ -64,15 +62,21 @@ bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig)
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,17 +93,15 @@ 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 )
{
_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()).arg(_multicastGroup);
QString errortext = QString ("(%1) %2, MulticastGroup: (%3)").arg(_udpSocket->error()).arg(_udpSocket->errorString(), _multicastGroup);
this->setInError( errortext );
}
else
@ -107,25 +109,24 @@ int LedDeviceAtmoOrb::open()
_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
{
// Everything is OK, device is ready
_deviceReady = true;
setEnable(true);
_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 <ColorRgb> &ledValues)
@ -211,7 +213,9 @@ int LedDeviceAtmoOrb::write(const std::vector <ColorRgb> &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<char>(color.green);
bytes[7] = static_cast<char>(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);

View File

@ -1,9 +1,8 @@
#pragma once
#ifndef LEDEVICEATMOORB_H
#define LEDEVICEATMOORB_H
// Qt includes
#include <QObject>
#include <QString>
#include <QNetworkAccessManager>
#include <QUdpSocket>
#include <QHostAddress>
#include <QVector>
@ -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;
private:
virtual int close() override;
///
/// Sends the given led-color values to the Orbs
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param ledValues The color-value per led
/// @return Zero on success else negative
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
private:
///
/// 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<int, int> lastColorBlueMap;
};
#endif // LEDEVICEATMOORB_H

View File

@ -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)
,_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,13 +46,12 @@ 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;
@ -45,7 +59,15 @@ bool LedDeviceFadeCandy::init(const QJsonObject &deviceConfig)
else
{
_host = deviceConfig["output"].toString("127.0.0.1");
_port = deviceConfig["port"].toInt(7890);
_port = deviceConfig["port"].toInt(STREAM_DEFAULT_PORT);
//If host not configured the init fails
if ( _host.isEmpty() )
{
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);
@ -71,6 +93,12 @@ bool LedDeviceFadeCandy::init(const QJsonObject &deviceConfig)
_opc_data[1] = OPC_SET_PIXELS;
_opc_data[2] = _ledRGBCount >> 8;
_opc_data[3] = _ledRGBCount & 0xff;
if ( initNetwork() )
{
isInitOK = true;
}
}
}
}
return isInitOK;
@ -78,49 +106,64 @@ bool LedDeviceFadeCandy::init(const QJsonObject &deviceConfig)
bool LedDeviceFadeCandy::initNetwork()
{
bool isInitOK = true;
bool isInitOK = false;
// TODO: Add Network-Error handling
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!" );
errortext = QString ("Failed to open device.");
this->setInError( errortext );
}
else
{
_deviceReady = true;
setEnable(true);
// 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
// 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 != nullptr )
{
if ( _client->state() == QAbstractSocket::UnconnectedState ) {
_client->connectToHost( _host, _port);
@ -133,7 +176,7 @@ bool LedDeviceFadeCandy::tryConnect()
}
}
}
}
return isConnected();
}
@ -148,15 +191,16 @@ int LedDeviceFadeCandy::write( const std::vector<ColorRgb> & 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() );
}
return -2;
}

View File

@ -1,10 +1,11 @@
#pragma once
#ifndef LEDEVICEFADECANDY_H
#define LEDEVICEFADECANDY_H
// STL/Qt includes
#include <QTcpSocket>
#include <QString>
// Leddevice includes
// LedDevice includes
#include <leddevice/LedDevice.h>
///
@ -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<ColorRgb> & 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<ColorRgb>& 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

View File

@ -1,8 +1,8 @@
// Local-Hyperion includes
#include "LedDeviceNanoleaf.h"
// ssdp discover
#include <ssdp/SSDPDiscover.h>
#include <utils/QStringUtils.h>
// Qt includes
#include <QEventLoop>
@ -12,61 +12,66 @@
#include <sstream>
#include <iomanip>
//
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()
if ( _restApi != nullptr )
{
_devConfig = deviceConfig;
_deviceReady = false;
_networkmanager = nullptr;
_extControlVersion = EXTCTRLVER_V2;
_panelLedCount = 0;
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<uint>( 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 IP defined nor Nanoleaf device was discovered");
return false;
}
}
// 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 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() )
{
this->setInError("No target hostname nor IP defined");
isInitOK = false;
}
else
{
QJsonObject jsonAllPanelInfo = doc.object();
if ( initRestAPI( _hostname, _apiPort, _authToken ) )
{
// 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);
}
}
}
}
return isInitOK;
}
bool LedDeviceNanoleaf::initLedsConfiguration()
{
bool isInitOK = true;
//Get Nanoleaf device details and configuration
// Read Panel count and panel Ids
_restApi->setPath(API_ROOT);
httpResponse response = _restApi->get();
if ( response.error() )
{
this->setInError ( response.getErrorReason() );
isInitOK = false;
}
else
{
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<uint, std::map<uint, uint>> panelMap;
// Loop over all children.
for (const QJsonValue & value : positionData)
for (const QJsonValue value : positionData)
{
QJsonObject panelObj = value.toObject();
@ -239,11 +270,15 @@ 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
{
// Sort panels right to left
@ -252,12 +287,16 @@ 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);
}
}
}
}
this->_panelLedCount = static_cast<uint>(_panelIds.size());
_devConfig["hardwareLedCount"] = static_cast<int>(_panelLedCount);
@ -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!" );
//Set default streaming port
_port = static_cast<uchar>(jsonStreamControllInfo[STREAM_CONTROL_PORT].toInt());
}
else
if ( ProviderUdp::open() == 0 )
{
if ( initLeds() )
{
_deviceReady = true;
setEnable(true);
// 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;
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)
#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;
}
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
QString apiHost = addressparts[0];
int apiPort;
QJsonDocument LedDeviceNanoleaf::changeToExternalControlMode()
if ( addressparts.size() > 1)
{
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;
}
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() );
}
}
apiPort = addressparts[1].toInt();
}
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;
apiPort = API_DEFAULT_PORT;
}
errorReason = QString ("%1:%2 [%3 %4] - %5").arg(_hostname, _api_port, QString(httpStatusCode) , httpReason, advise);
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()));
}
else {
errorReason = QString ("%1:%2 - %3").arg(_hostname, _api_port, reply->errorString());
properties.insert("properties", response.getBody().object());
Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
}
this->setInError ( errorReason );
return properties;
}
// Return response
return jsonDoc;
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()
{
_extControlVersion = EXTCTRLVER_V2;
//Enable UDP Mode v2
_restApi->setPath(API_EFFECT);
httpResponse response =_restApi->put(API_EXT_MODE_STRING_V2);
return response.getBody();
}
int LedDeviceNanoleaf::write(const std::vector<ColorRgb> & ledValues)
{
int retVal = 0;
uint udpBufferSize;
@ -573,46 +614,6 @@ int LedDeviceNanoleaf::write(const std::vector<ColorRgb> & 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<uchar>(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<uint8_t>& buffer ) const
{
std::stringstream ss;

View File

@ -1,12 +1,11 @@
#pragma once
#ifndef LEDEVICENANOLEAF_H
#define LEDEVICENANOLEAF_H
// Leddevice includes
// LedDevice includes
#include <leddevice/LedDevice.h>
#include "ProviderRestApi.h"
#include "ProviderUdp.h"
// ssdp discover
#include <ssdp/SSDPDiscover.h>
// Qt includes
#include <QString>
#include <QNetworkAccessManager>
@ -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
/// "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<ColorRgb> & 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<ColorRgb> & 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<uint8_t>& 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<uint> _panelIds;
};
#endif // LEDEVICENANOLEAF_H

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,9 @@
#include <QtCore/qmath.h>
#include <QStringList>
// Leddevice includes
// LedDevice includes
#include <leddevice/LedDevice.h>
#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<quint16,QJsonObject>& getLightMap();
const QMap<quint16,QJsonObject>& 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<quint16, QJsonObject> 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<quint16, QJsonObject> 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
///
/// @return Zero on success else negative
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb>& 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<quint16, QJsonObject> map);
bool setLights();
int writeSingleLights(const std::vector<ColorRgb>& 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<quint16, QJsonObject> &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<ColorRgb>& ledValues);
bool noSignalDetection();
@ -431,20 +522,19 @@ private:
/// 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;
/// 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<unsigned int> _lightIds;
std::vector<quint16> _lightIds;
/// Array to save the lamps.
std::vector<PhilipsHueLight> _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;
};

View File

@ -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)
{
_port = TPM2_DEFAULT_PORT;
bool isInitOK = ProviderUdp::init(deviceConfig);
bool isInitOK = false;
_port = TPM2_DEFAULT_PORT;
// 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;
}

View File

@ -1,39 +1,46 @@
#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<ColorRgb> & ledValues) override;
@ -42,3 +49,5 @@ private:
int _tpm2TotalPackets;
int _tpm2ThisPacket;
};
#endif // LEDEVICETPM2NET_H

View File

@ -1,3 +1,6 @@
// hyperion local includes
#include "LedDeviceUdpArtNet.h"
#ifdef _WIN32
#include <winsock.h>
#else
@ -6,16 +9,18 @@
#include <QHostInfo>
// 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)
{
_port = ARTNET_DEFAULT_PORT;
bool isInitOK = ProviderUdp::init(deviceConfig);
bool isInitOK = false;
_port = ARTNET_DEFAULT_PORT;
// 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<ColorRgb> &ledValues)

View File

@ -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
/// @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 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<ColorRgb> & 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

View File

@ -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<ColorRgb> &ledValues)
return retVal;
}

View File

@ -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
/// @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 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<ColorRgb> & 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

View File

@ -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;
}

View File

@ -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<ColorRgb> & ledValues) override;
QList<int> _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<ColorRgb> &ledValues) override;
};
#endif // LEDEVICEUDPH801_H

View File

@ -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<ColorRgb> &ledValues)
{
const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(ledValues.data());
return writeBytes((unsigned)_ledRGBCount, dataPtr);
return writeBytes(_ledRGBCount, dataPtr);
}

View File

@ -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 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<ColorRgb> & ledValues) override;
};
#endif // LEDEVICEUDPRAW_H

View File

@ -0,0 +1,291 @@
// Local-Hyperion includes
#include "LedDeviceWled.h"
#include <ssdp/SSDPDiscover.h>
#include <utils/QStringUtils.h>
// 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<ColorRgb> &ledValues)
{
const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(ledValues.data());
return writeBytes( _ledRGBCount, dataPtr);
}

View File

@ -0,0 +1,132 @@
#ifndef LEDDEVICEWLED_H
#define LEDDEVICEWLED_H
// LedDevice includes
#include <leddevice/LedDevice.h>
#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<ColorRgb> & 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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,627 @@
#ifndef LEDEVICEYEELIGHT_H
#define LEDEVICEYEELIGHT_H
// LedDevice includes
#include <leddevice/LedDevice.h>
// Qt includes
#include <QTcpSocket>
#include <QHostAddress>
#include <QTcpServer>
#include <QColor>
#include <chrono>
// 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 &params);
///
/// @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<ColorRgb> & 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<yeelightAddress> &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<yeelightAddress> _lightsAddressList;
/// Array to save the lights
std::vector<YeelightLight> _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

View File

@ -0,0 +1,247 @@
// Local-Hyperion includes
#include "ProviderRestApi.h"
// Qt includes
#include <QEventLoop>
#include <QNetworkReply>
#include <QByteArray>
//std includes
#include <iostream>
// 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;
}

View File

@ -0,0 +1,216 @@
#ifndef PROVIDERRESTKAPI_H
#define PROVIDERRESTKAPI_H
// Local-Hyperion includes
#include <utils/Logger.h>
// Qt includes
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QJsonDocument>
///
/// 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

View File

@ -22,7 +22,7 @@ ProviderUdp::ProviderUdp()
, _port(1)
, _defaultHost("127.0.0.1")
{
_deviceReady = false;
_isDeviceReady = false;
_latchTime_ms = 1;
}
@ -30,14 +30,17 @@ ProviderUdp::~ProviderUdp()
{
if ( _udpSocket != nullptr )
{
_udpSocket->deleteLater();
delete _udpSocket;
}
}
bool ProviderUdp::init(const QJsonObject &deviceConfig)
{
bool isInitOK = LedDevice::init(deviceConfig);
bool isInitOK = false;
// Initialise sub-class
if ( LedDevice::init(deviceConfig) )
{
QString host = deviceConfig["host"].toString(_defaultHost);
if (_address.setAddress(host) )
@ -73,16 +76,19 @@ bool ProviderUdp::init(const QJsonObject &deviceConfig)
{
_port = static_cast<int>(config_port);
Debug( _log, "UDP using %s:%d", _address.toString().toStdString().c_str() , _port );
}
_udpSocket = new QUdpSocket(this);
isInitOK = true;
}
}
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;
}
return isInitOK;
}
int ProviderUdp::open()
{
int retval = -1;
QString errortext;
_deviceReady = false;
if ( init(_devConfig) )
{
if ( ! initNetwork())
{
this->setInError( "UDP Network error!" );
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
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)

View File

@ -1,13 +1,15 @@
#pragma once
#ifndef PROVIDERUDP_H
#define PROVIDERUDP_H
// LedDevice includes
#include <leddevice/LedDevice.h>
// Hyperion includes
#include <leddevice/LedDevice.h>
#include <utils/Logger.h>
// qt
// Qt includes
#include <QHostAddress>
class QUdpSocket;
#include <QUdpSocket>
///
/// 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

View File

@ -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,8 +51,11 @@ ProviderUdpSSL::~ProviderUdpSSL()
bool ProviderUdpSSL::init(const QJsonObject &deviceConfig)
{
bool isInitOK = LedDevice::init(deviceConfig);
bool isInitOK = false;
// Initialise sub-class
if ( LedDevice::init(deviceConfig) )
{
_debugStreamer = deviceConfig["debugStreamer"].toBool(false);
_debugLevel = deviceConfig["debugLevel"].toString().toInt(0);
@ -120,6 +126,8 @@ bool ProviderUdpSSL::init(const QJsonObject &deviceConfig)
{
_ssl_port = config_port;
Debug( _log, "UDP SSL using %s:%u", QSTRING_CSTR( _address.toString() ), _ssl_port );
isInitOK = true;
}
}
return isInitOK;
}
@ -127,11 +135,12 @@ bool ProviderUdpSSL::init(const QJsonObject &deviceConfig)
int ProviderUdpSSL::open()
{
int retval = -1;
QString errortext;
_deviceReady = false;
_isDeviceReady = false;
// 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 ( init(_devConfig) )
{
if ( !initNetwork() )
{
this->setInError( "UDP SSL Network error!" );
@ -139,23 +148,34 @@ int ProviderUdpSSL::open()
else
{
// Everything is OK -> enable device
_deviceReady = true;
setEnable(true);
_isDeviceReady = true;
retval = 0;
}
}
return retval;
}
void ProviderUdpSSL::close()
int ProviderUdpSSL::close()
{
// 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: <unknown>" );
sslLog( "SSL Connection clean-up Error: <unknown>" );
}
}

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef PROVIDERUDPSSL_H
#define PROVIDERUDPSSL_H
#include <leddevice/LedDevice.h>
#include <utils/Logger.h>
@ -36,6 +37,7 @@
#include <string.h>
#include <cstring>
#include <chrono>
#include <mbedtls/net_sockets.h>
#include <mbedtls/ssl_ciphersuites.h>
@ -45,12 +47,11 @@
#include <mbedtls/error.h>
#include <mbedtls/debug.h>
#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

View File

@ -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() )
{
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);
QString errortext = QString ("(%1) %2, file: (%3)").arg(_file->error()).arg(_file->errorString(),_fileName);
this->setInError( errortext );
}
else
{
_deviceReady = true;
setEnable(true);
_isDeviceReady = true;
retval = 0;
}
if ( retval < 0 )
{
this->setInError( errortext );
}
}
}
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<ColorRgb> & ledValues)

View File

@ -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.
///

View File

@ -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,10 +86,8 @@ int LedDevicePiBlaster::open()
{
int retval = -1;
QString errortext;
_deviceReady = false;
_isDeviceReady = false;
if ( init(_devConfig) )
{
if (_fid != nullptr)
{
// The file pointer is already open
@ -109,31 +109,34 @@ int LedDevicePiBlaster::open()
else
{
Info( _log, "Connected to device(%s)", QSTRING_CSTR(_deviceName));
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
setEnable(true);
}
}
}
// 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<ColorRgb> & ledValues)

View File

@ -1,57 +1,64 @@
#pragma once
#ifndef LEDEVICEPIBLASTER_H
#define LEDEVICEPIBLASTER_H
// Hyperion-Leddevice includes
// LedDevice includes
#include <leddevice/LedDevice.h>
///
/// 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<ColorRgb> &ledValues) override;
@ -65,3 +72,5 @@ private:
FILE * _fid;
};
#endif // LEDEVICETEMPLATE_H

View File

@ -4,7 +4,9 @@ LedDeviceWS281x::LedDeviceWS281x(const QJsonObject &deviceConfig)
: LedDevice()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDeviceWS281x::~LedDeviceWS281x()
@ -20,10 +22,11 @@ bool LedDeviceWS281x::init(const QJsonObject &deviceConfig)
{
QString errortext;
bool isInitOK = LedDevice::init(deviceConfig);
if ( isInitOK )
{
bool isInitOK = false;
// Initialise sub-class
if ( LedDevice::init(deviceConfig) )
{
QString whiteAlgorithm = deviceConfig["whiteAlgorithm"].toString("white_off");
_whiteAlgorithm = RGBW::stringToWhiteAlgorithm(whiteAlgorithm);
@ -43,7 +46,7 @@ bool LedDeviceWS281x::init(const QJsonObject &deviceConfig)
else
{
memset(&_led_string, 0, sizeof(_led_string));
_led_string.freq = deviceConfig["freq"].toInt(800000ul);
_led_string.freq = deviceConfig["freq"].toInt(800000UL);
_led_string.dmanum = deviceConfig["dma"].toInt(5);
_led_string.channel[_channel].gpionum = deviceConfig["gpio"].toInt(18);
_led_string.channel[_channel].count = deviceConfig["leds"].toInt(256);
@ -59,18 +62,10 @@ bool LedDeviceWS281x::init(const QJsonObject &deviceConfig)
Debug( _log, "ws281x strip type : %d", _led_string.channel[_channel].strip_type );
if (ws2811_init(&_led_string) < 0)
{
errortext = "Unable to initialize ws281x library.";
isInitOK = false;
}
else
{
isInitOK = true;
}
}
}
}
if ( !isInitOK)
{
@ -79,14 +74,40 @@ bool LedDeviceWS281x::init(const QJsonObject &deviceConfig)
return isInitOK;
}
void LedDeviceWS281x::close()
int LedDeviceWS281x::open()
{
LedDevice::close();
int retval = -1;
_isDeviceReady = false;
if (_deviceReady)
// Try to open the LedDevice
ws2811_return_t rc = ws2811_init(&_led_string);
if ( rc != WS2811_SUCCESS )
{
QString errortext = QString ("Failed to open. Error message: %1").arg( ws2811_get_return_t_str(rc) );
this->setInError( errortext );
}
else
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
return retval;
}
int LedDeviceWS281x::close()
{
int retval = 0;
_isDeviceReady = false;
// LedDevice specific closing activities
if ( isInitialised() )
{
ws2811_fini(&_led_string);
}
return retval;
}
// Send new values down the LED chain

View File

@ -1,50 +1,63 @@
#pragma once
#ifndef LEDEVICEWS281X_H
#define LEDEVICEWS281X_H
// LedDevice includes
#include <leddevice/LedDevice.h>
#include <ws2811.h>
///
/// Implementation of the LedDevice interface for writing to Ws2812 led device via pwm.
/// Implementation of the LedDevice interface for writing to WS281x LED-device via pwm.
///
class LedDeviceWS281x : public LedDevice
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an WS281x LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceWS281x(const QJsonObject &deviceConfig);
///
/// Destructor of the LedDevice, waits for DMA to complete and then cleans up
/// @brief Destructor of the LedDevice
///
virtual ~LedDeviceWS281x() override;
/// constructs leddevice
///
/// @brief Destructor of the LedDevice
///
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;
/// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
protected:
///
/// Writes the led color values to the led-device
/// @brief Closes the output device.
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
/// @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<ColorRgb> & ledValues) override;
@ -55,3 +68,5 @@ private:
RGBW::WhiteAlgorithm _whiteAlgorithm;
ColorRgbw _temp_rgbw;
};
#endif // LEDEVICEWS281X_H

View File

@ -6,9 +6,9 @@ LedDeviceAdalight::LedDeviceAdalight(const QJsonObject &deviceConfig)
, _ligthBerryAPA102Mode(false)
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
connect(this,SIGNAL(receivedData(QByteArray)),this,SLOT(receivedData(QByteArray)));
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceAdalight::construct(const QJsonObject &deviceConfig)
@ -18,7 +18,11 @@ LedDevice* LedDeviceAdalight::construct(const QJsonObject &deviceConfig)
bool LedDeviceAdalight::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderRs232::init(deviceConfig);
bool isInitOK = false;
// Initialise sub-class
if ( ProviderRs232::init(deviceConfig) )
{
_ligthBerryAPA102Mode = deviceConfig["lightberry_apa102_mode"].toBool(false);
@ -55,6 +59,8 @@ bool LedDeviceAdalight::init(const QJsonObject &deviceConfig)
Debug( _log, "Adalight header for %d leds: %c%c%c 0x%02x 0x%02x 0x%02x", _ledCount,
_ledBuffer[0], _ledBuffer[1], _ledBuffer[2], _ledBuffer[3], _ledBuffer[4], _ledBuffer[5] );
isInitOK = true;
}
return isInitOK;
}
@ -75,10 +81,7 @@ int LedDeviceAdalight::write(const std::vector<ColorRgb> & ledValues)
memcpy(_headerSize + _ledBuffer.data(), ledValues.data(), ledValues.size() * 3);
}
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}
int rc = writeBytes(_ledBuffer.size(), _ledBuffer.data());
void LedDeviceAdalight::receivedData(QByteArray data)
{
Debug(_log, ">>received %d bytes data", data.size());
return rc;
}

View File

@ -1,37 +1,47 @@
#pragma once
#ifndef LEDEVICETADALIGHT_H
#define LEDEVICETADALIGHT_H
// hyperion includes
#include "ProviderRs232.h"
///
/// Implementation of the LedDevice interface for writing to an Adalight led device.
/// Implementation of the LedDevice interface for writing to an Adalight LED-device.
///
class LedDeviceAdalight : public ProviderRs232
{
Q_OBJECT
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an Adalight LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceAdalight(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:
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
public slots:
void receivedData(QByteArray data);
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<ColorRgb> & ledValues) override;
@ -39,3 +49,4 @@ private:
bool _ligthBerryAPA102Mode;
};
#endif // LEDEVICETADALIGHT_H

View File

@ -5,9 +5,12 @@ LedDeviceAtmo::LedDeviceAtmo(const QJsonObject &deviceConfig)
: ProviderRs232()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceAtmo::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceAtmo(deviceConfig);
@ -15,13 +18,13 @@ LedDevice* LedDeviceAtmo::construct(const QJsonObject &deviceConfig)
bool LedDeviceAtmo::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderRs232::init(deviceConfig);
bool isInitOK = false;
if ( isInitOK )
// Initialise sub-class
if ( ProviderRs232::init(deviceConfig) )
{
if (_ledCount != 5)
{
//Error( _log, "%d channels configured. This should always be 5!", _ledCount);
QString errortext = QString ("%1 channels configured. This should always be 5!").arg(_ledCount);
this->setInError(errortext);
isInitOK = false;
@ -33,6 +36,8 @@ bool LedDeviceAtmo::init(const QJsonObject &deviceConfig)
_ledBuffer[1] = 0x00; // StartChannel(Low)
_ledBuffer[2] = 0x00; // StartChannel(High)
_ledBuffer[3] = 0x0F; // Number of Databytes send (always! 15)
isInitOK = true;
}
}
return isInitOK;

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICEATMO_H
#define LEDEVICEATMO_H
// hyperion includes
#include "ProviderRs232.h"
@ -9,24 +10,35 @@
class LedDeviceAtmo : public ProviderRs232
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an Atmo LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceAtmo(const QJsonObject &deviceConfig);
/// constructs leddevice
///
/// @brief Destructor of the LedDevice
///
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;
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<ColorRgb> &ledValues) override;
};
#endif // LEDEVICEATMO_H

View File

@ -1,4 +1,5 @@
#include "LedDeviceDMX.h"
#include <QSerialPort>
#ifndef _WIN32
#include <time.h>
@ -13,9 +14,12 @@ LedDeviceDMX::LedDeviceDMX(const QJsonObject &deviceConfig)
, _dmxChannelCount(0)
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceDMX::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceDMX(deviceConfig);
@ -23,18 +27,19 @@ LedDevice* LedDeviceDMX::construct(const QJsonObject &deviceConfig)
bool LedDeviceDMX::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderRs232::init(deviceConfig);
bool isInitOK = false;
if ( isInitOK )
// Initialise sub-class
if ( ProviderRs232::init(deviceConfig) )
{
QString dmxString = deviceConfig["dmxdevice"].toString("invalid");
if (dmxString == "raw")
QString dmxTypeString = deviceConfig["dmxtype"].toString("invalid");
if (dmxTypeString == "raw")
{
_dmxDeviceType = 0;
_dmxStart = 1;
_dmxSlotsPerLed = 3;
}
else if (dmxString == "McCrypt")
else if (dmxTypeString == "McCrypt")
{
_dmxDeviceType = 1;
_dmxStart = 1;
@ -43,12 +48,12 @@ bool LedDeviceDMX::init(const QJsonObject &deviceConfig)
else
{
//Error(_log, "unknown dmx device type %s", QSTRING_CSTR(dmxString));
QString errortext = QString ("unknown dmx device type: %1").arg(dmxString);
QString errortext = QString ("unknown dmx device type: %1").arg(dmxTypeString);
this->setInError(errortext);
return false;
}
Debug(_log, "_dmxString \"%s\", _dmxDeviceType %d", QSTRING_CSTR(dmxString), _dmxDeviceType );
Debug(_log, "_dmxTypeString \"%s\", _dmxDeviceType %d", QSTRING_CSTR(dmxTypeString), _dmxDeviceType );
_rs232Port.setStopBits(QSerialPort::TwoStop);
_dmxLedCount = qMin(static_cast<int>(_ledCount), 512/_dmxSlotsPerLed);
@ -59,6 +64,8 @@ bool LedDeviceDMX::init(const QJsonObject &deviceConfig)
_ledBuffer.resize(_dmxChannelCount, 0);
_ledBuffer[0] = 0x00; // NULL START code
isInitOK = true;
}
return isInitOK;
}

View File

@ -1,37 +1,53 @@
#pragma once
#ifndef LEDEVICEDMX_H
#define LEDEVICEDMX_H
// hyperion includes
#include "ProviderRs232.h"
///
/// Implementation of the LedDevice interface for writing to DMX512 rs232 led device.
/// Implementation of the LedDevice interface for writing to DMX512 rs232 LED-device.
///
class LedDeviceDMX : public ProviderRs232
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a DMX LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceDMX(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:
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @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<ColorRgb> &ledValues) override;
int _dmxDeviceType = 0;
int _dmxStart = 1;
int _dmxSlotsPerLed = 3;
int _dmxLedCount = 0;
unsigned int _dmxChannelCount = 0;
};
#endif // LEDEVICEDMX_H

View File

@ -5,9 +5,9 @@ LedDeviceKarate::LedDeviceKarate(const QJsonObject &deviceConfig)
: ProviderRs232()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
connect(this,SIGNAL(receivedData(QByteArray)),this,SLOT(receivedData(QByteArray)));
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceKarate::construct(const QJsonObject &deviceConfig)
@ -17,9 +17,10 @@ LedDevice* LedDeviceKarate::construct(const QJsonObject &deviceConfig)
bool LedDeviceKarate::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderRs232::init(deviceConfig);
bool isInitOK = false;
if ( isInitOK )
// Initialise sub-class
if ( ProviderRs232::init(deviceConfig) )
{
if (_ledCount != 16)
{
@ -30,7 +31,6 @@ bool LedDeviceKarate::init(const QJsonObject &deviceConfig)
}
else
{
_ledBuffer.resize(4 + _ledCount * 3); // 4-byte header, 3 RGB values
_ledBuffer[0] = 0xAA; // Startbyte
_ledBuffer[1] = 0x12; // Send all Channels in Batch
@ -39,6 +39,8 @@ bool LedDeviceKarate::init(const QJsonObject &deviceConfig)
Debug( _log, "Karatelight header for %d leds: 0x%02x 0x%02x 0x%02x 0x%02x", _ledCount,
_ledBuffer[0], _ledBuffer[1], _ledBuffer[2], _ledBuffer[3] );
isInitOK = true;
}
}
return isInitOK;
@ -62,8 +64,3 @@ int LedDeviceKarate::write(const std::vector<ColorRgb> &ledValues)
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}
void LedDeviceKarate::receivedData(QByteArray data)
{
Debug(_log, ">>received %d bytes data %s", data.size(),data.data());
}

View File

@ -1,36 +1,47 @@
#pragma once
#ifndef LEDEVICEKARATE_H
#define LEDEVICEKARATE_H
// hyperion includes
#include "ProviderRs232.h"
///
/// Implementation of the LedDevice interface for writing to serial device using tpm2 protocol.
/// Implementation of the LedDevice interface for writing to serial device
///
class LedDeviceKarate : public ProviderRs232
{
Q_OBJECT
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a Karate LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceKarate(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:
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
public slots:
void receivedData(QByteArray data);
private:
/// @brief Writes the RGB-Color values to the LEDs.
///
/// Writes the led color values to the led-device
///
/// @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<ColorRgb> &ledValues) override;
};
#endif // LEDEVICEKARATE_H

View File

@ -10,7 +10,9 @@ LedDeviceSedu::LedDeviceSedu(const QJsonObject &deviceConfig)
: ProviderRs232()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceSedu::construct(const QJsonObject &deviceConfig)
@ -20,8 +22,11 @@ LedDevice* LedDeviceSedu::construct(const QJsonObject &deviceConfig)
bool LedDeviceSedu::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderRs232::init(deviceConfig);
bool isInitOK = false;
// Initialise sub-class
if ( ProviderRs232::init(deviceConfig) )
{
std::vector<FrameSpec> frameSpecs{{0xA1, 256}, {0xA2, 512}, {0xB0, 768}, {0xB1, 1536}, {0xB2, 3072} };
for (const FrameSpec& frameSpec : frameSpecs)
@ -37,14 +42,17 @@ bool LedDeviceSedu::init(const QJsonObject &deviceConfig)
}
}
if (_ledBuffer.size() == 0)
if (_ledBuffer.empty())
{
//Warning(_log, "More rgb-channels required then available");
QString errortext = "More rgb-channels required then available";
this->setInError(errortext);
isInitOK = false;
}
else
{
isInitOK = true;
}
}
return isInitOK;
}

View File

@ -1,32 +1,47 @@
#pragma once
#ifndef LEDEVICESEDU_H
#define LEDEVICESEDU_H
// hyperion includes
#include "ProviderRs232.h"
///
/// Implementation of the LedDevice interface for writing to SEDU led device.
/// Implementation of the LedDevice interface for writing to SEDU LED-device.
///
class LedDeviceSedu : public ProviderRs232
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a SEDU LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceSedu(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:
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @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<ColorRgb> &ledValues) override;
};
#endif // LEDEVICESEDU_H

View File

@ -5,9 +5,12 @@ LedDeviceTpm2::LedDeviceTpm2(const QJsonObject &deviceConfig)
: ProviderRs232()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceTpm2::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceTpm2(deviceConfig);
@ -15,7 +18,11 @@ LedDevice* LedDeviceTpm2::construct(const QJsonObject &deviceConfig)
bool LedDeviceTpm2::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderRs232::init(deviceConfig);
bool isInitOK = false;
// Initialise sub-class
if ( ProviderRs232::init(deviceConfig) )
{
_ledBuffer.resize(5 + _ledRGBCount);
_ledBuffer[0] = 0xC9; // block-start byte
@ -24,6 +31,8 @@ bool LedDeviceTpm2::init(const QJsonObject &deviceConfig)
_ledBuffer[3] = _ledRGBCount & 0xFF; // frame size low byte
_ledBuffer.back() = 0x36; // block-end byte
isInitOK = true;
}
return isInitOK;
}

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICETPM2_H
#define LEDEVICETPM2_H
// hyperion includes
#include "ProviderRs232.h"
@ -9,24 +10,38 @@
class LedDeviceTpm2 : public ProviderRs232
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a TPM 2 LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceTpm2(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:
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @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<ColorRgb> &ledValues) override;
};
#endif // LEDEVICETPM2_H

View File

@ -1,290 +1,297 @@
// STL includes
#include <cstring>
#include <iostream>
// Qt includes
#include <QTimer>
#include <QDateTime>
#include <QFile>
#include <QSerialPortInfo>
// Local Hyperion includes
// LedDevice includes
#include <leddevice/LedDevice.h>
#include "ProviderRs232.h"
// qt includes
#include <QSerialPortInfo>
#include <QEventLoop>
#include <chrono>
// Constants
constexpr std::chrono::milliseconds WRITE_TIMEOUT{1000}; // device write timeout in ms
constexpr std::chrono::milliseconds OPEN_TIMEOUT{5000}; // device open timeout in ms
const int MAX_WRITE_TIMEOUTS = 5; // maximum number of allowed timeouts
const int NUM_POWEROFF_WRITE_BLACK = 2; // Number of write "BLACK" during powering off
ProviderRs232::ProviderRs232()
: _rs232Port(this)
, _writeTimeout(this)
, _blockedForDelay(false)
, _stateChanged(true)
, _bytesToWrite(0)
,_baudRate_Hz(1000000)
,_isAutoDeviceName(false)
,_delayAfterConnect_ms(0)
,_frameDropCounter(0)
, _lastError(QSerialPort::NoError)
, _preOpenDelayTimeOut(0)
, _preOpenDelay(2000)
, _enableAutoDeviceName(false)
{
connect(&_rs232Port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(error(QSerialPort::SerialPortError)));
connect(&_rs232Port, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64)));
connect(&_rs232Port, SIGNAL(readyRead()), this, SLOT(readyRead()));
_writeTimeout.setInterval(5000);
_writeTimeout.setSingleShot(true);
connect(&_writeTimeout, SIGNAL(timeout()), this, SLOT(writeTimeout()));
}
bool ProviderRs232::init(const QJsonObject &deviceConfig)
{
closeDevice();
bool isInitOK = false;
bool isInitOK = LedDevice::init(deviceConfig);
// Initialise sub-class
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());
_deviceName = deviceConfig["output"].toString("auto");
_enableAutoDeviceName = _deviceName == "auto";
// If device name was given as unix /dev/ system-location, get port name
if ( _deviceName.startsWith(QLatin1String("/dev/")) )
_deviceName = _deviceName.mid(5);
_isAutoDeviceName = _deviceName.toLower() == "auto";
_baudRate_Hz = deviceConfig["rate"].toInt();
_delayAfterConnect_ms = deviceConfig["delayAfterConnect"].toInt(1500);
_preOpenDelay = deviceConfig["delayBeforeConnect"].toInt(1500);
Debug(_log, "deviceName : %s", QSTRING_CSTR(_deviceName));
Debug(_log, "AutoDevice : %d", _isAutoDeviceName);
Debug(_log, "baudRate_Hz : %d", _baudRate_Hz);
Debug(_log, "delayAfCon ms: %d", _delayAfterConnect_ms);
isInitOK = true;
}
return isInitOK;
}
void ProviderRs232::close()
{
LedDevice::close();
// LedDevice specific closing activites
closeDevice();
}
QString ProviderRs232::findSerialDevice()
{
// take first available usb serial port - currently no probing!
for( auto port : QSerialPortInfo::availablePorts())
{
if (port.hasProductIdentifier() && port.hasVendorIdentifier() && !port.isBusy())
{
Info(_log, "found serial device: %s", port.systemLocation().toLocal8Bit().constData());
return port.systemLocation();
}
}
return "";
}
void ProviderRs232::bytesWritten(qint64 bytes)
{
_bytesToWrite -= bytes;
if (_bytesToWrite <= 0)
{
_blockedForDelay = false;
_writeTimeout.stop();
}
}
void ProviderRs232::readyRead()
{
emit receivedData(_rs232Port.readAll());
//Debug(_log, "received data");
}
void ProviderRs232::error(QSerialPort::SerialPortError error)
{
if ( error != QSerialPort::NoError )
{
if (_lastError != error)
{
_lastError = error;
switch (error)
{
case QSerialPort::DeviceNotFoundError:
Error(_log, "An error occurred while attempting to open an non-existing device."); break;
case QSerialPort::PermissionError:
Error(_log, "An error occurred while attempting to open an already opened device by another process or a user not having enough permission and credentials to open. Device disabled.");
_deviceReady = false;
break;
case QSerialPort::OpenError:
Error(_log, "An error occurred while attempting to open an already opened device in this object."); break;
case QSerialPort::NotOpenError:
Error(_log, "This error occurs when an operation is executed that can only be successfully performed if the device is open."); break;
case QSerialPort::ParityError:
Error(_log, "Parity error detected by the hardware while reading data."); break;
case QSerialPort::FramingError:
Error(_log, "Framing error detected by the hardware while reading data."); break;
case QSerialPort::BreakConditionError:
Error(_log, "Break condition detected by the hardware on the input line."); break;
case QSerialPort::WriteError:
Error(_log, "An I/O error occurred while writing the data."); break;
case QSerialPort::ReadError:
Error(_log, "An I/O error occurred while reading the data."); break;
case QSerialPort::ResourceError:
Error(_log, "An I/O error occurred when a resource becomes unavailable, e.g. when the device is unexpectedly removed from the system."); break;
case QSerialPort::UnsupportedOperationError:
Error(_log, "The requested device operation is not supported or prohibited by the running operating system. Device disabled.");
_deviceReady = false;
break;
case QSerialPort::TimeoutError:
Error(_log, "A timeout error occurred."); break;
default:
Error(_log,"An unidentified error occurred. Device disabled. (%d)", error);
_deviceReady = false;
}
_rs232Port.clearError();
this->setInError( "Rs232 SerialPortError, see details in previous log lines!" );
closeDevice();
}
}
}
ProviderRs232::~ProviderRs232()
{
disconnect(&_rs232Port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(error(QSerialPort::SerialPortError)));
}
void ProviderRs232::closeDevice()
{
_writeTimeout.stop();
if (_rs232Port.isOpen())
{
_rs232Port.close();
Debug(_log,"Close UART: %s", _deviceName.toLocal8Bit().constData());
}
_stateChanged = true;
_bytesToWrite = 0;
_blockedForDelay = false;
_deviceReady = false;
}
int ProviderRs232::open()
{
int retval = -1;
_deviceReady = false;
_isDeviceReady = false;
_isInSwitchOff = false;
// General initialisation and configuration of LedDevice
if ( init(_devConfig) )
{
// open device physically
if ( tryOpen(_delayAfterConnect_ms) )
{
// Everything is OK -> enable device
_deviceReady = true;
setEnable(true);
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
else
{
this->setInError( "Error opening device!" );
return retval;
}
int ProviderRs232::close()
{
int retval = 0;
_isDeviceReady = false;
// Test, if device requires closing
if (_rs232Port.isOpen())
{
if ( _rs232Port.flush() )
{
Debug(_log,"Flush was successful");
}
Debug(_log,"Close UART: %s", QSTRING_CSTR(_deviceName) );
_rs232Port.close();
// Everything is OK -> device is closed
}
return retval;
}
bool ProviderRs232::powerOff()
{
// Simulate power-off by writing a final "Black" to have a defined outcome
bool rc = false;
if ( writeBlack( NUM_POWEROFF_WRITE_BLACK ) >= 0 )
{
rc = true;
}
return rc;
}
bool ProviderRs232::tryOpen(const int delayAfterConnect_ms)
{
if (_deviceName.isEmpty() || _rs232Port.portName().isEmpty())
{
if ( _enableAutoDeviceName )
if (!_rs232Port.isOpen())
{
_deviceName = findSerialDevice();
if ( _isAutoDeviceName )
{
_deviceName = discoverFirst();
if (_deviceName.isEmpty())
{
this->setInError( QString("No serial device found automatically!") );
return false;
}
}
Info(_log, "Opening UART: %s", _deviceName.toLocal8Bit().constData());
}
_rs232Port.setPortName(_deviceName);
}
if (!_rs232Port.isOpen())
{
Info(_log, "Opening UART: %s", QSTRING_CSTR(_deviceName));
_frameDropCounter = 0;
_rs232Port.setBaudRate( _baudRate_Hz );
Debug(_log, "_rs232Port.open(QIODevice::WriteOnly): %s, Baud rate [%d]bps", QSTRING_CSTR(_deviceName), _baudRate_Hz);
QSerialPortInfo serialPortInfo(_deviceName);
QJsonObject portInfo;
Debug(_log, "portName: %s", QSTRING_CSTR(serialPortInfo.portName()));
Debug(_log, "systemLocation: %s", QSTRING_CSTR(serialPortInfo.systemLocation()));
Debug(_log, "description: %s", QSTRING_CSTR(serialPortInfo.description()));
Debug(_log, "manufacturer: %s", QSTRING_CSTR(serialPortInfo.manufacturer()));
Debug(_log, "productIdentifier: %s", QSTRING_CSTR(QString("0x%1").arg(serialPortInfo.productIdentifier(), 0, 16)));
Debug(_log, "vendorIdentifier: %s", QSTRING_CSTR(QString("0x%1").arg(serialPortInfo.vendorIdentifier(), 0, 16)));
Debug(_log, "serialNumber: %s", QSTRING_CSTR(serialPortInfo.serialNumber()));
if (!serialPortInfo.isNull() )
{
if ( _preOpenDelayTimeOut > QDateTime::currentMSecsSinceEpoch() )
if ( !_rs232Port.open(QIODevice::WriteOnly) )
{
this->setInError(_rs232Port.errorString());
return false;
}
if ( ! _rs232Port.open(QIODevice::ReadWrite) )
{
if ( _stateChanged )
{
Error(_log, "Unable to open RS232 device (%s)", _deviceName.toLocal8Bit().constData());
_stateChanged = false;
}
return false;
}
Debug(_log, "Setting baud rate to %d", _baudRate_Hz);
_rs232Port.setBaudRate(_baudRate_Hz);
_stateChanged = true;
_preOpenDelayTimeOut = 0;
}
else
{
QString errortext = QString("Invalid serial device name: [%1]!").arg(_deviceName);
this->setInError( errortext );
_preOpenDelayTimeOut = QDateTime::currentMSecsSinceEpoch() + _preOpenDelay;
return false;
}
}
if (delayAfterConnect_ms > 0)
{
_blockedForDelay = true;
QTimer::singleShot(delayAfterConnect_ms, this, SLOT(unblockAfterDelay()));
Debug(_log, "Device blocked for %d ms", delayAfterConnect_ms);
Debug(_log, "delayAfterConnect for %d ms - start", delayAfterConnect_ms);
// Wait delayAfterConnect_ms before allowing write
QEventLoop loop;
QTimer::singleShot( delayAfterConnect_ms, &loop, SLOT( quit() ) );
loop.exec();
Debug(_log, "delayAfterConnect for %d ms - finished", delayAfterConnect_ms);
}
return _rs232Port.isOpen();
}
void ProviderRs232::setInError(const QString& errorMsg)
{
_rs232Port.clearError();
this->close();
LedDevice::setInError( errorMsg );
}
int ProviderRs232::writeBytes(const qint64 size, const uint8_t *data)
{
if (! _blockedForDelay)
{
DebugIf(_isInSwitchOff, _log, "_inClosing [%d], enabled [%d], _deviceReady [%d], _frameDropCounter [%d]", _isInSwitchOff, this->isEnabled(), _isDeviceReady, _frameDropCounter);
int rc = 0;
if (!_rs232Port.isOpen())
{
return tryOpen(5000) ? 0 : -1;
Debug(_log, "!_rs232Port.isOpen()");
if ( !tryOpen(OPEN_TIMEOUT.count()) )
{
return -1;
}
}
if (_frameDropCounter > 5)
{
Debug(_log, "%d frames dropped", _frameDropCounter);
}
_frameDropCounter = 0;
_blockedForDelay = true;
_bytesToWrite = size;
DebugIf( _isInSwitchOff, _log, "[%s]", QSTRING_CSTR(uint8_t_to_hex_string(data, size, 32)) );
qint64 bytesWritten = _rs232Port.write(reinterpret_cast<const char*>(data), size);
if (bytesWritten == -1 || bytesWritten != size)
{
Warning(_log,"failed writing data");
QTimer::singleShot(500, this, SLOT(unblockAfterDelay()));
return -1;
}
_writeTimeout.start();
this->setInError( QString ("Rs232 SerialPortError: %1").arg(_rs232Port.errorString()) );
rc = -1;
}
else
{
_frameDropCounter++;
}
return 0;
}
void ProviderRs232::writeTimeout()
if (!_rs232Port.waitForBytesWritten(WRITE_TIMEOUT.count()))
{
//Error(_log, "Timeout on write data to %s", _deviceName.toLocal8Bit().constData());
QString errortext = QString ("Timeout on write data to %1").arg(_deviceName);
setInError( errortext );
close();
if ( _rs232Port.error() == QSerialPort::TimeoutError )
{
Debug(_log, "Timeout after %dms: %d frames already dropped", WRITE_TIMEOUT, _frameDropCounter);
++_frameDropCounter;
// Check,if number of timeouts in a given time frame is greater than defined
// TODO: ProviderRs232::writeBytes - Add time frame to check for timeouts that devices does not close after absolute number of timeouts
if ( _frameDropCounter > MAX_WRITE_TIMEOUTS )
{
this->setInError( QString ("Timeout writing data to %1").arg(_deviceName) );
rc = -1;
}
else
{
//give it another try
_rs232Port.clearError();
}
}
else
{
this->setInError( QString ("Rs232 SerialPortError: %1").arg(_rs232Port.errorString()) );
rc = -1;
}
}
else
{
DebugIf(_isInSwitchOff,_log, "In Closing: bytesWritten [%d], _rs232Port.error() [%d], %s", bytesWritten, _rs232Port.error(), _rs232Port.error() == QSerialPort::NoError ? "No Error" : QSTRING_CSTR(_rs232Port.errorString()) );
}
}
void ProviderRs232::unblockAfterDelay()
{
_blockedForDelay = false;
DebugIf(_isInSwitchOff, _log, "[%d], _inClosing[%d], enabled [%d], _deviceReady [%d]", rc, _isInSwitchOff, this->isEnabled(), _isDeviceReady);
return rc;
}
QString ProviderRs232::discoverFirst()
{
// take first available USB serial port - currently no probing!
for (auto const & port : QSerialPortInfo::availablePorts())
{
if (!port.isNull() && !port.isBusy())
{
Info(_log, "found serial device: %s", QSTRING_CSTR(port.portName()));
return port.portName();
}
}
return "";
}
QJsonObject ProviderRs232::discover()
{
QJsonObject devicesDiscovered;
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
QJsonArray deviceList;
// Discover serial Devices
for (auto &port : QSerialPortInfo::availablePorts() )
{
if ( !port.isNull() )
{
QJsonObject portInfo;
portInfo.insert("description", port.description());
portInfo.insert("manufacturer", port.manufacturer());
portInfo.insert("portName", port.portName());
portInfo.insert("productIdentifier", QString("0x%1").arg(port.productIdentifier(), 0, 16));
portInfo.insert("serialNumber", port.serialNumber());
portInfo.insert("systemLocation", port.systemLocation());
portInfo.insert("vendorIdentifier", QString("0x%1").arg(port.vendorIdentifier(), 0, 16));
deviceList.append(portInfo);
}
}
devicesDiscovered.insert("devices", deviceList);
return devicesDiscovered;
}

View File

@ -1,13 +1,12 @@
#pragma once
#include <QObject>
#include <QSerialPort>
#include <QTimer>
#include <QString>
#ifndef PROVIDERRS232_H
#define PROVIDERRS232_H
// LedDevice includes
#include <leddevice/LedDevice.h>
// qt includes
#include <QSerialPort>
///
/// The ProviderRs232 implements an abstract base-class for LedDevices using a RS232-device.
///
@ -16,91 +15,106 @@ class ProviderRs232 : public LedDevice
Q_OBJECT
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a RS232 LED-device
///
ProviderRs232();
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// Destructor of the LedDevice; closes the output device if it is open
/// @brief Destructor of the UDP LED-device
///
virtual ~ProviderRs232() override;
///
/// Opens and configures the output device
///
/// @return Zero on succes else negative
///
int open() override;
public slots:
///
/// Closes the output device.
/// Includes switching-off the device and stopping refreshes
///
virtual void close() override;
private slots:
/// Unblock the device after a connection delay
void writeTimeout();
void unblockAfterDelay();
void error(QSerialPort::SerialPortError setInError);
void bytesWritten(qint64 bytes);
void readyRead();
signals:
void receivedData(QByteArray data);
protected:
/**
* Writes the given bytes to the RS232-device and
*
* @param[in[ size The length of the data
* @param[in] data The data
*
* @return Zero on success else negative
*/
///
/// @brief Initialise the RS232 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 UDP device.
///
/// @return Zero on success (i.e. device is closed), else negative
///
virtual int close() override;
///
/// @brief Power-/turn off a RS232-device
///
/// The off-state is simulated by writing "Black to LED"
///
/// @return True, if success
///
virtual bool powerOff() override;
///
/// @brief Discover first devices of a serial device available (for configuration)
///
/// @return A string of the device found
///
virtual QString discoverFirst() override;
///
/// @brief Discover RS232 serial devices available (for configuration).
///
/// @return A JSON structure holding a list of devices found
///
virtual QJsonObject discover() override;
///
/// @brief Write the given bytes to the RS232-device
///
/// @param[in[ size The length of the data
/// @param[in] data The data
/// @return Zero on success, else negative
///
int writeBytes(const qint64 size, const uint8_t *data);
void closeDevice();
QString findSerialDevice();
// tries to open device if not opened
bool tryOpen(const int delayAfterConnect_ms);
/// The name of the output device
QString _deviceName;
/// The used baudrate of the output device
/// The RS232 serial-device
QSerialPort _rs232Port;
/// The used baud-rate of the output device
qint32 _baudRate_Hz;
protected slots:
///
/// @brief Set device in error state
///
/// @param errorMsg The error message to be logged
///
virtual void setInError( const QString& errorMsg) override;
private:
///
/// @brief Try to open device if not opened
///
/// @return True,if on success
///
bool tryOpen(const int delayAfterConnect_ms);
/// Try to auto-discover device name?
bool _isAutoDeviceName;
/// Sleep after the connect before continuing
int _delayAfterConnect_ms;
/// The RS232 serial-device
QSerialPort _rs232Port;
/// A timeout timer for the asynchronous connection
QTimer _writeTimeout;
bool _blockedForDelay;
bool _stateChanged;
qint64 _bytesToWrite;
qint64 _frameDropCounter;
QSerialPort::SerialPortError _lastError;
qint64 _preOpenDelayTimeOut;
int _preOpenDelay;
bool _enableAutoDeviceName;
/// Frames dropped, as write failed
int _frameDropCounter;
};
#endif // PROVIDERRS232_H

View File

@ -4,7 +4,9 @@ LedDeviceAPA102::LedDeviceAPA102(const QJsonObject &deviceConfig)
: ProviderSpi()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceAPA102::construct(const QJsonObject &deviceConfig)
@ -14,10 +16,12 @@ LedDevice* LedDeviceAPA102::construct(const QJsonObject &deviceConfig)
bool LedDeviceAPA102::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderSpi::init(deviceConfig);
bool isInitOK = false;
if ( isInitOK )
// Initialise sub-class
if ( ProviderSpi::init(deviceConfig) )
{
const unsigned int startFrameSize = 4;
const unsigned int endFrameSize = qMax<unsigned int>(((_ledCount + 15) / 16), 4);
const unsigned int APAbufferSize = (_ledCount * 4) + startFrameSize + endFrameSize;
@ -27,6 +31,9 @@ bool LedDeviceAPA102::init(const QJsonObject &deviceConfig)
_ledBuffer[1] = 0x00;
_ledBuffer[2] = 0x00;
_ledBuffer[3] = 0x00;
isInitOK = true;
}
return isInitOK;
}

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICEAPA102_H
#define LEDEVICEAPA102_H
// hyperion includes
#include "ProviderSpi.h"
@ -9,26 +10,39 @@
class LedDeviceAPA102 : public ProviderSpi
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an APA102 LED-device
///
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceAPA102(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
virtual bool init(const QJsonObject &deviceConfig) override;
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 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<ColorRgb> & ledValues) override;
};
#endif // LEDEVICEAPA102_H

View File

@ -46,9 +46,12 @@ LedDeviceAPA104::LedDeviceAPA104(const QJsonObject &deviceConfig)
}
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceAPA104::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceAPA104(deviceConfig);
@ -58,12 +61,16 @@ bool LedDeviceAPA104::init(const QJsonObject &deviceConfig)
{
_baudRate_Hz = 2235000;
bool isInitOK = ProviderSpi::init(deviceConfig);
if ( isInitOK )
bool isInitOK = false;
// Initialise sub-class
if ( ProviderSpi::init(deviceConfig) )
{
WarningIf(( _baudRate_Hz < 2000000 || _baudRate_Hz > 2470000 ), _log, "SPI rate %d outside recommended range (2000000 -> 2470000)", _baudRate_Hz);
_ledBuffer.resize(_ledRGBCount * SPI_BYTES_PER_COLOUR + SPI_FRAME_END_LATCH_BYTES, 0x00);
isInitOK = true;
}
return isInitOK;
}

View File

@ -1,6 +1,7 @@
#pragma once
#ifndef LEDEVICEAPA104_H
#define LEDEVICEAPA104_H
// hyperion inclusdes
// hyperion includes
#include "ProviderSpi.h"
///
@ -9,29 +10,36 @@
class LedDeviceAPA104 : public ProviderSpi
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an APA104 LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceAPA104(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<ColorRgb> & ledValues) override;
@ -40,3 +48,5 @@ private:
uint8_t bitpair_to_byte[4];
};
#endif // LEDEVICEAPA104_H

View File

@ -4,7 +4,9 @@ LedDeviceLpd6803::LedDeviceLpd6803(const QJsonObject &deviceConfig)
: ProviderSpi()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceLpd6803::construct(const QJsonObject &deviceConfig)
@ -14,12 +16,16 @@ LedDevice* LedDeviceLpd6803::construct(const QJsonObject &deviceConfig)
bool LedDeviceLpd6803::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderSpi::init(deviceConfig);
if ( isInitOK )
bool isInitOK = false;
// Initialise sub-class
if ( ProviderSpi::init(deviceConfig) )
{
unsigned messageLength = 4 + 2*_ledCount + _ledCount/8 + 1;
// Initialise the buffer
_ledBuffer.resize(messageLength, 0x00);
isInitOK = true;
}
return isInitOK;
}

View File

@ -1,45 +1,55 @@
#pragma once
#ifndef LEDEVICELPD6803_H
#define LEDEVICELPD6803_H
// Local hyperion includes
#include "ProviderSpi.h"
///
/// Implementation of the LedDevice interface for writing to LDP6803 led device.
/// Implementation of the LedDevice interface for writing to LDP6803 LED-device.
///
/// 00000000 00000000 00000000 00000000 1RRRRRGG GGGBBBBB 1RRRRRGG GGGBBBBB ...
/// |---------------------------------| |---------------| |---------------|
/// 32 zeros to start the frame Led1 Led2 ...
/// 32 zeros to start the frame LED1 LED2 ...
///
/// For each led, the first bit is always 1, and then you have 5 bits each for red, green and blue
/// (R, G and B in the above illustration) making 16 bits per led. Total bytes = 4 + (2 x number of
/// leds)
/// (R, G and B in the above illustration) making 16 bits per led. Total bytes = 4 + (2 x number of LEDs)
///
class LedDeviceLpd6803 : public ProviderSpi
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a LDP6803 LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceLpd6803(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<ColorRgb> & ledValues) override;
};
#endif // LEDEVICELPD6803_H

View File

@ -4,7 +4,9 @@ LedDeviceLpd8806::LedDeviceLpd8806(const QJsonObject &deviceConfig)
: ProviderSpi()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceLpd8806::construct(const QJsonObject &deviceConfig)
@ -14,13 +16,18 @@ LedDevice* LedDeviceLpd8806::construct(const QJsonObject &deviceConfig)
bool LedDeviceLpd8806::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderSpi::init(deviceConfig);
bool isInitOK = false;
// Initialise sub-class
if ( ProviderSpi::init(deviceConfig) )
{
const unsigned clearSize = _ledCount/32+1;
unsigned messageLength = _ledRGBCount + clearSize;
// Initialise the buffer
_ledBuffer.resize(messageLength, 0x00);
isInitOK = true;
}
return isInitOK;
}

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICELPD8806_H
#define LEDEVICELPD8806_H
// Local hyperion includes
#include "ProviderSpi.h"
@ -32,7 +33,7 @@
/// applications. The 'subsequent' rule also means that at least one extra
/// byte must follow the last pixel, in order for the final blue LED to latch.
///
/// To reset the pass-through behavior and begin sending new data to the start
/// To reset the pass-through behaviour and begin sending new data to the start
/// of the strip, a number of zero bytes must be issued (remember, all color
/// data bytes have the high bit set, thus are in the range 128 to 255, so the
/// zero is 'special'). This should be done before each full payload of color
@ -69,7 +70,7 @@
/// Tested. Confirmed. Fact.
///
///
/// The summary of the story is that the following needs to be writen on the spi-device:
/// The summary of the story is that the following needs to be written on the spi-device:
/// 1RRRRRRR 1GGGGGGG 1BBBBBBB 1RRRRRRR 1GGGGGGG ... ... 1GGGGGGG 1BBBBBBB 00000000 00000000 ...
/// |---------led_1----------| |---------led_2-- -led_n----------| |----clear data--
///
@ -78,29 +79,39 @@
class LedDeviceLpd8806 : public ProviderSpi
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a LDP8806 LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceLpd8806(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<ColorRgb> & ledValues) override;
};
#endif // LEDEVICELPD8806_H

View File

@ -4,7 +4,9 @@ LedDeviceP9813::LedDeviceP9813(const QJsonObject &deviceConfig)
: ProviderSpi()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceP9813::construct(const QJsonObject &deviceConfig)
@ -14,10 +16,13 @@ LedDevice* LedDeviceP9813::construct(const QJsonObject &deviceConfig)
bool LedDeviceP9813::init(const QJsonObject &deviceConfig)
{
bool isInitOK = ProviderSpi::init(deviceConfig);
if ( isInitOK )
bool isInitOK = false;
// Initialise sub-class
if ( ProviderSpi::init(deviceConfig) )
{
_ledBuffer.resize(_ledCount * 4 + 8, 0x00);
isInitOK = true;
}
return isInitOK;
}

View File

@ -1,45 +1,54 @@
#pragma once
#ifndef LEDEVICEP9813_H
#define LEDEVICEP9813_H
// hyperion includes
#include "ProviderSpi.h"
///
/// Implementation of the LedDevice interface for writing to P9813 led device.
/// Implementation of the LedDevice interface for writing to P9813 LED-device.
///
class LedDeviceP9813 : public ProviderSpi
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a P9813 LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceP9813(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<ColorRgb> & ledValues) override;
///
/// Calculates the required checksum for one led
/// Calculates the required checksum for one LED
///
/// @param color The color of the led
/// @return The checksum for the led
///
uint8_t calculateChecksum(const ColorRgb & color) const;
};
#endif // LEDEVICEP9813_H

View File

@ -12,7 +12,9 @@
}
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceSk6812SPI::construct(const QJsonObject &deviceConfig)
@ -24,8 +26,10 @@ bool LedDeviceSk6812SPI::init(const QJsonObject &deviceConfig)
{
_baudRate_Hz = 3000000;
bool isInitOK = ProviderSpi::init(deviceConfig);
if ( isInitOK )
bool isInitOK = false;
// Initialise sub-class
if ( ProviderSpi::init(deviceConfig) )
{
QString whiteAlgorithm = deviceConfig["whiteAlgorithm"].toString("white_off");
@ -44,6 +48,8 @@ bool LedDeviceSk6812SPI::init(const QJsonObject &deviceConfig)
const int SPI_FRAME_END_LATCH_BYTES = 3;
_ledBuffer.resize(_ledRGBWCount * SPI_BYTES_PER_COLOUR + SPI_FRAME_END_LATCH_BYTES, 0x00);
isInitOK = true;
}
}
return isInitOK;

Some files were not shown because too many files have changed in this diff Show More