powersync-plugins/keba/devicepluginkeba.cpp

244 lines
11 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016-2018 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);
}
Device::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()) << "Cannot bind to port" << 7090;
delete m_kebaSocket;
return Device::DeviceSetupStatusFailure;
}
connect(m_kebaSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
qCDebug(dcKebaKeContact()) << "Create keba socket";
}
QHostAddress address = QHostAddress(device->paramValue(wallboxDeviceIpParamTypeId).toString());
//Check if the IP is empty
if (address.isNull()) {
delete m_kebaSocket;
return Device::DeviceSetupStatusFailure;
}
// check if IP is already added to another keba device
if(m_kebaDevices.keys().contains(address)){
return Device::DeviceSetupStatusFailure;
}
m_kebaDevices.insert(address, device);
return Device::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(wallboxDeviceIpParamTypeId).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);
}
}
Device::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(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(device->paramValue(wallboxDeviceIpParamTypeId).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(device->paramValue(wallboxDeviceIpParamTypeId).toString()) , 7090);
}
return Device::DeviceErrorNoError;
}
return Device::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(wallboxDeviceIpParamTypeId).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);
}
}
}