TP-Link: Add support for TP-Link Kasa Power Strip HS300
This commit is contained in:
parent
cdff51655f
commit
054eceb27a
@ -7,6 +7,7 @@ This plugin adds support for the following tp-link Kasa devices to nymea.
|
|||||||
* HS105 Kasa Smart Wi-Fi Plug Mini
|
* HS105 Kasa Smart Wi-Fi Plug Mini
|
||||||
* HS110 Kasa Smart Wi-Fi Plug With Energy Monitoring
|
* HS110 Kasa Smart Wi-Fi Plug With Energy Monitoring
|
||||||
* HS200 Kasa Smart Wi-Fi Light Switch
|
* HS200 Kasa Smart Wi-Fi Light Switch
|
||||||
|
* HS300 Kasa Smart Wi-Fi Power Strip
|
||||||
* KP100 Kasa Smart Wi-Fi Plug Slim Edition
|
* KP100 Kasa Smart Wi-Fi Plug Slim Edition
|
||||||
|
|
||||||
In order to use such a device, it must be connected to the same network as nymea. The Kasa app is required
|
In order to use such a device, it must be connected to the same network as nymea. The Kasa app is required
|
||||||
|
|||||||
@ -39,33 +39,62 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
|
|
||||||
// https://github.com/softScheck/tplink-smartplug/blob/master/tplink-smarthome-commands.txt
|
// Related projects:
|
||||||
|
|
||||||
|
// Local api:
|
||||||
|
// https://github.com/plasticrake/tplink-smarthome-api/
|
||||||
|
// https://github.com/softScheck/tplink-smartplug
|
||||||
|
|
||||||
|
// Cloud api:
|
||||||
|
// https://github.com/piekstra/tplink-cloud-api/
|
||||||
|
|
||||||
|
QHash<ThingClassId, ParamTypeId> idParamTypesMap = {
|
||||||
|
{kasaPlug100ThingClassId, kasaPlug100ThingIdParamTypeId},
|
||||||
|
{kasaPlug110ThingClassId, kasaPlug110ThingIdParamTypeId},
|
||||||
|
{kasaSwitch200ThingClassId, kasaSwitch200ThingIdParamTypeId},
|
||||||
|
{kasaPowerStrip300ThingClassId, kasaPowerStrip300ThingIdParamTypeId},
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<ThingClassId, StateTypeId> connectedStateTypesMap = {
|
||||||
|
{kasaPlug100ThingClassId, kasaPlug100ConnectedStateTypeId},
|
||||||
|
{kasaPlug110ThingClassId, kasaPlug110ConnectedStateTypeId},
|
||||||
|
{kasaSwitch200ThingClassId, kasaSwitch200ConnectedStateTypeId},
|
||||||
|
{kasaPowerStrip300ThingClassId, kasaPowerStrip300ConnectedStateTypeId},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QHash<ThingClassId, StateTypeId> signalStrengthStateTypesMap = {
|
||||||
|
{kasaPlug100ThingClassId, kasaPlug100SignalStrengthStateTypeId},
|
||||||
|
{kasaPlug110ThingClassId, kasaPlug110SignalStrengthStateTypeId},
|
||||||
|
{kasaSwitch200ThingClassId, kasaSwitch200SignalStrengthStateTypeId},
|
||||||
|
{kasaPowerStrip300ThingClassId, kasaPowerStrip300SignalStrengthStateTypeId},
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<ThingClassId, StateTypeId> powerStateTypesMap = {
|
||||||
|
{kasaPlug100ThingClassId, kasaPlug100PowerStateTypeId},
|
||||||
|
{kasaPlug110ThingClassId, kasaPlug110PowerStateTypeId},
|
||||||
|
{kasaSwitch200ThingClassId, kasaSwitch200PowerStateTypeId},
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<ThingClassId, StateTypeId> currentPowerStatetTypesMap = {
|
||||||
|
{kasaPlug110ThingClassId, kasaPlug110CurrentPowerStateTypeId},
|
||||||
|
{kasaPowerStrip300ThingClassId, kasaPowerStrip300CurrentPowerStateTypeId},
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<ThingClassId, StateTypeId> totalEnergyConsumedStatetTypesMap = {
|
||||||
|
{kasaPlug110ThingClassId, kasaPlug110TotalEnergyConsumedStateTypeId},
|
||||||
|
{kasaPowerStrip300ThingClassId, kasaPowerStrip300TotalEnergyConsumedStateTypeId},
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<ThingClassId, StateTypeId> powerActionParamTypesMap = {
|
||||||
|
{kasaPlug100ThingClassId, kasaPlug100PowerActionPowerParamTypeId},
|
||||||
|
{kasaPlug110ThingClassId, kasaPlug110PowerActionPowerParamTypeId},
|
||||||
|
{kasaSwitch200ThingClassId, kasaSwitch200PowerActionPowerParamTypeId},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
IntegrationPluginTPLink::IntegrationPluginTPLink()
|
IntegrationPluginTPLink::IntegrationPluginTPLink()
|
||||||
{
|
{
|
||||||
m_idParamTypesMap[kasaPlug100ThingClassId] = kasaPlug100ThingIdParamTypeId;
|
|
||||||
m_idParamTypesMap[kasaPlug110ThingClassId] = kasaPlug110ThingIdParamTypeId;
|
|
||||||
m_idParamTypesMap[kasaSwitch200ThingClassId] = kasaSwitch200ThingIdParamTypeId;
|
|
||||||
|
|
||||||
m_connectedStateTypesMap[kasaPlug100ThingClassId] = kasaPlug100ConnectedStateTypeId;
|
|
||||||
m_connectedStateTypesMap[kasaPlug110ThingClassId] = kasaPlug110ConnectedStateTypeId;
|
|
||||||
m_connectedStateTypesMap[kasaSwitch200ThingClassId] = kasaSwitch200ConnectedStateTypeId;
|
|
||||||
|
|
||||||
m_signalStrengthStateTypesMap[kasaPlug100ThingClassId] = kasaPlug100SignalStrengthStateTypeId;
|
|
||||||
m_signalStrengthStateTypesMap[kasaPlug110ThingClassId] = kasaPlug110SignalStrengthStateTypeId;
|
|
||||||
m_signalStrengthStateTypesMap[kasaSwitch200ThingClassId] = kasaSwitch200SignalStrengthStateTypeId;
|
|
||||||
|
|
||||||
m_powerStatetTypesMap[kasaPlug100ThingClassId] = kasaPlug100PowerStateTypeId;
|
|
||||||
m_powerStatetTypesMap[kasaPlug110ThingClassId] = kasaPlug110PowerStateTypeId;
|
|
||||||
m_powerStatetTypesMap[kasaSwitch200ThingClassId] = kasaSwitch200PowerStateTypeId;
|
|
||||||
|
|
||||||
m_currentPowerStatetTypesMap[kasaPlug110ThingClassId] = kasaPlug110CurrentPowerStateTypeId;
|
|
||||||
|
|
||||||
m_totalEnergyConsumedStatetTypesMap[kasaPlug110ThingClassId] = kasaPlug110TotalEnergyConsumedStateTypeId;
|
|
||||||
|
|
||||||
m_powerActionParamTypesMap[kasaPlug100ThingClassId] = kasaPlug100PowerActionPowerParamTypeId;
|
|
||||||
m_powerActionParamTypesMap[kasaPlug110ThingClassId] = kasaPlug110PowerActionPowerParamTypeId;
|
|
||||||
m_powerActionParamTypesMap[kasaSwitch200ThingClassId] = kasaSwitch200PowerActionPowerParamTypeId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegrationPluginTPLink::~IntegrationPluginTPLink()
|
IntegrationPluginTPLink::~IntegrationPluginTPLink()
|
||||||
@ -95,9 +124,9 @@ void IntegrationPluginTPLink::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
|
|
||||||
QTimer::singleShot(2000, info, [this, info](){
|
QTimer::singleShot(2000, info, [this, info](){
|
||||||
while(m_broadcastSocket->hasPendingDatagrams()) {
|
while(m_broadcastSocket->hasPendingDatagrams()) {
|
||||||
char buffer[1024];
|
char buffer[4096];
|
||||||
QHostAddress senderAddress;
|
QHostAddress senderAddress;
|
||||||
qint64 len = m_broadcastSocket->readDatagram(buffer, 1024, &senderAddress);
|
qint64 len = m_broadcastSocket->readDatagram(buffer, 4096, &senderAddress);
|
||||||
QByteArray data = decryptPayload(QByteArray::fromRawData(buffer, len));
|
QByteArray data = decryptPayload(QByteArray::fromRawData(buffer, len));
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||||
@ -117,18 +146,31 @@ void IntegrationPluginTPLink::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
modelFilter = QRegExp("HS110.*");
|
modelFilter = QRegExp("HS110.*");
|
||||||
} else if (info->thingClassId() == kasaSwitch200ThingClassId) {
|
} else if (info->thingClassId() == kasaSwitch200ThingClassId) {
|
||||||
modelFilter = QRegExp("HS200.*");
|
modelFilter = QRegExp("HS200.*");
|
||||||
|
} else if (info->thingClassId() == kasaPowerStrip300ThingClassId) {
|
||||||
|
modelFilter = QRegExp("HS300.*");
|
||||||
}
|
}
|
||||||
QString model = sysInfo.value("model").toString();
|
QString model = sysInfo.value("model").toString();
|
||||||
|
|
||||||
if (modelFilter.exactMatch(model)) {
|
if (modelFilter.exactMatch(model)) {
|
||||||
ThingDescriptor descriptor(info->thingClassId(), sysInfo.value("alias").toString(), sysInfo.value("dev_name").toString());
|
ThingDescriptor descriptor(info->thingClassId(), sysInfo.value("alias").toString(), sysInfo.value("dev_name").toString());
|
||||||
Param idParam = Param(m_idParamTypesMap.value(info->thingClassId()), sysInfo.value("deviceId").toString());
|
Param idParam = Param(idParamTypesMap.value(info->thingClassId()), sysInfo.value("deviceId").toString());
|
||||||
descriptor.setParams(ParamList() << idParam);
|
descriptor.setParams(ParamList() << idParam);
|
||||||
Thing *existingThing = myThings().findByParams(ParamList() << idParam);
|
Thing *existingThing = myThings().findByParams(ParamList() << idParam);
|
||||||
if (existingThing) {
|
if (existingThing) {
|
||||||
descriptor.setThingId(existingThing->id());
|
descriptor.setThingId(existingThing->id());
|
||||||
}
|
}
|
||||||
info->addThingDescriptor(descriptor);
|
|
||||||
|
// Sometimes kasa devices reply multiple times on the discovery call. Prevent duplicates in the search results.
|
||||||
|
bool found = false;
|
||||||
|
foreach (const ThingDescriptor &existingDescriptor, info->thingDescriptors()) {
|
||||||
|
if (existingDescriptor.params().paramValue(idParamTypesMap.value(info->thingClassId())) == idParam.value()) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
info->addThingDescriptor(descriptor);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
qCWarning(dcTplink()) << "Ignoring not matching device type:\n" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
qCWarning(dcTplink()) << "Ignoring not matching device type:\n" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
|
||||||
}
|
}
|
||||||
@ -140,6 +182,12 @@ void IntegrationPluginTPLink::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
|
|
||||||
void IntegrationPluginTPLink::setupThing(ThingSetupInfo *info)
|
void IntegrationPluginTPLink::setupThing(ThingSetupInfo *info)
|
||||||
{
|
{
|
||||||
|
if (info->thing()->thingClassId() == kasaSocketThingClassId) {
|
||||||
|
qCDebug(dcTplink()) << "Setup thing for child socket:" << info->thing()->paramValue(kasaSocketThingIdParamTypeId).toString();
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
QVariantMap getSysInfo;
|
QVariantMap getSysInfo;
|
||||||
getSysInfo.insert("get_sysinfo", QVariant());
|
getSysInfo.insert("get_sysinfo", QVariant());
|
||||||
@ -159,9 +207,9 @@ void IntegrationPluginTPLink::setupThing(ThingSetupInfo *info)
|
|||||||
QTimer::singleShot(2000, info, [this, info](){
|
QTimer::singleShot(2000, info, [this, info](){
|
||||||
|
|
||||||
while(m_broadcastSocket->hasPendingDatagrams()) {
|
while(m_broadcastSocket->hasPendingDatagrams()) {
|
||||||
char buffer[1024];
|
char buffer[4096];
|
||||||
QHostAddress senderAddress;
|
QHostAddress senderAddress;
|
||||||
qint64 len = m_broadcastSocket->readDatagram(buffer, 1024, &senderAddress);
|
qint64 len = m_broadcastSocket->readDatagram(buffer, 4096, &senderAddress);
|
||||||
QByteArray data = decryptPayload(QByteArray::fromRawData(buffer, len));
|
QByteArray data = decryptPayload(QByteArray::fromRawData(buffer, len));
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||||
@ -171,13 +219,33 @@ void IntegrationPluginTPLink::setupThing(ThingSetupInfo *info)
|
|||||||
}
|
}
|
||||||
QVariantMap properties = jsonDoc.toVariant().toMap();
|
QVariantMap properties = jsonDoc.toVariant().toMap();
|
||||||
QVariantMap sysInfo = properties.value("system").toMap().value("get_sysinfo").toMap();
|
QVariantMap sysInfo = properties.value("system").toMap().value("get_sysinfo").toMap();
|
||||||
if (info->thing()->paramValue(m_idParamTypesMap.value(info->thing()->thingClassId())).toString() == sysInfo.value("deviceId").toString()) {
|
if (info->thing()->paramValue(idParamTypesMap.value(info->thing()->thingClassId())).toString() == sysInfo.value("deviceId").toString()) {
|
||||||
qCDebug(dcTplink()) << "Found thing at" << senderAddress;
|
qCDebug(dcTplink()) << "Found thing at" << senderAddress;
|
||||||
|
|
||||||
|
// We need to finish the setup before we can generate childs for this thing
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
m_setupRetries.remove(info);
|
||||||
|
|
||||||
|
// Set up child sockets if needed (currently only the HS300)
|
||||||
|
QVariantList children = sysInfo.value("children").toList();
|
||||||
|
if (children.count() > 0 && myThings().filterByParentId(info->thing()->id()).isEmpty()) {
|
||||||
|
ThingDescriptors descriptors;
|
||||||
|
foreach (const QVariant &childVariant, children) {
|
||||||
|
QVariantMap childMap = childVariant.toMap();
|
||||||
|
ThingDescriptor descriptor(kasaSocketThingClassId, childMap.value("alias").toString(), QString(), info->thing()->id());
|
||||||
|
|
||||||
|
QString devId = QString("%1%2")
|
||||||
|
.arg(info->thing()->paramValue(idParamTypesMap.value(info->thing()->thingClassId())).toString())
|
||||||
|
.arg(childMap.value("id").toString());
|
||||||
|
descriptor.setParams(ParamList() << Param(kasaSocketThingIdParamTypeId, devId));
|
||||||
|
qCDebug(dcTplink()) << "Creating child socket for" << info->thing()->name() << "with ID" << devId;
|
||||||
|
descriptors.append(descriptor);
|
||||||
|
}
|
||||||
|
emit autoThingsAppeared(descriptors);
|
||||||
|
}
|
||||||
|
|
||||||
connectToDevice(info->thing(), senderAddress);
|
connectToDevice(info->thing(), senderAddress);
|
||||||
|
|
||||||
info->finish(Thing::ThingErrorNoError);
|
|
||||||
m_setupRetries.remove(info);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,6 +263,7 @@ void IntegrationPluginTPLink::setupThing(ThingSetupInfo *info)
|
|||||||
|
|
||||||
void IntegrationPluginTPLink::postSetupThing(Thing *thing)
|
void IntegrationPluginTPLink::postSetupThing(Thing *thing)
|
||||||
{
|
{
|
||||||
|
qCDebug(dcTplink()) << "Post setup thing" << thing->name();
|
||||||
connect(thing, &Thing::nameChanged, this, [this, thing](){
|
connect(thing, &Thing::nameChanged, this, [this, thing](){
|
||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
QVariantMap systemMap;
|
QVariantMap systemMap;
|
||||||
@ -221,12 +290,18 @@ void IntegrationPluginTPLink::postSetupThing(Thing *thing)
|
|||||||
m_timer = hardwareManager()->pluginTimerManager()->registerTimer(1);
|
m_timer = hardwareManager()->pluginTimerManager()->registerTimer(1);
|
||||||
connect(m_timer, &PluginTimer::timeout, this, [this](){
|
connect(m_timer, &PluginTimer::timeout, this, [this](){
|
||||||
foreach (Thing *d, myThings()) {
|
foreach (Thing *d, myThings()) {
|
||||||
if (!m_pendingJobs.contains(d) && m_jobQueue[d].isEmpty()) {
|
if (d->parentId().isNull() && !m_pendingJobs.contains(d) && m_jobQueue[d].isEmpty()) {
|
||||||
fetchState(d);
|
fetchState(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update connected state in case the parent connected before we've completed the child setups
|
||||||
|
if (thing->thingClassId() == kasaSocketThingClassId) {
|
||||||
|
Thing *parent = myThings().findById(thing->parentId());
|
||||||
|
thing->setStateValue(kasaSocketConnectedStateTypeId, parent->stateValue(connectedStateTypesMap.value(parent->thingClassId())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginTPLink::thingRemoved(Thing *thing)
|
void IntegrationPluginTPLink::thingRemoved(Thing *thing)
|
||||||
@ -244,14 +319,23 @@ void IntegrationPluginTPLink::thingRemoved(Thing *thing)
|
|||||||
|
|
||||||
void IntegrationPluginTPLink::executeAction(ThingActionInfo *info)
|
void IntegrationPluginTPLink::executeAction(ThingActionInfo *info)
|
||||||
{
|
{
|
||||||
QVariantMap map;
|
// The actual thing to send the command to (either directly a physical thing or in case of a virtual socket the parent thing)
|
||||||
QVariantMap systemMap;
|
Thing *targetThing = info->thing()->parentId().isNull() ? info->thing() : myThings().findById(info->thing()->parentId());
|
||||||
|
|
||||||
QVariantMap powerMap;
|
QVariantMap powerMap;
|
||||||
ParamTypeId powerActionParamTypeId = m_powerActionParamTypesMap.value(info->thing()->thingClassId());
|
powerMap.insert("state", info->action().param(info->action().actionTypeId()).value().toBool() ? 1 : 0);
|
||||||
powerMap.insert("state", info->action().param(powerActionParamTypeId).value().toBool() ? 1 : 0);
|
QVariantMap systemMap;
|
||||||
systemMap.insert("set_relay_state", powerMap);
|
systemMap.insert("set_relay_state", powerMap);
|
||||||
|
QVariantMap map;
|
||||||
map.insert("system", systemMap);
|
map.insert("system", systemMap);
|
||||||
|
|
||||||
|
// If we're switching a virtual child, we need to add a context map for the child_ids
|
||||||
|
if (info->thing()->thingClassId() == kasaSocketThingClassId) {
|
||||||
|
QVariantMap contextMap;
|
||||||
|
contextMap.insert("child_ids", QVariantList() << info->thing()->paramValue(kasaSocketThingIdParamTypeId).toString());
|
||||||
|
map.insert("context", contextMap);
|
||||||
|
}
|
||||||
|
|
||||||
qCDebug(dcTplink()) << "Executing action" << qUtf8Printable(QJsonDocument::fromVariant(map).toJson(QJsonDocument::Compact));
|
qCDebug(dcTplink()) << "Executing action" << qUtf8Printable(QJsonDocument::fromVariant(map).toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
QByteArray payload = encryptPayload(QJsonDocument::fromVariant(map).toJson(QJsonDocument::Compact));
|
QByteArray payload = encryptPayload(QJsonDocument::fromVariant(map).toJson(QJsonDocument::Compact));
|
||||||
@ -264,15 +348,15 @@ void IntegrationPluginTPLink::executeAction(ThingActionInfo *info)
|
|||||||
job.id = m_jobIdx++;
|
job.id = m_jobIdx++;
|
||||||
job.data = data;
|
job.data = data;
|
||||||
job.actionInfo = info;
|
job.actionInfo = info;
|
||||||
m_jobQueue[info->thing()].append(job);
|
m_jobQueue[targetThing].append(job);
|
||||||
connect(info, &ThingActionInfo::aborted, this, [=](){
|
connect(info, &ThingActionInfo::aborted, this, [=](){
|
||||||
m_jobQueue[info->thing()].removeAll(job);
|
m_jobQueue[targetThing].removeAll(job);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Directly queue up fetchState
|
// Directly queue up fetchState
|
||||||
fetchState(info->thing(), info);
|
fetchState(targetThing);
|
||||||
|
|
||||||
processQueue(info->thing());
|
processQueue(targetThing);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray IntegrationPluginTPLink::encryptPayload(const QByteArray &payload)
|
QByteArray IntegrationPluginTPLink::encryptPayload(const QByteArray &payload)
|
||||||
@ -311,9 +395,16 @@ void IntegrationPluginTPLink::connectToDevice(Thing *thing, const QHostAddress &
|
|||||||
m_sockets.insert(thing, socket);
|
m_sockets.insert(thing, socket);
|
||||||
|
|
||||||
connect(socket, &QTcpSocket::connected, thing, [this, thing, address] () {
|
connect(socket, &QTcpSocket::connected, thing, [this, thing, address] () {
|
||||||
qCDebug(dcTplink()) << "Connected to device" << address;
|
qCDebug(dcTplink()) << "Connected to device" << thing->name() << "at address:" << address;
|
||||||
StateTypeId connectedStateTypeId = m_connectedStateTypesMap.value(thing->thingClassId());
|
StateTypeId connectedStateTypeId = connectedStateTypesMap.value(thing->thingClassId());
|
||||||
thing->setStateValue(connectedStateTypeId, true);
|
thing->setStateValue(connectedStateTypeId, true);
|
||||||
|
|
||||||
|
qCDebug(dcTplink()) << "Has childs:" << myThings().count();
|
||||||
|
foreach (Thing *child, myThings().filterByParentId(thing->id())) {
|
||||||
|
qCDebug(dcTplink()) << "Setting child online:" << child->paramValue(kasaSocketThingIdParamTypeId);
|
||||||
|
child->setStateValue(kasaSocketConnectedStateTypeId, true);
|
||||||
|
}
|
||||||
|
|
||||||
fetchState(thing);
|
fetchState(thing);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -343,6 +434,7 @@ void IntegrationPluginTPLink::connectToDevice(Thing *thing, const QHostAddress &
|
|||||||
processQueue(thing);
|
processQueue(thing);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Job job = m_pendingJobs.take(thing);
|
Job job = m_pendingJobs.take(thing);
|
||||||
|
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
@ -368,21 +460,48 @@ void IntegrationPluginTPLink::connectToDevice(Thing *thing, const QHostAddress &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (systemMap.contains("get_sysinfo")) {
|
if (systemMap.contains("get_sysinfo")) {
|
||||||
int relayState = systemMap.value("get_sysinfo").toMap().value("relay_state").toInt();
|
StateTypeId signalStrengthStateTypeId = signalStrengthStateTypesMap.value(thing->thingClassId());
|
||||||
StateTypeId powerStateTypeId = m_powerStatetTypesMap.value(thing->thingClassId());
|
|
||||||
thing->setStateValue(powerStateTypeId, relayState == 1 ? true : false);
|
|
||||||
StateTypeId signalStrengthStateTypeId = m_signalStrengthStateTypesMap.value(thing->thingClassId());
|
|
||||||
int rssi = systemMap.value("get_sysinfo").toMap().value("rssi").toInt();
|
int rssi = systemMap.value("get_sysinfo").toMap().value("rssi").toInt();
|
||||||
int signalStrength = qMax(0, qMin(1000, 2 * (rssi + 100)));
|
int signalStrength = qMax(0, qMin(100, 2 * (rssi + 100)));
|
||||||
thing->setStateValue(signalStrengthStateTypeId, signalStrength);
|
thing->setStateValue(signalStrengthStateTypeId, signalStrength);
|
||||||
|
|
||||||
|
if (thing->thingClassId() == kasaSocketThingClassId) {
|
||||||
|
foreach (Thing *child, myThings().filterByParentId(kasaSocketThingClassId)) {
|
||||||
|
child->setStateValue(kasaSocketSignalStrengthStateTypeId, signalStrength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString alias = systemMap.value("get_sysinfo").toMap().value("alias").toString();
|
QString alias = systemMap.value("get_sysinfo").toMap().value("alias").toString();
|
||||||
if (thing->name() != alias) {
|
if (thing->name() != alias) {
|
||||||
thing->setName(alias);
|
thing->setName(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (systemMap.value("get_sysinfo").toMap().contains("relay_state")) {
|
||||||
|
int relayState = systemMap.value("get_sysinfo").toMap().value("relay_state").toInt();
|
||||||
|
StateTypeId powerStateTypeId = powerStateTypesMap.value(thing->thingClassId());
|
||||||
|
thing->setStateValue(powerStateTypeId, relayState == 1 ? true : false);
|
||||||
|
|
||||||
|
} else if (systemMap.value("get_sysinfo").toMap().contains("children")) {
|
||||||
|
// For now, only the HS300 has children, which we map to child things of type kasaSocket
|
||||||
|
QVariantList children = systemMap.value("get_sysinfo").toMap().value("children").toList();
|
||||||
|
foreach (const QVariant &childVariant, children) {
|
||||||
|
QVariantMap childMap = childVariant.toMap();
|
||||||
|
QString idParam = childMap.value("id").toString();
|
||||||
|
bool relayState = childMap.value("state").toInt() == 1;
|
||||||
|
Things things = myThings().filterByParentId(thing->id()).filterByParam(kasaSocketThingIdParamTypeId, idParam);
|
||||||
|
if (things.count() == 1) {
|
||||||
|
things.first()->setStateValue(kasaSocketPowerStateTypeId, relayState);
|
||||||
|
} else {
|
||||||
|
qCWarning(dcTplink()) << "Error matching child devices" << qUtf8Printable(jsonDoc.toJson());
|
||||||
|
foreach (Thing *child, myThings().filterByParentId(thing->id())) {
|
||||||
|
qCDebug(dcTplink()) << "Existing child device:" << child->name() << child->params();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (job.actionInfo) {
|
if (job.actionInfo) {
|
||||||
|
qCDebug(dcTplink()) << "Finishing action execution";
|
||||||
job.actionInfo->finish(Thing::ThingErrorNoError);
|
job.actionInfo->finish(Thing::ThingErrorNoError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,13 +510,13 @@ void IntegrationPluginTPLink::connectToDevice(Thing *thing, const QHostAddress &
|
|||||||
QVariantMap emeterMap = map.value("emeter").toMap();
|
QVariantMap emeterMap = map.value("emeter").toMap();
|
||||||
if (emeterMap.contains("get_realtime")) {
|
if (emeterMap.contains("get_realtime")) {
|
||||||
// This has quite a bit of jitter... Let's smoothen it while within +/- 0.1W to produce less events in the system
|
// This has quite a bit of jitter... Let's smoothen it while within +/- 0.1W to produce less events in the system
|
||||||
StateTypeId currentPowerStateTypeId = m_currentPowerStatetTypesMap.value(thing->thingClassId());
|
StateTypeId currentPowerStateTypeId = currentPowerStatetTypesMap.value(thing->thingClassId());
|
||||||
double oldValue = thing->stateValue(currentPowerStateTypeId).toDouble();
|
double oldValue = thing->stateValue(currentPowerStateTypeId).toDouble();
|
||||||
double newValue = emeterMap.value("get_realtime").toMap().value("power_mw").toDouble() / 1000;
|
double newValue = emeterMap.value("get_realtime").toMap().value("power_mw").toDouble() / 1000;
|
||||||
if (qAbs(oldValue - newValue) > 0.1) {
|
if (qAbs(oldValue - newValue) > 0.1) {
|
||||||
thing->setStateValue(currentPowerStateTypeId, newValue);
|
thing->setStateValue(currentPowerStateTypeId, newValue);
|
||||||
}
|
}
|
||||||
StateTypeId totalEnergyConsumedStateTypeId = m_totalEnergyConsumedStatetTypesMap.value(thing->thingClassId());
|
StateTypeId totalEnergyConsumedStateTypeId = totalEnergyConsumedStatetTypesMap.value(thing->thingClassId());
|
||||||
thing->setStateValue(totalEnergyConsumedStateTypeId, emeterMap.value("get_realtime").toMap().value("total_wh").toDouble() / 1000);
|
thing->setStateValue(totalEnergyConsumedStateTypeId, emeterMap.value("get_realtime").toMap().value("total_wh").toDouble() / 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,16 +525,23 @@ void IntegrationPluginTPLink::connectToDevice(Thing *thing, const QHostAddress &
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(socket, &QTcpSocket::disconnected, thing, [this, thing, address](){
|
connect(socket, &QTcpSocket::stateChanged, thing, [this, thing, address](QAbstractSocket::SocketState newState){
|
||||||
qCDebug(dcTplink()) << "Device disconnected";
|
if (newState == QAbstractSocket::UnconnectedState) {
|
||||||
m_sockets.take(thing)->deleteLater();
|
qCDebug(dcTplink()) << "Device disconnected";
|
||||||
if (m_pendingJobs.contains(thing)) {
|
m_sockets.take(thing)->deleteLater();
|
||||||
// Putting active job back to queue
|
if (m_pendingJobs.contains(thing)) {
|
||||||
m_jobQueue[thing].prepend(m_pendingJobs.take(thing));
|
// Putting active job back to queue
|
||||||
|
m_jobQueue[thing].prepend(m_pendingJobs.take(thing));
|
||||||
|
}
|
||||||
|
StateTypeId connectedStateTypeId = connectedStateTypesMap.value(thing->thingClassId());
|
||||||
|
thing->setStateValue(connectedStateTypeId, false);
|
||||||
|
|
||||||
|
foreach (Thing *child, myThings().filterByParentId(thing->id())) {
|
||||||
|
child->setStateValue(kasaSocketConnectedStateTypeId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTimer::singleShot(500, thing, [this, thing, address]() {connectToDevice(thing, address);});
|
||||||
}
|
}
|
||||||
StateTypeId connectedStateTypeId = m_connectedStateTypesMap.value(thing->thingClassId());
|
|
||||||
thing->setStateValue(connectedStateTypeId, false);
|
|
||||||
QTimer::singleShot(500, thing, [this, thing, address]() {connectToDevice(thing, address);});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket->connectToHost(address.toString(), 9999, QIODevice::ReadWrite);
|
socket->connectToHost(address.toString(), 9999, QIODevice::ReadWrite);
|
||||||
@ -423,11 +549,6 @@ void IntegrationPluginTPLink::connectToDevice(Thing *thing, const QHostAddress &
|
|||||||
|
|
||||||
void IntegrationPluginTPLink::fetchState(Thing *thing, ThingActionInfo *info)
|
void IntegrationPluginTPLink::fetchState(Thing *thing, ThingActionInfo *info)
|
||||||
{
|
{
|
||||||
QTcpSocket *socket = m_sockets.value(thing);
|
|
||||||
if (!socket || !socket->isOpen()) {
|
|
||||||
qCWarning(dcTplink()) << "Cannot fetch state";
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
QVariantMap getSysInfo;
|
QVariantMap getSysInfo;
|
||||||
getSysInfo.insert("get_sysinfo", QVariant());
|
getSysInfo.insert("get_sysinfo", QVariant());
|
||||||
@ -456,6 +577,7 @@ void IntegrationPluginTPLink::fetchState(Thing *thing, ThingActionInfo *info)
|
|||||||
void IntegrationPluginTPLink::processQueue(Thing *thing)
|
void IntegrationPluginTPLink::processQueue(Thing *thing)
|
||||||
{
|
{
|
||||||
if (m_pendingJobs.contains(thing)) {
|
if (m_pendingJobs.contains(thing)) {
|
||||||
|
qCDebug(dcTplink()) << "Already processing a message to" << thing->name();
|
||||||
// Busy
|
// Busy
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,14 +85,6 @@ private:
|
|||||||
QHash<Thing*, QByteArray> m_inputBuffers;
|
QHash<Thing*, QByteArray> m_inputBuffers;
|
||||||
|
|
||||||
PluginTimer *m_timer = nullptr;
|
PluginTimer *m_timer = nullptr;
|
||||||
|
|
||||||
QHash<ThingClassId, ParamTypeId> m_idParamTypesMap;
|
|
||||||
QHash<ThingClassId, StateTypeId> m_connectedStateTypesMap;
|
|
||||||
QHash<ThingClassId, StateTypeId> m_signalStrengthStateTypesMap;
|
|
||||||
QHash<ThingClassId, StateTypeId> m_powerStatetTypesMap;
|
|
||||||
QHash<ThingClassId, StateTypeId> m_currentPowerStatetTypesMap;
|
|
||||||
QHash<ThingClassId, StateTypeId> m_totalEnergyConsumedStatetTypesMap;
|
|
||||||
QHash<ThingClassId, ParamTypeId> m_powerActionParamTypesMap;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INTEGRATIONPLUGINANEL_H
|
#endif // INTEGRATIONPLUGINANEL_H
|
||||||
|
|||||||
@ -171,6 +171,111 @@
|
|||||||
"writable": true
|
"writable": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bbdee989-9431-4187-b135-5030a7a11be2",
|
||||||
|
"name": "kasaPowerStrip300",
|
||||||
|
"displayName": "Kasa Smart Wi-Fi Power Strip (HS300)",
|
||||||
|
"createMethods": ["discovery"],
|
||||||
|
"interfaces": [ "gateway", "extendedsmartmeterconsumer", "wirelessconnectable" ],
|
||||||
|
"paramTypes": [
|
||||||
|
{
|
||||||
|
"id": "62b38ddf-f6d7-44e1-955f-f04b375d960d",
|
||||||
|
"name": "id",
|
||||||
|
"displayName": "ID",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateTypes": [
|
||||||
|
{
|
||||||
|
"id": "9bcec21d-7207-44c6-8ebf-d2720bb7a63a",
|
||||||
|
"name": "connected",
|
||||||
|
"displayName": "Connected",
|
||||||
|
"displayNameEvent": "Connected changed",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1a587f62-8274-4c2e-bdc6-1b0b6368e5f7",
|
||||||
|
"name": "signalStrength",
|
||||||
|
"displayName": "Signal strength",
|
||||||
|
"displayNameEvent": "Signal strength changed",
|
||||||
|
"type": "uint",
|
||||||
|
"unit": "Percentage",
|
||||||
|
"minValue": 0,
|
||||||
|
"maxValue": 100,
|
||||||
|
"defaultValue": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f2992226-1f36-477f-b053-04dc5dc3a31c",
|
||||||
|
"name": "totalEnergyConsumed",
|
||||||
|
"displayName": "Total energy consumed",
|
||||||
|
"displayNameEvent": "Total energy consumed changed",
|
||||||
|
"type": "double",
|
||||||
|
"unit": "KiloWattHour",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f973b4e8-99fb-4331-8193-e287bd37f5a0",
|
||||||
|
"name": "currentPower",
|
||||||
|
"displayName": "Current power consumption",
|
||||||
|
"displayNameEvent": "Current power consumption changed",
|
||||||
|
"type": "double",
|
||||||
|
"unit": "Watt",
|
||||||
|
"defaultValue": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2f40e28c-2484-4e09-bda9-e7accc29ab41",
|
||||||
|
"name": "kasaSocket",
|
||||||
|
"displayName": "Kasa power socket",
|
||||||
|
"createMethods": ["auto"],
|
||||||
|
"interfaces": ["powersocket", "wirelessconnectable"],
|
||||||
|
"paramTypes": [
|
||||||
|
{
|
||||||
|
"id": "dd944807-c7f2-49da-b443-b69eb8387f41",
|
||||||
|
"name": "id",
|
||||||
|
"displayName": "ID",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateTypes": [
|
||||||
|
{
|
||||||
|
"id": "59feb3a8-d35e-4c10-ac2e-01361e711a5e",
|
||||||
|
"name": "connected",
|
||||||
|
"displayName": "Connected",
|
||||||
|
"displayNameEvent": "Connected changed",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "16b71516-2ff4-4a2f-b0fc-53fff11f3a16",
|
||||||
|
"name": "signalStrength",
|
||||||
|
"displayName": "Signal strength",
|
||||||
|
"displayNameEvent": "Signal strength changed",
|
||||||
|
"type": "uint",
|
||||||
|
"unit": "Percentage",
|
||||||
|
"minValue": 0,
|
||||||
|
"maxValue": 100,
|
||||||
|
"defaultValue": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b1d006a6-f2f5-475c-ab9e-8b431b1ac5f8",
|
||||||
|
"name": "power",
|
||||||
|
"displayName": "Power",
|
||||||
|
"displayNameEvent": "Turned on or off",
|
||||||
|
"displayNameAction": "Turn on or off",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"writable": true,
|
||||||
|
"ioType": "digitalOutput"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
37
tplink/sampledata/HS105-TCP.txt
Normal file
37
tplink/sampledata/HS105-TCP.txt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"emeter": {
|
||||||
|
"err_code": -1,
|
||||||
|
"err_msg": "module not support"
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"get_sysinfo": {
|
||||||
|
"active_mode": "none",
|
||||||
|
"alias": "Kasa Plug Mini HS105",
|
||||||
|
"dev_name": "Smart Wi-Fi Plug Mini",
|
||||||
|
"deviceId": "8006F282D515B22F869313C3D0F2E45F1D93EEC2",
|
||||||
|
"err_code": 0,
|
||||||
|
"feature": "TIM",
|
||||||
|
"hwId": "845AC58F61A995F454064635A2E42A92",
|
||||||
|
"hw_ver": "4.0",
|
||||||
|
"icon_hash": "",
|
||||||
|
"latitude_i": 398287,
|
||||||
|
"led_off": 0,
|
||||||
|
"longitude_i": -1049759,
|
||||||
|
"mac": "84:D8:1B:C2:C6:39",
|
||||||
|
"mic_type": "IOT.SMARTPLUGSWITCH",
|
||||||
|
"model": "HS105(US)",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"ntc_state": 0,
|
||||||
|
"obd_src": "tplink",
|
||||||
|
"oemId": "69A0595CB3878B4927CC17675B69DBA9",
|
||||||
|
"on_time": 468,
|
||||||
|
"relay_state": 1,
|
||||||
|
"rssi": -50,
|
||||||
|
"status": "new",
|
||||||
|
"sw_ver": "1.0.2 Build 200804 Rel.091847",
|
||||||
|
"updating": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
tplink/sampledata/HS105-UDP.txt
Normal file
32
tplink/sampledata/HS105-UDP.txt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"system": {
|
||||||
|
"get_sysinfo": {
|
||||||
|
"active_mode": "none",
|
||||||
|
"alias": "Kasa Plug Mini HS105",
|
||||||
|
"dev_name": "Smart Wi-Fi Plug Mini",
|
||||||
|
"deviceId": "80060D6638AF0FF5268C04E19ACD7A631D92524B",
|
||||||
|
"err_code": 0,
|
||||||
|
"feature": "TIM",
|
||||||
|
"hwId": "6EEF0BBCE1D91460D5F5201C65C8A549",
|
||||||
|
"hw_ver": "5.0",
|
||||||
|
"icon_hash": "",
|
||||||
|
"latitude_i": 398287,
|
||||||
|
"led_off": 0,
|
||||||
|
"longitude_i": -1049759,
|
||||||
|
"mac": "84:D8:1B:CB:A6:89",
|
||||||
|
"mic_type": "IOT.SMARTPLUGSWITCH",
|
||||||
|
"model": "HS105(US)",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"obd_src": "tplink",
|
||||||
|
"oemId": "A3BE45A15350D76F11728AC99180D5E8",
|
||||||
|
"on_time": 466,
|
||||||
|
"relay_state": 1,
|
||||||
|
"rssi": -39,
|
||||||
|
"status": "new",
|
||||||
|
"sw_ver": "1.0.2 Build 200819 Rel.103733",
|
||||||
|
"updating": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
tplink/sampledata/HS110-TCP.txt
Normal file
40
tplink/sampledata/HS110-TCP.txt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"emeter": {
|
||||||
|
"get_realtime": {
|
||||||
|
"current_ma": 16,
|
||||||
|
"err_code": 0,
|
||||||
|
"power_mw": 812,
|
||||||
|
"total_wh": 15,
|
||||||
|
"voltage_mv": 232313
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"get_sysinfo": {
|
||||||
|
"active_mode": "none",
|
||||||
|
"alias": "Kasa Plug With Energy Monitoring HS110",
|
||||||
|
"dev_name": "Smart Wi-Fi Plug With Energy Monitoring",
|
||||||
|
"deviceId": "8006BBCA9B42F7D495ED9636570C21571BA8D9C1",
|
||||||
|
"err_code": 0,
|
||||||
|
"feature": "TIM:ENE",
|
||||||
|
"fwId": "00000000000000000000000000000000",
|
||||||
|
"hwId": "044A516EE63C875F9458DA25C2CCC5A0",
|
||||||
|
"hw_ver": "2.0",
|
||||||
|
"icon_hash": "",
|
||||||
|
"latitude_i": 484108,
|
||||||
|
"led_off": 0,
|
||||||
|
"longitude_i": 99379,
|
||||||
|
"mac": "98:DA:C4:6A:67:34",
|
||||||
|
"model": "HS110(EU)",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"oemId": "1998A14DAA86E4E001FD7CAF42868B5E",
|
||||||
|
"on_time": 27113,
|
||||||
|
"relay_state": 1,
|
||||||
|
"rssi": -52,
|
||||||
|
"sw_ver": "1.5.6 Build 191125 Rel.083657",
|
||||||
|
"type": "IOT.SMARTPLUGSWITCH",
|
||||||
|
"updating": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
tplink/sampledata/HS110-UDP.txt
Normal file
31
tplink/sampledata/HS110-UDP.txt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"system": {
|
||||||
|
"get_sysinfo": {
|
||||||
|
"active_mode": "none",
|
||||||
|
"alias": "Kasa Plug With Power Monitoring HS110",
|
||||||
|
"dev_name": "Smart Wi-Fi Plug With Energy Monitoring",
|
||||||
|
"deviceId": "8006BBCA9B42F7D495ED9636570C21571BA8D9C1",
|
||||||
|
"err_code": 0,
|
||||||
|
"feature": "TIM:ENE",
|
||||||
|
"fwId": "00000000000000000000000000000000",
|
||||||
|
"hwId": "044A516EE63C875F9458DA25C2CCC5A0",
|
||||||
|
"hw_ver": "2.0",
|
||||||
|
"icon_hash": "",
|
||||||
|
"latitude_i": 484108,
|
||||||
|
"led_off": 0,
|
||||||
|
"longitude_i": 99379,
|
||||||
|
"mac": "98:DA:C4:6A:67:34",
|
||||||
|
"model": "HS110(EU)",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"oemId": "1998A14DAA86E4E001FD7CAF42868B5E",
|
||||||
|
"on_time": 27035,
|
||||||
|
"relay_state": 1,
|
||||||
|
"rssi": -52,
|
||||||
|
"sw_ver": "1.5.6 Build 191125 Rel.083657",
|
||||||
|
"type": "IOT.SMARTPLUGSWITCH",
|
||||||
|
"updating": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
tplink/sampledata/HS200-TCP.txt
Normal file
35
tplink/sampledata/HS200-TCP.txt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"emeter": {
|
||||||
|
"err_code": -1,
|
||||||
|
"err_msg": "module not support"
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"get_sysinfo": {
|
||||||
|
"active_mode": "none",
|
||||||
|
"alias": "Kasa Light Switch HS200",
|
||||||
|
"dev_name": "Smart Wi-Fi Light Switch",
|
||||||
|
"deviceId": "800637201AAD76731FEC32A6D953ABDA1D5A4663",
|
||||||
|
"err_code": 0,
|
||||||
|
"feature": "TIM",
|
||||||
|
"hwId": "F30CA3404523AE331168A102F897B7F0",
|
||||||
|
"hw_ver": "3.0",
|
||||||
|
"icon_hash": "",
|
||||||
|
"latitude_i": 398287,
|
||||||
|
"led_off": 0,
|
||||||
|
"longitude_i": -1049759,
|
||||||
|
"mac": "D8:07:B6:F7:E9:8D",
|
||||||
|
"mic_type": "IOT.SMARTPLUGSWITCH",
|
||||||
|
"model": "HS200(US)",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"oemId": "59F15C39F35FEBCF7E5A84FF7620AFA1",
|
||||||
|
"on_time": 0,
|
||||||
|
"relay_state": 0,
|
||||||
|
"rssi": -51,
|
||||||
|
"status": "new",
|
||||||
|
"sw_ver": "1.1.4 Build 200814 Rel.105352",
|
||||||
|
"updating": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
tplink/sampledata/HS200-UDP.txt
Normal file
31
tplink/sampledata/HS200-UDP.txt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"system": {
|
||||||
|
"get_sysinfo": {
|
||||||
|
"active_mode": "none",
|
||||||
|
"alias": "Kasa Light Switch HS200",
|
||||||
|
"dev_name": "Smart Wi-Fi Light Switch",
|
||||||
|
"deviceId": "424535436543654365435435425234591D5AF3F3",
|
||||||
|
"err_code": 0,
|
||||||
|
"feature": "TIM",
|
||||||
|
"hwId": "F305453657676574756546534653B7F0",
|
||||||
|
"hw_ver": "3.0",
|
||||||
|
"icon_hash": "",
|
||||||
|
"latitude_i": 123456,
|
||||||
|
"led_off": 0,
|
||||||
|
"longitude_i": -1234567,
|
||||||
|
"mac": "23:45:67:89:AB:CD",
|
||||||
|
"mic_type": "IOT.SMARTPLUGSWITCH",
|
||||||
|
"model": "HS200(US)",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"oemId": "5545465654756654365653455340AFA1",
|
||||||
|
"on_time": 0,
|
||||||
|
"relay_state": 0,
|
||||||
|
"rssi": -43,
|
||||||
|
"status": "new",
|
||||||
|
"sw_ver": "1.1.4 Build 200814 Rel.105352",
|
||||||
|
"updating": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
tplink/sampledata/HS300-TCP.txt
Normal file
89
tplink/sampledata/HS300-TCP.txt
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"emeter": {
|
||||||
|
"get_realtime": {
|
||||||
|
"current_ma": 4,
|
||||||
|
"err_code": 0,
|
||||||
|
"power_mw": 0,
|
||||||
|
"total_wh": 1475,
|
||||||
|
"voltage_mv": 123011
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"get_sysinfo": {
|
||||||
|
"alias": "TP-LINK_Power Strip_9BF2",
|
||||||
|
"child_num": 6,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 1",
|
||||||
|
"id": "80064FCD1EFC96482DF70A7E66F341841D58B49200",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 417662,
|
||||||
|
"state": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 2",
|
||||||
|
"id": "80064FCD1EFC96482DF70A7E66F341841D58B49201",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 418526,
|
||||||
|
"state": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 3",
|
||||||
|
"id": "80064FCD1EFC96482DF70A7E66F341841D58B49202",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 418526,
|
||||||
|
"state": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 4",
|
||||||
|
"id": "80064FCD1EFC96482DF70A7E66F341841D58B49203",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 418526,
|
||||||
|
"state": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 5",
|
||||||
|
"id": "80064FCD1EFC96482DF70A7E66F341841D58B49204",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 418526,
|
||||||
|
"state": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 6",
|
||||||
|
"id": "80064FCD1EFC96482DF70A7E66F341841D58B49205",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 417659,
|
||||||
|
"state": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"deviceId": "80064FCD1EFC96482DF70A7E66F341841D58B492",
|
||||||
|
"err_code": 0,
|
||||||
|
"feature": "TIM:ENE",
|
||||||
|
"hwId": "34C41AA028022D0CCEA5E678E8547C54",
|
||||||
|
"hw_ver": "1.0",
|
||||||
|
"latitude_i": 398287,
|
||||||
|
"led_off": 0,
|
||||||
|
"longitude_i": -1049759,
|
||||||
|
"mac": "3C:84:6A:66:9B:F2",
|
||||||
|
"mic_type": "IOT.SMARTPLUGSWITCH",
|
||||||
|
"model": "HS300(US)",
|
||||||
|
"oemId": "5C9E6254BEBAED63B2B6102966D24C17",
|
||||||
|
"rssi": -65,
|
||||||
|
"status": "new",
|
||||||
|
"sw_ver": "1.0.19 Build 200224 Rel.090814",
|
||||||
|
"updating": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
tplink/sampledata/HS300-UDP.txt
Normal file
80
tplink/sampledata/HS300-UDP.txt
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"system": {
|
||||||
|
"get_sysinfo": {
|
||||||
|
"alias": "TP-LINK_Power Strip_9BF2",
|
||||||
|
"child_num": 6,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 1",
|
||||||
|
"id": "00",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 417655,
|
||||||
|
"state": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 2",
|
||||||
|
"id": "01",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 418519,
|
||||||
|
"state": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 3",
|
||||||
|
"id": "02",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 418519,
|
||||||
|
"state": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 4",
|
||||||
|
"id": "03",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 418519,
|
||||||
|
"state": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 5",
|
||||||
|
"id": "04",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 418519,
|
||||||
|
"state": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Shipping table Plug 6",
|
||||||
|
"id": "05",
|
||||||
|
"next_action": {
|
||||||
|
"type": -1
|
||||||
|
},
|
||||||
|
"on_time": 417652,
|
||||||
|
"state": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"deviceId": "80064FCD1EFC96482DF70A7E66F341841D58B492",
|
||||||
|
"err_code": 0,
|
||||||
|
"feature": "TIM:ENE",
|
||||||
|
"hwId": "34C41AA028022D0CCEA5E678E8547C54",
|
||||||
|
"hw_ver": "1.0",
|
||||||
|
"latitude_i": 398287,
|
||||||
|
"led_off": 0,
|
||||||
|
"longitude_i": -1049759,
|
||||||
|
"mac": "3C:84:6A:66:9B:F2",
|
||||||
|
"mic_type": "IOT.SMARTPLUGSWITCH",
|
||||||
|
"model": "HS300(US)",
|
||||||
|
"oemId": "5C9E6254BEBAED63B2B6102966D24C17",
|
||||||
|
"rssi": -65,
|
||||||
|
"status": "new",
|
||||||
|
"sw_ver": "1.0.19 Build 200224 Rel.090814",
|
||||||
|
"updating": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
95
tplink/sampledata/simulator.cpp
Normal file
95
tplink/sampledata/simulator.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include <QGuiApplication>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QUdpSocket>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
|
#define MODEL "HS300"
|
||||||
|
|
||||||
|
QByteArray decryptPayload(const QByteArray &payload)
|
||||||
|
{
|
||||||
|
QByteArray result;
|
||||||
|
int k = 171;
|
||||||
|
for (int i = 0; i < payload.length(); i++){
|
||||||
|
char t = payload.at(i);
|
||||||
|
result.append(t xor k);
|
||||||
|
k = t;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray encryptPayload(const QByteArray &payload)
|
||||||
|
{
|
||||||
|
QByteArray result;
|
||||||
|
int k = 171;
|
||||||
|
for (int i = 0; i < payload.length(); i++){
|
||||||
|
char t = payload.at(i) xor k;
|
||||||
|
k = t;
|
||||||
|
result.append(t);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
|
||||||
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
|
qDebug() << "Simulating" << MODEL;
|
||||||
|
|
||||||
|
QFile f(QString("%1-UDP.txt").arg(MODEL));
|
||||||
|
f.open(QFile::ReadOnly);
|
||||||
|
QByteArray systemDataUdp = f.readAll();
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(systemDataUdp);
|
||||||
|
systemDataUdp = encryptPayload(jsonDoc.toJson(QJsonDocument::Compact));
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
f.setFileName(QString("%1-TCP.txt").arg(MODEL));
|
||||||
|
f.open(QFile::ReadOnly);
|
||||||
|
QByteArray systemDataTcp = f.readAll();
|
||||||
|
jsonDoc = QJsonDocument::fromJson(systemDataTcp);
|
||||||
|
systemDataTcp = encryptPayload(jsonDoc.toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
|
QUdpSocket *udp = new QUdpSocket(&app);
|
||||||
|
udp->bind(QHostAddress("0.0.0.0"), 9999);
|
||||||
|
QObject::connect(udp, &QUdpSocket::readyRead, &app, [=](){
|
||||||
|
while (udp->hasPendingDatagrams()) {
|
||||||
|
char buffer[4096];
|
||||||
|
QHostAddress sourceAddress;
|
||||||
|
quint16 sourcePort;
|
||||||
|
udp->readDatagram(buffer, 4096, &sourceAddress, &sourcePort);
|
||||||
|
QByteArray data(buffer);
|
||||||
|
data = decryptPayload(data);
|
||||||
|
qDebug() << "Incoming datagram:" << data << "from" << sourceAddress;
|
||||||
|
|
||||||
|
udp->writeDatagram(systemDataUdp, sourceAddress, sourcePort);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QTcpServer *tcp = new QTcpServer(&app);
|
||||||
|
bool success = tcp->listen(QHostAddress("0.0.0.0"), 9999);
|
||||||
|
qDebug() << "Listening on tcp" << success;
|
||||||
|
QObject::connect(tcp, &QTcpServer::newConnection, &app, [=](){
|
||||||
|
qDebug() << "new connection";
|
||||||
|
QTcpSocket *client = tcp->nextPendingConnection();
|
||||||
|
QObject::connect(client, &QTcpSocket::readyRead, tcp, [=](){
|
||||||
|
QByteArray data = decryptPayload(client->readAll());
|
||||||
|
qDebug() << "Incoming request:" << data;
|
||||||
|
|
||||||
|
QByteArray output;
|
||||||
|
QDataStream stream(&output, QIODevice::WriteOnly);
|
||||||
|
qint32 len = systemDataTcp.length();
|
||||||
|
stream << len;
|
||||||
|
output.append(systemDataTcp);
|
||||||
|
qDebug() << "written:" << client->write(output);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
5
tplink/sampledata/simulator.pro
Normal file
5
tplink/sampledata/simulator.pro
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
CONFIG += c++11
|
||||||
|
|
||||||
|
QT += network
|
||||||
|
|
||||||
|
SOURCES += simulator.cpp
|
||||||
@ -7,3 +7,10 @@ SOURCES += \
|
|||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
integrationplugintplink.h \
|
integrationplugintplink.h \
|
||||||
|
|
||||||
|
OTHER_FILES += \
|
||||||
|
sampledata/HS200.txt \
|
||||||
|
sampledata/HS300-UDP.txt
|
||||||
|
|
||||||
|
DISTFILES += \
|
||||||
|
sampledata/HS300-TCP.txt
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user