added keba plug-in
This commit is contained in:
parent
bfe6496381
commit
2904f3a782
243
keba/devicepluginkeba.cpp
Normal file
243
keba/devicepluginkeba.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2016 Simon Stuerz <simon.stuerz@guh.io> *
|
||||
* Copyright (C) 2016 Christian Stachowitz *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* Nymea is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* Nymea is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with nymea. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "devicepluginkeba.h"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
#include <QPointer>
|
||||
#include "plugininfo.h"
|
||||
#include <QUdpSocket>
|
||||
|
||||
|
||||
DevicePluginKeba::DevicePluginKeba()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DevicePluginKeba::~DevicePluginKeba()
|
||||
{
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
|
||||
}
|
||||
|
||||
void DevicePluginKeba::init()
|
||||
{
|
||||
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60);
|
||||
connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginKeba::updateData);
|
||||
}
|
||||
|
||||
DeviceManager::DeviceSetupStatus DevicePluginKeba::setupDevice(Device *device)
|
||||
{
|
||||
qCDebug(dcKebaKeContact()) << "Setting up a new device:" << device->name() << device->params();
|
||||
|
||||
if(m_kebaDevices.isEmpty())
|
||||
{
|
||||
m_kebaSocket = new QUdpSocket(this);
|
||||
if (!m_kebaSocket->bind(QHostAddress::AnyIPv4,7090)) {
|
||||
qCWarning(dcKebaKeContact()) << "can't bind to port";
|
||||
delete m_kebaSocket;
|
||||
return DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
connect(m_kebaSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
|
||||
qCDebug(dcKebaKeContact()) << "create keba socket";
|
||||
}
|
||||
QHostAddress address = QHostAddress(device->paramValue(wallboxIpParamTypeId).toString());
|
||||
|
||||
//Check if the IP is empty
|
||||
if(address.isNull()){
|
||||
return DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
// check if IP is already added to another keba device
|
||||
if(m_kebaDevices.keys().contains(address)){
|
||||
return DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
m_kebaDevices.insert(address, device);
|
||||
return DeviceManager::DeviceSetupStatusSuccess;
|
||||
}
|
||||
|
||||
void DevicePluginKeba::postSetupDevice(Device *device)
|
||||
{
|
||||
qCDebug(dcKebaKeContact()) << "Post setup" << device->name();
|
||||
QByteArray datagram;
|
||||
datagram.append("report 2");
|
||||
m_kebaSocket->writeDatagram(datagram.data(),datagram.size(), QHostAddress(device->paramValue(wallboxIpParamTypeId).toString()) , 7090);
|
||||
}
|
||||
|
||||
void DevicePluginKeba::deviceRemoved(Device *device)
|
||||
{
|
||||
// Remove devices
|
||||
QHostAddress address = m_kebaDevices.key(device);
|
||||
m_kebaDevices.remove(address);
|
||||
|
||||
if(m_kebaDevices.isEmpty()){
|
||||
m_kebaSocket->close();
|
||||
m_kebaSocket->deleteLater();
|
||||
qCDebug(dcKebaKeContact()) << "clear socket";
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginKeba::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 device
|
||||
m_kebaDevices.value(address)->setStateValue(wallboxReachableStateTypeId,false);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceManager::DeviceError DevicePluginKeba::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
qCDebug(dcKebaKeContact()) << "Execute action" << device->name() << action.actionTypeId().toString();
|
||||
|
||||
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(wallboxMaxCurrentStateParamTypeId).value().toString();
|
||||
QByteArray datagram;
|
||||
datagram.append("curr " + QVariant(action.param(wallboxMaxCurrentStateParamTypeId).value().toInt()*1000).toString());
|
||||
qCDebug(dcKebaKeContact()) << "datagram : " << datagram;
|
||||
m_kebaSocket->writeDatagram(datagram.data(),datagram.size(), QHostAddress(device->paramValue(wallboxIpParamTypeId).toString()) , 7090);
|
||||
}
|
||||
else if(action.actionTypeId() == wallboxOutEnableActionTypeId){
|
||||
// Print information that we are executing now the update action
|
||||
qCDebug(dcKebaKeContact()) << "output enable : " << action.param(wallboxOutEnableStateParamTypeId).value().toString();
|
||||
QByteArray datagram;
|
||||
if(action.param(wallboxOutEnableStateParamTypeId).value().toBool()){
|
||||
datagram.append("ena 1");
|
||||
}
|
||||
else{
|
||||
datagram.append("ena 0");
|
||||
}
|
||||
qCDebug(dcKebaKeContact()) << "datagram : " << datagram;
|
||||
m_kebaSocket->writeDatagram(datagram.data(),datagram.size(), QHostAddress(device->paramValue(wallboxIpParamTypeId).toString()) , 7090);
|
||||
}
|
||||
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
return DeviceManager::DeviceErrorDeviceClassNotFound;
|
||||
}
|
||||
|
||||
void DevicePluginKeba::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 << "device: " << 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(wallboxIpParamTypeId).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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
63
keba/devicepluginkeba.h
Normal file
63
keba/devicepluginkeba.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2016 Simon Stuerz <simon.stuerz@guh.io> *
|
||||
* Copyright (C) 2016 Christian Stachowitz *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* Nymea is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* Nymea is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with nymea. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef DEVICEPLUGINKEBA_H
|
||||
#define DEVICEPLUGINKEBA_H
|
||||
|
||||
#include "plugin/deviceplugin.h"
|
||||
#include "devicemanager.h"
|
||||
#include "plugintimer.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QNetworkReply>
|
||||
#include <QUdpSocket>
|
||||
|
||||
class DevicePluginKeba : public DevicePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginkeba.json")
|
||||
Q_INTERFACES(DevicePlugin)
|
||||
|
||||
public:
|
||||
explicit DevicePluginKeba();
|
||||
~DevicePluginKeba();
|
||||
|
||||
void init() override;
|
||||
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
|
||||
void postSetupDevice(Device* device) override;
|
||||
void deviceRemoved(Device* device) override;
|
||||
|
||||
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
void updateData();
|
||||
|
||||
private:
|
||||
PluginTimer *m_pluginTimer = nullptr;
|
||||
QHash<QHostAddress, Device *> m_kebaDevices;
|
||||
QUdpSocket *m_kebaSocket;
|
||||
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
|
||||
};
|
||||
|
||||
#endif // DEVICEPLUGINKEBA_H
|
||||
103
keba/devicepluginkeba.json
Normal file
103
keba/devicepluginkeba.json
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
"displayName": "Keba KeContact",
|
||||
"name": "KebaKeContact",
|
||||
"id": "9142b09f-30a9-43d0-9ede-2f8debe075ac",
|
||||
"vendors": [
|
||||
{
|
||||
"id": "f7cda40b-829a-4675-abaa-485697430f5f",
|
||||
"displayName": "Keba",
|
||||
"name": "keba",
|
||||
"deviceClasses": [
|
||||
{
|
||||
"id": "900dacec-cae7-4a37-95ba-501846368ea2",
|
||||
"name": "wallbox",
|
||||
"displayName": "Keba KeContact P30",
|
||||
"deviceIcon": "Energy",
|
||||
"createMethods": ["user"],
|
||||
"interfaces": [],
|
||||
"criticalStateTypeId": "b1a574a6-46b6-44ea-a0bb-9b4de3198967",
|
||||
"basicTags": [
|
||||
"Energy"
|
||||
],
|
||||
"paramTypes":[
|
||||
{
|
||||
"id": "730cd3d3-5f0e-4028-a8c2-ced7574f13f3",
|
||||
"name": "ip",
|
||||
"displayName": "IP Address",
|
||||
"type": "QString",
|
||||
"inputType": "IPv4Address",
|
||||
"defaultValue":"0.0.0.0"
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "539e5602-6dd9-465d-9705-3bb59bcf8982",
|
||||
"name": "activity",
|
||||
"displayName": "Activity",
|
||||
"displayNameEvent": "Activity changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "-"
|
||||
},
|
||||
{
|
||||
"id": "3b4d29f3-3101-47ad-90fd-269b6348783b",
|
||||
"name": "plugState",
|
||||
"displayName": "Plug State",
|
||||
"displayNameEvent": "Plug State changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "-"
|
||||
},
|
||||
{
|
||||
"id": "a29c1748-fe97-4830-a56e-e1cc4e618385",
|
||||
"name": "current",
|
||||
"displayName": "Current",
|
||||
"displayNameEvent": "Current changed",
|
||||
"type": "int",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"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,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "e8f069ca-7fa7-4568-8d4c-165f6d048720",
|
||||
"name": "power",
|
||||
"displayName": "Power",
|
||||
"displayNameEvent": "Power changed",
|
||||
"type": "int",
|
||||
"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": "b1a574a6-46b6-44ea-a0bb-9b4de3198967",
|
||||
"name": "reachable",
|
||||
"displayName": "reachable",
|
||||
"displayNameEvent": "Device Reachable changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
9
keba/keba.pro
Normal file
9
keba/keba.pro
Normal file
@ -0,0 +1,9 @@
|
||||
include(../plugins.pri)
|
||||
|
||||
TARGET = $$qtLibraryTarget(nymea_devicepluginkeba)
|
||||
|
||||
SOURCES += \
|
||||
devicepluginkeba.cpp \
|
||||
|
||||
HEADERS += \
|
||||
devicepluginkeba.h \
|
||||
@ -33,6 +33,7 @@ PLUGIN_DIRS = \
|
||||
gpio \
|
||||
snapd \
|
||||
simulation \
|
||||
keba \
|
||||
|
||||
|
||||
CONFIG+=all
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user