Keba: Update discovery mechanism
This commit is contained in:
parent
9e1780c5c4
commit
e42322694f
@ -30,12 +30,13 @@
|
|||||||
|
|
||||||
#include "plugininfo.h"
|
#include "plugininfo.h"
|
||||||
#include "integrationpluginkeba.h"
|
#include "integrationpluginkeba.h"
|
||||||
#include "network/networkdevicediscovery.h"
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QUdpSocket>
|
#include <QUdpSocket>
|
||||||
#include <QTimeZone>
|
#include <QTimeZone>
|
||||||
|
|
||||||
|
#include "kebadiscovery.h"
|
||||||
|
|
||||||
IntegrationPluginKeba::IntegrationPluginKeba()
|
IntegrationPluginKeba::IntegrationPluginKeba()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -48,49 +49,50 @@ void IntegrationPluginKeba::init()
|
|||||||
|
|
||||||
void IntegrationPluginKeba::discoverThings(ThingDiscoveryInfo *info)
|
void IntegrationPluginKeba::discoverThings(ThingDiscoveryInfo *info)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Init data layer if not already created
|
||||||
|
if (!m_kebaDataLayer){
|
||||||
|
qCDebug(dcKeba()) << "Creating new Keba data layer...";
|
||||||
|
m_kebaDataLayer= new KeContactDataLayer(this);
|
||||||
|
if (!m_kebaDataLayer->init()) {
|
||||||
|
m_kebaDataLayer->deleteLater();
|
||||||
|
m_kebaDataLayer = nullptr;
|
||||||
|
qCWarning(dcKeba()) << "Failed to create Keba data layer...";
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (info->thingClassId() == wallboxThingClassId) {
|
if (info->thingClassId() == wallboxThingClassId) {
|
||||||
qCDebug(dcKeba()) << "Discovering Keba Wallbox...";
|
// Create a discovery with the info as parent for auto deleting the object
|
||||||
NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover();
|
KebaDiscovery *discovery = new KebaDiscovery(m_kebaDataLayer, hardwareManager()->networkDeviceDiscovery(), info);
|
||||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
|
connect(discovery, &KebaDiscovery::discoveryFinished, info, [=](){
|
||||||
ThingDescriptors descriptors;
|
foreach (const KebaDiscovery::KebaDiscoveryResult &result, discovery->discoveryResults()) {
|
||||||
qCDebug(dcKeba()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices";
|
|
||||||
foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
|
|
||||||
if (!networkDeviceInfo.macAddressManufacturer().contains("keba", Qt::CaseSensitivity::CaseInsensitive))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
qCDebug(dcKeba()) << " - Keba Wallbox" << networkDeviceInfo;
|
ThingDescriptor descriptor(wallboxThingClassId, "Keba " + result.product, "Serial: " + result.serialNumber + " - " + result.networkDeviceInfo.address().toString());
|
||||||
QString title = "Keba Wallbox ";
|
|
||||||
if (networkDeviceInfo.hostName().isEmpty()) {
|
|
||||||
title += "(" + networkDeviceInfo.address().toString() + ")";
|
|
||||||
} else {
|
|
||||||
title += networkDeviceInfo.hostName() + " (" + networkDeviceInfo.address().toString() + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString description;
|
|
||||||
if (networkDeviceInfo.macAddressManufacturer().isEmpty()) {
|
|
||||||
description = networkDeviceInfo.macAddress();
|
|
||||||
} else {
|
|
||||||
description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
ThingDescriptor descriptor(wallboxThingClassId, title, description);
|
|
||||||
|
|
||||||
// Check if we already have set up this device
|
// Check if we already have set up this device
|
||||||
Things existingThings = myThings().filterByParam(wallboxThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
|
Things existingThings = myThings().filterByParam(wallboxThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||||
if (existingThings.count() == 1) {
|
if (existingThings.count() == 1) {
|
||||||
qCDebug(dcKeba()) << "This wallbox already exists in the system!" << networkDeviceInfo;
|
qCDebug(dcKeba()) << "This wallbox already exists in the system!" << result.networkDeviceInfo;
|
||||||
descriptor.setThingId(existingThings.first()->id());
|
descriptor.setThingId(existingThings.first()->id());
|
||||||
}
|
}
|
||||||
|
|
||||||
ParamList params;
|
ParamList params;
|
||||||
params << Param(wallboxThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
|
params << Param(wallboxThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||||
params << Param(wallboxThingIpAddressParamTypeId, networkDeviceInfo.address().toString());
|
params << Param(wallboxThingIpAddressParamTypeId, result.networkDeviceInfo.address().toString());
|
||||||
|
params << Param(wallboxThingModelParamTypeId, result.product);
|
||||||
|
params << Param(wallboxThingSerialNumberParamTypeId, result.serialNumber);
|
||||||
descriptor.setParams(params);
|
descriptor.setParams(params);
|
||||||
info->addThingDescriptor(descriptor);
|
info->addThingDescriptor(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
info->finish(Thing::ThingErrorNoError);
|
info->finish(Thing::ThingErrorNoError);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Start the discovery process
|
||||||
|
discovery->startDiscovery();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qCWarning(dcKeba()) << "Could not discover things because of unhandled thing class id" << info->thingClassId().toString();
|
qCWarning(dcKeba()) << "Could not discover things because of unhandled thing class id" << info->thingClassId().toString();
|
||||||
info->finish(Thing::ThingErrorThingClassNotFound);
|
info->finish(Thing::ThingErrorThingClassNotFound);
|
||||||
@ -143,6 +145,9 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info)
|
|||||||
connect(keba, &KeContact::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived);
|
connect(keba, &KeContact::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived);
|
||||||
connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived);
|
connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived);
|
||||||
|
|
||||||
|
// TODO: first test the ip, verify serial number if responds
|
||||||
|
// if no response, rediscover, reassign ip in case if changes
|
||||||
|
|
||||||
connect(keba, &KeContact::reportOneReceived, info, [info, this, keba] (const KeContact::ReportOne &report) {
|
connect(keba, &KeContact::reportOneReceived, info, [info, this, keba] (const KeContact::ReportOne &report) {
|
||||||
Thing *thing = info->thing();
|
Thing *thing = info->thing();
|
||||||
|
|
||||||
@ -155,8 +160,6 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info)
|
|||||||
|
|
||||||
thing->setStateValue(wallboxConnectedStateTypeId, true);
|
thing->setStateValue(wallboxConnectedStateTypeId, true);
|
||||||
thing->setStateValue(wallboxFirmwareStateTypeId, report.firmware);
|
thing->setStateValue(wallboxFirmwareStateTypeId, report.firmware);
|
||||||
thing->setStateValue(wallboxSerialnumberStateTypeId, report.serialNumber);
|
|
||||||
thing->setStateValue(wallboxModelStateTypeId, report.product);
|
|
||||||
thing->setStateValue(wallboxUptimeStateTypeId, report.seconds / 60);
|
thing->setStateValue(wallboxUptimeStateTypeId, report.seconds / 60);
|
||||||
|
|
||||||
m_kebaDevices.insert(thing->id(), keba);
|
m_kebaDevices.insert(thing->id(), keba);
|
||||||
@ -358,6 +361,11 @@ void IntegrationPluginKeba::searchNetworkDevices()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginKeba::onDiscoveryWaitUpdResponseTimeout()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void IntegrationPluginKeba::onConnectionChanged(bool status)
|
void IntegrationPluginKeba::onConnectionChanged(bool status)
|
||||||
{
|
{
|
||||||
KeContact *keba = static_cast<KeContact *>(sender());
|
KeContact *keba = static_cast<KeContact *>(sender());
|
||||||
@ -402,7 +410,7 @@ void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &repo
|
|||||||
if (!thing)
|
if (!thing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
qCDebug(dcKeba()) << "Report 2 received for" << thing->name() << "Serial number:" << thing->stateValue(wallboxSerialnumberStateTypeId).toString();
|
qCDebug(dcKeba()) << "Report 2 received for" << thing->name() << "Serial number:" << thing->paramValue(wallboxThingSerialNumberParamTypeId).toString();
|
||||||
qCDebug(dcKeba()) << " - State:" << reportTwo.state;
|
qCDebug(dcKeba()) << " - State:" << reportTwo.state;
|
||||||
qCDebug(dcKeba()) << " - Error 1:" << reportTwo.error1;
|
qCDebug(dcKeba()) << " - Error 1:" << reportTwo.error1;
|
||||||
qCDebug(dcKeba()) << " - Error 2:" << reportTwo.error2;
|
qCDebug(dcKeba()) << " - Error 2:" << reportTwo.error2;
|
||||||
@ -422,7 +430,7 @@ void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &repo
|
|||||||
qCDebug(dcKeba()) << " - Serial number:" << reportTwo.serialNumber;
|
qCDebug(dcKeba()) << " - Serial number:" << reportTwo.serialNumber;
|
||||||
qCDebug(dcKeba()) << " - Uptime:" << reportTwo.seconds/60 << "[min]";
|
qCDebug(dcKeba()) << " - Uptime:" << reportTwo.seconds/60 << "[min]";
|
||||||
|
|
||||||
if (reportTwo.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) {
|
if (reportTwo.serialNumber == thing->paramValue(wallboxThingSerialNumberParamTypeId).toString()) {
|
||||||
setDeviceState(thing, reportTwo.state);
|
setDeviceState(thing, reportTwo.state);
|
||||||
setDevicePlugState(thing, reportTwo.plugState);
|
setDevicePlugState(thing, reportTwo.plugState);
|
||||||
|
|
||||||
@ -458,7 +466,7 @@ void IntegrationPluginKeba::onReportThreeReceived(const KeContact::ReportThree &
|
|||||||
if (!thing)
|
if (!thing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
qCDebug(dcKeba()) << "Report 3 received for" << thing->name() << "Serial number:" << thing->stateValue(wallboxSerialnumberStateTypeId).toString();
|
qCDebug(dcKeba()) << "Report 3 received for" << thing->name() << "Serial number:" << thing->paramValue(wallboxThingSerialNumberParamTypeId).toString();
|
||||||
qCDebug(dcKeba()) << " - Current phase 1:" << reportThree.currentPhase1 << "[A]";
|
qCDebug(dcKeba()) << " - Current phase 1:" << reportThree.currentPhase1 << "[A]";
|
||||||
qCDebug(dcKeba()) << " - Current phase 2:" << reportThree.currentPhase2 << "[A]";
|
qCDebug(dcKeba()) << " - Current phase 2:" << reportThree.currentPhase2 << "[A]";
|
||||||
qCDebug(dcKeba()) << " - Current phase 3:" << reportThree.currentPhase3 << "[A]";
|
qCDebug(dcKeba()) << " - Current phase 3:" << reportThree.currentPhase3 << "[A]";
|
||||||
@ -471,7 +479,7 @@ void IntegrationPluginKeba::onReportThreeReceived(const KeContact::ReportThree &
|
|||||||
qCDebug(dcKeba()) << " - Serial number" << reportThree.serialNumber;
|
qCDebug(dcKeba()) << " - Serial number" << reportThree.serialNumber;
|
||||||
qCDebug(dcKeba()) << " - Uptime" << reportThree.seconds / 60 << "[min]";
|
qCDebug(dcKeba()) << " - Uptime" << reportThree.seconds / 60 << "[min]";
|
||||||
|
|
||||||
if (reportThree.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) {
|
if (reportThree.serialNumber == thing->paramValue(wallboxThingSerialNumberParamTypeId).toString()) {
|
||||||
thing->setStateValue(wallboxCurrentPhaseAEventTypeId, reportThree.currentPhase1);
|
thing->setStateValue(wallboxCurrentPhaseAEventTypeId, reportThree.currentPhase1);
|
||||||
thing->setStateValue(wallboxCurrentPhaseBEventTypeId, reportThree.currentPhase2);
|
thing->setStateValue(wallboxCurrentPhaseBEventTypeId, reportThree.currentPhase2);
|
||||||
thing->setStateValue(wallboxCurrentPhaseCEventTypeId, reportThree.currentPhase3);
|
thing->setStateValue(wallboxCurrentPhaseCEventTypeId, reportThree.currentPhase3);
|
||||||
@ -509,7 +517,7 @@ void IntegrationPluginKeba::onReport1XXReceived(int reportNumber, const KeContac
|
|||||||
if (!thing)
|
if (!thing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
qCDebug(dcKeba()) << "Report" << reportNumber << "received for" << thing->name() << "Serial number:" << thing->stateValue(wallboxSerialnumberStateTypeId).toString();
|
qCDebug(dcKeba()) << "Report" << reportNumber << "received for" << thing->name() << "Serial number:" << thing->paramValue(wallboxThingSerialNumberParamTypeId).toString();
|
||||||
qCDebug(dcKeba()) << " - Session Id" << report.sessionId;
|
qCDebug(dcKeba()) << " - Session Id" << report.sessionId;
|
||||||
qCDebug(dcKeba()) << " - Curr HW" << report.currHW;
|
qCDebug(dcKeba()) << " - Curr HW" << report.currHW;
|
||||||
qCDebug(dcKeba()) << " - Energy start" << report.startEnergy;
|
qCDebug(dcKeba()) << " - Energy start" << report.startEnergy;
|
||||||
@ -534,7 +542,7 @@ void IntegrationPluginKeba::onReport1XXReceived(int reportNumber, const KeContac
|
|||||||
|
|
||||||
} else if (reportNumber == 101) {
|
} else if (reportNumber == 101) {
|
||||||
// Report 101 is the lastest finished session
|
// Report 101 is the lastest finished session
|
||||||
if (report.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) {
|
if (report.serialNumber == thing->paramValue(wallboxThingSerialNumberParamTypeId).toString()) {
|
||||||
if (!m_lastSessionId.contains(thing->id())) {
|
if (!m_lastSessionId.contains(thing->id())) {
|
||||||
// This happens after reboot
|
// This happens after reboot
|
||||||
m_lastSessionId.insert(thing->id(), report.sessionId);
|
m_lastSessionId.insert(thing->id(), report.sessionId);
|
||||||
|
|||||||
@ -31,8 +31,10 @@
|
|||||||
#ifndef INTEGRATIONPLUGINKEBA_H
|
#ifndef INTEGRATIONPLUGINKEBA_H
|
||||||
#define INTEGRATIONPLUGINKEBA_H
|
#define INTEGRATIONPLUGINKEBA_H
|
||||||
|
|
||||||
#include "integrations/integrationplugin.h"
|
#include <integrations/integrationplugin.h>
|
||||||
#include "plugintimer.h"
|
#include <plugintimer.h>
|
||||||
|
#include <network/networkdevicediscovery.h>
|
||||||
|
|
||||||
#include "kecontact.h"
|
#include "kecontact.h"
|
||||||
#include "kecontactdatalayer.h"
|
#include "kecontactdatalayer.h"
|
||||||
|
|
||||||
@ -41,6 +43,7 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QUdpSocket>
|
#include <QUdpSocket>
|
||||||
|
|
||||||
|
|
||||||
class IntegrationPluginKeba : public IntegrationPlugin
|
class IntegrationPluginKeba : public IntegrationPlugin
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -64,12 +67,10 @@ public:
|
|||||||
private:
|
private:
|
||||||
PluginTimer *m_updateTimer = nullptr;
|
PluginTimer *m_updateTimer = nullptr;
|
||||||
PluginTimer *m_reconnectTimer = nullptr;
|
PluginTimer *m_reconnectTimer = nullptr;
|
||||||
|
|
||||||
KeContactDataLayer *m_kebaDataLayer = nullptr;
|
KeContactDataLayer *m_kebaDataLayer = nullptr;
|
||||||
|
|
||||||
QHash<ThingId, KeContact *> m_kebaDevices;
|
QHash<ThingId, KeContact *> m_kebaDevices;
|
||||||
QHash<ThingId, int> m_lastSessionId;
|
QHash<ThingId, int> m_lastSessionId;
|
||||||
|
|
||||||
QHash<QUuid, ThingActionInfo *> m_asyncActions;
|
QHash<QUuid, ThingActionInfo *> m_asyncActions;
|
||||||
|
|
||||||
void setDeviceState(Thing *device, KeContact::State state);
|
void setDeviceState(Thing *device, KeContact::State state);
|
||||||
@ -78,6 +79,8 @@ private:
|
|||||||
void searchNetworkDevices();
|
void searchNetworkDevices();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void onDiscoveryWaitUpdResponseTimeout();
|
||||||
|
|
||||||
void onConnectionChanged(bool status);
|
void onConnectionChanged(bool status);
|
||||||
void onCommandExecuted(QUuid requestId, bool success);
|
void onCommandExecuted(QUuid requestId, bool success);
|
||||||
void onReportTwoReceived(const KeContact::ReportTwo &reportTwo);
|
void onReportTwoReceived(const KeContact::ReportTwo &reportTwo);
|
||||||
|
|||||||
@ -31,6 +31,24 @@
|
|||||||
"inputType": "TextLine",
|
"inputType": "TextLine",
|
||||||
"defaultValue":"",
|
"defaultValue":"",
|
||||||
"readOnly": true
|
"readOnly": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "45255155-318b-4204-8ce6-2c106a56286d",
|
||||||
|
"name": "serialNumber",
|
||||||
|
"displayName": "Serial number",
|
||||||
|
"type": "QString",
|
||||||
|
"inputType": "TextLine",
|
||||||
|
"defaultValue":"",
|
||||||
|
"readOnly": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a996c698-4831-4977-8979-f76f78ac7da8",
|
||||||
|
"name": "model",
|
||||||
|
"displayName": "Product name",
|
||||||
|
"type": "QString",
|
||||||
|
"inputType": "TextLine",
|
||||||
|
"defaultValue":"",
|
||||||
|
"readOnly": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stateTypes": [
|
"stateTypes": [
|
||||||
@ -43,30 +61,6 @@
|
|||||||
"defaultValue": false,
|
"defaultValue": false,
|
||||||
"cached": false
|
"cached": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "c3fca233-95b9-4948-88c6-4c0f13cf53b1",
|
|
||||||
"name": "model",
|
|
||||||
"displayName": "Model",
|
|
||||||
"displayNameEvent": "Model changed",
|
|
||||||
"type": "QString",
|
|
||||||
"defaultValue": "Unknown"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e941ace5-fb7f-4dc2-b3f2-188233f4e934",
|
|
||||||
"name": "firmware",
|
|
||||||
"displayName": "Firmware",
|
|
||||||
"displayNameEvent": "Firmware changed",
|
|
||||||
"type": "QString",
|
|
||||||
"defaultValue": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "9a1b4316-ce01-4cd3-890f-a8c94b8b5029",
|
|
||||||
"name": "serialnumber",
|
|
||||||
"displayName": "Serial number",
|
|
||||||
"displayNameEvent": "Serial number changed",
|
|
||||||
"type": "QString",
|
|
||||||
"defaultValue": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "83ed0774-2a91-434d-b03c-d920d02f2981",
|
"id": "83ed0774-2a91-434d-b03c-d920d02f2981",
|
||||||
"name": "power",
|
"name": "power",
|
||||||
@ -328,6 +322,14 @@
|
|||||||
"writable": true,
|
"writable": true,
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e941ace5-fb7f-4dc2-b3f2-188233f4e934",
|
||||||
|
"name": "firmware",
|
||||||
|
"displayName": "Firmware",
|
||||||
|
"displayNameEvent": "Firmware changed",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"actionTypes": [
|
"actionTypes": [
|
||||||
|
|||||||
@ -6,10 +6,12 @@ TARGET = $$qtLibraryTarget(nymea_integrationpluginkeba)
|
|||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
integrationpluginkeba.cpp \
|
integrationpluginkeba.cpp \
|
||||||
|
kebadiscovery.cpp \
|
||||||
kecontact.cpp \
|
kecontact.cpp \
|
||||||
kecontactdatalayer.cpp
|
kecontactdatalayer.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
integrationpluginkeba.h \
|
integrationpluginkeba.h \
|
||||||
|
kebadiscovery.h \
|
||||||
kecontact.h \
|
kecontact.h \
|
||||||
kecontactdatalayer.h
|
kecontactdatalayer.h
|
||||||
|
|||||||
136
keba/kebadiscovery.cpp
Normal file
136
keba/kebadiscovery.cpp
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2021, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#include "kebadiscovery.h"
|
||||||
|
#include "kecontactdatalayer.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <network/networkdevicediscovery.h>
|
||||||
|
|
||||||
|
KebaDiscovery::KebaDiscovery(KeContactDataLayer *kebaDataLayer, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_kebaDataLayer(kebaDataLayer),
|
||||||
|
m_networkDeviceDiscovery(networkDeviceDiscovery)
|
||||||
|
{
|
||||||
|
// Timer for waiting if network devices responded to the "report 1 request"
|
||||||
|
m_responseTimer.setInterval(5000);
|
||||||
|
m_responseTimer.setSingleShot(true);
|
||||||
|
connect(&m_responseTimer, &QTimer::timeout, this, [=](){
|
||||||
|
qCDebug(dcKeba()) << "Discovery: Report response timeout. Found" << m_results.count() << "Keba Wallbox";
|
||||||
|
emit discoveryFinished();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Read data from the keba data layer and verify if it is a keba report
|
||||||
|
connect (m_kebaDataLayer, &KeContactDataLayer::datagramReceived, this, [=](const QHostAddress &address, const QByteArray &datagram){
|
||||||
|
|
||||||
|
// Just continue if this is a new address we have no result for
|
||||||
|
if (alreadyDiscovered(address)) {
|
||||||
|
qCDebug(dcKeba()) << "Discovery: Skipping datagram from already discovered Keba on" << address.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to convert the received data to a json document
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(datagram, &error);
|
||||||
|
if (error.error != QJsonParseError::NoError) {
|
||||||
|
qCWarning(dcKeba()) << "Discovery: Received data from the keba data link but failed to parse the data as JSON:" << datagram << ":" << error.errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify JSON data
|
||||||
|
QVariantMap dataMap = jsonDoc.toVariant().toMap();
|
||||||
|
if (!dataMap.contains("ID") || !dataMap.contains("Serial") || !dataMap.contains("Product") || !dataMap.contains("Firmware")) {
|
||||||
|
qCDebug(dcKeba()) << "Discovery: Received valid JSON data on data layer but they don't seem to be what we are listening for:" << qUtf8Printable(jsonDoc.toJson());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataMap.value("ID").toInt() != 1) {
|
||||||
|
qCDebug(dcKeba()) << "Discovery: Received valid Keba JSON data on data layer but this is not a report 1 message:" << qUtf8Printable(jsonDoc.toJson());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have received a report 1 datagram, let's add it to the result
|
||||||
|
foreach (const NetworkDeviceInfo &networkDeviceInfo, m_networkDeviceInfos) {
|
||||||
|
if (networkDeviceInfo.address() == address) {
|
||||||
|
KebaDiscoveryResult result;
|
||||||
|
result.networkDeviceInfo = networkDeviceInfo;
|
||||||
|
result.product = dataMap.value("Product").toString();
|
||||||
|
result.serialNumber = dataMap.value("Serial").toString();
|
||||||
|
result.firmwareVersion = dataMap.value("Firmware").toString();
|
||||||
|
m_results.append(result);
|
||||||
|
qCDebug(dcKeba()) << "Discovery: -->" << networkDeviceInfo.address().toString() << networkDeviceInfo.macAddress() << result.product << result.serialNumber << result.firmwareVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
KebaDiscovery::~KebaDiscovery()
|
||||||
|
{
|
||||||
|
qCDebug(dcKeba()) << "Discovery: Destroying object.";
|
||||||
|
}
|
||||||
|
|
||||||
|
void KebaDiscovery::startDiscovery()
|
||||||
|
{
|
||||||
|
// Clean up
|
||||||
|
m_networkDeviceInfos.clear();
|
||||||
|
m_results.clear();
|
||||||
|
|
||||||
|
qCDebug(dcKeba()) << "Discovery: Start discovering Keba Wallboxs...";
|
||||||
|
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||||
|
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
|
||||||
|
qCDebug(dcKeba()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
|
||||||
|
m_networkDeviceInfos = discoveryReply->networkDeviceInfos();
|
||||||
|
|
||||||
|
// Send a report 1 request to all discovered network devices and see which one responds
|
||||||
|
qCDebug(dcKeba()) << "Discovery: Start sending \"report 1\" request to all discovered network devices";
|
||||||
|
foreach (const NetworkDeviceInfo &networkDeviceInfo, m_networkDeviceInfos)
|
||||||
|
m_kebaDataLayer->write(networkDeviceInfo.address(), QByteArray("report 1\n"));
|
||||||
|
|
||||||
|
m_responseTimer.start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<KebaDiscovery::KebaDiscoveryResult> KebaDiscovery::discoveryResults() const
|
||||||
|
{
|
||||||
|
return m_results;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KebaDiscovery::alreadyDiscovered(const QHostAddress &address)
|
||||||
|
{
|
||||||
|
foreach (const KebaDiscoveryResult &result, m_results) {
|
||||||
|
if (result.networkDeviceInfo.address() == address) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
73
keba/kebadiscovery.h
Normal file
73
keba/kebadiscovery.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2021, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#ifndef KEBADISCOVERY_H
|
||||||
|
#define KEBADISCOVERY_H
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <network/networkdeviceinfos.h>
|
||||||
|
|
||||||
|
class KeContactDataLayer;
|
||||||
|
class NetworkDeviceDiscovery;
|
||||||
|
|
||||||
|
class KebaDiscovery : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
typedef struct KebaDiscoveryResult {
|
||||||
|
QString product;
|
||||||
|
QString serialNumber;
|
||||||
|
QString firmwareVersion;
|
||||||
|
NetworkDeviceInfo networkDeviceInfo;
|
||||||
|
} KebaDiscoveryResult;
|
||||||
|
|
||||||
|
explicit KebaDiscovery(KeContactDataLayer *kebaDataLayer, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
|
||||||
|
~KebaDiscovery();
|
||||||
|
|
||||||
|
void startDiscovery();
|
||||||
|
|
||||||
|
QList<KebaDiscoveryResult> discoveryResults() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void discoveryFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
KeContactDataLayer *m_kebaDataLayer = nullptr;
|
||||||
|
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||||
|
QTimer m_responseTimer;
|
||||||
|
NetworkDeviceInfos m_networkDeviceInfos;
|
||||||
|
QList<KebaDiscoveryResult> m_results;
|
||||||
|
|
||||||
|
bool alreadyDiscovered(const QHostAddress &address);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEBADISCOVERY_H
|
||||||
Loading…
x
Reference in New Issue
Block a user