added auto rediscovery
This commit is contained in:
parent
cdff51655f
commit
0803221e68
@ -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
|
||||
|
||||
|
||||
@ -42,20 +42,45 @@ IntegrationPluginKeba::IntegrationPluginKeba()
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::init()
|
||||
{
|
||||
m_discovery = new Discovery(this);
|
||||
connect(m_discovery, &Discovery::finished, this, [this](const QList<Host> &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<Host> &hosts) {
|
||||
|
||||
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;
|
||||
|
||||
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,39 +156,24 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing)
|
||||
qCDebug(dcKebaKeContact()) << "Post setup" << thing->name();
|
||||
KeContact *keba = m_kebaDevices.value(thing->id());
|
||||
if (!keba) {
|
||||
return;
|
||||
}
|
||||
keba->getReport2();
|
||||
keba->getReport3();
|
||||
|
||||
if (!m_pluginTimer) {
|
||||
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60);
|
||||
connect(m_pluginTimer, &PluginTimer::timeout, this, &IntegrationPluginKeba::updateData);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::thingRemoved(Thing *thing)
|
||||
{
|
||||
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) {
|
||||
qCWarning(dcKebaKeContact()) << "No Keba connection found for this thing";
|
||||
} else {
|
||||
keba->getReport2();
|
||||
keba->getReport3();
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
@ -163,6 +186,37 @@ void IntegrationPluginKeba::updateData()
|
||||
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()) {
|
||||
qCDebug(dcKebaKeContact()) << "Stopping plugin timers";
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_reconnectTimer);
|
||||
m_reconnectTimer = nullptr;
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_updateTimer);
|
||||
m_updateTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::setDeviceState(Thing *thing, KeContact::State state)
|
||||
@ -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<KeContact *>(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<KeContact *>(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<KeContact *>(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()));
|
||||
|
||||
@ -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<ThingId, KeContact *> m_kebaDevices;
|
||||
QHash<KeContact *, ThingSetupInfo *> m_asyncSetup;
|
||||
QHash<QUuid, ThingActionInfo *> m_asyncActions;
|
||||
|
||||
@ -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": [
|
||||
|
||||
@ -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<int>(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")){
|
||||
@ -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();
|
||||
|
||||
@ -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<QUuid> 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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user