Add xy color mechanism
parent
ac803152f0
commit
511d9c641a
|
|
@ -274,6 +274,11 @@ void ZigbeeInterface::disable()
|
|||
|
||||
void ZigbeeInterface::sendMessage(const ZigbeeInterfaceMessage &message)
|
||||
{
|
||||
if (!available()) {
|
||||
qCWarning(dcZigbeeInterface()) << "Could not send message. The serial port is not available.";
|
||||
return;
|
||||
}
|
||||
|
||||
quint16 messageTypeValue = static_cast<quint16>(message.messageType());
|
||||
quint16 lengthValue = static_cast<quint16>(message.data().count());
|
||||
quint8 crcValue = calculateCrc(messageTypeValue, lengthValue, message.data());
|
||||
|
|
|
|||
|
|
@ -585,6 +585,25 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandMoveToColourTemperature(
|
|||
return sendRequest(request);
|
||||
}
|
||||
|
||||
ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandMoveToColor(quint8 addressMode, quint16 targetShortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, quint16 x, quint16 y, quint16 transitionTime)
|
||||
{
|
||||
QByteArray data;
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream << addressMode;
|
||||
stream << targetShortAddress;
|
||||
stream << sourceEndpoint;
|
||||
stream << destinationEndpoint;
|
||||
stream << x << y;
|
||||
stream << transitionTime;
|
||||
|
||||
ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeMoveToColor, data));
|
||||
//request.setExpectedAdditionalMessageType(Zigbee::MessageTypeDefaultResponse);
|
||||
request.setDescription(QString("Move to colour %1, %2").arg(x).arg(y));
|
||||
request.setTimoutIntervall(5000);
|
||||
|
||||
return sendRequest(request);
|
||||
}
|
||||
|
||||
ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandMoveToHueSaturation(quint8 addressMode, quint16 targetShortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, quint8 hue, quint8 saturation, quint16 transitionTime)
|
||||
{
|
||||
QByteArray data;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ public:
|
|||
|
||||
// Level
|
||||
ZigbeeInterfaceReply *commandMoveToColourTemperature(quint8 addressMode, quint16 targetShortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, quint16 colourTemperature, quint16 transitionTime);
|
||||
ZigbeeInterfaceReply *commandMoveToColor(quint8 addressMode, quint16 targetShortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, quint16 x, quint16 y, quint16 transitionTime);
|
||||
ZigbeeInterfaceReply *commandMoveToHueSaturation(quint8 addressMode, quint16 targetShortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, quint8 hue, quint8 saturation, quint16 transitionTime);
|
||||
ZigbeeInterfaceReply *commandMoveToHue(quint8 addressMode, quint16 targetShortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, quint8 hue, quint16 transitionTime);
|
||||
ZigbeeInterfaceReply *commandMoveToSaturation(quint8 addressMode, quint16 targetShortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, quint8 saturation, quint16 transitionTime);
|
||||
|
|
|
|||
|
|
@ -210,6 +210,28 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToColorTemperature(quint16 co
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToColor(double x, double y, quint16 transitionTime)
|
||||
{
|
||||
qCDebug(dcZigbeeNode()) << "Move to color request" << node() << x << y << transitionTime;
|
||||
|
||||
quint16 normalizedX = static_cast<quint16>(qRound(x * 65536));
|
||||
quint16 normalizedY = static_cast<quint16>(qRound(y * 65536));
|
||||
|
||||
ZigbeeInterfaceReply *reply = m_controller->commandMoveToColor(0x02, node()->shortAddress(), 0x01, endpointId(), normalizedX, normalizedY, transitionTime);
|
||||
connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){
|
||||
reply->deleteLater();
|
||||
|
||||
if (reply->status() != ZigbeeInterfaceReply::Success) {
|
||||
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
|
||||
});
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToHueSaturation(quint8 hue, quint8 saturation, quint16 transitionTime)
|
||||
{
|
||||
qCDebug(dcZigbeeNode()) << "Move to hue saturation request" << node() << hue << saturation << transitionTime;
|
||||
|
|
|
|||
|
|
@ -50,8 +50,13 @@ public:
|
|||
ZigbeeNetworkReply *bindUnicast(Zigbee::ClusterId clusterId, const ZigbeeAddress &destinationAddress, quint8 destinationEndpoint) override;
|
||||
ZigbeeNetworkReply *sendOnOffClusterCommand(ZigbeeCluster::OnOffClusterCommand command) override;
|
||||
ZigbeeNetworkReply *addGroup(quint8 destinationEndpoint, quint16 groupAddress) override;
|
||||
|
||||
// Level control
|
||||
ZigbeeNetworkReply *sendLevelCommand(ZigbeeCluster::LevelClusterCommand command, quint8 level, bool triggersOnOff, quint16 transitionTime) override;
|
||||
|
||||
// Color control
|
||||
ZigbeeNetworkReply *sendMoveToColorTemperature(quint16 colourTemperature, quint16 transitionTime) override;
|
||||
ZigbeeNetworkReply *sendMoveToColor(double x, double y, quint16 transitionTime) override;
|
||||
ZigbeeNetworkReply *sendMoveToHueSaturation(quint8 hue, quint8 saturation, quint16 transitionTime) override;
|
||||
ZigbeeNetworkReply *sendMoveToHue(quint8 hue, quint16 transitionTime) override;
|
||||
ZigbeeNetworkReply *sendMoveToSaturation(quint8 saturation, quint16 transitionTime) override;
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ public:
|
|||
|
||||
// Color commands
|
||||
virtual ZigbeeNetworkReply *sendMoveToColorTemperature(quint16 colourTemperature, quint16 transitionTime) = 0;
|
||||
virtual ZigbeeNetworkReply *sendMoveToColor(double x, double y, quint16 transitionTime) = 0;
|
||||
virtual ZigbeeNetworkReply *sendMoveToHueSaturation(quint8 hue, quint8 saturation, quint16 transitionTime) = 0;
|
||||
virtual ZigbeeNetworkReply *sendMoveToHue(quint8 hue, quint16 transitionTime) = 0;
|
||||
virtual ZigbeeNetworkReply *sendMoveToSaturation(quint8 saturation, quint16 transitionTime) = 0;
|
||||
|
|
|
|||
|
|
@ -27,10 +27,127 @@
|
|||
|
||||
#include "zigbeeutils.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QMetaEnum>
|
||||
#include <QDataStream>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static QList<QColor> colorTemperatureScale = {
|
||||
QColor(255, 56, 0), // 1000 K
|
||||
QColor(255, 71, 0),
|
||||
QColor(255, 83, 0),
|
||||
QColor(255, 93, 0),
|
||||
QColor(255, 101, 0),
|
||||
QColor(255, 109, 0),
|
||||
QColor(255, 115, 0),
|
||||
QColor(255, 121, 0),
|
||||
QColor(255, 126, 0),
|
||||
QColor(255, 131, 0),
|
||||
QColor(255, 138, 18),
|
||||
QColor(255, 142, 33),
|
||||
QColor(255, 147, 44),
|
||||
QColor(255, 152, 54),
|
||||
QColor(255, 157, 63),
|
||||
QColor(255, 161, 72),
|
||||
QColor(255, 165, 79),
|
||||
QColor(255, 169, 87),
|
||||
QColor(255, 173, 94),
|
||||
QColor(255, 177, 101),
|
||||
QColor(255, 180, 107),
|
||||
QColor(255, 184, 114),
|
||||
QColor(255, 187, 120),
|
||||
QColor(255, 190, 126),
|
||||
QColor(255, 193, 132),
|
||||
QColor(255, 196, 137),
|
||||
QColor(255, 199, 143),
|
||||
QColor(255, 201, 148),
|
||||
QColor(255, 204, 153),
|
||||
QColor(255, 206, 159),
|
||||
QColor(255, 209, 163),
|
||||
QColor(255, 211, 168),
|
||||
QColor(255, 213, 173),
|
||||
QColor(255, 215, 177),
|
||||
QColor(255, 217, 182),
|
||||
QColor(255, 219, 186),
|
||||
QColor(255, 221, 190),
|
||||
QColor(255, 223, 194),
|
||||
QColor(255, 225, 198),
|
||||
QColor(255, 227, 202),
|
||||
QColor(255, 228, 206),
|
||||
QColor(255, 230, 210),
|
||||
QColor(255, 232, 213),
|
||||
QColor(255, 233, 217),
|
||||
QColor(255, 235, 220),
|
||||
QColor(255, 236, 224),
|
||||
QColor(255, 238, 227),
|
||||
QColor(255, 239, 230),
|
||||
QColor(255, 240, 233),
|
||||
QColor(255, 242, 236),
|
||||
QColor(255, 243, 239),
|
||||
QColor(255, 244, 242),
|
||||
QColor(255, 245, 245),
|
||||
QColor(255, 246, 247),
|
||||
QColor(255, 248, 251),
|
||||
QColor(255, 249, 253),
|
||||
QColor(254, 249, 255),
|
||||
QColor(252, 247, 255),
|
||||
QColor(249, 246, 255),
|
||||
QColor(247, 245, 255),
|
||||
QColor(245, 243, 255),
|
||||
QColor(243, 242, 255),
|
||||
QColor(240, 241, 255),
|
||||
QColor(239, 240, 255),
|
||||
QColor(237, 239, 255),
|
||||
QColor(235, 238, 255),
|
||||
QColor(233, 237, 255),
|
||||
QColor(231, 236, 255),
|
||||
QColor(230, 235, 255),
|
||||
QColor(228, 234, 255),
|
||||
QColor(227, 233, 255),
|
||||
QColor(225, 232, 255),
|
||||
QColor(224, 231, 255),
|
||||
QColor(222, 230, 255),
|
||||
QColor(221, 230, 255),
|
||||
QColor(220, 229, 255),
|
||||
QColor(218, 229, 255),
|
||||
QColor(217, 227, 255),
|
||||
QColor(216, 227, 255),
|
||||
QColor(215, 226, 255),
|
||||
QColor(214, 225, 255),
|
||||
QColor(212, 225, 255),
|
||||
QColor(211, 224, 255),
|
||||
QColor(210, 223, 255),
|
||||
QColor(209, 223, 255),
|
||||
QColor(208, 222, 255),
|
||||
QColor(207, 221, 255),
|
||||
QColor(207, 221, 255),
|
||||
QColor(206, 220, 255),
|
||||
QColor(205, 220, 255),
|
||||
QColor(207, 218, 255),
|
||||
QColor(207, 218, 255),
|
||||
QColor(206, 217, 255),
|
||||
QColor(205, 217, 255),
|
||||
QColor(204, 216, 255),
|
||||
QColor(204, 216, 255),
|
||||
QColor(203, 215, 255),
|
||||
QColor(202, 215, 255),
|
||||
QColor(202, 214, 255),
|
||||
QColor(201, 214, 255),
|
||||
QColor(200, 213, 255),
|
||||
QColor(200, 213, 255),
|
||||
QColor(199, 212, 255),
|
||||
QColor(198, 212, 255),
|
||||
QColor(198, 212, 255),
|
||||
QColor(197, 211, 255),
|
||||
QColor(197, 211, 255),
|
||||
QColor(197, 210, 255),
|
||||
QColor(196, 210, 255),
|
||||
QColor(195, 210, 255),
|
||||
QColor(195, 209, 255) // 12000 K
|
||||
};
|
||||
|
||||
QBitArray ZigbeeUtils::convertByteArrayToBitArray(const QByteArray &byteArray)
|
||||
{
|
||||
QBitArray bitArray(byteArray.count() * 8);
|
||||
|
|
@ -175,5 +292,132 @@ quint64 ZigbeeUtils::generateRandomPanId()
|
|||
|
||||
QPointF ZigbeeUtils::convertColorToXY(const QColor &color)
|
||||
{
|
||||
floar r = color.red()
|
||||
// https://developers.meethue.com/develop/application-design-guidance/color-conversion-formulas-rgb-to-xy-and-back/
|
||||
|
||||
// Color values between [0, 1]
|
||||
|
||||
// Gamma correction
|
||||
double redGamma = (color.redF() > 0.04045) ? pow((color.redF() + 0.055) / (1.0 + 0.055), 2.4) : (color.redF() / 12.92);
|
||||
double greenGamme = (color.greenF() > 0.04045) ? pow((color.greenF() + 0.055) / (1.0 + 0.055), 2.4) : (color.greenF() / 12.92);
|
||||
double blueGamme = (color.blueF() > 0.04045) ? pow((color.blueF() + 0.055) / (1.0 + 0.055), 2.4) : (color.blueF() / 12.92);
|
||||
|
||||
// Convert the RGB values to XYZ using the Wide RGB D65 conversion formula
|
||||
double xx = redGamma * 0.664511 + greenGamme * 0.154324 + blueGamme * 0.162028;
|
||||
double yy = redGamma * 0.283881 + greenGamme * 0.668433 + blueGamme * 0.047685;
|
||||
double zz = redGamma * 0.000088 + greenGamme * 0.072310 + blueGamme * 0.986039;
|
||||
|
||||
//qWarning() << "xyz" << xx << yy << zz;
|
||||
|
||||
double x = xx / (xx + yy + zz);
|
||||
double y = yy / (xx + yy + zz);
|
||||
|
||||
// Correct brightness if required
|
||||
if (y >= 1) y = 1.0;
|
||||
|
||||
//qWarning() << "xy" << x << y;
|
||||
|
||||
// TODO: ceck if this point is within the color gamut triangle of the light, otherwise get closest point
|
||||
|
||||
return QPointF(x, y);
|
||||
}
|
||||
|
||||
QColor ZigbeeUtils::convertXYToColor(const QPointF &xyColor)
|
||||
{
|
||||
// https://developers.meethue.com/develop/application-design-guidance/color-conversion-formulas-rgb-to-xy-and-back/
|
||||
|
||||
// TODO: ceck if this point is within the color gamut triangle of the light, otherwise get closest point
|
||||
|
||||
// Extract x y and z
|
||||
double xx = xyColor.x();
|
||||
double yy = xyColor.y();
|
||||
double zz = 1.0 - xx - yy;
|
||||
|
||||
// Get x, y and z
|
||||
double y = 1.0; // assume full brightness for the calculation
|
||||
double x = (y / yy) * xx;
|
||||
double z = (y / yy) * zz;
|
||||
|
||||
//qWarning() << "xyz" << x << y << z;
|
||||
|
||||
// Convert to r, g and b according D65
|
||||
double r = x * 1.656492 - y * 0.354851 - z * 0.255038;
|
||||
double g = -x * 0.707196 + y * 1.655397 + z * 0.036152;
|
||||
double b = x * 0.051713 - y * 0.121364 + z * 1.011530;
|
||||
|
||||
if (r > b && r > g && r > 1.0) {
|
||||
// red is too big
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0;
|
||||
} else if (g > b && g > r && g > 1.0) {
|
||||
// green is too big
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0;
|
||||
} else if (b > r && b > g && b > 1.0) {
|
||||
// blue is too big
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0;
|
||||
}
|
||||
|
||||
// Apply gamma correction
|
||||
r = (r <= 0.0031308) ? 12.92 * r : (1.0 + 0.055) * pow(r, (1.0 / 2.4)) - 0.055;
|
||||
g = (g <= 0.0031308) ? 12.92 * g : (1.0 + 0.055) * pow(g, (1.0 / 2.4)) - 0.055;
|
||||
b = (b <= 0.0031308) ? 12.92 * b : (1.0 + 0.055) * pow(b, (1.0 / 2.4)) - 0.055;
|
||||
|
||||
if (r > b && r > g) {
|
||||
// red is biggest
|
||||
if (r > 1.0) {
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0;
|
||||
}
|
||||
} else if (g > b && g > r) {
|
||||
// green is biggest
|
||||
if (g > 1.0) {
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0;
|
||||
}
|
||||
} else if (b > r && b > g) {
|
||||
// blue is biggest
|
||||
if (b > 1.0) {
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we don't have any negative round error values
|
||||
if (r < 0) r = 0;
|
||||
if (g < 0) g = 0;
|
||||
if (b < 0) b = 0;
|
||||
|
||||
//qWarning() << r << g << b;
|
||||
QColor color;
|
||||
color.setRedF(r);
|
||||
color.setGreenF(g);
|
||||
color.setBlueF(b);
|
||||
color.setAlphaF(1.0);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
QColor ZigbeeUtils::convertXYToColor(quint16 x, quint16 y)
|
||||
{
|
||||
QPointF xy(x / 65536.0, y / 65536.0);
|
||||
return convertXYToColor(xy);
|
||||
}
|
||||
|
||||
QColor ZigbeeUtils::interpolateColorFromColorTemperature(int colorTemperature, int minValue, int maxValue)
|
||||
{
|
||||
Q_ASSERT_X(colorTemperature >= minValue && colorTemperature <= maxValue, "interpolate colors", "Interpolation value not between min and max value");
|
||||
int intervalSize = maxValue - minValue;
|
||||
int intervalPosition = colorTemperature - minValue;
|
||||
double percentage = intervalPosition * 1.0 / intervalSize;
|
||||
//qWarning() << "Interpolate color" << intervalSize << intervalPosition << percentage;
|
||||
int closestColorIndex = qRound((colorTemperatureScale.count() - 1) * (1.0 - percentage));
|
||||
//qWarning() << "Colors size" << colorTemperatureScale.count() << "Color position according to percentage" << closestColorIndex;
|
||||
return colorTemperatureScale.at(closestColorIndex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <QColor>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QPointF>
|
||||
#include <QByteArray>
|
||||
#include <QBitArray>
|
||||
|
||||
|
|
@ -64,7 +65,13 @@ public:
|
|||
// Generate random data
|
||||
static quint64 generateRandomPanId();
|
||||
|
||||
// Color converter
|
||||
static QPointF convertColorToXY(const QColor &color);
|
||||
static QColor convertXYToColor(const QPointF &xyColor);
|
||||
static QColor convertXYToColor(quint16 x, quint16 y);
|
||||
|
||||
// Color temperature interpolation
|
||||
static QColor interpolateColorFromColorTemperature(int colorTemperature, int minValue, int maxValue);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue