Tuya: Add light devices
parent
64dfb1beb7
commit
ea2171cea2
|
|
@ -49,15 +49,25 @@
|
||||||
const int queryInterval = 130;
|
const int queryInterval = 130;
|
||||||
const int discoveryInterval = 610;
|
const int discoveryInterval = 610;
|
||||||
|
|
||||||
|
QHash<ThingClassId, ParamTypeId> idParamTypeIdsMap = {
|
||||||
|
{tuyaClosableThingClassId, tuyaClosableThingIdParamTypeId},
|
||||||
|
{tuyaSwitchThingClassId, tuyaSwitchThingIdParamTypeId},
|
||||||
|
{tuyaLightThingClassId, tuyaLightThingIdParamTypeId}
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<ThingClassId, StateTypeId> connectedStateTypeIdsMap = {
|
||||||
|
{tuyaClosableThingClassId, tuyaClosableConnectedStateTypeId},
|
||||||
|
{tuyaSwitchThingClassId, tuyaSwitchConnectedStateTypeId},
|
||||||
|
{tuyaLightThingClassId, tuyaLightConnectedStateTypeId}
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<ActionTypeId, ThingClassId> powerStateTypeIdsMap = {
|
||||||
|
{tuyaSwitchThingClassId, tuyaSwitchPowerStateTypeId},
|
||||||
|
{tuyaLightThingClassId, tuyaLightPowerStateTypeId}
|
||||||
|
};
|
||||||
|
|
||||||
IntegrationPluginTuya::IntegrationPluginTuya(QObject *parent): IntegrationPlugin(parent)
|
IntegrationPluginTuya::IntegrationPluginTuya(QObject *parent): IntegrationPlugin(parent)
|
||||||
{
|
{
|
||||||
m_devIdParamTypeIdsMap[tuyaClosableThingClassId] = tuyaClosableThingIdParamTypeId;
|
|
||||||
m_devIdParamTypeIdsMap[tuyaSwitchThingClassId] = tuyaSwitchThingIdParamTypeId;
|
|
||||||
|
|
||||||
m_connectedStateTypeIdsMap[tuyaClosableThingClassId] = tuyaClosableConnectedStateTypeId;
|
|
||||||
m_connectedStateTypeIdsMap[tuyaSwitchThingClassId] = tuyaSwitchConnectedStateTypeId;
|
|
||||||
|
|
||||||
m_powerStateTypeIdsMap[tuyaSwitchThingClassId] = tuyaSwitchPowerStateTypeId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegrationPluginTuya::~IntegrationPluginTuya()
|
IntegrationPluginTuya::~IntegrationPluginTuya()
|
||||||
|
|
@ -236,19 +246,20 @@ void IntegrationPluginTuya::confirmPairing(ThingPairingInfo *info, const QString
|
||||||
|
|
||||||
void IntegrationPluginTuya::executeAction(ThingActionInfo *info)
|
void IntegrationPluginTuya::executeAction(ThingActionInfo *info)
|
||||||
{
|
{
|
||||||
if (info->action().actionTypeId() == tuyaSwitchPowerActionTypeId) {
|
QString devId = info->thing()->paramValue(idParamTypeIdsMap.value(info->thing()->thingClassId())).toString();
|
||||||
bool on = info->action().param(tuyaSwitchPowerActionPowerParamTypeId).value().toBool();
|
|
||||||
QString devId = info->thing()->paramValue(tuyaSwitchThingIdParamTypeId).toString();
|
if (powerStateTypeIdsMap.values().contains(info->action().actionTypeId())) {
|
||||||
|
bool on = info->action().paramValue(info->action().actionTypeId()).toBool();
|
||||||
controlTuyaSwitch(devId, "turnOnOff", on ? "1" : "0", info);
|
controlTuyaSwitch(devId, "turnOnOff", on ? "1" : "0", info);
|
||||||
connect(info, &ThingActionInfo::finished, [info, on](){
|
connect(info, &ThingActionInfo::finished, [info, on](){
|
||||||
if (info->status() == Thing::ThingErrorNoError) {
|
if (info->status() == Thing::ThingErrorNoError) {
|
||||||
info->thing()->setStateValue(tuyaSwitchPowerStateTypeId, on);
|
info->thing()->setStateValue(powerStateTypeIdsMap.value(info->thing()->thingClassId()), on);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->thing()->thingClassId() == tuyaClosableThingClassId) {
|
if (info->thing()->thingClassId() == tuyaClosableThingClassId) {
|
||||||
QString devId = info->thing()->paramValue(tuyaClosableThingIdParamTypeId).toString();
|
|
||||||
if (info->action().actionTypeId() == tuyaClosableOpenActionTypeId) {
|
if (info->action().actionTypeId() == tuyaClosableOpenActionTypeId) {
|
||||||
controlTuyaSwitch(devId, "turnOnOff", "1", info);
|
controlTuyaSwitch(devId, "turnOnOff", "1", info);
|
||||||
return;
|
return;
|
||||||
|
|
@ -261,7 +272,45 @@ void IntegrationPluginTuya::executeAction(ThingActionInfo *info)
|
||||||
controlTuyaSwitch(devId, "startStop", "0", info);
|
controlTuyaSwitch(devId, "startStop", "0", info);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->action().actionTypeId() == tuyaLightBrightnessActionTypeId) {
|
||||||
|
int brightness = info->action().paramValue(tuyaLightBrightnessActionBrightnessParamTypeId).toInt() * 10;
|
||||||
|
controlTuyaSwitch(devId, "brightnessSet", QString::number(qMax(10, brightness)), info);
|
||||||
|
connect(info, &ThingActionInfo::finished, [info, brightness](){
|
||||||
|
if (info->status() == Thing::ThingErrorNoError) {
|
||||||
|
info->thing()->setStateValue(tuyaLightBrightnessStateTypeId, brightness / 10);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->action().actionTypeId() == tuyaLightColorActionTypeId) {
|
||||||
|
QColor color = info->action().paramValue(tuyaLightColorActionColorParamTypeId).value<QColor>();
|
||||||
|
QVariantMap colorMap;
|
||||||
|
colorMap.insert("hue", color.hsvHue());
|
||||||
|
colorMap.insert("saturation", color.hsvSaturation());
|
||||||
|
colorMap.insert("brightness", qMax(10, info->thing()->stateValue(tuyaLightBrightnessStateTypeId).toInt() * 10));
|
||||||
|
controlTuyaSwitch(devId, "colorSet", colorMap, info);
|
||||||
|
connect(info, &ThingActionInfo::finished, [info, color](){
|
||||||
|
if (info->status() == Thing::ThingErrorNoError) {
|
||||||
|
info->thing()->setStateValue(tuyaLightColorStateTypeId, color);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->action().actionTypeId() == tuyaLightColorTemperatureActionTypeId) {
|
||||||
|
int ct = info->action().paramValue(tuyaLightColorTemperatureStateTypeId).toInt();
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("value", ct * 10);
|
||||||
|
controlTuyaSwitch(devId, "colorTemperatureSet", params, info);
|
||||||
|
connect(info, &ThingActionInfo::finished, [info, ct](){
|
||||||
|
if (info->status() == Thing::ThingErrorNoError) {
|
||||||
|
info->thing()->setStateValue(tuyaLightColorTemperatureStateTypeId, ct);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
Q_ASSERT_X(false, "tuyaplugin", "Unhandled action type " + info->action().actionTypeId().toByteArray());
|
Q_ASSERT_X(false, "tuyaplugin", "Unhandled action type " + info->action().actionTypeId().toByteArray());
|
||||||
}
|
}
|
||||||
|
|
@ -406,33 +455,50 @@ void IntegrationPluginTuya::updateChildDevices(Thing *thing)
|
||||||
QString name = deviceMap.value("name").toString();
|
QString name = deviceMap.value("name").toString();
|
||||||
|
|
||||||
if (devType == "switch") {
|
if (devType == "switch") {
|
||||||
bool online = deviceMap.value("data").toMap().value("online").toBool();
|
|
||||||
bool state = deviceMap.value("data").toMap().value("state").toBool();
|
|
||||||
|
|
||||||
Thing *d = myThings().findByParams(ParamList() << Param(tuyaSwitchThingIdParamTypeId, id));
|
Thing *d = myThings().findByParams(ParamList() << Param(tuyaSwitchThingIdParamTypeId, id));
|
||||||
if (d) {
|
if (!d) {
|
||||||
qCDebug(dcTuya()) << "Found existing Tuya switch" << d->name() << id << name << (online ? "online:" : "offline") << (state ? "on": "off");
|
|
||||||
d->setStateValue(tuyaSwitchConnectedStateTypeId, online);
|
|
||||||
d->setStateValue(tuyaSwitchPowerStateTypeId, state);
|
|
||||||
} else {
|
|
||||||
qCDebug(dcTuya()) << "Found new Tuya switch" << id << name;
|
qCDebug(dcTuya()) << "Found new Tuya switch" << id << name;
|
||||||
ThingDescriptor descriptor(tuyaSwitchThingClassId, name, QString(), thing->id());
|
ThingDescriptor descriptor(tuyaSwitchThingClassId, name, QString(), thing->id());
|
||||||
descriptor.setParams(ParamList() << Param(tuyaSwitchThingIdParamTypeId, id));
|
descriptor.setParams(ParamList() << Param(tuyaSwitchThingIdParamTypeId, id));
|
||||||
unknownDevices.append(descriptor);
|
unknownDevices.append(descriptor);
|
||||||
}
|
|
||||||
} else if (devType == "cover") {
|
|
||||||
bool online = deviceMap.value("data").toMap().value("online").toBool();
|
|
||||||
|
|
||||||
Thing *d = myThings().findByParams(ParamList() << Param(tuyaClosableThingIdParamTypeId, id));
|
|
||||||
if (d) {
|
|
||||||
qCDebug(dcTuya()) << "Found existing Tuya cover" << d->name() << id << name << (online ? "online" : "offline");
|
|
||||||
d->setStateValue(tuyaClosableConnectedStateTypeId, online);
|
|
||||||
} else {
|
} else {
|
||||||
|
bool online = deviceMap.value("data").toMap().value("online").toBool();
|
||||||
|
bool state = deviceMap.value("data").toMap().value("state").toBool();
|
||||||
|
qCDebug(dcTuya()) << "Found existing Tuya switch" << d->name() << id << name << (online ? "online:" : "offline") << (state ? "on": "off");
|
||||||
|
d->setStateValue(tuyaSwitchConnectedStateTypeId, online);
|
||||||
|
d->setStateValue(tuyaSwitchPowerStateTypeId, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (devType == "cover") {
|
||||||
|
Thing *d = myThings().findByParams(ParamList() << Param(tuyaClosableThingIdParamTypeId, id));
|
||||||
|
if (!d) {
|
||||||
qCDebug(dcTuya()) << "Found new Tuya cover" << id << name;
|
qCDebug(dcTuya()) << "Found new Tuya cover" << id << name;
|
||||||
ThingDescriptor descriptor(tuyaClosableThingClassId, name, QString(), thing->id());
|
ThingDescriptor descriptor(tuyaClosableThingClassId, name, QString(), thing->id());
|
||||||
descriptor.setParams(ParamList() << Param(tuyaClosableThingIdParamTypeId, id));
|
descriptor.setParams(ParamList() << Param(tuyaClosableThingIdParamTypeId, id));
|
||||||
unknownDevices.append(descriptor);
|
unknownDevices.append(descriptor);
|
||||||
|
} else {
|
||||||
|
bool online = deviceMap.value("data").toMap().value("online").toBool();
|
||||||
|
qCDebug(dcTuya()) << "Found existing Tuya cover" << d->name() << id << name << (online ? "online" : "offline");
|
||||||
|
d->setStateValue(tuyaClosableConnectedStateTypeId, online);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (devType == "light") {
|
||||||
|
Thing *d = myThings().findByParams(ParamList() << Param(tuyaLightThingIdParamTypeId, id));
|
||||||
|
if (!d) {
|
||||||
|
qCDebug(dcTuya()) << "Found new color Tuya light" << id << name;
|
||||||
|
ThingDescriptor descriptor(tuyaLightThingClassId, name, QString(), thing->id());
|
||||||
|
descriptor.setParams(ParamList() << Param(tuyaLightThingIdParamTypeId, id));
|
||||||
|
unknownDevices.append(descriptor);
|
||||||
|
} else {
|
||||||
|
bool online = deviceMap.value("data").toMap().value("online").toBool();
|
||||||
|
bool state = deviceMap.value("data").toMap().value("state").toBool();
|
||||||
|
qCDebug(dcTuya()) << "Found existing Tuya color light" << d->name() << id << name << (online ? "online" : "offline");
|
||||||
|
int brightness = deviceMap.value("data").toMap().value("brightness").toInt() / 10; // Apparently the value in the tuya cloud is 10 - 1000
|
||||||
|
d->setStateValue(tuyaLightConnectedStateTypeId, online);
|
||||||
|
d->setStateValue(tuyaLightPowerStateTypeId, state);
|
||||||
|
d->setStateValue(tuyaLightBrightnessStateTypeId, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qCWarning(dcTuya()) << "Skipping unsupported thing type:" << devType;
|
qCWarning(dcTuya()) << "Skipping unsupported thing type:" << devType;
|
||||||
qCWarning(dcTuya()) << "Please report this including the following data:\n" << qUtf8Printable(QJsonDocument::fromVariant(deviceVariant).toJson());
|
qCWarning(dcTuya()) << "Please report this including the following data:\n" << qUtf8Printable(QJsonDocument::fromVariant(deviceVariant).toJson());
|
||||||
|
|
@ -451,7 +517,7 @@ void IntegrationPluginTuya::queryDevice(Thing *thing)
|
||||||
{
|
{
|
||||||
qCDebug(dcTuya()) << "Updating thing:" << thing;
|
qCDebug(dcTuya()) << "Updating thing:" << thing;
|
||||||
|
|
||||||
QString devId = thing->paramValue(m_devIdParamTypeIdsMap.value(thing->thingClassId())).toString();
|
QString devId = thing->paramValue(idParamTypeIdsMap.value(thing->thingClassId())).toString();
|
||||||
|
|
||||||
pluginStorage()->beginGroup(thing->parentId().toString());
|
pluginStorage()->beginGroup(thing->parentId().toString());
|
||||||
QString accesToken = pluginStorage()->value("accessToken").toString();
|
QString accesToken = pluginStorage()->value("accessToken").toString();
|
||||||
|
|
@ -510,15 +576,15 @@ void IntegrationPluginTuya::queryDevice(Thing *thing)
|
||||||
bool state = result.value("payload").toMap().value("data").toMap().value("state").toBool();
|
bool state = result.value("payload").toMap().value("data").toMap().value("state").toBool();
|
||||||
qCDebug(dcTuya()) << "Device" << thing->name() << "is online:" << connected << "on:" << state;
|
qCDebug(dcTuya()) << "Device" << thing->name() << "is online:" << connected << "on:" << state;
|
||||||
|
|
||||||
thing->setStateValue(m_connectedStateTypeIdsMap.value(thing->thingClassId()), connected);
|
thing->setStateValue(connectedStateTypeIdsMap.value(thing->thingClassId()), connected);
|
||||||
|
|
||||||
if (m_powerStateTypeIdsMap.contains(thing->thingClassId())) {
|
if (powerStateTypeIdsMap.contains(thing->thingClassId())) {
|
||||||
thing->setStateValue(m_powerStateTypeIdsMap.value(thing->thingClassId()), state);
|
thing->setStateValue(powerStateTypeIdsMap.value(thing->thingClassId()), state);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginTuya::controlTuyaSwitch(const QString &devId, const QString &command, const QString &value, ThingActionInfo *info)
|
void IntegrationPluginTuya::controlTuyaSwitch(const QString &devId, const QString &command, const QVariant &value, ThingActionInfo *info)
|
||||||
{
|
{
|
||||||
Thing *thing = info->thing();
|
Thing *thing = info->thing();
|
||||||
Thing *parentDevice = myThings().findById(thing->parentId());
|
Thing *parentDevice = myThings().findById(thing->parentId());
|
||||||
|
|
@ -550,6 +616,8 @@ void IntegrationPluginTuya::controlTuyaSwitch(const QString &devId, const QStrin
|
||||||
|
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(data);
|
QJsonDocument jsonDoc = QJsonDocument::fromVariant(data);
|
||||||
|
|
||||||
|
qCDebug(dcTuya()) << "Controlling payload:" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
||||||
|
|
||||||
QNetworkReply *reply = hardwareManager()->networkManager()->post(request, jsonDoc.toJson(QJsonDocument::Compact));
|
QNetworkReply *reply = hardwareManager()->networkManager()->post(request, jsonDoc.toJson(QJsonDocument::Compact));
|
||||||
connect(reply, &QNetworkReply::finished, [reply](){reply->deleteLater();});
|
connect(reply, &QNetworkReply::finished, [reply](){reply->deleteLater();});
|
||||||
connect(reply, &QNetworkReply::finished, info, [info, reply](){
|
connect(reply, &QNetworkReply::finished, info, [info, reply](){
|
||||||
|
|
@ -571,7 +639,7 @@ void IntegrationPluginTuya::controlTuyaSwitch(const QString &devId, const QStrin
|
||||||
QVariantMap dataMap = jsonDoc.toVariant().toMap();
|
QVariantMap dataMap = jsonDoc.toVariant().toMap();
|
||||||
bool success = dataMap.value("header").toMap().value("code").toString() == "SUCCESS";
|
bool success = dataMap.value("header").toMap().value("code").toString() == "SUCCESS";
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qCWarning(dcTuya()) << "Tuya response indicates an issue...";
|
qCWarning(dcTuya()) << "Tuya response indicates an issue:" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
||||||
info->finish(Thing::ThingErrorHardwareFailure);
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,16 +64,12 @@ private:
|
||||||
void updateChildDevices(Thing *thing);
|
void updateChildDevices(Thing *thing);
|
||||||
void queryDevice(Thing *thing);
|
void queryDevice(Thing *thing);
|
||||||
|
|
||||||
void controlTuyaSwitch(const QString &devId, const QString &command, const QString &value, ThingActionInfo *info);
|
void controlTuyaSwitch(const QString &devId, const QString &command, const QVariant &value, ThingActionInfo *info);
|
||||||
|
|
||||||
QHash<ThingId, QTimer*> m_tokenExpiryTimers;
|
QHash<ThingId, QTimer*> m_tokenExpiryTimers;
|
||||||
PluginTimer *m_pluginTimerQuery = nullptr;
|
PluginTimer *m_pluginTimerQuery = nullptr;
|
||||||
PluginTimer *m_pluginTimerDiscovery = nullptr;
|
PluginTimer *m_pluginTimerDiscovery = nullptr;
|
||||||
|
|
||||||
QHash<ThingClassId, ParamTypeId> m_devIdParamTypeIdsMap;
|
|
||||||
QHash<ThingClassId, StateTypeId> m_connectedStateTypeIdsMap;
|
|
||||||
QHash<ThingClassId, StateTypeId> m_powerStateTypeIdsMap;
|
|
||||||
|
|
||||||
QHash<Thing*, QList<Thing*>> m_pollQueue;
|
QHash<Thing*, QList<Thing*>> m_pollQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,79 @@
|
||||||
"displayName": "Stop"
|
"displayName": "Stop"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6aaa67b5-b1ef-41e8-af72-0915e529b3c9",
|
||||||
|
"name": "tuyaLight",
|
||||||
|
"displayName": "Tuya color light",
|
||||||
|
"createMethods": ["auto"],
|
||||||
|
"interfaces": ["colorlight", "connectable"],
|
||||||
|
"paramTypes": [
|
||||||
|
{
|
||||||
|
"id": "7fda1e07-6e80-465a-8322-872d8ab42583",
|
||||||
|
"name": "id",
|
||||||
|
"displayName": "ID",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateTypes": [
|
||||||
|
{
|
||||||
|
"id": "6735b5eb-091b-4cad-aaa4-33ff76690e68",
|
||||||
|
"name": "connected",
|
||||||
|
"displayName": "Connected",
|
||||||
|
"displayNameEvent": "Connected changed",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5b2f7c3e-6701-447f-9158-53abb5e81395",
|
||||||
|
"name": "power",
|
||||||
|
"displayName": "Power",
|
||||||
|
"displayNameEvent": "Power changed",
|
||||||
|
"displayNameAction": "Set power",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"writable": true,
|
||||||
|
"ioType": "digitalOutput"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e2eba44d-73f9-43bc-97a3-ac4ff347b02c",
|
||||||
|
"name": "brightness",
|
||||||
|
"displayName": "Brightness",
|
||||||
|
"displayNameEvent": "Brightness changed",
|
||||||
|
"displayNameAction": "Set brightness",
|
||||||
|
"type": "int",
|
||||||
|
"minValue": 0,
|
||||||
|
"maxValue": 100,
|
||||||
|
"unit": "Percentage",
|
||||||
|
"defaultValue": "0",
|
||||||
|
"writable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7ccf9dc3-6b3f-4793-b0d5-3c97afb34cf0",
|
||||||
|
"name": "color",
|
||||||
|
"displayName": "Color",
|
||||||
|
"displayNameEvent": "Color changed",
|
||||||
|
"displayNameAction": "Set color",
|
||||||
|
"type": "QColor",
|
||||||
|
"defaultValue": "white",
|
||||||
|
"writable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8a07b6ca-3cb1-43f1-9b49-405349c2f146",
|
||||||
|
"name": "colorTemperature",
|
||||||
|
"displayName": "Color temperature",
|
||||||
|
"displayNameEvent": "Color temperature changed",
|
||||||
|
"displayNameAction": "Set color temperature",
|
||||||
|
"type": "int",
|
||||||
|
"minValue": 0,
|
||||||
|
"maxValue": 100,
|
||||||
|
"defaultValue": "50",
|
||||||
|
"writable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue