From 0803221e682fd6aefed4cf01d97dd07322c1ee09 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Wed, 13 Jan 2021 12:19:16 +0100 Subject: [PATCH 1/9] added auto rediscovery --- keba/README.md | 14 ++- keba/integrationpluginkeba.cpp | 205 ++++++++++++++++++++++---------- keba/integrationpluginkeba.h | 7 +- keba/integrationpluginkeba.json | 127 +++++++++++++++++--- keba/kecontact.cpp | 113 +++++++++++++----- keba/kecontact.h | 70 +++++++---- 6 files changed, 393 insertions(+), 143 deletions(-) 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/integrationpluginkeba.cpp b/keba/integrationpluginkeba.cpp index 381fccd1..36d13ce6 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -42,20 +42,45 @@ 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() == 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; ThingDescriptor descriptor(wallboxThingClassId, "Wallbox", host.address() + " (" + host.macAddress() + ")"); + // Rediscovery foreach (Thing *existingThing, myThings()) { if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString() == host.macAddress()) { descriptor.setThingId(existingThing->id()); @@ -86,7 +111,7 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) QHostAddress address = QHostAddress(thing->paramValue(wallboxThingIpAddressParamTypeId).toString()); KeContact *keba = new KeContact(address, this); - connect(keba, &KeContact::connectionChanged, this, &IntegrationPluginKeba::onConnectionChanged); + connect(keba, &KeContact::reachableChanged, this, &IntegrationPluginKeba::onConnectionChanged); connect(keba, &KeContact::commandExecuted, this, &IntegrationPluginKeba::onCommandExecuted); connect(keba, &KeContact::reportOneReceived, this, &IntegrationPluginKeba::onReportOneReceived); connect(keba, &KeContact::reportTwoReceived, this, &IntegrationPluginKeba::onReportTwoReceived); @@ -98,13 +123,26 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) 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); + m_kebaDevices.insert(thing->id(), keba); keba->getReport1(); - connect(info, &ThingSetupInfo::aborted, this, [id, keba, this]{ - m_asyncSetup.remove(keba); - m_kebaDevices.remove(id); + connect(keba, &KeContact::reportOneReceived, info, [info] (const KeContact::ReportOne &report) { + Thing *thing = info->thing(); + + qCDebug(dcKebaKeContact()) << "Report one received for" << thing->name(); + qCDebug(dcKebaKeContact()) << " - Firmware" << report.firmware; + qCDebug(dcKebaKeContact()) << " - Product" << report.product; + qCDebug(dcKebaKeContact()) << " - Uptime" << report.seconds; + qCDebug(dcKebaKeContact()) << " - Com Module" << report.comModule; + + thing->setStateValue(wallboxConnectedStateTypeId, true); + thing->setStateValue(wallboxFirmwareStateTypeId, report.firmware); + thing->setStateValue(wallboxModelStateTypeId, report.product); + thing->setStateValue(wallboxUptimeStateTypeId, report.seconds); + info->finish(Thing::ThingErrorNoError); + }); + connect(info, &ThingSetupInfo::aborted, keba, &KeContact::deleteLater); + connect(keba, &KeContact::destroyed, this, [thing, keba, this]{ + m_kebaDevices.remove(thing->id()); keba->deleteLater(); }); } else { @@ -118,50 +156,66 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing) qCDebug(dcKebaKeContact()) << "Post setup" << thing->name(); KeContact *keba = m_kebaDevices.value(thing->id()); if (!keba) { - return; + qCWarning(dcKebaKeContact()) << "No Keba connection found for this thing"; + } else { + keba->getReport2(); + keba->getReport3(); } - keba->getReport2(); - keba->getReport3(); - 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 (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); + } + } + }); + } + + 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; - } -} - -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; } } @@ -230,15 +284,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 +309,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,8 +316,36 @@ void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &repo if (!thing) return; + thing->setStateValue() thing->setStateValue(wallboxPowerStateTypeId, reportTwo.enableUser); - thing->setStateValue(wallboxMaxChargingCurrentPercentStateTypeId, reportTwo.MaxCurrentPercentage); + thing->setStateValue(wallboxError1StateTypeId, reportTwo.error1); + thing->setStateValue(wallboxError2StateTypeId, reportTwo.error2); + + qCDebug(dcKebaKeContact()) << "Report 2 recieved for" << thing->name(); + 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()) << " - Serialnumber:" << reportTwo.serialNumber; + qCDebug(dcKebaKeContact()) << " - Uptime:" << reportTwo.seconds; + + + //thing->setStateValue(wallboxMaxChargingCurrentAmpereStateTypeId, reportTwo.maxCurrent); + thing->setStateValue(wallboxMaxChargingCurrentPercentStateTypeId, reportTwo.maxCurrentPercentage); + //thing->setStateValue(wallboxCurrentHardwareLimitationStateTypeId, reportTwo.currentHardwareLimitation); + setDeviceState(thing, reportTwo.state); setDevicePlugState(thing, reportTwo.plugState); @@ -286,14 +358,15 @@ 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(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); //TODO check state name + thing->setStateValue(wallboxPowerFactorStateTypeId, reportThree.PowerFactor); thing->setStateValue(wallboxTotalEnergyConsumedStateTypeId, reportThree.EnergyTotal); } @@ -311,7 +384,7 @@ void IntegrationPluginKeba::onBroadcastReceived(KeContact::BroadcastType type, c case KeContact::BroadcastTypeInput: break; case KeContact::BroadcastTypeEPres: - thing->setStateValue(wallboxEPStateTypeId, content.toInt()); + thing->setStateValue(wallboxSessionEnergyStateTypeId, content.toInt()); break; case KeContact::BroadcastTypeState: setDeviceState(thing, KeContact::State(content.toInt())); diff --git a/keba/integrationpluginkeba.h b/keba/integrationpluginkeba.h index ecbc6ec6..b5377cb6 100644 --- a/keba/integrationpluginkeba.h +++ b/keba/integrationpluginkeba.h @@ -52,6 +52,7 @@ class IntegrationPluginKeba : public IntegrationPlugin public: explicit IntegrationPluginKeba(); + void init() override; void discoverThings(ThingDiscoveryInfo *info) override; void setupThing(ThingSetupInfo *info) override; @@ -59,10 +60,12 @@ 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; + + Discovery *m_discovery = nullptr; QHash m_kebaDevices; QHash m_asyncSetup; QHash m_asyncActions; diff --git a/keba/integrationpluginkeba.json b/keba/integrationpluginkeba.json index c6567716..b1e6d0fe 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -29,7 +29,8 @@ "displayName": "MAC Address", "type": "QString", "inputType": "TextLine", - "defaultValue":"" + "defaultValue":"", + "readOnly": true } ], "stateTypes": [ @@ -42,6 +43,60 @@ "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", + "possibleValues": [ + "Unknown", + "Keba P20", + "Keba P30", + "BMW" + ] + }, + { + "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": "1d30ce60-2ea0-450f-817e-5c88f59ebfbf", + "name": "sessionId", + "displayName": "Session ID", + "displayNameEvent": "Session ID changed", + "type": "uint", + "defaultValue": "" + }, { "id": "83ed0774-2a91-434d-b03c-d920d02f2981", "name": "power", @@ -103,7 +158,7 @@ }, { "id": "4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9", - "name": "U1", + "name": "voltagePhase1", "displayName": "Voltage phase 1", "displayNameEvent": "Voltage phase 1 changed", "type": "int", @@ -112,7 +167,7 @@ }, { "id": "c8344ca5-21ac-4cd1-8f4b-e5ed202c5862", - "name": "U2", + "name": "voltagePhase2", "displayName": "Voltage Phase 2", "displayNameEvent": "Voltage phase 2 changed", "type": "int", @@ -121,7 +176,7 @@ }, { "id": "5f01e86c-0943-4849-a01a-db441916ebd5", - "name": "U3", + "name": "voltagePhase3", "displayName": "Voltage Phase 3", "displayNameEvent": "Voltage phase 3 changed", "type": "int", @@ -130,40 +185,49 @@ }, { "id": "31ec17b0-11e3-4332-92b0-fea821cf024f", - "name": "I1", + "name": "currentPhase1", "displayName": "Current Phase 1", "displayNameEvent": "Current phase 1 changed", "type": "int", - "unit": "MilliAmpere", + "unit": "Ampere", "defaultValue": 0 }, { "id": "cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97", - "name": "I2", + "name": "currentPhase2", "displayName": "Current Phase 2", "displayNameEvent": "Current phase 2 changed", - "type": "int", - "unit": "MilliAmpere", + "type": "double", + "unit": "Ampere", "defaultValue": 0 }, { "id": "da838dc8-85f0-4e55-b4b5-cb93a43b373d", - "name": "I3", + "name": "currentPhase3", "displayName": "Current Phase 3", "displayNameEvent": "Current phase 3 changed", - "type": "int", - "unit": "MilliAmpere", + "type": "double", + "unit": "Ampere", "defaultValue": 0 }, { "id": "7af9e93b-099d-4d9d-a480-9c0f66aecd8b", - "name": "P", + "name": "powerConsumption", "displayName": "Power consumption", "displayNameEvent": "Power consumtion changed", - "type": "int", - "unit": "MilliWatt", + "type": "double", + "unit": "Watt", "defaultValue": 0 }, + { + "id": "889c3c9a-96b4-4408-bd9a-d79e36ed9296", + "name": "powerFactor", + "displayName": "Power factor", + "displayNameEvent": "Power factor changed", + "type": "double", + "unit": "Percentage", + "defaultValue": 0.00 + }, { "id": "a6f35ea0-aaea-438b-b818-6d161762611e", "name": "sessionTime", @@ -175,9 +239,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 +254,33 @@ "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": "Seconds", + "defaultValue": 0 } ], "actionTypes": [ diff --git a/keba/kecontact.cpp b/keba/kecontact.cpp index 3477ec72..71c22194 100644 --- a/keba/kecontact.cpp +++ b/keba/kecontact.cpp @@ -42,7 +42,7 @@ KeContact::KeContact(QHostAddress address, QObject *parent) : 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; @@ -72,18 +72,52 @@ QHostAddress KeContact::address() return m_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(QHostAddress address) { 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) { qCWarning(dcKebaKeContact()) << "UDP socket not initialized"; - emit connectionChanged(false); + emit reachableChanged(false); return; } + if(m_deviceBlocked) { //add command to queue m_commandList.append(command); @@ -99,7 +133,10 @@ void KeContact::handleNextCommandInQueue() { if (!m_udpSocket) { qCWarning(dcKebaKeContact()) << "UDP socket not initialized"; - emit connectionChanged(false); + 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(); @@ -125,13 +162,7 @@ QUuid KeContact::enableOutput(bool state) 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); - } - }); + sendCommand(datagram, requestId); return requestId; } @@ -143,12 +174,8 @@ QUuid KeContact::setMaxAmpere(int milliAmpere) 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()) << "sSnd command: " << data; + sendCommand(data, requestId); return requestId; } @@ -169,12 +196,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()) << "Send 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()) << "Send 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()) << "Send command: " << data; + sendCommand(data, requestId); return requestId; } @@ -237,7 +287,10 @@ void KeContact::readPendingDatagrams() //Only process data from the target device continue; } - emit connectionChanged(true); + if (m_reachable != true) { + m_reachable = true; + emit reachableChanged(true); + } qCDebug(dcKebaKeContact()) << "Data received" << datagram; if(datagram.contains("TCH-OK")){ @@ -257,7 +310,7 @@ void KeContact::readPendingDatagrams() } else { //Probably the response has taken too long and the requestId has been already removed } - } else if(datagram.left(8).contains("Firmware")){ + } else if(datagram.left(8).contains("Firmware")){ //Command response has been received, now send the next command m_deviceBlocked = false; @@ -306,12 +359,12 @@ void KeContact::readPendingDatagrams() 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.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.currentFailsafe = data.value("Curr FS").toInt(); + reportTwo.timeoutFailsafe = data.value("Tmo FS").toInt(); reportTwo.output = data.value("Output").toInt(); reportTwo.input= data.value("Input").toInt(); reportTwo.serialNumber = data.value("Serial").toString(); diff --git a/keba/kecontact.h b/keba/kecontact.h index 20bd3ce6..c73b3ec0 100644 --- a/keba/kecontact.h +++ b/keba/kecontact.h @@ -46,6 +46,14 @@ public: ~KeContact(); bool init(); + enum Model { + ModelUnkown, + ModelP20, + ModelP30, + ModelBMW + }; + Q_ENUM(Model) + enum State { StateStarting = 0, StateNotReady, @@ -54,6 +62,7 @@ public: StateError, StateAuthorizationRejected }; + Q_ENUM(State) enum PlugState { PlugStateUnplugged = 0, @@ -62,6 +71,7 @@ public: PlugStatePluggedOnChargingStationAndPluggedOnEV = 5, PlugStatePluggedOnChargingStationAndPlugLockedAndPluggedOnEV = 7 }; + Q_ENUM(PlugState) enum BroadcastType { BroadcastTypeState = 0, @@ -73,9 +83,11 @@ public: }; 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,15 +97,18 @@ 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. + 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 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. + int 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. }; @@ -112,21 +127,34 @@ public: }; 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(); + // Command “report 1xx” + + // Command “currtime” + // Command “output” + + + private: + bool m_reachable = false; QUdpSocket *m_udpSocket = nullptr; QHostAddress m_address; QByteArrayList m_commandList; @@ -136,11 +164,13 @@ private: int m_serialNumber; QList m_pendingRequests; + + void sendCommand(const QByteArray &data, const QUuid &requestId); void sendCommand(const QByteArray &data); 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); @@ -151,7 +181,5 @@ signals: private slots: void readPendingDatagrams(); }; - - #endif // KECONTACT_H From 5b38c6c9bc6dfe13e99639905d1955ad7aedc0a1 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Wed, 13 Jan 2021 14:32:41 +0100 Subject: [PATCH 2/9] added session finished event --- keba/integrationpluginkeba.cpp | 148 +++++++++++++++++++++++--------- keba/integrationpluginkeba.h | 4 +- keba/integrationpluginkeba.json | 32 +++++++ keba/kecontact.cpp | 57 ++++++++---- keba/kecontact.h | 39 ++++++--- 5 files changed, 209 insertions(+), 71 deletions(-) diff --git a/keba/integrationpluginkeba.cpp b/keba/integrationpluginkeba.cpp index 36d13ce6..fc51f26d 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -44,27 +44,27 @@ IntegrationPluginKeba::IntegrationPluginKeba() void IntegrationPluginKeba::init() { - m_discovery = new Discovery(this); - connect(m_discovery, &Discovery::finished, this, [this](const QList &hosts) { + 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 (const Host &host, hosts) { + if (!host.hostName().contains("keba", Qt::CaseSensitivity::CaseInsensitive)) + continue; - foreach (Thing *existingThing, myThings()) { - 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()); + foreach (Thing *existingThing, myThings()) { + 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; - } - } - } - }); + } else { + qCDebug(dcKebaKeContact()) << "Keba Wallbox" << existingThing->name() << "IP address has not changed" << host.address(); + } + break; + } + } + } + }); } void IntegrationPluginKeba::discoverThings(ThingDiscoveryInfo *info) @@ -113,9 +113,9 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) KeContact *keba = new KeContact(address, this); connect(keba, &KeContact::reachableChanged, 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::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived); connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived); if (!keba->init()){ qCWarning(dcKebaKeContact()) << "Cannot bind to port" << 7090; @@ -316,12 +316,7 @@ void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &repo if (!thing) return; - thing->setStateValue() - thing->setStateValue(wallboxPowerStateTypeId, reportTwo.enableUser); - thing->setStateValue(wallboxError1StateTypeId, reportTwo.error1); - thing->setStateValue(wallboxError2StateTypeId, reportTwo.error2); - - qCDebug(dcKebaKeContact()) << "Report 2 recieved for" << thing->name(); + 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; @@ -338,17 +333,27 @@ void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &repo qCDebug(dcKebaKeContact()) << " - Timeout CT:" << reportTwo.timeoutCt; qCDebug(dcKebaKeContact()) << " - Output:" << reportTwo.output; qCDebug(dcKebaKeContact()) << " - Input:" << reportTwo.input; - qCDebug(dcKebaKeContact()) << " - Serialnumber:" << reportTwo.serialNumber; + qCDebug(dcKebaKeContact()) << " - Serial number:" << reportTwo.serialNumber; qCDebug(dcKebaKeContact()) << " - Uptime:" << reportTwo.seconds; + if (reportTwo.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) { + setDeviceState(thing, reportTwo.state); + setDevicePlugState(thing, reportTwo.plugState); - //thing->setStateValue(wallboxMaxChargingCurrentAmpereStateTypeId, reportTwo.maxCurrent); - thing->setStateValue(wallboxMaxChargingCurrentPercentStateTypeId, reportTwo.maxCurrentPercentage); - //thing->setStateValue(wallboxCurrentHardwareLimitationStateTypeId, reportTwo.currentHardwareLimitation); + thing->setStateValue(wallboxPowerStateTypeId, reportTwo.enableUser); + thing->setStateValue(wallboxError1StateTypeId, reportTwo.error1); + thing->setStateValue(wallboxError2StateTypeId, reportTwo.error2); + //thing->setStateValue(wallboxMaxChargingCurrentAmpereStateTypeId, reportTwo.maxCurrent); + thing->setStateValue(wallboxMaxChargingCurrentPercentStateTypeId, reportTwo.maxCurrentPercentage); + //thing->setStateValue(wallboxCurrentHardwareLimitationStateTypeId, reportTwo.currentHardwareLimitation); + thing->setStateValue(wallboxOutputX2StateTypeId, reportTwo.output); + thing->setStateValue(wallboxInputStateTypeId, reportTwo.input); - setDeviceState(thing, reportTwo.state); - setDevicePlugState(thing, reportTwo.plugState); + thing->setStateValue(wallboxUptimeStateTypeId, reportTwo.seconds); + } else { + qCWarning(dcKebaKeContact()) << "Received report but the serial number didn't match"; + } } void IntegrationPluginKeba::onReportThreeReceived(const KeContact::ReportThree &reportThree) @@ -358,16 +363,77 @@ void IntegrationPluginKeba::onReportThreeReceived(const KeContact::ReportThree & if (!thing) return; - 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); //TODO check state name - thing->setStateValue(wallboxPowerFactorStateTypeId, reportThree.PowerFactor); - 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 << "[mA]"; + qCDebug(dcKebaKeContact()) << " - Current phase 2:" << reportThree.currentPhase2 << "[mA]"; + qCDebug(dcKebaKeContact()) << " - Current phase 3:" << reportThree.currentPhase3 << "[mA]"; + 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 << "[W]"; + qCDebug(dcKebaKeContact()) << " - Energy session" << reportThree.energySession << "[Wh]"; + qCDebug(dcKebaKeContact()) << " - Energy total" << reportThree.energyTotal << "[Wh]"; + qCDebug(dcKebaKeContact()) << " - Serial number" << reportThree.serialNumber; + qCDebug(dcKebaKeContact()) << " - Uptime" << reportThree.seconds; + + 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.ePres; + 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; + + // Report 101 is the lastest finished session + if (reportNumber == 101) { + if (report.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) { + if (!m_lastSessionId.contains(thing->id())) { + 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.setThingId(thing->id()); + ParamList params; + //params << Param(wallboxSessio); + 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) diff --git a/keba/integrationpluginkeba.h b/keba/integrationpluginkeba.h index b5377cb6..c0689dcf 100644 --- a/keba/integrationpluginkeba.h +++ b/keba/integrationpluginkeba.h @@ -67,6 +67,8 @@ private: Discovery *m_discovery = nullptr; QHash m_kebaDevices; + QHash m_lastSessionId; + QHash m_asyncSetup; QHash m_asyncActions; QHash m_chargingSessionStartTime; @@ -77,9 +79,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 b1e6d0fe..1f49bf49 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -298,6 +298,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": "Seconds", + "defaultValue": 0 + }, + { + "id": "c8de58b6-b671-4fee-b552-d2c14a37a769", + "name": "energy", + "displayName": "Energy", + "type": "double", + "defaultValue": 0.00, + "unit": "KiloWattHour" + } + ] + } ] } ] diff --git a/keba/kecontact.cpp b/keba/kecontact.cpp index 71c22194..cb263f39 100644 --- a/keba/kecontact.cpp +++ b/keba/kecontact.cpp @@ -92,7 +92,7 @@ QUuid KeContact::stop(const QByteArray &rfidToken) return requestId; } -void KeContact::setAddress(QHostAddress address) +void KeContact::setAddress(const QHostAddress &address) { m_address = address; } @@ -339,19 +339,29 @@ void KeContact::readPendingDatagrams() QVariantMap data = jsonDoc.toVariant().toMap(); if(data.contains("ID")) { - - if (data.value("ID").toString() == "1") { + 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();; + reportOne.serialNumber = data.value("Serial").toString(); + 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(data.value("ID").toString() == "2"){ + } else if (id == 2) { ReportTwo reportTwo; - qCDebug(dcKebaKeContact()) << "Report 2 reveiced"; + qCDebug(dcKebaKeContact()) << "Report 2 received"; int state = data.value("State").toInt(); reportTwo.state = State(state); reportTwo.error1 = data.value("Error1").toInt(); @@ -371,22 +381,33 @@ void KeContact::readPendingDatagrams() reportTwo.seconds = data.value("Sec").toInt(); emit reportTwoReceived(reportTwo); - } else if(data.value("ID").toString() == "3"){ + } else if (id == 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(); + 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; + report.sessionId = data.value("Session ID").toInt(); + report.currHW = data.value("Curr HW").toInt(); + //report. = data.value("Curr HW").toInt(); TODO + report.currHW = data.value("Curr HW").toInt(); + report.currHW = data.value("Curr HW").toInt(); + report.currHW = data.value("Curr HW").toInt(); + report.currHW = data.value("Curr HW").toInt(); + emit report1XXReceived(id, report); } } else { if (data.contains("State")) { diff --git a/keba/kecontact.h b/keba/kecontact.h index c73b3ec0..91ba7159 100644 --- a/keba/kecontact.h +++ b/keba/kecontact.h @@ -113,17 +113,32 @@ public: }; 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 in 0,1% (cosphi) + double energySession; //Power consumption of the current loading session in 0,1Wh; Reset with new loading session (state = 2). + double energyTotal; //Total power consumption (persistent) without current loading session 0,1Wh; 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" + int currHW; // maximum charging current of the cable and the charging station setting (equal to report 2)"E + double startEnergy; // total energy value at the beginning of the session" + double ePres; // delivered energy until now (equal to E pres in report 3)" + int startTime; // system time when the session was started (seconds from reboot; NTP implementation is still under progress)" + 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; hexadecimal; first character is the lowest nibble" + QByteArray rfidClass; // RFID classifier shows the defined color code if the used card is a BMW card (for example “010104” means the white card)" + QString serialNumber; // serial number of the charging station" + int seconds; // current time when the report was generated }; QHostAddress address(); @@ -145,6 +160,7 @@ public: void getReport1(); // Command “report” void getReport2(); void getReport3(); + void getReport1XX(int reportNumber = 100); // Command “report 1xx” @@ -176,6 +192,7 @@ signals: 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: From 396804bbd5386c2dda94ba29b339d5edb0935464 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Thu, 14 Jan 2021 11:36:40 +0100 Subject: [PATCH 3/9] multi keba bug fixed --- keba/integrationpluginkeba.cpp | 83 +++++++++++++++++++++----------- keba/integrationpluginkeba.h | 5 +- keba/integrationpluginkeba.json | 28 +++++++---- keba/kecontact.cpp | 85 +++++++++++++++++++-------------- keba/kecontact.h | 22 +++------ 5 files changed, 133 insertions(+), 90 deletions(-) diff --git a/keba/integrationpluginkeba.cpp b/keba/integrationpluginkeba.cpp index fc51f26d..2b3ee8e0 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -52,7 +52,14 @@ void IntegrationPluginKeba::init() continue; foreach (Thing *existingThing, myThings()) { - if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString() == host.macAddress()) { + 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()); @@ -109,8 +116,16 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) if (thing->thingClassId() == wallboxThingClassId) { + if(!m_udpSocket){ + m_udpSocket = new QUdpSocket(this); + if (!m_udpSocket->bind(QHostAddress::AnyIPv4, 7090, QAbstractSocket::ShareAddress)) { + qCWarning(dcKebaKeContact()) << "Cannot bind to port" << 7090; + return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port.")); + } + } + QHostAddress address = QHostAddress(thing->paramValue(wallboxThingIpAddressParamTypeId).toString()); - KeContact *keba = new KeContact(address, this); + KeContact *keba = new KeContact(address, m_udpSocket, m_udpSocket); connect(keba, &KeContact::reachableChanged, this, &IntegrationPluginKeba::onConnectionChanged); connect(keba, &KeContact::commandExecuted, this, &IntegrationPluginKeba::onCommandExecuted); connect(keba, &KeContact::reportTwoReceived, this, &IntegrationPluginKeba::onReportTwoReceived); @@ -118,7 +133,6 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) connect(keba, &KeContact::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived); 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.")); } @@ -154,6 +168,11 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) void IntegrationPluginKeba::postSetupThing(Thing *thing) { qCDebug(dcKebaKeContact()) << "Post setup" << thing->name(); + if (thing->thingClassId() != wallboxThingClassId) { + qCWarning(dcKebaKeContact()) << "Thing class id not supported" << thing->thingClassId(); + return; + } + KeContact *keba = m_kebaDevices.value(thing->id()); if (!keba) { qCWarning(dcKebaKeContact()) << "No Keba connection found for this thing"; @@ -161,6 +180,9 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing) keba->getReport2(); keba->getReport3(); } + if (thing->paramValue(wallboxThingMacAddressParamTypeId).toString().isEmpty()) { + m_discovery->discoverHosts(25); + } if (!m_updateTimer) { m_updateTimer = hardwareManager()->pluginTimerManager()->registerTimer(60); @@ -173,17 +195,8 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing) } keba->getReport2(); keba->getReport3(); - - 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); + if (thing->stateValue(wallboxActivityStateTypeId).toString() == "Charging") { + keba->getReport1XX(100); } } }); @@ -241,16 +254,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) @@ -403,7 +406,7 @@ void IntegrationPluginKeba::onReport1XXReceived(int reportNumber, const KeContac qCDebug(dcKebaKeContact()) << " - Session Id" << report.sessionId; qCDebug(dcKebaKeContact()) << " - Curr HW" << report.currHW; qCDebug(dcKebaKeContact()) << " - Energy start" << report.startEnergy; - qCDebug(dcKebaKeContact()) << " - Energy present" << report.ePres; + qCDebug(dcKebaKeContact()) << " - Energy present" << report.presentEnergy; qCDebug(dcKebaKeContact()) << " - Start time" << report.startTime; qCDebug(dcKebaKeContact()) << " - End time" << report.endTime; qCDebug(dcKebaKeContact()) << " - Stop reason" << report.stopReason; @@ -412,18 +415,32 @@ void IntegrationPluginKeba::onReport1XXReceived(int reportNumber, const KeContac qCDebug(dcKebaKeContact()) << " - Serial number" << report.serialNumber; qCDebug(dcKebaKeContact()) << " - Uptime" << report.seconds; - // Report 101 is the lastest finished session - if (reportNumber == 101) { + 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(wallboxSessio); + params << Param(wallboxChargingSessionFinishedEventEnergyParamTypeId, report.presentEnergy); + params << Param(wallboxChargingSessionFinishedEventDurationParamTypeId, report.endTime); + params << Param(wallboxChargingSessionFinishedEventIdParamTypeId); event.setParams(params); emitEvent(event); } @@ -443,14 +460,17 @@ 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(wallboxSessionEnergyStateTypeId, content.toInt()); + thing->setStateValue(wallboxSessionEnergyStateTypeId, content.toInt()/10000.00); break; case KeContact::BroadcastTypeState: setDeviceState(thing, KeContact::State(content.toInt())); @@ -459,6 +479,7 @@ void IntegrationPluginKeba::onBroadcastReceived(KeContact::BroadcastType type, c thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, content.toInt()); break; case KeContact::BroadcastTypeEnableSys: + qCDebug(dcKebaKeContact()) << "Broadcast enable sys not implemented"; break; } } @@ -491,6 +512,10 @@ void IntegrationPluginKeba::executeAction(ThingActionInfo *info) m_asyncActions.insert(requestId, info); connect(info, &ThingActionInfo::aborted, this, [requestId, this]{m_asyncActions.remove(requestId);}); + } else if(action.actionTypeId() == wallboxOutputX2ActionTypeId){ + + } else if(action.actionTypeId() == wallboxFailsafeModeActionTypeId){ + } else { qCWarning(dcKebaKeContact()) << "Unhandled ActionTypeId:" << action.actionTypeId(); info->finish(Thing::ThingErrorActionTypeNotFound); diff --git a/keba/integrationpluginkeba.h b/keba/integrationpluginkeba.h index c0689dcf..ec7d2338 100644 --- a/keba/integrationpluginkeba.h +++ b/keba/integrationpluginkeba.h @@ -41,6 +41,7 @@ #include #include #include +#include class IntegrationPluginKeba : public IntegrationPlugin { @@ -65,13 +66,13 @@ private: PluginTimer *m_updateTimer = nullptr; PluginTimer *m_reconnectTimer = nullptr; + QUdpSocket *m_udpSocket = nullptr; + Discovery *m_discovery = nullptr; QHash m_kebaDevices; QHash m_lastSessionId; - QHash m_asyncSetup; QHash m_asyncActions; - QHash m_chargingSessionStartTime; void setDeviceState(Thing *device, KeContact::State state); void setDevicePlugState(Thing *device, KeContact::PlugState plugState); diff --git a/keba/integrationpluginkeba.json b/keba/integrationpluginkeba.json index 1f49bf49..28dcd33c 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -65,13 +65,7 @@ "displayName": "Model", "displayNameEvent": "Model changed", "type": "QString", - "defaultValue": "Unknown", - "possibleValues": [ - "Unknown", - "Keba P20", - "Keba P30", - "BMW" - ] + "defaultValue": "Unknown" }, { "id": "e941ace5-fb7f-4dc2-b3f2-188233f4e934", @@ -107,6 +101,14 @@ "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", @@ -216,7 +218,7 @@ "displayName": "Power consumption", "displayNameEvent": "Power consumtion changed", "type": "double", - "unit": "Watt", + "unit": "KiloWatt", "defaultValue": 0 }, { @@ -281,6 +283,16 @@ "type": "int", "unit": "Seconds", "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": [ diff --git a/keba/kecontact.cpp b/keba/kecontact.cpp index cb263f39..286d087b 100644 --- a/keba/kecontact.cpp +++ b/keba/kecontact.cpp @@ -34,8 +34,9 @@ #include -KeContact::KeContact(QHostAddress address, QObject *parent) : +KeContact::KeContact(const QHostAddress &address, QUdpSocket *udpSocket, QObject *parent) : QObject(parent), + m_udpSocket(udpSocket), m_address(address) { m_requestTimeoutTimer = new QTimer(this); @@ -54,15 +55,16 @@ KeContact::~KeContact() { } 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; + qCDebug(dcKebaKeContact()) << "Initializing Keba connection"; + if(m_udpSocket){ + connect(m_udpSocket, &QUdpSocket::readyRead, this, &KeContact::readPendingDatagrams); + if (!m_udpSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress)) { + qCWarning(dcKebaKeContact()) << "Cannot bind to port" << m_port; return false; } - connect(m_udpSocket, &QUdpSocket::readyRead, this, &KeContact::readPendingDatagrams); + } else { + qCWarning(dcKebaKeContact()) << "UDP socket not valid"; + return false; } return true; } @@ -94,6 +96,7 @@ QUuid KeContact::stop(const QByteArray &rfidToken) void KeContact::setAddress(const QHostAddress &address) { + qCDebug(dcKebaKeContact()) << "Updating Keba connection address" << address.toString(); m_address = address; } @@ -123,7 +126,7 @@ void KeContact::sendCommand(const QByteArray &command) m_commandList.append(command); } else { //send command - m_udpSocket->writeDatagram(command, m_address, 7090); + m_udpSocket->writeDatagram(command, m_address, m_port); m_requestTimeoutTimer->start(5000); m_deviceBlocked = true; } @@ -142,7 +145,7 @@ void KeContact::handleNextCommandInQueue() 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); + m_udpSocket->writeDatagram(command, m_address, m_port); m_requestTimeoutTimer->start(5000); } else { //nothing to do @@ -161,7 +164,7 @@ QUuid KeContact::enableOutput(bool state) } else{ datagram.append("ena 0"); } - qCDebug(dcKebaKeContact()) << "Datagram : " << datagram; + qCDebug(dcKebaKeContact()) << "Enable output, command:" << datagram; sendCommand(datagram, requestId); return requestId; } @@ -174,7 +177,7 @@ QUuid KeContact::setMaxAmpere(int milliAmpere) qCDebug(dcKebaKeContact()) << "Update max current to : " << milliAmpere; QByteArray data; data.append("curr " + QVariant(milliAmpere).toByteArray()); - qCDebug(dcKebaKeContact()) << "sSnd command: " << data; + qCDebug(dcKebaKeContact()) << "Set max. ampere, command: " << data; sendCommand(data, requestId); return requestId; } @@ -196,7 +199,7 @@ QUuid KeContact::displayMessage(const QByteArray &message) modifiedMessage.resize(23); } data.append("display 0 0 0 0 " + modifiedMessage); - qCDebug(dcKebaKeContact()) << "Send command: " << data; + qCDebug(dcKebaKeContact()) << "Display message, command: " << data; sendCommand(data, requestId); return requestId; } @@ -208,7 +211,7 @@ QUuid KeContact::chargeWithEnergyLimit(double energy) QByteArray data; data.append("setenergy " + QVariant(static_cast(energy*10000)).toByteArray()); - qCDebug(dcKebaKeContact()) << "Send command: " << data; + qCDebug(dcKebaKeContact()) << "Charge with energy limit, command: " << data; sendCommand(data, requestId); return requestId; } @@ -223,7 +226,7 @@ QUuid KeContact::setFailsafe(int timeout, int current, bool save) data.append(" "+QVariant(timeout).toByteArray()); data.append(" "+QVariant(current).toByteArray()); data.append((save ? " 1":" 0")); - qCDebug(dcKebaKeContact()) << "Send command: " << data; + qCDebug(dcKebaKeContact()) << "Set failsafe mode, command: " << data; sendCommand(data, requestId); return requestId; } @@ -233,31 +236,35 @@ 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); +} + +void KeContact::getReport(int reportNumber) { QByteArray data; - data.append("report 3"); - qCDebug(dcKebaKeContact()) << "data: " << data; + data.append("report "+QVariant(reportNumber).toByteArray()); + qCDebug(dcKebaKeContact()) << "Get report" << reportNumber << "Command:" << data; sendCommand(data); } @@ -267,7 +274,7 @@ 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; } @@ -281,18 +288,21 @@ void KeContact::readPendingDatagrams() quint16 senderPort; 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; } + + datagram.resize(socket->pendingDatagramSize()); + socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); + qCDebug(dcKebaKeContact()) << "Data received" << datagram; + if (m_reachable != true) { m_reachable = true; emit reachableChanged(true); } - qCDebug(dcKebaKeContact()) << "Data received" << datagram; if(datagram.contains("TCH-OK")){ //Command response has been received, now send the next command @@ -399,14 +409,19 @@ void KeContact::readPendingDatagrams() reportThree.seconds = data.value("Sec").toInt(); emit reportThreeReceived(reportThree); } else if (id >= 100) { + Report1XX report; report.sessionId = data.value("Session ID").toInt(); report.currHW = data.value("Curr HW").toInt(); - //report. = data.value("Curr HW").toInt(); TODO - report.currHW = data.value("Curr HW").toInt(); - report.currHW = data.value("Curr HW").toInt(); - report.currHW = data.value("Curr HW").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); } } else { diff --git a/keba/kecontact.h b/keba/kecontact.h index 91ba7159..62c20053 100644 --- a/keba/kecontact.h +++ b/keba/kecontact.h @@ -42,18 +42,10 @@ class KeContact : public QObject { Q_OBJECT public: - explicit KeContact(QHostAddress address, QObject *parent = nullptr); + explicit KeContact(const QHostAddress &address, QUdpSocket *udpSocket, QObject *parent = nullptr); ~KeContact(); bool init(); - enum Model { - ModelUnkown, - ModelP20, - ModelP30, - ModelBMW - }; - Q_ENUM(Model) - enum State { StateStarting = 0, StateNotReady, @@ -81,6 +73,7 @@ public: BroadcastTypeMaxCurr, BroadcastTypeEPres }; + Q_ENUM(BroadcastType) struct ReportOne { QString product; // Model name (variant @@ -131,7 +124,7 @@ public: int sessionId; // running session counter; not resettable" int currHW; // maximum charging current of the cable and the charging station setting (equal to report 2)"E double startEnergy; // total energy value at the beginning of the session" - double ePres; // delivered energy until now (equal to E pres in report 3)" + 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; NTP implementation is still under progress)" int endTime; // system time when the session has ended" int stopReason; // reason for stopping the session (1 = vehicle unplug; 10 = Rfid token)" @@ -160,16 +153,13 @@ public: void getReport1(); // Command “report” void getReport2(); void getReport3(); - void getReport1XX(int reportNumber = 100); - - // Command “report 1xx” + void getReport1XX(int reportNumber = 100); // Command “report 1xx” // Command “currtime” // Command “output” - - private: + int m_port = 7090; bool m_reachable = false; QUdpSocket *m_udpSocket = nullptr; QHostAddress m_address; @@ -180,7 +170,7 @@ private: int m_serialNumber; QList m_pendingRequests; - + void getReport(int reportNumber); void sendCommand(const QByteArray &data, const QUuid &requestId); void sendCommand(const QByteArray &data); void handleNextCommandInQueue(); From e24a4ad3157d8958c84075980cccfa9551a49ffd Mon Sep 17 00:00:00 2001 From: Boernsman Date: Thu, 14 Jan 2021 11:56:59 +0100 Subject: [PATCH 4/9] updated json strings --- keba/integrationpluginkeba.json | 28 +- ...2b09f-30a9-43d0-9ede-2f8debe075ac-en_US.ts | 513 +++++++++++++----- 2 files changed, 382 insertions(+), 159 deletions(-) diff --git a/keba/integrationpluginkeba.json b/keba/integrationpluginkeba.json index 28dcd33c..c21b1ee2 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -18,7 +18,7 @@ { "id": "730cd3d3-5f0e-4028-a8c2-ced7574f13f3", "name": "ipAddress", - "displayName": "IPv4 Address", + "displayName": "IP address", "type": "QString", "inputType": "IPv4Address", "defaultValue":"0.0.0.0" @@ -26,7 +26,7 @@ { "id": "c2df921d-ff8b-411c-9b1d-04a437d7dfa6", "name": "macAddress", - "displayName": "MAC Address", + "displayName": "MAC address", "type": "QString", "inputType": "TextLine", "defaultValue":"", @@ -38,7 +38,7 @@ "id": "ce813458-d7d8-4f40-9648-dba4c41e92f0", "name": "connected", "displayName": "Connected", - "displayNameEvent": "Connection changed", + "displayNameEvent": "Connected changed", "type": "bool", "defaultValue": false, "cached": false @@ -94,9 +94,9 @@ { "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 @@ -120,8 +120,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": "-" }, @@ -150,7 +150,7 @@ { "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", @@ -170,7 +170,7 @@ { "id": "c8344ca5-21ac-4cd1-8f4b-e5ed202c5862", "name": "voltagePhase2", - "displayName": "Voltage Phase 2", + "displayName": "Voltage phase 2", "displayNameEvent": "Voltage phase 2 changed", "type": "int", "unit": "Volt", @@ -179,7 +179,7 @@ { "id": "5f01e86c-0943-4849-a01a-db441916ebd5", "name": "voltagePhase3", - "displayName": "Voltage Phase 3", + "displayName": "Voltage phase 3", "displayNameEvent": "Voltage phase 3 changed", "type": "int", "unit": "Volt", @@ -188,7 +188,7 @@ { "id": "31ec17b0-11e3-4332-92b0-fea821cf024f", "name": "currentPhase1", - "displayName": "Current Phase 1", + "displayName": "Current phase 1", "displayNameEvent": "Current phase 1 changed", "type": "int", "unit": "Ampere", @@ -197,7 +197,7 @@ { "id": "cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97", "name": "currentPhase2", - "displayName": "Current Phase 2", + "displayName": "Current phase 2", "displayNameEvent": "Current phase 2 changed", "type": "double", "unit": "Ampere", @@ -206,7 +206,7 @@ { "id": "da838dc8-85f0-4e55-b4b5-cb93a43b373d", "name": "currentPhase3", - "displayName": "Current Phase 3", + "displayName": "Current phase 3", "displayNameEvent": "Current phase 3 changed", "type": "double", "unit": "Ampere", 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 - - From 1c2c04f1879bb87546977850dceab1da31f3c27c Mon Sep 17 00:00:00 2001 From: Boernsman Date: Thu, 14 Jan 2021 12:08:02 +0100 Subject: [PATCH 5/9] added german translation --- ...9142b09f-30a9-43d0-9ede-2f8debe075ac-de.ts | 550 ++++++++++++++++++ 1 file changed, 550 insertions(+) create mode 100644 keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-de.ts 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 + + + From 238efba31c5a1ce46123a9fe65a038a204365a49 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Thu, 14 Jan 2021 22:02:15 +0100 Subject: [PATCH 6/9] fixed udp init, polished debug output, fixed missing debian dependency --- debian/control | 1 + keba/discovery.cpp | 6 +++--- keba/integrationpluginkeba.cpp | 17 ++++++++++++----- keba/integrationpluginkeba.json | 11 ++++++++++- keba/kecontact.cpp | 4 ---- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/debian/control b/debian/control index a3c0e990..a782dbe0 100644 --- a/debian/control +++ b/debian/control @@ -973,6 +973,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/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 2b3ee8e0..2a022422 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -85,11 +85,13 @@ void IntegrationPluginKeba::discoverThings(ThingDiscoveryInfo *info) 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; } @@ -118,7 +120,7 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) if(!m_udpSocket){ m_udpSocket = new QUdpSocket(this); - if (!m_udpSocket->bind(QHostAddress::AnyIPv4, 7090, QAbstractSocket::ShareAddress)) { + if (!m_udpSocket->bind(QHostAddress::AnyIPv4, 7090, QAbstractSocket::DefaultForPlatform)) { qCWarning(dcKebaKeContact()) << "Cannot bind to port" << 7090; return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port.")); } @@ -137,9 +139,8 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port.")); } - m_kebaDevices.insert(thing->id(), keba); keba->getReport1(); - connect(keba, &KeContact::reportOneReceived, info, [info] (const KeContact::ReportOne &report) { + connect(keba, &KeContact::reportOneReceived, info, [info, this, keba] (const KeContact::ReportOne &report) { Thing *thing = info->thing(); qCDebug(dcKebaKeContact()) << "Report one received for" << thing->name(); @@ -152,12 +153,13 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) thing->setStateValue(wallboxFirmwareStateTypeId, report.firmware); thing->setStateValue(wallboxModelStateTypeId, report.product); thing->setStateValue(wallboxUptimeStateTypeId, report.seconds); + + m_kebaDevices.insert(thing->id(), keba); info->finish(Thing::ThingErrorNoError); }); connect(info, &ThingSetupInfo::aborted, keba, &KeContact::deleteLater); - connect(keba, &KeContact::destroyed, this, [thing, keba, this]{ + connect(keba, &KeContact::destroyed, this, [thing, this]{ m_kebaDevices.remove(thing->id()); - keba->deleteLater(); }); } else { qCWarning(dcKebaKeContact()) << "setupDevice, unhandled device class" << thing->thingClass(); @@ -224,6 +226,11 @@ void IntegrationPluginKeba::thingRemoved(Thing *thing) } if (myThings().empty()) { + qCDebug(dcKebaKeContact()) << "Closing UDP Ports"; + m_udpSocket->close(); + m_udpSocket->deleteLater(); + m_udpSocket = nullptr; + qCDebug(dcKebaKeContact()) << "Stopping plugin timers"; hardwareManager()->pluginTimerManager()->unregisterTimer(m_reconnectTimer); m_reconnectTimer = nullptr; diff --git a/keba/integrationpluginkeba.json b/keba/integrationpluginkeba.json index c21b1ee2..d87bc29d 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -142,7 +142,7 @@ "displayNameAction": "Set maximal charging current", "type": "uint", "unit": "MilliAmpere", - "defaultValue": 6000, + "defaultValue": 6, "minValue": 6000, "maxValue": 63000, "writable": true @@ -158,6 +158,15 @@ "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": "voltagePhase1", diff --git a/keba/kecontact.cpp b/keba/kecontact.cpp index 286d087b..9e681aab 100644 --- a/keba/kecontact.cpp +++ b/keba/kecontact.cpp @@ -58,10 +58,6 @@ bool KeContact::init(){ qCDebug(dcKebaKeContact()) << "Initializing Keba connection"; if(m_udpSocket){ connect(m_udpSocket, &QUdpSocket::readyRead, this, &KeContact::readPendingDatagrams); - if (!m_udpSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress)) { - qCWarning(dcKebaKeContact()) << "Cannot bind to port" << m_port; - return false; - } } else { qCWarning(dcKebaKeContact()) << "UDP socket not valid"; return false; From 078b5f0522c30d73662f70aa03f9e20a4e90afea Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 18 Jan 2021 09:58:35 +0100 Subject: [PATCH 7/9] fixed conversions added shared data layer --- keba/integrationpluginkeba.cpp | 74 ++++--- keba/integrationpluginkeba.h | 3 +- keba/integrationpluginkeba.json | 45 +++-- keba/keba.pro | 2 + keba/kecontact.cpp | 345 ++++++++++++++++---------------- keba/kecontact.h | 42 ++-- keba/kecontactdatalayer.cpp | 39 ++++ keba/kecontactdatalayer.h | 27 +++ 8 files changed, 318 insertions(+), 259 deletions(-) create mode 100644 keba/kecontactdatalayer.cpp create mode 100644 keba/kecontactdatalayer.h diff --git a/keba/integrationpluginkeba.cpp b/keba/integrationpluginkeba.cpp index 2a022422..19fda8c1 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -118,26 +118,23 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) if (thing->thingClassId() == wallboxThingClassId) { - if(!m_udpSocket){ - m_udpSocket = new QUdpSocket(this); - if (!m_udpSocket->bind(QHostAddress::AnyIPv4, 7090, QAbstractSocket::DefaultForPlatform)) { - qCWarning(dcKebaKeContact()) << "Cannot bind to port" << 7090; + if(!m_kebaData){ + qCDebug(dcKebaKeContact()) << "Creating new Keba data layer"; + m_kebaData = new KeContactDataLayer; + if (!m_kebaData->init()) { + m_kebaData->deleteLater(); return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port.")); } } QHostAddress address = QHostAddress(thing->paramValue(wallboxThingIpAddressParamTypeId).toString()); - KeContact *keba = new KeContact(address, m_udpSocket, m_udpSocket); + 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); - if (!keba->init()){ - keba->deleteLater(); - return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port.")); - } keba->getReport1(); connect(keba, &KeContact::reportOneReceived, info, [info, this, keba] (const KeContact::ReportOne &report) { @@ -146,13 +143,13 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) qCDebug(dcKebaKeContact()) << "Report one received for" << thing->name(); qCDebug(dcKebaKeContact()) << " - Firmware" << report.firmware; qCDebug(dcKebaKeContact()) << " - Product" << report.product; - qCDebug(dcKebaKeContact()) << " - Uptime" << report.seconds; + qCDebug(dcKebaKeContact()) << " - Uptime" << report.seconds/60 << "[min]"; qCDebug(dcKebaKeContact()) << " - Com Module" << report.comModule; thing->setStateValue(wallboxConnectedStateTypeId, true); thing->setStateValue(wallboxFirmwareStateTypeId, report.firmware); thing->setStateValue(wallboxModelStateTypeId, report.product); - thing->setStateValue(wallboxUptimeStateTypeId, report.seconds); + thing->setStateValue(wallboxUptimeStateTypeId, report.seconds/60); m_kebaDevices.insert(thing->id(), keba); info->finish(Thing::ThingErrorNoError); @@ -174,7 +171,7 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing) qCWarning(dcKebaKeContact()) << "Thing class id not supported" << thing->thingClassId(); return; } - + thing->setStateValue(wallboxConnectedStateTypeId, true); KeContact *keba = m_kebaDevices.value(thing->id()); if (!keba) { qCWarning(dcKebaKeContact()) << "No Keba connection found for this thing"; @@ -227,9 +224,8 @@ void IntegrationPluginKeba::thingRemoved(Thing *thing) if (myThings().empty()) { qCDebug(dcKebaKeContact()) << "Closing UDP Ports"; - m_udpSocket->close(); - m_udpSocket->deleteLater(); - m_udpSocket = nullptr; + m_kebaData->deleteLater(); + m_kebaData = nullptr; qCDebug(dcKebaKeContact()) << "Stopping plugin timers"; hardwareManager()->pluginTimerManager()->unregisterTimer(m_reconnectTimer); @@ -344,7 +340,7 @@ void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &repo qCDebug(dcKebaKeContact()) << " - Output:" << reportTwo.output; qCDebug(dcKebaKeContact()) << " - Input:" << reportTwo.input; qCDebug(dcKebaKeContact()) << " - Serial number:" << reportTwo.serialNumber; - qCDebug(dcKebaKeContact()) << " - Uptime:" << reportTwo.seconds; + qCDebug(dcKebaKeContact()) << " - Uptime:" << reportTwo.seconds/60 << "[min]"; if (reportTwo.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) { setDeviceState(thing, reportTwo.state); @@ -353,14 +349,16 @@ void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &repo thing->setStateValue(wallboxPowerStateTypeId, reportTwo.enableUser); thing->setStateValue(wallboxError1StateTypeId, reportTwo.error1); thing->setStateValue(wallboxError2StateTypeId, reportTwo.error2); - //thing->setStateValue(wallboxMaxChargingCurrentAmpereStateTypeId, reportTwo.maxCurrent); + thing->setStateValue(wallboxSystemEnabledStateTypeId, reportTwo.enableSys); + + thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, reportTwo.maxCurrent); thing->setStateValue(wallboxMaxChargingCurrentPercentStateTypeId, reportTwo.maxCurrentPercentage); - //thing->setStateValue(wallboxCurrentHardwareLimitationStateTypeId, reportTwo.currentHardwareLimitation); + thing->setStateValue(wallboxMaxPossibleChargingCurrentStateTypeId, reportTwo.currentHardwareLimitation); thing->setStateValue(wallboxOutputX2StateTypeId, reportTwo.output); thing->setStateValue(wallboxInputStateTypeId, reportTwo.input); - thing->setStateValue(wallboxUptimeStateTypeId, reportTwo.seconds); + thing->setStateValue(wallboxUptimeStateTypeId, reportTwo.seconds/60); } else { qCWarning(dcKebaKeContact()) << "Received report but the serial number didn't match"; } @@ -374,17 +372,17 @@ void IntegrationPluginKeba::onReportThreeReceived(const KeContact::ReportThree & return; qCDebug(dcKebaKeContact()) << "Report 3 received for" << thing->name() << "Serial number:" << thing->stateValue(wallboxSerialnumberStateTypeId).toString(); - qCDebug(dcKebaKeContact()) << " - Current phase 1:" << reportThree.currentPhase1 << "[mA]"; - qCDebug(dcKebaKeContact()) << " - Current phase 2:" << reportThree.currentPhase2 << "[mA]"; - qCDebug(dcKebaKeContact()) << " - Current phase 3:" << reportThree.currentPhase3 << "[mA]"; + 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 << "[W]"; - qCDebug(dcKebaKeContact()) << " - Energy session" << reportThree.energySession << "[Wh]"; - qCDebug(dcKebaKeContact()) << " - Energy total" << reportThree.energyTotal << "[Wh]"; + 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; + qCDebug(dcKebaKeContact()) << " - Uptime" << reportThree.seconds/60 << "[min]"; if (reportThree.serialNumber == thing->stateValue(wallboxSerialnumberStateTypeId).toString()) { thing->setStateValue(wallboxCurrentPhase1EventTypeId, reportThree.currentPhase1); @@ -483,7 +481,7 @@ void IntegrationPluginKeba::onBroadcastReceived(KeContact::BroadcastType type, c setDeviceState(thing, KeContact::State(content.toInt())); break; case KeContact::BroadcastTypeMaxCurr: - thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, content.toInt()); + thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, content.toInt()/1000.00); break; case KeContact::BroadcastTypeEnableSys: qCDebug(dcKebaKeContact()) << "Broadcast enable sys not implemented"; @@ -503,30 +501,28 @@ 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);}); + 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){ + } else if(action.actionTypeId() == wallboxOutputX2ActionTypeId) { + requestId = keba->setOutputX2(action.param(wallboxOutputX2ActionOutputX2ParamTypeId).value().toBool()); } else if(action.actionTypeId() == wallboxFailsafeModeActionTypeId){ - + requestId = keba->setFailsafe(60, 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 ec7d2338..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" @@ -66,7 +67,7 @@ private: PluginTimer *m_updateTimer = nullptr; PluginTimer *m_reconnectTimer = nullptr; - QUdpSocket *m_udpSocket = nullptr; + KeContactDataLayer *m_kebaData = nullptr; Discovery *m_discovery = nullptr; QHash m_kebaDevices; diff --git a/keba/integrationpluginkeba.json b/keba/integrationpluginkeba.json index d87bc29d..c47540b7 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -83,14 +83,7 @@ "type": "QString", "defaultValue": "" }, - { - "id": "1d30ce60-2ea0-450f-817e-5c88f59ebfbf", - "name": "sessionId", - "displayName": "Session ID", - "displayNameEvent": "Session ID changed", - "type": "uint", - "defaultValue": "" - }, + { "id": "83ed0774-2a91-434d-b03c-d920d02f2981", "name": "power", @@ -130,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", @@ -140,11 +133,11 @@ "displayName": "Maximal charging current", "displayNameEvent": "Maximal charging current changed", "displayNameAction": "Set maximal charging current", - "type": "uint", - "unit": "MilliAmpere", - "defaultValue": 6, - "minValue": 6000, - "maxValue": 63000, + "type": "double", + "unit": "Ampere", + "defaultValue": 6.00, + "minValue": 6.00, + "maxValue": 63.00, "writable": true }, { @@ -199,9 +192,9 @@ "name": "currentPhase1", "displayName": "Current phase 1", "displayNameEvent": "Current phase 1 changed", - "type": "int", + "type": "double", "unit": "Ampere", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97", @@ -210,7 +203,7 @@ "displayNameEvent": "Current phase 2 changed", "type": "double", "unit": "Ampere", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "da838dc8-85f0-4e55-b4b5-cb93a43b373d", @@ -219,7 +212,7 @@ "displayNameEvent": "Current phase 3 changed", "type": "double", "unit": "Ampere", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "7af9e93b-099d-4d9d-a480-9c0f66aecd8b", @@ -228,7 +221,7 @@ "displayNameEvent": "Power consumtion changed", "type": "double", "unit": "KiloWatt", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "889c3c9a-96b4-4408-bd9a-d79e36ed9296", @@ -239,6 +232,14 @@ "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", "name": "sessionTime", @@ -290,7 +291,7 @@ "displayName": "Uptime", "displayNameEvent": "Uptime changed", "type": "int", - "unit": "Seconds", + "unit": "Minutes", "defaultValue": 0 }, { @@ -338,7 +339,7 @@ "name": "duration", "displayName": "Duration", "type": "int", - "unit": "Seconds", + "unit": "Minutes", "defaultValue": 0 }, { 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 9e681aab..8e940424 100644 --- a/keba/kecontact.cpp +++ b/keba/kecontact.cpp @@ -34,11 +34,12 @@ #include -KeContact::KeContact(const QHostAddress &address, QUdpSocket *udpSocket, QObject *parent) : +KeContact::KeContact(const QHostAddress &address, KeContactDataLayer *dataLayer, QObject *parent) : QObject(parent), - m_udpSocket(udpSocket), + 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] { @@ -48,23 +49,15 @@ KeContact::KeContact(const QHostAddress &address, QUdpSocket *udpSocket, QObject 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(){ - qCDebug(dcKebaKeContact()) << "Initializing Keba connection"; - if(m_udpSocket){ - connect(m_udpSocket, &QUdpSocket::readyRead, this, &KeContact::readPendingDatagrams); - } else { - qCWarning(dcKebaKeContact()) << "UDP socket not valid"; - return false; - } - return true; -} - QHostAddress KeContact::address() { return m_address; @@ -105,13 +98,12 @@ void KeContact::sendCommand(const QByteArray &command, const QUuid &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 reachableChanged(false); return; @@ -121,8 +113,8 @@ void KeContact::sendCommand(const QByteArray &command) //add command to queue m_commandList.append(command); } else { - //send command - m_udpSocket->writeDatagram(command, m_address, m_port); + qCDebug(dcKebaKeContact()) << "Writing datagram" << command << m_address; + m_dataLayer->write( m_address, command); m_requestTimeoutTimer->start(5000); m_deviceBlocked = true; } @@ -130,8 +122,8 @@ void KeContact::sendCommand(const QByteArray &command) void KeContact::handleNextCommandInQueue() { - if (!m_udpSocket) { - qCWarning(dcKebaKeContact()) << "UDP socket not initialized"; + if (!m_dataLayer) { + qCWarning(dcKebaKeContact()) << "Data layer not initialized"; if (m_reachable == true) { m_reachable = false; emit reachableChanged(false); @@ -141,10 +133,9 @@ void KeContact::handleNextCommandInQueue() 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, m_port); + qCDebug(dcKebaKeContact()) << "Writing datagram" << command << m_address; + m_dataLayer->write( m_address, command); m_requestTimeoutTimer->start(5000); - } else { - //nothing to do } } @@ -256,6 +247,17 @@ void KeContact::getReport1XX(int reportNumber) getReport(reportNumber); } +QUuid KeContact::setOutputX2(bool state) +{ + QUuid requestId = QUuid::createUuid(); + m_pendingRequests.append(requestId); + QByteArray 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; @@ -275,170 +277,159 @@ QUuid KeContact::unlockCharger() 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()) { + //Command response has been received, now send the next command + m_deviceBlocked = false; + m_requestTimeoutTimer->stop(); + handleNextCommandInQueue(); - if (sender != m_address) { - //Only process data from the target device - continue; - } - - datagram.resize(socket->pendingDatagramSize()); - socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); - qCDebug(dcKebaKeContact()) << "Data received" << datagram; - - if (m_reachable != true) { - m_reachable = true; - emit reachableChanged(true); - } - - 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 reveiced"; + 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 reveiced"; + 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; + 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")) { - 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(); - 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; - reportTwo.maxCurrentPercentage = data.value("Max curr %").toInt()/10; - reportTwo.currentHardwareLimitation = data.value("Curr HW").toInt()/1000; - reportTwo.currentUser = data.value("Curr user").toInt(); - reportTwo.currentFailsafe = data.value("Curr FS").toInt(); - reportTwo.timeoutFailsafe = 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 (id == 3) { - - ReportThree reportThree; - qCDebug(dcKebaKeContact()) << "Report 3 reveiced"; - 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; - 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); - } - } 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 62c20053..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(const QHostAddress &address, QUdpSocket *udpSocket, QObject *parent = nullptr); + explicit KeContact(const QHostAddress &address, KeContactDataLayer *dataLayer, QObject *parent = nullptr); ~KeContact(); bool init(); @@ -90,15 +92,15 @@ 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 currentFailsafe; //Current preset value for 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. - int setEnergy; //Shows the set energy limit + 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; //Serial number @@ -113,23 +115,23 @@ public: double currentPhase2; //current in A double currentPhase3; //current in A double power; //Current power in W (Real Power). - double powerFactor; //Power factor in 0,1% (cosphi) - double energySession; //Power consumption of the current loading session in 0,1Wh; Reset with new loading session (state = 2). - double energyTotal; //Total power consumption (persistent) without current loading session 0,1Wh; Is summed up after each completed charging session (state = 0). + 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" - int currHW; // maximum charging current of the cable and the charging station setting (equal to report 2)"E + 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; NTP implementation is still under progress)" + 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; hexadecimal; first character is the lowest nibble" - QByteArray rfidClass; // RFID classifier shows the defined color code if the used card is a BMW card (for example “010104” means the white card)" + 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 }; @@ -156,12 +158,12 @@ public: void getReport1XX(int reportNumber = 100); // Command “report 1xx” // Command “currtime” - // Command “output” + QUuid setOutputX2(bool state); // Command “output” private: - int m_port = 7090; + KeContactDataLayer *m_dataLayer; bool m_reachable = false; - QUdpSocket *m_udpSocket = nullptr; + QHostAddress m_address; QByteArrayList m_commandList; bool m_deviceBlocked = false; @@ -171,8 +173,8 @@ private: QList m_pendingRequests; void getReport(int reportNumber); - void sendCommand(const QByteArray &data, const QUuid &requestId); - void sendCommand(const QByteArray &data); + void sendCommand(const QByteArray &command, const QUuid &requestId); + void sendCommand(const QByteArray &command); void handleNextCommandInQueue(); signals: @@ -186,7 +188,7 @@ signals: 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..4d794e04 --- /dev/null +++ b/keba/kecontactdatalayer.cpp @@ -0,0 +1,39 @@ +#include "kecontactdatalayer.h" +#include "extern-plugininfo.h" + +KeContactDataLayer::KeContactDataLayer(QObject *parent) : QObject(parent) +{ + m_udpSocket = new QUdpSocket(this); + connect(m_udpSocket, &QUdpSocket::readyRead, this, &KeContactDataLayer::readPendingDatagrams); +} + +bool KeContactDataLayer::init() +{ + if (!m_udpSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress)) { + qCWarning(dcKebaKeContact()) << "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()) << "Data received" << datagram << senderAddress; + emit datagramReceived(senderAddress, datagram); + } +} diff --git a/keba/kecontactdatalayer.h b/keba/kecontactdatalayer.h new file mode 100644 index 00000000..469a9dfd --- /dev/null +++ b/keba/kecontactdatalayer.h @@ -0,0 +1,27 @@ +#ifndef KECONTACTDATALAYER_H +#define KECONTACTDATALAYER_H + +#include +#include + +class KeContactDataLayer : public QObject +{ + Q_OBJECT +public: + explicit KeContactDataLayer(QObject *parent = nullptr); + 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(); +}; + +#endif // KECONTACTDATALAYER_H From 9e2a2b640d0e43230efda8ed1ef3909bfe66e443 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Fri, 29 Jan 2021 14:39:50 +0100 Subject: [PATCH 8/9] fixed sys enabled state and set max current action --- keba/integrationpluginkeba.cpp | 24 ++++++++++++++++-------- keba/kecontact.cpp | 9 +++++++-- keba/kecontactdatalayer.cpp | 22 ++++++++++++++++++++-- keba/kecontactdatalayer.h | 3 +++ 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/keba/integrationpluginkeba.cpp b/keba/integrationpluginkeba.cpp index 19fda8c1..09efb12c 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -120,9 +120,11 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) if(!m_kebaData){ qCDebug(dcKebaKeContact()) << "Creating new Keba data layer"; - m_kebaData = new KeContactDataLayer; + 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.")); } } @@ -142,19 +144,21 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) 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); + m_kebaDevices.insert(thing->id(), keba); info->finish(Thing::ThingErrorNoError); }); - connect(info, &ThingSetupInfo::aborted, keba, &KeContact::deleteLater); + 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()); }); @@ -351,7 +355,7 @@ void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &repo thing->setStateValue(wallboxError2StateTypeId, reportTwo.error2); thing->setStateValue(wallboxSystemEnabledStateTypeId, reportTwo.enableSys); - thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, reportTwo.maxCurrent); + thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, reportTwo.currentUser); thing->setStateValue(wallboxMaxChargingCurrentPercentStateTypeId, reportTwo.maxCurrentPercentage); thing->setStateValue(wallboxMaxPossibleChargingCurrentStateTypeId, reportTwo.currentHardwareLimitation); @@ -481,10 +485,10 @@ void IntegrationPluginKeba::onBroadcastReceived(KeContact::BroadcastType type, c setDeviceState(thing, KeContact::State(content.toInt())); break; case KeContact::BroadcastTypeMaxCurr: - thing->setStateValue(wallboxMaxChargingCurrentStateTypeId, content.toInt()/1000.00); + //Current preset value via Control pilot in milliampere break; case KeContact::BroadcastTypeEnableSys: - qCDebug(dcKebaKeContact()) << "Broadcast enable sys not implemented"; + thing->setStateValue(wallboxSystemEnabledStateTypeId, (content.toInt() != 0)); break; } } @@ -503,7 +507,7 @@ void IntegrationPluginKeba::executeAction(ThingActionInfo *info) QUuid requestId; if(action.actionTypeId() == wallboxMaxChargingCurrentActionTypeId){ - int milliAmpere = action.param(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).value().toInt(); + int milliAmpere = action.param(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).value().toDouble()*1000; requestId = keba->setMaxAmpere(milliAmpere); } else if(action.actionTypeId() == wallboxPowerActionTypeId){ @@ -516,7 +520,11 @@ void IntegrationPluginKeba::executeAction(ThingActionInfo *info) requestId = keba->setOutputX2(action.param(wallboxOutputX2ActionOutputX2ParamTypeId).value().toBool()); } else if(action.actionTypeId() == wallboxFailsafeModeActionTypeId){ - requestId = keba->setFailsafe(60, 0, false); + int timeout = 0; + if (action.param(wallboxFailsafeModeActionFailsafeModeParamTypeId).value().toBool()) { + timeout = 60; + } + requestId = keba->setFailsafe(timeout, 0, false); } else { qCWarning(dcKebaKeContact()) << "Unhandled ActionTypeId:" << action.actionTypeId(); return info->finish(Thing::ThingErrorActionTypeNotFound); diff --git a/keba/kecontact.cpp b/keba/kecontact.cpp index 8e940424..ec02fac0 100644 --- a/keba/kecontact.cpp +++ b/keba/kecontact.cpp @@ -158,6 +158,10 @@ QUuid KeContact::enableOutput(bool state) 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 @@ -307,7 +311,7 @@ void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray m_requestTimeoutTimer->stop(); handleNextCommandInQueue(); - qCDebug(dcKebaKeContact()) << "Firmware information reveiced"; + qCDebug(dcKebaKeContact()) << "Firmware information received"; QByteArrayList firmware = datagram.split(':'); if (firmware.length() >= 2) { emit deviceInformationReceived(firmware[1]); @@ -382,7 +386,7 @@ void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray } else if (id == 3) { ReportThree reportThree; - qCDebug(dcKebaKeContact()) << "Report 3 reveiced"; + 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; @@ -399,6 +403,7 @@ void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray } 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; diff --git a/keba/kecontactdatalayer.cpp b/keba/kecontactdatalayer.cpp index 4d794e04..2633631c 100644 --- a/keba/kecontactdatalayer.cpp +++ b/keba/kecontactdatalayer.cpp @@ -3,14 +3,22 @@ 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()) << "Cannot bind to port" << m_port; + qCWarning(dcKebaKeContact()) << "KeContactDataLayer: Cannot bind to port" << m_port; return false; } return true; @@ -33,7 +41,17 @@ void KeContactDataLayer::readPendingDatagrams() datagram.resize(socket->pendingDatagramSize()); socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort); - qCDebug(dcKebaKeContact()) << "Data received" << datagram << senderAddress; + 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 index 469a9dfd..ae343ac8 100644 --- a/keba/kecontactdatalayer.h +++ b/keba/kecontactdatalayer.h @@ -9,6 +9,7 @@ class KeContactDataLayer : public QObject Q_OBJECT public: explicit KeContactDataLayer(QObject *parent = nullptr); + ~KeContactDataLayer(); bool init(); void write(const QHostAddress &address, const QByteArray &data); @@ -22,6 +23,8 @@ signals: private slots: void readPendingDatagrams(); + void onSocketError(QAbstractSocket::SocketError error); + void onSocketStateChanged(QAbstractSocket::SocketState socketState); }; #endif // KECONTACTDATALAYER_H From c15bef65ba759ffe163b7bd00465d88552044301 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 15 Feb 2021 16:41:26 +0100 Subject: [PATCH 9/9] changed to evcharger interface --- keba/integrationpluginkeba.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keba/integrationpluginkeba.json b/keba/integrationpluginkeba.json index c47540b7..c0e193e4 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -13,7 +13,7 @@ "name": "wallbox", "displayName": "Keba KeContact", "createMethods": ["discovery", "user"], - "interfaces": ["extendedevcharger", "smartmeterconsumer", "connectable"], + "interfaces": ["evcharger", "smartmeterconsumer", "connectable"], "paramTypes":[ { "id": "730cd3d3-5f0e-4028-a8c2-ced7574f13f3",