From e70b4a679af1b5ed5d92770d7be555a20f16c92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 13 Dec 2021 15:51:07 +0100 Subject: [PATCH] Update state after successfull sent command --- keba/integrationpluginkeba.cpp | 194 +++++++++++++++++---------------- keba/kecontact.cpp | 35 +++--- keba/kecontact.h | 1 - keba/kecontactdatalayer.cpp | 3 +- 4 files changed, 119 insertions(+), 114 deletions(-) diff --git a/keba/integrationpluginkeba.cpp b/keba/integrationpluginkeba.cpp index b119838f..bb6333a9 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -161,7 +161,7 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) qCDebug(dcKeba()) << " - Firmware" << report.firmware; qCDebug(dcKeba()) << " - Serial" << report.serialNumber; qCDebug(dcKeba()) << " - Product" << report.product; - qCDebug(dcKeba()) << " - Uptime" << report.seconds/60 << "[min]"; + qCDebug(dcKeba()) << " - Uptime" << report.seconds / 60 << "[min]"; qCDebug(dcKeba()) << " - Com Module" << report.comModule; // Verify the DIP switches and warn the user in case if wrong configuration @@ -174,7 +174,6 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) return; } - thing->setStateValue(wallboxConnectedStateTypeId, true); thing->setStateValue(wallboxFirmwareStateTypeId, report.firmware); thing->setStateValue(wallboxUptimeStateTypeId, report.seconds / 60); @@ -219,7 +218,7 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing) } if (!m_updateTimer) { - m_updateTimer = hardwareManager()->pluginTimerManager()->registerTimer(60); + m_updateTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); connect(m_updateTimer, &PluginTimer::timeout, this, [this]() { foreach (Thing *thing, myThings().filterByThingClassId(wallboxThingClassId)) { KeContact *keba = m_kebaDevices.value(thing->id()); @@ -239,7 +238,7 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing) } if (!m_reconnectTimer) { - m_reconnectTimer = hardwareManager()->pluginTimerManager()->registerTimer(60*5); + m_reconnectTimer = hardwareManager()->pluginTimerManager()->registerTimer(60 * 5); connect(m_reconnectTimer, &PluginTimer::timeout, this, [this] { // Only search for new network devices if there is one keba which is not connected foreach (Thing *thing, myThings().filterByThingClassId(wallboxThingClassId)) { @@ -286,6 +285,86 @@ void IntegrationPluginKeba::thingRemoved(Thing *thing) } } +void IntegrationPluginKeba::executeAction(ThingActionInfo *info) +{ + Thing *thing = info->thing(); + Action action = info->action(); + + if (thing->thingClassId() == wallboxThingClassId) { + KeContact *keba = m_kebaDevices.value(thing->id()); + if (!keba) { + qCWarning(dcKeba()) << "Device not properly initialized, Keba object missing"; + return info->finish(Thing::ThingErrorHardwareNotAvailable); + } + + QUuid requestId; + if (action.actionTypeId() == wallboxMaxChargingCurrentActionTypeId) { + int milliAmpere = action.paramValue(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt() * 1000; + requestId = keba->setMaxAmpereGeneral(milliAmpere); + } else if (action.actionTypeId() == wallboxPowerActionTypeId) { + requestId = keba->enableOutput(action.param(wallboxPowerActionTypeId).value().toBool()); + } else if (action.actionTypeId() == wallboxDisplayActionTypeId) { + requestId = keba->displayMessage(action.param(wallboxDisplayActionMessageParamTypeId).value().toByteArray()); + } else if (action.actionTypeId() == wallboxOutputX2ActionTypeId) { + requestId = keba->setOutputX2(action.param(wallboxOutputX2ActionOutputX2ParamTypeId).value().toBool()); + } else if (action.actionTypeId() == wallboxFailsafeModeActionTypeId) { + int timeout = 0; + if (action.param(wallboxFailsafeModeActionFailsafeModeParamTypeId).value().toBool()) { + timeout = 60; + } + requestId = keba->setFailsafe(timeout, 0, false); + } else { + qCWarning(dcKeba()) << "Unhandled ActionTypeId:" << action.actionTypeId(); + return info->finish(Thing::ThingErrorActionTypeNotFound); + } + + // If the keba returns an invalid uuid, something went wrong + if (requestId.isNull()) { + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + m_asyncActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, this, [requestId, this]{ m_asyncActions.remove(requestId); }); + } else { + qCWarning(dcKeba()) << "Execute action, unhandled device class" << thing->thingClass(); + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + +void IntegrationPluginKeba::onCommandExecuted(QUuid requestId, bool success) +{ + if (m_asyncActions.contains(requestId)) { + KeContact *keba = static_cast(sender()); + + //keba->getReport2(); // Check if the state was actually set + + Thing *thing = myThings().findById(m_kebaDevices.key(keba)); + if (!thing) { + qCWarning(dcKeba()) << "On command executed: missing device object"; + return; + } + + ThingActionInfo *info = m_asyncActions.take(requestId); + if (success) { + qCDebug(dcKeba()) << "Action execution finished successfully. Request ID:" << requestId.toString(); + info->finish(Thing::ThingErrorNoError); + + // Set the value to the state so we don't have to wait for the report 2 response + if (info->action().actionTypeId() == wallboxMaxChargingCurrentActionTypeId) { + uint value = info->action().paramValue(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt(); + info->thing()->setStateValue(wallboxMaxChargingCurrentStateTypeId, value); + } else if (info->action().actionTypeId() == wallboxPowerActionTypeId) { + info->thing()->setStateValue(wallboxPowerStateTypeId, info->action().paramValue(wallboxPowerActionTypeId).toBool()); + } + + } else { + qCWarning(dcKeba()) << "Action execution finished with error. Request ID:" << requestId.toString(); + info->finish(Thing::ThingErrorHardwareFailure); + } + } +} + void IntegrationPluginKeba::setDeviceState(Thing *thing, KeContact::State state) { switch (state) { @@ -342,34 +421,34 @@ void IntegrationPluginKeba::setDevicePlugState(Thing *thing, KeContact::PlugStat void IntegrationPluginKeba::searchNetworkDevices() { qCDebug(dcKeba()) << "Start searching for things..."; - NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); - connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ - ThingDescriptors descriptors; - qCDebug(dcKeba()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices"; - foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { - if (!networkDeviceInfo.hostName().contains("keba", Qt::CaseSensitivity::CaseInsensitive)) - continue; + KebaDiscovery *discovery = new KebaDiscovery(m_kebaDataLayer, hardwareManager()->networkDeviceDiscovery(), this); + connect(discovery, &KebaDiscovery::discoveryFinished, this, [=](){ + foreach (const KebaDiscovery::KebaDiscoveryResult &result, discovery->discoveryResults()) { foreach (Thing *existingThing, myThings().filterByThingClassId(wallboxThingClassId)) { if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString().isEmpty()) { //This device got probably manually setup, to enable auto rediscovery the MAC address needs to setup - if (existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() == networkDeviceInfo.address().toString()) { - qCDebug(dcKeba()) << "Keba Wallbox MAC Address has been discovered" << existingThing->name() << networkDeviceInfo.macAddress(); - existingThing->setParamValue(wallboxThingMacAddressParamTypeId, networkDeviceInfo.macAddress()); + if (existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() == result.networkDeviceInfo.address().toString()) { + qCDebug(dcKeba()) << "Keba Wallbox MAC Address has been discovered" << existingThing->name() << result.networkDeviceInfo.macAddress(); + existingThing->setParamValue(wallboxThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); } - } else if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString() == networkDeviceInfo.macAddress()) { + } else if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString() == result.networkDeviceInfo.macAddress()) { // We found the existing keba thing, lets check if the ip has changed - if (existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() != networkDeviceInfo.address().toString()) { - qCDebug(dcKeba()) << "Keba Wallbox IP Address has changed, from" << existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() << "to" << networkDeviceInfo.address().toString(); - existingThing->setParamValue(wallboxThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); + if (existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() != result.networkDeviceInfo.address().toString()) { + qCDebug(dcKeba()) << "Keba Wallbox IP Address has changed, from" << existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() << "to" << result.networkDeviceInfo.address().toString(); + existingThing->setParamValue(wallboxThingIpAddressParamTypeId, result.networkDeviceInfo.address().toString()); KeContact *keba = m_kebaDevices.value(existingThing->id()); if (keba) { - keba->setAddress(QHostAddress(networkDeviceInfo.address())); + keba->setAddress(QHostAddress(result.networkDeviceInfo.address())); + + // Refresh + keba->getReport2(); + keba->getReport3(); } else { - qCWarning(dcKeba()) << "Could not update IP address, for" << existingThing; + qCWarning(dcKeba()) << "Could not update IP address since there is no keba connection for" << existingThing; } } else { - qCDebug(dcKeba()) << "Keba Wallbox" << existingThing->name() << "IP address has not changed" << networkDeviceInfo.address().toString(); + qCDebug(dcKeba()) << "Keba Wallbox" << existingThing->name() << "IP address has not changed" << result.networkDeviceInfo.address().toString(); } break; } @@ -393,28 +472,6 @@ void IntegrationPluginKeba::onConnectionChanged(bool status) } } -void IntegrationPluginKeba::onCommandExecuted(QUuid requestId, bool success) -{ - if (m_asyncActions.contains(requestId)) { - KeContact *keba = static_cast(sender()); - - keba->getReport2(); // Check if the state was actually set - - Thing *thing = myThings().findById(m_kebaDevices.key(keba)); - if (!thing) { - qCWarning(dcKeba()) << "On command executed: missing device object"; - return; - } - - ThingActionInfo *info = m_asyncActions.take(requestId); - if (success) { - info->finish(Thing::ThingErrorNoError); - } else { - info->finish(Thing::ThingErrorHardwareFailure); - } - } -} - void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &reportTwo) { KeContact *keba = static_cast(sender()); @@ -610,54 +667,3 @@ void IntegrationPluginKeba::onBroadcastReceived(KeContact::BroadcastType type, c break; } } - -void IntegrationPluginKeba::executeAction(ThingActionInfo *info) -{ - Thing *thing = info->thing(); - Action action = info->action(); - - if (thing->thingClassId() == wallboxThingClassId) { - KeContact *keba = m_kebaDevices.value(thing->id()); - if (!keba) { - qCWarning(dcKeba()) << "Device not properly initialized, Keba object missing"; - return info->finish(Thing::ThingErrorHardwareNotAvailable); - } - - QUuid requestId; - if (action.actionTypeId() == wallboxMaxChargingCurrentActionTypeId) { - int milliAmpere = action.paramValue(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt() * 1000; - requestId = keba->setMaxAmpereGeneral(milliAmpere); - // Note: since the response only indicates the successful receiving of the command, - // and we only can request the report every 2 seconds as verification, lets set the value right the way - // to prevent jitter while moving the slider - //thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, action.paramValue(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt()); - } else if(action.actionTypeId() == wallboxPowerActionTypeId) { - requestId = keba->enableOutput(action.param(wallboxPowerActionTypeId).value().toBool()); - } else if(action.actionTypeId() == wallboxDisplayActionTypeId) { - requestId = keba->displayMessage(action.param(wallboxDisplayActionMessageParamTypeId).value().toByteArray()); - } else if(action.actionTypeId() == wallboxOutputX2ActionTypeId) { - requestId = keba->setOutputX2(action.param(wallboxOutputX2ActionOutputX2ParamTypeId).value().toBool()); - } else if(action.actionTypeId() == wallboxFailsafeModeActionTypeId) { - int timeout = 0; - if (action.param(wallboxFailsafeModeActionFailsafeModeParamTypeId).value().toBool()) { - timeout = 60; - } - requestId = keba->setFailsafe(timeout, 0, false); - } else { - qCWarning(dcKeba()) << "Unhandled ActionTypeId:" << action.actionTypeId(); - return info->finish(Thing::ThingErrorActionTypeNotFound); - } - - // If the keba returns an invalid uuid, something went wrong - if (requestId.isNull()) { - info->finish(Thing::ThingErrorHardwareFailure); - return; - } - - m_asyncActions.insert(requestId, info); - connect(info, &ThingActionInfo::aborted, this, [requestId, this]{ m_asyncActions.remove(requestId); }); - } else { - qCWarning(dcKeba()) << "Execute action, unhandled device class" << thing->thingClass(); - info->finish(Thing::ThingErrorThingClassNotFound); - } -} diff --git a/keba/kecontact.cpp b/keba/kecontact.cpp index 79815c93..ef182775 100644 --- a/keba/kecontact.cpp +++ b/keba/kecontact.cpp @@ -129,7 +129,6 @@ void KeContact::sendCommand(const QByteArray &command) return; } - qCDebug(dcKeba()) << "--> Writing datagram to" << m_address.toString() << command; m_dataLayer->write(m_address, command); m_requestTimeoutTimer->start(5000); } @@ -157,6 +156,8 @@ void KeContact::setReachable(bool reachable) qCDebug(dcKeba()) << "The keba wallbox on" << m_address.toString() << "is now reachable again."; } else { qCWarning(dcKeba()) << "The keba wallbox on" << m_address.toString() << "is not reachable any more."; + m_requestQueue.clear(); + m_currentRequest = KeContactRequest(); } m_reachable = reachable; @@ -231,7 +232,6 @@ QUuid KeContact::setMaxAmpereGeneral(int milliAmpere) QByteArray datagram = commandLine.toUtf8(); KeContactRequest request(QUuid::createUuid(), datagram); request.setDelayUntilNextCommand(1200); - qCDebug(dcKeba()) << "Set max general charging amps: Datagram:" << datagram; m_requestQueue.enqueue(request); sendNextCommand(); return request.requestId(); @@ -390,11 +390,8 @@ QUuid KeContact::unlockCharger() void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray &datagram) { // Make sure the datagram is for this keba - if (address != m_address) { + if (address != m_address) return; - } - - qCDebug(dcKeba()) << "<--" << qUtf8Printable(datagram); if (datagram.contains("TCH-OK")){ // We received valid data from the address over the data link, so the wallbox must be reachable @@ -462,7 +459,7 @@ void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray setReachable(true); ReportOne reportOne; - qCDebug(dcKeba()) << "Report 1 received"; + //qCDebug(dcKeba()) << "Report 1 received"; reportOne.product = data.value("Product").toString(); reportOne.firmware = data.value("Firmware").toString(); reportOne.serialNumber = data.value("Serial").toString(); @@ -474,7 +471,7 @@ void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray reportOne.dipSw2 = data.value("DIP-Sw2").toString().remove("0x").toUInt(nullptr, 16); if (data.contains("COM-module")) { - reportOne.comModule = (data.value("COM-moDIP-Sw1dule").toInt() == 1); + reportOne.comModule = (data.value("COM-module").toInt() == 1); } else { reportOne.comModule = false; } @@ -490,7 +487,7 @@ void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray setReachable(true); ReportTwo reportTwo; - qCDebug(dcKeba()) << "Report 2 received"; + //qCDebug(dcKeba()) << "Report 2 received"; int state = data.value("State").toInt(); reportTwo.state = State(state); reportTwo.error1 = data.value("Error1").toInt(); @@ -521,17 +518,17 @@ void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray setReachable(true); ReportThree reportThree; - qCDebug(dcKeba()) << "Report 3 received"; - reportThree.currentPhase1 = data.value("I1").toInt()/1000.00; - reportThree.currentPhase2 = data.value("I2").toInt()/1000.00; - reportThree.currentPhase3 = data.value("I3").toInt()/1000.00; + //qCDebug(dcKeba()) << "Report 3 received"; + reportThree.currentPhase1 = data.value("I1").toInt() / 1000.00; + reportThree.currentPhase2 = data.value("I2").toInt() / 1000.00; + reportThree.currentPhase3 = data.value("I3").toInt() / 1000.00; reportThree.voltagePhase1 = data.value("U1").toInt(); reportThree.voltagePhase2 = data.value("U2").toInt(); reportThree.voltagePhase3 = data.value("U3").toInt(); - reportThree.power = data.value("P").toInt()/1000.00; - reportThree.powerFactor = data.value("PF").toInt()/10.00; - reportThree.energySession = data.value("E pres").toInt()/10000.00; - reportThree.energyTotal = data.value("E total").toInt()/10000.00; + reportThree.power = data.value("P").toInt() / 1000.00; + reportThree.powerFactor = data.value("PF").toInt() / 10.00; + reportThree.energySession = data.value("E pres").toInt() / 10000.00; + reportThree.energyTotal = data.value("E total").toInt() / 10000.00; reportThree.serialNumber = data.value("Serial").toString(); reportThree.seconds = data.value("Sec").toInt(); emit reportThreeReceived(reportThree); @@ -540,7 +537,7 @@ void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray setReachable(true); Report1XX report; - qCDebug(dcKeba()) << "Report" << id << "received"; + //qCDebug(dcKeba()) << "Report" << id << "received"; report.sessionId = data.value("Session ID").toInt(); report.currHW = data.value("Curr HW").toInt(); report.startEnergy = data.value("E start").toInt() / 10000.00; @@ -555,6 +552,8 @@ void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray emit report1XXReceived(id, report); } } else { + // Broadcast message, lets see what we recognize + if (data.contains("State")) { // We received valid data from the address over the data link, so the wallbox must be reachable setReachable(true); diff --git a/keba/kecontact.h b/keba/kecontact.h index f5dbe938..3df318d1 100644 --- a/keba/kecontact.h +++ b/keba/kecontact.h @@ -62,7 +62,6 @@ private: }; - class KeContact : public QObject { Q_OBJECT diff --git a/keba/kecontactdatalayer.cpp b/keba/kecontactdatalayer.cpp index c0772647..8f9dd66b 100644 --- a/keba/kecontactdatalayer.cpp +++ b/keba/kecontactdatalayer.cpp @@ -66,6 +66,7 @@ bool KeContactDataLayer::initialized() const void KeContactDataLayer::write(const QHostAddress &address, const QByteArray &data) { + qCDebug(dcKeba()) << "KeContactDataLayer: -->" << address.toString() << data; m_udpSocket->writeDatagram(data, address, m_port); } @@ -80,7 +81,7 @@ void KeContactDataLayer::readPendingDatagrams() while (socket->hasPendingDatagrams()) { datagram.resize(socket->pendingDatagramSize()); socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort); - qCDebug(dcKeba()) << "KeContactDataLayer: Data received from" << senderAddress.toString() << datagram ; + qCDebug(dcKeba()) << "KeContactDataLayer: <--" << senderAddress.toString() << datagram; emit datagramReceived(senderAddress, datagram); } }