cloud actions do now work

This commit is contained in:
bernhard.trinnes 2020-07-06 19:36:38 +02:00
parent 8907fb7b47
commit 5e40675829
12 changed files with 488 additions and 309 deletions

View File

@ -1,9 +1,18 @@
# Lifx # Lifx
This plug-in implements the LAN API for Lifx devices This plug-in integrates LIFX lights to nymea.
## Supported Things ## Supported Things
* All LIFX lights
## Requirements ## Requirements
* LIFX cloud access token.
** Get the token from https://cloud.lifx.com/settings
* Internet connection
* The package 'nymea-plugin-lifx' must be installed.
## More ## More
https://www.lifx.com/

View File

@ -63,52 +63,45 @@ void IntegrationPluginLifx::init()
m_brightnessStateTypeIds.insert(colorBulbThingClassId, colorBulbBrightnessStateTypeId); m_brightnessStateTypeIds.insert(colorBulbThingClassId, colorBulbBrightnessStateTypeId);
m_brightnessStateTypeIds.insert(dimmableBulbThingClassId, dimmableBulbBrightnessStateTypeId); m_brightnessStateTypeIds.insert(dimmableBulbThingClassId, dimmableBulbBrightnessStateTypeId);
m_colorTemperatureStateTypeIds.insert(colorBulbThingClassId, colorBulbColorTemperatureStateTypeId); m_colorTemperatureStateTypeIds.insert(colorBulbThingClassId, colorBulbColorTemperatureStateTypeId);
m_colorTemperatureStateTypeIds.insert(dimmableBulbThingClassId, dimmableBulbColorTemperatureStateTypeId); m_colorTemperatureStateTypeIds.insert(dimmableBulbThingClassId, dimmableBulbColorTemperatureStateTypeId);
m_hostAddressParamTypeIds.insert(colorBulbThingClassId, colorBulbThingHostParamTypeId);
m_hostAddressParamTypeIds.insert(dimmableBulbThingClassId, dimmableBulbThingHostParamTypeId);
m_portParamTypeIds.insert(colorBulbThingClassId, colorBulbThingPortParamTypeId);
m_portParamTypeIds.insert(dimmableBulbThingClassId, dimmableBulbThingPortParamTypeId);
m_idParamTypeIds.insert(colorBulbThingClassId, colorBulbThingIdParamTypeId); m_idParamTypeIds.insert(colorBulbThingClassId, colorBulbThingIdParamTypeId);
m_idParamTypeIds.insert(dimmableBulbThingClassId, dimmableBulbThingIdParamTypeId); m_idParamTypeIds.insert(dimmableBulbThingClassId, dimmableBulbThingIdParamTypeId);
m_serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_hap._tcp"); // discovers all homekit devices m_serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_hap._tcp"); // discovers all homekit devices
QFile file; // TODO for LAN connection, get id and device features
file.setFileName("/tmp/products.json"); // QFile file;
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { // file.setFileName("/tmp/products.json");
qCWarning(dcLifx()) << "Could not open products file" << file.errorString() << "file name:" << file.fileName(); // if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
} else { // qCWarning(dcLifx()) << "Could not open products file" << file.errorString() << "file name:" << file.fileName();
QJsonDocument productsJson = QJsonDocument::fromJson(file.readAll()); // } else {
file.close(); // QJsonDocument productsJson = QJsonDocument::fromJson(file.readAll());
// file.close();
if (!productsJson.isArray()) {
qCWarning(dcLifx()) << "Products JSON is not a valid array";
} else {
QJsonArray productsArray = productsJson.array().first().toObject().value("products").toArray();
foreach (QJsonValue value, productsArray) {
QJsonObject object = value.toObject();
Lifx::LifxProduct product;
product.pid = object["pid"].toInt();
product.name = object["name"].toString();
qCDebug(dcLifx()) << "Lifx product JSON, found product. PID:" << product.pid << "Name" << product.name;
QJsonObject features = object["features"].toObject();
product.color = features["color"].toBool();
product.infrared = features["infrared"].toBool();
product.matrix = features["matrix"].toBool();
product.multizone = features["multizone"].toBool();
product.minColorTemperature = features["temperature_range"].toArray().first().toInt();
product.maxColorTemperature = features["temperature_range"].toArray().last().toInt();
product.chain = features["chain"].toBool();
m_lifxProducts.insert(product.pid, product);
}
}
}
// if (!productsJson.isArray()) {
// qCWarning(dcLifx()) << "Products JSON is not a valid array";
// } else {
// QJsonArray productsArray = productsJson.array().first().toObject().value("products").toArray();
// foreach (QJsonValue value, productsArray) {
// QJsonObject object = value.toObject();
// LifxLan::LifxProduct product;
// product.pid = object["pid"].toInt();
// product.name = object["name"].toString();
// qCDebug(dcLifx()) << "Lifx product JSON, found product. PID:" << product.pid << "Name" << product.name;
// QJsonObject features = object["features"].toObject();
// product.color = features["color"].toBool();
// product.infrared = features["infrared"].toBool();
// product.matrix = features["matrix"].toBool();
// product.multizone = features["multizone"].toBool();
// product.minColorTemperature = features["temperature_range"].toArray().first().toInt();
// product.maxColorTemperature = features["temperature_range"].toArray().last().toInt();
// product.chain = features["chain"].toBool();
// m_lifxProducts.insert(product.pid, product);
// }
// }
// }
m_networkManager = hardwareManager()->networkManager(); m_networkManager = hardwareManager()->networkManager();
} }
@ -156,6 +149,7 @@ void IntegrationPluginLifx::confirmPairing(ThingPairingInfo *info, const QString
void IntegrationPluginLifx::discoverThings(ThingDiscoveryInfo *info) void IntegrationPluginLifx::discoverThings(ThingDiscoveryInfo *info)
{ {
// NOTE: the LAN API is not yet finished, to enable LAN discovery add "discovery" to the createMethods
if ((info->thingClassId() == colorBulbThingClassId) || (info->thingClassId() == dimmableBulbThingClassId)) { if ((info->thingClassId() == colorBulbThingClassId) || (info->thingClassId() == dimmableBulbThingClassId)) {
QHash<QString, ThingDescriptor> descriptors; QHash<QString, ThingDescriptor> descriptors;
foreach (const ZeroConfServiceEntry avahiEntry, m_serviceBrowser->serviceEntries()) { foreach (const ZeroConfServiceEntry avahiEntry, m_serviceBrowser->serviceEntries()) {
@ -203,15 +197,20 @@ void IntegrationPluginLifx::setupThing(ThingSetupInfo *info)
Thing *thing = info->thing(); Thing *thing = info->thing();
if (thing->thingClassId() == colorBulbThingClassId || thing->thingClassId() == dimmableBulbThingClassId) { if (thing->thingClassId() == colorBulbThingClassId || thing->thingClassId() == dimmableBulbThingClassId) {
Lifx *lifx = new Lifx(QHostAddress(thing->paramValue(m_hostAddressParamTypeIds.value(thing->thingClassId())).toString()), if (thing->parentId().isNull()) {
thing->paramValue(m_portParamTypeIds.value(thing->thingClassId())).toInt(), this); // Lifx LAN
if(lifx->enable()) { //LifxLan *lifx = new LifxLan(, this);
m_lifxConnections.insert(thing, lifx); //if(lifx->enable()) {
//TODO async setup // m_lifxLanConnections.insert(thing, lifx);
info->finish(Thing::ThingErrorNoError); //TODO async setup for LAN devices
} else { // info->finish(Thing::ThingErrorNoError);
lifx->deleteLater(); //} else {
// lifx->deleteLater();
info->finish(Thing::ThingErrorSetupFailed); info->finish(Thing::ThingErrorSetupFailed);
//}
} else {
// Lifx Cloud
info->finish(Thing::ThingErrorNoError);
} }
} else if (thing->thingClassId() == lifxAccountThingClassId) { } else if (thing->thingClassId() == lifxAccountThingClassId) {
@ -250,8 +249,9 @@ void IntegrationPluginLifx::postSetupThing(Thing *thing)
if (!m_pluginTimer) { if (!m_pluginTimer) {
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60); m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60);
connect(m_pluginTimer, &PluginTimer::timeout, this, [this]() { connect(m_pluginTimer, &PluginTimer::timeout, this, [this]() {
foreach (Lifx *lifx, m_lifxConnections) { foreach (LifxLan *lifx, m_lifxLanConnections) {
Q_UNUSED(lifx) Q_UNUSED(lifx)
//TODO update LAN device states
} }
foreach (LifxCloud *lifx, m_lifxCloudConnections) { foreach (LifxCloud *lifx, m_lifxCloudConnections) {
@ -271,11 +271,11 @@ void IntegrationPluginLifx::executeAction(ThingActionInfo *info)
Thing *thing = info->thing(); Thing *thing = info->thing();
Action action = info->action(); Action action = info->action();
bool cloudDevice = false; bool cloudDevice = false;
Lifx *lifx; LifxLan *lifx;
LifxCloud *lifxCloud; LifxCloud *lifxCloud;
if (m_lifxConnections.contains(thing)) { if (m_lifxLanConnections.contains(thing)) {
lifx = m_lifxConnections.value(thing); lifx = m_lifxLanConnections.value(thing);
} else if (m_lifxCloudConnections.contains(myThings().findById(thing->parentId()))) { } else if (m_lifxCloudConnections.contains(myThings().findById(thing->parentId()))) {
lifxCloud = m_lifxCloudConnections.value(myThings().findById(thing->parentId())); lifxCloud = m_lifxCloudConnections.value(myThings().findById(thing->parentId()));
cloudDevice = true; cloudDevice = true;
@ -299,9 +299,13 @@ void IntegrationPluginLifx::executeAction(ThingActionInfo *info)
} else if (action.actionTypeId() == colorBulbBrightnessActionTypeId) { } else if (action.actionTypeId() == colorBulbBrightnessActionTypeId) {
if (!thing->stateValue(colorBulbPowerStateTypeId).toBool()) if (!thing->stateValue(colorBulbPowerStateTypeId).toBool()){
lifx->setPower(true); if (cloudDevice) {
lifxCloud->setPower(lightId, true);
} else {
lifx->setPower(true);
}
}
int brightness = info->action().param(colorBulbBrightnessActionBrightnessParamTypeId).value().toInt(); int brightness = info->action().param(colorBulbBrightnessActionBrightnessParamTypeId).value().toInt();
int requestId; int requestId;
if (cloudDevice) { if (cloudDevice) {
@ -313,8 +317,13 @@ void IntegrationPluginLifx::executeAction(ThingActionInfo *info)
m_asyncActions.insert(requestId, info); m_asyncActions.insert(requestId, info);
} else if (action.actionTypeId() == colorBulbColorActionColorParamTypeId) { } else if (action.actionTypeId() == colorBulbColorActionColorParamTypeId) {
QRgb color = QColor(action.param(colorBulbColorActionColorParamTypeId).value().toString()).rgba(); QRgb color = QColor(action.param(colorBulbColorActionColorParamTypeId).value().toString()).rgba();
if (!thing->stateValue(colorBulbPowerStateTypeId).toBool()) if (!thing->stateValue(colorBulbPowerStateTypeId).toBool()){
lifx->setPower(true); if (cloudDevice) {
lifxCloud->setPower(lightId, true);
} else {
lifx->setPower(true);
}
}
int requestId; int requestId;
if (cloudDevice) { if (cloudDevice) {
requestId = lifxCloud->setColor(lightId, color); requestId = lifxCloud->setColor(lightId, color);
@ -325,8 +334,13 @@ void IntegrationPluginLifx::executeAction(ThingActionInfo *info)
m_asyncActions.insert(requestId, info); m_asyncActions.insert(requestId, info);
} else if (action.actionTypeId() == colorBulbColorTemperatureActionTypeId) { } else if (action.actionTypeId() == colorBulbColorTemperatureActionTypeId) {
int colorTemperature = 6500 - (action.param(colorBulbColorTemperatureActionColorTemperatureParamTypeId).value().toUInt() * -11.12); int colorTemperature = 6500 - (action.param(colorBulbColorTemperatureActionColorTemperatureParamTypeId).value().toUInt() * -11.12);
if (!thing->stateValue(colorBulbPowerStateTypeId).toBool()) if (!thing->stateValue(colorBulbPowerStateTypeId).toBool()){
lifx->setPower(true); if (cloudDevice) {
lifxCloud->setPower(lightId, true);
} else {
lifx->setPower(true);
}
}
int requestId; int requestId;
if (cloudDevice) { if (cloudDevice) {
requestId = lifxCloud->setColorTemperature(lightId, colorTemperature); requestId = lifxCloud->setColorTemperature(lightId, colorTemperature);
@ -335,6 +349,40 @@ void IntegrationPluginLifx::executeAction(ThingActionInfo *info)
} }
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info); m_asyncActions.insert(requestId, info);
} else if (action.actionTypeId() == colorBulbEffectStateTypeId) {
if (!thing->stateValue(colorBulbPowerStateTypeId).toBool()){
if (cloudDevice) {
lifxCloud->setPower(lightId, true);
} else {
lifx->setPower(true);
}
}
QString effectString = action.param(colorBulbColorTemperatureActionColorTemperatureParamTypeId).value().toString();
int requestId;
LifxCloud::Effect effect;
if (effectString == "None") {
effect = LifxCloud::EffectNone;
} else if (effectString == "Breathe") {
effect = LifxCloud::EffectBreathe;
} else if (effectString == "Move") {
effect = LifxCloud::EffectMove;
} else if (effectString == "Morph") {
effect = LifxCloud::EffectMove;
} else if (effectString == "Flame") {
effect = LifxCloud::EffectMove;
} else if (effectString == "Pulse") {
effect = LifxCloud::EffectMove;
}
if (cloudDevice) {
QColor color = QColor(thing->stateValue(colorBulbColorStateTypeId).toString());
requestId = lifxCloud->setEffect(lightId, effect, color);
} else {
qCWarning(dcLifx()) << "LAN devices are not yet supported";
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
} else { } else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8()); Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
} }
@ -351,8 +399,13 @@ void IntegrationPluginLifx::executeAction(ThingActionInfo *info)
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
} else if (action.actionTypeId() == dimmableBulbBrightnessActionTypeId) { } else if (action.actionTypeId() == dimmableBulbBrightnessActionTypeId) {
int brightness = action.param(dimmableBulbBrightnessActionBrightnessParamTypeId).value().toInt(); int brightness = action.param(dimmableBulbBrightnessActionBrightnessParamTypeId).value().toInt();
if (!thing->stateValue(dimmableBulbPowerStateTypeId).toBool()) if (!thing->stateValue(colorBulbPowerStateTypeId).toBool()){
lifx->setPower(true); if (cloudDevice) {
lifxCloud->setPower(lightId, true);
} else {
lifx->setPower(true);
}
}
int requestId; int requestId;
if (cloudDevice) { if (cloudDevice) {
requestId = lifxCloud->setBrightnesss(lightId, brightness); requestId = lifxCloud->setBrightnesss(lightId, brightness);
@ -361,6 +414,39 @@ void IntegrationPluginLifx::executeAction(ThingActionInfo *info)
} }
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info); m_asyncActions.insert(requestId, info);
} else if (action.actionTypeId() == dimmableBulbEffectStateTypeId) {
if (!thing->stateValue(colorBulbPowerStateTypeId).toBool()){
if (cloudDevice) {
lifxCloud->setPower(lightId, true);
} else {
lifx->setPower(true);
}
}
QString effectString = action.param(dimmableBulbColorTemperatureActionColorTemperatureParamTypeId).value().toString();
int requestId;
LifxCloud::Effect effect;
if (effectString == "None") {
effect = LifxCloud::EffectNone;
} else if (effectString == "Breathe") {
effect = LifxCloud::EffectBreathe;
} else if (effectString == "Move") {
effect = LifxCloud::EffectMove;
} else if (effectString == "Morph") {
effect = LifxCloud::EffectMove;
} else if (effectString == "Flame") {
effect = LifxCloud::EffectMove;
} else if (effectString == "Pulse") {
effect = LifxCloud::EffectMove;
}
if (cloudDevice) {
requestId = lifxCloud->setEffect(lightId, effect, QColor("0xffffff"));
} else {
qCWarning(dcLifx()) << "LAN devices are not yet supported";
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
} else { } else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8()); Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
} }
@ -372,8 +458,8 @@ void IntegrationPluginLifx::executeAction(ThingActionInfo *info)
void IntegrationPluginLifx::thingRemoved(Thing *thing) void IntegrationPluginLifx::thingRemoved(Thing *thing)
{ {
if (thing->thingClassId() == colorBulbThingClassId || thing->thingClassId() == dimmableBulbThingClassId) { if (thing->thingClassId() == colorBulbThingClassId || thing->thingClassId() == dimmableBulbThingClassId) {
if (m_lifxConnections.contains(thing)) if (m_lifxLanConnections.contains(thing))
m_lifxConnections.take(thing)->deleteLater(); m_lifxLanConnections.take(thing)->deleteLater();
} else if (thing->thingClassId() == lifxAccountThingClassId) { } else if (thing->thingClassId() == lifxAccountThingClassId) {
if (m_lifxCloudConnections.contains(thing)) if (m_lifxCloudConnections.contains(thing))
m_lifxCloudConnections.take(thing)->deleteLater(); m_lifxCloudConnections.take(thing)->deleteLater();
@ -412,17 +498,17 @@ void IntegrationPluginLifx::executeBrowserItem(BrowserActionInfo *info)
connect(info, &BrowserActionInfo::aborted, this, [requestId, this] {m_asyncBrowserItem.remove(requestId);}); connect(info, &BrowserActionInfo::aborted, this, [requestId, this] {m_asyncBrowserItem.remove(requestId);});
} }
void IntegrationPluginLifx::onConnectionChanged(bool connected) void IntegrationPluginLifx::onLifxLanConnectionChanged(bool connected)
{ {
Q_UNUSED(connected) Q_UNUSED(connected)
Lifx *lifx = static_cast<Lifx *>(sender()); LifxLan *lifx = static_cast<LifxLan *>(sender());
Thing *thing = m_lifxConnections.key(lifx); Thing *thing = m_lifxLanConnections.key(lifx);
if (!thing) if (!thing)
return; return;
thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), connected); thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), connected);
} }
void IntegrationPluginLifx::onRequestExecuted(int requestId, bool success) void IntegrationPluginLifx::onLifxLanRequestExecuted(int requestId, bool success)
{ {
if (m_asyncActions.contains(requestId)) { if (m_asyncActions.contains(requestId)) {
ThingActionInfo *info = m_asyncActions.take(requestId); ThingActionInfo *info = m_asyncActions.take(requestId);
@ -492,8 +578,8 @@ void IntegrationPluginLifx::onLifxCloudLightsListReceived(const QList<LifxCloud:
ThingDescriptor thingDescriptor(thingClassId, light.product.name, light.location.name, parentThing->id()); ThingDescriptor thingDescriptor(thingClassId, light.product.name, light.location.name, parentThing->id());
foreach (Thing * thing, myThings().filterByParam(m_idParamTypeIds.value(thingClassId), light.id)) { foreach (Thing * thing, myThings().filterByParam(m_idParamTypeIds.value(thingClassId), light.id)) {
thing->setStateValue(m_connectedStateTypeIds.value(thingClassId), light.connected); thing->setStateValue(m_connectedStateTypeIds.value(thingClassId), light.connected);
thing->setStateValue(m_brightnessStateTypeIds.value(thingClassId), light.brightness); thing->setStateValue(m_brightnessStateTypeIds.value(thingClassId), light.brightness*100.00);
thing->setStateValue(m_colorTemperatureStateTypeIds.value(thingClassId), light.colorTemperature); thing->setStateValue(m_colorTemperatureStateTypeIds.value(thingClassId), light.colorTemperature); //TODO Kelvin to mired
thing->setStateValue(m_powerStateTypeIds.value(thingClassId), light.power); thing->setStateValue(m_powerStateTypeIds.value(thingClassId), light.power);
if (thingClassId == colorBulbThingClassId) { if (thingClassId == colorBulbThingClassId) {
thing->setStateValue(colorBulbColorStateTypeId, light.color); thing->setStateValue(colorBulbColorStateTypeId, light.color);
@ -537,7 +623,6 @@ void IntegrationPluginLifx::onLifxCloudRequestExecuted(int requestId, bool succe
} }
} }
void IntegrationPluginLifx::onLifxCloudScenesListReceived(const QList<LifxCloud::Scene> &scenes) void IntegrationPluginLifx::onLifxCloudScenesListReceived(const QList<LifxCloud::Scene> &scenes)
{ {
LifxCloud *lifxCloud = static_cast<LifxCloud *>(sender()); LifxCloud *lifxCloud = static_cast<LifxCloud *>(sender());

View File

@ -33,7 +33,7 @@
#include "integrations/integrationplugin.h" #include "integrations/integrationplugin.h"
#include "plugintimer.h" #include "plugintimer.h"
#include "lifx.h" #include "lifxlan.h"
#include "lifxcloud.h" #include "lifxcloud.h"
#include "network/networkaccessmanager.h" #include "network/networkaccessmanager.h"
@ -71,7 +71,7 @@ private:
PluginTimer *m_pluginTimer = nullptr; PluginTimer *m_pluginTimer = nullptr;
QHash<LifxCloud *, ThingSetupInfo *> m_asyncCloudSetups; QHash<LifxCloud *, ThingSetupInfo *> m_asyncCloudSetups;
QHash<int, ThingActionInfo *> m_asyncActions; QHash<int, ThingActionInfo *> m_asyncActions;
QHash<Thing *, Lifx *> m_lifxConnections; QHash<Thing *, LifxLan *> m_lifxLanConnections;
QHash<Thing *, LifxCloud *> m_lifxCloudConnections; QHash<Thing *, LifxCloud *> m_lifxCloudConnections;
QHash<LifxCloud *, BrowseResult *> m_asyncBrowseResults; QHash<LifxCloud *, BrowseResult *> m_asyncBrowseResults;
QHash<int, BrowserActionInfo *> m_asyncBrowserItem; QHash<int, BrowserActionInfo *> m_asyncBrowserItem;
@ -87,11 +87,11 @@ private:
QHash<ThingClassId, ParamTypeId> m_idParamTypeIds; QHash<ThingClassId, ParamTypeId> m_idParamTypeIds;
QHash<ThingId, ThingActionInfo *> m_pendingBrightnessAction; QHash<ThingId, ThingActionInfo *> m_pendingBrightnessAction;
QHash<int, Lifx::LifxProduct> m_lifxProducts; QHash<int, LifxLan::LifxProduct> m_lifxProducts;
private slots: private slots:
void onConnectionChanged(bool connected); void onLifxLanConnectionChanged(bool connected);
void onRequestExecuted(int requestId, bool success); void onLifxLanRequestExecuted(int requestId, bool success);
void onLifxCloudConnectionChanged(bool connected); void onLifxCloudConnectionChanged(bool connected);
void onLifxCloudAuthenticationChanged(bool authenticated); void onLifxCloudAuthenticationChanged(bool authenticated);

View File

@ -51,23 +51,9 @@
"id": "12907c9c-e7f0-47f2-bd58-39d52ffdf24e", "id": "12907c9c-e7f0-47f2-bd58-39d52ffdf24e",
"name": "colorBulb", "name": "colorBulb",
"displayName": "Color", "displayName": "Color",
"createMethods": ["auto", "discovery"], "createMethods": ["auto"],
"interfaces": ["colorlight", "connectable"], "interfaces": ["colorlight", "connectable"],
"paramTypes": [ "paramTypes": [
{
"id": "fd1c4817-5111-433a-b5b9-fd9f49d4975c",
"name": "host",
"displayName": "Host address",
"type" : "QString",
"readOnly": true
},
{
"id": "44c13745-300c-491f-b617-3a8d53472998",
"name": "port",
"displayName": "Port",
"type" : "int",
"readOnly": true
},
{ {
"id": "976ecea0-ac25-47d4-9dc5-362962ddb6c0", "id": "976ecea0-ac25-47d4-9dc5-362962ddb6c0",
"name": "id", "name": "id",
@ -139,10 +125,14 @@
"displayNameEvent": "Effect changed", "displayNameEvent": "Effect changed",
"displayNameAction": "Set effect", "displayNameAction": "Set effect",
"type": "QString", "type": "QString",
"defaultValue": "none", "defaultValue": "None",
"possibleValues": [ "possibleValues": [
"none", "None",
"color loop" "Breathe",
"Move",
"Morph",
"Flame",
"Pulse"
], ],
"writable": true "writable": true
} }
@ -152,23 +142,9 @@
"id": "a5b02af8-7c97-4a78-9c78-bafee7407b5e", "id": "a5b02af8-7c97-4a78-9c78-bafee7407b5e",
"name": "dimmableBulb", "name": "dimmableBulb",
"displayName": "Day and Dusk", "displayName": "Day and Dusk",
"createMethods": ["auto", "discovery"], "createMethods": ["auto"],
"interfaces": ["colortemperaturelight", "connectable"], "interfaces": ["colortemperaturelight", "connectable"],
"paramTypes": [ "paramTypes": [
{
"id": "cc0a765b-a753-4e07-a6e5-47e9272c4346",
"name": "host",
"displayName": "Host address",
"type" : "QString",
"readOnly": true
},
{
"id": "d233d9bf-6662-414d-92f6-dd3e267051b5",
"name": "port",
"displayName": "Port",
"type" : "int",
"readOnly": true
},
{ {
"id": "f157a97b-3fe5-4d9e-b5e3-5636f80d46ed", "id": "f157a97b-3fe5-4d9e-b5e3-5636f80d46ed",
"name": "id", "name": "id",
@ -221,6 +197,24 @@
"minValue": 153, "minValue": 153,
"maxValue": 500, "maxValue": 500,
"writable": true "writable": true
},
{
"id": "be47c474-eca1-479e-9393-68281a43d72a",
"name": "effect",
"displayName": "Effect",
"displayNameEvent": "Effect changed",
"displayNameAction": "Set effect",
"type": "QString",
"defaultValue": "None",
"possibleValues": [
"None",
"Breathe",
"Move",
"Morph",
"Flame",
"Pulse"
],
"writable": true
} }
] ]
} }

View File

@ -1,14 +0,0 @@
include(../plugins.pri)
QT += network
SOURCES += \
integrationpluginlifx.cpp \
lifx.cpp \
lifxcloud.cpp \
HEADERS += \
integrationpluginlifx.h \
lifx.h \
lifxcloud.h \

View File

@ -74,21 +74,9 @@ void LifxCloud::listLights()
QNetworkReply *reply = m_networkManager->get(request); QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [reply, this] { connect(reply, &QNetworkReply::finished, this, [reply, this] {
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if(!checkHttpStatusCode(reply)) {
// check HTTP status code
if (status > 207) {
qCWarning(dcLifx()) << "Error get lights list" << status << reply->errorString();
return; return;
} }
if (!m_authenticated) {
m_authenticated = true;
emit authenticationChanged(true);
}
if (!m_connected) {
m_connected = true;
emit authenticationChanged(true);
}
QByteArray rawData = reply->readAll(); QByteArray rawData = reply->readAll();
QJsonDocument data; QJsonParseError error; QJsonDocument data; QJsonParseError error;
@ -110,6 +98,11 @@ void LifxCloud::listLights()
Light light; Light light;
light.id = object["id"].toString().toUtf8(); light.id = object["id"].toString().toUtf8();
light.uuid = object["uuid"].toString().toUtf8(); light.uuid = object["uuid"].toString().toUtf8();
if (object["power"].toString() == "on") {
light.power = true;
} else {
light.power = false;
}
light.label = object["label"].toString(); light.label = object["label"].toString();
light.connected = object["connected"].toBool(); light.connected = object["connected"].toBool();
light.brightness = object["brightness"].toDouble(); light.brightness = object["brightness"].toDouble();
@ -162,21 +155,9 @@ void LifxCloud::listScenes()
QNetworkReply *reply = m_networkManager->get(request); QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [reply, this] { connect(reply, &QNetworkReply::finished, this, [reply, this] {
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if(!checkHttpStatusCode(reply)) {
// check HTTP status code
if (status != 200) {
qCWarning(dcLifx()) << "Error get scene list" << status << reply->errorString();
return; return;
} }
if (!m_authenticated) {
m_authenticated = true;
emit authenticationChanged(true);
}
if (!m_connected) {
m_connected = true;
emit authenticationChanged(true);
}
QByteArray rawData = reply->readAll(); QByteArray rawData = reply->readAll();
qCDebug(dcLifx()) << "Got list scenes reply" << rawData; qCDebug(dcLifx()) << "Got list scenes reply" << rawData;
QJsonDocument data; QJsonParseError error; QJsonDocument data; QJsonParseError error;
@ -202,27 +183,27 @@ void LifxCloud::listScenes()
int LifxCloud::setPower(const QString &lightId, bool power, int duration) int LifxCloud::setPower(const QString &lightId, bool power, int duration)
{ {
return setState(lightId, StatePower, power, duration); return setState("id:"+lightId, StatePower, power, duration);
} }
int LifxCloud::setBrightnesss(const QString &lightId, int brightness, int duration) int LifxCloud::setBrightnesss(const QString &lightId, int brightness, int duration)
{ {
return setState(lightId, StateBrightness, brightness/100.00, duration); return setState("id:"+lightId, StateBrightness, brightness/100.00, duration);
} }
int LifxCloud::setColor(const QString &lightId, QColor color, int duration) int LifxCloud::setColor(const QString &lightId, QColor color, int duration)
{ {
return setState(lightId, StateColor, color.name(), duration); return setState("id:"+lightId, StateColor, color.name(), duration);
} }
int LifxCloud::setColorTemperature(const QString &selector, int kelvin, int duration) int LifxCloud::setColorTemperature(const QString &lightId, int kelvin, int duration)
{ {
return setState(selector, StateColorTemperature, kelvin, duration); return setState("id:"+lightId, StateColorTemperature, kelvin, duration);
} }
int LifxCloud::setInfrared(const QString &lightId, int infrared, int duration) int LifxCloud::setInfrared(const QString &lightId, int infrared, int duration)
{ {
return setState(lightId, StateColor, infrared/100.00, duration); return setState("id:"+lightId, StateColor, infrared/100.00, duration);
} }
int LifxCloud::activateScene(const QString &sceneId) int LifxCloud::activateScene(const QString &sceneId)
@ -240,34 +221,59 @@ int LifxCloud::activateScene(const QString &sceneId)
QNetworkReply *reply = m_networkManager->put(request, ""); QNetworkReply *reply = m_networkManager->put(request, "");
connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] { connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] {
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); emit requestExecuted(requestId, checkHttpStatusCode(reply));
// check HTTP status code
if (status == 401 || status == 403) {
if (m_authenticated) {
m_authenticated = false;
emit authenticationChanged(false);
}
}
if (status > 207) {
qCWarning(dcLifx()) << "Error activate Scene" << status << reply->errorString();
emit requestExecuted(requestId, false);
return;
}
if (!m_authenticated) {
m_authenticated = true;
emit authenticationChanged(true);
}
if (!m_connected) {
m_connected = true;
emit authenticationChanged(true);
}
emit requestExecuted(requestId, true);
QByteArray rawData = reply->readAll(); QByteArray rawData = reply->readAll();
qCDebug(dcLifx()) << "Got activate scene reply" << rawData; qCDebug(dcLifx()) << "Got activate scene reply" << rawData;
}); });
return requestId; return requestId;
} }
int LifxCloud::setEffect(const QString &lightId, LifxCloud::Effect effect, QColor color)
{
if (m_authorizationToken.isEmpty()) {
qCWarning(dcLifx()) << "Authorization token is not set";
return -1;
}
int requestId = qrand();
QNetworkRequest request;
QJsonObject payload;
switch (effect) {
case LifxCloud::EffectNone:
request.setUrl(QUrl(QString("https://api.lifx.com/v1/lights/id:%1/effects/off").arg(lightId)));
break;
case LifxCloud::EffectBreathe:
request.setUrl(QUrl(QString("https://api.lifx.com/v1/lights/id:%1/effects/breathe").arg(lightId)));
payload["color"] = color.name();
break;
case LifxCloud::EffectMove:
request.setUrl(QUrl(QString("https://api.lifx.com/v1/lights/id:%1/effects/move").arg(lightId)));
break;
case LifxCloud::EffectMorph:
request.setUrl(QUrl(QString("https://api.lifx.com/v1/lights/id:%1/effects/morph").arg(lightId)));
break;
case LifxCloud::EffectFlame:
request.setUrl(QUrl(QString("https://api.lifx.com/v1/lights/id:%1/effects/flame").arg(lightId)));
break;
case LifxCloud::EffectPulse:
request.setUrl(QUrl(QString("https://api.lifx.com/v1/lights/id:%1/effects/pulse").arg(lightId)));
payload["color"] = color.name();
break;
}
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization","Bearer "+m_authorizationToken);
QJsonDocument doc;
doc.setObject(payload);
QNetworkReply *reply = m_networkManager->put(request, doc.toJson());
connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] {
QByteArray rawData = reply->readAll();
qCDebug(dcLifx()) << "Got set state reply" << rawData;
emit requestExecuted(requestId, checkHttpStatusCode(reply));
});
return requestId;
}
int LifxCloud::setState(const QString &selector, State state, QVariant stateValue, int duration) int LifxCloud::setState(const QString &selector, State state, QVariant stateValue, int duration)
{ {
if (m_authorizationToken.isEmpty()) { if (m_authorizationToken.isEmpty()) {
@ -275,7 +281,6 @@ int LifxCloud::setState(const QString &selector, State state, QVariant stateValu
return -1; return -1;
} }
int requestId = qrand(); int requestId = qrand();
QNetworkRequest request; QNetworkRequest request;
request.setUrl(QUrl(QString("https://api.lifx.com/v1/lights/%1/state").arg(selector))); request.setUrl(QUrl(QString("https://api.lifx.com/v1/lights/%1/state").arg(selector)));
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
@ -287,45 +292,77 @@ int LifxCloud::setState(const QString &selector, State state, QVariant stateValu
switch (state) { switch (state) {
case StatePower: case StatePower:
if (stateValue.toBool()) if (stateValue.toBool())
payload["power"] = "ON"; payload["power"] = "on";
else else
payload["power"] = "OFF"; payload["power"] = "off";
qCDebug(dcLifx()) << "Set state power" << stateValue.toBool();
break; break;
case StateBrightness: case StateBrightness:
payload["brightness"] = stateValue.toDouble(); payload["brightness"] = stateValue.toDouble();
qCDebug(dcLifx()) << "Set state brightness" << stateValue;
break; break;
case StateColor: case StateColor:
payload["color"] = stateValue.toString(); payload["color"] = stateValue.toString();
qCDebug(dcLifx()) << "Set state color" << stateValue;
break; break;
case StateColorTemperature: case StateColorTemperature:
payload["color"] = "kelvin:"+stateValue.toString(); payload["color"] = "kelvin:"+stateValue.toString();
qCDebug(dcLifx()) << "Set state color" << stateValue;
break; break;
case StateInfrared: case StateInfrared:
payload["infrared"] = stateValue.toDouble(); payload["infrared"] = stateValue.toDouble();
qCDebug(dcLifx()) << "Set state infrared" << stateValue;
} }
doc.setObject(payload); doc.setObject(payload);
QNetworkReply *reply = m_networkManager->post(request, doc.toJson()); qCDebug(dcLifx()) << "Set state request" << request.url() << doc.toJson();
QNetworkReply *reply = m_networkManager->put(request, doc.toJson());
connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] { connect(reply, &QNetworkReply::finished, this, [requestId, duration,reply, this] {
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// check HTTP status code
if (status > 207) {
qCWarning(dcLifx()) << "Error get scene list" << status << reply->errorString();
emit requestExecuted(requestId, false);
return;
}
if (!m_authenticated) {
m_authenticated = true;
emit authenticationChanged(true);
}
if (!m_connected) {
m_connected = true;
emit authenticationChanged(true);
}
QByteArray rawData = reply->readAll(); QByteArray rawData = reply->readAll();
qCDebug(dcLifx()) << "Got set state reply" << rawData; qCDebug(dcLifx()) << "Got set state reply" << rawData;
emit requestExecuted(requestId, true); if (checkHttpStatusCode(reply)) {
emit requestExecuted(requestId, true);
QTimer::singleShot(duration*1000+500, this, [=] {listLights();});
} else {
emit requestExecuted(requestId, false);
}
}); });
return requestId; return requestId;
} }
bool LifxCloud::checkHttpStatusCode(QNetworkReply *reply)
{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcLifx()) << "Request error:" << status << reply->errorString();
if (m_connected) {
m_connected = false;
emit connectionChanged(false);
}
return false;
}
// check HTTP status code
if (status == 401 || status == 403) {
if (m_authenticated) {
m_authenticated = false;
emit authenticationChanged(false);
}
}
if (status > 207) {
qCWarning(dcLifx()) << "Error get scene list" << status;
return false;
}
if (!m_authenticated) {
m_authenticated = true;
emit authenticationChanged(true);
}
if (!m_connected) {
m_connected = true;
emit connectionChanged(true);
}
return true;
}

View File

@ -47,6 +47,16 @@ public:
StateColorTemperature, StateColorTemperature,
StateInfrared StateInfrared
}; };
enum Effect {
EffectNone,
EffectBreathe,
EffectMove,
EffectMorph,
EffectFlame,
EffectPulse
};
struct Group { struct Group {
QByteArray id; QByteArray id;
QString name; QString name;
@ -101,19 +111,22 @@ public:
void listLights(); void listLights();
void listScenes(); void listScenes();
int setPower(const QString &lightId, bool power, int duration = 3); int setPower(const QString &lightId, bool power, int duration = 5);
int setBrightnesss(const QString &lightId, int brightness, int duration = 3); int setBrightnesss(const QString &lightId, int brightness, int duration = 5);
int setColor(const QString &selector, QColor color, int duration = 3); int setColor(const QString &lightId, QColor color, int duration = 5);
int setColorTemperature(const QString &selector, int kelvin, int duration = 3); int setColorTemperature(const QString &lightId, int kelvin, int duration = 5);
int setInfrared(const QString &lightId, int infrared, int duration = 3); int setInfrared(const QString &lightId, int infrared, int duration = 5);
int activateScene(const QString &sceneId); int activateScene(const QString &sceneId);
int setEffect(const QString &lightId, Effect effect, QColor color);
private: private:
NetworkAccessManager *m_networkManager = nullptr; NetworkAccessManager *m_networkManager = nullptr;
QByteArray m_authorizationToken; QByteArray m_authorizationToken;
int setState(const QString &lightId, State state, QVariant stateValue, int duration); int setState(const QString &lightId, State state, QVariant stateValue, int duration);
bool checkHttpStatusCode(QNetworkReply *reply);
bool m_authenticated = false; bool m_authenticated = false;
bool m_connected = false; bool m_connected = false;
signals: signals:

View File

@ -29,20 +29,16 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "lifx.h" #include "lifxlan.h"
#include "extern-plugininfo.h" #include "extern-plugininfo.h"
#include <QColor> #include <QColor>
LifxLan::LifxLan(const QHostAddress &address, quint16 port, QObject *parent) :
Lifx::Lifx(const QHostAddress &address, quint16 port, QObject *parent) :
QObject(parent), QObject(parent),
m_host(address), m_host(address),
m_port(port) m_port(port)
{ {
m_reconnectTimer = new QTimer(this);
m_reconnectTimer->setSingleShot(true);
connect(m_reconnectTimer, &QTimer::timeout, this, &Lifx::onReconnectTimer);
m_clientId = qrand(); m_clientId = qrand();
m_socket = new QUdpSocket(this); m_socket = new QUdpSocket(this);
@ -51,7 +47,7 @@ Lifx::Lifx(const QHostAddress &address, quint16 port, QObject *parent) :
m_socket->setSocketOption(QAbstractSocket::MulticastLoopbackOption, QVariant(1)); m_socket->setSocketOption(QAbstractSocket::MulticastLoopbackOption, QVariant(1));
} }
Lifx::~Lifx() LifxLan::~LifxLan()
{ {
if (m_socket) { if (m_socket) {
m_socket->waitForBytesWritten(1000); m_socket->waitForBytesWritten(1000);
@ -59,7 +55,7 @@ Lifx::~Lifx()
} }
} }
bool Lifx::enable() bool LifxLan::enable()
{ {
// Bind udp socket and join multicast group // Bind udp socket and join multicast group
if(!m_socket->bind(QHostAddress::AnyIPv4, m_port, QUdpSocket::ShareAddress)){ if(!m_socket->bind(QHostAddress::AnyIPv4, m_port, QUdpSocket::ShareAddress)){
@ -75,21 +71,21 @@ bool Lifx::enable()
m_socket = nullptr; m_socket = nullptr;
return false; return false;
} }
connect(m_socket, &QUdpSocket::readyRead, this, &Lifx::onReadyRead); connect(m_socket, &QUdpSocket::readyRead, this, &LifxLan::onReadyRead);
return true; return true;
} }
void Lifx::setHostAddress(const QHostAddress &address) void LifxLan::setHostAddress(const QHostAddress &address)
{ {
m_host = address; m_host = address;
} }
void Lifx::setPort(quint16 port) void LifxLan::setPort(quint16 port)
{ {
m_port = port; m_port = port;
} }
int Lifx::setColorTemperature(uint mirad, uint msFadeTime) int LifxLan::setColorTemperature(uint mirad, uint msFadeTime)
{ {
Q_UNUSED(mirad) Q_UNUSED(mirad)
Q_UNUSED(msFadeTime) Q_UNUSED(msFadeTime)
@ -99,59 +95,48 @@ int Lifx::setColorTemperature(uint mirad, uint msFadeTime)
return requestId; return requestId;
} }
int Lifx::setColor(QColor color, uint msFadeTime) int LifxLan::setColor(QColor color, uint msFadeTime)
{ {
Q_UNUSED(color) Q_UNUSED(color)
Q_UNUSED(msFadeTime) Q_UNUSED(msFadeTime)
int requestId = qrand(); int requestId = qrand();
Message message; Message message;
//TODO create LAN message
sendMessage(message); sendMessage(message);
return requestId; return requestId;
} }
int Lifx::setBrightness(uint percentage, uint msFadeTime) int LifxLan::setBrightness(uint percentage, uint msFadeTime)
{ {
Q_UNUSED(percentage) Q_UNUSED(percentage)
Q_UNUSED(msFadeTime) Q_UNUSED(msFadeTime)
int requestId = qrand(); int requestId = qrand();
Message message; Message message;
sendMessage(message); sendMessage(message);
//TODO create LAN message
return requestId; return requestId;
} }
int Lifx::setPower(bool power, uint msFadeTime) int LifxLan::setPower(bool power, uint msFadeTime)
{ {
Q_UNUSED(power) Q_UNUSED(power)
Q_UNUSED(msFadeTime) Q_UNUSED(msFadeTime)
int requestId = qrand(); int requestId = qrand();
Message message; Message message;
sendMessage(message); sendMessage(message);
//TODO create LAN message
return requestId; return requestId;
} }
int Lifx::flash() void LifxLan::sendMessage(const LifxLan::Message &message)
{
int requestId = qrand();
return requestId;
}
int Lifx::flash15s()
{
int requestId = qrand();
return requestId;
}
void Lifx::sendMessage(const Lifx::Message &message)
{ {
QByteArray header; QByteArray header;
// -- FRAME -- // -- FRAME --
// Protocol number: must be 1024 (decimal) // Protocol number: must be 1024 (decimal)
quint16 protocol = 1024; quint16 protocol = 1024;
protocol |= (0x0001 << 4); //Message includes a target address: must be one (1) protocol |= (0x0001 << 4); //Message includes a target address: must be one (1)
protocol |= (message.frame.Tagged << 5); // Determines usage of the Frame Address target field protocol |= (message.frame.Tagged << 5); // Determines usage of the Frame Address target field
protocol &= ~(0x0003); // Message origin indicator: must be zero (0) protocol &= ~(0x0003); // Message origin indicator: must be zero (0)
header.append(protocol >> 8); header.append(protocol >> 8);
header.append(protocol & 0xff); header.append(protocol & 0xff);
@ -188,15 +173,12 @@ void Lifx::sendMessage(const Lifx::Message &message)
header.append(2, '0'); header.append(2, '0');
QByteArray fullMessage; QByteArray fullMessage;
//message.append(header); //fullMessage = QByteArray::fromHex("0x310000340000000000000000000000000000000000000000000000000000000066000000005555FFFFFFFFAC0D00040000"); // test message - set all lights green
//message.append(payload); //std::reverse(fullMessage.begin(), fullMessage.end());
//message.append(message.length());
fullMessage = QByteArray::fromHex("0x310000340000000000000000000000000000000000000000000000000000000066000000005555FFFFFFFFAC0D00040000");
std::reverse(fullMessage.begin(), fullMessage.end());
m_socket->writeDatagram(fullMessage, m_host, m_port); m_socket->writeDatagram(fullMessage, m_host, m_port);
} }
void Lifx::onStateChanged(QAbstractSocket::SocketState state) void LifxLan::onStateChanged(QAbstractSocket::SocketState state)
{ {
switch (state) { switch (state) {
case QAbstractSocket::SocketState::ConnectedState: case QAbstractSocket::SocketState::ConnectedState:
@ -212,13 +194,8 @@ void Lifx::onStateChanged(QAbstractSocket::SocketState state)
} }
} }
void Lifx::onReadyRead() void LifxLan::onReadyRead()
{ {
QByteArray data = m_socket->readAll(); QByteArray data = m_socket->readAll();
qCDebug(dcLifx()) << "Message received" << data; qCDebug(dcLifx()) << "Message received" << data;
} }
void Lifx::onReconnectTimer()
{
}

View File

@ -28,8 +28,8 @@
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef LIFX_H #ifndef LIFXLAN_H
#define LIFX_H #define LIFXLAN_H
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
@ -40,7 +40,7 @@
#include <QColor> #include <QColor>
class Lifx : public QObject class LifxLan : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -122,8 +122,8 @@ public:
bool chain; bool chain;
}; };
explicit Lifx(const QHostAddress &address, quint16 port = 56700, QObject *parent = nullptr); explicit LifxLan(const QHostAddress &address, quint16 port = 56700, QObject *parent = nullptr);
~Lifx(); ~LifxLan();
bool enable(); bool enable();
void setHostAddress(const QHostAddress &address); void setHostAddress(const QHostAddress &address);
void setPort(quint16 port); void setPort(quint16 port);
@ -132,8 +132,6 @@ public:
int setColor(QColor color, uint msFadeTime = 500); int setColor(QColor color, uint msFadeTime = 500);
int setBrightness(uint percentage, uint msFadeTime = 500); int setBrightness(uint percentage, uint msFadeTime = 500);
int setPower(bool power, uint msFadeTime = 500); int setPower(bool power, uint msFadeTime = 500);
int flash();
int flash15s();
private: private:
quint32 m_clientId = 0; quint32 m_clientId = 0;
@ -148,19 +146,9 @@ private:
private slots: private slots:
void onStateChanged(QAbstractSocket::SocketState state); void onStateChanged(QAbstractSocket::SocketState state);
void onReadyRead(); void onReadyRead();
void onReconnectTimer();
signals: signals:
void connectionChanged(bool connected); void connectionChanged(bool connected);
void requestExecuted(int requestId, bool success); void requestExecuted(int requestId, bool success);
//void errorReceived(int code, const QString &message);
//void powerNotificationReceived(bool status);
//void brightnessNotificationReceived(int percentage);
//void colorTemperatureNotificationReceived(int kelvin);
//void rgbNotificationReceived(QRgb rgbColor);
//void hueNotificationReceived(int hueColor);
//void nameNotificationReceived(const QString &name);
//void saturationNotificationReceived(int percentage);
}; };
#endif // LIFX_H #endif // LIFXLAN_H

View File

@ -3,7 +3,7 @@
"tagline": "Control LIFX light bulbs.", "tagline": "Control LIFX light bulbs.",
"icon": "lifx.png", "icon": "lifx.png",
"stability": "consumer", "stability": "consumer",
"offline": true, "offline": false,
"technologies": [ "technologies": [
"network" "network"
], ],

View File

@ -1,6 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS> <!DOCTYPE TS>
<TS version="2.1" language="de"> <TS version="2.1" language="de">
<context>
<name>IntegrationPluginLifx</name>
<message>
<source>LIFX server is not reachable.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This token is invalid.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>Lifx</name> <name>Lifx</name>
<message> <message>
@ -68,18 +79,6 @@ The name of the EventType ({dd7d7e70-5552-4531-8789-2d0f750488be}) of ThingClass
<extracomment>The name of the ThingClass ({a5b02af8-7c97-4a78-9c78-bafee7407b5e})</extracomment> <extracomment>The name of the ThingClass ({a5b02af8-7c97-4a78-9c78-bafee7407b5e})</extracomment>
<translation>Tag und Sonnenaufgang</translation> <translation>Tag und Sonnenaufgang</translation>
</message> </message>
<message>
<source>Host address</source>
<extracomment>The name of the ParamType (ThingClass: dimmableBulb, Type: thing, ID: {cc0a765b-a753-4e07-a6e5-47e9272c4346})
----------
The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {fd1c4817-5111-433a-b5b9-fd9f49d4975c})</extracomment>
<translation>Adresse</translation>
</message>
<message>
<source>Id</source>
<extracomment>The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {976ecea0-ac25-47d4-9dc5-362962ddb6c0})</extracomment>
<translation>ID</translation>
</message>
<message> <message>
<source>LIFX</source> <source>LIFX</source>
<extracomment>The name of the vendor ({e5e48c0d-cff7-4c0f-983e-d23bd3e4ba87}) <extracomment>The name of the vendor ({e5e48c0d-cff7-4c0f-983e-d23bd3e4ba87})
@ -87,13 +86,6 @@ The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {fd1c4817-511
The name of the plugin Lifx ({4e00ee30-79e2-447b-8dcc-c34470f41992})</extracomment> The name of the plugin Lifx ({4e00ee30-79e2-447b-8dcc-c34470f41992})</extracomment>
<translation>LIFX</translation> <translation>LIFX</translation>
</message> </message>
<message>
<source>Port</source>
<extracomment>The name of the ParamType (ThingClass: dimmableBulb, Type: thing, ID: {d233d9bf-6662-414d-92f6-dd3e267051b5})
----------
The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {44c13745-300c-491f-b617-3a8d53472998})</extracomment>
<translation>Port</translation>
</message>
<message> <message>
<source>Power</source> <source>Power</source>
<extracomment>The name of the ParamType (ThingClass: dimmableBulb, ActionType: power, ID: {9e1344ea-cd05-4dd8-8948-8d2f5e00e1b0}) <extracomment>The name of the ParamType (ThingClass: dimmableBulb, ActionType: power, ID: {9e1344ea-cd05-4dd8-8948-8d2f5e00e1b0})
@ -148,7 +140,9 @@ The name of the ActionType ({dd7d7e70-5552-4531-8789-2d0f750488be}) of ThingClas
</message> </message>
<message> <message>
<source>Set effect</source> <source>Set effect</source>
<extracomment>The name of the ActionType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass colorBulb</extracomment> <extracomment>The name of the ActionType ({be47c474-eca1-479e-9393-68281a43d72a}) of ThingClass dimmableBulb
----------
The name of the ActionType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass colorBulb</extracomment>
<translation>Setze Effekt</translation> <translation>Setze Effekt</translation>
</message> </message>
<message> <message>
@ -160,7 +154,13 @@ The name of the ActionType ({12de3f8f-2454-4057-aa12-9290296fdbdd}) of ThingClas
</message> </message>
<message> <message>
<source>Effect</source> <source>Effect</source>
<extracomment>The name of the ParamType (ThingClass: colorBulb, ActionType: effect, ID: {65f88396-2958-480e-b0be-c4695400a343}) <extracomment>The name of the ParamType (ThingClass: dimmableBulb, ActionType: effect, ID: {be47c474-eca1-479e-9393-68281a43d72a})
----------
The name of the ParamType (ThingClass: dimmableBulb, EventType: effect, ID: {be47c474-eca1-479e-9393-68281a43d72a})
----------
The name of the StateType ({be47c474-eca1-479e-9393-68281a43d72a}) of ThingClass dimmableBulb
----------
The name of the ParamType (ThingClass: colorBulb, ActionType: effect, ID: {65f88396-2958-480e-b0be-c4695400a343})
---------- ----------
The name of the ParamType (ThingClass: colorBulb, EventType: effect, ID: {65f88396-2958-480e-b0be-c4695400a343}) The name of the ParamType (ThingClass: colorBulb, EventType: effect, ID: {65f88396-2958-480e-b0be-c4695400a343})
---------- ----------
@ -169,12 +169,16 @@ The name of the StateType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass
</message> </message>
<message> <message>
<source>Effect changed</source> <source>Effect changed</source>
<extracomment>The name of the EventType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass colorBulb</extracomment> <extracomment>The name of the EventType ({be47c474-eca1-479e-9393-68281a43d72a}) of ThingClass dimmableBulb
----------
The name of the EventType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass colorBulb</extracomment>
<translation>Effekt geändert</translation> <translation>Effekt geändert</translation>
</message> </message>
<message> <message>
<source>ID</source> <source>ID</source>
<extracomment>The name of the ParamType (ThingClass: dimmableBulb, Type: thing, ID: {f157a97b-3fe5-4d9e-b5e3-5636f80d46ed})</extracomment> <extracomment>The name of the ParamType (ThingClass: dimmableBulb, Type: thing, ID: {f157a97b-3fe5-4d9e-b5e3-5636f80d46ed})
----------
The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {976ecea0-ac25-47d4-9dc5-362962ddb6c0})</extracomment>
<translation>ID</translation> <translation>ID</translation>
</message> </message>
<message> <message>
@ -184,5 +188,46 @@ The name of the StateType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass
The name of the ActionType ({8bd20350-0e79-45dc-b68a-84da99356863}) of ThingClass colorBulb</extracomment> The name of the ActionType ({8bd20350-0e79-45dc-b68a-84da99356863}) of ThingClass colorBulb</extracomment>
<translation>Setze Helligkeit</translation> <translation>Setze Helligkeit</translation>
</message> </message>
<message>
<source>Connected</source>
<extracomment>The name of the ParamType (ThingClass: lifxAccount, EventType: connected, ID: {3e7b358b-d7de-4db4-8a3a-b9860eae186f})
----------
The name of the StateType ({3e7b358b-d7de-4db4-8a3a-b9860eae186f}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Connected changed</source>
<extracomment>The name of the EventType ({3e7b358b-d7de-4db4-8a3a-b9860eae186f}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>LIFX cloud account</source>
<extracomment>The name of the ThingClass ({387c87f6-3e5b-4d6a-ba4d-372d0efad79f})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Logged in</source>
<extracomment>The name of the ParamType (ThingClass: lifxAccount, EventType: loggedIn, ID: {0db34069-5de0-4233-baec-27f039228524})
----------
The name of the StateType ({0db34069-5de0-4233-baec-27f039228524}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Logged in changed</source>
<extracomment>The name of the EventType ({0db34069-5de0-4233-baec-27f039228524}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>User name</source>
<extracomment>The name of the ParamType (ThingClass: lifxAccount, EventType: userDisplayName, ID: {554afd9b-a2ec-4d28-9065-2b9ab3a9e3b2})
----------
The name of the StateType ({554afd9b-a2ec-4d28-9065-2b9ab3a9e3b2}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>User name changed</source>
<extracomment>The name of the EventType ({554afd9b-a2ec-4d28-9065-2b9ab3a9e3b2}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View File

@ -1,6 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS> <!DOCTYPE TS>
<TS version="2.1"> <TS version="2.1">
<context>
<name>IntegrationPluginLifx</name>
<message>
<source>LIFX server is not reachable.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This token is invalid.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>Lifx</name> <name>Lifx</name>
<message> <message>
@ -68,18 +79,6 @@ The name of the EventType ({dd7d7e70-5552-4531-8789-2d0f750488be}) of ThingClass
<extracomment>The name of the ThingClass ({a5b02af8-7c97-4a78-9c78-bafee7407b5e})</extracomment> <extracomment>The name of the ThingClass ({a5b02af8-7c97-4a78-9c78-bafee7407b5e})</extracomment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Host address</source>
<extracomment>The name of the ParamType (ThingClass: dimmableBulb, Type: thing, ID: {cc0a765b-a753-4e07-a6e5-47e9272c4346})
----------
The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {fd1c4817-5111-433a-b5b9-fd9f49d4975c})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Id</source>
<extracomment>The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {976ecea0-ac25-47d4-9dc5-362962ddb6c0})</extracomment>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>LIFX</source> <source>LIFX</source>
<extracomment>The name of the vendor ({e5e48c0d-cff7-4c0f-983e-d23bd3e4ba87}) <extracomment>The name of the vendor ({e5e48c0d-cff7-4c0f-983e-d23bd3e4ba87})
@ -87,13 +86,6 @@ The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {fd1c4817-511
The name of the plugin Lifx ({4e00ee30-79e2-447b-8dcc-c34470f41992})</extracomment> The name of the plugin Lifx ({4e00ee30-79e2-447b-8dcc-c34470f41992})</extracomment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Port</source>
<extracomment>The name of the ParamType (ThingClass: dimmableBulb, Type: thing, ID: {d233d9bf-6662-414d-92f6-dd3e267051b5})
----------
The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {44c13745-300c-491f-b617-3a8d53472998})</extracomment>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>Power</source> <source>Power</source>
<extracomment>The name of the ParamType (ThingClass: dimmableBulb, ActionType: power, ID: {9e1344ea-cd05-4dd8-8948-8d2f5e00e1b0}) <extracomment>The name of the ParamType (ThingClass: dimmableBulb, ActionType: power, ID: {9e1344ea-cd05-4dd8-8948-8d2f5e00e1b0})
@ -148,7 +140,9 @@ The name of the ActionType ({dd7d7e70-5552-4531-8789-2d0f750488be}) of ThingClas
</message> </message>
<message> <message>
<source>Set effect</source> <source>Set effect</source>
<extracomment>The name of the ActionType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass colorBulb</extracomment> <extracomment>The name of the ActionType ({be47c474-eca1-479e-9393-68281a43d72a}) of ThingClass dimmableBulb
----------
The name of the ActionType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass colorBulb</extracomment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
@ -160,7 +154,13 @@ The name of the ActionType ({12de3f8f-2454-4057-aa12-9290296fdbdd}) of ThingClas
</message> </message>
<message> <message>
<source>Effect</source> <source>Effect</source>
<extracomment>The name of the ParamType (ThingClass: colorBulb, ActionType: effect, ID: {65f88396-2958-480e-b0be-c4695400a343}) <extracomment>The name of the ParamType (ThingClass: dimmableBulb, ActionType: effect, ID: {be47c474-eca1-479e-9393-68281a43d72a})
----------
The name of the ParamType (ThingClass: dimmableBulb, EventType: effect, ID: {be47c474-eca1-479e-9393-68281a43d72a})
----------
The name of the StateType ({be47c474-eca1-479e-9393-68281a43d72a}) of ThingClass dimmableBulb
----------
The name of the ParamType (ThingClass: colorBulb, ActionType: effect, ID: {65f88396-2958-480e-b0be-c4695400a343})
---------- ----------
The name of the ParamType (ThingClass: colorBulb, EventType: effect, ID: {65f88396-2958-480e-b0be-c4695400a343}) The name of the ParamType (ThingClass: colorBulb, EventType: effect, ID: {65f88396-2958-480e-b0be-c4695400a343})
---------- ----------
@ -169,12 +169,16 @@ The name of the StateType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass
</message> </message>
<message> <message>
<source>Effect changed</source> <source>Effect changed</source>
<extracomment>The name of the EventType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass colorBulb</extracomment> <extracomment>The name of the EventType ({be47c474-eca1-479e-9393-68281a43d72a}) of ThingClass dimmableBulb
----------
The name of the EventType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass colorBulb</extracomment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>ID</source> <source>ID</source>
<extracomment>The name of the ParamType (ThingClass: dimmableBulb, Type: thing, ID: {f157a97b-3fe5-4d9e-b5e3-5636f80d46ed})</extracomment> <extracomment>The name of the ParamType (ThingClass: dimmableBulb, Type: thing, ID: {f157a97b-3fe5-4d9e-b5e3-5636f80d46ed})
----------
The name of the ParamType (ThingClass: colorBulb, Type: thing, ID: {976ecea0-ac25-47d4-9dc5-362962ddb6c0})</extracomment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
@ -184,5 +188,46 @@ The name of the StateType ({65f88396-2958-480e-b0be-c4695400a343}) of ThingClass
The name of the ActionType ({8bd20350-0e79-45dc-b68a-84da99356863}) of ThingClass colorBulb</extracomment> The name of the ActionType ({8bd20350-0e79-45dc-b68a-84da99356863}) of ThingClass colorBulb</extracomment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Connected</source>
<extracomment>The name of the ParamType (ThingClass: lifxAccount, EventType: connected, ID: {3e7b358b-d7de-4db4-8a3a-b9860eae186f})
----------
The name of the StateType ({3e7b358b-d7de-4db4-8a3a-b9860eae186f}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Connected changed</source>
<extracomment>The name of the EventType ({3e7b358b-d7de-4db4-8a3a-b9860eae186f}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>LIFX cloud account</source>
<extracomment>The name of the ThingClass ({387c87f6-3e5b-4d6a-ba4d-372d0efad79f})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Logged in</source>
<extracomment>The name of the ParamType (ThingClass: lifxAccount, EventType: loggedIn, ID: {0db34069-5de0-4233-baec-27f039228524})
----------
The name of the StateType ({0db34069-5de0-4233-baec-27f039228524}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Logged in changed</source>
<extracomment>The name of the EventType ({0db34069-5de0-4233-baec-27f039228524}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>User name</source>
<extracomment>The name of the ParamType (ThingClass: lifxAccount, EventType: userDisplayName, ID: {554afd9b-a2ec-4d28-9065-2b9ab3a9e3b2})
----------
The name of the StateType ({554afd9b-a2ec-4d28-9065-2b9ab3a9e3b2}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>User name changed</source>
<extracomment>The name of the EventType ({554afd9b-a2ec-4d28-9065-2b9ab3a9e3b2}) of ThingClass lifxAccount</extracomment>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>