add a generic MQTT client plugin
parent
3ca44f103f
commit
7ee77fbc92
|
|
@ -0,0 +1,195 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2018 Michael Zanetti <michael.zanetti@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
/*!
|
||||
\page mqtt.html
|
||||
\title Generic MQTT
|
||||
\brief Plugin for catching UDP commands from the network.
|
||||
|
||||
\ingroup plugins
|
||||
\ingroup nymea-plugins-maker
|
||||
|
||||
This plugin allows to receive UDP packages over a certain UDP port and generates an \l{Event} if the message content matches
|
||||
the \l{Param} command.
|
||||
|
||||
\note This plugin is ment to be combined with a \l{nymeaserver::Rule}.
|
||||
|
||||
\section3 Example
|
||||
|
||||
If you create an UDP Commander on port 2323 and with the command \c{"Light 1 ON"}, following command will trigger an \l{Event} in nymea
|
||||
and allows you to connect this \l{Event} with a \l{nymeaserver::Rule}.
|
||||
|
||||
\note In this example nymea is running on \c localhost
|
||||
|
||||
\code
|
||||
$ echo "Light 1 ON" | nc -u localhost 2323
|
||||
OK
|
||||
\endcode
|
||||
|
||||
This allows you to execute \l{Action}{Actions} in your home automation system when a certain UDP message will be sent to nymea.
|
||||
|
||||
If the command will be recognized from nymea, the sender will receive as answere a \c{"OK"} string.
|
||||
|
||||
\chapter Plugin properties
|
||||
Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses}
|
||||
and \l{Vendor}{Vendors} of this \l{DevicePlugin}.
|
||||
|
||||
For more details how to read this JSON file please check out the documentation for \l{The plugin JSON File}.
|
||||
|
||||
\quotefile plugins/deviceplugins/udpcommander/devicepluginudpcommander.json
|
||||
*/
|
||||
|
||||
#include "devicepluginmqtt.h"
|
||||
#include "plugin/device.h"
|
||||
#include "plugininfo.h"
|
||||
#include "network/mqtt/mqttprovider.h"
|
||||
|
||||
#include "nymea-mqtt/mqttclient.h"
|
||||
|
||||
DevicePluginMqtt::DevicePluginMqtt()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DeviceManager::DeviceSetupStatus DevicePluginMqtt::setupDevice(Device *device)
|
||||
{
|
||||
MqttClient *client = nullptr;
|
||||
if (device->deviceClassId() == internalMqttClientDeviceClassId) {
|
||||
client = hardwareManager()->mqttProvider()->createInternalClient(device->id());
|
||||
} else if (device->deviceClassId() == mqttClientDeviceClassId){
|
||||
client = new MqttClient("nymea-" + device->id().toString().remove(QRegExp("[{}]")).left(8), this);
|
||||
client->setUsername(device->paramValue(mqttClientDeviceUsernameParamTypeId).toString());
|
||||
client->setPassword(device->paramValue(mqttClientDevicePasswordParamTypeId).toString());
|
||||
client->connectToHost(device->paramValue(mqttClientDeviceServerAddressParamTypeId).toString(), device->paramValue(mqttClientDeviceServerPortParamTypeId).toInt());
|
||||
}
|
||||
m_clients.insert(device, client);
|
||||
|
||||
connect(client, &MqttClient::connected, this, [this, device](){
|
||||
subscribe(device);
|
||||
});
|
||||
connect(client, &MqttClient::subscribed, this, [this, device](quint16 packetId, const Mqtt::SubscribeReturnCodes returnCodes){
|
||||
Q_UNUSED(packetId)
|
||||
emit deviceSetupFinished(device, returnCodes.first() == Mqtt::SubscribeReturnCodeFailure ? DeviceManager::DeviceSetupStatusFailure : DeviceManager::DeviceSetupStatusSuccess);
|
||||
});
|
||||
connect(client, &MqttClient::publishReceived, this, &DevicePluginMqtt::publishReceived);
|
||||
connect(client, &MqttClient::published, this, &DevicePluginMqtt::published);
|
||||
// In case we're already connected, manually call subscribe now
|
||||
if (client->isConnected()) {
|
||||
subscribe(device);
|
||||
}
|
||||
|
||||
return DeviceManager::DeviceSetupStatusAsync;
|
||||
}
|
||||
|
||||
|
||||
DeviceManager::DeviceError DevicePluginMqtt::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
ParamTypeId topicParamTypeId = internalMqttClientTriggerActionTopicParamTypeId;
|
||||
ParamTypeId payloadParamTypeId = internalMqttClientTriggerActionDataParamTypeId;
|
||||
ParamTypeId qosParamTypeId = internalMqttClientTriggerActionQosParamTypeId;
|
||||
ParamTypeId retainParamTypeId = internalMqttClientTriggerActionRetainParamTypeId;
|
||||
|
||||
if (device->deviceClassId() == mqttClientDeviceClassId) {
|
||||
topicParamTypeId = mqttClientTriggerActionTopicParamTypeId;
|
||||
payloadParamTypeId = mqttClientTriggerActionDataParamTypeId;
|
||||
qosParamTypeId = mqttClientTriggerActionQosParamTypeId;
|
||||
retainParamTypeId = mqttClientTriggerActionRetainParamTypeId;
|
||||
}
|
||||
|
||||
MqttClient *client = m_clients.value(device);
|
||||
if (!client) {
|
||||
qCWarning(dcMqttclient) << "No valid MQTT client for device" << device->name();
|
||||
return DeviceManager::DeviceErrorDeviceNotFound;
|
||||
}
|
||||
Mqtt::QoS qos = Mqtt::QoS0;
|
||||
switch (action.param(qosParamTypeId).value().toInt()) {
|
||||
case 0:
|
||||
qos = Mqtt::QoS0;
|
||||
break;
|
||||
case 1:
|
||||
qos = Mqtt::QoS1;
|
||||
break;
|
||||
case 2:
|
||||
qos = Mqtt::QoS2;
|
||||
break;
|
||||
}
|
||||
quint16 packetId = client->publish(action.param(topicParamTypeId).value().toString(),
|
||||
action.param(payloadParamTypeId).value().toByteArray(),
|
||||
qos,
|
||||
action.param(retainParamTypeId).value().toBool());
|
||||
m_pendingPublishes.insert(packetId, action);
|
||||
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
|
||||
void DevicePluginMqtt::subscribe(Device *device)
|
||||
{
|
||||
MqttClient *client = m_clients.value(device);
|
||||
if (!client) {
|
||||
// Device might have been removed
|
||||
return;
|
||||
}
|
||||
if (device->deviceClassId() == internalMqttClientDeviceClassId) {
|
||||
client->subscribe(device->paramValue(internalMqttClientDeviceTopicFilterParamTypeId).toString());
|
||||
} else {
|
||||
client->subscribe(device->paramValue(mqttClientDeviceTopicFilterParamTypeId).toString());
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginMqtt::publishReceived(const QString &topic, const QByteArray &payload, bool retained)
|
||||
{
|
||||
qCDebug(dcMqttclient()) << "Publish received" << topic << payload << retained;
|
||||
|
||||
MqttClient* client = static_cast<MqttClient*>(sender());
|
||||
Device *device = m_clients.key(client);
|
||||
if (!device) {
|
||||
qCWarning(dcMqttclient) << "Received a publish message from a client where de don't have a matching device";
|
||||
return;
|
||||
}
|
||||
|
||||
EventTypeId eventTypeId = internalMqttClientTriggeredEventTypeId;
|
||||
ParamTypeId topicParamTypeId = internalMqttClientTriggeredEventTopicParamTypeId;
|
||||
ParamTypeId payloadParamTypeId = internalMqttClientTriggeredEventDataParamTypeId;
|
||||
|
||||
if (device->deviceClassId() == mqttClientDeviceClassId) {
|
||||
eventTypeId = mqttClientTriggeredEventTypeId;
|
||||
topicParamTypeId = mqttClientTriggeredEventTopicParamTypeId;
|
||||
payloadParamTypeId = mqttClientTriggeredEventDataParamTypeId;
|
||||
}
|
||||
emitEvent(Event(eventTypeId, device->id(), ParamList() << Param(topicParamTypeId, topic) << Param(payloadParamTypeId, payload)));
|
||||
}
|
||||
|
||||
void DevicePluginMqtt::published(quint16 packetId)
|
||||
{
|
||||
if (!m_pendingPublishes.contains(packetId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit actionExecutionFinished(m_pendingPublishes.take(packetId).id(), DeviceManager::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
void DevicePluginMqtt::deviceRemoved(Device *device)
|
||||
{
|
||||
qCDebug(dcMqttclient) << device;
|
||||
m_clients.take(device)->deleteLater();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2018 Michael Zanetti <michael.zanetti@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef DEVICEPLUGINMQTT_H
|
||||
#define DEVICEPLUGINMQTT_H
|
||||
|
||||
#include "plugin/deviceplugin.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QDebug>
|
||||
#include <QUdpSocket>
|
||||
|
||||
class MqttClient;
|
||||
|
||||
class DevicePluginMqtt: public DevicePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginmqtt.json")
|
||||
Q_INTERFACES(DevicePlugin)
|
||||
|
||||
public:
|
||||
explicit DevicePluginMqtt();
|
||||
|
||||
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
|
||||
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
|
||||
private slots:
|
||||
void subscribe(Device *device);
|
||||
|
||||
void publishReceived(const QString &topic, const QByteArray &payload, bool retained);
|
||||
void published(quint16 packetId);
|
||||
|
||||
|
||||
private:
|
||||
QHash<Device*, MqttClient*> m_clients;
|
||||
|
||||
QHash<quint16, Action> m_pendingPublishes;
|
||||
};
|
||||
|
||||
#endif // DEVICEPLUGINMQTT_H
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
{
|
||||
"name": "mqttclient",
|
||||
"displayName": "MQTT client",
|
||||
"id": "27c58205-07c8-4482-85ad-b435387803a5",
|
||||
"vendors": [
|
||||
{
|
||||
"name": "guh",
|
||||
"displayName": "guh GmbH",
|
||||
"id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6",
|
||||
"deviceClasses": [
|
||||
{
|
||||
"id": "19117099-a5ef-44a1-b2bb-2efafe00f197",
|
||||
"name": "internalMqttClient",
|
||||
"displayName": "Internal MQTT client",
|
||||
"interfaces": ["inputtrigger", "outputtrigger"],
|
||||
"createMethods": ["user"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "4e91772a-82d8-498f-8b62-bba90a682e76",
|
||||
"name": "topicFilter",
|
||||
"displayName": "Subscription topic filter",
|
||||
"type": "QString",
|
||||
"defaultValue": "#"
|
||||
}
|
||||
],
|
||||
"eventTypes": [
|
||||
{
|
||||
"id": "d4ea2a70-da5a-49e0-9f30-aac1334b6a02",
|
||||
"name": "triggered",
|
||||
"displayName": "Publish received",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "27ec8baf-0c13-4d0a-aaee-313582592695",
|
||||
"name": "topic",
|
||||
"displayName": "Topic",
|
||||
"type": "QString"
|
||||
},
|
||||
{
|
||||
"id": "8af98566-79d9-4e65-b1dc-9067e4f93af1",
|
||||
"name": "data",
|
||||
"displayName": "Playload",
|
||||
"type": "QString"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"actionTypes": [
|
||||
{
|
||||
"id": "2f90ff12-dd67-4ddf-815d-330b4e2d56bf",
|
||||
"name": "trigger",
|
||||
"displayName": "Publish",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "bed321c2-a8c4-4420-b831-c4faa8501115",
|
||||
"name": "topic",
|
||||
"displayName": "Topic",
|
||||
"type": "QString",
|
||||
"defaultValue": "/"
|
||||
},
|
||||
{
|
||||
"id": "5bff6492-e6c7-4e50-a1c1-69881250561d",
|
||||
"name": "data",
|
||||
"displayName": "Payload",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "b019b678-aaf1-46d0-a0f8-af2131f14e55",
|
||||
"name": "qos",
|
||||
"displayName": "QoS",
|
||||
"type": "int",
|
||||
"minValue": 0,
|
||||
"maxValue": 2,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "c2c6386e-5b7d-4a2a-a8e8-e9c259ba926b",
|
||||
"name": "retain",
|
||||
"displayName": "Retain message",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "e325b581-8d7f-446e-b761-67554c5aacd4",
|
||||
"name": "mqttClient",
|
||||
"displayName": "MQTT client",
|
||||
"interfaces": ["inputtrigger", "outputtrigger"],
|
||||
"createMethods": ["user"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "a9a97dd6-9f80-43eb-a956-f5f3e4c6e3e2",
|
||||
"name": "serverAddress",
|
||||
"displayName": "Address",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "91973ede-b64e-4cae-ae67-6087df79eeb4",
|
||||
"name": "serverPort",
|
||||
"displayName": "Port",
|
||||
"type": "int",
|
||||
"minValue": 0,
|
||||
"maxValue": 65535,
|
||||
"defaultValue": 1883
|
||||
},
|
||||
{
|
||||
"id": "ae19fcc2-80ae-4d3f-8bac-4cf0db98d9e7",
|
||||
"name": "username",
|
||||
"displayName": "Username",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "d8211599-52f7-46f6-a741-a7204b987309",
|
||||
"name": "password",
|
||||
"displayName": "Password",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "53e2715a-e72f-445a-ae6b-2ac4e6031114",
|
||||
"name": "topicFilter",
|
||||
"displayName": "Subscription topic filter",
|
||||
"type": "QString",
|
||||
"defaultValue": "#"
|
||||
}
|
||||
],
|
||||
"eventTypes": [
|
||||
{
|
||||
"id": "243ec6ee-a72e-47e0-91dd-b9b918c43072",
|
||||
"name": "triggered",
|
||||
"displayName": "Publish received",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "bd83c7ec-3a14-46c6-a064-25757ceb0207",
|
||||
"name": "topic",
|
||||
"displayName": "Topic",
|
||||
"type": "QString"
|
||||
},
|
||||
{
|
||||
"id": "a947a277-a17a-4cb2-addb-f8ecec1cc63c",
|
||||
"name": "data",
|
||||
"displayName": "Playload",
|
||||
"type": "QString"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"actionTypes": [
|
||||
{
|
||||
"id": "39df4723-c888-4a3f-a151-9408699a9d25",
|
||||
"name": "trigger",
|
||||
"displayName": "Publish",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "193655ec-1714-4ea0-b8ee-f1dc312f15d3",
|
||||
"name": "topic",
|
||||
"displayName": "Topic",
|
||||
"type": "QString",
|
||||
"defaultValue": "/"
|
||||
},
|
||||
{
|
||||
"id": "a0e8989b-2797-4447-8d67-408382bfebae",
|
||||
"name": "data",
|
||||
"displayName": "Payload",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "4d2130be-8123-4103-b0bb-43ba876e147f",
|
||||
"name": "qos",
|
||||
"displayName": "QoS",
|
||||
"type": "int",
|
||||
"minValue": 0,
|
||||
"maxValue": 2,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "097774cc-7947-4eb1-bd30-ec4566afa628",
|
||||
"name": "retain",
|
||||
"displayName": "Retain message",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
include(../plugins.pri)
|
||||
|
||||
QT += network
|
||||
|
||||
TARGET = $$qtLibraryTarget(nymea_devicepluginmqtt)
|
||||
|
||||
SOURCES += \
|
||||
devicepluginmqtt.cpp
|
||||
|
||||
HEADERS += \
|
||||
devicepluginmqtt.h
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
</TS>
|
||||
|
|
@ -21,6 +21,7 @@ PLUGIN_DIRS = \
|
|||
leynew \
|
||||
lgsmarttv \
|
||||
mailnotification \
|
||||
mqtt \
|
||||
netatmo \
|
||||
networkdetector \
|
||||
openweathermap \
|
||||
|
|
|
|||
Loading…
Reference in New Issue