mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
628 lines
16 KiB
C++
628 lines
16 KiB
C++
#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() 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
|
|
/// @return 0: success, -1: error, -2: command quota exceeded
|
|
///
|
|
int writeCommand( const QJsonDocument &command );
|
|
|
|
///
|
|
/// @brief Execute a Yeelight-API command
|
|
///
|
|
/// @param[in] command The API command request in JSON
|
|
/// @param[out] result The response to the command in JSON
|
|
/// @return 0: success, -1: error, -2: command quota exceeded
|
|
///
|
|
int writeCommand( const QJsonDocument &command, QJsonArray &result );
|
|
|
|
///
|
|
/// @brief Stream a Yeelight-API command
|
|
///
|
|
/// Yeelight must be in music mode, i.e. Streaming socket is established
|
|
///
|
|
/// @param[in] command The API command request in JSON
|
|
/// @return True, on success
|
|
///
|
|
bool streamCommand( const QJsonDocument &command );
|
|
|
|
///
|
|
/// @brief Set the Yeelight light streaming socket
|
|
///
|
|
/// @param[in] socket
|
|
///
|
|
void setStreamSocket( QTcpSocket* socket );
|
|
|
|
///
|
|
/// @brief Power on/off on the Yeelight light
|
|
///
|
|
/// @param[in] on True: power on, False: power off
|
|
///
|
|
/// @return True, if success
|
|
///
|
|
bool setPower( bool on );
|
|
|
|
///
|
|
/// @brief Power on/off on the Yeelight light
|
|
///
|
|
/// @param[in] on True: power on, False: power off
|
|
/// @param[in] effect Transition effect, sudden or smooth
|
|
/// @param[in] duration Duration of the transition, if smooth
|
|
/// @param[in] mode Color mode after powering on
|
|
///
|
|
/// @return True, if success
|
|
///
|
|
bool setPower( bool on, API_EFFECT effect, int duration, API_MODE mode = API_RGB_MODE );
|
|
|
|
///
|
|
/// @brief Set the Yeelight light to the given color (using RGB mode)
|
|
///
|
|
/// @param[in] color as RGB value
|
|
///
|
|
/// @return True, if success
|
|
///
|
|
bool setColorRGB( const ColorRgb &color );
|
|
|
|
///
|
|
/// @brief Set the Yeelight light to the given color (using HSV mode)
|
|
///
|
|
/// @param[in] color as RGB value
|
|
///
|
|
/// @return True, if success
|
|
///
|
|
bool setColorHSV( const ColorRgb &color );
|
|
|
|
///
|
|
/// @brief Set the Yeelight light effect and duration while transiting between color updates
|
|
///
|
|
/// @param[in] effect Transition effect, sudden or smooth
|
|
/// @param[in] duration Duration of the transition, if smooth
|
|
///
|
|
void setTransitionEffect ( API_EFFECT effect ,int duration = API_PARAM_DURATION.count() );
|
|
|
|
///
|
|
/// @brief Set the Yeelight light brightness configuration behaviour
|
|
///
|
|
/// @param[in] min Minimum Brightness (in %). Every value lower than minimum will be set to minimum.
|
|
/// @param[in] max Maximum Brightness (in %). Every value greater than maximum will be set to maximum.
|
|
/// @param[in] switchoff True, power-off light, if brightness is lower then minimum
|
|
/// @param[in] extraTime Additional time (in ms), which added to transition duration while powering-off
|
|
/// @param[in] factor Brightness factor to multiply on color change.
|
|
///
|
|
void setBrightnessConfig (int min = 1, int max = 100, bool switchoff = false, int extraTime = 0, double factor = 1);
|
|
|
|
///
|
|
/// @brief Set the Yeelight light into music-mode
|
|
///
|
|
/// @param[in] on True: music-mode on, False: music-mode off
|
|
/// @param[in] hostAddress of the music-mode server
|
|
/// @param[in] port of the music-mode server
|
|
///
|
|
bool setMusicMode( bool on, const QHostAddress &hostAddress = {} , int port = -1 );
|
|
|
|
///
|
|
/// @brief Set the wait-time between two Yeelight light commands
|
|
///
|
|
/// The write of a command is delayed by the given wait-time, if the last write happen in the wait-time time frame.
|
|
/// Used to avoid that the Yeelight light runs into the quota exceed error scenario.
|
|
/// A Yeelight light can do 60 commands/min ( -> wait-time = 1000ms).
|
|
///
|
|
/// @param[in] waitTime in milliseconds
|
|
///
|
|
void setQuotaWaitTime( int waitTime ) { _waitTimeQuota = waitTime; }
|
|
|
|
///
|
|
/// @brief Get the Yeelight light properties
|
|
///
|
|
/// @return properties as JSON-object
|
|
///
|
|
QJsonObject getProperties();
|
|
|
|
///
|
|
/// @brief Get the Yeelight light properties and store them along the Yeelight light for later access
|
|
///
|
|
void storeState();
|
|
|
|
///
|
|
/// @brief Restore the Yeelight light's original state.
|
|
///
|
|
/// Restore the device's state as before hyperion color streaming started.
|
|
///
|
|
/// @return True, if success
|
|
///
|
|
virtual bool restoreState();
|
|
|
|
///
|
|
/// @brief Check, if light was originally powered on before hyperion color streaming started..
|
|
///
|
|
/// @return True, if light was on at start
|
|
///
|
|
bool wasOriginallyOn() const { return _power == API_METHOD_POWER_ON ? true : false; }
|
|
|
|
///
|
|
/// @brief Check, if the Yeelight light is ready for updates
|
|
///
|
|
/// @return True, if ready
|
|
///
|
|
bool isReady() const { return !_isInError; }
|
|
|
|
///
|
|
/// @brief Check, if the Yeelight light is powered on
|
|
///
|
|
/// @return True, if powered on
|
|
///
|
|
bool isOn() const { return _isOn; }
|
|
|
|
///
|
|
/// @brief Check, if the Yeelight light is in music-mode
|
|
///
|
|
/// @return True, if in music mode
|
|
///
|
|
bool isInMusicMode( bool deviceCheck = false );
|
|
|
|
///
|
|
/// @brief Set the Yeelight light in error state
|
|
///
|
|
/// @param[in] errorMsg The error message to be logged
|
|
///
|
|
void setInError( const QString& errorMsg );
|
|
|
|
///
|
|
/// @brief Set the Yeelight light debug-level
|
|
///
|
|
/// @param[in] level Debug level (0: no debug output, 1-3: verbosity level)
|
|
///
|
|
void setDebuglevel ( int level ) { _debugLevel = level; }
|
|
|
|
private:
|
|
|
|
YeelightResponse handleResponse(int correlationID, QByteArray const &response );
|
|
|
|
///
|
|
/// @brief Build Yeelight-API command
|
|
///
|
|
/// @param[in] method Control method to be invoked
|
|
/// @param[in] params Parameters for control method
|
|
/// @return Yeelight-API command in JSON format
|
|
///
|
|
QJsonDocument getCommand(const QString &method, const QJsonArray ¶ms);
|
|
|
|
///
|
|
/// @brief Map Yeelight light properties into the Yeelight light members for direct access
|
|
///
|
|
/// @param[in] properties Yeelight light's properties as JSON-Object
|
|
///
|
|
void mapProperties(const QJsonObject &properties);
|
|
|
|
///
|
|
/// @brief Write a Yeelight light specific log-line for debugging purposed
|
|
///
|
|
/// @param[in] logLevel Debug level (0: no debug output, 1-3: verbosity level)
|
|
/// @param[in] msg Log message prefix (max 20 characters)
|
|
/// @param[in] type log message text
|
|
/// @param[in] ... variable input to log message text
|
|
/// ///
|
|
void log(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
|