#ifndef LEDEVICEYEELIGHT_H #define LEDEVICEYEELIGHT_H // LedDevice includes #include // Qt includes #include #include #include #include #include // Constants namespace { // List of State Information const char API_METHOD_POWER[] = "set_power"; const char API_METHOD_POWER_ON[] = "on"; const char API_METHOD_POWER_OFF[] = "off"; const char API_METHOD_MUSIC_MODE[] = "set_music"; const int API_METHOD_MUSIC_MODE_ON = 1; const int API_METHOD_MUSIC_MODE_OFF = 0; const char API_METHOD_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, }; API_REPLY error() const { 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(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 /// @param[in] ignoreErrors Return success, even if errors occured /// @return 0: success, -1: error, -2: command quota exceeded /// int writeCommand( const QJsonDocument &command, bool ignoreErrors = false ); /// /// @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 /// @param[in] ignoreErrors Return success, even if errors occured /// @return 0: success, -1: error, -2: command quota exceeded /// int writeCommand( const QJsonDocument &command, QJsonArray &result, bool ignoreErrors = false ); /// /// @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 /// bool restoreState(); /// /// @brief Check, if light was originally powered on before hyperion color streaming started.. /// /// @return True, if light was on at start /// bool wasOriginallyOn() const { return _power == API_METHOD_POWER_ON ? true : false; } /// /// @brief Check, if the Yeelight light is ready for updates /// /// @return True, if ready /// bool isReady() const { return !_isInError; } /// /// @brief Check, if the Yeelight light is powered on /// /// @return True, if powered on /// bool isOn() const { return _isOn; } /// /// @brief Check, if the Yeelight light is in music-mode /// /// @return True, if in music mode /// bool isInMusicMode( bool deviceCheck = false ); /// /// @brief Set the Yeelight light in error state /// /// @param[in] errorMsg The error message to be logged /// void setInError( const QString& errorMsg ); /// /// @brief Set the Yeelight light debug-level /// /// @param[in] level Debug level (0: no debug output, 1-3: verbosity level) /// void setDebuglevel ( int level ) { _debugLevel = level; } private: YeelightResponse handleResponse(int correlationID, QByteArray const &response ); /// /// @brief Build Yeelight-API command /// /// @param[in] method Control method to be invoked /// @param[in] params Parameters for control method /// @return Yeelight-API command in JSON format /// QJsonDocument getCommand(const QString &method, const QJsonArray ¶ms); /// /// @brief Map Yeelight light properties into the Yeelight light members for direct access /// /// @param[in] properties Yeelight light's properties as JSON-Object /// void mapProperties(const QJsonObject &properties); /// /// @brief Write a Yeelight light specific log-line for debugging purposed /// /// @param[in] logLevel Debug level (0: no debug output, 1-3: verbosity level) /// @param[in] msg Log message prefix (max 20 characters) /// @param[in] type log message text /// @param[in] ... variable input to log message text /// /// void log(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 /// ~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); /// @param[in] params Parameters used to overwrite discovery default behaviour /// /// @return A JSON structure holding a list of devices found /// QJsonObject discover(const QJsonObject& params) override; /// /// @brief Get a Yeelight device's resource properties /// /// Following parameters are required /// @code /// { /// "host" : "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 /// QJsonObject getProperties(const QJsonObject& params) override; /// /// @brief Send an update to the Yeelight device to identify it. /// /// Following parameters are required /// @code /// { /// "host" : "hostname or IP", /// "port" : port, default port 55443 is used when not provided /// } ///@endcode /// /// @param[in] params Parameters to address device /// void identify(const QJsonObject& params) override; protected: /// /// @brief Initialise the device's configuration /// /// @param[in] deviceConfig the JSON device configuration /// @return True, if success /// bool init(const QJsonObject &deviceConfig) override; /// /// @brief Opens the output device. /// /// @return Zero on success (i.e. device is ready), else negative /// int open() override; /// /// @brief Closes the output device. /// /// @return Zero on success (i.e. device is closed), else negative /// 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 /// int write(const std::vector & ledValues) override; /// /// @brief Power-/turn on the Nanoleaf device. /// /// @brief Store the device's original state. /// bool powerOn() override; /// /// @brief Power-/turn off the Nanoleaf device. /// /// @return True if success /// 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 /// 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 /// bool restoreState() override; private: struct yeelightAddress { QString host; int port; bool operator == (yeelightAddress const& a) const { return ((host == a.host) && (port == a.port)); } }; enum COLOR_MODEL{ MODEL_HSV, MODEL_RGB }; /// /// @brief Start music-mode server /// /// @return True, if music mode server is running /// bool startMusicModeServer(); /// /// @brief Stop music-mode server /// /// @return True, if music mode server has been stopped /// bool stopMusicModeServer(); /// /// @brief Update list of Yeelight lights handled by the LED-device /// /// @param[in] list List of Yeelight lights /// /// @return False, if no lights were provided /// bool updateLights(const QVector &list); /// /// @brief Set the number of Yeelight lights handled by the LED-device /// /// @param[in] lightsCount Number of Yeelight lights /// void setLightsCount( unsigned int lightsCount ) { _lightsCount = lightsCount; } /// /// @brief Get the number of Yeelight lights handled by the LED-device /// /// @return Number of Yeelight lights /// uint getLightsCount() const { return _lightsCount; } /// /// @brief Discover Yeelight devices available (for configuration). /// Yeelight specific UDP Broadcast discovery /// /// @return A JSON structure holding a list of devices found /// QJsonArray discover(); /// Array of the Yeelight addresses handled by the LED-device QVector _lightsAddressList; /// Array to save the lights std::vector _lights; unsigned int _lightsCount; /// Yeelight configuration/behavioural parameters int _outputColorModel; YeelightLight::API_EFFECT _transitionEffect; int _transitionDuration; int _extraTimeDarkness; int _brightnessMin; bool _isBrightnessSwitchOffMinimum; int _brightnessMax; double _brightnessFactor; int _waitTimeQuota; int _debuglevel; ///Music mode Server details QHostAddress _musicModeServerAddress; int _musicModeServerPort; QTcpServer* _tcpMusicModeServer = nullptr; }; #endif // LEDEVICEYEELIGHT_H