// SPDX-License-Identifier: GPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2024 - 2025, chargebyte austria GmbH * * This file is part of nymea-plugins. * * nymea-plugins 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, either version 3 of the License, or * (at your option) any later version. * * nymea-plugins 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-plugins. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "integrationplugintcpcommander.h" #include "plugininfo.h" #include "tcpserver.h" #include IntegrationPluginTcpCommander::IntegrationPluginTcpCommander() { } void IntegrationPluginTcpCommander::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); if (thing->thingClassId() == tcpClientThingClassId) { quint16 port = thing->paramValue(tcpClientThingPortParamTypeId).toUInt(); QHostAddress address= QHostAddress(thing->paramValue(tcpClientThingIpv4addressParamTypeId).toString()); QTcpSocket *tcpSocket = m_tcpSockets.value(thing); if (!tcpSocket) { tcpSocket = new QTcpSocket(this); m_tcpSockets.insert(thing, tcpSocket); } else { // In case of a reconfigure, make sure we reconnect tcpSocket->disconnectFromHost(); } connect(tcpSocket, &QTcpSocket::stateChanged, thing, [=](QAbstractSocket::SocketState state){ thing->setStateValue(tcpClientConnectedStateTypeId, state == QAbstractSocket::ConnectedState); if (state == QAbstractSocket::UnconnectedState) { QTimer::singleShot(10000, tcpSocket, [tcpSocket, address, port](){ qCDebug(dcTCPCommander()) << "Reconnecting to server" << address << port; tcpSocket->connectToHost(address, port); }); } }); connect(tcpSocket, &QTcpSocket::readyRead, thing, [this, thing, tcpSocket](){ QByteArray data = tcpSocket->readAll(); ParamList params; params << Param(tcpClientTriggeredEventDataParamTypeId, data); emit emitEvent(Event(tcpClientTriggeredEventTypeId, thing->id(), params)); }); tcpSocket->connectToHost(address, port); info->finish(Thing::ThingErrorNoError); return; } if (thing->thingClassId() == tcpServerThingClassId) { int port = thing->paramValue(tcpServerThingPortParamTypeId).toInt(); TcpServer *tcpServer = m_tcpServers.value(thing); if (tcpServer) { // In case of reconfigure, make sure to re-setup the server delete tcpServer; } tcpServer = new TcpServer(port, this); tcpServer->setConfirmCommands(thing->setting(tcpServerSettingsConfirmCommandParamTypeId).toBool()); if (tcpServer->isValid()) { m_tcpServers.insert(thing, tcpServer); connect(thing, &Thing::settingChanged, tcpServer, [tcpServer](const ParamTypeId ¶mTypeId, const QVariant &value){ if (paramTypeId == tcpServerSettingsConfirmCommandParamTypeId) { tcpServer->setConfirmCommands(value.toBool()); } }); connect(tcpServer, &TcpServer::connectionCountChanged, this, &IntegrationPluginTcpCommander::onTcpServerConnectionCountChanged); connect(tcpServer, &TcpServer::commandReceived, this, &IntegrationPluginTcpCommander::onTcpServerCommandReceived); info->finish(Thing::ThingErrorNoError); // Set the initial connected state since the server is running thing->setStateValue("connected", true); return; } else { tcpServer->deleteLater(); qCDebug(dcTCPCommander()) << "Could not open TCP Server"; info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("Error opening TCP port.")); return; } } } void IntegrationPluginTcpCommander::executeAction(ThingActionInfo *info) { Thing *thing = info->thing(); Action action = info->action(); if (action.actionTypeId() == tcpClientTriggerActionTypeId) { QTcpSocket *tcpSocket = m_tcpSockets.value(thing); QByteArray data = action.param(tcpClientTriggerActionDataParamTypeId).value().toByteArray(); qint64 len = tcpSocket->write(data); if (len == data.length()) { info->finish(Thing::ThingErrorNoError); } else { info->finish(Thing::ThingErrorHardwareNotAvailable); } } else if (action.actionTypeId() == tcpServerTriggerActionTypeId){ TcpServer *server = m_tcpServers.value(thing); QByteArray data = action.param(tcpServerTriggerActionDataParamTypeId).value().toByteArray(); QString clientIp = action.param(tcpServerTriggerActionClientIpParamTypeId).value().toString(); bool success = server->sendCommand(clientIp, data); if (success) { info->finish(Thing::ThingErrorNoError); } else { info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Failed to send the command to the specified client(s).")); } } } void IntegrationPluginTcpCommander::thingRemoved(Thing *thing) { if(thing->thingClassId() == tcpClientThingClassId){ QTcpSocket *tcpSocket = m_tcpSockets.take(thing); tcpSocket->deleteLater(); } else if(thing->thingClassId() == tcpServerThingClassId){ TcpServer *tcpServer = m_tcpServers.take(thing); tcpServer->deleteLater(); } } void IntegrationPluginTcpCommander::onTcpSocketConnectionChanged(bool connected) { QTcpSocket *tcpSocket = static_cast(sender()); Thing *thing = m_tcpSockets.key(tcpSocket); if (thing->thingClassId() == tcpClientThingClassId) { thing->setStateValue(tcpClientConnectedStateTypeId, connected); } } void IntegrationPluginTcpCommander::onTcpServerConnectionCountChanged(int connections) { TcpServer *tcpServer = static_cast(sender()); Thing *thing = m_tcpServers.key(tcpServer); if (thing && thing->thingClassId() == tcpServerThingClassId) { qCDebug(dcTCPCommander()) << thing->name() << "Tcp Server Client connected"; thing->setStateValue(tcpServerConnectionCountStateTypeId, connections); } } void IntegrationPluginTcpCommander::onTcpServerCommandReceived(const QString &clientIp, const QByteArray &data) { TcpServer *tcpServer = static_cast(sender()); Thing *thing = m_tcpServers.key(tcpServer); qCDebug(dcTCPCommander()) << thing->name() << "Message received" << data; ParamList params; params.append(Param(tcpServerTriggeredEventDataParamTypeId, data)); params.append(Param(tcpServerTriggeredEventClientIpParamTypeId, clientIp)); emit emitEvent(Event(tcpServerTriggeredEventTypeId, thing->id(), params)); }