Update state after successfull sent command

master
Simon Stürz 2021-12-13 15:51:07 +01:00
parent 8bf4001c07
commit e70b4a679a
4 changed files with 119 additions and 114 deletions

View File

@ -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<KeContact *>(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<KeContact *>(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<KeContact *>(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);
}
}

View File

@ -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);

View File

@ -62,7 +62,6 @@ private:
};
class KeContact : public QObject
{
Q_OBJECT

View File

@ -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);
}
}