/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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 . * * 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 "plugininfo.h" #include "integrationplugingoecharger.h" #include "network/networkdevicediscovery.h" #include #include #include #include #include // API documentation: https://github.com/goecharger/go-eCharger-API-v1 IntegrationPluginGoECharger::IntegrationPluginGoECharger() { } void IntegrationPluginGoECharger::discoverThings(ThingDiscoveryInfo *info) { if (!hardwareManager()->networkDeviceDiscovery()->available()) { qCWarning(dcGoECharger()) << "The network discovery is not available on this platform."; info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available.")); return; } // Perform a network device discovery and filter for "go-eCharger" hosts NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { qCDebug(dcGoECharger()) << "Checking discovered" << networkDeviceInfo; // Filter by hostname if (!networkDeviceInfo.hostName().contains("go-eCharger")) continue; // We need also the mac address if (networkDeviceInfo.macAddress().isEmpty()) continue; QString title; if (networkDeviceInfo.hostName().isEmpty()) { title = networkDeviceInfo.address().toString(); } else { title = "go-eCharger (" + networkDeviceInfo.address().toString() + ")"; } QString description; if (networkDeviceInfo.macAddressManufacturer().isEmpty()) { description = networkDeviceInfo.macAddress(); } else { description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")"; } ThingDescriptor descriptor(goeHomeThingClassId, title, description); ParamList params; params << Param(goeHomeThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); params << Param(goeHomeThingMacAddressParamTypeId, networkDeviceInfo.macAddress()); descriptor.setParams(params); // Check if we already have set up this device Things existingThings = myThings().filterByParam(goeHomeThingMacAddressParamTypeId, networkDeviceInfo.macAddress()); if (existingThings.count() == 1) { qCDebug(dcGoECharger()) << "This go-eCharger already exists in the system" << networkDeviceInfo; descriptor.setThingId(existingThings.first()->id()); } info->addThingDescriptor(descriptor); } info->finish(Thing::ThingErrorNoError); }); } void IntegrationPluginGoECharger::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); if (thing->thingClassId() == goeHomeThingClassId) { QHostAddress address = QHostAddress(thing->paramValue(goeHomeThingIpAddressParamTypeId).toString()); QUrl requestUrl; requestUrl.setScheme("http"); requestUrl.setHost(address.toString()); requestUrl.setPath("/status"); QNetworkRequest request(requestUrl); QNetworkReply *reply = hardwareManager()->networkManager()->get(request); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, info, [=](){ if (reply->error() != QNetworkReply::NoError) { qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable.")); return; } QByteArray data = reply->readAll(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString(); info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data.")); return; } qCDebug(dcGoECharger()) << "Received" << qUtf8Printable(jsonDoc.toJson()); // Verify mqtt client and set it up setupMqttChannel(info, address, jsonDoc.toVariant().toMap()); }); return; } Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); } void IntegrationPluginGoECharger::thingRemoved(Thing *thing) { if (m_channels.contains(thing)) { hardwareManager()->mqttProvider()->releaseChannel(m_channels.take(thing)); } } void IntegrationPluginGoECharger::executeAction(ThingActionInfo *info) { Thing *thing = info->thing(); Action action = info->action(); if (thing->thingClassId() != goeHomeThingClassId) { info->finish(Thing::ThingErrorThingClassNotFound); return; } if (!thing->stateValue(goeHomeConnectedStateTypeId).toBool()) { qCWarning(dcGoECharger()) << thing << "failed to execute action. The device seems not to be connected."; info->finish(Thing::ThingErrorHardwareNotAvailable); return; } if (thing->stateValue(goeHomeSerialNumberStateTypeId).toString().isEmpty()) { qCDebug(dcGoECharger()) << "Could not execute action because the serial number is missing."; info->finish(Thing::ThingErrorHardwareFailure); return; } if (action.actionTypeId() == goeHomePowerActionTypeId) { bool power = action.paramValue(goeHomePowerActionPowerParamTypeId).toBool(); qCDebug(dcGoECharger()) << "Setting charging allowed to" << power; // Set the allow value QString configuration = QString("alw=%1").arg(power ? 1: 0); sendActionRequest(thing, info, configuration); return; } else if (action.actionTypeId() == goeHomeMaxChargingCurrentActionTypeId) { int maxChargingCurrent = action.paramValue(goeHomeMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt(); qCDebug(dcGoECharger()) << "Setting max charging current to" << maxChargingCurrent << "A"; // Set the allow value QString configuration = QString("ama=%1").arg(maxChargingCurrent); sendActionRequest(thing, info, configuration); return; } else if (action.actionTypeId() == goeHomeCloudActionTypeId) { bool enabled = action.paramValue(goeHomeCloudActionCloudParamTypeId).toBool(); qCDebug(dcGoECharger()) << "Set cloud" << (enabled ? "enabled" : "disabled"); // Set the allow value QString configuration = QString("cdi=%1").arg(enabled ? 1: 0); sendActionRequest(thing, info, configuration); return; } else if (action.actionTypeId() == goeHomeLedBrightnessActionTypeId) { quint8 brightness = action.paramValue(goeHomeLedBrightnessActionLedBrightnessParamTypeId).toUInt(); qCDebug(dcGoECharger()) << "Set led brightnss to" << brightness << "/" << 255; // Set the allow value QString configuration = QString("lbr=%1").arg(brightness); sendActionRequest(thing, info, configuration); return; } else if (action.actionTypeId() == goeHomeLedEnergySaveActionTypeId) { bool enabled = action.paramValue(goeHomeLedEnergySaveActionLedEnergySaveParamTypeId).toBool(); qCDebug(dcGoECharger()) << "Set led energy saving" << (enabled ? "enabled" : "disabled"); // Set the allow value QString configuration = QString("lse=%1").arg(enabled ? 1: 0); sendActionRequest(thing, info, configuration); return; } else { info->finish(Thing::ThingErrorActionTypeNotFound); } } void IntegrationPluginGoECharger::onClientConnected(MqttChannel *channel) { Thing *thing = m_channels.key(channel); if (!thing) { qCWarning(dcGoECharger()) << "Received a client connect for an unknown thing. Ignoring the event."; return; } qCDebug(dcGoECharger()) << thing << "connected"; thing->setStateValue(goeHomeConnectedStateTypeId, true); } void IntegrationPluginGoECharger::onClientDisconnected(MqttChannel *channel) { Thing *thing = m_channels.key(channel); if (!thing) { qCWarning(dcGoECharger()) << "Received a client disconnect for an unknown thing. Ignoring the event."; return; } qCDebug(dcGoECharger()) << thing << "connected"; thing->setStateValue(goeHomeConnectedStateTypeId, false); } void IntegrationPluginGoECharger::onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload) { Thing *thing = m_channels.key(channel); if (!thing) { qCWarning(dcGoECharger()) << "Received a MQTT client publish from an unknown thing. Ignoring the event."; return; } qCDebug(dcGoECharger()) << thing << "publish received" << topic; QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error); if (error.error != QJsonParseError::NoError) { qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(payload) << error.errorString(); return; } QString serialNumber = thing->stateValue(goeHomeSerialNumberStateTypeId).toString(); if (topic == QString("go-eCharger/%1/status").arg(serialNumber)) { update(thing, jsonDoc.toVariant().toMap()); } else { qCDebug(dcGoECharger()) << "Unhandled topic publish received:" << topic << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Compact)); } } void IntegrationPluginGoECharger::update(Thing *thing, const QVariantMap &statusMap) { if (thing->thingClassId() == goeHomeThingClassId) { // Parse status map and update states... CarState carState = static_cast(statusMap.value("car").toUInt()); switch (carState) { case CarStateReadyNoCar: thing->setStateValue(goeHomeCarStatusStateTypeId, "Ready but no vehicle connected"); break; case CarStateCharging: thing->setStateValue(goeHomeCarStatusStateTypeId, "Vehicle loads"); break; case CarStateWaitForCar: thing->setStateValue(goeHomeCarStatusStateTypeId, "Waiting for vehicle"); break; case CarStateChargedCarConnected: thing->setStateValue(goeHomeCarStatusStateTypeId, "Charging finished and vehicle still connected"); break; } Access accessStatus = static_cast(statusMap.value("ast").toUInt()); switch (accessStatus) { case AccessOpen: thing->setStateValue(goeHomeAccessStateTypeId, "Open"); break; case AccessRfid: thing->setStateValue(goeHomeAccessStateTypeId, "RFID"); break; case AccessAuto: thing->setStateValue(goeHomeAccessStateTypeId, "Automatic"); break; } QVariantList temperatureSensorList = statusMap.value("tma").toList(); if (temperatureSensorList.count() == 4) { thing->setStateValue(goeHomeTemperatureSensor1StateTypeId, temperatureSensorList.at(0).toDouble()); thing->setStateValue(goeHomeTemperatureSensor2StateTypeId, temperatureSensorList.at(1).toDouble()); thing->setStateValue(goeHomeTemperatureSensor3StateTypeId, temperatureSensorList.at(2).toDouble()); thing->setStateValue(goeHomeTemperatureSensor4StateTypeId, temperatureSensorList.at(3).toDouble()); } thing->setStateValue(goeHomeTotalEnergyConsumedStateTypeId, statusMap.value("eto").toUInt() / 10.0); thing->setStateValue(goeHomeChargeEnergyStateTypeId, statusMap.value("dws").toUInt() / 360000.0); thing->setStateValue(goeHomePowerStateTypeId, (statusMap.value("alw").toUInt() == 0 ? false : true)); thing->setStateValue(goeHomeUpdateAvailableStateTypeId, (statusMap.value("upd").toUInt() == 0 ? false : true)); thing->setStateValue(goeHomeAdapterConnectedStateTypeId, (statusMap.value("adi").toUInt() == 0 ? false : true)); thing->setStateValue(goeHomeCloudStateTypeId, (statusMap.value("cdi").toUInt() == 0 ? false : true)); thing->setStateValue(goeHomeFirmwareVersionStateTypeId, statusMap.value("fwv").toString()); thing->setStateValue(goeHomeMaxChargingCurrentStateTypeId, statusMap.value("ama").toUInt()); thing->setStateValue(goeHomeLedBrightnessStateTypeId, statusMap.value("lbr").toUInt()); thing->setStateValue(goeHomeLedEnergySaveStateTypeId, statusMap.value("lse").toBool()); thing->setStateValue(goeHomeSerialNumberStateTypeId, statusMap.value("sse").toString()); } } QNetworkRequest IntegrationPluginGoECharger::buildConfigurationRequest(const QHostAddress &address, const QString &configuration) { QUrl requestUrl; requestUrl.setScheme("http"); requestUrl.setHost(address.toString()); requestUrl.setPath("/mqtt"); QUrlQuery query; query.addQueryItem("payload", configuration); requestUrl.setQuery(query); return QNetworkRequest(requestUrl); } void IntegrationPluginGoECharger::sendActionRequest(Thing *thing, ThingActionInfo *info, const QString &configuration) { // Lets use rest here since we get a reply on the rest request. For using MQTT publish to topic "go-eCharger//cmd/req" QNetworkRequest request = buildConfigurationRequest(QHostAddress(thing->paramValue(goeHomeThingIpAddressParamTypeId).toString()), configuration); QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET"); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, info, [=](){ if (reply->error() != QNetworkReply::NoError) { qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable.")); return; } QByteArray data = reply->readAll(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString(); info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data.")); return; } info->finish(Thing::ThingErrorNoError); update(thing, jsonDoc.toVariant().toMap()); }); } void IntegrationPluginGoECharger::setupMqttChannel(ThingSetupInfo *info, const QHostAddress &address, const QVariantMap &statusMap) { Thing *thing = info->thing(); QString serialNumber = statusMap.value("sse").toString(); QString clientId = QString("go-eCharger:%1:%2").arg(serialNumber).arg(statusMap.value("rbc").toInt()); QString statusTopic = QString("go-eCharger/%1/status").arg(serialNumber); QString commandTopic = QString("go-eCharger/%1/cmd/req").arg(serialNumber); qCDebug(dcGoECharger()) << "Setting up mqtt channel for" << thing << address.toString() << statusTopic << commandTopic; MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel(clientId, address, {statusTopic, commandTopic}); if (!channel) { qCWarning(dcGoECharger()) << "Failed to create MQTT channel for" << thing; info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error creating MQTT channel. Please check MQTT server settings.")); return; } m_channels.insert(thing, channel); connect(channel, &MqttChannel::clientConnected, this, &IntegrationPluginGoECharger::onClientConnected); connect(channel, &MqttChannel::clientDisconnected, this, &IntegrationPluginGoECharger::onClientDisconnected); connect(channel, &MqttChannel::publishReceived, this, &IntegrationPluginGoECharger::onPublishReceived); // Configure the mqtt server on the go-e QNetworkRequest request = buildConfigurationRequest(address, QString("mcs=%1").arg(channel->serverAddress().toString())); qCDebug(dcGoECharger()) << "Configure nymea mqtt server address on" << thing << request.url().toString(); QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET"); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, info, [=](){ if (reply->error() != QNetworkReply::NoError) { qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable.")); return; } QByteArray data = reply->readAll(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString(); info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data.")); return; } // Verify response matches the requsted value if (jsonDoc.toVariant().toMap().value("mcs").toString() != channel->serverAddress().toString()) { qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested server address" << channel->serverAddress().toString(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox.")); return; } else { qCDebug(dcGoECharger()) << "Configured successfully MQTT server" << thing << channel->serverAddress().toString(); } QNetworkRequest request = buildConfigurationRequest(address, QString("mcp=%1").arg(channel->serverPort())); qCDebug(dcGoECharger()) << "Configure nymea mqtt server port on" << thing << request.url().toString(); QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET"); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, info, [=](){ if (reply->error() != QNetworkReply::NoError) { qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable.")); return; } QByteArray data = reply->readAll(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString(); info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data.")); return; } // Verify response matches the requsted value if (jsonDoc.toVariant().toMap().value("mcp").toUInt() != channel->serverPort()) { qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested server port" << channel->serverPort(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox.")); return; } else { qCDebug(dcGoECharger()) << "Configured successfully MQTT server" << thing << channel->serverPort(); } QNetworkRequest request = buildConfigurationRequest(address, QString("mcu=%1").arg(channel->username())); qCDebug(dcGoECharger()) << "Configure nymea mqtt server user name on" << thing << request.url().toString(); QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET"); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, info, [=](){ if (reply->error() != QNetworkReply::NoError) { qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable.")); return; } QByteArray data = reply->readAll(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString(); info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data.")); return; } // Verify response matches the requsted value if (jsonDoc.toVariant().toMap().value("mcu").toString() != channel->username()) { qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested server username" << channel->username(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox.")); return; } else { qCDebug(dcGoECharger()) << "Configured successfully MQTT server" << thing << channel->username(); } QNetworkRequest request = buildConfigurationRequest(address, QString("mck=%1").arg(channel->password())); qCDebug(dcGoECharger()) << "Configure nymea mqtt server password on" << thing << request.url().toString(); QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET"); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, info, [=](){ if (reply->error() != QNetworkReply::NoError) { qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable.")); return; } QByteArray data = reply->readAll(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString(); info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data.")); return; } // Verify response matches the requsted value if (jsonDoc.toVariant().toMap().value("mck").toString() != channel->password()) { qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested server password" << channel->password(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox.")); return; } else { qCDebug(dcGoECharger()) << "Configured successfully MQTT server" << thing << channel->password(); } QNetworkRequest request = buildConfigurationRequest(address, QString("mce=1")); qCDebug(dcGoECharger()) << "Enable custom mqtt server on" << thing << request.url().toString(); QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET"); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, info, [=](){ if (reply->error() != QNetworkReply::NoError) { qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString(); info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable.")); return; } QByteArray data = reply->readAll(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString(); info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data.")); return; } // Verify response matches the requsted value QVariantMap statusMap = jsonDoc.toVariant().toMap(); if (statusMap.value("mce").toInt() != 1) { qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested value 1"; info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox.")); return; } else { qCDebug(dcGoECharger()) << "Configured successfully MQTT server enabled" << thing; } info->finish(Thing::ThingErrorNoError); qCDebug(dcGoECharger()) << "Configuration of MQTT for" << thing << "finished successfully"; // Update states... update(thing, statusMap); }); }); }); }); }); }