moved to async device setup
parent
ac6dcf0765
commit
1d42550609
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Host> &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<KeContact *>(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<KeContact *>(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<KeContact *>(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<KeContact *>(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<KeContact *>(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<QUdpSocket*>(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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
|
||||
#include "integrations/integrationplugin.h"
|
||||
#include "plugintimer.h"
|
||||
#include "kecontact.h"
|
||||
#include "discovery.h"
|
||||
#include "host.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QNetworkReply>
|
||||
|
|
@ -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<QHostAddress, Thing *> m_kebaDevices;
|
||||
QHash<DeviceId, KeContact *> m_kebaDevices;
|
||||
QHash<KeContact *, DeviceSetupInfo *> m_asyncSetup;
|
||||
QHash<QUuid, DeviceActionInfo *> 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
|
||||
|
|
|
|||
|
|
@ -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 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
Loading…
Reference in New Issue