diff --git a/debian/control b/debian/control index 4d205c81..c5875e0a 100644 --- a/debian/control +++ b/debian/control @@ -988,6 +988,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, nymea-plugins-translations, nmap, + arping, Replaces: guh-plugin-keba Description: nymea.io plugin for keba The nymea daemon is a plugin based IoT (Internet of Things) server. The diff --git a/keba/README.md b/keba/README.md index 87bdbebb..3d1046d0 100644 --- a/keba/README.md +++ b/keba/README.md @@ -4,17 +4,19 @@ This plugin allows to control Keba KeContact EV-Charging stations. ## Supported Things -* KeContact - * Enable/disable the charging stations - * Set maximum charging current - * Get all informations e.g. voltage, current ... - * Print messages on the display +* KeContact Wallbox + * P20 + * P30 + * BMW ## Requirments * nymea and the wallbox are required to be in the same network. -* Port 7090 must not be blocked by a firewall or router. +* UDP Port 7090 must not be blocked by a firewall or router. * The package "nymea-plugin-keba" must be installed. +* KeContact P20 Charging station with network connection (LSA+ socket). Firmware version: 2.5 or higher. +* KeContact P30 Charging station or BMW wallbox. Firmware version 3.05 of higher. +* Enabled UDP function with DIP-switch DWS1.3 = ON. ## More diff --git a/keba/discovery.cpp b/keba/discovery.cpp index 070f64e1..a41192c4 100644 --- a/keba/discovery.cpp +++ b/keba/discovery.cpp @@ -109,7 +109,7 @@ void Discovery::discoveryFinished(int exitCode, QProcess::ExitStatus exitStatus) QXmlStreamReader reader(data); int foundHosts = 0; - + qCDebug(dcKebaKeContact()) << "nmap finished network discovery:"; while (!reader.atEnd() && !reader.hasError()) { QXmlStreamReader::TokenType token = reader.readNext(); if(token == QXmlStreamReader::StartDocument) @@ -143,7 +143,7 @@ void Discovery::discoveryFinished(int exitCode, QProcess::ExitStatus exitStatus) if (isUp) { foundHosts++; - qCDebug(dcKebaKeContact()) << "Have host:" << address; + qCDebug(dcKebaKeContact()) << " - host:" << address; Host *host = new Host(); host->setAddress(address); @@ -264,7 +264,7 @@ void Discovery::finishDiscovery() qDeleteAll(m_scanResults); m_scanResults.clear(); - qCDebug(dcKebaKeContact()) << "Emitting device discovered for" << hosts.count() << "devices"; + qCDebug(dcKebaKeContact()) << "Found" << hosts.count() << "network devices"; m_timeoutTimer.stop(); emit finished(hosts); } diff --git a/keba/integrationpluginkeba.cpp b/keba/integrationpluginkeba.cpp index 381fccd1..09efb12c 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -42,22 +42,56 @@ IntegrationPluginKeba::IntegrationPluginKeba() } +void IntegrationPluginKeba::init() +{ + m_discovery = new Discovery(this); + connect(m_discovery, &Discovery::finished, this, [this](const QList &hosts) { + + foreach (const Host &host, hosts) { + if (!host.hostName().contains("keba", Qt::CaseSensitivity::CaseInsensitive)) + continue; + + foreach (Thing *existingThing, myThings()) { + 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() == host.address()) { + qCDebug(dcKebaKeContact()) << "Keba Wallbox MAC Address has been discovered" << existingThing->name() << host.macAddress(); + existingThing->setParamValue(wallboxThingMacAddressParamTypeId, host.macAddress()); + + } + } else if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString() == host.macAddress()) { + if (existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() != host.address()) { + qCDebug(dcKebaKeContact()) << "Keba Wallbox IP Address has changed, from" << existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() << "to" << host.address(); + existingThing->setParamValue(wallboxThingIpAddressParamTypeId, host.address()); + + } else { + qCDebug(dcKebaKeContact()) << "Keba Wallbox" << existingThing->name() << "IP address has not changed" << host.address(); + } + break; + } + } + } + }); +} + void IntegrationPluginKeba::discoverThings(ThingDiscoveryInfo *info) { if (info->thingClassId() == wallboxThingClassId) { - Discovery *discovery = new Discovery(info); - discovery->discoverHosts(25); + qCDebug(dcKebaKeContact()) << "Discovering Keba Wallbox"; + m_discovery->discoverHosts(25); + connect(m_discovery, &Discovery::finished, info, [this, info] (const QList &hosts) { - connect(discovery, &Discovery::finished, info, [this, info](const QList &hosts) { - qCDebug(dcKebaKeContact()) << "Discovery finished. Found" << hosts.count() << "devices"; foreach (const Host &host, hosts) { if (!host.hostName().contains("keba", Qt::CaseSensitivity::CaseInsensitive)) continue; + qCDebug(dcKebaKeContact()) << " - Keba Wallbox" << host.address() << host.macAddress(); ThingDescriptor descriptor(wallboxThingClassId, "Wallbox", host.address() + " (" + host.macAddress() + ")"); + // Rediscovery foreach (Thing *existingThing, myThings()) { if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString() == host.macAddress()) { + qCDebug(dcKebaKeContact()) << " - Device is already added"; descriptor.setThingId(existingThing->id()); break; } @@ -84,28 +118,49 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) if (thing->thingClassId() == wallboxThingClassId) { - QHostAddress address = QHostAddress(thing->paramValue(wallboxThingIpAddressParamTypeId).toString()); - KeContact *keba = new KeContact(address, this); - connect(keba, &KeContact::connectionChanged, this, &IntegrationPluginKeba::onConnectionChanged); - connect(keba, &KeContact::commandExecuted, this, &IntegrationPluginKeba::onCommandExecuted); - connect(keba, &KeContact::reportOneReceived, this, &IntegrationPluginKeba::onReportOneReceived); - connect(keba, &KeContact::reportTwoReceived, this, &IntegrationPluginKeba::onReportTwoReceived); - connect(keba, &KeContact::reportThreeReceived, this, &IntegrationPluginKeba::onReportThreeReceived); - connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived); - if (!keba->init()){ - qCWarning(dcKebaKeContact()) << "Cannot bind to port" << 7090; - keba->deleteLater(); - return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port.")); + if(!m_kebaData){ + qCDebug(dcKebaKeContact()) << "Creating new Keba data layer"; + m_kebaData = new KeContactDataLayer(this); + if (!m_kebaData->init()) { + m_kebaData->deleteLater(); + m_kebaData = nullptr; + connect(info, &ThingSetupInfo::aborted, m_kebaData, &KeContactDataLayer::deleteLater); // Clean up if the setup fails + return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port.")); + } } - ThingId id = thing->id(); - m_kebaDevices.insert(id, keba); - m_asyncSetup.insert(keba, info); + QHostAddress address = QHostAddress(thing->paramValue(wallboxThingIpAddressParamTypeId).toString()); + KeContact *keba = new KeContact(address, m_kebaData, m_kebaData); + connect(keba, &KeContact::reachableChanged, this, &IntegrationPluginKeba::onConnectionChanged); + connect(keba, &KeContact::commandExecuted, this, &IntegrationPluginKeba::onCommandExecuted); + connect(keba, &KeContact::reportTwoReceived, this, &IntegrationPluginKeba::onReportTwoReceived); + connect(keba, &KeContact::reportThreeReceived, this, &IntegrationPluginKeba::onReportThreeReceived); + connect(keba, &KeContact::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived); + connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived); + keba->getReport1(); - connect(info, &ThingSetupInfo::aborted, this, [id, keba, this]{ - m_asyncSetup.remove(keba); - m_kebaDevices.remove(id); - keba->deleteLater(); + connect(keba, &KeContact::reportOneReceived, info, [info, this, keba] (const KeContact::ReportOne &report) { + Thing *thing = info->thing(); + + qCDebug(dcKebaKeContact()) << "Report one received for" << thing->name(); + qCDebug(dcKebaKeContact()) << " - Firmware" << report.firmware; + qCDebug(dcKebaKeContact()) << " - Serial" << report.serialNumber; + qCDebug(dcKebaKeContact()) << " - Product" << report.product; + qCDebug(dcKebaKeContact()) << " - Uptime" << report.seconds/60 << "[min]"; + qCDebug(dcKebaKeContact()) << " - Com Module" << report.comModule; + + thing->setStateValue(wallboxConnectedStateTypeId, true); + thing->setStateValue(wallboxFirmwareStateTypeId, report.firmware); + thing->setStateValue(wallboxSerialnumberStateTypeId, report.serialNumber); + thing->setStateValue(wallboxModelStateTypeId, report.product); + thing->setStateValue(wallboxUptimeStateTypeId, report.seconds/60); + + m_kebaDevices.insert(thing->id(), keba); + info->finish(Thing::ThingErrorNoError); + }); + connect(info, &ThingSetupInfo::aborted, keba, &KeContact::deleteLater); // Clean up if the setup fails + connect(keba, &KeContact::destroyed, this, [thing, this]{ + m_kebaDevices.remove(thing->id()); }); } else { qCWarning(dcKebaKeContact()) << "setupDevice, unhandled device class" << thing->thingClass(); @@ -116,52 +171,71 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) void IntegrationPluginKeba::postSetupThing(Thing *thing) { qCDebug(dcKebaKeContact()) << "Post setup" << thing->name(); - KeContact *keba = m_kebaDevices.value(thing->id()); - if (!keba) { + if (thing->thingClassId() != wallboxThingClassId) { + qCWarning(dcKebaKeContact()) << "Thing class id not supported" << thing->thingClassId(); return; } - keba->getReport2(); - keba->getReport3(); + thing->setStateValue(wallboxConnectedStateTypeId, true); + KeContact *keba = m_kebaDevices.value(thing->id()); + if (!keba) { + qCWarning(dcKebaKeContact()) << "No Keba connection found for this thing"; + } else { + keba->getReport2(); + keba->getReport3(); + } + if (thing->paramValue(wallboxThingMacAddressParamTypeId).toString().isEmpty()) { + m_discovery->discoverHosts(25); + } - if (!m_pluginTimer) { - m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60); - connect(m_pluginTimer, &PluginTimer::timeout, this, &IntegrationPluginKeba::updateData); + if (!m_updateTimer) { + m_updateTimer = hardwareManager()->pluginTimerManager()->registerTimer(60); + connect(m_updateTimer, &PluginTimer::timeout, this, [this] { + + foreach (Thing *thing, myThings().filterByThingClassId(wallboxThingClassId)) { + KeContact *keba = m_kebaDevices.value(thing->id()); + if (!keba) { + qCWarning(dcKebaKeContact()) << "No Keba connection found for" << thing->name(); + } + keba->getReport2(); + keba->getReport3(); + if (thing->stateValue(wallboxActivityStateTypeId).toString() == "Charging") { + keba->getReport1XX(100); + } + } + }); + } + + if (!m_reconnectTimer) { + m_reconnectTimer = hardwareManager()->pluginTimerManager()->registerTimer(60*5); + connect(m_reconnectTimer, &PluginTimer::timeout, this, [this] { + Q_FOREACH(Thing *thing, myThings().filterByThingClassId(wallboxThingClassId)) { + if (thing->stateValue(wallboxConnectedStateTypeId) == false) { + m_discovery->discoverHosts(25); + break; // start discovery once for every device as soon as one device is disconnected + } + } + }); } } void IntegrationPluginKeba::thingRemoved(Thing *thing) { + qCDebug(dcKebaKeContact()) << "Deleting" << thing->name(); if (thing->thingClassId() == wallboxThingClassId) { KeContact *keba = m_kebaDevices.take(thing->id()); keba->deleteLater(); } if (myThings().empty()) { - // last device has been removed the plug in timer can be stopped again - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); - m_pluginTimer = nullptr; - } -} + qCDebug(dcKebaKeContact()) << "Closing UDP Ports"; + m_kebaData->deleteLater(); + m_kebaData = nullptr; -void IntegrationPluginKeba::updateData() -{ - foreach (KeContact *keba, m_kebaDevices) { - keba->getReport2(); - keba->getReport3(); - } - - foreach (Thing *thing, myThings().filterByThingClassId(wallboxThingClassId)) { - if (m_chargingSessionStartTime.contains(thing->id())) { - QDateTime startTime = m_chargingSessionStartTime.value(thing->id()); - - QTimeZone tz = QTimeZone(QTimeZone::systemTimeZoneId()); - QDateTime currentTime = QDateTime::currentDateTime().toTimeZone(tz); - - int minutes = (currentTime.toMSecsSinceEpoch() - startTime.toMSecsSinceEpoch())/60000; - thing->setStateValue(wallboxSessionTimeStateTypeId, minutes); - } else { - thing->setStateValue(wallboxSessionTimeStateTypeId, 0); - } + qCDebug(dcKebaKeContact()) << "Stopping plugin timers"; + hardwareManager()->pluginTimerManager()->unregisterTimer(m_reconnectTimer); + m_reconnectTimer = nullptr; + hardwareManager()->pluginTimerManager()->unregisterTimer(m_updateTimer); + m_updateTimer = nullptr; } } @@ -187,16 +261,6 @@ void IntegrationPluginKeba::setDeviceState(Thing *thing, KeContact::State state) thing->setStateValue(wallboxActivityStateTypeId, "Authorization rejected"); break; } - - if (state == KeContact::StateCharging) { - //Set charging session - QTimeZone tz = QTimeZone(QTimeZone::systemTimeZoneId()); - QDateTime startedChargingSession = QDateTime::currentDateTime().toTimeZone(tz); - m_chargingSessionStartTime.insert(thing->id(), startedChargingSession); - } else { - m_chargingSessionStartTime.remove(thing->id()); - thing->setStateValue(wallboxSessionTimeStateTypeId, 0); - } } void IntegrationPluginKeba::setDevicePlugState(Thing *thing, KeContact::PlugState plugState) @@ -230,15 +294,17 @@ void IntegrationPluginKeba::onConnectionChanged(bool status) } thing->setStateValue(wallboxConnectedStateTypeId, status); if (!status) { - //TODO start rediscovery + m_discovery->discoverHosts(25); } } void IntegrationPluginKeba::onCommandExecuted(QUuid requestId, bool success) { - updateData(); 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(dcKebaKeContact()) << "On command executed: missing device object"; @@ -253,18 +319,6 @@ void IntegrationPluginKeba::onCommandExecuted(QUuid requestId, bool success) } } -void IntegrationPluginKeba::onReportOneReceived(const KeContact::ReportOne &reportOne) -{ - Q_UNUSED(reportOne); - KeContact *keba = static_cast(sender()); - if (m_asyncSetup.contains(keba)) { - ThingSetupInfo *info = m_asyncSetup.value(keba); - info->finish(Thing::ThingErrorNoError); - } else { - qCDebug(dcKebaKeContact()) << "Report one received without an associated async setup"; - } -} - void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &reportTwo) { KeContact *keba = static_cast(sender()); @@ -272,11 +326,46 @@ void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &repo if (!thing) return; - thing->setStateValue(wallboxPowerStateTypeId, reportTwo.enableUser); - thing->setStateValue(wallboxMaxChargingCurrentPercentStateTypeId, reportTwo.MaxCurrentPercentage); + qCDebug(dcKebaKeContact()) << "Report 2 received for" << thing->name() << "Serial number:" << thing->stateValue(wallboxSerialnumberStateTypeId).toString(); + qCDebug(dcKebaKeContact()) << " - State:" << reportTwo.state; + qCDebug(dcKebaKeContact()) << " - Error 1:" << reportTwo.error1; + qCDebug(dcKebaKeContact()) << " - Error 2:" << reportTwo.error2; + qCDebug(dcKebaKeContact()) << " - Plug:" << reportTwo.plugState; + qCDebug(dcKebaKeContact()) << " - Enable sys:" << reportTwo.enableSys; + qCDebug(dcKebaKeContact()) << " - Enable user:" << reportTwo.enableUser; + qCDebug(dcKebaKeContact()) << " - Max curr:" << reportTwo.maxCurrent; + qCDebug(dcKebaKeContact()) << " - Max curr %:" << reportTwo.maxCurrentPercentage; + qCDebug(dcKebaKeContact()) << " - Curr HW:" << reportTwo.currentHardwareLimitation; + qCDebug(dcKebaKeContact()) << " - Curr User:" << reportTwo.currentUser; + qCDebug(dcKebaKeContact()) << " - Curr FS:" << reportTwo.currentFailsafe; + qCDebug(dcKebaKeContact()) << " - Tmo FS:" << reportTwo.timeoutFailsafe; + qCDebug(dcKebaKeContact()) << " - Curr timer:" << reportTwo.currTimer; + qCDebug(dcKebaKeContact()) << " - Timeout CT:" << reportTwo.timeoutCt; + qCDebug(dcKebaKeContact()) << " - Output:" << reportTwo.output; + qCDebug(dcKebaKeContact()) << " - Input:" << reportTwo.input; + qCDebug(dcKebaKeContact()) << " - Serial number:" << reportTwo.serialNumber; + qCDebug(dcKebaKeContact()) << " - Uptime:" << reportTwo.seconds/60 << "[min]"; - setDeviceState(thing, reportTwo.state); - setDevicePlugState(thing, reportTwo.plugState); + if (reportTwo.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) { + setDeviceState(thing, reportTwo.state); + setDevicePlugState(thing, reportTwo.plugState); + + thing->setStateValue(wallboxPowerStateTypeId, reportTwo.enableUser); + thing->setStateValue(wallboxError1StateTypeId, reportTwo.error1); + thing->setStateValue(wallboxError2StateTypeId, reportTwo.error2); + thing->setStateValue(wallboxSystemEnabledStateTypeId, reportTwo.enableSys); + + thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, reportTwo.currentUser); + thing->setStateValue(wallboxMaxChargingCurrentPercentStateTypeId, reportTwo.maxCurrentPercentage); + thing->setStateValue(wallboxMaxPossibleChargingCurrentStateTypeId, reportTwo.currentHardwareLimitation); + + thing->setStateValue(wallboxOutputX2StateTypeId, reportTwo.output); + thing->setStateValue(wallboxInputStateTypeId, reportTwo.input); + + thing->setStateValue(wallboxUptimeStateTypeId, reportTwo.seconds/60); + } else { + qCWarning(dcKebaKeContact()) << "Received report but the serial number didn't match"; + } } void IntegrationPluginKeba::onReportThreeReceived(const KeContact::ReportThree &reportThree) @@ -286,15 +375,91 @@ void IntegrationPluginKeba::onReportThreeReceived(const KeContact::ReportThree & if (!thing) return; - thing->setStateValue(wallboxI1EventTypeId, reportThree.CurrentPhase1); - thing->setStateValue(wallboxI2EventTypeId, reportThree.CurrentPhase2); - thing->setStateValue(wallboxI3EventTypeId, reportThree.CurrentPhase3); - thing->setStateValue(wallboxU1EventTypeId, reportThree.VoltagePhase1); - thing->setStateValue(wallboxU2EventTypeId, reportThree.VoltagePhase2); - thing->setStateValue(wallboxU3EventTypeId, reportThree.VoltagePhase3); - thing->setStateValue(wallboxPStateTypeId, reportThree.Power); - thing->setStateValue(wallboxEPStateTypeId, reportThree.EnergySession); - thing->setStateValue(wallboxTotalEnergyConsumedStateTypeId, reportThree.EnergyTotal); + qCDebug(dcKebaKeContact()) << "Report 3 received for" << thing->name() << "Serial number:" << thing->stateValue(wallboxSerialnumberStateTypeId).toString(); + qCDebug(dcKebaKeContact()) << " - Current phase 1:" << reportThree.currentPhase1 << "[A]"; + qCDebug(dcKebaKeContact()) << " - Current phase 2:" << reportThree.currentPhase2 << "[A]"; + qCDebug(dcKebaKeContact()) << " - Current phase 3:" << reportThree.currentPhase3 << "[A]"; + qCDebug(dcKebaKeContact()) << " - Voltage phase 1:" << reportThree.voltagePhase1 << "[V]"; + qCDebug(dcKebaKeContact()) << " - Voltage phase 2:" << reportThree.voltagePhase2 << "[V]"; + qCDebug(dcKebaKeContact()) << " - Voltage phase 3:" << reportThree.voltagePhase3 << "[V]"; + qCDebug(dcKebaKeContact()) << " - Power consumption:" << reportThree.power << "[kW]"; + qCDebug(dcKebaKeContact()) << " - Energy session" << reportThree.energySession << "[kWh]"; + qCDebug(dcKebaKeContact()) << " - Energy total" << reportThree.energyTotal << "[kWh]"; + qCDebug(dcKebaKeContact()) << " - Serial number" << reportThree.serialNumber; + qCDebug(dcKebaKeContact()) << " - Uptime" << reportThree.seconds/60 << "[min]"; + + if (reportThree.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) { + thing->setStateValue(wallboxCurrentPhase1EventTypeId, reportThree.currentPhase1); + thing->setStateValue(wallboxCurrentPhase2EventTypeId, reportThree.currentPhase2); + thing->setStateValue(wallboxCurrentPhase3EventTypeId, reportThree.currentPhase3); + thing->setStateValue(wallboxVoltagePhase1EventTypeId, reportThree.voltagePhase1); + thing->setStateValue(wallboxVoltagePhase2EventTypeId, reportThree.voltagePhase2); + thing->setStateValue(wallboxVoltagePhase3EventTypeId, reportThree.voltagePhase3); + thing->setStateValue(wallboxPowerConsumptionStateTypeId, reportThree.power); + thing->setStateValue(wallboxSessionEnergyStateTypeId, reportThree.energySession); + thing->setStateValue(wallboxPowerFactorStateTypeId, reportThree.powerFactor); + thing->setStateValue(wallboxTotalEnergyConsumedStateTypeId, reportThree.energyTotal); + } else { + qCWarning(dcKebaKeContact()) << "Received report but the serial number didn't match"; + } +} + +void IntegrationPluginKeba::onReport1XXReceived(int reportNumber, const KeContact::Report1XX &report) +{ + KeContact *keba = static_cast(sender()); + Thing *thing = myThings().findById(m_kebaDevices.key(keba)); + if (!thing) + return; + + qCDebug(dcKebaKeContact()) << "Report" << reportNumber << "received for" << thing->name() << "Serial number:" << thing->stateValue(wallboxSerialnumberStateTypeId).toString(); + qCDebug(dcKebaKeContact()) << " - Session Id" << report.sessionId; + qCDebug(dcKebaKeContact()) << " - Curr HW" << report.currHW; + qCDebug(dcKebaKeContact()) << " - Energy start" << report.startEnergy; + qCDebug(dcKebaKeContact()) << " - Energy present" << report.presentEnergy; + qCDebug(dcKebaKeContact()) << " - Start time" << report.startTime; + qCDebug(dcKebaKeContact()) << " - End time" << report.endTime; + qCDebug(dcKebaKeContact()) << " - Stop reason" << report.stopReason; + qCDebug(dcKebaKeContact()) << " - RFID Tag" << report.rfidTag; + qCDebug(dcKebaKeContact()) << " - RFID Class" << report.rfidClass; + qCDebug(dcKebaKeContact()) << " - Serial number" << report.serialNumber; + qCDebug(dcKebaKeContact()) << " - Uptime" << report.seconds; + + if (reportNumber == 100) { + // Report 100 is the current charging session + if (report.endTime == 0) { + // if the charing session is finished the end time will be set + double duration = (report.seconds - report.startTime)/60.00; + thing->setStateValue(wallboxSessionTimeStateTypeId, duration); + } else { + // Charging session is finished and copied to Report 101 + } + + } else if (reportNumber == 101) { + // Report 101 is the lastest finished session + if (report.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) { + if (!m_lastSessionId.contains(thing->id())) { + // This happens after reboot + m_lastSessionId.insert(thing->id(), report.sessionId); + } else { + if (m_lastSessionId.value(thing->id()) != report.sessionId) { + qCDebug(dcKebaKeContact()) << "New session id receivd"; + Event event; + event.setEventTypeId(wallboxChargingSessionFinishedEventTypeId); + event.setThingId(thing->id()); + ParamList params; + params << Param(wallboxChargingSessionFinishedEventEnergyParamTypeId, report.presentEnergy); + params << Param(wallboxChargingSessionFinishedEventDurationParamTypeId, report.endTime); + params << Param(wallboxChargingSessionFinishedEventIdParamTypeId); + event.setParams(params); + emitEvent(event); + } + } + } else { + qCWarning(dcKebaKeContact()) << "Received report but the serial number didn't match"; + } + } else { + qCWarning(dcKebaKeContact()) << "Received unhandled report" << reportNumber; + } } void IntegrationPluginKeba::onBroadcastReceived(KeContact::BroadcastType type, const QVariant &content) @@ -304,22 +469,26 @@ void IntegrationPluginKeba::onBroadcastReceived(KeContact::BroadcastType type, c if (!thing) return; + qCDebug(dcKebaKeContact()) << "Broadcast received" << type << "value" << content; + switch (type) { case KeContact::BroadcastTypePlug: setDevicePlugState(thing, KeContact::PlugState(content.toInt())); break; case KeContact::BroadcastTypeInput: + thing->setStateValue(wallboxInputStateTypeId, (content.toInt() == 1)); break; case KeContact::BroadcastTypeEPres: - thing->setStateValue(wallboxEPStateTypeId, content.toInt()); + thing->setStateValue(wallboxSessionEnergyStateTypeId, content.toInt()/10000.00); break; case KeContact::BroadcastTypeState: setDeviceState(thing, KeContact::State(content.toInt())); break; case KeContact::BroadcastTypeMaxCurr: - thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, content.toInt()); + //Current preset value via Control pilot in milliampere break; case KeContact::BroadcastTypeEnableSys: + thing->setStateValue(wallboxSystemEnabledStateTypeId, (content.toInt() != 0)); break; } } @@ -336,26 +505,32 @@ void IntegrationPluginKeba::executeAction(ThingActionInfo *info) return info->finish(Thing::ThingErrorHardwareNotAvailable); } + QUuid requestId; if(action.actionTypeId() == wallboxMaxChargingCurrentActionTypeId){ - int milliAmpere = action.param(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).value().toInt(); - QUuid requestId = keba->setMaxAmpere(milliAmpere); - m_asyncActions.insert(requestId, info); - connect(info, &ThingActionInfo::aborted, this, [requestId, this]{m_asyncActions.remove(requestId);}); + int milliAmpere = action.param(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).value().toDouble()*1000; + requestId = keba->setMaxAmpere(milliAmpere); } else if(action.actionTypeId() == wallboxPowerActionTypeId){ - QUuid requestId = keba->enableOutput(action.param(wallboxPowerActionTypeId).value().toBool()); - m_asyncActions.insert(requestId, info); - connect(info, &ThingActionInfo::aborted, this, [requestId, this]{m_asyncActions.remove(requestId);}); + requestId = keba->enableOutput(action.param(wallboxPowerActionTypeId).value().toBool()); } else if(action.actionTypeId() == wallboxDisplayActionTypeId){ - QUuid requestId = keba->displayMessage(action.param(wallboxDisplayActionMessageParamTypeId).value().toByteArray()); - m_asyncActions.insert(requestId, info); - connect(info, &ThingActionInfo::aborted, this, [requestId, this]{m_asyncActions.remove(requestId);}); + 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(dcKebaKeContact()) << "Unhandled ActionTypeId:" << action.actionTypeId(); - info->finish(Thing::ThingErrorActionTypeNotFound); + return info->finish(Thing::ThingErrorActionTypeNotFound); } + m_asyncActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, this, [requestId, this]{m_asyncActions.remove(requestId);}); } else { qCWarning(dcKebaKeContact()) << "Execute action, unhandled device class" << thing->thingClass(); info->finish(Thing::ThingErrorThingClassNotFound); diff --git a/keba/integrationpluginkeba.h b/keba/integrationpluginkeba.h index ecbc6ec6..438efce5 100644 --- a/keba/integrationpluginkeba.h +++ b/keba/integrationpluginkeba.h @@ -34,6 +34,7 @@ #include "integrations/integrationplugin.h" #include "plugintimer.h" #include "kecontact.h" +#include "kecontactdatalayer.h" #include "discovery.h" #include "host.h" @@ -41,6 +42,7 @@ #include #include #include +#include class IntegrationPluginKeba : public IntegrationPlugin { @@ -52,6 +54,7 @@ class IntegrationPluginKeba : public IntegrationPlugin public: explicit IntegrationPluginKeba(); + void init() override; void discoverThings(ThingDiscoveryInfo *info) override; void setupThing(ThingSetupInfo *info) override; @@ -59,14 +62,18 @@ public: void thingRemoved(Thing* thing) override; void executeAction(ThingActionInfo *info) override; - void updateData(); private: - PluginTimer *m_pluginTimer = nullptr; + PluginTimer *m_updateTimer = nullptr; + PluginTimer *m_reconnectTimer = nullptr; + + KeContactDataLayer *m_kebaData = nullptr; + + Discovery *m_discovery = nullptr; QHash m_kebaDevices; - QHash m_asyncSetup; + QHash m_lastSessionId; + QHash m_asyncActions; - QHash m_chargingSessionStartTime; void setDeviceState(Thing *device, KeContact::State state); void setDevicePlugState(Thing *device, KeContact::PlugState plugState); @@ -74,9 +81,9 @@ private: private slots: void onConnectionChanged(bool status); void onCommandExecuted(QUuid requestId, bool success); - void onReportOneReceived(const KeContact::ReportOne &reportOne); void onReportTwoReceived(const KeContact::ReportTwo &reportTwo); void onReportThreeReceived(const KeContact::ReportThree &reportThree); + void onReport1XXReceived(int reportNumber, const KeContact::Report1XX &report); void onBroadcastReceived(KeContact::BroadcastType type, const QVariant &content); }; diff --git a/keba/integrationpluginkeba.json b/keba/integrationpluginkeba.json index c6567716..c0e193e4 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -13,12 +13,12 @@ "name": "wallbox", "displayName": "Keba KeContact", "createMethods": ["discovery", "user"], - "interfaces": ["extendedevcharger", "smartmeterconsumer", "connectable"], + "interfaces": ["evcharger", "smartmeterconsumer", "connectable"], "paramTypes":[ { "id": "730cd3d3-5f0e-4028-a8c2-ced7574f13f3", "name": "ipAddress", - "displayName": "IPv4 Address", + "displayName": "IP address", "type": "QString", "inputType": "IPv4Address", "defaultValue":"0.0.0.0" @@ -26,10 +26,11 @@ { "id": "c2df921d-ff8b-411c-9b1d-04a437d7dfa6", "name": "macAddress", - "displayName": "MAC Address", + "displayName": "MAC address", "type": "QString", "inputType": "TextLine", - "defaultValue":"" + "defaultValue":"", + "readOnly": true } ], "stateTypes": [ @@ -37,21 +38,70 @@ "id": "ce813458-d7d8-4f40-9648-dba4c41e92f0", "name": "connected", "displayName": "Connected", - "displayNameEvent": "Connection changed", + "displayNameEvent": "Connected changed", "type": "bool", "defaultValue": false, "cached": false }, + { + "id": "b44bc948-1234-4f87-9a22-bfb6de09df4d", + "name": "error1", + "displayName": "Error 1", + "displayNameEvent": "Error 1 changed", + "type": "int", + "defaultValue": 0 + }, + { + "id": "afca201a-5213-43fe-bfec-cae6ce7509d2", + "name": "error2", + "displayName": "Error 2", + "displayNameEvent": "Error 2 changed", + "type": "int", + "defaultValue": 0 + }, + { + "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", "name": "power", - "displayName": "Power", - "displayNameEvent": "Power changed", - "displayNameAction": "Set Power", + "displayName": "Charging enabled", + "displayNameEvent": "Charging enabled changed", + "displayNameAction": "Set charging enabled", "type": "bool", "writable": true, "defaultValue": false }, + { + "id": "e5631593-f486-47cb-9951-b7597d0b769b", + "name": "systemEnabled", + "displayName": "System enabled", + "displayNameEvent": "System enabled changed", + "type": "bool", + "defaultValue": false + }, { "id": "539e5602-6dd9-465d-9705-3bb59bcf8982", "name": "activity", @@ -63,8 +113,8 @@ { "id": "3b4d29f3-3101-47ad-90fd-269b6348783b", "name": "plugState", - "displayName": "Plug State", - "displayNameEvent": "Plug State changed", + "displayName": "Plug state", + "displayNameEvent": "Plug state changed", "type": "QString", "defaultValue": "-" }, @@ -73,9 +123,9 @@ "name": "current", "displayName": "Current", "displayNameEvent": "Current changed", - "type": "int", + "type": "double", "unit": "Ampere", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "593656f0-babf-4308-8767-68f34e10fb15", @@ -83,17 +133,17 @@ "displayName": "Maximal charging current", "displayNameEvent": "Maximal charging current changed", "displayNameAction": "Set maximal charging current", - "type": "uint", - "unit": "MilliAmpere", - "defaultValue": 6000, - "minValue": 6000, - "maxValue": 63000, + "type": "double", + "unit": "Ampere", + "defaultValue": 6.00, + "minValue": 6.00, + "maxValue": 63.00, "writable": true }, { "id": "3c7b83a0-0e42-47bf-9788-dde6aab5ceea", "name": "maxChargingCurrentPercent", - "displayName": "Maximal charging current in Percent", + "displayName": "Maximal charging current in percent", "displayNameEvent": "Maximal charging current percentage changed", "type": "uint", "unit": "Percentage", @@ -101,9 +151,18 @@ "minValue": 0, "maxValue": 100 }, + { + "id": "08bb9872-8d63-49b0-a8ce-7a449341f13b", + "name": "maxPossibleChargingCurrent", + "displayName": "Maximum possible charging current", + "displayNameEvent": "Maximum possible charging current changed", + "type": "double", + "unit": "Ampere", + "defaultValue": 6.00 + }, { "id": "4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9", - "name": "U1", + "name": "voltagePhase1", "displayName": "Voltage phase 1", "displayNameEvent": "Voltage phase 1 changed", "type": "int", @@ -112,8 +171,8 @@ }, { "id": "c8344ca5-21ac-4cd1-8f4b-e5ed202c5862", - "name": "U2", - "displayName": "Voltage Phase 2", + "name": "voltagePhase2", + "displayName": "Voltage phase 2", "displayNameEvent": "Voltage phase 2 changed", "type": "int", "unit": "Volt", @@ -121,8 +180,8 @@ }, { "id": "5f01e86c-0943-4849-a01a-db441916ebd5", - "name": "U3", - "displayName": "Voltage Phase 3", + "name": "voltagePhase3", + "displayName": "Voltage phase 3", "displayNameEvent": "Voltage phase 3 changed", "type": "int", "unit": "Volt", @@ -130,39 +189,56 @@ }, { "id": "31ec17b0-11e3-4332-92b0-fea821cf024f", - "name": "I1", - "displayName": "Current Phase 1", + "name": "currentPhase1", + "displayName": "Current phase 1", "displayNameEvent": "Current phase 1 changed", - "type": "int", - "unit": "MilliAmpere", - "defaultValue": 0 + "type": "double", + "unit": "Ampere", + "defaultValue": 0.00 }, { "id": "cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97", - "name": "I2", - "displayName": "Current Phase 2", + "name": "currentPhase2", + "displayName": "Current phase 2", "displayNameEvent": "Current phase 2 changed", - "type": "int", - "unit": "MilliAmpere", - "defaultValue": 0 + "type": "double", + "unit": "Ampere", + "defaultValue": 0.00 }, { "id": "da838dc8-85f0-4e55-b4b5-cb93a43b373d", - "name": "I3", - "displayName": "Current Phase 3", + "name": "currentPhase3", + "displayName": "Current phase 3", "displayNameEvent": "Current phase 3 changed", - "type": "int", - "unit": "MilliAmpere", - "defaultValue": 0 + "type": "double", + "unit": "Ampere", + "defaultValue": 0.00 }, { "id": "7af9e93b-099d-4d9d-a480-9c0f66aecd8b", - "name": "P", + "name": "powerConsumption", "displayName": "Power consumption", "displayNameEvent": "Power consumtion changed", - "type": "int", - "unit": "MilliWatt", - "defaultValue": 0 + "type": "double", + "unit": "KiloWatt", + "defaultValue": 0.00 + }, + { + "id": "889c3c9a-96b4-4408-bd9a-d79e36ed9296", + "name": "powerFactor", + "displayName": "Power factor", + "displayNameEvent": "Power factor changed", + "type": "double", + "unit": "Percentage", + "defaultValue": 0.00 + }, + { + "id": "1d30ce60-2ea0-450f-817e-5c88f59ebfbf", + "name": "sessionId", + "displayName": "Session ID", + "displayNameEvent": "Session ID changed", + "type": "uint", + "defaultValue": "" }, { "id": "a6f35ea0-aaea-438b-b818-6d161762611e", @@ -175,9 +251,9 @@ }, { "id": "8e277efe-21ef-4536-bfc0-901b32d44d7c", - "name": "EP", - "displayName": "Present energy", - "displayNameEvent": "Present energy changed", + "name": "sessionEnergy", + "displayName": "Session energy", + "displayNameEvent": "Session energy changed", "type": "double", "unit": "KiloWattHour", "defaultValue": 0 @@ -190,6 +266,43 @@ "type": "double", "unit": "KiloWattHour", "defaultValue": 0 + }, + { + "id": "96b2d176-6460-4109-8824-3af4679c6573", + "name": "outputX2", + "displayName": "Output X2", + "displayNameEvent": "Output X2 changed", + "displayNameAction": "Set output X2", + "type": "bool", + "writable": true, + "defaultValue": false + }, + { + "id": "ba600276-8b36-4404-b8ec-415245e5bc15", + "name": "input", + "displayName": "Input", + "displayNameEvent": "Input changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "3421ecf9-c95f-4dc1-ad0c-144e9b6ae056", + "name": "uptime", + "displayName": "Uptime", + "displayNameEvent": "Uptime changed", + "type": "int", + "unit": "Minutes", + "defaultValue": 0 + }, + { + "id": "f1758c5c-2c02-41cb-93ec-b778a3c78d28", + "name": "failsafeMode", + "displayName": "Failsafe mode", + "displayNameEvent": "Failsafe mode changed", + "displayNameAction": "Set failsafe mode", + "writable": true, + "type": "bool", + "defaultValue": false } ], "actionTypes": [ @@ -207,6 +320,38 @@ } ] } + ], + "eventTypes": [ + { + "id": "dac02c37-f051-481a-ae99-1de0885ef37a", + "name": "chargingSessionFinished", + "displayName": "Charging session finished", + "paramTypes": [ + { + "id": "33446eae-f2cc-4cf2-af29-b3a45e4b91c0", + "name": "id", + "displayName": "ID", + "type": "int", + "defaultValue": "0" + }, + { + "id": "60494d6f-853b-42b8-894e-108a52ed6feb", + "name": "duration", + "displayName": "Duration", + "type": "int", + "unit": "Minutes", + "defaultValue": 0 + }, + { + "id": "c8de58b6-b671-4fee-b552-d2c14a37a769", + "name": "energy", + "displayName": "Energy", + "type": "double", + "defaultValue": 0.00, + "unit": "KiloWattHour" + } + ] + } ] } ] diff --git a/keba/keba.pro b/keba/keba.pro index 72cd6d1a..4507354c 100644 --- a/keba/keba.pro +++ b/keba/keba.pro @@ -9,9 +9,11 @@ SOURCES += \ kecontact.cpp \ discovery.cpp \ host.cpp \ + kecontactdatalayer.cpp HEADERS += \ integrationpluginkeba.h \ kecontact.h \ discovery.h \ host.h \ + kecontactdatalayer.h diff --git a/keba/kecontact.cpp b/keba/kecontact.cpp index 3477ec72..ec02fac0 100644 --- a/keba/kecontact.cpp +++ b/keba/kecontact.cpp @@ -34,62 +34,87 @@ #include -KeContact::KeContact(QHostAddress address, QObject *parent) : +KeContact::KeContact(const QHostAddress &address, KeContactDataLayer *dataLayer, QObject *parent) : QObject(parent), + m_dataLayer(dataLayer), m_address(address) { + qCDebug(dcKebaKeContact()) << "Creating KeContact connection for address" << m_address; m_requestTimeoutTimer = new QTimer(this); m_requestTimeoutTimer->setSingleShot(true); connect(m_requestTimeoutTimer, &QTimer::timeout, this, [this] { //This timer will be started when a request is sent and stopped or resetted when a response has been received - emit connectionChanged(false); + emit reachableChanged(false); //Try to send the next command handleNextCommandInQueue(); m_deviceBlocked = false; }); + + connect(m_dataLayer, &KeContactDataLayer::datagramReceived, this, &KeContact::onReceivedDatagram); } -KeContact::~KeContact() { +KeContact::~KeContact() +{ qCDebug(dcKebaKeContact()) << "Deleting KeContact connection for address" << m_address; } -bool KeContact::init(){ - - if(!m_udpSocket){ - m_udpSocket = new QUdpSocket(this); - if (!m_udpSocket->bind(QHostAddress::AnyIPv4, 7090, QAbstractSocket::ShareAddress)) { - qCWarning(dcKebaKeContact()) << "Cannot bind to port" << 7090; - delete m_udpSocket; - return false; - } - connect(m_udpSocket, &QUdpSocket::readyRead, this, &KeContact::readPendingDatagrams); - } - return true; -} - QHostAddress KeContact::address() { return m_address; } -void KeContact::setAddress(QHostAddress address) +QUuid KeContact::start(const QByteArray &rfidToken, const QByteArray &rfidClassifier) { + QUuid requestId = QUuid::createUuid(); + m_pendingRequests.append(requestId); + QByteArray datagram = "start "+rfidToken + " " + rfidClassifier; + qCDebug(dcKebaKeContact()) << "Datagram : " << datagram; + sendCommand(datagram, requestId);; + return requestId; +} + +QUuid KeContact::stop(const QByteArray &rfidToken) +{ + QUuid requestId = QUuid::createUuid(); + m_pendingRequests.append(requestId); + QByteArray datagram = "stop "+rfidToken; + qCDebug(dcKebaKeContact()) << "Datagram : " << datagram; + sendCommand(datagram, requestId); + return requestId; +} + +void KeContact::setAddress(const QHostAddress &address) +{ + qCDebug(dcKebaKeContact()) << "Updating Keba connection address" << address.toString(); m_address = address; } + +void KeContact::sendCommand(const QByteArray &command, const QUuid &requestId) +{ + QTimer::singleShot(5000, this, [requestId, this] { + if (m_pendingRequests.contains(requestId)) { + m_pendingRequests.removeOne(requestId); + emit commandExecuted(requestId, false); + } + }); + sendCommand(command); +} + void KeContact::sendCommand(const QByteArray &command) { - if (!m_udpSocket) { + if (!m_dataLayer) { qCWarning(dcKebaKeContact()) << "UDP socket not initialized"; - emit connectionChanged(false); + emit reachableChanged(false); return; } + if(m_deviceBlocked) { //add command to queue m_commandList.append(command); } else { - //send command - m_udpSocket->writeDatagram(command, m_address, 7090); + qCDebug(dcKebaKeContact()) << "Writing datagram" << command << m_address; + m_dataLayer->write( m_address, command); m_requestTimeoutTimer->start(5000); m_deviceBlocked = true; } @@ -97,18 +122,20 @@ void KeContact::sendCommand(const QByteArray &command) void KeContact::handleNextCommandInQueue() { - if (!m_udpSocket) { - qCWarning(dcKebaKeContact()) << "UDP socket not initialized"; - emit connectionChanged(false); + if (!m_dataLayer) { + qCWarning(dcKebaKeContact()) << "Data layer not initialized"; + if (m_reachable == true) { + m_reachable = false; + emit reachableChanged(false); + } return; } qCDebug(dcKebaKeContact()) << "Handle Command Queue- Pending commands" << m_commandList.length() << "Pending requestIds" << m_pendingRequests.length(); if (!m_commandList.isEmpty()) { QByteArray command = m_commandList.takeFirst(); - m_udpSocket->writeDatagram(command, m_address, 7090); + qCDebug(dcKebaKeContact()) << "Writing datagram" << command << m_address; + m_dataLayer->write( m_address, command); m_requestTimeoutTimer->start(5000); - } else { - //nothing to do } } @@ -124,31 +151,25 @@ QUuid KeContact::enableOutput(bool state) } else{ datagram.append("ena 0"); } - qCDebug(dcKebaKeContact()) << "Datagram : " << datagram; - sendCommand(datagram); - QTimer::singleShot(5000, this, [requestId, this] { - if (m_pendingRequests.contains(requestId)) { - m_pendingRequests.removeOne(requestId); - emit commandExecuted(requestId, false); - } - }); + qCDebug(dcKebaKeContact()) << "Enable output, command:" << datagram; + sendCommand(datagram, requestId); return requestId; } QUuid KeContact::setMaxAmpere(int milliAmpere) { + if (milliAmpere < 6000 || milliAmpere > 63000) { + qCWarning(dcKebaKeContact()) << "KeContact: Set max ampere, mA out of range [6000, 63000]" << milliAmpere; + return ""; + } QUuid requestId = QUuid::createUuid(); m_pendingRequests.append(requestId); // Print information that we are executing now the update action qCDebug(dcKebaKeContact()) << "Update max current to : " << milliAmpere; QByteArray data; data.append("curr " + QVariant(milliAmpere).toByteArray()); - qCDebug(dcKebaKeContact()) << "send command: " << data; - sendCommand(data); - if (m_pendingRequests.contains(requestId)) { - m_pendingRequests.removeOne(requestId); - emit commandExecuted(requestId, false); - } + qCDebug(dcKebaKeContact()) << "Set max. ampere, command: " << data; + sendCommand(data, requestId); return requestId; } @@ -169,12 +190,35 @@ QUuid KeContact::displayMessage(const QByteArray &message) modifiedMessage.resize(23); } data.append("display 0 0 0 0 " + modifiedMessage); - qCDebug(dcKebaKeContact()) << "send command: " << data; - sendCommand(data); - if (m_pendingRequests.contains(requestId)) { - m_pendingRequests.removeOne(requestId); - emit commandExecuted(requestId, false); - } + qCDebug(dcKebaKeContact()) << "Display message, command: " << data; + sendCommand(data, requestId); + return requestId; +} + +QUuid KeContact::chargeWithEnergyLimit(double energy) +{ + QUuid requestId = QUuid::createUuid(); + m_pendingRequests.append(requestId); + + QByteArray data; + data.append("setenergy " + QVariant(static_cast(energy*10000)).toByteArray()); + qCDebug(dcKebaKeContact()) << "Charge with energy limit, command: " << data; + sendCommand(data, requestId); + return requestId; +} + +QUuid KeContact::setFailsafe(int timeout, int current, bool save) +{ + QUuid requestId = QUuid::createUuid(); + m_pendingRequests.append(requestId); + + QByteArray data; + data.append("failsave"); + data.append(" "+QVariant(timeout).toByteArray()); + data.append(" "+QVariant(current).toByteArray()); + data.append((save ? " 1":" 0")); + qCDebug(dcKebaKeContact()) << "Set failsafe mode, command: " << data; + sendCommand(data, requestId); return requestId; } @@ -183,31 +227,46 @@ void KeContact::getDeviceInformation() { QByteArray data; data.append("i"); - qCDebug(dcKebaKeContact()) << "send command: " << data; + qCDebug(dcKebaKeContact()) << "Get device information, command: " << data; sendCommand(data); } void KeContact::getReport1() { - QByteArray data; - data.append("report 1"); - qCDebug(dcKebaKeContact()) << "send command : " << data; - sendCommand(data); + getReport(1); } void KeContact::getReport2() { - QByteArray data; - data.append("report 2"); - qCDebug(dcKebaKeContact()) << "send command: " << data; - sendCommand(data); + getReport(2); } void KeContact::getReport3() { + getReport(3); +} + +void KeContact::getReport1XX(int reportNumber) +{ + getReport(reportNumber); +} + +QUuid KeContact::setOutputX2(bool state) +{ + QUuid requestId = QUuid::createUuid(); + m_pendingRequests.append(requestId); QByteArray data; - data.append("report 3"); - qCDebug(dcKebaKeContact()) << "data: " << data; + data.append("output "+QVariant((state ? 1 : 0)).toByteArray()); + qCDebug(dcKebaKeContact()) << "Set Output X2, state:" << state << "Command:" << data; + sendCommand(data, requestId); + return requestId; +} + +void KeContact::getReport(int reportNumber) +{ + QByteArray data; + data.append("report "+QVariant(reportNumber).toByteArray()); + qCDebug(dcKebaKeContact()) << "Get report" << reportNumber << "Command:" << data; sendCommand(data); } @@ -217,143 +276,165 @@ QUuid KeContact::unlockCharger() m_pendingRequests.append(requestId); QByteArray data; data.append("unlock"); - qCDebug(dcKebaKeContact()) << "send command: " << data; + qCDebug(dcKebaKeContact()) << "Unlock charger, command: " << data; sendCommand(data); return requestId; } -void KeContact::readPendingDatagrams() +void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray &datagram) { - QUdpSocket *socket= qobject_cast(sender()); + if (address != m_address) { + return; + } - QByteArray datagram; - QHostAddress sender; - quint16 senderPort; + if(datagram.contains("TCH-OK")){ - while (socket->hasPendingDatagrams()) { - datagram.resize(socket->pendingDatagramSize()); - socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); - if (sender != m_address) { - //Only process data from the target device - continue; - } - emit connectionChanged(true); + //Command response has been received, now send the next command + m_deviceBlocked = false; + m_requestTimeoutTimer->stop(); + handleNextCommandInQueue(); - qCDebug(dcKebaKeContact()) << "Data received" << datagram; - if(datagram.contains("TCH-OK")){ - - //Command response has been received, now send the next command - m_deviceBlocked = false; - m_requestTimeoutTimer->stop(); - handleNextCommandInQueue(); - - if (!m_pendingRequests.isEmpty()) { - QUuid requestId = m_pendingRequests.takeFirst(); - if (datagram.contains("done")) { - emit commandExecuted(requestId, true); - } else { - emit commandExecuted(requestId, false); - } + if (!m_pendingRequests.isEmpty()) { + QUuid requestId = m_pendingRequests.takeFirst(); + if (datagram.contains("done")) { + emit commandExecuted(requestId, true); } else { - //Probably the response has taken too long and the requestId has been already removed - } - } else if(datagram.left(8).contains("Firmware")){ - - //Command response has been received, now send the next command - m_deviceBlocked = false; - m_requestTimeoutTimer->stop(); - handleNextCommandInQueue(); - - qCDebug(dcKebaKeContact()) << "Firmware information reveiced"; - QByteArrayList firmware = datagram.split(':'); - if (firmware.length() >= 2) { - emit deviceInformationReceived(firmware[1]); + emit commandExecuted(requestId, false); } } else { + //Probably the response has taken too long and the requestId has been already removed + } + } else if(datagram.left(8).contains("Firmware")){ - //Command response has been received, now send the next command - m_deviceBlocked = false; - m_requestTimeoutTimer->stop(); - handleNextCommandInQueue(); + //Command response has been received, now send the next command + m_deviceBlocked = false; + m_requestTimeoutTimer->stop(); + handleNextCommandInQueue(); - // Convert the rawdata to a json document - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(datagram, &error); - if (error.error != QJsonParseError::NoError) { - qCWarning(dcKebaKeContact()) << "Failed to parse JSON data" << datagram << ":" << error.errorString(); + qCDebug(dcKebaKeContact()) << "Firmware information received"; + QByteArrayList firmware = datagram.split(':'); + if (firmware.length() >= 2) { + emit deviceInformationReceived(firmware[1]); + } + } else { + + //Command response has been received, now send the next command + m_deviceBlocked = false; + m_requestTimeoutTimer->stop(); + handleNextCommandInQueue(); + + // Convert the rawdata to a json document + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(datagram, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcKebaKeContact()) << "Failed to parse JSON data" << datagram << ":" << error.errorString(); + } + + QVariantMap data = jsonDoc.toVariant().toMap(); + + if(data.contains("ID")) { + int id = data.value("ID").toInt(); + if (id == 1) { + ReportOne reportOne; + qCDebug(dcKebaKeContact()) << "Report 1 received"; + reportOne.product = data.value("Product").toString(); + reportOne.firmware = data.value("Firmware").toString(); + reportOne.serialNumber = data.value("Serial").toString(); + //"Backend:" + //"timeQ": 3 + //"DIP-Sw1": "0x22" + //"DIP-Sw2": + if (data.contains("COM-module")) { + reportOne.comModule = (data.value("COM-module").toInt() == 1); + } else { + reportOne.comModule = false; + } + if (data.contains("Sec")) { + reportOne.comModule = data.value("Sec").toInt(); + } else { + reportOne.comModule = 0; + } + emit reportOneReceived(reportOne); + + } else if (id == 2) { + + ReportTwo reportTwo; + qCDebug(dcKebaKeContact()) << "Report 2 received"; + int state = data.value("State").toInt(); + reportTwo.state = State(state); + reportTwo.error1 = data.value("Error1").toInt(); + reportTwo.error2 = data.value("Error2").toInt(); + reportTwo.plugState = PlugState(data.value("Plug").toInt()); + reportTwo.enableUser = data.value("Enable user").toBool(); + reportTwo.enableSys = data.value("Enable sys").toBool(); + reportTwo.maxCurrent = data.value("Max curr").toInt()/1000.00; + reportTwo.maxCurrentPercentage = data.value("Max curr %").toInt()/10.00; + reportTwo.currentHardwareLimitation = data.value("Curr HW").toInt()/1000.00; + reportTwo.currentUser = data.value("Curr user").toInt()/1000.00; + reportTwo.currentFailsafe = data.value("Curr FS").toInt()/1000.00; + reportTwo.timeoutFailsafe = data.value("Tmo FS").toInt(); + reportTwo.setEnergy = data.value("Setenergy").toInt()/10000.00; + reportTwo.output = data.value("Output").toInt(); + reportTwo.input= data.value("Input").toInt(); + reportTwo.serialNumber = data.value("Serial").toString(); + reportTwo.seconds = data.value("Sec").toInt(); + // Not documented: + //"AuthON": 0 + //"Authreq": 0 + emit reportTwoReceived(reportTwo); + + } else if (id == 3) { + + ReportThree reportThree; + qCDebug(dcKebaKeContact()) << "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.serialNumber = data.value("Serial").toString(); + reportThree.seconds = data.value("Sec").toInt(); + emit reportThreeReceived(reportThree); + } else if (id >= 100) { + + Report1XX report; + qCDebug(dcKebaKeContact()) << "Report" << id << "received"; + report.sessionId = data.value("Session ID").toInt(); + report.currHW = data.value("Curr HW").toInt(); + report.startTime = data.value("E Start ").toInt()/10000.00; + report.presentEnergy = data.value("E Pres ").toInt()/10000.00; + report.startTime = data.value("started[s]").toInt(); + report.endTime = data.value("ended[s] ").toInt(); + report.stopReason = data.value("reason ").toInt(); + report.rfidTag = data.value("RFID tag").toByteArray(); + report.rfidClass = data.value("RFID class").toByteArray(); + report.serialNumber = data.value("Serial").toString(); + report.seconds = data.value("Sec").toInt(); + emit report1XXReceived(id, report); } - - QVariantMap data = jsonDoc.toVariant().toMap(); - - if(data.contains("ID")) { - - if (data.value("ID").toString() == "1") { - ReportOne reportOne; - qCDebug(dcKebaKeContact()) << "Report 1 received"; - reportOne.product = data.value("Product").toString(); - reportOne.firmware = data.value("Firmware").toString(); - reportOne.serialNumber = data.value("Serial").toString();; - emit reportOneReceived(reportOne); - - } else if(data.value("ID").toString() == "2"){ - - ReportTwo reportTwo; - qCDebug(dcKebaKeContact()) << "Report 2 reveiced"; - int state = data.value("State").toInt(); - reportTwo.state = State(state); - reportTwo.error1 = data.value("Error1").toInt(); - reportTwo.error2 = data.value("Error2").toInt(); - reportTwo.plugState = PlugState(data.value("Plug").toInt()); - reportTwo.enableUser = data.value("Enable user").toBool(); - reportTwo.enableSys = data.value("Enable sys").toBool(); - reportTwo.MaxCurrent = data.value("Max curr").toInt()/1000; - reportTwo.MaxCurrentPercentage = data.value("Max curr %").toInt()/10; - reportTwo.CurrentHardwareLimitation = data.value("Curr HW").toInt()/1000; - reportTwo.CurrentUser = data.value("Curr user").toInt(); - reportTwo.CurrFS = data.value("Curr FS").toInt(); - reportTwo.TmoFS = data.value("Tmo FS").toInt(); - reportTwo.output = data.value("Output").toInt(); - reportTwo.input= data.value("Input").toInt(); - reportTwo.serialNumber = data.value("Serial").toString(); - reportTwo.seconds = data.value("Sec").toInt(); - emit reportTwoReceived(reportTwo); - - } else if(data.value("ID").toString() == "3"){ - - ReportThree reportThree; - qCDebug(dcKebaKeContact()) << "Report 3 reveiced"; - reportThree.CurrentPhase1 = data.value("I1").toInt(); - reportThree.CurrentPhase2 = data.value("I2").toInt(); - reportThree.CurrentPhase3 = data.value("I3").toInt(); - reportThree.VoltagePhase1 = data.value("U1").toInt(); - reportThree.VoltagePhase2 = data.value("U2").toInt(); - reportThree.VoltagePhase3 = data.value("U3").toInt(); - reportThree.Power = data.value("P").toInt(); - reportThree.PowerFactor = data.value("PF").toInt()/10; - reportThree.EnergySession = data.value("E pres").toInt()/10000.00; - reportThree.EnergyTotal = data.value("E total").toInt()/10000.00; - reportThree.SerialNumber = data.value("Serial").toString(); - emit reportThreeReceived(reportThree); - } - } else { - if (data.contains("State")) { - emit broadcastReceived(BroadcastType::BroadcastTypeState, data.value("State")); - } - if (data.contains("Plug")) { - emit broadcastReceived(BroadcastType::BroadcastTypePlug, data.value("Plug")); - } - if (data.contains("Input")) { - emit broadcastReceived(BroadcastType::BroadcastTypeInput, data.value("Input")); - } - if (data.contains("Enable sys")) { - emit broadcastReceived(BroadcastType::BroadcastTypeEnableSys, data.value("Enable sys")); - } - if (data.contains("Max curr")) { - emit broadcastReceived(BroadcastType::BroadcastTypeMaxCurr, data.value("Max curr")); - } - if (data.contains("E pres")) { - emit broadcastReceived(BroadcastType::BroadcastTypeEPres, data.value("E pres")); - } + } else { + if (data.contains("State")) { + emit broadcastReceived(BroadcastType::BroadcastTypeState, data.value("State")); + } + if (data.contains("Plug")) { + emit broadcastReceived(BroadcastType::BroadcastTypePlug, data.value("Plug")); + } + if (data.contains("Input")) { + emit broadcastReceived(BroadcastType::BroadcastTypeInput, data.value("Input")); + } + if (data.contains("Enable sys")) { + emit broadcastReceived(BroadcastType::BroadcastTypeEnableSys, data.value("Enable sys")); + } + if (data.contains("Max curr")) { + emit broadcastReceived(BroadcastType::BroadcastTypeMaxCurr, data.value("Max curr")); + } + if (data.contains("E pres")) { + emit broadcastReceived(BroadcastType::BroadcastTypeEPres, data.value("E pres")); } } } diff --git a/keba/kecontact.h b/keba/kecontact.h index 20bd3ce6..af04d23f 100644 --- a/keba/kecontact.h +++ b/keba/kecontact.h @@ -38,11 +38,13 @@ #include #include +#include "kecontactdatalayer.h" + class KeContact : public QObject { Q_OBJECT public: - explicit KeContact(QHostAddress address, QObject *parent = nullptr); + explicit KeContact(const QHostAddress &address, KeContactDataLayer *dataLayer, QObject *parent = nullptr); ~KeContact(); bool init(); @@ -54,6 +56,7 @@ public: StateError, StateAuthorizationRejected }; + Q_ENUM(State) enum PlugState { PlugStateUnplugged = 0, @@ -62,6 +65,7 @@ public: PlugStatePluggedOnChargingStationAndPluggedOnEV = 5, PlugStatePluggedOnChargingStationAndPlugLockedAndPluggedOnEV = 7 }; + Q_ENUM(PlugState) enum BroadcastType { BroadcastTypeState = 0, @@ -71,11 +75,14 @@ public: BroadcastTypeMaxCurr, BroadcastTypeEPres }; + Q_ENUM(BroadcastType) struct ReportOne { - QString product; - QString serialNumber; - QString firmware; + QString product; // Model name (variant + QString serialNumber; // Serial number + QString firmware; // Firmware version + bool comModule; // Communication module is installed (only P30) + int seconds; // Current system clock since restart of the charging station.(only P30) }; struct ReportTwo { @@ -85,49 +92,78 @@ public: PlugState plugState; //Current condition of the loading connection bool enableSys; //Enable state for charging (contains Enable input, RFID, UDP,..). bool enableUser; //Enable condition via UDP. - int MaxCurrent; //Current preset value via Control pilot in milliampere. - int MaxCurrentPercentage; //Current preset value via Control pilot in 0,1% of the PWM value - int CurrentHardwareLimitation; //Highest possible charging current of the charging connection. Contains device maximum, DIP-switch setting, cable coding and temperature reduction. - int CurrentUser; //Current preset value of the user via UDP; Default = 63000mA. - int CurrFS; //Current preset value for the Failsafe function. - int TmoFS; //Communication timeout before triggering the Failsafe function. + double maxCurrent; //Current preset value via Control pilot in ampere. + double maxCurrentPercentage; //Current preset value via Control pilot in 0,1% of the PWM value + double currentHardwareLimitation; //Highest possible charging current of the charging connection. Contains device maximum, DIP-switch setting, cable coding and temperature reduction. + double currentUser; //Current preset value of the user via UDP; Default = 63000mA. + double currentFailsafe; //Current preset value for the Failsafe function. + int timeoutFailsafe; //Communication timeout before triggering the Failsafe function. + int currTimer; //Shows the current preset value of currtime. + int timeoutCt; //Shows the remaining time until the current value is accepted. + double setEnergy; //Shows the set energy limit bool output; //State of the output X2. bool input; //State of the potential free Enable input X1. When using the input, please pay attention to the information in the installation manual. - QString serialNumber; // + QString serialNumber; //Serial number int seconds; //Current system clock since restart of the charging station. }; struct ReportThree { - int VoltagePhase1; //voltage in V - int VoltagePhase2; //voltage in V - int VoltagePhase3; //voltage in V - int CurrentPhase1; //current in mA - int CurrentPhase2; //current in mA - int CurrentPhase3; //current in mA - int Power; //Current power in mW (Real Power). - int PowerFactor; //Power factor in 0,1% (cosphi) - int EnergySession; //Power consumption of the current loading session in 0,1Wh; Reset with new loading session (state = 2). - int EnergyTotal; //Total power consumption (persistent) without current loading session 0,1Wh; Is summed up after each completed charging session (state = 0). - QString SerialNumber; + int voltagePhase1; //voltage in V + int voltagePhase2; //voltage in V + int voltagePhase3; //voltage in V + double currentPhase1; //current in A + double currentPhase2; //current in A + double currentPhase3; //current in A + double power; //Current power in W (Real Power). + double powerFactor; //Power factor (cosphi) + double energySession; //Power consumption of the current loading session in kWh; Reset with new loading session (state = 2). + double energyTotal; //Total power consumption (persistent) without current loading session kWh; Is summed up after each completed charging session (state = 0). + QString serialNumber; + int seconds; //Current system clock since restart of the charging station. + }; + + struct Report1XX { + int sessionId; // running session counter; not resettable" + double currHW; // maximum charging current of the cable and the charging station setting + double startEnergy; // total energy value at the beginning of the session" + double presentEnergy; // delivered energy until now (equal to E pres in report 3)" + int startTime; // system time when the session was started (seconds from reboot; + int endTime; // system time when the session has ended" + int stopReason; // reason for stopping the session (1 = vehicle unplug; 10 = Rfid token)" + QByteArray rfidTag; // RFID Token ID if session started with rfid + QByteArray rfidClass; // RFID classifier shows the defined color code + QString serialNumber; // serial number of the charging station" + int seconds; // current time when the report was generated }; QHostAddress address(); - int serialNumber(); + void setAddress(const QHostAddress &address); - void setAddress(QHostAddress address); + bool reachable(); - QUuid enableOutput(bool state); - QUuid setMaxAmpere(int milliAmpere); - QUuid unlockCharger(); - QUuid displayMessage(const QByteArray &message); + QUuid start(const QByteArray &rfidToken, const QByteArray &rfidClassifier); // Command “start” + QUuid stop(const QByteArray &rfidToken); // Command “stop” - void getDeviceInformation(); - void getReport1(); + QUuid enableOutput(bool state); // Command “ena” + QUuid setMaxAmpere(int milliAmpere); // Command “curr” + QUuid unlockCharger(); // Command “unlock" + QUuid displayMessage(const QByteArray &message); // Command “display” + QUuid chargeWithEnergyLimit(double energy); // Command “setenergy” + QUuid setFailsafe(int timeout, int current, bool save); // Command “failsafe” + + void getDeviceInformation(); // Command “i” + void getReport1(); // Command “report” void getReport2(); void getReport3(); + void getReport1XX(int reportNumber = 100); // Command “report 1xx” + + // Command “currtime” + QUuid setOutputX2(bool state); // Command “output” private: - QUdpSocket *m_udpSocket = nullptr; + KeContactDataLayer *m_dataLayer; + bool m_reachable = false; + QHostAddress m_address; QByteArrayList m_commandList; bool m_deviceBlocked = false; @@ -136,22 +172,23 @@ private: int m_serialNumber; QList m_pendingRequests; - void sendCommand(const QByteArray &data); + void getReport(int reportNumber); + void sendCommand(const QByteArray &command, const QUuid &requestId); + void sendCommand(const QByteArray &command); void handleNextCommandInQueue(); signals: - void connectionChanged(bool status); + void reachableChanged(bool status); void commandExecuted(QUuid requestId, bool success); void deviceInformationReceived(const QString &firmware); void reportOneReceived(const ReportOne &reportOne); void reportTwoReceived(const ReportTwo &reportTwo); void reportThreeReceived(const ReportThree &reportThree); + void report1XXReceived(int reportNumber, const Report1XX &report); void broadcastReceived(BroadcastType type, const QVariant &content); private slots: - void readPendingDatagrams(); + void onReceivedDatagram(const QHostAddress &address, const QByteArray &datagram); }; - - #endif // KECONTACT_H diff --git a/keba/kecontactdatalayer.cpp b/keba/kecontactdatalayer.cpp new file mode 100644 index 00000000..2633631c --- /dev/null +++ b/keba/kecontactdatalayer.cpp @@ -0,0 +1,57 @@ +#include "kecontactdatalayer.h" +#include "extern-plugininfo.h" + +KeContactDataLayer::KeContactDataLayer(QObject *parent) : QObject(parent) +{ + qCDebug(dcKebaKeContact()) << "KeContactDataLayer: Creating UDP socket"; + m_udpSocket = new QUdpSocket(this); + connect(m_udpSocket, &QUdpSocket::readyRead, this, &KeContactDataLayer::readPendingDatagrams); + connect(m_udpSocket, &QUdpSocket::stateChanged, this, &KeContactDataLayer::onSocketStateChanged); + connect(m_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError))); +} + +KeContactDataLayer::~KeContactDataLayer() +{ + qCDebug(dcKebaKeContact()) << "KeContactDataLayer: Deleting UDP socket"; +} + +bool KeContactDataLayer::init() +{ + if (!m_udpSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress)) { + qCWarning(dcKebaKeContact()) << "KeContactDataLayer: Cannot bind to port" << m_port; + return false; + } + return true; +} + +void KeContactDataLayer::write(const QHostAddress &address, const QByteArray &data) +{ + m_udpSocket->writeDatagram(data, address, m_port); +} + +void KeContactDataLayer::readPendingDatagrams() +{ + QUdpSocket *socket= qobject_cast(sender()); + + QByteArray datagram; + QHostAddress senderAddress; + quint16 senderPort; + + while (socket->hasPendingDatagrams()) { + + datagram.resize(socket->pendingDatagramSize()); + socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort); + qCDebug(dcKebaKeContact()) << "KeContactDataLayer: Data received" << datagram << senderAddress; + emit datagramReceived(senderAddress, datagram); + } +} + +void KeContactDataLayer::onSocketError(QAbstractSocket::SocketError error) +{ + qCDebug(dcKebaKeContact()) << "KeContactDataLayer: Socket error" << error; +} + +void KeContactDataLayer::onSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + qCDebug(dcKebaKeContact()) << "KeContactDataLayer: Socket state changed" << socketState; +} diff --git a/keba/kecontactdatalayer.h b/keba/kecontactdatalayer.h new file mode 100644 index 00000000..ae343ac8 --- /dev/null +++ b/keba/kecontactdatalayer.h @@ -0,0 +1,30 @@ +#ifndef KECONTACTDATALAYER_H +#define KECONTACTDATALAYER_H + +#include +#include + +class KeContactDataLayer : public QObject +{ + Q_OBJECT +public: + explicit KeContactDataLayer(QObject *parent = nullptr); + ~KeContactDataLayer(); + bool init(); + + void write(const QHostAddress &address, const QByteArray &data); + +private: + int m_port = 7090; + QUdpSocket *m_udpSocket = nullptr; + +signals: + void datagramReceived(const QHostAddress &address, const QByteArray &data); + +private slots: + void readPendingDatagrams(); + void onSocketError(QAbstractSocket::SocketError error); + void onSocketStateChanged(QAbstractSocket::SocketState socketState); +}; + +#endif // KECONTACTDATALAYER_H diff --git a/keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-de.ts b/keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-de.ts new file mode 100644 index 00000000..e2b97b14 --- /dev/null +++ b/keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-de.ts @@ -0,0 +1,550 @@ + + + + + IntegrationPluginKeba + + + + Error opening network port. + Fehler beim Öffnen des Netzwerkports. + + + + KebaKeContact + + + + Activity + The name of the ParamType (ThingClass: wallbox, EventType: activity, ID: {539e5602-6dd9-465d-9705-3bb59bcf8982}) +---------- +The name of the StateType ({539e5602-6dd9-465d-9705-3bb59bcf8982}) of ThingClass wallbox + Aktivität + + + + Activity changed + The name of the EventType ({539e5602-6dd9-465d-9705-3bb59bcf8982}) of ThingClass wallbox + Aktivität geändert + + + + + + Charging enabled + The name of the ParamType (ThingClass: wallbox, ActionType: power, ID: {83ed0774-2a91-434d-b03c-d920d02f2981}) +---------- +The name of the ParamType (ThingClass: wallbox, EventType: power, ID: {83ed0774-2a91-434d-b03c-d920d02f2981}) +---------- +The name of the StateType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass wallbox + Laden ermöglicht + + + + Charging enabled changed + The name of the EventType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass wallbox + Laden ermäglicht geändert + + + + Charging session finished + The name of the EventType ({dac02c37-f051-481a-ae99-1de0885ef37a}) of ThingClass wallbox + Ladesession beendet + + + + + Connected + The name of the ParamType (ThingClass: wallbox, EventType: connected, ID: {ce813458-d7d8-4f40-9648-dba4c41e92f0}) +---------- +The name of the StateType ({ce813458-d7d8-4f40-9648-dba4c41e92f0}) of ThingClass wallbox + Verbunden + + + + Connected changed + The name of the EventType ({ce813458-d7d8-4f40-9648-dba4c41e92f0}) of ThingClass wallbox + Verbunden geändert + + + + + Current + The name of the ParamType (ThingClass: wallbox, EventType: current, ID: {a29c1748-fe97-4830-a56e-e1cc4e618385}) +---------- +The name of the StateType ({a29c1748-fe97-4830-a56e-e1cc4e618385}) of ThingClass wallbox + Stromstärke + + + + Current changed + The name of the EventType ({a29c1748-fe97-4830-a56e-e1cc4e618385}) of ThingClass wallbox + Stromstärke geändert + + + + + Current phase 1 + The name of the ParamType (ThingClass: wallbox, EventType: currentPhase1, ID: {31ec17b0-11e3-4332-92b0-fea821cf024f}) +---------- +The name of the StateType ({31ec17b0-11e3-4332-92b0-fea821cf024f}) of ThingClass wallbox + Stromstärke Phase 1 + + + + Current phase 1 changed + The name of the EventType ({31ec17b0-11e3-4332-92b0-fea821cf024f}) of ThingClass wallbox + Stromstärke Phase 1 geändert + + + + + Current phase 2 + The name of the ParamType (ThingClass: wallbox, EventType: currentPhase2, ID: {cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) +---------- +The name of the StateType ({cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) of ThingClass wallbox + Stromstärke Phase 2 + + + + Current phase 2 changed + The name of the EventType ({cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) of ThingClass wallbox + Stromstärke Phase 2 geändert + + + + + Current phase 3 + The name of the ParamType (ThingClass: wallbox, EventType: currentPhase3, ID: {da838dc8-85f0-4e55-b4b5-cb93a43b373d}) +---------- +The name of the StateType ({da838dc8-85f0-4e55-b4b5-cb93a43b373d}) of ThingClass wallbox + Stromstärke Phase 3 + + + + Current phase 3 changed + The name of the EventType ({da838dc8-85f0-4e55-b4b5-cb93a43b373d}) of ThingClass wallbox + Stromstärke Phase 3 geändert + + + + Display + The name of the ActionType ({158b1a8f-fde9-4191-bf42-4ece5fe582e6}) of ThingClass wallbox + Anzeige + + + + Display message + The name of the ParamType (ThingClass: wallbox, ActionType: display, ID: {4e69a761-f4f1-42d0-83db-380894a86ebc}) + Nachricht + + + + Duration + The name of the ParamType (ThingClass: wallbox, EventType: chargingSessionFinished, ID: {60494d6f-853b-42b8-894e-108a52ed6feb}) + Dauer + + + + Energy + The name of the ParamType (ThingClass: wallbox, EventType: chargingSessionFinished, ID: {c8de58b6-b671-4fee-b552-d2c14a37a769}) + Energie + + + + + Error 1 + The name of the ParamType (ThingClass: wallbox, EventType: error1, ID: {b44bc948-1234-4f87-9a22-bfb6de09df4d}) +---------- +The name of the StateType ({b44bc948-1234-4f87-9a22-bfb6de09df4d}) of ThingClass wallbox + Error 1 + + + + Error 1 changed + The name of the EventType ({b44bc948-1234-4f87-9a22-bfb6de09df4d}) of ThingClass wallbox + Error 1 geändert + + + + + Error 2 + The name of the ParamType (ThingClass: wallbox, EventType: error2, ID: {afca201a-5213-43fe-bfec-cae6ce7509d2}) +---------- +The name of the StateType ({afca201a-5213-43fe-bfec-cae6ce7509d2}) of ThingClass wallbox + Error 2 + + + + Error 2 changed + The name of the EventType ({afca201a-5213-43fe-bfec-cae6ce7509d2}) of ThingClass wallbox + Error 2 geändert + + + + + + Failsafe mode + The name of the ParamType (ThingClass: wallbox, ActionType: failsafeMode, ID: {f1758c5c-2c02-41cb-93ec-b778a3c78d28}) +---------- +The name of the ParamType (ThingClass: wallbox, EventType: failsafeMode, ID: {f1758c5c-2c02-41cb-93ec-b778a3c78d28}) +---------- +The name of the StateType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass wallbox + Failsafe Module + + + + Failsafe mode changed + The name of the EventType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass wallbox + Failsafe Modus geändert + + + + + Firmware + The name of the ParamType (ThingClass: wallbox, EventType: firmware, ID: {e941ace5-fb7f-4dc2-b3f2-188233f4e934}) +---------- +The name of the StateType ({e941ace5-fb7f-4dc2-b3f2-188233f4e934}) of ThingClass wallbox + Firmware + + + + Firmware changed + The name of the EventType ({e941ace5-fb7f-4dc2-b3f2-188233f4e934}) of ThingClass wallbox + Firmware geändert + + + + ID + The name of the ParamType (ThingClass: wallbox, EventType: chargingSessionFinished, ID: {33446eae-f2cc-4cf2-af29-b3a45e4b91c0}) + ID + + + + IP address + The name of the ParamType (ThingClass: wallbox, Type: thing, ID: {730cd3d3-5f0e-4028-a8c2-ced7574f13f3}) + IP Adresse + + + + + Input + The name of the ParamType (ThingClass: wallbox, EventType: input, ID: {ba600276-8b36-4404-b8ec-415245e5bc15}) +---------- +The name of the StateType ({ba600276-8b36-4404-b8ec-415245e5bc15}) of ThingClass wallbox + Eingang + + + + Input changed + The name of the EventType ({ba600276-8b36-4404-b8ec-415245e5bc15}) of ThingClass wallbox + Eingang geändert + + + + Keba + The name of the vendor ({f7cda40b-829a-4675-abaa-485697430f5f}) + Keba + + + + + Keba KeContact + The name of the ThingClass ({900dacec-cae7-4a37-95ba-501846368ea2}) +---------- +The name of the plugin KebaKeContact ({9142b09f-30a9-43d0-9ede-2f8debe075ac}) + Keba KeContact + + + + MAC address + The name of the ParamType (ThingClass: wallbox, Type: thing, ID: {c2df921d-ff8b-411c-9b1d-04a437d7dfa6}) + MAC Adresse + + + + + + Maximal charging current + The name of the ParamType (ThingClass: wallbox, ActionType: maxChargingCurrent, ID: {593656f0-babf-4308-8767-68f34e10fb15}) +---------- +The name of the ParamType (ThingClass: wallbox, EventType: maxChargingCurrent, ID: {593656f0-babf-4308-8767-68f34e10fb15}) +---------- +The name of the StateType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass wallbox + Maximaler Ladestrom + + + + Maximal charging current changed + The name of the EventType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass wallbox + Maximaler Ladestrom geändert + + + + + Maximal charging current in percent + The name of the ParamType (ThingClass: wallbox, EventType: maxChargingCurrentPercent, ID: {3c7b83a0-0e42-47bf-9788-dde6aab5ceea}) +---------- +The name of the StateType ({3c7b83a0-0e42-47bf-9788-dde6aab5ceea}) of ThingClass wallbox + Maximaler Ladestrom in Prozent + + + + Maximal charging current percentage changed + The name of the EventType ({3c7b83a0-0e42-47bf-9788-dde6aab5ceea}) of ThingClass wallbox + Maximaler Ladestrom in Prozent geändert + + + + + Model + The name of the ParamType (ThingClass: wallbox, EventType: model, ID: {c3fca233-95b9-4948-88c6-4c0f13cf53b1}) +---------- +The name of the StateType ({c3fca233-95b9-4948-88c6-4c0f13cf53b1}) of ThingClass wallbox + Modell + + + + Model changed + The name of the EventType ({c3fca233-95b9-4948-88c6-4c0f13cf53b1}) of ThingClass wallbox + Modell geändert + + + + + + Output X2 + The name of the ParamType (ThingClass: wallbox, ActionType: outputX2, ID: {96b2d176-6460-4109-8824-3af4679c6573}) +---------- +The name of the ParamType (ThingClass: wallbox, EventType: outputX2, ID: {96b2d176-6460-4109-8824-3af4679c6573}) +---------- +The name of the StateType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass wallbox + Ausgang X2 + + + + Output X2 changed + The name of the EventType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass wallbox + Ausgang X2 geändert + + + + + Plug state + The name of the ParamType (ThingClass: wallbox, EventType: plugState, ID: {3b4d29f3-3101-47ad-90fd-269b6348783b}) +---------- +The name of the StateType ({3b4d29f3-3101-47ad-90fd-269b6348783b}) of ThingClass wallbox + Stecker-Status + + + + Plug state changed + The name of the EventType ({3b4d29f3-3101-47ad-90fd-269b6348783b}) of ThingClass wallbox + Stecker-Status geändert + + + + + Power consumption + The name of the ParamType (ThingClass: wallbox, EventType: powerConsumption, ID: {7af9e93b-099d-4d9d-a480-9c0f66aecd8b}) +---------- +The name of the StateType ({7af9e93b-099d-4d9d-a480-9c0f66aecd8b}) of ThingClass wallbox + Leistungsaufnahme + + + + Power consumtion changed + The name of the EventType ({7af9e93b-099d-4d9d-a480-9c0f66aecd8b}) of ThingClass wallbox + Leistungsaufnahme geändert + + + + + Power factor + The name of the ParamType (ThingClass: wallbox, EventType: powerFactor, ID: {889c3c9a-96b4-4408-bd9a-d79e36ed9296}) +---------- +The name of the StateType ({889c3c9a-96b4-4408-bd9a-d79e36ed9296}) of ThingClass wallbox + Leistungsfaktor + + + + Power factor changed + The name of the EventType ({889c3c9a-96b4-4408-bd9a-d79e36ed9296}) of ThingClass wallbox + Leistungsfaktor + + + + + Serial number + The name of the ParamType (ThingClass: wallbox, EventType: serialnumber, ID: {9a1b4316-ce01-4cd3-890f-a8c94b8b5029}) +---------- +The name of the StateType ({9a1b4316-ce01-4cd3-890f-a8c94b8b5029}) of ThingClass wallbox + Seriennummer + + + + Serial number changed + The name of the EventType ({9a1b4316-ce01-4cd3-890f-a8c94b8b5029}) of ThingClass wallbox + Seriennnummer geändert + + + + + Session ID + The name of the ParamType (ThingClass: wallbox, EventType: sessionId, ID: {1d30ce60-2ea0-450f-817e-5c88f59ebfbf}) +---------- +The name of the StateType ({1d30ce60-2ea0-450f-817e-5c88f59ebfbf}) of ThingClass wallbox + Session ID + + + + Session ID changed + The name of the EventType ({1d30ce60-2ea0-450f-817e-5c88f59ebfbf}) of ThingClass wallbox + Session ID geändert + + + + + Session energy + The name of the ParamType (ThingClass: wallbox, EventType: sessionEnergy, ID: {8e277efe-21ef-4536-bfc0-901b32d44d7c}) +---------- +The name of the StateType ({8e277efe-21ef-4536-bfc0-901b32d44d7c}) of ThingClass wallbox + Session Energie + + + + Session energy changed + The name of the EventType ({8e277efe-21ef-4536-bfc0-901b32d44d7c}) of ThingClass wallbox + Session Energie geändert + + + + + Session time + The name of the ParamType (ThingClass: wallbox, EventType: sessionTime, ID: {a6f35ea0-aaea-438b-b818-6d161762611e}) +---------- +The name of the StateType ({a6f35ea0-aaea-438b-b818-6d161762611e}) of ThingClass wallbox + Sessiondauer + + + + Session time changed + The name of the EventType ({a6f35ea0-aaea-438b-b818-6d161762611e}) of ThingClass wallbox + Sessiondauer geändert + + + + Set charging enabled + The name of the ActionType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass wallbox + Setze Laden ermöglicht + + + + Set failsafe mode + The name of the ActionType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass wallbox + Setze Failsafe Modus + + + + Set maximal charging current + The name of the ActionType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass wallbox + Setze maximaler Ladestrom + + + + Set output X2 + The name of the ActionType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass wallbox + Setze Ausgang X2 + + + + + System enabled + The name of the ParamType (ThingClass: wallbox, EventType: systemEnabled, ID: {e5631593-f486-47cb-9951-b7597d0b769b}) +---------- +The name of the StateType ({e5631593-f486-47cb-9951-b7597d0b769b}) of ThingClass wallbox + System ermöglicht + + + + System enabled changed + The name of the EventType ({e5631593-f486-47cb-9951-b7597d0b769b}) of ThingClass wallbox + System ermöglicht geändert + + + + + Total energy consumed + The name of the ParamType (ThingClass: wallbox, EventType: totalEnergyConsumed, ID: {41e179b3-29a2-43ec-b537-023a527081e8}) +---------- +The name of the StateType ({41e179b3-29a2-43ec-b537-023a527081e8}) of ThingClass wallbox + Gesamter Energieverbrauch + + + + Total energy consumption changed + The name of the EventType ({41e179b3-29a2-43ec-b537-023a527081e8}) of ThingClass wallbox + Gesamter Energieverbrauch geändert + + + + + Uptime + The name of the ParamType (ThingClass: wallbox, EventType: uptime, ID: {3421ecf9-c95f-4dc1-ad0c-144e9b6ae056}) +---------- +The name of the StateType ({3421ecf9-c95f-4dc1-ad0c-144e9b6ae056}) of ThingClass wallbox + Betriebszeit + + + + Uptime changed + The name of the EventType ({3421ecf9-c95f-4dc1-ad0c-144e9b6ae056}) of ThingClass wallbox + Betriebszeit geändert + + + + + Voltage phase 1 + The name of the ParamType (ThingClass: wallbox, EventType: voltagePhase1, ID: {4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9}) +---------- +The name of the StateType ({4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9}) of ThingClass wallbox + Spannung Phase 1 + + + + Voltage phase 1 changed + The name of the EventType ({4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9}) of ThingClass wallbox + Spannung Phase 1 geändert + + + + + Voltage phase 2 + The name of the ParamType (ThingClass: wallbox, EventType: voltagePhase2, ID: {c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) +---------- +The name of the StateType ({c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) of ThingClass wallbox + Spannung Phase 2 + + + + Voltage phase 2 changed + The name of the EventType ({c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) of ThingClass wallbox + Spannung Phase 2 geändert + + + + + Voltage phase 3 + The name of the ParamType (ThingClass: wallbox, EventType: voltagePhase3, ID: {5f01e86c-0943-4849-a01a-db441916ebd5}) +---------- +The name of the StateType ({5f01e86c-0943-4849-a01a-db441916ebd5}) of ThingClass wallbox + Spannung Phase 3 + + + + Voltage phase 3 changed + The name of the EventType ({5f01e86c-0943-4849-a01a-db441916ebd5}) of ThingClass wallbox + Spannung Phase 3 geändert + + + diff --git a/keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-en_US.ts b/keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-en_US.ts index 5735065f..bf0f2385 100644 --- a/keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-en_US.ts +++ b/keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-en_US.ts @@ -4,7 +4,8 @@ IntegrationPluginKeba - + + Error opening network port. @@ -12,29 +13,8 @@ KebaKeContact - - - Keba KeContact - The name of the ThingClass ({900dacec-cae7-4a37-95ba-501846368ea2}) ----------- -The name of the plugin KebaKeContact ({9142b09f-30a9-43d0-9ede-2f8debe075ac}) - - - - - Keba - The name of the vendor ({f7cda40b-829a-4675-abaa-485697430f5f}) - - - - - Activity changed - The name of the EventType ({539e5602-6dd9-465d-9705-3bb59bcf8982}) of ThingClass wallbox - - - - - + + Activity The name of the ParamType (ThingClass: wallbox, EventType: activity, ID: {539e5602-6dd9-465d-9705-3bb59bcf8982}) ---------- @@ -42,8 +22,38 @@ The name of the StateType ({539e5602-6dd9-465d-9705-3bb59bcf8982}) of ThingClass - - + + Activity changed + The name of the EventType ({539e5602-6dd9-465d-9705-3bb59bcf8982}) of ThingClass wallbox + + + + + + + Charging enabled + The name of the ParamType (ThingClass: wallbox, ActionType: power, ID: {83ed0774-2a91-434d-b03c-d920d02f2981}) +---------- +The name of the ParamType (ThingClass: wallbox, EventType: power, ID: {83ed0774-2a91-434d-b03c-d920d02f2981}) +---------- +The name of the StateType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass wallbox + + + + + Charging enabled changed + The name of the EventType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass wallbox + + + + + Charging session finished + The name of the EventType ({dac02c37-f051-481a-ae99-1de0885ef37a}) of ThingClass wallbox + + + + + Connected The name of the ParamType (ThingClass: wallbox, EventType: connected, ID: {ce813458-d7d8-4f40-9648-dba4c41e92f0}) ---------- @@ -51,84 +61,210 @@ The name of the StateType ({ce813458-d7d8-4f40-9648-dba4c41e92f0}) of ThingClass - - Connection changed + + Connected changed The name of the EventType ({ce813458-d7d8-4f40-9648-dba4c41e92f0}) of ThingClass wallbox - - - Current Phase 1 - The name of the ParamType (ThingClass: wallbox, EventType: I1, ID: {31ec17b0-11e3-4332-92b0-fea821cf024f}) + + + Current + The name of the ParamType (ThingClass: wallbox, EventType: current, ID: {a29c1748-fe97-4830-a56e-e1cc4e618385}) +---------- +The name of the StateType ({a29c1748-fe97-4830-a56e-e1cc4e618385}) of ThingClass wallbox + + + + + Current changed + The name of the EventType ({a29c1748-fe97-4830-a56e-e1cc4e618385}) of ThingClass wallbox + + + + + + Current phase 1 + The name of the ParamType (ThingClass: wallbox, EventType: currentPhase1, ID: {31ec17b0-11e3-4332-92b0-fea821cf024f}) ---------- The name of the StateType ({31ec17b0-11e3-4332-92b0-fea821cf024f}) of ThingClass wallbox - - - Current Phase 2 - The name of the ParamType (ThingClass: wallbox, EventType: I2, ID: {cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) ----------- -The name of the StateType ({cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) of ThingClass wallbox - - - - - - Current Phase 3 - The name of the ParamType (ThingClass: wallbox, EventType: I3, ID: {da838dc8-85f0-4e55-b4b5-cb93a43b373d}) ----------- -The name of the StateType ({da838dc8-85f0-4e55-b4b5-cb93a43b373d}) of ThingClass wallbox - - - - + Current phase 1 changed The name of the EventType ({31ec17b0-11e3-4332-92b0-fea821cf024f}) of ThingClass wallbox - + + + Current phase 2 + The name of the ParamType (ThingClass: wallbox, EventType: currentPhase2, ID: {cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) +---------- +The name of the StateType ({cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) of ThingClass wallbox + + + + Current phase 2 changed The name of the EventType ({cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) of ThingClass wallbox - + + + Current phase 3 + The name of the ParamType (ThingClass: wallbox, EventType: currentPhase3, ID: {da838dc8-85f0-4e55-b4b5-cb93a43b373d}) +---------- +The name of the StateType ({da838dc8-85f0-4e55-b4b5-cb93a43b373d}) of ThingClass wallbox + + + + Current phase 3 changed The name of the EventType ({da838dc8-85f0-4e55-b4b5-cb93a43b373d}) of ThingClass wallbox - + Display The name of the ActionType ({158b1a8f-fde9-4191-bf42-4ece5fe582e6}) of ThingClass wallbox - + Display message The name of the ParamType (ThingClass: wallbox, ActionType: display, ID: {4e69a761-f4f1-42d0-83db-380894a86ebc}) - - IPv4 Address + + Duration + The name of the ParamType (ThingClass: wallbox, EventType: chargingSessionFinished, ID: {60494d6f-853b-42b8-894e-108a52ed6feb}) + + + + + Energy + The name of the ParamType (ThingClass: wallbox, EventType: chargingSessionFinished, ID: {c8de58b6-b671-4fee-b552-d2c14a37a769}) + + + + + + Error 1 + The name of the ParamType (ThingClass: wallbox, EventType: error1, ID: {b44bc948-1234-4f87-9a22-bfb6de09df4d}) +---------- +The name of the StateType ({b44bc948-1234-4f87-9a22-bfb6de09df4d}) of ThingClass wallbox + + + + + Error 1 changed + The name of the EventType ({b44bc948-1234-4f87-9a22-bfb6de09df4d}) of ThingClass wallbox + + + + + + Error 2 + The name of the ParamType (ThingClass: wallbox, EventType: error2, ID: {afca201a-5213-43fe-bfec-cae6ce7509d2}) +---------- +The name of the StateType ({afca201a-5213-43fe-bfec-cae6ce7509d2}) of ThingClass wallbox + + + + + Error 2 changed + The name of the EventType ({afca201a-5213-43fe-bfec-cae6ce7509d2}) of ThingClass wallbox + + + + + + + Failsafe mode + The name of the ParamType (ThingClass: wallbox, ActionType: failsafeMode, ID: {f1758c5c-2c02-41cb-93ec-b778a3c78d28}) +---------- +The name of the ParamType (ThingClass: wallbox, EventType: failsafeMode, ID: {f1758c5c-2c02-41cb-93ec-b778a3c78d28}) +---------- +The name of the StateType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass wallbox + + + + + Failsafe mode changed + The name of the EventType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass wallbox + + + + + + Firmware + The name of the ParamType (ThingClass: wallbox, EventType: firmware, ID: {e941ace5-fb7f-4dc2-b3f2-188233f4e934}) +---------- +The name of the StateType ({e941ace5-fb7f-4dc2-b3f2-188233f4e934}) of ThingClass wallbox + + + + + Firmware changed + The name of the EventType ({e941ace5-fb7f-4dc2-b3f2-188233f4e934}) of ThingClass wallbox + + + + + ID + The name of the ParamType (ThingClass: wallbox, EventType: chargingSessionFinished, ID: {33446eae-f2cc-4cf2-af29-b3a45e4b91c0}) + + + + + IP address The name of the ParamType (ThingClass: wallbox, Type: thing, ID: {730cd3d3-5f0e-4028-a8c2-ced7574f13f3}) - - MAC Address + + + Input + The name of the ParamType (ThingClass: wallbox, EventType: input, ID: {ba600276-8b36-4404-b8ec-415245e5bc15}) +---------- +The name of the StateType ({ba600276-8b36-4404-b8ec-415245e5bc15}) of ThingClass wallbox + + + + + Input changed + The name of the EventType ({ba600276-8b36-4404-b8ec-415245e5bc15}) of ThingClass wallbox + + + + + Keba + The name of the vendor ({f7cda40b-829a-4675-abaa-485697430f5f}) + + + + + + Keba KeContact + The name of the ThingClass ({900dacec-cae7-4a37-95ba-501846368ea2}) +---------- +The name of the plugin KebaKeContact ({9142b09f-30a9-43d0-9ede-2f8debe075ac}) + + + + + MAC address The name of the ParamType (ThingClass: wallbox, Type: thing, ID: {c2df921d-ff8b-411c-9b1d-04a437d7dfa6}) - - - + + + Maximal charging current The name of the ParamType (ThingClass: wallbox, ActionType: maxChargingCurrent, ID: {593656f0-babf-4308-8767-68f34e10fb15}) ---------- @@ -138,68 +274,152 @@ The name of the StateType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass - + Maximal charging current changed The name of the EventType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass wallbox - - - Maximal charging current in Percent + + + Maximal charging current in percent The name of the ParamType (ThingClass: wallbox, EventType: maxChargingCurrentPercent, ID: {3c7b83a0-0e42-47bf-9788-dde6aab5ceea}) ---------- The name of the StateType ({3c7b83a0-0e42-47bf-9788-dde6aab5ceea}) of ThingClass wallbox - + Maximal charging current percentage changed The name of the EventType ({3c7b83a0-0e42-47bf-9788-dde6aab5ceea}) of ThingClass wallbox - - - Plug State + + + Model + The name of the ParamType (ThingClass: wallbox, EventType: model, ID: {c3fca233-95b9-4948-88c6-4c0f13cf53b1}) +---------- +The name of the StateType ({c3fca233-95b9-4948-88c6-4c0f13cf53b1}) of ThingClass wallbox + + + + + Model changed + The name of the EventType ({c3fca233-95b9-4948-88c6-4c0f13cf53b1}) of ThingClass wallbox + + + + + + + Output X2 + The name of the ParamType (ThingClass: wallbox, ActionType: outputX2, ID: {96b2d176-6460-4109-8824-3af4679c6573}) +---------- +The name of the ParamType (ThingClass: wallbox, EventType: outputX2, ID: {96b2d176-6460-4109-8824-3af4679c6573}) +---------- +The name of the StateType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass wallbox + + + + + Output X2 changed + The name of the EventType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass wallbox + + + + + + Plug state The name of the ParamType (ThingClass: wallbox, EventType: plugState, ID: {3b4d29f3-3101-47ad-90fd-269b6348783b}) ---------- The name of the StateType ({3b4d29f3-3101-47ad-90fd-269b6348783b}) of ThingClass wallbox - - + + Plug state changed + The name of the EventType ({3b4d29f3-3101-47ad-90fd-269b6348783b}) of ThingClass wallbox + + + + + Power consumption - The name of the ParamType (ThingClass: wallbox, EventType: P, ID: {7af9e93b-099d-4d9d-a480-9c0f66aecd8b}) + The name of the ParamType (ThingClass: wallbox, EventType: powerConsumption, ID: {7af9e93b-099d-4d9d-a480-9c0f66aecd8b}) ---------- The name of the StateType ({7af9e93b-099d-4d9d-a480-9c0f66aecd8b}) of ThingClass wallbox - + Power consumtion changed The name of the EventType ({7af9e93b-099d-4d9d-a480-9c0f66aecd8b}) of ThingClass wallbox - - - Present energy - The name of the ParamType (ThingClass: wallbox, EventType: EP, ID: {8e277efe-21ef-4536-bfc0-901b32d44d7c}) + + + Power factor + The name of the ParamType (ThingClass: wallbox, EventType: powerFactor, ID: {889c3c9a-96b4-4408-bd9a-d79e36ed9296}) +---------- +The name of the StateType ({889c3c9a-96b4-4408-bd9a-d79e36ed9296}) of ThingClass wallbox + + + + + Power factor changed + The name of the EventType ({889c3c9a-96b4-4408-bd9a-d79e36ed9296}) of ThingClass wallbox + + + + + + Serial number + The name of the ParamType (ThingClass: wallbox, EventType: serialnumber, ID: {9a1b4316-ce01-4cd3-890f-a8c94b8b5029}) +---------- +The name of the StateType ({9a1b4316-ce01-4cd3-890f-a8c94b8b5029}) of ThingClass wallbox + + + + + Serial number changed + The name of the EventType ({9a1b4316-ce01-4cd3-890f-a8c94b8b5029}) of ThingClass wallbox + + + + + + Session ID + The name of the ParamType (ThingClass: wallbox, EventType: sessionId, ID: {1d30ce60-2ea0-450f-817e-5c88f59ebfbf}) +---------- +The name of the StateType ({1d30ce60-2ea0-450f-817e-5c88f59ebfbf}) of ThingClass wallbox + + + + + Session ID changed + The name of the EventType ({1d30ce60-2ea0-450f-817e-5c88f59ebfbf}) of ThingClass wallbox + + + + + + Session energy + The name of the ParamType (ThingClass: wallbox, EventType: sessionEnergy, ID: {8e277efe-21ef-4536-bfc0-901b32d44d7c}) ---------- The name of the StateType ({8e277efe-21ef-4536-bfc0-901b32d44d7c}) of ThingClass wallbox - - Present energy changed + + Session energy changed The name of the EventType ({8e277efe-21ef-4536-bfc0-901b32d44d7c}) of ThingClass wallbox - - + + Session time The name of the ParamType (ThingClass: wallbox, EventType: sessionTime, ID: {a6f35ea0-aaea-438b-b818-6d161762611e}) ---------- @@ -207,26 +427,53 @@ The name of the StateType ({a6f35ea0-aaea-438b-b818-6d161762611e}) of ThingClass - + Session time changed The name of the EventType ({a6f35ea0-aaea-438b-b818-6d161762611e}) of ThingClass wallbox - - Set Power + + Set charging enabled The name of the ActionType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass wallbox - + + Set failsafe mode + The name of the ActionType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass wallbox + + + + Set maximal charging current The name of the ActionType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass wallbox - - + + Set output X2 + The name of the ActionType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass wallbox + + + + + + System enabled + The name of the ParamType (ThingClass: wallbox, EventType: systemEnabled, ID: {e5631593-f486-47cb-9951-b7597d0b769b}) +---------- +The name of the StateType ({e5631593-f486-47cb-9951-b7597d0b769b}) of ThingClass wallbox + + + + + System enabled changed + The name of the EventType ({e5631593-f486-47cb-9951-b7597d0b769b}) of ThingClass wallbox + + + + + Total energy consumed The name of the ParamType (ThingClass: wallbox, EventType: totalEnergyConsumed, ID: {41e179b3-29a2-43ec-b537-023a527081e8}) ---------- @@ -234,94 +481,70 @@ The name of the StateType ({41e179b3-29a2-43ec-b537-023a527081e8}) of ThingClass - + Total energy consumption changed The name of the EventType ({41e179b3-29a2-43ec-b537-023a527081e8}) of ThingClass wallbox - - - Voltage Phase 2 - The name of the ParamType (ThingClass: wallbox, EventType: U2, ID: {c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) + + + Uptime + The name of the ParamType (ThingClass: wallbox, EventType: uptime, ID: {3421ecf9-c95f-4dc1-ad0c-144e9b6ae056}) ---------- -The name of the StateType ({c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) of ThingClass wallbox +The name of the StateType ({3421ecf9-c95f-4dc1-ad0c-144e9b6ae056}) of ThingClass wallbox - - - Voltage Phase 3 - The name of the ParamType (ThingClass: wallbox, EventType: U3, ID: {5f01e86c-0943-4849-a01a-db441916ebd5}) ----------- -The name of the StateType ({5f01e86c-0943-4849-a01a-db441916ebd5}) of ThingClass wallbox + + Uptime changed + The name of the EventType ({3421ecf9-c95f-4dc1-ad0c-144e9b6ae056}) of ThingClass wallbox - - + + Voltage phase 1 - The name of the ParamType (ThingClass: wallbox, EventType: U1, ID: {4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9}) + The name of the ParamType (ThingClass: wallbox, EventType: voltagePhase1, ID: {4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9}) ---------- The name of the StateType ({4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9}) of ThingClass wallbox - + Voltage phase 1 changed The name of the EventType ({4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9}) of ThingClass wallbox - + + + Voltage phase 2 + The name of the ParamType (ThingClass: wallbox, EventType: voltagePhase2, ID: {c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) +---------- +The name of the StateType ({c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) of ThingClass wallbox + + + + Voltage phase 2 changed The name of the EventType ({c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) of ThingClass wallbox - + + + Voltage phase 3 + The name of the ParamType (ThingClass: wallbox, EventType: voltagePhase3, ID: {5f01e86c-0943-4849-a01a-db441916ebd5}) +---------- +The name of the StateType ({5f01e86c-0943-4849-a01a-db441916ebd5}) of ThingClass wallbox + + + + Voltage phase 3 changed The name of the EventType ({5f01e86c-0943-4849-a01a-db441916ebd5}) of ThingClass wallbox - - - - Current - The name of the ParamType (ThingClass: wallbox, EventType: current, ID: {a29c1748-fe97-4830-a56e-e1cc4e618385}) ----------- -The name of the StateType ({a29c1748-fe97-4830-a56e-e1cc4e618385}) of ThingClass wallbox - - - - - - - Power - The name of the ParamType (ThingClass: wallbox, ActionType: power, ID: {83ed0774-2a91-434d-b03c-d920d02f2981}) ----------- -The name of the ParamType (ThingClass: wallbox, EventType: power, ID: {83ed0774-2a91-434d-b03c-d920d02f2981}) ----------- -The name of the StateType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass wallbox - - - - - Plug State changed - The name of the EventType ({3b4d29f3-3101-47ad-90fd-269b6348783b}) of ThingClass wallbox - - - - - Current changed - The name of the EventType ({a29c1748-fe97-4830-a56e-e1cc4e618385}) of ThingClass wallbox - - - - - Power changed - The name of the EventType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass wallbox - -