diff --git a/keba/README.md b/keba/README.md index f436ea21..d134350f 100644 --- a/keba/README.md +++ b/keba/README.md @@ -1 +1,12 @@ # Keba Wallbox + +This plugin allows to control Keba KeContact EV-Charging stations. + +* Enable/disable the charging stations +* Set maximum charging current +* Get all informations about the power supply +* Print messages on the display + +nymea and the wallbox are required to be in the same network and +port 7090 must not be blocked by a firewall or router. + diff --git a/keba/integrationpluginkeba.cpp b/keba/integrationpluginkeba.cpp index 5fb231b4..4afd45d0 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -42,15 +42,45 @@ IntegrationPluginKeba::IntegrationPluginKeba() } -IntegrationPluginKeba::~IntegrationPluginKeba() +void DevicePluginKeba::init() { - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); + } -void IntegrationPluginKeba::init() +void DevicePluginKeba::discoverDevices(DeviceDiscoveryInfo *info) { - m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60); - connect(m_pluginTimer, &PluginTimer::timeout, this, &IntegrationPluginKeba::updateData); + if (info->deviceClassId() == wallboxDeviceClassId) { + Discovery *discovery = new Discovery(this); + discovery->discoverHosts(25); + + // clean up discovery object when this discovery info is deleted + connect(info, &DeviceDiscoveryInfo::destroyed, discovery, &Discovery::deleteLater); + + 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; + + DeviceDescriptor descriptor(wallboxDeviceClassId, host.hostName().isEmpty() ? host.address() : host.hostName(), host.address() + " (" + host.macAddress() + ")"); + + foreach (Device *existingDevice, myDevices()) { + if (existingDevice->paramValue(wallboxDeviceMacAddressParamTypeId).toString() == host.macAddress()) { + descriptor.setDeviceId(existingDevice->id()); + break; + } + } + ParamList params; + params << Param(wallboxDeviceMacAddressParamTypeId, host.macAddress()); + params << Param(wallboxDeviceIpAddressParamTypeId, host.address()); + descriptor.setParams(params); + info->addDeviceDescriptor(descriptor); + } + info->finish(Device::DeviceErrorNoError); + }); + } else { + info->finish(Device::DeviceErrorDeviceClassNotFound); + } } void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) @@ -59,202 +89,233 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) qCDebug(dcKebaKeContact()) << "Setting up a new thing:" << thing->name() << thing->params(); - if(m_kebaDevices.isEmpty()) { - m_kebaSocket = new QUdpSocket(this); - if (!m_kebaSocket->bind(QHostAddress::AnyIPv4, 7090)) { + if (device->deviceClassId() == wallboxDeviceClassId) { + + QHostAddress address = QHostAddress(device->paramValue(wallboxDeviceIpAddressParamTypeId).toString()); + KeContact *keba = new KeContact(address, this); + connect(keba, &KeContact::connectionChanged, this, &DevicePluginKeba::onConnectionChanged); + connect(keba, &KeContact::commandExecuted, this, &DevicePluginKeba::onCommandExecuted); + connect(keba, &KeContact::reportOneReceived, this, &DevicePluginKeba::onReportOneReceived); + connect(keba, &KeContact::reportTwoReceived, this, &DevicePluginKeba::onReportTwoReceived); + connect(keba, &KeContact::reportThreeReceived, this, &DevicePluginKeba::onReportThreeReceived); + if (!keba->init()){ qCWarning(dcKebaKeContact()) << "Cannot bind to port" << 7090; - delete m_kebaSocket; - //: Error setting up thing - return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port.")); + keba->deleteLater(); + return info->finish(Device::DeviceErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port.")); } - connect(m_kebaSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); - qCDebug(dcKebaKeContact()) << "Create keba socket"; + + DeviceId id = device->id(); + m_kebaDevices.insert(id, keba); + m_asyncSetup.insert(keba, info); + keba->getReport1(); + connect(info, &DeviceSetupInfo::aborted, this, [id, keba, this]{ + m_asyncSetup.remove(keba); + m_kebaDevices.remove(id); + keba->deleteLater(); + }); + } else { + info->finish(Device::DeviceErrorDeviceClassNotFound); } - - QHostAddress address = QHostAddress(thing->paramValue(wallboxThingIpParamTypeId).toString()); - - //Check if the IP is empty - if (address.isNull()) { - delete m_kebaSocket; - //: Error setting up thing - return info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The provided IP adress is not valid.")); - } - - // check if IP is already added to another keba thing - if(m_kebaDevices.keys().contains(address)){ - //: Error setting up thing - return info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("Device with IP adress %1 is already added in the system.")); - } - - m_kebaDevices.insert(address, thing); - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginKeba::postSetupThing(Thing *thing) { - qCDebug(dcKebaKeContact()) << "Post setup" << thing->name(); - QByteArray datagram; - datagram.append("report 2"); - m_kebaSocket->writeDatagram(datagram.data(), datagram.size(), QHostAddress(thing->paramValue(wallboxThingIpParamTypeId).toString()), 7090); + qCDebug(dcKebaKeContact()) << "Post setup" << device->name(); + KeContact *keba = m_kebaDevices.value(device->id()); + if (!keba) { + return; + } + keba->getReport2(); + keba->getReport3(); + + if (!m_pluginTimer) { + m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60); + connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginKeba::updateData); + } } -void IntegrationPluginKeba::thingRemoved(Thing *thing) -{ - // Remove devices - QHostAddress address = m_kebaDevices.key(thing); - m_kebaDevices.remove(address); +void DevicePluginKeba::deviceRemoved(Device *device) +{ + if (device->deviceClassId() == wallboxDeviceClassId) { + m_kebaDevices.remove(device->id()); + } if(m_kebaDevices.isEmpty()){ m_kebaSocket->close(); m_kebaSocket->deleteLater(); qCDebug(dcKebaKeContact()) << "clear socket"; } + + if (myDevices().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 (QHostAddress address, m_kebaDevices.keys()) { - QByteArray datagram; - datagram.append("report 2"); - qCDebug(dcKebaKeContact()) << "datagram : " << datagram; - m_kebaSocket->writeDatagram(datagram.data(),datagram.size(), address , 7090); - //set reachable false until successful reply from thing - m_kebaDevices.value(address)->setStateValue(wallboxReachableStateTypeId,false); + foreach (KeContact *keba, m_kebaDevices) { + keba->getReport2(); + keba->getReport3(); } } -void IntegrationPluginKeba::executeAction(ThingActionInfo *info) +void DevicePluginKeba::onConnectionChanged(bool status) { - Thing *thing = info->thing(); + KeContact *keba = static_cast(sender()); + Device *device = myDevices().findById(m_kebaDevices.key(keba)); + if (!device) { + qCWarning(dcKebaKeContact()) << "On connection changed: missing device object"; + return; + } + device->setStateValue(wallboxConnectedStateTypeId, status); + if (!status) { + //TODO start rediscovery + } +} + +void DevicePluginKeba::onCommandExecuted(QUuid requestId, bool success) +{ + if (m_asyncActions.contains(requestId)) { + KeContact *keba = static_cast(sender()); + Device *device = myDevices().findById(m_kebaDevices.key(keba)); + if (!device) { + qCWarning(dcKebaKeContact()) << "On command executed: missing device object"; + return; + } + DeviceActionInfo *info = m_asyncActions.take(requestId); + if (success) { + info->finish(Device::DeviceErrorNoError); + } else { + info->finish(Device::DeviceErrorHardwareFailure); + } + } +} + +void DevicePluginKeba::onReportOneReceived(const KeContact::ReportOne &reportOne) +{ + KeContact *keba = static_cast(sender()); + if (m_asyncSetup.contains(keba)) { + DeviceSetupInfo *info = m_asyncSetup.value(keba); + info->finish(Device::DeviceErrorNoError); + + } else { + qCDebug(dcKebaKeContact()) << "Report one received without an associated async setup"; + + Device *device = myDevices().findById(m_kebaDevices.key(keba)); + if (!device) { + qCWarning(dcKebaKeContact()) << "Could not set serialnumber because of missing device object"; + return; + } + device->setParamValue(wallboxDeviceSerialnumberParamTypeId, reportOne.serialNumber); + } +} + +void DevicePluginKeba::onReportTwoReceived(const KeContact::ReportTwo &reportTwo) +{ + KeContact *keba = static_cast(sender()); + Device *device = myDevices().findById(m_kebaDevices.key(keba)); + if (!device) + return; + + switch (reportTwo.state) { + case KeContact::State::Starting: + device->setStateValue(wallboxActivityStateTypeId, QT_TR_NOOP("Starting")); + break; + case KeContact::State::NotReady: + device->setStateValue(wallboxActivityStateTypeId, QT_TR_NOOP("Not ready for charging")); + break; + case KeContact::State::Ready: + device->setStateValue(wallboxActivityStateTypeId, QT_TR_NOOP("Ready for charging")); + break; + case KeContact::State::Charging: + device->setStateValue(wallboxActivityStateTypeId, QT_TR_NOOP("Charging")); + break; + case KeContact::State::Error: + device->setStateValue(wallboxActivityStateTypeId, QT_TR_NOOP("Erro")); + break; + case KeContact::State::AuthorizationRejected: + device->setStateValue(wallboxActivityStateTypeId, QT_TR_NOOP("Authorization rejected")); + break; + } + + switch (reportTwo.plugState) { + case KeContact::PlugState::Unplugged: + device->setStateValue(wallboxPlugStateStateTypeId, QT_TR_NOOP("Unplugged")); + break; + case KeContact::PlugState::PluggedOnChargingStation: + device->setStateValue(wallboxPlugStateStateTypeId, QT_TR_NOOP("Plugged in charging station")); + break; + case KeContact::PlugState::PluggedOnChargingStationAndPluggedOnEV: + device->setStateValue(wallboxPlugStateStateTypeId, QT_TR_NOOP("Plugged in on EV")); + break; + case KeContact::PlugState::PluggedOnChargingStationAndPlugLocked: + device->setStateValue(wallboxPlugStateStateTypeId, QT_TR_NOOP("Plugged in and locked")); + break; + case KeContact::PlugState::PluggedOnChargingStationAndPlugLockedAndPluggedOnEV: + device->setStateValue(wallboxPlugStateStateTypeId, QT_TR_NOOP("Plugged in on EV and locked")); + break; + } +} + +void DevicePluginKeba::onReportThreeReceived(const KeContact::ReportThree &reportThree) +{ + KeContact *keba = static_cast(sender()); + Device *device = myDevices().findById(m_kebaDevices.key(keba)); + if (!device) + return; + + device->setStateValue(wallboxI1EventTypeId, reportThree.CurrentPhase1); + device->setStateValue(wallboxI2EventTypeId, reportThree.CurrentPhase2); + device->setStateValue(wallboxI3EventTypeId, reportThree.CurrentPhase3); + device->setStateValue(wallboxU1EventTypeId, reportThree.VoltagePhase1); + device->setStateValue(wallboxU2EventTypeId, reportThree.VoltagePhase2); + device->setStateValue(wallboxU3EventTypeId, reportThree.VoltagePhase3); + device->setStateValue(wallboxPStateTypeId, reportThree.Power); + device->setStateValue(wallboxEPStateTypeId, reportThree.EnergySession); + device->setStateValue(wallboxTotalEnergyConsumedStateTypeId, reportThree.EnergyTotal); +} + +void DevicePluginKeba::executeAction(DeviceActionInfo *info) +{ + Device *device = info->device(); Action action = info->action(); - qCDebug(dcKebaKeContact()) << "Execute action" << thing->name() << action.actionTypeId().toString(); + qCDebug(dcKebaKeContact()) << "Execute action" << device->name() << action.actionTypeId().toString(); - if (thing->thingClassId() == wallboxThingClassId) { + if (device->deviceClassId() == wallboxDeviceClassId) { // Print information that we are executing now the update action qCDebug(dcKebaKeContact()) << "Execute update action" << action.id(); - - if(action.actionTypeId() == wallboxMaxCurrentActionTypeId){ - // Print information that we are executing now the update action - qCDebug(dcKebaKeContact()) << "Update max current to : " << action.param(wallboxMaxCurrentActionMaxCurrentParamTypeId).value().toString(); - QByteArray datagram; - datagram.append("curr " + QVariant(action.param(wallboxMaxCurrentActionMaxCurrentParamTypeId).value().toInt()*1000).toString()); - qCDebug(dcKebaKeContact()) << "Datagram : " << datagram; - m_kebaSocket->writeDatagram(datagram.data(),datagram.size(), QHostAddress(thing->paramValue(wallboxThingIpParamTypeId).toString()) , 7090); - } - else if(action.actionTypeId() == wallboxOutEnableActionTypeId){ - // Print information that we are executing now the update action - qCDebug(dcKebaKeContact()) << "output enable : " << action.param(wallboxOutEnableActionOutEnableParamTypeId).value().toString(); - QByteArray datagram; - if(action.param(wallboxOutEnableActionOutEnableParamTypeId).value().toBool()){ - datagram.append("ena 1"); - } - else{ - datagram.append("ena 0"); - } - qCDebug(dcKebaKeContact()) << "Datagram : " << datagram; - m_kebaSocket->writeDatagram(datagram.data(),datagram.size(), QHostAddress(thing->paramValue(wallboxThingIpParamTypeId).toString()) , 7090); + KeContact *keba = m_kebaDevices.value(device->id()); + if (!keba) { + qCWarning(dcKebaKeContact()) << "Device not properly initialized, Keba object missing"; + return info->finish(Device::DeviceErrorHardwareNotAvailable); } - return info->finish(Thing::ThingErrorNoError); + if(action.actionTypeId() == wallboxMaxChargingCurrentActionTypeId){ + int ampere = action.param(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).value().toInt()*1000; + QUuid requestId = keba->setMaxAmpere(ampere); + m_asyncActions.insert(requestId, info); + connect(info, &DeviceActionInfo::aborted, this, [requestId, this]{m_asyncActions.remove(requestId);}); + + } else if(action.actionTypeId() == wallboxPowerActionTypeId){ + QUuid requestId = keba->enableOutput(action.param(wallboxPowerActionTypeId).value().toBool()); + m_asyncActions.insert(requestId, info); + connect(info, &DeviceActionInfo::aborted, this, [requestId, this]{m_asyncActions.remove(requestId);}); + + } else if(action.actionTypeId() == wallboxDisplayActionTypeId){ + QUuid requestId = keba->displayMessage(action.param(wallboxDisplayActionMessageParamTypeId).value().toByteArray()); + m_asyncActions.insert(requestId, info); + connect(info, &DeviceActionInfo::aborted, this, [requestId, this]{m_asyncActions.remove(requestId);}); + + } else { + qCWarning(dcKebaKeContact()) << "Unhandled ActionTypeId:" << action.actionTypeId(); + info->finish(Device::DeviceErrorActionTypeNotFound); + } + } else { + qCWarning(dcKebaKeContact()) << "Execute action, unhandled device class" << device->deviceClass(); + info->finish(Device::DeviceErrorDeviceClassNotFound); } - - info->finish(Thing::ThingErrorThingClassNotFound); -} - -void IntegrationPluginKeba::readPendingDatagrams() -{ - QUdpSocket *socket= qobject_cast(sender()); - - QByteArray datagram; - QHostAddress sender; - quint16 senderPort; - - while (socket->hasPendingDatagrams()) { - datagram.resize(socket->pendingDatagramSize()); - socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); - - qCDebug(dcKebaKeContact()) << " got command from" << sender.toString() << senderPort; - } - - if(!m_kebaDevices.keys().contains(sender)){ - qCDebug(dcKebaKeContact()) << " unknown sender:" << sender.toString() << senderPort; - return; - } - - // 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(); - return; - } - - // print the fetched data in json format to stdout - //qCDebug(dcKebaKeContact()) << qUtf8Printable(jsonDoc.toJson()); - - QVariantMap data = jsonDoc.toVariant().toMap(); - - qCDebug(dcKebaKeContact()) << "IP" << sender << "thing: " << m_kebaDevices.value(sender); - - if(data.contains("ID")){ - // check if ID matches report 2 or report 3 - if(data.value("ID").toString() == "2"){ - //set reachable - m_kebaDevices.value(sender)->setStateValue(wallboxReachableStateTypeId,true); - //activity state - if(data.value("State").toString() == "0"){ - m_kebaDevices.value(sender)->setStateValue(wallboxActivityStateTypeId,"starting"); - } - else if(data.value("State").toString() == "1"){ - m_kebaDevices.value(sender)->setStateValue(wallboxActivityStateTypeId,"not ready for charging"); - } - else if(data.value("State").toString() == "2"){ - m_kebaDevices.value(sender)->setStateValue(wallboxActivityStateTypeId,"ready for charging"); - } - else if(data.value("State").toString() == "3"){ - m_kebaDevices.value(sender)->setStateValue(wallboxActivityStateTypeId,"charging"); - } - else if(data.value("State").toString() == "4"){ - m_kebaDevices.value(sender)->setStateValue(wallboxActivityStateTypeId,"error"); - } - else if(data.value("State").toString() == "5"){ - m_kebaDevices.value(sender)->setStateValue(wallboxActivityStateTypeId,"authorization rejected"); - } - // plug state - if(data.value("Plug").toString() == "0"){ - m_kebaDevices.value(sender)->setStateValue(wallboxPlugStateStateTypeId,"unplugged"); - } - else if(data.value("Plug").toString() == "1"){ - m_kebaDevices.value(sender)->setStateValue(wallboxPlugStateStateTypeId,"plugged on charging station"); - } - else if(data.value("Plug").toString() == "3"){ - m_kebaDevices.value(sender)->setStateValue(wallboxPlugStateStateTypeId,"locked plug on charging station"); - } - else if(data.value("Plug").toString() == "5"){ - m_kebaDevices.value(sender)->setStateValue(wallboxPlugStateStateTypeId,"plugged on charging station and vehicle"); - } - else if(data.value("Plug").toString() == "7"){ - m_kebaDevices.value(sender)->setStateValue(wallboxPlugStateStateTypeId,"locked plug on charging station and vehicle"); - } - //maximum current setting - m_kebaDevices.value(sender)->setStateValue(wallboxMaxCurrentStateTypeId,data.value("Curr user").toInt()/1000); - //output setting - m_kebaDevices.value(sender)->setStateValue(wallboxOutEnableStateTypeId,data.value("Enable user").toBool()); - - //request next report - QByteArray datagram; - datagram.append("report 3"); - qCDebug(dcKebaKeContact()) << "datagram : " << datagram; - socket->writeDatagram(datagram.data(),datagram.size(), QHostAddress(m_kebaDevices.value(sender)->paramValue(wallboxThingIpParamTypeId).toString()) , 7090); - } - else if(data.value("ID").toString() == "3"){ - //power of current charging session - m_kebaDevices.value(sender)->setStateValue(wallboxPowerStateTypeId,data.value("E pres").toInt() / 1000); - //current phase 1 - m_kebaDevices.value(sender)->setStateValue(wallboxCurrentStateTypeId,data.value("I1").toInt() * 1000); - } - } - } diff --git a/keba/integrationpluginkeba.h b/keba/integrationpluginkeba.h index 768b916d..2ecf7da8 100644 --- a/keba/integrationpluginkeba.h +++ b/keba/integrationpluginkeba.h @@ -33,6 +33,9 @@ #include "integrations/integrationplugin.h" #include "plugintimer.h" +#include "kecontact.h" +#include "discovery.h" +#include "host.h" #include #include @@ -46,11 +49,12 @@ class IntegrationPluginKeba : public IntegrationPlugin Q_INTERFACES(IntegrationPlugin) public: - explicit IntegrationPluginKeba(); - ~IntegrationPluginKeba(); + explicit DevicePluginKeba(); void init() override; - void setupThing(ThingSetupInfo *info) override; + + void discoverDevices(DeviceDiscoveryInfo *info) override; + void setupDevice(DeviceSetupInfo *info) override; void postSetupThing(Thing* thing) override; void thingRemoved(Thing* thing) override; @@ -60,12 +64,17 @@ public: private: PluginTimer *m_pluginTimer = nullptr; - QHash m_kebaDevices; + QHash m_kebaDevices; + QHash m_asyncSetup; + QHash m_asyncActions; QUdpSocket *m_kebaSocket; private slots: - void readPendingDatagrams(); - + 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); }; #endif // INTEGRATIONPLUGINKEBA_H diff --git a/keba/integrationpluginkeba.json b/keba/integrationpluginkeba.json index f602c29a..fc1b9a20 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -11,20 +11,54 @@ { "id": "900dacec-cae7-4a37-95ba-501846368ea2", "name": "wallbox", - "displayName": "Keba KeContact P30", - "createMethods": ["user"], - "interfaces": [], + "displayName": "Keba KeContact", + "createMethods": ["discovery", "user"], + "interfaces": ["extendedevcharger", "smartmeterconsumer", "connectable"], "paramTypes":[ { "id": "730cd3d3-5f0e-4028-a8c2-ced7574f13f3", - "name": "ip", - "displayName": "IP Address", + "name": "ipAddress", + "displayName": "IPv4 Address", "type": "QString", "inputType": "IPv4Address", "defaultValue":"0.0.0.0" + }, + { + "id": "c2df921d-ff8b-411c-9b1d-04a437d7dfa6", + "name": "macAddress", + "displayName": "MAC Address", + "type": "QString", + "inputType": "TextLine", + "defaultValue":"" + }, + { + "id": "1a600fb6-08b2-4155-a4ad-ceca1d4fa7e1", + "name": "serialnumber", + "displayName": "Serialnumber", + "type": "QString", + "inputType": "TextLine", + "defaultValue":"" } ], "stateTypes": [ + { + "id": "ce813458-d7d8-4f40-9648-dba4c41e92f0", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connection changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "83ed0774-2a91-434d-b03c-d920d02f2981", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Power changed", + "displayNameAction": "Set Power", + "type": "bool", + "writable": true, + "defaultValue": false + }, { "id": "539e5602-6dd9-465d-9705-3bb59bcf8982", "name": "activity", @@ -52,43 +86,124 @@ }, { "id": "593656f0-babf-4308-8767-68f34e10fb15", - "name": "maxCurrent", - "displayName": "maximal Current", - "displayNameEvent": "Maximal Current changed", - "displayNameAction": "Set maximal current", - "type": "int", - "unit": "Ampere", - "defaultValue": 6, - "minValue": 6, - "maxValue": 63, + "name": "maxChargingCurrent", + "displayName": "Maximal charging current", + "displayNameEvent": "Maximal charging current changed", + "displayNameAction": "Set maximal charging current", + "type": "uint", + "unit": "MilliAmpere", + "defaultValue": 6000, + "minValue": 6000, + "maxValue": 63000, "writable": true }, - { - "id": "e8f069ca-7fa7-4568-8d4c-165f6d048720", - "name": "power", - "displayName": "Power", - "displayNameEvent": "Power changed", + { + "id": "3c7b83a0-0e42-47bf-9788-dde6aab5ceea", + "name": "maxChargingCurrentPercent", + "displayName": "Maximal charging current in Percent", + "displayNameEvent": "Maximal charging current percentage changed", + "type": "uint", + "unit": "Percentage", + "defaultValue": 100, + "minValue": 0, + "maxValue": 100 + }, + { + "id": "4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9", + "name": "U1", + "displayName": "Voltage phase 1", + "displayNameEvent": "Voltage phase 1 changed", "type": "int", + "unit": "Volt", + "defaultValue": 0 + }, + { + "id": "c8344ca5-21ac-4cd1-8f4b-e5ed202c5862", + "name": "U2", + "displayName": "Voltage Phase 2", + "displayNameEvent": "Voltage phase 2 changed", + "type": "int", + "unit": "Volt", + "defaultValue": 0 + }, + { + "id": "5f01e86c-0943-4849-a01a-db441916ebd5", + "name": "U3", + "displayName": "Voltage Phase 3", + "displayNameEvent": "Voltage phase 3 changed", + "type": "int", + "unit": "Volt", + "defaultValue": 0 + }, + { + "id": "31ec17b0-11e3-4332-92b0-fea821cf024f", + "name": "I1", + "displayName": "Current Phase 1", + "displayNameEvent": "Current phase 1 changed", + "type": "int", + "unit": "MilliAmpere", + "defaultValue": 0 + }, + { + "id": "cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97", + "name": "I2", + "displayName": "Current Phase 2", + "displayNameEvent": "Current phase 2 changed", + "type": "int", + "unit": "MilliAmpere", + "defaultValue": 0 + }, + { + "id": "da838dc8-85f0-4e55-b4b5-cb93a43b373d", + "name": "I3", + "displayName": "Current Phase 3", + "displayNameEvent": "Current phase 3 changed", + "type": "int", + "unit": "MilliAmpere", + "defaultValue": 0 + }, + { + "id": "7af9e93b-099d-4d9d-a480-9c0f66aecd8b", + "name": "P", + "displayName": "Power consumption", + "displayNameEvent": "Power consumtion changed", + "type": "int", + "unit": "MilliWatt", + "defaultValue": 0 + }, + { + "id": "8e277efe-21ef-4536-bfc0-901b32d44d7c", + "name": "EP", + "displayName": "Present energy", + "displayNameEvent": "Present energy changed", + "type": "double", "unit": "KiloWattHour", "defaultValue": 0 }, { - "id": "0cd5396a-bc41-4c8f-b037-db10991a76c7", - "name": "outEnable", - "displayName": "Output", - "displayNameEvent": "Output Enable changed", - "displayNameAction": "Set Output", - "type": "bool", - "defaultValue": false, - "writable": true - }, + "id": "41e179b3-29a2-43ec-b537-023a527081e8", + "name": "totalEnergyConsumed", + "displayName": "Total energy consumed", + "displayNameEvent": "Total energy consumption changed", + "type": "double", + "unit": "KiloWattHour", + "defaultValue": 0 + } + ], + "actionTypes": [ { - "id": "b1a574a6-46b6-44ea-a0bb-9b4de3198967", - "name": "reachable", - "displayName": "reachable", - "displayNameEvent": "Device Reachable changed", - "type": "bool", - "defaultValue": false + "id": "158b1a8f-fde9-4191-bf42-4ece5fe582e6", + "name": "display", + "displayName": "Display", + "paramTypes": [ + { + "id": "4e69a761-f4f1-42d0-83db-380894a86ebc", + "name": "message", + "displayName": "Display message", + "type": "QString", + "defaultValue": "" + } + ] } ] } @@ -96,3 +211,4 @@ } ] } + diff --git a/keba/keba.pro b/keba/keba.pro index f2871341..daa5e418 100644 --- a/keba/keba.pro +++ b/keba/keba.pro @@ -5,7 +5,13 @@ QT += network TARGET = $$qtLibraryTarget(nymea_integrationpluginkeba) SOURCES += \ - integrationpluginkeba.cpp \ + devicepluginkeba.cpp \ + kecontact.cpp \ + discovery.cpp \ + host.cpp \ HEADERS += \ - integrationpluginkeba.h \ + devicepluginkeba.h \ + kecontact.h \ + discovery.h \ + host.h \